java解析SZY206-2016传输规约
SZY206-2016传输规约解析
·
帧结构(报文结构)
示例:681A68B35303810D00C00059000000826110000001106000004508090A3516
-
681A68:开始字符串 字符串长度 开始字符串
-
B3为控制域(数据类型)
1)0000B—雨量;
- 0001B—水位;
- 0010B—流量(水量);
- 0011B—流速;
- 0100B—闸位;
- 0101B—功率;
- 0110B—气压;
- 0111B—风速(风向);
- 1000B—水温;
- 1001B—水质;
- 1010B—土壤含水率;
- 1011B—水压;
- 1100B—1111B 备用。
- 5303810D00为地址域(测站地址) 22 24 06为行政区代码 07 00测站地址
- 0059000000826110000001106000004508090A 用户数据(报文正文)
- 35: 校验CS
- 16: 结束字符(固定)
用户数据域解析(正文解析)
正文需要从后面开始解析
正文:0059000000826110000001106000004508090A
-
后10位为时间 :004508090A //00(秒-十位个位)45(分-十位个位)08(时-十位个位)09(日-十位个位),解析后为09日 08:45:00 年月需要自己补充。
-
再往前8位 01106000为报警值 转换为2进制 00000001000100000000011000000000 (每位转成4位2进制数不足补0)
前16位数
后16位数
-
剩余的为相关数据 00590000008261100000
由于数据类型为 B3 0010B—流量(水量)
按照文档6.2.26解析各种类型的数据
传输规约是5个字节代表一个数据,00 59 00 00 00 为流量数据,82 61 10 00 00 为水量数据
再按照每个位置代表的 数字位数解析后
流量为 00 00 00 59 00 实际数据为5.9 m3/s
水量 00 00 10 61 82 实际为106182 m3/h
java代码
/**
* @author CaoLongMin
* @version 1.0
* @description TODO
* @since 2022/6/20
*/
@Slf4j
@Service
public class Szy206Parser extends BaseParser {
@Autowired
private LotDeviceMapper lotDeviceMapper;
@Autowired
private ParserService parserService;
@Autowired
private RedisTemplate redisTemplate;
private final String SZY_REDIS_KEY="device:data:szy:";
@Override
public UrlDTO getUrlInfo(String interfaceId, String deviceId) {
return null;
}
@Override
public Map<String, Object> dataParser(String msg, String deviceId) {
Szy206InfoDTO szy206InfoDTO=new Szy206InfoDTO();
//示例 681A68B35303810D00C00059000000826110000001106000004508090A3516
szy206InfoDTO.setContent(msg);
//681A68开始字符串 字符串长度 开始字符串
Integer index=0;
//开始字符串
String startStr=msg.substring(index,index+6);
szy206InfoDTO.setStartStr(startStr);
index+=6;
//B3为数据类型(流量(水量))
//1) 0000B—雨量;
//2) 0001B—水位;
//3) 0010B—流量(水量);
//4) 0011B—流速;
//5) 0100B—闸位;
//6) 0101B—功率;
//7) 0110B—气压;
//8) 0111B—风速(风向);
//9) 1000B—水温;
//10) 1001B—水质;
//11) 1010B—土壤含水率;
//12) 1011B—水压;
//13) 1100B—1111B 备用。
//数据类型
String dataType=msg.substring(index,index+2);
szy206InfoDTO.setDataType(dataType);
index+=2;
//5303810D00为测站地址 530381为行政区代码 0D00测站地址
//遥测地址
String stationAddr=msg.substring(index,index+10);
szy206InfoDTO.setStationAddr(stationAddr);
szy206InfoDTO.setDeviceId(dataType+stationAddr);
index+=10;
//C0 C0H遥测终端自报实时数据 遥测终端 中心站
//应用层功能码
String functionCode=msg.substring(index,index+2);
szy206InfoDTO.setFunctionCode(functionCode);
index+=2;
//0059000000826110000001106000004508090A 报文正文
//正文
String content=msg.substring(index,msg.length()-4);
index=msg.length()-4;
//35 crc校验
//crc校验码
String crcCode=msg.substring(index,index+2);
szy206InfoDTO.setCrcCode(crcCode);
index+=2;
//16 结束字符(固定)
//结束字符串16
String endStr=msg.substring(index,index+2);
szy206InfoDTO.setEndStr(endStr);
parseContent(content,szy206InfoDTO);
Map<String,Object> map= (Map<String, Object>) JSONUtil.parse(szy206InfoDTO);
return map;
}
private void parseContent(String content, Szy206InfoDTO szy206InfoDTO) {
szy206InfoDTO.setIsHeartbeat(false);
if(content.length()<3||szy206InfoDTO.getFunctionCode().equals("02")){
szy206InfoDTO.setIsHeartbeat(true);
log.info("心跳检测数据");
}else{
Map<String,Object> mainBody=new HashMap<>();
Map<String,Object> mainHead=new HashMap<>();
//倒着解析
int index=content.length();
String deviceIndex="";
//C0是RTU自报数据 B0是平台查询
if(szy206InfoDTO.getFunctionCode().equals("C0")&&index>18) {
//004508090A
//00(秒-十位个位)45(分-十位个位)08(时-十位个位)09(日-十位个位),解析后为09日 08:45:00 年月需要自己补充。
String time = content.substring(index - 10, index);
Integer year = DateTime.now().year();
Integer month = DateTime.now().monthBaseOne();
Integer day = Integer.parseInt(time.substring(6, 8));
Integer hour = Integer.parseInt(time.substring(4, 6));
Integer minutes = Integer.parseInt(time.substring(2, 4));
Integer seconds = Integer.parseInt(time.substring(0, 2));
String dateTimeStr = year + "/" + month + "/" + day + " " + hour + ":" + minutes + ":" + seconds;
DateTime dateTime = DateUtil.parseDateTime(dateTimeStr);
String timeStr = dateTime.toString();
szy206InfoDTO.setDataTime(timeStr);
mainBody.put(DataInfoConstant.DATA_TIME, timeStr);
mainHead.put(DataInfoConstant.DATA_TIME, DataInfoConstant.DATA_TIME_MEAN);
index -= 10;
}
String warningStatus=content.substring(index-8,index);
//暂时只解析电压状态
char[] binArr=warningStatus.toCharArray();
String warningStr="";
for(char ch:binArr){
String ss=String.valueOf(ch);
warningStr+=MathUtil.hexString2binaryString(ss);
}
//第二位为电压报警状态 0为不报警 1为报警
String voltageStr;
if(warningStr.substring(1,2).equals("0")){
voltageStr="正常";
}else{
voltageStr="报警";
}
mainBody.put(Szy206TypeEnum.VOLTAGE.getKey(),voltageStr);
mainHead.put(Szy206TypeEnum.VOLTAGE.getKey(),Szy206TypeEnum.VOLTAGE.getDescription());
index-=8;
String dataContent=content.substring(0,index);
if(Szy206TypeEnum.RAINFALL.getKey().equals(szy206InfoDTO.getDataType())){
//降雨量
deviceIndex=Szy206TypeEnum.RAINFALL.getDeviceIndex();
//每N个数组 代表一个数据
int oneDataLength=6;
//小数点位数
int decimalPoint=5;
parentDataInfo(szy206InfoDTO,dataContent,oneDataLength,decimalPoint,false,mainBody,mainHead);
}else if(Szy206TypeEnum.WATER_LEVEL.getKey().equals(szy206InfoDTO.getDataType())){
//水位
deviceIndex=Szy206TypeEnum.WATER_LEVEL.getDeviceIndex();
//每N个数组 代表一个数据
int oneDataLength=8;
//小数点位数
int decimalPoint=5;
parentDataInfo(szy206InfoDTO,dataContent,oneDataLength,decimalPoint,true,mainBody,mainHead);
}else if(Szy206TypeEnum.TRAFFIC.getKey().equals(szy206InfoDTO.getDataType())){
//流量
deviceIndex=Szy206TypeEnum.TRAFFIC.getDeviceIndex();
//每N个数组 代表一个数据
int oneDataLength=10;
//小数点位数
int decimalPoint=7;
int dataLength=dataContent.length();
if(dataLength%oneDataLength==0) {
int cycleNum = 0;
int flag=2;
while (dataLength > 0) {
try {
String data = dataContent.substring(cycleNum * oneDataLength, (cycleNum + 1) * oneDataLength);
cycleNum++;
dataLength -= oneDataLength;
char[] dataChar = data.toCharArray();
String result;
//实时流量在前 累计流量在后
if(flag%2==0){
//流量实时数据
result = char2String(dataChar, decimalPoint,true);
Double resultData=Double.parseDouble(result);
mainBody.put("flow",resultData);
mainHead.put("flow","流量");
log.info("遥测地址:"+szy206InfoDTO.getStationAddr()+" 类型:流量"+" 结果:"+resultData);
}else{
//累计流量(水量)
result = char2String(dataChar, 0,true);
Double resultData=Double.parseDouble(result);
mainBody.put("countFlow",resultData);
mainHead.put("countFlow","累计流量");
log.info("遥测地址:"+szy206InfoDTO.getStationAddr()+" 类型:累计流量"+" 结果:"+resultData);
}
flag++;
} catch (Exception e) {
System.out.println(e.toString());
}
}
}
}else if(Szy206TypeEnum.VELOCITY.getKey().equals(szy206InfoDTO.getDataType())){
//流速
deviceIndex=Szy206TypeEnum.VELOCITY.getDeviceIndex();
//每N个数组 代表一个数据
int oneDataLength=6;
//小数点位数
int decimalPoint=3;
parentDataInfo(szy206InfoDTO,dataContent,oneDataLength,decimalPoint,true,mainBody,mainHead);
}else if(Szy206TypeEnum.GATES.getKey().equals(szy206InfoDTO.getDataType())){
//闸位
deviceIndex=Szy206TypeEnum.GATES.getDeviceIndex();
//每N个数组 代表一个数据
int oneDataLength=6;
//小数点位数
int decimalPoint=4;
parentDataInfo(szy206InfoDTO,dataContent,oneDataLength,decimalPoint,false,mainBody,mainHead);
}else if(Szy206TypeEnum.POWER.getKey().equals(szy206InfoDTO.getDataType())){
//功率
deviceIndex=Szy206TypeEnum.POWER.getDeviceIndex();
//每N个数组 代表一个数据
int oneDataLength=6;
//小数点位数
int decimalPoint=0;
parentDataInfo(szy206InfoDTO,dataContent,oneDataLength,decimalPoint,false,mainBody,mainHead);
}else if(Szy206TypeEnum.AIR_PRESSURE.getKey().equals(szy206InfoDTO.getDataType())){
//气压
deviceIndex=Szy206TypeEnum.AIR_PRESSURE.getDeviceIndex();
//每N个数组 代表一个数据
int oneDataLength=6;
//小数点位数
int decimalPoint=0;
parentDataInfo(szy206InfoDTO,dataContent,oneDataLength,decimalPoint,false,mainBody,mainHead);
}else if(Szy206TypeEnum.WIND_SPEED.getKey().equals(szy206InfoDTO.getDataType())){
//风速
deviceIndex=Szy206TypeEnum.WIND_SPEED.getDeviceIndex();
//每N个数组 代表一个数据
int oneDataLength=6;
//小数点位数
int decimalPoint=4;
parentDataInfo(szy206InfoDTO,dataContent,oneDataLength,decimalPoint,false,mainBody,mainHead);
}else if(Szy206TypeEnum.WATER_TEMPERATURE.getKey().equals(szy206InfoDTO.getDataType())){
//水温
deviceIndex=Szy206TypeEnum.WATER_TEMPERATURE.getDeviceIndex();
//每N个数组 代表一个数据
int oneDataLength=4;
//小数点位数
int decimalPoint=3;
parentDataInfo(szy206InfoDTO,dataContent,oneDataLength,decimalPoint,false,mainBody,mainHead);
}else if(Szy206TypeEnum.MOISTURE_CONTENT.getKey().equals(szy206InfoDTO.getDataType())){
//土壤含水率
deviceIndex=Szy206TypeEnum.MOISTURE_CONTENT.getDeviceIndex();
//每N个数组 代表一个数据
int oneDataLength=4;
//小数点位数
int decimalPoint=4;
parentDataInfo(szy206InfoDTO,dataContent,oneDataLength,decimalPoint,false,mainBody,mainHead);
}else if(Szy206TypeEnum.WATER_PRESSURE.getKey().equals(szy206InfoDTO.getDataType())){
//水压
deviceIndex=Szy206TypeEnum.WATER_PRESSURE.getDeviceIndex();
//每N个数组 代表一个数据
int oneDataLength=8;
//小数点位数
int decimalPoint=6;
parentDataInfo(szy206InfoDTO,dataContent,oneDataLength,decimalPoint,false,mainBody,mainHead);
}else if(Szy206TypeEnum.WATER_QUALITY.getKey().equals(szy206InfoDTO.getDataType())){
//水质
deviceIndex=Szy206TypeEnum.WATER_QUALITY.getDeviceIndex();
//每N个数组 代表一个数据
int oneDataLength=8;
//前十位是检测种类
String typeStr=dataContent.substring(0,10);
//每位转成4位二进制数 前面补0
char[] typeChar=typeStr.toCharArray();
String binStr="";
for(char ch:typeChar){
binStr+=MathUtil.hexString2binaryString(String.valueOf(ch));
}
//根据2进制字符串判断 存在哪些水质参数 1代表存在。
char[] binaryTypeChar=binStr.toCharArray();
//存在粪大肠菌群的话 每5个字符代表一个数值 否则4个 (5+N*4(+1【参数含粪大肠菌群时】)+4 个字节。)
if(String.valueOf(binaryTypeChar[24]).equals("1")){
oneDataLength=10;
}
List<Integer> indexList=new ArrayList<>();
//记录1的位置
for(int i=0;i<binaryTypeChar.length;i++){
if(new StringBuilder(binaryTypeChar[i]).toString().equals("1")){
indexList.add(i);
}
}
//获取所有数据list
int dataLength=dataContent.length();
List<String> dataList=new ArrayList<>();
if(dataLength%oneDataLength==0) {
int cycleNum=0;
while (dataLength > 0) {
String data = dataContent.substring(cycleNum * oneDataLength, (cycleNum + 1) * oneDataLength);
dataList.add(data);
}
}
if(indexList.size()==dataList.size()){
for(int i=0;i<indexList.size();i++){
Szy206WaterQualityEnum szy206WaterQualityEnum =getQualityEnumByKey("D"+indexList.get(i));
if(ObjectUtil.isNotEmpty(szy206WaterQualityEnum)){
//N(3,1) oneDataLength-3+(3-1) ==oneDataLength-1; 00000123 小数位数为7
Integer decimalPoint=oneDataLength-Integer.parseInt(szy206WaterQualityEnum.getPrecision().split(",")[1].replace(")",""));
//TODO不确定是直接将数字倒序就行还是 以下方式(待验证)
Double value=Double.parseDouble(char2String(dataList.get(i).toCharArray(),decimalPoint,false));
String key=szy206WaterQualityEnum.getKey();
log.info("遥测地址:"+szy206InfoDTO.getStationAddr()+"类型:"+szy206WaterQualityEnum.getName()+"结果:"+value);
mainBody.put(key,value);
mainHead.put(key,szy206WaterQualityEnum.getName()+"("+szy206WaterQualityEnum.getUnit()+")");
}
}
}
}else if(Szy206TypeEnum.OTHERS.getKey().equals(szy206InfoDTO.getDataType())){
//电压
deviceIndex=Szy206TypeEnum.OTHERS.getDeviceIndex();
//每N个数字 代表一个数据
int oneDataLength=4;
//小数点位数 第N个数组后
int decimalPoint=2;
parentDataInfo(szy206InfoDTO,dataContent,oneDataLength,decimalPoint,false,mainBody,mainHead);
}else{
log.warn("数据类型未匹配:"+szy206InfoDTO.getDataType()+"报文为:"+szy206InfoDTO.getContent());
}
szy206InfoDTO.setDeviceIndex(deviceIndex);
szy206InfoDTO.setMainBody(mainBody);
szy206InfoDTO.setMainHead(mainHead);
}
}
public Szy206WaterQualityEnum getQualityEnumByKey(String key){
for(Szy206WaterQualityEnum e:Szy206WaterQualityEnum.values()){
if(e.getKey().equals(key)){
return e;
}
}
return null;
}
/**
* 解析正文
* @param szy206InfoDTO 解析实体
* @param dataContent 正文
* @param oneDataLength 一个数据长度
* @param decimalPoint 小数位数
* @param isNegative 是否判断正方
* @param mainBody 数据
* @param mainHead 头部
*/
public void parentDataInfo( Szy206InfoDTO szy206InfoDTO,String dataContent,Integer oneDataLength, int decimalPoint,Boolean isNegative,Map<String,Object> mainBody,Map<String,Object> mainHead){
int dataLength=dataContent.length();
if(dataLength%oneDataLength==0){
int cycleNum=0;
while (dataLength>0){
try{
String data=dataContent.substring(cycleNum*oneDataLength,(cycleNum+1)*oneDataLength);
cycleNum++;
dataLength-=oneDataLength;
char[] dataChar=data.toCharArray();
String result=char2String(dataChar,decimalPoint,isNegative);
Szy206TypeEnum szy206TypeEnum=getTypeEnum(szy206InfoDTO.getDataType());
//数据值
Double resultData=Double.parseDouble(result);
mainBody.put(szy206TypeEnum.getKey(),resultData);
mainHead.put(szy206TypeEnum.getKey(),szy206TypeEnum.getDescription());
log.info("遥测地址:"+szy206InfoDTO.getStationAddr()+" 类型:"+szy206TypeEnum.getDescription()+" 结果:"+result);
}catch (Exception e){
System.out.println(e.toString());
}
}
}
}
public Szy206TypeEnum getTypeEnum(String typeKey){
for(Szy206TypeEnum szy206TypeEnum:Szy206TypeEnum.values()){
if(szy206TypeEnum.getKey().equals(typeKey)){
return szy206TypeEnum;
}
}
return null;
}
@Override
public void insertData(String deviceId, String data) {
//判断device表中是否有数据
LambdaQueryWrapper<LotDevicePO> lambdaQueryWrapper=new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(LotDevicePO::getDeviceId,deviceId);
List<LotDevicePO> poList= lotDeviceMapper.selectList(lambdaQueryWrapper);
if(ObjectUtil.isEmpty(poList)){
JSONObject map= JSONUtil.parseObj(data);
LotDevicePO lotDevicePO=new LotDevicePO();
lotDevicePO.setDeviceId(deviceId);
lotDevicePO.setIsAdd(0);
//离线判断时间默认10分钟
lotDevicePO.setOfflineTime(10);
lotDevicePO.setDeviceType(FieldConstan.DEVICE_TYPE_SELF_BUILT);
lotDevicePO.setOnlineTime(DateTime.now());
lotDevicePO.setOnlineStatus(1);
if(ObjectUtil.isNotEmpty(map.get(DataInfoConstant.DEVICE_INDEX))){
lotDevicePO.setDeviceIndex(map.get(DataInfoConstant.DEVICE_INDEX).toString());
}
lotDevicePO.setProtocol("SZY206");
lotDeviceMapper.insert(lotDevicePO);
}
parserService.insertData(deviceId,data);
}
@Override
public void isOnline(String deviceId, Map<String, Object> msg) {
//修改状态采集时间
LambdaQueryWrapper<LotDevicePO> lambdaQueryWrapper=new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(LotDevicePO::getDeviceId,deviceId);
List<LotDevicePO> poList= lotDeviceMapper.selectList(lambdaQueryWrapper);
if(ObjectUtil.isNotEmpty(poList)&&poList.size()>0){
LotDevicePO lotSensorPO=poList.get(0);
lotSensorPO.setOnlineTime(DateTime.now());
lotSensorPO.setOnlineStatus(1);
lotDeviceMapper.update(lotSensorPO,lambdaQueryWrapper);
}
}
public void addVoltageData(Szy206InfoDTO szy206InfoDTO){
if(ObjectUtil.isNotEmpty(szy206InfoDTO.getMainBody().get("BD"))){
String dy=szy206InfoDTO.getMainBody().get("BD").toString();
log.info(szy206InfoDTO.getStationAddr()+"电压数据为"+szy206InfoDTO.getMainBody().get("BD"));
//将电压数据拼接到 原数据中
//查询出原数据
// *号 必须要加,否则无法模糊查询
String prefix = SZY_REDIS_KEY+szy206InfoDTO.getStationAddr()+"*";
// 获取所有addr相同的数据
Set<String> keys = redisTemplate.keys(prefix);
List<Object> myObjectListRedis = redisTemplate.opsForValue().multiGet(keys);
if(ObjectUtil.isNotEmpty(myObjectListRedis)&&myObjectListRedis.size()>0){
for(Object o:myObjectListRedis){
Szy206InfoDTO redisDto= BeanUtil.toBean(o,Szy206InfoDTO.class);
if(ObjectUtil.isNotEmpty(redisDto.getMainHead())&&ObjectUtil.isNotEmpty(redisDto.getMainBody())){
redisDto.getMainBody().put("BD",dy);
redisDto.getMainHead().put("BD","电压(v)");
//插入数据
insertData(redisDto.getDeviceId(),JSONUtil.toJsonStr(redisDto));
isOnline(redisDto.getDeviceId(),(Map<String, Object>) JSONUtil.parse(redisDto));
}
}
//删除redis
redisTemplate.delete(keys);
}
}
}
public void saveDataToRedis(Szy206InfoDTO szy206InfoDTO) {
String key=SZY_REDIS_KEY+szy206InfoDTO.getStationAddr()+ IdUtil.fastUUID();
redisTemplate.opsForValue().set(key,szy206InfoDTO);
}
public String char2String(char[] ch,Integer decimalPoint,Boolean isNegative){
//将char 2位分为一组 并倒序排列(低位在前高位在后)
int length=ch.length/2;
String[] dataArray=new String[length];
for(int i=0;i<ch.length;i+=2){
String c=ch[i]+""+ch[i+1];
dataArray[length-1]=c;
length--;
}
StringBuilder stringBuilder=new StringBuilder();
for(int j=0;j<dataArray.length;j++){
if(j==0){
if(isNegative){
//第一位代表+ -值
if(Integer.parseInt(dataArray[j].substring(0,1))!=0){
stringBuilder.append("-");
}else{
stringBuilder.append("+");
}
stringBuilder.append(dataArray[j].substring(1,2));
}else{
stringBuilder.append(dataArray[j]);
}
}else{
stringBuilder.append(dataArray[j]);
}
}
String resultDataStr=stringBuilder.toString();
//添加小数点
if(decimalPoint!=0){
resultDataStr=stringBuilder.toString().substring(0,decimalPoint)+"."+stringBuilder.toString().substring(decimalPoint,stringBuilder.length());
}
return resultDataStr;
}
//
}
/**
* 多项式 X^7+X^6+X^5+X^2+1 (0xE5)
* @param str
* @return
*/
public static String calcCrc8(String str) {
byte[] data = HexUtil.decodeHex(str);
int crc = 0x00;
int dxs = 0xE5;
int hibyte;
int sbit;
for (byte datum : data) {
crc = crc ^ datum;
for (int j = 0; j < 8; j++) {
sbit = crc & 0x80;
crc = crc << 1;
if (sbit != 0) {
crc ^= dxs;
}
}
}
String code = Integer.toHexString(crc & 0xff).toUpperCase();
return code;
}
更多推荐
所有评论(0)