1、消防主机设置
2、接口文档
3、编写程序
server端加上特殊字符解码器
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
//分隔符
ByteBuf delimiter = Unpooled.copiedBuffer("##".getBytes());
//特殊字符结束符包解析
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(ParamManage.MAXLENGTH,
false,
true,
delimiter));
ch.pipeline().addLast(new ServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 1024)
.childOption(ChannelOption.SO_KEEPALIVE, true);
处理handler
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String strReq = Util.bytesToHexString(req);
log.info("收到的请求是:" + strReq);
if(StrUtil.isNotEmpty(strReq)){
log.info(AnalysisUtil.getSendTime(req));
log.info(AnalysisUtil.getSourceAddress(req));
log.info(AnalysisUtil.getDestinationAddress(req));
int unitLength = (req[24] & 0xff) + ((req[25] & 0xff) << 8);
log.info("unitLength = " + unitLength);
int command = req[26] & 0xff;
log.info("command = " + command);
int messageLength = 30;
if (unitLength > 0 && req.length == messageLength + unitLength){
servicesHandle(req);
}
if (req.length != messageLength + unitLength){
byte[] result = new ResponseBean().fail(req);
log.info("MESSAGE LENGTH ERROR,FAIL MESSAGE:" + Util.bytesToHexString(result));
ByteBuf echo = Unpooled.copiedBuffer(result);
ctx.writeAndFlush(echo);
}else{
byte[] result = new ResponseBean().success(req);
log.info("RIGHT,RESPOND:" + Util.bytesToHexString(result));
ByteBuf echo = Unpooled.copiedBuffer(result);
ctx.writeAndFlush(echo);
}
}else{
ByteBuf echo= Unpooled.copiedBuffer(new ResponseBean().wrongFormat());
ctx.writeAndFlush(echo);
}
super.channelRead(ctx, msg);
}
统一返回bean
public class ResponseBean {
private static final String SUCCESS = "SUCCESS";
private static final String FAIL = "FAIL";
public byte[] success(byte[] bytes){
return handle(bytes,SUCCESS);
}
public byte[] wrongFormat(){
return handle(null,null);
}
public byte[] fail(byte[] bytes){
return handle(bytes,FAIL);
}
private byte[] handle(byte[] bytes ,String command){
Calendar c = Calendar.getInstance();
int second = c.get(Calendar.SECOND) & 0xff;
int minute = c.get(Calendar.MINUTE) & 0xff;
int hour = c.get(Calendar.HOUR_OF_DAY) & 0xff;
int day = c.get(Calendar.DATE) & 0xff;
int month = (c.get(Calendar.MONTH) + 1) & 0xff;
int year = Integer.parseInt(String.valueOf(c.get(Calendar.YEAR)).substring(2)) & 0xff;
if(SUCCESS.equals(command) || FAIL.equals(command)){
byte[] comBytes = new byte[] { bytes[2], bytes[3], (byte)0x01, (byte)0x01,
(byte)second, (byte)minute, (byte)hour,(byte)day,(byte)month,(byte)year,
bytes[18], bytes[19], bytes[20],bytes[21],bytes[22],bytes[23],
bytes[12], bytes[13], bytes[14],bytes[15],bytes[16],bytes[17],(byte)0x00, (byte)0x00, SUCCESS.equals(command) ? (byte)0x03 : (byte)0x06};
byte crc = calCRC(comBytes);
byte[] result = new byte[]{(byte)0x40, (byte)0x40,
bytes[2], bytes[3], (byte)0x01, (byte)0x01,
(byte)second, (byte)minute, (byte)hour,(byte)day,(byte)month,(byte)year,
bytes[18], bytes[19], bytes[20],bytes[21],bytes[22],bytes[23],
bytes[12], bytes[13], bytes[14],bytes[15],bytes[16],bytes[17],(byte)0x00, (byte)0x00, SUCCESS.equals(command) ? (byte)0x03 : (byte)0x06,
crc,(byte)0x23, (byte)0x23};
return result;
}else{
byte[] comBytes = new byte[] { (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x01,
(byte)second, (byte)minute, (byte)hour,(byte)day,(byte)month,(byte)year,
(byte)0x00, (byte)0x00, (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00, (byte)0x00,(byte)0x06};
byte crc = calCRC(comBytes);
byte[] result = new byte[]{(byte)0x40, (byte)0x40,
(byte)0x00, (byte)0x00, (byte)0x01, (byte)0x01,
(byte)second, (byte)minute, (byte)hour,(byte)day,(byte)month,(byte)year,
(byte)0x00, (byte)0x00, (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00, (byte)0x00,(byte)0x06,
crc,(byte)0x23, (byte)0x23};
return result;
}
}
private byte calCRC(byte[] data){
int sum = 0, len = data.length;
for (int i = 0; i < len; i++){
sum += data[i];
}
byte crc = (byte)(sum & 0xff);
return crc;
}
}
应用数据单元类型标志定义表 | ||
类型值 | 说明 | 方向 |
0 | 预留 | 上行 |
1 | 上传建筑消防设施系统状态 | |
2 | 上传建筑消防设施部件运行状态 | |
3 | 上传建筑消防设施部件模拟量值 | |
4 | 上传建筑消防设施操作信息 | |
5 | 上传建筑消防设施软件版本 | |
6 | 上传建筑消防设施系统配置情况 | |
7 | 上传建筑消防设施部件配置情况 | |
8 | 上传建筑消防设施系统时间 | |
9~20 | 预留(建筑消防设施信息) | |
21 | 上传用户信息传输装置运行状态 | |
22 | 预留 | |
23 | 预留 | |
24 | 上传用户信息传输装置操作信息 | |
25 | 上传用户信息传输装置软件版本 | |
26 | 上传用户信息传输装置配置情况 | |
27 | 预留 | |
28 | 上传用户信息传输装置系统时间 | |
29~40 | 预留(用户信息传输装置信息) | |
41~60 | 预留(控制信息) | |
61 | 读建筑消防设施系统状态 | 下行 |
62 | 读建筑消防设施部件运行状态 | |
63 | 读建筑消防设施部件模拟量值 | |
64 | 读建筑消防设施操作信息 | |
65 | 读建筑消防设施软件版本 | |
66 | 读建筑消防设施系统配置情况 | |
67 | 读建筑消防设施部件配置情况 | |
68 | 读建筑消防设施系统时间 | |
69~80 | 顶留 | |
81 | 读用户信息传输装置运行状态 | |
82 | 预留 | |
83 | 预留 | |
84 | 读用户信息传输装置操作信息记录 | |
85 | 读用户信息传输装置软件版本 | |
86 | 读用户信息传输装置配置情况 | |
87 | 预留 | |
88 | 读用户信息传输装置系统时间 | |
89 | 初始化用户信息传输装置 | |
90 | 同步用户信息传输装置时钟 | |
91 | 查岗命令 | |
92~127 | 预留 | |
128~254 | 用户自定义 | |
128 | 上传用户信息传输装置生产日期 | 上行 |
129 | 上传用户报名信息 | |
130 | 上传用户信息传输装置开机时间信息 | |
131 | 上传用户信息传输装置关机时间信息 | |
132 | 上传建筑消防设施系统开机信息 | |
133 | 上传建筑消防设施系统关机信息 | |
134 | 上传建筑消防设施系统状态恢复 | |
135 | 上传建筑消防设施部件运行状态恢复 | |
136 | 上传用户信息传输装置运行状态恢复 | |
137 | 上传建筑消防设施部件运行状态(自定义) | |
138 | 上传建筑消防设施部件运行状态(自定义)恢复 | |
139 | 上传用户信息传输装置用户身份识别信息 | |
140 | 上传建筑消防设施系统状态(自定义) | |
141 | 上传建筑消防设施系统状态(自定义)恢复 | |
188 | 读用户信息传输装置生产日期 | 下行 |
189 | 设置用户信息传输装置报名时间 | |
197 | 读建筑消防设施部件运行状态(自定义) |
demo目前处理2种类型
case 2 处理方法
public static void faultAlarm(byte[] bytes, int nums){
for (int i = 0; i < nums; i++) {
//系统类型
int sysType = bytes[29 + i * 46] & 0xff;
//系统地址
int sysAddress = bytes[30 + i * 46] & 0xff;
//部件地址
int parts = bytes[31 + i * 46] & 0xff;
log.info("sysType:"+sysType+" sysAddress:"+sysAddress+" parts:"+parts);
int mill = (bytes[69+ i * 46] & 0xff);
int minute = (bytes[70+ i * 46] & 0xff);
int hour = (bytes[71+ i * 46] & 0xff);
int day = (bytes[72+ i * 46] & 0xff);
int month = (bytes[73+ i * 46] & 0xff);
int year = (bytes[74+ i * 46] & 0xff);
Date createTime = null;
try {
createTime = new SimpleDateFormat( "yy-MM-dd HH:mm:ss" ).parse(year+"-"+month+"-"+day+ " " +
hour + ":"+ minute + ":" + mill);
} catch (ParseException e) {
e.printStackTrace();
}
// 上传部件地址与消防系统设备地址的对应关系
String deviceAddress = AddressMapping.getDeviceAddress(bytes[32 + i * 46], bytes[33 + i * 46], bytes[34 + i * 46], bytes[35 + i * 46]);
//火警
int data1 = bytes[36+ i * 46] >>> 1 & 0x1;
//故障
int data2 = bytes[36+ i * 46] >>> 2 & 0x1;
if(data1 == 1){
log.warn("火警"+deviceAddress+" "+createTime);
}
if(data2 == 1){
log.warn("故障"+deviceAddress+" "+createTime);
}
}
}
地址对应关系可以查看文档
附上地址对应处理方法。部分type 试过 ,常用为0,1,2,3
public static String getDeviceAddress(byte lowBit, byte highBit, byte lowArea, byte highArea){
int bitCode;
bitCode = (lowBit & 0xff);
bitCode += (highBit & 0xff) << 8;
int areaCode;
areaCode = (lowArea & 0xff);
areaCode += (highArea & 0xff) << 8;
log.info("bitCode:"+bitCode+" areaCode:"+areaCode);
String tempAreaCode = String.format("%05d", areaCode);
String arrCode="";
switch (TYPE){
case 0 :
//回路号 -设备号
arrCode = areaCode + "-" + bitCode;
break;
case 1 :
//主机号2 -回路号3 -设备号
arrCode = Integer.parseInt(tempAreaCode.substring(0,2)) + "-" + Integer.parseInt(tempAreaCode.substring(2)) + "-" + String.format("%03d", bitCode);
break;
case 2 :
//主机号3 -回路号2 -设备号
arrCode = Integer.parseInt(tempAreaCode.substring(0,3)) + "-" + Integer.parseInt(tempAreaCode.substring(3)) + "-" + String.format("%03d", bitCode);
break;
case 3 :
arrCode += highArea & 0xff;
arrCode += lowArea & 0xff;
arrCode += highBit & 0xff;
arrCode += lowBit & 0xff;
break;
case 4 :
//栋/区/层/设备号
arrCode += ((highArea & 0xff) +"/");
arrCode += ((lowArea & 0xff) +"/");
arrCode += ((highBit & 0xff) +"/");
arrCode += ((lowBit & 0xff) +"/");
break;
default:
break;
}
return arrCode;
}
4、测试报文
------无数据包
4040 @@
0000 流水号
01 主版本号
01 协议版本号
07 7秒
1D 29分
0A 10时
1B 27日
04 4月
16 22年
F60100000000 源地址
FE7900000000 目标地址
0000 应用数据长度
02 命令
D5 校验和
2323 ##
------手报
4040
0100
01
01
30 30s
2A 42
0D 13h
07
05
16
F60100000000
FE7900000000
0A 00 10L
02
1801040A002A0D070516 数据长度 18 为24是系统类型标志( 上传用户信息传输装置操作信息) 002A0D070516 时间
86
2323
------火警
4040
0500
01
01
16
2A
0E
11
05
16
410100000000
2A3000000000
3000 48L
02
02010101007100E903020020202020202020202020202020202020202020202020202020202020202000002A0E110516
D7
2323
02
01
01 系统类型
01 系统地址
00 部件类型
7100 位号(设备号) 113 01110001 00000000
E903 区号( )1001 11101001 00000011
0200 数据
20202020202020202020202020202020202020202020202020202020202000 部件说明
002A0E110516 时间