19 Commits

Author SHA1 Message Date
372b1a2d11 #实时消息和参数 2026-10-23 09:12:53 +08:00
e57699dbdf #更新实时消息具体字段 2026-10-20 09:04:57 +08:00
9e3ec6a1dd #修改远程遥控控制方式 将左右轮速改成线速度和角速度 2026-10-16 09:19:31 +08:00
11984316d4 #一键寻车接口预留 2026-10-14 09:08:33 +08:00
11e209bcb3 #任务下发状态推送 2026-10-13 12:57:29 +08:00
bde6b89965 #增加系统参数配置模块 2026-10-12 15:40:23 +08:00
13eefaf152 #角色菜单查询去重 2026-10-10 10:31:37 +08:00
4fad6cc929 #注释调整 2026-09-24 14:47:26 +08:00
99d4be4ee4 #ota 接口调整 2026-09-24 12:50:02 +08:00
eb1884b972 #调整在线升级 2026-09-24 09:58:49 +08:00
c69e89fa7d #update 设备在线升级的功能 2026-09-24 08:52:26 +08:00
9cd3b8e496 #update 设备在线升级的功能 2026-09-23 15:24:49 +08:00
25a05ae9d8 #update 设备在线升级的功能 2026-09-23 14:38:12 +08:00
5c0ee1f811 #temp 2026-09-23 13:17:39 +08:00
fce873b662 #update ota 在线升级 2026-09-23 09:55:05 +08:00
1d2636f641 #优化设备下线推送 2026-09-22 13:50:19 +08:00
40c6edda36 Merge branch 'dev_map' into dev_new_connect
# Conflicts:
#	maibu-common/src/main/java/com/maibu/mqtt/HostMessageHandler.java
#	maibu-netty-server/src/main/java/com/maibu/service/DeviceThreadService.java
2026-09-21 10:30:03 +08:00
be27d2b56e #修改更新状态 2026-09-20 13:40:25 +08:00
f690786847 #接受路径规划接口日志 2026-09-14 13:06:08 +08:00
73 changed files with 2313 additions and 1316 deletions

View File

@@ -0,0 +1,53 @@
package com.maibu.web.controller.system;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
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.RestController;
import com.maibu.core.controller.BaseController;
import com.maibu.core.domain.AjaxResult;
import com.maibu.core.domain.entity.SysParam;
import com.maibu.service.SysParamService;
/**
* 系统参数管理
*
* @author ruoyi
*/
@RestController
@RequestMapping("/system/param")
public class SysParamController extends BaseController {
@Autowired
private SysParamService sysParamService;
/**
* 查询系统参数列表
*/
@GetMapping("/list")
public AjaxResult list() {
try {
return AjaxResult.success(sysParamService.list());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 新增或者修改系统参数
*/
@PostMapping("/save")
public AjaxResult save(@RequestBody List<SysParam> sysParams) {
try {
return AjaxResult.success(sysParamService.save(sysParams));
} catch (Exception e) {
return AjaxResult.error("新增修改系统参数出错", e.getMessage());
}
}
}

View File

@@ -1,3 +1,28 @@
system.param.systemName.name = 系统名称
system.param.systemName.desc = 系统名称
system.param.defaultStationName.name = 默认场站
system.param.defaultStationName.desc = "默认场站
system.param.defaultLanguage.name = 默认语言
system.param.defaultLanguage.desc = 默认语言
system.param.defaultZoneId.name = 默认时区
system.param.defaultZoneId.desc = 默认时区
system.param.daylightSavingTime.name = 夏令时
system.param.daylightSavingTime.desc = 夏令时
system.param.defaultUnit.name = 默认单位
system.param.defaultUnit.desc = 默认单位
system.param.defaultDateFormat.name = 默认日期格式
system.param.defaultDateFormat.desc = 默认日期格式
#\u9519\u8BEF\u6D88\u606F
not.null=* \u5FC5\u987B\u586B\u5199
user.jcaptcha.error=\u9A8C\u8BC1\u7801\u9519\u8BEF

View File

@@ -1,3 +1,32 @@
system.param.systemName.name = System Name
system.param.systemName.desc = System Name
system.param.defaultStationName.name = Default Station
system.param.defaultStationName.desc = Default Station
system.param.defaultLanguage.name = Default Language
system.param.defaultLanguage.desc = Default Language
system.param.defaultZoneId.name = Default Zone
system.param.defaultZoneId.desc = Default Zone
system.param.daylightSavingTime.name = Daylight Saving Time
system.param.daylightSavingTime.desc = Daylight Saving Time
system.param.defaultUnit.name = Default Unit
system.param.defaultUnit.desc = Default Unit
system.param.defaultDateFormat.name = Default Date Format
system.param.defaultDateFormat.desc = Default Date Format
#\u9519\u8BEF\u6D88\u606F
not.null=* \u5FC5\u987B\u586B\u5199
user.jcaptcha.error=\u9A8C\u8BC1\u7801\u9519\u8BEF

View File

@@ -1,3 +1,31 @@
system.param.systemName.name = 系统名称
system.param.systemName.desc = 系统名称
system.param.defaultStationName.name = 默认场站
system.param.defaultStationName.desc = "默认场站
system.param.defaultLanguage.name = 默认语言
system.param.defaultLanguage.desc = 默认语言
system.param.defaultZoneId.name = 默认时区
system.param.defaultZoneId.desc = 默认时区
system.param.daylightSavingTime.name = 夏令时
system.param.daylightSavingTime.desc = 夏令时
system.param.defaultUnit.name = 默认单位
system.param.defaultUnit.desc = 默认单位
system.param.defaultDateFormat.name = 默认日期格式
system.param.defaultDateFormat.desc = 默认日期格式
#\u9519\u8BEF\u6D88\u606F
not.null=* \u5FC5\u987B\u586B\u5199
user.jcaptcha.error=\u9A8C\u8BC1\u7801\u9519\u8BEF

View File

@@ -1,19 +1,19 @@
package com.maibu.constant;
import com.maibu.core.business.DeviceRunParam;
import com.maibu.core.enums.ConnectionCloseReason;
import io.netty.util.AttributeKey;
import lombok.Data;
import java.util.concurrent.CompletableFuture;
@Data
import com.maibu.core.business.DeviceRunParam;
import com.maibu.core.enums.ConnectionCloseReason;
import io.netty.util.AttributeKey;
public class Constant {
public static final AttributeKey<String> ATT_DEVICE_ID = AttributeKey.valueOf("deviceId");
public static final AttributeKey<CompletableFuture<DeviceRunParam>> READ_CONFIG_KEY =
AttributeKey.valueOf("READ_CONFIG");
public static final AttributeKey<CompletableFuture<DeviceRunParam>> READ_CONFIG_KEY = AttributeKey
.valueOf("READ_CONFIG");
public static final AttributeKey<ConnectionCloseReason> ATT_CLOSE_REASON = AttributeKey.valueOf("closeReason");
@@ -27,5 +27,10 @@ public class Constant {
public static final String mqttHandleKey = "hostHandle";
public static final Long hostOfflineInterval = 10000L; //单位毫秒
public static final Long hostOfflineInterval = 10000L; // 单位毫秒
// ==================== 远程控制 ====================
public static final String protocol_version = "1.4.3";
public static final String remote_control_msg_prefix = "remote-msg";
public static final String remote_control_cmd_prefix = "remote-cmd";
}

View File

@@ -86,7 +86,7 @@ public class Device extends BaseDO {
*/
@ApiModelProperty("固件版本")
@Excel(name = "固件版本")
private BigDecimal firmwareVersion;
private String firmwareVersion;
/**
* WIFI固件版本

View File

@@ -1,22 +1,19 @@
package com.maibu.core.business;
import java.time.LocalDate;
import java.util.List;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.maibu.annotation.Excel;
import com.maibu.constant.CommandConstant;
import com.maibu.core.domain.BaseDO;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Data
@TableName(value = "iot_device_run_param", autoResultMap = true)
@@ -25,366 +22,440 @@ public class DeviceRunParam extends BaseDO {
@TableId(type = IdType.AUTO)
private Long id;
// private String deviceId;
// private String deviceId;
private Long siteId;
private Long orgId;
private Integer runSpeed; //不下发到设备自己用
private Integer runSpeed; // 不下发到设备自己用
@Excel(name = "设备序列号", cellType = Excel.ColumnType.STRING, prompt = "设备序列号")
private String deviceId;//设备序列号
private String deviceId;// 设备序列号
@Excel(name = "产品型号", cellType = Excel.ColumnType.STRING, prompt = "产品型号")
private String productModel;//产品型号
private String productModel;// 产品型号
@Excel(name = "设备类型", cellType = Excel.ColumnType.STRING, prompt = "设备类型")
private String deviceType;//设备类型:mower-割草机器人
private String deviceType;// 设备类型:mower-割草机器人
@Excel(name = "RTK模式", cellType = Excel.ColumnType.STRING, prompt = "RTK模式")
private String rtkMode;//RTK模式:network-4G网络差分
private String rtkMode;// RTK模式:network-4G网络差分
@Excel(name = "摄像头数量", cellType = Excel.ColumnType.STRING, prompt = "摄像头数量")
private Integer cameraNum;//摄像头数量
private Integer cameraNum;// 摄像头数量
@Excel(name = "动力类型", cellType = Excel.ColumnType.STRING, prompt = "动力类型")
private String powerType;//动力类型:hybrid-混动
private String powerType;// 动力类型:hybrid-混动
@Excel(name = "车轮类型", cellType = Excel.ColumnType.STRING, prompt = "车轮类型")
private String wheelType;//车轮类型:track-履带式
private String wheelType;// 车轮类型:track-履带式
@Excel(name = "发动机类型", cellType = Excel.ColumnType.STRING, prompt = "发动机类型")
private String engineType;//发动机类型:honda-本田发动机
private String engineType;// 发动机类型:honda-本田发动机
@Excel(name = "是否搭载上位机",cellType = Excel.ColumnType.NUMERIC)
private Integer hasHost = 1;//是否搭载上位机
@Excel(name = "是否搭载上位机", cellType = Excel.ColumnType.NUMERIC)
private Integer hasHost = 1;// 是否搭载上位机
@Excel(name = "设备能力列表")
@TableField(exist = false)
private String capabilities;
private String factoryCode; // 工厂码
private String lineNum;// 产线号
private LocalDate productionDate;// 生产日期
private String serialNumber;// 流水号
@Excel(name = "电池类型", cellType = Excel.ColumnType.STRING, prompt = "电池类型")
private String batteryType;// 电池类型
@Excel(name = "行走驱动类型", cellType = Excel.ColumnType.STRING, prompt = "行走驱动类型")
private String walkingDriveType;// 行走驱动类型
@Excel(name = "割刀驱动类型", cellType = Excel.ColumnType.STRING, prompt = "割刀驱动类型")
private String cutterDriveType;// 割刀驱动类型
@Excel(name = "RTK天线数", cellType = Excel.ColumnType.STRING, prompt = "RTK天线数")
private String rtkAntennasNum;// RTK天线数
@Excel(name = "手柄类型", cellType = Excel.ColumnType.STRING, prompt = "手柄类型")
private String handleType;// 手柄类型
@Excel(name = "PCB硬件版本", cellType = Excel.ColumnType.STRING, prompt = "PCB硬件版本")
private String pcbVersion;// PCB硬件版本
@Excel(name = "长宽高", cellType = Excel.ColumnType.STRING, prompt = "长宽高")
private String dimensions;// 长宽高
@Excel(name = "割刀宽度", cellType = Excel.ColumnType.STRING, prompt = "割刀宽度")
private String bladeWidth;// 割刀宽度
@Excel(name = "轮胎尺寸", cellType = Excel.ColumnType.STRING, prompt = "轮胎尺寸")
private String tyreSize;// 轮胎尺寸
@Excel(name = "转速比", cellType = Excel.ColumnType.STRING, prompt = "转速比")
private String speedRatio;// 转速比
@Excel(name = "蜂窝网络通信信息", cellType = Excel.ColumnType.STRING, prompt = "蜂窝网络通信信息")
private String cellularInfo;// 蜂窝网络通信信息
@Excel(name = "wifi名称", cellType = Excel.ColumnType.STRING, prompt = "wifi名称")
private String wifiName;// wifi名称
@Excel(name = "蓝牙广播名", cellType = Excel.ColumnType.STRING, prompt = "蓝牙广播名")
private String bluetoothName;// 蓝牙广播名
@Excel(name = "硬件标识信息", cellType = Excel.ColumnType.STRING, prompt = "硬件标识信息")
private String hardwareIdentifiers;// 硬件标识信息
@Excel(name = "IMU型号", cellType = Excel.ColumnType.STRING, prompt = "IMU型号")
private String imuModel;// IMU型号
@Excel(name = "温湿度传感器", cellType = Excel.ColumnType.STRING, prompt = "温湿度传感器")
private String tempHumiditySensor;// 温湿度传感器
@Excel(name = "气压计在位", cellType = Excel.ColumnType.STRING, prompt = "气压计在位")
private String barometerPresent;// 气压计在位
@Excel(name = "超声波在位", cellType = Excel.ColumnType.STRING, prompt = "超声波在位")
private String ultrasonicPresent;// 超声波在位
@Excel(name = "DAC在位", cellType = Excel.ColumnType.STRING, prompt = "DAC在位")
private String dacPresent;// DAC在位
@Excel(name = "质检员", cellType = Excel.ColumnType.STRING, prompt = "质检员")
private String operator;// 质检员
@Excel(name = "检测报告号", cellType = Excel.ColumnType.STRING, prompt = "检测报告号")
private String inspectionNo;// 检测报告号
@Excel(name = "固件版本", cellType = Excel.ColumnType.STRING, prompt = "固件版本")
private String firmwareVersion;// 固件版本
@Excel(name = "经销商", cellType = Excel.ColumnType.STRING, prompt = "经销商")
private String dealer;// DAC在位
@TableField(typeHandler = JacksonTypeHandler.class)
private List<String> capabilityList;//设备能力列表:heartbeat-上报心跳/location-上报位置/vehicle_status-上报整机状态/task_control-任务控制/single_point-单点作业/coverage-全覆盖割草作业/path_upload-作业路径上传
private List<String> capabilityList;// 设备能力列表:heartbeat-上报心跳/location-上报位置/vehicle_status-上报整机状态/task_control-任务控制/single_point-单点作业/coverage-全覆盖割草作业/path_upload-作业路径上传
// @TableField(exist = false)
// public byte header1;
// @TableField(exist = false)
// public byte header2;
// @TableField(exist = false)
// public byte cmd;
// public byte chipUidSign;// 芯片UID固化标志位
// public String chipUid;// 芯片UID
// public byte[] remoteConfig;// 遥控器通道配置
// public int knifeMotorMode;// 割刀电机模式
// public int walkMotorMode;// 行走电机模式
// public int leftMotorReverse;// 左轮电机方向极性
// public int rightMotorReverse;// 右轮电机方向极性
// public int swapChannel;// 左右轮通道互换标志位
// public int use4G;// 联网标志位
// public short forwardSpeedLimit;// 前进速度限制
// public short turnSpeedLimit;// 转向速度限制
// public int knifePolarity;// 割刀通道极性
// public int fanPolarity;// 风门通道极性
// public int throttlePolarity;// 油门通道极性
// public int liftProtectTime;// 底盘升降电机保护时间
// public int dualRtk;// RTK配置
// public int knifeChannel;// 割刀通道配置
// public int fanChannel;// 风门通道配置
// public int throttleChannel;// 油门通道配置
// public int remoteType;// 遥控器类型
// public boolean relayBoard;// 是否搭载继电器板
// public int liftChannel;// 底盘升通道配置
// public int chassisChannel;// 底盘降通道配置
// public int armChannel;// 抱闸通道配置
// public int fuelPumpChannel;// 燃油励磁通道配置
// public String wifiName;// wifi名称
// public String wifiPassword;// wifi密码
// public int batteryType;// 电池类型
// public int driveType;// 行走驱动配置
// public float gearRatio;// 转速比
// public float robotLength;// 机器人长度
// public float robotWidth;// 机器人宽度
// public float robotHeight;// 机器人高度
// public float knifeWidth;// 割刀宽度
// public float tyreSize;// 轮胎尺寸
// public float leftForwardGain;// 左轮前进增益
// public float leftBackwardGain;// 左轮后退增益
// public float rightForwardGain;// 右轮前进增益
// public float rightBackwardGain;// 右轮后退增益
// public String firmwareVersion;// 固件版本
// @TableField(exist = false)
// public int crc16;
// @TableField(exist = false)
// public byte tail1;
// @TableField(exist = false)
// public byte tail2;
// @TableField(exist = false)
// private byte[] originalData;
// public static byte[] toReadBytes() {
// ByteBuffer buffer = ByteBuffer.allocate(7); // 修改为27字节
// buffer.order(ByteOrder.LITTLE_ENDIAN);
// buffer.put((byte) 0xAB);
// buffer.put((byte) 0xAA);
// buffer.put(CommandConstant.readConfig);
// buffer.putShort((short) 0);
// buffer.put((byte) 0xAA);
// buffer.put((byte) 0xAB);
// return buffer.array();
// }
// public byte[] toWriteBytes() {
// // 如果没有原始数据,使用静态方法重新构建
// if (originalData == null || originalData.length < 178) {
// return null;
// }
// // 克隆原始数据,避免修改原始数据
// byte[] result = originalData.clone();
// ByteBuffer buffer = ByteBuffer.wrap(result);
// buffer.order(ByteOrder.LITTLE_ENDIAN);
// // 修改指令为写配置(字节2)
// buffer.put(2, CommandConstant.writeConfig);
// // 修改字节68:遥控器配置
// byte remote = 0;
// remote |= (getKnifeMotorMode() & 0x03);
// remote |= (getWalkMotorMode() & 0x03) << 2;
// remote |= (getLeftMotorReverse() & 0x01) << 4;
// remote |= (getRightMotorReverse() & 0x01) << 5;
// remote |= (getSwapChannel() & 0x01) << 6;
// remote |= (getUse4G() & 0x01) << 7;
// buffer.put(68, remote);
// // 修改字节69-72:速度限制
// buffer.putShort(69, getForwardSpeedLimit());
// buffer.putShort(71, getTurnSpeedLimit());
// // 修改字节73:通道极性
// byte b73 = 0;
// b73 |= (getKnifePolarity() & 1);
// b73 |= (getFanPolarity() & 1) << 1;
// b73 |= (getThrottlePolarity() & 1) << 2;
// b73 |= (getLiftProtectTime() & 0x0F) << 3;
// b73 |= (getDualRtk() & 1) << 7;
// buffer.put(73, b73);
// // 修改字节74-77:通道配置
// byte b74 = 0;
// b74 |= (getKnifeChannel() & 0x0F);
// b74 |= (getFanChannel() & 0x0F) << 4;
// buffer.put(74, b74);
// byte b75 = 0;
// b75 |= (getThrottleChannel() & 0x0F);
// b75 |= (getRemoteType() & 0x07) << 4;
// if (isRelayBoard())
// b75 |= 1 << 7;
// buffer.put(75, b75);
// byte b76 = 0;
// b76 |= (getLiftChannel() & 0x0F);
// b76 |= (getChassisChannel() & 0x0F) << 4;
// buffer.put(76, b76);
// byte b77 = 0;
// b77 |= (getArmChannel() & 0x0F);
// b77 |= (getFuelPumpChannel() & 0x0F) << 4;
// buffer.put(77, b77);
// // // 修改字节78-117:WiFi信息
// // byte[] wifiNameBytes = new byte[20];
// // if (getWifiName() != null) {
// // byte[] src = getWifiName().getBytes(StandardCharsets.UTF_8);
// // System.arraycopy(src, 0, wifiNameBytes, 0, Math.min(src.length, 20));
// // }
// // System.arraycopy(wifiNameBytes, 0, result, 78, 20);
// //
// // byte[] wifiPwdBytes = new byte[20];
// // if (getWifiPassword() != null) {
// // byte[] src = getWifiPassword().getBytes(StandardCharsets.UTF_8);
// // System.arraycopy(src, 0, wifiPwdBytes, 0, Math.min(src.length, 20));
// // }
// // System.arraycopy(wifiPwdBytes, 0, result, 98, 20);
// // // 修改字节118:电池和驱动配置
// // byte b118 = 0;
// // b118 |= (getBatteryType() & 0x03);
// // b118 |= (getDriveType() & 0x03) << 2;
// // buffer.put(118, b118);
// // 修改字节119-158:float参数
// buffer.putFloat(119, getGearRatio());
// buffer.putFloat(123, getRobotLength());
// buffer.putFloat(127, getRobotWidth());
// buffer.putFloat(131, getRobotHeight());
// buffer.putFloat(135, getKnifeWidth());
// buffer.putFloat(139, getTyreSize());
// buffer.putFloat(143, getLeftForwardGain());
// buffer.putFloat(147, getLeftBackwardGain());
// buffer.putFloat(151, getRightForwardGain());
// buffer.putFloat(155, getRightBackwardGain());
// // 修改字节159-173:固件版本
// // byte[] fwBytes = new byte[15];
// // if (getFirmwareVersion() != null) {
// // byte[] src = getFirmwareVersion().getBytes(StandardCharsets.UTF_8);
// // System.arraycopy(src, 0, fwBytes, 0, Math.min(src.length, 15));
// // }
// // System.arraycopy(fwBytes, 0, result, 159, 15);
// // 保留原始的 CRC 和尾部,不强制修改
// // 移除以下强制修改:
// // buffer.putShort(174, (short) 0);
// // buffer.put(176, (byte) 0xAA);
// // buffer.put(177, (byte) 0xAB);
// return result;
// }
// public static void putFixedString(ByteBuffer buffer, String value, int len) {
// byte[] bytes = new byte[len];
// if (value != null) {
// byte[] src = value.getBytes(StandardCharsets.UTF_8);
// System.arraycopy(src, 0, bytes, 0, Math.min(src.length, len));
// }
// buffer.put(bytes);
// }
// public static DeviceRunParam fromBytes(byte[] data) {
// // 183
// if (data == null || data.length < 178) {
// throw new IllegalArgumentException("数据长度不足");
// }
// ByteBuffer buffer = ByteBuffer.wrap(data);
// buffer.order(ByteOrder.LITTLE_ENDIAN);
// DeviceRunParam p = new DeviceRunParam();
// p.setOriginalData(data);
@TableField(exist = false)
public byte header1;
@TableField(exist = false)
public byte header2;
@TableField(exist = false)
public byte cmd;
public byte chipUidSign;//芯片UID固化标志位
public String chipUid;//芯片UID
public byte[] remoteConfig;//遥控器通道配置
public int knifeMotorMode;//割刀电机模式
public int walkMotorMode;//行走电机模式
public int leftMotorReverse;//左轮电机方向极性
public int rightMotorReverse;//右轮电机方向极性
public int swapChannel;//左右轮通道互换标志位
public int use4G;//联网标志位
public short forwardSpeedLimit;//前进速度限制
public short turnSpeedLimit;//转向速度限制
public int knifePolarity;//割刀通道极性
public int fanPolarity;//风门通道极性
public int throttlePolarity;//油门通道极性
public int liftProtectTime;//底盘升降电机保护时间
public int dualRtk;//RTK配置
public int knifeChannel;//割刀通道配置
public int fanChannel;//风门通道配置
public int throttleChannel;//油门通道配置
public int remoteType;//遥控器类型
public boolean relayBoard;//是否搭载继电器板
public int liftChannel;//底盘升通道配置
public int chassisChannel;//底盘降通道配置
public int armChannel;//抱闸通道配置
public int fuelPumpChannel;//燃油励磁通道配置
public String wifiName;//wifi名称
public String wifiPassword;//wifi密码
public int batteryType;//电池类型
public int driveType;//行走驱动配置
public float gearRatio;//转速比
public float robotLength;//机器人长度
public float robotWidth;//机器人宽度
public float robotHeight;//机器人高度
public float knifeWidth;//割刀宽度
public float tyreSize;//轮胎尺寸
public float leftForwardGain;//左轮前进增益
public float leftBackwardGain;//左轮后退增益
public float rightForwardGain;//右轮前进增益
public float rightBackwardGain;//右轮后退增益
public String firmwareVersion;//固件版本
@TableField(exist = false)
public int crc16;
@TableField(exist = false)
public byte tail1;
@TableField(exist = false)
public byte tail2;
@TableField(exist = false)
private byte[] originalData;
public static byte[] toReadBytes() {
ByteBuffer buffer = ByteBuffer.allocate(7); // 修改为27字节
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.put((byte) 0xAB);
buffer.put((byte) 0xAA);
buffer.put(CommandConstant.readConfig);
buffer.putShort((short) 0);
buffer.put((byte) 0xAA);
buffer.put((byte) 0xAB);
return buffer.array();
}
public byte[] toWriteBytes() {
// 如果没有原始数据,使用静态方法重新构建
if (originalData == null || originalData.length < 178) {
return null;
}
// 克隆原始数据,避免修改原始数据
byte[] result = originalData.clone();
ByteBuffer buffer = ByteBuffer.wrap(result);
buffer.order(ByteOrder.LITTLE_ENDIAN);
// 修改指令为写配置(字节2)
buffer.put(2, CommandConstant.writeConfig);
// 修改字节68:遥控器配置
byte remote = 0;
remote |= (getKnifeMotorMode() & 0x03);
remote |= (getWalkMotorMode() & 0x03) << 2;
remote |= (getLeftMotorReverse() & 0x01) << 4;
remote |= (getRightMotorReverse() & 0x01) << 5;
remote |= (getSwapChannel() & 0x01) << 6;
remote |= (getUse4G() & 0x01) << 7;
buffer.put(68, remote);
// 修改字节69-72:速度限制
buffer.putShort(69, getForwardSpeedLimit());
buffer.putShort(71, getTurnSpeedLimit());
// 修改字节73:通道极性
byte b73 = 0;
b73 |= (getKnifePolarity() & 1);
b73 |= (getFanPolarity() & 1) << 1;
b73 |= (getThrottlePolarity() & 1) << 2;
b73 |= (getLiftProtectTime() & 0x0F) << 3;
b73 |= (getDualRtk() & 1) << 7;
buffer.put(73, b73);
// 修改字节74-77:通道配置
byte b74 = 0;
b74 |= (getKnifeChannel() & 0x0F);
b74 |= (getFanChannel() & 0x0F) << 4;
buffer.put(74, b74);
byte b75 = 0;
b75 |= (getThrottleChannel() & 0x0F);
b75 |= (getRemoteType() & 0x07) << 4;
if (isRelayBoard()) b75 |= 1 << 7;
buffer.put(75, b75);
byte b76 = 0;
b76 |= (getLiftChannel() & 0x0F);
b76 |= (getChassisChannel() & 0x0F) << 4;
buffer.put(76, b76);
byte b77 = 0;
b77 |= (getArmChannel() & 0x0F);
b77 |= (getFuelPumpChannel() & 0x0F) << 4;
buffer.put(77, b77);
// // 修改字节78-117:WiFi信息
// byte[] wifiNameBytes = new byte[20];
// if (getWifiName() != null) {
// byte[] src = getWifiName().getBytes(StandardCharsets.UTF_8);
// System.arraycopy(src, 0, wifiNameBytes, 0, Math.min(src.length, 20));
// }
// System.arraycopy(wifiNameBytes, 0, result, 78, 20);
//
// byte[] wifiPwdBytes = new byte[20];
// if (getWifiPassword() != null) {
// byte[] src = getWifiPassword().getBytes(StandardCharsets.UTF_8);
// System.arraycopy(src, 0, wifiPwdBytes, 0, Math.min(src.length, 20));
// }
// System.arraycopy(wifiPwdBytes, 0, result, 98, 20);
// // 修改字节118:电池和驱动配置
// byte b118 = 0;
// b118 |= (getBatteryType() & 0x03);
// b118 |= (getDriveType() & 0x03) << 2;
// buffer.put(118, b118);
// 修改字节119-158:float参数
buffer.putFloat(119, getGearRatio());
buffer.putFloat(123, getRobotLength());
buffer.putFloat(127, getRobotWidth());
buffer.putFloat(131, getRobotHeight());
buffer.putFloat(135, getKnifeWidth());
buffer.putFloat(139, getTyreSize());
buffer.putFloat(143, getLeftForwardGain());
buffer.putFloat(147, getLeftBackwardGain());
buffer.putFloat(151, getRightForwardGain());
buffer.putFloat(155, getRightBackwardGain());
// 修改字节159-173:固件版本
// byte[] fwBytes = new byte[15];
// if (getFirmwareVersion() != null) {
// byte[] src = getFirmwareVersion().getBytes(StandardCharsets.UTF_8);
// System.arraycopy(src, 0, fwBytes, 0, Math.min(src.length, 15));
// }
// System.arraycopy(fwBytes, 0, result, 159, 15);
// 保留原始的 CRC 和尾部,不强制修改
// 移除以下强制修改:
// buffer.putShort(174, (short) 0);
// buffer.put(176, (byte) 0xAA);
// buffer.put(177, (byte) 0xAB);
return result;
}
public static void putFixedString(ByteBuffer buffer, String value, int len) {
byte[] bytes = new byte[len];
if (value != null) {
byte[] src = value.getBytes(StandardCharsets.UTF_8);
System.arraycopy(src, 0, bytes, 0, Math.min(src.length, len));
}
buffer.put(bytes);
}
public static DeviceRunParam fromBytes(byte[] data) {
// 183
if (data == null || data.length < 178) {
throw new IllegalArgumentException("数据长度不足");
}
ByteBuffer buffer = ByteBuffer.wrap(data);
buffer.order(ByteOrder.LITTLE_ENDIAN);
DeviceRunParam p = new DeviceRunParam();
// p.setHeader1(buffer.get());
// p.setHeader2(buffer.get());
p.setOriginalData(data);
// p.setCmd(buffer.get());
p.setHeader1(buffer.get());
p.setHeader2(buffer.get());
// p.setChipUidSign(buffer.get());
p.setCmd(buffer.get());
// p.setChipUid(readString(buffer, 45));
p.setChipUidSign(buffer.get());
// // 跳过字节49-67(共19字节,预留不用)
// buffer.position(buffer.position() + 19);
p.setChipUid(readString(buffer, 45));
// byte remote = buffer.get();
// 跳过字节49-67(共19字节,预留不用)
buffer.position(buffer.position() + 19);
// // p.setRemoteConfig(remote);
byte remote = buffer.get();
// p.setKnifeMotorMode(remote & 0x03);
// p.setWalkMotorMode((remote >> 2) & 0x03);
// p.setLeftMotorReverse((remote >> 4) & 0x01);
// p.setRightMotorReverse((remote >> 5) & 0x01);
// p.setSwapChannel((remote >> 6) & 0x01);
// p.setUse4G((remote >> 7) & 0x01);
// p.setRemoteConfig(remote);
// p.setForwardSpeedLimit(buffer.getShort());
// p.setTurnSpeedLimit(buffer.getShort());
p.setKnifeMotorMode(remote & 0x03);
p.setWalkMotorMode((remote >> 2) & 0x03);
p.setLeftMotorReverse((remote >> 4) & 0x01);
p.setRightMotorReverse((remote >> 5) & 0x01);
p.setSwapChannel((remote >> 6) & 0x01);
p.setUse4G((remote >> 7) & 0x01);
// byte b73 = buffer.get();
p.setForwardSpeedLimit(buffer.getShort());
p.setTurnSpeedLimit(buffer.getShort());
// p.setKnifePolarity(b73 & 1);
// p.setFanPolarity((b73 >> 1) & 1);
// p.setThrottlePolarity((b73 >> 2) & 1);
// p.setLiftProtectTime((b73 >> 3) & 0x0F);
// p.setDualRtk((b73 >> 7) & 1);
byte b73 = buffer.get();
// byte b74 = buffer.get();
p.setKnifePolarity(b73 & 1);
p.setFanPolarity((b73 >> 1) & 1);
p.setThrottlePolarity((b73 >> 2) & 1);
p.setLiftProtectTime((b73 >> 3) & 0x0F);
p.setDualRtk((b73 >> 7) & 1);
// p.setKnifeChannel(b74 & 0x0F);
// p.setFanChannel((b74 >> 4) & 0x0F);
byte b74 = buffer.get();
// byte b75 = buffer.get();
p.setKnifeChannel(b74 & 0x0F);
p.setFanChannel((b74 >> 4) & 0x0F);
// p.setThrottleChannel(b75 & 0x0F);
// p.setRemoteType((b75 >> 4) & 0x07);
// p.setRelayBoard(((b75 >> 7) & 1) == 1);
byte b75 = buffer.get();
// byte b76 = buffer.get();
p.setThrottleChannel(b75 & 0x0F);
p.setRemoteType((b75 >> 4) & 0x07);
p.setRelayBoard(((b75 >> 7) & 1) == 1);
// p.setLiftChannel(b76 & 0x0F);
// p.setChassisChannel((b76 >> 4) & 0x0F);
byte b76 = buffer.get();
// byte b77 = buffer.get();
p.setLiftChannel(b76 & 0x0F);
p.setChassisChannel((b76 >> 4) & 0x0F);
// p.setArmChannel(b77 & 0x0F);
// p.setFuelPumpChannel((b77 >> 4) & 0x0F);
byte b77 = buffer.get();
// p.setWifiName(readString(buffer, 20));
// p.setWifiPassword(readString(buffer, 20));
p.setArmChannel(b77 & 0x0F);
p.setFuelPumpChannel((b77 >> 4) & 0x0F);
// byte b118 = buffer.get();
p.setWifiName(readString(buffer, 20));
p.setWifiPassword(readString(buffer, 20));
// p.setBatteryType(b118 & 0x03);
// p.setDriveType((b118 >> 2) & 0x03);
byte b118 = buffer.get();
// // float
// p.setGearRatio(buffer.getFloat());
// p.setRobotLength(buffer.getFloat());
// p.setRobotWidth(buffer.getFloat());
// p.setRobotHeight(buffer.getFloat());
// p.setKnifeWidth(buffer.getFloat());
// p.setTyreSize(buffer.getFloat());
// p.setLeftForwardGain(buffer.getFloat());
// p.setLeftBackwardGain(buffer.getFloat());
// p.setRightForwardGain(buffer.getFloat());
// p.setRightBackwardGain(buffer.getFloat());
p.setBatteryType(b118 & 0x03);
p.setDriveType((b118 >> 2) & 0x03);
// p.setFirmwareVersion(readString(buffer, 15));
// float
p.setGearRatio(buffer.getFloat());
p.setRobotLength(buffer.getFloat());
p.setRobotWidth(buffer.getFloat());
p.setRobotHeight(buffer.getFloat());
p.setKnifeWidth(buffer.getFloat());
p.setTyreSize(buffer.getFloat());
p.setLeftForwardGain(buffer.getFloat());
p.setLeftBackwardGain(buffer.getFloat());
p.setRightForwardGain(buffer.getFloat());
p.setRightBackwardGain(buffer.getFloat());
// p.setCrc16(Short.toUnsignedInt(buffer.getShort()));
p.setFirmwareVersion(readString(buffer, 15));
// p.setTail1(buffer.get());
// p.setTail2(buffer.get());
p.setCrc16(Short.toUnsignedInt(buffer.getShort()));
// return p;
// }
p.setTail1(buffer.get());
p.setTail2(buffer.get());
// public static String readString(ByteBuffer buffer, int len) {
// byte[] bytes = new byte[len];
// buffer.get(bytes);
return p;
}
// int end = 0;
// while (end < bytes.length && bytes[end] != 0) {
// end++;
// }
public static String readString(ByteBuffer buffer, int len) {
byte[] bytes = new byte[len];
buffer.get(bytes);
int end = 0;
while (end < bytes.length && bytes[end] != 0) {
end++;
}
return new String(bytes, 0, end, StandardCharsets.UTF_8);
}
// return new String(bytes, 0, end, StandardCharsets.UTF_8);
// }
}

View File

@@ -1,4 +1,4 @@
package com.maibu.dto;
package com.maibu.core.business;
import lombok.Data;
@@ -42,13 +42,13 @@ public class LawnMoverProtocolEntity {
}
public byte[] toBytes() {
ByteBuffer buffer = ByteBuffer.allocate(15);
ByteBuffer buffer = ByteBuffer.allocate(27);
buffer.order(ByteOrder.LITTLE_ENDIAN); // 根据协议决定字节序
buffer.put(Header1);
buffer.put(Header2);
buffer.put(CommandType);
buffer.putShort(LeftWheelSpeed);
buffer.putShort(RightWheelSpeed);
buffer.putDouble(linearSpeed);
buffer.putDouble(angularSpeed);
buffer.put(ChassisLiftFlag);
buffer.put(MowerSpeedFlag);
buffer.put(IgnitionFlag);
@@ -67,7 +67,46 @@ public class LawnMoverProtocolEntity {
return sb.toString().trim();
}
/**
* 将控制指令字节数组解析为实体对象
*/
public static LawnMoverProtocolEntity parseControlCommand(byte[] data) {
if (data == null || data.length != 27) {
throw new IllegalArgumentException("控制指令长度必须为15字节");
}
ByteBuffer buffer = ByteBuffer.wrap(data);
buffer.order(ByteOrder.LITTLE_ENDIAN);
LawnMoverProtocolEntity entity = new LawnMoverProtocolEntity();
// 帧头
entity.setHeader1(buffer.get());
entity.setHeader2(buffer.get());
// 命令类型
entity.setCommandType(buffer.get());
// 左右轮速度:int16
entity.setLinearSpeed(buffer.getDouble());
entity.setAngularSpeed(buffer.getDouble());
// 控制标志:uint8
entity.setChassisLiftFlag(buffer.get());
entity.setMowerSpeedFlag(buffer.get());
entity.setIgnitionFlag(buffer.get());
entity.setEmergencyStopFlag(buffer.get());
// 预留字段
buffer.get();
buffer.get();
// 帧尾
entity.setEndFlag1(buffer.get());
entity.setEndFlag2(buffer.get());
return entity;
}
// 包头起始字节1
private byte Header1;
@@ -79,6 +118,10 @@ public class LawnMoverProtocolEntity {
private short LeftWheelSpeed;
// 右轮速度:16位有符号
private short RightWheelSpeed;
private double linearSpeed;
private double angularSpeed;
// 底盘升降标志位
private byte ChassisLiftFlag;
// 割刀速度标志位

View File

@@ -1,6 +1,7 @@
package com.maibu.core.business.device;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.maibu.constant.Constant;
import com.maibu.core.business.*;
import com.maibu.core.business.path.LatAndLngEntity;
import com.maibu.core.business.path.RoutePlanSendEntity;
@@ -9,6 +10,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 +28,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;
@@ -138,7 +137,6 @@ public class NettyDevice extends Connector {
return finishId;
}
public synchronized boolean cancelingTask() throws MqttException {
if (task != null) {
WorkRecord workRecord = workRecordMapper.selectById(task.getRouteId());
@@ -151,7 +149,7 @@ public class NettyDevice extends Connector {
dto.setMode(workRecord.getMode());
dto.setTimestamp(System.currentTimeMillis());
dto.setMessage_id(task.getId() + "_" + System.currentTimeMillis());
dto.setProtocol_version("1.4");
dto.setProtocol_version(Constant.protocol_version);
GlobalMemory.customMqttDeviceMonitor.publish(mqttClientId,
String.format(MqttTopic.MOWER_TASK_ROUTE_SET, task.getDeviceId()),
@@ -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());
@@ -248,24 +246,24 @@ public class NettyDevice extends Connector {
}
}
public void readConfigCommand() {
byte[] bytes = DeviceRunParam.toReadBytes();
log.debug("下发读取配置的报文:{}", toHexString(bytes));
if (this.getChannel() != null && this.getChannel().isActive()) {
this.getChannel().writeAndFlush(Unpooled.wrappedBuffer(bytes));
}
log.debug("下发读取配置的报文完成:{}", toHexString(bytes));
}
// public void readConfigCommand() {
// byte[] bytes = DeviceRunParam.toReadBytes();
// log.debug("下发读取配置的报文:{}", toHexString(bytes));
// if (this.getChannel() != null && this.getChannel().isActive()) {
// this.getChannel().writeAndFlush(Unpooled.wrappedBuffer(bytes));
// }
// log.debug("下发读取配置的报文完成:{}", toHexString(bytes));
// }
public void writeConfigCommand(DeviceRunParam deviceRunParam) {
log.debug("下发修改配置的属性:{}", JsonUtils.toJsonString(deviceRunParam));
byte[] bytes = deviceRunParam.toWriteBytes();
log.debug("下发修改配置的报文:{}", toHexString(bytes));
if (this.getChannel() != null && this.getChannel().isActive()) {
this.getChannel().writeAndFlush(Unpooled.wrappedBuffer(bytes));
}
log.debug("下发修改配置的报文完成:{}", toHexString(bytes));
}
// public void writeConfigCommand(DeviceRunParam deviceRunParam) {
// log.debug("下发修改配置的属性:{}", JsonUtils.toJsonString(deviceRunParam));
// byte[] bytes = deviceRunParam.toWriteBytes();
// log.debug("下发修改配置的报文:{}", toHexString(bytes));
// if (this.getChannel() != null && this.getChannel().isActive()) {
// this.getChannel().writeAndFlush(Unpooled.wrappedBuffer(bytes));
// }
// log.debug("下发修改配置的报文完成:{}", toHexString(bytes));
// }
// todo 优化
public synchronized boolean pauseTask() {
@@ -296,7 +294,6 @@ public class NettyDevice extends Connector {
}
}
public void startTask() {
try {
if (executingTask)
@@ -365,7 +362,7 @@ public class NettyDevice extends Connector {
dto.setMode(workRecord.getMode());
dto.setTimestamp(System.currentTimeMillis());
dto.setMessage_id(task.getId() + "_" + System.currentTimeMillis());
dto.setProtocol_version("1.4");
dto.setProtocol_version(Constant.protocol_version);
PointEntity point = new PointEntity();
if (!StringUtils.isEmpty(workRecord.getMode())) {
@@ -403,4 +400,32 @@ 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));
}
public void searchDevice(String deviceId) throws MqttException {
Map<String, Object> payload = new HashMap<>();
payload.put("action", "search_device");
payload.put("timestamp", System.currentTimeMillis());
payload.put("message_id", "search_device_" + System.currentTimeMillis());
GlobalMemory.customMqttDeviceMonitor.publish(
mqttClientId,
String.format(MqttTopic.MOWER_SEARCH_DEVICE_SET, deviceId),
JsonUtils.toJsonString(payload));
}
}

View File

@@ -7,12 +7,14 @@ public class DeviceMapPushDTO {
private String id;
private double lat;
private double lng;
private double y; //yaw
private String f; //fix_status
private double a; //altitude
private String s;//status 运行状态
private Long t; //taskId 任务id
private double b; //电量
private boolean ha = false;//hasAlarm 有无告警
private double y; // yaw
private String f; // fix_status
private double a; // altitude
private String s;// status 运行状态
private Long t; // taskId 任务id
private double b; // 电量
private boolean ha = false;// hasAlarm 有无告警
private String cm;// 控制模式 manual_remote|auto
}

View File

@@ -1,4 +1,4 @@
package com.maibu.dto;
package com.maibu.core.business.dto;
import com.maibu.core.enums.MesType;
@@ -12,4 +12,6 @@ public class WebOlineStatusMessageDTO {
private String errorCode;
private String description;
private MesType type;
private Long time;
}

View File

@@ -0,0 +1,40 @@
package com.maibu.core.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.maibu.core.domain.OrgNoUserDO;
import com.maibu.core.enums.SysParamType;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
@TableName(value = "sys_param")
public class SysParam extends OrgNoUserDO {
@TableId(type = IdType.AUTO)
private Long id;
private SysParamType paramType;
private String paramKey;
private String nameTranslateKey;
private String paramValue;
private String descTranslateKey;
@TableField(exist = false)
private String nameTranslate;
@TableField(exist = false)
private String descTranslate;
private String valueRange;
private String dataType;
}

View File

@@ -1,27 +1,26 @@
package com.maibu.core.enums;
import com.maibu.annotation.Excel;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum AlarmType {
UAV_FAULT("UAV_FAULT", "无人机故障"),
MOWER_FAULT("MOWER_FAULT", "割草机故障"),
OTHER_DEVICE_FAULT("OTHER_DEVICE_FAULT", "其他设备故障"),
SERVER_FAILURE("SERVER_FAILURE", "服务器故障"),
SYSTEM_ERROR("SYSTEM_ERROR", "系统错误");
/** 错误来源值(存储在数据库中) */
private final String value;
/** 错误来源名称(用于前端显示) */
private final String name;
}
package com.maibu.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum AlarmType {
UAV_FAULT("UAV_FAULT", "无人机故障"),
MOWER_FAULT("MOWER_FAULT", "割草机故障"),
OTHER_DEVICE_FAULT("OTHER_DEVICE_FAULT", "其他设备故障"),
SERVER_FAILURE("SERVER_FAILURE", "服务器故障"),
SYSTEM_ERROR("SYSTEM_ERROR", "系统错误");
/** 错误来源值(存储在数据库中) */
private final String value;
/** 错误来源名称(用于前端显示) */
private final String name;
}

View File

@@ -0,0 +1,18 @@
package com.maibu.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum OtaOperate {
upload("upload", "上传"),
upgrade("upgrade", "升级"),
rollback("rollback", "回滚"),
reboot("reboot", "重启");
private final String name;
private final String value;
}

View File

@@ -0,0 +1,22 @@
package com.maibu.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum SysParamType {
site("site", "场站维度"),
org("org", "组织维度"),
global("global", "全局维度"),
personal("personal", "个人维度");
private final String value;
private final String name;
}

View File

@@ -0,0 +1,15 @@
package com.maibu.core.host;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 上位机远程控制参数
* DeviceRemoteControlDTO
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class DeviceRemoteControlDTO extends HostBase {
private String command_id;
private ParamDetail data;
}

View File

@@ -0,0 +1,18 @@
package com.maibu.core.host;
import lombok.Data;
@Data
public class ParamDetail {
// private short left_wheel_rpm; // 左轮转速
// private short right_wheel_rpm; // 右轮转速
private double linear_x; // 线速度
private double angular_z; // 角速度
private int chassis_lift; // 底盘升降 0:停止 1:升 2:降
private int cutter; // 割刀加减速 0:停止 1:加速 2:减速
private int ignition; // 点火熄火 0:停止 1:点火 2:熄火
private int emergency_stop; // 急停 0:正常 1:急停
private int valid_for_ms; // 指令有效时间,单位毫秒
}

View File

@@ -5,12 +5,33 @@ import lombok.Data;
@Data
public class VehicleMessage {
private double speed;
private double battery_percentage;// 电量
private double battery_voltage;// 电压
private String motion;// moving|stopped|blocked
private boolean emergency_stop; // 急停状态
private double battery_percentage;// 实时电量百分比
private double battery_voltage;// 实时电压
private double distance; // 里程数
private double left_motor_current;// 实时左电机电流
private double right_motor_current;// 实时右电机电流
private double left_motor_temperature;// 实时左电机温度
private double right_motor_temperature;// 实时右电机温度
private double main_temperature;// 实时主温度
private double yaw;// 实时航向角度
private double pitch;// 实时俯仰角度
private String heading_status;// 实时航向状态
private double total_work_area;// 累计作业面积
private double total_work_duration;// 累计作业时长
private double total_work_distance;// 累计作业里程
private String motion;// moving|stopped|blocked 运动状态
private Boolean emergency_stop; // 急停状态
private double left_target_rpm; // 实时左电机目标转速
private double right_target_rpm;// 实时右电机目标转速
private double left_measured_rpm;// 实时左电机测量转速
private double right_measured_rpm;// 实时右电机测量转速
private double left_motor_rpm;// 实时左电机转速
private double right_motor_rpm;// 实时右电机转速
private double roll;// 实时翻滚角度
private String control_mode;// 遥控器拨杆对应的当前控制模式
private double linear_speed; // 实时线速度
private double angular_speed; // 实时角速度
}

View File

@@ -1,43 +0,0 @@
package com.maibu.core.mq.message;
import com.maibu.core.ota.OtaPackageCode;
import com.maibu.core.protocol.Message;
import com.maibu.core.protocol.modbus.ModbusCode;
import io.netty.buffer.ByteBuf;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 消息解析model
* @author gsb
* @date 2022/10/10 15:53
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Builder
public class DeviceData extends Message {
/*topic*/
private String topicName;
/*设备编号*/
private String serialNumber;
/*原数据*/
private byte[] data;
private ByteBuf buf;
private Object body;
/*MQTT OR 其他*/
private int type;
/*Modbus*/
private ModbusCode code;
/**产品id*/
private Long productId;
private OtaPackageCode netModbusCode;
private int bitCount;
}

View File

@@ -1,59 +0,0 @@
package com.maibu.core.mq.message;
import com.maibu.core.protocol.modbus.ModbusCode;
import com.maibu.enums.ServerType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 设备下发指令model
*
* @author gsb
* @date 2022/10/10 16:18
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DeviceDownMessage {
private String messageId;
/**
* 时间戳,单位毫秒
*/
private Long timestamp;
/**
* 消息体
*/
private Object body;
/*下发的指令,服务调用的时候就是服务标识符*/
private String identifier;
/*产品id*/
private Long productId;
/**
* 设备编码
*/
private String serialNumber;
/**
* 从机编号
*/
private Integer slaveId;
private ModbusCode code;
private String protocolCode;
private List<PropRead> values;
private String topic;
private String subCode;
private ServerType serverType;
public DeviceDownMessage(List<PropRead> values, String topic, String subCode,String transport) {
this.values = values;
this.topic = topic;
this.subCode = subCode;
this.serverType = ServerType.explain(transport);
}
}

View File

@@ -1,33 +0,0 @@
package com.maibu.core.mq.message;
import lombok.Data;
/**
* 平台下发指令数据model
* @author bill
*/
@Data
public class DeviceFunctionMessage {
/*流水号,兼容modbus标准协议没有消息流水号*/
private String seqNo;
/*平台时间*/
private Long pfTimestamp;
/*下发的消息体*/
private Object body;
/*下发的指令物模型标识符*/
private String identifier;
/*下发的数据寄存器地址*/
private String hexAddress;
/*产品ID*/
private Long productId;
/*设备编号*/
private String serialNumber;
/*网关设备编号*/
private String protocolCode;
/*是否有子设备 0-否,1-是*/
private Integer hasSub;
/*子设备从机编号 例如 02 编号从机。通过主机集控下发的指定从机编号*/
private String subDeviceCode;
}

View File

@@ -1,31 +0,0 @@
package com.maibu.core.mq.message;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import java.util.Date;
/**
* 设备消息
* @author bill
*/
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class DeviceMessage {
/** 下发的数据*/
private Object message;
/** 下发的topic*/
private String topicName;
/** 设备编号*/
private String serialNumber;
private String dataType;
/** 时间 */
@JsonFormat(pattern ="yyyy-MM-dd HH:mm:ss:SSS")
private Date time;
}

View File

@@ -1,24 +0,0 @@
package com.maibu.core.mq.message;
import lombok.Data;
/**
* 指令下发组将的model
* @author bill
*/
@Data
public class FunctionCallBackBo {
/*下发的数据*/
private byte[] message;
/*MQTt-下发的topic*/
private String topicName;
/*设备编号*/
private String serialNumber;
/**
* 原数据包
*/
private String sources;
}

View File

@@ -1,20 +0,0 @@
package com.maibu.core.mq.message;
import lombok.Data;
/**
* 指令下发组将的model
* @author bill
*/
@Data
public class InstructionsMessage {
/*下发的数据*/
private byte[] message;
/*MQTt-下发的topic*/
private String topicName;
/*设备编号*/
private String serialNumber;
}

View File

@@ -1,32 +0,0 @@
package com.maibu.core.mq.message;
import lombok.Data;
import java.util.List;
/**
* @author gsb
* @date 2024/6/20 10:53
*/
@Data
public class ModbusPollMsg {
/**
* 下发指令
*/
private List<String> commandList;
/**
* 服务端类型
*/
private Integer serverType;
/**
* 产品id
*/
private Long productId;
/**
* 设备编码
*/
private String serialNumber;
private String transport;
}

View File

@@ -1,26 +0,0 @@
package com.maibu.core.mq.message;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.util.Date;
/**
* @author bill
*/
@Data
public class MqttBo {
/*主题*/
private String topic;
/*数据*/
private String data;
/*消息质量*/
private int qos = 1;
/*发送方向*/
private String direction;
/*时间*/
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date ts;
}

View File

@@ -1,39 +0,0 @@
package com.maibu.core.mq.message;
import com.maibu.core.protocol.modbus.ModbusCode;
import lombok.Data;
/**
* @author gsb
* @date 2022/12/9 10:15
*/
@Data
public class PropRead {
/**设备编号*/
private String serialNumber;
/**寄存器起始地址*/
private int address;
/**
* 读取寄存器个数
*/
private int count;
/**数据结果长度计算值*/
private int length;
/**
* 从机地址
*/
private int slaveId;
/**
* 读取个数
*/
private int quantity;
/**
* 数据
*/
private String data;
/**
* 功能码
*/
private ModbusCode code;
}

View File

@@ -1,21 +0,0 @@
package com.maibu.core.mq.message;
import lombok.Data;
/**
* 协议bean
* @author gsb
* @date 2022/10/25 14:54
*/
@Data
public class ProtocolDto {
/**协议编号*/
private String code;
private String name;
/*外部协议url*/
private String protocolUrl;
private String description;
/**协议类型 协议类型 0:系统协议 1:jar,2.js,3.c*/
private Integer protocolType;
}

View File

@@ -1,18 +0,0 @@
package com.maibu.core.mq.message;
import lombok.Data;
/**
* 网关子设备model
* @author gsb
* @date 2022/10/10 10:18
*/
@Data
public class SubDeviceMessage {
/*子设备编号或编码*/
private String serialNumber;
/*数据*/
private byte[] data;
/*消息id*/
private String messageId;
}

View File

@@ -1,21 +0,0 @@
package com.maibu.core.mq.ota;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import java.math.BigDecimal;
/**
* OTA升级回复model
* @author gsb
* @date 2022/10/24 17:20
*/
@Data
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class OtaReplyMessage {
private Long taskId;
private BigDecimal version;
private int status;
private int progress;
}

View File

@@ -1,40 +0,0 @@
package com.maibu.core.mq.ota;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Builder;
import lombok.Data;
import java.math.BigDecimal;
/**
* OTA远程升级
* @author gsb
* @date 2022/10/10 10:22
*/
@Data
@Builder
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class OtaUpgradeBo {
// 设备编码
private String serialNumber;
// 升级任务ID
private Long taskId;
// 消息内容
private byte[] msg;
// 设备状态
private int status;
// topic
private String topicName;
// 固件包版本
private BigDecimal version;
// 固件包http地址
private String url;
// 固件包分包传输大小
private int packageSize;
// 固件包传输偏移量
private int offset;
// 升级进度
private int progress;
}

View File

@@ -1,63 +0,0 @@
package com.maibu.core.mq.ota;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.maibu.utils.DateUtils;
import lombok.Data;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
/**
* ota升级发送,实现Delayed延时接口
*
* @author bill
*/
@Data
public class OtaUpgradeDelayTask implements Delayed {
/**
* 固件id
*/
private Long firmwareId;
/**
* 1:指定产品 2:指定设备
*/
private Long upgradeType;
private List<String> devices;
/**
* 任务id
*/
private Long taskId;
/**
* 开始升级时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date startTime;
/**
* 设置延迟执行时间 开始升级时间 -当前时间
*
* @param unit
* @return
*/
@Override
public long getDelay(TimeUnit unit) {
return startTime.getTime() - DateUtils.getTimestamp();
}
@Override
public int compareTo(Delayed o) {
OtaUpgradeDelayTask delayTask = (OtaUpgradeDelayTask) o;
//比较
long diff = this.startTime.getTime() - delayTask.startTime.getTime();
if (diff <= 0) {
return -1;
} else {
return 1;
}
}
}

View File

@@ -0,0 +1,36 @@
package com.maibu.core.ota;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.maibu.core.domain.BaseDO;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
@Data
@TableName("iot_ota_firmware")
public class DeviceOtaFirmware extends BaseDO {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.AUTO)
private Long id;
private String version; //版本号
private String fileName; //文件名
private String minioObjectName; //服务器文件名
private String fileUrl; //存放地址
private String deviceType;
private String fileSize;
private String description;
}

View File

@@ -0,0 +1,37 @@
package com.maibu.core.ota;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.maibu.core.domain.BaseDO;
import com.maibu.core.enums.OtaOperate;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
@Data
@TableName(value = "iot_ota_operate_record", autoResultMap = true)
public class DeviceOtaOperateRecord extends BaseDO {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.AUTO)
private Long id;
private OtaOperate operateType; // 操作类型
private String fileName; // 名称
private Long firmwareId; // id
private String deviceId; // 名称
private String deviceType;
private String preVersion;
private String nowVersion;
}

View File

@@ -1,45 +0,0 @@
package com.maibu.core.ota;
import com.maibu.exception.ServiceException;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum OtaPackageCode {
OTA_01("查询产品工装号",(byte) 0x01),
OTA_0A("OTA升级启动",(byte) 0x0A),
OTA_0B("OTA升级包传输",(byte) 0x0B)
;
private String desc;
private byte code;
public static OtaPackageCode getInstance(int code) {
switch ((byte)code) {
case 0x01:
return OTA_01;
case 0x0A:
return OTA_0A;
case 0x0B:
return OTA_0B;
default:
throw new ServiceException("功能码[" + code + "],未定义");
}
}
public static String getDes(int code){
switch ((byte)code) {
case 0x01:
return OTA_01.desc;
case 0x0A:
return OTA_0A.desc;
case 0x0B:
return OTA_0B.desc;
default:
return "UNKOWN";
}
}
}

View File

@@ -0,0 +1,11 @@
package com.maibu.mapper;
import com.maibu.core.ota.DeviceOtaFirmware;
import com.maibu.mybatis.mapper.BaseMapperX;
import org.springframework.stereotype.Repository;
@Repository
public interface DeviceOtaFirmwareMapper extends BaseMapperX<DeviceOtaFirmware> {
}

View File

@@ -0,0 +1,11 @@
package com.maibu.mapper;
import com.maibu.core.ota.DeviceOtaOperateRecord;
import com.maibu.mybatis.mapper.BaseMapperX;
import org.springframework.stereotype.Repository;
@Repository
public interface DeviceOtaOperateRecordMapper extends BaseMapperX<DeviceOtaOperateRecord> {
}

View File

@@ -5,8 +5,12 @@ import com.maibu.core.business.device.Connector;
import com.maibu.core.business.device.NettyDevice;
import com.maibu.core.enums.ConnectorStatus;
import com.maibu.core.enums.ConnectorType;
import com.maibu.utils.json.JsonUtils;
import io.netty.channel.Channel;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -23,6 +27,7 @@ import java.util.stream.Collectors;
*/
@Component
@Data
@Slf4j
public class DeviceSessionManager {
private static final Logger logger = LoggerFactory.getLogger(DeviceSessionManager.class);
@@ -187,7 +192,7 @@ public class DeviceSessionManager {
public List<NettyDevice> getSlaveControl(String slaveId) {
// 获取所有连接的主机 且是active状态的
return deviceMap.values().stream()
.filter(device -> ObjectUtil.equals(device.getCurrentSlaveId(), slaveId)
.filter(device -> Objects.equals(device.getCurrentSlaveId(), slaveId)
&& ConnectorStatus.ACTIVE.equals(device.getConnectorStatus()))
.collect(Collectors.toList());
}
@@ -195,14 +200,16 @@ public class DeviceSessionManager {
public List<NettyDevice> getAllSlaveControl(String slaveId) {
// 获取所有连接的主机 不区分状态
return deviceMap.values().stream()
.filter(device -> ObjectUtil.equals(device.getCurrentSlaveId(), slaveId))
.filter(device -> !StringUtils.isEmpty(device.getCurrentSlaveId())
&& Objects.equals(device.getCurrentSlaveId(), slaveId)
&& device.getDeviceType().equals(ConnectorType.MASTER))
.collect(Collectors.toList());
}
public NettyDevice getCurrentControl(String slaveId) {
// 获取所有连接的主机 不区分状态
return deviceMap.values().stream()
.filter(device -> ObjectUtil.equals(device.getCurrentSlaveId(), slaveId)
.filter(device -> Objects.equals(device.getCurrentSlaveId(), slaveId)
&& ConnectorStatus.ACTIVE.equals(device.getConnectorStatus()))
.findFirst().orElse(null);
}
@@ -312,8 +319,11 @@ public class DeviceSessionManager {
if (taskId == null) {
return null;
}
log.debug("sendMessage deviceMap:{},taskId={}", JsonUtils.toJsonString(deviceMap), taskId);
for (String key : deviceMap.keySet()) {
NettyDevice nettyDevice = deviceMap.get(key);
log.debug("sendMessage currentTaskId:{},equal {}", JsonUtils.toJsonString(nettyDevice.getCurrentTaskId()),
Objects.equals(nettyDevice.getCurrentTaskId(), taskId));
if (nettyDevice != null && Objects.equals(nettyDevice.getCurrentTaskId(), taskId)) {
return nettyDevice.getConnectorId();
}

View File

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

View File

@@ -16,6 +16,7 @@ import org.springframework.util.CollectionUtils;
import com.maibu.core.domain.entity.SysSite;
import com.maibu.core.enums.ErrorSource;
import com.maibu.core.host.VehicleMessage;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
@@ -59,14 +60,31 @@ public class SiteMemory {
public static ConcurrentHashMap<String, DeviceMapPushDTO> deviceMapPushMap = new ConcurrentHashMap<>();
public static ConcurrentHashMap<String, VehicleMessage> vehicleMessageMap = new ConcurrentHashMap<>();
public VehicleMessage getVehicleMessage(String deviceId) {
return vehicleMessageMap.get(deviceId);
}
public void saveVehicleMessage(String deviceId, VehicleMessage vehicleMessage) {
vehicleMessageMap.put(deviceId, vehicleMessage);
}
public void removeVehicleMessage(String deviceId) {
vehicleMessageMap.remove(deviceId);
}
public Map<String, DeviceMapPushDTO> getAllDeviceMapPush() {
return deviceMapPushMap;
}
public void saveMaPushDTO(String deviceId, DeviceMapPushDTO dto) {
public void saveMapPushDTO(String deviceId, DeviceMapPushDTO dto) {
deviceMapPushMap.put(deviceId, dto);
}
public void removeMapPushDTO(String deviceId) {
deviceMapPushMap.remove(deviceId);
}
public void saveRunStatistics(DeviceRunStatistics deviceRunStatistics) {
if (deviceRunStatistics != null && !StringUtils.isEmpty(deviceRunStatistics.getDeviceId())) {

View File

@@ -1,29 +1,18 @@
package com.maibu.mqtt;
import java.util.ArrayList;
import java.util.List;
import com.maibu.core.business.dto.DeviceMapPushDTO;
import org.springframework.util.CollectionUtils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.maibu.constant.Constant;
import com.maibu.constant.NettyCacheKey;
import com.maibu.core.business.DeviceRunStatistics;
import com.maibu.core.business.device.DeviceStatusDetail;
import com.maibu.core.business.device.NettyDevice;
import com.maibu.core.business.dto.DeviceMapPushDTO;
import com.maibu.core.business.dto.WebDeviceTaskStatusMessageDTO;
import com.maibu.core.business.dto.WebStatusMessageDTO;
import com.maibu.core.business.inter.WebsocketMesDispather;
import com.maibu.core.enums.DeviceTaskStaus;
import com.maibu.core.enums.MesType;
import com.maibu.core.host.HeartBeatDTO;
import com.maibu.core.host.HostLocationRelTimeDTO;
import com.maibu.core.host.HostNavigationReplyDTO;
import com.maibu.core.host.HostTaskStatusDTO;
import com.maibu.core.host.HostVehicleRelTimeDTO;
import com.maibu.core.host.LocationMessage;
import com.maibu.core.host.*;
import com.maibu.core.redis.RedisCache;
import com.maibu.enums.DeviceStatusEnum;
import com.maibu.memory.DeviceSessionManager;
@@ -32,8 +21,12 @@ import com.maibu.memory.SiteMemory;
import com.maibu.utils.StringUtils;
import com.maibu.utils.json.JsonUtils;
import com.maibu.utils.spring.SpringUtils;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
/**
* mqtt相关的 上位机处理器
@@ -41,8 +34,6 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
public class HostMessageHandler implements CustomMqttMessageHandler {
private final ObjectMapper objectMapper = new ObjectMapper();
private final DeviceSessionManager deviceSessionManager = SpringUtils.getBean(DeviceSessionManager.class);
private final WebsocketMesDispather websocketMesDispather = SpringUtils.getBean(WebsocketMesDispather.class);
@@ -113,6 +104,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;
@@ -154,6 +148,15 @@ public class HostMessageHandler implements CustomMqttMessageHandler {
locationMessage.setDeviceId(deviceId);
redisCache.setCacheObject(key, locationMessage);
// todo 推送实时位置和状态消息到外部
try {
GlobalMemory.customMqttDeviceMonitor.publish(mqttClientId,
String.format(MqttTopic.DEVICE_LOCATION_MESSAGE_TOPIC, deviceId),
JsonUtils.toJsonString(locationRelTimeDTO));
} catch (MqttException e) {
log.error("位置实时数据推送失败: deviceId={}, error={}", deviceId, e.getMessage(), e);
}
}
} catch (Exception e) {
log.error("位置消息解析失败: deviceId={}, payload={}, error={}", deviceId, payload, e);
@@ -161,14 +164,20 @@ public class HostMessageHandler implements CustomMqttMessageHandler {
}
public WebStatusMessageDTO createWebDeviceLocationMessage(HostLocationRelTimeDTO locationRelTimeDTO,
String deviceId) {
String deviceId) {
WebStatusMessageDTO webStatusMessageDTO = new WebStatusMessageDTO();
List<DeviceStatusDetail> transferData = new ArrayList<>();
DeviceStatusDetail d = new DeviceStatusDetail();
d.setName("deviceId");
d.setValue(deviceId);
d.setUnit("");
transferData.add(d);
DeviceStatusDetail lat = new DeviceStatusDetail();
lat.setName("latitude");
lat.setValue(String.valueOf(locationRelTimeDTO.getData().getLatitude()));
lat.setUnit("");
transferData.add(lat);
DeviceStatusDetail lng = new DeviceStatusDetail();
@@ -227,15 +236,20 @@ public class HostMessageHandler implements CustomMqttMessageHandler {
DeviceMapPushDTO dto = new DeviceMapPushDTO();
dto.setA(Double.parseDouble(altitude.getValue()));
dto.setId(deviceId);
// dto.setB();
// dto.setHa();
// dto.setHa();
dto.setF(fix_status.getValue());
dto.setS(status.getValue());
dto.setLat(Double.parseDouble(lat.getValue()));
dto.setLng(Double.parseDouble(lng.getValue()));
dto.setT(nettyDevice.getCurrentTaskId());
dto.setY(Double.parseDouble(yaw.getValue()));
siteMemory.saveMaPushDTO(deviceId, dto);
siteMemory.getVehicleMessage(deviceId);
VehicleMessage vehicleMessage = siteMemory.getVehicleMessage(deviceId);
if (vehicleMessage != null) {
dto.setCm(vehicleMessage.getControl_mode());
dto.setB(vehicleMessage.getBattery_percentage());
}
siteMemory.saveMapPushDTO(deviceId, dto);
}
}
@@ -248,17 +262,82 @@ public class HostMessageHandler implements CustomMqttMessageHandler {
WebStatusMessageDTO webStatusMessageDTO = new WebStatusMessageDTO();
List<DeviceStatusDetail> transferData = new ArrayList<>();
DeviceStatusDetail voltage = new DeviceStatusDetail();
voltage.setName("voltage");
voltage.setValue(String.valueOf(vehicleRelTimeDTO.getData().getBattery_voltage()));
voltage.setName("battery_percentage");
voltage.setValue(String.valueOf(vehicleRelTimeDTO.getData().getBattery_percentage()));
voltage.setUnit("");
transferData.add(voltage);
DeviceStatusDetail speed = new DeviceStatusDetail();
speed.setName("speed");
speed.setValue(String.valueOf(vehicleRelTimeDTO.getData().getSpeed()));
speed.setUnit("");
transferData.add(speed);
DeviceStatusDetail battery_voltage = new DeviceStatusDetail();
battery_voltage.setName("battery_voltage");
battery_voltage.setValue(String.valueOf(vehicleRelTimeDTO.getData().getBattery_voltage()));
battery_voltage.setUnit("");
transferData.add(battery_voltage);
DeviceStatusDetail left_motor_current = new DeviceStatusDetail();
left_motor_current.setName("left_motor_current");
left_motor_current.setValue(String.valueOf(vehicleRelTimeDTO.getData().getLeft_motor_current()));
left_motor_current.setUnit("");
transferData.add(left_motor_current);
DeviceStatusDetail right_motor_current = new DeviceStatusDetail();
right_motor_current.setName("right_motor_current");
right_motor_current.setValue(String.valueOf(vehicleRelTimeDTO.getData().getRight_motor_current()));
right_motor_current.setUnit("");
transferData.add(right_motor_current);
DeviceStatusDetail left_motor_temperature = new DeviceStatusDetail();
left_motor_temperature.setName("left_motor_temperature");
left_motor_temperature.setValue(String.valueOf(vehicleRelTimeDTO.getData().getLeft_motor_temperature()));
left_motor_temperature.setUnit("");
transferData.add(left_motor_temperature);
DeviceStatusDetail right_motor_temperature = new DeviceStatusDetail();
right_motor_temperature.setName("right_motor_temperature");
right_motor_temperature.setValue(String.valueOf(vehicleRelTimeDTO.getData().getRight_motor_temperature()));
right_motor_temperature.setUnit("");
transferData.add(right_motor_temperature);
DeviceStatusDetail main_temperature = new DeviceStatusDetail();
main_temperature.setName("main_temperature");
main_temperature.setValue(String.valueOf(vehicleRelTimeDTO.getData().getMain_temperature()));
main_temperature.setUnit("");
transferData.add(main_temperature);
DeviceStatusDetail yaw = new DeviceStatusDetail();
yaw.setName("yaw");
yaw.setValue(String.valueOf(vehicleRelTimeDTO.getData().getYaw()));
yaw.setUnit("");
transferData.add(yaw);
DeviceStatusDetail pitch = new DeviceStatusDetail();
pitch.setName("pitch");
pitch.setValue(String.valueOf(vehicleRelTimeDTO.getData().getPitch()));
pitch.setUnit("");
transferData.add(pitch);
DeviceStatusDetail heading_status = new DeviceStatusDetail();
heading_status.setName("heading_status");
heading_status.setValue(String.valueOf(vehicleRelTimeDTO.getData().getHeading_status()));
heading_status.setUnit("");
transferData.add(heading_status);
DeviceStatusDetail total_work_area = new DeviceStatusDetail();
total_work_area.setName("total_work_area");
total_work_area.setValue(String.valueOf(vehicleRelTimeDTO.getData().getTotal_work_area()));
total_work_area.setUnit("");
transferData.add(total_work_area);
DeviceStatusDetail total_work_duration = new DeviceStatusDetail();
total_work_duration.setName("total_work_duration");
total_work_duration.setValue(String.valueOf(vehicleRelTimeDTO.getData().getTotal_work_duration()));
total_work_duration.setUnit("");
transferData.add(total_work_duration);
DeviceStatusDetail total_work_distance = new DeviceStatusDetail();
total_work_distance.setName("total_work_distance");
total_work_distance.setValue(String.valueOf(vehicleRelTimeDTO.getData().getTotal_work_distance()));
total_work_distance.setUnit("");
transferData.add(total_work_distance);
DeviceStatusDetail motion = new DeviceStatusDetail();
motion.setName("motion");
@@ -266,11 +345,71 @@ public class HostMessageHandler implements CustomMqttMessageHandler {
motion.setUnit("");
transferData.add(motion);
DeviceStatusDetail percentage = new DeviceStatusDetail();
percentage.setName("percentage");
percentage.setValue(String.valueOf(vehicleRelTimeDTO.getData().getBattery_percentage()));
percentage.setUnit("");
transferData.add(percentage);
DeviceStatusDetail emergency_stop = new DeviceStatusDetail();
emergency_stop.setName("emergency_stop");
emergency_stop.setValue(String.valueOf(vehicleRelTimeDTO.getData().getEmergency_stop()));
emergency_stop.setUnit("");
transferData.add(emergency_stop);
DeviceStatusDetail left_target_rpm = new DeviceStatusDetail();
left_target_rpm.setName("left_target_rpm");
left_target_rpm.setValue(String.valueOf(vehicleRelTimeDTO.getData().getLeft_target_rpm()));
left_target_rpm.setUnit("");
transferData.add(left_target_rpm);
DeviceStatusDetail right_target_rpm = new DeviceStatusDetail();
right_target_rpm.setName("right_target_rpm");
right_target_rpm.setValue(String.valueOf(vehicleRelTimeDTO.getData().getRight_target_rpm()));
right_target_rpm.setUnit("");
transferData.add(right_target_rpm);
DeviceStatusDetail left_measured_rpm = new DeviceStatusDetail();
left_measured_rpm.setName("left_measured_rpm");
left_measured_rpm.setValue(String.valueOf(vehicleRelTimeDTO.getData().getLeft_measured_rpm()));
left_measured_rpm.setUnit("");
transferData.add(left_measured_rpm);
DeviceStatusDetail right_measured_rpm = new DeviceStatusDetail();
right_measured_rpm.setName("right_measured_rpm");
right_measured_rpm.setValue(String.valueOf(vehicleRelTimeDTO.getData().getRight_measured_rpm()));
right_measured_rpm.setUnit("");
transferData.add(right_measured_rpm);
DeviceStatusDetail left_motor_rpm = new DeviceStatusDetail();
left_motor_rpm.setName("left_motor_rpm");
left_motor_rpm.setValue(String.valueOf(vehicleRelTimeDTO.getData().getLeft_motor_rpm()));
left_motor_rpm.setUnit("");
transferData.add(left_motor_rpm);
DeviceStatusDetail right_motor_rpm = new DeviceStatusDetail();
right_motor_rpm.setName("right_motor_rpm");
right_motor_rpm.setValue(String.valueOf(vehicleRelTimeDTO.getData().getRight_motor_rpm()));
right_motor_rpm.setUnit("");
transferData.add(right_motor_rpm);
DeviceStatusDetail roll = new DeviceStatusDetail();
roll.setName("roll");
roll.setValue(String.valueOf(vehicleRelTimeDTO.getData().getRoll()));
roll.setUnit("");
transferData.add(roll);
DeviceStatusDetail control_mode = new DeviceStatusDetail();
control_mode.setName("control_mode");
control_mode.setValue(String.valueOf(vehicleRelTimeDTO.getData().getControl_mode()));
control_mode.setUnit("");
transferData.add(control_mode);
DeviceStatusDetail linear_speed = new DeviceStatusDetail();
linear_speed.setName("linear_speed");
linear_speed.setValue(String.valueOf(vehicleRelTimeDTO.getData().getLinear_speed()));
linear_speed.setUnit("");
transferData.add(linear_speed);
DeviceStatusDetail angular_speed = new DeviceStatusDetail();
angular_speed.setName("angular_speed");
angular_speed.setValue(String.valueOf(vehicleRelTimeDTO.getData().getAngular_speed()));
angular_speed.setUnit("");
transferData.add(angular_speed);
webStatusMessageDTO.setData(transferData);
webStatusMessageDTO.setType(MesType.deviceInfo);
@@ -287,7 +426,7 @@ public class HostMessageHandler implements CustomMqttMessageHandler {
if (vehicleRelTimeDTO != null) {
// todo 更新里程数 里程数 * 割幅0.8
vehicleRelTimeDTO.getData().getDistance();
vehicleRelTimeDTO.getData().getTotal_work_distance();
NettyDevice nettyDevice = deviceSessionManager.getDevice(deviceId);
if (nettyDevice != null) {
Long orgId = nettyDevice.getDevice().getOrgId();
@@ -299,9 +438,11 @@ public class HostMessageHandler implements CustomMqttMessageHandler {
deviceRunStatistics = new DeviceRunStatistics();
}
deviceRunStatistics.setDeviceId(deviceId);
deviceRunStatistics.setDistance(vehicleRelTimeDTO.getData().getDistance());
deviceRunStatistics.setWorkArea(vehicleRelTimeDTO.getData().getDistance() * 0.8);
deviceRunStatistics.setDistance(vehicleRelTimeDTO.getData().getTotal_work_distance());
deviceRunStatistics.setWorkArea(vehicleRelTimeDTO.getData().getTotal_work_distance() * 0.8);
siteMemory.saveRunStatistics(deviceRunStatistics);
siteMemory.saveVehicleMessage(deviceId, vehicleRelTimeDTO.getData());
}
}
@@ -318,6 +459,14 @@ public class HostMessageHandler implements CustomMqttMessageHandler {
}
}
}
// todo 推送实时位置和状态消息到外部
try {
GlobalMemory.customMqttDeviceMonitor.publish(mqttClientId,
String.format(MqttTopic.DEVICE_VEHICLE_MESSAGE_TOPIC, deviceId),
JsonUtils.toJsonString(vehicleRelTimeDTO));
} catch (MqttException e) {
log.error("位置实时数据推送失败: deviceId={}, error={}", deviceId, e.getMessage(), e);
}
}
} catch (Exception e) {
log.error("实时消息解析失败: deviceId={}, payload={}, error={}", deviceId, payload, e.getMessage());
@@ -371,11 +520,19 @@ public class HostMessageHandler implements CustomMqttMessageHandler {
try {
websocketMesDispather.dispather(x.getConnectorId(), JsonUtils.toJsonString(sendDto));
} catch (Exception e) {
log.error("实时数据websocket推送失败: connectorId={}, error={}", x.getConnectorId(),
log.error("实时数据websocket任务状态推送失败: connectorId={}, error={}", x.getConnectorId(),
e.getMessage());
}
}
}
// todo 推送实时位置和状态消息到外部
try {
GlobalMemory.customMqttDeviceMonitor.publish(mqttClientId,
String.format(MqttTopic.DEVICE_TASK_STATUS_TOPIC, taskId),
JsonUtils.toJsonString(sendDto));
} catch (MqttException e) {
log.error("实时数据websocket任务状态推送失败: deviceId={}, error={}", deviceId, e.getMessage(), e);
}
}
if ("cancelled".equals(dto.getStatus())) {
@@ -388,7 +545,7 @@ public class HostMessageHandler implements CustomMqttMessageHandler {
if ("failed".equals(dto.getStatus())) {
nettyDevice.failedTask();
}
// if("accepted".equals(dto.getStatus())){
// if ("accepted".equals(dto.getStatus())) {
// NettyDevice nettyDevice = deviceSessionManager.getDevice(deviceId);
// nettyDevice.finishTask();
// }
@@ -427,10 +584,56 @@ public class HostMessageHandler implements CustomMqttMessageHandler {
if (device != null && device.getTask() != null
&& String.valueOf(device.getTask().getId()).equals(replyDTO.getTask_id())) {
device.getTask().setTaskAccepted(true);
WebDeviceTaskStatusMessageDTO sendDto = new WebDeviceTaskStatusMessageDTO();
sendDto.setDeviceId(deviceId);
sendDto.setTaskId(device.getTask().getId());
sendDto.setStatus(DeviceTaskStaus.EXECUTING.toString());
sendDto.setType(MesType.device_task_change);
List<NettyDevice> controlMasters = deviceSessionManager
.getAllSlaveControl(deviceId);
if (!CollectionUtils.isEmpty(controlMasters)) {
for (NettyDevice x : controlMasters) {
try {
websocketMesDispather.dispather(x.getConnectorId(), JsonUtils.toJsonString(sendDto));
} catch (Exception e) {
log.error("实时数据websocket推送失败: connectorId={}, error={}", x.getConnectorId(),
e.getMessage(), e);
}
}
}
// todo 推送实时位置和状态消息到外部
try {
GlobalMemory.customMqttDeviceMonitor.publish(mqttClientId,
String.format(MqttTopic.DEVICE_TASK_STATUS_TOPIC, device.getTask().getId()),
JsonUtils.toJsonString(sendDto));
} catch (MqttException e) {
log.error("实时数据websocket推送失败: deviceId={}, error={}", deviceId, e.getMessage(), e);
}
}
}
}
/**
* 处理控制指令响应
*/
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}/...
@@ -481,6 +684,8 @@ 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;
}

View File

@@ -2,18 +2,22 @@ package com.maibu.mqtt;
public class MqttTopic {
/** @deprecated 通用设备主题,建议使用 MOWER_* 系列主题 */
/** 通用设备主题,建议使用 MOWER_* 系列主题 */
public static final String DEVICE_STATUS_TOPIC = "device/%s/realTimeMessage";
public static final String DEVICE_LOCATION_TOPIC = "device/%s/locationMessage";
/** @deprecated 通用设备主题,建议使用 MOWER_* 系列主题 */
public static final String DEVICE_VEHICLE_MESSAGE_TOPIC = "device/%s/host/realTime/message";
public static final String DEVICE_LOCATION_MESSAGE_TOPIC = "device/%s/host/location/message";
/** 通用设备主题,建议使用 MOWER_* 系列主题 */
public static final String DEVICE_TASK_STATUS_TOPIC = "task/%s/status";
/** @deprecated 通用设备主题,建议使用 MOWER_* 系列主题 */
/** 通用设备主题,建议使用 MOWER_* 系列主题 */
public static final String DEVICE_TASK_ARRIVE_TOPIC = "task/%s/arrive";
/** @deprecated 通用设备主题,建议使用 MOWER_* 系列主题 */
/** 通用设备主题,建议使用 MOWER_* 系列主题 */
public static final String DEVICE_ERROR_PUSH_TOPIC = "device/%s/error";
public static final String DEVICE_ROUTE_PUSH_TOPIC = "device/%s/route/post";
public static final String DEVICE_STATUS_PUSH_TOPIC = "device/%s/status/change";
/** 产品标识:割草机 */
public static final String MOWER_PRODUCT = "mower";
@@ -43,7 +47,7 @@ public class MqttTopic {
// ==================== 远程控制 ====================
/** 远程控制指令下发:前进/后退/转向/割草等(平台->设备),对应 TCP 0x00 remoteControl */
public static final String MOWER_CONTROL_SET = "mower/%s/action/control/set";
public static final String MOWER_CONTROL_SET = "mower/%s/control/remote/set";
// ==================== 任务管理 ====================
/** 路径规划下发(平台->设备),对应 TCP 0x01 path */
@@ -64,15 +68,15 @@ 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";
/** 寻车指令(平台->设备) */
public static final String MOWER_SEARCH_DEVICE_SET = "mower/%s/action/search/set";
// ==================== 设备信息 ====================
// /** 查询设备信息(平台->设备) */

View File

@@ -1,9 +1,6 @@
package com.maibu.utils;
import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.*;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@@ -44,6 +41,15 @@ public class MinioUtil {
// 拼接 public URL
return endpoint + "/" + defaultBucket + "/" + objectName;
}
public void deleteFile(String objectName) throws Exception {
minioClient.removeObject(
RemoveObjectArgs.builder()
.bucket(defaultBucket)
.object(objectName)
.build()
);
}
}

View File

@@ -116,7 +116,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
// 过滤请求
.authorizeRequests()
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
.antMatchers("/iot/host/upload-path-csv","/api/thermal/**","/iotDA/device/**", "/websocket/allMemory", "/iot/UAV/**", "/iot/api/**", "/iot/device/userDevice", "/external/sms/sendAliSmsCode", "/login", "/register", "/captchaImage", "/iot/tool/register", "/iot/tool/ntp",
.antMatchers("/system/param/list","/iot/host/upload-path-csv","/api/thermal/**","/iotDA/device/**", "/websocket/allMemory", "/iot/UAV/**", "/iot/api/**", "/iot/device/userDevice", "/external/sms/sendAliSmsCode", "/login", "/register", "/captchaImage", "/iot/tool/register", "/iot/tool/ntp",
"/iot/tool/mqtt/auth", "/iot/workRecord/**", "/iot/tool/mqtt/authv5", "/iot/tool/mqtt/webhook", "/iot/tool/mqtt/webhookv5", "/auth/**/**",
"/wechat/mobileLogin", "/wechat/miniLogin", "/wechat/wxBind/callback").permitAll()
.antMatchers("/zlmhook/**").permitAll()

View File

@@ -44,5 +44,4 @@ public class NettyDeviceController extends BaseController {
nettyDeviceService.sendCommand(command);
return AjaxResult.success();
}
}

View File

@@ -1,34 +1,64 @@
package com.maibu.init;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import com.maibu.core.business.*;
import com.maibu.mapper.*;
import com.maibu.service.impl.IotDeviceCommonService;
import org.checkerframework.checker.units.qual.s;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.maibu.constant.FastBeeConstant;
import com.maibu.core.business.AlarmOrderConfig;
import com.maibu.core.business.Device;
import com.maibu.core.business.DeviceModel;
import com.maibu.core.business.DevicePlan;
import com.maibu.core.business.DevicePlanTask;
import com.maibu.core.business.DeviceRunParam;
import com.maibu.core.business.ErrorIdentificationStandard;
import com.maibu.core.business.IoTCommonDevice;
import com.maibu.core.business.OrderModel;
import com.maibu.core.business.Product;
import com.maibu.core.domain.entity.SysParam;
import com.maibu.core.domain.entity.SysSite;
import com.maibu.core.enums.DeviceTaskStaus;
import com.maibu.influxdb.util.InfluxDBUtil;
import com.maibu.mapper.AlarmOrderConfigMapper;
import com.maibu.mapper.DevicePlanMapper;
import com.maibu.mapper.DevicePlanTaskMapper;
import com.maibu.mapper.DeviceProductMapper;
import com.maibu.mapper.DeviceRunParamMapper;
import com.maibu.mapper.ErrorIdentificationStandardMapper;
import com.maibu.mapper.IotDeviceCommonMapper;
import com.maibu.mapper.OrderModelMapper;
import com.maibu.mapper.SysDeviceModelMapper;
import com.maibu.mapper.SysParamMapper;
import com.maibu.mapper.SysSiteMapper;
import com.maibu.memory.GlobalMemory;
import com.maibu.memory.SiteMemory;
import com.maibu.mqtt.MqttClientUtil;
import com.maibu.mybatis.LambdaQueryWrapperX;
import com.maibu.service.DevicePlanTaskMonitorService;
import com.maibu.service.DeviceThreadService;
import org.springframework.beans.factory.annotation.Value;
import com.maibu.service.impl.IotDeviceCommonService;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@@ -81,6 +111,7 @@ public class InitThread implements ApplicationRunner {
@Autowired
private ErrorIdentificationStandardMapper errorIdentificationStandardMapper;
@Autowired
private OrderModelMapper orderModelMapper;
@Autowired
private AlarmOrderConfigMapper alarmOrderConfigMapper;
@@ -88,6 +119,12 @@ public class InitThread implements ApplicationRunner {
@Autowired
private IotDeviceCommonService commonService;
@Autowired
private ResourceLoader resourceLoader;
@Autowired
private SysParamMapper sysParamMapper;
@Value("${influxdb.url}")
private String influxDBUrl;
@@ -119,12 +156,15 @@ public class InitThread implements ApplicationRunner {
private String mqttClientId;
@Override
public void run(ApplicationArguments args) throws MqttException {
public void run(ApplicationArguments args) throws Exception {
recoverMemory();
init();
}
private void init() throws MqttException {
private void init() throws Exception {
//初始化系统参数
initSystemParam();
// 初始化InfluxDBUtil
GlobalMemory.influxDBUtil = new InfluxDBUtil(influxDBUrl, influxDBToken, influxDBOrg, influxDBBucket);
@@ -185,7 +225,7 @@ public class InitThread implements ApplicationRunner {
});
}
//外部设备
// 外部设备
List<IoTCommonDevice> ioTCommonDeviceList = iotDeviceCommonMapper.selectList();
if (!CollectionUtils.isEmpty(ioTCommonDeviceList)) {
ioTCommonDeviceList.forEach(x -> {
@@ -197,7 +237,7 @@ public class InitThread implements ApplicationRunner {
}
});
}
//产品
// 产品
List<Product> productList = deviceProductMapper.selectList();
if (!CollectionUtils.isEmpty(productList)) {
productList.forEach(x -> {
@@ -209,7 +249,7 @@ public class InitThread implements ApplicationRunner {
}
});
}
//设备模型
// 设备模型
List<DeviceModel> deviceModelList = deviceModelMapper.selectList();
if (!CollectionUtils.isEmpty(deviceModelList)) {
deviceModelList.forEach(x -> {
@@ -221,7 +261,7 @@ public class InitThread implements ApplicationRunner {
}
});
}
//车辆参数
// 车辆参数
List<DeviceRunParam> deviceRunParamList = deviceRunParamMapper.selectList();
if (!CollectionUtils.isEmpty(deviceRunParamList)) {
deviceRunParamList.forEach(x -> {
@@ -233,7 +273,7 @@ public class InitThread implements ApplicationRunner {
}
});
}
//错误标准
// 错误标准
List<ErrorIdentificationStandard> standadList = errorIdentificationStandardMapper.selectList();
if (!CollectionUtils.isEmpty(standadList)) {
standadList.forEach(x -> {
@@ -245,7 +285,7 @@ public class InitThread implements ApplicationRunner {
}
});
}
//订单模型
// 订单模型
List<OrderModel> orderModelList = orderModelMapper.selectList();
if (!CollectionUtils.isEmpty(orderModelList)) {
orderModelList.forEach(x -> {
@@ -257,7 +297,7 @@ public class InitThread implements ApplicationRunner {
}
});
}
//告警订单配置
// 告警订单配置
List<AlarmOrderConfig> alarmOrderList = alarmOrderConfigMapper.selectList();
if (!CollectionUtils.isEmpty(alarmOrderList)) {
alarmOrderList.forEach(x -> {
@@ -317,4 +357,38 @@ public class InitThread implements ApplicationRunner {
}
}
public void initSystemParam() throws IOException {
LambdaQueryWrapper<SysParam> queryWrapper = new LambdaQueryWrapper<>();
List<SysParam> list = sysParamMapper.selectList(queryWrapper);
try {
// 读取resource目录下的dsystemParam.json文件
org.springframework.core.io.Resource resource = resourceLoader
.getResource("classpath:systemParam.json");
// 使用fastjson2转换为DeviceConfig对象
String jsonContent;
jsonContent = IoUtil.readUtf8(resource.getInputStream());
List<SysParam> params = JSON.parseArray(jsonContent, SysParam.class);
List<SysParam> saveList = new ArrayList<>();
if (CollectionUtils.isEmpty(list)) {
saveList.addAll(params);
} else {
Map<String, SysParam> map = list.stream()
.collect(Collectors.toMap(
SysParam::getParamKey,
sysParam -> sysParam));
if (!CollectionUtils.isEmpty(params)) {
params.forEach(x -> {
if (!map.containsKey(x.getParamKey())) {
saveList.add(x);
}
});
}
}
sysParamMapper.saveOrUpdateBatch(saveList);
} catch (Exception e) {
log.error("初始化系统参数失败", e);
}
}
}

View File

@@ -1,11 +1,15 @@
package com.maibu.netty.handler;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Set;
import com.maibu.memory.DeviceSessionManager;
import com.maibu.memory.GlobalMemory;
import com.maibu.mqtt.MqttTopic;
import com.maibu.utils.json.JsonUtils;
import org.apache.commons.codec.binary.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -15,9 +19,13 @@ import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import com.maibu.constant.CommandConstant;
import com.maibu.constant.Constant;
import com.maibu.core.business.LawnMoverProtocolEntity;
import com.maibu.core.business.device.Connector;
import com.maibu.core.business.device.NettyDevice;
import com.maibu.core.enums.ConnectorType;
import com.maibu.core.host.DeviceRemoteControlDTO;
import com.maibu.core.host.ParamDetail;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
@@ -29,7 +37,6 @@ import io.netty.channel.SimpleChannelInboundHandler;
@ChannelHandler.Sharable
public class CommandForwardHandler extends SimpleChannelInboundHandler<String> {
private static final Logger logger = LoggerFactory.getLogger(CommandForwardHandler.class);
@Autowired
@@ -44,12 +51,11 @@ public class CommandForwardHandler extends SimpleChannelInboundHandler<String> {
byte[] bytes = Hex.decodeHex(msg.toCharArray());
if (bytes.length > 3 && (bytes[2] == CommandConstant.remoteControl
|| bytes[2] == CommandConstant.path
|| bytes[2] == CommandConstant.status
)) {
|| bytes[2] == CommandConstant.status)) {
String senderDeviceId = deviceSessionManager.getDeviceIdByChannel(currentChannel);
NettyDevice senderNettyDevice =
senderDeviceId != null ? deviceSessionManager.getDevice(senderDeviceId) : null;
NettyDevice senderNettyDevice = senderDeviceId != null ? deviceSessionManager.getDevice(senderDeviceId)
: null;
if (senderNettyDevice == null) {
logger.debug("发送拒绝,设备:{}未发送上线指令", senderDeviceId);
@@ -76,22 +82,73 @@ public class CommandForwardHandler extends SimpleChannelInboundHandler<String> {
return;
}
for (Connector target : connectedDevices) {
if (!target.isOnline()) {
logger.debug("发送拒绝master:{},消息到达的salve设备:{},已离线", senderDeviceId, target.getConnectorId());
continue;
}
if (!target.canReceive()) {
if (target == null || !target.canReceive()) {
logger.debug("发送拒绝master:{},消息到达的slave设备:{},处于禁止接收状态", senderDeviceId, target.getConnectorId());
continue;
}
// todo 向上位机发送控制消息
NettyDevice targetDevice = deviceSessionManager.getDevice(target.getConnectorId());
if (targetDevice == null)
continue;
// 区分有上位机的还有无上位机的 有上位机的通过上位机下发 没有上位机的按照原来的方式下发
if (targetDevice.getDevice().getHasHost()) {
if (bytes[2] == CommandConstant.remoteControl) {
// todo 转换 原先的报文重新组装成上位机的报文格式
DeviceRemoteControlDTO deviceRemoteControlDTO = new DeviceRemoteControlDTO();
LawnMoverProtocolEntity pEntity = LawnMoverProtocolEntity.parseControlCommand(bytes);
if (pEntity != null) {
GlobalMemory.customMqttDeviceMonitor.publish(mqttClientId, String.format(MqttTopic.MOWER_CONTROL_SET, target.getConnectorId()), msg);
// Channel targetChannel = target.getChannel();
// if (targetChannel != null && targetChannel.isActive()) {
// targetChannel.writeAndFlush(msg);
// logger.debug("从master:{},向slave:{}, 转发消息:{}", senderDeviceId, target.getConnectorId(), msg);
// }
ParamDetail data = new ParamDetail();
// data.setLeft_wheel_rpm(pEntity.getLeftWheelSpeed());
// data.setRight_wheel_rpm(pEntity.getRightWheelSpeed());
double angularSpeed = BigDecimal.valueOf(pEntity.getAngularSpeed())
.setScale(2, RoundingMode.HALF_UP)
.doubleValue();
double linearSpeed = BigDecimal.valueOf(pEntity.getLinearSpeed())
.setScale(2, RoundingMode.HALF_UP)
.doubleValue();
data.setLinear_x(linearSpeed);
data.setAngular_z(angularSpeed);
data.setChassis_lift(pEntity.getChassisLiftFlag());
data.setCutter(pEntity.getMowerSpeedFlag());
data.setIgnition(pEntity.getIgnitionFlag());
data.setEmergency_stop(pEntity.getEmergencyStopFlag());
data.setValid_for_ms(500);
deviceRemoteControlDTO.setData(data);
deviceRemoteControlDTO
.setCommand_id(Constant.remote_control_cmd_prefix + System.currentTimeMillis());
deviceRemoteControlDTO.setSn(target.getConnectorId());
deviceRemoteControlDTO.setTimestamp(System.currentTimeMillis());
deviceRemoteControlDTO
.setMessage_id(Constant.remote_control_msg_prefix + System.currentTimeMillis());
deviceRemoteControlDTO.setProtocol_version(Constant.protocol_version);
GlobalMemory.customMqttDeviceMonitor.publish(mqttClientId,
String.format(MqttTopic.MOWER_CONTROL_SET, target.getConnectorId()),
JsonUtils.toJsonString(deviceRemoteControlDTO));
logger.debug("master:{},向slave:{} 有上位机, 转发遥控指令:{}", senderDeviceId,
target.getConnectorId(),
JsonUtils.toJsonString(deviceRemoteControlDTO));
}
}
} else {
if (!target.isOnline()) {
logger.debug("发送拒绝master:{},消息到达的salve设备:{},已离线", senderDeviceId,
target.getConnectorId());
continue;
}
Channel targetChannel = target.getChannel();
if (targetChannel != null && targetChannel.isActive()) {
targetChannel.writeAndFlush(msg);
logger.debug("从master:{},向slave:{} 无上位机, 转发消息:{}", senderDeviceId,
target.getConnectorId(), msg);
}
}
}
} else if (ConnectorType.SLAVE.equals(senderNettyDevice.getDeviceType())) {
List<NettyDevice> masters = deviceSessionManager.getMastersBySlaveId(senderDeviceId);

View File

@@ -140,13 +140,13 @@ public class DataToDataBaseHandler extends SimpleChannelInboundHandler<String> {
nettyDevice.setLatestStatus(latestStatus);
}
} else if (bytes[2] == CommandConstant.readConfig) {
DeviceRunParam runParam = DeviceRunParam.fromBytes(bytes);
logger.debug("收到返回的设备运行参数: {}", JsonUtils.toJsonString(runParam));
CompletableFuture<DeviceRunParam> future = ctx.channel().attr(Constant.READ_CONFIG_KEY).get();
if (future != null) {
future.complete(runParam);
ctx.channel().attr(Constant.READ_CONFIG_KEY).set(null);
}
// DeviceRunParam runParam = DeviceRunParam.fromBytes(bytes);
// logger.debug("收到返回的设备运行参数: {}", JsonUtils.toJsonString(runParam));
// CompletableFuture<DeviceRunParam> future = ctx.channel().attr(Constant.READ_CONFIG_KEY).get();
// if (future != null) {
// future.complete(runParam);
// ctx.channel().attr(Constant.READ_CONFIG_KEY).set(null);
// }
}
ctx.fireChannelRead(msg);
} catch (Exception e) {

View File

@@ -220,7 +220,7 @@ public class DeviceConnectHandler extends SimpleChannelInboundHandler<String> {
device.setDeviceName(data);
device.setProductId(-1L);
device.setTenantId(-1L);
device.setFirmwareVersion(BigDecimal.valueOf(1.0));
device.setFirmwareVersion("1.0");
device.setProductName("割草机产品MC700");
device.setCreateTime(LocalDateTime.now());
device.setUpdateTime(LocalDateTime.now());

View File

@@ -75,20 +75,20 @@ public class ScheduledTask {
System.out.println("0点定时刷新状态执行完成");
}
public static void main(String[] args) throws DecoderException {
String hex = "AB AA 05 88 4D 43 37 30 30 50 4C 55 53 2D 43 4E 2D 4A 53 2D 31 37 38 34 38 37 31 39 37 39 31 39 30 2D 30 30 30 30 30 30 30 41 2D 36 30 00 B1 FB 7F FE FE FE 00 00 00 00 00 00 00 00 00 00 00 00 00 12 01 48 95 E8 03 E8 03 28 32 91 53 24 4D 43 37 30 30 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 31 32 33 34 35 36 37 38 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 CD CC A4 41 00 00 80 3F 33 33 33 3F B8 1E 05 3F 33 33 33 3F 9A 99 19 3E 00 00 80 3F 00 00 80 3F 00 00 80 3F 32 36 2D 34 2D 32 37 00 2D 32 37 00 50 B1 BF 00 00 AA AB";
String cleanHex = hex.replaceAll("\\s+", ""); // 移除所有空白字符
byte[] bytes = Hex.decodeHex(cleanHex);
DeviceRunParam runParam = DeviceRunParam.fromBytes(bytes);
System.out.println(JsonUtils.toJsonString(runParam));
// public static void main(String[] args) throws DecoderException {
// String hex = "AB AA 05 88 4D 43 37 30 30 50 4C 55 53 2D 43 4E 2D 4A 53 2D 31 37 38 34 38 37 31 39 37 39 31 39 30 2D 30 30 30 30 30 30 30 41 2D 36 30 00 B1 FB 7F FE FE FE 00 00 00 00 00 00 00 00 00 00 00 00 00 12 01 48 95 E8 03 E8 03 28 32 91 53 24 4D 43 37 30 30 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 31 32 33 34 35 36 37 38 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 CD CC A4 41 00 00 80 3F 33 33 33 3F B8 1E 05 3F 33 33 33 3F 9A 99 19 3E 00 00 80 3F 00 00 80 3F 00 00 80 3F 32 36 2D 34 2D 32 37 00 2D 32 37 00 50 B1 BF 00 00 AA AB";
// String cleanHex = hex.replaceAll("\\s+", ""); // 移除所有空白字符
// byte[] bytes = Hex.decodeHex(cleanHex);
// DeviceRunParam runParam = DeviceRunParam.fromBytes(bytes);
// System.out.println(JsonUtils.toJsonString(runParam));
runParam.setLeftBackwardGain(0.2f);
runParam.setLeftForwardGain(0.2f);
runParam.setRightForwardGain(0.2f);
runParam.setRightBackwardGain(0.2f);
// runParam.setLeftBackwardGain(0.2f);
// runParam.setLeftForwardGain(0.2f);
// runParam.setRightForwardGain(0.2f);
// runParam.setRightBackwardGain(0.2f);
byte[] newB = runParam.toWriteBytes();
System.out.println(Hex.encodeHexString(newB));
// byte[] newB = runParam.toWriteBytes();
// System.out.println(Hex.encodeHexString(newB));
}
// }
}

View File

@@ -453,9 +453,9 @@ public class DeviceTaskService {
x.setTaskStausTranslate(taskStaus.getDescription());
});
list.sort(
Comparator.comparing(DevicePlanTask::getFinishTime,
Comparator.comparing(DevicePlanTask::getCreateTime,
Comparator.nullsLast(Comparator.reverseOrder()))
.thenComparing(DevicePlanTask::getCreateTime,
.thenComparing(DevicePlanTask::getFinishTime,
Comparator.nullsLast(Comparator.reverseOrder())));
return list;
}

View File

@@ -16,6 +16,7 @@ import java.util.stream.Collectors;
import com.maibu.core.business.dto.DeviceMapPushDTO;
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 org.apache.commons.lang3.StringUtils;
@@ -43,6 +44,7 @@ import com.maibu.core.host.HeartBeatDTO;
import com.maibu.core.host.LocationMessage;
import com.maibu.core.redis.RedisCache;
import com.maibu.dto.DeviceErrorPushDTO;
import com.maibu.dto.DeviceTaskStatusMessageDTO;
import com.maibu.mapper.DeviceRunStatisticsMapper;
import com.maibu.mapper.ErrorIdentificationStandardMapper;
import com.maibu.mapper.HostLocationMapper;
@@ -223,7 +225,7 @@ public class DeviceThreadService {
}
public List<AlarmMessage> compareErrorStandard(Long siteId, Long orgId, DeviceRunningStatusHistory history,
List<ErrorIdentificationStandard> standards) {
List<ErrorIdentificationStandard> standards) {
if (history == null || CollectionUtils.isEmpty(standards))
return null;
List<AlarmMessage> list = new ArrayList<>();
@@ -297,7 +299,7 @@ public class DeviceThreadService {
}
if (nowTime - lastActiveTime >= Constant.hostOfflineInterval) {
// 超时了判断离线
if (nettyDevice != null) {
if (nettyDevice != null && nettyDevice.getOnlineStatus() == 1) {
nettyDevice.status(DeviceStatus.offline.getType());
// todo 推送设备离线
String key = NettyCacheKey.latestPositonKey + deviceId;
@@ -309,7 +311,10 @@ public class DeviceThreadService {
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);
@@ -343,8 +348,12 @@ public class DeviceThreadService {
}
deviceRunStatisticsMapper.saveOrUpdate(deviceRunStatistics);
}
// todo 删除实时消息推送里面的数据
siteMemory.removeMapPushDTO(deviceId);
siteMemory.removeVehicleMessage(deviceId);
}
statusChangePush(deviceId, siteId, 0);
sessionManager.removeDevice(deviceId);
// TODO 统计总里程、总作业面积
@@ -359,7 +368,7 @@ public class DeviceThreadService {
device.setDeviceName(deviceId);
device.setProductId(-1L);
device.setTenantId(-1L);
device.setFirmwareVersion(BigDecimal.valueOf(1.0));
device.setFirmwareVersion("1.0");
device.setProductName("割草机产品MC700");
device.setCreateTime(LocalDateTime.now());
device.setUpdateTime(LocalDateTime.now());
@@ -378,11 +387,14 @@ public class DeviceThreadService {
if (slaveDevice != null) {
slaveDevice.setOnlineStatus(1);
slaveDevice.setDevice(device);
statusChangePush(deviceId, device.getSiteId(), 1);
}
} else {
// todo 推送设备上线
nettyDevice.status(1);
nettyDevice.setDevice(device);
nettyDevice.setConnectorStatus(ConnectorStatus.ACTIVE);
statusChangePush(deviceId, device.getSiteId(), 1);
}
}
});
@@ -393,9 +405,30 @@ public class DeviceThreadService {
}, 0, 500, TimeUnit.MILLISECONDS);
}
public void statusChangePush(String deviceId, Long siteId, Integer status) {
// todo 推送设备离线
WebOlineStatusMessageDTO dto = new WebOlineStatusMessageDTO();
dto.setDeviceId(deviceId);
dto.setType(MesType.device_status_changed);
dto.setOnlineStatus(status);
try {
// 当前控制的web端
websocketMesDispather.dispather(deviceId, JsonUtils.toJsonString(dto));
// 地图监控页面推送
websocketMesDispather.mapMessageDispather(siteId,
JsonUtils.toJsonString(dto));
// 推送到mqtt服务器
GlobalMemory.customMqttDeviceMonitor.publish(mqttClientId,
String.format(MqttTopic.DEVICE_STATUS_PUSH_TOPIC, deviceId), JsonUtils.toJsonString(dto));
} catch (Exception e) {
log.error("机器状态变化推送失败: siteId={}, error={}", siteId,
e.getMessage(), e);
}
}
/**
* 监控心跳
* 地图监控
*/
public void mapMessagePush() {
executor.scheduleWithFixedDelay(() -> {
@@ -404,18 +437,16 @@ public class DeviceThreadService {
if (!CollectionUtils.isEmpty(list)) {
list.forEach(x -> {
Map<String, DeviceMapPushDTO> map = x.getAllDeviceMapPush();
if (!CollectionUtils.isEmpty(map)) {
List<DeviceMapPushDTO> pushList = new ArrayList<>(map.values());
WebMapMessageDTO dto = new WebMapMessageDTO();
dto.setData(pushList);
dto.setType(MesType.mapPush);
try {
websocketMesDispather.mapMessageDispather(x.getSiteId(), JsonUtils.toJsonString(pushList));
} catch (Exception e) {
log.error("实时地图数据websocket推送失败: siteId={}, error={}", x.getSiteId(),
e.getMessage(), e);
}
List<DeviceMapPushDTO> pushList = new ArrayList<>(map.values());
WebMapMessageDTO dto = new WebMapMessageDTO();
dto.setData(pushList);
dto.setType(MesType.mapPush);
try {
websocketMesDispather.mapMessageDispather(x.getSiteId(),
JsonUtils.toJsonString(dto));
} catch (Exception e) {
log.error("实时地图数据websocket推送失败: siteId={}, error={}", x.getSiteId(),
e.getMessage(), e);
}
});
}

View File

@@ -43,12 +43,11 @@ public class TransferDeviceService {
@Autowired
private DeviceMapper deviceMapper;
public AjaxResult bind(DeviceBindDTO bindDTO, LoginUser user) {
try {
String deviceId = bindDTO.getDeviceId();
String deviceAlias = bindDTO.getDeviceAlias();
//判断当前设备是否存
// 判断当前设备是否存
Device device = iDeviceService.selectDeviceBySerialNumber(deviceId);
if (device == null) {
return AjaxResult.error("设备不存在");
@@ -65,7 +64,7 @@ public class TransferDeviceService {
device.setDeviceAlias(deviceAlias);
device.setActiveTime(new Date());
iDeviceService.updateDeviceBySelf(device);
// deviceMapper.updateById(device);
// deviceMapper.updateById(device);
return AjaxResult.success(true);
}
} catch (Exception e) {
@@ -77,7 +76,7 @@ public class TransferDeviceService {
public AjaxResult unbind(DeviceBindDTO bindDTO, LoginUser user) {
try {
String deviceId = bindDTO.getDeviceId();
//判断当前设备是否存
// 判断当前设备是否存
Device device = iDeviceService.selectDeviceBySerialNumber(deviceId);
if (device == null) {
return AjaxResult.error("设备不存在");
@@ -93,9 +92,9 @@ public class TransferDeviceService {
device.setTenantName("");
device.setUpdateTime(LocalDateTime.now());
device.setDeviceAlias("");
// deviceMapper.updateById(device);
// deviceMapper.updateById(device);
iDeviceService.updateDeviceBySelf(device);
//判断是否正在控制 如果正在控制,删除控制状态
// 判断是否正在控制 如果正在控制,删除控制状态
LambdaQueryWrapper<UserClient> lambdaWrapper = new LambdaQueryWrapper<>();
lambdaWrapper.eq(UserClient::getDeviceName, deviceId);
List<UserClient> users = userClientMapper.selectList(lambdaWrapper);
@@ -108,12 +107,15 @@ public class TransferDeviceService {
NettyDevice deviceMaster = sessionManager.getDevice(masterId);
if (deviceMaster != null) {
String oldSlave = userClient.getDeviceName();
//删除主的连接关系
deviceMaster.getConnectedConnectors().removeIf(x -> x.getConnectorId().equals(oldSlave));
//删除从的连接关系
// 删除主的连接关系
deviceMaster.getConnectedConnectors()
.removeIf(x -> x.getConnectorId().equals(oldSlave));
// 删除从的连接关系
NettyDevice oldSlaveDevice = sessionManager.getDevice(oldSlave);
if (oldSlaveDevice != null && !CollectionUtils.isEmpty(oldSlaveDevice.getConnectedConnectors())) {
Set<com.maibu.core.business.device.Connector> connectedDevices = oldSlaveDevice.getConnectedConnectors();
if (oldSlaveDevice != null
&& !CollectionUtils.isEmpty(oldSlaveDevice.getConnectedConnectors())) {
Set<com.maibu.core.business.device.Connector> connectedDevices = oldSlaveDevice
.getConnectedConnectors();
connectedDevices.removeIf(x -> x.getConnectorId().equals(masterId));
}
}
@@ -130,21 +132,20 @@ public class TransferDeviceService {
}
}
public AjaxResult updateDeviceAlias(DeviceBindDTO bindDTO, LoginUser user) {
try {
String deviceId = bindDTO.getDeviceId();
//判断当前设备是否存
// 判断当前设备是否存
Device device = iDeviceService.selectDeviceBySerialNumber(deviceId);
if (device == null) {
return AjaxResult.error("设备不存在");
} else {
// Long tenantId = device.getTenantId();
// if (tenantId == -1) {
// return AjaxResult.error("设备未绑定");
// return AjaxResult.error("设备未绑定");
// }
// if (!tenantId.equals(user.getUserId())) {
// return AjaxResult.error("无操作权限");
// return AjaxResult.error("无操作权限");
// }
device.setDeviceAlias(bindDTO.getDeviceAlias());
device.setUpdateTime(LocalDateTime.now());
@@ -158,76 +159,76 @@ public class TransferDeviceService {
}
}
// public synchronized boolean switchDevice(DeviceBindDTO bindDTO, LoginUser user) {
// try {
// String slaveId = bindDTO.getDeviceId();
// String platform = bindDTO.getPlatform();
// String userName = user.getUsername();
// String masterId = userName + ":" + platform;
// //todo 判断是否有权限 正在控制才能切
// //todo 离线没有控制权的怎么切
// log.info("switchDevice:{}", masterId);
// NettyDevice deviceMaster = sessionManager.getDevice(masterId);
// log.info("deviceMaster:{}", deviceMaster);
// if (deviceMaster != null) {
// log.info("ConnectorStatus:{}", deviceMaster.getConnectorStatus());
// if (ConnectorStatus.RECEIVER.equals(deviceMaster.getConnectorStatus())) {
// //当前账号没有控制则可以切换为控制?
// NettyDevice activeDevice = sessionManager.getUserControl(userName);
// deviceMaster.getConnectedConnectors().clear();
// if (activeDevice == null) {
// log.info("bindConnector ACTIVE :{}", masterId);
// sessionManager.bindConnector(masterId, slaveId);
// deviceMaster.setConnectorStatus(ConnectorStatus.ACTIVE);
// deviceMaster.setCurrentSlaveId(slaveId);
// sysUserClientService.updateAliveStatus(masterId, slaveId, 1);
// } else {
// log.info("bindConnector RECEIVER :{}", masterId);
// sessionManager.bindConnector(masterId, slaveId);
// deviceMaster.setConnectorStatus(ConnectorStatus.RECEIVER);
// deviceMaster.setCurrentSlaveId(slaveId);
// sysUserClientService.updateAliveStatus(masterId, slaveId, 2);
// }
// } else {
// //从关联其他设备切换到 当前禁言 假如有控制且不是当前更新为2
// List<NettyDevice> controls = sessionManager.getSlaveControl(slaveId);
// if (CollectionUtils.isEmpty(controls)) {
// deviceMaster.getConnectedConnectors().clear();
// log.info("bindConnector 1:{}", masterId);
// sessionManager.bindConnector(masterId, slaveId);
// deviceMaster.setConnectorStatus(ConnectorStatus.ACTIVE);
// deviceMaster.setCurrentSlaveId(slaveId);
// sysUserClientService.updateAliveStatus(masterId, slaveId, 1);
// } else {
// NettyDevice controlDevice = controls.get(0);
// if (!masterId.equals(controlDevice.getConnectorId())) {
// //当前的设备有控制 的主机
// deviceMaster.getConnectedConnectors().clear();
// log.info("bindConnector RECEIVER 2:{}", masterId);
// sessionManager.bindConnector(masterId, slaveId);
// deviceMaster.setConnectorStatus(ConnectorStatus.RECEIVER);
// deviceMaster.setCurrentSlaveId(slaveId);
// sysUserClientService.updateAliveStatus(masterId, slaveId, 2);
// }
// //当前 切当前不做操作
// }
// }
// return true;
// }
// } catch (Exception e) {
// log.error("切换设备失败: {}", e.getMessage(), e);
// }
// return false;
// }
// public synchronized boolean switchDevice(DeviceBindDTO bindDTO, LoginUser
// user) {
// try {
// String slaveId = bindDTO.getDeviceId();
// String platform = bindDTO.getPlatform();
// String userName = user.getUsername();
// String masterId = userName + ":" + platform;
// //todo 判断是否有权限 正在控制才能切
// //todo 离线没有控制权的怎么切
// log.info("switchDevice:{}", masterId);
// NettyDevice deviceMaster = sessionManager.getDevice(masterId);
// log.info("deviceMaster:{}", deviceMaster);
// if (deviceMaster != null) {
// log.info("ConnectorStatus:{}", deviceMaster.getConnectorStatus());
// if (ConnectorStatus.RECEIVER.equals(deviceMaster.getConnectorStatus())) {
// //当前账号没有控制则可以切换为控制?
// NettyDevice activeDevice = sessionManager.getUserControl(userName);
// deviceMaster.getConnectedConnectors().clear();
// if (activeDevice == null) {
// log.info("bindConnector ACTIVE :{}", masterId);
// sessionManager.bindConnector(masterId, slaveId);
// deviceMaster.setConnectorStatus(ConnectorStatus.ACTIVE);
// deviceMaster.setCurrentSlaveId(slaveId);
// sysUserClientService.updateAliveStatus(masterId, slaveId, 1);
// } else {
// log.info("bindConnector RECEIVER :{}", masterId);
// sessionManager.bindConnector(masterId, slaveId);
// deviceMaster.setConnectorStatus(ConnectorStatus.RECEIVER);
// deviceMaster.setCurrentSlaveId(slaveId);
// sysUserClientService.updateAliveStatus(masterId, slaveId, 2);
// }
// } else {
// //从关联其他设备切换到 当前禁言 假如有控制且不是当前更新为2
// List<NettyDevice> controls = sessionManager.getSlaveControl(slaveId);
// if (CollectionUtils.isEmpty(controls)) {
// deviceMaster.getConnectedConnectors().clear();
// log.info("bindConnector 1:{}", masterId);
// sessionManager.bindConnector(masterId, slaveId);
// deviceMaster.setConnectorStatus(ConnectorStatus.ACTIVE);
// deviceMaster.setCurrentSlaveId(slaveId);
// sysUserClientService.updateAliveStatus(masterId, slaveId, 1);
// } else {
// NettyDevice controlDevice = controls.get(0);
// if (!masterId.equals(controlDevice.getConnectorId())) {
// //当前的设备有控制 的主机
// deviceMaster.getConnectedConnectors().clear();
// log.info("bindConnector RECEIVER 2:{}", masterId);
// sessionManager.bindConnector(masterId, slaveId);
// deviceMaster.setConnectorStatus(ConnectorStatus.RECEIVER);
// deviceMaster.setCurrentSlaveId(slaveId);
// sysUserClientService.updateAliveStatus(masterId, slaveId, 2);
// }
// //当前 切当前不做操作
// }
// }
// return true;
// }
// } catch (Exception e) {
// log.error("切换设备失败: {}", e.getMessage(), e);
// }
// return false;
// }
public synchronized boolean switchDevice(DeviceBindDTO bindDTO) {
try {
String slaveId = bindDTO.getDeviceId();
// String platform = bindDTO.getPlatform();
// String userName = user.getUsername();
// String masterId = userName + ":" + platform;
// String platform = bindDTO.getPlatform();
// String userName = user.getUsername();
// String masterId = userName + ":" + platform;
String token = bindDTO.getToken();
@@ -247,9 +248,10 @@ public class TransferDeviceService {
sessionManager.lock(slaveId, token);
log.info("switchDevice bindConnector slaveId:{},token:{}", slaveId, token);
} else {
List<String> connectorIds = controls.stream().map(NettyDevice::getConnectorId).collect(Collectors.toList());
List<String> connectorIds = controls.stream().map(NettyDevice::getConnectorId)
.collect(Collectors.toList());
if (!connectorIds.contains(token)) {
//当前的设备有控制 的主机
// 当前的设备有控制 的主机
deviceMaster.getConnectedConnectors().clear();
sessionManager.bindConnector(token, slaveId);
deviceMaster.setConnectorStatus(ConnectorStatus.RECEIVER);
@@ -260,16 +262,15 @@ public class TransferDeviceService {
deviceMaster.setCurrentSlaveId(slaveId);
sessionManager.lock(slaveId, token);
}
//当前 切当前不做操作
// 当前 切当前不做操作
}
return true;
} catch (Exception e) {
log.error("切换设备失败: {}", e.getMessage(), e);
log.error("切换设备失败", e);
}
return false;
}
public DeviceControlDTO remoteControl(DeviceBindDTO bindDTO) {
try {
String token = bindDTO.getToken();
@@ -290,7 +291,6 @@ public class TransferDeviceService {
return null;
}
public DeviceSwitchControlDTO switchControl(DeviceBindDTO bindDTO, LoginUser user) {
try {
String slaveId = bindDTO.getDeviceId();
@@ -299,11 +299,11 @@ public class TransferDeviceService {
String masterId = userName + ":" + platform;
DeviceSwitchControlDTO dto = new DeviceSwitchControlDTO();
//收到明确的切换设备请求了 从哪边切换到另一边
//判断当前是否有控制的设备
// 收到明确的切换设备请求了 从哪边切换到另一边
// 判断当前是否有控制的设备
NettyDevice masterDevice = sessionManager.getDevice(masterId);
if (masterDevice == null || !masterDevice.getChannel().isActive()) {
//主机已离线
// 主机已离线
dto.setSwitchControl(false);
dto.setReason("主机已离线");
return dto;
@@ -311,8 +311,8 @@ public class TransferDeviceService {
NettyDevice slaveDevice = sessionManager.getDevice(slaveId);
if (slaveDevice == null || !masterDevice.getChannel().isActive()) {
//从机已离线
//主机已离线
// 从机已离线
// 主机已离线
dto.setSwitchControl(false);
dto.setReason("从机已离线");
return dto;
@@ -321,7 +321,7 @@ public class TransferDeviceService {
List<NettyDevice> controlList = sessionManager.getSlaveControl(slaveId);
if (!CollectionUtils.isEmpty(controlList)) {
//如果当前设备有控制
// 如果当前设备有控制
controlList.forEach(c -> {
if (!c.getConnectorId().equals(masterId)) {
c.setCurrentSlaveId(null);
@@ -333,7 +333,8 @@ public class TransferDeviceService {
}
masterDevice.setCurrentSlaveId(slaveId);
masterDevice.setConnectorStatus(ConnectorStatus.ACTIVE);
// sysUserClientService.updateAliveStatus(masterDevice.getConnectorId(), slaveId, 1);
// sysUserClientService.updateAliveStatus(masterDevice.getConnectorId(),
// slaveId, 1);
dto.setSwitchControl(true);
dto.setHolder(platform);

View File

@@ -0,0 +1,65 @@
[
{
"paramType": "global",
"paramKey": "systemName",
"nameTranslateKey": "system.param.systemName.name",
"paramValue": "光伏机器人环境感知服务平台",
"descTranslateKey": "system.param.systemName.desc",
"dataType": "String",
"range": ""
},
{
"paramType": "global",
"paramKey": "defaultStationName",
"nameTranslateKey": "system.param.defaultStationName.name",
"paramValue": "",
"descTranslateKey": "system.param.defaultStationName.desc",
"dataType": "String",
"range": ""
},
{
"paramType": "global",
"paramKey": "defaultLanguage",
"nameTranslateKey": "system.param.defaultLanguage.name",
"paramValue": "zh_CN",
"descTranslateKey": "system.param.defaultLanguage.desc",
"dataType": "String",
"range": ""
},
{
"paramType": "global",
"paramKey": "defaultZoneId",
"nameTranslateKey": "system.param.defaultZoneId.name",
"paramValue": "Asia/Shanghai",
"descTranslateKey": "system.param.defaultZoneId.desc",
"dataType": "String",
"range": ""
},
{
"paramType": "global",
"paramKey": "daylightSavingTime",
"nameTranslateKey": "system.param.daylightSavingTime.name",
"paramValue": "true",
"descTranslateKey": "system.param.daylightSavingTime.desc",
"dataType": "Boolean",
"range": ""
},
{
"paramType": "global",
"paramKey": "defaultUnit",
"nameTranslateKey": "system.param.defaultUnit.name",
"paramValue": "公制",
"descTranslateKey": "system.param.defaultUnit.desc",
"dataType": "String",
"range": ""
},
{
"paramType": "global",
"paramKey": "defaultDateFormat",
"nameTranslateKey": "system.param.defaultDateFormat.name",
"paramValue": "YYYY-MM-DD HH:mm:ss",
"descTranslateKey": "system.param.defaultDateFormat.desc",
"dataType": "String",
"range": ""
}
]

View File

@@ -0,0 +1,129 @@
package com.maibu.controller;
import com.maibu.core.controller.BaseController;
import com.maibu.core.domain.AjaxResult;
import com.maibu.core.domain.R;
import com.maibu.core.enums.OtaOperate;
import com.maibu.core.ota.DeviceOtaFirmware;
import com.maibu.core.ota.DeviceOtaOperateRecord;
import com.maibu.core.page.TableDataInfo;
import com.maibu.dto.BaseQueryDTO;
import com.maibu.dto.DeviceOtaOperateDTO;
import com.maibu.service.impl.DeviceOtaService;
import com.maibu.utils.MinioUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.List;
@Api(tags = "OTA固件管理")
@RestController
@RequestMapping("/iot/deviceOta")
public class DeviceOtaController extends BaseController {
@Autowired
private DeviceOtaService service;
@Autowired
private MinioUtil minioUtil;
// ==================== 固件管理 ====================
@ApiOperation("新增或修改固件")
@PostMapping("/firmware/save")
public R saveFirmware(
@RequestPart("file") MultipartFile file, // 接收上传的图片
@RequestPart("firmware") DeviceOtaFirmware firmware // 接收表单里的 JSON 对象
) {
try (InputStream inputStream = file.getInputStream()) {
String fileName = "ota/firmware/" + firmware.getVersion();
// 上传到 MinIO
minioUtil.uploadFile(fileName, inputStream, "application/octet-stream");
// 获取访问 URL
String url = minioUtil.getPublicUrl(fileName);
System.out.println("固件上传成功,访问地址: " + url);
// 这里可以把 URL 存到 workRecord 里
firmware.setMinioObjectName(fileName);
firmware.setFileUrl(url);
boolean b = service.saveFirmware(firmware, file.getSize());
DeviceOtaOperateRecord record = new DeviceOtaOperateRecord();
record.setFileName(firmware.getVersion());
record.setFirmwareId(firmware.getId());
record.setOperateType(OtaOperate.upload);
record.setCreateBy(getUsername());
record.setCreateTime(LocalDateTime.now());
service.saveOperateRecord(record);
return !b ? R.fail("新增失败") : R.ok("新增成功");
} catch (Exception e) {
return R.fail("新增失败: " + e.getMessage());
}
}
@ApiOperation("固件分页列表")
@PostMapping("/firmware/list")
public TableDataInfo listFirmware() {
startPage();
return getDataTable(service.listFirmware());
}
@ApiOperation("根据ID查询固件")
@GetMapping("/firmware/detail")
public AjaxResult selectFirmwareById(@RequestParam Long id) {
return AjaxResult.success(service.selectFirmwareById(id));
}
@ApiOperation("更新固件")
@PostMapping("/firmware/upgrade")
public AjaxResult upgrade(@RequestBody DeviceOtaOperateDTO dto) {
return service.upgrade(dto);
}
@ApiOperation("批量删除固件")
@PostMapping("/firmware/delete")
public AjaxResult deleteFirmware(@RequestBody List<Long> ids) {
return AjaxResult.success(service.deleteFirmware(ids));
}
// ==================== 操作记录管理 ====================
@ApiOperation("新增或修改操作记录")
@PostMapping("/record/save")
public AjaxResult saveOperateRecord(@RequestBody DeviceOtaOperateRecord record) {
return AjaxResult.success(service.saveOperateRecord(record));
}
@ApiOperation("操作记录分页列表")
@PostMapping("/record/list")
public TableDataInfo listOperateRecord(@RequestBody BaseQueryDTO dto) {
startPage();
return getDataTable(service.listOperateRecord(dto));
}
@ApiOperation("根据ID查询操作记录")
@GetMapping("/record/detail")
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")
public AjaxResult deleteOperateRecord(@RequestBody List<Long> ids) {
return AjaxResult.success(service.deleteOperateRecord(ids));
}
}

View File

@@ -1,30 +1,27 @@
package com.maibu.controller;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import com.maibu.annotation.Log;
import com.maibu.core.domain.entity.SysUser;
import com.maibu.enums.BusinessType;
import com.maibu.utils.poi.ExcelUtil;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
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.RestController;
import com.maibu.core.business.DeviceRunParam;
import com.maibu.core.controller.BaseController;
import com.maibu.core.domain.AjaxResult;
import com.maibu.core.page.TableDataInfo;
import com.maibu.dto.BaseQueryDTO;
import com.maibu.service.impl.DeviceRunParamService;
import com.maibu.utils.poi.ExcelUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
@RestController
@RequestMapping("/iot/deviceRunParam")
public class DeviceRunParamController extends BaseController {
@@ -33,7 +30,7 @@ public class DeviceRunParamController extends BaseController {
private DeviceRunParamService service;
@PostMapping("/save")
public AjaxResult save(@RequestBody DeviceRunParam deviceRunParam) throws ExecutionException, InterruptedException, TimeoutException {
public AjaxResult save(@RequestBody DeviceRunParam deviceRunParam) {
return AjaxResult.success(service.save(deviceRunParam));
}
@@ -45,7 +42,8 @@ public class DeviceRunParamController extends BaseController {
}
@GetMapping("/selectByDeviceId")
public AjaxResult selectByDeviceId(@RequestParam String deviceId) throws ExecutionException, InterruptedException, TimeoutException {
public AjaxResult selectByDeviceId(@RequestParam String deviceId)
throws ExecutionException, InterruptedException, TimeoutException {
return AjaxResult.success(service.selectByDeviceId(deviceId));
}
@@ -53,7 +51,7 @@ public class DeviceRunParamController extends BaseController {
public AjaxResult delete(@RequestBody List<Long> ids) {
return AjaxResult.success(service.delete(ids));
}
@PostMapping("/importData")
public AjaxResult importData(MultipartFile file) throws Exception {
ExcelUtil<DeviceRunParam> util = new ExcelUtil<DeviceRunParam>(DeviceRunParam.class);
@@ -61,4 +59,31 @@ public class DeviceRunParamController extends BaseController {
boolean message = service.importParam(paramList);
return success(message);
}
/**
* 下载机器属性导入模板
*/
@GetMapping("/downloadTemplate")
public void downloadTemplate(HttpServletResponse response) throws IOException {
String fileName = "机器属性导入模板.xlsx";
try (InputStream inputStream = new ClassPathResource("机器属性导入模板.xlsx").getInputStream()) {
response.setContentType(
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("UTF-8");
response.setHeader(
"Content-Disposition",
"attachment; filename*=UTF-8''" +
URLEncoder.encode(fileName, "UTF-8").replace("+", "%20"));
ServletOutputStream outputStream = response.getOutputStream();
byte[] buffer = new byte[8192];
int len;
while ((len = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
}
outputStream.flush();
}
}
}

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.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@@ -18,11 +19,13 @@ 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.device.NettyDevice;
import com.maibu.core.business.dto.WebRouteStatusMessageDTO;
import com.maibu.core.business.inter.WebsocketMesDispather;
import com.maibu.core.business.path.LatAndLngEntity;
import com.maibu.core.controller.BaseController;
import com.maibu.core.domain.AjaxResult;
import com.maibu.core.domain.R;
import com.maibu.core.enums.MesType;
import com.maibu.memory.DeviceSessionManager;
@@ -245,4 +248,12 @@ public class HostComputerController extends BaseController {
String.format("%.6f", minLng), String.format("%.6f", maxLng));
}
@GetMapping("/searchDevice")
public AjaxResult searchDevice(@RequestParam String deviceId) throws Exception {
hostComputerService.searchDevice(deviceId);
return success();
}
}

View File

@@ -3,14 +3,11 @@ package com.maibu.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
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.RestController;
import com.maibu.core.business.DeviceRunParam;
import com.maibu.core.business.OrderModel;
import com.maibu.core.controller.BaseController;
import com.maibu.core.domain.AjaxResult;

View File

@@ -1,28 +1,20 @@
package com.maibu.controller;
import com.maibu.core.business.WorkRecord;
import com.maibu.core.controller.BaseController;
import com.maibu.core.domain.AjaxResult;
import com.maibu.core.domain.R;
import com.maibu.memory.GlobalMemory;
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;
import com.maibu.core.domain.AjaxResult;
import com.maibu.memory.GlobalMemory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
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.path.LatAndLngEntity;
import com.maibu.core.controller.BaseController;
import com.maibu.core.domain.R;
import com.maibu.service.impl.WorkRecordService;
import com.maibu.utils.MinioUtil;
@RestController
@RequestMapping("/iot/workRecord")
public class WorkRecordController extends BaseController {

View File

@@ -0,0 +1,15 @@
package com.maibu.dto;
import com.maibu.core.enums.OtaOperate;
import lombok.Data;
import java.util.List;
@Data
public class DeviceOtaOperateDTO {
private OtaOperate operateType;
private Long firmwareId;
private List<String> deviceModels;
}

View File

@@ -0,0 +1,204 @@
package com.maibu.service.impl;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.maibu.core.business.Device;
import com.maibu.core.business.device.NettyDevice;
import com.maibu.core.domain.AjaxResult;
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.memory.DeviceSessionManager;
import com.maibu.service.IDeviceService;
import com.maibu.utils.MinioUtil;
import com.maibu.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
@Service
@Slf4j
public class DeviceOtaService {
@Autowired
private DeviceOtaFirmwareMapper deviceOtaFirmwareMapper;
@Autowired
private DeviceOtaOperateRecordMapper deviceOtaOperateRecordMapper;
@Autowired
private MinioUtil minioUtil;
@Autowired
private DeviceSessionManager deviceSessionManager;
@Autowired
private IDeviceService deviceService;
private static final int BATCH_SIZE = 5;
public boolean saveFirmware(DeviceOtaFirmware firmware, Long fileSize) {
firmware.setCreateTime(LocalDateTime.now());
firmware.setCreateBy(SecurityUtils.getUsername());
firmware.setFileSize(formatFileSize(fileSize));
return deviceOtaFirmwareMapper.saveOrUpdate(firmware);
}
public static String formatFileSize(long size) {
if (size < 1024) {
return size + " B";
}
double kb = size / 1024.0;
if (kb < 1024) {
return String.format("%.2f KB", kb);
}
double mb = kb / 1024.0;
if (mb < 1024) {
return String.format("%.2f MB", mb);
}
double gb = mb / 1024.0;
return String.format("%.2f GB", gb);
}
public List<DeviceOtaFirmware> listFirmware() {
LambdaQueryWrapper<DeviceOtaFirmware> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.orderByDesc(DeviceOtaFirmware::getCreateTime);
return deviceOtaFirmwareMapper.selectList(queryWrapper);
}
public DeviceOtaFirmware selectFirmwareById(Long id) {
return deviceOtaFirmwareMapper.selectById(id);
}
public AjaxResult upgrade(DeviceOtaOperateDTO dto) {
if (dto.getFirmwareId() == null) {
return AjaxResult.error("固件ID不能为空");
}
if (CollectionUtils.isEmpty(dto.getDeviceModels())) {
return AjaxResult.error("请选择要升级的设备");
}
DeviceOtaFirmware firmware = deviceOtaFirmwareMapper.selectById(dto.getFirmwareId());
if (firmware == null) {
return AjaxResult.error("固件不存在");
}
List<DeviceOtaOperateRecord> records = new ArrayList<>();
int totalDevices = dto.getDeviceModels().size();
int successCount = 0;
int offlineCount = 0;
for (int i = 0; i < totalDevices; i += BATCH_SIZE) {
List<String> batch = dto.getDeviceModels().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);
Device updatedDevice = device.getDevice();
DeviceOtaOperateRecord record = new DeviceOtaOperateRecord();
record.setOperateType(dto.getOperateType());
record.setFirmwareId(firmware.getId());
record.setFileName(firmware.getFileName());
record.setDeviceType(firmware.getDeviceType());
record.setCreateTime(LocalDateTime.now());
record.setCreateBy(SecurityUtils.getUsername());
record.setDeviceId(deviceId);
record.setPreVersion(updatedDevice.getFirmwareVersion());
record.setNowVersion(firmware.getVersion());
records.add(record);
updatedDevice.setFirmwareVersion(firmware.getVersion());
deviceService.updateDeviceBySelf(updatedDevice);
successCount++;
log.info("OTA 升级指令已下发: deviceId={}, version={}", deviceId, firmware.getVersion());
} catch (Exception e) {
log.error("OTA 升级指令下发失败: deviceId={}", deviceId, e);
}
}
}
deviceOtaOperateRecordMapper.saveOrUpdateBatch(records);
String msg = String.format("升级指令下发完成: 成功=%d, 离线=%d, 总计=%d",
successCount, offlineCount, totalDevices);
log.info(msg);
return AjaxResult.success(msg);
}
public int deleteFirmware(List<Long> ids) {
if (CollectionUtils.isEmpty(ids)) {
return 0;
}
List<DeviceOtaFirmware> list = deviceOtaFirmwareMapper.selectBatchIds(ids);
if (!CollectionUtils.isEmpty(list)) {
list.forEach(l -> {
try {
minioUtil.deleteFile(l.getMinioObjectName());
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
return deviceOtaFirmwareMapper.deleteBatchIds(ids);
}
public boolean saveOperateRecord(DeviceOtaOperateRecord record) {
return deviceOtaOperateRecordMapper.saveOrUpdate(record);
}
public List<DeviceOtaOperateRecord> listOperateRecord(BaseQueryDTO dto) {
LambdaQueryWrapper<DeviceOtaOperateRecord> queryWrapper = new LambdaQueryWrapper<>();
if (dto != null) {
if (!StringUtils.isEmpty(dto.getDeviceId())) {
queryWrapper.eq(DeviceOtaOperateRecord::getDeviceType, dto.getDeviceId());
}
}
queryWrapper.orderByDesc(DeviceOtaOperateRecord::getCreateTime);
return deviceOtaOperateRecordMapper.selectList(queryWrapper);
}
public DeviceOtaOperateRecord selectOperateRecordById(Long id) {
return deviceOtaOperateRecordMapper.selectById(id);
}
public List<DeviceOtaOperateRecord> selectListByDeviceId(String deviceId) {
LambdaQueryWrapper<DeviceOtaOperateRecord> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DeviceOtaOperateRecord::getDeviceId, deviceId);
return deviceOtaOperateRecordMapper.selectList(wrapper);
}
public int deleteOperateRecord(List<Long> ids) {
if (CollectionUtils.isEmpty(ids)) {
return 0;
}
return deviceOtaOperateRecordMapper.deleteBatchIds(ids);
}
}

View File

@@ -1,7 +1,10 @@
package com.maibu.service.impl;
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;
@@ -39,47 +42,55 @@ public class DeviceRunParamService {
@Autowired
private DeviceSessionManager sessionManager;
public boolean save(DeviceRunParam deviceRunParam)
throws ExecutionException, InterruptedException, TimeoutException {
// public boolean save(DeviceRunParam deviceRunParam)
// throws ExecutionException, InterruptedException, TimeoutException {
NettyDevice nettyDevice = sessionManager.getDevice(deviceRunParam.getDeviceId());
if (nettyDevice == null || !nettyDevice.getChannel().isActive()) {
throw new ServiceException("设备不在线,无法操作!");
}
// NettyDevice nettyDevice =
// sessionManager.getDevice(deviceRunParam.getDeviceId());
// if (nettyDevice == null || !nettyDevice.getChannel().isActive()) {
// throw new ServiceException("设备不在线,无法操作!");
// }
CompletableFuture<DeviceRunParam> future = new CompletableFuture<>();
nettyDevice.getChannel().attr(Constant.READ_CONFIG_KEY).set(future);
// CompletableFuture<DeviceRunParam> future = new CompletableFuture<>();
// nettyDevice.getChannel().attr(Constant.READ_CONFIG_KEY).set(future);
try {
nettyDevice.readConfigCommand();
// try {
// nettyDevice.readConfigCommand();
DeviceRunParam param = future.get(5, TimeUnit.SECONDS);
// DeviceRunParam param = future.get(5, TimeUnit.SECONDS);
if (param == null) {
throw new ServiceException("获取设备当前属性失败!");
}
// if (param == null) {
// throw new ServiceException("获取设备当前属性失败!");
// }
param.setLeftForwardGain(deviceRunParam.getLeftForwardGain());
param.setLeftBackwardGain(deviceRunParam.getLeftBackwardGain());
param.setRightForwardGain(deviceRunParam.getRightForwardGain());
param.setRightBackwardGain(deviceRunParam.getRightBackwardGain());
// param.setLeftForwardGain(deviceRunParam.getLeftForwardGain());
// param.setLeftBackwardGain(deviceRunParam.getLeftBackwardGain());
// param.setRightForwardGain(deviceRunParam.getRightForwardGain());
// param.setRightBackwardGain(deviceRunParam.getRightBackwardGain());
nettyDevice.writeConfigCommand(param);
// nettyDevice.writeConfigCommand(param);
param.setRunSpeed(deviceRunParam.getRunSpeed());
param.setDeviceId(deviceRunParam.getDeviceId());
param.setSiteId(deviceRunParam.getSiteId());
param.setOrgId(deviceRunParam.getOrgId());
param.setId(deviceRunParam.getId());
// param.setRunSpeed(deviceRunParam.getRunSpeed());
// param.setDeviceId(deviceRunParam.getDeviceId());
// param.setSiteId(deviceRunParam.getSiteId());
// param.setOrgId(deviceRunParam.getOrgId());
// param.setId(deviceRunParam.getId());
// SiteMemory siteMemory = GlobalMemory.getSiteMemory(deviceRunParam.getOrgId(),
// deviceRunParam.getSiteId());
// if (siteMemory != null) {
// siteMemory.saveDeviceRunParam(param);
// }
// return deviceRunParamMapper.saveOrUpdate(param);
// } finally {
// nettyDevice.getChannel().attr(Constant.READ_CONFIG_KEY).set(null);
// }
// }
public boolean save(DeviceRunParam deviceRunParam) {
return deviceRunParamMapper.saveOrUpdate(deviceRunParam);
SiteMemory siteMemory = GlobalMemory.getSiteMemory(deviceRunParam.getOrgId(), deviceRunParam.getSiteId());
if (siteMemory != null) {
siteMemory.saveDeviceRunParam(param);
}
return deviceRunParamMapper.saveOrUpdate(param);
} finally {
nettyDevice.getChannel().attr(Constant.READ_CONFIG_KEY).set(null);
}
}
public List<DeviceRunParam> list(BaseQueryDTO dto) {
@@ -127,6 +138,49 @@ public class DeviceRunParamService {
// return null;
// }
// public DeviceRunParam selectByDeviceId(String deviceId)
// throws ExecutionException, InterruptedException, TimeoutException {
// LambdaQueryWrapper<DeviceRunParam> queryWrapper = new LambdaQueryWrapper<>();
// if (!StringUtils.isEmpty(deviceId)) {
// queryWrapper.eq(DeviceRunParam::getDeviceId, deviceId);
// }
// DeviceRunParam result = deviceRunParamMapper.selectOne(queryWrapper);
// if (result != null && result.getHasHost() != null && result.getHasHost() ==
// 1) {
// return result;
// } else {
// NettyDevice nettyDevice = sessionManager.getDevice(deviceId);
// CompletableFuture<DeviceRunParam> future = new CompletableFuture<>();
// if (nettyDevice != null && nettyDevice.getChannel() != null &&
// nettyDevice.getChannel().isActive()) {
// nettyDevice.getChannel().attr(Constant.READ_CONFIG_KEY).set(future);
// try {
// nettyDevice.readConfigCommand();
// DeviceRunParam param = future.get(5, TimeUnit.SECONDS);
// // LambdaQueryWrapper<DeviceRunParam> queryWrapper = new
// LambdaQueryWrapper<>();
// // if (!StringUtils.isEmpty(deviceId)) {
// // queryWrapper.eq(DeviceRunParam::getDeviceId, deviceId);
// // }
// // DeviceRunParam result = deviceRunParamMapper.selectOne(queryWrapper);
// if (result != null) {
// param.setId(result.getId());
// param.setDeviceId(result.getDeviceId());
// param.setRunSpeed(result.getRunSpeed());
// param.setOrgId(nettyDevice.getDevice().getOrgId());
// param.setSiteId(nettyDevice.getDevice().getSiteId());
// }
// return param;
// } finally {
// nettyDevice.getChannel().attr(Constant.READ_CONFIG_KEY).set(null);
// }
// }
// }
// return null;
// }
public DeviceRunParam selectByDeviceId(String deviceId)
throws ExecutionException, InterruptedException, TimeoutException {
@@ -135,36 +189,7 @@ public class DeviceRunParamService {
queryWrapper.eq(DeviceRunParam::getDeviceId, deviceId);
}
DeviceRunParam result = deviceRunParamMapper.selectOne(queryWrapper);
if (result != null && result.getHasHost() != null && result.getHasHost() == 1) {
return result;
} else {
NettyDevice nettyDevice = sessionManager.getDevice(deviceId);
CompletableFuture<DeviceRunParam> future = new CompletableFuture<>();
if (nettyDevice != null && nettyDevice.getChannel() != null && nettyDevice.getChannel().isActive()) {
nettyDevice.getChannel().attr(Constant.READ_CONFIG_KEY).set(future);
try {
nettyDevice.readConfigCommand();
DeviceRunParam param = future.get(5, TimeUnit.SECONDS);
// LambdaQueryWrapper<DeviceRunParam> queryWrapper = new LambdaQueryWrapper<>();
// if (!StringUtils.isEmpty(deviceId)) {
// queryWrapper.eq(DeviceRunParam::getDeviceId, deviceId);
// }
// DeviceRunParam result = deviceRunParamMapper.selectOne(queryWrapper);
if (result != null) {
param.setId(result.getId());
param.setDeviceId(result.getDeviceId());
param.setRunSpeed(result.getRunSpeed());
param.setOrgId(nettyDevice.getDevice().getOrgId());
param.setSiteId(nettyDevice.getDevice().getSiteId());
}
return param;
} finally {
nettyDevice.getChannel().attr(Constant.READ_CONFIG_KEY).set(null);
}
}
}
return null;
return result;
}
public int delete(List<Long> ids) {
@@ -211,19 +236,51 @@ public class DeviceRunParamService {
}
if (deviceRunParam != null) {
deviceRunParam.setCapabilityList(x.getCapabilityList());
deviceRunParam.setDeviceId(x.getDeviceId());
parseSn(x.getDeviceId(), deviceRunParam);
deviceRunParam.setProductModel(x.getProductModel());
deviceRunParam.setRtkMode(x.getRtkMode());
deviceRunParam.setDeviceType(x.getDeviceType());
deviceRunParam.setPowerType(x.getPowerType());
deviceRunParam.setRtkMode(x.getRtkMode());
deviceRunParam.setCameraNum(x.getCameraNum());
deviceRunParam.setPowerType(x.getPowerType());
deviceRunParam.setWheelType(x.getWheelType());
deviceRunParam.setEngineType(x.getEngineType());
deviceRunParam.setHasHost(x.getHasHost());
deviceRunParam.setBatteryType(x.getBatteryType());
deviceRunParam.setWalkingDriveType(x.getWalkingDriveType());
deviceRunParam.setCutterDriveType(x.getCutterDriveType());
deviceRunParam.setRtkAntennasNum(x.getRtkAntennasNum());
deviceRunParam.setHandleType(x.getHandleType());
deviceRunParam.setPcbVersion(x.getPcbVersion());
deviceRunParam.setDimensions(x.getDimensions());
deviceRunParam.setBladeWidth(x.getBladeWidth());
deviceRunParam.setTyreSize(x.getTyreSize());
deviceRunParam.setSpeedRatio(x.getSpeedRatio());
deviceRunParam.setCellularInfo(x.getCellularInfo());
deviceRunParam.setWifiName(x.getWifiName());
deviceRunParam.setBluetoothName(x.getBluetoothName());
deviceRunParam.setHardwareIdentifiers(x.getHardwareIdentifiers());
deviceRunParam.setImuModel(x.getImuModel());
deviceRunParam.setTempHumiditySensor(x.getTempHumiditySensor());
deviceRunParam.setBarometerPresent(x.getBarometerPresent());
deviceRunParam.setUltrasonicPresent(x.getUltrasonicPresent());
deviceRunParam.setDacPresent(x.getDacPresent());
deviceRunParam.setOperator(x.getOperator());
deviceRunParam.setInspectionNo(x.getInspectionNo());
deviceRunParam.setFirmwareVersion(x.getFirmwareVersion());
deviceRunParam.setDealer(x.getDealer());
saveList.add(deviceRunParam);
} else {
parseSn(x.getDeviceId(), x);
saveList.add(x);
}
});
// todo 增加 解析sn码的功能
return deviceRunParamMapper.saveOrUpdateBatch(saveList);
}
}
@@ -233,4 +290,21 @@ public class DeviceRunParamService {
}
}
public static void parseSn(String sn, DeviceRunParam deviceRunParam) {
if (sn == null || sn.length() != 21) {
throw new IllegalArgumentException("SN 必须是 21 位字符串");
}
deviceRunParam.setProductModel(sn.substring(0, 2));
deviceRunParam.setFactoryCode(sn.substring(2, 4));
deviceRunParam.setLineNum(sn.substring(4, 6));
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyMMdd");
LocalDate date = LocalDate.parse(sn.substring(6, 12), formatter);
deviceRunParam.setProductionDate(date);
deviceRunParam.setSerialNumber(sn.substring(12, 17));
}
}

View File

@@ -1,18 +1,25 @@
package com.maibu.service.impl;
import com.maibu.core.business.WorkRecord;
import com.maibu.memory.GlobalMemory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.maibu.core.business.device.NettyDevice;
import com.maibu.memory.DeviceSessionManager;
import lombok.extern.slf4j.Slf4j;
@Service
@Slf4j
public class HostComputerService {
@Autowired
private WorkRecordService workRecordService;
private DeviceSessionManager deviceSessionManager;
public void searchDevice(String deviceId) throws Exception {
NettyDevice device = deviceSessionManager.getDevice(deviceId);
if (device != null && device.getOnlineStatus() == 1) {
device.searchDevice(deviceId);
}
}
}

View File

@@ -3,7 +3,6 @@ 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.path.LatAndLngEntity;
import com.maibu.mapper.DevicePlanTaskMapper;
import com.maibu.mapper.WorkRecordMapper;
import org.springframework.beans.factory.annotation.Autowired;

View File

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

View File

@@ -0,0 +1,46 @@
package com.maibu.service;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
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.domain.entity.SysParam;
import com.maibu.core.enums.SysParamType;
import com.maibu.mapper.SysParamMapper;
import com.maibu.utils.MessageUtils;
@Service
public class SysParamService {
@Autowired
private SysParamMapper sysParamMapper;
public Map<SysParamType, List<SysParam>> list() {
LambdaQueryWrapper<SysParam> queryWrapper = new LambdaQueryWrapper<>();
List<SysParam> sysParams = sysParamMapper.selectList(queryWrapper);
translateSysParam(sysParams);
Map<SysParamType, List<SysParam>> result = sysParams.stream()
.collect(Collectors.groupingBy(SysParam::getParamType));
return result;
}
public void translateSysParam(List<SysParam> sysParams) {
if (!CollectionUtils.isEmpty(sysParams)) {
sysParams.forEach(x -> {
x.setNameTranslate(MessageUtils.message(x.getNameTranslateKey()));
x.setDescTranslate(MessageUtils.message(x.getDescTranslateKey()));
});
}
}
public boolean save(List<SysParam> sysParam) {
return sysParamMapper.saveOrUpdateBatch(sysParam);
}
}

View File

@@ -1,15 +1,6 @@
package com.maibu.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.maibu.core.business.Device;
import com.maibu.core.domain.entity.SysRole;
import com.maibu.core.domain.entity.SysSite;
import com.maibu.core.domain.model.LoginUser;
import com.maibu.exception.ServiceException;
import com.maibu.mapper.SysRegionMapper;
import com.maibu.mapper.SysSiteMapper;
import com.maibu.memory.GlobalMemory;
import com.maibu.memory.SiteMemory;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -17,8 +8,13 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.maibu.core.domain.entity.SysRole;
import com.maibu.core.domain.entity.SysSite;
import com.maibu.core.domain.model.LoginUser;
import com.maibu.mapper.SysSiteMapper;
import com.maibu.memory.GlobalMemory;
import com.maibu.memory.SiteMemory;
@Service
public class SysSiteService {

View File

@@ -98,8 +98,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
r.role_level,
GROUP_CONCAT(srm.menu_id) AS menu_list
FROM sys_role r
LEFT JOIN sys_user_role ur ON ur.role_id = r.role_id
LEFT JOIN sys_user u ON u.user_id = ur.user_id
<!-- LEFT JOIN sys_user_role ur ON ur.role_id = r.role_id
LEFT JOIN sys_user u ON u.user_id = ur.user_id -->
LEFT JOIN sys_role_menu srm ON srm.role_id = r.role_id
where r.del_flag = 0
<if test="roleLevel != null and roleLevel != 0">

View File

@@ -1,9 +1,12 @@
package com.maibu.control;
import lombok.Data;
@Data
public class DiffSteer {
public static final int Polarity_High = 0; //0
public static final int Polarity_Low = 0; //1
public static final int Polarity_High = 0; // 0
public static final int Polarity_Low = 0; // 1
private float turnSpeedScale;
private float forwardSpeedScale;
@@ -15,6 +18,9 @@ public class DiffSteer {
private int leftWheelSpeed;
private int rightWheelSpeed;
private double linearSpeed;
private double angularSpeed;
public int getLeftWheelSpeed() {
return leftWheelSpeed;
}
@@ -63,6 +69,14 @@ public class DiffSteer {
rightWheelSpeed = clamp(rightWheelSpeed, -speedAmplLimit, speedAmplLimit);
}
// 计算线速度和轮速度
public void calcLinearAngularSpeed(int x, int y, int maxLinear, int maxAngular) {
this.linearSpeed = y * maxLinear / 1000.0;
this.angularSpeed = x * maxAngular / 1000.0;
}
// 设置前向速比
public void setTurnSpeedScale(int turnSpeedMax, int xMax) {
this.turnSpeedScale = (float) turnSpeedMax / (float) xMax;
@@ -88,5 +102,4 @@ public class DiffSteer {
return (val < min) ? min : ((val > max) ? max : val);
}
}

View File

@@ -8,6 +8,7 @@ import java.util.Objects;
import java.util.concurrent.TimeUnit;
import com.maibu.core.business.dto.WebDeviceTaskStatusMessageDTO;
import com.maibu.core.business.dto.WebOlineStatusMessageDTO;
import com.maibu.core.business.dto.WebStatusMessageDTO;
import org.apache.commons.lang3.StringUtils;
import org.java_websocket.WebSocket;
@@ -24,7 +25,6 @@ import com.maibu.dto.DeviceRespondDTO;
import com.maibu.dto.NettySwitchDeviceDTO;
import com.maibu.dto.ResultDTO;
import com.maibu.dto.WebAuthResponseDTO;
import com.maibu.dto.WebOlineStatusMessageDTO;
import com.maibu.dto.WebSwitchControlResponseDTO;
import com.maibu.memory.MiddleGlobalMemory;
import com.maibu.netty.NettyClient;

View File

@@ -1,9 +1,8 @@
package com.maibu.utils;
import com.maibu.common.MiddleCommandConstant;
import com.maibu.control.DiffSteer;
import com.maibu.dto.LawnMoverProtocolEntity;
import com.maibu.core.business.LawnMoverProtocolEntity;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -11,24 +10,29 @@ import java.util.List;
public class ControlUtils {
public static LawnMoverProtocolEntity getCarControlMessage(DiffSteer diffSteer, int originX, int originY, List<Boolean> buttons) {
public static LawnMoverProtocolEntity getCarControlMessage(DiffSteer diffSteer, int originX, int originY,
List<Boolean> buttons, int maxLinear, int maxAngular) {
LawnMoverProtocolEntity state = new LawnMoverProtocolEntity();
diffSteer.calcWheelSpeed(originX, originY);
diffSteer.calcLinearAngularSpeed(originX, originY, maxLinear, maxAngular);
state.setCommandType(MiddleCommandConstant.remoteControl);
state.setLeftWheelSpeed((short) diffSteer.getLeftWheelSpeed());
state.setRightWheelSpeed((short) diffSteer.getRightWheelSpeed());
//对应的 button数组 L1 button【4】 R1 button【5】 A对应的button【0】 B对应button【1】 X对应button【2】 Y对应button【3】 button[7]急停
//A升B降底盘 X割刀加速Y减速 L1点火 R1熄火
state.setLinearSpeed(diffSteer.getLinearSpeed());
state.setAngularSpeed(diffSteer.getAngularSpeed());
// 对应的 button数组 L1 button【4】 R1 button【5】 A对应的button【0】 B对应button【1】
// X对应button【2】 Y对应button【3】 button[7]急停
// A升B降底盘 X割刀加速Y减速 L1点火 R1熄火
Boolean A = buttons.get(0);
Boolean B = buttons.get(1);
Boolean X = buttons.get(2);
Boolean Y = buttons.get(3);
Boolean L1 = buttons.get(4);
Boolean R1 = buttons.get(5);
Boolean eStop = buttons.get(8); //急停
//底盘升降
Boolean eStop = buttons.get(8); // 急停
// 底盘升降
int chassisLiftFlag = 0;
if (A & !B) {
chassisLiftFlag = 1;
@@ -36,7 +40,7 @@ public class ControlUtils {
chassisLiftFlag = 2;
}
state.setChassisLiftFlag((byte) chassisLiftFlag);
//割刀加减速
// 割刀加减速
int mowerSpeedFlag = 0;
if (X & !Y) {
mowerSpeedFlag = 1;
@@ -44,7 +48,7 @@ public class ControlUtils {
mowerSpeedFlag = 2;
}
state.setMowerSpeedFlag((byte) mowerSpeedFlag);
//点火熄火
// 点火熄火
int ignitionFlag = 0;
if (L1 & !R1) {
ignitionFlag = 1;
@@ -55,26 +59,29 @@ public class ControlUtils {
int emergencyStopFlag = eStop ? 1 : 0;
state.setEmergencyStopFlag((byte) emergencyStopFlag);
// val toBytes = state.toBytes()
// println("toBytes==" + LawnMoverProtocolEntity.printBytesToHex(toBytes))
// val toBytes = state.toBytes()
// println("toBytes==" + LawnMoverProtocolEntity.printBytesToHex(toBytes))
return state;
}
public static byte[] buildControlCommand(LawnMoverProtocolEntity entity) {
ByteBuffer buffer = ByteBuffer.allocate(15);
ByteBuffer buffer = ByteBuffer.allocate(27); // 15
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.put(entity.getHeader1());
buffer.put(entity.getHeader2());
buffer.put(entity.getCommandType());
buffer.putShort(entity.getLeftWheelSpeed());//左轮速度
buffer.putShort(entity.getRightWheelSpeed());//右轮速度
// buffer.putShort(entity.getLeftWheelSpeed());//左轮速度
// buffer.putShort(entity.getRightWheelSpeed());//右轮速度
buffer.put(entity.getChassisLiftFlag());//底盘升降
buffer.put(entity.getMowerSpeedFlag());//割刀加减速
buffer.put(entity.getIgnitionFlag());//点火状态
buffer.put(entity.getEmergencyStopFlag());//急停
buffer.putDouble(entity.getAngularSpeed());// 左轮速度
buffer.putDouble(entity.getLinearSpeed());// 右轮速度
buffer.put(entity.getChassisLiftFlag());// 底盘升降
buffer.put(entity.getMowerSpeedFlag());// 割刀加减速
buffer.put(entity.getIgnitionFlag());// 点火状态
buffer.put(entity.getEmergencyStopFlag());// 急停
buffer.put((byte) 0x00);
buffer.put((byte) 0x00);

View File

@@ -2,6 +2,7 @@ package com.maibu.websocket;
import com.maibu.constant.CommandConstant;
import com.maibu.common.MiddleConstant;
import com.maibu.core.business.LawnMoverProtocolEntity;
import com.maibu.core.enums.CommandRequestType;
import com.maibu.core.enums.MesType;
import com.maibu.dto.*;
@@ -36,7 +37,6 @@ import java.util.concurrent.TimeUnit;
public class WebsocketHandler extends WebSocketServer {
private static final Logger logger = LoggerFactory.getLogger(WebsocketHandler.class);
// 存储客户端连接:key=WebSocket连接对象,value=最后一次活跃时间(毫秒)
private final Map<WebSocket, Long> clientActiveTimeMap = new ConcurrentHashMap<>();
// 空闲超时时间(10秒):超过10秒无消息则判定为断开
@@ -44,17 +44,17 @@ public class WebsocketHandler extends WebSocketServer {
// 定时检测线程池(每隔10秒检测一次空闲连接)
private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");
public WebsocketHandler(int port) {
super(new InetSocketAddress(port));
// 启动空闲连接检测任务
// startIdleCheckTask();
// startIdleCheckTask();
}
@Override
public void onOpen(WebSocket conn, ClientHandshake handshake) {
logger.info("客户端连接成功: remoteAddress={}, 当前在线数: {}", conn.getRemoteSocketAddress(), MiddleGlobalMemory.onlineSockets.size());
logger.info("客户端连接成功: remoteAddress={}, 当前在线数: {}", conn.getRemoteSocketAddress(),
MiddleGlobalMemory.onlineSockets.size());
}
@Override
@@ -62,7 +62,8 @@ public class WebsocketHandler extends WebSocketServer {
String key = MiddleGlobalMemory.findBySocket(conn);
if (key != null) {
releaseResource(key);
logger.info("客户端断开连接: remoteAddress={}, code={}, reason={}, 当前在线数: {}", conn.getRemoteSocketAddress(), code, reason, MiddleGlobalMemory.onlineSockets.size());
logger.info("客户端断开连接: remoteAddress={}, code={}, reason={}, 当前在线数: {}", conn.getRemoteSocketAddress(), code,
reason, MiddleGlobalMemory.onlineSockets.size());
}
MiddleGlobalMemory.removeByWebSocket(conn);
}
@@ -100,32 +101,34 @@ public class WebsocketHandler extends WebSocketServer {
client.initConnect(MiddleConstant.vertxIp, 9001);
}
} else if (MesType.path.equals(type)) {
//todo下发路径 预留
// todo下发路径 预留
pathDistribute();
} else if (MesType.remoteControl.equals(type)) {
//遥控
// 遥控
RemoteData remoteData = webMessageBaseDTO.getRemoteData();
if (remoteData != null) {
List<Double> axes = remoteData.getAxes();
List<Boolean> buttons = remoteData.getButtons();
Double y = axes.get(1) * 1000 * -1; //前后值反了
Double y = axes.get(1) * 1000 * -1; // 前后值反了
Double x = axes.get(2) * 1000;
int intX = (int) x.doubleValue();
int intY = (int) y.doubleValue();
// todo 计算线速度和角速度 还需要根据车型来给线速度 角速度范围最大值
MiddleGlobalMemory.initDiff();
LawnMoverProtocolEntity entity = ControlUtils.getCarControlMessage(MiddleGlobalMemory.diffSteer, intX, intY, buttons);
LawnMoverProtocolEntity entity = ControlUtils.getCarControlMessage(MiddleGlobalMemory.diffSteer,
intX, intY, buttons, 1, 2);
byte[] buf = ControlUtils.buildControlCommand(entity);
logger.debug("Netty客户端:{}发送遥控数据: {}", userName, JsonUtils.toJsonString(entity));
NettyClient nettyClient = MiddleGlobalMemory.nettyClientMap.get(key);
if (nettyClient != null && nettyClient.isConnected()) {
// if (!nettyClient.isConnected()) nettyClient.connect();
// if (!nettyClient.isConnected()) nettyClient.connect();
nettyClient.sendData(buf);
}
}
}
// else if(MesType.heartbeat.equals(type)){
// clientActiveTimeMap.put(conn,System.currentTimeMillis());
// }
// else if(MesType.heartbeat.equals(type)){
// clientActiveTimeMap.put(conn,System.currentTimeMillis());
// }
else if (MesType.switchPermission.equals(type)) {
DeviceRequestDTO requestDTO = new DeviceRequestDTO();
requestDTO.setRequest(CommandRequestType.switch_control);
@@ -138,7 +141,7 @@ public class WebsocketHandler extends WebSocketServer {
ByteBuf buf = Unpooled.buffer();
CommandUtils.buildCommand(buf, requestDTO, CommandConstant.interaction);
if (nettyClient != null && nettyClient.isConnected()) {
// if (!nettyClient.isConnected()) nettyClient.connect();
// if (!nettyClient.isConnected()) nettyClient.connect();
nettyClient.sendData(buf.array());
}
} else if (MesType.switchResult.equals(type)) {
@@ -156,7 +159,7 @@ public class WebsocketHandler extends WebSocketServer {
ByteBuf buf = Unpooled.buffer();
CommandUtils.buildCommand(buf, requestDTO, CommandConstant.interaction);
if (nettyClient != null && nettyClient.isConnected()) {
// if (!nettyClient.isConnected()) nettyClient.connect();
// if (!nettyClient.isConnected()) nettyClient.connect();
nettyClient.sendData(buf.array());
}
}
@@ -186,7 +189,8 @@ public class WebsocketHandler extends WebSocketServer {
@Override
public void onError(WebSocket conn, Exception ex) {
String key = MiddleGlobalMemory.findBySocket(conn);
logger.error("WebSocket错误: clientId={}, remoteAddress={}, error={}", key, conn != null ? conn.getRemoteSocketAddress() : null, ex.getMessage(), ex);
logger.error("WebSocket错误: clientId={}, remoteAddress={}, error={}", key,
conn != null ? conn.getRemoteSocketAddress() : null, ex.getMessage(), ex);
// 发生错误时清理连接
if (key != null) {
releaseResource(key);
@@ -222,7 +226,6 @@ public class WebsocketHandler extends WebSocketServer {
logger.debug("广播消息给所有客户端: message={}, 客户端数量: {}", message, connections.size());
}
// 核心:定时检测空闲连接(每隔10秒执行一次)
private void startIdleCheckTask() {
executor.scheduleAtFixedRate(() -> {
@@ -261,21 +264,21 @@ public class WebsocketHandler extends WebSocketServer {
}
}
}
// clientActiveTimeMap.clear(); // 清空空闲记录
//
// // 2. 关闭定时检测线程池(核心:终止非守护线程)
// if (!idleCheckExecutor.isShutdown()) {
// idleCheckExecutor.shutdownNow(); // 立即关闭线程池
// try {
// // 等待线程池终止,最多等3秒
// if (!idleCheckExecutor.awaitTermination(3, TimeUnit.SECONDS)) {
// logger.warn("空闲检测线程池未正常终止");
// }
// } catch (InterruptedException e) {
// idleCheckExecutor.shutdownNow();
// Thread.currentThread().interrupt();
// }
// }
// clientActiveTimeMap.clear(); // 清空空闲记录
//
// // 2. 关闭定时检测线程池(核心:终止非守护线程)
// if (!idleCheckExecutor.isShutdown()) {
// idleCheckExecutor.shutdownNow(); // 立即关闭线程池
// try {
// // 等待线程池终止,最多等3秒
// if (!idleCheckExecutor.awaitTermination(3, TimeUnit.SECONDS)) {
// logger.warn("空闲检测线程池未正常终止");
// }
// } catch (InterruptedException e) {
// idleCheckExecutor.shutdownNow();
// Thread.currentThread().interrupt();
// }
// }
// 3. 批量关闭所有Netty客户端连接
for (NettyClient client : MiddleGlobalMemory.nettyClientMap.values()) {
@@ -300,5 +303,3 @@ public class WebsocketHandler extends WebSocketServer {
}
}
}