This commit is contained in:
2026-11-10 16:40:15 +08:00
parent 99c7a3ab87
commit c3a2aee8fb
36 changed files with 932 additions and 262 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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<MutiPointEntity> 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<LatAndLngEntity> 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<MutiPointEntity> boundary_points = new ArrayList<>();
List<LatAndLngEntity> 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<String, Object> 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));
}
}

View File

@@ -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;
}

View File

@@ -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 有无告警

View File

@@ -15,7 +15,7 @@ public class MowerCommandDispather {
@Resource
private ApplicationContext applicationContext;
public Long dispathDeviceTask(DeviceTaskCommandDTO dto) {
public Long dispathDeviceTask(DeviceTaskCommandDTO dto) throws Exception {
Map<String, MowerCommandInter> beanMap = applicationContext.getBeansOfType(MowerCommandInter.class);
// 遍历
Long id = null;

View File

@@ -4,6 +4,6 @@ import com.maibu.core.business.dto.DeviceTaskCommandDTO;
public interface MowerCommandInter {
Long createDeviceTask(DeviceTaskCommandDTO dto);
Long createDeviceTask(DeviceTaskCommandDTO dto) throws Exception;
}

View File

@@ -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<String, Object> data; // map key control_mode 2=自动任务,3=远程遥控
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -9,7 +9,7 @@ import lombok.Data;
@Data
public class ObstacleDetectionData {
private MesType mesType;
private MesType type;
/**
* 融合状态

View File

@@ -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<MutiPointEntity> 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; // 是直接执行还是创建自定义任务
}

View File

@@ -17,9 +17,10 @@ import com.maibu.mybatis.mapper.BaseMapperX;
@Repository
public interface DevicePlanTaskMapper extends BaseMapperX<DevicePlanTask> {
@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<DevicePlanTask> selectTaskHistoryWithDeviceAlias(
@Param(Constants.WRAPPER) Wrapper<DevicePlanTask> wrapper);

View File

@@ -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<String, CompletableFuture<Boolean>> 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);
}

View File

@@ -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<String, DeviceRunStatistics> deviceRunStatisticsMap = new ConcurrentHashMap<>();
public static ConcurrentHashMap<String, DeviceMapPushDTO> deviceMapPushMap = new ConcurrentHashMap<>();
public static final ConcurrentHashMap<String, DeviceMapPushDTO> deviceMapPushMap = new ConcurrentHashMap<>();
public static ConcurrentHashMap<String, VehicleMessage> vehicleMessageMap = new ConcurrentHashMap<>();
public static ConcurrentHashMap<RuleType, IotAutoRule> iotAutoRuleMap = new ConcurrentHashMap<>();
public static final ConcurrentHashMap<RuleType, List<IotAutoRule>> iotAutoRuleMap = new ConcurrentHashMap<>();
public static final ConcurrentHashMap<String, RegionConsecutiveTaskRecord> regionConsecutiveTaskMap = new ConcurrentHashMap<>();
@@ -109,20 +106,21 @@ public class SiteMemory {
return record;
}
public List<IotAutoRule> getAllIotAutoRule() {
return new ArrayList<>(iotAutoRuleMap.values());
}
public IotAutoRule getIotAutoRule(RuleType ruleType) {
public List<IotAutoRule> getIotAutoRule(RuleType ruleType) {
return iotAutoRuleMap.get(ruleType);
}
public void saveIotAutoRule(RuleType ruleType, IotAutoRule iotAutoRule) {
iotAutoRuleMap.put(ruleType, iotAutoRule);
List<IotAutoRule> 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<IotAutoRule> list = iotAutoRuleMap.get(iotAutoRule.getRuleType());
list.removeIf(i -> iotAutoRule.getId().equals(i.getId()));
}
public List<VehicleMessage> 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);
}

View File

@@ -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<NettyDevice> 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<Boolean> 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<Boolean> 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<Boolean> 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;
}

View File

@@ -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
*

View File

@@ -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<RegionConsecutiveTaskRecord> list = deviceTaskService.getRegionConsecutiveTaskRecords(dto);
return getDataTable(list);
}
}

View File

@@ -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");
}

View File

@@ -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<IotAutoRule> 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<SiteMapInfo> 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);
}

View File

@@ -117,7 +117,7 @@ public class CommandForwardHandler extends SimpleChannelInboundHandler<String> {
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());

View File

@@ -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<SiteMemory> 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<DevicePlanTask> 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<SiteMemory> list = GlobalMemory.getAllSiteMemory();

View File

@@ -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<Long> routeIds = list.stream()
.map(DevicePlanTask::getRouteId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
if (!routeIds.isEmpty()) {
List<WorkRecord> workRecords = workRecordMapper.selectList(
new LambdaQueryWrapper<WorkRecord>()
.select(WorkRecord::getId, WorkRecord::getWorkName)
.in(WorkRecord::getId, routeIds));
Map<Long, String> workNameMap = workRecords.stream()
.collect(Collectors.toMap(
WorkRecord::getId,
WorkRecord::getWorkName,
(a, b) -> a));
list.forEach(task -> task.setWorkName(workNameMap.get(task.getRouteId())));
}
// 查别名
Set<String> deviceIds = list.stream()
.map(DevicePlanTask::getDeviceId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
if (!deviceIds.isEmpty()) {
List<Device> devices = deviceMapper.selectList(
new LambdaQueryWrapper<Device>()
.select(Device::getSerialNumber, Device::getDeviceAlias)
.in(Device::getSerialNumber, deviceIds));
Map<String, String> 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<SysRegion> 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<RegionConsecutiveTaskRecord> getRegionConsecutiveTaskRecords(ConsecutiveTaskQueryDTO dto) {
LambdaQueryWrapper<RegionConsecutiveTaskRecord> 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("当前设备切换模式失败!"));
}
}
}
}

View File

@@ -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<LocationMessage> 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<DeviceRunStatistics> 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<LocationMessage> 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<DeviceRunStatistics> 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<SiteMemory> list = GlobalMemory.getAllSiteMemory();
if (!CollectionUtils.isEmpty(list)) {
list.forEach(x -> {
List<IotAutoRule> rules = x.getAllIotAutoRule();
if (!CollectionUtils.isEmpty(rules)) {
rules.forEach(rule -> {
// 根据不同的 类型区分处理方式
handleAutoRule(rule, x);
if (!CollectionUtils.isEmpty(x.iotAutoRuleMap)) {
x.iotAutoRuleMap.keySet().forEach(y -> {
List<IotAutoRule> 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);
}

View File

@@ -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));
}
}

View File

@@ -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;

View File

@@ -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("更新成功");
}

View File

@@ -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<IotAutoRule> selectBySiteId(Long siteId) {
LambdaQueryWrapper<IotAutoRule> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(IotAutoRule::getSiteId, siteId);
return iotAutoRuleMapper.selectOne(queryWrapper);
return iotAutoRuleMapper.selectList(queryWrapper);
}
public List<IotAutoRuleRecord> selectAllRecord(IotAutoRuleRecordQueryDTO queryDto) {
@@ -77,6 +79,16 @@ public class IotAutoRuleService {
}
public int delete(List<Long> ids) {
return iotAutoRuleRecordMapper.deleteBatchIds(ids);
List<IotAutoRule> 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);
}
}

View File

@@ -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);
}

View File

@@ -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<Boolean> future = new CompletableFuture<>();
// 一个设备同时只允许一个模式切换请求
CompletableFuture<Boolean> 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);
}
}
}

View File

@@ -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();

View File

@@ -235,9 +235,9 @@ public class WorkOrderServiceImpl extends ServiceImpl<IotWorkOrderMapper, IOTWor
throw new ServiceException("工单不存在");
Long userId = SecurityUtils.getUserId();
// if (!SecurityUtils.isAdmin(userId)) {
// validateWorkOrderAccess(workOrder);
// }
// if (!SecurityUtils.isAdmin(userId)) {
// validateWorkOrderAccess(workOrder);
// }
return workOrder;
}
@@ -297,13 +297,13 @@ public class WorkOrderServiceImpl extends ServiceImpl<IotWorkOrderMapper, IOTWor
throw new ServiceException("工单ID不能为空");
}
// Long userId = SecurityUtils.getUserId();
// if (!SecurityUtils.isAdmin(userId)) {
// IOTWorkOrder existWorkOrder = baseMapper.selectById(workOrder.getId());
// if (existWorkOrder != null) {
// validateWorkOrderAccess(existWorkOrder);
// }
// }
// Long userId = SecurityUtils.getUserId();
// if (!SecurityUtils.isAdmin(userId)) {
// IOTWorkOrder existWorkOrder = baseMapper.selectById(workOrder.getId());
// if (existWorkOrder != null) {
// validateWorkOrderAccess(existWorkOrder);
// }
// }
IOTWorkOrder updateEntity = new IOTWorkOrder();
updateEntity.setId(workOrder.getId());
@@ -340,10 +340,10 @@ public class WorkOrderServiceImpl extends ServiceImpl<IotWorkOrderMapper, IOTWor
if (workOrder == null)
throw new ServiceException("工单不存在");
// Long userId = SecurityUtils.getUserId();
// if (!SecurityUtils.isAdmin(userId)) {
// validateWorkOrderAccess(workOrder);
// }
// Long userId = SecurityUtils.getUserId();
// if (!SecurityUtils.isAdmin(userId)) {
// validateWorkOrderAccess(workOrder);
// }
IOTWorkOrder updateWorkOrder = new IOTWorkOrder();
updateWorkOrder.setId(dispatchDTO.getOrderId());
@@ -381,11 +381,18 @@ public class WorkOrderServiceImpl extends ServiceImpl<IotWorkOrderMapper, IOTWor
workOrder.setAssigneeName(userName);
// todo 派发自定义的路径规划任务
Long modelId = workOrder.getModelId();
if (modelId == null) return;
if (modelId == null)
return;
Long siteId = workOrder.getSiteId();
Long orgId = workOrder.getOrgId();
String id = orderModelService.createTask(dto.getDeviceId(), siteId, orgId, modelId);
workOrder.setTaskId(id);
String id;
try {
id = orderModelService.createTask(dto.getDeviceId(), siteId, orgId, modelId);
workOrder.setTaskId(id);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
return baseMapper.saveOrUpdateBatch(workOrders);
}
@@ -397,10 +404,10 @@ public class WorkOrderServiceImpl extends ServiceImpl<IotWorkOrderMapper, IOTWor
if (existWorkOrder == null)
throw new ServiceException("工单不存在");
// Long userId = SecurityUtils.getUserId();
// if (!SecurityUtils.isAdmin(userId)) {
// validateWorkOrderAccess(existWorkOrder);
// }
// Long userId = SecurityUtils.getUserId();
// if (!SecurityUtils.isAdmin(userId)) {
// validateWorkOrderAccess(existWorkOrder);
// }
IOTWorkOrder updateWorkOrder = new IOTWorkOrder();
updateWorkOrder.setId(workOrder.getId());
@@ -416,7 +423,7 @@ public class WorkOrderServiceImpl extends ServiceImpl<IotWorkOrderMapper, IOTWor
if (existWorkOrder.getAlarmId() != null) {
Alarm alarm = alarmMapper.selectById(existWorkOrder.getAlarmId());
if (alarm != null) {
// alarm.setHandleResult(AlarmHandleResult.Handled);
// alarm.setHandleResult(AlarmHandleResult.Handled);
alarm.setHandleStatus(2);
alarm.setUpdateTime(LocalDateTime.now());
alarm.setUpdateBy("system");
@@ -460,15 +467,15 @@ public class WorkOrderServiceImpl extends ServiceImpl<IotWorkOrderMapper, IOTWor
@Override
public int deleteWorkOrderByIds(Long[] ids) {
// Long userId = SecurityUtils.getUserId();
// if (!SecurityUtils.isAdmin(userId)) {
// for (Long id : ids) {
// IOTWorkOrder workOrder = baseMapper.selectById(id);
// if (workOrder != null) {
// validateWorkOrderAccess(workOrder);
// }
// }
// }
// Long userId = SecurityUtils.getUserId();
// if (!SecurityUtils.isAdmin(userId)) {
// for (Long id : ids) {
// IOTWorkOrder workOrder = baseMapper.selectById(id);
// if (workOrder != null) {
// validateWorkOrderAccess(workOrder);
// }
// }
// }
return iotWorkOrderMapper.deleteBatchIds(Arrays.asList(ids));
}

View File

@@ -3,11 +3,16 @@ package com.maibu.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.maibu.core.business.DevicePlanTask;
import com.maibu.core.business.WorkRecord;
import com.maibu.core.business.device.NettyDevice;
import com.maibu.mapper.DevicePlanTaskMapper;
import com.maibu.mapper.WorkRecordMapper;
import com.maibu.memory.DeviceSessionManager;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.Comparator;
@@ -22,9 +27,19 @@ public class WorkRecordService {
@Autowired
private DevicePlanTaskMapper devicePlanTaskMapper;
public int addWorkRecord(WorkRecord workRecord) throws Exception {
@Autowired
private DeviceSessionManager deviceSessionManager;
public int addWorkRecord(WorkRecord workRecord, String deviceId) throws Exception {
int i = workRecordMapper.insert(workRecord);
// int i = workRecordMapper.insertWorkRecord(workRecord);
if (StringUtils.hasText(deviceId)) {
NettyDevice device = deviceSessionManager.getDevice(deviceId);
if (device != null) {
device.sendPredefined(workRecord);
}
}
return i;
}
@@ -53,8 +68,19 @@ public class WorkRecordService {
return workRecordMapper.delete(queryWrapper);
}
public boolean updateWorkRecord(WorkRecord workRecord, String deviceId) throws MqttException {
boolean i = workRecordMapper.saveOrUpdate(workRecord);
NettyDevice device = deviceSessionManager.getDevice(deviceId);
if (device != null) {
device.sendPredefined(workRecord);
}
return i;
}
public boolean updateWorkRecord(WorkRecord workRecord) {
return workRecordMapper.saveOrUpdate(workRecord);
boolean i = workRecordMapper.saveOrUpdate(workRecord);
return i;
}
public List<WorkRecord> selectBySiteId(Long siteId) {

View File

@@ -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);
}