This commit is contained in:
2026-06-03 16:53:02 +08:00
parent f02329b2d0
commit 133a2bd223
31 changed files with 929 additions and 296 deletions

View File

@@ -14,6 +14,7 @@ import org.springframework.data.annotation.Transient;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* 设备对象 iot_device
@@ -106,7 +107,7 @@ public class Device extends BaseDO {
private Integer onlineStatus;
@TableField(exist = false)
private Integer feStatus; //前端大屏显示 1任务中/2空闲/3离线
private Integer feStatus; //前端大屏显示 1任务中/2空闲
/**
* wifi信号强度(信号极好4格[-55— 0],信号好3格[-70— -55],信号一般2格[-85— -70],信号差1格[-100— -85])
@@ -223,4 +224,7 @@ public class Device extends BaseDO {
return lastRunningStatus != null ? lastRunningStatus : new DeviceRunningStatusHistory();
}
@TableField(exist = false)
private Map<Integer,Integer> statusStatistics;
}

View File

@@ -5,14 +5,19 @@ 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.maibu.core.domain.OrgBaseDO;
import com.maibu.core.enums.DeviceTaskStaus;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDate;
import java.time.LocalDateTime;
@EqualsAndHashCode(callSuper = true)
@Data
@TableName(value = "iot_device_plan", autoResultMap = true)
public class DevicePlan {
public class DevicePlan extends OrgBaseDO {
@TableId(type = IdType.AUTO)
private Long id;
@@ -31,25 +36,15 @@ public class DevicePlan {
private Long routeId; // 线路id
private Long userId; // 用户id
@TableField(typeHandler = JacksonTypeHandler.class)
private DeviceTaskPlanRule deviceTaskPlanRules; //计划
private LocalDateTime createTime;
private String createBy;
private LocalDateTime updateTime;
private String updateBy;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime finishTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime startTime; //开始时间 时分秒
private int delFlag = 0; //删除状态 1删除
@TableField(exist = false)
private boolean dayTriggerFlag = false; //当天的任务是否已经生成
}

View File

@@ -4,14 +4,18 @@ 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.BaseDO;
import com.maibu.core.domain.OrgBaseDO;
import com.maibu.core.enums.DeviceTaskStaus;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
@EqualsAndHashCode(callSuper = true)
@Data
@TableName("iot_device_task")
public class DevicePlanTask {
public class DevicePlanTask extends OrgBaseDO {
@TableId(type = IdType.AUTO)
private Long id;
@@ -31,8 +35,6 @@ public class DevicePlanTask {
private LocalDateTime startTime; //开始时间 时分秒
private LocalDateTime createTime; //创建时间
private String taskType; //任务类型分类
}

View File

@@ -0,0 +1,17 @@
package com.maibu.core.business;
import com.maibu.core.business.path.LatAndLngEntity;
import lombok.Data;
import java.util.List;
@Data
public class PlanPath {
private String name;
private List<LatAndLngEntity> path;
private List<LatAndLngEntity> outer;
private String img;
private String planModel;
}

View File

@@ -1,15 +1,30 @@
package com.maibu.core.business;
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.core.domain.OrgBaseDO;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
@Data
public class WorkRecord implements Serializable {
@EqualsAndHashCode(callSuper = true)
@TableName(value = "iot_work_record", autoResultMap = true)
public class WorkRecord extends OrgBaseDO {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.AUTO)
private Long id;
private String workName;
private Long userId;
private String jsonData;
// private String jsonData;
@TableField(typeHandler = JacksonTypeHandler.class)
private PlanPath jsonData; //计划
private String imgUrl;
}

View File

@@ -1,12 +1,26 @@
package com.maibu.core.business.device;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.maibu.core.business.DevicePlanTask;
import com.maibu.core.business.DeviceRunStatistics;
import com.maibu.core.business.PlanPath;
import com.maibu.core.business.WorkRecord;
import com.maibu.core.business.path.RoutePlanSendEntity;
import com.maibu.core.enums.ConnectorType;
import com.maibu.core.enums.DeviceTaskStaus;
import com.maibu.mapper.DevicePlanTaskMapper;
import com.maibu.mapper.WorkRecordMapper;
import com.maibu.memory.GlobalMemory;
import com.maibu.memory.SiteMemory;
import com.maibu.utils.spring.SpringUtils;
import io.netty.buffer.Unpooled;
import lombok.Data;
import lombok.EqualsAndHashCode;
import com.maibu.core.business.path.LatAndLngEntity;
import org.springframework.util.CollectionUtils;
import java.time.LocalDateTime;
import java.util.LinkedList;
import java.util.Queue;
@@ -20,14 +34,24 @@ public class NettyDevice extends Connector {
private Long currentTaskId; //当前执行的任务;
private DevicePlanTask task; //当前执行的任务;
private Integer onlineStatus; //在线状态 0 离线 1在线
private LatAndLngEntity currentPoint; //执行任务的当前点
private Queue<LatAndLngEntity> locationQueue = new LinkedList<>();
private DeviceRunStatistics latestStatus;
private Long latestLoginTime;
private boolean executingTask = false;
private DevicePlanTaskMapper devicePlanTaskMapper = SpringUtils.getBean(DevicePlanTaskMapper.class);
private WorkRecordMapper workRecordMapper = SpringUtils.getBean(WorkRecordMapper.class);
public NettyDevice(String connectorId) {
super(connectorId);
this.deviceType = ConnectorType.MASTER; // 默认上位机角色
@@ -38,5 +62,105 @@ public class NettyDevice extends Connector {
this.deviceType = deviceType;
}
public void sendNextPoint() {
if (task.getTaskStaus().equals(DeviceTaskStaus.PAUSE)) return;
if (!locationQueue.isEmpty()) {
LatAndLngEntity entity = locationQueue.poll();
if (entity != null) {
RoutePlanSendEntity routePlanSendEntity = new RoutePlanSendEntity();
routePlanSendEntity.setCommandType((byte) 0x01);
routePlanSendEntity.setPointCounts((short) 1);
routePlanSendEntity.setTargetLatitude(entity.getLat());
routePlanSendEntity.setTargetLongitude(entity.getLng());
routePlanSendEntity.setSpeed((short) 1000);
byte[] bytes = routePlanSendEntity.toBytes();
System.out.println("====Lat:" + entity.getLat() + " ====Lng:" + entity.getLng());
currentPoint = entity;
if (this.getChannel() != null && this.getChannel().isActive()) {
this.getChannel().writeAndFlush(Unpooled.wrappedBuffer(bytes));
}
}
}
}
public synchronized void finishTask() {
if (executingTask) {
task.setTaskStaus(DeviceTaskStaus.FINISH);
task.setFinishTime(LocalDateTime.now());
devicePlanTaskMapper.saveOrUpdate(task);
SiteMemory siteMemory = GlobalMemory.getSiteMemory(task.getOrgId(), task.getSiteId());
siteMemory.removeDevicePlanExecute(task.getDeviceId());
task = null;
currentTaskId = null;
executingTask = false;
}
}
public synchronized boolean cancelTask() {
if (executingTask) {
task.setTaskStaus(DeviceTaskStaus.CANCELED);
task.setUpdateTime(LocalDateTime.now());
devicePlanTaskMapper.saveOrUpdate(task);
SiteMemory siteMemory = GlobalMemory.getSiteMemory(task.getOrgId(), task.getSiteId());
siteMemory.removeDevicePlanExecute(task.getDeviceId());
task = null;
currentTaskId = null;
locationQueue.clear();
executingTask = false;
return true;
} else {
return false;
}
}
//todo 优化
public synchronized boolean pauseTask() {
if (executingTask) {
task.setTaskStaus(DeviceTaskStaus.PAUSE);
executingTask = false;
return true;
} else {
return false;
}
}
//todo 优化
public synchronized boolean recoveryTask() {
if (!executingTask) {
task.setTaskStaus(DeviceTaskStaus.EXECUTING);
executingTask = true;
sendNextPoint();
return true;
}else {
return false;
}
}
public void startTask() {
try {
if (executingTask) return;
if (task != null) {
Long routeId = task.getRouteId();
WorkRecord workRecord = workRecordMapper.selectById(routeId);
if (workRecord != null) {
PlanPath planPath = workRecord.getJsonData();
if (planPath != null && !CollectionUtils.isEmpty(planPath.getPath())) {
planPath.getPath().forEach(x -> {
locationQueue.offer(x);
});
}
}
task.setStartTime(LocalDateTime.now());
}
executingTask = true;
sendNextPoint();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -42,7 +42,7 @@ public class BaseDO implements Serializable {
/** 逻辑删除 */
@ApiModelProperty("逻辑删除")
@TableLogic
private Boolean delFlag;
private Boolean delFlag = false;
/** 请求参数 */
@TableField(exist = false)

View File

@@ -0,0 +1,24 @@
package com.maibu.core.domain;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 拓展多租户的 BaseDO 基类
*
* @author fastbee
*/
@Data
@EqualsAndHashCode(callSuper = true)
public abstract class OrgBaseDO extends BaseDO {
/**
* 多租户编号
*/
private Long orgId;
private Long siteId;
private Long userId;
}

View File

@@ -10,6 +10,7 @@ public enum DeviceTaskStaus {
NEW("NEW","新建"),
EXECUTING("EXECUTING","执行中"),
PAUSE("PAUSE","暂停中"),
CANCELED("CANCELED","已取消"),
FINISH("FINISH","执行成功"),
FAILED("FAILED","执行失败");

View File

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

View File

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

View File

@@ -0,0 +1,10 @@
package com.maibu.mapper;
import com.maibu.core.business.WorkRecord;
import com.maibu.mybatis.mapper.BaseMapperX;
public interface WorkRecordMapper extends BaseMapperX<WorkRecord> {
}

View File

@@ -9,6 +9,7 @@ import lombok.Data;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -35,7 +36,7 @@ public class GlobalMemory {
public static ConcurrentHashMap<Long, IoTCommonProduct> productMap = new ConcurrentHashMap<>();
public SiteMemory getSiteMemory(Long orgId, Long siteId) {
public static SiteMemory getSiteMemory(Long orgId, Long siteId) {
if (orgId != null && siteId != null) {
Map<Long, SiteMemory> siteMemories = orgAttrs.get(orgId);
if (!CollectionUtils.isEmpty(siteMemories)) {
@@ -45,13 +46,23 @@ public class GlobalMemory {
return null;
}
public static List<SiteMemory> getAllSiteMemory() {
List<SiteMemory> list = new ArrayList<>();
orgAttrs.values().forEach(x -> {
list.addAll(x.values());
});
return list;
}
/**
* 新增场站内存
* @param orgId
* @param siteId
* @param siteMemory
*/
public void addSiteMemory(Long orgId, Long siteId, SiteMemory siteMemory) {
//todo 初始化
public static void addSiteMemory(Long orgId, Long siteId, SiteMemory siteMemory) {
if (orgId != null && siteId != null) {
Map<Long, SiteMemory> siteMemories = orgAttrs.get(orgId);
if (CollectionUtils.isEmpty(siteMemories)) {
@@ -67,7 +78,7 @@ public class GlobalMemory {
* @param orgId
* @param siteId
*/
public void removeSiteMemory(Long orgId, Long siteId) {
public static void removeSiteMemory(Long orgId, Long siteId) {
if (orgId != null && siteId != null) {
Map<Long, SiteMemory> siteMemories = orgAttrs.get(orgId);
if (!CollectionUtils.isEmpty(siteMemories)) {

View File

@@ -38,7 +38,6 @@ public class SiteMemory {
//key deviceId execute
public static ConcurrentHashMap<String, DevicePlanTask> devicePlanTaskExecuteMap = new ConcurrentHashMap<>();
public static void saveDevice(Device device) {
deviceMap.put(device.getSerialNumber(), device);
}
@@ -49,7 +48,7 @@ public class SiteMemory {
}
}
public static void addDevicePlan(DevicePlan devicePlan) {
public void addDevicePlan(DevicePlan devicePlan) {
if (devicePlan != null && !StringUtils.isEmpty(devicePlan.getDeviceId())) {
String deviceId = devicePlan.getDeviceId();
List<DevicePlan> list = devicePlanMap.get(deviceId);
@@ -66,7 +65,7 @@ public class SiteMemory {
}
public static void removeDevicePlan(DevicePlan devicePlan) {
public void removeDevicePlan(DevicePlan devicePlan) {
if (devicePlan != null && !StringUtils.isEmpty(devicePlan.getDeviceId())) {
String deviceId = devicePlan.getDeviceId();
List<DevicePlan> list = devicePlanMap.get(deviceId);
@@ -76,7 +75,7 @@ public class SiteMemory {
}
}
public static void addDevicePlanPrepare(DevicePlanTask devicePlanTask) {
public void addDevicePlanPrepare(DevicePlanTask devicePlanTask) {
if (devicePlanTask != null && !StringUtils.isEmpty(devicePlanTask.getDeviceId())) {
String deviceId = devicePlanTask.getDeviceId();
List<DevicePlanTask> list = devicePlanTaskPrepareMap.get(deviceId);
@@ -88,7 +87,7 @@ public class SiteMemory {
}
}
public static void removeDevicePlanPrepare(DevicePlanTask devicePlanTask) {
public void removeDevicePlanPrepare(DevicePlanTask devicePlanTask) {
if (devicePlanTask != null && !StringUtils.isEmpty(devicePlanTask.getDeviceId())) {
String deviceId = devicePlanTask.getDeviceId();
List<DevicePlanTask> list = devicePlanTaskPrepareMap.get(deviceId);
@@ -99,18 +98,33 @@ public class SiteMemory {
}
public static void addDevicePlanExecute(DevicePlanTask devicePlanTask) {
public void addDevicePlanExecute(DevicePlanTask devicePlanTask) {
if (devicePlanTask != null && !StringUtils.isEmpty(devicePlanTask.getDeviceId())) {
String deviceId = devicePlanTask.getDeviceId();
devicePlanTaskExecuteMap.put(deviceId, devicePlanTask);
}
}
public static void removeDevicePlanExecute(String deviceId) {
public void removeDevicePlanExecute(String deviceId) {
if (!StringUtils.isEmpty(deviceId)) {
devicePlanTaskExecuteMap.remove(deviceId);
}
}
public boolean hasTask(String deviceId) {
DevicePlanTask task = devicePlanTaskExecuteMap.get(deviceId);
List<DevicePlanTask> list = devicePlanTaskPrepareMap.get(deviceId);
return task != null || !CollectionUtils.isEmpty(list);
}
public DevicePlanTask getDeviceExecuteTask(String deviceId) {
if (!StringUtils.isEmpty(deviceId)) {
return devicePlanTaskExecuteMap.get(deviceId);
}
return null;
}
}