(初次设计,如有错误之处,敬请指出,持续更新中)由于工作需要,需要搭建物联网终台,主要框架为:
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,启动定时任务取消费指令队列,由于已经消费过指令队列中的数据,即使发送失败,数据也将不存在。
摸索中前行,欢迎广大同行,前来一起探讨