(初次设计,如有错误之处,敬请指出,持续更新中)由于工作需要,需要搭建物联网终台,主要框架为:
1,协议包搭建: 总体使用netty框架。1,设计时确保只有一个程序入口。即:只用一个main函数,程序jar包运行不启动任何端口号,只能通过main函数,启动端口号,并执行协议解析操作。,协议解析时:要校验内容为:帧头:帧尾:数据长度,等信息,校验通过后,确认为一条合法帧,将设备解析后:通过http接口将数据上送到终台,执行相关业务,然后需要将程序的连接信息,存入到系统内存中。相关代码如下
程序入口:
/**
* 程序入口类
*/
public class StartService {
public static void main(String[] args) throws Exception {
List<Integer> ports = Arrays.asList(9999);
System.out.println("服务开始启动启动");
StartPlcInstruct.start();
ChineseProverbServer cps = new ChineseProverbServer();
cps.run(ports);
}
}
存入系统内存:
public class VoltmeterControllerPojo {
public static Map<String, DatagramPacket> mapPacket = new HashMap<String, DatagramPacket>();// 存放回复消息变量值
/**
* 存放发送消息时的变量,key为:设备地址a+设备地址b+packet,和 设备地址a+设备地址b+ctx
* */
public static Map<String, ChannelHandlerContext> mapContext = new HashMap<String, ChannelHandlerContext>();
}
VoltmeterControllerPojo.mapPacket.put(hexValue.substring(6, 10) + "packet", packet);
VoltmeterControllerPojo.mapContext.put(hexValue.substring(6, 10) + "ctx", ctx);
协议包通过http接口将数据上送到物联网终台:
HttpUtils.sendPost("http://localhost:6606/qsPlc/sendMqtt", "state="+data+"&adress="+adress);
2,物联网终台,新建类与协议包中的接口对应: 物联网终台新建一个类,用来接受设备数据,读取产品管理中设计的物模型,从服务/事件/属性三个维度,封装json,将设备上传数据上传到mqtt中。
相关代码:
/**
* 发送mqtt消息
*
* @param state 设备数据
* @param adress 设备地址
* @return
*/
@PostMapping("/sendMqtt")
public String sendMsg(String state, String adress) {
JSONObject finalObject = new JSONObject();// 请求参数的最终json数据
// 定义属性/服务/事件三个集合
JSONArray jsarray = new JSONArray();
finalObject.put("properties", jsarray);// 属性
mqttGateway.sendToMqtt(finalObject.toString(), MqttPlcTopicType.TOPIC);
return "success";
}
3,设备命令下发: ,新建类,订阅业务层下发的指令,将信息,存入redis队列中。相关代码
public class ReceiveMessageHandler implements MessageHandler {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
// String topic =
// message.getHeaders().get("mqtt_receivedTopic").toString();//订阅的主题
String msg = message.getPayload().toString();
System.out.println("接受到的消息 " + msg);
Gson gson = new Gson();
Map<String, Object> map = new HashMap<String, Object>();
map = gson.fromJson(msg, map.getClass());
String dataValue = map.get("dataValue").toString();
map.put("dataValue", FormatString.getEquimentString(dataValue));
String str = JSON.toJSONString(map);
// 处理设备控制,将下发的指令,存放到redis中
RedisUtil.setListValue(RedisToPic.TOPIC, str);
}
}
设备控制命令下发: 在协议jar包处,启动定时任务,定时扫描redis队列,如果获取到redis存在待发送的指令,则取出消费,对设备下发控制指令。主要代码:
public class StartPlcInstruct {
public static void start() {
System.out.println("线程开始启动:定时查询需要下发的命令");
/**
* Runnable:实现了Runnable接口,jdk就知道这个类是一个线程
*/
Runnable runnable = new Runnable() {
// 创建 run 方法
public void run() {
Jedis consumer = RedisUtil.getJedis();
String value = consumer.lpop("QS_PLC");
if (value != null) {
// 有数据则继续消费
Gson gson = new Gson();
Map<String, Object> map = new HashMap<String, Object>();
map = gson.fromJson(value, map.getClass());
try {
PlcController.voltmeterController("03", map.get("dataValue").toString(),
map.get("adress").toString());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
// 做为并发工具类被引进的,这是最理想的定时任务实现方式。
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
service.scheduleAtFixedRate(runnable, 10, 3, TimeUnit.SECONDS);
}
}
至此:设备的注册、上报、控制,整体框架已经搭建完毕。整体目录结构如下:
这样搭建仍然有一些问题如下:诸位如果有解决方案,可私聊:
一:会存在命令失效的情况;
使用场景:1,设备在物联网终台已经注册过数据,此时关闭或者重启协议包:qs_iot_plc
原因:由于将设备的连接session存放在内存中,重启或者关闭协议包,导致内存重新加载将之间的连接信息清空。此时发送指令,由于获取不到连接信息,导致命令失效
二:存在命令未发送,或者命令发送失败,命令在redis中消失的情况
使用场景:在步骤一的基础上操作
原因:物联网终台:qs_iot,接受到业务层下发的指令,将信息存放到redis缓存中,协议包:qs_iot_plc,启动定时任务取消费指令队列,由于已经消费过指令队列中的数据,即使发送失败,数据也将不存在。
摸索中前行,欢迎广大同行,前来一起探讨