szy:java解析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@Servicepublic 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; }

    相关推荐

    相关文章