第一次提交

This commit is contained in:
wyw
2024-08-08 00:31:26 +08:00
commit c202e2b63d
1819 changed files with 221890 additions and 0 deletions

View 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>

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}
}

View File

@ -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
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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());
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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");
}
}

View File

@ -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);
}
}

View File

@ -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){
}
}

View File

@ -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;
}

View File

@ -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));
}
}

View File

@ -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;
}

View File

@ -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;
}
}
}

View File

@ -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 数据包

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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];
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}