diff --git a/maibu-admin/src/main/resources/application-dev.yml b/maibu-admin/src/main/resources/application-dev.yml index f0e6540..0d6d5d7 100644 --- a/maibu-admin/src/main/resources/application-dev.yml +++ b/maibu-admin/src/main/resources/application-dev.yml @@ -3,7 +3,7 @@ spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://1.95.137.212:3306/maibu?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + url: jdbc:mysql://localhost:3306/maibu?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: satabot password: satabot @@ -31,7 +31,7 @@ spring: mqtt: username: maibu # 账号 password: jsmbzn520 # 密码 - host-url: tcp://1.95.137.212:1883 # mqtt连接tcp地址 + host-url: tcp://localhost:1883 # mqtt连接tcp地址 client-id: maibu-mqtt-client # 客户端Id,不能相同,采用随机数 ${random.value} default-topic: test # 默认主题 timeout: 30 # 超时时间 @@ -84,3 +84,6 @@ influxdb: org: maibu token: maibu-token url: http://localhost:8086 + +minio: + endpoint : https://oss.satabot.com diff --git a/maibu-common/src/main/java/com/maibu/core/business/DevicePlanTask.java b/maibu-common/src/main/java/com/maibu/core/business/DevicePlanTask.java index 1610977..4dbeced 100644 --- a/maibu-common/src/main/java/com/maibu/core/business/DevicePlanTask.java +++ b/maibu-common/src/main/java/com/maibu/core/business/DevicePlanTask.java @@ -29,6 +29,9 @@ public class DevicePlanTask extends OrgBaseDO { @TableField(exist = false) private String deviceAlias; // 别名 + @TableField(exist = false) + private String workName; // 路径规划名称 + private DeviceTaskStaus taskStaus; // 任务状态 @TableField(exist = false) diff --git a/maibu-common/src/main/java/com/maibu/core/business/WorkRecord.java b/maibu-common/src/main/java/com/maibu/core/business/WorkRecord.java index 0ec967b..d55a904 100644 --- a/maibu-common/src/main/java/com/maibu/core/business/WorkRecord.java +++ b/maibu-common/src/main/java/com/maibu/core/business/WorkRecord.java @@ -21,7 +21,7 @@ public class WorkRecord extends OrgBaseDO { private String workName; private Long userId; - private String mode;// 导航任务模式 single_point|coverage + private String mode;// 导航任务模式 single_point|coverage |predefined_path // private String jsonData; @@ -31,4 +31,6 @@ public class WorkRecord extends OrgBaseDO { private PlanPath jsonData; // 计划 private String imgUrl; + + private String fileUrl; // 存放的 路径规划的id } diff --git a/maibu-common/src/main/java/com/maibu/core/business/device/NettyDevice.java b/maibu-common/src/main/java/com/maibu/core/business/device/NettyDevice.java index 7621edb..086b791 100644 --- a/maibu-common/src/main/java/com/maibu/core/business/device/NettyDevice.java +++ b/maibu-common/src/main/java/com/maibu/core/business/device/NettyDevice.java @@ -8,6 +8,7 @@ import com.maibu.core.business.path.RoutePlanSendEntity; import com.maibu.core.enums.ConnectorType; import com.maibu.core.enums.DeviceTaskStaus; import com.maibu.core.enums.RouteType; +import com.maibu.core.host.HostModeChangeDTO; import com.maibu.core.host.HostWorkRecordCreateDTO; import com.maibu.core.host.MutiPointEntity; import com.maibu.core.host.PointEntity; @@ -149,7 +150,6 @@ public class NettyDevice extends Connector { regionConsecutiveTaskRecordMapper.saveOrUpdate(record); } - siteMemory.removeDevicePlanExecute(task.getDeviceId()); GlobalMemory.taskPathMap.remove(task.getId()); task = null; @@ -293,11 +293,26 @@ public class NettyDevice extends Connector { // } // todo 优化 - public synchronized boolean pauseTask() { + public synchronized boolean pauseTask() throws MqttException { if (executingTask) { task.setTaskStaus(DeviceTaskStaus.PAUSE); - sendPathCommand((byte) 0x01, (short) 2, 0.00d, 0.00d, (short) 0); - // executingTask = false; + HostWorkRecordCreateDTO dto = new HostWorkRecordCreateDTO(); + dto.setAction("pause"); + dto.setCommand_id(task.getId() + "_" + System.currentTimeMillis()); + dto.setTask_id(String.valueOf(task.getId())); + dto.setSn(task.getDeviceId()); + // TODO 存内存 + WorkRecord workRecord = workRecordMapper.selectById(task.getRouteId()); + log.debug("发送开始任务 workRecord {}", workRecord); + if (workRecord != null) { + dto.setMode(workRecord.getMode()); + } + dto.setTimestamp(System.currentTimeMillis()); + dto.setMessage_id(task.getId() + "_" + System.currentTimeMillis()); + dto.setProtocol_version(Constant.protocol_version); + GlobalMemory.customMqttDeviceMonitor.publish(mqttClientId, + String.format(MqttTopic.MOWER_TASK_ROUTE_SET, task.getDeviceId()), + JsonUtils.toJsonString(dto)); return true; } else { return false; @@ -306,15 +321,26 @@ public class NettyDevice extends Connector { } // todo 优化 - public synchronized boolean recoveryTask() throws InterruptedException { + public synchronized boolean recoveryTask() throws InterruptedException, MqttException { if (task.getTaskStaus().equals(DeviceTaskStaus.PAUSE)) { - task.setTaskStaus(DeviceTaskStaus.EXECUTING); - executingTask = true; - sendPathCommand((byte) 0x01, (short) 3, 0.00d, 0.00d, (short) 0); - // Thread.sleep(1000); - // sendPathCommand((byte) 0x01, (short) 1, currentPoint.getLat(), - // currentPoint.getLng(), (short) 0); - // sendNextPoint(); + task.setTaskStaus(DeviceTaskStaus.PAUSE); + HostWorkRecordCreateDTO dto = new HostWorkRecordCreateDTO(); + dto.setAction("resume"); + dto.setCommand_id(task.getId() + "_" + System.currentTimeMillis()); + dto.setTask_id(String.valueOf(task.getId())); + dto.setSn(task.getDeviceId()); + // TODO 存内存 + WorkRecord workRecord = workRecordMapper.selectById(task.getRouteId()); + log.debug("发送开始任务 workRecord {}", workRecord); + if (workRecord != null) { + dto.setMode(workRecord.getMode()); + } + dto.setTimestamp(System.currentTimeMillis()); + dto.setMessage_id(task.getId() + "_" + System.currentTimeMillis()); + dto.setProtocol_version(Constant.protocol_version); + GlobalMemory.customMqttDeviceMonitor.publish(mqttClientId, + String.format(MqttTopic.MOWER_TASK_ROUTE_SET, task.getDeviceId()), + JsonUtils.toJsonString(dto)); return true; } else { return false; @@ -372,7 +398,7 @@ public class NettyDevice extends Connector { } } - public void sendNavigationCommand() throws MqttException { + public synchronized void sendNavigationCommand() throws MqttException { log.debug("发送开始任务 taskStatus:{},routeId:{}", task.getTaskStaus(), task.getRouteId()); if (!task.getTaskStaus().equals(DeviceTaskStaus.EXECUTING)) @@ -404,13 +430,6 @@ public class NettyDevice extends Connector { } if (workRecord.getMode().equals("coverage")) { List boundary_points = new ArrayList<>(); - // workRecord.getJsonData().getPath().forEach(x -> { - // MutiPointEntity mutiPoint = new MutiPointEntity(); - // mutiPoint.setLongitude(x.getLng()); - // mutiPoint.setLatitude(x.getLat()); - // mutiPoint.setIndex(); - // boundary_points.add(mutiPoint); - // }); List path = workRecord.getJsonData().getPath(); for (int i = 0; i < path.size(); i++) { @@ -424,8 +443,59 @@ public class NettyDevice extends Connector { } point.setBoundary_points(boundary_points); } + if (workRecord.getMode().equals("predefined_path")) { + point.setPath_id(workRecord.getId()); + point.setUrl(workRecord.getFileUrl()); + point.setVersion("v2"); + } } } + point.setExecute(true); + dto.setData(point); + GlobalMemory.customMqttDeviceMonitor.publish(mqttClientId, + String.format(MqttTopic.MOWER_TASK_ROUTE_SET, task.getDeviceId()), + JsonUtils.toJsonString(dto)); + } + } + + public synchronized void sendPredefined(WorkRecord workRecord) throws MqttException { + + log.debug("发送开始任务 taskStatus:{},routeId:{}", task.getTaskStaus(), task.getRouteId()); + if (task != null) + return; + + if (workRecord != null) { + HostWorkRecordCreateDTO dto = new HostWorkRecordCreateDTO(); + dto.setAction("start"); + dto.setCommand_id(task.getId() + "_" + System.currentTimeMillis()); + dto.setTask_id(String.valueOf(task.getId())); + dto.setSn(task.getDeviceId()); + + dto.setMode(workRecord.getMode()); + + dto.setTimestamp(System.currentTimeMillis()); + dto.setMessage_id("predefined" + "_" + System.currentTimeMillis()); + dto.setProtocol_version(Constant.protocol_version); + + PointEntity point = new PointEntity(); + + List boundary_points = new ArrayList<>(); + List path = workRecord.getJsonData().getPath(); + + for (int i = 0; i < path.size(); i++) { + LatAndLngEntity x = path.get(i); + + MutiPointEntity mutiPoint = new MutiPointEntity(); + mutiPoint.setLongitude(x.getLng()); + mutiPoint.setLatitude(x.getLat()); + mutiPoint.setIndex(i); + boundary_points.add(mutiPoint); + } + point.setBoundary_points(boundary_points); + point.setPath_id(workRecord.getId()); + point.setVersion("v2"); + point.setExecute(false); + dto.setData(point); GlobalMemory.customMqttDeviceMonitor.publish(mqttClientId, String.format(MqttTopic.MOWER_TASK_ROUTE_SET, task.getDeviceId()), @@ -461,4 +531,28 @@ public class NettyDevice extends Connector { JsonUtils.toJsonString(payload)); } + /** + * 下发模式切换指令 + * + * @param deviceId + * @param controlMode + * @throws MqttException + */ + public void modeChage(String deviceId, Integer controlMode) throws MqttException { + HostModeChangeDTO payload = new HostModeChangeDTO(); + payload.setSn(deviceId); + payload.setTimestamp(System.currentTimeMillis()); + payload.setMessage_id("modeChange_" + System.currentTimeMillis()); + payload.setCommand_id("modeChangeCommand_ " + System.currentTimeMillis()); + payload.setProtocol_version(Constant.protocol_version); + Map data = new HashMap<>(); + data.put("control_mode", controlMode); + payload.setData(data); + + GlobalMemory.customMqttDeviceMonitor.publish( + mqttClientId, + String.format(MqttTopic.MOWER_MODE_CHANGE_SET, deviceId), + JsonUtils.toJsonString(payload)); + } + } diff --git a/maibu-common/src/main/java/com/maibu/core/business/dto/ConsecutiveTaskQueryDTO.java b/maibu-common/src/main/java/com/maibu/core/business/dto/ConsecutiveTaskQueryDTO.java new file mode 100644 index 0000000..af8468a --- /dev/null +++ b/maibu-common/src/main/java/com/maibu/core/business/dto/ConsecutiveTaskQueryDTO.java @@ -0,0 +1,29 @@ +package com.maibu.core.business.dto; + +import java.time.LocalDateTime; + +import org.springframework.format.annotation.DateTimeFormat; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.maibu.core.enums.DeviceTaskStaus; + +import lombok.Data; + +@Data +public class ConsecutiveTaskQueryDTO { + + private Long siteId; + + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime startTime; + + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime endTime; + + private String deviceId; + + private DeviceTaskStaus taskStatus; + +} diff --git a/maibu-common/src/main/java/com/maibu/core/business/dto/DeviceMapPushDTO.java b/maibu-common/src/main/java/com/maibu/core/business/dto/DeviceMapPushDTO.java index 6e0d626..5cbc519 100644 --- a/maibu-common/src/main/java/com/maibu/core/business/dto/DeviceMapPushDTO.java +++ b/maibu-common/src/main/java/com/maibu/core/business/dto/DeviceMapPushDTO.java @@ -12,6 +12,8 @@ public class DeviceMapPushDTO { private double a; // altitude private String s;// status 运行状态 private Long t; // taskId 任务id + private double p; // 任务进度 + private String wk; // 任务进度 private double b; // 电量 private boolean ha = false;// hasAlarm 有无告警 diff --git a/maibu-common/src/main/java/com/maibu/core/business/inter/mower/MowerCommandDispather.java b/maibu-common/src/main/java/com/maibu/core/business/inter/mower/MowerCommandDispather.java index 0340e5c..fa44214 100644 --- a/maibu-common/src/main/java/com/maibu/core/business/inter/mower/MowerCommandDispather.java +++ b/maibu-common/src/main/java/com/maibu/core/business/inter/mower/MowerCommandDispather.java @@ -15,7 +15,7 @@ public class MowerCommandDispather { @Resource private ApplicationContext applicationContext; - public Long dispathDeviceTask(DeviceTaskCommandDTO dto) { + public Long dispathDeviceTask(DeviceTaskCommandDTO dto) throws Exception { Map beanMap = applicationContext.getBeansOfType(MowerCommandInter.class); // 遍历 Long id = null; diff --git a/maibu-common/src/main/java/com/maibu/core/business/inter/mower/MowerCommandInter.java b/maibu-common/src/main/java/com/maibu/core/business/inter/mower/MowerCommandInter.java index 23f3851..dfe4e17 100644 --- a/maibu-common/src/main/java/com/maibu/core/business/inter/mower/MowerCommandInter.java +++ b/maibu-common/src/main/java/com/maibu/core/business/inter/mower/MowerCommandInter.java @@ -4,6 +4,6 @@ import com.maibu.core.business.dto.DeviceTaskCommandDTO; public interface MowerCommandInter { - Long createDeviceTask(DeviceTaskCommandDTO dto); + Long createDeviceTask(DeviceTaskCommandDTO dto) throws Exception; } diff --git a/maibu-common/src/main/java/com/maibu/core/host/HostModeChangeDTO.java b/maibu-common/src/main/java/com/maibu/core/host/HostModeChangeDTO.java new file mode 100644 index 0000000..53505cd --- /dev/null +++ b/maibu-common/src/main/java/com/maibu/core/host/HostModeChangeDTO.java @@ -0,0 +1,16 @@ +package com.maibu.core.host; + +import java.util.Map; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +@Data +public class HostModeChangeDTO extends HostBase { + + private String command_id; + + private Map data; // map key control_mode 2=自动任务,3=远程遥控 + +} diff --git a/maibu-common/src/main/java/com/maibu/core/host/HostModeChangeReplyDTO.java b/maibu-common/src/main/java/com/maibu/core/host/HostModeChangeReplyDTO.java new file mode 100644 index 0000000..a1b93c3 --- /dev/null +++ b/maibu-common/src/main/java/com/maibu/core/host/HostModeChangeReplyDTO.java @@ -0,0 +1,16 @@ +package com.maibu.core.host; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +@Data +public class HostModeChangeReplyDTO extends HostBase { + + private Boolean accepted; + + private String code; + + private String message; + +} diff --git a/maibu-common/src/main/java/com/maibu/core/host/HostTaskStatusDetailDTO.java b/maibu-common/src/main/java/com/maibu/core/host/HostTaskStatusDetailDTO.java index e8d860e..4d5c43c 100644 --- a/maibu-common/src/main/java/com/maibu/core/host/HostTaskStatusDetailDTO.java +++ b/maibu-common/src/main/java/com/maibu/core/host/HostTaskStatusDetailDTO.java @@ -1,12 +1,25 @@ package com.maibu.core.host; - import lombok.Data; @Data public class HostTaskStatusDetailDTO { - private float progress; + private double progress;// 已完成作业路径比例,转移段持平且任务内不回退。规划完成前的 0 不代表已开始覆盖。 + + private double distance_remaining; // 整任务剩余作业路径 + private Integer number_of_recoveries;// 恢复行为次数,不代表障碍数量。 + private double elapsed_s; + private double eta_s; + private Integer current_cell_index; + private Integer total_cells; + private double distance_traveled; + private double speed; + private Integer task_phase; + private String error_code; private String error_message; - + private double total_distance_m; + private double distance_traveled_m; + private double execution_time_s; + private Integer cells_completed; } diff --git a/maibu-common/src/main/java/com/maibu/core/host/HostWorkRecordCreateDTO.java b/maibu-common/src/main/java/com/maibu/core/host/HostWorkRecordCreateDTO.java index 679c343..36d2571 100644 --- a/maibu-common/src/main/java/com/maibu/core/host/HostWorkRecordCreateDTO.java +++ b/maibu-common/src/main/java/com/maibu/core/host/HostWorkRecordCreateDTO.java @@ -1,20 +1,20 @@ package com.maibu.core.host; - import lombok.Data; import lombok.EqualsAndHashCode; @EqualsAndHashCode(callSuper = true) @Data -public class HostWorkRecordCreateDTO extends HostBase { +public class HostWorkRecordCreateDTO extends HostBase { - private String task_id; //任务唯一编号 - private String command_id;//指令唯一编号 - private String mode;//导航任务模式 single_point|coverage - private String action;//任务动作 start/pause/resume/cancel -// private String frame_id; //WGS84 + private String task_id; // 任务唯一编号 + private String command_id;// 指令唯一编号 + private String mode;// 导航任务模式 single_point|coverage + private String action;// 任务动作 start/pause/resume/cancel + // private String frame_id; //WGS84 private PointEntity data; + } diff --git a/maibu-common/src/main/java/com/maibu/core/host/ObstacleDetectionData.java b/maibu-common/src/main/java/com/maibu/core/host/ObstacleDetectionData.java index 5b8964c..9d0cf6e 100644 --- a/maibu-common/src/main/java/com/maibu/core/host/ObstacleDetectionData.java +++ b/maibu-common/src/main/java/com/maibu/core/host/ObstacleDetectionData.java @@ -9,7 +9,7 @@ import lombok.Data; @Data public class ObstacleDetectionData { - private MesType mesType; + private MesType type; /** * 融合状态 diff --git a/maibu-common/src/main/java/com/maibu/core/host/PointEntity.java b/maibu-common/src/main/java/com/maibu/core/host/PointEntity.java index 905b3ae..9031f92 100644 --- a/maibu-common/src/main/java/com/maibu/core/host/PointEntity.java +++ b/maibu-common/src/main/java/com/maibu/core/host/PointEntity.java @@ -9,12 +9,16 @@ public class PointEntity { private double latitude; private double longitude; - private double yaw;//目标航向,正北0°、顺时针正、0-360° + private double yaw;// 目标航向,正北0°、顺时针正、0-360° private List boundary_points; - private double cell_size;//覆盖分辨率,单位m + private double cell_size;// 覆盖分辨率,单位m - private double angle;//覆盖作业方向,单位度 + private double angle;// 覆盖作业方向,单位度 + private String url; // minio 存放文件的地址 + private Long path_id; // 路径id 系统保存用的routeId; + private String version; + private boolean execute = true; // 是直接执行还是创建自定义任务 } diff --git a/maibu-common/src/main/java/com/maibu/mapper/DevicePlanTaskMapper.java b/maibu-common/src/main/java/com/maibu/mapper/DevicePlanTaskMapper.java index fd31adc..36aea95 100644 --- a/maibu-common/src/main/java/com/maibu/mapper/DevicePlanTaskMapper.java +++ b/maibu-common/src/main/java/com/maibu/mapper/DevicePlanTaskMapper.java @@ -17,9 +17,10 @@ import com.maibu.mybatis.mapper.BaseMapperX; @Repository public interface DevicePlanTaskMapper extends BaseMapperX { - @Select("SELECT t.*, d.device_alias " + + @Select("SELECT t.*, iwr.work_name,d.device_alias " + "FROM iot_device_task t " + - "LEFT JOIN iot_device d ON d.device_id = t.device_id " + + "LEFT JOIN iot_device d ON d.serial_number = t.device_id " + + "left join iot_work_record iwr on t.route_id = iwr.id " + "${ew.customSqlSegment}") List selectTaskHistoryWithDeviceAlias( @Param(Constants.WRAPPER) Wrapper wrapper); diff --git a/maibu-common/src/main/java/com/maibu/memory/GlobalMemory.java b/maibu-common/src/main/java/com/maibu/memory/GlobalMemory.java index 103cda2..a4a1963 100644 --- a/maibu-common/src/main/java/com/maibu/memory/GlobalMemory.java +++ b/maibu-common/src/main/java/com/maibu/memory/GlobalMemory.java @@ -11,6 +11,7 @@ import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import java.util.*; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; @Component @@ -29,6 +30,8 @@ public class GlobalMemory { public static InfluxDBUtil influxDBUtil; + public static ConcurrentHashMap> modeFutureMap = new ConcurrentHashMap<>(); + // public static MqttClientUtil mqttClientUtil; public static CustomMqttDeviceMonitor customMqttDeviceMonitor = null; // 这里面包含了 连我们自己的mqtt 和外部的一些设备,自己的用clientId @@ -114,8 +117,11 @@ public class GlobalMemory { MqttTopic.MOWER_WILDCARD_REALTIME, // 状态消息 MqttTopic.MOWER_WILDCARD_TASK_TARGET_REPLY, // 路径规划下发回复 MqttTopic.MOWER_OTA_RESULT_POST, // 在线升级 - MqttTopic.MOWER_OBSTACLE - )); + MqttTopic.MOWER_OBSTACLE, // 障碍物 + MqttTopic.MOWER_MODE_REPLY, // 模式切换回复 + MqttTopic.MOWER_FAULT_POST, // 故障上报 + MqttTopic.MOWER_FAULT_RECOVERY_REPLY // 故障恢复回复 + )); deviceConfig.setHandlerType(Constant.mqttHandleKey); // 唯一的 用于处理上位机mqtt的 customMqttDeviceMonitor.registerDevice(deviceConfig); } diff --git a/maibu-common/src/main/java/com/maibu/memory/SiteMemory.java b/maibu-common/src/main/java/com/maibu/memory/SiteMemory.java index 456acc8..f618a98 100644 --- a/maibu-common/src/main/java/com/maibu/memory/SiteMemory.java +++ b/maibu-common/src/main/java/com/maibu/memory/SiteMemory.java @@ -1,15 +1,12 @@ package com.maibu.memory; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.lang3.StringUtils; -import org.apache.poi.ss.formula.functions.T; import org.eclipse.paho.client.mqttv3.MqttClient; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; @@ -75,11 +72,11 @@ public class SiteMemory { // 记录 设备上线后的运行数据 下线后清除并保存到数据库 public static ConcurrentHashMap deviceRunStatisticsMap = new ConcurrentHashMap<>(); - public static ConcurrentHashMap deviceMapPushMap = new ConcurrentHashMap<>(); + public static final ConcurrentHashMap deviceMapPushMap = new ConcurrentHashMap<>(); public static ConcurrentHashMap vehicleMessageMap = new ConcurrentHashMap<>(); - public static ConcurrentHashMap iotAutoRuleMap = new ConcurrentHashMap<>(); + public static final ConcurrentHashMap> iotAutoRuleMap = new ConcurrentHashMap<>(); public static final ConcurrentHashMap regionConsecutiveTaskMap = new ConcurrentHashMap<>(); @@ -109,20 +106,21 @@ public class SiteMemory { return record; } - public List getAllIotAutoRule() { - return new ArrayList<>(iotAutoRuleMap.values()); - } - - public IotAutoRule getIotAutoRule(RuleType ruleType) { + public List getIotAutoRule(RuleType ruleType) { return iotAutoRuleMap.get(ruleType); } public void saveIotAutoRule(RuleType ruleType, IotAutoRule iotAutoRule) { - iotAutoRuleMap.put(ruleType, iotAutoRule); + List list = iotAutoRuleMap.computeIfAbsent( + ruleType, k -> new ArrayList<>()); + + list.removeIf(item -> Objects.equals(item.getId(), iotAutoRule.getId())); + list.add(iotAutoRule); } - public void removeIotAutoRule(RuleType ruleType) { - iotAutoRuleMap.remove(ruleType); + public void removeIotAutoRule(IotAutoRule iotAutoRule) { + List list = iotAutoRuleMap.get(iotAutoRule.getRuleType()); + list.removeIf(i -> iotAutoRule.getId().equals(i.getId())); } public List getAllVehicleMessage() { @@ -154,6 +152,10 @@ public class SiteMemory { return deviceMapPushMap; } + public DeviceMapPushDTO getDeviceMapPusById(String deviceId) { + return deviceMapPushMap.get(deviceId); + } + public void saveMapPushDTO(String deviceId, DeviceMapPushDTO dto) { deviceMapPushMap.put(deviceId, dto); } diff --git a/maibu-common/src/main/java/com/maibu/mqtt/HostMessageHandler.java b/maibu-common/src/main/java/com/maibu/mqtt/HostMessageHandler.java index 80abc9b..348939c 100644 --- a/maibu-common/src/main/java/com/maibu/mqtt/HostMessageHandler.java +++ b/maibu-common/src/main/java/com/maibu/mqtt/HostMessageHandler.java @@ -28,6 +28,7 @@ import org.springframework.util.CollectionUtils; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CompletableFuture; /** * mqtt相关的 上位机处理器 @@ -111,6 +112,15 @@ public class HostMessageHandler implements CustomMqttMessageHandler { case "obstacle_post": handleObstaclePost(deviceId, topic, payload); break; + case "mode_reply": + handleModeReply(deviceId, topic, payload); + break; + case "fault_post": + handleFaultPost(deviceId, topic, payload); + break; + case "fault_recovery_reply": + handleFaultRecoveryReply(deviceId, topic, payload); + break; default: log.debug("割草机消息: deviceId={}, topic={}, action={}", deviceId, topic, action); @@ -255,6 +265,7 @@ public class HostMessageHandler implements CustomMqttMessageHandler { dto.setMs(vehicleMessage.getMotion()); dto.setB(vehicleMessage.getBattery_percentage()); } + dto.setWk(nettyDevice.getTask() != null ? nettyDevice.getTask().getWorkName() : ""); siteMemory.saveMapPushDTO(deviceId, dto); } } @@ -560,6 +571,16 @@ public class HostMessageHandler implements CustomMqttMessageHandler { // NettyDevice nettyDevice = deviceSessionManager.getDevice(deviceId); // nettyDevice.finishTask(); // } + // 任务进度 百分比 + Long siteId = nettyDevice.getDevice().getSiteId(); + Long orgId = nettyDevice.getDevice().getOrgId(); + SiteMemory siteMemory = GlobalMemory.getSiteMemory(orgId, siteId); + if (siteMemory != null) { + DeviceMapPushDTO pushDto = siteMemory.getDeviceMapPusById(deviceId); + if (pushDto != null && dto.getData() != null) { + pushDto.setP(dto.getData().getProgress()); + } + } } } @@ -700,7 +721,7 @@ public class HostMessageHandler implements CustomMqttMessageHandler { List controlMasters = deviceSessionManager .getAllSlaveControl(deviceId); - data.setMesType(MesType.detectionReport); + data.setType(MesType.detectionReport); if (!CollectionUtils.isEmpty(controlMasters)) { for (NettyDevice x : controlMasters) { @@ -732,6 +753,44 @@ public class HostMessageHandler implements CustomMqttMessageHandler { } + private void handleModeReply(String deviceId, String topic, String payload) { + log.debug("【模式切换回复】deviceId={}, payload={}", deviceId, payload); + + HostModeChangeReplyDTO data = JsonUtils.parseObject(payload, HostModeChangeReplyDTO.class); + + CompletableFuture future = GlobalMemory.modeFutureMap.get(deviceId); + + if (future != null && data != null) { + future.complete(data.getAccepted()); + } + } + + private void handleFaultPost(String deviceId, String topic, String payload) { + log.debug("【故障上报】deviceId={}, payload={}", deviceId, payload); + + // HostFaultPostDTO data = JsonUtils.parseObject(payload, + // HostFaultPostDTO.class); + + // CompletableFuture future = GlobalMemory.modeFutureMap.get(deviceId); + + // if (future != null && data != null) { + // future.complete(data.getAccepted()); + // } + } + + private void handleFaultRecoveryReply(String deviceId, String topic, String payload) { + log.debug("【故障恢复回复】deviceId={}, payload={}", deviceId, payload); + + // HostFaultRecoveryReplyDTO data = JsonUtils.parseObject(payload, + // HostFaultRecoveryReplyDTO.class); + + // CompletableFuture future = GlobalMemory.modeFutureMap.get(deviceId); + + // if (future != null && data != null) { + // future.complete(data.getAccepted()); + // } + } + /** * 从 Topic 中解析 deviceId * Topic 格式:mower/{deviceId}/... @@ -782,10 +841,16 @@ public class HostMessageHandler implements CustomMqttMessageHandler { return "control_reply"; if (remaining.equals("task/target/reply")) return "route_reply"; - if (remaining.equals("/ota/result/post")) + if (remaining.equals("ota/result/post")) return "upgrade_reply"; - if (remaining.equals("/perception/obstacle/post")) + if (remaining.equals("perception/obstacle/post")) return "obstacle_post"; + if (remaining.equals("control/mode/set/reply")) + return "mode_reply"; + if (remaining.equals("event/fault/post")) + return "fault_post"; + if (remaining.equals("fault/recovery/reply")) + return "fault_recovery_reply"; return null; } diff --git a/maibu-common/src/main/java/com/maibu/mqtt/MqttTopic.java b/maibu-common/src/main/java/com/maibu/mqtt/MqttTopic.java index 0b58129..6ac8392 100644 --- a/maibu-common/src/main/java/com/maibu/mqtt/MqttTopic.java +++ b/maibu-common/src/main/java/com/maibu/mqtt/MqttTopic.java @@ -109,9 +109,24 @@ public class MqttTopic { public static final String MOWER_HEARTBEAT = "mower/+/event/heartbeat/post"; - //障碍物 + // 障碍物 public static final String MOWER_OBSTACLE = "mower/+/perception/obstacle/post"; + /** 模式切换指令 无遥才能下发(平台->设备) */ + public static final String MOWER_MODE_CHANGE_SET = "mower/%s/control/mode/set"; + + // 模式切换回复 + public static final String MOWER_MODE_REPLY = "mower/+/control/mode/reply"; + + // 故障上报 + public static final String MOWER_FAULT_POST = "mower/+/event/fault/post"; + + // 故障恢复 (平台->设备) + public static final String MOWER_FAULT_RECOVERY = "mower/%s/fault/recovery/set"; + + // 故障恢复回复 + public static final String MOWER_FAULT_RECOVERY_REPLY = "mower/+/fault/recovery/reply"; + /** * 格式化 Topic 模板,将 %s 替换为设备ID * diff --git a/maibu-netty-server/src/main/java/com/maibu/controller/DeviceTaskController.java b/maibu-netty-server/src/main/java/com/maibu/controller/DeviceTaskController.java index df4c708..3ecbee9 100644 --- a/maibu-netty-server/src/main/java/com/maibu/controller/DeviceTaskController.java +++ b/maibu-netty-server/src/main/java/com/maibu/controller/DeviceTaskController.java @@ -15,7 +15,9 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.maibu.core.business.DevicePlan; +import com.maibu.core.business.RegionConsecutiveTaskRecord; import com.maibu.core.business.dto.ConsecutiveTaskDTO; +import com.maibu.core.business.dto.ConsecutiveTaskQueryDTO; import com.maibu.core.business.dto.DeviceTaskCommandDTO; import com.maibu.core.controller.BaseController; import com.maibu.core.domain.AjaxResult; @@ -106,7 +108,7 @@ public class DeviceTaskController extends BaseController { } @PostMapping("/createDeviceTask") - public AjaxResult createDeviceTask(@RequestBody DeviceTaskCommandDTO dto) { + public AjaxResult createDeviceTask(@RequestBody DeviceTaskCommandDTO dto) throws Exception { String deviceId = dto.getDeviceId(); Long siteId = dto.getSiteId(); Long orgId = dto.getOrgId(); @@ -135,7 +137,7 @@ public class DeviceTaskController extends BaseController { } @PostMapping("/createRegionDefaultTask") - public AjaxResult createRegionDeviceTask(@RequestBody DeviceTaskCommandDTO dto) { + public AjaxResult createRegionDeviceTask(@RequestBody DeviceTaskCommandDTO dto) throws Exception { String deviceId = dto.getDeviceId(); Long siteId = dto.getSiteId(); Long orgId = dto.getOrgId(); @@ -177,7 +179,7 @@ public class DeviceTaskController extends BaseController { } @PostMapping("/pauseTask") - public AjaxResult pauseTask(@RequestBody DeviceTaskCommandDTO dto) { + public AjaxResult pauseTask(@RequestBody DeviceTaskCommandDTO dto) throws MqttException { String deviceId = dto.getDeviceId(); if (StringUtils.isEmpty(deviceId) && dto.getTaskId() == null) { return AjaxResult.error("deviceId或taskId为空!"); @@ -186,7 +188,7 @@ public class DeviceTaskController extends BaseController { } @PostMapping("/recoveryTask") - public AjaxResult recoveryTask(@RequestBody DeviceTaskCommandDTO dto) throws InterruptedException { + public AjaxResult recoveryTask(@RequestBody DeviceTaskCommandDTO dto) throws InterruptedException, MqttException { String deviceId = dto.getDeviceId(); if (StringUtils.isEmpty(deviceId) && dto.getTaskId() == null) { return AjaxResult.error("deviceId或taskId为空!"); @@ -195,7 +197,7 @@ public class DeviceTaskController extends BaseController { } @PostMapping("/backToRestPoint") - public AjaxResult backToRestPoint(@RequestBody DeviceTaskCommandDTO dto) { + public AjaxResult backToRestPoint(@RequestBody DeviceTaskCommandDTO dto) throws Exception { String deviceId = dto.getDeviceId(); Long siteId = dto.getSiteId(); Long orgId = dto.getOrgId(); @@ -223,8 +225,8 @@ public class DeviceTaskController extends BaseController { return AjaxResult.success(deviceTaskService.backToRestPoint(dto, getLoginUser().getUsername())); } - @PostMapping("/createConsecutiveTasks") - public AjaxResult createConsecutiveTasks(@RequestBody ConsecutiveTaskDTO dto) { + @PostMapping("/createConsecutiveTask") + public AjaxResult createConsecutiveTasks(@RequestBody ConsecutiveTaskDTO dto) throws Exception { String deviceId = dto.getDeviceId(); Long siteId = dto.getSiteId(); Long orgId = dto.getOrgId(); @@ -278,4 +280,11 @@ public class DeviceTaskController extends BaseController { return AjaxResult.success(); } + @PostMapping("/getConsecutiveTask") + public TableDataInfo getConsecutiveTask(@RequestBody ConsecutiveTaskQueryDTO dto) { + startPage(); + List list = deviceTaskService.getRegionConsecutiveTaskRecords(dto); + return getDataTable(list); + } + } diff --git a/maibu-netty-server/src/main/java/com/maibu/handler/MowerCommamdHandler.java b/maibu-netty-server/src/main/java/com/maibu/handler/MowerCommamdHandler.java index c8e3296..5b6323f 100644 --- a/maibu-netty-server/src/main/java/com/maibu/handler/MowerCommamdHandler.java +++ b/maibu-netty-server/src/main/java/com/maibu/handler/MowerCommamdHandler.java @@ -14,7 +14,7 @@ public class MowerCommamdHandler implements MowerCommandInter { private DeviceTaskService dService; @Override - public Long createDeviceTask(DeviceTaskCommandDTO dto) { + public Long createDeviceTask(DeviceTaskCommandDTO dto) throws Exception { return dService.createDeviceTask(dto, "system"); } diff --git a/maibu-netty-server/src/main/java/com/maibu/init/InitThread.java b/maibu-netty-server/src/main/java/com/maibu/init/InitThread.java index 1480876..3f45cc6 100644 --- a/maibu-netty-server/src/main/java/com/maibu/init/InitThread.java +++ b/maibu-netty-server/src/main/java/com/maibu/init/InitThread.java @@ -36,6 +36,7 @@ import com.maibu.core.business.WorkRecord; import com.maibu.core.domain.entity.SysParam; import com.maibu.core.domain.entity.SysSite; import com.maibu.core.domain.map.SiteMapInfo; +import com.maibu.core.domain.rule.IotAutoRule; import com.maibu.core.enums.DeviceTaskStaus; import com.maibu.core.enums.RouteType; import com.maibu.influxdb.util.InfluxDBUtil; @@ -45,6 +46,7 @@ import com.maibu.mapper.DevicePlanTaskMapper; import com.maibu.mapper.DeviceProductMapper; import com.maibu.mapper.DeviceRunParamMapper; import com.maibu.mapper.ErrorIdentificationStandardMapper; +import com.maibu.mapper.IotAutoRuleMapper; import com.maibu.mapper.IotDeviceCommonMapper; import com.maibu.mapper.OrderModelMapper; import com.maibu.mapper.SiteMapInfoMapper; @@ -138,6 +140,9 @@ public class InitThread implements ApplicationRunner { @Autowired private WorkRecordMapper workRecordMapper; + @Autowired + private IotAutoRuleMapper iotAutoRuleMapper; + @Value("${influxdb.url}") private String influxDBUrl; @@ -250,6 +255,19 @@ public class InitThread implements ApplicationRunner { }); } + // 任务触发规则 + List iotAutoRuleList = iotAutoRuleMapper.selectList(); + if (!CollectionUtils.isEmpty(iotAutoRuleList)) { + iotAutoRuleList.forEach(x -> { + Long orgId = x.getOrgId(); + Long siteId = x.getSiteId(); + SiteMemory siteMemory = GlobalMemory.getSiteMemory(orgId, siteId); + if (siteMemory != null) { + siteMemory.saveIotAutoRule(x.getRuleType(), x); + } + }); + } + // 场站的 地图配置信息 List siteMapInfoList = siteMapInfoMapper.selectList(); if (!CollectionUtils.isEmpty(siteMapInfoList)) { @@ -452,7 +470,7 @@ public class InitThread implements ApplicationRunner { workRecord -> workRecord.getRouteType().toString())); if (!CollectionUtils.isEmpty(records)) { records.forEach(x -> { - if (!map.containsKey(x.getRouteType())) { + if (!map.containsKey(x.getRouteType().toString())) { x.setUserId(1L); saveList.add(x); } diff --git a/maibu-netty-server/src/main/java/com/maibu/netty/handler/CommandForwardHandler.java b/maibu-netty-server/src/main/java/com/maibu/netty/handler/CommandForwardHandler.java index 5a417a1..e4116a8 100644 --- a/maibu-netty-server/src/main/java/com/maibu/netty/handler/CommandForwardHandler.java +++ b/maibu-netty-server/src/main/java/com/maibu/netty/handler/CommandForwardHandler.java @@ -117,7 +117,7 @@ public class CommandForwardHandler extends SimpleChannelInboundHandler { data.setCutter(pEntity.getMowerSpeedFlag()); data.setIgnition(pEntity.getIgnitionFlag()); data.setEmergency_stop(pEntity.getEmergencyStopFlag()); - data.setValid_for_ms(500); + data.setValid_for_ms(200); //间隔 可调整 deviceRemoteControlDTO.setData(data); deviceRemoteControlDTO .setCommand_id(Constant.remote_control_cmd_prefix + System.currentTimeMillis()); diff --git a/maibu-netty-server/src/main/java/com/maibu/service/DevicePlanTaskMonitorService.java b/maibu-netty-server/src/main/java/com/maibu/service/DevicePlanTaskMonitorService.java index d013b92..f3f6751 100644 --- a/maibu-netty-server/src/main/java/com/maibu/service/DevicePlanTaskMonitorService.java +++ b/maibu-netty-server/src/main/java/com/maibu/service/DevicePlanTaskMonitorService.java @@ -1,9 +1,26 @@ package com.maibu.service; +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +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.maibu.core.business.DevicePlan; import com.maibu.core.business.DevicePlanTask; import com.maibu.core.business.DeviceTaskPlanRule; +import com.maibu.core.business.WorkRecord; import com.maibu.core.business.device.NettyDevice; import com.maibu.core.enums.DeviceTaskPlanRepeatType; import com.maibu.core.enums.DeviceTaskStaus; @@ -13,21 +30,8 @@ import com.maibu.mapper.WorkRecordMapper; 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 javax.annotation.PreDestroy; -import java.time.DayOfWeek; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.util.*; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; +import lombok.extern.slf4j.Slf4j; @Slf4j @Service @@ -47,6 +51,7 @@ public class DevicePlanTaskMonitorService { @Autowired private ScheduledExecutorService executor; + /** * 计划迟的任务生成机器执行任务 */ @@ -71,7 +76,8 @@ public class DevicePlanTaskMonitorService { } private void createPlanTask(DevicePlan devicePlan) { - if (devicePlan.isDayTriggerFlag()) return; //当天应该创建的全部创建完 + if (devicePlan.isDayTriggerFlag()) + return; // 当天应该创建的全部创建完 DeviceTaskPlanRule rules = devicePlan.getDeviceTaskPlanRules(); if (rules != null) { DeviceTaskPlanRepeatType type = rules.getType(); @@ -85,9 +91,9 @@ public class DevicePlanTaskMonitorService { case MONTH: monthPlan(devicePlan); break; -// case ONCE: -// oncePlan(devicePlan); -// break; + // case ONCE: + // oncePlan(devicePlan); + // break; } } } @@ -102,18 +108,25 @@ public class DevicePlanTaskMonitorService { devicePlanTask.setDeviceId(devicePlan.getDeviceId()); devicePlanTask.setTaskStaus(DeviceTaskStaus.NEW); devicePlanTask.setRouteId(devicePlan.getRouteId()); + // 设置当前的路径规划信息 + WorkRecord workRecord = workRecordMapper.selectById(devicePlan.getRouteId()); + if (workRecord != null) { + devicePlanTask.setRouteType(workRecord.getRouteType()); + devicePlanTask.setWorkName(workRecord.getWorkName()); + } + devicePlanTask.setCreateTime(LocalDateTime.now()); - devicePlanTask.setTaskType("");//待拓展任务类型 + devicePlanTask.setTaskType("");// 待拓展任务类型 devicePlanTask.setOrgId(devicePlan.getOrgId()); devicePlanTask.setSiteId(devicePlan.getSiteId()); devicePlanTaskMapper.insert(devicePlanTask); SiteMemory siteMemory = GlobalMemory.getSiteMemory(devicePlan.getOrgId(), devicePlan.getSiteId()); siteMemory.addDevicePlanPrepare(devicePlanTask); - //啥时候重置?每日0点? + // 啥时候重置?每日0点? devicePlan.setDayTriggerFlag(true); devicePlan.setUpdateTime(LocalDateTime.now()); devicePlan.setTaskStaus(DeviceTaskStaus.EXECUTING); - //todo 怎么判断任务全部排完了?排完了就删除 + // todo 怎么判断任务全部排完了?排完了就删除 LocalDate today = LocalDate.now(); // 获取当前日期(系统时区) LocalDate endTime = rules.getEndTime(); if (endTime != null && endTime.isEqual(today)) { @@ -139,18 +152,24 @@ public class DevicePlanTaskMonitorService { devicePlanTask.setDeviceId(devicePlan.getDeviceId()); devicePlanTask.setTaskStaus(DeviceTaskStaus.NEW); devicePlanTask.setRouteId(devicePlan.getRouteId()); + // 设置当前的路径规划信息 + WorkRecord workRecord = workRecordMapper.selectById(devicePlan.getRouteId()); + if (workRecord != null) { + devicePlanTask.setRouteType(workRecord.getRouteType()); + devicePlanTask.setWorkName(workRecord.getWorkName()); + } devicePlanTask.setCreateTime(LocalDateTime.now()); devicePlanTask.setOrgId(devicePlan.getOrgId()); devicePlanTask.setSiteId(devicePlan.getSiteId()); - devicePlanTask.setTaskType("");//待拓展任务类型 + devicePlanTask.setTaskType("");// 待拓展任务类型 devicePlanTaskMapper.insert(devicePlanTask); SiteMemory siteMemory = GlobalMemory.getSiteMemory(devicePlan.getOrgId(), devicePlan.getSiteId()); siteMemory.addDevicePlanPrepare(devicePlanTask); - //啥时候重置?每日0点? + // 啥时候重置?每日0点? devicePlan.setDayTriggerFlag(true); devicePlan.setUpdateTime(LocalDateTime.now()); devicePlan.setTaskStaus(DeviceTaskStaus.EXECUTING); - //todo 怎么判断任务全部排完了?排完了就删除 + // todo 怎么判断任务全部排完了?排完了就删除 LocalDate endTime = rules.getEndTime(); if (endTime != null && endTime.isEqual(today)) { devicePlan.setFinishTime(LocalDateTime.now()); @@ -168,24 +187,31 @@ public class DevicePlanTaskMonitorService { LocalTime nowTime = LocalTime.now(); int dayOfMonth = today.getDayOfMonth(); LocalTime dayTime = LocalTime.parse(rules.getDayTime()); - if (!CollectionUtils.isEmpty(dayOfMonthList) && dayOfMonthList.contains(dayOfMonth) && nowTime.isAfter(dayTime)) { + if (!CollectionUtils.isEmpty(dayOfMonthList) && dayOfMonthList.contains(dayOfMonth) + && nowTime.isAfter(dayTime)) { DevicePlanTask devicePlanTask = new DevicePlanTask(); devicePlanTask.setPlanId(devicePlan.getId()); devicePlanTask.setDeviceId(devicePlan.getDeviceId()); devicePlanTask.setTaskStaus(DeviceTaskStaus.NEW); devicePlanTask.setRouteId(devicePlan.getRouteId()); + // 设置当前的路径规划信息 + WorkRecord workRecord = workRecordMapper.selectById(devicePlan.getRouteId()); + if (workRecord != null) { + devicePlanTask.setRouteType(workRecord.getRouteType()); + devicePlanTask.setWorkName(workRecord.getWorkName()); + } devicePlanTask.setCreateTime(LocalDateTime.now()); - devicePlanTask.setTaskType("");//待拓展任务类型 + devicePlanTask.setTaskType("");// 待拓展任务类型 devicePlanTask.setOrgId(devicePlan.getOrgId()); devicePlanTask.setSiteId(devicePlan.getSiteId()); devicePlanTaskMapper.insert(devicePlanTask); SiteMemory siteMemory = GlobalMemory.getSiteMemory(devicePlan.getOrgId(), devicePlan.getSiteId()); siteMemory.addDevicePlanPrepare(devicePlanTask); - //啥时候重置?每日0点? + // 啥时候重置?每日0点? devicePlan.setDayTriggerFlag(true); devicePlan.setUpdateTime(LocalDateTime.now()); devicePlan.setTaskStaus(DeviceTaskStaus.EXECUTING); - //todo 怎么判断任务全部排完了?排完了就删除 + // todo 怎么判断任务全部排完了?排完了就删除 LocalDate endTime = rules.getEndTime(); if (endTime != null && endTime.isEqual(today)) { devicePlan.setFinishTime(LocalDateTime.now()); @@ -197,13 +223,12 @@ public class DevicePlanTaskMonitorService { } public void oncePlan(DevicePlan devicePlan) { -// DeviceTaskPlanRule rules = devicePlan.getDeviceTaskPlanRules(); - + // DeviceTaskPlanRule rules = devicePlan.getDeviceTaskPlanRules(); } public void doLoop() throws InterruptedException { - //prepare 进执行execute的逻辑 + // prepare 进执行execute的逻辑 executor.scheduleWithFixedDelay(() -> { try { List list = GlobalMemory.getAllSiteMemory(); @@ -213,17 +238,16 @@ public class DevicePlanTaskMonitorService { if (!CollectionUtils.isEmpty(planMap.keySet())) { planMap.keySet().forEach(key -> { NettyDevice device = deviceSessionManager.getDevice(key); - //判断是否能进execute - if (device != null && device.getTask() == null && device.getOnlineStatus() != null && device.getOnlineStatus() == 1) { + // 判断是否能进execute + if (device != null && device.getTask() == null && device.getOnlineStatus() != null + && device.getOnlineStatus() == 1) { List tasks = planMap.get(key); if (!CollectionUtils.isEmpty(tasks)) { tasks.removeIf(Objects::isNull); - tasks.sort( + tasks.sort( Comparator.comparing( DevicePlanTask::getStartTime, - Comparator.nullsLast(Comparator.naturalOrder()) - ) - ); + Comparator.nullsLast(Comparator.naturalOrder()))); if (!CollectionUtils.isEmpty(tasks)) { DevicePlanTask devicePlanTask = tasks.get(0); String deviceId = devicePlanTask.getDeviceId(); @@ -248,7 +272,7 @@ public class DevicePlanTaskMonitorService { } public void doExecute() { - //prepare 进执行execute的逻辑 + // prepare 进执行execute的逻辑 executor.scheduleWithFixedDelay(() -> { try { List list = GlobalMemory.getAllSiteMemory(); diff --git a/maibu-netty-server/src/main/java/com/maibu/service/DeviceTaskService.java b/maibu-netty-server/src/main/java/com/maibu/service/DeviceTaskService.java index 83c1c1f..50649d9 100644 --- a/maibu-netty-server/src/main/java/com/maibu/service/DeviceTaskService.java +++ b/maibu-netty-server/src/main/java/com/maibu/service/DeviceTaskService.java @@ -6,7 +6,10 @@ import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.Random; +import java.util.Set; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; @@ -27,6 +30,7 @@ import com.maibu.core.business.RegionConsecutiveTaskRecord; import com.maibu.core.business.WorkRecord; import com.maibu.core.business.device.NettyDevice; import com.maibu.core.business.dto.ConsecutiveTaskDTO; +import com.maibu.core.business.dto.ConsecutiveTaskQueryDTO; import com.maibu.core.business.dto.DeviceTaskCommandDTO; import com.maibu.core.domain.entity.SysRegion; import com.maibu.core.domain.entity.SysSite; @@ -35,6 +39,7 @@ import com.maibu.core.domain.map.SiteMapInfo; import com.maibu.core.domain.model.LoginUser; import com.maibu.core.enums.DeviceTaskStaus; import com.maibu.core.enums.RouteType; +import com.maibu.core.host.VehicleMessage; import com.maibu.dto.DevicePlanStatisticsDTO; import com.maibu.dto.DevicePlanTaskStatisticsDTO; import com.maibu.dto.DeviceTaskQueryDTO; @@ -50,6 +55,7 @@ import com.maibu.mapper.WorkRecordMapper; import com.maibu.memory.DeviceSessionManager; import com.maibu.memory.GlobalMemory; import com.maibu.memory.SiteMemory; +import com.maibu.service.impl.HostComputerService; import com.maibu.utils.MessageUtils; import lombok.extern.slf4j.Slf4j; @@ -82,6 +88,9 @@ public class DeviceTaskService { @Autowired private RegionConsecutiveTaskRecordMapper regionConsecutiveTaskRecordMapper; + @Autowired + private HostComputerService hostComputerService; + private static final ObjectMapper objectMapper = new ObjectMapper(); public int insertOrUpdate(DevicePlan devicePlan, LoginUser loginUser) { @@ -422,6 +431,47 @@ public class DeviceTaskService { && !x.getCreateTime().isAfter(dto.getEndTime())) .collect(Collectors.toList()); } + + // 批量查询路线名称 + Set routeIds = list.stream() + .map(DevicePlanTask::getRouteId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + if (!routeIds.isEmpty()) { + List workRecords = workRecordMapper.selectList( + new LambdaQueryWrapper() + .select(WorkRecord::getId, WorkRecord::getWorkName) + .in(WorkRecord::getId, routeIds)); + + Map workNameMap = workRecords.stream() + .collect(Collectors.toMap( + WorkRecord::getId, + WorkRecord::getWorkName, + (a, b) -> a)); + + list.forEach(task -> task.setWorkName(workNameMap.get(task.getRouteId()))); + } + + // 查别名 + Set deviceIds = list.stream() + .map(DevicePlanTask::getDeviceId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + if (!deviceIds.isEmpty()) { + List devices = deviceMapper.selectList( + new LambdaQueryWrapper() + .select(Device::getSerialNumber, Device::getDeviceAlias) + .in(Device::getSerialNumber, deviceIds)); + Map deviceAliasMap = devices.stream() + .collect(Collectors.toMap( + Device::getSerialNumber, + Device::getDeviceAlias, + (a, b) -> a)); + + list.forEach(task -> task.setDeviceAlias(deviceAliasMap.get(task.getDeviceId()))); + } + return transferDeviceTaskData(list); } @@ -476,7 +526,10 @@ public class DeviceTaskService { return list; } - public Long createDeviceTask(DeviceTaskCommandDTO dto, String username) { + public Long createDeviceTask(DeviceTaskCommandDTO dto, String username) throws Exception { + SiteMemory siteMemory = GlobalMemory.getSiteMemory(dto.getOrgId(), dto.getSiteId()); + + sendAutoTaskMode(siteMemory, dto.getDeviceId()); // 检验权限 DevicePlanTask task = new DevicePlanTask(); task.setOrgId(dto.getOrgId()); task.setSiteId(dto.getSiteId()); @@ -490,6 +543,7 @@ public class DeviceTaskService { WorkRecord workRecord = workRecordMapper.selectById(dto.getRouteId()); if (workRecord != null) { task.setRouteType(workRecord.getRouteType()); + task.setWorkName(workRecord.getWorkName()); } devicePlanTaskMapper.saveOrUpdate(task); @@ -499,12 +553,12 @@ public class DeviceTaskService { dto.setOrgId(site.getOrgId()); } } - SiteMemory siteMemory = GlobalMemory.getSiteMemory(task.getOrgId(), task.getSiteId()); + siteMemory.addDevicePlanPrepare(task); return task.getId(); } - public Long createRegionDeviceTask(DeviceTaskCommandDTO dto, String username) { + public Long createRegionDeviceTask(DeviceTaskCommandDTO dto, String username) throws Exception { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysRegion::getId, dto.getRegionId()); SysRegion region = sysRegionMapper.selectOne(queryWrapper); @@ -517,6 +571,8 @@ public class DeviceTaskService { routeId = region.getDefaultRoute(); } + SiteMemory siteMemory = GlobalMemory.getSiteMemory(dto.getOrgId(), dto.getSiteId()); + sendAutoTaskMode(siteMemory, dto.getDeviceId()); // 检验权限 if (dto.getOrgId() == null) { SysSite site = sysSiteMapper.selectById(dto.getSiteId()); if (site != null) { @@ -536,11 +592,11 @@ public class DeviceTaskService { WorkRecord workRecord = workRecordMapper.selectById(region.getDefaultRoute()); if (workRecord != null) { task.setRouteType(workRecord.getRouteType()); + task.setWorkName(workRecord.getWorkName()); } devicePlanTaskMapper.saveOrUpdate(task); - SiteMemory siteMemory = GlobalMemory.getSiteMemory(task.getOrgId(), task.getSiteId()); siteMemory.addDevicePlanPrepare(task); return task.getId(); } @@ -558,6 +614,7 @@ public class DeviceTaskService { NettyDevice device = deviceSessionManager.getDevice(deviceId); if (device != null && device.getTask() != null) { if (dto.isForceCancel()) { + device.cancelingTask(); return device.canceledTask() != null; } else { return device.cancelingTask(); @@ -579,7 +636,7 @@ public class DeviceTaskService { } // todo 完善 - public boolean pauseTask(DeviceTaskCommandDTO dto, String username) { + public boolean pauseTask(DeviceTaskCommandDTO dto, String username) throws MqttException { if (!StringUtils.isEmpty(dto.getDeviceId())) { NettyDevice device = deviceSessionManager.getDevice(dto.getDeviceId()); if (device != null) { @@ -599,7 +656,7 @@ public class DeviceTaskService { } // todo 完善 - public boolean recoveryTask(DeviceTaskCommandDTO dto, String username) throws InterruptedException { + public boolean recoveryTask(DeviceTaskCommandDTO dto, String username) throws InterruptedException, MqttException { if (!StringUtils.isEmpty(dto.getDeviceId())) { NettyDevice device = deviceSessionManager.getDevice(dto.getDeviceId()); if (device != null) { @@ -618,7 +675,9 @@ public class DeviceTaskService { return false; } - public Long backToRestPoint(DeviceTaskCommandDTO dto, String username) { + public Long backToRestPoint(DeviceTaskCommandDTO dto, String username) throws Exception { + + // 先判断 mode 是不是 0 是0 切换成2 成功 失败返回 不是 0 判断是不是2 ,是2 执行 不是2 切换 等结果 if (dto.getOrgId() == null) { SysSite site = sysSiteMapper.selectById(dto.getSiteId()); @@ -628,12 +687,13 @@ public class DeviceTaskService { } SiteMemory siteMemory = GlobalMemory.getSiteMemory(dto.getOrgId(), dto.getSiteId()); - SiteMapInfo siteMapInfo; if (siteMemory == null) { throw new ServiceException(MessageUtils.message("任务下发出错!")); } - siteMapInfo = siteMemory.getSysSite().getSiteMapInfo(); + sendAutoTaskMode(siteMemory, dto.getDeviceId()); // 检验权限 + + SiteMapInfo siteMapInfo = siteMemory.getSysSite().getSiteMapInfo(); if (siteMapInfo == null || CollectionUtils.isEmpty(siteMapInfo.getRestPoints())) { throw new ServiceException(MessageUtils.message("当前机器未配置返回点!")); @@ -659,6 +719,7 @@ public class DeviceTaskService { task.setRouteId(workRecord.getId()); task.setRouteType(workRecord.getRouteType()); + task.setWorkName(workRecord.getWorkName()); task.setBackTaskPoint(switchRestPoint); task.setOrgId(dto.getOrgId()); @@ -674,7 +735,7 @@ public class DeviceTaskService { return task.getId(); } - public void createConsecutiveTasks(ConsecutiveTaskDTO dto, String username) { + public void createConsecutiveTasks(ConsecutiveTaskDTO dto, String username) throws Exception { if (dto.getOrgId() == null) { SysSite site = sysSiteMapper.selectById(dto.getSiteId()); @@ -686,6 +747,7 @@ public class DeviceTaskService { if (siteMemory == null) { throw new ServiceException(MessageUtils.message("任务下发出错!")); } + sendAutoTaskMode(siteMemory, dto.getDeviceId()); // 检验权限 siteMemory.saveRegionConsecutiveTask(dto.getDeviceId(), dto, username); } @@ -720,4 +782,38 @@ public class DeviceTaskService { } } + public List getRegionConsecutiveTaskRecords(ConsecutiveTaskQueryDTO dto) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(RegionConsecutiveTaskRecord::getSiteId, dto.getSiteId()); + if (!StringUtils.isEmpty(dto.getDeviceId())) { + queryWrapper.eq(RegionConsecutiveTaskRecord::getDeviceId, dto.getDeviceId()); + } + if (dto.getTaskStatus() != null) { + queryWrapper.eq(RegionConsecutiveTaskRecord::getTaskStatus, dto.getTaskStatus()); + } + if (dto.getStartTime() != null) { + queryWrapper.ge(RegionConsecutiveTaskRecord::getCreateTime, dto.getStartTime()); + } + if (dto.getEndTime() != null) { + queryWrapper.le(RegionConsecutiveTaskRecord::getCreateTime, dto.getEndTime()); + } + queryWrapper.orderByDesc(RegionConsecutiveTaskRecord::getCreateTime); + return regionConsecutiveTaskRecordMapper.selectList(queryWrapper); + } + + public void sendAutoTaskMode(SiteMemory siteMemory, String deviceId) throws Exception { + VehicleMessage vehicleMessage = siteMemory.getVehicleMessage(deviceId); + if (vehicleMessage == null || vehicleMessage.getControl_mode() == null) { + throw new ServiceException(MessageUtils.message("当前机器未连接!")); + } + Integer controlMode = vehicleMessage.getControl_mode(); + boolean isSend = controlMode != 2; + if (isSend) { + boolean result = hostComputerService.modeChage(deviceId, 2); + if (!result) { + throw new ServiceException(MessageUtils.message("当前设备切换模式失败!")); + } + } + } + } diff --git a/maibu-netty-server/src/main/java/com/maibu/service/DeviceThreadService.java b/maibu-netty-server/src/main/java/com/maibu/service/DeviceThreadService.java index 601e659..45b7f5a 100644 --- a/maibu-netty-server/src/main/java/com/maibu/service/DeviceThreadService.java +++ b/maibu-netty-server/src/main/java/com/maibu/service/DeviceThreadService.java @@ -2,7 +2,6 @@ package com.maibu.service; import java.io.IOException; import java.lang.reflect.Field; -import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Collection; @@ -14,16 +13,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import com.maibu.core.business.dto.DeviceMapPushDTO; -import com.maibu.core.business.dto.DeviceTaskCommandDTO; -import com.maibu.core.business.dto.WebMapMessageDTO; -import com.maibu.core.business.dto.WebOlineStatusMessageDTO; -import com.maibu.core.enums.*; -import com.maibu.utils.json.JsonUtils; -import com.maibu.utils.spring.SpringUtils; - import org.apache.commons.lang3.StringUtils; -import org.checkerframework.checker.units.qual.s; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.Resource; @@ -44,17 +34,27 @@ import com.maibu.core.business.ErrorIdentificationStandard; import com.maibu.core.business.RegionConsecutiveTaskRecord; import com.maibu.core.business.alarm_center.AlarmMessage; import com.maibu.core.business.device.NettyDevice; +import com.maibu.core.business.dto.DeviceMapPushDTO; +import com.maibu.core.business.dto.DeviceTaskCommandDTO; +import com.maibu.core.business.dto.WebMapMessageDTO; +import com.maibu.core.business.dto.WebOlineStatusMessageDTO; import com.maibu.core.business.inter.WebsocketMesDispather; -import com.maibu.core.domain.AjaxResult; import com.maibu.core.domain.rule.ActionConfig; import com.maibu.core.domain.rule.IotAutoRule; import com.maibu.core.domain.rule.TriggerConditions; +import com.maibu.core.enums.ActionType; +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.MesType; +import com.maibu.core.enums.RespondCode; +import com.maibu.core.enums.RuleType; import com.maibu.core.host.HeartBeatDTO; import com.maibu.core.host.LocationMessage; import com.maibu.core.host.VehicleMessage; import com.maibu.core.redis.RedisCache; import com.maibu.dto.DeviceErrorPushDTO; -import com.maibu.dto.DeviceTaskStatusMessageDTO; import com.maibu.mapper.DeviceRunStatisticsMapper; import com.maibu.mapper.ErrorIdentificationStandardMapper; import com.maibu.mapper.HostLocationMapper; @@ -64,6 +64,7 @@ import com.maibu.memory.GlobalMemory; import com.maibu.memory.SiteMemory; import com.maibu.mqtt.MqttTopic; import com.maibu.utils.CompareUtils; +import com.maibu.utils.json.JsonUtils; import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IoUtil; @@ -162,7 +163,7 @@ public class DeviceThreadService { }); } } catch (Exception e) { - log.error("执行监测错误线程错误:{}", e.getMessage()); + log.error("执行监测错误线程错误", e); } }, 0, 5, TimeUnit.SECONDS); } @@ -237,7 +238,7 @@ public class DeviceThreadService { }); } } catch (Exception e) { - log.error("推送错误信息失败 device:{}error:{}", deviceId, e.getMessage()); + log.error("推送错误信息失败 device:{}error", deviceId); } } @@ -287,8 +288,7 @@ public class DeviceThreadService { Object value = field.get(history); return (String) value; } catch (NoSuchFieldException | IllegalAccessException e) { - System.out.println("获取字段失败: " + e.getMessage()); - log.error("获取属性值失败:{}", e.getMessage()); + log.error("获取属性值失败", e); } return null; } @@ -318,63 +318,44 @@ public class DeviceThreadService { // 超时了判断离线 if (nettyDevice != null && nettyDevice.getOnlineStatus() != null && nettyDevice.getOnlineStatus() == 1) { - nettyDevice.status(DeviceStatus.offline.getType()); - // todo 推送设备离线 - String key = NettyCacheKey.latestPositonKey + deviceId; - // 保存一下最新一次的数据到 数据库 - LocationMessage locationMessage = redisCache.getCacheObject(key); - if (locationMessage != null) { - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(LocationMessage::getDeviceId, deviceId); - LocationMessage existing = hostLocationMapper.selectOne(queryWrapper); - if (existing != null) { - locationMessage.setId(existing.getId()); - } else { - locationMessage.setCreateTime(LocalDateTime.now()); - } - locationMessage.setUpdateTime(LocalDateTime.now()); - hostLocationMapper.saveOrUpdate(locationMessage); - } - Device device = deviceService.selectDeviceBySerialNumber(deviceId); - if (device != null) { - device.setOnlineStatus(DeviceStatus.offline.getType()); - device.setUpdateTime(LocalDateTime.now()); - deviceService.updateDeviceBySelf(device); - } Long orgId = nettyDevice.getDevice().getOrgId(); Long siteId = nettyDevice.getDevice().getSiteId(); - SiteMemory siteMemory = GlobalMemory.getSiteMemory(orgId, siteId); - if (siteMemory != null) { - DeviceRunStatistics deviceRunStatistics = siteMemory.getRunStatistics(deviceId); - if (deviceRunStatistics != null) { - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(DeviceRunStatistics::getDeviceId, deviceId); - DeviceRunStatistics existing = deviceRunStatisticsMapper - .selectOne(queryWrapper); - if (existing != null) { - deviceRunStatistics.setId(existing.getId()); - deviceRunStatistics.setDistance( - existing.getDistance() + deviceRunStatistics.getDistance()); - deviceRunStatistics.setWorkArea( - existing.getWorkArea() + deviceRunStatistics.getWorkArea()); - long time = System.currentTimeMillis() - nettyDevice.getLatestLoginTime(); - deviceRunStatistics.setTime(existing.getTime() + time / (1000 * 60)); // 累加时长,单位为分钟 - } else { - long time = System.currentTimeMillis() - nettyDevice.getLatestLoginTime(); - deviceRunStatistics.setTime(time / (1000 * 60)); // 累加时长,单位为分钟 - } - deviceRunStatisticsMapper.saveOrUpdate(deviceRunStatistics); + + try { + // 标记离线 + nettyDevice.status(DeviceStatus.offline.getType()); + updatelatestLocation(deviceId); + Device device = deviceService.selectDeviceBySerialNumber(deviceId); + if (device != null) { + device.setOnlineStatus(DeviceStatus.offline.getType()); + device.setUpdateTime(LocalDateTime.now()); + deviceService.updateDeviceBySelf(device); + } + + SiteMemory siteMemory = GlobalMemory.getSiteMemory(orgId, siteId); + if (siteMemory != null) { + updateTotalArea(nettyDevice, siteMemory, deviceId); + } + // TODO 统计总里程、总作业面积 + statusChangePush(deviceId, siteId, 0); + sessionManager.removeDevice(deviceId); + + } catch (Exception e) { + log.error("设备离线处理异常,deviceId={}", deviceId, e); + + } finally { + // 必须执行:清理实时缓存 + try { + SiteMemory siteMemory = GlobalMemory.getSiteMemory(orgId, siteId); + if (siteMemory != null) { + siteMemory.removeMapPushDTO(deviceId); + siteMemory.removeVehicleMessage(deviceId); + } + } catch (Exception e) { + log.error("设备离线缓存清理失败,deviceId={}", deviceId, e); } - // todo 删除实时消息推送里面的数据 - siteMemory.removeMapPushDTO(deviceId); - siteMemory.removeVehicleMessage(deviceId); } - - statusChangePush(deviceId, siteId, 0); - sessionManager.removeDevice(deviceId); - // TODO 统计总里程、总作业面积 - } } else { Device device = deviceService.selectDeviceBySerialNumber(deviceId); @@ -428,6 +409,50 @@ public class DeviceThreadService { }, 0, 500, TimeUnit.MILLISECONDS); } + public void updatelatestLocation(String deviceId) { + // todo 推送设备离线 + String key = NettyCacheKey.latestPositonKey + deviceId; + // 保存一下最新一次的数据到 数据库 + LocationMessage locationMessage = redisCache.getCacheObject(key); + if (locationMessage != null) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(LocationMessage::getDeviceId, deviceId); + LocationMessage existing = hostLocationMapper.selectOne(queryWrapper); + if (existing != null) { + locationMessage.setId(existing.getId()); + } else { + locationMessage.setCreateTime(LocalDateTime.now()); + } + locationMessage.setUpdateTime(LocalDateTime.now()); + hostLocationMapper.saveOrUpdate(locationMessage); + } + } + + public void updateTotalArea(NettyDevice nettyDevice, SiteMemory siteMemory, String deviceId) { + DeviceRunStatistics deviceRunStatistics = siteMemory.getRunStatistics(deviceId); + if (deviceRunStatistics != null) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(DeviceRunStatistics::getDeviceId, deviceId); + DeviceRunStatistics existing = deviceRunStatisticsMapper + .selectOne(queryWrapper); + if (existing != null) { + deviceRunStatistics.setId(existing.getId()); + deviceRunStatistics.setDistance( + existing.getDistance() + deviceRunStatistics.getDistance()); + deviceRunStatistics.setWorkArea( + existing.getWorkArea() + deviceRunStatistics.getWorkArea()); + long time = System.currentTimeMillis() + - nettyDevice.getLatestLoginTime(); + deviceRunStatistics.setTime(existing.getTime() + time / (1000 * 60)); // 累加时长,单位为分钟 + } else { + long time = System.currentTimeMillis() + - nettyDevice.getLatestLoginTime(); + deviceRunStatistics.setTime(time / (1000 * 60)); // 累加时长,单位为分钟 + } + deviceRunStatisticsMapper.saveOrUpdate(deviceRunStatistics); + } + } + public void statusChangePush(String deviceId, Long siteId, Integer status) { // todo 推送设备离线 WebOlineStatusMessageDTO dto = new WebOlineStatusMessageDTO(); @@ -474,7 +499,7 @@ public class DeviceThreadService { }); } } catch (Exception e) { - log.error("执行监测心跳线程错误:{}", e.getMessage()); + log.error("执行监测心跳线程错误", e); } }, 0, 100, TimeUnit.MILLISECONDS); } @@ -488,17 +513,24 @@ public class DeviceThreadService { List list = GlobalMemory.getAllSiteMemory(); if (!CollectionUtils.isEmpty(list)) { list.forEach(x -> { - List rules = x.getAllIotAutoRule(); - if (!CollectionUtils.isEmpty(rules)) { - rules.forEach(rule -> { - // 根据不同的 类型区分处理方式 - handleAutoRule(rule, x); + if (!CollectionUtils.isEmpty(x.iotAutoRuleMap)) { + x.iotAutoRuleMap.keySet().forEach(y -> { + List rules = x.iotAutoRuleMap.get(y); + if (!CollectionUtils.isEmpty(rules)) { + rules.forEach(rule -> { + // 根据不同的 类型区分处理方式 + if (rule != null && rule.getEnabled()) { + handleAutoRule(rule, x); + } + }); + } }); } + }); } } catch (Exception e) { - log.error("执行规则出发线程错误:{}", e.getMessage()); + log.error("执行规则出发线程错误", e); } }, 0, 500, TimeUnit.MILLISECONDS); } @@ -529,7 +561,12 @@ public class DeviceThreadService { dto.setOrgId(siteMemory.getSysSite().getOrgId()); dto.setSiteId(siteMemory.getSysSite().getId()); - deviceTaskService.backToRestPoint(dto, "system"); + try { + deviceTaskService.backToRestPoint(dto, "system"); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } } if (ActionType.AUTO_TASK.equals(actionType)) { if (siteMemory.hasTask(deviceId)) { @@ -541,7 +578,12 @@ public class DeviceThreadService { dto.setOrgId(siteMemory.getSysSite().getOrgId()); dto.setSiteId(siteMemory.getSysSite().getId()); dto.setRouteId(routeId); - deviceTaskService.createDeviceTask(dto, "system"); + try { + deviceTaskService.createDeviceTask(dto, "system"); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } } if (ActionType.ALARM.equals(actionType)) { @@ -568,10 +610,9 @@ public class DeviceThreadService { Field field = vehicleClass.getDeclaredField(fieldName); field.setAccessible(true); Object value = field.get(history); - return (String) value; + return String.valueOf(value); } catch (NoSuchFieldException | IllegalAccessException e) { - System.out.println("获取字段失败: " + e.getMessage()); - log.error("获取属性值失败:{}", e.getMessage()); + log.error("获取属性值失败:", e); } return null; } @@ -602,7 +643,13 @@ public class DeviceThreadService { dto.setOrgId(siteMemory.getSysSite().getOrgId()); dto.setSiteId(siteMemory.getSysSite().getId()); dto.setRouteId(routeId); - Long taskId = deviceTaskService.createDeviceTask(dto, "system"); + Long taskId = null; + try { + taskId = deviceTaskService.createDeviceTask(dto, "system"); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } if (taskId != null) { record.getTaskIds().add(taskId); record.setCurrentIndex(currentIndex + 1); @@ -616,7 +663,7 @@ public class DeviceThreadService { }); } } catch (Exception e) { - log.error("区域连续任务处理线程错误:{}", e.getMessage()); + log.error("区域连续任务处理线程错误", e); } }, 0, 500, TimeUnit.MILLISECONDS); } diff --git a/maibu-service/maibu-iot-service/src/main/java/com/maibu/controller/HostComputerController.java b/maibu-service/maibu-iot-service/src/main/java/com/maibu/controller/HostComputerController.java index 018c206..81f9859 100644 --- a/maibu-service/maibu-iot-service/src/main/java/com/maibu/controller/HostComputerController.java +++ b/maibu-service/maibu-iot-service/src/main/java/com/maibu/controller/HostComputerController.java @@ -11,6 +11,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -19,7 +20,7 @@ import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; -import com.maibu.core.business.DeviceRunParam; +import com.maibu.core.business.WorkRecord; import com.maibu.core.business.device.NettyDevice; import com.maibu.core.business.dto.WebRouteStatusMessageDTO; import com.maibu.core.business.inter.WebsocketMesDispather; @@ -32,6 +33,8 @@ import com.maibu.memory.DeviceSessionManager; import com.maibu.memory.GlobalMemory; import com.maibu.mqtt.MqttTopic; import com.maibu.service.impl.HostComputerService; +import com.maibu.service.impl.WorkRecordService; +import com.maibu.utils.MinioUtil; import com.maibu.utils.json.JsonUtils; import lombok.extern.slf4j.Slf4j; @@ -52,9 +55,18 @@ public class HostComputerController extends BaseController { @Autowired private DeviceSessionManager deviceSessionManager; + @Autowired + private WorkRecordService workRecordService; + @Value("${mqtt.client-id}") private String mqttClientId; + @Value("${minio.endpoint}") + private String minioUrl; + + @Autowired + private MinioUtil minioUtil; + /** * 上传 csv.gz 轨迹文件并解析为 LatAndLngEntity 列表(分批处理,每批 BATCH_SIZE 条) * @@ -69,7 +81,8 @@ public class HostComputerController extends BaseController { @PostMapping(value = "/upload-path-csv", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public R uploadCsv( @RequestPart("file") MultipartFile file, - @RequestParam("taskId") Long taskId) { + @RequestParam(value = "taskId") Long taskId, + @RequestParam(value = "pathId", required = false) Long pathId) { if (file == null || file.isEmpty()) { return R.fail("文件不能为空"); } @@ -136,8 +149,22 @@ public class HostComputerController extends BaseController { log.info("csv 解析完成: taskId={}, 共 {} 个轨迹点, 分 {} 批处理", taskId, allPath.size(), batchIndex); - GlobalMemory.taskPathMap.put(taskId, allPath); - sendMessage(taskId, allPath); + if (pathId == null) { + GlobalMemory.taskPathMap.put(taskId, allPath); + sendMessage(taskId, allPath); + } else { + // 上传到 MinIO + minioUtil.uploadFile(originalFilename, gzipInputStream, "application/gzip"); + // 获取访问 URL + String url = minioUtil.getPublicUrl(originalFilename); + + WorkRecord workRecord = workRecordService.selectById(pathId); + if (workRecord != null) { + workRecord.setFileUrl(url); + workRecordService.updateWorkRecord(workRecord); + } + } + // TODO 推送数据到前端 return R.ok(); @@ -261,7 +288,12 @@ public class HostComputerController extends BaseController { @GetMapping("/searchDevice") public AjaxResult searchDevice(@RequestParam String deviceId) throws Exception { hostComputerService.searchDevice(deviceId); - return success(); + return AjaxResult.success(); + } + + @GetMapping("/modeChage") + public AjaxResult modeChange(@RequestParam String deviceId, @RequestParam Integer controlMode) throws Exception { + return AjaxResult.success(hostComputerService.modeChage(deviceId, controlMode)); } } diff --git a/maibu-service/maibu-iot-service/src/main/java/com/maibu/controller/IotAutoRuleController.java b/maibu-service/maibu-iot-service/src/main/java/com/maibu/controller/IotAutoRuleController.java index 43aa962..fdaa2f9 100644 --- a/maibu-service/maibu-iot-service/src/main/java/com/maibu/controller/IotAutoRuleController.java +++ b/maibu-service/maibu-iot-service/src/main/java/com/maibu/controller/IotAutoRuleController.java @@ -12,7 +12,6 @@ import org.springframework.web.bind.annotation.RestController; import com.maibu.core.controller.BaseController; import com.maibu.core.domain.AjaxResult; -import com.maibu.core.domain.entity.SysMenu; import com.maibu.core.domain.rule.IotAutoRule; import com.maibu.core.domain.rule.IotAutoRuleRecord; import com.maibu.core.page.TableDataInfo; diff --git a/maibu-service/maibu-iot-service/src/main/java/com/maibu/controller/WorkRecordController.java b/maibu-service/maibu-iot-service/src/main/java/com/maibu/controller/WorkRecordController.java index a7c369e..f3de21d 100644 --- a/maibu-service/maibu-iot-service/src/main/java/com/maibu/controller/WorkRecordController.java +++ b/maibu-service/maibu-iot-service/src/main/java/com/maibu/controller/WorkRecordController.java @@ -1,20 +1,32 @@ package com.maibu.controller; +import java.io.InputStream; +import java.util.List; + +import org.apache.commons.collections4.CollectionUtils; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + import com.maibu.core.business.WorkRecord; +import com.maibu.core.business.dto.DeviceMapPushDTO; import com.maibu.core.controller.BaseController; import com.maibu.core.domain.AjaxResult; import com.maibu.core.domain.R; import com.maibu.core.enums.RouteType; import com.maibu.memory.GlobalMemory; +import com.maibu.memory.SiteMemory; import com.maibu.service.impl.WorkRecordService; import com.maibu.utils.MinioUtil; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; - -import java.io.InputStream; -import java.util.List; @RestController @RequestMapping("/iot/workRecord") @@ -49,7 +61,26 @@ public class WorkRecordController extends BaseController { if (workRecord1 != null) { return R.fail("该任务已存在"); } - + // 自定义模式的任务需要有在线 且空闲的车辆; + String deviceId = null; + if ("predefined_path".equals(workRecord.getMode())) { + Long siteId = workRecord.getSiteId(); + Long orgId = workRecord.getOrgId(); + SiteMemory siteMemory = GlobalMemory.getSiteMemory(orgId, siteId); + if (siteMemory != null) { + if (CollectionUtils.isEmpty(siteMemory.deviceMapPushMap.values())) { + return R.fail("当前模式的规划需有在线且空闲的机器!"); + } else { + DeviceMapPushDTO dto = siteMemory.deviceMapPushMap.values().stream() + .filter(x -> x.getT() == null) + .findFirst().orElse(null); + if (dto == null) { + return R.fail("当前模式的规划需有在线且空闲的机器!"); + } + deviceId = dto.getId(); + } + } + } try (InputStream inputStream = file.getInputStream()) { String fileName = "routePlanImage/rt" + workRecord.getWorkName() + System.currentTimeMillis() + ".png"; @@ -65,7 +96,7 @@ public class WorkRecordController extends BaseController { workRecord.setImgUrl(url); workRecord.setUserId(getUserId()); workRecord.setOrgId(getLoginUser().getOrgId()); - i = workRecordService.addWorkRecord(workRecord); + i = workRecordService.addWorkRecord(workRecord, deviceId); } catch (Exception e) { return R.fail("新增失败: " + e.getMessage()); } @@ -92,8 +123,25 @@ public class WorkRecordController extends BaseController { } @PostMapping("/update") - public R updateWorkRecord(@RequestBody WorkRecord workRecord) { - boolean b = workRecordService.updateWorkRecord(workRecord); + public R updateWorkRecord(@RequestBody WorkRecord workRecord) throws MqttException { + String deviceId = null; + Long siteId = workRecord.getSiteId(); + Long orgId = workRecord.getOrgId(); + SiteMemory siteMemory = GlobalMemory.getSiteMemory(orgId, siteId); + if (siteMemory != null) { + if (CollectionUtils.isEmpty(siteMemory.deviceMapPushMap.values())) { + return R.fail("当前模式的规划需有在线且空闲的机器!"); + } else { + DeviceMapPushDTO dto = siteMemory.deviceMapPushMap.values().stream() + .filter(x -> StringUtils.isEmpty(x.getT())) + .findFirst().orElse(null); + if (dto == null) { + return R.fail("当前模式的规划需有在线且空闲的机器!"); + } + deviceId = dto.getId(); + } + } + boolean b = workRecordService.updateWorkRecord(workRecord, deviceId); return !b ? R.fail("更新失败") : R.ok("更新成功"); } diff --git a/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IotAutoRuleService.java b/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IotAutoRuleService.java index 520b8b5..38e2a81 100644 --- a/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IotAutoRuleService.java +++ b/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IotAutoRuleService.java @@ -4,8 +4,10 @@ import java.util.List; 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.core.business.DeviceRunParam; import com.maibu.core.domain.map.SiteMapInfo; import com.maibu.core.domain.rule.IotAutoRule; import com.maibu.core.domain.rule.IotAutoRuleRecord; @@ -31,7 +33,7 @@ public class IotAutoRuleService { public boolean save(IotAutoRule iotAutoRule) { try { boolean b = iotAutoRuleMapper.saveOrUpdate(iotAutoRule); - SiteMemory siteMemory = GlobalMemory.getSiteMemory(iotAutoRule.getOrgId(), iotAutoRule.getId()); + SiteMemory siteMemory = GlobalMemory.getSiteMemory(iotAutoRule.getOrgId(), iotAutoRule.getSiteId()); if (siteMemory != null && siteMemory.sysSite != null) { siteMemory.saveIotAutoRule(iotAutoRule.getRuleType(), iotAutoRule); return b; @@ -48,10 +50,10 @@ public class IotAutoRuleService { return iotAutoRuleMapper.selectById(id); } - public IotAutoRule selectBySiteId(Long siteId) { + public List selectBySiteId(Long siteId) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(IotAutoRule::getSiteId, siteId); - return iotAutoRuleMapper.selectOne(queryWrapper); + return iotAutoRuleMapper.selectList(queryWrapper); } public List selectAllRecord(IotAutoRuleRecordQueryDTO queryDto) { @@ -77,6 +79,16 @@ public class IotAutoRuleService { } public int delete(List ids) { - return iotAutoRuleRecordMapper.deleteBatchIds(ids); + + List iotAutoRules = iotAutoRuleMapper.selectBatchIds(ids); + if (!CollectionUtils.isEmpty(iotAutoRules)) { + iotAutoRules.forEach(x -> { + SiteMemory siteMemory = GlobalMemory.getSiteMemory(x.getOrgId(), x.getSiteId()); + if (siteMemory != null) { + siteMemory.removeIotAutoRule(x); + } + }); + } + return iotAutoRuleMapper.deleteBatchIds(ids); } } diff --git a/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/DeviceRunParamService.java b/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/DeviceRunParamService.java index 0f92622..bf002d3 100644 --- a/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/DeviceRunParamService.java +++ b/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/DeviceRunParamService.java @@ -4,12 +4,9 @@ import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Arrays; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.Function; import java.util.stream.Collectors; @@ -20,11 +17,8 @@ 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; @@ -89,6 +83,8 @@ public class DeviceRunParamService { public boolean save(DeviceRunParam deviceRunParam) { + parseSn(deviceRunParam.getDeviceId(), deviceRunParam); + return deviceRunParamMapper.saveOrUpdate(deviceRunParam); } diff --git a/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/HostComputerService.java b/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/HostComputerService.java index 489ac40..2fa83d4 100644 --- a/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/HostComputerService.java +++ b/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/HostComputerService.java @@ -1,10 +1,19 @@ package com.maibu.service.impl; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.maibu.core.business.device.NettyDevice; +import com.maibu.core.host.VehicleMessage; +import com.maibu.exception.ServiceException; import com.maibu.memory.DeviceSessionManager; +import com.maibu.memory.GlobalMemory; +import com.maibu.memory.SiteMemory; import lombok.extern.slf4j.Slf4j; @@ -22,4 +31,80 @@ public class HostComputerService { } } + // public void modeChage(String deviceId, Integer controlMode) throws Exception + // { + // NettyDevice device = deviceSessionManager.getDevice(deviceId); + + // if (device == null || device.getOnlineStatus() == null || + // device.getOnlineStatus() == 0) { + // throw new ServiceException("设备不在线"); + // } + // Long orgId = device.getDevice().getOrgId(); + // Long siteId = device.getDevice().getSiteId(); + // SiteMemory siteMemory = GlobalMemory.getSiteMemory(orgId, siteId); + // if (siteMemory != null) { + // VehicleMessage vehicleMessage = siteMemory.getVehicleMessage(deviceId); + // if (vehicleMessage != null && vehicleMessage.getControl_mode() != null) { + // Integer nowMode = vehicleMessage.getControl_mode(); + // if (controlMode.equals(nowMode)) { + // throw new ServiceException("设备已处于该模式"); + // } + // } + // } + // device.modeChage(deviceId, controlMode); + // } + + public Boolean modeChage(String deviceId, Integer controlMode) throws Exception { + NettyDevice device = deviceSessionManager.getDevice(deviceId); + + if (device == null || device.getOnlineStatus() == null + || device.getOnlineStatus() == 0) { + throw new ServiceException("设备不在线"); + } + + Long orgId = device.getDevice().getOrgId(); + Long siteId = device.getDevice().getSiteId(); + + SiteMemory siteMemory = GlobalMemory.getSiteMemory(orgId, siteId); + + if (siteMemory != null) { + VehicleMessage vehicleMessage = siteMemory.getVehicleMessage(deviceId); + + if (vehicleMessage != null && vehicleMessage.getControl_mode() != null) { + Integer nowMode = vehicleMessage.getControl_mode(); + + if (controlMode.equals(nowMode)) { + throw new ServiceException("设备已处于该模式"); + } + } + } + + // 创建等待回传的 Future + CompletableFuture future = new CompletableFuture<>(); + + // 一个设备同时只允许一个模式切换请求 + CompletableFuture oldFuture = GlobalMemory.modeFutureMap.putIfAbsent(deviceId, future); + + if (oldFuture != null) { + throw new ServiceException("设备正在执行模式切换,请稍后再试"); + } + + try { + // 下发指令 + device.modeChage(deviceId, controlMode); + + // 等待设备回传,最多等待 5 秒 + Boolean result = future.get(5, TimeUnit.SECONDS); + + return result; + + } catch (Exception e) { + throw new ServiceException("设备模式切换失败"); + + } finally { + // 无论成功、失败还是超时,都清理 Future + GlobalMemory.modeFutureMap.remove(deviceId, future); + } + } + } diff --git a/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/OrderModelService.java b/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/OrderModelService.java index a04be9b..2d50b73 100644 --- a/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/OrderModelService.java +++ b/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/OrderModelService.java @@ -73,7 +73,7 @@ public class OrderModelService { return i; } - public String createTask(String deviceId, Long siteId, Long orgId, Long modelId) { + public String createTask(String deviceId, Long siteId, Long orgId, Long modelId) throws Exception { SiteMemory siteMemory = GlobalMemory.getSiteMemory(orgId, siteId); OrderModel orderModel = siteMemory.getOrderModel(modelId); OrderType orderType = orderModel.getOrderType(); diff --git a/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/WorkOrderServiceImpl.java b/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/WorkOrderServiceImpl.java index 949beaa..b652144 100644 --- a/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/WorkOrderServiceImpl.java +++ b/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/WorkOrderServiceImpl.java @@ -235,9 +235,9 @@ public class WorkOrderServiceImpl extends ServiceImpl selectBySiteId(Long siteId) { diff --git a/maibu-service/maibu-system-service/src/main/java/com/maibu/service/SiteMapInfoService.java b/maibu-service/maibu-system-service/src/main/java/com/maibu/service/SiteMapInfoService.java index 12b3c47..fe5a276 100644 --- a/maibu-service/maibu-system-service/src/main/java/com/maibu/service/SiteMapInfoService.java +++ b/maibu-service/maibu-system-service/src/main/java/com/maibu/service/SiteMapInfoService.java @@ -19,7 +19,7 @@ public class SiteMapInfoService { public boolean save(SiteMapInfo siteMapInfo) { try { boolean b = siteMapInfoMapper.saveOrUpdate(siteMapInfo); - SiteMemory siteMemory = GlobalMemory.getSiteMemory(siteMapInfo.getOrgId(), siteMapInfo.getId()); + SiteMemory siteMemory = GlobalMemory.getSiteMemory(siteMapInfo.getOrgId(), siteMapInfo.getSiteId()); if (siteMemory != null && siteMemory.sysSite != null) { siteMemory.sysSite.setSiteMapInfo(siteMapInfo); }