#update 设备在线升级的功能
This commit is contained in:
@@ -9,6 +9,7 @@ import com.maibu.core.enums.DeviceTaskStaus;
|
||||
import com.maibu.core.host.HostWorkRecordCreateDTO;
|
||||
import com.maibu.core.host.MutiPointEntity;
|
||||
import com.maibu.core.host.PointEntity;
|
||||
import com.maibu.core.ota.DeviceOtaFirmware;
|
||||
import com.maibu.mapper.DevicePlanTaskMapper;
|
||||
import com.maibu.mapper.WorkRecordMapper;
|
||||
import com.maibu.memory.GlobalMemory;
|
||||
@@ -26,10 +27,7 @@ import org.eclipse.paho.client.mqttv3.MqttException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -182,7 +180,7 @@ public class NettyDevice extends Connector {
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized boolean failedTask() {
|
||||
public synchronized boolean failedTask() {
|
||||
if (task != null) {
|
||||
task.setTaskStaus(DeviceTaskStaus.FAILED);
|
||||
task.setUpdateTime(LocalDateTime.now());
|
||||
@@ -216,7 +214,7 @@ public class NettyDevice extends Connector {
|
||||
|
||||
// 配置左右轮增益系数
|
||||
public void sendWheelGainCoefficientsCommand(double leftForwardGain, double leftBackwardGain,
|
||||
double rightForwardGain, double rightBackwardGain) {
|
||||
double rightForwardGain, double rightBackwardGain) {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(39);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
@@ -403,4 +401,21 @@ public class NettyDevice extends Connector {
|
||||
}
|
||||
}
|
||||
|
||||
public void upgrade(String deviceId, DeviceOtaFirmware firmware) throws MqttException {
|
||||
Map<String, Object> payload = new HashMap<>();
|
||||
payload.put("action", "ota_upgrade");
|
||||
payload.put("version", firmware.getVersion());
|
||||
payload.put("file_url", firmware.getFileUrl());
|
||||
payload.put("firmware_id", firmware.getId());
|
||||
payload.put("sn", deviceId);
|
||||
payload.put("timestamp", System.currentTimeMillis());
|
||||
payload.put("message_id", firmware.getId() + "_" + System.currentTimeMillis());
|
||||
|
||||
GlobalMemory.customMqttDeviceMonitor.publish(
|
||||
mqttClientId,
|
||||
String.format(MqttTopic.MOWER_OTA_UPGRADE_SET, deviceId),
|
||||
JsonUtils.toJsonString(payload)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ public class DeviceOtaOperateRecord extends BaseDO {
|
||||
private OtaOperate operateType; //操作类型
|
||||
|
||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||
private List<String> deviceIds; //文件名
|
||||
private List<String> deviceModels; //文件名
|
||||
|
||||
private String fileName; //名称
|
||||
|
||||
|
||||
@@ -111,7 +111,8 @@ public class GlobalMemory {
|
||||
MqttTopic.MOWER_WILDCARD_TASK_STATUS,//任务状态
|
||||
MqttTopic.MOWER_WILDCARD_ERROR,//错误
|
||||
MqttTopic.MOWER_WILDCARD_REALTIME, //状态消息
|
||||
MqttTopic.MOWER_WILDCARD_TASK_TARGET_REPLY //路径规划下发回复
|
||||
MqttTopic.MOWER_WILDCARD_TASK_TARGET_REPLY, //路径规划下发回复
|
||||
MqttTopic.MOWER_OTA_RESULT_POST
|
||||
));
|
||||
deviceConfig.setHandlerType(Constant.mqttHandleKey); //唯一的 用于处理上位机mqtt的
|
||||
customMqttDeviceMonitor.registerDevice(deviceConfig);
|
||||
|
||||
@@ -114,6 +114,9 @@ public class HostMessageHandler implements CustomMqttMessageHandler {
|
||||
case "route_reply":
|
||||
handleRouteReply(deviceId, topic, payload);
|
||||
break;
|
||||
case "upgrade_reply":
|
||||
handleUpgradeReply(deviceId, topic, payload);
|
||||
break;
|
||||
default:
|
||||
log.debug("割草机消息: deviceId={}, topic={}, action={}", deviceId, topic, action);
|
||||
break;
|
||||
@@ -162,7 +165,7 @@ public class HostMessageHandler implements CustomMqttMessageHandler {
|
||||
}
|
||||
|
||||
public WebStatusMessageDTO createWebDeviceLocationMessage(HostLocationRelTimeDTO locationRelTimeDTO,
|
||||
String deviceId) {
|
||||
String deviceId) {
|
||||
WebStatusMessageDTO webStatusMessageDTO = new WebStatusMessageDTO();
|
||||
List<DeviceStatusDetail> transferData = new ArrayList<>();
|
||||
|
||||
@@ -447,6 +450,23 @@ public class HostMessageHandler implements CustomMqttMessageHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理控制指令响应
|
||||
*/
|
||||
private void handleUpgradeReply(String deviceId, String topic, String payload) {
|
||||
// log.debug("【路径任务下发响应】deviceId={}, payload={}", deviceId, payload);
|
||||
//
|
||||
// // todo 更新任务接收状态
|
||||
// HostNavigationReplyDTO replyDTO = JsonUtils.parseObject(payload, HostNavigationReplyDTO.class);
|
||||
// NettyDevice device = deviceSessionManager.getDevice(deviceId);
|
||||
// if (replyDTO.isAccepted()) {
|
||||
// if (device != null && device.getTask() != null
|
||||
// && String.valueOf(device.getTask().getId()).equals(replyDTO.getTask_id())) {
|
||||
// device.getTask().setTaskAccepted(true);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Topic 中解析 deviceId
|
||||
* Topic 格式:mower/{deviceId}/...
|
||||
@@ -497,6 +517,9 @@ public class HostMessageHandler implements CustomMqttMessageHandler {
|
||||
return "control_reply";
|
||||
if (remaining.equals("task/target/reply"))
|
||||
return "route_reply";
|
||||
if (remaining.equals("/ota/result/post"))
|
||||
return "upgrade_reply";
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -64,15 +64,12 @@ public class MqttTopic {
|
||||
public static final String MOWER_ERROR_POST = "mower/%s/event/error/post";
|
||||
|
||||
// ==================== OTA 固件升级 ====================
|
||||
// /** 下发 OTA 升级指令(平台->设备) */
|
||||
// public static final String MOWER_OTA_UPGRADE_SET =
|
||||
// "mower/%s/ota/upgrade/set";
|
||||
// /** OTA 升级进度上报(设备->平台) */
|
||||
// public static final String MOWER_OTA_PROGRESS_POST =
|
||||
// "mower/%s/ota/progress/post";
|
||||
// /** OTA 升级结果上报(设备->平台) */
|
||||
// public static final String MOWER_OTA_RESULT_POST =
|
||||
// "mower/%s/ota/result/post";
|
||||
/** 下发 OTA 升级指令(平台->设备) */
|
||||
public static final String MOWER_OTA_UPGRADE_SET = "mower/%s/ota/upgrade/set";
|
||||
/** OTA 升级进度上报(设备->平台) */
|
||||
public static final String MOWER_OTA_PROGRESS_POST = "mower/+/ota/progress/post";
|
||||
/** OTA 升级结果上报(设备->平台) */
|
||||
public static final String MOWER_OTA_RESULT_POST = "mower/+/ota/result/post";
|
||||
|
||||
// ==================== 设备信息 ====================
|
||||
// /** 查询设备信息(平台->设备) */
|
||||
|
||||
@@ -86,7 +86,7 @@ public class DeviceOtaController extends BaseController {
|
||||
@ApiOperation("更新固件")
|
||||
@PostMapping("/firmware/upgrade")
|
||||
public AjaxResult upgrade(@RequestBody DeviceOtaOperateDTO dto) {
|
||||
return AjaxResult.success(service.upgrade(dto));
|
||||
return service.upgrade(dto);
|
||||
}
|
||||
|
||||
@ApiOperation("批量删除固件")
|
||||
@@ -115,6 +115,11 @@ public class DeviceOtaController extends BaseController {
|
||||
public AjaxResult selectOperateRecordById(@RequestParam Long id) {
|
||||
return AjaxResult.success(service.selectOperateRecordById(id));
|
||||
}
|
||||
|
||||
@GetMapping("/record/selectListByDeviceId")
|
||||
public AjaxResult selectListByDeviceId(@RequestParam String deviceId) {
|
||||
return AjaxResult.success(service.selectListByDeviceId(deviceId));
|
||||
}
|
||||
|
||||
@ApiOperation("批量删除操作记录")
|
||||
@PostMapping("/record/delete")
|
||||
|
||||
@@ -1,19 +1,27 @@
|
||||
package com.maibu.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.maibu.core.business.device.NettyDevice;
|
||||
import com.maibu.core.domain.AjaxResult;
|
||||
import com.maibu.core.enums.OtaOperate;
|
||||
import com.maibu.core.ota.DeviceOtaFirmware;
|
||||
import com.maibu.core.ota.DeviceOtaOperateRecord;
|
||||
import com.maibu.dto.BaseQueryDTO;
|
||||
import com.maibu.dto.DeviceOtaOperateDTO;
|
||||
import com.maibu.mapper.DeviceOtaFirmwareMapper;
|
||||
import com.maibu.mapper.DeviceOtaOperateRecordMapper;
|
||||
import com.maibu.core.ota.DeviceOtaFirmware;
|
||||
import com.maibu.core.ota.DeviceOtaOperateRecord;
|
||||
import com.maibu.memory.DeviceSessionManager;
|
||||
import com.maibu.utils.MinioUtil;
|
||||
import com.maibu.utils.SecurityUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@@ -29,6 +37,11 @@ public class DeviceOtaService {
|
||||
@Autowired
|
||||
private MinioUtil minioUtil;
|
||||
|
||||
@Autowired
|
||||
private DeviceSessionManager deviceSessionManager;
|
||||
|
||||
private static final int BATCH_SIZE = 5;
|
||||
|
||||
public boolean saveFirmware(DeviceOtaFirmware firmware) {
|
||||
return deviceOtaFirmwareMapper.saveOrUpdate(firmware);
|
||||
}
|
||||
@@ -44,8 +57,63 @@ public class DeviceOtaService {
|
||||
}
|
||||
|
||||
|
||||
public void upgrade(DeviceOtaOperateDTO dto) {
|
||||
public AjaxResult upgrade(DeviceOtaOperateDTO dto) {
|
||||
if (dto.getFirmwareId() == null) {
|
||||
return AjaxResult.error("固件ID不能为空");
|
||||
}
|
||||
if (CollectionUtils.isEmpty(dto.getDeviceIds())) {
|
||||
return AjaxResult.error("请选择要升级的设备");
|
||||
}
|
||||
|
||||
DeviceOtaFirmware firmware = deviceOtaFirmwareMapper.selectById(dto.getFirmwareId());
|
||||
if (firmware == null) {
|
||||
return AjaxResult.error("固件不存在");
|
||||
}
|
||||
|
||||
DeviceOtaOperateRecord record = new DeviceOtaOperateRecord();
|
||||
record.setOperateType(OtaOperate.upgrade);
|
||||
record.setFirmwareId(firmware.getId());
|
||||
record.setFileName(firmware.getFileName());
|
||||
record.setDeviceType(firmware.getDeviceType());
|
||||
record.setDeviceModels(new ArrayList<>());
|
||||
record.setCreateTime(LocalDateTime.now());
|
||||
record.setCreateBy(SecurityUtils.getUsername());
|
||||
|
||||
int totalDevices = dto.getDeviceIds().size();
|
||||
int successCount = 0;
|
||||
int offlineCount = 0;
|
||||
|
||||
for (int i = 0; i < totalDevices; i += BATCH_SIZE) {
|
||||
List<String> batch = dto.getDeviceIds().subList(i, Math.min(i + BATCH_SIZE, totalDevices));
|
||||
log.info("OTA 灰度升级 第 {} 批, 本批设备数: {}", (i / BATCH_SIZE) + 1, batch.size());
|
||||
|
||||
for (String deviceId : batch) {
|
||||
NettyDevice device = deviceSessionManager.getDevice(deviceId);
|
||||
if (device == null || device.getOnlineStatus() == 0) {
|
||||
log.warn("设备 {} 离线,跳过升级", deviceId);
|
||||
offlineCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
device.upgrade(deviceId, firmware);
|
||||
record.getDeviceModels().add(deviceId);
|
||||
successCount++;
|
||||
log.info("OTA 升级指令已下发: deviceId={}, version={}", deviceId, firmware.getVersion());
|
||||
} catch (Exception e) {
|
||||
log.error("OTA 升级指令下发失败: deviceId={}", deviceId, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!record.getDeviceModels().isEmpty()) {
|
||||
deviceOtaOperateRecordMapper.insert(record);
|
||||
}
|
||||
|
||||
String msg = String.format("升级指令下发完成: 成功=%d, 离线=%d, 总计=%d",
|
||||
successCount, offlineCount, totalDevices);
|
||||
log.info(msg);
|
||||
return AjaxResult.success(msg);
|
||||
}
|
||||
|
||||
|
||||
@@ -85,6 +153,15 @@ public class DeviceOtaService {
|
||||
return deviceOtaOperateRecordMapper.selectById(id);
|
||||
}
|
||||
|
||||
public List<DeviceOtaOperateRecord> selectListByDeviceId(String deviceId) {
|
||||
LambdaQueryWrapper<DeviceOtaOperateRecord> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.apply(
|
||||
"JSON_CONTAINS(device_models, {0})",
|
||||
"\"" + deviceId + "\""
|
||||
);
|
||||
return deviceOtaOperateRecordMapper.selectList(wrapper);
|
||||
}
|
||||
|
||||
public int deleteOperateRecord(List<Long> ids) {
|
||||
if (CollectionUtils.isEmpty(ids)) {
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user