This commit is contained in:
2026-07-09 08:40:32 +08:00
parent caf4e2c415
commit 8f7a35e3c7
44 changed files with 1328 additions and 188 deletions

View File

@@ -1,6 +1,14 @@
package com.maibu.core.business;
import com.baomidou.mybatisplus.annotation.*;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
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.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.maibu.annotation.Excel;
@@ -11,12 +19,6 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.data.annotation.Transient;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* 设备对象 iot_device
@@ -228,4 +230,7 @@ public class Device extends BaseDO {
@TableField(exist = false)
private Map<Integer,Integer> statusStatistics;
@TableField(exist = false)
private DeviceRunParam runParam;
}

View File

@@ -1,25 +1,18 @@
package com.maibu.core.business;
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.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.maibu.annotation.Excel;
import com.maibu.core.domain.BaseDO;
import com.maibu.core.enums.ConnectType;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.data.annotation.Transient;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
@EqualsAndHashCode(callSuper = true)

View File

@@ -0,0 +1,27 @@
package com.maibu.core.business;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.maibu.core.domain.BaseDO;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
@TableName(value = "iot_device_run_param", autoResultMap = true)
public class DeviceRunParam extends BaseDO {
@TableId(type = IdType.AUTO)
private Long id;
private String deviceId;
private Long siteId;
private Long orgId;
private Double runSpeed;
}

View File

@@ -3,7 +3,10 @@ package com.maibu.core.business;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.maibu.core.enums.CompareEnum;import lombok.Data;
import com.maibu.core.enums.CompareEnum;
import com.maibu.core.enums.ErrorLevel;
import com.maibu.core.enums.ErrorSource;
import lombok.Data;
@Data
@@ -16,4 +19,8 @@ public class ErrorIdentificationStandard {
private CompareEnum compareType;
private String errorDescription;
private String errorName;
private ErrorSource errorSource;
private String errorCode;
private ErrorLevel errorLevel;
private String suggestion;
}

View File

@@ -1 +1,112 @@
package com.maibu.core.business;
package com.maibu.core.business;
import com.baomidou.mybatisplus.annotation.*;
import com.maibu.core.domain.TenantBaseDO;
import com.maibu.core.enums.ErrorLevel;
import com.maibu.core.enums.ErrorSource;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("iot_work_order")
@ApiModel("工单记录")
public class IOTWorkOrder extends TenantBaseDO {
@ApiModelProperty("主键 ID")
@TableId(type = IdType.AUTO)
private Long id;
@ApiModelProperty("工单编号")
private String orderNo;
@ApiModelProperty("工单标题")
private String orderTitle;
@ApiModelProperty("工单来源:1-AI 诊断 2-无人机巡检 3-清洗优化 4-视频监控 5-装备控制 6-人工创建 7-告警关联")
private Integer sourceType;
@ApiModelProperty("工单类型:1-巡检 2-检修 3-清洗 4-维修 5-运维")
private Integer orderType;
@ApiModelProperty("优先级:1-高 2-中 3-低")
private ErrorLevel priorityLevel;
@ApiModelProperty("关联设备 ID")
private String deviceId;
@ApiModelProperty("关联设备名称")
private String deviceName;
@ApiModelProperty("设备类型")
private ErrorSource deviceType;
@ApiModelProperty("关联场站 ID")
private Long siteId;
@ApiModelProperty("关联场站名称")
private String siteName;
@ApiModelProperty("关联组织 ID")
private Long orgId;
@ApiModelProperty("负责人 ID")
private Long assigneeId;
@ApiModelProperty("负责人姓名")
private String assigneeName;
@ApiModelProperty("协同人员 ID 列表 (逗号分隔)")
private String collaboratorIds;
@ApiModelProperty("协同人员姓名列表 (逗号分隔)")
private String collaboratorNames;
@ApiModelProperty("计划开始时间")
private LocalDateTime planStartTime;
@ApiModelProperty("计划结束时间")
private LocalDateTime planEndTime;
@ApiModelProperty("截止时间")
private LocalDateTime deadlineTime;
@ApiModelProperty("实际开始时间")
private LocalDateTime actualStartTime;
@ApiModelProperty("实际完成时间")
private LocalDateTime actualEndTime;
@ApiModelProperty("工单状态:1-待处理 2-已派发 3-执行中 4-已完成 5-已挂起")
private Integer orderStatus;
@ApiModelProperty("关联告警 ID")
private Long alarmId;
@ApiModelProperty("关联告警编号")
private String alarmNo;
@ApiModelProperty("任务描述")
private String taskDescription;
@ApiModelProperty("AI 建议")
private String aiSuggestion;
@ApiModelProperty("建议携带设备")
private String requiredEquipment;
@ApiModelProperty("预计影响发电量 (%)")
private BigDecimal estimatedImpact;
@ApiModelProperty("处理结果")
private String handleResult;
@ApiModelProperty("处理备注")
private String handleRemark;
}

View File

@@ -3,6 +3,9 @@ package com.maibu.core.business.alarm_center;
import com.baomidou.mybatisplus.annotation.*;
import com.maibu.annotation.Excel;
import com.maibu.core.domain.TenantBaseDO;
import com.maibu.core.enums.ErrorLevel;
import com.maibu.core.enums.ErrorSource;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@@ -27,7 +30,7 @@ public class Alarm extends TenantBaseDO {
@ApiModelProperty("告警级别:1-严重 2-重要 3-一般 4-提示")
@Excel(name = "告警级别", readConverterExp = "1=严重,2=重要,3=一般,4=提示")
private Integer alarmLevel;
private ErrorLevel alarmLevel;
@ApiModelProperty("告警类型:1-设备故障 2-数据异常 3-通信中断 4-性能告警 5-安全告警 6-环境告警")
@Excel(name = "告警类型", readConverterExp = "1=设备故障,2=数据异常,3=通信中断,4=性能告警,5=安全告警,6=环境告警")
@@ -43,7 +46,7 @@ public class Alarm extends TenantBaseDO {
@ApiModelProperty("设备ID")
@Excel(name = "设备ID", cellType = Excel.ColumnType.NUMERIC)
private Long deviceId;
private String deviceId;
@ApiModelProperty("设备名称")
@Excel(name = "设备名称")
@@ -51,7 +54,7 @@ public class Alarm extends TenantBaseDO {
@ApiModelProperty("设备类型:1-逆变器 2-汇流箱 3-组串 4-气象站 5-电表 6-智能汇流箱 7-箱变 8-SVG")
@Excel(name = "设备类型", readConverterExp = "1=逆变器,2=汇流箱,3=组串,4=气象站,5=电表,6=智能汇流箱,7=箱变,8=SVG")
private Integer deviceType;
private ErrorSource deviceType;
@ApiModelProperty("告警发生时间")
@Excel(name = "告警时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")

View File

@@ -0,0 +1,43 @@
package com.maibu.core.business.alarm_center;
import java.time.LocalDateTime;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.maibu.core.enums.ErrorLevel;
import lombok.Data;
@Data
public class AlarmMessage {
/** 告警等级 P1/P2/P3/P4 */
private ErrorLevel level;
/** 告警名称 */
private String name;
/** 告警code */
private String no;
/** 告警时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime time;
/** 设备名称 */
private String deviceName;
/** 设备SN */
private String deviceSn;
/** 告警内容 */
private String content;
/** 所在位置 */
private String location;
/** 处理建议 */
private String suggestion;
/** 状态 */
private String status;
}

View File

@@ -7,7 +7,9 @@ import java.util.Queue;
import org.springframework.util.CollectionUtils;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.maibu.core.business.Device;
import com.maibu.core.business.DevicePlanTask;
import com.maibu.core.business.DeviceRunParam;
import com.maibu.core.business.DeviceRunStatistics;
import com.maibu.core.business.PlanPath;
import com.maibu.core.business.WorkRecord;
@@ -50,6 +52,8 @@ public class NettyDevice extends Connector {
private Long latestLoginTime;
private boolean executingTask = false;
// todo 注册的时候更新 siteId
private Device device;
@JsonIgnore
private DevicePlanTaskMapper devicePlanTaskMapper = SpringUtils.getBean(DevicePlanTaskMapper.class);
@@ -74,7 +78,17 @@ public class NettyDevice extends Connector {
if (!locationQueue.isEmpty()) {
LatAndLngEntity entity = locationQueue.poll();
if (entity != null) {
sendPathCommand((byte) 0x01, (short) 1, entity.getLat(), entity.getLng(), (short) 1000);
short speed = 1000;
if (device != null) {
SiteMemory siteMemory = GlobalMemory.getSiteMemory(device.getOrgId(), device.getSiteId());
if (siteMemory != null) {
DeviceRunParam deviceRunParam = siteMemory.getDeviceRunParam(device.getSerialNumber());
if (deviceRunParam != null && deviceRunParam.getRunSpeed() != null) {
speed = deviceRunParam.getRunSpeed().shortValue();
}
}
}
sendPathCommand((byte) 0x01, (short) 1, entity.getLat(), entity.getLng(), speed);
currentPoint = entity;
}
}

View File

@@ -1,14 +1,14 @@
package com.maibu.core.business.dto;
import java.time.LocalDateTime;
import org.springframework.format.annotation.DateTimeFormat;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
@Data
@ApiModel("告警查询参数")
@@ -46,7 +46,7 @@ public class AlarmQueryDTO {
private String keyword;
@ApiModelProperty("设备ID")
private Long deviceId;
private String deviceId;
@ApiModelProperty("场站ID")
private Long siteId;

View File

@@ -31,6 +31,14 @@ public class SysSite extends BaseDO {
private String remark;
private Integer status;
private Integer dingTalkPush; // 钉钉推送 0-关闭 1-开启
private Integer emailPush;// 邮件推送 0-关闭 1-开启
private Integer alertPush;// 告警弹窗推送 0-关闭 1-开启
private String dingTalkSecret; // 钉钉推送密钥
private String dingTalkToken; // 钉钉推送token
}

View File

@@ -0,0 +1,21 @@
package com.maibu.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum ErrorLevel {
INFO("INFO", "信息"),
WARNING("WARNING", "警告"),
ERROR("ERROR", "错误");
/** 错误来源值(存储在数据库中) */
private final String value;
/** 错误来源名称(用于前端显示) */
private final String name;
}

View File

@@ -0,0 +1,24 @@
package com.maibu.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum ErrorSource {
/** 无人机 */
UAV("UAV", "无人机"),
/** 割草机 */
MOWER("MOWER", "割草机"),
/** 其他 */
OTHERS("OTHERS", "其他");
/** 错误来源值(存储在数据库中) */
private final String value;
/** 错误来源名称(用于前端显示) */
private final String name;
}

View File

@@ -1,11 +1,15 @@
package com.maibu.influxdb;
import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.util.CollectionUtils;
import com.influxdb.query.FluxRecord;
import com.influxdb.query.FluxTable;
import com.maibu.influxdb.inter.InfluxField;
import com.maibu.influxdb.inter.InfluxMeasurement;
import com.maibu.influxdb.inter.InfluxTag;
@@ -152,4 +156,101 @@ public class MowerRealTimeData {
return data;
}
// 适用于flux默认行存储结构,按device分组合并成一条实体
public static Map<String, List<MowerRealTimeData>> fluxTableMergeToEntity(List<FluxTable> tables) {
Map<String, MowerRealTimeData> tempMap = new HashMap<>();
for (FluxTable table : tables) {
for (FluxRecord record : table.getRecords()) {
String deviceId = (String) record.getValueByKey("deviceId");
Instant time = record.getTime();
String fieldName = (String) record.getValueByKey("_field");
String fieldVal = (String) record.getValueByKey("_value");
// 唯一key:设备+时间
String key = deviceId + "_" + time.toEpochMilli();
MowerRealTimeData data = tempMap.getOrDefault(key, new MowerRealTimeData());
data.setTime(time);
data.setDeviceId(deviceId);
// 根据_field填充对应属性
switch (fieldName) {
case "voltage":
data.setVoltage(fieldVal);
break;
case "leftTargetSpeed":
data.setLeftTargetSpeed(fieldVal);
break;
case "rightTargetSpeed":
data.setRightTargetSpeed(fieldVal);
break;
case "leftMeasureSpeed":
data.setLeftMeasureSpeed(fieldVal);
break;
case "rightMeasureSpeed":
data.setRightMeasureSpeed(fieldVal);
break;
case "leftMotorTemp":
data.setLeftMotorTemp(fieldVal);
break;
case "rightMotorTemp":
data.setRightMotorTemp(fieldVal);
break;
case "leftCurrent":
data.setLeftCurrent(fieldVal);
break;
case "rightCurrent":
data.setRightCurrent(fieldVal);
break;
case "chipTemp":
data.setChipTemp(fieldVal);
break;
case "yaw":
data.setYaw(fieldVal);
break;
case "pitch":
data.setPitch(fieldVal);
break;
case "roll":
data.setRoll(fieldVal);
break;
case "satelliteCnt":
data.setSatelliteCnt(fieldVal);
break;
case "qual":
data.setQual(fieldVal);
break;
case "headingStatus":
data.setHeadingStatus(fieldVal);
break;
case "latitude":
data.setLatitude(fieldVal);
break;
case "longitude":
data.setLongitude(fieldVal);
break;
case "cuttingSpeed":
data.setCuttingSpeed(fieldVal);
break;
case "controlMode":
data.setControlMode(fieldVal);
break;
case "battery":
data.setBattery(fieldVal);
break;
case "workingArea":
data.setWorkingArea(fieldVal);
break;
case "obstacleSign":
data.setObstacleSign(fieldVal);
break;
}
tempMap.put(key, data);
}
}
// 转List返回
return tempMap.values().stream()
.collect(Collectors.groupingBy(MowerRealTimeData::getDeviceId));
}
}

View File

@@ -0,0 +1,12 @@
package com.maibu.mapper;
import org.springframework.stereotype.Repository;
import com.maibu.core.business.DeviceRunParam;
import com.maibu.mybatis.mapper.BaseMapperX;
@Repository
public interface DeviceRunParamMapper extends BaseMapperX<DeviceRunParam> {
}

View File

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

View File

@@ -1,8 +1,8 @@
package com.maibu.memory;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
@@ -13,8 +13,11 @@ import org.springframework.util.CollectionUtils;
import com.maibu.core.business.Device;
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.domain.entity.SysSite;
import com.maibu.core.enums.ErrorSource;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
@@ -24,19 +27,23 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
public class SiteMemory {
public List<ErrorIdentificationStandard> standard = null;
public Map<ErrorSource, List<ErrorIdentificationStandard>> standards = null;
public Long siteId;
public SysSite sysSite;
public static final ConcurrentHashMap<String, DeviceRunParam> deviceRunParamMap = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<String, Device> deviceMap = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<String, IoTCommonDevice> commonDeviceMap = new ConcurrentHashMap<>();
//key deviceId
// key deviceId
public static ConcurrentHashMap<String, List<DevicePlan>> devicePlanMap = new ConcurrentHashMap<>();
//key deviceId prepare
// key deviceId prepare
public static ConcurrentHashMap<String, List<DevicePlanTask>> devicePlanTaskPrepareMap = new ConcurrentHashMap<>();
//key deviceId execute
// key deviceId execute
public static ConcurrentHashMap<String, DevicePlanTask> devicePlanTaskExecuteMap = new ConcurrentHashMap<>();
public static void saveDevice(Device device) {
@@ -49,6 +56,23 @@ public class SiteMemory {
}
}
public void saveDeviceRunParam(DeviceRunParam deviceRunParam) {
deviceRunParamMap.put(deviceRunParam.getDeviceId(), deviceRunParam);
}
public DeviceRunParam getDeviceRunParam(String deviceId) {
if (!StringUtils.isEmpty(deviceId)) {
return deviceRunParamMap.get(deviceId);
}
return null;
}
public void removeDeviceRunParam(String deviceId) {
if (!StringUtils.isEmpty(deviceId)) {
deviceRunParamMap.remove(deviceId);
}
}
public void addDevicePlan(DevicePlan devicePlan) {
if (devicePlan != null && !StringUtils.isEmpty(devicePlan.getDeviceId())) {
String deviceId = devicePlan.getDeviceId();
@@ -65,7 +89,6 @@ public class SiteMemory {
}
}
public void removeDevicePlan(DevicePlan devicePlan) {
if (devicePlan != null && !StringUtils.isEmpty(devicePlan.getDeviceId())) {
String deviceId = devicePlan.getDeviceId();
@@ -98,7 +121,6 @@ public class SiteMemory {
}
}
public void addDevicePlanExecute(DevicePlanTask devicePlanTask) {
if (devicePlanTask != null && !StringUtils.isEmpty(devicePlanTask.getDeviceId())) {
String deviceId = devicePlanTask.getDeviceId();
@@ -120,12 +142,9 @@ public class SiteMemory {
public DevicePlanTask getDeviceExecuteTask(String deviceId) {
if (!StringUtils.isEmpty(deviceId)) {
return devicePlanTaskExecuteMap.get(deviceId);
return devicePlanTaskExecuteMap.get(deviceId);
}
return null;
}
}

View File

@@ -7,4 +7,9 @@ public class MqttTopic {
public static final String DEVICE_TASK_STATUS_TOPIC = "task/%s/status"; // 任务状态 完成、取消、暂停、继续
public static final String DEVICE_TASK_ARRIVE_TOPIC = "task/%s/arrive"; // 任务到达
public static final String DEVICE_ERROR_PUSH_TOPIC = "device/%s/error"; // 设备错误推送
}