springboot+socketio+动态订阅mqtt

1.依赖 socket

<!-- netty-socketio-->
<dependency>
    <groupId>com.corundumstudio.socketio</groupId>
    <artifactId>netty-socketio</artifactId>
    <version>1.7.13</version>
</dependency>
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-resolver</artifactId>
    <version>4.1.15.Final</version>
</dependency>
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-transport</artifactId>
    <version>4.1.15.Final</version>
</dependency>
<dependency>
    <groupId>io.socket</groupId>
    <artifactId>socket.io-client</artifactId>
    <version>1.0.0</version>
</dependency>

mqtt依赖

<dependency>
	    <groupId>org.springframework.integration</groupId>
	    <artifactId>spring-integration-stream</artifactId>
	</dependency>
	<dependency>
	   <groupId>org.springframework.integration</groupId>
	   <artifactId>spring-integration-mqtt</artifactId>
	</dependency>

2.配置

socketIo:
  serverIp: 127.0.0.1   #设置socket所在ip地址 前后端约定地址
  socketPort: 9098        #socket端口  前后端约定端口
  pingInterval: 25000     #Ping消息间隔(毫秒)
  pingTimeout: 60000      #Ping消息超时时间(毫秒)

3.socketio配置类

package com.xgit.iot.service.nettySocketIo;

import com.bkrwin.ufast.infra.infra.log.LogHelper;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.SpringAnnotationScanner;
import com.xgit.iot.service.nettySocketIo.model.SocketProperties;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.net.InetAddress;

/**
 * 配置socketIo的bean
 */
@Configuration
public class NettySocketConfig {
    @Autowired
    private SocketProperties socketProperties;
    @Bean
    public SocketIOServer socketIOServer() {
        /*
         * 创建Socket,并设置监听端口
         */
        com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration();
        // 设置主机名,默认是0.0.0.0
        config.setHostname(socketProperties.getServerIp());
        // 设置监听端口
        config.setPort(socketProperties.getSocketPort());
        // 协议升级超时时间(毫秒),默认10000。HTTP握手升级为ws协议超时时间
        config.setUpgradeTimeout(10000);
        // Ping消息间隔(毫秒),默认25000。客户端向服务器发送一条心跳消息间隔
        config.setPingInterval(socketProperties.getPingInterval());
        // Ping消息超时时间(毫秒),默认60000,这个时间间隔内没有接收到心跳消息就会发送超时事件
        config.setPingTimeout(socketProperties.getPingTimeout());
        // 握手协议参数使用JWT的Token认证方案
        config.setAuthorizationListener(data -> {
        // 这里可以做参数校验
        //socketio 心跳功能,不间断的进行访问,可以不间断校验
        //如果只想校验第一次,则不在这里做校验
                String userId = data.getSingleUrlParam("userId");

			return true;
        });
        return new SocketIOServer(config);
    }
    @Bean
    public SpringAnnotationScanner springAnnotationScanner(SocketIOServer socketServer) {
        return new SpringAnnotationScanner(socketServer);
    }

4.启动类

package com.xgit.iot.service.nettySocketIo;

import com.bkrwin.ufast.infra.infra.log.LogHelper;
import com.corundumstudio.socketio.SocketIOServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
 * SpringBoot启动之后执行
 */
@Component
@Order(1)
public class ServerRunner implements CommandLineRunner {
    @Autowired
    private SocketIOServer server;
    @Override
    public void run(String... args) {
        LogHelper.monitor("SocketIOServerRunner 开始启动啦...");
        try {
            server.start();
        } catch (Exception e) {
            LogHelper.fatal("socket.io启动失败!!! ", e);
            String hostName = server.getConfiguration().getHostname();
            LogHelper.fatal("hostName=" + hostName, e);
        }
    }
}

5.添加socket三大事件

package com.xgit.iot.service.nettySocketIo;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.bkrwin.ufast.infra.infra.log.LogHelper;
import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.HandshakeData;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.annotation.OnConnect;
import com.corundumstudio.socketio.annotation.OnDisconnect;
import com.corundumstudio.socketio.annotation.OnEvent;
import com.xgit.iot.infra.ErrorCode;
import com.xgit.iot.infra.mqtt.MqttReceiveConfig;
import com.xgit.iot.service.machine.MocVariateConfigService;
import com.xgit.iot.service.nettySocketIo.model.ClientVariateConfig;
import com.xgit.iot.service.nettySocketIo.model.MachineVariateValueVo;
import com.xgit.iot.service.nettySocketIo.model.Message;
import com.xgit.iot.service.vo.machine.MocVariateConfigByCloudConfigVO;


/**
 * 消息事件处理器 详细参考https://socket.io/
 */
@Component
public class MessageEventHandler {

    @Autowired
    MqttReceiveConfig mqttReceiveConfig;


    @Autowired
    private MocVariateConfigService mocVariateConfigService;
    
    
    /**
     * 添加connect事件,当客户端发起连接时调用
     *
     * @param client
     */
    @OnConnect
    public void onConnect(SocketIOClient client) {
        try {
            if (client != null) {
                String sessionId = client.getSessionId().toString();
                HandshakeData handData = client.getHandshakeData();
                String userId = handData.getSingleUrlParam("userId");
                String orgId = handData.getSingleUrlParam("orgId");// 之前业务用
                String visionId = handData.getSingleUrlParam("visionId");
                String machineCodeAndVariateCode = handData.getSingleUrlParam("machineCodeAndVariateCodeList");

                String[] machineCodeAndVariateCodes = machineCodeAndVariateCode.split(",");
                String frequency = handData.getSingleUrlParam("frequency");// icon表达式实时计算用

                if (StringUtils.isBlank(orgId)) {
                    LogHelper.monitor("连接失败,租户为空");
                    return;
                } else if (ObjectUtils.isNotEmpty(machineCodeAndVariateCodes) && StringUtils.isNotBlank(visionId)) {

                    //1.查询模板  业务逻辑 无须理会
                    List<MocVariateConfigByCloudConfigVO> mocVariateConfigList = mocVariateConfigService
                            .listByCloudConfig(machineCodeAndVariateCodes);
                    if (ObjectUtils.isEmpty(mocVariateConfigList)) {
                        LogHelper.monitor("连接失败,采点为空, visionId=" + visionId + ", sessionId=" + client.getSessionId());
                        return;
                    }
                    
                    //2.存入推送池  存client 同时 讲业务模板存入  无须理会业务模板
                    SocketIoServerMapUtil.put(mocVariateConfigList, visionId + "|" + sessionId, client);
                    List<MocVariateConfigByCloudConfigVO> noNeedCaledConfigVo = new ArrayList<>();
                    Set<String> dpuList = noNeedCaledConfigVo.stream()
                            .map(MocVariateConfigByCloudConfigVO::getDpuCode).collect(Collectors.toSet());
                    
                    //3.不计算部分mqtt加入订阅     这里通过mqtt 触发推送 
                    mqttReceiveConfig.addVariateTopic(visionId + "|" + sessionId, orgId, dpuList);
                    LogHelper.monitor("连接成功, visionId=" + visionId);
                } else {
                    client.joinRoom(orgId); // room房间通知 内置session socket自带功能 会通过roomid发送给所有此orgid的用户
                    LogHelper.monitor(
                            "连接成功, sessionId=" + sessionId + ", orgId=" + orgId);
                }
            } else {
                LogHelper.error("客户端为空", ErrorCode.Failure.getCode());
            }
        } catch (Exception e) {
            LogHelper.fatal("捕获到socket.io异常", e);
        }
    }

    /**
     * 添加@OnDisconnect事件,客户端断开连接时调用,刷新客户端信息
     *
     * @param client
     */
    @OnDisconnect
    public void onDisconnect(SocketIOClient client) {
        String sessionId = client.getSessionId().toString();
        LogHelper.monitor("客户端断开连接, sessionId=" + sessionId);
        HandshakeData handData = client.getHandshakeData();
        
        String userId = handData.getSingleUrlParam("userId");
        String orgId = handData.getSingleUrlParam("orgId");// 之前业务用
        String visionId = handData.getSingleUrlParam("visionId");
        String machineCodeAndVariateCode = handData.getSingleUrlParam("machineCodeAndVariateCodeList");
        String[] machineCodeAndVariateCodes = machineCodeAndVariateCode.split(",");

        if (ObjectUtils.isNotEmpty(machineCodeAndVariateCodes) && StringUtils.isNotBlank(visionId) && StringUtils.isNotBlank(sessionId)) {
            //清除池子 并清除mqtt
            boolean isRemove = SocketIoServerMapUtil.remove(Arrays.asList(machineCodeAndVariateCodes), visionId + "|" + sessionId);
            if (isRemove) {
                //清除mqtt订阅  对接mqtt 无须理会
                mqttReceiveConfig.removeVariateTopic(visionId + "|" + sessionId, orgId);
            }
        } else if (StringUtils.isNotBlank(orgId)) {
            client.leaveRoom(orgId); 
        }
        client.disconnect();
    }

    /**
     * 前后端消息监听,确保消息链接无误
     * 
     * @param client
     * @param ackRequest
     * @param message    前段参数
     */
    //visionMessage 后台监测前段发送消息,确保消息无误 前段发送方法 emit socket.emit('visionMessage', JSON.stringify({userId:"xxxxxx"}));
    @OnEvent(value = "visionMessage")
    public void visionMessage(SocketIOClient client, AckRequest ackRequest, Message msg) {
        //对服务器的响应
        ackRequest.sendAckData("服务器回答message" + ",message=" + msg.getMessage());
        //
        //visionMessage 这里的的标签与前段约定一致,前段监测 visionMessage事件,与上面的visionMessage不是同一个
        //若无法区分 可以改名 message  前段监控此事件 socket.on('message', function (data) {
        client.sendEvent("visionMessage", "");
    }

    
    /**
     * 获取变动的对象 组态画面后端主动推送
     * 
     * @param alarmMessage
     */
    public void batchSendVisionMessage(List<MachineVariateValueVo> machineVariateValueList) {
        // visionID -->{client,configMap}
        Map<String,ClientVariateConfig> configSet = SocketIoServerMapUtil.getClientList(machineVariateValueList);
        configSet.values().forEach(clientVariateConfig -> {
            List<MocVariateConfigByCloudConfigVO> configList = new ArrayList<MocVariateConfigByCloudConfigVO>(
                    clientVariateConfig.getConfigMap().values());
            //取出 client 发 消息内容
            clientVariateConfig.getClient().sendEvent("visionMessage", configList);
        });
    }
}

cleint池子

package com.xgit.iot.service.nettySocketIo;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.corundumstudio.socketio.SocketIOClient;
import com.xgit.iot.service.nettySocketIo.model.ClientVariateConfig;
import com.xgit.iot.service.nettySocketIo.model.MachineVariateValueVo;
import com.xgit.iot.service.vo.machine.MocVariateConfigByCloudConfigVO;

public class SocketIoServerMapUtil {

    //根据id存放client 因为业务关系,写的比较复杂 无须理会,自己存自己client即可
    //     private static Map<String, List<SocketIOClient>> socketClientMap = new ConcurrentHashMap<>();
    //Map<tag, Map<visionId|userId, ClientVariateConfig>>
    private static Map<String, Map<String, ClientVariateConfig>> socketClientMap = new ConcurrentHashMap<>();

    /**
     * 为每个tag增加当前 visionId->client
     * 
     * @param tagList  machineCode + variateCode
     * @param visionId
     * @param client TODO 待自测
     */
    public static void put(List<MocVariateConfigByCloudConfigVO> mocVariateConfigList, String visionAndUserId,
            SocketIOClient client) {
        //Map<tag,vo> 模板map 通过map 方便命中tag修改值
        Map<String, MocVariateConfigByCloudConfigVO> configMap = mocVariateConfigList.stream().collect(
                Collectors.toMap(v -> v.getMachineCode() + "|" + v.getVariateCode(), Function.identity(), (v1, v2) -> v2));

        //这里不要直接用map循环,因为里面操作了map
        mocVariateConfigList.forEach(variateConfigVo -> {
            String tag = variateConfigVo.getMachineCode() + "|" + variateConfigVo.getVariateCode();
            Map<String, ClientVariateConfig> cilentMap = socketClientMap.get(tag);
            if (null == cilentMap) {
                cilentMap = new HashMap<>();
            }
            // 池子已经存在 换新的模板   tag中 vision相同  则 存同  client 同 configMap
            if (cilentMap.containsKey(visionAndUserId)) {
                ClientVariateConfig clientVariateConfig = cilentMap.get(visionAndUserId);
                clientVariateConfig.setNewClient(client, configMap);
            }else {
                // 有的tag可能是 第一次 有的tag可能是好几次 所以每次都搞新对象
                ClientVariateConfig clientVariateConfig = new ClientVariateConfig(client, configMap);
                cilentMap.put(visionAndUserId, clientVariateConfig);
            }
            socketClientMap.put(tag, cilentMap);
        });

    }
    
    /**
     * 移除每个tag中 visionId->client 如果tag没有visionId也移除
     * 
     * @param tagList
     * @param visionId
     * @return false没有做清除操作 true 做了清除操作
     */
    public static boolean remove(List<String> tagList, String visionAndUserId) {
        boolean isNeedRemove = false;
        for (String tag : tagList) {
            Map<String, ClientVariateConfig> cilentMap = socketClientMap.get(tag);// tag下每个客户端
            if (null != cilentMap) {
                ClientVariateConfig clientVariateConfig = cilentMap.get(visionAndUserId);
                //数量-1 如果>0则不动
                if (clientVariateConfig.decrementAndGet() <= 0) {
                    cilentMap.remove(visionAndUserId);// 移除所有
                    isNeedRemove = true;
                }
                if (cilentMap.isEmpty()) {
                    socketClientMap.remove(tag);
                }
            }
        }
        return isNeedRemove;
    }

    /**
     * 根据tag
     * @param machineVariateValueList 大数据返回的对象
     * @return
     */
    public static Map<String, ClientVariateConfig> getClientList(List<MachineVariateValueVo> machineVariateValueList) {
        //visionId ->ClientVariateConfig
        //同一个画面 对象不同 但时对象 内 client跟map相同
        Map<String, ClientVariateConfig> valueMap = new HashMap<>();
        machineVariateValueList.forEach(machineVariateValueVo -> {
            // Map<vision,ClientVariateConfig>
            Map<String, ClientVariateConfig> cilentMap = socketClientMap.get(machineVariateValueVo.getTag());
            if (null == cilentMap) {
                return;// 等于continue
            }
            // Map<tag, Map<visionId|userId, ClientVariateConfig>>  同  client 同 configMap
            //  只修改对象中tag自己的部分
            cilentMap.forEach((visionAndUserId,variateconfig) -> {
                Map<String,MocVariateConfigByCloudConfigVO> configMap = variateconfig.getConfigMap();
                MocVariateConfigByCloudConfigVO configVo = configMap.get(machineVariateValueVo.getTag());
                configVo.setVariateValue(machineVariateValueVo.getValue().toString());
                valueMap.put(visionAndUserId, variateconfig);//根据id 存对象 避免一样内容对象反复出现
            });
        });
        return valueMap;
    }
    
    /**
     * 从tag:machineCode|variateCode 取 machineCode
     * @return
     */
    public static Set<String> getMachineListFromTag(){
        Set<String> machineSet = new HashSet<String>();
        socketClientMap.keySet().forEach(key->{
            machineSet.add(key.split("\\|")[0]);
        });
        return machineSet;
    }
}

mqtt配置类 +动态 订阅

package com.xgit.iot.infra.mqtt;


import java.text.MessageFormat;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.IntegrationComponentScan;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.core.MessageProducer;
import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter;
import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessagingException;

import com.alibaba.fastjson.JSONObject;
import com.xgit.iot.infra.mqtt.model.MachineAndVariateValueVo;
import com.xgit.iot.infra.mqtt.service.MqttReceiveService;

import lombok.extern.slf4j.Slf4j;

@Configuration
@IntegrationComponentScan
@Slf4j
public class MqttReceiveConfig {

    @Value("${mqtt.username}")
    private String username;

    @Value("${mqtt.password}")
    private String password;

    @Value("${mqtt.host}")
    private String hostUrl;

    @Value("${mqtt.clientid}")
    private String clientId;

    @Value("${mqtt.timeout}")
    private short timeOut;

    @Value("${mqtt.keepalive}")
    private short keepAlive;

    @Value("${mqtt.defaulttopic}")
    private String variateDefaultTopic;
    
    /**
     * 事实上不同的visionId dpu有可能是一样的  关闭当前不能影响其他画面
     * 同一个画面允许多个租户打开  关闭当前画面 不能影响其他用户
     * Map<dpu,List<visionAndUserId>>
     */
    private static Map<String, Set<String>> topicMap = new ConcurrentHashMap<>();

    private MqttPahoMessageDrivenChannelAdapter adapter;
    
    @Autowired
    private MqttReceiveService mqttReceiveService;

    @Bean
    public MqttConnectOptions getMqttConnectOptions() {
        MqttConnectOptions mqttConnectOptions = new MqttConnectOptions();
        mqttConnectOptions.setCleanSession(true);
        mqttConnectOptions.setUserName(username);
        mqttConnectOptions.setPassword(password.toCharArray());
        mqttConnectOptions.setServerURIs(new String[] {hostUrl});
        mqttConnectOptions.setConnectionTimeout(timeOut);
        mqttConnectOptions.setKeepAliveInterval(keepAlive);
        mqttConnectOptions.setAutomaticReconnect(true);
        return mqttConnectOptions;
    }

    @Bean
    public MqttPahoClientFactory mqttClientFactory() {
        DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
        factory.setConnectionOptions(getMqttConnectOptions());
        return factory;
    }

    /**
     * 接收通道 可以配置多个 mqttInputChannel1 mqttInputChannel2
     * 
     * @return
     */
    @Bean
    public MessageChannel mqttInputChannel() {
        return new DirectChannel();
    }

    /**
     * 每个inbound对应每个channel client 监听topic
     * 
     * @return
     */
    @Bean
    public MessageProducer inbound() {
        //默认主题模板
        adapter = new MqttPahoMessageDrivenChannelAdapter(clientId + "_inbound", mqttClientFactory(), variateDefaultTopic);
        adapter.setCompletionTimeout(timeOut);
        adapter.setConverter(new DefaultPahoMessageConverter());
        adapter.setQos(1);
        adapter.setOutputChannel(mqttInputChannel());
        log.info("mqtt receiver start success");
        return adapter;
    }

    /**
     * 获取通道数据 与channel一一对应
     * 
     * @return
     */
    @Bean
    @ServiceActivator(inputChannel = "mqttInputChannel")
    public MessageHandler handler() {
        return new MessageHandler() {
            @Override
            public void handleMessage(Message<?> message) throws MessagingException {
                // 不同的topic取不同的值
                String topic = message.getHeaders().get("mqtt_receivedTopic").toString();
                mqttReceiveService.handlerVariateMqttMsg(topic, JSONObject.parseObject(message.getPayload().toString(), MachineAndVariateValueVo.class));
            }
        };
    }

    /**
     * 动态添加采点主题
     * @param topicArr
     */
    public void addVariateTopic(String visionAndUserId, String orgId, Set<String> dpuSet) {
        if (StringUtils.isBlank(visionAndUserId) || StringUtils.isBlank(orgId) || ObjectUtils.isEmpty(dpuSet)) {
            return;
        }
        if (null == adapter) {
            adapter = new MqttPahoMessageDrivenChannelAdapter(clientId + "_inbound", mqttClientFactory(), variateDefaultTopic);
            adapter.removeTopic(variateDefaultTopic);
        }

        dpuSet.forEach(dpu->{
            Set<String> visionAndUserSet = topicMap.get(dpu);// Map<dpu,List<visionAndUserId>> 原先添加的tag
            if (null == visionAndUserSet) {
                visionAndUserSet = new HashSet<>();
                topicMap.put(dpu, visionAndUserSet);
            }
            //visionId本身区分orgId 无需再判断
            //已经存在 不需要订阅
            if (!visionAndUserSet.contains(visionAndUserId)) {
                visionAndUserSet.add(visionAndUserId);
                adapter.addTopic(MessageFormat.format(variateDefaultTopic, orgId, dpu));
            }
        });
    }

    /**
     * 动态移除主题
     * @param topic
     */
    public void removeVariateTopic(String visionAndUserId, String orgId) {
        if (null == adapter) {
            return;
        }
        topicMap.forEach((dpu, visionAndUserIdSet)->{
            if (visionAndUserIdSet.remove(visionAndUserId)) {
                adapter.removeTopic(MessageFormat.format(variateDefaultTopic, orgId, dpu));
            }
            if (visionAndUserIdSet.isEmpty()) {
                topicMap.remove(dpu);
            }
        });
    }

    /**
     * 动态添加主题
     * @param topicArr
     */
    public void addTopic(List<String> topicList) {
        if (ObjectUtils.isEmpty(topicList)) {
            return;
        }
        if (null == adapter) {
            adapter = new MqttPahoMessageDrivenChannelAdapter(clientId + "_inbound", mqttClientFactory(), "");
        }
        for (String topic : topicList) {
             adapter.addTopic(topic);
        }
        adapter.removeTopic();
    }
    
    /**
     * 动态移除主题
     * @param topic
     */
    public void removeListenTopic(String topic) {
        if (null == adapter) {
            adapter = new MqttPahoMessageDrivenChannelAdapter(clientId + "_inbound", mqttClientFactory(), "");
        }
        adapter.removeTopic(topic);
    }
}

处理类接口

package com.xgit.iot.infra.mqtt.service;
import com.xgit.iot.infra.mqtt.model.MachineAndVariateValueVo;
public interface MqttReceiveService {
    void handlerVariateMqttMsg(String topic,MachineAndVariateValueVo valueVo);
}
核心处理类  接收消息,并发送socket
package com.xgit.iot.infra.mqtt.service.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.xgit.iot.infra.mqtt.model.MachineAndVariateValueVo;
import com.xgit.iot.infra.mqtt.service.MqttReceiveService;
import com.xgit.iot.infra.util.ht.CommUtil;
import com.xgit.iot.service.nettySocketIo.MessageEventHandler;
import com.xgit.iot.service.nettySocketIo.SocketIoServerMapUtil;
import com.xgit.iot.service.nettySocketIo.model.MachineVariateValueVo;

@Service
public class MqttReceiveServiceImpl implements MqttReceiveService {

    @Autowired
    private MessageEventHandler messageEventHandler;
    
   /***
    * {
    * "t":timestamp,
    * "d":dpucode,
    * "id":machinecode,
    * "v":{"value":value,"timxx":xxxx }
    *  }
    */
    @Override
    public void handlerVariateMqttMsg(String topic, MachineAndVariateValueVo valueVo) {
        if (!CommUtil.isVariateValue(topic)) {
            return;
        }
        //{variateCode:value,variateCode:value}
        Map<String, Object> variateValueMap = valueVo.getV();
        String machineCode = valueVo.getId();
        
        //先判断是否命中machinCode
        if (!SocketIoServerMapUtil.getMachineListFromTag().contains(machineCode)){
            return;
        }
        List<MachineVariateValueVo> machineVariateValueList = new ArrayList<>();
        variateValueMap.forEach((variateCode,value)->{
            // 判断value是否是double  clientid唯一
            if (null != value && isDouble(value)) {
                MachineVariateValueVo machineVariateValueVo = new MachineVariateValueVo();
                machineVariateValueVo.setTag(machineCode + "|" + variateCode);
                machineVariateValueVo.setValue(value);
                machineVariateValueList.add(machineVariateValueVo);
            }
        });
        //1.校验topic 通过则 采集tag
        //调用sent  检测到订阅消息后,发送sockeio
        messageEventHandler.batchSendVisionMessage(machineVariateValueList);
    }
    
    private static boolean isDouble(Object str) {
        try {
            Double.parseDouble(str.toString());
            return true;
        } catch (NumberFormatException ex) {
            return false;
        }
    }
}

前段 支持数组

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>NETTY SOCKET.IO DEMO</title>
    <base>
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://cdn.bootcss.com/socket.io/2.1.1/socket.io.js"></script>
    <style>
        body {
            padding: 20px;
        }
        #console {
            height: 450px;
            overflow: auto;
        }
        .username-msg {
            color: orange;
        }
        .connect-msg {
            color: green;
        }
        .disconnect-msg {
            color: red;
        }
    </style>
</head>
 
<body>
    <div id="console" class="well"></div>
</body>
<script type="text/javascript">
    var socket;
    connect();
 
    function connect() {
        var opts = {
            query: {
            	"token":"xxxxxxxxxxx",
       	        "userId":"xxxxxxxxx",
       	        "visionId":"menu1/menu2/name",
       	        "frequency":"*/10 * * * * ?",
           	    "machineCodeAndVariateCodeList":[
          			"XZZBZT-001|Q0.0",
          			"XZZBZT-001|Q0.1",
          			"XZZBZT-001|Q0.2",
          			"XZZBZT-001|Q0.3",
          			"XZZBZT-001|V1.0",
          			"XZZBZT-001|V1.1",
          			"XZZBZT-001|VD3048",
          			"XZZBZT-001|VD1126",
          			"XZZBZT-001|VD3008",
          			"XZZBZT-001|VW2004",
          			"XZZBZT-001|VD3060",
          			"XZZBZT-001|VD3036",
          			"XZZBZT-001|VW2016"
      				]
        		}
        }; 


        socket = io.connect('http://localhost:9098', opts);
        socket.on('connect', function () {
            socket.emit('visionMessage', JSON.stringify({userId:"xxxxxx"}));
            console.log("连接成功");
            serverOutput('<span class="connect-msg">连接成功</span>');
        });
        socket.on('visionMessage', function (data) {
            output('<span class="username-msg">' + data + ' </span>');
            console.log(data);
        });
 
        socket.on('disconnect', function () {
            serverOutput('<span class="disconnect-msg">' + '已下线! </span>');
        });
    }
    
    function output(message) {
        var element = $("<div>" + " " + message + "</div>");
        $('#console').prepend(element);
    }
 
    function serverOutput(message) {
        var element = $("<div>" + message + "</div>");
        $('#console').prepend(element);
    }

</script>
</html>

预览

在这里插入图片描述

基于room聊天室的html 监听message

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>NETTY SOCKET.IO DEMO</title>
<base>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/socket.io/2.1.1/socket.io.js"></script>
<style>
body {
	padding: 20px;
}

#console {
	height: 450px;
	overflow: auto;
}

.username-msg {
	color: orange;
}

.connect-msg {
	color: green;
}

.disconnect-msg {
	color: red;
}
</style>
</head>

<body>
	<div id="console" class="well"></div>
</body>
<script type="text/javascript">
	var socket;
	connect();

	function connect() {
		var opts = {
			query : {
				"userId" : "561937412343726080",
				"orgId" : "100299"
			}
		};

		socket = io.connect('http://10.100.2.254:9098', opts);
		socket.on('connect', function() {
			console.log("连接成功");
			/* socket.emit('message', {orgId:"100299",userId : "567645317437063168"}
	        ); */
			serverOutput('<span class="connect-msg">连接成功</span>');
		});
		
		socket.on('message', function(data) {
			output('<span class="username-msg">' + data + ' </span>');
			console.log(data);
		});

		socket.on('disconnect', function() {
			serverOutput('<span class="disconnect-msg">' + '已下线! </span>');
		});
	}

	function output(message) {
		var element = $("<div>" + " " + message + "</div>");
		$('#console').prepend(element);
	}

	function serverOutput(message) {
		var element = $("<div>" + message + "</div>");
		$('#console').prepend(element);
	}
</script>
</html>
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值