|
@@ -0,0 +1,176 @@
|
|
|
|
|
+package com.ruoyi.device.mqtt.handler;
|
|
|
|
|
+
|
|
|
|
|
+import com.ruoyi.common.constant.CacheConstants;
|
|
|
|
|
+import com.ruoyi.common.core.redis.RedisCache;
|
|
|
|
|
+import com.ruoyi.device.mqtt.enums.DeviceLineStatusEnum;
|
|
|
|
|
+import com.ruoyi.device.mqtt.vo.DeviceOnlineInfo;
|
|
|
|
|
+import jakarta.annotation.Resource;
|
|
|
|
|
+import org.slf4j.Logger;
|
|
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
|
|
+
|
|
|
|
|
+import java.util.Collection;
|
|
|
|
|
+import java.util.Date;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 调试宝设备 MQTT 登录在线注册表(deviceSn → 设备信息/用户信息,Redis String 维护)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @author lwm
|
|
|
|
|
+ */
|
|
|
|
|
+@Component
|
|
|
|
|
+public class DeviceOnlineManager
|
|
|
|
|
+{
|
|
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(DeviceOnlineManager.class);
|
|
|
|
|
+
|
|
|
|
|
+ /** 默认心跳超时(毫秒),超过该时间未收到心跳则判定离线 */
|
|
|
|
|
+ public static final long DEFAULT_HEARTBEAT_TIMEOUT_MS = 90_000L;
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private RedisCache redisCache;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 登录在线:写入/覆盖设备登录信息,并标记在线、刷新心跳时间
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param info 设备登录信息
|
|
|
|
|
+ */
|
|
|
|
|
+ public void loginOnline(DeviceOnlineInfo info)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (info == null || info.getDeviceSn() == null)
|
|
|
|
|
+ {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ info.setLineStatus(DeviceLineStatusEnum.ON_LINE.getStatus());
|
|
|
|
|
+ info.setHeartbeatTime(new Date());
|
|
|
|
|
+ saveDeviceLoginInfo(info);
|
|
|
|
|
+ log.info("设备登录在线, deviceSn={}, userId={}", info.getDeviceSn(), info.getUserId());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 登出离线:保留登录信息,仅标记离线
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param deviceSn 设备SN码
|
|
|
|
|
+ */
|
|
|
|
|
+ public void logoutOffline(Long deviceSn)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (deviceSn == null)
|
|
|
|
|
+ {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ DeviceOnlineInfo info = getDeviceLoginInfo(deviceSn);
|
|
|
|
|
+ if (info == null)
|
|
|
|
|
+ {
|
|
|
|
|
+ log.info("设备登出离线,登录信息不存在, deviceSn={}", deviceSn);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ info.setLineStatus(DeviceLineStatusEnum.OFF_LINE.getStatus());
|
|
|
|
|
+ saveDeviceLoginInfo(info);
|
|
|
|
|
+ log.info("设备登出离线, deviceSn={}, userId={}", deviceSn, info.getUserId());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 心跳在线:刷新心跳时间,恢复/保持在线状态
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param deviceSn 设备SN码
|
|
|
|
|
+ */
|
|
|
|
|
+ public void heartbeatOnline(Long deviceSn)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (deviceSn == null)
|
|
|
|
|
+ {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ DeviceOnlineInfo info = getDeviceLoginInfo(deviceSn);
|
|
|
|
|
+ if (info == null)
|
|
|
|
|
+ {
|
|
|
|
|
+ log.debug("设备心跳,登录信息不存在, deviceSn={}", deviceSn);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ info.setLineStatus(DeviceLineStatusEnum.ON_LINE.getStatus());
|
|
|
|
|
+ info.setHeartbeatTime(new Date());
|
|
|
|
|
+ saveDeviceLoginInfo(info);
|
|
|
|
|
+ log.debug("设备心跳在线, deviceSn={}", deviceSn);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 长时间未心跳离线:标记离线(由定时扫描或外部检测调用)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param deviceSn 设备SN码
|
|
|
|
|
+ */
|
|
|
|
|
+ public void offlineByHeartbeatTimeout(Long deviceSn)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (deviceSn == null)
|
|
|
|
|
+ {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ DeviceOnlineInfo info = getDeviceLoginInfo(deviceSn);
|
|
|
|
|
+ if (info == null || DeviceLineStatusEnum.isOffLine(info.getLineStatus()))
|
|
|
|
|
+ {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ info.setLineStatus(DeviceLineStatusEnum.OFF_LINE.getStatus());
|
|
|
|
|
+ saveDeviceLoginInfo(info);
|
|
|
|
|
+ log.info("设备心跳超时离线, deviceSn={}, lastHeartbeat={}", deviceSn, info.getHeartbeatTime());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 扫描所有在线设备,将心跳超时的设备标记为离线
|
|
|
|
|
+ *
|
|
|
|
|
+ */
|
|
|
|
|
+ public void scanOfflineByHeartbeatTimeout()
|
|
|
|
|
+ {
|
|
|
|
|
+ Collection<String> keys = redisCache.keys(CacheConstants.TSB_DEVICE_ONLINE_KEY + "*");
|
|
|
|
|
+ if (keys == null || keys.isEmpty())
|
|
|
|
|
+ {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ Date now = new Date();
|
|
|
|
|
+ for (String key : keys)
|
|
|
|
|
+ {
|
|
|
|
|
+ DeviceOnlineInfo info = redisCache.getCacheObject(key);
|
|
|
|
|
+ if (info == null || DeviceLineStatusEnum.isOffLine(info.getLineStatus()))
|
|
|
|
|
+ {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ Date lastHeartbeat = info.getHeartbeatTime();
|
|
|
|
|
+ if (lastHeartbeat == null || now.getTime() - lastHeartbeat.getTime() > DEFAULT_HEARTBEAT_TIMEOUT_MS)
|
|
|
|
|
+ {
|
|
|
|
|
+ offlineByHeartbeatTimeout(info.getDeviceSn());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取设备登录信息(含在线状态与心跳时间)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param deviceSn 设备SN码
|
|
|
|
|
+ * @return 设备登录信息;未登录过则返回 null
|
|
|
|
|
+ */
|
|
|
|
|
+ public DeviceOnlineInfo getDeviceLoginInfo(Long deviceSn)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (deviceSn == null)
|
|
|
|
|
+ {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ return redisCache.getCacheObject(getDeviceOnlineKey(deviceSn));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 设备是否在线
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param deviceSn 设备SN码
|
|
|
|
|
+ */
|
|
|
|
|
+ public boolean isOnline(Long deviceSn)
|
|
|
|
|
+ {
|
|
|
|
|
+ DeviceOnlineInfo info = getDeviceLoginInfo(deviceSn);
|
|
|
|
|
+ return info != null && DeviceLineStatusEnum.isOnLine(info.getLineStatus());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void saveDeviceLoginInfo(DeviceOnlineInfo info)
|
|
|
|
|
+ {
|
|
|
|
|
+ redisCache.setCacheObject(getDeviceOnlineKey(info.getDeviceSn()), info);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private String getDeviceOnlineKey(Long deviceSn)
|
|
|
|
|
+ {
|
|
|
|
|
+ return CacheConstants.TSB_DEVICE_ONLINE_KEY + deviceSn;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|