第一次提交
This commit is contained in:
32
fastbee-protocol/fastbee-protocol-base/pom.xml
Normal file
32
fastbee-protocol/fastbee-protocol-base/pom.xml
Normal file
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>fastbee-protocol</artifactId>
|
||||
<groupId>com.fastbee</groupId>
|
||||
<version>3.8.5</version>
|
||||
</parent>
|
||||
|
||||
<description>网关协议管理模块</description>
|
||||
|
||||
<artifactId>fastbee-protocol-base</artifactId>
|
||||
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fastbee</groupId>
|
||||
<artifactId>fastbee-iot-service</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fastbee</groupId>
|
||||
<artifactId>base-server</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
</project>
|
@ -0,0 +1,33 @@
|
||||
package com.fastbee.protocol;
|
||||
|
||||
import com.fastbee.protocol.base.model.WModel;
|
||||
import com.fastbee.protocol.util.SingleVersionUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* @author bill
|
||||
*/
|
||||
public class PrepareLoadStore<T> {
|
||||
|
||||
private final Map<T, WModel> models = new TreeMap<>();
|
||||
|
||||
public PrepareLoadStore<T> addSchema(T key, WModel schema) {
|
||||
models.put(key, schema);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PrepareLoadStore<T> addSchema(T key, Class typeClass) {
|
||||
WModel<Object> model = SingleVersionUtils.getActiveModel(typeClass);
|
||||
models.put(key, model);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<T, WModel> build() {
|
||||
Map<T, WModel> a = new HashMap<>(models.size());
|
||||
a.putAll(models);
|
||||
return a;
|
||||
}
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
package com.fastbee.protocol;
|
||||
|
||||
import com.fastbee.protocol.base.annotation.Column;
|
||||
import com.fastbee.protocol.base.annotation.Columns;
|
||||
import com.fastbee.protocol.base.annotation.MergeSubClass;
|
||||
import com.fastbee.protocol.base.model.ActiveModel;
|
||||
import com.fastbee.protocol.base.model.ModelRegistry;
|
||||
import com.fastbee.protocol.base.model.WModel;
|
||||
import com.fastbee.protocol.base.struc.BaseStructure;
|
||||
import com.fastbee.protocol.util.ArrayMap;
|
||||
import com.fastbee.protocol.util.ClassUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 消息架构加载
|
||||
* @author bill
|
||||
*/
|
||||
public class ProtocolLoadUtils {
|
||||
private static final Map<String, ArrayMap<ActiveModel>> CACHE = new WeakHashMap<>();
|
||||
|
||||
public static ArrayMap<ActiveModel> getActiveMap(Class typeClass) {
|
||||
return getActiveMap(CACHE, typeClass);
|
||||
}
|
||||
|
||||
public static ActiveModel getActiveMap(Class typeClass, int version) {
|
||||
ArrayMap<ActiveModel> schemaMap = getActiveMap(CACHE, typeClass);
|
||||
if (schemaMap == null) return null;
|
||||
return schemaMap.getOrDefault(version);
|
||||
}
|
||||
|
||||
public static ArrayMap<ActiveModel> getActiveMap(Map<String, ArrayMap<ActiveModel>> root, final Class typeClass) {
|
||||
ArrayMap<ActiveModel> schemaMap = root.get(typeClass.getName());
|
||||
//不支持循环引用
|
||||
if (schemaMap != null) return schemaMap;
|
||||
|
||||
List<Field> fs = findFields(typeClass);
|
||||
if (fs.isEmpty()) return null;
|
||||
|
||||
root.put(typeClass.getName(), schemaMap = new ArrayMap<>());
|
||||
|
||||
Map<Integer, Set<BaseStructure>> multiVersionFields = findMultiVersionFields(root, fs);
|
||||
Set<BaseStructure> defFields = multiVersionFields.get(Integer.MAX_VALUE);
|
||||
for (Map.Entry<Integer, Set<BaseStructure>> entry : multiVersionFields.entrySet()) {
|
||||
|
||||
Integer version = entry.getKey();
|
||||
Set<BaseStructure> fieldList = entry.getValue();
|
||||
if (defFields != null && !version.equals(Integer.MAX_VALUE)) {
|
||||
for (BaseStructure defField : defFields) {
|
||||
if (!fieldList.contains(defField))
|
||||
fieldList.add(defField);
|
||||
}
|
||||
}
|
||||
|
||||
BaseStructure[] fields = fieldList.toArray(new BaseStructure[fieldList.size()]);
|
||||
Arrays.sort(fields);
|
||||
|
||||
ActiveModel schema = new ActiveModel(typeClass, version, fields);
|
||||
schemaMap.put(version, schema);
|
||||
}
|
||||
root.put(typeClass.getName(), schemaMap.fillDefaultValue());
|
||||
return schemaMap;
|
||||
}
|
||||
|
||||
private static List<Field> findFields(Class typeClass) {
|
||||
LinkedList<Field> fs = new LinkedList<>();
|
||||
|
||||
boolean addFirst = false;
|
||||
Class<?> temp = typeClass;
|
||||
|
||||
while (temp != null) {
|
||||
if (addFirst)
|
||||
fs.addAll(0, Arrays.asList(temp.getDeclaredFields()));
|
||||
else
|
||||
fs.addAll(Arrays.asList(temp.getDeclaredFields()));
|
||||
MergeSubClass marge = temp.getAnnotation(MergeSubClass.class);
|
||||
if (marge == null)
|
||||
break;
|
||||
addFirst = marge.addBefore();
|
||||
temp = typeClass.getSuperclass();
|
||||
}
|
||||
|
||||
List<Field> result = new ArrayList<>(fs.size());
|
||||
for (Field f : fs) {
|
||||
if (f.isAnnotationPresent(Columns.class) || f.isAnnotationPresent(Column.class)) {
|
||||
f.setAccessible(true);
|
||||
result.add(f);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Map<Integer, Set<BaseStructure>> findMultiVersionFields(Map<String, ArrayMap<ActiveModel>> root, List<Field> fs) {
|
||||
final int size = fs.size();
|
||||
Map<Integer, Set<BaseStructure>> multiVersionFields = new TreeMap<Integer, Set<BaseStructure>>() {
|
||||
@Override
|
||||
public Set<BaseStructure> get(Object key) {
|
||||
Set result = super.get(key);
|
||||
if (result == null) super.put((Integer) key, result = new HashSet(size));
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
Field f = fs.get(i);
|
||||
|
||||
Column column = f.getDeclaredAnnotation(Column.class);
|
||||
if (column != null) {
|
||||
fillField(root, multiVersionFields, column, f, i);
|
||||
} else {
|
||||
Column[] clos = f.getDeclaredAnnotation(Columns.class).value();
|
||||
for (int j = 0; j < clos.length; j++)
|
||||
fillField(root, multiVersionFields, clos[j], f, i);
|
||||
}
|
||||
}
|
||||
return multiVersionFields;
|
||||
}
|
||||
|
||||
private static void fillField(Map<String, ArrayMap<ActiveModel>> root, Map<Integer, Set<BaseStructure>> multiVersionFields, Column column, Field field, int position) {
|
||||
BaseStructure BaseStructure = ModelRegistry.get(column, field);
|
||||
int[] versions = getVersions(column, ALL);
|
||||
if (BaseStructure != null) {
|
||||
for (int ver : versions) {
|
||||
multiVersionFields.get(ver).add(BaseStructure.init(column, field, position));
|
||||
}
|
||||
} else {
|
||||
ArrayMap<ActiveModel> modelMap = getActiveMap(root, ClassUtils.getGenericType(field));
|
||||
if (versions == ALL)
|
||||
versions = modelMap.keys();
|
||||
for (int ver : versions) {
|
||||
WModel model = modelMap.getOrDefault(ver);
|
||||
BaseStructure = ModelRegistry.get(column, field, model);
|
||||
multiVersionFields.get(ver).add(BaseStructure.init(column, field, position));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final int[] ALL = {Integer.MAX_VALUE};
|
||||
|
||||
private static int[] getVersions(Column column, int[] def) {
|
||||
int[] result = column.version();
|
||||
if (result.length == 0)
|
||||
result = def;
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package com.fastbee.protocol;
|
||||
|
||||
import com.fastbee.protocol.base.annotation.Protocol;
|
||||
import com.fastbee.protocol.base.model.ActiveModel;
|
||||
import com.fastbee.protocol.util.ArrayMap;
|
||||
import com.fastbee.protocol.util.ClassUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 消息架构管理类
|
||||
*
|
||||
* @author bill
|
||||
*/
|
||||
public class WModelManager {
|
||||
|
||||
private final Map<Integer, ArrayMap<ActiveModel>> typeIdMapping;
|
||||
|
||||
private final Map<String, ArrayMap<ActiveModel>> typeClassMapping;
|
||||
|
||||
public WModelManager() {
|
||||
this(128);
|
||||
}
|
||||
|
||||
public WModelManager(int initialCapacity) {
|
||||
this.typeIdMapping = new HashMap<>(initialCapacity);
|
||||
this.typeClassMapping = new HashMap<>(initialCapacity);
|
||||
}
|
||||
|
||||
public WModelManager(String... basePackages) {
|
||||
this(256, basePackages);
|
||||
}
|
||||
|
||||
public WModelManager(int initialCapacity, String... basePackages) {
|
||||
this(initialCapacity);
|
||||
for (String basePackage : basePackages) {
|
||||
List<Class> types = ClassUtils.getClassList(basePackage);
|
||||
for (Class<?> type : types) {
|
||||
Protocol protocol = type.getAnnotation(Protocol.class);
|
||||
if (protocol != null) {
|
||||
int[] values = protocol.value();
|
||||
for (Integer typeId : values) {
|
||||
loadRuntimeSchema(typeId, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void loadRuntimeSchema(Integer typeId, Class typeClass) {
|
||||
ArrayMap<ActiveModel> schemaMap = ProtocolLoadUtils.getActiveMap(typeClassMapping, typeClass);
|
||||
if (schemaMap != null) {
|
||||
typeIdMapping.put(typeId, schemaMap);
|
||||
}
|
||||
}
|
||||
|
||||
public <T> ActiveModel<T> getActiveMap(Class<T> typeClass, int version) {
|
||||
ArrayMap<ActiveModel> schemaMap = ProtocolLoadUtils.getActiveMap(typeClassMapping, typeClass);
|
||||
if (schemaMap == null) {
|
||||
return null;
|
||||
}
|
||||
return schemaMap.getOrDefault(version);
|
||||
}
|
||||
|
||||
public ArrayMap<ActiveModel> getActiveMap(Class typeClass) {
|
||||
return ProtocolLoadUtils.getActiveMap(typeClassMapping, typeClass);
|
||||
}
|
||||
|
||||
public ActiveModel getActiveMap(Integer typeId, int version) {
|
||||
ArrayMap<ActiveModel> schemaMap = typeIdMapping.get(typeId);
|
||||
if (schemaMap == null) {
|
||||
return null;
|
||||
}
|
||||
return schemaMap.getOrDefault(version);
|
||||
}
|
||||
|
||||
public ArrayMap<ActiveModel> getActiveMap(Integer typeId) {
|
||||
return typeIdMapping.get(typeId);
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package com.fastbee.protocol.base.annotation;
|
||||
|
||||
import com.fastbee.protocol.base.model.WModel;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 报文解析字段注解
|
||||
* @author bill
|
||||
*/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Column {
|
||||
|
||||
/** 长度 默认使用类型长度 -1读取剩余报文长度*/
|
||||
int length() default -1;
|
||||
|
||||
/** 排序,默认按照model字段顺序编解码*/
|
||||
int index() default 0;
|
||||
|
||||
/** 该字段的前置长度 1- BYTE 2-WORD 3.double 4.DWORD */
|
||||
int lengthUnit() default -1;
|
||||
|
||||
/** 该字段的前置数量单位 1- BYTE 2-WORD 3.double 4.DWORD*/
|
||||
int totalUnit() default -1;
|
||||
|
||||
/** 字符集 HEX ,UTF8 ,GBK, BCD...*/
|
||||
String charset() default "HEX";
|
||||
|
||||
/**描述*/
|
||||
String desc() default "";
|
||||
|
||||
/** 版本号 ,默认不区分*/
|
||||
int[] version() default {};
|
||||
|
||||
/** 自定义报文转换器*/
|
||||
Class<? extends WModel> converter() default WModel.class;
|
||||
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.fastbee.protocol.base.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author bill
|
||||
*/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Columns {
|
||||
|
||||
Column[] value();
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.fastbee.protocol.base.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 将父类字段合并到子类
|
||||
* @author bill
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface MergeSubClass {
|
||||
|
||||
/**
|
||||
* 合并父类属性到当前类属性前
|
||||
*/
|
||||
boolean addBefore() default false;
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.fastbee.protocol.base.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 协议类型标注
|
||||
* @author bill
|
||||
*/
|
||||
@Inherited
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Protocol {
|
||||
|
||||
int[] value() default {};
|
||||
|
||||
String desc() default "";
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.fastbee.protocol.base.message;
|
||||
|
||||
/**
|
||||
* 通用消息体
|
||||
* @author bill
|
||||
*/
|
||||
public interface MessageBody {
|
||||
|
||||
/**
|
||||
* 消息体
|
||||
* @return
|
||||
*/
|
||||
byte[] getPayload();
|
||||
|
||||
/**
|
||||
* 消息体长度
|
||||
* @return
|
||||
*/
|
||||
default int getLength(){
|
||||
return getPayload().length;
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package com.fastbee.protocol.base.message;
|
||||
|
||||
/**
|
||||
* @author bill
|
||||
*/
|
||||
public interface MessageHead {
|
||||
|
||||
/**
|
||||
* 设备编号 modbus对应从机编号
|
||||
* @return
|
||||
*/
|
||||
String getSerialNumber();
|
||||
|
||||
/**
|
||||
* 设置设备编号
|
||||
* @param serialNumber
|
||||
*/
|
||||
MessageHead setSerialNumber(String serialNumber);
|
||||
|
||||
/**
|
||||
* 获取消息ID
|
||||
* @return
|
||||
*/
|
||||
String getMessageId();
|
||||
|
||||
/**
|
||||
* 设置消息id
|
||||
* @param messageId 消息ID
|
||||
* @return
|
||||
*/
|
||||
MessageHead setMessageId(String messageId);
|
||||
|
||||
/**
|
||||
* 消息头data
|
||||
* @return
|
||||
*/
|
||||
byte[] getMessage();
|
||||
|
||||
/**
|
||||
* 消息头长度
|
||||
* @return
|
||||
*/
|
||||
default int getLength() {
|
||||
return getMessage().length;
|
||||
}
|
||||
}
|
@ -0,0 +1,157 @@
|
||||
package com.fastbee.protocol.base.model;
|
||||
|
||||
import com.fastbee.protocol.base.struc.BaseStructure;
|
||||
import com.fastbee.protocol.util.ExplainUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
/**
|
||||
* 运行时根据Class生成的消息结构model,序列化对象
|
||||
* @author bill
|
||||
*/
|
||||
public class ActiveModel<T> implements WModel<T>{
|
||||
|
||||
protected int version;
|
||||
protected int length;
|
||||
protected Class<T> typeClass;
|
||||
protected BaseStructure[] structures;
|
||||
protected Constructor<T> constructor;
|
||||
|
||||
public ActiveModel(Class<T> typeClass, int version, BaseStructure[] structures) {
|
||||
this.typeClass = typeClass;
|
||||
this.version = version;
|
||||
this.structures = structures;
|
||||
int length = 0;
|
||||
for (BaseStructure structure : structures)
|
||||
length += structure.length();
|
||||
this.length = length;
|
||||
try {
|
||||
this.constructor = typeClass.getDeclaredConstructor((Class[]) null);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public T newInstance() {
|
||||
try {
|
||||
return constructor.newInstance((Object[]) null);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("newInstance failed " + typeClass.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public T mergeFrom(ByteBuf input, T result) {
|
||||
int i = 0;
|
||||
try {
|
||||
for (; i < structures.length; i++) {
|
||||
structures[i].readAndSet(input, result);
|
||||
}
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Read failed " + i + " " + typeClass.getName() + " " + structures[i].filedName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public T mergeFrom(ByteBuf input, T result, ExplainUtils explain) {
|
||||
int i = 0;
|
||||
try {
|
||||
if (explain == null)
|
||||
for (; i < structures.length; i++)
|
||||
structures[i].readAndSet(input, result);
|
||||
else
|
||||
for (; i < structures.length; i++)
|
||||
structures[i].readAndSet(input, result, explain);
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Read failed " + i + " " + typeClass.getName() + " " + structures[i].filedName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T readFrom(ByteBuf input) {
|
||||
int i = 0;
|
||||
try {
|
||||
T result = constructor.newInstance((Object[]) null);
|
||||
for (; i < structures.length; i++) {
|
||||
structures[i].readAndSet(input, result);
|
||||
}
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Read failed " + i + " " + typeClass.getName() + " " + structures[i].filedName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T readFrom(ByteBuf input, ExplainUtils explain) {
|
||||
int i = 0;
|
||||
try {
|
||||
T result = constructor.newInstance((Object[]) null);
|
||||
if (explain == null) {
|
||||
for (; i < structures.length; i++) {
|
||||
structures[i].readAndSet(input, result);
|
||||
}
|
||||
} else {
|
||||
for (; i < structures.length; i++) {
|
||||
structures[i].readAndSet(input, result, explain);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Read failed " + i + " " + typeClass.getName() + " " + structures[i].filedName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, T message) {
|
||||
int i = 0;
|
||||
try {
|
||||
for (; i < structures.length; i++) {
|
||||
structures[i].getAndWrite(output, message);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Write failed " + i + " " + typeClass.getName() + " " + structures[i].filedName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, T message, ExplainUtils explain) {
|
||||
int i = 0;
|
||||
try {
|
||||
if (explain == null) {
|
||||
for (; i < structures.length; i++) {
|
||||
structures[i].getAndWrite(output, message);
|
||||
}
|
||||
} else {
|
||||
for (; i < structures.length; i++) {
|
||||
structures[i].getAndWrite(output, message, explain);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Write failed " + i + " " + typeClass.getName() + " " + structures[i].filedName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public Class<T> typeClass() {
|
||||
return typeClass;
|
||||
}
|
||||
|
||||
public int version() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder(48);
|
||||
sb.append("{typeClass=").append(typeClass.getSimpleName());
|
||||
sb.append(", version=").append(version);
|
||||
sb.append(", length=").append(length);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,168 @@
|
||||
package com.fastbee.protocol.base.model;
|
||||
|
||||
import com.fastbee.protocol.base.struc.BaseStructure;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2022/11/9 17:33
|
||||
*/
|
||||
public class ArrayModel {
|
||||
public static final WModel<char[]> CHARS = new CharArray();
|
||||
public static final WModel<byte[]> BYTES = new ByteArray();
|
||||
public static final WModel<short[]> SHORTS = new ShortArray();
|
||||
public static final WModel<int[]> INTS = new IntArray();
|
||||
public static final WModel<float[]> FLOATS = new FloatArray();
|
||||
public static final WModel<long[]> LONGS = new LongArray();
|
||||
public static final WModel<double[]> DOUBLES = new DoubleArray();
|
||||
|
||||
protected static class ByteArray extends BaseStructure<byte[]> {
|
||||
@Override
|
||||
public byte[] readFrom(ByteBuf input) {
|
||||
byte[] array = new byte[input.readableBytes()];
|
||||
input.readBytes(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, byte[] array) {
|
||||
if (array == null) {
|
||||
return;
|
||||
}
|
||||
output.writeBytes(array);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class CharArray extends BaseStructure<char[]> {
|
||||
@Override
|
||||
public char[] readFrom(ByteBuf input) {
|
||||
int total = input.readableBytes() >> 1;
|
||||
char[] array = new char[total];
|
||||
for (int i = 0; i < total; i++) {
|
||||
array[i] = input.readChar();
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, char[] array) {
|
||||
if (array == null) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
output.writeChar(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class ShortArray extends BaseStructure<short[]> {
|
||||
@Override
|
||||
public short[] readFrom(ByteBuf input) {
|
||||
int total = input.readableBytes() >> 1;
|
||||
short[] array = new short[total];
|
||||
for (int i = 0; i < total; i++) {
|
||||
array[i] = input.readShort();
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, short[] array) {
|
||||
if (array == null) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
output.writeShort(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class IntArray extends BaseStructure<int[]> {
|
||||
@Override
|
||||
public int[] readFrom(ByteBuf input) {
|
||||
int total = input.readableBytes() >> 2;
|
||||
int[] array = new int[total];
|
||||
for (int i = 0; i < total; i++) {
|
||||
array[i] = input.readInt();
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, int[] array) {
|
||||
if (array == null) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
output.writeInt(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class LongArray extends BaseStructure<long[]> {
|
||||
@Override
|
||||
public long[] readFrom(ByteBuf input) {
|
||||
int total = input.readableBytes() >> 3;
|
||||
long[] array = new long[total];
|
||||
for (int i = 0; i < total; i++) {
|
||||
array[i] = input.readLong();
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, long[] array) {
|
||||
if (array == null) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
output.writeLong(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class FloatArray extends BaseStructure<float[]> {
|
||||
@Override
|
||||
public float[] readFrom(ByteBuf input) {
|
||||
int total = input.readableBytes() >> 2;
|
||||
float[] array = new float[total];
|
||||
for (int i = 0; i < total; i++) {
|
||||
array[i] = input.readFloat();
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, float[] array) {
|
||||
if (array == null) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
output.writeFloat(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class DoubleArray extends BaseStructure<double[]> {
|
||||
@Override
|
||||
public double[] readFrom(ByteBuf input) {
|
||||
int total = input.readableBytes() >> 3;
|
||||
double[] array = new double[total];
|
||||
for (int i = 0; i < total; i++) {
|
||||
array[i] = input.readDouble();
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, double[] array) {
|
||||
if (array == null) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
output.writeDouble(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package com.fastbee.protocol.base.model;
|
||||
|
||||
import com.fastbee.protocol.base.struc.BaseStructure;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2022/11/9 17:37
|
||||
*/
|
||||
public class BufferModel {
|
||||
|
||||
public static class ByteBufSchema extends BaseStructure<ByteBuf> {
|
||||
@Override
|
||||
public ByteBuf readFrom(ByteBuf input) {
|
||||
return input.readSlice(input.readableBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, ByteBuf value) {
|
||||
output.writeBytes(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ByteBufferSchema extends BaseStructure<ByteBuffer> {
|
||||
@Override
|
||||
public ByteBuffer readFrom(ByteBuf input) {
|
||||
ByteBuffer message = input.nioBuffer();
|
||||
input.skipBytes(input.readableBytes());
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, ByteBuffer value) {
|
||||
output.writeBytes(value);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
package com.fastbee.protocol.base.model;
|
||||
|
||||
import com.fastbee.protocol.base.struc.BaseStructure;
|
||||
import com.fastbee.protocol.util.DateTool;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2022/11/9 17:35
|
||||
*/
|
||||
public class DateTimeModel {
|
||||
|
||||
public static final WModel<LocalTime> BYTE_TIME = new Time(DateTool.BYTE);
|
||||
public static final WModel<LocalDate> BYTE_DATE = new Date(DateTool.BYTE);
|
||||
public static final WModel<LocalDateTime> BYTE_DATETIME = new DateTime(DateTool.BYTE);
|
||||
|
||||
public static final WModel<LocalTime> BCD_TIME = new Time(DateTool.BCD);
|
||||
public static final WModel<LocalDate> BCD_DATE = new Date(DateTool.BCD);
|
||||
public static final WModel<LocalDateTime> BCD_DATETIME = new DateTime(DateTool.BCD);
|
||||
|
||||
protected static class DateTime extends BaseStructure<LocalDateTime> {
|
||||
protected final DateTool tool;
|
||||
|
||||
protected DateTime(DateTool tool) {
|
||||
this.tool = tool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDateTime readFrom(ByteBuf input) {
|
||||
byte[] bytes = new byte[6];
|
||||
input.readBytes(bytes);
|
||||
return tool.toDateTime(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, LocalDateTime value) {
|
||||
output.writeBytes(tool.from(value));
|
||||
}
|
||||
}
|
||||
|
||||
protected static class Date extends BaseStructure<LocalDate> {
|
||||
protected final DateTool tool;
|
||||
|
||||
protected Date(DateTool tool) {
|
||||
this.tool = tool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDate readFrom(ByteBuf input) {
|
||||
byte[] bytes = new byte[3];
|
||||
input.readBytes(bytes);
|
||||
return tool.toDate(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, LocalDate value) {
|
||||
output.writeBytes(tool.from(value));
|
||||
}
|
||||
}
|
||||
|
||||
protected static class Time extends BaseStructure<LocalTime> {
|
||||
protected final DateTool tool;
|
||||
|
||||
protected Time(DateTool tool) {
|
||||
this.tool = tool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalTime readFrom(ByteBuf input) {
|
||||
byte[] bytes = new byte[3];
|
||||
input.readBytes(bytes);
|
||||
return tool.toTime(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, LocalTime value) {
|
||||
output.writeBytes(tool.from(value));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
package com.fastbee.protocol.base.model;
|
||||
|
||||
import com.fastbee.protocol.PrepareLoadStore;
|
||||
import com.fastbee.protocol.base.struc.BaseStructure;
|
||||
import com.fastbee.protocol.util.IntTool;
|
||||
import com.fastbee.protocol.util.KeyValuePair;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author bill
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class MapModel<K,V> extends BaseStructure<Map.Entry<K, V>> {
|
||||
|
||||
public final WModel<K> kwModel;
|
||||
public final int lengthUnit;
|
||||
public final IntTool intTool;
|
||||
public final Map<K, WModel> valueSchema;
|
||||
|
||||
public MapModel(WModel<K> keySchema, int lengthUnit) {
|
||||
this.kwModel = keySchema;
|
||||
this.lengthUnit = lengthUnit;
|
||||
this.intTool = IntTool.getInstance(lengthUnit);
|
||||
PrepareLoadStore<K> loadStrategy = new PrepareLoadStore<>();
|
||||
addSchemas(loadStrategy);
|
||||
this.valueSchema = loadStrategy.build();
|
||||
}
|
||||
|
||||
protected abstract void addSchemas(PrepareLoadStore<K> schemaRegistry);
|
||||
|
||||
@Override
|
||||
public KeyValuePair<K, V> readFrom(ByteBuf in) {
|
||||
K key = kwModel.readFrom(in);
|
||||
KeyValuePair<K, V> result = new KeyValuePair<>(key);
|
||||
|
||||
int length = intTool.read(in);
|
||||
if (length > 0) {
|
||||
int writerIndex = in.writerIndex();
|
||||
in.writerIndex(in.readerIndex() + length);
|
||||
|
||||
WModel<V> model = valueSchema.get(key);
|
||||
if (model != null) {
|
||||
V value = model.readFrom(in, length);
|
||||
result.setValue(value);
|
||||
} else {
|
||||
byte[] bytes = new byte[length];
|
||||
in.readBytes(bytes);
|
||||
result.setValue((V) bytes);
|
||||
}
|
||||
in.writerIndex(writerIndex);
|
||||
|
||||
} else if (length < 0) {
|
||||
WModel<V> model = valueSchema.get(key);
|
||||
if (model != null) {
|
||||
V value = model.readFrom(in);
|
||||
result.setValue(value);
|
||||
} else {
|
||||
byte[] bytes = new byte[in.readableBytes()];
|
||||
in.readBytes(bytes);
|
||||
result.setValue((V) bytes);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf out, Map.Entry<K, V> entry) {
|
||||
if (entry == null)
|
||||
return;
|
||||
K key = entry.getKey();
|
||||
kwModel.writeTo(out, key);
|
||||
|
||||
WModel model = valueSchema.get(key);
|
||||
if (model != null) {
|
||||
int begin = out.writerIndex();
|
||||
intTool.write(out, 0);
|
||||
|
||||
Object value = entry.getValue();
|
||||
if (value != null) {
|
||||
model.writeTo(out, value);
|
||||
int length = out.writerIndex() - begin - lengthUnit;
|
||||
intTool.set(out, begin, length);
|
||||
}
|
||||
} else {
|
||||
log.warn("未注册的信息:ID[{}], Value[{}]", key, entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,218 @@
|
||||
package com.fastbee.protocol.base.model;
|
||||
|
||||
import com.google.protobuf.MapField;
|
||||
import com.fastbee.protocol.base.annotation.Column;
|
||||
import com.fastbee.protocol.base.struc.*;
|
||||
import com.fastbee.protocol.util.DateTool;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.temporal.Temporal;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 消息编码类注册
|
||||
*
|
||||
* @author gsb
|
||||
* @date 2022/11/9 16:55
|
||||
*/
|
||||
public class ModelRegistry {
|
||||
|
||||
private static final Map<String, Function<DateTool, BaseStructure>> TIME_MODEL = new HashMap<>(6);
|
||||
|
||||
private static final Map<String, Supplier<BaseStructure>> NO_ARGS = new HashMap<>(128);
|
||||
|
||||
private static final Map<String, Integer> NUMBER = new HashMap<>(12);
|
||||
|
||||
static {
|
||||
NUMBER.put(boolean.class.getName(), 1);
|
||||
NUMBER.put(char.class.getName(), 2);
|
||||
NUMBER.put(byte.class.getName(), 1);
|
||||
NUMBER.put(short.class.getName(), 2);
|
||||
NUMBER.put(int.class.getName(), 4);
|
||||
NUMBER.put(long.class.getName(), 8);
|
||||
NUMBER.put(float.class.getName(), 4);
|
||||
NUMBER.put(double.class.getName(), 8);
|
||||
|
||||
NUMBER.put(Boolean.class.getName(), 1);
|
||||
NUMBER.put(Character.class.getName(), 2);
|
||||
NUMBER.put(Byte.class.getName(), 1);
|
||||
NUMBER.put(Short.class.getName(), 2);
|
||||
NUMBER.put(Integer.class.getName(), 4);
|
||||
NUMBER.put(Long.class.getName(), 8);
|
||||
NUMBER.put(Float.class.getName(), 4);
|
||||
NUMBER.put(Double.class.getName(), 8);
|
||||
|
||||
register(short.class, NumberPModel.WORD2ShortLE::new, 2, "LE");
|
||||
register(int.class, NumberPModel.WORD2IntLE::new, 2, "LE");
|
||||
register(int.class, NumberPModel.DWORD2IntLE::new, 4, "LE");
|
||||
register(long.class, NumberPModel.DWORD2LongLE::new, 4, "LE");
|
||||
register(long.class, NumberPModel.QWORD2LongLE::new, 8, "LE");
|
||||
register(short.class, NumberPModel.WORD2ShortLE::new, "LE");
|
||||
register(int.class, NumberPModel.DWORD2IntLE::new, "LE");
|
||||
register(long.class, NumberPModel.QWORD2LongLE::new, "LE");
|
||||
register(float.class, NumberPModel.DWORD2FloatLE::new, "LE");
|
||||
register(double.class, NumberPModel.QWORD2DoubleLE::new, "LE");
|
||||
register(byte.class, NumberPModel.BYTE2Byte::new, 1);
|
||||
register(short.class, NumberPModel.BYTE2Short::new, 1);
|
||||
register(int.class, NumberPModel.BYTE2Int::new, 1);
|
||||
register(short.class, NumberPModel.WORD2Short::new, 2);
|
||||
register(int.class, NumberPModel.WORD2Int::new, 2);
|
||||
register(int.class, NumberPModel.DWORD2Int::new, 4);
|
||||
register(long.class, NumberPModel.DWORD2Long::new, 4);
|
||||
register(long.class, NumberPModel.QWORD2Long::new, 8);
|
||||
register(boolean.class, NumberPModel.BOOL::new);
|
||||
register(char.class, NumberPModel.CHAR::new);
|
||||
register(byte.class, NumberPModel.BYTE2Byte::new);
|
||||
register(short.class, NumberPModel.WORD2Short::new);
|
||||
register(int.class, NumberPModel.DWORD2Int::new);
|
||||
register(long.class, NumberPModel.QWORD2Long::new);
|
||||
register(float.class, NumberPModel.DWORD2Float::new);
|
||||
register(double.class, NumberPModel.QWORD2Double::new);
|
||||
|
||||
register(Short.class, NumberModel.WORD2ShortLE::new, 2, "LE");
|
||||
register(Integer.class, NumberModel.WORD2IntLE::new, 2, "LE");
|
||||
register(Integer.class, NumberModel.DWORD2IntLE::new, 4, "LE");
|
||||
register(Long.class, NumberModel.DWORD2LongLE::new, 4, "LE");
|
||||
register(Long.class, NumberModel.QWORD2LongLE::new, 8, "LE");
|
||||
register(Short.class, NumberModel.WORD2ShortLE::new, "LE");
|
||||
register(Integer.class, NumberModel.DWORD2IntLE::new, "LE");
|
||||
register(Long.class, NumberModel.QWORD2LongLE::new, "LE");
|
||||
register(Float.class, NumberModel.DWORD2FloatLE::new, "LE");
|
||||
register(Double.class, NumberModel.QWORD2DoubleLE::new, "LE");
|
||||
register(Byte.class, NumberModel.BYTE2Byte::new, 1);
|
||||
register(Short.class, NumberModel.BYTE2Short::new, 1);
|
||||
register(Integer.class, NumberModel.BYTE2Int::new, 1);
|
||||
register(Short.class, NumberModel.WORD2Short::new, 2);
|
||||
register(Integer.class, NumberModel.WORD2Int::new, 2);
|
||||
register(Integer.class, NumberModel.DWORD2Int::new, 4);
|
||||
register(Long.class, NumberModel.DWORD2Long::new, 4);
|
||||
register(Long.class, NumberModel.QWORD2Long::new, 8);
|
||||
register(Boolean.class, NumberModel.BOOL::new);
|
||||
register(Character.class, /**/NumberModel.CHAR::new);
|
||||
register(Byte.class, NumberModel.BYTE2Byte::new);
|
||||
register(Short.class, NumberModel.WORD2Short::new);
|
||||
register(Integer.class, NumberModel.DWORD2Int::new);
|
||||
register(Long.class, NumberModel.QWORD2Long::new);
|
||||
register(Float.class, NumberModel.DWORD2Float::new);
|
||||
register(Double.class, NumberModel.QWORD2Double::new);
|
||||
|
||||
register(byte[].class, ArrayModel.ByteArray::new);
|
||||
register(char[].class, ArrayModel.CharArray::new);
|
||||
register(short[].class, ArrayModel.ShortArray::new);
|
||||
register(int[].class, ArrayModel.IntArray::new);
|
||||
register(long[].class, ArrayModel.LongArray::new);
|
||||
register(float[].class, ArrayModel.FloatArray::new);
|
||||
register(double[].class, ArrayModel.DoubleArray::new);
|
||||
register(ByteBuffer.class, BufferModel.ByteBufferSchema::new);
|
||||
register(ByteBuf.class, BufferModel.ByteBufSchema::new);
|
||||
|
||||
TIME_MODEL.put(LocalTime.class.getName(), DateTimeModel.Time::new);
|
||||
TIME_MODEL.put(LocalDate.class.getName(), DateTimeModel.Date::new);
|
||||
TIME_MODEL.put(LocalDateTime.class.getName(), DateTimeModel.DateTime::new);
|
||||
}
|
||||
|
||||
public static void register(Class typeClass, Supplier<BaseStructure> supplier, int length, String charset) {
|
||||
NO_ARGS.put(typeClass.getName() + "/" + length + "/" + charset, supplier);
|
||||
}
|
||||
|
||||
public static void register(Class typeClass, Supplier<BaseStructure> supplier, int length) {
|
||||
NO_ARGS.put(typeClass.getName() + "/" + length, supplier);
|
||||
}
|
||||
|
||||
public static void register(Class typeClass, Supplier<BaseStructure> supplier, String charset) {
|
||||
NO_ARGS.put(typeClass.getName() + "/" + charset, supplier);
|
||||
}
|
||||
|
||||
public static void register(Class typeClass, Supplier model) {
|
||||
NO_ARGS.put(typeClass.getName(), model);
|
||||
}
|
||||
|
||||
public static WModel getCustom(Class<? extends WModel> clazz) {
|
||||
try {
|
||||
return clazz.getDeclaredConstructor((Class[]) null).newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static BaseStructure get(Column column, java.lang.reflect.Field f) {
|
||||
Class typeClass = f.getType();
|
||||
String name = typeClass.getName();
|
||||
String charset = column.charset().toUpperCase();
|
||||
int length = column.length();
|
||||
|
||||
if (NUMBER.containsKey(name)) {
|
||||
if (length > 0)
|
||||
name += "/" + length;
|
||||
if (charset.equals("LE"))
|
||||
name += "/LE";
|
||||
return NO_ARGS.get(name).get();
|
||||
}
|
||||
|
||||
if (String.class.isAssignableFrom(typeClass)) {
|
||||
return StringModel.getInstance(charset, length, column.lengthUnit());
|
||||
}
|
||||
if (Temporal.class.isAssignableFrom(typeClass)) {
|
||||
return TIME_MODEL.get(name).apply(charset.equals("BCD") ? DateTool.BCD : DateTool.BYTE);
|
||||
}
|
||||
|
||||
if (WModel.class != column.converter()) {
|
||||
return get(column, f, getCustom(column.converter()));
|
||||
}
|
||||
|
||||
Supplier<BaseStructure> supplier = NO_ARGS.get(name);
|
||||
if (supplier != null)
|
||||
return get(column, f, supplier.get());
|
||||
return null;
|
||||
}
|
||||
|
||||
public static BaseStructure get(Column column, java.lang.reflect.Field f, WModel model) {
|
||||
Class typeClass = f.getType();
|
||||
if (column.totalUnit() > 0) {
|
||||
if (Collection.class.isAssignableFrom(typeClass)) {
|
||||
return new TotalCollectionStructure(model, column.totalUnit());
|
||||
}
|
||||
if (Map.class.isAssignableFrom(typeClass)) {
|
||||
return new TotalMapStructure((MapModel) model, column.totalUnit(), typeClass);
|
||||
}
|
||||
if (typeClass.isArray()) {
|
||||
typeClass = typeClass.getComponentType();
|
||||
if (typeClass.isPrimitive())
|
||||
return new TotalArrayPrimitiveStructure(model, column.totalUnit(), typeClass);
|
||||
return new TotalArrayObjectStructure(model, column.totalUnit(), typeClass);
|
||||
}
|
||||
}
|
||||
|
||||
if (column.lengthUnit() > 0) {
|
||||
if (Collection.class.isAssignableFrom(typeClass))
|
||||
return new LengthUnitCollectionStructure(model, column.lengthUnit());
|
||||
return new LengthStructure(model, column.lengthUnit());
|
||||
}
|
||||
|
||||
if (column.length() > 0) {
|
||||
return new LengthStructure(model, column.length());
|
||||
}
|
||||
if (Collection.class.isAssignableFrom(typeClass)) {
|
||||
return new CollectionStructure(model);
|
||||
} else if (Map.class.isAssignableFrom(typeClass)) {
|
||||
return new MapStructure((MapModel) model, typeClass);
|
||||
}
|
||||
return (BaseStructure) model;
|
||||
}
|
||||
|
||||
public static int getLength(Class typeClass) {
|
||||
Integer len = NUMBER.get(typeClass.getName());
|
||||
if (len == null)
|
||||
return -1;
|
||||
return len;
|
||||
}
|
||||
}
|
@ -0,0 +1,367 @@
|
||||
package com.fastbee.protocol.base.model;
|
||||
|
||||
import com.fastbee.protocol.base.struc.BaseStructure;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2022/11/9 16:43
|
||||
*/
|
||||
public class NumberModel {
|
||||
|
||||
public static final WModel<Boolean> BOOL = new BOOL();
|
||||
public static final WModel<Character> CHAR = new CHAR();
|
||||
public static final WModel<Byte> BYTE_BYTE = new BYTE2Byte();
|
||||
public static final WModel<Short> BYTE_SHORT = new BYTE2Short();
|
||||
public static final WModel<Integer> BYTE_INT = new BYTE2Int();
|
||||
|
||||
public static final WModel<Short> WORD_SHORT = new WORD2Short();
|
||||
public static final WModel<Integer> WORD_INT = new WORD2Int();
|
||||
public static final WModel<Integer> DWORD_INT = new DWORD2Int();
|
||||
public static final WModel<Long> DWORD_LONG = new DWORD2Long();
|
||||
public static final WModel<Float> DWORD_FLOAT = new DWORD2Float();
|
||||
public static final WModel<Long> QWORD_LONG = new QWORD2Long();
|
||||
public static final WModel<Double> QWORD_DOUBLE = new QWORD2Double();
|
||||
|
||||
public static final WModel<Short> WORD_SHORT_LE = new WORD2ShortLE();
|
||||
public static final WModel<Integer> WORD_INT_LE = new WORD2IntLE();
|
||||
public static final WModel<Integer> DWORD_INT_LE = new DWORD2IntLE();
|
||||
public static final WModel<Long> DWORD_LONG_LE = new DWORD2LongLE();
|
||||
public static final WModel<Float> DWORD_FLOAT_LE = new DWORD2FloatLE();
|
||||
public static final WModel<Long> QWORD_LONG_LE = new QWORD2LongLE();
|
||||
public static final WModel<Double> QWORD_DOUBLE_LE = new QWORD2DoubleLE();
|
||||
|
||||
protected static class BOOL extends BaseStructure<Boolean> {
|
||||
@Override
|
||||
public Boolean readFrom(ByteBuf input) {
|
||||
if (!input.isReadable()) {
|
||||
return null;
|
||||
}
|
||||
return input.readBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Boolean value) {
|
||||
if (value != null) {
|
||||
output.writeBoolean(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class CHAR extends BaseStructure<Character> {
|
||||
@Override
|
||||
public Character readFrom(ByteBuf input) {
|
||||
if (!input.isReadable()) {
|
||||
return null;
|
||||
}
|
||||
return input.readChar();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Character value) {
|
||||
if (value != null) {
|
||||
output.writeChar(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class BYTE2Byte extends BaseStructure<Byte> {
|
||||
@Override
|
||||
public Byte readFrom(ByteBuf input) {
|
||||
if (!input.isReadable()) {
|
||||
return null;
|
||||
}
|
||||
return input.readByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Byte value) {
|
||||
if (value != null) {
|
||||
output.writeByte(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class BYTE2Short extends BaseStructure<Short> {
|
||||
@Override
|
||||
public Short readFrom(ByteBuf input) {
|
||||
if (!input.isReadable()) {
|
||||
return null;
|
||||
}
|
||||
return input.readUnsignedByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Short value) {
|
||||
if (value != null) {
|
||||
output.writeByte(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class BYTE2Int extends BaseStructure<Integer> {
|
||||
@Override
|
||||
public Integer readFrom(ByteBuf input) {
|
||||
if (!input.isReadable()) {
|
||||
return null;
|
||||
}
|
||||
return (int) input.readUnsignedByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Integer value) {
|
||||
if (value != null) {
|
||||
output.writeByte(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class WORD2Short extends BaseStructure<Short> {
|
||||
@Override
|
||||
public Short readFrom(ByteBuf input) {
|
||||
if (!input.isReadable()) {
|
||||
return null;
|
||||
}
|
||||
return input.readShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Short value) {
|
||||
if (value != null) {
|
||||
output.writeShort(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class WORD2Int extends BaseStructure<Integer> {
|
||||
|
||||
@Override
|
||||
public void readAndSet(ByteBuf input, Object obj) throws Exception {
|
||||
super.readAndSet(input, obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getAndWrite(ByteBuf output, Object obj) throws Exception {
|
||||
super.getAndWrite(output, obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer readFrom(ByteBuf input) {
|
||||
if (!input.isReadable()) {
|
||||
return null;
|
||||
}
|
||||
return input.readUnsignedShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Integer value) {
|
||||
if (value != null) {
|
||||
output.writeShort(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class DWORD2Int extends BaseStructure<Integer> {
|
||||
@Override
|
||||
public Integer readFrom(ByteBuf input) {
|
||||
if (!input.isReadable()) {
|
||||
return null;
|
||||
}
|
||||
return input.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Integer value) {
|
||||
if (value != null) {
|
||||
output.writeInt(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class DWORD2Long extends BaseStructure<Long> {
|
||||
@Override
|
||||
public Long readFrom(ByteBuf input) {
|
||||
if (!input.isReadable()) {
|
||||
return null;
|
||||
}
|
||||
return input.readUnsignedInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Long value) {
|
||||
if (value != null) {
|
||||
output.writeInt(value.intValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class DWORD2Float extends BaseStructure<Float> {
|
||||
@Override
|
||||
public Float readFrom(ByteBuf input) {
|
||||
if (!input.isReadable()) {
|
||||
return null;
|
||||
}
|
||||
return input.readFloat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Float value) {
|
||||
if (value != null) {
|
||||
output.writeFloat(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class QWORD2Long extends BaseStructure<Long> {
|
||||
@Override
|
||||
public Long readFrom(ByteBuf input) {
|
||||
if (!input.isReadable()) {
|
||||
return null;
|
||||
}
|
||||
return input.readLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Long value) {
|
||||
if (value != null) {
|
||||
output.writeLong(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class QWORD2Double extends BaseStructure<Double> {
|
||||
@Override
|
||||
public Double readFrom(ByteBuf input) {
|
||||
if (!input.isReadable()) {
|
||||
return null;
|
||||
}
|
||||
return input.readDouble();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Double value) {
|
||||
if (value != null) {
|
||||
output.writeDouble(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class WORD2ShortLE extends BaseStructure<Short> {
|
||||
@Override
|
||||
public Short readFrom(ByteBuf input) {
|
||||
if (!input.isReadable()) {
|
||||
return null;
|
||||
}
|
||||
return input.readShortLE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Short value) {
|
||||
if (value != null) {
|
||||
output.writeShortLE(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class WORD2IntLE extends BaseStructure<Integer> {
|
||||
@Override
|
||||
public Integer readFrom(ByteBuf input) {
|
||||
if (!input.isReadable()) {
|
||||
return null;
|
||||
}
|
||||
return input.readUnsignedShortLE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Integer value) {
|
||||
if (value != null) {
|
||||
output.writeShortLE(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class DWORD2IntLE extends BaseStructure<Integer> {
|
||||
@Override
|
||||
public Integer readFrom(ByteBuf input) {
|
||||
if (!input.isReadable()) {
|
||||
return null;
|
||||
}
|
||||
return input.readIntLE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Integer value) {
|
||||
if (value != null) {
|
||||
output.writeIntLE(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class DWORD2LongLE extends BaseStructure<Long> {
|
||||
@Override
|
||||
public Long readFrom(ByteBuf input) {
|
||||
if (!input.isReadable()) {
|
||||
return null;
|
||||
}
|
||||
return input.readUnsignedIntLE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Long value) {
|
||||
if (value != null) {
|
||||
output.writeIntLE(value.intValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class DWORD2FloatLE extends BaseStructure<Float> {
|
||||
@Override
|
||||
public Float readFrom(ByteBuf input) {
|
||||
if (!input.isReadable()) {
|
||||
return null;
|
||||
}
|
||||
return input.readFloatLE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Float value) {
|
||||
if (value != null) {
|
||||
output.writeFloatLE(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class QWORD2LongLE extends BaseStructure<Long> {
|
||||
@Override
|
||||
public Long readFrom(ByteBuf input) {
|
||||
if (!input.isReadable()) {
|
||||
return null;
|
||||
}
|
||||
return input.readLongLE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Long value) {
|
||||
if (value != null) {
|
||||
output.writeLongLE(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class QWORD2DoubleLE extends BaseStructure<Double> {
|
||||
@Override
|
||||
public Double readFrom(ByteBuf input) {
|
||||
if (!input.isReadable()) {
|
||||
return null;
|
||||
}
|
||||
return input.readDoubleLE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Double value) {
|
||||
if (value != null) {
|
||||
output.writeDoubleLE(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,454 @@
|
||||
package com.fastbee.protocol.base.model;
|
||||
|
||||
import com.fastbee.protocol.base.struc.BaseStructure;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2022/11/9 16:56
|
||||
*/
|
||||
public class NumberPModel {
|
||||
|
||||
public static final WModel<Boolean> BOOL = new BOOL();
|
||||
public static final WModel<Character> CHAR = new CHAR();
|
||||
public static final WModel<Byte> BYTE_BYTE = new BYTE2Byte();
|
||||
public static final WModel<Short> BYTE_SHORT = new BYTE2Short();
|
||||
public static final WModel<Integer> BYTE_INT = new BYTE2Int();
|
||||
|
||||
public static final WModel<Short> WORD_SHORT = new WORD2Short();
|
||||
public static final WModel<Integer> WORD_INT = new WORD2Int();
|
||||
public static final WModel<Integer> DWORD_INT = new DWORD2Int();
|
||||
public static final WModel<Long> DWORD_LONG = new DWORD2Long();
|
||||
public static final WModel<Float> DWORD_FLOAT = new DWORD2Float();
|
||||
public static final WModel<Long> QWORD_LONG = new QWORD2Long();
|
||||
public static final WModel<Double> QWORD_DOUBLE = new QWORD2Double();
|
||||
|
||||
public static final WModel<Short> WORD_SHORT_LE = new WORD2ShortLE();
|
||||
public static final WModel<Integer> WORD_INT_LE = new WORD2IntLE();
|
||||
public static final WModel<Integer> DWORD_INT_LE = new DWORD2IntLE();
|
||||
public static final WModel<Long> DWORD_LONG_LE = new DWORD2LongLE();
|
||||
public static final WModel<Float> DWORD_FLOAT_LE = new DWORD2FloatLE();
|
||||
public static final WModel<Long> QWORD_LONG_LE = new QWORD2LongLE();
|
||||
public static final WModel<Double> QWORD_DOUBLE_LE = new QWORD2DoubleLE();
|
||||
|
||||
protected static class BOOL extends BaseStructure<Boolean> {
|
||||
|
||||
@Override
|
||||
public void readAndSet(ByteBuf in, Object obj) throws Exception {
|
||||
field.setBoolean(obj, in.readBoolean());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getAndWrite(ByteBuf out, Object obj) throws Exception {
|
||||
out.writeBoolean(field.getBoolean(obj));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean readFrom(ByteBuf in) {
|
||||
return in.readBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf out, Boolean value) {
|
||||
out.writeBoolean(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class CHAR extends BaseStructure<Character> {
|
||||
@Override
|
||||
public void readAndSet(ByteBuf in, Object obj) throws Exception {
|
||||
field.setChar(obj, in.readChar());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void getAndWrite(ByteBuf out, Object obj) throws Exception {
|
||||
out.writeChar(field.getChar(obj));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Character readFrom(ByteBuf in) {
|
||||
return in.readChar();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf out, Character value) {
|
||||
out.writeChar(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class BYTE2Byte extends BaseStructure<Byte> {
|
||||
@Override
|
||||
public void readAndSet(ByteBuf in, Object obj) throws Exception {
|
||||
field.setByte(obj, in.readByte());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getAndWrite(ByteBuf out, Object obj) throws Exception {
|
||||
out.writeByte(field.getByte(obj));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Byte readFrom(ByteBuf in) {
|
||||
return in.readByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf out, Byte value) {
|
||||
out.writeByte(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class BYTE2Short extends BaseStructure<Short> {
|
||||
@Override
|
||||
public void readAndSet(ByteBuf in, Object obj) throws Exception {
|
||||
field.setShort(obj, in.readUnsignedByte());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getAndWrite(ByteBuf out, Object obj) throws Exception {
|
||||
out.writeByte(field.getShort(obj));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Short readFrom(ByteBuf in) {
|
||||
return in.readUnsignedByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf out, Short value) {
|
||||
out.writeByte(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class BYTE2Int extends BaseStructure<Integer> {
|
||||
@Override
|
||||
public void readAndSet(ByteBuf in, Object obj) throws Exception {
|
||||
field.setInt(obj, in.readUnsignedByte());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getAndWrite(ByteBuf out, Object obj) throws Exception {
|
||||
out.writeByte(field.getInt(obj));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer readFrom(ByteBuf in) {
|
||||
return (int) in.readUnsignedByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf out, Integer value) {
|
||||
out.writeByte(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class WORD2Short extends BaseStructure<Short> {
|
||||
@Override
|
||||
public void readAndSet(ByteBuf in, Object obj) throws Exception {
|
||||
field.setShort(obj, in.readShort());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getAndWrite(ByteBuf out, Object obj) throws Exception {
|
||||
out.writeShort(field.getShort(obj));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Short readFrom(ByteBuf in) {
|
||||
return in.readShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf out, Short value) {
|
||||
out.writeShort(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class WORD2Int extends BaseStructure<Integer> {
|
||||
@Override
|
||||
public void readAndSet(ByteBuf in, Object obj) throws Exception {
|
||||
field.setInt(obj, in.readUnsignedShort());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getAndWrite(ByteBuf out, Object obj) throws Exception {
|
||||
out.writeShort(field.getInt(obj));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer readFrom(ByteBuf in) {
|
||||
return in.readUnsignedShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf out, Integer value) {
|
||||
out.writeShort(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class DWORD2Int extends BaseStructure<Integer> {
|
||||
@Override
|
||||
public void readAndSet(ByteBuf in, Object obj) throws Exception {
|
||||
field.setInt(obj, in.readInt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getAndWrite(ByteBuf out, Object obj) throws Exception {
|
||||
out.writeInt(field.getInt(obj));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer readFrom(ByteBuf in) {
|
||||
return in.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf out, Integer value) {
|
||||
out.writeInt(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class DWORD2Long extends BaseStructure<Long> {
|
||||
@Override
|
||||
public void readAndSet(ByteBuf in, Object obj) throws Exception {
|
||||
field.setLong(obj, in.readUnsignedInt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getAndWrite(ByteBuf out, Object obj) throws Exception {
|
||||
out.writeInt((int) field.getLong(obj));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long readFrom(ByteBuf in) {
|
||||
return in.readUnsignedInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf out, Long value) {
|
||||
out.writeInt(value.intValue());
|
||||
}
|
||||
}
|
||||
|
||||
protected static class DWORD2Float extends BaseStructure<Float> {
|
||||
@Override
|
||||
public void readAndSet(ByteBuf in, Object obj) throws Exception {
|
||||
field.setFloat(obj, in.readFloat());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getAndWrite(ByteBuf out, Object obj) throws Exception {
|
||||
out.writeFloat(field.getFloat(obj));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float readFrom(ByteBuf in) {
|
||||
return in.readFloat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf out, Float value) {
|
||||
out.writeFloat(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class QWORD2Long extends BaseStructure<Long> {
|
||||
@Override
|
||||
public void readAndSet(ByteBuf in, Object obj) throws Exception {
|
||||
field.setLong(obj, in.readLong());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getAndWrite(ByteBuf out, Object obj) throws Exception {
|
||||
out.writeLong(field.getLong(obj));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long readFrom(ByteBuf in) {
|
||||
return in.readLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf out, Long value) {
|
||||
out.writeLong(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class QWORD2Double extends BaseStructure<Double> {
|
||||
@Override
|
||||
public void readAndSet(ByteBuf in, Object obj) throws Exception {
|
||||
field.setDouble(obj, in.readDouble());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getAndWrite(ByteBuf out, Object obj) throws Exception {
|
||||
out.writeDouble(field.getDouble(obj));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double readFrom(ByteBuf in) {
|
||||
return in.readDouble();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf out, Double value) {
|
||||
out.writeDouble(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class WORD2ShortLE extends BaseStructure<Short> {
|
||||
@Override
|
||||
public void readAndSet(ByteBuf in, Object obj) throws Exception {
|
||||
field.setShort(obj, in.readShortLE());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getAndWrite(ByteBuf out, Object obj) throws Exception {
|
||||
out.writeShortLE(field.getShort(obj));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Short readFrom(ByteBuf in) {
|
||||
return in.readShortLE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf out, Short value) {
|
||||
out.writeShortLE(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class WORD2IntLE extends BaseStructure<Integer> {
|
||||
@Override
|
||||
public void readAndSet(ByteBuf in, Object obj) throws Exception {
|
||||
field.setInt(obj, in.readUnsignedShortLE());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getAndWrite(ByteBuf out, Object obj) throws Exception {
|
||||
out.writeShortLE(field.getInt(obj));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer readFrom(ByteBuf in) {
|
||||
return in.readUnsignedShortLE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf out, Integer value) {
|
||||
out.writeShortLE(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class DWORD2IntLE extends BaseStructure<Integer> {
|
||||
@Override
|
||||
public void readAndSet(ByteBuf in, Object obj) throws Exception {
|
||||
field.setInt(obj, in.readIntLE());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getAndWrite(ByteBuf out, Object obj) throws Exception {
|
||||
out.writeIntLE(field.getInt(obj));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer readFrom(ByteBuf in) {
|
||||
return in.readIntLE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf out, Integer value) {
|
||||
out.writeIntLE(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class DWORD2LongLE extends BaseStructure<Long> {
|
||||
@Override
|
||||
public void readAndSet(ByteBuf in, Object obj) throws Exception {
|
||||
field.setLong(obj, in.readUnsignedIntLE());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getAndWrite(ByteBuf out, Object obj) throws Exception {
|
||||
out.writeIntLE((int) field.getLong(obj));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long readFrom(ByteBuf in) {
|
||||
return in.readUnsignedIntLE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf out, Long value) {
|
||||
out.writeIntLE(value.intValue());
|
||||
}
|
||||
}
|
||||
|
||||
protected static class DWORD2FloatLE extends BaseStructure<Float> {
|
||||
@Override
|
||||
public void readAndSet(ByteBuf in, Object obj) throws Exception {
|
||||
field.setFloat(obj, in.readFloatLE());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getAndWrite(ByteBuf out, Object obj) throws Exception {
|
||||
out.writeFloatLE(field.getFloat(obj));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float readFrom(ByteBuf in) {
|
||||
return in.readFloatLE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf out, Float value) {
|
||||
out.writeFloatLE(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class QWORD2LongLE extends BaseStructure<Long> {
|
||||
@Override
|
||||
public void readAndSet(ByteBuf in, Object obj) throws Exception {
|
||||
field.setLong(obj, in.readLongLE());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getAndWrite(ByteBuf out, Object obj) throws Exception {
|
||||
out.writeLongLE(field.getLong(obj));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long readFrom(ByteBuf in) {
|
||||
return in.readLongLE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf out, Long value) {
|
||||
out.writeLongLE(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class QWORD2DoubleLE extends BaseStructure<Double> {
|
||||
@Override
|
||||
public void readAndSet(ByteBuf in, Object obj) throws Exception {
|
||||
field.setDouble(obj, in.readDoubleLE());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getAndWrite(ByteBuf out, Object obj) throws Exception {
|
||||
out.writeDoubleLE(field.getDouble(obj));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double readFrom(ByteBuf in) {
|
||||
return in.readDoubleLE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf out, Double value) {
|
||||
out.writeDoubleLE(value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,170 @@
|
||||
package com.fastbee.protocol.base.model;
|
||||
|
||||
import com.fastbee.protocol.base.struc.BaseStructure;
|
||||
import com.fastbee.protocol.base.struc.LengthStructure;
|
||||
import com.fastbee.protocol.util.CharsBuilder;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2022/11/9 17:39
|
||||
*/
|
||||
public class StringModel {
|
||||
public static final WModel<String> HEX = new HEX(-1);
|
||||
public static final WModel<String> BCD = new BCD(-1);
|
||||
public static final WModel<String> GBK = new STR(Charset.forName("GBK"), -1);
|
||||
public static final WModel<String> UTF8 = new STR(StandardCharsets.UTF_8, -1);
|
||||
public static final WModel<String> ASCII = new STR(StandardCharsets.US_ASCII, -1);
|
||||
|
||||
public static BaseStructure<String> getInstance(String charset, int length, int lengthUnit) {
|
||||
final String cs = charset.toUpperCase();
|
||||
BaseStructure<String> model;
|
||||
if ("BCD".equals(cs)) {
|
||||
model = new BCD(length);
|
||||
} else if ("HEX".equals(cs)) {
|
||||
model = new HEX(length);
|
||||
} else {
|
||||
model = new STR(Charset.forName(charset), length);
|
||||
}
|
||||
|
||||
if (lengthUnit > 0) {
|
||||
model = new LengthStructure<>(model, lengthUnit);
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
public static class STR extends BaseStructure<String> {
|
||||
private static final ByteBuffer EMPTY = ByteBuffer.allocate(0);
|
||||
private final Charset charset;
|
||||
private final int length;
|
||||
private final boolean fixed;
|
||||
|
||||
private STR(Charset charset, int length) {
|
||||
this.charset = charset;
|
||||
this.length = length;
|
||||
this.fixed = length > -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readFrom(ByteBuf input) {
|
||||
int len = input.readableBytes();
|
||||
|
||||
if (fixed && len > length)
|
||||
len = length;
|
||||
|
||||
byte[] bytes = new byte[len];
|
||||
input.readBytes(bytes);
|
||||
|
||||
int st = 0;
|
||||
while ((st < len) && (bytes[st] == 0)) {
|
||||
st++;
|
||||
}
|
||||
while ((st < len) && (bytes[len - 1] == 0)) {
|
||||
len--;
|
||||
}
|
||||
return new String(bytes, st, len - st, charset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, String value) {
|
||||
if (fixed) {
|
||||
ByteBuffer buffer;
|
||||
if (value == null) {
|
||||
buffer = EMPTY;
|
||||
} else {
|
||||
buffer = charset.encode(value);
|
||||
}
|
||||
|
||||
int srcPos = length - buffer.limit();
|
||||
|
||||
if (srcPos > 0) {
|
||||
output.writeBytes(buffer);
|
||||
output.writeBytes(new byte[srcPos]);
|
||||
} else if (srcPos < 0) {
|
||||
buffer.position(-srcPos);
|
||||
output.writeBytes(buffer);
|
||||
} else {
|
||||
output.writeBytes(buffer);
|
||||
}
|
||||
} else {
|
||||
if (value != null) {
|
||||
output.writeBytes(charset.encode(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class HEX extends BaseStructure<String> {
|
||||
protected final int length;
|
||||
protected final int charSize;
|
||||
protected final boolean fixed;
|
||||
|
||||
public HEX(int length) {
|
||||
this.length = length;
|
||||
this.charSize = length << 1;
|
||||
this.fixed = length > -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readFrom(ByteBuf input) {
|
||||
return readCharsBuilder(input).toString();
|
||||
}
|
||||
|
||||
protected CharsBuilder readCharsBuilder(ByteBuf input) {
|
||||
int len = fixed ? length : input.readableBytes();
|
||||
byte[] bytes = new byte[len];
|
||||
input.readBytes(bytes);
|
||||
|
||||
CharsBuilder cb = new CharsBuilder(charSize);
|
||||
StringUtil.toHexStringPadded(cb, bytes);
|
||||
return cb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, String value) {
|
||||
if (value == null) {
|
||||
if (fixed) {
|
||||
output.writeBytes(new byte[length]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int charSize = this.charSize;
|
||||
int strLength = value.length();
|
||||
|
||||
if (!fixed) {
|
||||
charSize = strLength + (strLength & 1);
|
||||
}
|
||||
|
||||
char[] chars = new char[charSize];
|
||||
int i = charSize - strLength;
|
||||
if (i >= 0) {
|
||||
value.getChars(0, charSize - i, chars, i);
|
||||
while (i > 0) {
|
||||
chars[--i] = '0';
|
||||
}
|
||||
} else {
|
||||
value.getChars(-i, charSize - i, chars, 0);
|
||||
}
|
||||
byte[] src = StringUtil.decodeHexDump(new CharsBuilder(chars));
|
||||
output.writeBytes(src);
|
||||
}
|
||||
}
|
||||
|
||||
public static class BCD extends HEX {
|
||||
public BCD(int length) {
|
||||
super(length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readFrom(ByteBuf input) {
|
||||
return readCharsBuilder(input).leftStrip('0');
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
package com.fastbee.protocol.base.model;
|
||||
|
||||
import com.fastbee.protocol.util.ExplainUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
/**
|
||||
* 消息结构
|
||||
*
|
||||
* @author gsb
|
||||
* @date 2022/11/9 11:55
|
||||
*/
|
||||
public interface WModel<T> {
|
||||
|
||||
/**
|
||||
* 读缓存
|
||||
*/
|
||||
T readFrom(ByteBuf in);
|
||||
|
||||
void writeTo(ByteBuf out, T value);
|
||||
|
||||
default T readFrom(ByteBuf in, int length) {
|
||||
/*输入的报文长度*/
|
||||
int readLength = in.readerIndex() + length;
|
||||
/*读索引位*/
|
||||
int writerIndex = in.writerIndex();
|
||||
in.writerIndex(readLength);
|
||||
T value = readFrom(in);
|
||||
in.setIndex(readLength, writerIndex);
|
||||
return value;
|
||||
}
|
||||
|
||||
default void writeTo(ByteBuf out, int length, T value) {
|
||||
int writeLength = out.writerIndex() + length;
|
||||
writeTo(out, value);
|
||||
out.writerIndex(writeLength);
|
||||
}
|
||||
|
||||
default T readFrom(ByteBuf in, ExplainUtils explain) {
|
||||
int start = in.readerIndex();
|
||||
T value = readFrom(in);
|
||||
explain.readField(start,desc(), value,in);
|
||||
return value;
|
||||
}
|
||||
|
||||
default void writeTo(ByteBuf out, T value, ExplainUtils explain) {
|
||||
int begin = out.writerIndex();
|
||||
writeTo(out, value);
|
||||
explain.writeField(begin, desc(), value, out);
|
||||
}
|
||||
|
||||
default T readFrom(ByteBuf in, int length, ExplainUtils explain) {
|
||||
int readerLength = in.readerIndex() + length;
|
||||
int writerIndex = in.writerIndex();
|
||||
in.writerIndex(readerLength);
|
||||
T value = readFrom(in, explain);
|
||||
in.setIndex(readerLength, writerIndex);
|
||||
return value;
|
||||
}
|
||||
|
||||
default void writeTo(ByteBuf out, int length, T value, ExplainUtils explain) {
|
||||
int writerLength = out.writerIndex() + length;
|
||||
writeTo(out, value, explain);
|
||||
out.writerIndex(writerLength);
|
||||
}
|
||||
|
||||
/** 内存分配 */
|
||||
default int length() {
|
||||
return 32;
|
||||
}
|
||||
|
||||
default String desc() {
|
||||
return "";
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package com.fastbee.protocol.base.protocol;
|
||||
|
||||
|
||||
import com.fastbee.common.core.mq.DeviceReport;
|
||||
import com.fastbee.common.core.mq.MQSendMessageBo;
|
||||
import com.fastbee.common.core.mq.message.DeviceData;
|
||||
import com.fastbee.common.core.mq.message.FunctionCallBackBo;
|
||||
|
||||
/**
|
||||
* 基础协议
|
||||
* @author gsb
|
||||
* @date 2022/10/10 15:48
|
||||
*/
|
||||
public interface IProtocol {
|
||||
|
||||
DeviceReport decode(DeviceData data, String clientId);
|
||||
|
||||
FunctionCallBackBo encode(MQSendMessageBo message);
|
||||
|
||||
/**
|
||||
* 默认方法,处理设备回复的报文编码
|
||||
*/
|
||||
public default byte[] encodeCallBack(Object message) {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
public default byte[] encodeOTA(Object message) {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
package com.fastbee.protocol.base.struc;
|
||||
|
||||
import com.fastbee.protocol.base.annotation.Column;
|
||||
import com.fastbee.protocol.base.model.ModelRegistry;
|
||||
import com.fastbee.protocol.base.model.WModel;
|
||||
import com.fastbee.protocol.util.ExplainUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* 基础消息结构
|
||||
*
|
||||
* @author gsb
|
||||
* @date 2022/11/9 16:45
|
||||
*/
|
||||
public abstract class BaseStructure<T> implements WModel<T>, Comparable<BaseStructure> {
|
||||
|
||||
protected Field field;
|
||||
protected Column column;
|
||||
protected int index;
|
||||
protected int length;
|
||||
protected String description;
|
||||
|
||||
|
||||
public void readAndSet(ByteBuf in, Object obj) throws Exception {
|
||||
T value = readFrom(in);
|
||||
field.set(obj, value);
|
||||
}
|
||||
|
||||
public void getAndWrite(ByteBuf out, Object obj) throws Exception {
|
||||
T value = (T) field.get(obj);
|
||||
writeTo(out, value);
|
||||
}
|
||||
|
||||
public void readAndSet(ByteBuf in, Object obj, ExplainUtils explain) throws Exception {
|
||||
T value = readFrom(in, explain);
|
||||
field.set(obj, value);
|
||||
}
|
||||
|
||||
public void getAndWrite(ByteBuf out, Object obj, ExplainUtils explain) throws Exception {
|
||||
T value = (T) field.get(obj);
|
||||
writeTo(out, value, explain);
|
||||
}
|
||||
|
||||
public BaseStructure<T> init(Column column, Field field, int position) {
|
||||
if (this.field == null && this.column == null) {
|
||||
this.field = field;
|
||||
this.column = column;
|
||||
length = column.length() > 0 ? column.length() : ModelRegistry.getLength(field.getType());
|
||||
length = length > 0 ? length : 16;
|
||||
description = column.desc();
|
||||
if (description.isEmpty()) {
|
||||
description = field.getName();
|
||||
}
|
||||
index = column.index();
|
||||
if (index == 0) {
|
||||
index = position;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public String filedName(){
|
||||
return field.getName();
|
||||
}
|
||||
|
||||
public String description(){
|
||||
return description;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int compareTo(BaseStructure that) {
|
||||
return Integer.compare(this.index, that.index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof BaseStructure)) {
|
||||
return false;
|
||||
}
|
||||
BaseStructure that = (BaseStructure) other;
|
||||
return field.equals(that.field);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return field.hashCode();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder(12);
|
||||
sb.append(description).append(' ').append(field);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package com.fastbee.protocol.base.struc;
|
||||
|
||||
import com.fastbee.protocol.base.model.WModel;
|
||||
import com.fastbee.protocol.util.ExplainUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 集合域,位于消息末尾
|
||||
* @author bill
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class CollectionStructure<T> extends BaseStructure<Collection<T>> {
|
||||
|
||||
private final WModel<T> schema;
|
||||
|
||||
|
||||
@Override
|
||||
public Collection<T> readFrom(ByteBuf input) {
|
||||
Collection list = new ArrayList<>();
|
||||
while (input.isReadable()) {
|
||||
T t = schema.readFrom(input);
|
||||
list.add(t);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Collection<T> list) {
|
||||
if (list != null) {
|
||||
for (T t : list) {
|
||||
schema.writeTo(output, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<T> readFrom(ByteBuf input, ExplainUtils explain) {
|
||||
Collection list = new ArrayList<>();
|
||||
while (input.isReadable()) {
|
||||
T t = schema.readFrom(input, explain);
|
||||
list.add(t);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Collection<T> list, ExplainUtils explain) {
|
||||
if (list != null) {
|
||||
for (T t : list) {
|
||||
schema.writeTo(output, t, explain);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package com.fastbee.protocol.base.struc;
|
||||
|
||||
import com.fastbee.protocol.base.model.WModel;
|
||||
import com.fastbee.protocol.util.ExplainUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
/**
|
||||
* 指定长度报文结构
|
||||
* @author bill
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class LengthStructure<T> extends BaseStructure<T> {
|
||||
|
||||
private final WModel<T> schema;
|
||||
private final int length;
|
||||
|
||||
@Override
|
||||
public T readFrom(ByteBuf input) {
|
||||
if (input.isReadable(length))
|
||||
return schema.readFrom(input, length);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, T value) {
|
||||
if (value != null)
|
||||
schema.writeTo(output, length, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T readFrom(ByteBuf input, ExplainUtils explain) {
|
||||
if (input.isReadable(length))
|
||||
return schema.readFrom(input, length, explain);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, T value, ExplainUtils explain) {
|
||||
if (value != null)
|
||||
schema.writeTo(output, length, value, explain);
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package com.fastbee.protocol.base.struc;
|
||||
|
||||
import com.fastbee.protocol.base.model.WModel;
|
||||
import com.fastbee.protocol.util.ExplainUtils;
|
||||
import com.fastbee.protocol.util.IntTool;
|
||||
import com.fastbee.protocol.util.Msg;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 每个元素长度单位的集合域,位于消息末尾
|
||||
* @author bill
|
||||
*/
|
||||
public class LengthUnitCollectionStructure<T> extends BaseStructure<Collection<T>> {
|
||||
|
||||
private final WModel<T> model;
|
||||
private final int lengthUnit;
|
||||
private final IntTool intTool;
|
||||
|
||||
|
||||
public LengthUnitCollectionStructure(WModel<T> model, int lengthUnit) {
|
||||
this.model = model;
|
||||
this.lengthUnit = lengthUnit;
|
||||
this.intTool = IntTool.getInstance(lengthUnit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<T> readFrom(ByteBuf input) {
|
||||
Collection list = new ArrayList<>();
|
||||
while (input.isReadable()) {
|
||||
int length = intTool.read(input);
|
||||
T t = model.readFrom(input, length);
|
||||
list.add(t);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Collection<T> list) {
|
||||
if (list != null) {
|
||||
for (T t : list) {
|
||||
if (t != null) {
|
||||
int begin = output.writerIndex();
|
||||
intTool.write(output, 0);
|
||||
model.writeTo(output, t);
|
||||
int length = output.writerIndex() - begin - lengthUnit;
|
||||
intTool.set(output, begin, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<T> readFrom(ByteBuf input, ExplainUtils explain) {
|
||||
Collection list = new ArrayList<>();
|
||||
while (input.isReadable()) {
|
||||
int length = intTool.read(input);
|
||||
explain.lengthField(input.readerIndex() - lengthUnit, description + "长度", length, lengthUnit);
|
||||
T t = model.readFrom(input, length, explain);
|
||||
list.add(t);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Collection<T> list, ExplainUtils explain) {
|
||||
if (list != null) {
|
||||
for (T t : list) {
|
||||
if (t != null) {
|
||||
int begin = output.writerIndex();
|
||||
Msg msg = explain.lengthField(begin, description + "长度", 0, lengthUnit);
|
||||
intTool.write(output, 0);
|
||||
model.writeTo(output, t, explain);
|
||||
int length = output.writerIndex() - begin - lengthUnit;
|
||||
intTool.set(output, begin, length);
|
||||
msg.setLength(length, lengthUnit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package com.fastbee.protocol.base.struc;
|
||||
|
||||
import com.fastbee.protocol.base.model.WModel;
|
||||
import com.fastbee.protocol.util.ExplainUtils;
|
||||
import com.fastbee.protocol.util.IntTool;
|
||||
import com.fastbee.protocol.util.Msg;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
/**
|
||||
* 指定长度单位域
|
||||
* @author bill
|
||||
*/
|
||||
public class LengthUnitStructure<T> extends BaseStructure<T> {
|
||||
|
||||
private final WModel<T> schema;
|
||||
private final int lengthUnit;
|
||||
private final IntTool intTool;
|
||||
|
||||
public LengthUnitStructure(WModel<T> model, int lengthUnit) {
|
||||
this.schema = model;
|
||||
this.lengthUnit = lengthUnit;
|
||||
this.intTool = IntTool.getInstance(lengthUnit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T readFrom(ByteBuf in) {
|
||||
int length = intTool.read(in);
|
||||
return schema.readFrom(in, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf out, T value) {
|
||||
int begin = out.writerIndex();
|
||||
intTool.write(out, 0);
|
||||
if (value != null) {
|
||||
schema.writeTo(out, value);
|
||||
int length = out.writerIndex() - begin - lengthUnit;
|
||||
intTool.set(out, begin, length);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T readFrom(ByteBuf in, ExplainUtils explain) {
|
||||
int length = intTool.read(in);
|
||||
explain.lengthField(in.readerIndex() - lengthUnit, description + "长度", length, lengthUnit);
|
||||
T value = schema.readFrom(in, length, explain);
|
||||
explain.setLastDesc(description);
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf out, T value, ExplainUtils explain) {
|
||||
int begin = out.writerIndex();
|
||||
Msg msg = explain.lengthField(begin, description + "长度", 0, lengthUnit);
|
||||
intTool.write(out, 0);
|
||||
if (value != null) {
|
||||
schema.writeTo(out, value, explain);
|
||||
explain.setLastDesc(description);
|
||||
int length = out.writerIndex() - begin - lengthUnit;
|
||||
intTool.set(out, begin, length);
|
||||
msg.setLength(length, lengthUnit);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,188 @@
|
||||
package com.fastbee.protocol.base.struc;
|
||||
|
||||
import com.fastbee.protocol.base.model.MapModel;
|
||||
import com.fastbee.protocol.base.model.WModel;
|
||||
import com.fastbee.protocol.util.ExplainUtils;
|
||||
import com.fastbee.protocol.util.IntTool;
|
||||
import com.fastbee.protocol.util.Msg;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* map类型,位于报文末尾
|
||||
* @author bill
|
||||
*/
|
||||
@Slf4j
|
||||
public class MapStructure<K,V> extends BaseStructure<Map<K,V>> {
|
||||
|
||||
private final WModel<K> kwModel;
|
||||
private final Map<K, WModel<V>> valuesModel;
|
||||
private final int lengthUnit;
|
||||
private final IntTool valueIntTool;
|
||||
private final boolean treeMap;
|
||||
|
||||
public MapStructure(MapModel mapModel, Class typeClass) {
|
||||
this.kwModel = mapModel.kwModel;
|
||||
this.valuesModel = mapModel.valueSchema;
|
||||
this.lengthUnit = mapModel.lengthUnit;
|
||||
this.valueIntTool = mapModel.intTool;
|
||||
this.treeMap = !HashMap.class.isAssignableFrom(typeClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<K, V> readFrom(ByteBuf in) {
|
||||
if (!in.isReadable())
|
||||
return null;
|
||||
|
||||
Map map;
|
||||
if (treeMap) {
|
||||
map = new TreeMap();
|
||||
}
|
||||
else{ map = new HashMap(8);}
|
||||
|
||||
K key = null;
|
||||
int length = 0;
|
||||
try {
|
||||
do {
|
||||
key = kwModel.readFrom(in);
|
||||
|
||||
length = valueIntTool.read(in);
|
||||
if (length <= 0)
|
||||
continue;
|
||||
|
||||
int writerIndex = in.writerIndex();
|
||||
int readerIndex = in.readerIndex() + length;
|
||||
if (writerIndex > readerIndex) {
|
||||
in.writerIndex(readerIndex);
|
||||
Object value = readValue(key, in);
|
||||
map.put(key, value);
|
||||
in.setIndex(readerIndex, writerIndex);
|
||||
} else {
|
||||
Object value = readValue(key, in);
|
||||
map.put(key, value);
|
||||
break;
|
||||
}
|
||||
} while (in.isReadable());
|
||||
} catch (Exception e) {
|
||||
log.warn("解析出错:ID[{}], LENGTH[{}], {}", key, length, e.getMessage());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public Object readValue(Object key, ByteBuf in) {
|
||||
WModel model = valuesModel.get(key);
|
||||
if (model != null) {
|
||||
return model.readFrom(in);
|
||||
}
|
||||
byte[] bytes = new byte[in.readableBytes()];
|
||||
in.readBytes(bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Map<K, V> map) {
|
||||
if (map == null)
|
||||
return;
|
||||
|
||||
for (Map.Entry<K, V> entry : map.entrySet()) {
|
||||
K key = entry.getKey();
|
||||
kwModel.writeTo(output, key);
|
||||
|
||||
V value = entry.getValue();
|
||||
WModel<V> schema = valuesModel.get(key);
|
||||
if (schema != null) {
|
||||
int begin = output.writerIndex();
|
||||
valueIntTool.write(output, 0);
|
||||
schema.writeTo(output, value);
|
||||
int length = output.writerIndex() - begin - lengthUnit;
|
||||
valueIntTool.set(output, begin, length);
|
||||
} else {
|
||||
log.warn("未注册的信息:ID[{}], VALUE[{}]", key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<K, V> readFrom(ByteBuf in, ExplainUtils explain) {
|
||||
if (!in.isReadable())
|
||||
return null;
|
||||
|
||||
Map map;
|
||||
if (treeMap) map = new TreeMap();
|
||||
else map = new HashMap(8);
|
||||
|
||||
K key = null;
|
||||
int length = 0;
|
||||
try {
|
||||
do {
|
||||
key = kwModel.readFrom(in, explain);
|
||||
explain.setLastDesc(description + "ID");
|
||||
|
||||
length = valueIntTool.read(in);
|
||||
explain.lengthField(in.readerIndex() - lengthUnit, description + "长度", length, lengthUnit);
|
||||
if (length <= 0)
|
||||
continue;
|
||||
|
||||
int writerIndex = in.writerIndex();
|
||||
int readerIndex = in.readerIndex() + length;
|
||||
if (writerIndex > readerIndex) {
|
||||
in.writerIndex(readerIndex);
|
||||
Object value = readValue(key, in, explain);
|
||||
map.put(key, value);
|
||||
in.setIndex(readerIndex, writerIndex);
|
||||
} else {
|
||||
Object value = readValue(key, in, explain);
|
||||
map.put(key, value);
|
||||
break;
|
||||
}
|
||||
} while (in.isReadable());
|
||||
} catch (Exception e) {
|
||||
log.warn("解析出错:ID[{}], LENGTH[{}], {}", key, length, e.getMessage());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public Object readValue(Object key, ByteBuf in, ExplainUtils explain) {
|
||||
WModel model = valuesModel.get(key);
|
||||
if (model != null) {
|
||||
Object value = model.readFrom(in, explain);
|
||||
return value;
|
||||
}
|
||||
int begin = in.readerIndex();
|
||||
byte[] bytes = new byte[in.readableBytes()];
|
||||
in.readBytes(bytes);
|
||||
explain.readField(begin, description, ByteBufUtil.hexDump(bytes), in);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Map<K, V> map, ExplainUtils explain) {
|
||||
if (map == null)
|
||||
return;
|
||||
|
||||
for (Map.Entry<K, V> entry : map.entrySet()) {
|
||||
K key = entry.getKey();
|
||||
kwModel.writeTo(output, key, explain);
|
||||
explain.setLastDesc(description + "ID");
|
||||
|
||||
V value = entry.getValue();
|
||||
WModel<V> model = valuesModel.get(key);
|
||||
if (model != null) {
|
||||
int begin = output.writerIndex();
|
||||
Msg msg = explain.lengthField(begin, description + "长度", 0, lengthUnit);
|
||||
valueIntTool.write(output, 0);
|
||||
model.writeTo(output, value, explain);
|
||||
int length = output.writerIndex() - begin - lengthUnit;
|
||||
valueIntTool.set(output, begin, length);
|
||||
msg.setLength(length, lengthUnit);
|
||||
} else {
|
||||
log.warn("未注册的信息:ID[{}], VALUE[{}]", key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
package com.fastbee.protocol.base.struc;
|
||||
|
||||
import com.fastbee.protocol.base.model.WModel;
|
||||
import com.fastbee.protocol.util.ExplainUtils;
|
||||
import com.fastbee.protocol.util.IntTool;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
|
||||
/**
|
||||
* 指定前置数量的数组域
|
||||
* @author bill
|
||||
*/
|
||||
public class TotalArrayObjectStructure<T> extends BaseStructure<T[]> {
|
||||
|
||||
private final WModel<T> model;
|
||||
private final int totalUnit;
|
||||
private final IntTool intTool;
|
||||
private final Class<T> arrayClass;
|
||||
|
||||
public TotalArrayObjectStructure(WModel<T> model, int totalUnit, Class<T> arrayClass) {
|
||||
this.model = model;
|
||||
this.totalUnit = totalUnit;
|
||||
this.intTool = IntTool.getInstance(totalUnit);
|
||||
this.arrayClass = arrayClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T[] readFrom(ByteBuf in) {
|
||||
int total = intTool.read(in);
|
||||
if (total <= 0)
|
||||
return null;
|
||||
T[] value = (T[]) Array.newInstance(arrayClass, total);
|
||||
for (int i = 0; i < total; i++) {
|
||||
T t = model.readFrom(in);
|
||||
value[i] = t;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf out, T[] value) {
|
||||
if (value == null) {
|
||||
intTool.write(out, 0);
|
||||
} else {
|
||||
int length = value.length;
|
||||
intTool.write(out, length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
T t = value[i];
|
||||
model.writeTo(out, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T[] readFrom(ByteBuf in, ExplainUtils explain) {
|
||||
int total = intTool.read(in);
|
||||
explain.lengthField(in.readerIndex() - totalUnit, description + "数量", total, totalUnit);
|
||||
if (total <= 0)
|
||||
return null;
|
||||
T[] value = (T[]) Array.newInstance(arrayClass, total);
|
||||
for (int i = 0; i < total; i++) {
|
||||
T t = model.readFrom(in, explain);
|
||||
value[i] = t;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf out, T[] value, ExplainUtils explain) {
|
||||
if (value == null) {
|
||||
explain.lengthField(out.writerIndex(), description + "数量", 0, totalUnit);
|
||||
intTool.write(out, 0);
|
||||
} else {
|
||||
int total = value.length;
|
||||
explain.lengthField(out.writerIndex(), description + "数量", total, totalUnit);
|
||||
intTool.write(out, total);
|
||||
for (int i = 0; i < total; i++) {
|
||||
T t = value[i];
|
||||
model.writeTo(out, t, explain);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package com.fastbee.protocol.base.struc;
|
||||
|
||||
import com.fastbee.protocol.base.model.ModelRegistry;
|
||||
import com.fastbee.protocol.base.model.WModel;
|
||||
import com.fastbee.protocol.util.ExplainUtils;
|
||||
import com.fastbee.protocol.util.IntTool;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
|
||||
/**
|
||||
* 指定前置数量的数组域
|
||||
*
|
||||
* @author bill
|
||||
*/
|
||||
public class TotalArrayPrimitiveStructure extends BaseStructure {
|
||||
|
||||
private final WModel model;
|
||||
private final int totalUnit;
|
||||
private final int valueUnit;
|
||||
private final IntTool intTool;
|
||||
|
||||
public TotalArrayPrimitiveStructure(WModel model, int totalUnit, Class arrayClass) {
|
||||
this.model = model;
|
||||
this.totalUnit = totalUnit;
|
||||
this.valueUnit = ModelRegistry.getLength(arrayClass);
|
||||
this.intTool = IntTool.getInstance(totalUnit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object readFrom(ByteBuf in) {
|
||||
int total = intTool.read(in);
|
||||
if (total <= 0) {
|
||||
return null;
|
||||
}
|
||||
int length = valueUnit * (total/2);
|
||||
return model.readFrom(in, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf out, Object value) {
|
||||
if (value == null) {
|
||||
intTool.write(out, 0);
|
||||
} else {
|
||||
int total = Array.getLength(value);
|
||||
intTool.write(out, total);
|
||||
model.writeTo(out, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object readFrom(ByteBuf in, ExplainUtils explain) {
|
||||
int total = intTool.read(in);
|
||||
explain.lengthField(in.readerIndex() - totalUnit, description + "数量", total, totalUnit);
|
||||
if (total <= 0)
|
||||
return null;
|
||||
int length = valueUnit * total;
|
||||
return model.readFrom(in, length, explain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf out, Object value, ExplainUtils explain) {
|
||||
if (value == null) {
|
||||
explain.lengthField(out.writerIndex(), description + "数量", 0, totalUnit);
|
||||
intTool.write(out, 0);
|
||||
} else {
|
||||
int total = Array.getLength(value);
|
||||
explain.lengthField(out.writerIndex(), description + "数量", total, totalUnit);
|
||||
intTool.write(out, total);
|
||||
model.writeTo(out, value, explain);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package com.fastbee.protocol.base.struc;
|
||||
|
||||
import com.fastbee.protocol.base.model.WModel;
|
||||
import com.fastbee.protocol.util.ExplainUtils;
|
||||
import com.fastbee.protocol.util.IntTool;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 前置数量的集合报文结构
|
||||
* @author bill
|
||||
*/
|
||||
public class TotalCollectionStructure<T> extends BaseStructure<Collection<T>> {
|
||||
|
||||
private final WModel<T> model;
|
||||
private final int totalUnit;
|
||||
private final IntTool intTool;
|
||||
|
||||
public TotalCollectionStructure(WModel<T> model, int totalUnit){
|
||||
this.model =model;
|
||||
this.totalUnit =totalUnit;
|
||||
this.intTool = IntTool.getInstance(totalUnit);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Collection<T> readFrom(ByteBuf input) {
|
||||
int total = intTool.read(input);
|
||||
if (total <= 0)
|
||||
return null;
|
||||
ArrayList<T> list = new ArrayList<>(total);
|
||||
for (int i = 0; i < total; i++) {
|
||||
T t = model.readFrom(input);
|
||||
list.add(t);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Collection<T> list) {
|
||||
if (list != null) {
|
||||
intTool.write(output, list.size());
|
||||
for (T t : list) {
|
||||
model.writeTo(output, t);
|
||||
}
|
||||
} else {
|
||||
intTool.write(output, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<T> readFrom(ByteBuf input, ExplainUtils explain) {
|
||||
int total = intTool.read(input);
|
||||
explain.lengthField(input.readerIndex() - totalUnit, description + "数量", total, totalUnit);
|
||||
if (total <= 0)
|
||||
return null;
|
||||
ArrayList<T> list = new ArrayList<>(total);
|
||||
for (int i = 0; i < total; i++) {
|
||||
T t = model.readFrom(input, explain);
|
||||
list.add(t);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Collection<T> list, ExplainUtils explain) {
|
||||
if (list != null) {
|
||||
int total = list.size();
|
||||
explain.lengthField(output.writerIndex(), description + "数量", total, totalUnit);
|
||||
intTool.write(output, total);
|
||||
for (T t : list) {
|
||||
model.writeTo(output, t, explain);
|
||||
}
|
||||
} else {
|
||||
explain.lengthField(output.writerIndex(), description + "数量", 0, totalUnit);
|
||||
intTool.write(output, 0);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,198 @@
|
||||
package com.fastbee.protocol.base.struc;
|
||||
|
||||
import com.fastbee.protocol.base.model.MapModel;
|
||||
import com.fastbee.protocol.base.model.WModel;
|
||||
import com.fastbee.protocol.util.ExplainUtils;
|
||||
import com.fastbee.protocol.util.IntTool;
|
||||
import com.fastbee.protocol.util.Msg;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* @author bill
|
||||
*/
|
||||
@Slf4j
|
||||
public class TotalMapStructure<K,V> extends BaseStructure<Map<K,V>> {
|
||||
|
||||
private final WModel<K> kwModel;
|
||||
private final Map<K, WModel<V>> valueModel;
|
||||
private final int lengthUnit;
|
||||
private final IntTool valueIntTool;
|
||||
private final int totalUnit;
|
||||
private final IntTool totalIntTool;
|
||||
private final boolean treeMap;
|
||||
|
||||
public TotalMapStructure(MapModel model, int totalUnit, Class typeClass) {
|
||||
this.kwModel = model.kwModel;
|
||||
this.valueModel = model.valueSchema;
|
||||
this.lengthUnit = model.lengthUnit;
|
||||
this.valueIntTool = model.intTool;
|
||||
this.totalUnit = totalUnit;
|
||||
this.totalIntTool = IntTool.getInstance(totalUnit);
|
||||
this.treeMap = !HashMap.class.isAssignableFrom(typeClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<K, V> readFrom(ByteBuf in) {
|
||||
if (!in.isReadable())
|
||||
return null;
|
||||
int total = totalIntTool.read(in);
|
||||
if (total <= 0)
|
||||
return null;
|
||||
|
||||
Map map;
|
||||
if (treeMap) map = new TreeMap();
|
||||
else map = new HashMap((int) (total / 0.75) + 1);
|
||||
|
||||
K key = null;
|
||||
int length = 0;
|
||||
try {
|
||||
for (int i = 0; i < total; i++) {
|
||||
key = kwModel.readFrom(in);
|
||||
|
||||
length = valueIntTool.read(in);
|
||||
if (length <= 0)
|
||||
continue;
|
||||
|
||||
int writerIndex = in.writerIndex();
|
||||
int readerIndex = in.readerIndex() + length;
|
||||
if (writerIndex > readerIndex) {
|
||||
in.writerIndex(readerIndex);
|
||||
Object value = readValue(key, in);
|
||||
map.put(key, value);
|
||||
in.setIndex(readerIndex, writerIndex);
|
||||
} else {
|
||||
Object value = readValue(key, in);
|
||||
map.put(key, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("解析出错:ID[{}], LENGTH[{}], {}", key, length, e.getMessage());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public Object readValue(Object key, ByteBuf in) {
|
||||
WModel model = valueModel.get(key);
|
||||
if (model != null) {
|
||||
return model.readFrom(in);
|
||||
}
|
||||
byte[] bytes = new byte[in.readableBytes()];
|
||||
in.readBytes(bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Map<K, V> map) {
|
||||
if (map == null)
|
||||
return;
|
||||
totalIntTool.write(output, map.size());
|
||||
|
||||
for (Map.Entry<K, V> entry : map.entrySet()) {
|
||||
K key = entry.getKey();
|
||||
kwModel.writeTo(output, key);
|
||||
|
||||
V value = entry.getValue();
|
||||
WModel<V> model = valueModel.get(key);
|
||||
if (model != null) {
|
||||
int begin = output.writerIndex();
|
||||
valueIntTool.write(output, 0);
|
||||
model.writeTo(output, value);
|
||||
int length = output.writerIndex() - begin - lengthUnit;
|
||||
valueIntTool.set(output, begin, length);
|
||||
} else {
|
||||
log.warn("未注册的信息:ID[{}], VALUE[{}]", key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<K, V> readFrom(ByteBuf in, ExplainUtils explain) {
|
||||
if (!in.isReadable())
|
||||
return null;
|
||||
int total = totalIntTool.read(in);
|
||||
explain.lengthField(in.readerIndex() - totalUnit, description + "数量", total, totalUnit);
|
||||
if (total <= 0)
|
||||
return null;
|
||||
|
||||
Map map;
|
||||
if (treeMap) map = new TreeMap();
|
||||
else map = new HashMap((int) (total / 0.75) + 1);
|
||||
|
||||
K key = null;
|
||||
int length = 0;
|
||||
try {
|
||||
for (int i = 0; i < total; i++) {
|
||||
key = kwModel.readFrom(in, explain);
|
||||
explain.setLastDesc(description + "ID");
|
||||
|
||||
length = valueIntTool.read(in);
|
||||
explain.lengthField(in.readerIndex() - lengthUnit, description + "长度", length, lengthUnit);
|
||||
if (length <= 0)
|
||||
continue;
|
||||
|
||||
int writerIndex = in.writerIndex();
|
||||
int readerIndex = in.readerIndex() + length;
|
||||
if (writerIndex > readerIndex) {
|
||||
in.writerIndex(readerIndex);
|
||||
Object value = readValue(key, in, explain);
|
||||
map.put(key, value);
|
||||
in.setIndex(readerIndex, writerIndex);
|
||||
} else {
|
||||
Object value = readValue(key, in, explain);
|
||||
map.put(key, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("解析出错:ID[{}], LENGTH[{}], {}", key, length, e.getMessage());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public Object readValue(Object key, ByteBuf in, ExplainUtils explain) {
|
||||
WModel model = valueModel.get(key);
|
||||
if (model != null) {
|
||||
Object value = model.readFrom(in, explain);
|
||||
return value;
|
||||
}
|
||||
int begin = in.readerIndex();
|
||||
byte[] bytes = new byte[in.readableBytes()];
|
||||
in.readBytes(bytes);
|
||||
explain.readField(begin, description, ByteBufUtil.hexDump(bytes), in);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteBuf output, Map<K, V> map, ExplainUtils explain) {
|
||||
if (map == null)
|
||||
return;
|
||||
totalIntTool.write(output, map.size());
|
||||
|
||||
for (Map.Entry<K, V> entry : map.entrySet()) {
|
||||
K key = entry.getKey();
|
||||
kwModel.writeTo(output, key, explain);
|
||||
explain.setLastDesc(description + "ID");
|
||||
|
||||
V value = entry.getValue();
|
||||
WModel<V> model = valueModel.get(key);
|
||||
if (model != null) {
|
||||
int begin = output.writerIndex();
|
||||
Msg msg = explain.lengthField(begin, description + "长度", 0, lengthUnit);
|
||||
valueIntTool.write(output, 0);
|
||||
model.writeTo(output, value, explain);
|
||||
int length = output.writerIndex() - begin - lengthUnit;
|
||||
valueIntTool.set(output, begin, length);
|
||||
msg.setLength(length, lengthUnit);
|
||||
} else {
|
||||
log.warn("未注册的信息:ID[{}], VALUE[{}]", key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.fastbee.protocol.domain;
|
||||
|
||||
import com.fastbee.protocol.base.protocol.IProtocol;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 设备协议model
|
||||
* @author gsb
|
||||
* @date 2022/10/25 13:38
|
||||
*/
|
||||
@Data
|
||||
public class DeviceProtocol {
|
||||
|
||||
/**协议实例*/
|
||||
private IProtocol protocol;
|
||||
|
||||
/**产品id*/
|
||||
private Long productId;
|
||||
|
||||
/**设备编号*/
|
||||
private String serialNumber;
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.fastbee.protocol.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 位定义 0-关闭 1-k开启 ,如果不同状态清重写
|
||||
* @author bill
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ModbusBitStatus {
|
||||
|
||||
OPEN((byte) 0x01),
|
||||
CLOSED((byte) 0x00)
|
||||
;
|
||||
private byte bit;
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.fastbee.protocol.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 线圈状态
|
||||
* @author bill
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ModbusCoilStatus {
|
||||
|
||||
//线圈开启
|
||||
ON(new byte[] {(byte) 0xFF, 0x00}),
|
||||
//线圈关闭
|
||||
OFF(new byte[] {0x00, 0x00})
|
||||
;
|
||||
private byte[] data;
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package com.fastbee.protocol.enums;
|
||||
|
||||
import com.fastbee.common.exception.ServiceException;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ModbusErrCode {
|
||||
E01((byte) 0x01, "非法功能"),
|
||||
E02((byte) 0x02, "非法数据地址"),
|
||||
E03((byte) 0x03, "非法数据值"),
|
||||
E04((byte) 0x04, "从站设备故障"),
|
||||
E05((byte) 0x05, "确认"),
|
||||
E06((byte) 0x06, "从属设备忙"),
|
||||
E07((byte) 0x07, "从属设备忙"),
|
||||
E08((byte) 0x08, "存储奇偶性差错"),
|
||||
E0A((byte) 0x0A, "不可用网关路径"),
|
||||
E0B((byte) 0x0B, "网关目标设备响应失败")
|
||||
;
|
||||
|
||||
private byte code;
|
||||
private String desc;
|
||||
|
||||
|
||||
|
||||
public static ModbusErrCode valueOf(int code) {
|
||||
switch (code) {
|
||||
case 0x01: return E01;
|
||||
case 0x02: return E02;
|
||||
case 0x03: return E03;
|
||||
case 0x04: return E04;
|
||||
case 0x05: return E05;
|
||||
case 0x06: return E06;
|
||||
case 0x07: return E07;
|
||||
case 0x08: return E08;
|
||||
case 0x0A: return E0A;
|
||||
case 0x0B: return E0B;
|
||||
|
||||
default: throw new ServiceException("未知错误码["+code+"]");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package com.fastbee.protocol.service;
|
||||
|
||||
import com.fastbee.common.core.mq.message.ProtocolDto;
|
||||
import com.fastbee.protocol.base.protocol.IProtocol;
|
||||
import com.fastbee.protocol.domain.DeviceProtocol;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2022/10/10 17:07
|
||||
*/
|
||||
public interface IProtocolManagerService {
|
||||
|
||||
/**
|
||||
*获取所有的协议,包含脚本解析协议和系统内部定义协议
|
||||
*/
|
||||
public List<ProtocolDto> getAllProtocols();
|
||||
|
||||
/**
|
||||
* 根据协议编码获取系统内部协议
|
||||
* @param protocolCode 协议编码
|
||||
* @return 协议
|
||||
*/
|
||||
IProtocol getProtocolByProtocolCode(String protocolCode);
|
||||
|
||||
/**
|
||||
* 根据设备编号获取系统内部协议实例
|
||||
* @param serialNumber 产品编号
|
||||
* @return 协议实例
|
||||
*/
|
||||
DeviceProtocol getProtocolBySerialNumber(String serialNumber);
|
||||
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
package com.fastbee.protocol.service.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fastbee.common.annotation.SysProtocol;
|
||||
import com.fastbee.common.core.device.DeviceAndProtocol;
|
||||
import com.fastbee.common.core.mq.message.ProtocolDto;
|
||||
import com.fastbee.common.utils.DateUtils;
|
||||
import com.fastbee.common.utils.StringUtils;
|
||||
import com.fastbee.common.utils.spring.SpringUtils;
|
||||
import com.fastbee.iot.domain.Protocol;
|
||||
import com.fastbee.iot.service.IDeviceService;
|
||||
import com.fastbee.iot.service.IProtocolService;
|
||||
import com.fastbee.protocol.base.protocol.IProtocol;
|
||||
import com.fastbee.protocol.domain.DeviceProtocol;
|
||||
import com.fastbee.protocol.service.IProtocolManagerService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.poi.ss.formula.functions.T;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 设备内部协议管理类
|
||||
* @author bill
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ProtocolManagerServiceImpl<M> implements IProtocolManagerService {
|
||||
|
||||
@Autowired
|
||||
private IDeviceService deviceService;
|
||||
@Autowired
|
||||
private IProtocolService protocolService;
|
||||
private final Map<String, IProtocol> protocolMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
*获取所有的协议,包含脚本解析协议和系统内部定义协议
|
||||
*/
|
||||
@Override
|
||||
public List<ProtocolDto> getAllProtocols(){
|
||||
List<ProtocolDto> result = new ArrayList<>();
|
||||
//获取@SysProtocol注解的bean
|
||||
Map<String, Object> annotations = SpringUtils.getBeanWithAnnotation(SysProtocol.class);
|
||||
//获取外部配置协议
|
||||
Protocol protocol = new Protocol();
|
||||
protocol.setProtocolStatus(1);
|
||||
protocol.setDelFlag(0);
|
||||
List<Protocol> protocolList = protocolService.selectByCondition(protocol);
|
||||
annotations.forEach((key,value)->{
|
||||
SysProtocol annotation = value.getClass().getAnnotation(SysProtocol.class);
|
||||
ProtocolDto protocolDto = new ProtocolDto();
|
||||
protocolDto.setCode(annotation.protocolCode());
|
||||
protocolDto.setName(annotation.name());
|
||||
protocolDto.setDescription(annotation.description());
|
||||
/*系统内部协议*/
|
||||
protocolDto.setProtocolType(0);
|
||||
result.add(protocolDto);
|
||||
boolean match = protocolList.stream().anyMatch(po -> po.getProtocolCode().equals(annotation.protocolCode()));
|
||||
if (!match){
|
||||
Protocol newPo = new Protocol();
|
||||
newPo.setProtocolCode(annotation.protocolCode());
|
||||
newPo.setProtocolName(annotation.name());
|
||||
newPo.setJarSign(annotation.description());
|
||||
newPo.setProtocolStatus(1);
|
||||
newPo.setDelFlag(0);
|
||||
newPo.setCreateTime(DateUtils.getNowDate());
|
||||
protocolService.insertProtocol(newPo);
|
||||
}
|
||||
});
|
||||
/**外部协议*/
|
||||
for (Protocol item : protocolList) {
|
||||
ProtocolDto protocolDto = new ProtocolDto();
|
||||
protocolDto.setCode(item.getProtocolCode());
|
||||
protocolDto.setName(item.getProtocolName());
|
||||
protocolDto.setProtocolUrl(item.getProtocolFileUrl());
|
||||
protocolDto.setProtocolType(item.getProtocolType());
|
||||
result.add(protocolDto);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据协议编码获取系统内部协议
|
||||
* @param protocolCode 协议编码
|
||||
* @return 协议
|
||||
*/
|
||||
@Override
|
||||
public IProtocol getProtocolByProtocolCode(String protocolCode) {
|
||||
if (!CollectionUtils.isEmpty(this.protocolMap)){
|
||||
return protocolMap.get(protocolCode);
|
||||
}
|
||||
Map<String, IProtocol> annotations = SpringUtils.getBeanWithAnnotation(SysProtocol.class);
|
||||
IProtocol protocol = null;
|
||||
for (IProtocol item : annotations.values()) {
|
||||
SysProtocol annotation = item.getClass().getAnnotation(SysProtocol.class);
|
||||
protocolMap.put(annotation.protocolCode(), item);
|
||||
if (annotation.protocolCode().equals(protocolCode)){
|
||||
protocol = item;
|
||||
}
|
||||
}
|
||||
return protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据设备编号获取协议实例
|
||||
* @param serialNumber 产品编号
|
||||
* @return 协议实例
|
||||
*/
|
||||
@Override
|
||||
public DeviceProtocol getProtocolBySerialNumber(String serialNumber){
|
||||
DeviceAndProtocol deviceAndProtocol = deviceService.selectProtocolBySerialNumber(serialNumber);
|
||||
String protocolCode = deviceAndProtocol.getProtocolCode();
|
||||
if (StringUtils.isEmpty(protocolCode)){
|
||||
log.error("=>设备的协议编号为空{}",serialNumber);
|
||||
return null;
|
||||
}
|
||||
DeviceProtocol deviceProtocol = new DeviceProtocol();
|
||||
deviceProtocol.setSerialNumber(deviceAndProtocol.getSerialNumber());
|
||||
deviceProtocol.setProductId(deviceProtocol.getProductId());
|
||||
IProtocol baseProtocol = getProtocolByProtocolCode(protocolCode);
|
||||
deviceProtocol.setProtocol(baseProtocol);
|
||||
return deviceProtocol;
|
||||
}
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
package com.fastbee.protocol.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 使用数组实现key为int的Map,数组的长度等于key的最大值减去最小值
|
||||
* 因此key的区间跨度太大会造成内存空间上的浪费,仅适用于小范围且连续的key
|
||||
* 可指定value为null的缺省返回值,该返回值位于数组的末位,key为int的最大值
|
||||
* @author bill
|
||||
*/
|
||||
public class ArrayMap<T> {
|
||||
|
||||
public static final int DEFAULT_KEY = Integer.MAX_VALUE;
|
||||
private T[] array = (T[]) new Object[2];
|
||||
private boolean init = true;
|
||||
private int min;
|
||||
private int max;
|
||||
|
||||
public T get(int key) {
|
||||
if (key < min || key > max)
|
||||
return null;
|
||||
return array[key - min];
|
||||
}
|
||||
|
||||
public T getOrDefault(int key) {
|
||||
if (key < min || key > max)
|
||||
return array[array.length - 1];
|
||||
return array[key - min];
|
||||
}
|
||||
|
||||
public void defaultValue(T value) {
|
||||
array[array.length - 1] = value;
|
||||
}
|
||||
|
||||
public void put(int key, T value) {
|
||||
if (key == DEFAULT_KEY) {
|
||||
defaultValue(value);
|
||||
return;
|
||||
}
|
||||
if (this.init) {
|
||||
this.init = false;
|
||||
this.min = key;
|
||||
this.max = key;
|
||||
}
|
||||
|
||||
int offset = Math.max(0, min - key);
|
||||
if (key < min) min = key;
|
||||
if (key > max) max = key;
|
||||
|
||||
ensureCapacityInternal(offset);
|
||||
array[key - min] = value;
|
||||
}
|
||||
|
||||
private void ensureCapacityInternal(int offset) {
|
||||
final int minCapacity = max - min + 2;
|
||||
if (minCapacity > 256)
|
||||
throw new IllegalArgumentException("min:" + min + ", max:" + max + "key的区间过大");
|
||||
final int length = array.length - 1;
|
||||
if (minCapacity >= length) {
|
||||
T[] temp = (T[]) new Object[minCapacity];
|
||||
System.arraycopy(array, 0, temp, offset, length);
|
||||
temp[temp.length - 1] = array[length];
|
||||
array = temp;
|
||||
} else {
|
||||
if (offset > 0) {
|
||||
System.arraycopy(array, 0, array, offset, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<T> values() {
|
||||
List<T> values = new ArrayList<>(array.length);
|
||||
for (T t : array)
|
||||
if (t != null)
|
||||
values.add(t);
|
||||
return values;
|
||||
}
|
||||
|
||||
public int[] keys() {
|
||||
int length = size();
|
||||
int[] keys = new int[length];
|
||||
|
||||
int count = 0;
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
if (array[i] != null) keys[count++] = i + min;
|
||||
}
|
||||
|
||||
if (array[array.length - 1] != null)
|
||||
keys[keys.length - 1] = DEFAULT_KEY;
|
||||
return keys;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
int count = 0;
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
if (array[i] != null) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public ArrayMap<T> fillDefaultValue() {
|
||||
if (array.length == 2) {
|
||||
if (array[0] == null)
|
||||
array[0] = array[1];
|
||||
} else {
|
||||
for (int i = 0; i < array.length - 2; i++) {
|
||||
if (array[i] == null)
|
||||
array[i] = array[array.length - 1];
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
@ -0,0 +1,243 @@
|
||||
package com.fastbee.protocol.util;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* Bytes和 Hex转换工具类
|
||||
*/
|
||||
public class ByteToHexUtil {
|
||||
|
||||
/**
|
||||
* 单个Hex字符串转byte
|
||||
*
|
||||
* @param hexStr
|
||||
* @return
|
||||
*/
|
||||
public static byte hexToByte(String hexStr) {
|
||||
return (byte) Integer.parseInt(hexStr, 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* hex字符串转为 byte数组
|
||||
*
|
||||
* @param inHex 转换的 hex字符串
|
||||
* @return 转换后的 byte数组
|
||||
*/
|
||||
public static byte[] hexToByteArray(String inHex) {
|
||||
int hexlen = inHex.length();
|
||||
byte[] result;
|
||||
if (hexlen % 2 == 1) {
|
||||
// 奇数
|
||||
hexlen++;
|
||||
result = new byte[(hexlen / 2)];
|
||||
inHex = "0" + inHex;
|
||||
} else {
|
||||
// 偶数
|
||||
result = new byte[(hexlen / 2)];
|
||||
}
|
||||
int j = 0;
|
||||
for (int i = 0; i < hexlen; i += 2) {
|
||||
result[j] = hexToByte(inHex.substring(i, i + 2));
|
||||
j++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个字节转换十六进制
|
||||
*
|
||||
* @param b byte字节
|
||||
* @return 转换后的单个hex字符
|
||||
*/
|
||||
public static String byteToHex(byte b) {
|
||||
String hex = Integer.toHexString(b & 0xFF);
|
||||
if (hex.length() < 2) {
|
||||
hex = "0" + hex;
|
||||
}
|
||||
return hex;
|
||||
}
|
||||
|
||||
/**
|
||||
* byte数组转换炒年糕十六进制字符串
|
||||
*
|
||||
* @param bArray byte数组
|
||||
* @return hex字符串
|
||||
*/
|
||||
public static String bytesToHexString(byte[] bArray) {
|
||||
StringBuffer sb = new StringBuffer(bArray.length);
|
||||
for (int i = 0; i < bArray.length; i++) {
|
||||
String hexStr = Integer.toHexString(0xFF & bArray[i]);
|
||||
if (hexStr.length() < 2)
|
||||
sb.append(0);
|
||||
sb.append(hexStr.toUpperCase());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 将hex转为正负数 2个字节
|
||||
*
|
||||
* @param hexStr hex字符串
|
||||
* @return 结果
|
||||
*/
|
||||
public static int parseHex2(String hexStr) {
|
||||
if (hexStr.length() != 4) {
|
||||
throw new NumberFormatException("Wrong length: " + hexStr.length() + ", must be 4.");
|
||||
}
|
||||
int ret = Integer.parseInt(hexStr, 16);
|
||||
ret = ((ret & 0x8000) > 0) ? (ret - 0x10000) : (ret);
|
||||
return ret;
|
||||
}
|
||||
public static Integer cutMessageHexTo(byte[] source, int startIndex, int endIndex){
|
||||
byte[] subarray = ArrayUtils.subarray(source, startIndex, endIndex);
|
||||
String s = bytesToHexString(subarray);
|
||||
return Integer.parseInt(s,16);
|
||||
}
|
||||
|
||||
public static String bSubstring(String s,int start, int length) throws Exception
|
||||
{
|
||||
|
||||
byte[] bytes = s.getBytes("Unicode");
|
||||
int n = 0; // 表示当前的字节数
|
||||
int i = start; // 要截取的字节数,从第3个字节开始
|
||||
for (; i < bytes.length && n < length; i++)
|
||||
{
|
||||
// 奇数位置,如3、5、7等,为UCS2编码中两个字节的第二个字节
|
||||
if (i % 2 == 1)
|
||||
{
|
||||
n++; // 在UCS2第二个字节时n加1
|
||||
}
|
||||
else
|
||||
{
|
||||
// 当UCS2编码的第一个字节不等于0时,该UCS2字符为汉字,一个汉字算两个字节
|
||||
if (bytes[i] != 0)
|
||||
{
|
||||
n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果i为奇数时,处理成偶数
|
||||
if (i % 2 == 1)
|
||||
|
||||
{
|
||||
// 该UCS2字符是汉字时,去掉这个截一半的汉字
|
||||
if (bytes[i - 1] != 0)
|
||||
i = i - 1;
|
||||
// 该UCS2字符是字母或数字,则保留该字符
|
||||
else
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
return new String(bytes, 0, i, "Unicode");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param orignal 要截取的字符串
|
||||
* @param start 开始下标
|
||||
* @param count 截取长度
|
||||
* @return
|
||||
*/
|
||||
public static String substringByte(String orignal, int start, int count) {
|
||||
// 如果目标字符串为空,则直接返回,不进入截取逻辑;
|
||||
if (orignal == null || "".equals(orignal)){
|
||||
return orignal;
|
||||
}
|
||||
|
||||
// 截取Byte长度必须>0
|
||||
if (count <= 0) {
|
||||
return orignal;
|
||||
}
|
||||
|
||||
// 截取的起始字节数必须比
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
}
|
||||
|
||||
// 目标char Pull buff缓存区间;
|
||||
StringBuffer buff = new StringBuffer();
|
||||
|
||||
try {
|
||||
// 截取字节起始字节位置大于目标String的Byte的length则返回空值
|
||||
if (start >= getStringByteLenths(orignal)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int len = 0;
|
||||
char c;
|
||||
|
||||
// 遍历String的每一个Char字符,计算当前总长度
|
||||
// 如果到当前Char的的字节长度大于要截取的字符总长度,则跳出循环返回截取的字符串。
|
||||
for (int i = 0; i < orignal.toCharArray().length; i++) {
|
||||
|
||||
c = orignal.charAt(i);
|
||||
|
||||
// 当起始位置为0时候
|
||||
if (start == 0) {
|
||||
len += String.valueOf(c).getBytes("GBK").length;
|
||||
if (len <= count) {
|
||||
buff.append(c);
|
||||
}else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// 截取字符串从非0位置开始
|
||||
len += String.valueOf(c).getBytes("GBK").length;
|
||||
if (len > start && len <= start + count) {
|
||||
buff.append(c);
|
||||
}
|
||||
if (len > start + count) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// 返回最终截取的字符结果;
|
||||
// 创建String对象,传入目标char Buff对象
|
||||
return new String(buff);
|
||||
}
|
||||
|
||||
public static int getStringByteLenths(String args) throws UnsupportedEncodingException {
|
||||
return args != null && args != "" ? args.getBytes("Unicode").length : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
//String str = "000000000111";
|
||||
//byte[] result = hexToByteArray(str);
|
||||
//System.out.println("hex字符串转byte数组 ---" + Arrays.toString(result));
|
||||
//
|
||||
//System.out.println("hex字符串---"+bytesToHexString(result));
|
||||
//int parseInt = Integer.parseInt(str, 16);
|
||||
//System.out.println(parseInt);
|
||||
//
|
||||
//int va = -40;
|
||||
//byte[] bytes = IntegerToByteUtil.intToBytes2(va);
|
||||
//System.out.println("hex字符串转byte数组 ---" + Arrays.toString(bytes));
|
||||
//
|
||||
//int num = 1713;
|
||||
//String hexString = Integer.toHexString(num);
|
||||
//System.out.println(hexString);
|
||||
|
||||
ScriptEngine js = new ScriptEngineManager().getEngineByName("JavaScript");
|
||||
String str = "8*%s";
|
||||
try {
|
||||
System.out.println(js.eval(str.replace("%s","0.42")));
|
||||
}catch (Exception e){
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package com.fastbee.protocol.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* @author bill
|
||||
*/
|
||||
public class Cache<K,V> {
|
||||
private volatile Map<K, V> cache;
|
||||
|
||||
public Cache() {
|
||||
this(32);
|
||||
}
|
||||
|
||||
public Cache(Map<K, V> cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
public Cache(int initialCapacity) {
|
||||
this.cache = new HashMap<>((int) (initialCapacity / 0.75) + 1);
|
||||
}
|
||||
|
||||
public V get(K key) {
|
||||
return cache.get(key);
|
||||
}
|
||||
|
||||
public V get(K key, Supplier<V> function) {
|
||||
V value = cache.get(key);
|
||||
if (value == null) {
|
||||
synchronized (cache) {
|
||||
value = cache.get(key);
|
||||
if (value == null) {
|
||||
cache.put(key, value = function.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public V put(K key, V value) {
|
||||
synchronized (cache) {
|
||||
cache.put(key, value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return cache.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
package com.fastbee.protocol.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2022/11/9 17:47
|
||||
*/
|
||||
public class CharsBuilder implements CharSequence, Appendable{
|
||||
|
||||
private char[] value;
|
||||
private int pos;
|
||||
|
||||
public CharsBuilder(int length) {
|
||||
this.value = new char[length];
|
||||
}
|
||||
|
||||
public CharsBuilder(char[] chars) {
|
||||
this.value = chars;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Appendable append(CharSequence s) {
|
||||
return append(s, 0, s.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Appendable append(CharSequence s, int start, int end) {
|
||||
int len = end - start;
|
||||
for (int i = start, j = pos; i < end; i++, j++)
|
||||
value[j] = s.charAt(i);
|
||||
pos += len;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Appendable append(char c) {
|
||||
value[pos++] = c;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char charAt(int index) {
|
||||
return value[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence subSequence(int start, int end) {
|
||||
if (start == end) {
|
||||
return new CharsBuilder(Math.min(16, value.length));
|
||||
}
|
||||
return new CharsBuilder(Arrays.copyOfRange(value, start, end));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
return value.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new String(value);
|
||||
}
|
||||
|
||||
public String leftStrip(char c) {
|
||||
int i = leftOf(value, c);
|
||||
return new String(value, i, value.length - i);
|
||||
}
|
||||
|
||||
public String rightStrip(char c) {
|
||||
int i = rightOf(value, c);
|
||||
return new String(value, 0, i);
|
||||
}
|
||||
|
||||
public static int leftOf(char[] chars, char pad) {
|
||||
int i = 0, len = chars.length;
|
||||
while (i < len && chars[i] == pad) i++;
|
||||
return i;
|
||||
}
|
||||
|
||||
public static int rightOf(char[] chars, char pad) {
|
||||
int i = 0, len = chars.length;
|
||||
while ((i < len) && (chars[len - 1] <= pad)) len--;
|
||||
return len;
|
||||
}
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
package com.fastbee.protocol.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.net.JarURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
/**
|
||||
* @author bill
|
||||
*/
|
||||
public class ClassUtils {
|
||||
|
||||
public static List<Class> getClassList(String packageName, Class<? extends Annotation> annotationClass) {
|
||||
List<Class> classList = getClassList(packageName);
|
||||
classList.removeIf(next -> !next.isAnnotationPresent(annotationClass));
|
||||
return classList;
|
||||
}
|
||||
|
||||
public static List<Class> getClassList(String packageName) {
|
||||
List<Class> classList = new LinkedList<>();
|
||||
String path = packageName.replace(".", "/");
|
||||
try {
|
||||
Enumeration<URL> urls = ClassUtils.getClassLoader().getResources(path);
|
||||
while (urls.hasMoreElements()) {
|
||||
URL url = urls.nextElement();
|
||||
|
||||
if (url != null) {
|
||||
String protocol = url.getProtocol();
|
||||
|
||||
if (protocol.equals("file")) {
|
||||
addClass(classList, url.toURI().getPath(), packageName);
|
||||
|
||||
} else if (protocol.equals("jar")) {
|
||||
JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
|
||||
JarFile jarFile = jarURLConnection.getJarFile();
|
||||
|
||||
Enumeration<JarEntry> jarEntries = jarFile.entries();
|
||||
while (jarEntries.hasMoreElements()) {
|
||||
|
||||
JarEntry jarEntry = jarEntries.nextElement();
|
||||
String entryName = jarEntry.getName();
|
||||
|
||||
if (entryName.startsWith(path) && entryName.endsWith(".class")) {
|
||||
String className = entryName.substring(0, entryName.lastIndexOf(".")).replaceAll("/", ".");
|
||||
addClass(classList, className);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Initial class error!");
|
||||
}
|
||||
return classList;
|
||||
}
|
||||
|
||||
private static void addClass(List<Class> classList, String packagePath, String packageName) {
|
||||
try {
|
||||
File[] files = new File(packagePath).listFiles(file -> (file.isDirectory() || file.getName().endsWith(".class")));
|
||||
if (files != null)
|
||||
for (File file : files) {
|
||||
String fileName = file.getName();
|
||||
if (file.isFile()) {
|
||||
String className = fileName.substring(0, fileName.lastIndexOf("."));
|
||||
if (packageName != null) {
|
||||
className = packageName + "." + className;
|
||||
}
|
||||
addClass(classList, className);
|
||||
} else {
|
||||
String subPackagePath = fileName;
|
||||
if (packageName != null) {
|
||||
subPackagePath = packagePath + "/" + subPackagePath;
|
||||
}
|
||||
String subPackageName = fileName;
|
||||
if (packageName != null) {
|
||||
subPackageName = packageName + "." + subPackageName;
|
||||
}
|
||||
addClass(classList, subPackagePath, subPackageName);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addClass(List<Class> classList, String className) {
|
||||
classList.add(loadClass(className, false));
|
||||
}
|
||||
|
||||
public static Class loadClass(String className, boolean isInitialized) {
|
||||
try {
|
||||
return Class.forName(className, isInitialized, getClassLoader());
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static ClassLoader getClassLoader() {
|
||||
return Thread.currentThread().getContextClassLoader();
|
||||
}
|
||||
|
||||
public static Class getGenericType(Field f) {
|
||||
Class typeClass = f.getType();
|
||||
if (Collection.class.isAssignableFrom(typeClass)) {
|
||||
return (Class) ((ParameterizedType) f.getGenericType()).getActualTypeArguments()[0];
|
||||
} else if (Map.class.isAssignableFrom(typeClass)) {
|
||||
return (Class) ((ParameterizedType) f.getGenericType()).getActualTypeArguments()[1];
|
||||
} else if (typeClass.isArray()) {
|
||||
return typeClass.getComponentType();
|
||||
}
|
||||
return typeClass;
|
||||
}
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
package com.fastbee.protocol.util;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
|
||||
/**
|
||||
* 时间编码工具
|
||||
* @author gsb
|
||||
* @date 2022/11/9 17:26
|
||||
*/
|
||||
public class DateTool {
|
||||
|
||||
public static final DateTool BYTE = new DateTool();
|
||||
public static final DateTool BCD = new BCD();
|
||||
|
||||
public static final int YEAR = LocalDate.now().getYear();
|
||||
public static final int YEAR_RANGE = YEAR - 30;
|
||||
public static final int HUNDRED_YEAR = YEAR_RANGE / 100 * 100;
|
||||
|
||||
public static int getYear(int year) {
|
||||
year += HUNDRED_YEAR;
|
||||
if (year < YEAR_RANGE) {
|
||||
year += 100;
|
||||
}
|
||||
return year;
|
||||
}
|
||||
|
||||
/** 时间转byte[] (yyMMddHHmmss) */
|
||||
public final byte[] from(LocalDateTime dateTime) {
|
||||
byte[] bytes = new byte[6];
|
||||
bytes[0] = toByte(dateTime.getYear() % 100);
|
||||
bytes[1] = toByte(dateTime.getMonthValue());
|
||||
bytes[2] = toByte(dateTime.getDayOfMonth());
|
||||
bytes[3] = toByte(dateTime.getHour());
|
||||
bytes[4] = toByte(dateTime.getMinute());
|
||||
bytes[5] = toByte(dateTime.getSecond());
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/** byte[]转时间 (yyMMddHHmmss) */
|
||||
public final LocalDateTime toDateTime(byte[] bytes) {
|
||||
try {
|
||||
return LocalDateTime.of(
|
||||
getYear(toInt(bytes[0])),
|
||||
toInt(bytes[1]),
|
||||
toInt(bytes[2]),
|
||||
toInt(bytes[3]),
|
||||
toInt(bytes[4]),
|
||||
toInt(bytes[5]));
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** 日期转byte[] (yyMMdd) */
|
||||
public final byte[] from(LocalDate date) {
|
||||
return new byte[]{
|
||||
toByte((date.getYear() % 100)),
|
||||
toByte(date.getMonthValue()),
|
||||
toByte(date.getDayOfMonth())
|
||||
};
|
||||
}
|
||||
|
||||
/** byte[]转日期 (yyMMdd) */
|
||||
public final LocalDate toDate(byte[] bytes) {
|
||||
return LocalDate.of(
|
||||
getYear(toInt(bytes[0])),
|
||||
toInt(bytes[1]),
|
||||
toInt(bytes[2])
|
||||
);
|
||||
}
|
||||
|
||||
/** 时间转byte[] (HHmmss) */
|
||||
public final byte[] from(LocalTime time) {
|
||||
return new byte[]{
|
||||
toByte(time.getHour()),
|
||||
toByte(time.getMinute()),
|
||||
toByte(time.getSecond())
|
||||
};
|
||||
}
|
||||
|
||||
/** byte[]转时间 (HHmmss) */
|
||||
public final LocalTime toTime(byte[] bytes) {
|
||||
return LocalTime.of(
|
||||
toInt(bytes[0]),
|
||||
toInt(bytes[1]),
|
||||
toInt(bytes[2])
|
||||
);
|
||||
}
|
||||
|
||||
/** 写入2位时间(HHmm) */
|
||||
public final void writeTime2(ByteBuf output, LocalTime time) {
|
||||
output.writeByte(toByte(time.getHour())).writeByte(toByte(time.getMinute()));
|
||||
}
|
||||
|
||||
/** 读取2位时间(HHmm) */
|
||||
public final LocalTime readTime2(ByteBuf input) {
|
||||
return LocalTime.of(toInt(input.readByte()), toInt(input.readByte()));
|
||||
}
|
||||
|
||||
private DateTool() {
|
||||
}
|
||||
|
||||
public byte toByte(int i) {
|
||||
return (byte) i;
|
||||
}
|
||||
|
||||
public int toInt(byte b) {
|
||||
return b & 0xff;
|
||||
}
|
||||
|
||||
private static class BCD extends DateTool {
|
||||
@Override
|
||||
public byte toByte(int i) {
|
||||
return (byte) ((i / 10 << 4) | (i % 10 & 0xf));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int toInt(byte b) {
|
||||
return (b >> 4 & 0xf) * 10 + (b & 0xf);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package com.fastbee.protocol.util;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* 编解码分析
|
||||
*
|
||||
* @author gsb
|
||||
* @date 2022/11/9 16:35
|
||||
*/
|
||||
public class ExplainUtils extends LinkedList<Msg> {
|
||||
|
||||
public void readField(int index, String desc, Object value, ByteBuf input) {
|
||||
if (value != null) {
|
||||
this.add(Msg.field(index, desc, value, ByteBufUtil.hexDump(input, index, input.readerIndex() - index)));
|
||||
}
|
||||
}
|
||||
|
||||
public void writeField(int index, String desc, Object value, ByteBuf output) {
|
||||
if (value != null) {
|
||||
this.add(Msg.field(index, desc, value, ByteBufUtil.hexDump(output, index, output.writerIndex() - index)));
|
||||
}
|
||||
}
|
||||
|
||||
public Msg lengthField(int index, String desc, int length, int lengthUnit) {
|
||||
Msg info = Msg.lengthField(index, desc, length, lengthUnit);
|
||||
this.add(info);
|
||||
return info;
|
||||
}
|
||||
|
||||
public void setLastDesc(String desc) {
|
||||
this.get(this.size() - 1).desc = desc;
|
||||
}
|
||||
|
||||
public void println() {
|
||||
for (Msg info : this) {
|
||||
System.out.println(info);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder(this.size() << 5);
|
||||
for (Msg info : this) {
|
||||
sb.append(info).append('\n');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,140 @@
|
||||
package com.fastbee.protocol.util;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
/**
|
||||
* @author bill
|
||||
*/
|
||||
public interface IntTool {
|
||||
static IntTool getInstance(int length) {
|
||||
switch (length) {
|
||||
case -1:
|
||||
case 0:
|
||||
return ALL;
|
||||
case 1:
|
||||
return BYTE;
|
||||
case 2:
|
||||
return WORD;
|
||||
case 3:
|
||||
return MEDIUM;
|
||||
case 4:
|
||||
return DWORD;
|
||||
default:
|
||||
throw new IllegalArgumentException("unsupported length: " + length + " (expected: 1, 2, 3, 4)");
|
||||
}
|
||||
}
|
||||
|
||||
int get(ByteBuf in, int i);
|
||||
|
||||
void set(ByteBuf out, int i, int n);
|
||||
|
||||
int read(ByteBuf in);
|
||||
|
||||
void write(ByteBuf out, int n);
|
||||
|
||||
/** -1读取剩余所有字节 */
|
||||
IntTool ALL = new IntTool() {
|
||||
@Override
|
||||
public int get(ByteBuf in, int i) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuf out, int i, int n) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(ByteBuf in) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf out, int n) {
|
||||
}
|
||||
};
|
||||
|
||||
IntTool BYTE = new IntTool() {
|
||||
@Override
|
||||
public int get(ByteBuf in, int i) {
|
||||
return in.getUnsignedByte(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuf out, int i, int n) {
|
||||
out.setByte(i, n);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(ByteBuf in) {
|
||||
return in.readUnsignedByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf out, int n) {
|
||||
out.writeByte(n);
|
||||
}
|
||||
};
|
||||
IntTool WORD = new IntTool() {
|
||||
@Override
|
||||
public int get(ByteBuf in, int i) {
|
||||
return in.getUnsignedShort(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuf out, int i, int n) {
|
||||
out.setShort(i, n);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(ByteBuf in) {
|
||||
return in.readUnsignedShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf out, int n) {
|
||||
out.writeShort(n);
|
||||
}
|
||||
};
|
||||
IntTool MEDIUM = new IntTool() {
|
||||
@Override
|
||||
public int get(ByteBuf in, int i) {
|
||||
return in.getUnsignedMedium(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuf out, int i, int n) {
|
||||
out.setMedium(i, n);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(ByteBuf in) {
|
||||
return in.readUnsignedMedium();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf out, int n) {
|
||||
out.writeMedium(n);
|
||||
}
|
||||
};
|
||||
IntTool DWORD = new IntTool() {
|
||||
@Override
|
||||
public int get(ByteBuf in, int i) {
|
||||
return in.getInt(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ByteBuf out, int i, int n) {
|
||||
out.setInt(i, n);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(ByteBuf in) {
|
||||
return in.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf out, int n) {
|
||||
out.writeInt(n);
|
||||
}
|
||||
};
|
||||
}
|
@ -0,0 +1,232 @@
|
||||
package com.fastbee.protocol.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Integer转 byte
|
||||
*/
|
||||
public class IntegerToByteUtil {
|
||||
|
||||
/**
|
||||
* 将int转换为byte数组并指定长度
|
||||
*
|
||||
* @param data int数据
|
||||
* @param byteLen 指定长度
|
||||
* @return byte[]
|
||||
*/
|
||||
public static byte[] intToByteArr(int data, int byteLen) {
|
||||
byte[] bytes = new byte[byteLen];
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
bytes[byteLen - i - 1] = (byte) (data % 256);
|
||||
data = data / 256;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* byte转int
|
||||
*
|
||||
* @param b
|
||||
* @return
|
||||
*/
|
||||
public static int byteToInt(byte b) {
|
||||
return b & 0xff;
|
||||
}
|
||||
|
||||
/**
|
||||
* byte[]转int
|
||||
*
|
||||
* @param bytes 需要转换成int的数组
|
||||
* @return int值
|
||||
*/
|
||||
public static int byteArrayToInt(byte[] src) {
|
||||
int value;
|
||||
int offset = 0;
|
||||
value = (int) ((src[offset] & 0xFF)
|
||||
| ((src[offset+1] & 0xFF)<<8)
|
||||
| ((src[offset+2] & 0xFF)<<16)
|
||||
| ((src[offset+3] & 0xFF)<<24));
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将int数值转换为占四个字节的byte数组,本方法适用于(低位在前,高位在后)的顺序。 和bytesToInt()配套使用
|
||||
*
|
||||
* @param value 要转换的int值
|
||||
* @return byte数组
|
||||
*/
|
||||
public static byte[] intToBytes(int value) {
|
||||
byte[] src = new byte[4];
|
||||
src[3] = (byte) ((value >> 24) & 0xFF);
|
||||
src[2] = (byte) ((value >> 16) & 0xFF);
|
||||
src[1] = (byte) ((value >> 8) & 0xFF);
|
||||
src[0] = (byte) (value & 0xFF);
|
||||
return src;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将int数值转换为占2个字节的byte数组,本方法适用于(高位在前,低位在后)的顺序。 和bytesToInt2()配套使用
|
||||
*/
|
||||
public static byte[] intToBytes2(int value) {
|
||||
byte[] src = new byte[2];
|
||||
src[0] = (byte) ((value >> 8) & 0xFF);
|
||||
src[1] = (byte) (value & 0xFF);
|
||||
return src;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* byte数组中取int数值,本方法适用于(低位在前,高位在后)的顺序,和和intToBytes()配套使用
|
||||
*
|
||||
* @param src byte数组
|
||||
* @param offset 从数组的第offset位开始
|
||||
* @return int数值
|
||||
*/
|
||||
public static int bytesToInt(byte[] src, int offset) {
|
||||
int value;
|
||||
value = (int) ((src[offset] & 0xFF)
|
||||
| ((src[offset + 1] & 0xFF) << 8)
|
||||
| ((src[offset + 2] & 0xFF) << 16)
|
||||
| ((src[offset + 3] & 0xFF) << 24));
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* byte数组中取int数值,本方法适用于(低位在后,高位在前)的顺序。和intToBytes2()配套使用
|
||||
*/
|
||||
public static int bytesToInt2(byte[] src, int offset) {
|
||||
int value;
|
||||
value = (int) (((src[offset] & 0xFF) << 24)
|
||||
| ((src[offset + 1] & 0xFF) << 16)
|
||||
| ((src[offset + 2] & 0xFF) << 8)
|
||||
| (src[offset + 3] & 0xFF));
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将不满4位的byte数组转为四位
|
||||
*
|
||||
* @param src 不满四位数组
|
||||
* @return 满4位数组
|
||||
*/
|
||||
public static byte[] bytesToBytes4(byte[] src) {
|
||||
if (src.length < 4) {
|
||||
byte[] result = new byte[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (i >= 4 - src.length) {
|
||||
result[i] = src[Math.abs(4 - i - src.length)];
|
||||
} else {
|
||||
result[i] = 0; //补全0
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
return src;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将16进制转换为二进制
|
||||
*/
|
||||
public static String hexString2binaryString(int num, int radix) {
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
switch (radix) {
|
||||
case 2:
|
||||
String s = Integer.toBinaryString(num);
|
||||
for (int i = 0; i < 16 - s.length(); i++) {
|
||||
sb.append("0");
|
||||
}
|
||||
return sb.append(s).toString();
|
||||
case 8:
|
||||
return Integer.toOctalString(num);
|
||||
case 16:
|
||||
return Integer.toHexString(num);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static int binaryToInt(String binary) {
|
||||
return Integer.parseInt(binary, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将某个数的n位变为1或0
|
||||
*
|
||||
* @param value 1/0
|
||||
* @param data 待修改的数据
|
||||
* @param position 更改位置
|
||||
* @return 结果
|
||||
*/
|
||||
public static int bitOperation(int data, int value, int position) {
|
||||
|
||||
if (value == 0) {
|
||||
return data & ~(1 << position);
|
||||
} else if (value == 1) {
|
||||
return data | (1 << position);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量修改bit位
|
||||
*
|
||||
* @param data 源数据
|
||||
* @param map 数据map
|
||||
* @return 结果
|
||||
*/
|
||||
public static int bitOperationBatch(int data, Map<Integer, Integer> map) {
|
||||
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
|
||||
if (entry.getValue() == 0) {
|
||||
data = data & ~(1 << entry.getKey());
|
||||
} else if (entry.getValue() == 1) {
|
||||
data = data | (1 << entry.getKey());
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private static String encodeHexString(byte[] data) {
|
||||
char[] hexArray = "0123456789abcdef".toCharArray();
|
||||
char[] out = new char[data.length * 2];
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
int v = data[i] & 0xFF;//取byte的后八位
|
||||
out[i * 2] = hexArray[v >>> 4];
|
||||
out[i * 2 + 1] = hexArray[v & 0x0F];
|
||||
}
|
||||
return new String(out);
|
||||
|
||||
}
|
||||
|
||||
public static String intToHexString6(int number){
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String hexString = Integer.toHexString(number);
|
||||
for (int i = 0; i < 12 - hexString.length(); i++) {
|
||||
sb.append("0");
|
||||
}
|
||||
return sb.append(hexString).toString();
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
byte[] bytes = intToByteArr(-40, 2);
|
||||
System.out.println("将整数转换为byte数组并指定长度---" + Arrays.toString(bytes));
|
||||
System.out.println(IntegerToByteUtil.encodeHexString(bytes));
|
||||
|
||||
String hexString6 = intToHexString6(17);
|
||||
byte[] bytes1 = ByteToHexUtil.hexToByteArray(hexString6);
|
||||
|
||||
int ret = bitOperation(0, 98, 6);
|
||||
byte[] bytes2 = IntegerToByteUtil.intToByteArr(ret, 2);
|
||||
byte[] b1 = {(byte) 0x01,(byte) 0x02};
|
||||
System.out.println(ByteToHexUtil.bytesToHexString(b1));
|
||||
System.out.println(ByteToHexUtil.bytesToHexString(bytes2));
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package com.fastbee.protocol.util;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 键值对
|
||||
* @author bill
|
||||
*/
|
||||
public class KeyValuePair<K, V> implements Map.Entry<K, V> {
|
||||
|
||||
private K key;
|
||||
private V value;
|
||||
|
||||
public KeyValuePair() {
|
||||
}
|
||||
|
||||
public KeyValuePair(K key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(K key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V setValue(V value) {
|
||||
this.value = value;
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder(32);
|
||||
sb.append("KeyValuePair{key=").append(key);
|
||||
sb.append(", value=").append(value);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package com.fastbee.protocol.util;
|
||||
|
||||
import com.fastbee.common.utils.StringUtils;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2022/11/9 16:28
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class Msg {
|
||||
protected int index;
|
||||
protected String desc;
|
||||
protected Object value;
|
||||
protected String raw;
|
||||
|
||||
|
||||
public static Msg field(int index, String desc, Object value, String raw) {
|
||||
return new Msg(index, desc, value, raw);
|
||||
}
|
||||
|
||||
public static Msg lengthField(int index, String desc, int value, int lengthUnit) {
|
||||
return new Msg(index, desc, value, StringUtils.leftPad(Integer.toHexString(value), 1 << lengthUnit, '0'));
|
||||
}
|
||||
|
||||
|
||||
public void setLength(int length, int lengthUnit) {
|
||||
this.value = length;
|
||||
this.raw = StringUtils.leftPad(Integer.toHexString(length), 1 << lengthUnit, '0');
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (desc == null) {
|
||||
return index + "\t[" + raw + "] [" + StringUtils.toString(value) + "]";
|
||||
}
|
||||
return index + "\t[" + raw + "] [" + StringUtils.toString(value) + "] " + desc;
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package com.fastbee.protocol.util;
|
||||
|
||||
import com.fastbee.protocol.base.annotation.Column;
|
||||
import com.fastbee.protocol.base.model.ActiveModel;
|
||||
import com.fastbee.protocol.base.model.ModelRegistry;
|
||||
import com.fastbee.protocol.base.struc.BaseStructure;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 单版本加载
|
||||
* @author bill
|
||||
*/
|
||||
public abstract class SingleVersionUtils {
|
||||
private static final Map<String, ActiveModel> CACHE = new WeakHashMap<>();
|
||||
|
||||
public static <T> ActiveModel<T> getActiveModel(Class<T> typeClass) {
|
||||
return getActiveModel(CACHE, typeClass);
|
||||
}
|
||||
|
||||
public static <T> ActiveModel<T> getActiveModel(Map<String, ActiveModel> root, Class<T> typeClass) {
|
||||
ActiveModel<T> schema = root.get(typeClass.getName());
|
||||
//不支持循环引用
|
||||
if (schema != null) return schema;
|
||||
|
||||
List<Field> fs = findFields(typeClass);
|
||||
if (fs.isEmpty()) return null;
|
||||
|
||||
List<BaseStructure> fieldList = findFields(root, fs);
|
||||
BaseStructure[] fields = fieldList.toArray(new BaseStructure[fieldList.size()]);
|
||||
Arrays.sort(fields);
|
||||
|
||||
schema = new ActiveModel(typeClass, 0, fields);
|
||||
root.put(typeClass.getName(), schema);
|
||||
return schema;
|
||||
}
|
||||
|
||||
private static List<Field> findFields(Class typeClass) {
|
||||
Field[] fields = typeClass.getDeclaredFields();
|
||||
List<Field> result = new ArrayList<>(fields.length);
|
||||
|
||||
for (Field f : fields) {
|
||||
if (f.isAnnotationPresent(Column.class)) {
|
||||
result.add(f);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<BaseStructure> findFields(Map<String, ActiveModel> root, List<Field> fs) {
|
||||
int size = fs.size();
|
||||
List<BaseStructure> fields = new ArrayList<>(size);
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
Field f = fs.get(i);
|
||||
Column column = f.getDeclaredAnnotation(Column.class);
|
||||
if (column != null) {
|
||||
f.setAccessible(true);
|
||||
fillField(root, fields, column, f, i);
|
||||
}
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
private static void fillField(Map<String, ActiveModel> root, List<BaseStructure> fields, Column column, Field f, int position) {
|
||||
BaseStructure BaseStructure = ModelRegistry.get(column, f);
|
||||
if (BaseStructure != null) {
|
||||
fields.add(BaseStructure.init(column, f, position));
|
||||
} else {
|
||||
ActiveModel schema = getActiveModel(root, ClassUtils.getGenericType(f));
|
||||
BaseStructure = ModelRegistry.get(column, f, schema);
|
||||
fields.add(BaseStructure.init(column, f, position));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,144 @@
|
||||
package com.fastbee.protocol.util;
|
||||
|
||||
|
||||
import java.beans.Transient;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* @author bill
|
||||
*/
|
||||
public class ToStringBuilder {
|
||||
private static Cache<String, Builder[]> CACHE = new Cache<>(new WeakHashMap<>());
|
||||
|
||||
public static String toString(Object object) {
|
||||
return toString(null, object, true, (String[]) null);
|
||||
}
|
||||
|
||||
public static String toString(Object object, boolean superclass, String... ignores) {
|
||||
return toString(null, object, superclass, ignores);
|
||||
}
|
||||
|
||||
public static String toString(StringBuilder sb, Object object, boolean superclass, String... ignores) {
|
||||
Class<?> typeClass = object.getClass();
|
||||
Builder[] builders = getBuilders(typeClass, ignores);
|
||||
if (sb == null)
|
||||
sb = new StringBuilder(builders.length * 10);
|
||||
|
||||
String name = typeClass.getName();
|
||||
sb.append(name, name.lastIndexOf('.') + 1, name.length());
|
||||
sb.append('{');
|
||||
try {
|
||||
if (superclass) {
|
||||
for (Builder builder : builders)
|
||||
builder.append(sb, object);
|
||||
} else {
|
||||
for (Builder builder : builders)
|
||||
if (!builder.superclass)
|
||||
builder.append(sb, object);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
sb.setCharAt(sb.length() - 1, '}');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static Builder[] getBuilders(Class<?> typeClass, String... ignores) {
|
||||
return CACHE.get(typeClass.getName(), () -> {
|
||||
Method[] methods = typeClass.getMethods();
|
||||
ArrayList<Builder> result = new ArrayList<>(methods.length);
|
||||
|
||||
for (Method method : methods) {
|
||||
String mName = method.getName();
|
||||
String name = getName(mName);
|
||||
if ((mName.startsWith("get") || mName.startsWith("is")) &&
|
||||
!"class".equals(name) &&
|
||||
!contains(ignores, name) &&
|
||||
method.getParameterCount() == 0 && !method.isAnnotationPresent(Transient.class))
|
||||
result.add(new Builder(name, method, !typeClass.equals(method.getDeclaringClass())));
|
||||
}
|
||||
|
||||
Builder[] temp = new Builder[result.size()];
|
||||
result.toArray(temp);
|
||||
Arrays.sort(temp);
|
||||
return temp;
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean contains(Object[] array, Object obj) {
|
||||
if (array == null || array.length == 0 || obj == null)
|
||||
return false;
|
||||
for (Object t : array) {
|
||||
if (obj.equals(t))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static String getName(String methodName) {
|
||||
char[] name = methodName.toCharArray();
|
||||
if (name[0] == 'g') {
|
||||
name[3] += 32;
|
||||
return new String(name, 3, name.length - 3);
|
||||
} else {
|
||||
name[2] += 32;
|
||||
return new String(name, 2, name.length - 2);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Builder implements Comparable<Builder> {
|
||||
private static final BiConsumer<StringBuilder, Object> APPEND_OBJ = StringBuilder::append;
|
||||
private static final BiConsumer<StringBuilder, Object> APPEND_ARRAY = (sb, array) -> {
|
||||
sb.append('[');
|
||||
int length = Array.getLength(array);
|
||||
boolean tooLong = length > 140;
|
||||
length = tooLong ? 140 : length;
|
||||
for (int i = 0; i < length; i++)
|
||||
sb.append(Array.get(array, i)).append(',');
|
||||
if (tooLong)
|
||||
sb.append("......");
|
||||
sb.setCharAt(sb.length() - 1, ']');
|
||||
};
|
||||
|
||||
public final String name;
|
||||
public final boolean superclass;
|
||||
private final Method method;
|
||||
private final BiConsumer<StringBuilder, Object> append;
|
||||
|
||||
public void append(StringBuilder sb, Object obj) throws Exception {
|
||||
Object value = method.invoke(obj);
|
||||
if (value != null) {
|
||||
sb.append(name).append('=');
|
||||
append.accept(sb, value);
|
||||
sb.append(',');
|
||||
}
|
||||
}
|
||||
|
||||
public Builder(String name, Method method, boolean superclass) {
|
||||
this.name = name;
|
||||
this.method = method;
|
||||
this.superclass = superclass;
|
||||
if (method.getReturnType().isArray()) {
|
||||
append = APPEND_ARRAY;
|
||||
} else {
|
||||
append = APPEND_OBJ;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Builder that) {
|
||||
Class<?> thatType = that.method.getReturnType();
|
||||
if (Iterable.class.isAssignableFrom(thatType) || thatType.isArray())
|
||||
return -1;
|
||||
Class<?> thisType = this.method.getReturnType();
|
||||
if (Iterable.class.isAssignableFrom(thisType) || thisType.isArray())
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
24
fastbee-protocol/fastbee-protocol-collect/pom.xml
Normal file
24
fastbee-protocol/fastbee-protocol-collect/pom.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>fastbee-protocol</artifactId>
|
||||
<groupId>com.fastbee</groupId>
|
||||
<version>3.8.5</version>
|
||||
</parent>
|
||||
|
||||
|
||||
<artifactId>fastbee-protocol-collect</artifactId>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fastbee</groupId>
|
||||
<artifactId>fastbee-protocol-base</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
@ -0,0 +1,179 @@
|
||||
package com.fastbee.ch;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fastbee.common.annotation.SysProtocol;
|
||||
import com.fastbee.common.constant.FastBeeConstant;
|
||||
import com.fastbee.common.core.mq.DeviceReport;
|
||||
import com.fastbee.common.core.mq.MQSendMessageBo;
|
||||
import com.fastbee.common.core.mq.message.DeviceData;
|
||||
import com.fastbee.common.core.mq.message.FunctionCallBackBo;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelValuesInput;
|
||||
import com.fastbee.common.utils.DateUtils;
|
||||
import com.fastbee.protocol.base.protocol.IProtocol;
|
||||
import com.fastbee.protocol.util.ByteToHexUtil;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2024/5/11 16:50
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@SysProtocol(name = "水质监测协议", protocolCode = FastBeeConstant.PROTOCOL.CH, description = "水质监测协议")
|
||||
public class ChProtocolService implements IProtocol {
|
||||
|
||||
|
||||
@Override
|
||||
public DeviceReport decode(DeviceData data, String clientId) {
|
||||
DeviceReport reportMessage = new DeviceReport();
|
||||
ByteBuf buf = data.getBuf();
|
||||
String hexDump = ByteBufUtil.hexDump(buf);
|
||||
JSONObject params = dealMsg(hexDump);
|
||||
List<ThingsModelSimpleItem> result = new ArrayList<>();
|
||||
for (Map.Entry<String, Object> entry : params.entrySet()) {
|
||||
ThingsModelSimpleItem item = new ThingsModelSimpleItem();
|
||||
String val = entry.getValue() + "";
|
||||
item.setTs(DateUtils.getNowDate());
|
||||
item.setValue(val);
|
||||
item.setId(entry.getKey());
|
||||
result.add(item);
|
||||
}
|
||||
reportMessage.setThingsModelSimpleItem(result);
|
||||
reportMessage.setIsPackage(true);
|
||||
reportMessage.setClientId(clientId);
|
||||
reportMessage.setMessageId("900");
|
||||
reportMessage.setSerialNumber(clientId);
|
||||
return reportMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionCallBackBo encode(MQSendMessageBo message) {
|
||||
return new FunctionCallBackBo();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 数据解析(适用于精讯云2.0传输协议)
|
||||
* @param msg 数据
|
||||
* @return
|
||||
*/
|
||||
public static JSONObject dealMsg(String msg) {
|
||||
// 数据示例 FEDC10081609852469E90409003C000000000000165F00002516000000B40000003B0000005500000000000000C1000001C700018A00000000000000000700000008000000000000007581C1
|
||||
JSONObject data = new JSONObject();
|
||||
// 获取帧头 两字节
|
||||
String frameHeader = ByteToHexUtil.substringByte(msg, 0, 4);
|
||||
data.put("frameHeader",frameHeader);
|
||||
// 获取上传状态命令Order
|
||||
String order = ByteToHexUtil.substringByte(msg, 4, 4);
|
||||
data.put("order",order);
|
||||
// 设备id
|
||||
String imei = ByteToHexUtil.substringByte(msg, 8, 12);
|
||||
data.put("imei",imei);
|
||||
// DIN
|
||||
String din = ByteToHexUtil.substringByte(msg, 20, 4);
|
||||
data.put("din",din);
|
||||
// 数据长度
|
||||
String dataLength = ByteToHexUtil.substringByte(msg, 24, 4);
|
||||
data.put("dataLength",dataLength);
|
||||
// 有效数据
|
||||
String validData = ByteToHexUtil.substringByte(msg, 28, msg.getBytes().length - 32);
|
||||
JSONObject analyze = validData1008Analyze(validData);
|
||||
data.putAll(analyze);
|
||||
// 校验和
|
||||
String checksum = ByteToHexUtil.substringByte(msg, msg.getBytes().length - 4, 4);
|
||||
data.put("checksum",checksum);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 有效数据解析(1008 GPS设备上传经纬度包)
|
||||
* (适用于精讯云2.0传输协议)
|
||||
*/
|
||||
public static JSONObject validData1008Analyze(String dataStr){
|
||||
/**
|
||||
* 数据顺序:
|
||||
* 一氧化碳 两位小数
|
||||
* 二氧化碳 两位小数
|
||||
* 臭氧 两位小数
|
||||
* 二氧化硫 两位小数
|
||||
* PM2.5 一位小数
|
||||
* PM10 一位小数
|
||||
* TVOC 两位小数
|
||||
* 温度 一位小数
|
||||
* 湿度 一位小数
|
||||
* 大气压 三位小数
|
||||
* 风速 一位小数
|
||||
* 风向 0位小数
|
||||
* 信号强度 两位小数
|
||||
* 错误码 整数
|
||||
* 版本号 一位小数
|
||||
*/
|
||||
JSONObject data = new JSONObject();
|
||||
// 一氧化碳
|
||||
BigInteger co = new BigInteger(ByteToHexUtil.substringByte(dataStr, 0 * 8, 8),16);
|
||||
data.put("co",BigDecimal.valueOf(co.intValue()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP));
|
||||
// 二氧化碳
|
||||
BigInteger co2 = new BigInteger(ByteToHexUtil.substringByte(dataStr, 1 * 8, 8),16);
|
||||
data.put("co2",BigDecimal.valueOf(co2.intValue()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP));
|
||||
// 臭氧
|
||||
BigInteger o3 = new BigInteger(ByteToHexUtil.substringByte(dataStr, 2 * 8, 8),16);
|
||||
data.put("o3",BigDecimal.valueOf(o3.intValue()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP));
|
||||
// 二氧化硫
|
||||
BigInteger so2 = new BigInteger(ByteToHexUtil.substringByte(dataStr, 3 * 8, 8),16);
|
||||
data.put("so2",BigDecimal.valueOf(so2.intValue()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP));
|
||||
// PM2.5
|
||||
BigInteger pm25 = new BigInteger(ByteToHexUtil.substringByte(dataStr, 4 * 8, 8),16);
|
||||
data.put("pm25",BigDecimal.valueOf(pm25.intValue()).divide(BigDecimal.valueOf(10), 1, RoundingMode.HALF_UP));
|
||||
// PM10
|
||||
BigInteger pm10 = new BigInteger(ByteToHexUtil.substringByte(dataStr, 5 * 8, 8),16);
|
||||
data.put("pm10",BigDecimal.valueOf(pm10.intValue()).divide(BigDecimal.valueOf(10), 1, RoundingMode.HALF_UP));
|
||||
// TVOC
|
||||
BigInteger tvoc = new BigInteger(ByteToHexUtil.substringByte(dataStr, 6 * 8, 8),16);
|
||||
data.put("tvoc",BigDecimal.valueOf(tvoc.intValue()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP));
|
||||
// 温度
|
||||
BigInteger temperature = new BigInteger(ByteToHexUtil.substringByte(dataStr, 7 * 8, 8),16);
|
||||
data.put("temperature",BigDecimal.valueOf(temperature.intValue()).divide(BigDecimal.valueOf(10), 1, RoundingMode.HALF_UP));
|
||||
// 湿度
|
||||
BigInteger humidness = new BigInteger(ByteToHexUtil.substringByte(dataStr, 8 * 8, 8),16);
|
||||
data.put("humidness", BigDecimal.valueOf(humidness.intValue()).divide(BigDecimal.valueOf(10), 1, RoundingMode.HALF_UP));
|
||||
// 大气压
|
||||
BigInteger atmosphericPressure = new BigInteger(ByteToHexUtil.substringByte(dataStr, 9 * 8, 8),16);
|
||||
data.put("atmosphericPressure",BigDecimal.valueOf(atmosphericPressure.intValue()).divide(BigDecimal.valueOf(1000), 3, RoundingMode.HALF_UP));
|
||||
// 风速
|
||||
BigInteger windSpeed = new BigInteger(ByteToHexUtil.substringByte(dataStr, 10 * 8, 8),16);
|
||||
data.put("windSpeed",BigDecimal.valueOf(windSpeed.intValue()).divide(BigDecimal.valueOf(10), 1, RoundingMode.HALF_UP));
|
||||
// 风向
|
||||
BigInteger windDirection = new BigInteger(ByteToHexUtil.substringByte(dataStr, 11 * 8, 8),16);
|
||||
data.put("windDirection",windDirection.intValue());
|
||||
// 信号强度
|
||||
BigInteger signalStrength = new BigInteger(ByteToHexUtil.substringByte(dataStr, 12 * 8, 8),16);
|
||||
data.put("signalStrength",BigDecimal.valueOf(signalStrength.intValue()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP));
|
||||
// 错误码
|
||||
BigInteger errCode = new BigInteger(ByteToHexUtil.substringByte(dataStr, 13 * 8, 8),16);
|
||||
data.put("errCode",errCode.intValue());
|
||||
// 版本号
|
||||
BigInteger version = new BigInteger(ByteToHexUtil.substringByte(dataStr, 14 * 8, 8),16);
|
||||
data.put("version",BigDecimal.valueOf(version.intValue()).divide(BigDecimal.valueOf(10), 1, RoundingMode.HALF_DOWN));
|
||||
return data;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String msg = "FEDC10081609852469E90409003C000000000000165F00002516000000B40000003B0000005500000000000000C1000001C700018A00000000000000000700000008000000000000007581C1";
|
||||
JSONObject data = dealMsg(msg);
|
||||
System.out.println(data);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package com.fastbee.common;
|
||||
|
||||
import com.fastbee.protocol.base.protocol.IProtocol;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author bill
|
||||
*/
|
||||
@Data
|
||||
public class ProtocolColl {
|
||||
|
||||
private IProtocol protocol;
|
||||
|
||||
private Long productId;
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package com.fastbee.common;
|
||||
|
||||
import com.fastbee.common.core.iot.response.DeCodeBo;
|
||||
import com.fastbee.common.core.mq.message.DeviceData;
|
||||
import com.fastbee.common.exception.ServiceException;
|
||||
import com.fastbee.common.utils.gateway.CRC16Utils;
|
||||
import com.fastbee.modbus.codec.ModbusDecoder;
|
||||
import com.fastbee.modbus.codec.ModbusEncoder;
|
||||
import com.fastbee.modbus.model.ModbusRtu;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 协议编解码
|
||||
*
|
||||
* @author gsb
|
||||
* @date 2023/4/8 15:50
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class ProtocolDeCodeService {
|
||||
|
||||
private static ModbusDecoder decoder = new ModbusDecoder("com.fastbee.modbus");
|
||||
private static ModbusEncoder encoder = new ModbusEncoder("com.fastbee.modbus");
|
||||
|
||||
public String protocolDeCode(DeCodeBo bo) {
|
||||
if (null == bo) {
|
||||
throw new ServiceException("输入内容为空");
|
||||
}
|
||||
String payload = bo.getPayload();
|
||||
/*1-解析 2-读指令 3-写指令 4-CRC生成 5-CRC校验*/
|
||||
switch (bo.getType()) {
|
||||
case 1:
|
||||
ByteBuf buf = Unpooled.wrappedBuffer(ByteBufUtil.decodeHexDump(payload));
|
||||
DeviceData data = DeviceData.builder()
|
||||
.buf(buf)
|
||||
.build();
|
||||
ModbusRtu message = decoder.decode(data);
|
||||
ReferenceCountUtil.release(buf);
|
||||
String[] split = message.toString().split("Modbus");
|
||||
return split[0].replace(";", "<br/>");
|
||||
case 2:
|
||||
case 3:
|
||||
ModbusRtu rtu = new ModbusRtu();
|
||||
BeanUtils.copyProperties(bo,rtu);
|
||||
ByteBuf in = encoder.encode(rtu);
|
||||
byte[] bytes = ByteBufUtil.getBytes(in);
|
||||
byte[] result = CRC16Utils.CRC(bytes);
|
||||
String hexDump = ByteBufUtil.hexDump(result);
|
||||
ReferenceCountUtil.release(in);
|
||||
return hexDump;
|
||||
case 4:
|
||||
byte[] crc16Byte = ByteBufUtil.decodeHexDump(payload);
|
||||
String crc = CRC16Utils.getCRC(crc16Byte);
|
||||
return payload + crc;
|
||||
case 5:
|
||||
byte[] crcByte = ByteBufUtil.decodeHexDump(payload);
|
||||
byte[] checksCRC = {crcByte[crcByte.length -2],crcByte[crcByte.length-1]};
|
||||
byte[] sourceCRC = ArrayUtils.subarray(crcByte, 0, crcByte.length - 2);
|
||||
String crc1 = CRC16Utils.getCRC(sourceCRC);
|
||||
String check = ByteBufUtil.hexDump(checksCRC);
|
||||
if (!crc1.equalsIgnoreCase(check)){
|
||||
return "原报文CRC:" + check +"校验失败,CRC值应为:" + crc1 +
|
||||
"<br/>完整报文:" + ByteBufUtil.hexDump(sourceCRC) +crc1;
|
||||
}else {
|
||||
return "校验通过!";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package com.fastbee.flowdev.codec;
|
||||
|
||||
import com.fastbee.common.core.mq.message.DeviceData;
|
||||
import com.fastbee.common.exception.ServiceException;
|
||||
import com.fastbee.common.utils.gateway.CRC8Utils;
|
||||
import com.fastbee.flowdev.model.FlowDev;
|
||||
import com.fastbee.protocol.WModelManager;
|
||||
import com.fastbee.protocol.base.model.ActiveModel;
|
||||
import com.fastbee.protocol.util.ArrayMap;
|
||||
import com.fastbee.protocol.util.ExplainUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2023/5/17 16:37
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@NoArgsConstructor
|
||||
public class FlowDevDecoder {
|
||||
|
||||
@Resource
|
||||
private WModelManager modelManager;
|
||||
private ArrayMap<ActiveModel> headerSchemaMap;
|
||||
|
||||
public FlowDevDecoder(String...basePackages){
|
||||
this.modelManager = new WModelManager(basePackages);
|
||||
this.headerSchemaMap = this.modelManager.getActiveMap(FlowDev.class);
|
||||
}
|
||||
|
||||
public FlowDev decode(DeviceData deviceData, ExplainUtils explain){
|
||||
this.build();
|
||||
ByteBuf in = deviceData.getBuf();
|
||||
verify(in);
|
||||
ActiveModel<FlowDev> activeModel = headerSchemaMap.get(0);
|
||||
FlowDev flowDev = new FlowDev();
|
||||
activeModel.mergeFrom(in,flowDev,explain);
|
||||
log.info("=>流量计数据解析:[{}]",flowDev);
|
||||
return flowDev;
|
||||
}
|
||||
|
||||
/*CRC校验*/
|
||||
private void verify(ByteBuf in){
|
||||
ByteBuf copy = in.duplicate();
|
||||
byte[] source = new byte[in.writerIndex()];
|
||||
copy.readBytes(source);
|
||||
//取倒数第二位校验CRC8
|
||||
byte checkBytes = source[source.length -2];
|
||||
byte[] sourceCheck = ArrayUtils.subarray(source,3,source.length -2);
|
||||
byte crc = CRC8Utils.calcCrc8_E5(sourceCheck);
|
||||
if (crc != checkBytes){
|
||||
log.warn("=>CRC校验异常,报文={}",ByteBufUtil.hexDump(source));
|
||||
throw new ServiceException("CRC校验异常");
|
||||
}
|
||||
}
|
||||
|
||||
private void build(){
|
||||
if (this.headerSchemaMap == null) {
|
||||
this.headerSchemaMap = this.modelManager.getActiveMap(FlowDev.class);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package com.fastbee.flowdev.codec;
|
||||
|
||||
import com.fastbee.flowdev.model.FlowDev;
|
||||
import com.fastbee.protocol.WModelManager;
|
||||
import com.fastbee.protocol.base.model.ActiveModel;
|
||||
import com.fastbee.protocol.util.ArrayMap;
|
||||
import com.fastbee.protocol.util.ExplainUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.buffer.PooledByteBufAllocator;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2023/5/17 16:45
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@NoArgsConstructor
|
||||
public class FlowDevEncoder {
|
||||
|
||||
private static final ByteBufAllocator ALLOC = PooledByteBufAllocator.DEFAULT;
|
||||
|
||||
@Resource
|
||||
private WModelManager modelManager;
|
||||
private ArrayMap<ActiveModel> headerSchemaMap;
|
||||
|
||||
public FlowDevEncoder(String...basePackages){
|
||||
this.modelManager = new WModelManager(basePackages);
|
||||
this.headerSchemaMap = this.modelManager.getActiveMap(FlowDev.class);
|
||||
}
|
||||
|
||||
public ByteBuf encode(FlowDev message, ExplainUtils explain){
|
||||
this.build();
|
||||
ByteBuf buf = ALLOC.buffer();
|
||||
ActiveModel activeModel = headerSchemaMap.get(1);
|
||||
activeModel.writeTo(buf,message,explain);
|
||||
return buf;
|
||||
}
|
||||
|
||||
private void build() {
|
||||
if (this.headerSchemaMap == null) {
|
||||
this.headerSchemaMap = this.modelManager.getActiveMap(FlowDev.class);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
package com.fastbee.flowdev.codec;
|
||||
|
||||
import com.fastbee.common.annotation.SysProtocol;
|
||||
import com.fastbee.common.constant.FastBeeConstant;
|
||||
import com.fastbee.common.core.mq.DeviceReport;
|
||||
import com.fastbee.common.core.mq.MQSendMessageBo;
|
||||
import com.fastbee.common.core.mq.message.DeviceData;
|
||||
import com.fastbee.common.core.mq.message.FunctionCallBackBo;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelValuesInput;
|
||||
import com.fastbee.common.exception.ServiceException;
|
||||
import com.fastbee.common.utils.BeanMapUtilByReflect;
|
||||
import com.fastbee.common.utils.DateUtils;
|
||||
import com.fastbee.common.utils.StringUtils;
|
||||
import com.fastbee.common.utils.gateway.CRC8Utils;
|
||||
import com.fastbee.flowdev.model.FlowDev;
|
||||
import com.fastbee.protocol.base.protocol.IProtocol;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2023/5/17 16:56
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@SysProtocol(name = "流量计解析协议",protocolCode = FastBeeConstant.PROTOCOL.FlowMeter,description = "流量计解析协议")
|
||||
public class FlowDevProtocol implements IProtocol {
|
||||
|
||||
@Resource
|
||||
private FlowDevDecoder devDecoder;
|
||||
@Resource
|
||||
private FlowDevEncoder devEncoder;
|
||||
|
||||
|
||||
@Override
|
||||
public DeviceReport decode(DeviceData data, String clientId) {
|
||||
try {
|
||||
DeviceReport report = new DeviceReport();
|
||||
FlowDev flowDev = devDecoder.decode(data,null);
|
||||
this.handlerData(flowDev);
|
||||
List<ThingsModelSimpleItem> items = BeanMapUtilByReflect.beanToItem(flowDev);
|
||||
report.setThingsModelSimpleItem(items);
|
||||
report.setClientId(clientId);
|
||||
report.setMessageId(flowDev.getStart()+"");
|
||||
report.setIsPackage(true);
|
||||
return report;
|
||||
}catch (Exception e){
|
||||
log.error("=>数据解析出错",e);
|
||||
throw new ServiceException("数据解析出错"+e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionCallBackBo encode(MQSendMessageBo message) {
|
||||
FunctionCallBackBo callBack = new FunctionCallBackBo();
|
||||
FlowDev flowDev = new FlowDev();
|
||||
flowDev.setImei(message.getSerialNumber());
|
||||
flowDev.setDire(0x33);
|
||||
flowDev.setLength(0x08);
|
||||
ByteBuf buf = devEncoder.encode(flowDev, null);
|
||||
byte[] source = ByteBufUtil.getBytes(buf, 3, buf.writerIndex() - 5);
|
||||
byte[] result = new byte[ByteBufUtil.getBytes(buf).length];
|
||||
byte b = CRC8Utils.calcCrc8_E5(source);
|
||||
byte[] crc = new byte[]{b,0x16};
|
||||
System.arraycopy(source,0,result,0,source.length);
|
||||
System.arraycopy(crc,0,result,result.length -2,2);
|
||||
System.out.println(ByteBufUtil.hexDump(buf));
|
||||
//删除缓存,防止netty内存溢出
|
||||
ReferenceCountUtil.release(buf);
|
||||
callBack.setSources(ByteBufUtil.hexDump(buf));
|
||||
callBack.setMessage(result);
|
||||
return callBack;
|
||||
}
|
||||
|
||||
private FlowDev handlerData(FlowDev flowDev){
|
||||
//时间处理
|
||||
String ts = flowDev.getTs();
|
||||
if (StringUtils.isNotEmpty(ts)){
|
||||
Date date = DateUtils.dateTime(DateUtils.SS_MM_HH_DD_HH_YY, ts);
|
||||
String s = DateUtils.dateTimeYY(date);
|
||||
flowDev.setTs(s);
|
||||
}
|
||||
String sum = flowDev.getSum();
|
||||
if (StringUtils.isNotEmpty(sum)){
|
||||
String replace = sum.replace("0", "");
|
||||
flowDev.setSum(replace.equals("")? "0":replace);
|
||||
}
|
||||
String instant = flowDev.getInstant();
|
||||
if (StringUtils.isNotEmpty(instant)){
|
||||
String replace = instant.replace("0", "");
|
||||
flowDev.setInstant(replace.equals("")? "0":replace);
|
||||
int val = Integer.parseInt(flowDev.getInstant())/1000;
|
||||
flowDev.setInstant(val+"");
|
||||
}
|
||||
return flowDev;
|
||||
}
|
||||
}
|
@ -0,0 +1,162 @@
|
||||
package com.fastbee.flowdev.model;
|
||||
|
||||
import com.fastbee.common.core.protocol.Message;
|
||||
import com.fastbee.protocol.base.annotation.Column;
|
||||
import com.fastbee.protocol.util.ToStringBuilder;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2023/5/17 15:02
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
public class FlowDev extends Message {
|
||||
|
||||
@Column(length = 1,version = {0,1},desc = "起始地址")
|
||||
protected int start = 0x68;
|
||||
@Column(length = 1,version = {0,1},desc = "长度")
|
||||
protected int length;
|
||||
@Column(length = 1,version = {0,1} ,desc = "起始位")
|
||||
protected int start1 =0x68;
|
||||
@Column(length = 1,version = {0,1},desc = "方向")
|
||||
protected int dire;
|
||||
@Column(length = 5,version = {0,1},desc = "设备编号")
|
||||
protected String imei;
|
||||
@Column(length = 1,version = {0,1},desc = "功能码")
|
||||
protected String code = "C1";
|
||||
@Column(length = 5,version = 0,desc = "瞬时流量")
|
||||
protected String instant;
|
||||
@Column(length = 5,version = 0,desc = "累积流量")
|
||||
protected String sum;
|
||||
@Column(length = 4,version = 0,desc = "不解析值")
|
||||
protected int no;
|
||||
@Column(length = 6,version = 0,desc = "数据时间")
|
||||
protected String ts;
|
||||
@Column(length = 1,version = {1},desc = "无意义值")
|
||||
protected int oo = 0;
|
||||
@Column(length = 1,version = {0,1},desc = "CRC")
|
||||
protected int crc;
|
||||
@Column(length = 1,version = {0,1},desc = "结束地址")
|
||||
protected int end = 0x16;
|
||||
|
||||
public int getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public void setStart(int start) {
|
||||
this.start = start;
|
||||
}
|
||||
|
||||
public int getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
public void setLength(int length) {
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public int getStart1() {
|
||||
return start1;
|
||||
}
|
||||
|
||||
public void setStart1(int start1) {
|
||||
this.start1 = start1;
|
||||
}
|
||||
|
||||
public int getDire() {
|
||||
return dire;
|
||||
}
|
||||
|
||||
public void setDire(int dire) {
|
||||
this.dire = dire;
|
||||
}
|
||||
|
||||
public String getImei() {
|
||||
return imei;
|
||||
}
|
||||
|
||||
public void setImei(String imei) {
|
||||
this.imei = imei;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getInstant() {
|
||||
return instant;
|
||||
}
|
||||
|
||||
public void setInstant(String instant) {
|
||||
this.instant = instant;
|
||||
}
|
||||
|
||||
public String getSum() {
|
||||
return sum;
|
||||
}
|
||||
|
||||
public void setSum(String sum) {
|
||||
this.sum = sum;
|
||||
}
|
||||
|
||||
public int getNo() {
|
||||
return no;
|
||||
}
|
||||
|
||||
public void setNo(int no) {
|
||||
this.no = no;
|
||||
}
|
||||
|
||||
public String getTs() {
|
||||
return ts;
|
||||
}
|
||||
|
||||
public void setTs(String ts) {
|
||||
this.ts = ts;
|
||||
}
|
||||
|
||||
public int getCrc() {
|
||||
return crc;
|
||||
}
|
||||
|
||||
public void setCrc(int crc) {
|
||||
this.crc = crc;
|
||||
}
|
||||
|
||||
public int getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
public void setEnd(int end) {
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
public int getOo() {
|
||||
return oo;
|
||||
}
|
||||
|
||||
public void setOo(int oo) {
|
||||
this.oo = oo;
|
||||
}
|
||||
|
||||
protected StringBuilder toStringHead() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(";[长度]: ").append(length);
|
||||
sb.append(";[方向]: ").append(dire);
|
||||
sb.append(";[设备编号]: ").append(imei);
|
||||
sb.append(";[功能码]: ").append(code);
|
||||
sb.append(";[瞬时流量]: ").append(instant);
|
||||
sb.append(";[累积流量]: ").append(sum);
|
||||
sb.append(";[数据时间]: ").append(ts);
|
||||
return sb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.toString(toStringHead(), this, true);
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package com.fastbee.flowdev.model;
|
||||
|
||||
import com.fastbee.base.core.annotation.Node;
|
||||
import com.fastbee.base.core.annotation.PakMapping;
|
||||
import com.fastbee.base.session.Session;
|
||||
import com.fastbee.common.core.mq.DeviceReport;
|
||||
import com.fastbee.common.core.protocol.Message;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2023/5/19 14:09
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
@Node
|
||||
public class FlowEndPoint {
|
||||
|
||||
@PakMapping(types = 0x68)
|
||||
public Message register(DeviceReport message, Session session){
|
||||
//检测设备是否注册,未注册,进行注册
|
||||
if (!session.isRegistered()){
|
||||
// 注册设备
|
||||
session.register(message);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package com.fastbee.flowdev.test;
|
||||
|
||||
import com.fastbee.common.core.mq.message.DeviceData;
|
||||
import com.fastbee.flowdev.codec.FlowDevDecoder;
|
||||
import com.fastbee.flowdev.codec.FlowDevEncoder;
|
||||
import com.fastbee.flowdev.model.FlowDev;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
/**
|
||||
* @author bill
|
||||
*/
|
||||
public class FlowDevCodeTest {
|
||||
|
||||
private static FlowDevDecoder decoder = new FlowDevDecoder("com.fastbee");
|
||||
private static FlowDevEncoder encoder = new FlowDevEncoder("com.fastbee");
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
String flowData = "681B68B33701120008C100000000000000000000022050004341231811215716";
|
||||
ByteBuf in = Unpooled.wrappedBuffer(ByteBufUtil.decodeHexDump(flowData));
|
||||
DeviceData data = DeviceData.builder()
|
||||
.buf(in).build();
|
||||
FlowDev flowDev = decoder.decode(data, null);
|
||||
System.out.println(flowDev);
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,180 @@
|
||||
package com.fastbee.hp;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fastbee.common.annotation.SysProtocol;
|
||||
import com.fastbee.common.constant.FastBeeConstant;
|
||||
import com.fastbee.common.core.mq.DeviceReport;
|
||||
import com.fastbee.common.core.mq.MQSendMessageBo;
|
||||
import com.fastbee.common.core.mq.SubDeviceBo;
|
||||
import com.fastbee.common.core.mq.message.DeviceData;
|
||||
import com.fastbee.common.core.mq.message.DeviceDownMessage;
|
||||
import com.fastbee.common.core.mq.message.FunctionCallBackBo;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelValuesInput;
|
||||
import com.fastbee.common.exception.ServiceException;
|
||||
import com.fastbee.common.utils.DateUtils;
|
||||
import com.fastbee.common.utils.StringUtils;
|
||||
import com.fastbee.iot.domain.ModbusConfig;
|
||||
import com.fastbee.iot.model.ThingsModels.ThingsModelValueItem;
|
||||
import com.fastbee.iot.service.ISubGatewayService;
|
||||
import com.fastbee.modbusToJson.FYModel;
|
||||
import com.fastbee.protocol.base.protocol.IProtocol;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2023/8/14 16:04
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@SysProtocol(name = "Modbus转Json解析协议-华普物联",protocolCode = FastBeeConstant.PROTOCOL.ModbusToJsonHP,description = "modbus转json解析协议-华普物联")
|
||||
public class ModbusToJsonHPProtocolService implements IProtocol {
|
||||
|
||||
@Resource
|
||||
private ISubGatewayService subGatewayService;
|
||||
|
||||
/**
|
||||
* 上报数据格式: <p>
|
||||
* device1: 从机1标识 <p>
|
||||
* name: 物模型标识符 <p>
|
||||
* value: 上报值 <p>
|
||||
* {
|
||||
* "device1": [
|
||||
* {
|
||||
* "name": "J2",
|
||||
* "value": 8.331631
|
||||
* },
|
||||
* {
|
||||
* "name": "J1",
|
||||
* "value": -130.123718
|
||||
* }
|
||||
* ],
|
||||
* "device2": [
|
||||
* {
|
||||
* "name": "J4",
|
||||
* "value": -16.350224
|
||||
* },
|
||||
* {
|
||||
* "name": "J3",
|
||||
* "value": 94.769806
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
*
|
||||
* 下发报文格式<p>
|
||||
* device 从机编号 <p>
|
||||
* name 标识符 <p>
|
||||
* value 值 <p>
|
||||
* serNo 流水号 <p>
|
||||
* {
|
||||
* "device": 1,
|
||||
* "name": "template",
|
||||
* "value": 111,
|
||||
* "serNo": "213245489543789"
|
||||
* }
|
||||
* </p>
|
||||
*
|
||||
* 下发指令回复格式<p>
|
||||
* serNo 平台的流水号,用于对应回复消息 <p>
|
||||
* ack 下发指令状态 0是失败 1是成功 <p>
|
||||
* {
|
||||
* "serNo": "213245489543789",
|
||||
* "ack": 1
|
||||
* }
|
||||
* </p>
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public DeviceReport decode(DeviceData deviceData, String clientId) {
|
||||
try {
|
||||
DeviceReport reportMessage = new DeviceReport();
|
||||
String data = new String(deviceData.getData(),StandardCharsets.UTF_8);
|
||||
List<ThingsModelSimpleItem> result = new ArrayList<>();
|
||||
Map<String,Object> values = JSON.parseObject(data, Map.class);
|
||||
if (values.containsKey("serNo")){
|
||||
reportMessage.setIsReply(true);
|
||||
reportMessage.setProtocolCode(FastBeeConstant.PROTOCOL.ModbusToJsonHP);
|
||||
for (Map.Entry<String, Object> entry : values.entrySet()) {
|
||||
ThingsModelSimpleItem simpleItem = new ThingsModelSimpleItem();
|
||||
simpleItem.setTs(DateUtils.getNowDate());
|
||||
simpleItem.setId(entry.getKey());
|
||||
simpleItem.setValue(entry.getValue()+"");
|
||||
if (entry.getKey().equals("serNo")){
|
||||
reportMessage.setMessageId(entry.getValue()+"");
|
||||
}
|
||||
result.add(simpleItem);
|
||||
}
|
||||
}else {
|
||||
for (Map.Entry<String, Object> entry : values.entrySet()) {
|
||||
String slaveKey = entry.getKey();
|
||||
Integer slaveId = StringUtils.matcherNum(slaveKey);
|
||||
matchSubDev(reportMessage,clientId,slaveId);
|
||||
List<FYModel> valueList = JSON.parseArray(JSON.toJSONString(entry.getValue()), FYModel.class);
|
||||
for (FYModel fyModel : valueList) {
|
||||
ThingsModelSimpleItem item = new ThingsModelSimpleItem();
|
||||
item.setTs(DateUtils.getNowDate());
|
||||
item.setValue(fyModel.getValue());
|
||||
item.setId(fyModel.getName());
|
||||
result.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
reportMessage.setThingsModelSimpleItem(result);
|
||||
reportMessage.setSources(data);
|
||||
return reportMessage;
|
||||
}catch (Exception e){
|
||||
throw new ServiceException("数据解析异常"+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 匹配子设备编号
|
||||
*/
|
||||
private void matchSubDev(DeviceReport report, String serialNumber, Integer slaveId) {
|
||||
List<SubDeviceBo> subDeviceList = subGatewayService.getSubDeviceListByGw(serialNumber);
|
||||
if (!CollectionUtils.isEmpty(subDeviceList)) {
|
||||
for (SubDeviceBo vo : subDeviceList) {
|
||||
if (vo.getSlaveId().equals(slaveId)) {
|
||||
report.setSubDeviceBo(vo);
|
||||
report.setSerialNumber(vo.getSubDeviceNo());
|
||||
report.setProductId(vo.getSubProductId());
|
||||
report.setClientId(vo.getSubDeviceNo());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionCallBackBo encode(MQSendMessageBo message) {
|
||||
try {
|
||||
FunctionCallBackBo callBack = new FunctionCallBackBo();
|
||||
String thingsModel = message.getThingsModel();
|
||||
ThingsModelValueItem item = JSONObject.parseObject(thingsModel, ThingsModelValueItem.class);
|
||||
ModbusConfig modbusConfig = item.getConfig();
|
||||
JSONObject params = new JSONObject();
|
||||
params.put("device",modbusConfig.getSlave());
|
||||
params.put("name",message.getIdentifier());
|
||||
params.put("value",message.getValue());
|
||||
params.put("serNo",message.getMessageId());
|
||||
String msg = JSONObject.toJSONString(params);
|
||||
callBack.setSources(msg);
|
||||
callBack.setMessage(msg.getBytes());
|
||||
return callBack;
|
||||
}catch (Exception e){
|
||||
log.error("=>指令编码异常,device={}",message.getSerialNumber());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
1. 上报数据格式:
|
||||
device1: 从机1标识
|
||||
name: 物模型标识符
|
||||
value: 上报值
|
||||
{
|
||||
"device1": [
|
||||
{
|
||||
"name": "J2",
|
||||
"value": 8.331631
|
||||
},
|
||||
{
|
||||
"name": "J1",
|
||||
"value": -130.123718
|
||||
}
|
||||
],
|
||||
"device2": [
|
||||
{
|
||||
"name": "J4",
|
||||
"value": -16.350224
|
||||
},
|
||||
{
|
||||
"name": "J3",
|
||||
"value": 94.769806
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
2. 下发报文格式
|
||||
device 从机编号
|
||||
name 标识符
|
||||
value 值
|
||||
serNo 流水号
|
||||
{
|
||||
"device": 1,
|
||||
"name": "template",
|
||||
"value": 111,
|
||||
"serNo": "213245489543789"
|
||||
}
|
||||
|
||||
3. 下发指令回复格式
|
||||
serNo 平台的流水号,用于对应回复消息
|
||||
ack 下发指令状态 0是失败 1是成功
|
||||
{
|
||||
"serNo": "213245489543789",
|
||||
"ack": 1
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
package com.fastbee.json;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fastbee.common.annotation.SysProtocol;
|
||||
import com.fastbee.common.constant.FastBeeConstant;
|
||||
import com.fastbee.common.core.mq.DeviceReport;
|
||||
import com.fastbee.common.core.mq.MQSendMessageBo;
|
||||
import com.fastbee.common.core.mq.message.DeviceData;
|
||||
import com.fastbee.common.core.mq.message.FunctionCallBackBo;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelValuesInput;
|
||||
import com.fastbee.common.exception.ServiceException;
|
||||
import com.fastbee.common.utils.DateUtils;
|
||||
import com.fastbee.iot.model.ThingsModels.ValueItem;
|
||||
import com.fastbee.protocol.base.protocol.IProtocol;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2022/10/10 16:55
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@SysProtocol(name = "JSONArray解析协议", protocolCode = FastBeeConstant.PROTOCOL.JsonArray, description = "系统内置JSONArray解析协议")
|
||||
public class JsonProtocolService implements IProtocol {
|
||||
|
||||
/**
|
||||
* 上报数据格式 <p>
|
||||
* [
|
||||
* {
|
||||
* "id": "switch",
|
||||
* "value": "0"
|
||||
* },
|
||||
* {
|
||||
* "id": "gear",
|
||||
* "value": "0"
|
||||
* }
|
||||
* ]
|
||||
*/
|
||||
@Override
|
||||
public DeviceReport decode(DeviceData deviceData, String clientId) {
|
||||
try {
|
||||
DeviceReport reportMessage = new DeviceReport();
|
||||
// bytep[] 转String
|
||||
String data = new String(deviceData.getData(), StandardCharsets.UTF_8);
|
||||
List<ThingsModelSimpleItem> values = JSON.parseArray(data, ThingsModelSimpleItem.class);
|
||||
//上报数据时间
|
||||
for (ThingsModelSimpleItem value : values) {
|
||||
value.setTs(DateUtils.getNowDate());
|
||||
}
|
||||
reportMessage.setThingsModelSimpleItem(values);
|
||||
reportMessage.setClientId(clientId);
|
||||
reportMessage.setSerialNumber(clientId);
|
||||
reportMessage.setSources(data);
|
||||
return reportMessage;
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException("数据解析异常" + e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 下发 [{"id":"switch","value":"0","remark":""}]
|
||||
*
|
||||
* @param message
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public FunctionCallBackBo encode(MQSendMessageBo message) {
|
||||
try {
|
||||
FunctionCallBackBo callBack = new FunctionCallBackBo();
|
||||
JSONObject params = message.getParams();
|
||||
ValueItem valueItem = new ValueItem();
|
||||
for (Map.Entry<String, Object> entry : params.entrySet()) {
|
||||
valueItem.setId(entry.getKey());
|
||||
valueItem.setValue(entry.getValue() + "");
|
||||
valueItem.setRemark("");
|
||||
}
|
||||
String msg = "[" + JSONObject.toJSONString(valueItem) + "]";
|
||||
callBack.setSources(msg);
|
||||
callBack.setMessage(msg.getBytes());
|
||||
return callBack;
|
||||
} catch (Exception e) {
|
||||
log.error("=>指令编码异常,device={},data={}", message.getSerialNumber(),
|
||||
message.getParams());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package com.fastbee.jsonPak;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fastbee.common.annotation.SysProtocol;
|
||||
import com.fastbee.common.constant.FastBeeConstant;
|
||||
import com.fastbee.common.core.mq.DeviceReport;
|
||||
import com.fastbee.common.core.mq.MQSendMessageBo;
|
||||
import com.fastbee.common.core.mq.message.DeviceData;
|
||||
import com.fastbee.common.core.mq.message.FunctionCallBackBo;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelValuesInput;
|
||||
import com.fastbee.common.exception.ServiceException;
|
||||
import com.fastbee.common.utils.DateUtils;
|
||||
import com.fastbee.protocol.base.protocol.IProtocol;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2022/10/10 16:55
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@SysProtocol(name = "JSONObject解析协议",protocolCode = FastBeeConstant.PROTOCOL.JsonObject,description = "系统内置JSONObject解析协议")
|
||||
public class JsonPakProtocolService implements IProtocol {
|
||||
|
||||
/**
|
||||
* 上报数据格式 <p>
|
||||
* {
|
||||
* "params": {
|
||||
* "DI" : 0,
|
||||
* "DO" : 1
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
@Override
|
||||
public DeviceReport decode(DeviceData deviceData, String clientId) {
|
||||
try {
|
||||
DeviceReport reportMessage = new DeviceReport();
|
||||
// bytep[] 转String
|
||||
String data = new String(deviceData.getData(),StandardCharsets.UTF_8);
|
||||
JSONObject params = JSONObject.parseObject(data);
|
||||
List<ThingsModelSimpleItem> result = new ArrayList<>();
|
||||
for (Map.Entry<String, Object> entry : params.entrySet()) {
|
||||
ThingsModelSimpleItem item = new ThingsModelSimpleItem();
|
||||
item.setTs(DateUtils.getNowDate());
|
||||
item.setValue(entry.getValue()+"");
|
||||
item.setId(entry.getKey());
|
||||
result.add(item);
|
||||
}
|
||||
reportMessage.setThingsModelSimpleItem(result);
|
||||
reportMessage.setIsPackage(true);
|
||||
reportMessage.setMessageId("0");
|
||||
reportMessage.setClientId(clientId);
|
||||
reportMessage.setSerialNumber(clientId);
|
||||
return reportMessage;
|
||||
}catch (Exception e){
|
||||
throw new ServiceException("数据解析异常"+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionCallBackBo encode(MQSendMessageBo message) {
|
||||
try {
|
||||
FunctionCallBackBo callBack = new FunctionCallBackBo();
|
||||
String msg = message.getParams().toString();
|
||||
callBack.setMessage(msg.getBytes());
|
||||
callBack.setSources(msg);
|
||||
return callBack;
|
||||
}catch (Exception e){
|
||||
log.error("=>指令编码异常,device={},data={}",message.getSerialNumber(),
|
||||
message.getParams());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package com.fastbee.jsonPak.pak;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.fastbee.base.core.annotation.Node;
|
||||
import com.fastbee.base.core.annotation.PakMapping;
|
||||
import com.fastbee.base.session.Session;
|
||||
import com.fastbee.common.core.mq.DeviceReport;
|
||||
import com.fastbee.common.core.protocol.Message;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2023/5/4 15:09
|
||||
*/
|
||||
@Node
|
||||
@Component
|
||||
@Slf4j
|
||||
public class JsonEndPoint {
|
||||
|
||||
|
||||
@PakMapping(types = 0)
|
||||
public void register(DeviceReport message, Session session){
|
||||
// 注册设备
|
||||
session.register(message);
|
||||
//String callback = "{\"pak_ty\":\"set_inf\",\"cj_s\":null,\"up_s\":3600,\"xt_s\":3600,\"x_yz\":500,\"y_yz\":500,\"z_yz\":500,\"nian\":2022,\"yue\":3,\"ri\":25,\"shi\":12,\"fen\":23,\"miao\":33}";
|
||||
//message.setBody(callback);
|
||||
//return message;
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package com.fastbee.jsonchenyi;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fastbee.common.annotation.SysProtocol;
|
||||
import com.fastbee.common.constant.FastBeeConstant;
|
||||
import com.fastbee.common.core.mq.DeviceReport;
|
||||
import com.fastbee.common.core.mq.MQSendMessageBo;
|
||||
import com.fastbee.common.core.mq.message.DeviceData;
|
||||
import com.fastbee.common.core.mq.message.FunctionCallBackBo;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelValuesInput;
|
||||
import com.fastbee.common.exception.ServiceException;
|
||||
import com.fastbee.common.utils.DateUtils;
|
||||
import com.fastbee.protocol.base.protocol.IProtocol;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2022/10/10 16:55
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@SysProtocol(name = "JSON-Data解析协议", protocolCode = FastBeeConstant.PROTOCOL.JsonObject_ChenYi, description = "系统内置JSONObject解析协议")
|
||||
public class JsonChenYiProtocolService implements IProtocol {
|
||||
|
||||
/**
|
||||
* 解析json格式数据
|
||||
* 上报数据格式:
|
||||
*/
|
||||
@Override
|
||||
public DeviceReport decode(DeviceData deviceData, String clientId) {
|
||||
try {
|
||||
DeviceReport reportMessage = new DeviceReport();
|
||||
// bytep[] 转String
|
||||
String data = new String(deviceData.getData(), StandardCharsets.UTF_8);
|
||||
Map<String, Object> values = JSON.parseObject(data, Map.class);
|
||||
JSONArray array = (JSONArray) values.get("data");
|
||||
List<ThingsModelSimpleItem> result = new ArrayList<>();
|
||||
JSONObject jsonObject = (JSONObject) array.get(0);
|
||||
for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
|
||||
ThingsModelSimpleItem item = new ThingsModelSimpleItem();
|
||||
item.setTs(DateUtils.getNowDate());
|
||||
item.setValue(entry.getValue() + "");
|
||||
item.setId(entry.getKey());
|
||||
result.add(item);
|
||||
}
|
||||
reportMessage.setThingsModelSimpleItem(result);
|
||||
reportMessage.setClientId(clientId);
|
||||
reportMessage.setSerialNumber(clientId);
|
||||
reportMessage.setSources(data);
|
||||
return reportMessage;
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException("数据解析异常" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionCallBackBo encode(MQSendMessageBo message) {
|
||||
try {
|
||||
FunctionCallBackBo callBack = new FunctionCallBackBo();
|
||||
String msg = message.getParams().toString();
|
||||
callBack.setSources(msg);
|
||||
callBack.setMessage(msg.getBytes());
|
||||
return callBack;
|
||||
}catch (Exception e){
|
||||
log.error("=>指令编码异常,device={},data={}",message.getSerialNumber(),
|
||||
message.getParams());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
package com.fastbee.modbus.codec;
|
||||
|
||||
import com.fastbee.common.ProtocolColl;
|
||||
import com.fastbee.common.constant.FastBeeConstant;
|
||||
import com.fastbee.common.core.mq.DeviceReport;
|
||||
import com.fastbee.common.core.mq.message.DeviceData;
|
||||
import com.fastbee.common.core.protocol.Message;
|
||||
import com.fastbee.common.exception.ServiceException;
|
||||
import com.fastbee.common.utils.StringUtils;
|
||||
import com.fastbee.iot.model.ProductCode;
|
||||
import com.fastbee.iot.service.IProductService;
|
||||
import com.fastbee.protocol.base.protocol.IProtocol;
|
||||
import com.fastbee.protocol.service.IProtocolManagerService;
|
||||
import com.fastbee.base.codec.MessageDecoder;
|
||||
import com.fastbee.base.codec.MessageEncoder;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 消息编解码适配器
|
||||
*
|
||||
* @author bill
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class MessageAdapter implements MessageDecoder, MessageEncoder{
|
||||
|
||||
|
||||
@Autowired
|
||||
private IProtocolManagerService managerService;
|
||||
@Autowired
|
||||
private IProductService productService;
|
||||
|
||||
/**
|
||||
* modbus消息解码
|
||||
*
|
||||
* @param buf 原数据
|
||||
* @param clientId 客户端id
|
||||
* @return 解析后bean
|
||||
*/
|
||||
@Override
|
||||
public DeviceReport decode(ByteBuf buf, String clientId) {
|
||||
IProtocol protocol = null;
|
||||
Long productId = null;
|
||||
String devNum = new String(ByteBufUtil.getBytes(buf));
|
||||
String dump = ByteBufUtil.hexDump(buf);
|
||||
log.info("=>上报hex数据:{}", dump);
|
||||
|
||||
//这里兼容一下TCP整包发送的数据(整包==设备编号,数据一起发送)
|
||||
if (clientId == null){
|
||||
if (StringUtils.isNotEmpty(dump)) {
|
||||
assert dump != null;
|
||||
if (dump.startsWith("68") && dump.endsWith("16")) {
|
||||
protocol = managerService.getProtocolByProtocolCode(FastBeeConstant.PROTOCOL.FlowMeter);
|
||||
}else if (dump.startsWith("7e")&& dump.endsWith("7e")){
|
||||
protocol = managerService.getProtocolByProtocolCode(FastBeeConstant.PROTOCOL.ModbusRtu);
|
||||
}else if (devNum.startsWith("{") && devNum.endsWith("}")){
|
||||
protocol = managerService.getProtocolByProtocolCode(FastBeeConstant.PROTOCOL.SGZ);
|
||||
}else if (devNum.startsWith("[") && devNum.endsWith("]")){
|
||||
protocol = managerService.getProtocolByProtocolCode(FastBeeConstant.PROTOCOL.JsonArray);
|
||||
}else if (dump.startsWith("fedc")){
|
||||
protocol = managerService.getProtocolByProtocolCode(FastBeeConstant.PROTOCOL.CH);
|
||||
}
|
||||
}
|
||||
|
||||
}else {
|
||||
ProtocolColl coll = selectedProtocol(clientId);
|
||||
protocol = coll.getProtocol();
|
||||
productId = coll.getProductId();
|
||||
}
|
||||
DeviceData deviceData = DeviceData.builder()
|
||||
.buf(buf)
|
||||
.productId(productId)
|
||||
.serialNumber(clientId)
|
||||
.data(ByteBufUtil.getBytes(buf))
|
||||
.build();
|
||||
return protocol.decode(deviceData, clientId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* modbus消息编码
|
||||
*
|
||||
* @param message modbusbean
|
||||
* @return 编码指令
|
||||
*/
|
||||
@Override
|
||||
public ByteBuf encode(Message message, String clientId) {
|
||||
String serialNumber = StringUtils.isNotEmpty(clientId) ? clientId : message.getClientId();
|
||||
ProtocolColl protocol = selectedProtocol(serialNumber);
|
||||
IProtocol instance = protocol.getProtocol();
|
||||
DeviceData data = DeviceData.builder().body(message).build();
|
||||
byte[] out = instance.encodeCallBack(data);
|
||||
log.info("应答设备,clientId=[{}],指令=[{}]", serialNumber, ByteBufUtil.hexDump(out));
|
||||
return Unpooled.wrappedBuffer(out);
|
||||
}
|
||||
|
||||
private ProtocolColl selectedProtocol(String serialNumber) {
|
||||
ProtocolColl protocolColl = new ProtocolColl();
|
||||
try {
|
||||
ProductCode protocol = productService.getProtocolBySerialNumber(serialNumber);
|
||||
if (protocol == null || StringUtils.isEmpty(protocol.getProtocolCode())) {
|
||||
protocol.setProtocolCode(FastBeeConstant.PROTOCOL.ModbusRtu);
|
||||
}
|
||||
IProtocol code = managerService.getProtocolByProtocolCode(protocol.getProtocolCode());
|
||||
protocolColl.setProtocol(code);
|
||||
protocolColl.setProductId(protocol.getProductId());
|
||||
return protocolColl;
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
package com.fastbee.modbus.codec;
|
||||
|
||||
import com.fastbee.common.core.mq.message.DeviceData;
|
||||
import com.fastbee.common.core.protocol.modbus.ModbusCode;
|
||||
import com.fastbee.common.exception.ServiceException;
|
||||
import com.fastbee.common.utils.gateway.CRC16Utils;
|
||||
import com.fastbee.modbus.pak.TcpDtu;
|
||||
import com.fastbee.modbus.model.ModbusRtu;
|
||||
import com.fastbee.protocol.WModelManager;
|
||||
import com.fastbee.protocol.base.model.ActiveModel;
|
||||
import com.fastbee.protocol.util.ArrayMap;
|
||||
import com.fastbee.protocol.util.ExplainUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
|
||||
/**
|
||||
* modbus-rtu协议解码器
|
||||
*
|
||||
* @author bill
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@NoArgsConstructor
|
||||
public class ModbusDecoder {
|
||||
|
||||
@Resource
|
||||
private WModelManager modelManager;
|
||||
private ArrayMap<ActiveModel> headerSchemaMap;
|
||||
|
||||
public ModbusDecoder(String... basePackages) {
|
||||
this.modelManager = new WModelManager(basePackages);
|
||||
this.headerSchemaMap = this.modelManager.getActiveMap(ModbusRtu.class);
|
||||
}
|
||||
|
||||
public ModbusRtu decode(DeviceData deviceData) {
|
||||
try {
|
||||
this.build();
|
||||
ByteBuf in = deviceData.getBuf();
|
||||
int start = in.getUnsignedByte(0);
|
||||
int messageId = in.getUnsignedByte(1);
|
||||
int currentMessageId = messageId;
|
||||
if (!(start == TcpDtu.起始位 && (messageId == TcpDtu.注册报文 || messageId == TcpDtu.心跳包))) {
|
||||
//03解析
|
||||
currentMessageId = 0;
|
||||
if (deviceData.getCode() == ModbusCode.Write06 || messageId == ModbusCode.Write06.getCode() ||
|
||||
deviceData.getCode() == ModbusCode.Write05 || messageId == ModbusCode.Write05.getCode()) {
|
||||
//设备回复06解析
|
||||
currentMessageId = 2;
|
||||
} else if (deviceData.getCode() == ModbusCode.Read01 || messageId == ModbusCode.Read01.getCode() ||
|
||||
deviceData.getCode() == ModbusCode.Read02 || messageId == ModbusCode.Read02.getCode()) {
|
||||
// 01、02解析
|
||||
currentMessageId = 4;
|
||||
}
|
||||
in = verify(in);
|
||||
}
|
||||
messageId = currentMessageId;
|
||||
ActiveModel<ModbusRtu> activeModel = headerSchemaMap.get(messageId);
|
||||
ModbusRtu message = new ModbusRtu();
|
||||
message.setPayload(in);
|
||||
activeModel.mergeFrom(in, message, null);
|
||||
log.info("=>解析:[{}]", message);
|
||||
return message;
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private ByteBuf verify(ByteBuf in) {
|
||||
ByteBuf copy = in.duplicate();
|
||||
byte[] source = new byte[in.writerIndex()];
|
||||
copy.readBytes(source);
|
||||
byte[] checkBytes = {source[source.length - 2], source[source.length - 1]};
|
||||
byte[] sourceCheck = ArrayUtils.subarray(source, 0, source.length - 2);
|
||||
String crc = CRC16Utils.getCRC(sourceCheck);
|
||||
if (!crc.equalsIgnoreCase(ByteBufUtil.hexDump(checkBytes))) {
|
||||
log.warn("=>CRC校验异常,报文={}", ByteBufUtil.hexDump(source));
|
||||
throw new ServiceException("CRC校验异常");
|
||||
}
|
||||
return Unpooled.wrappedBuffer(sourceCheck);
|
||||
}
|
||||
|
||||
private void build() {
|
||||
if (this.headerSchemaMap == null) {
|
||||
this.headerSchemaMap = this.modelManager.getActiveMap(ModbusRtu.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package com.fastbee.modbus.codec;
|
||||
|
||||
import com.fastbee.modbus.model.ModbusRtu;
|
||||
import com.fastbee.protocol.WModelManager;
|
||||
import com.fastbee.protocol.base.model.ActiveModel;
|
||||
import com.fastbee.common.core.protocol.modbus.ModbusCode;
|
||||
import com.fastbee.protocol.util.ArrayMap;
|
||||
import com.fastbee.protocol.util.ExplainUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.buffer.PooledByteBufAllocator;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* modbus-rtu协议编码器
|
||||
*
|
||||
* @author bill
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@NoArgsConstructor
|
||||
public class ModbusEncoder {
|
||||
|
||||
private static final ByteBufAllocator ALLOC = PooledByteBufAllocator.DEFAULT;
|
||||
|
||||
@Autowired
|
||||
private WModelManager modelManager;
|
||||
|
||||
private ArrayMap<ActiveModel> headerSchemaMap;
|
||||
|
||||
public ModbusEncoder(String... basePackages) {
|
||||
this.modelManager = new WModelManager(basePackages);
|
||||
this.headerSchemaMap = this.modelManager.getActiveMap(ModbusRtu.class);
|
||||
}
|
||||
|
||||
public ByteBuf encode(ModbusRtu message) {
|
||||
this.build();
|
||||
/*下发读指令*/
|
||||
int version = 1;
|
||||
/*下发写指令*/
|
||||
if (message.getCode() == ModbusCode.Write06.getCode()
|
||||
|| message.getCode() == ModbusCode.Write05.getCode()) {
|
||||
version = 2;
|
||||
}else if (message.getCode() == ModbusCode.Write10.getCode()){
|
||||
version = 16;
|
||||
}else if (message.getCode() == ModbusCode.Write0F.getCode()){
|
||||
version = 15;
|
||||
}
|
||||
ByteBuf buf = ALLOC.buffer(6);
|
||||
ActiveModel activeModel = headerSchemaMap.get(version);
|
||||
activeModel.writeTo(buf, message, null);
|
||||
return buf;
|
||||
}
|
||||
|
||||
private void build() {
|
||||
if (this.headerSchemaMap == null) {
|
||||
this.headerSchemaMap = this.modelManager.getActiveMap(ModbusRtu.class);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,421 @@
|
||||
package com.fastbee.modbus.codec;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fastbee.common.annotation.SysProtocol;
|
||||
import com.fastbee.common.constant.FastBeeConstant;
|
||||
import com.fastbee.common.core.mq.DeviceReport;
|
||||
import com.fastbee.common.core.mq.MQSendMessageBo;
|
||||
import com.fastbee.common.core.mq.SubDeviceBo;
|
||||
import com.fastbee.common.core.mq.message.DeviceData;
|
||||
import com.fastbee.common.core.mq.message.FunctionCallBackBo;
|
||||
import com.fastbee.common.core.protocol.modbus.ModbusCode;
|
||||
import com.fastbee.common.core.redis.RedisCache;
|
||||
import com.fastbee.common.core.redis.RedisKeyBuilder;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
|
||||
import com.fastbee.common.enums.FunctionReplyStatus;
|
||||
import com.fastbee.common.enums.ModbusDataType;
|
||||
import com.fastbee.common.exception.ServiceException;
|
||||
import com.fastbee.common.utils.CaculateUtils;
|
||||
import com.fastbee.common.utils.DateUtils;
|
||||
import com.fastbee.common.utils.StringUtils;
|
||||
import com.fastbee.common.utils.gateway.CRC16Utils;
|
||||
import com.fastbee.common.utils.modbus.BitUtils;
|
||||
import com.fastbee.common.utils.modbus.ModbusUtils;
|
||||
import com.fastbee.common.utils.modbus.Mparams;
|
||||
import com.fastbee.iot.cache.IModbusConfigCache;
|
||||
import com.fastbee.iot.domain.ModbusConfig;
|
||||
import com.fastbee.iot.model.ThingsModelItem.Datatype;
|
||||
import com.fastbee.iot.model.ThingsModels.ThingsModelValueItem;
|
||||
import com.fastbee.iot.model.gateWay.SubDeviceListVO;
|
||||
import com.fastbee.iot.service.IDeviceService;
|
||||
import com.fastbee.iot.service.ISubGatewayService;
|
||||
import com.fastbee.iot.service.IThingsModelService;
|
||||
import com.fastbee.modbus.model.ModbusRtu;
|
||||
import com.fastbee.protocol.base.protocol.IProtocol;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author bill
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@SysProtocol(name = "ModbusRtu协议", protocolCode = FastBeeConstant.PROTOCOL.ModbusRtu, description = "系统内置ModbusRtu解析协议")
|
||||
public class ModbusProtocol implements IProtocol {
|
||||
|
||||
|
||||
@Resource
|
||||
private ModbusDecoder messageDecoder;
|
||||
@Resource
|
||||
private ModbusEncoder messageEncoder;
|
||||
@Resource
|
||||
private RedisCache redisCache;
|
||||
@Resource
|
||||
private IModbusConfigCache modbusConfigCache;
|
||||
@Resource
|
||||
private ISubGatewayService subGatewayService;
|
||||
|
||||
@Override
|
||||
public DeviceReport decode(DeviceData deviceData, String clientId) {
|
||||
try {
|
||||
DeviceReport report = new DeviceReport();
|
||||
report.setProductId(deviceData.getProductId());
|
||||
report.setSerialNumber(deviceData.getSerialNumber());
|
||||
//原始报文字符串
|
||||
String hexDump = ByteBufUtil.hexDump(deviceData.getBuf());
|
||||
ModbusRtu message = messageDecoder.decode(deviceData);
|
||||
int slaveId = message.getSlaveId();
|
||||
//获取功能码
|
||||
int code = message.getCode();
|
||||
if (message.getmId() != 0) {
|
||||
report.setClientId(message.getMac());
|
||||
report.setMessageId(String.valueOf(message.getmId()));
|
||||
report.setSources(hexDump);
|
||||
return report;
|
||||
}
|
||||
matchSubDev(report, deviceData.getSerialNumber(), slaveId);
|
||||
String serialNumber = report.getSerialNumber();
|
||||
Long productId = report.getProductId();
|
||||
ModbusCode modbusCode = ModbusCode.getInstance(code);
|
||||
List<ThingsModelSimpleItem> values = new ArrayList<>();
|
||||
switch (modbusCode) {
|
||||
case Read01:
|
||||
case Read02:
|
||||
//起始地址
|
||||
String address0102 = getCacheModbusAddress(deviceData.getSerialNumber(),slaveId,code);
|
||||
//读线圈或读离散型寄存器处理
|
||||
handleRead0102(productId, message, address0102, values);
|
||||
break;
|
||||
case Read03:
|
||||
case Read04:
|
||||
//起始地址
|
||||
String address0304 = getCacheModbusAddress(deviceData.getSerialNumber(),slaveId,code);
|
||||
//读输入、保持寄存器
|
||||
handleRead0304(productId, message, address0304, values, hexDump);
|
||||
break;
|
||||
case Write06:
|
||||
case Write05:
|
||||
//如果返回06编码,说明是设备回复,更新对应寄存器的值,并发送通知前端
|
||||
report.setClientId(serialNumber);
|
||||
report.setSerialNumber(serialNumber);
|
||||
report.setProductId(productId);
|
||||
report.setIsReply(true);
|
||||
report.setSources(hexDump);
|
||||
report.setProtocolCode(FastBeeConstant.PROTOCOL.ModbusRtu);
|
||||
report.setStatus(FunctionReplyStatus.SUCCESS);
|
||||
this.handleMsgReply(message,report,modbusCode);
|
||||
return report;
|
||||
}
|
||||
|
||||
report.setSerialNumber(serialNumber);
|
||||
report.setClientId(serialNumber);
|
||||
report.setProductId(productId);
|
||||
report.setThingsModelSimpleItem(values);
|
||||
report.setSources(hexDump);
|
||||
return report;
|
||||
} catch (Exception e) {
|
||||
log.error("=>解码异常[{}]", e, e.getMessage());
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读线圈或读离散型寄存器处理
|
||||
*
|
||||
* @param productId 产品id
|
||||
* @param message 数据集合
|
||||
* @param address 起始地址
|
||||
* @param simpleItemList 返回的物模型值
|
||||
*/
|
||||
private void handleRead0102(Long productId, ModbusRtu message, String address, List<ThingsModelSimpleItem> simpleItemList) {
|
||||
//字节数
|
||||
int bitCount = message.getBitCount();
|
||||
//字节数据集合
|
||||
byte[] byteDatas = message.getBitData();
|
||||
String hexDump = ByteBufUtil.hexDump(byteDatas);
|
||||
//处理多个的情况
|
||||
Map<Integer, List<ModbusConfig>> modbusConfigMap = modbusConfigCache.getModbusConfigCacheByProductId(productId);
|
||||
if (CollectionUtils.isEmpty(modbusConfigMap)) {
|
||||
log.warn("寄存器地址:{},不存在", address);
|
||||
return;
|
||||
}
|
||||
List<ModbusConfig> modbusConfigList = new ArrayList<>();
|
||||
for (Map.Entry<Integer, List<ModbusConfig>> entry : modbusConfigMap.entrySet()) {
|
||||
modbusConfigList.addAll(entry.getValue());
|
||||
}
|
||||
Map<Integer, List<ModbusConfig>> listMap = modbusConfigList.stream().filter(config -> config.getType() == 1).collect(Collectors.groupingBy(ModbusConfig::getAddress));
|
||||
//分割值
|
||||
List<String> values = StringUtils.splitEvenly(hexDump, 2);
|
||||
int ioAd = Integer.parseInt(address);
|
||||
//匹配寄存器,一个字节有8个位需要处理
|
||||
for (int i = 0; i < bitCount * 8; i++) {
|
||||
List<ModbusConfig> configList = listMap.get(ioAd);
|
||||
if (!CollectionUtils.isEmpty(configList) && configList.size() == 1) {
|
||||
ModbusConfig modbusConfig = configList.get(0);
|
||||
String hex = values.get(i / 8);
|
||||
int result = BitUtils.deterHex(hex, i % 8);
|
||||
ThingsModelSimpleItem simpleItem = new ThingsModelSimpleItem();
|
||||
simpleItem.setId(modbusConfig.getIdentifier());
|
||||
simpleItem.setValue(result + "");
|
||||
simpleItem.setTs(DateUtils.getNowDate());
|
||||
simpleItemList.add(simpleItem);
|
||||
}
|
||||
ioAd += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理读保持寄存器/输入寄存器
|
||||
*
|
||||
* @param productId 产品id
|
||||
* @param message 数据
|
||||
* @param address 起始地址
|
||||
* @param simpleItemList 返回的物模型值
|
||||
* @param sourceStr 原始报文字符串
|
||||
*/
|
||||
private void handleRead0304(Long productId, ModbusRtu message, String address, List<ThingsModelSimpleItem> simpleItemList, String sourceStr) {
|
||||
short[] data = message.getData();
|
||||
int length = data.length;
|
||||
if (length == 1) {
|
||||
//单个寄存器上报情况处理
|
||||
List<ModbusConfig> modbusConfig = modbusConfigCache.getSingleModbusConfig(productId, address);
|
||||
if (CollectionUtils.isEmpty(modbusConfig)) {
|
||||
log.warn("寄存器地址:{},不存在", address);
|
||||
return;
|
||||
}
|
||||
Map<Integer, List<ModbusConfig>> listMap = modbusConfig.stream().collect(Collectors.groupingBy(ModbusConfig::getType));
|
||||
//处理IO类型
|
||||
List<ModbusConfig> IOConfigList = listMap.get(1);
|
||||
if (!CollectionUtils.isEmpty(IOConfigList)) {
|
||||
if (IOConfigList.size() > 1) {
|
||||
//03按位运行情况,读寄存器需要将16进制转换为2进制,按位取值
|
||||
// 如1-4个继电器开关情况,寄存器值是0x0007 从右到左,1-4 对应 on-on-on-off
|
||||
for (ModbusConfig config : IOConfigList) {
|
||||
int result = BitUtils.deter(data[0], config.getBitOrder());
|
||||
ThingsModelSimpleItem simpleItem = new ThingsModelSimpleItem();
|
||||
simpleItem.setId(config.getIdentifier());
|
||||
simpleItem.setValue(result + "");
|
||||
simpleItem.setTs(DateUtils.getNowDate());
|
||||
simpleItemList.add(simpleItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
//单个寄存器值
|
||||
List<ModbusConfig> dataConfigList = listMap.get(2);
|
||||
if (!CollectionUtils.isEmpty(dataConfigList)) {
|
||||
ModbusConfig config = dataConfigList.get(0);
|
||||
//普通取值,应该只有一个数据,将identity与address替换
|
||||
ThingsModelSimpleItem simpleItem = new ThingsModelSimpleItem();
|
||||
simpleItem.setId(config.getIdentifier());
|
||||
simpleItem.setValue(CaculateUtils.handleToUnSign16(data[0] + "", config.getDataType()));
|
||||
simpleItem.setTs(DateUtils.getNowDate());
|
||||
simpleItemList.add(simpleItem);
|
||||
}
|
||||
} else {
|
||||
//原数据字符串
|
||||
String dataHex = sourceStr.substring(6, sourceStr.length() - 4);
|
||||
//处理多个情况
|
||||
Map<Integer, List<ModbusConfig>> modbusConfigMap = modbusConfigCache.getModbusConfigCacheByProductId(productId);
|
||||
if (CollectionUtils.isEmpty(modbusConfigMap)) {
|
||||
log.warn("寄存器数据不存在");
|
||||
return;
|
||||
}
|
||||
List<ModbusConfig> modbusConfigList = new ArrayList<>();
|
||||
for (Map.Entry<Integer, List<ModbusConfig>> entry : modbusConfigMap.entrySet()) {
|
||||
modbusConfigList.addAll(entry.getValue());
|
||||
}
|
||||
Map<Integer, ModbusConfig> configMap = modbusConfigList.stream().filter(config -> config.getType() == 2).collect(Collectors.toMap(ModbusConfig::getAddress, Function.identity()));
|
||||
int registerAd = Integer.parseInt(address);
|
||||
for (int i = 0; i < length; i++) {
|
||||
ModbusConfig modbusConfig = configMap.get(registerAd);
|
||||
if (Objects.isNull(modbusConfig)) {
|
||||
//处理可能是 03按位运行情况,判断是否有寄存器,而且寄存器对应物模型多个
|
||||
List<ModbusConfig> dataToIoList = modbusConfigMap.get(registerAd);
|
||||
if (!CollectionUtils.isEmpty(dataToIoList) && dataToIoList.size() > 1) {
|
||||
for (ModbusConfig config : dataToIoList) {
|
||||
int result = BitUtils.deter(data[i], config.getBitOrder());
|
||||
ThingsModelSimpleItem simpleItem = new ThingsModelSimpleItem();
|
||||
simpleItem.setId(config.getIdentifier());
|
||||
simpleItem.setValue(result + "");
|
||||
simpleItem.setTs(DateUtils.getNowDate());
|
||||
simpleItemList.add(simpleItem);
|
||||
}
|
||||
} else {
|
||||
log.warn("寄存器地址:{},不存在", registerAd);
|
||||
}
|
||||
registerAd += 1;
|
||||
continue;
|
||||
}
|
||||
//个数
|
||||
Integer quantity = modbusConfig.getQuantity();
|
||||
ThingsModelSimpleItem simpleItem = new ThingsModelSimpleItem();
|
||||
simpleItem.setId(modbusConfig.getIdentifier());
|
||||
simpleItem.setTs(DateUtils.getNowDate());
|
||||
if (quantity == 1) {
|
||||
//将identity与address替换
|
||||
simpleItem.setValue(CaculateUtils.handleToUnSign16(data[i] + "", modbusConfig.getDataType()));
|
||||
simpleItemList.add(simpleItem);
|
||||
dataHex = dataHex.substring(4);
|
||||
} else if (quantity == 2) {
|
||||
//这里做数据处理
|
||||
String handleHex = dataHex.substring(0, 8);
|
||||
String value = CaculateUtils.parseValue(modbusConfig.getDataType(), handleHex);
|
||||
simpleItem.setValue(value);
|
||||
simpleItemList.add(simpleItem);
|
||||
//处理dataHex
|
||||
dataHex = dataHex.substring(8);
|
||||
//处理quantity ==2的情况,跳过一次循环
|
||||
i += 1;
|
||||
}
|
||||
//寄存器地址+1
|
||||
registerAd += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理下发指令设备回复消息,按照modbus协议约定会返回一样的指令
|
||||
* @param message 解析后数据
|
||||
*/
|
||||
private void handleMsgReply(ModbusRtu message,DeviceReport report,ModbusCode code){
|
||||
//单个寄存器上报情况处理
|
||||
String address = message.getAddress() + "";
|
||||
List<ModbusConfig> modbusConfig = modbusConfigCache.getSingleModbusConfig(report.getProductId(), address);
|
||||
if (CollectionUtils.isEmpty(modbusConfig)) {
|
||||
log.warn("寄存器地址:{},不存在", address);
|
||||
return;
|
||||
}
|
||||
Map<Integer, List<ModbusConfig>> listMap = modbusConfig.stream().collect(Collectors.groupingBy(ModbusConfig::getType));
|
||||
// 1是IO类型,2是寄存器类型
|
||||
int type = code.equals(ModbusCode.Write05) ? 1 : 2;
|
||||
List<ModbusConfig> modbusConfigList = listMap.get(type);
|
||||
ModbusConfig config = modbusConfigList.get(0);
|
||||
List<ThingsModelSimpleItem> values = new ArrayList<>();
|
||||
ThingsModelSimpleItem simpleItem = new ThingsModelSimpleItem();
|
||||
simpleItem.setId(config.getIdentifier());
|
||||
simpleItem.setTs(DateUtils.getNowDate());
|
||||
int data = message.getWriteData();
|
||||
if (type == 1){
|
||||
//对于IO类型的数据做一个数据转换 0x0000 是关 0xff00 是开 对应值 1
|
||||
if (data == 0xff00){
|
||||
data = 1;
|
||||
}
|
||||
}
|
||||
simpleItem.setValue(data +"");
|
||||
values.add(simpleItem);
|
||||
report.setThingsModelSimpleItem(values);
|
||||
}
|
||||
|
||||
/**
|
||||
* 匹配子设备编号
|
||||
*/
|
||||
private void matchSubDev(DeviceReport report, String serialNumber, Integer slaveId) {
|
||||
List<SubDeviceBo> subDeviceList = subGatewayService.getSubDeviceListByGw(serialNumber);
|
||||
if (!CollectionUtils.isEmpty(subDeviceList)) {
|
||||
for (SubDeviceBo vo : subDeviceList) {
|
||||
if (vo.getSlaveId().equals(slaveId)) {
|
||||
report.setSubDeviceBo(vo);
|
||||
report.setSerialNumber(vo.getSubDeviceNo());
|
||||
report.setProductId(vo.getSubProductId());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionCallBackBo encode(MQSendMessageBo message) {
|
||||
FunctionCallBackBo callBack = new FunctionCallBackBo();
|
||||
ModbusRtu rtu = new ModbusRtu();
|
||||
String thingsModel = message.getThingsModel();
|
||||
ThingsModelValueItem item = JSONObject.parseObject(thingsModel, ThingsModelValueItem.class);
|
||||
ModbusConfig config = item.getConfig();
|
||||
switch (config.getModbusCode()) {
|
||||
case Read01:
|
||||
case Read02:
|
||||
case Read03:
|
||||
case Read04:
|
||||
this.read03(config, rtu);
|
||||
break;
|
||||
case Write05:
|
||||
write05(config,message.getValue(), rtu);
|
||||
break;
|
||||
case Write06:
|
||||
write06(config, message.getValue(), rtu);
|
||||
break;
|
||||
}
|
||||
ByteBuf out = messageEncoder.encode(rtu);
|
||||
byte[] data = new byte[out.writerIndex()];
|
||||
out.readBytes(data);
|
||||
ReferenceCountUtil.release(out);
|
||||
byte[] result = CRC16Utils.AddCRC(data);
|
||||
callBack.setMessage(result);
|
||||
callBack.setSources(ByteBufUtil.hexDump(result));
|
||||
return callBack;
|
||||
}
|
||||
|
||||
/**
|
||||
* read03指令
|
||||
*/
|
||||
private void read03(ModbusConfig modbusConfig, ModbusRtu rtu) {
|
||||
rtu.setSlaveId(modbusConfig.getSlave());
|
||||
rtu.setCount(modbusConfig.getQuantity());
|
||||
rtu.setAddress(modbusConfig.getAddress());
|
||||
rtu.setCode(modbusConfig.getModbusCode().getCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* writ05/06指令配置
|
||||
*/
|
||||
private void write05(ModbusConfig modbusConfig, String value, ModbusRtu rtu) {
|
||||
int data;
|
||||
if (value.contains("0x")) {
|
||||
data = Integer.parseInt(value.substring(2), 16);
|
||||
} else {
|
||||
data = Integer.parseInt(value);
|
||||
}
|
||||
rtu.setWriteData(data == 1 ? 0xFF00 : 0x0000);
|
||||
rtu.setAddress(modbusConfig.getAddress());
|
||||
rtu.setCode(modbusConfig.getModbusCode().getCode());
|
||||
rtu.setSlaveId(modbusConfig.getSlave());
|
||||
}
|
||||
|
||||
/**
|
||||
* writ05/06指令配置
|
||||
*/
|
||||
private void write06(ModbusConfig modbusConfig, String value, ModbusRtu rtu) {
|
||||
rtu.setWriteData(Integer.parseInt(value));
|
||||
rtu.setAddress(modbusConfig.getAddress());
|
||||
rtu.setCode(modbusConfig.getModbusCode().getCode());
|
||||
rtu.setSlaveId(modbusConfig.getSlave());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取modbus地址
|
||||
*/
|
||||
private String getCacheModbusAddress(String serialNumber,int salveId,int code) {
|
||||
String key = RedisKeyBuilder.buildModbusRuntimeCacheKey(serialNumber);
|
||||
Set<String> commandSet = redisCache.zRange(key, 0, -1);
|
||||
if (commandSet.size() == 0) {
|
||||
throw new ServiceException("No cache modbus address found");
|
||||
}
|
||||
for (String command : commandSet) {
|
||||
Mparams params = ModbusUtils.getModbusParams(command);
|
||||
redisCache.zRem(key, command);
|
||||
if (params.getSlaveId() == salveId && params.getCode() == code){
|
||||
return params.getAddress()+"";
|
||||
}
|
||||
}
|
||||
throw new ServiceException("No cache modbus address found");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,252 @@
|
||||
package com.fastbee.modbus.model;
|
||||
|
||||
import com.fastbee.protocol.base.annotation.Column;
|
||||
import com.fastbee.common.core.protocol.modbus.ModbusCode;
|
||||
import com.fastbee.protocol.util.ToStringBuilder;
|
||||
import com.fastbee.common.core.protocol.Message;
|
||||
import com.fastbee.base.session.Session;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.beans.Transient;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* modbus采集方式一: 云端轮询方式,报文为标准ModbusRtu
|
||||
*
|
||||
* @author bill
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
public class ModbusRtu extends Message {
|
||||
/**
|
||||
* version来决定数据
|
||||
* <p>
|
||||
* 0: 读寄存器03/04(批量)上报 {@link ModbusCode}
|
||||
* 例如: |从机地址|功能码|返回字节个数|寄存器40005数据|寄存器40006数据|CRC校验|
|
||||
* |01|03|04|00 00|00 00|21 33|
|
||||
* <p>
|
||||
* 1: 批量读寄存器 {@link ModbusCode}
|
||||
* 例如: |从机地址|功能码|寄存器起始地址|读取寄存器个数|CRC校验|
|
||||
* |01|03|00 04|00 02|85 ca|
|
||||
* <p>
|
||||
* 2: 单个写保持寄存器 0x06 {@link ModbusCode}
|
||||
* 写单个线圈 0x05 {@link ModbusCode}
|
||||
* 例如:
|
||||
* |0x01|06|00 01|00 17|98 04|
|
||||
* |0x01|05|00 01|00 1|98 04|
|
||||
* <p>
|
||||
* 4: 解析读线圈 01/02
|
||||
* <p>
|
||||
* 15: 写多个线圈
|
||||
* <p>
|
||||
* 16: 写多个寄存器
|
||||
* <p>
|
||||
*/
|
||||
@Column(length = 1, version = {0x80, 0x81}, desc = "起始地址")
|
||||
protected int start;
|
||||
@Column(length = 1, version = {0x80, 0x81}, desc = "标识位,实例设备是0x80")
|
||||
protected int mId;
|
||||
@Column(length = 6, version = 0x80, desc = "MAC地址,6个字节")
|
||||
protected String mac;
|
||||
@Column(length = 1, version = {0x80, 0x81}, desc = "结尾,如:7E")
|
||||
protected int end;
|
||||
|
||||
|
||||
|
||||
@Column(length = 1, version = {0, 1, 2,4,15,16}, desc = "从机地址")
|
||||
protected int slaveId;
|
||||
@Column(length = 1, version = {0,1,2,4,15,16}, desc = "功能码")
|
||||
protected int code;
|
||||
@Column(length = 2, desc = "寄存器地址", version = {1,2})
|
||||
protected int address;
|
||||
@Column(length = 2, desc = "寄存器个数", version = {1,15,16})
|
||||
protected int count;
|
||||
@Column(totalUnit = 1, desc = "上报数据", version = 0)
|
||||
protected short[] data;
|
||||
@Column(length = 2, desc = "下发数据", version = 2)
|
||||
protected int writeData;
|
||||
@Column(length = 2 ,version = {15,16} , desc = "寄存器数量")
|
||||
protected int byteCount;
|
||||
@Column(length = 1 ,version = {4,15,16} , desc = "字节数")
|
||||
protected int bitCount;
|
||||
@Column(totalUnit = 0,desc = "多个寄存器",version = 16)
|
||||
protected short[] tenWriteData;
|
||||
@Column(totalUnit = 0,desc = "多个线圈",version = {4,15})
|
||||
protected byte[] bitData;
|
||||
/*原始的bit字符串*/
|
||||
protected String bitString;
|
||||
|
||||
protected transient Session session;
|
||||
|
||||
protected transient ByteBuf payload;
|
||||
|
||||
protected transient String hex;
|
||||
|
||||
public int getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public void setStart(int start) {
|
||||
this.start = start;
|
||||
}
|
||||
|
||||
public int getmId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
public void setmId(int mId) {
|
||||
this.mId = mId;
|
||||
}
|
||||
|
||||
public String getMac() {
|
||||
return mac;
|
||||
}
|
||||
|
||||
public void setMac(String mac) {
|
||||
this.mac = mac;
|
||||
}
|
||||
|
||||
public int getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
public void setEnd(int end) {
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
public int getSlaveId() {
|
||||
return slaveId;
|
||||
}
|
||||
|
||||
public void setSlaveId(int slaveId) {
|
||||
this.slaveId = slaveId;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public int getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(int address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public void setCount(int count) {
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public short[] getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(short[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public int getWriteData() {
|
||||
return writeData;
|
||||
}
|
||||
|
||||
public void setWriteData(int writeData) {
|
||||
this.writeData = writeData;
|
||||
}
|
||||
|
||||
public int getByteCount() {
|
||||
return byteCount;
|
||||
}
|
||||
|
||||
public void setByteCount(int byteCount) {
|
||||
this.byteCount = byteCount;
|
||||
}
|
||||
|
||||
public int getBitCount() {
|
||||
return bitCount;
|
||||
}
|
||||
|
||||
public void setBitCount(int bitCount) {
|
||||
this.bitCount = bitCount;
|
||||
}
|
||||
|
||||
public short[] getTenWriteData() {
|
||||
return tenWriteData;
|
||||
}
|
||||
|
||||
public void setTenWriteData(short[] tenWriteData) {
|
||||
this.tenWriteData = tenWriteData;
|
||||
}
|
||||
|
||||
public byte[] getBitData() {
|
||||
return bitData;
|
||||
}
|
||||
|
||||
public void setBitData(byte[] bitData) {
|
||||
this.bitData = bitData;
|
||||
}
|
||||
|
||||
public String getBitString() {
|
||||
return bitString;
|
||||
}
|
||||
|
||||
public void setBitString(String bitString) {
|
||||
this.bitString = bitString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuf getPayload() {
|
||||
return payload;
|
||||
}
|
||||
|
||||
public String getHex() {
|
||||
return hex;
|
||||
}
|
||||
|
||||
public void setHex(String hex) {
|
||||
this.hex = hex;
|
||||
}
|
||||
|
||||
@Transient
|
||||
public Session getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
public void setSession(Session session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public void setPayload(ByteBuf payload) {
|
||||
this.hex = ByteBufUtil.hexDump(payload);
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
protected StringBuilder toStringHead() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
String des = ModbusCode.getDes(this.code);
|
||||
sb.append(des).append(": ");
|
||||
sb.append(";[从机地址]: ").append(slaveId);
|
||||
sb.append(";[功能码]: ").append(code);
|
||||
sb.append(";[返回数据个数]: ").append(data == null ? 0 : data.length);
|
||||
sb.append(";[上报数据]: ").append(Arrays.toString(data));
|
||||
sb.append(";[读取寄存器个数]: ").append(count);
|
||||
sb.append(";[寄存器地址]: ").append(address);
|
||||
return sb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.toString(toStringHead(), this, true);
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package com.fastbee.modbus.pak;
|
||||
|
||||
|
||||
import com.fastbee.common.core.mq.DeviceReport;
|
||||
import com.fastbee.base.core.annotation.Node;
|
||||
import com.fastbee.base.core.annotation.PakMapping;
|
||||
import com.fastbee.base.model.DeviceMsg;
|
||||
import com.fastbee.base.model.SessionKey;
|
||||
import com.fastbee.base.session.Session;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import static com.fastbee.modbus.pak.TcpDtu.心跳包;
|
||||
import static com.fastbee.modbus.pak.TcpDtu.注册报文;
|
||||
|
||||
/**
|
||||
* 报文消息映射处理
|
||||
* @author gsb
|
||||
* @date 2022/11/25 14:08
|
||||
*/
|
||||
@Node
|
||||
@Component
|
||||
@Slf4j
|
||||
public class ModbusEndPoint {
|
||||
|
||||
@PakMapping(types = 注册报文)
|
||||
public DeviceReport register(DeviceReport message, Session session){
|
||||
//注册设备
|
||||
session.register(message);
|
||||
//记录设备信息
|
||||
DeviceMsg deviceMsg = new DeviceMsg();
|
||||
deviceMsg.setClientId(message.getClientId());
|
||||
session.setAttribute(SessionKey.DeviceMsg,deviceMsg);
|
||||
String hex = "FE8001FE";
|
||||
message.setBody(hex);
|
||||
return message;
|
||||
}
|
||||
|
||||
@PakMapping(types = 心跳包)
|
||||
public void heartbeat(DeviceReport message, Session session){
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.fastbee.modbus.pak;
|
||||
|
||||
/**
|
||||
* TCP-Dtu协议
|
||||
* @author gsb
|
||||
* @date 2022/11/25 14:12
|
||||
*/
|
||||
public interface TcpDtu {
|
||||
|
||||
int 注册报文 = 0x80;
|
||||
int 心跳包 = 0x81;
|
||||
int 起始位 = 0x7e;
|
||||
int 整包消息 = 0x00;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
package com.fastbee.modbus.test;
|
||||
|
||||
import com.fastbee.common.core.mq.message.DeviceData;
|
||||
import com.fastbee.common.core.protocol.modbus.ModbusCode;
|
||||
import com.fastbee.common.utils.CaculateUtils;
|
||||
import com.fastbee.common.utils.gateway.protocol.ByteUtils;
|
||||
import com.fastbee.modbus.codec.ModbusDecoder;
|
||||
import com.fastbee.modbus.codec.ModbusEncoder;
|
||||
import com.fastbee.modbus.model.ModbusRtu;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 协议解析本地测试
|
||||
* @author bill
|
||||
*/
|
||||
@Slf4j
|
||||
public class ModbusTest {
|
||||
|
||||
private static ModbusDecoder decoder = new ModbusDecoder("com.fastbee.modbus");
|
||||
private static ModbusEncoder encoder = new ModbusEncoder("com.fastbee.modbus");
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
/*单个03寄存器数据解析*/
|
||||
String hex = "0103020016398a";
|
||||
ByteBuf buf = Unpooled.wrappedBuffer(ByteBufUtil.decodeHexDump(hex));
|
||||
DeviceData data = DeviceData.builder()
|
||||
.buf(buf)
|
||||
.code(ModbusCode.Read03)
|
||||
.build();
|
||||
ModbusRtu message = decoder.decode(data);
|
||||
System.out.println("单个解析="+message);
|
||||
buf.release();
|
||||
/*批量读返回 03数据解析*/
|
||||
String hexMore = "01031000000000000000000000000000000000E459";
|
||||
ByteBuf bufMore = Unpooled.wrappedBuffer(ByteBufUtil.decodeHexDump(hexMore));
|
||||
DeviceData dataMore = DeviceData.builder()
|
||||
.buf(bufMore).build();
|
||||
ModbusRtu messageMore = decoder.decode(dataMore);
|
||||
System.out.println("批量上传报文解析="+messageMore);
|
||||
bufMore.release();
|
||||
|
||||
/*01、02解析*/
|
||||
String hex01 = "110105CD6BB20E1B45E6";
|
||||
ByteBuf buf01 = Unpooled.wrappedBuffer(ByteBufUtil.decodeHexDump(hex01));
|
||||
DeviceData data01 = DeviceData.builder()
|
||||
.buf(buf01)
|
||||
.code(ModbusCode.Read01)
|
||||
.build();
|
||||
ModbusRtu message01 = decoder.decode(data01);
|
||||
log.info("01、02解析=> 字节数:[{}],hex值:[{}]",message01.getBitCount(),ByteBufUtil.hexDump(message01.getBitData()));
|
||||
buf01.release();
|
||||
|
||||
|
||||
ModbusRtu read03 = new ModbusRtu();
|
||||
read03.setSlaveId(1);
|
||||
read03.setAddress(1);
|
||||
read03.setCode(3);
|
||||
read03.setCount(3);
|
||||
ByteBuf out = encoder.encode(read03);
|
||||
System.out.println(ByteBufUtil.hexDump(out));
|
||||
|
||||
ModbusRtu write06 = new ModbusRtu();
|
||||
write06.setSlaveId(1);
|
||||
write06.setAddress(1);
|
||||
write06.setCode(6);
|
||||
write06.setWriteData(17);
|
||||
ByteBuf writeOut = encoder.encode(write06);
|
||||
System.out.println(ByteBufUtil.hexDump(writeOut));
|
||||
|
||||
|
||||
String s = Integer.toHexString(Integer.parseInt("16878"));
|
||||
String s1 = Integer.toHexString(16878);
|
||||
System.out.println(s);
|
||||
String hexToBytes = "41EE8000";
|
||||
byte[] bytes = ByteBufUtil.decodeHexDump(hexToBytes);
|
||||
String v = CaculateUtils.toFloat(bytes);
|
||||
System.out.println(v);
|
||||
|
||||
System.out.println(ByteUtils.intToHex(-32768));
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package com.fastbee.modbusToJson;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2023/8/14 17:24
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class FYModel {
|
||||
|
||||
/**
|
||||
* 采集点标识符
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 数量
|
||||
*/
|
||||
private Integer quality;
|
||||
/**
|
||||
* 数值
|
||||
*/
|
||||
private String value;
|
||||
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
package com.fastbee.modbusToJson;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fastbee.common.annotation.SysProtocol;
|
||||
import com.fastbee.common.constant.FastBeeConstant;
|
||||
import com.fastbee.common.core.mq.DeviceReport;
|
||||
import com.fastbee.common.core.mq.MQSendMessageBo;
|
||||
import com.fastbee.common.core.mq.message.DeviceData;
|
||||
import com.fastbee.common.core.mq.message.FunctionCallBackBo;
|
||||
import com.fastbee.common.core.thingsModel.NeuronModel;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelValuesInput;
|
||||
import com.fastbee.common.exception.ServiceException;
|
||||
import com.fastbee.common.utils.DateUtils;
|
||||
import com.fastbee.protocol.base.protocol.IProtocol;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2022/10/10 16:55
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@SysProtocol(name = "Modbus转Json解析协议",protocolCode = FastBeeConstant.PROTOCOL.ModbusToJson,description = "modbus转json解析协议")
|
||||
public class ModbusToJsonProtocolService implements IProtocol {
|
||||
|
||||
/**
|
||||
* 解析json格式数据
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public DeviceReport decode(DeviceData deviceData, String clientId) {
|
||||
try {
|
||||
DeviceReport reportMessage = new DeviceReport();
|
||||
String data = new String(deviceData.getData(),StandardCharsets.UTF_8);
|
||||
List<ThingsModelSimpleItem> result = new ArrayList<>();
|
||||
NeuronModel model = JSON.parseObject(data, NeuronModel.class);
|
||||
JSONObject values = model.getValues();
|
||||
for (Map.Entry<String, Object> entry : values.entrySet()) {
|
||||
ThingsModelSimpleItem item = new ThingsModelSimpleItem();
|
||||
item.setTs(DateUtils.getNowDate());
|
||||
item.setValue(entry.getValue()+"");
|
||||
item.setId(entry.getKey());
|
||||
result.add(item);
|
||||
}
|
||||
reportMessage.setThingsModelSimpleItem(result);
|
||||
reportMessage.setClientId(clientId);
|
||||
reportMessage.setSerialNumber(clientId);
|
||||
reportMessage.setSources(data);
|
||||
return reportMessage;
|
||||
}catch (Exception e){
|
||||
throw new ServiceException("数据解析异常"+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionCallBackBo encode(MQSendMessageBo message) {
|
||||
try {
|
||||
FunctionCallBackBo callBack = new FunctionCallBackBo();
|
||||
String msg = JSONObject.toJSONString(message.getParams());
|
||||
callBack.setSources(msg);
|
||||
callBack.setMessage(msg.getBytes());
|
||||
return callBack;
|
||||
}catch (Exception e){
|
||||
log.error("=>指令编码异常,device={},data={}",message.getSerialNumber(),
|
||||
message.getParams());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
|
||||
****.约定报文格式如下*****
|
||||
modbus-rtu报文示例:
|
||||
1. 上报: 010302030578B7
|
||||
01 03 02 0305 78B7
|
||||
设备地址 命令号 返回数据字节数 数据 CRC校验
|
||||
|
||||
2. 下发: 0106010100151839
|
||||
01 06 0101 0015 1839
|
||||
设备地址 命令号 寄存器地址 数据位 CRC校验
|
||||
|
||||
1.设备主动上报数据组成:
|
||||
上报的指令数据
|
||||
FFAA0001010302030578B7
|
||||
FFAA 0001 010302030578B7
|
||||
包头 起始寄存器 数据包
|
||||
|
||||
2.服务下发数据组成
|
||||
下发的指令数据
|
||||
FFDDa690351586788884480106010100151839
|
||||
FFDD a69035158678888448 0106010100151839
|
||||
固定报文头 9字节消息ID 数据包
|
||||
|
||||
3.设备应答服务下发数据组成
|
||||
下发的指令数据
|
||||
FFDDa690351586788884480106010100151839
|
||||
FFDD a69035158678888448 0106010100151839
|
||||
固定报文头 9字节消息ID 数据包
|
@ -0,0 +1,87 @@
|
||||
package com.fastbee.pakModbus.codec;
|
||||
|
||||
import com.fastbee.common.exception.ServiceException;
|
||||
import com.fastbee.common.utils.gateway.CRC16Utils;
|
||||
import com.fastbee.pakModbus.model.PakModbusRtu;
|
||||
import com.fastbee.protocol.WModelManager;
|
||||
import com.fastbee.protocol.base.model.ActiveModel;
|
||||
import com.fastbee.protocol.util.ArrayMap;
|
||||
import com.fastbee.protocol.util.ExplainUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
||||
/**
|
||||
* modbus-rtu协议解码器
|
||||
* @author bill
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@NoArgsConstructor
|
||||
public class ModbusRtuPakDecoder {
|
||||
|
||||
@Autowired
|
||||
private WModelManager modelManager;
|
||||
private ArrayMap<ActiveModel> headerSchemaMap;
|
||||
|
||||
public ModbusRtuPakDecoder(String...basePackages) {
|
||||
this.modelManager = new WModelManager(basePackages);
|
||||
this.headerSchemaMap = this.modelManager.getActiveMap(PakModbusRtu.class);
|
||||
}
|
||||
|
||||
public PakModbusRtu decode(ByteBuf in){
|
||||
return decode(in,null);
|
||||
}
|
||||
|
||||
public PakModbusRtu decode(ByteBuf in, ExplainUtils explain){
|
||||
this.build();
|
||||
ByteBuf copy = in.duplicate();
|
||||
byte[] bytes = new byte[in.writerIndex()];
|
||||
copy.readBytes(bytes);
|
||||
verify(bytes);
|
||||
ActiveModel<PakModbusRtu> activeModel = headerSchemaMap.get(0);
|
||||
PakModbusRtu message = new PakModbusRtu();
|
||||
message.setPayload(in);
|
||||
message.setVerified(false);
|
||||
activeModel.mergeFrom(in,message,explain);
|
||||
log.info("=>解析:[{}]",message);
|
||||
return message;
|
||||
}
|
||||
|
||||
public PakModbusRtu decodeMessage(ByteBuf in, int version){
|
||||
this.build();
|
||||
ByteBuf copy = in.duplicate();
|
||||
byte[] bytes = new byte[in.writerIndex()];
|
||||
copy.readBytes(bytes);
|
||||
ActiveModel<PakModbusRtu> activeModel = headerSchemaMap.get(version);
|
||||
PakModbusRtu message = new PakModbusRtu();
|
||||
message.setPayload(in);
|
||||
message.setVerified(false);
|
||||
activeModel.mergeFrom(in,message,null);
|
||||
log.info("=>解析:[{}]",message);
|
||||
return message;
|
||||
}
|
||||
|
||||
private void verify(byte[] source){
|
||||
byte[] checkBytes = {source[source.length - 3],source[source.length -2]};
|
||||
byte[] sourceCheck = ArrayUtils.subarray(source,4,source.length -3);
|
||||
String crc = CRC16Utils.getCRC(sourceCheck);
|
||||
if (!crc.equalsIgnoreCase(ByteBufUtil.hexDump(checkBytes))){
|
||||
log.warn("=>CRC校验异常,报文={}",ByteBufUtil.hexDump(source));
|
||||
throw new ServiceException("CRC校验异常");
|
||||
}
|
||||
}
|
||||
|
||||
private void build(){
|
||||
if (this.headerSchemaMap == null) {
|
||||
this.headerSchemaMap = this.modelManager.getActiveMap(PakModbusRtu.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package com.fastbee.pakModbus.codec;
|
||||
|
||||
import com.fastbee.common.core.protocol.modbus.ModbusCode;
|
||||
import com.fastbee.pakModbus.model.PakModbusRtu;
|
||||
import com.fastbee.protocol.WModelManager;
|
||||
import com.fastbee.protocol.base.model.ActiveModel;
|
||||
import com.fastbee.protocol.util.ArrayMap;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.buffer.PooledByteBufAllocator;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2022/11/15 11:34
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class ModbusRtuPakEncoder {
|
||||
|
||||
private static final ByteBufAllocator ALLOC = PooledByteBufAllocator.DEFAULT;
|
||||
|
||||
@Autowired
|
||||
private WModelManager modelManager;
|
||||
private ArrayMap<ActiveModel> headerSchemaMap;
|
||||
|
||||
public ModbusRtuPakEncoder(String...basePackages) {
|
||||
this.modelManager = new WModelManager(basePackages);
|
||||
this.headerSchemaMap = this.modelManager.getActiveMap(PakModbusRtu.class);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 组装下发指令
|
||||
* @param pakModbusRtu
|
||||
* @return
|
||||
*/
|
||||
public ByteBuf encode(PakModbusRtu pakModbusRtu){
|
||||
this.build();
|
||||
ByteBuf buf = ALLOC.buffer();
|
||||
// 下发读
|
||||
int version = 1;
|
||||
if (pakModbusRtu.getCode() == ModbusCode.Write05.getCode() || pakModbusRtu.getCode() == ModbusCode.Write06.getCode()) {
|
||||
// 下发写单个
|
||||
version = 2;
|
||||
} else if (pakModbusRtu.getCode() == ModbusCode.Write10.getCode()) {
|
||||
// 下发写多个
|
||||
version = 3;
|
||||
}else if (pakModbusRtu.getCode() == ModbusCode.Write0F.getCode()){
|
||||
version = 5;
|
||||
}
|
||||
ActiveModel activeModel = headerSchemaMap.get(version);
|
||||
activeModel.writeTo(buf, pakModbusRtu, null);
|
||||
return buf;
|
||||
}
|
||||
|
||||
private void build(){
|
||||
if (this.headerSchemaMap == null) {
|
||||
this.headerSchemaMap = this.modelManager.getActiveMap(PakModbusRtu.class);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,187 @@
|
||||
package com.fastbee.pakModbus.codec;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fastbee.common.annotation.SysProtocol;
|
||||
import com.fastbee.common.constant.FastBeeConstant;
|
||||
import com.fastbee.common.core.mq.DeviceReport;
|
||||
import com.fastbee.common.core.mq.MQSendMessageBo;
|
||||
import com.fastbee.common.core.mq.message.DeviceData;
|
||||
import com.fastbee.common.core.mq.message.DeviceDownMessage;
|
||||
import com.fastbee.common.core.mq.message.FunctionCallBackBo;
|
||||
import com.fastbee.common.core.protocol.modbus.ModbusCode;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelValuesInput;
|
||||
import com.fastbee.common.utils.DateUtils;
|
||||
import com.fastbee.common.utils.gateway.CRC16Utils;
|
||||
import com.fastbee.common.utils.modbus.BitUtils;
|
||||
import com.fastbee.iot.cache.IModbusConfigCache;
|
||||
import com.fastbee.iot.domain.ModbusConfig;
|
||||
import com.fastbee.iot.model.ThingsModels.ThingsModelValueItem;
|
||||
import com.fastbee.pakModbus.model.PakModbusRtu;
|
||||
import com.fastbee.protocol.base.protocol.IProtocol;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 包装过的modbus-rtu协议
|
||||
*
|
||||
* @author gsb
|
||||
* @date 2022/11/15 11:16
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@SysProtocol(name = "ModbusRtu扩展自定义协议", protocolCode = FastBeeConstant.PROTOCOL.ModbusRtuPak, description = "ModbusRtu扩展自定义协议")
|
||||
public class ModbusRtuPakProtocol implements IProtocol {
|
||||
|
||||
/**
|
||||
* 1.约定报文格式如下
|
||||
* 1.设备主动上报数据组成:
|
||||
* * 上报的指令数据
|
||||
* * FFAA 0001 010302030578B7
|
||||
* * FFAA 0001 010302030578B7
|
||||
* * 包头 起始寄存器 数据包
|
||||
* <p>
|
||||
* * 数据包
|
||||
* * 01 03 02 0305 78B7
|
||||
* * 设备地址 命令号 返回数据字节数 数据 CRC校验
|
||||
* <p>
|
||||
* 2.服务下发数据组成
|
||||
* * 下发的指令数据
|
||||
* * FFDD a69035158678888448 01 06 0101 0015 1839
|
||||
* * FFDD a69035158678888448 0106010100151839
|
||||
* * 固定报文头 9字节消息ID 数据包
|
||||
* </p>
|
||||
* * 数据包
|
||||
* * 01 06 0101 0015 1839
|
||||
* * 设备地址 命令号 寄存器地址 数据位 CRC校验
|
||||
* <p>
|
||||
* 3.设备应答服务下发数据组成
|
||||
* * 下发的指令数据
|
||||
* * FFDD a69035158678888448 01 06 0101 0015 1839
|
||||
* * FFDD a69035158678888448 0106010100151839
|
||||
* * 固定报文头 9字节消息ID 数据包
|
||||
* <p>
|
||||
* * 数据包
|
||||
* * 01 06 0101 0015 1839
|
||||
* * 设备地址 命令号 寄存器地址 数据位 CRC校验
|
||||
*/
|
||||
|
||||
private final static String FFDD = "ffdd";
|
||||
|
||||
@Resource
|
||||
private ModbusRtuPakDecoder rtuPakDecoder;
|
||||
@Resource
|
||||
private ModbusRtuPakEncoder rtuPakEncoder;
|
||||
@Resource
|
||||
private IModbusConfigCache modbusConfigCache;
|
||||
|
||||
@Override
|
||||
public DeviceReport decode(DeviceData deviceData, String clientId) {
|
||||
DeviceReport report = new DeviceReport();
|
||||
ByteBuf buf = deviceData.getBuf();
|
||||
String hexDump = ByteBufUtil.hexDump(buf);
|
||||
if (hexDump.startsWith(FFDD)){
|
||||
report.setIsReply(true);
|
||||
report.setMessageId(hexDump.substring(4,22));
|
||||
report.setClientId(clientId);
|
||||
report.setProtocolCode(FastBeeConstant.PROTOCOL.ModbusRtuPak);
|
||||
return report;
|
||||
}
|
||||
PakModbusRtu message = rtuPakDecoder.decode(buf);
|
||||
List<ThingsModelSimpleItem> values = new ArrayList<>();
|
||||
short[] data = message.getData();
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
int address = message.getAddress() + i;
|
||||
handleModbusDataType(deviceData.getProductId(),address+"",data[i]+"",values);
|
||||
}
|
||||
report.setClientId(clientId);
|
||||
report.setThingsModelSimpleItem(values);
|
||||
report.setSources(hexDump);
|
||||
return report;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理不同数据
|
||||
* @param productId
|
||||
* @param values
|
||||
*/
|
||||
private void handleModbusDataType(Long productId,String address,String value,List<ThingsModelSimpleItem> values){
|
||||
|
||||
int nValue = Integer.parseInt(value);
|
||||
List<ModbusConfig> modbusConfig = modbusConfigCache.getSingleModbusConfig(productId, address);
|
||||
if (CollectionUtils.isEmpty(modbusConfig)){
|
||||
log.warn("寄存器地址:{},不存在",address); return;
|
||||
}
|
||||
Map<Integer, List<ModbusConfig>> listMap = modbusConfig.stream().collect(Collectors.groupingBy(ModbusConfig::getType));
|
||||
//处理IO类型
|
||||
List<ModbusConfig> IOConfigList = listMap.get(1);
|
||||
if (!CollectionUtils.isEmpty(IOConfigList)){
|
||||
if (IOConfigList.size() > 1){
|
||||
//按位运行情况,需要将16进制转换为2进制,按位取值
|
||||
for (ModbusConfig config : IOConfigList) {
|
||||
int result = BitUtils.deter(nValue, config.getBitOrder());
|
||||
ThingsModelSimpleItem simpleItem = new ThingsModelSimpleItem();
|
||||
simpleItem.setId(config.getIdentifier());
|
||||
//simpleItem.setSlaveId(config.getSlave());
|
||||
simpleItem.setValue(result+"");
|
||||
simpleItem.setTs(DateUtils.getNowDate());
|
||||
values.add(simpleItem);
|
||||
}
|
||||
}else {
|
||||
//普通IO取值,应该只有一个数据,将identity与address替换
|
||||
ThingsModelSimpleItem simpleItem = new ThingsModelSimpleItem();
|
||||
simpleItem.setId(IOConfigList.get(0).getIdentifier());
|
||||
//simpleItem.setSlaveId(IOConfigList.get(0).getSlave());
|
||||
simpleItem.setValue(value);
|
||||
simpleItem.setTs(DateUtils.getNowDate());
|
||||
values.add(simpleItem);
|
||||
}
|
||||
}
|
||||
List<ModbusConfig> dataConfigList = listMap.get(2);
|
||||
if (!CollectionUtils.isEmpty(dataConfigList)){
|
||||
//普通取值,应该只有一个数据,将identity与address替换
|
||||
ThingsModelSimpleItem simpleItem = new ThingsModelSimpleItem();
|
||||
simpleItem.setId(dataConfigList.get(0).getIdentifier());
|
||||
//simpleItem.setSlaveId(dataConfigList.get(0).getSlave());
|
||||
simpleItem.setValue(value);
|
||||
simpleItem.setTs(DateUtils.getNowDate());
|
||||
values.add(simpleItem);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public FunctionCallBackBo encode(MQSendMessageBo bo) {
|
||||
FunctionCallBackBo callBack = new FunctionCallBackBo();
|
||||
String thingsModel = bo.getThingsModel();
|
||||
ThingsModelValueItem item = JSONObject.parseObject(thingsModel, ThingsModelValueItem.class);
|
||||
ModbusConfig modbusConfig = item.getConfig();
|
||||
String value = bo.getValue();
|
||||
PakModbusRtu modbusRtu = new PakModbusRtu();
|
||||
modbusRtu.setMessageId(bo.getMessageId());
|
||||
modbusRtu.setSlaveId(modbusConfig.getSlave());
|
||||
modbusRtu.setCode(modbusConfig.getModbusCode().getCode());
|
||||
modbusRtu.setDownAdd(modbusConfig.getAddress());
|
||||
modbusRtu.setWriteData(Integer.parseInt(value));
|
||||
ByteBuf out = rtuPakEncoder.encode(modbusRtu);
|
||||
byte[] result = new byte[out.writerIndex()];
|
||||
out.readBytes(result);
|
||||
ReferenceCountUtil.release(out);
|
||||
callBack.setMessage(result);
|
||||
callBack.setSources(ByteBufUtil.hexDump(result));
|
||||
return callBack;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
package com.fastbee.pakModbus.model;
|
||||
|
||||
|
||||
import com.fastbee.protocol.util.ByteToHexUtil;
|
||||
import com.fastbee.protocol.util.IntegerToByteUtil;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* bit位数据组装
|
||||
*
|
||||
* @author gsb
|
||||
* @date 2022/5/24 11:26
|
||||
*/
|
||||
|
||||
public class CombineFactory {
|
||||
|
||||
/**
|
||||
* 是否是位值修改的物模型 0-单值single_value 1-多选multiple_bit 2-单选single_bit 3-多值multiple_value,4-单选值 single_select_value
|
||||
*
|
||||
* @param type MCU数据类型
|
||||
* @param map position:value
|
||||
* @param oldValue 最新记录的数据
|
||||
* @return 结果
|
||||
*/
|
||||
public static byte[] toBytes(int type, Map<Integer, Integer> map, Object oldValue) {
|
||||
switch (type) {
|
||||
case 1:
|
||||
return toOldBitBytes(map, oldValue);
|
||||
case 2:
|
||||
return toBitBytes(map);
|
||||
case 3:
|
||||
return toByte2Bytes(map);
|
||||
default:
|
||||
return toValueBytes(map);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 1-多选multiple_bit
|
||||
*
|
||||
* @param map 数据map
|
||||
* @param oldValue 原始数据值
|
||||
* @return 结果
|
||||
*/
|
||||
private static byte[] toOldBitBytes(Map<Integer, Integer> map, Object oldValue) {
|
||||
oldValue = oldValue == null ? 0 : oldValue;
|
||||
String strV = String.join("", oldValue.toString().split(","));
|
||||
int data = IntegerToByteUtil.binaryToInt(strV);
|
||||
int result = IntegerToByteUtil.bitOperationBatch(data, map);
|
||||
return IntegerToByteUtil.intToByteArr(result, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 多值multiple_value
|
||||
*
|
||||
* @param map 数据
|
||||
* @return 结果
|
||||
*/
|
||||
private static byte[] toByte2Bytes(Map<Integer, Integer> map) {
|
||||
byte[] result = new byte[2];
|
||||
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
|
||||
if (entry.getKey() ==0){
|
||||
result[1] = (byte)entry.getValue().intValue();
|
||||
}
|
||||
if (entry.getKey() ==1){
|
||||
result[0] = (byte)entry.getValue().intValue();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 2-单选single_bit
|
||||
*
|
||||
* @param map 数据值
|
||||
* @return 结果
|
||||
*/
|
||||
private static byte[] toBitBytes(Map<Integer, Integer> map) {
|
||||
if (!CollectionUtils.isEmpty(map)) {
|
||||
int result = IntegerToByteUtil.bitOperationBatch(0, map);
|
||||
return IntegerToByteUtil.intToByteArr(result, 2);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 0-单值single_value
|
||||
*
|
||||
* @param map 数据
|
||||
* @return 结果
|
||||
*/
|
||||
private static byte[] toValueBytes(Map<Integer, Integer> map) {
|
||||
if (!CollectionUtils.isEmpty(map) && map.size() == 1) {
|
||||
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
|
||||
return IntegerToByteUtil.intToBytes2(entry.getValue());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Map<Integer, Integer> map = new HashMap<>();
|
||||
map.put(0,0);
|
||||
map.put(1,1);
|
||||
map.put(2,0);
|
||||
//map.put(1,15); 0000001100000110
|
||||
byte[] bytes = toOldBitBytes(map,"0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1");
|
||||
byte[] bytes1 = toBitBytes(map);
|
||||
String string = ByteToHexUtil.bytesToHexString(bytes1);
|
||||
System.out.println(string);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,292 @@
|
||||
package com.fastbee.pakModbus.model;
|
||||
|
||||
import com.fastbee.common.core.protocol.Message;
|
||||
import com.fastbee.common.core.protocol.modbus.ModbusCode;
|
||||
import com.fastbee.protocol.base.annotation.Column;
|
||||
import com.fastbee.protocol.util.ToStringBuilder;
|
||||
import com.fastbee.base.session.Session;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* modbus采集方式二:dtu或模组主动轮询,变化上报,以约定报文格式进行上报
|
||||
*
|
||||
* @author gsb
|
||||
* @date 2022/12/5 16:43
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
public class PakModbusRtu extends Message {
|
||||
|
||||
/**
|
||||
* 1.约定报文格式如下
|
||||
* 1.设备主动上报数据组成:
|
||||
* * 上报的指令数据
|
||||
* * FFAA 0001 010302030578B7
|
||||
* * FFAA 0001 010302030578B7
|
||||
* * 包头 起始寄存器 数据包
|
||||
* <p>
|
||||
* * 数据包
|
||||
* * 01 03 02 0305 78B7
|
||||
* * 设备地址 命令号 返回数据字节数 数据 CRC校验
|
||||
* <p>
|
||||
* 2.服务下发数据组成
|
||||
* * 下发的指令数据
|
||||
* * FFDD a69035158678888448 01 06 0101 0015 1839
|
||||
* * FFDD a69035158678888448 0106010100151839
|
||||
* * 固定报文头 9字节消息ID 数据包
|
||||
* </p>
|
||||
* * 数据包
|
||||
* * 01 06 0101 0015 1839
|
||||
* * 设备地址 命令号 寄存器地址 数据位 CRC校验
|
||||
* <p>
|
||||
* 3.设备应答服务下发数据组成
|
||||
* * 下发的指令数据
|
||||
* * FFDD a69035158678888448 01 06 0101 0015 1839
|
||||
* * FFDD a69035158678888448 0106010100151839
|
||||
* * 固定报文头 9字节消息ID 数据包
|
||||
* <p>
|
||||
* * 数据包
|
||||
* * 01 06 0101 0015 1839
|
||||
* * 设备地址 命令号 寄存器地址 数据位 CRC校验
|
||||
*/
|
||||
|
||||
@Column(length = 1, version = {0x80, 0x81}, desc = "起始地址")
|
||||
protected int begin;
|
||||
@Column(length = 1, version = {0x80, 0x81}, desc = "标识位,实例设备是0x80")
|
||||
private int mId;
|
||||
@Column(length = 6, version = 0x80, desc = "MAC地址,6个字节")
|
||||
private String mac;
|
||||
@Column(length = 1, version = {0x80, 0x81}, desc = "结尾,如:7E")
|
||||
private int end;
|
||||
@Column(length = 2, desc = "固定报文头: FFDD或FFAA",version = {0,1,2,3,4,5})
|
||||
protected int start = 0xFFDD;
|
||||
@Column(length = 9,desc = "消息id",version = {1,2,3,4,5})
|
||||
protected String messageId;
|
||||
@Column(length = 2, desc = "寄存器地址" ,version = 0)
|
||||
protected int address;
|
||||
@Column(length = 1,desc = "从机地址",version = {0,1,2,3,4,5})
|
||||
protected int slaveId;
|
||||
@Column(length = 1,desc = "功能码" ,version = {0,1,2,3,4,5})
|
||||
protected int code;
|
||||
@Column(length = 2 ,desc ="下发寄存器",version = {1,2,3,4,5})
|
||||
protected int downAdd;
|
||||
@Column(length = 2,desc = "寄存器数量",version = {1,3,4,5})
|
||||
protected int count;
|
||||
@Column(length = 1,desc = "字节数",version = {3,5})
|
||||
protected int bitCount;
|
||||
@Column(totalUnit = 1,desc = "上报数据",version = {0,4})
|
||||
protected short[] data;
|
||||
@Column(length = 2, desc = "下发数据", version = 2)
|
||||
protected int writeData;
|
||||
@Column(totalUnit = 0,desc = "10下发数据",version = 3)
|
||||
protected short[] tenWriteData;
|
||||
@Column(totalUnit = 0,desc = "10下发数据",version = 5)
|
||||
protected byte[] bitData;
|
||||
/*原始的bit字符串*/
|
||||
protected String bitString;
|
||||
|
||||
|
||||
public String getBitString() {
|
||||
return bitString;
|
||||
}
|
||||
|
||||
public void setBitString(String bitString) {
|
||||
this.bitString = bitString;
|
||||
}
|
||||
|
||||
public byte[] getBitData() {
|
||||
return bitData;
|
||||
}
|
||||
|
||||
public void setBitData(byte[] bitData) {
|
||||
this.bitData = bitData;
|
||||
}
|
||||
|
||||
/**
|
||||
* crc校验
|
||||
*/
|
||||
protected boolean verified = true;
|
||||
|
||||
protected transient Session session;
|
||||
|
||||
protected transient ByteBuf payload;
|
||||
|
||||
protected transient int serialNo;
|
||||
|
||||
public int getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public void setStart(int start) {
|
||||
this.start = start;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public void setCount(int count) {
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public int getBitCount() {
|
||||
return bitCount;
|
||||
}
|
||||
|
||||
public void setBitCount(int bitCount) {
|
||||
this.bitCount = bitCount;
|
||||
}
|
||||
|
||||
public short[] getTenWriteData() {
|
||||
return tenWriteData;
|
||||
}
|
||||
|
||||
public void setTenWriteData(short[] tenWriteData) {
|
||||
this.tenWriteData = tenWriteData;
|
||||
}
|
||||
|
||||
public int getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(int address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public int getSlaveId() {
|
||||
return slaveId;
|
||||
}
|
||||
|
||||
public void setSlaveId(int slaveId) {
|
||||
this.slaveId = slaveId;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public short[] getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(short[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public boolean isVerified() {
|
||||
return verified;
|
||||
}
|
||||
|
||||
public void setVerified(boolean verified) {
|
||||
this.verified = verified;
|
||||
}
|
||||
|
||||
public Session getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
public void setSession(Session session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public ByteBuf getPayload() {
|
||||
return payload;
|
||||
}
|
||||
|
||||
public void setPayload(ByteBuf payload) {
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public int getSerialNo() {
|
||||
return serialNo;
|
||||
}
|
||||
|
||||
public void setSerialNo(int serialNo) {
|
||||
this.serialNo = serialNo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessageId(String messageId) {
|
||||
this.messageId = messageId;
|
||||
}
|
||||
|
||||
public int getDownAdd() {
|
||||
return downAdd;
|
||||
}
|
||||
|
||||
public void setDownAdd(int downAdd) {
|
||||
this.downAdd = downAdd;
|
||||
}
|
||||
|
||||
public int getWriteData() {
|
||||
return writeData;
|
||||
}
|
||||
|
||||
public void setWriteData(int writeData) {
|
||||
this.writeData = writeData;
|
||||
}
|
||||
|
||||
public int getBegin() {
|
||||
return begin;
|
||||
}
|
||||
|
||||
public void setBegin(int begin) {
|
||||
this.begin = begin;
|
||||
}
|
||||
|
||||
public int getmId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
public void setmId(int mId) {
|
||||
this.mId = mId;
|
||||
}
|
||||
|
||||
public String getMac() {
|
||||
return mac;
|
||||
}
|
||||
|
||||
public void setMac(String mac) {
|
||||
this.mac = mac;
|
||||
}
|
||||
|
||||
public int getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
public void setEnd(int end) {
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
protected StringBuilder toStringHead() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
String des = ModbusCode.getDes(this.code);
|
||||
sb.append(des);
|
||||
sb.append("[");
|
||||
sb.append(",slaveId[从机地址]=").append(slaveId);
|
||||
sb.append(",code[功能码]=").append(code);
|
||||
sb.append(",length[返回数据个数]").append(data==null? 0: data.length);
|
||||
sb.append(",data[上报数据]=").append(Arrays.toString(data));
|
||||
// sb.append(",count[读取寄存器个数]=").append(len);
|
||||
sb.append(",address[寄存器地址]=").append(address);
|
||||
sb.append(",writeData[下发数据]=").append(writeData);
|
||||
sb.append("]");
|
||||
return sb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.toString(toStringHead(), this, false);
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package com.fastbee.pakModbus.test;
|
||||
|
||||
import com.fastbee.common.core.protocol.modbus.ModbusCode;
|
||||
import com.fastbee.common.utils.gateway.CRC16Utils;
|
||||
import com.fastbee.iot.util.SnowflakeIdWorker;
|
||||
import com.fastbee.pakModbus.codec.ModbusRtuPakDecoder;
|
||||
import com.fastbee.pakModbus.codec.ModbusRtuPakEncoder;
|
||||
import com.fastbee.pakModbus.model.PakModbusRtu;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
//import org.checkerframework.checker.units.qual.A;
|
||||
|
||||
/**
|
||||
* @author bill
|
||||
*/
|
||||
public class PakModbusTest {
|
||||
|
||||
private static ModbusRtuPakDecoder decoder = new ModbusRtuPakDecoder("com.fastbee.pakModbus");
|
||||
private static ModbusRtuPakEncoder encoder = new ModbusRtuPakEncoder("com.fastbee.pakModbus");
|
||||
private static SnowflakeIdWorker snowflakeIdWorker = new SnowflakeIdWorker(6);
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
/**
|
||||
* 单个解析
|
||||
*/
|
||||
String hex = "ffaa000001030400d800dafb930d";
|
||||
ByteBuf buf = Unpooled.wrappedBuffer(ByteBufUtil.decodeHexDump(hex));
|
||||
PakModbusRtu message = decoder.decode(buf);
|
||||
System.out.println("单个解析=" + message);
|
||||
buf.release();
|
||||
/**
|
||||
* 批量上报解析
|
||||
*/
|
||||
String hexMore = "FFAA00010103a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000cff039c00200000ffffffff0000006e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa0000000000000000ffc900100019001e0010001900190057000747790D";
|
||||
ByteBuf bufMore = Unpooled.wrappedBuffer(ByteBufUtil.decodeHexDump(hexMore));
|
||||
PakModbusRtu messageMore = decoder.decode(bufMore);
|
||||
System.out.println("批量上传报文解析=" + messageMore);
|
||||
bufMore.release();
|
||||
|
||||
PakModbusRtu modbusRtu = new PakModbusRtu();
|
||||
modbusRtu.setMessageId(snowflakeIdWorker.nextId()+"");
|
||||
modbusRtu.setSlaveId(1);
|
||||
modbusRtu.setCode(ModbusCode.Write06.getCode());
|
||||
modbusRtu.setDownAdd(1);
|
||||
modbusRtu.setWriteData(100);
|
||||
ByteBuf out = encoder.encode(modbusRtu);
|
||||
byte[] result = new byte[out.writerIndex()];
|
||||
out.readBytes(result);
|
||||
ReferenceCountUtil.release(out);
|
||||
byte[] crc = CRC16Utils.CRC(result);
|
||||
System.out.println("->下发指令:"+ ByteBufUtil.hexDump(crc));
|
||||
|
||||
// ModbusRtu read03 = new ModbusRtu();
|
||||
// read03.setSlaveId(1);
|
||||
// read03.setAddress(1);
|
||||
// read03.setCode(3);
|
||||
// read03.setCount(3);
|
||||
// ByteBuf out = encoder.encode(read03);
|
||||
// System.out.println(ByteBufUtil.hexDump(out));
|
||||
//
|
||||
// ModbusRtu write06 = new ModbusRtu();
|
||||
// write06.setSlaveId(1);
|
||||
// write06.setAddress(1);
|
||||
// write06.setCode(6);
|
||||
// write06.setWriteData(17);
|
||||
// ByteBuf writeOut = encoder.encode(write06);
|
||||
// System.out.println(ByteBufUtil.hexDump(writeOut));
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
package com.fastbee.rj45;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fastbee.common.annotation.SysProtocol;
|
||||
import com.fastbee.common.constant.FastBeeConstant;
|
||||
import com.fastbee.common.core.mq.DeviceReport;
|
||||
import com.fastbee.common.core.mq.MQSendMessageBo;
|
||||
import com.fastbee.common.core.mq.message.DeviceData;
|
||||
import com.fastbee.common.core.mq.message.FunctionCallBackBo;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelValuesInput;
|
||||
import com.fastbee.common.exception.ServiceException;
|
||||
import com.fastbee.common.utils.BeanMapUtilByReflect;
|
||||
import com.fastbee.common.utils.DateUtils;
|
||||
import com.fastbee.protocol.base.protocol.IProtocol;
|
||||
import com.fastbee.rj45.model.RfId;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2022/10/10 16:55
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@SysProtocol(name = "RJ45解析协议",protocolCode = FastBeeConstant.PROTOCOL.RJ45,description = "系统内置RJ45解析协议")
|
||||
public class Rj45ProtocolService implements IProtocol {
|
||||
|
||||
/**
|
||||
* 解析RJ45格式数据
|
||||
*/
|
||||
@Override
|
||||
public DeviceReport decode(DeviceData deviceData, String clientId) {
|
||||
try {
|
||||
DeviceReport reportMessage = new DeviceReport();
|
||||
String data = new String(deviceData.getData(),StandardCharsets.UTF_8);
|
||||
String[] split = data.split(",");
|
||||
String s = split[2];
|
||||
RfId rfId = new RfId();
|
||||
rfId.setLabelType(s.substring(0,2));
|
||||
rfId.setModelNo(s.substring(2,4));
|
||||
rfId.setEpc(s.substring(4));
|
||||
List<ThingsModelSimpleItem> values = BeanMapUtilByReflect.beanToItem(rfId);
|
||||
reportMessage.setThingsModelSimpleItem(values);
|
||||
reportMessage.setSerialNumber(clientId);
|
||||
reportMessage.setClientId(clientId);
|
||||
return reportMessage;
|
||||
}catch (Exception e){
|
||||
throw new ServiceException("数据解析异常"+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionCallBackBo encode(MQSendMessageBo message) {
|
||||
try {
|
||||
FunctionCallBackBo callBack = new FunctionCallBackBo();
|
||||
String msg = JSONObject.toJSONString(message.getParams());
|
||||
callBack.setSources(msg);
|
||||
callBack.setMessage(msg.getBytes());
|
||||
return callBack;
|
||||
}catch (Exception e){
|
||||
log.error("=>指令编码异常,device={},data={}",message.getSerialNumber(),
|
||||
message.getParams());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.fastbee.rj45.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author bill
|
||||
*/
|
||||
@Data
|
||||
public class RfId {
|
||||
|
||||
/*标签类型*/
|
||||
private String labelType;
|
||||
/*天线号*/
|
||||
private String modelNo;
|
||||
/*EPC*/
|
||||
private String epc;
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package com.fastbee.sgz;
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.fastbee.base.core.annotation.Node;
|
||||
import com.fastbee.base.core.annotation.PakMapping;
|
||||
import com.fastbee.base.session.Session;
|
||||
import com.fastbee.common.core.mq.DeviceReport;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2024/3/29 16:06
|
||||
*/
|
||||
@Node
|
||||
@Component
|
||||
public class SgzEndPoint {
|
||||
|
||||
@PakMapping(types = 150)
|
||||
public DeviceReport heart(DeviceReport report, Session session){
|
||||
JSONObject params = new JSONObject();
|
||||
params.putIfAbsent("device",report.getClientId());
|
||||
List<ThingsModelSimpleItem> items = report.getThingsModelSimpleItem();
|
||||
for (ThingsModelSimpleItem item : items) {
|
||||
if (item.getId().equals("dtype")){
|
||||
params.putIfAbsent("dtype",item.getValue());
|
||||
}
|
||||
}
|
||||
params.putIfAbsent("fuc",SgzMessageType.LINKING.type);
|
||||
params.putIfAbsent("sdata",SgzMessageType.LINKING.type);
|
||||
report.setBody(params);
|
||||
return report;
|
||||
}
|
||||
|
||||
@PakMapping(types = 154)
|
||||
public DeviceReport dataCallBack(DeviceReport report){
|
||||
JSONObject params = new JSONObject();
|
||||
params.putIfAbsent("device",report.getClientId());
|
||||
List<ThingsModelSimpleItem> items = report.getThingsModelSimpleItem();
|
||||
for (ThingsModelSimpleItem item : items) {
|
||||
if (item.getId().equals("dtype")){
|
||||
params.putIfAbsent("dtype",item.getValue());
|
||||
}
|
||||
}
|
||||
params.putIfAbsent("fuc",SgzMessageType.CZROK.type);
|
||||
params.putIfAbsent("sdata",SgzMessageType.CZROK.type);
|
||||
report.setBody(params);
|
||||
return report;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package com.fastbee.sgz;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2024/3/29 11:31
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum SgzMessageType {
|
||||
|
||||
HEART(150,"heart","心跳"),
|
||||
LINKING(151," LINKING"," LINKING已连接"),
|
||||
CZOK(152," CZOK","开始充装"),
|
||||
UNLOCK(153,"UNLOCK","已解锁"),
|
||||
CZSTOP(154,"CZSTOP","数据上报"),
|
||||
CZROK(155,"CZROK","充装结束回复设备");
|
||||
|
||||
int messageId;
|
||||
String type;
|
||||
String desc;
|
||||
|
||||
public static SgzMessageType convert(String type){
|
||||
for (SgzMessageType value : SgzMessageType.values()) {
|
||||
if (value.type.equals(type)){
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return HEART;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
package com.fastbee.sgz;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fastbee.common.annotation.SysProtocol;
|
||||
import com.fastbee.common.constant.FastBeeConstant;
|
||||
import com.fastbee.common.core.mq.DeviceReport;
|
||||
import com.fastbee.common.core.mq.MQSendMessageBo;
|
||||
import com.fastbee.common.core.mq.message.DeviceData;
|
||||
import com.fastbee.common.core.mq.message.DeviceDownMessage;
|
||||
import com.fastbee.common.core.mq.message.FunctionCallBackBo;
|
||||
import com.fastbee.common.core.redis.RedisCache;
|
||||
import com.fastbee.common.core.redis.RedisKeyBuilder;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelValuesInput;
|
||||
import com.fastbee.common.exception.ServiceException;
|
||||
import com.fastbee.common.utils.DateUtils;
|
||||
import com.fastbee.iot.model.ThingsModels.ValueItem;
|
||||
import com.fastbee.protocol.base.protocol.IProtocol;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2024/3/29 11:27
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@SysProtocol(name = "数码灌装解析协议", protocolCode = FastBeeConstant.PROTOCOL.SGZ, description = "数码灌装解析协议")
|
||||
public class SgzProtocolService implements IProtocol {
|
||||
|
||||
@Resource
|
||||
private RedisCache redisCache;
|
||||
|
||||
/**
|
||||
* 上报数据格式 <p>
|
||||
* {
|
||||
* "params": {
|
||||
* "DI" : 0,
|
||||
* "DO" : 1
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
@Override
|
||||
public DeviceReport decode(DeviceData deviceData, String clientId) {
|
||||
SgzMessageType dtype = SgzMessageType.HEART;
|
||||
DeviceReport reportMessage = new DeviceReport();
|
||||
String data = new String(deviceData.getData(), StandardCharsets.UTF_8);
|
||||
JSONObject params = (JSONObject) JSON.parse(data);
|
||||
List<ThingsModelSimpleItem> result = new ArrayList<>();
|
||||
for (Map.Entry<String, Object> entry : params.entrySet()) {
|
||||
ThingsModelSimpleItem item = new ThingsModelSimpleItem();
|
||||
String val = entry.getValue() + "";
|
||||
item.setTs(DateUtils.getNowDate());
|
||||
item.setValue(val);
|
||||
item.setId(entry.getKey());
|
||||
result.add(item);
|
||||
if (entry.getKey().equals("fuc")) {
|
||||
dtype = SgzMessageType.convert(val);
|
||||
}
|
||||
if (dtype.equals(SgzMessageType.CZSTOP)) {
|
||||
String[] strings = {"stime", "sno", "scode", "smiao", "sjz", "spz", "etime", "stotal"};
|
||||
String[] split = val.split(",");
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
ThingsModelSimpleItem simpleItem = new ThingsModelSimpleItem();
|
||||
simpleItem.setTs(DateUtils.getNowDate());
|
||||
simpleItem.setValue(split[i]);
|
||||
simpleItem.setId(strings[i]);
|
||||
result.add(simpleItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
reportMessage.setThingsModelSimpleItem(result);
|
||||
reportMessage.setIsPackage(true);
|
||||
reportMessage.setMessageId(dtype.messageId + "");
|
||||
reportMessage.setClientId(clientId);
|
||||
reportMessage.setSerialNumber(clientId);
|
||||
return reportMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionCallBackBo encode(MQSendMessageBo message) {
|
||||
try {
|
||||
FunctionCallBackBo callBack = new FunctionCallBackBo();
|
||||
String cacheKey = RedisKeyBuilder.buildTSLVCacheKey(message.getDp().getProductId(), message.getSerialNumber());
|
||||
String data = redisCache.getCacheMapValue(cacheKey, "dtype");
|
||||
ValueItem valueItem = JSON.parseObject(data, ValueItem.class);
|
||||
JSONObject params = message.getParams();
|
||||
Object scode = params.get("scode");
|
||||
params.put("device", message.getSerialNumber());
|
||||
params.put("fuc", SgzMessageType.CZOK);
|
||||
params.put("dtype", valueItem.getValue());
|
||||
params.put("sdata", scode);
|
||||
String out = JSON.toJSONString(params);
|
||||
callBack.setMessage(out.getBytes());
|
||||
callBack.setSources(out);
|
||||
return callBack;
|
||||
// }
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException("指令编号异常" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeCallBack(Object message) {
|
||||
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package com.fastbee.yinerda;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fastbee.common.annotation.SysProtocol;
|
||||
import com.fastbee.common.constant.FastBeeConstant;
|
||||
import com.fastbee.common.core.mq.DeviceReport;
|
||||
import com.fastbee.common.core.mq.MQSendMessageBo;
|
||||
import com.fastbee.common.core.mq.message.DeviceData;
|
||||
import com.fastbee.common.core.mq.message.FunctionCallBackBo;
|
||||
import com.fastbee.common.enums.FunctionReplyStatus;
|
||||
import com.fastbee.common.exception.ServiceException;
|
||||
import com.fastbee.protocol.base.protocol.IProtocol;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2022/10/10 16:55
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@SysProtocol(name = "YinErDa解析协议",protocolCode = FastBeeConstant.PROTOCOL.YinErDa,description = "YinErDa解析协议")
|
||||
public class YiDaErProtocolService implements IProtocol {
|
||||
|
||||
|
||||
/**
|
||||
* YiDaEr,这里处理设备回复,不进行数据存储
|
||||
* 上报数据格式:
|
||||
* on:done
|
||||
* off:done
|
||||
*/
|
||||
@Override
|
||||
public DeviceReport decode(DeviceData deviceData, String clientId) {
|
||||
try {
|
||||
DeviceReport reportMessage = new DeviceReport();
|
||||
String data = new String(deviceData.getData(),StandardCharsets.UTF_8);
|
||||
reportMessage.setReplyMessage(data);
|
||||
reportMessage.setStatus(FunctionReplyStatus.SUCCESS);
|
||||
reportMessage.setIsReply(true);
|
||||
reportMessage.setProtocolCode(FastBeeConstant.PROTOCOL.YinErDa);
|
||||
return reportMessage;
|
||||
}catch (Exception e){
|
||||
throw new ServiceException("数据解析异常"+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionCallBackBo encode(MQSendMessageBo message) {
|
||||
try {
|
||||
FunctionCallBackBo callBack = new FunctionCallBackBo();
|
||||
String value = message.getValue();
|
||||
value = "0".equals(value) ? "off" : "on";
|
||||
callBack.setMessage(value.getBytes());
|
||||
callBack.setSources(value);
|
||||
return callBack;
|
||||
}catch (Exception e){
|
||||
log.error("=>指令编码异常,device={},data={}",message.getSerialNumber(),
|
||||
message.getParams());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,183 @@
|
||||
package com.fastbee.zqwl;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fastbee.common.annotation.SysProtocol;
|
||||
import com.fastbee.common.constant.FastBeeConstant;
|
||||
import com.fastbee.common.core.mq.DeviceReport;
|
||||
import com.fastbee.common.core.mq.MQSendMessageBo;
|
||||
import com.fastbee.common.core.mq.message.DeviceData;
|
||||
import com.fastbee.common.core.mq.message.FunctionCallBackBo;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelValuesInput;
|
||||
import com.fastbee.common.exception.ServiceException;
|
||||
import com.fastbee.common.utils.DateUtils;
|
||||
import com.fastbee.common.utils.gateway.CRC16Utils;
|
||||
import com.fastbee.iot.domain.ModbusConfig;
|
||||
import com.fastbee.iot.model.ThingsModels.ThingsModelValueItem;
|
||||
import com.fastbee.modbus.codec.ModbusEncoder;
|
||||
import com.fastbee.modbus.model.ModbusRtu;
|
||||
import com.fastbee.protocol.base.protocol.IProtocol;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author bill
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@SysProtocol(name = "GEC6100D发电机控制器协议", protocolCode = FastBeeConstant.PROTOCOL.GEC6100D, description = "GEC6100D发电机控制器协议-繁易")
|
||||
public class GEC6100ToZqwlProtocolService implements IProtocol {
|
||||
|
||||
@Resource
|
||||
private ModbusEncoder messageEncoder;
|
||||
|
||||
|
||||
@Override
|
||||
public DeviceReport decode(DeviceData deviceData, String clientId) {
|
||||
|
||||
try {
|
||||
DeviceReport reportMessage = new DeviceReport();
|
||||
String data = new String(deviceData.getData(), StandardCharsets.UTF_8);
|
||||
List<ThingsModelSimpleItem> result = new ArrayList<>();
|
||||
Map<String, Object> values = JSON.parseObject(data, Map.class);
|
||||
if (values.containsKey("sn")) {
|
||||
reportMessage.setIsReply(true);
|
||||
for (Map.Entry<String, Object> entry : values.entrySet()) {
|
||||
ThingsModelSimpleItem item = new ThingsModelSimpleItem();
|
||||
item.setTs(DateUtils.getNowDate());
|
||||
item.setId(entry.getKey());
|
||||
item.setValue(entry.getValue()+"");
|
||||
result.add(item);
|
||||
if (entry.getKey().equals("sn")){
|
||||
reportMessage.setMessageId(entry.getValue()+"");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (Map.Entry<String, Object> entry : values.entrySet()) {
|
||||
if (entry.getValue() instanceof JSONArray) {
|
||||
JSONArray array = (JSONArray) entry.getValue();
|
||||
int index = parseKey(entry.getKey());
|
||||
for (int i = 0; i < array.size(); i++) {
|
||||
ThingsModelSimpleItem item = new ThingsModelSimpleItem();
|
||||
item.setTs(DateUtils.getNowDate());
|
||||
String s = array.get(i) + "";
|
||||
if (s.equals("32768")){
|
||||
s = "-1";
|
||||
}
|
||||
item.setValue(s);
|
||||
item.setId("k" + (index + i));
|
||||
result.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
reportMessage.setThingsModelSimpleItem(result);
|
||||
reportMessage.setClientId(deviceData.getSerialNumber());
|
||||
reportMessage.setSerialNumber(deviceData.getSerialNumber());
|
||||
reportMessage.setProductId(deviceData.getProductId());
|
||||
reportMessage.setProtocolCode(FastBeeConstant.PROTOCOL.GEC6100D);
|
||||
reportMessage.setSources(data);
|
||||
return reportMessage;
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException("数据解析异常" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionCallBackBo encode(MQSendMessageBo message) {
|
||||
try {
|
||||
FunctionCallBackBo callBack = new FunctionCallBackBo();
|
||||
ModbusRtu rtu = new ModbusRtu();
|
||||
String thingsModel = message.getThingsModel();
|
||||
ThingsModelValueItem item = JSONObject.parseObject(thingsModel, ThingsModelValueItem.class);
|
||||
ModbusConfig modbusConfig = item.getConfig();
|
||||
switch (modbusConfig.getModbusCode()) {
|
||||
case Write05:
|
||||
case Write06:
|
||||
write0506(message,item, rtu);
|
||||
break;
|
||||
case Write10:
|
||||
write10(message,item,rtu);
|
||||
break;
|
||||
|
||||
}
|
||||
rtu.setByteCount(1);
|
||||
//rtu.setByteLength(2);
|
||||
ByteBuf out = messageEncoder.encode(rtu);
|
||||
byte[] data = new byte[out.writerIndex()];
|
||||
out.readBytes(data);
|
||||
ReferenceCountUtil.release(out);
|
||||
byte[] bytes = CRC(data);
|
||||
//下发指令
|
||||
String hexDump = ByteBufUtil.hexDump(bytes);
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("mb", hexDump);
|
||||
jsonObject.put("sn", message.getMessageId());
|
||||
jsonObject.put("ack", 1);
|
||||
jsonObject.put("crc", 1);
|
||||
String result = JSONObject.toJSONString(jsonObject);
|
||||
callBack.setMessage(result.getBytes());
|
||||
callBack.setSources(result);
|
||||
return callBack;
|
||||
} catch (Exception e) {
|
||||
log.error("=>指令编码异常,device={},data={},msg={}", message.getSerialNumber(),
|
||||
message.getParams(), e);
|
||||
throw new ServiceException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static int parseKey(String key) {
|
||||
String s = key.substring(1);
|
||||
return Integer.parseInt(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* writ05/06指令配置
|
||||
*/
|
||||
private void write0506(MQSendMessageBo message,ThingsModelValueItem item, ModbusRtu rtu) {
|
||||
ModbusConfig modbusConfig = item.getConfig();
|
||||
rtu.setAddress(modbusConfig.getAddress());
|
||||
String value = message.getValue();
|
||||
int data;
|
||||
if (value.contains("0x")) {
|
||||
data = Integer.parseInt(value.substring(2), 16);
|
||||
} else {
|
||||
data = Integer.parseInt(value);
|
||||
}
|
||||
rtu.setWriteData(data);
|
||||
rtu.setCode(modbusConfig.getModbusCode().getCode());
|
||||
rtu.setSlaveId(modbusConfig.getSlave() == null ? 1 : modbusConfig.getSlave());
|
||||
}
|
||||
|
||||
/**
|
||||
* writ05/06指令配置
|
||||
*/
|
||||
private void write10(MQSendMessageBo message,ThingsModelValueItem item, ModbusRtu rtu) {
|
||||
ModbusConfig modbusConfig = item.getConfig();
|
||||
rtu.setAddress(modbusConfig.getAddress());
|
||||
String value = message.getValue();
|
||||
int data = Integer.parseInt(value);
|
||||
//rtu.setControl(data);
|
||||
rtu.setCode(modbusConfig.getModbusCode().getCode());
|
||||
rtu.setSlaveId(modbusConfig.getSlave() == null ? 1 : modbusConfig.getSlave());
|
||||
}
|
||||
|
||||
public byte[] CRC(byte[] source) {
|
||||
byte[] result = new byte[source.length + 2];
|
||||
byte[] crc16Byte = CRC16Utils.getCrc16Byte(source);
|
||||
System.arraycopy(source, 0, result, 0, source.length);
|
||||
System.arraycopy(crc16Byte, 0, result, result.length - 2, 2);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,209 @@
|
||||
package com.fastbee.zqwl;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fastbee.common.annotation.SysProtocol;
|
||||
import com.fastbee.common.constant.FastBeeConstant;
|
||||
import com.fastbee.common.core.mq.DeviceReport;
|
||||
import com.fastbee.common.core.mq.MQSendMessageBo;
|
||||
import com.fastbee.common.core.mq.message.DeviceData;
|
||||
import com.fastbee.common.core.mq.message.FunctionCallBackBo;
|
||||
import com.fastbee.common.core.redis.RedisCache;
|
||||
import com.fastbee.common.core.redis.RedisKeyBuilder;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelValuesInput;
|
||||
import com.fastbee.common.exception.ServiceException;
|
||||
import com.fastbee.common.utils.DateUtils;
|
||||
import com.fastbee.common.utils.gateway.mq.TopicsUtils;
|
||||
import com.fastbee.iot.model.ThingsModels.ThingsModelValueItem;
|
||||
import com.fastbee.iot.service.IDeviceService;
|
||||
import com.fastbee.protocol.base.protocol.IProtocol;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2023/8/14 16:04
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@SysProtocol(name = "8路继电器+Modbus转Json-智嵌物联",protocolCode = FastBeeConstant.PROTOCOL.ModbusToJsonZQWL,description = "8路继电器+Modbus转Json-智嵌物联")
|
||||
public class ModbusToJsonZQWLProtocolService implements IProtocol {
|
||||
|
||||
|
||||
@Resource
|
||||
private RedisCache redisCache;
|
||||
@Resource
|
||||
private TopicsUtils topicsUtils;
|
||||
@Resource
|
||||
private IDeviceService deviceService;
|
||||
|
||||
/**
|
||||
* 上报数据格式: <p>
|
||||
* device1: 从机1标识 <p>
|
||||
* name: 物模型标识符 <p>
|
||||
* value: 上报值 <p>
|
||||
* {
|
||||
* "device1": [
|
||||
* {
|
||||
* "name": "J2",
|
||||
* "value": 8.331631
|
||||
* },
|
||||
* {
|
||||
* "name": "J1",
|
||||
* "value": -130.123718
|
||||
* }
|
||||
* ],
|
||||
* "device2": [
|
||||
* {
|
||||
* "name": "J4",
|
||||
* "value": -16.350224
|
||||
* },
|
||||
* {
|
||||
* "name": "J3",
|
||||
* "value": 94.769806
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
*
|
||||
* 下发报文格式<p>
|
||||
* device 从机编号 <p>
|
||||
* name 标识符 <p>
|
||||
* value 值 <p>
|
||||
* serNo 流水号 <p>
|
||||
* {
|
||||
* "device": 1,
|
||||
* "name": "template",
|
||||
* "value": 111,
|
||||
* "serNo": "213245489543789"
|
||||
* }
|
||||
* </p>
|
||||
*
|
||||
* 下发指令回复格式<p>
|
||||
* serNo 平台的流水号,用于对应回复消息 <p>
|
||||
* ack 下发指令状态 0是失败 1是成功 <p>
|
||||
* {
|
||||
* "serNo": "213245489543789",
|
||||
* "ack": 1
|
||||
* }
|
||||
* </p>
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public DeviceReport decode(DeviceData deviceData, String clientId) {
|
||||
try {
|
||||
DeviceReport reportMessage = new DeviceReport();
|
||||
String data = new String(deviceData.getData(),StandardCharsets.UTF_8);
|
||||
List<ThingsModelSimpleItem> result = new ArrayList<>();
|
||||
Map<String,Object> values = JSON.parseObject(data, Map.class);
|
||||
if (values.containsKey("addr") || (values.containsKey("cmd") && values.get("cmd").equals("ret"))){
|
||||
for (Map.Entry<String, Object> entry : values.entrySet()) {
|
||||
if (entry.getKey().equals("x")){
|
||||
JSONArray value = (JSONArray) entry.getValue();
|
||||
for (int i = 0; i < value.size(); i++) {
|
||||
ThingsModelSimpleItem simpleItem = new ThingsModelSimpleItem();
|
||||
simpleItem.setTs(DateUtils.getNowDate());
|
||||
simpleItem.setId(entry.getKey()+(i+1));
|
||||
simpleItem.setValue(value.get(i)+"");
|
||||
result.add(simpleItem);
|
||||
}
|
||||
}
|
||||
if (entry.getKey().equals("y")){
|
||||
JSONArray value = (JSONArray) entry.getValue();
|
||||
for (int i = 0; i < value.size(); i++) {
|
||||
ThingsModelSimpleItem simpleItem = new ThingsModelSimpleItem();
|
||||
simpleItem.setTs(DateUtils.getNowDate());
|
||||
simpleItem.setId(entry.getKey()+ (i+1));
|
||||
simpleItem.setValue(value.get(i)+"");
|
||||
result.add(simpleItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}else {
|
||||
for (Map.Entry<String, Object> entry : values.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
if (key.contains("-")) {
|
||||
String slaveKey = key.split("-")[0];
|
||||
Integer slaveId = Integer.parseInt(slaveKey);
|
||||
ThingsModelSimpleItem item = new ThingsModelSimpleItem();
|
||||
item.setTs(DateUtils.getNowDate());
|
||||
item.setValue(entry.getValue() + "");
|
||||
item.setId(key);
|
||||
//item.setSlaveId(slaveId);
|
||||
result.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
reportMessage.setThingsModelSimpleItem(result);
|
||||
reportMessage.setClientId(clientId);
|
||||
reportMessage.setSerialNumber(clientId);
|
||||
return reportMessage;
|
||||
}catch (Exception e){
|
||||
throw new ServiceException("数据解析异常"+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionCallBackBo encode(MQSendMessageBo message) {
|
||||
try {
|
||||
FunctionCallBackBo callBack = new FunctionCallBackBo();
|
||||
Long productId = message.getDp().getProductId();
|
||||
String messageValue = message.getValue();
|
||||
JSONObject params = new JSONObject();
|
||||
params.put("addr",1);
|
||||
params.put("cmd","set");
|
||||
String identifier = message.getIdentifier();
|
||||
int idIndex = Integer.parseInt(identifier.substring(1, 2));
|
||||
int value = Integer.parseInt(messageValue);
|
||||
int[] ints = new int[8];
|
||||
List<ThingsModelValueItem> cacheValueList = getCacheDeviceStatus(productId, message.getSerialNumber());
|
||||
if (identifier.contains("x")){
|
||||
for (ThingsModelValueItem valueItem : cacheValueList) {
|
||||
if (valueItem.getId().contains("x")){
|
||||
int itemIndex = Integer.parseInt(valueItem.getId().substring(1, 2));
|
||||
ints[itemIndex-1] = Integer.parseInt(valueItem.getValue());
|
||||
}
|
||||
}
|
||||
ints[idIndex-1] = value;
|
||||
params.put("x",ints);
|
||||
}else if (identifier.contains("y")){
|
||||
for (ThingsModelValueItem valueItem : cacheValueList) {
|
||||
if (valueItem.getId().contains("y")){
|
||||
int itemIndex = Integer.parseInt(valueItem.getId().substring(1, 2));
|
||||
ints[itemIndex-1] = Integer.parseInt(valueItem.getValue());
|
||||
}
|
||||
}
|
||||
ints[idIndex-1] = value;
|
||||
params.put("y",ints);
|
||||
}
|
||||
String msg = JSONObject.toJSONString(params);
|
||||
callBack.setSources(msg);
|
||||
callBack.setMessage(msg.getBytes());
|
||||
return callBack;
|
||||
}catch (Exception e){
|
||||
log.error("=>指令编码异常,device={}",message.getSerialNumber());
|
||||
throw new ServiceException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private List<ThingsModelValueItem> getCacheDeviceStatus(Long productId, String deviceNumber) {
|
||||
String key = RedisKeyBuilder.buildTSLVCacheKey(productId, deviceNumber);
|
||||
Map<String, String> map = redisCache.hashEntity(key);
|
||||
List<ThingsModelValueItem> valueList = new ArrayList<>();
|
||||
if (map != null && map.size() >0){
|
||||
// 获取redis缓存的物模型值
|
||||
valueList = map.values().stream().map(s -> JSONObject.parseObject(s, ThingsModelValueItem.class))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
return valueList;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package com.fastbee.zqwl;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 设备DI DO ABC ,脉冲数据
|
||||
* @author gsb
|
||||
* @date 2024/3/13 11:41
|
||||
*/
|
||||
@Data
|
||||
public class ZQWLDIDORet {
|
||||
|
||||
/**
|
||||
*设备地址
|
||||
*/
|
||||
private Integer addr;
|
||||
/**
|
||||
* 设备应答命令
|
||||
*/
|
||||
private String cmd;
|
||||
/**
|
||||
* 8路DI状态
|
||||
*/
|
||||
private List<Integer> x;
|
||||
/**
|
||||
* 8路DO状态
|
||||
*/
|
||||
private List<Integer> y;
|
||||
/**
|
||||
* 8路DI脉冲计算值
|
||||
*/
|
||||
private List<Integer> count;
|
||||
/**
|
||||
* 2路ADC值
|
||||
*/
|
||||
private List<Integer> abc;
|
||||
|
||||
}
|
32
fastbee-protocol/pom.xml
Normal file
32
fastbee-protocol/pom.xml
Normal file
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>fastbee</artifactId>
|
||||
<groupId>com.fastbee</groupId>
|
||||
<version>3.8.5</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>fastbee-protocol</artifactId>
|
||||
<description>设备协议模块</description>
|
||||
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>fastbee-protocol-base</module>
|
||||
<module>fastbee-protocol-collect</module>
|
||||
</modules>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fastbee</groupId>
|
||||
<artifactId>fastbee-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
</project>
|
Reference in New Issue
Block a user