#优化更新 连接上位机相关

This commit is contained in:
2026-09-09 09:01:54 +08:00
parent fda1060b38
commit 4ee3f6be90
21 changed files with 330 additions and 218 deletions

View File

@@ -1,9 +1,9 @@
package com.maibu.common; package com.maibu.constant;
import lombok.Data;
public class NettyCacheKey { public class NettyCacheKey {
public static final String deviceRunningStatusKey = "running_status:"; public static final String deviceRunningStatusKey = "running_status:";
public static final String latestPositonKey = "latest_position:";
} }

View File

@@ -13,6 +13,8 @@ import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.maibu.annotation.Excel; import com.maibu.annotation.Excel;
import com.maibu.core.domain.BaseDO; import com.maibu.core.domain.BaseDO;
import com.maibu.core.host.LocationMessage;
import com.maibu.core.host.VehicleMessage;
import com.maibu.influxdb.MowerRealTimeData; import com.maibu.influxdb.MowerRealTimeData;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
@@ -72,7 +74,6 @@ public class Device extends BaseDO {
@Excel(name = "产品名称") @Excel(name = "产品名称")
private String productName; private String productName;
/** /**
* 租户名称 * 租户名称
*/ */
@@ -87,7 +88,6 @@ public class Device extends BaseDO {
@Excel(name = "固件版本") @Excel(name = "固件版本")
private BigDecimal firmwareVersion; private BigDecimal firmwareVersion;
/** /**
* WIFI固件版本 * WIFI固件版本
*/ */
@@ -95,7 +95,6 @@ public class Device extends BaseDO {
@Excel(name = "HTTP推送固件版本") @Excel(name = "HTTP推送固件版本")
private BigDecimal wirelessVersion; private BigDecimal wirelessVersion;
/** /**
* 设备状态(1-未激活,2-禁用,3-在线,4-离线) * 设备状态(1-未激活,2-禁用,3-在线,4-离线)
*/ */
@@ -111,7 +110,7 @@ public class Device extends BaseDO {
private Integer onlineStatus; private Integer onlineStatus;
@TableField(exist = false) @TableField(exist = false)
private Integer feStatus; //前端大屏显示 1任务中/2空闲 private Integer feStatus; // 前端大屏显示 1任务中/2空闲
/** /**
* wifi信号强度(信号极好4格[-55— 0],信号好3格[-70— -55],信号一般2格[-85— -70],信号差1格[-100— -85]) * wifi信号强度(信号极好4格[-55— 0],信号好3格[-70— -55],信号一般2格[-85— -70],信号差1格[-100— -85])
@@ -194,7 +193,6 @@ public class Device extends BaseDO {
@ApiModelProperty("设备摘要") @ApiModelProperty("设备摘要")
private String summary; private String summary;
/** /**
* 是否是模拟设备 * 是否是模拟设备
*/ */
@@ -206,12 +204,12 @@ public class Device extends BaseDO {
@ApiModelProperty("子设备地址") @ApiModelProperty("子设备地址")
private Integer slaveId; private Integer slaveId;
@TableField(updateStrategy = FieldStrategy.IGNORED) @TableField(updateStrategy = FieldStrategy.IGNORED)
private Long siteId; private Long siteId;
@TableField(updateStrategy = FieldStrategy.IGNORED) @TableField(updateStrategy = FieldStrategy.IGNORED)
private Long orgId; private Long orgId;
/** /**
* 租户ID * 租户ID
*/ */
@@ -220,7 +218,6 @@ public class Device extends BaseDO {
@TableField(updateStrategy = FieldStrategy.IGNORED) @TableField(updateStrategy = FieldStrategy.IGNORED)
private Long tenantId; private Long tenantId;
@TableField(exist = false) @TableField(exist = false)
private DeviceRunningStatusHistory lastRunningStatus; private DeviceRunningStatusHistory lastRunningStatus;
@@ -228,12 +225,19 @@ public class Device extends BaseDO {
private MowerRealTimeData lastMowerRealTimeData; private MowerRealTimeData lastMowerRealTimeData;
@TableField(exist = false) @TableField(exist = false)
private Map<Integer,Integer> statusStatistics; private Map<Integer, Integer> statusStatistics;
@TableField(exist = false) @TableField(exist = false)
private DeviceRunParam runParam; private DeviceRunParam runParam;
private boolean hasRegistered = false; private boolean hasRegistered = false;
private boolean hasHost = false;
@TableField(exist = false)
private LocationMessage locationMessage;
@TableField(exist = false)
private VehicleMessage vehicleMessage;
} }

View File

@@ -1,18 +1,14 @@
package com.maibu.core.business; package com.maibu.core.business;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.maibu.core.domain.BaseDO; import com.maibu.core.domain.BaseDO;
import com.maibu.core.enums.ConnectType;
import io.swagger.annotations.ApiModel;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import java.util.List;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)

View File

@@ -58,7 +58,7 @@ public class DeviceRunParam extends BaseDO {
private String engineType;//发动机类型:honda-本田发动机 private String engineType;//发动机类型:honda-本田发动机
@Excel(name = "是否搭载上位机",cellType = Excel.ColumnType.NUMERIC) @Excel(name = "是否搭载上位机",cellType = Excel.ColumnType.NUMERIC)
private Integer hasHost;//是否搭载上位机 private Integer hasHost = 1;//是否搭载上位机
@Excel(name = "设备能力列表") @Excel(name = "设备能力列表")
@TableField(exist = false) @TableField(exist = false)

View File

@@ -138,24 +138,6 @@ public class NettyDevice extends Connector {
return finishId; return finishId;
} }
// public synchronized boolean cancelTask() {
// if (task != null) {
// sendPathCommand((byte) 0x01, (short) 0, 0.00d, 0.00d, (short) 0);
// task.setTaskStaus(DeviceTaskStaus.CANCELED);
// task.setUpdateTime(LocalDateTime.now());
// devicePlanTaskMapper.saveOrUpdate(task);
// SiteMemory siteMemory = GlobalMemory.getSiteMemory(task.getOrgId(),
// task.getSiteId());
// siteMemory.removeDevicePlanPrepare(task);
// siteMemory.removeDevicePlanExecute(task.getDeviceId());
// task = null;
// currentTaskId = null;
// currentPoint = null;
// locationQueue.clear();
// executingTask = false;
// }
// return true;
// }
public synchronized boolean cancelingTask() throws MqttException { public synchronized boolean cancelingTask() throws MqttException {
if (task != null) { if (task != null) {
@@ -200,6 +182,24 @@ public class NettyDevice extends Connector {
return true; return true;
} }
public synchronized boolean failedTask() {
if (task != null) {
task.setTaskStaus(DeviceTaskStaus.FAILED);
task.setUpdateTime(LocalDateTime.now());
devicePlanTaskMapper.saveOrUpdate(task);
SiteMemory siteMemory = GlobalMemory.getSiteMemory(task.getOrgId(), task.getSiteId());
siteMemory.removeDevicePlanPrepare(task);
siteMemory.removeDevicePlanExecute(task.getDeviceId());
GlobalMemory.taskPathMap.remove(task.getId());
task = null;
currentTaskId = null;
currentPoint = null;
locationQueue.clear();
executingTask = false;
}
return true;
}
public void sendPathCommand(byte commandType, short pointCounts, double lat, double lng, short speed) { public void sendPathCommand(byte commandType, short pointCounts, double lat, double lng, short speed) {
RoutePlanSendEntity routePlanSendEntity = new RoutePlanSendEntity(); RoutePlanSendEntity routePlanSendEntity = new RoutePlanSendEntity();
routePlanSendEntity.setCommandType(commandType); routePlanSendEntity.setCommandType(commandType);
@@ -296,29 +296,6 @@ public class NettyDevice extends Connector {
} }
} }
// public void startTask() {
// try {
// if (executingTask)
// return;
// if (task != null) {
// Long routeId = task.getRouteId();
// WorkRecord workRecord = workRecordMapper.selectById(routeId);
// if (workRecord != null) {
// PlanPath planPath = workRecord.getJsonData();
// if (planPath != null && !CollectionUtils.isEmpty(planPath.getPath())) {
// planPath.getPath().forEach(x -> {
// locationQueue.offer(x);
// });
// }
// }
// task.setStartTime(LocalDateTime.now());
// }
// executingTask = true;
// sendNextPoint();
// } catch (Exception e) {
// throw new RuntimeException(e);
// }
// }
public void startTask() { public void startTask() {
try { try {

View File

@@ -1,14 +1,26 @@
package com.maibu.core.host; package com.maibu.core.host;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.maibu.core.domain.OrgNoUserDO;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
@Data @Data
public class LocationMessage { @EqualsAndHashCode(callSuper = true)
@TableName("iot_host_location_message")
public class LocationMessage extends OrgNoUserDO {
@TableId(type = IdType.AUTO)
private Long id;
private String deviceId;
private double latitude; private double latitude;
private double longitude; private double longitude;
private double yaw;//目标航向,正北0°、顺时针正、0-360° private double yaw;// 目标航向,正北0°、顺时针正、0-360°
private String fix_status; private String fix_status;
private double altitude; private double altitude;

View File

@@ -6,8 +6,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.springframework.util.CollectionUtils;
import com.influxdb.query.FluxRecord; import com.influxdb.query.FluxRecord;
import com.influxdb.query.FluxTable; import com.influxdb.query.FluxTable;
import com.maibu.influxdb.inter.InfluxField; import com.maibu.influxdb.inter.InfluxField;

View File

@@ -0,0 +1,14 @@
package com.maibu.mapper;
import org.springframework.stereotype.Repository;
import com.maibu.core.host.LocationMessage;
import com.maibu.mybatis.mapper.BaseMapperX;
/**
* 设备历史状态
*/
@Repository
public interface HostLocationMapper extends BaseMapperX<LocationMessage> {
}

View File

@@ -262,6 +262,13 @@ public class DeviceSessionManager {
for (Connector connected : new HashSet<>(nettyDevice.getConnectedConnectors())) { for (Connector connected : new HashSet<>(nettyDevice.getConnectedConnectors())) {
nettyDevice.removeConnectedDevice(connected); nettyDevice.removeConnectedDevice(connected);
} }
// 清理channel和状态
nettyDevice.setChannel(null);
Channel channel = channelMap.remove(deviceId);
if (channel != null) {
channelToDeviceIdMap.remove(channel);
channel.close();
}
} else { } else {
// 如果是下位机,则保留绑定关系,只清理channel和状态 // 如果是下位机,则保留绑定关系,只清理channel和状态
nettyDevice.setChannel(null); nettyDevice.setChannel(null);

View File

@@ -3,17 +3,20 @@ package com.maibu.mqtt;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.maibu.constant.Constant; import com.maibu.constant.Constant;
import com.maibu.constant.NettyCacheKey;
import com.maibu.core.business.device.DeviceStatusDetail; import com.maibu.core.business.device.DeviceStatusDetail;
import com.maibu.core.business.device.NettyDevice; import com.maibu.core.business.device.NettyDevice;
import com.maibu.core.business.dto.WebStatusMessageDTO; import com.maibu.core.business.dto.WebStatusMessageDTO;
import com.maibu.core.business.inter.WebsocketMesDispather; import com.maibu.core.business.inter.WebsocketMesDispather;
import com.maibu.core.enums.MesType; import com.maibu.core.enums.MesType;
import com.maibu.core.host.*; import com.maibu.core.host.*;
import com.maibu.core.redis.RedisCache;
import com.maibu.memory.DeviceSessionManager; import com.maibu.memory.DeviceSessionManager;
import com.maibu.memory.GlobalMemory; import com.maibu.memory.GlobalMemory;
import com.maibu.utils.StringUtils; import com.maibu.utils.StringUtils;
import com.maibu.utils.json.JsonUtils; import com.maibu.utils.json.JsonUtils;
import com.maibu.utils.spring.SpringUtils; import com.maibu.utils.spring.SpringUtils;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
@@ -34,6 +37,8 @@ public class HostMessageHandler implements CustomMqttMessageHandler {
private final String mqttClientId = "maibu-mqtt-client"; private final String mqttClientId = "maibu-mqtt-client";
private final RedisCache redisCache = SpringUtils.getBean(RedisCache.class);
@Override @Override
public String getHandlerType() { public String getHandlerType() {
return Constant.mqttHandleKey; return Constant.mqttHandleKey;
@@ -131,6 +136,12 @@ public class HostMessageHandler implements CustomMqttMessageHandler {
// GlobalMemory.customMqttDeviceMonitor.publish(mqttClientId, // GlobalMemory.customMqttDeviceMonitor.publish(mqttClientId,
// String.format(MqttTopic.DEVICE_LOCATION_TOPIC, deviceId), // String.format(MqttTopic.DEVICE_LOCATION_TOPIC, deviceId),
// JsonUtils.toJsonString(dto)); // JsonUtils.toJsonString(dto));
// todo 保存到内存
String key = NettyCacheKey.latestPositonKey + deviceId;
LocationMessage locationMessage = locationRelTimeDTO.getData();
locationMessage.setDeviceId(deviceId);
redisCache.setCacheObject(key, locationMessage);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("位置消息解析失败: deviceId={}, payload={}, error={}", deviceId, payload, e.getMessage()); log.error("位置消息解析失败: deviceId={}, payload={}, error={}", deviceId, payload, e.getMessage());
@@ -242,14 +253,6 @@ public class HostMessageHandler implements CustomMqttMessageHandler {
} }
} }
/**
* 处理状态变更事件
* topic: mower/{deviceId}/event/status/post
*/
private void handleStatusPost(String deviceId, String topic, String payload) {
log.info("【状态变更】deviceId={}, payload={}", deviceId, payload);
}
/** /**
* 处理心跳 * 处理心跳
* topic: mower/{deviceId}/event/heartbeat/post * topic: mower/{deviceId}/event/heartbeat/post
@@ -291,10 +294,9 @@ public class HostMessageHandler implements CustomMqttMessageHandler {
// NettyDevice nettyDevice = deviceSessionManager.getDevice(deviceId); // NettyDevice nettyDevice = deviceSessionManager.getDevice(deviceId);
// nettyDevice.finishTask(); // nettyDevice.finishTask();
// } // }
// if("failed".equals(dto.getStatus())){ if ("failed".equals(dto.getStatus())) {
// NettyDevice nettyDevice = deviceSessionManager.getDevice(deviceId); nettyDevice.failedTask();
// nettyDevice.finishTask(); }
// }
// if("accepted".equals(dto.getStatus())){ // if("accepted".equals(dto.getStatus())){
// NettyDevice nettyDevice = deviceSessionManager.getDevice(deviceId); // NettyDevice nettyDevice = deviceSessionManager.getDevice(deviceId);
// nettyDevice.finishTask(); // nettyDevice.finishTask();

View File

@@ -6,7 +6,7 @@ import com.huaweicloud.sdk.iot.device.client.requests.CommandRsp;
import com.huaweicloud.sdk.iot.device.client.requests.DeviceMessage; import com.huaweicloud.sdk.iot.device.client.requests.DeviceMessage;
import com.huaweicloud.sdk.iot.device.client.requests.ServiceProperty; import com.huaweicloud.sdk.iot.device.client.requests.ServiceProperty;
import com.huaweicloud.sdk.iot.device.transport.ActionListener; import com.huaweicloud.sdk.iot.device.transport.ActionListener;
import com.maibu.common.NettyCacheKey; import com.maibu.constant.NettyCacheKey;
import com.maibu.core.business.DeviceRunningStatusHistory; import com.maibu.core.business.DeviceRunningStatusHistory;
import com.maibu.core.business.DeviceStatusRecordDTO; import com.maibu.core.business.DeviceStatusRecordDTO;
import com.maibu.core.redis.RedisCache; import com.maibu.core.redis.RedisCache;

View File

@@ -23,7 +23,7 @@ import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import com.maibu.constant.CommandConstant; import com.maibu.constant.CommandConstant;
import com.maibu.common.NettyCacheKey; import com.maibu.constant.NettyCacheKey;
import com.maibu.core.business.DeviceRunStatistics; import com.maibu.core.business.DeviceRunStatistics;
import com.maibu.core.business.DeviceRunningStatusHistory; import com.maibu.core.business.DeviceRunningStatusHistory;
import com.maibu.core.business.DeviceStatusRecordDTO; import com.maibu.core.business.DeviceStatusRecordDTO;

View File

@@ -1,6 +1,6 @@
package com.maibu.scheduled; package com.maibu.scheduled;
import com.maibu.common.NettyCacheKey; import com.maibu.constant.NettyCacheKey;
import com.maibu.core.business.DevicePlan; import com.maibu.core.business.DevicePlan;
import com.maibu.core.business.DeviceRunParam; import com.maibu.core.business.DeviceRunParam;
import com.maibu.core.business.DeviceStatusRecordDTO; import com.maibu.core.business.DeviceStatusRecordDTO;

View File

@@ -1,29 +1,19 @@
package com.maibu.service; package com.maibu.service;
import cn.hutool.core.io.IORuntimeException; import java.io.IOException;
import cn.hutool.core.io.IoUtil; import java.lang.reflect.Field;
import com.alibaba.fastjson2.JSON; import java.math.BigDecimal;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import java.time.LocalDateTime;
import com.maibu.common.NettyCacheKey; import java.util.ArrayList;
import com.maibu.constant.Constant; import java.util.Collection;
import com.maibu.core.business.Device; import java.util.Comparator;
import com.maibu.core.business.DeviceRunningStatusHistory; import java.util.List;
import com.maibu.core.business.DeviceStatusRecordDTO; import java.util.Map;
import com.maibu.core.business.ErrorIdentificationStandard; import java.util.concurrent.ConcurrentHashMap;
import com.maibu.core.business.alarm_center.AlarmMessage; import java.util.concurrent.ScheduledExecutorService;
import com.maibu.core.business.device.NettyDevice; import java.util.concurrent.TimeUnit;
import com.maibu.core.business.inter.WebsocketMesDispather; import java.util.stream.Collectors;
import com.maibu.core.enums.*;
import com.maibu.core.host.HeartBeatDTO;
import com.maibu.core.redis.RedisCache;
import com.maibu.dto.DeviceErrorPushDTO;
import com.maibu.mapper.ErrorIdentificationStandardMapper;
import com.maibu.memory.DeviceSessionManager;
import com.maibu.memory.GlobalMemory;
import com.maibu.memory.SiteMemory;
import com.maibu.mqtt.MqttTopic;
import com.maibu.utils.CompareUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
@@ -32,17 +22,37 @@ import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import javax.annotation.PreDestroy; import com.alibaba.fastjson2.JSON;
import java.io.IOException; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import java.lang.reflect.Field; import com.maibu.constant.Constant;
import java.math.BigDecimal; import com.maibu.constant.NettyCacheKey;
import java.time.LocalDateTime; import com.maibu.core.business.Device;
import java.util.*; import com.maibu.core.business.DeviceRunningStatusHistory;
import java.util.concurrent.ConcurrentHashMap; import com.maibu.core.business.DeviceStatusRecordDTO;
import java.util.concurrent.Executors; import com.maibu.core.business.ErrorIdentificationStandard;
import java.util.concurrent.ScheduledExecutorService; import com.maibu.core.business.alarm_center.AlarmMessage;
import java.util.concurrent.TimeUnit; import com.maibu.core.business.device.NettyDevice;
import java.util.stream.Collectors; import com.maibu.core.business.inter.WebsocketMesDispather;
import com.maibu.core.enums.CompareEnum;
import com.maibu.core.enums.ConnectorStatus;
import com.maibu.core.enums.DeviceStatus;
import com.maibu.core.enums.ErrorSource;
import com.maibu.core.enums.RespondCode;
import com.maibu.core.host.HeartBeatDTO;
import com.maibu.core.host.LocationMessage;
import com.maibu.core.redis.RedisCache;
import com.maibu.dto.DeviceErrorPushDTO;
import com.maibu.mapper.ErrorIdentificationStandardMapper;
import com.maibu.mapper.HostLocationMapper;
import com.maibu.memory.DeviceSessionManager;
import com.maibu.memory.GlobalMemory;
import com.maibu.memory.SiteMemory;
import com.maibu.mqtt.MqttTopic;
import com.maibu.utils.CompareUtils;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
@Service @Service
@@ -75,10 +85,13 @@ public class DeviceThreadService {
@Autowired @Autowired
private ScheduledExecutorService executor; private ScheduledExecutorService executor;
@Autowired
private HostLocationMapper hostLocationMapper;
public void deviceErrorMonitor() throws InterruptedException, IOException { public void deviceErrorMonitor() throws InterruptedException, IOException {
initStandard(); initStandard();
// TODO fix 后续是否分多个线程 一个site一个线程 // TODO fix 后续是否分多个线程 一个site一个线程
// 有还需要更新接受故障方式。 // 有还需要更新接受故障方式。
executor.scheduleWithFixedDelay(() -> { executor.scheduleWithFixedDelay(() -> {
try { try {
@@ -188,8 +201,10 @@ public class DeviceThreadService {
// } // }
// } // }
try { try {
GlobalMemory.customMqttDeviceMonitor.publish(mqttClientId, String.format(MqttTopic.DEVICE_ERROR_PUSH_TOPIC, deviceId), dto); GlobalMemory.customMqttDeviceMonitor.publish(mqttClientId,
// GlobalMemory.mqttClientUtil.publish(String.format(MqttTopic.DEVICE_ERROR_PUSH_TOPIC, deviceId), dto); String.format(MqttTopic.DEVICE_ERROR_PUSH_TOPIC, deviceId), dto);
// GlobalMemory.mqttClientUtil.publish(String.format(MqttTopic.DEVICE_ERROR_PUSH_TOPIC,
// deviceId), dto);
List<NettyDevice> controlMasters = sessionManager.getAllSlaveControl(deviceId); List<NettyDevice> controlMasters = sessionManager.getAllSlaveControl(deviceId);
if (!CollectionUtils.isEmpty(controlMasters)) { if (!CollectionUtils.isEmpty(controlMasters)) {
controlMasters.forEach(x -> { controlMasters.forEach(x -> {
@@ -203,7 +218,7 @@ public class DeviceThreadService {
} }
public List<AlarmMessage> compareErrorStandard(Long siteId, Long orgId, DeviceRunningStatusHistory history, public List<AlarmMessage> compareErrorStandard(Long siteId, Long orgId, DeviceRunningStatusHistory history,
List<ErrorIdentificationStandard> standards) { List<ErrorIdentificationStandard> standards) {
if (history == null || CollectionUtils.isEmpty(standards)) if (history == null || CollectionUtils.isEmpty(standards))
return null; return null;
List<AlarmMessage> list = new ArrayList<>(); List<AlarmMessage> list = new ArrayList<>();
@@ -212,7 +227,8 @@ public class DeviceThreadService {
String compareValues = standard.getCompareValues(); String compareValues = standard.getCompareValues();
CompareEnum compareType = standard.getCompareType(); CompareEnum compareType = standard.getCompareType();
String targetValue = knownClassFieldFinding(fieldName, history); String targetValue = knownClassFieldFinding(fieldName, history);
log.debug("compare2 compareType:{} compareValues:{},targetValue:{}", compareType, compareValues, targetValue); log.debug("compare2 compareType:{} compareValues:{},targetValue:{}", compareType, compareValues,
targetValue);
if (!StringUtils.isEmpty(compareValues) && !StringUtils.isEmpty(targetValue)) { if (!StringUtils.isEmpty(compareValues) && !StringUtils.isEmpty(targetValue)) {
boolean result = CompareUtils.compare(compareType, targetValue, compareValues); boolean result = CompareUtils.compare(compareType, targetValue, compareValues);
log.debug("compare3 result:{}", result); log.debug("compare3 result:{}", result);
@@ -253,7 +269,6 @@ public class DeviceThreadService {
return null; return null;
} }
/** /**
* 监控心跳 * 监控心跳
*/ */
@@ -268,10 +283,22 @@ public class DeviceThreadService {
Long lastActiveTime = x.getSysTimestamp(); Long lastActiveTime = x.getSysTimestamp();
NettyDevice nettyDevice = sessionManager.getDevice(x.getSn()); NettyDevice nettyDevice = sessionManager.getDevice(x.getSn());
if (nowTime - lastActiveTime >= Constant.hostOfflineInterval) { if (nowTime - lastActiveTime >= Constant.hostOfflineInterval) {
//超时了判断离线 // 超时了判断离线
if (nettyDevice != null) { if (nettyDevice != null) {
nettyDevice.status(0); nettyDevice.status(DeviceStatus.offline.getType());
//todo 推送设备离线 // todo 推送设备离线
String key = NettyCacheKey.latestPositonKey + deviceId;
// 保存一下最新一次的数据到 数据库
LocationMessage locationMessage = redisCache.getCacheObject(key);
if (locationMessage != null) {
LambdaQueryWrapper<LocationMessage> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(LocationMessage::getDeviceId, deviceId);
LocationMessage existing = hostLocationMapper.selectOne(queryWrapper);
if (existing != null) {
locationMessage.setId(existing.getId());
}
hostLocationMapper.saveOrUpdate(locationMessage);
}
} }
} else { } else {
if (nettyDevice == null) { if (nettyDevice == null) {
@@ -289,31 +316,36 @@ public class DeviceThreadService {
device.setProductName("割草机产品MC700"); device.setProductName("割草机产品MC700");
device.setCreateTime(LocalDateTime.now()); device.setCreateTime(LocalDateTime.now());
device.setUpdateTime(LocalDateTime.now()); device.setUpdateTime(LocalDateTime.now());
device.setHasHost(true);
deviceService.insertDeviceBySelf(device); deviceService.insertDeviceBySelf(device);
} else { } else {
device.setStatus(3); device.setStatus(3);
device.setOnlineStatus(DeviceStatus.online.getType()); device.setOnlineStatus(DeviceStatus.online.getType());
device.setUpdateTime(LocalDateTime.now()); device.setUpdateTime(LocalDateTime.now());
if (!device.isHasHost()) {
device.setHasHost(true);
}
deviceService.updateDeviceBySelf(device); deviceService.updateDeviceBySelf(device);
} }
// 推送json 找到设备绑定的主机进行推送 // 推送json 找到设备绑定的主机进行推送
// List<NettyDevice> controlMasters = sessionManager.getAllSlaveControl(device.getSerialNumber()); // List<NettyDevice> controlMasters =
// if (!CollectionUtils.isEmpty(controlMasters)) { // sessionManager.getAllSlaveControl(device.getSerialNumber());
// controlMasters.forEach(x -> { // if (!CollectionUtils.isEmpty(controlMasters)) {
// DeviceStatusChangeDTO changeDTO = new DeviceStatusChangeDTO(); // controlMasters.forEach(x -> {
// changeDTO.setDeviceId(data); // DeviceStatusChangeDTO changeDTO = new DeviceStatusChangeDTO();
// changeDTO.setStatus(DeviceStatus.online.getCode()); // changeDTO.setDeviceId(data);
// changeDTO.setEvent(RespondCode.device_status_changed); // changeDTO.setStatus(DeviceStatus.online.getCode());
// ByteBuf buf = ctx.alloc().buffer(); // changeDTO.setEvent(RespondCode.device_status_changed);
// CommandUtils.buildCommand(buf, changeDTO, CommandConstant.interaction); // ByteBuf buf = ctx.alloc().buffer();
// String content = buf.toString(CharsetUtil.UTF_8); // CommandUtils.buildCommand(buf, changeDTO, CommandConstant.interaction);
// logger.info("推送下位机登录状态消息:{}", content); // String content = buf.toString(CharsetUtil.UTF_8);
// if (x.getChannel() != null && x.getChannel().isActive()) { // logger.info("推送下位机登录状态消息:{}", content);
// x.getChannel().writeAndFlush(buf); // if (x.getChannel() != null && x.getChannel().isActive()) {
// } // x.getChannel().writeAndFlush(buf);
// }); // }
// });
} else { } else {
//todo 推送设备上线 // todo 推送设备上线
nettyDevice.status(1); nettyDevice.status(1);
} }
} }

View File

@@ -162,6 +162,7 @@ public class HostComputerController extends BaseController {
for (NettyDevice x : controlMasters) { for (NettyDevice x : controlMasters) {
try { try {
websocketMesDispather.dispather(x.getConnectorId(), JsonUtils.toJsonString(dto)); websocketMesDispather.dispather(x.getConnectorId(), JsonUtils.toJsonString(dto));
log.info("位置数据websocket推送成功: connectorId={}, taskId={}", x.getConnectorId(), taskId);
} catch (Exception e) { } catch (Exception e) {
log.error("位置数据websocket推送失败: connectorId={}, error={}", x.getConnectorId(), log.error("位置数据websocket推送失败: connectorId={}, error={}", x.getConnectorId(),
e.getMessage()); e.getMessage());

View File

@@ -40,9 +40,11 @@ public class WorkRecordController extends BaseController {
) { ) {
int i = -1; int i = -1;
// WorkRecord workRecordObj = JsonUtils.parseObject(workRecord, WorkRecord.class); // WorkRecord workRecordObj = JsonUtils.parseObject(workRecord,
// WorkRecord.class);
WorkRecord workRecord1 = workRecordService.selectWorkRecordByWorkNameAndSiteId(workRecord.getWorkName(), workRecord.getSiteId()); WorkRecord workRecord1 = workRecordService.selectWorkRecordByWorkNameAndSiteId(workRecord.getWorkName(),
workRecord.getSiteId());
if (workRecord1 != null) { if (workRecord1 != null) {
return R.fail("该任务已存在"); return R.fail("该任务已存在");
} }
@@ -108,8 +110,8 @@ public class WorkRecordController extends BaseController {
@GetMapping("/selectByTaskId") @GetMapping("/selectByTaskId")
public R selectByTaskId(@RequestParam Long taskId) { public R selectByTaskId(@RequestParam Long taskId) {
List<LatAndLngEntity> path = workRecordService.selectByTaskId(taskId); WorkRecord workRecord = workRecordService.selectByTaskId(taskId);
return R.ok(path); return R.ok(workRecord);
} }
@GetMapping("getTaskPath") @GetMapping("getTaskPath")

View File

@@ -1,22 +1,5 @@
package com.maibu.service.impl; package com.maibu.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.maibu.annotation.Excel;
import com.maibu.constant.Constant;
import com.maibu.core.business.DeviceRunParam;
import com.maibu.core.business.device.NettyDevice;
import com.maibu.dto.BaseQueryDTO;
import com.maibu.exception.ServiceException;
import com.maibu.mapper.DeviceRunParamMapper;
import com.maibu.memory.DeviceSessionManager;
import com.maibu.memory.GlobalMemory;
import com.maibu.memory.SiteMemory;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@@ -28,6 +11,24 @@ import java.util.concurrent.TimeoutException;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.maibu.constant.Constant;
import com.maibu.core.business.DeviceRunParam;
import com.maibu.core.business.device.NettyDevice;
import com.maibu.dto.BaseQueryDTO;
import com.maibu.exception.ServiceException;
import com.maibu.mapper.DeviceRunParamMapper;
import com.maibu.memory.DeviceSessionManager;
import com.maibu.memory.GlobalMemory;
import com.maibu.memory.SiteMemory;
import lombok.extern.slf4j.Slf4j;
@Service @Service
@Slf4j @Slf4j
public class DeviceRunParamService { public class DeviceRunParamService {
@@ -38,7 +39,8 @@ public class DeviceRunParamService {
@Autowired @Autowired
private DeviceSessionManager sessionManager; private DeviceSessionManager sessionManager;
public boolean save(DeviceRunParam deviceRunParam) throws ExecutionException, InterruptedException, TimeoutException { public boolean save(DeviceRunParam deviceRunParam)
throws ExecutionException, InterruptedException, TimeoutException {
NettyDevice nettyDevice = sessionManager.getDevice(deviceRunParam.getDeviceId()); NettyDevice nettyDevice = sessionManager.getDevice(deviceRunParam.getDeviceId());
if (nettyDevice == null || !nettyDevice.getChannel().isActive()) { if (nettyDevice == null || !nettyDevice.getChannel().isActive()) {
@@ -93,30 +95,73 @@ public class DeviceRunParamService {
return deviceRunParamMapper.selectList(queryWrapper); return deviceRunParamMapper.selectList(queryWrapper);
} }
public DeviceRunParam selectByDeviceId(String deviceId) throws ExecutionException, InterruptedException, TimeoutException { // public DeviceRunParam selectByDeviceId(String deviceId)
NettyDevice nettyDevice = sessionManager.getDevice(deviceId); // throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<DeviceRunParam> future = new CompletableFuture<>();
if (nettyDevice != null && nettyDevice.getChannel() != null && nettyDevice.getChannel().isActive()) {
nettyDevice.getChannel().attr(Constant.READ_CONFIG_KEY).set(future);
try {
nettyDevice.readConfigCommand();
DeviceRunParam param = future.get(5, TimeUnit.SECONDS);
LambdaQueryWrapper<DeviceRunParam> queryWrapper = new LambdaQueryWrapper<>(); // NettyDevice nettyDevice = sessionManager.getDevice(deviceId);
if (!StringUtils.isEmpty(deviceId)) { // CompletableFuture<DeviceRunParam> future = new CompletableFuture<>();
queryWrapper.eq(DeviceRunParam::getDeviceId, deviceId); // if (nettyDevice != null && nettyDevice.getChannel() != null &&
// nettyDevice.getChannel().isActive()) {
// nettyDevice.getChannel().attr(Constant.READ_CONFIG_KEY).set(future);
// try {
// nettyDevice.readConfigCommand();
// DeviceRunParam param = future.get(5, TimeUnit.SECONDS);
// LambdaQueryWrapper<DeviceRunParam> queryWrapper = new LambdaQueryWrapper<>();
// if (!StringUtils.isEmpty(deviceId)) {
// queryWrapper.eq(DeviceRunParam::getDeviceId, deviceId);
// }
// DeviceRunParam result = deviceRunParamMapper.selectOne(queryWrapper);
// if (result != null) {
// param.setId(result.getId());
// param.setDeviceId(result.getDeviceId());
// param.setRunSpeed(result.getRunSpeed());
// param.setOrgId(nettyDevice.getDevice().getOrgId());
// param.setSiteId(nettyDevice.getDevice().getSiteId());
// }
// return param;
// } finally {
// nettyDevice.getChannel().attr(Constant.READ_CONFIG_KEY).set(null);
// }
// }
// return null;
// }
public DeviceRunParam selectByDeviceId(String deviceId)
throws ExecutionException, InterruptedException, TimeoutException {
LambdaQueryWrapper<DeviceRunParam> queryWrapper = new LambdaQueryWrapper<>();
if (!StringUtils.isEmpty(deviceId)) {
queryWrapper.eq(DeviceRunParam::getDeviceId, deviceId);
}
DeviceRunParam result = deviceRunParamMapper.selectOne(queryWrapper);
if (result != null && result.getHasHost() != null && result.getHasHost() == 1) {
return result;
} else {
NettyDevice nettyDevice = sessionManager.getDevice(deviceId);
CompletableFuture<DeviceRunParam> future = new CompletableFuture<>();
if (nettyDevice != null && nettyDevice.getChannel() != null && nettyDevice.getChannel().isActive()) {
nettyDevice.getChannel().attr(Constant.READ_CONFIG_KEY).set(future);
try {
nettyDevice.readConfigCommand();
DeviceRunParam param = future.get(5, TimeUnit.SECONDS);
// LambdaQueryWrapper<DeviceRunParam> queryWrapper = new LambdaQueryWrapper<>();
// if (!StringUtils.isEmpty(deviceId)) {
// queryWrapper.eq(DeviceRunParam::getDeviceId, deviceId);
// }
// DeviceRunParam result = deviceRunParamMapper.selectOne(queryWrapper);
if (result != null) {
param.setId(result.getId());
param.setDeviceId(result.getDeviceId());
param.setRunSpeed(result.getRunSpeed());
param.setOrgId(nettyDevice.getDevice().getOrgId());
param.setSiteId(nettyDevice.getDevice().getSiteId());
}
return param;
} finally {
nettyDevice.getChannel().attr(Constant.READ_CONFIG_KEY).set(null);
} }
DeviceRunParam result = deviceRunParamMapper.selectOne(queryWrapper);
if (result != null) {
param.setId(result.getId());
param.setDeviceId(result.getDeviceId());
param.setRunSpeed(result.getRunSpeed());
param.setOrgId(nettyDevice.getDevice().getOrgId());
param.setSiteId(nettyDevice.getDevice().getSiteId());
}
return param;
} finally {
nettyDevice.getChannel().attr(Constant.READ_CONFIG_KEY).set(null);
} }
} }
return null; return null;
@@ -149,10 +194,11 @@ public class DeviceRunParamService {
Map<String, DeviceRunParam> map = oldList.stream() Map<String, DeviceRunParam> map = oldList.stream()
.collect(Collectors.toMap( .collect(Collectors.toMap(
DeviceRunParam::getDeviceId, DeviceRunParam::getDeviceId,
Function.identity() Function.identity()));
));
List<DeviceRunParam> saveList = new ArrayList<>(); List<DeviceRunParam> saveList = new ArrayList<>();
params.forEach(x -> { params.forEach(x -> {
if (StringUtils.isEmpty(x.getDeviceId()))
return;
DeviceRunParam deviceRunParam = null; DeviceRunParam deviceRunParam = null;
if (!CollectionUtils.isEmpty(map)) { if (!CollectionUtils.isEmpty(map)) {
deviceRunParam = map.get(x.getDeviceId()); deviceRunParam = map.get(x.getDeviceId());
@@ -188,4 +234,3 @@ public class DeviceRunParamService {
} }
} }

View File

@@ -8,12 +8,11 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.annotation.Resource; import javax.annotation.Resource;
import com.maibu.constant.CacheConstants;
import com.maibu.dto.DeviceParamAuthDTO;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -22,8 +21,8 @@ import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.influxdb.query.FluxRecord; import com.maibu.constant.CacheConstants;
import com.influxdb.query.FluxTable; import com.maibu.constant.NettyCacheKey;
import com.maibu.core.business.Device; import com.maibu.core.business.Device;
import com.maibu.core.business.DevicePlan; import com.maibu.core.business.DevicePlan;
import com.maibu.core.business.DeviceRunningStatusHistory; import com.maibu.core.business.DeviceRunningStatusHistory;
@@ -34,13 +33,13 @@ import com.maibu.core.domain.entity.SysRole;
import com.maibu.core.domain.entity.SysSite; import com.maibu.core.domain.entity.SysSite;
import com.maibu.core.domain.model.LoginUser; import com.maibu.core.domain.model.LoginUser;
import com.maibu.core.enums.DeviceTaskStaus; import com.maibu.core.enums.DeviceTaskStaus;
import com.maibu.core.host.LocationMessage;
import com.maibu.core.redis.RedisCache; import com.maibu.core.redis.RedisCache;
import com.maibu.influxdb.MowerRealTimeData; import com.maibu.dto.DeviceParamAuthDTO;
import com.maibu.influxdb.util.InfluxSqlBuilder;
import com.maibu.influxdb.util.LambdaUtils;
import com.maibu.mapper.DeviceMapper; import com.maibu.mapper.DeviceMapper;
import com.maibu.mapper.DevicePlanMapper; import com.maibu.mapper.DevicePlanMapper;
import com.maibu.mapper.DeviceStatusHistoryMapper; import com.maibu.mapper.DeviceStatusHistoryMapper;
import com.maibu.mapper.HostLocationMapper;
import com.maibu.mapper.SysSiteMapper; import com.maibu.mapper.SysSiteMapper;
import com.maibu.memory.GlobalMemory; import com.maibu.memory.GlobalMemory;
import com.maibu.memory.SiteMemory; import com.maibu.memory.SiteMemory;
@@ -75,6 +74,9 @@ public class DeviceServiceImpl implements IDeviceService {
@Autowired @Autowired
private SysSiteMapper sysSiteMapper; private SysSiteMapper sysSiteMapper;
@Autowired
private HostLocationMapper hostLocationMapper;
/** /**
* 根据设备编号查询设备 * 根据设备编号查询设备
* *
@@ -147,6 +149,13 @@ public class DeviceServiceImpl implements IDeviceService {
Map<String, List<DeviceRunningStatusHistory>> statusMap = allStatusList.stream() Map<String, List<DeviceRunningStatusHistory>> statusMap = allStatusList.stream()
.collect(Collectors.groupingBy(DeviceRunningStatusHistory::getDeviceId)); .collect(Collectors.groupingBy(DeviceRunningStatusHistory::getDeviceId));
LambdaQueryWrapper<LocationMessage> query = new LambdaQueryWrapper<>();
query.in(LocationMessage::getDeviceId, ids);
List<LocationMessage> hostLocations = hostLocationMapper.selectList(query);
Map<String, LocationMessage> locationMap = hostLocations.stream()
.collect(Collectors.toMap(LocationMessage::getDeviceId, Function.identity()));
deviceList.forEach(x -> { deviceList.forEach(x -> {
Integer s = x.getOnlineStatus() == null ? 0 : x.getOnlineStatus(); Integer s = x.getOnlineStatus() == null ? 0 : x.getOnlineStatus();
if (0 == s) { if (0 == s) {
@@ -183,6 +192,13 @@ public class DeviceServiceImpl implements IDeviceService {
} }
} }
x.setLastRunningStatus(lastRunningStatus); x.setLastRunningStatus(lastRunningStatus);
String locationKey = NettyCacheKey.latestPositonKey + x.getDeviceName();
LocationMessage locationMessage = redisCache.getCacheObject(locationKey);
if (locationMessage == null) {
locationMessage = locationMap.get(x.getDeviceName());
}
x.setLocationMessage(locationMessage);
}); });
deviceList.sort(Comparator.comparing(Device::getFeStatus)); deviceList.sort(Comparator.comparing(Device::getFeStatus));
} }
@@ -353,7 +369,9 @@ public class DeviceServiceImpl implements IDeviceService {
}); });
} }
} }
deviceList.sort(Comparator.comparing(Device::getFeStatus)); if (!CollectionUtils.isEmpty(deviceList)) {
deviceList.sort(Comparator.comparing(Device::getFeStatus));
}
return deviceList; return deviceList;
} }

View File

@@ -10,6 +10,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import java.util.Comparator;
import java.util.List; import java.util.List;
@Service @Service
@@ -64,6 +65,7 @@ public class WorkRecordService {
list.forEach(workRecord -> { list.forEach(workRecord -> {
workRecord.setJsonData(null); workRecord.setJsonData(null);
}); });
list.sort(Comparator.comparing(WorkRecord::getId).reversed());
} }
return list; return list;
} }
@@ -72,16 +74,13 @@ public class WorkRecordService {
return workRecordMapper.selectById(id); return workRecordMapper.selectById(id);
} }
public List<LatAndLngEntity> selectByTaskId(Long taskId) { public WorkRecord selectByTaskId(Long taskId) {
DevicePlanTask devicePlanTask = devicePlanTaskMapper.selectById(taskId); // 先查询设备任务,确认任务存在 DevicePlanTask devicePlanTask = devicePlanTaskMapper.selectById(taskId); // 先查询设备任务,确认任务存在
if (devicePlanTask == null) { if (devicePlanTask == null) {
return null; return null;
} }
Long routeId = devicePlanTask.getRouteId(); // 获取任务关联的路线ID Long routeId = devicePlanTask.getRouteId(); // 获取任务关联的路线ID
WorkRecord workRecord = workRecordMapper.selectById(routeId); // 查询路线ID对应的工作记录 WorkRecord workRecord = workRecordMapper.selectById(routeId); // 查询路线ID对应的工作记录
if (workRecord != null && workRecord.getJsonData() != null) { return workRecord; // 如果没有找到对应的工作记录或路线数据,返回 null
return workRecord.getJsonData().getPath(); // 返回路线数据
}
return null; // 如果没有找到对应的工作记录或路线数据,返回 null
} }
} }

View File

@@ -39,10 +39,14 @@
<result property="guid" column="guid"/> <result property="guid" column="guid"/>
<result property="protocolCode" column="protocol_code"/> <result property="protocolCode" column="protocol_code"/>
<result property="onlineStatus" column="online_status"/> <result property="onlineStatus" column="online_status"/>
<result property="hasHost" column="has_host"/>
<result property="hasRegistered" column="has_registered"/>
</resultMap> </resultMap>
<sql id="selectDeviceVo"> <sql id="selectDeviceVo">
select d.online_status, select d.online_status,
d.has_host,
d.has_registered,
d.device_alias, d.device_alias,
d.device_id, d.device_id,
d.org_id, d.org_id,
@@ -145,6 +149,8 @@
and d.active_time between #{params.beginActiveTime} and #{params.endActiveTime} and d.active_time between #{params.beginActiveTime} and #{params.endActiveTime}
</if> </if>
<if test="onlineStatus != null ">and d.online_status = #{onlineStatus}</if> <if test="onlineStatus != null ">and d.online_status = #{onlineStatus}</if>
<if test="hasHost != null ">and d.has_host = #{hasHost}</if>
<if test="hasRegistered != null ">and d.has_registered = #{hasRegistered}</if>
</where> </where>
order by d.create_time desc order by d.create_time desc
</select> </select>
@@ -302,6 +308,8 @@
<if test="onlineStatus != null">online_status,</if> <if test="onlineStatus != null">online_status,</if>
<if test="orgId != null">org_id,</if> <if test="orgId != null">org_id,</if>
<if test="siteId != null">site_id,</if> <if test="siteId != null">site_id,</if>
<if test="hasHost != null">has_host,</if>
<if test="hasRegistered != null">has_registered,</if>
</trim> </trim>
<trim prefix="values (" suffix=")" suffixOverrides=","> <trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="deviceName != null and deviceName != ''">#{deviceName},</if> <if test="deviceName != null and deviceName != ''">#{deviceName},</if>
@@ -335,6 +343,8 @@
<if test="onlineStatus != null">#{onlineStatus},</if> <if test="onlineStatus != null">#{onlineStatus},</if>
<if test="orgId != null">#{orgId},</if> <if test="orgId != null">#{orgId},</if>
<if test="siteId != null">#{siteId},</if> <if test="siteId != null">#{siteId},</if>
<if test="hasHost != null">#{hasHost},</if>
<if test="hasRegistered != null">#{hasRegistered},</if>
</trim> </trim>
</insert> </insert>
@@ -395,6 +405,8 @@
<if test="onlineStatus != null">online_status = #{onlineStatus},</if> <if test="onlineStatus != null">online_status = #{onlineStatus},</if>
<if test="orgId != null">org_id = #{orgId},</if> <if test="orgId != null">org_id = #{orgId},</if>
<if test="siteId != null">site_id = #{siteId},</if> <if test="siteId != null">site_id = #{siteId},</if>
<if test="hasHost != null">has_host = #{hasHost},</if>
<if test="hasRegistered != null">has_registered = #{hasRegistered},</if>
</trim> </trim>
where device_id = #{deviceId} where device_id = #{deviceId}
</update> </update>

View File

@@ -44,7 +44,6 @@ import java.util.concurrent.TimeUnit;
@Service @Service
public class IotDeviceCommonService { public class IotDeviceCommonService {
@Autowired @Autowired
private IotDeviceCommonMapper iotDeviceCommonMapper; private IotDeviceCommonMapper iotDeviceCommonMapper;
@@ -92,16 +91,13 @@ public class IotDeviceCommonService {
device.setDelFlag(false); device.setDelFlag(false);
device.setCreateTime(LocalDateTime.now()); device.setCreateTime(LocalDateTime.now());
log.info("设备 {} 已加入未注册缓存", deviceDTO.getSn()); log.info("设备 {} 已加入未注册缓存", deviceDTO.getSn());
} }
public List<IoTCommonDevice> getUnRegisterDevices() { public List<IoTCommonDevice> getUnRegisterDevices() {
return new ArrayList<>(GlobalMemory.unRegisterCommonDeviceMap.values()); return new ArrayList<>(GlobalMemory.unRegisterCommonDeviceMap.values());
} }
public Page<IoTCommonDevice> selectDevicePage(Page<IoTCommonDevice> page, IoTCommonDevice device) { public Page<IoTCommonDevice> selectDevicePage(Page<IoTCommonDevice> page, IoTCommonDevice device) {
LambdaQueryWrapper<IoTCommonDevice> wrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<IoTCommonDevice> wrapper = new LambdaQueryWrapper<>();
@@ -123,7 +119,6 @@ public class IotDeviceCommonService {
return iotDeviceCommonMapper.selectPage(page, wrapper); return iotDeviceCommonMapper.selectPage(page, wrapper);
} }
public IoTCommonDevice getBySn(String sn) { public IoTCommonDevice getBySn(String sn) {
LambdaQueryWrapper<IoTCommonDevice> wrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<IoTCommonDevice> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(IoTCommonDevice::getDeviceId, sn) wrapper.eq(IoTCommonDevice::getDeviceId, sn)
@@ -131,7 +126,6 @@ public class IotDeviceCommonService {
return iotDeviceCommonMapper.selectOne(wrapper); return iotDeviceCommonMapper.selectOne(wrapper);
} }
public void registerDevice(IoTCommonDevice device) { public void registerDevice(IoTCommonDevice device) {
device.setHasRegister(true); device.setHasRegister(true);
device.setUpdateTime(LocalDateTime.now()); device.setUpdateTime(LocalDateTime.now());
@@ -144,7 +138,6 @@ public class IotDeviceCommonService {
GlobalMemory.unRegisterCommonDeviceMap.remove(device.getSn()); GlobalMemory.unRegisterCommonDeviceMap.remove(device.getSn());
} }
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean updateDevice(DeviceConfigDTO deviceDTO) { public boolean updateDevice(DeviceConfigDTO deviceDTO) {
// 1. 查询设备是否存在 // 1. 查询设备是否存在
@@ -181,7 +174,6 @@ public class IotDeviceCommonService {
return iotDeviceCommonMapper.saveOrUpdate(device); return iotDeviceCommonMapper.saveOrUpdate(device);
} }
/** /**
* 设备连接测试(根据协议类型) * 设备连接测试(根据协议类型)
*/ */
@@ -201,12 +193,10 @@ public class IotDeviceCommonService {
} }
} }
public IoTCommonDevice getFromUnRegisterCache(String sn) { public IoTCommonDevice getFromUnRegisterCache(String sn) {
return GlobalMemory.unRegisterCommonDeviceMap.get(sn); return GlobalMemory.unRegisterCommonDeviceMap.get(sn);
} }
public void updateUnRegisterCache(IoTCommonDevice device) { public void updateUnRegisterCache(IoTCommonDevice device) {
if (device.getDeviceId() == null) { if (device.getDeviceId() == null) {
throw new RuntimeException("设备 ID (SN) 不能为空"); throw new RuntimeException("设备 ID (SN) 不能为空");
@@ -216,14 +206,14 @@ public class IotDeviceCommonService {
log.info("未注册设备 {} 缓存配置已更新", device.getDeviceId()); log.info("未注册设备 {} 缓存配置已更新", device.getDeviceId());
} }
/** /**
* MQTT 连接测试 * MQTT 连接测试
*/ */
private boolean testMqttConnection(String ip, Integer port, String username, String password) { private boolean testMqttConnection(String ip, Integer port, String username, String password) {
try { try {
// TODO: 使用 MQTT 客户端测试连接 // TODO: 使用 MQTT 客户端测试连接
// MqttClient client = new MqttClient("tcp://" + ip + ":" + port, "test-" + UUID.randomUUID()); // MqttClient client = new MqttClient("tcp://" + ip + ":" + port, "test-" +
// UUID.randomUUID());
// ... // ...
return true; // 暂时返回 true,后续接入真实 MQTT 库 return true; // 暂时返回 true,后续接入真实 MQTT 库
} catch (Exception e) { } catch (Exception e) {
@@ -258,14 +248,13 @@ public class IotDeviceCommonService {
} }
} }
public void removeByIds(List<Long> ids) { public void removeByIds(List<Long> ids) {
if (CollectionUtils.isEmpty(ids)) return; if (CollectionUtils.isEmpty(ids))
return;
iotDeviceCommonMapper.deleteBatchIds(ids); iotDeviceCommonMapper.deleteBatchIds(ids);
} }
// 3. 按 productId 分组,缓存映射配置避免重复查询 // 3. 按 productId 分组,缓存映射配置避免重复查询
public static Map<Long, List<DeviceModelPropertyMapping>> mappingCache = new ConcurrentHashMap<>(); public static Map<Long, List<DeviceModelPropertyMapping>> mappingCache = new ConcurrentHashMap<>();
@@ -300,7 +289,8 @@ public class IotDeviceCommonService {
// 注册到处理器,用 productId 区分不同产品的映射 // 注册到处理器,用 productId 区分不同产品的映射
String handlerType = "property-" + productId; String handlerType = "property-" + productId;
GenericPropertyMessageHandler handler = (GenericPropertyMessageHandler) monitor.getMessageHandler(handlerType); GenericPropertyMessageHandler handler = (GenericPropertyMessageHandler) monitor
.getMessageHandler(handlerType);
handler.registerMappingConfig(handlerType, mappingConfig); handler.registerMappingConfig(handlerType, mappingConfig);
// 获取产品信息 // 获取产品信息
@@ -334,11 +324,14 @@ public class IotDeviceCommonService {
/** /**
* 根据数据库映射列表构建 PropertyMappingConfig * 根据数据库映射列表构建 PropertyMappingConfig
* *
* <p>将 {@link DeviceModelPropertyMapping} 转换为 {@link PropertyMappingConfig.FieldMapping}:</p> * <p>
* 将 {@link DeviceModelPropertyMapping} 转换为
* {@link PropertyMappingConfig.FieldMapping}:
* </p>
* <ul> * <ul>
* <li>sourceKey → FieldMapping.source(外部 JSON 中的字段名)</li> * <li>sourceKey → FieldMapping.source(外部 JSON 中的字段名)</li>
* <li>standardKey → FieldMapping.target(统一后的内部属性名)</li> * <li>standardKey → FieldMapping.target(统一后的内部属性名)</li>
* <li>dataType → FieldMapping.valueType(自动类型转换)</li> * <li>dataType → FieldMapping.valueType(自动类型转换)</li>
* </ul> * </ul>
*/ */
private PropertyMappingConfig buildMappingConfig(List<DeviceModelPropertyMapping> mappings) { private PropertyMappingConfig buildMappingConfig(List<DeviceModelPropertyMapping> mappings) {