diff --git a/iot-access/access-api/src/main/java/com/njcn/access/api/AskDeviceDataFeignClient.java b/iot-access/access-api/src/main/java/com/njcn/access/api/AskDeviceDataFeignClient.java new file mode 100644 index 0000000..b738bde --- /dev/null +++ b/iot-access/access-api/src/main/java/com/njcn/access/api/AskDeviceDataFeignClient.java @@ -0,0 +1,34 @@ +package com.njcn.access.api; + +import com.njcn.access.api.fallback.AskDeviceDataClientFallbackFactory; +import com.njcn.common.pojo.constant.ServerInfo; +import com.njcn.common.pojo.response.HttpResult; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * @author xy + */ +@FeignClient(value = ServerInfo.ACCESS_BOOT, path = "/askDeviceData", fallbackFactory = AskDeviceDataClientFallbackFactory.class,contextId = "askDeviceData") +public interface AskDeviceDataFeignClient { + + @PostMapping("/askDeviceRootPath") + HttpResult askDeviceRootPath(@RequestParam("nDid") String nDid); + + @PostMapping("/askDeviceFileOrDir") + HttpResult askDeviceFileOrDir(@RequestParam("nDid") String nDid, @RequestParam("name") String name); + + @PostMapping("/downloadFile") + HttpResult downloadFile(@RequestParam("nDid") String nDid, @RequestParam("name") String name, @RequestParam("size") Integer size, @RequestParam("fileCheck") String fileCheck); + + @PostMapping("/rebootDevice") + HttpResult rebootDevice(@RequestParam("nDid") String nDid); + + @PostMapping("/createFolder") + HttpResult createFolder(@RequestParam("nDid") String nDid, @RequestParam("path") String path); + + @PostMapping("/deleteFolder") + HttpResult deleteFolder(@RequestParam("nDid") String nDid, @RequestParam("path") String path); + +} diff --git a/iot-access/access-api/src/main/java/com/njcn/access/api/CsSoftInfoFeignClient.java b/iot-access/access-api/src/main/java/com/njcn/access/api/CsSoftInfoFeignClient.java new file mode 100644 index 0000000..8f2b1df --- /dev/null +++ b/iot-access/access-api/src/main/java/com/njcn/access/api/CsSoftInfoFeignClient.java @@ -0,0 +1,20 @@ +package com.njcn.access.api; + +import com.njcn.access.api.fallback.CsSoftInfoClientFallbackFactory; +import com.njcn.access.pojo.po.CsSoftInfoPO; +import com.njcn.common.pojo.constant.ServerInfo; +import com.njcn.common.pojo.response.HttpResult; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * @author xy + */ +@FeignClient(value = ServerInfo.ACCESS_BOOT, path = "/csSoftInfo", fallbackFactory = CsSoftInfoClientFallbackFactory.class,contextId = "csSoftInfo") +public interface CsSoftInfoFeignClient { + + @PostMapping("/findSoftInfo") + HttpResult findSoftInfo(@RequestParam("id") String id); + +} diff --git a/iot-access/access-api/src/main/java/com/njcn/access/api/fallback/AskDeviceDataClientFallbackFactory.java b/iot-access/access-api/src/main/java/com/njcn/access/api/fallback/AskDeviceDataClientFallbackFactory.java new file mode 100644 index 0000000..0ff180f --- /dev/null +++ b/iot-access/access-api/src/main/java/com/njcn/access/api/fallback/AskDeviceDataClientFallbackFactory.java @@ -0,0 +1,72 @@ +package com.njcn.access.api.fallback; + +import com.njcn.access.api.AskDeviceDataFeignClient; +import com.njcn.common.pojo.enums.response.CommonResponseEnum; +import com.njcn.common.pojo.exception.BusinessException; +import com.njcn.common.pojo.response.HttpResult; +import com.njcn.redis.utils.RedisUtil; +import feign.hystrix.FallbackFactory; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * @author xy + */ +@Slf4j +@Component +public class AskDeviceDataClientFallbackFactory implements FallbackFactory { + + @Resource + private RedisUtil redisUtil; + + @Override + public AskDeviceDataFeignClient create(Throwable cause) { + //判断抛出异常是否为解码器抛出的业务异常 + Enum exceptionEnum = CommonResponseEnum.SERVICE_FALLBACK; + if (cause.getCause() instanceof BusinessException) { + BusinessException businessException = (BusinessException) cause.getCause(); + } + Enum finalExceptionEnum = exceptionEnum; + return new AskDeviceDataFeignClient() { + @Override + public HttpResult askDeviceRootPath(String nDid) { + log.error("{}异常,降级处理,异常为:{}","平台询问装置报文",cause.toString()); + throw new BusinessException(finalExceptionEnum); + } + + @Override + public HttpResult askDeviceFileOrDir(String nDid, String name) { + log.error("{}异常,降级处理,异常为:{}","设备文件/目录信息询问",cause.toString()); + throw new BusinessException(finalExceptionEnum); + } + + @Override + public HttpResult downloadFile(String nDid, String name, Integer size, String fileCheck) { + log.error("{}异常,降级处理,异常为:{}","文件下载",cause.toString()); + redisUtil.delete("fileDowning:" + nDid); + redisUtil.delete("fileCheck"+name); + throw new BusinessException(finalExceptionEnum); + } + + @Override + public HttpResult rebootDevice(String nDid) { + log.error("{}异常,降级处理,异常为:{}","设备重启",cause.toString()); + throw new BusinessException(finalExceptionEnum); + } + + @Override + public HttpResult createFolder(String nDid, String path) { + log.error("{}异常,降级处理,异常为:{}","创建文件夹",cause.toString()); + throw new BusinessException(finalExceptionEnum); + } + + @Override + public HttpResult deleteFolder(String nDid, String path) { + log.error("{}异常,降级处理,异常为:{}","删除文件夹",cause.toString()); + throw new BusinessException(finalExceptionEnum); + } + }; + } +} diff --git a/iot-access/access-api/src/main/java/com/njcn/access/api/fallback/CsSoftInfoClientFallbackFactory.java b/iot-access/access-api/src/main/java/com/njcn/access/api/fallback/CsSoftInfoClientFallbackFactory.java new file mode 100644 index 0000000..303931a --- /dev/null +++ b/iot-access/access-api/src/main/java/com/njcn/access/api/fallback/CsSoftInfoClientFallbackFactory.java @@ -0,0 +1,34 @@ +package com.njcn.access.api.fallback; + +import com.njcn.access.api.CsSoftInfoFeignClient; +import com.njcn.access.pojo.po.CsSoftInfoPO; +import com.njcn.common.pojo.enums.response.CommonResponseEnum; +import com.njcn.common.pojo.exception.BusinessException; +import com.njcn.common.pojo.response.HttpResult; +import feign.hystrix.FallbackFactory; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * @author xy + */ +@Slf4j +@Component +public class CsSoftInfoClientFallbackFactory implements FallbackFactory { + @Override + public CsSoftInfoFeignClient create(Throwable cause) { + //判断抛出异常是否为解码器抛出的业务异常 + Enum exceptionEnum = CommonResponseEnum.SERVICE_FALLBACK; + if (cause.getCause() instanceof BusinessException) { + BusinessException businessException = (BusinessException) cause.getCause(); + } + Enum finalExceptionEnum = exceptionEnum; + return new CsSoftInfoFeignClient() { + @Override + public HttpResult findSoftInfo(String id) { + log.error("{}异常,降级处理,异常为:{}","获取装置软件信息",cause.toString()); + throw new BusinessException(finalExceptionEnum); + } + }; + } +} diff --git a/iot-access/access-api/src/main/java/com/njcn/access/enums/AccessResponseEnum.java b/iot-access/access-api/src/main/java/com/njcn/access/enums/AccessResponseEnum.java index 939505f..bbbd639 100644 --- a/iot-access/access-api/src/main/java/com/njcn/access/enums/AccessResponseEnum.java +++ b/iot-access/access-api/src/main/java/com/njcn/access/enums/AccessResponseEnum.java @@ -14,9 +14,10 @@ public enum AccessResponseEnum { * A0301 ~ A0399 用于用户模块的枚举 *

*/ - NDID_NO_FIND("A0301", "此设备未录入或已注册!"), + NDID_NO_FIND("A0301", "此装置未录入!"), + NDID_SAME_STEP("A0301", "此装置已注册!"), - MISSING_CLIENT("A0302","设备客户端不在线!"), + MISSING_CLIENT("A0302","装置端不在线!"), MODEL_REPEAT("A0302", "模板存在,请勿重复录入!"), MODEL_NO_FIND("A0302", "模板不存在,请先录入模板数据!"), MODEL_ERROR("A0302", "模板未找到,生成监测点失败!"), @@ -29,7 +30,7 @@ public enum AccessResponseEnum { DEV_MODEL_NOT_FIND("A0303","装置型号未找到!"), DEV_IS_NOT_ZL("A0303","注册装置不是直连装置!"), DEV_IS_NOT_WG("A0303","注册装置不是网关!"), - DEV_IS_NOT_PORTABLE("A0303","注册装置不是便携式设备!"), + DEV_IS_NOT_PORTABLE("A0303","注册装置不是便携式装置!"), REGISTER_RESPONSE_ERROR("A0304","装置注册,装置侧应答失败!"), ACCESS_RESPONSE_ERROR("A0304","装置注册,装置侧应答失败!"), @@ -57,9 +58,11 @@ public enum AccessResponseEnum { MODEL_MISS("A0308","模板信息缺失!"), MODEL_VERSION_ERROR("A0308","询问装置模板信息错误"), + UPLOAD_ERROR("A0308","平台上送文件异常"), + RELOAD_UPLOAD_ERROR("A0308","平台重新上送文件异常"), CLDID_IS_NULL("A0309","逻辑子设备标识为空"), - MODULE_NUMBER_IS_NULL("A0309","设备子模块个数为空"), + MODULE_NUMBER_IS_NULL("A0309","装置子模块个数为空"), LDEVINFO_IS_NULL("A0309","逻辑设备信息为空"), SOFTINFO_IS_NULL("A0309","软件信息为空"), @@ -67,6 +70,9 @@ public enum AccessResponseEnum { PROCESS_SAME_ERROR("A0311","当前调试已完成,请勿重复调试"), PROCESS_MISSING_ERROR("A0311","调试流程缺失,请核查功能调试、出厂调试"), + PROCESS_ERROR("A0311","调试流程异常,请先进行功能调试、出厂调试!"), + + FILE_CHECK_ERROR("A0312","文件校验码不一致!"), ; private final String code; diff --git a/iot-access/access-api/src/main/java/com/njcn/access/enums/TypeEnum.java b/iot-access/access-api/src/main/java/com/njcn/access/enums/TypeEnum.java index 08ca8fe..5652355 100644 --- a/iot-access/access-api/src/main/java/com/njcn/access/enums/TypeEnum.java +++ b/iot-access/access-api/src/main/java/com/njcn/access/enums/TypeEnum.java @@ -44,6 +44,7 @@ public enum TypeEnum { TYPE_28("4662","设备根目录查询应答"), TYPE_29("9217","设备心跳请求"), TYPE_30("4865","设备数据主动上送"), + TYPE_31("8503","设备控制命令"), /** * 数据类型 @@ -63,6 +64,7 @@ public enum TypeEnum { DATA_13("13","内部定值InSet"), DATA_14("14","控制Ctrl"), DATA_16("16","波形文件"), + DATA_48("48","工程信息"), /** * 数据模型列表 diff --git a/iot-access/access-api/src/main/java/com/njcn/access/pojo/RspDataDto.java b/iot-access/access-api/src/main/java/com/njcn/access/pojo/RspDataDto.java index 05e4246..dda6299 100644 --- a/iot-access/access-api/src/main/java/com/njcn/access/pojo/RspDataDto.java +++ b/iot-access/access-api/src/main/java/com/njcn/access/pojo/RspDataDto.java @@ -84,4 +84,83 @@ public class RspDataDto { private Double capacityA; } + /** + * 工程信息 + */ + @Data + public static class ProjectInfo { + + @SerializedName("PrjName") + @ApiModelProperty("项目名称") + private String prjName; + + @SerializedName("PrjTimeStart") + @ApiModelProperty("项目起始时间") + private Long prjTimeStart; + + @SerializedName("PrjTimeEnd") + @ApiModelProperty("项目结束时间") + private Long prjTimeEnd; + + @SerializedName("PrjDataPath") + @ApiModelProperty("文件路径") + private String prjDataPath; + + @SerializedName("DevType") + @ApiModelProperty("装置型号") + private String devType; + + @SerializedName("DevMac") + @ApiModelProperty("装置mac") + private String devMac; + + @SerializedName("AppVersion") + @ApiModelProperty("设备应用程序版本信息") + private String appVersion; + + @SerializedName("Cldid") + @ApiModelProperty("逻辑子设备ID(0-逻辑设备本身)") + private Integer clDid; + + @SerializedName("StatCycle") + @ApiModelProperty("分钟数据统计时间间隔(1~10分钟)") + private Integer statCycle; + + @SerializedName("VolGrade") + @ApiModelProperty("电压等级(kV)") + private Double volGrade; + + @SerializedName("VolConType") + @ApiModelProperty("电压接线方式 (0-星型, 1-角型, 2-V型)") + private Integer volConType; + + @SerializedName("CurConSel") + @ApiModelProperty("电流接线方式 (0-正常, 1-合成IB, 2-合成IC)") + private Integer curConSel; + + @SerializedName("PtRatio") + @ApiModelProperty("PT变比") + private Integer ptRatio; + + @SerializedName("CtRatio") + @ApiModelProperty("CT变比") + private Integer ctRatio; + + @SerializedName("CapacitySscb") + @ApiModelProperty("基准短路容量(MVA)") + private Double capacitySscb; + + @SerializedName("CapacitySscmin") + @ApiModelProperty("最小短路容量(MVA)") + private Double capacitySscmin; + + @SerializedName("CapacitySt") + @ApiModelProperty("供电设备容量(MVA)") + private Double capacitySt; + + @SerializedName("CapacitySi") + @ApiModelProperty("用户协议容量(MVA)") + private Double capacitySi; + } + } diff --git a/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/AutoDataDto.java b/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/AutoDataDto.java index eb8c16d..d241fe5 100644 --- a/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/AutoDataDto.java +++ b/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/AutoDataDto.java @@ -70,8 +70,84 @@ public class AutoDataDto { @ApiModelProperty("数据是否参与合格率统计") private Integer dataTag; + @SerializedName("Code") + @ApiModelProperty("事件码") + private Integer code; + @SerializedName("Data") private String data; + + @SerializedName("PrjName") + @ApiModelProperty("工程名称") + private String prjName; + + @SerializedName("PrjTimeStart") + @ApiModelProperty("装置启动时间") + private Long prjTimeStart; + + @SerializedName("PrjTimeEnd") + @ApiModelProperty("装置结束时间") + private Long prjTimeEnd; + + @SerializedName("PrjDataPath") + @ApiModelProperty("装置数据路径") + private String prjDataPath; + + @SerializedName("DevType") + @ApiModelProperty("装置型号") + private String devType; + + @SerializedName("DevMac") + @ApiModelProperty("装置mac地址") + private String devMac; + + @SerializedName("AppVersion") + @ApiModelProperty("装置程序版本") + private String appVersion; + + @SerializedName("Cldid") + @ApiModelProperty("逻辑子设备id") + private Integer clDid; + + @SerializedName("StatCycle") + @ApiModelProperty("统计间隔") + private Integer statCycle; + + @SerializedName("VolGrade") + @ApiModelProperty("电压等级") + private Float volGrade; + + @SerializedName("VolConType") + @ApiModelProperty("电压接线方式(0-星型, 1-角型, 2-V型)") + private Integer volConType; + + @SerializedName("CurConSel") + @ApiModelProperty("电流接线方式(0-正常, 1-合成IB, 2-合成IC)") + private Integer curConSel; + + @SerializedName("PtRatio") + @ApiModelProperty("PT变比") + private Integer ptRatio; + + @SerializedName("CtRatio") + @ApiModelProperty("ct变比") + private Integer ctRatio; + + @SerializedName("CapacitySscb") + @ApiModelProperty("基准短路容量") + private Float capacitySscb; + + @SerializedName("CapacitySscmin") + @ApiModelProperty("最小短路容量") + private Float capacitySscmin; + + @SerializedName("CapacitySt") + @ApiModelProperty("供电设备容量") + private Float capacitySt; + + @SerializedName("CapacitySi") + @ApiModelProperty("用户协议容量") + private Float capacitySi; } } diff --git a/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/ControlDto.java b/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/ControlDto.java new file mode 100644 index 0000000..879fbab --- /dev/null +++ b/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/ControlDto.java @@ -0,0 +1,22 @@ +package com.njcn.access.pojo.dto; + +import com.alibaba.nacos.shaded.com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * @author xy + */ +@Data +public class ControlDto implements Serializable { + + @SerializedName("Cldid") + private Integer clDid; + + @SerializedName("CmdType") + private String cmdType; + + @SerializedName("CmdParm") + private String cmdParm; +} diff --git a/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/DeviceRedisInfoDto.java b/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/DeviceRedisInfoDto.java new file mode 100644 index 0000000..ac58700 --- /dev/null +++ b/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/DeviceRedisInfoDto.java @@ -0,0 +1,52 @@ +package com.njcn.access.pojo.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + * @author xy + * 装置缓存信息 + */ +@Data +public class DeviceRedisInfoDto { + + @ApiModelProperty("装置id") + private String deviceId; + + @ApiModelProperty("装置nDid") + private String nDid; + + @ApiModelProperty("装置类型") + private String deviceType; + + @ApiModelProperty("装置模板id") + private String modelId; + + @ApiModelProperty("模板名称") + private String modelName; + + @ApiModelProperty("模板版本") + private String modelVersion; + + @ApiModelProperty("模板类型 0:治理模板 1:电能质量模板") + private Integer modelType; + + @ApiModelProperty("监测点信息") + private List lineList; + + @Data + public static class LineRedisInfo { + + @ApiModelProperty("监测点id") + private String lineId; + + @ApiModelProperty("监测点位置") + private String location; + + @ApiModelProperty("逻辑设备编码") + private Integer clDid; + } + +} diff --git a/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/EventDto.java b/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/EventDto.java index 95b38da..a05bb4a 100644 --- a/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/EventDto.java +++ b/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/EventDto.java @@ -76,6 +76,10 @@ public class EventDto { @ApiModelProperty("告警故障编码(一般显示为Hex)") private String code; + @SerializedName("DataTag") + @ApiModelProperty("数据标识,1-标识数据异常") + private Integer dataTag; + @SerializedName("Parm") private List param; } diff --git a/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/ModelRedisInfoDto.java b/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/ModelRedisInfoDto.java new file mode 100644 index 0000000..ffd9071 --- /dev/null +++ b/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/ModelRedisInfoDto.java @@ -0,0 +1,63 @@ +package com.njcn.access.pojo.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.time.LocalDate; +import java.util.List; + +/** + * @author xy + * 模板缓存信息 + */ +@Data +public class ModelRedisInfoDto { + + @ApiModelProperty("模板id") + private String modelId; + + @ApiModelProperty("模板名称") + private String modelName; + + @ApiModelProperty("模板时间") + private LocalDate versionDate; + + @ApiModelProperty("模板版本") + private String version; + + @ApiModelProperty("数据集集合") + private List dataSetList; + + @Data + public static class DataSet { + + @ApiModelProperty("数据集id") + private String dataSetId; + + @ApiModelProperty("数据集名称") + private String dataSetName; + + @ApiModelProperty("数据指标集合") + private List dataArrayList; + } + + @Data + public static class DataArray { + + @ApiModelProperty("数据指标id") + private String dataArrayId; + + @ApiModelProperty("数据指标名称") + private String dataArrayName; + + @ApiModelProperty("数据指标别名") + private String anotherName; + + @ApiModelProperty("数据指标统计方式") + private String statMethod; + + @ApiModelProperty("数据指标相别") + private String phase; + } + +} diff --git a/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/UploadFileDto.java b/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/UploadFileDto.java new file mode 100644 index 0000000..c16ff41 --- /dev/null +++ b/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/UploadFileDto.java @@ -0,0 +1,41 @@ +package com.njcn.access.pojo.dto; + +import com.alibaba.nacos.shaded.com.google.gson.annotations.SerializedName; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @author xy + */ +@Data +public class UploadFileDto { + + @SerializedName("Name") + @ApiModelProperty("文件名称,全路径") + private String name; + + @SerializedName("FileSize") + @ApiModelProperty("文件总大小(单位字节)") + private Integer fileSize; + + @SerializedName("Offset") + @ApiModelProperty("当前上送数据包在文件中偏移位置") + private Integer offset; + + @SerializedName("Len") + @ApiModelProperty("当前上送数据包长度") + private Integer len; + + @SerializedName("Data") + @ApiModelProperty("文件包数据") + private String data; + + @SerializedName("FileCheck") + @ApiModelProperty("文件校验码") + private String fileCheck; + + @SerializedName("StepFileCheck") + @ApiModelProperty("当前帧文件校验码") + private String stepFileCheck; + +} diff --git a/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/devModel/DataSetDto.java b/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/devModel/DataSetDto.java index d904751..22ae444 100644 --- a/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/devModel/DataSetDto.java +++ b/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/devModel/DataSetDto.java @@ -36,6 +36,11 @@ public class DataSetDto implements Serializable { @ApiModelProperty("0-不存储;1-存储") private Integer storeFlag; + @SerializedName("DataLevel") + @NotNull(message = "数据标识(一次值、二次值),不可为空") + @ApiModelProperty("Primary-一次值;Secondary-二次值") + private String dataLevel; + @SerializedName("DataArray") @NotEmpty(message = "数据集合描述,不可为空") private List dataArrayDtoList; diff --git a/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/file/FileDto.java b/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/file/FileDto.java index 2b0d3ec..2a57019 100644 --- a/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/file/FileDto.java +++ b/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/file/FileDto.java @@ -5,6 +5,7 @@ import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.io.Serializable; +import java.util.List; /** * 类的介绍: @@ -42,7 +43,10 @@ public class FileDto implements Serializable { private String type; @SerializedName("FileInfo") - private FileDto.FileInfo fileInfo; + private FileInfo fileInfo; + + @SerializedName("DirInfo") + private List dirInfo; @SerializedName("Data") private String data; @@ -86,4 +90,22 @@ public class FileDto implements Serializable { private String fileChkType; } + @Data + public static class DirInfo{ + + @SerializedName("Name") + private String name; + + @SerializedName("Type") + private String type; + + @SerializedName("Size") + @ApiModelProperty("文件大小,单位KB") + private Integer size; + + @SerializedName("Time") + @ApiModelProperty("时间") + private Long time; + } + } diff --git a/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/file/FileRedisDto.java b/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/file/FileRedisDto.java new file mode 100644 index 0000000..1dfbcd0 --- /dev/null +++ b/iot-access/access-api/src/main/java/com/njcn/access/pojo/dto/file/FileRedisDto.java @@ -0,0 +1,22 @@ +package com.njcn.access.pojo.dto.file; + +import lombok.Data; + +/** + * @author xy + */ +@Data +public class FileRedisDto { + + private String name; + + private Integer allStep; + + private Integer nowStep; + + private Integer retry = 0; + + private Integer code; + + +} diff --git a/iot-access/access-api/src/main/java/com/njcn/access/pojo/po/CsSoftInfoPO.java b/iot-access/access-api/src/main/java/com/njcn/access/pojo/po/CsSoftInfoPO.java index a6c8204..182c7be 100644 --- a/iot-access/access-api/src/main/java/com/njcn/access/pojo/po/CsSoftInfoPO.java +++ b/iot-access/access-api/src/main/java/com/njcn/access/pojo/po/CsSoftInfoPO.java @@ -1,10 +1,8 @@ package com.njcn.access.pojo.po; import com.baomidou.mybatisplus.annotation.TableName; -import com.njcn.db.bo.BaseEntity; import lombok.Data; -import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Date; @@ -47,7 +45,7 @@ public class CsSoftInfoPO { /** * 应用程序发布日期 */ - private Date appDate; + private LocalDateTime appDate; /** * 应用程序校验码 diff --git a/iot-access/access-api/src/main/java/com/njcn/access/utils/CRC32Utils.java b/iot-access/access-api/src/main/java/com/njcn/access/utils/CRC32Utils.java new file mode 100644 index 0000000..b23095a --- /dev/null +++ b/iot-access/access-api/src/main/java/com/njcn/access/utils/CRC32Utils.java @@ -0,0 +1,55 @@ +package com.njcn.access.utils; + +import org.springframework.stereotype.Component; + +/** + * @author xy + */ +@Component +public class CRC32Utils { + + // CRC-32/MPEG-2 多项式, x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1 + private static final int POLYNOMIAL = 0x04C11DB7; + + public static int calculateCRC32(byte[] buf, int len, int seed) { + if (buf == null || len <= 0) { + return seed; + } + + int crc = seed; + int count = 0; + // 对长度进行填充以适应32位整数 + int rem = len % Integer.BYTES; + if (rem > 0) { + int n = Integer.BYTES - rem; + byte[] newBuf = new byte[len + n]; + System.arraycopy(buf, 0, newBuf, 0, len); + // 填充字节用0xFF + for (int i = len; i < len + n; i++) { + newBuf[i] = (byte) 0xFF; + } + buf = newBuf; + len += n; + } + + int uiCount = len / Integer.BYTES; + for (int k = 0; k < uiCount; k++) { + int uiTemp = 0; + for (int i = 0; i < Integer.BYTES; i++) { + uiTemp |= (buf[k * Integer.BYTES + i] & 0xFF) << (8 * (3 - i)); + } + for (int j = 0; j < 32; j++) { + // 检查最高位是否为1 + if ((crc ^ uiTemp) < 0) { + crc = 0x04C11DB7 ^ (crc << 1); + count ++; + } else { + crc <<= 1; + } + uiTemp <<= 1; + } + } + return crc; + } + +} diff --git a/iot-access/access-api/src/main/java/com/njcn/access/utils/ChannelObjectUtil.java b/iot-access/access-api/src/main/java/com/njcn/access/utils/ChannelObjectUtil.java new file mode 100644 index 0000000..c17d7dd --- /dev/null +++ b/iot-access/access-api/src/main/java/com/njcn/access/utils/ChannelObjectUtil.java @@ -0,0 +1,45 @@ +package com.njcn.access.utils; + +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author xy + */ +@Component +public class ChannelObjectUtil { + + /** + * 将list转成对应实体 + * @param object + * @param clazz + * @return + * @param + */ + public List objectToList(Object object, Class clazz) { + List resultList = new ArrayList<>(); + if (object instanceof List) { + for (Object o : (List) object) { + resultList.add(clazz.cast(o)); + } + } + return resultList; + } + + /** + * 将object转成对应实体 + * @param object + * @param clazz + * @return + * @param + */ + public T objectToSingleObject(Object object, Class clazz) { + if (clazz.isInstance(object)) { + return clazz.cast(object); + } + // 或者抛出异常,根据您的需求 + return null; + } +} diff --git a/iot-access/access-boot/src/main/java/com/njcn/access/utils/JsonUtil.java b/iot-access/access-api/src/main/java/com/njcn/access/utils/JsonUtil.java similarity index 100% rename from iot-access/access-boot/src/main/java/com/njcn/access/utils/JsonUtil.java rename to iot-access/access-api/src/main/java/com/njcn/access/utils/JsonUtil.java diff --git a/iot-access/access-boot/src/main/java/com/njcn/access/utils/MqttUtil.java b/iot-access/access-api/src/main/java/com/njcn/access/utils/MqttUtil.java similarity index 100% rename from iot-access/access-boot/src/main/java/com/njcn/access/utils/MqttUtil.java rename to iot-access/access-api/src/main/java/com/njcn/access/utils/MqttUtil.java diff --git a/iot-access/access-boot/pom.xml b/iot-access/access-boot/pom.xml index 6345f2e..786f3ab 100644 --- a/iot-access/access-boot/pom.xml +++ b/iot-access/access-boot/pom.xml @@ -63,7 +63,12 @@ common-mq ${project.version} - + + com.njcn + zl-event-api + 1.0.0 + compile + diff --git a/iot-access/access-boot/src/main/java/com/njcn/access/controller/AskDeviceDataController.java b/iot-access/access-boot/src/main/java/com/njcn/access/controller/AskDeviceDataController.java new file mode 100644 index 0000000..3a6867f --- /dev/null +++ b/iot-access/access-boot/src/main/java/com/njcn/access/controller/AskDeviceDataController.java @@ -0,0 +1,113 @@ +package com.njcn.access.controller; + +import com.njcn.access.service.AskDeviceDataService; +import com.njcn.common.pojo.annotation.OperateInfo; +import com.njcn.common.pojo.enums.common.LogEnum; +import com.njcn.common.pojo.enums.response.CommonResponseEnum; +import com.njcn.common.pojo.response.HttpResult; +import com.njcn.common.utils.HttpResultUtil; +import com.njcn.web.controller.BaseController; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author xy + */ +@Slf4j +@RestController +@RequestMapping("/askDeviceData") +@Api(tags = "平台操作装置报文") +@AllArgsConstructor +//@ApiIgnore +public class AskDeviceDataController extends BaseController { + + private final AskDeviceDataService askDeviceDataService; + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/askDeviceRootPath") + @ApiOperation("设备根目录询问") + @ApiImplicitParam(name = "nDid", value = "nDid", required = true) + public HttpResult askDeviceRootPath(@RequestParam("nDid") String nDid){ + String methodDescribe = getMethodDescribe("askDeviceRootPath"); + askDeviceDataService.askDeviceRootPath(nDid); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe); + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/askDeviceFileOrDir") + @ApiOperation("设备文件/目录信息询问") + @ApiImplicitParams({ + @ApiImplicitParam(name = "nDid", value = "nDid", required = true), + @ApiImplicitParam(name = "name", value = "name", required = true) + }) + public HttpResult askDeviceFileOrDir(@RequestParam("nDid") String nDid, @RequestParam("name") String name){ + String methodDescribe = getMethodDescribe("askDeviceFileOrDir"); + askDeviceDataService.askDeviceFileOrDir(nDid,name); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe); + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/downloadFile") + @ApiOperation("设备文件下载") + @ApiImplicitParams({ + @ApiImplicitParam(name = "nDid", value = "nDid", required = true), + @ApiImplicitParam(name = "name", value = "文件路径名", required = true), + @ApiImplicitParam(name = "size", value = "文件大小(单位byte)", required = true), + @ApiImplicitParam(name = "fileCheck", value = "文件校验码", required = true) + }) + public HttpResult downloadFile(@RequestParam("nDid") String nDid, @RequestParam("name") String name, @RequestParam("size") Integer size, @RequestParam("fileCheck") String fileCheck){ + String methodDescribe = getMethodDescribe("downloadFile"); + boolean result = askDeviceDataService.downloadFile(nDid,name,size,fileCheck); + if (result) { + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe); + } else { + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, false, methodDescribe); + } + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/rebootDevice") + @ApiOperation("重启设备") + @ApiImplicitParam(name = "nDid", value = "nDid", required = true) + public HttpResult rebootDevice(@RequestParam("nDid") String nDid){ + String methodDescribe = getMethodDescribe("rebootDevice"); + askDeviceDataService.rebootDevice(nDid); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe); + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/createFolder") + @ApiOperation("创建文件") + @ApiImplicitParams({ + @ApiImplicitParam(name = "nDid", value = "nDid", required = true), + @ApiImplicitParam(name = "path", value = "文件路径", required = true) + }) + public HttpResult createFolder(@RequestParam("nDid") String nDid, @RequestParam("path") String path){ + String methodDescribe = getMethodDescribe("createFolder"); + askDeviceDataService.createFolder(nDid,path); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe); + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/deleteFolder") + @ApiOperation("删除文件") + @ApiImplicitParams({ + @ApiImplicitParam(name = "nDid", value = "nDid", required = true), + @ApiImplicitParam(name = "path", value = "文件路径", required = true) + }) + public HttpResult deleteFolder(@RequestParam("nDid") String nDid, @RequestParam("path") String path){ + String methodDescribe = getMethodDescribe("deleteFolder"); + askDeviceDataService.deleteFolder(nDid,path); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe); + } + + +} diff --git a/iot-access/access-boot/src/main/java/com/njcn/access/controller/CsDevModelController.java b/iot-access/access-boot/src/main/java/com/njcn/access/controller/CsDevModelController.java index 241ea45..2360812 100644 --- a/iot-access/access-boot/src/main/java/com/njcn/access/controller/CsDevModelController.java +++ b/iot-access/access-boot/src/main/java/com/njcn/access/controller/CsDevModelController.java @@ -13,10 +13,7 @@ import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestPart; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; /** @@ -48,5 +45,14 @@ public class CsDevModelController extends BaseController { return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe); } + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/uploadDevFile") + @ApiOperation("上传文件至装置") + @Transactional(rollbackFor = {Exception.class}) + public HttpResult uploadDevFile(@RequestPart("file") @Validated MultipartFile file, @RequestParam("id") @Validated String id, @RequestParam("filePath") @Validated String path){ + String methodDescribe = getMethodDescribe("uploadDevFile"); + csDevModelService.uploadDevFile(file,id,path); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe); + } } diff --git a/iot-access/access-boot/src/main/java/com/njcn/access/controller/CsDeviceController.java b/iot-access/access-boot/src/main/java/com/njcn/access/controller/CsDeviceController.java index 286f796..e9fffc5 100644 --- a/iot-access/access-boot/src/main/java/com/njcn/access/controller/CsDeviceController.java +++ b/iot-access/access-boot/src/main/java/com/njcn/access/controller/CsDeviceController.java @@ -37,7 +37,7 @@ public class CsDeviceController extends BaseController { @OperateInfo(info = LogEnum.BUSINESS_COMMON) @PostMapping("/register") - @ApiOperation("直连设备状态判断") + @ApiOperation("直连设备注册") @ApiImplicitParams({ @ApiImplicitParam(name = "nDid", value = "设备识别码", required = true), @ApiImplicitParam(name = "type", value = "流程标识(2:功能调试 3:出厂调试 4:设备注册)", required = true) @@ -62,7 +62,7 @@ public class CsDeviceController extends BaseController { @OperateInfo(info = LogEnum.BUSINESS_COMMON) @PostMapping("/access") - @ApiOperation("直连设备注册") + @ApiOperation("直连设备接入") @ApiImplicitParam(name = "devAccessParam", value = "接入参数", required = true) public HttpResult devAccess(@RequestBody @Validated DevAccessParam devAccessParam){ String methodDescribe = getMethodDescribe("devAccess"); @@ -80,6 +80,20 @@ public class CsDeviceController extends BaseController { return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe); } + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/manualAccess") + @ApiOperation("手动发起接入") + @ApiImplicitParam(name = "nDid", value = "设备id", required = true) + public HttpResult manualAccess(@RequestParam String nDid){ + String methodDescribe = getMethodDescribe("manualAccess"); + boolean result = csDeviceService.manualAccess(nDid); + if (result) { + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, "成功", methodDescribe); + } else { + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, "失败", methodDescribe); + } + } + @OperateInfo(info = LogEnum.BUSINESS_COMMON) @PostMapping("/wlRegister") @ApiOperation("便携式设备注册") diff --git a/iot-access/access-boot/src/main/java/com/njcn/access/controller/CsSoftInfoController.java b/iot-access/access-boot/src/main/java/com/njcn/access/controller/CsSoftInfoController.java index a927f60..77b9d7c 100644 --- a/iot-access/access-boot/src/main/java/com/njcn/access/controller/CsSoftInfoController.java +++ b/iot-access/access-boot/src/main/java/com/njcn/access/controller/CsSoftInfoController.java @@ -1,10 +1,24 @@ package com.njcn.access.controller; -import org.springframework.web.bind.annotation.RequestMapping; - -import org.springframework.web.bind.annotation.RestController; +import com.njcn.access.mapper.CsSoftInfoMapper; +import com.njcn.access.pojo.po.CsSoftInfoPO; +import com.njcn.common.pojo.annotation.OperateInfo; +import com.njcn.common.pojo.enums.common.LogEnum; +import com.njcn.common.pojo.enums.response.CommonResponseEnum; +import com.njcn.common.pojo.response.HttpResult; +import com.njcn.common.utils.HttpResultUtil; import com.njcn.web.controller.BaseController; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; /** *

@@ -14,9 +28,25 @@ import com.njcn.web.controller.BaseController; * @author xuyang * @since 2023-08-09 */ +@Slf4j @RestController @RequestMapping("/csSoftInfo") +@Api(tags = "装置程序信息") +@AllArgsConstructor +@Validated public class CsSoftInfoController extends BaseController { + private final CsSoftInfoMapper csSoftInfoMapper; + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/findSoftInfo") + @ApiOperation("获取程序软件信息") + @ApiImplicitParam(name = "id", value = "id", required = true) + public HttpResult findSoftInfo(@RequestParam String id){ + String methodDescribe = getMethodDescribe("findSoftInfo"); + CsSoftInfoPO po = csSoftInfoMapper.selectById(id); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, po, methodDescribe); + } + } diff --git a/iot-access/access-boot/src/main/java/com/njcn/access/controller/CsTopicController.java b/iot-access/access-boot/src/main/java/com/njcn/access/controller/CsTopicController.java index 3fc63b8..f44bde9 100644 --- a/iot-access/access-boot/src/main/java/com/njcn/access/controller/CsTopicController.java +++ b/iot-access/access-boot/src/main/java/com/njcn/access/controller/CsTopicController.java @@ -1,6 +1,5 @@ package com.njcn.access.controller; -import com.njcn.access.pojo.po.CsLineModel; import com.njcn.access.service.ICsTopicService; import com.njcn.common.pojo.annotation.OperateInfo; import com.njcn.common.pojo.enums.common.LogEnum; @@ -13,7 +12,10 @@ import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiOperation; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; import springfox.documentation.annotations.ApiIgnore; /** diff --git a/iot-access/access-boot/src/main/java/com/njcn/access/handler/MqttMessageHandler.java b/iot-access/access-boot/src/main/java/com/njcn/access/handler/MqttMessageHandler.java index d54aa28..6e6366a 100644 --- a/iot-access/access-boot/src/main/java/com/njcn/access/handler/MqttMessageHandler.java +++ b/iot-access/access-boot/src/main/java/com/njcn/access/handler/MqttMessageHandler.java @@ -17,6 +17,7 @@ import com.njcn.access.enums.TypeEnum; import com.njcn.access.pojo.RspDataDto; import com.njcn.access.pojo.dto.*; import com.njcn.access.pojo.dto.file.FileDto; +import com.njcn.access.pojo.dto.file.FileRedisDto; import com.njcn.access.pojo.param.ReqAndResParam; import com.njcn.access.pojo.po.CsDeviceOnlineLogs; import com.njcn.access.pojo.po.CsLineModel; @@ -56,9 +57,14 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.Instant; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.temporal.ChronoField; import java.util.*; import java.util.stream.Collectors; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + /** * @author hongawen * @version 1.0.0 @@ -246,6 +252,8 @@ public class MqttMessageHandler { logDto.setResult(0); logDto.setFailReason(AccessResponseEnum.MODEL_VERSION_ERROR.getMessage()); csLogsFeignClient.addUserLog(logDto); + //有异常删除缓存的模板信息 + redisUtil.delete(AppRedisKey.MODEL + nDid); throw new BusinessException(AccessResponseEnum.MODEL_VERSION_ERROR); } //校验前置传递的装置模板库中是否存在 @@ -264,6 +272,8 @@ public class MqttMessageHandler { logDto.setResult(0); logDto.setFailReason(AccessResponseEnum.MODEL_NO_FIND.getMessage()); csLogsFeignClient.addUserLog(logDto); + //有异常删除缓存的模板信息 + redisUtil.delete(AppRedisKey.MODEL + nDid); throw new BusinessException(AccessResponseEnum.MODEL_NO_FIND); } if (Objects.equals(po.getType(),0)){ @@ -272,6 +282,8 @@ public class MqttMessageHandler { logDto.setResult(0); logDto.setFailReason(AccessResponseEnum.MODULE_NUMBER_IS_NULL.getMessage()); csLogsFeignClient.addUserLog(logDto); + //有异常删除缓存的模板信息 + redisUtil.delete(AppRedisKey.MODEL + nDid); throw new BusinessException(AccessResponseEnum.MODULE_NUMBER_IS_NULL); } csModelDto.setModuleNumber(dataSetList.size()); @@ -343,8 +355,15 @@ public class MqttMessageHandler { onlineLogsService.save(csDeviceOnlineLogs); } } + //修改redis的mid + redisUtil.saveByKey(AppRedisKey.DEVICE_MID + nDid,1); + redisUtil.saveByKeyWithExpire("online" + nDid,"online",10L); //询问设备软件信息 askDevData(nDid,version,1,mid); + //更新治理监测点信息和设备容量 + askDevData(nDid,version,2,(res.getMid()+1)); + //更新电网侧、负载侧监测点信息 + askDevData(nDid,version,3,(res.getMid()+1)); } else { log.info(AccessResponseEnum.ACCESS_RESPONSE_ERROR.getMessage()); logDto.setResult(0); @@ -358,35 +377,38 @@ public class MqttMessageHandler { RspDataDto rspDataDto = JSON.parseObject(JSON.toJSONString(res.getMsg()), RspDataDto.class); switch (rspDataDto.getDataType()){ case 1: - log.info("{} 更新设备软件信息",nDid); logDto.setOperate(nDid + "更新设备软件信息"); RspDataDto.SoftInfo softInfo = JSON.parseObject(JSON.toJSONString(rspDataDto.getDataArray()), RspDataDto.SoftInfo.class); //记录设备软件信息 CsSoftInfoPO csSoftInfoPo = new CsSoftInfoPO(); BeanUtils.copyProperties(softInfo,csSoftInfoPo); - try { - String id = IdUtil.fastSimpleUUID(); - csSoftInfoPo.setId(id); - csSoftInfoPo.setAppDate(new SimpleDateFormat("yyyy-MM-dd").parse(softInfo.getAppDate())); - csSoftInfoService.save(csSoftInfoPo); - //更新设备软件id 先看是否存在软件信息,删除 然后在录入 - CsEquipmentDeliveryPO po = equipmentFeignClient.findDevByNDid(nDid).getData(); - String soft = po.getSoftinfoId(); - if (StringUtil.isNotBlank(soft)){ - csSoftInfoService.removeById(soft); - } - equipmentFeignClient.updateSoftInfo(nDid,csSoftInfoPo.getId()); - //询问设备容量信息 - askDevData(nDid,version,2,(res.getMid()+1)); - } catch (ParseException e) { - e.printStackTrace(); + String id = IdUtil.fastSimpleUUID(); + csSoftInfoPo.setId(id); + DateTimeFormatter formatter = new DateTimeFormatterBuilder() + .appendPattern("yyyy-MM-dd[[HH][:mm][:ss]]") + .parseDefaulting(ChronoField.HOUR_OF_DAY, 0) + .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0) + .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0) + .parseDefaulting(ChronoField.MILLI_OF_SECOND, 0) + .toFormatter(); + LocalDateTime localDateTime = LocalDateTime.parse(softInfo.getAppDate(), formatter); + assertThat(localDateTime).isNotNull(); + csSoftInfoPo.setAppDate(localDateTime); + csSoftInfoService.save(csSoftInfoPo); + //更新设备软件id 先看是否存在软件信息,删除 然后在录入 + CsEquipmentDeliveryPO po = equipmentFeignClient.findDevByNDid(nDid).getData(); + String soft = po.getSoftinfoId(); + if (StringUtil.isNotBlank(soft)){ + csSoftInfoService.removeById(soft); } + equipmentFeignClient.updateSoftInfo(nDid,csSoftInfoPo.getId()); + //询问设备容量信息 + //askDevData(nDid,version,2,(res.getMid()+1)); break; case 2: List devInfo = JSON.parseArray(JSON.toJSONString(rspDataDto.getDataArray()), RspDataDto.LdevInfo.class); if (CollectionUtil.isNotEmpty(devInfo)){ if (Objects.equals(res.getDid(),1)){ - log.info("{} 更新治理监测点信息和设备容量",nDid); logDto.setOperate(nDid + "更新治理监测点信息和设备容量"); List list = new ArrayList<>(); devInfo.forEach(item->{ @@ -411,9 +433,8 @@ public class MqttMessageHandler { //3.更新设备模块个数 equipmentFeignClient.updateModuleNumber(nDid,(devInfo.size()-1)); //4.询问监测点pt/ct信息 - askDevData(nDid,version,3,(res.getMid()+1)); + //askDevData(nDid,version,3,(res.getMid()+1)); } else if (Objects.equals(res.getDid(),2)) { - log.info("{} 更新电网侧、负载侧监测点信息",nDid); logDto.setOperate(nDid + "更新电网侧、负载侧监测点信息"); //1.更新电网侧、负载侧监测点相关信息 devInfo.forEach(item->{ @@ -428,10 +449,24 @@ public class MqttMessageHandler { } } break; + case 48: + log.info("询问装置项目列表"); + logDto.setOperate("监测点:" + (nDid + rspDataDto.getClDid()) + "询问项目列表"); + List projectInfoList = JSON.parseArray(JSON.toJSONString(rspDataDto.getDataArray()), RspDataDto.ProjectInfo.class); + String key = AppRedisKey.PROJECT_INFO + nDid + rspDataDto.getClDid(); + redisUtil.saveByKeyWithExpire(key,projectInfoList,60L); + break; default: break; } break; + case 4663: + log.info("装置操作应答"); + if (Objects.equals(res.getCode(),AccessEnum.SUCCESS.getCode())){ + String key = AppRedisKey.CONTROL + nDid; + redisUtil.saveByKeyWithExpire(key,"success",10L); + } + break; default: break; } @@ -460,8 +495,8 @@ public class MqttMessageHandler { case 4865: //设置心跳时间,超时改为掉线 redisUtil.saveByKeyWithExpire("MQTT:" + nDid, Instant.now().toEpochMilli(),180L); - //装置改成在线 - csEquipmentDeliveryService.updateRunStatusBynDid(nDid,AccessEnum.ONLINE.getCode()); + //有心跳,则将装置改成在线 + //csEquipmentDeliveryService.updateRunStatusBynDid(nDid,AccessEnum.ONLINE.getCode()); //处理心跳 ReqAndResDto.Res reqAndResParam = new ReqAndResDto.Res(); reqAndResParam.setMid(res.getMid()); @@ -502,10 +537,9 @@ public class MqttMessageHandler { } //判断事件类型 switch (dataDto.getMsg().getDataAttr()) { - //暂态事件、录波处理 + //暂态事件、录波处理、工程信息 case 0: - log.info("处理事件"); - log.info("事件报文为:" + new String(message.getPayload(), StandardCharsets.UTF_8)); + log.info(nDid + "事件报文为:" + new String(message.getPayload(), StandardCharsets.UTF_8)); EventDto eventDto = gson.fromJson(new String(message.getPayload(), StandardCharsets.UTF_8), EventDto.class); JSONObject jsonObject0 = JSONObject.parseObject(JSON.toJSONString(eventDto)); AppEventMessage appEventMessage = JSONObject.toJavaObject(jsonObject0, AppEventMessage.class); @@ -514,14 +548,16 @@ public class MqttMessageHandler { break; //实时数据 case 1: - log.info("处理实时数据"); + log.info(nDid + "处理实时数据"); break; //处理主动上送的统计数据 case 2: - log.info("处理统计数据"); JSONObject jsonObject2 = JSONObject.parseObject(JSON.toJSONString(dataDto)); AppAutoDataMessage appAutoDataMessage = JSONObject.toJavaObject(jsonObject2, AppAutoDataMessage.class); appAutoDataMessage.setId(nDid); + appAutoDataMessage.getMsg().getDataArray().forEach(item->{ + log.info(nDid + "处理统计数据" + item.getDataAttr()); + }); appAutoDataMessageTemplate.sendMember(appAutoDataMessage); break; default: @@ -553,12 +589,24 @@ public class MqttMessageHandler { //响应请求 switch (fileDto.getType()){ case 4657: - log.info("获取文件信息"); - log.info("文件信息响应:" + fileDto); - appFileMessageTemplate.sendMember(appFileMessage); + log.info("获取文件信息" + fileDto); + String key = AppRedisKey.PROJECT_INFO + nDid; + if (Objects.isNull(fileDto.getMsg().getType())) { + handleDefaultCase(fileDto, nDid); + } else { + if (Objects.equals("dir", fileDto.getMsg().getType())) { + saveDirectoryInfo(fileDto.getMsg().getDirInfo(), key); + } else if (Objects.equals("file", fileDto.getMsg().getType())){ + saveFileInfo(fileDto.getMsg().getFileInfo(), key); + appFileMessageTemplate.sendMember(appFileMessage); + } + } break; case 4658: log.info("获取文件流信息"); + FileRedisDto dto = new FileRedisDto(); + dto.setCode(fileDto.getCode()); + redisUtil.saveByKeyWithExpire(AppRedisKey.DOWNLOAD + fileDto.getMsg().getName() + fileDto.getMid(),dto,10L); if (Objects.equals(fileDto.getCode(),AccessEnum.SUCCESS.getCode())){ appFileStreamMessageTemplate.sendMember(appFileMessage); } @@ -567,11 +615,71 @@ public class MqttMessageHandler { log.info("需要缓存请求的文件信息"); } break; + case 4659: + log.info("装置收到系统上传的文件"); + FileRedisDto fileRedisDto = new FileRedisDto(); + fileRedisDto.setCode(fileDto.getCode()); + redisUtil.saveByKeyWithExpire(AppRedisKey.UPLOAD,fileRedisDto,10L); + redisUtil.saveByKeyWithExpire("uploading","uploading",20L); + break; + case 4660: + log.info("设备目录/文件删除应答"); + redisUtil.saveByKeyWithExpire( "deleteDir"+ nDid,fileDto.getCode(),10L); + break; + case 4661: + log.info("设备目录创建应答"); + redisUtil.saveByKeyWithExpire( "createDir"+ nDid,fileDto.getCode(),10L); + break; + case 4662: + log.info("装置根目录应答"); + redisUtil.saveByKeyWithExpire(AppRedisKey.DEVICE_ROOT_PATH + nDid,fileDto.getMsg().getName(),10L); default: break; } } + /** + * 装置异常事件记录 + * @param topic + * @param message + * @param version + * @param nDid + * @param payload + */ + @MqttSubscribe(value = "/Dev/Error/{edgeId}",qos = 1) + @Transactional(rollbackFor = Exception.class) + public void devErrorInfo(String topic, MqttMessage message, @NamedValue("version") String version, @NamedValue("edgeId") String nDid, @Payload String payload) { + //解析数据 + Gson gson = new Gson(); + log.info(nDid + "事件报文为:" + new String(message.getPayload(), StandardCharsets.UTF_8)); + EventDto eventDto = gson.fromJson(new String(message.getPayload(), StandardCharsets.UTF_8), EventDto.class); + JSONObject jsonObject0 = JSONObject.parseObject(JSON.toJSONString(eventDto)); + AppEventMessage appEventMessage = JSONObject.toJavaObject(jsonObject0, AppEventMessage.class); + appEventMessage.setId(nDid); + appEventMessageTemplate.sendMember(appEventMessage); + } + + private void saveDirectoryInfo(List dirInfo, String key) { + if (!CollectionUtil.isEmpty(dirInfo)) { + redisUtil.saveByKeyWithExpire(key, dirInfo, 10L); + } + } + + private void saveFileInfo(FileDto.FileInfo fileInfo, String key) { + if (!Objects.isNull(fileInfo)) { + redisUtil.saveByKeyWithExpire(key, fileInfo, 10L); + } + } + + private void handleDefaultCase(FileDto fileDto, String nDid) { + List list = fileDto.getMsg().getDirInfo(); + String keyDir = AppRedisKey.PROJECT_INFO + nDid; + saveDirectoryInfo(list, keyDir); + + FileDto.FileInfo fileInfo = fileDto.getMsg().getFileInfo(); + String keyFile = AppRedisKey.FILE_INFO + nDid; + saveFileInfo(fileInfo, keyFile); + } /** * type含义 @@ -584,7 +692,7 @@ public class MqttMessageHandler { reqAndResParam.setMid(mid); reqAndResParam.setPri(AccessEnum.FIRST_CHANNEL.getCode()); reqAndResParam.setType(Integer.parseInt(TypeEnum.TYPE_6.getCode())); - reqAndResParam.setExpire(1); + reqAndResParam.setExpire(-1); AskDataDto askDataDto = new AskDataDto(); askDataDto.setDataAttr(0); askDataDto.setOperate(1); @@ -606,6 +714,12 @@ public class MqttMessageHandler { askDataDto.setCldid(-1); askDataDto.setDataType(2); break; + //询问工程信息 + case 48: + reqAndResParam.setDid(1); + askDataDto.setCldid(1); + askDataDto.setDataType(48); + break; default: break; } @@ -614,5 +728,4 @@ public class MqttMessageHandler { publisher.send("/Pfm/DevCmd/"+version+"/"+nDid, new Gson().toJson(reqAndResParam),1,false); } - } diff --git a/iot-access/access-boot/src/main/java/com/njcn/access/listener/RedisKeyExpirationListener.java b/iot-access/access-boot/src/main/java/com/njcn/access/listener/RedisKeyExpirationListener.java index 7f0677b..c100461 100644 --- a/iot-access/access-boot/src/main/java/com/njcn/access/listener/RedisKeyExpirationListener.java +++ b/iot-access/access-boot/src/main/java/com/njcn/access/listener/RedisKeyExpirationListener.java @@ -1,16 +1,24 @@ package com.njcn.access.listener; +import com.alibaba.nacos.shaded.com.google.gson.Gson; import com.njcn.access.enums.AccessEnum; -import com.njcn.access.pojo.po.CsDeviceOnlineLogs; import com.njcn.access.service.ICsDeviceOnlineLogsService; import com.njcn.access.service.ICsEquipmentDeliveryService; import com.njcn.access.service.ICsTopicService; import com.njcn.access.service.impl.CsDeviceServiceImpl; +import com.njcn.access.utils.MqttUtil; import com.njcn.common.pojo.dto.DeviceLogDTO; -import com.njcn.common.pojo.enums.response.CommonResponseEnum; -import com.njcn.common.pojo.exception.BusinessException; +import com.njcn.csdevice.api.CsDeviceUserFeignClient; +import com.njcn.csdevice.api.CsLedgerFeignClient; import com.njcn.csdevice.api.CsLogsFeignClient; -import com.njcn.web.utils.RequestUtil; +import com.njcn.csdevice.api.EquipmentFeignClient; +import com.njcn.csdevice.pojo.dto.DevDetailDTO; +import com.njcn.csdevice.pojo.po.CsEquipmentDeliveryPO; +import com.njcn.redis.utils.RedisUtil; +import com.njcn.user.api.AppUserFeignClient; +import com.njcn.user.api.UserFeignClient; +import com.njcn.user.pojo.po.User; +import com.njcn.zlevent.pojo.dto.NoticeUserDto; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.data.redis.connection.Message; @@ -19,11 +27,22 @@ import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.stereotype.Component; import javax.annotation.Resource; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; import java.util.Objects; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; /** * @author xy @@ -36,23 +55,40 @@ public class RedisKeyExpirationListener extends KeyExpirationEventMessageListene @Resource private ICsTopicService csTopicService; - @Resource private ICsEquipmentDeliveryService csEquipmentDeliveryService; - @Resource private CsDeviceServiceImpl csDeviceService; - @Resource private CsLogsFeignClient csLogsFeignClient; - @Resource private ICsDeviceOnlineLogsService onlineLogsService; + @Resource + private MqttUtil mqttUtil; + @Resource + private CsLedgerFeignClient csLedgerFeignclient; + @Resource + private EquipmentFeignClient equipmentFeignClient; + @Resource + private AppUserFeignClient appUserFeignClient; + @Resource + private CsDeviceUserFeignClient csDeviceUserFeignClient; + @Resource + private UserFeignClient userFeignClient; + @Resource + private RedisUtil redisUtil; + + private final Object lock = new Object(); public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) { super(listenerContainer); } + // 当前尝试次数 + private static int attemptCount = 1; + //最大告警次数 + private static int MAX_WARNING_TIMES = 0; + /** * 针对redis数据失效事件,进行数据处理 * 注意message.toString()可以获取失效的key @@ -62,58 +98,187 @@ public class RedisKeyExpirationListener extends KeyExpirationEventMessageListene if (StringUtils.isBlank(message.toString())) { return; } - //日志实体 - DeviceLogDTO logDto = new DeviceLogDTO(); - try{ - logDto.setUserName(RequestUtil.getUsername()); - logDto.setLoginName(RequestUtil.getLoginName()); - } catch (Exception e) { - logDto.setUserName("redis失效存储"); - logDto.setLoginName(null); - } - logDto.setResult(1); //判断失效的key是否为MQTT消费端存入的 String expiredKey = message.toString(); if(expiredKey.startsWith("MQTT:")){ - String nDid = expiredKey.split(":")[1]; - //装置下线 - csEquipmentDeliveryService.updateRunStatusBynDid(nDid, AccessEnum.OFFLINE.getCode()); - logDto.setOperate("装置掉线,装置为:" + nDid); - csLogsFeignClient.addUserLog(logDto); - //记录装置下线日志 - CsDeviceOnlineLogs record = onlineLogsService.findLastData(nDid); - record.setOfflineTime(LocalDateTime.now()); - onlineLogsService.updateById(record); - try { - //装置掉线等待10分钟,发起接入请求 - String version = csTopicService.getVersion(nDid); - log.info("装置掉线10分钟发送接入请求,接入失败则进入定时接入任务"); - Thread.sleep(600000); - csDeviceService.devAccess(nDid,version); - //接入再次失败,则定时发起接入请求 - Thread.sleep(1000); - Integer status = csEquipmentDeliveryService.queryEquipmentBynDid(nDid).getRunStatus(); - if (!Objects.isNull(status) && Objects.equals(status,AccessEnum.OFFLINE.getCode())){ - final int[] mid = {2}; - ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(2); - ScheduledFuture runnableFuture = executor.scheduleAtFixedRate(() -> { - log.info("定时发送接入请求..."); - csDeviceService.devAccessMid(nDid,version, mid[0]); - Integer status2 = csEquipmentDeliveryService.queryEquipmentBynDid(nDid).getRunStatus(); - if (Objects.equals(status2,AccessEnum.ONLINE.getCode())){ - throw new BusinessException(CommonResponseEnum.SUCCESS); - } else { - mid[0] = mid[0] + 1; - } - //记录日志 - logDto.setOperate("装置掉线,定时发送接入请求,装置为:" + nDid + ",请求的时间戳为:" + System.currentTimeMillis()); - csLogsFeignClient.addUserLog(logDto); - }, 1, 600, TimeUnit.SECONDS); - } - } catch (InterruptedException e) { - e.printStackTrace(); - } + String version = csTopicService.getVersion(nDid); + ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); + executeMainTask(scheduler,nDid,version); } } + + //主任务 + //1.装置心跳断连 + //2.MQTT客户端不在线 + private void executeMainTask(ScheduledExecutorService scheduler, String nDid, String version) { + System.out.println("正在执行主任务..."); + DeviceLogDTO logDto = new DeviceLogDTO(); + logDto.setUserName("装置失去心跳触发"); + //判断mqtt + String clientName = "NJCN-" + nDid.substring(nDid.length() - 6); + boolean mqttClient = mqttUtil.judgeClientOnline(clientName); + //心跳异常,但是客户端在线,则发送接入请求 + //这边可能存在装置已经掉线,但是客户端仍然在线的情况 + if (mqttClient) { + csDeviceService.devAccessAskTemplate(nDid,version,1); + try { + Thread.sleep(2000); + Object object = redisUtil.getObjectByKey("online" + nDid); + if (Objects.nonNull(object)) { + scheduler.shutdown(); + logDto.setOperate("客户端在线重连成功"); + } else { + //装置下线 + csEquipmentDeliveryService.updateRunStatusBynDid(nDid, AccessEnum.OFFLINE.getCode()); + startScheduledTask(scheduler,nDid,version); + logDto.setOperate("客户端离线进入定时任务"); + } + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + //客户端不在线则修改装置状态,进入定时任务 + else { + //装置下线 + csEquipmentDeliveryService.updateRunStatusBynDid(nDid, AccessEnum.OFFLINE.getCode()); + startScheduledTask(scheduler,nDid,version); + } + csLogsFeignClient.addUserLog(logDto); + } + + private void startScheduledTask(ScheduledExecutorService scheduler, String nDid, String version) { + synchronized (lock) { + NoticeUserDto dto = sendOffLine(nDid); + sendEventToUser(dto); + String clientName = "NJCN-" + nDid.substring(nDid.length() - 6); + ScheduledFuture future = scheduler.scheduleAtFixedRate(() -> { + System.out.println(nDid + "执行重连定时任务..."); + DeviceLogDTO logDto = new DeviceLogDTO(); + logDto.setOperate(nDid + "重连定时任务"); + //判断客户端 + boolean mqttClient = mqttUtil.judgeClientOnline(clientName); + if (mqttClient) { + csDeviceService.devAccessAskTemplate(nDid,version,attemptCount++); + Integer status = csEquipmentDeliveryService.queryEquipmentBynDid(nDid).getRunStatus(); + if (Objects.equals(status,AccessEnum.ONLINE.getCode())){ + logDto.setResult(1); + scheduler.shutdown(); + return; + } else { + logDto.setResult(0); + //一个小时未连接上,则推送告警消息 + MAX_WARNING_TIMES++; + if (MAX_WARNING_TIMES == 30) { + NoticeUserDto dto2 = sendConnectFail(nDid); + sendEventToUser(dto2); + } + } + } else { + //一个小时未连接上,则推送告警消息 + MAX_WARNING_TIMES++; + if (MAX_WARNING_TIMES == 30) { + NoticeUserDto dto2 = sendConnectFail(nDid); + sendEventToUser(dto2); + } + logDto.setResult(0); + } + csLogsFeignClient.addUserLog(logDto); + }, 0, 2, TimeUnit.MINUTES); + } + } + + //掉线通知 + private NoticeUserDto sendOffLine(String nDid) { + NoticeUserDto dto = new NoticeUserDto(); + NoticeUserDto.Payload payload = new NoticeUserDto.Payload(); + dto.setTitle("设备离线"); + CsEquipmentDeliveryPO po = equipmentFeignClient.findDevByNDid(nDid).getData(); + DevDetailDTO devDetailDto = csLedgerFeignclient.queryDevDetail(po.getId()).getData(); + DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + LocalDateTime localDateTime = LocalDateTime.now(); + String dateStr = localDateTime.format(fmt); + String content = String.format(devDetailDto.getEngineeringName() + "-" + devDetailDto.getProjectName() + "-" + devDetailDto.getEquipmentName() + "于" + dateStr + "离线"); + dto.setContent(content); + dto.setPushClientId(getEventUser(po.getId(),true)); + payload.setType(3); + payload.setPath("/pages/message/message?type="+payload.getType()); + dto.setPayload(payload); + return dto; + } + + //重连失败通知 + private NoticeUserDto sendConnectFail(String nDid) { + NoticeUserDto dto = new NoticeUserDto(); + NoticeUserDto.Payload payload = new NoticeUserDto.Payload(); + dto.setTitle("设备接入失败"); + CsEquipmentDeliveryPO po = equipmentFeignClient.findDevByNDid(nDid).getData(); + DevDetailDTO devDetailDto = csLedgerFeignclient.queryDevDetail(po.getId()).getData(); + DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + LocalDateTime localDateTime = LocalDateTime.now(); + String dateStr = localDateTime.format(fmt); + String content = String.format(devDetailDto.getEngineeringName() + "-" + devDetailDto.getProjectName() + "-" + devDetailDto.getEquipmentName() + "于" + dateStr + "多次接入失败"); + dto.setContent(content); + dto.setPushClientId(getEventUser(po.getId(),true)); + payload.setType(3); + payload.setPath("/pages/message/message?type="+payload.getType()); + dto.setPayload(payload); + + return dto; + } + + + /** + * 获取所有需要推送的用户id + */ + public List getEventUser(String devId, boolean isAdmin) { + List adminUser = appUserFeignClient.getAdminInfo().getData(); + List adminList = adminUser.stream().map(User::getId).collect(Collectors.toList()); + if (isAdmin) { + List list = csDeviceUserFeignClient.findUserById(devId).getData(); + adminList.addAll(list); + } + List users = userFeignClient.appuserByIdList(adminList).getData(); + return users.stream().map(User::getDevCode).filter(Objects::nonNull).filter(StringUtils::isNotBlank).distinct().collect(Collectors.toList()); + } + + public void sendEventToUser(NoticeUserDto noticeUserDto) { + try { + // 创建一个URL对象,指定目标HTTPS接口地址 + URL url = new URL("https://fc-mp-ff7b310f-94c9-4468-8260-109111c0a6b2.next.bspapp.com/push"); + // 打开HTTPS连接 + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + // 设置请求方法为POST + connection.setRequestMethod("POST"); + // 设置请求头,指定Content-Type为application/json + connection.setRequestProperty("Content-Type", "application/json"); + // 启用输出流以发送JSON数据 + connection.setDoOutput(true); + // 将JSON数据写入输出流 + OutputStream outputStream = connection.getOutputStream(); + log.info(new Gson().toJson(noticeUserDto).replace("pushClientId", "push_clientid")); + outputStream.write(new Gson().toJson(noticeUserDto).replace("pushClientId", "push_clientid").getBytes(StandardCharsets.UTF_8)); + outputStream.flush(); + outputStream.close(); + // 获取响应代码 + int responseCode = connection.getResponseCode(); + log.info("Response Code: " + responseCode); + // 读取响应数据 + BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); + String inputLine; + StringBuilder response = new StringBuilder(); + while ((inputLine = reader.readLine()) != null) { + response.append(inputLine); + } + reader.close(); + // 打印响应内容 + log.info("Response Content: " + response.toString()); + // 关闭连接 + connection.disconnect(); + } catch (IOException e) { + e.getMessage(); + } + } + + } diff --git a/iot-access/access-boot/src/main/java/com/njcn/access/runner/AccessApplicationRunner.java b/iot-access/access-boot/src/main/java/com/njcn/access/runner/AccessApplicationRunner.java index fc726b4..fd2a1f9 100644 --- a/iot-access/access-boot/src/main/java/com/njcn/access/runner/AccessApplicationRunner.java +++ b/iot-access/access-boot/src/main/java/com/njcn/access/runner/AccessApplicationRunner.java @@ -26,10 +26,8 @@ public class AccessApplicationRunner implements ApplicationRunner { @Resource private CsDeviceServiceImpl csDeviceService; - @Resource private ICsTopicService csTopicService; - @Resource private ICsEquipmentDeliveryService csEquipmentDeliveryService; @@ -39,7 +37,7 @@ public class AccessApplicationRunner implements ApplicationRunner { list.forEach(item->{ String version = csTopicService.getVersion(item.getNdid()); if (!Objects.isNull(version)){ - csDeviceService.devAccess(item.getNdid(),version); + csDeviceService.devAccessAskTemplate(item.getNdid(),version,1); } }); } diff --git a/iot-access/access-boot/src/main/java/com/njcn/access/runner/AccessScheduledTask.java b/iot-access/access-boot/src/main/java/com/njcn/access/runner/AccessScheduledTask.java index 854d7e0..10c62d0 100644 --- a/iot-access/access-boot/src/main/java/com/njcn/access/runner/AccessScheduledTask.java +++ b/iot-access/access-boot/src/main/java/com/njcn/access/runner/AccessScheduledTask.java @@ -1,50 +1,53 @@ -package com.njcn.access.runner; - -import com.njcn.access.service.ICsEquipmentDeliveryService; -import com.njcn.access.service.ICsTopicService; -import com.njcn.access.service.impl.CsDeviceServiceImpl; -import com.njcn.csdevice.pojo.po.CsEquipmentDeliveryPO; -import lombok.extern.slf4j.Slf4j; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; -import java.util.List; -import java.util.Objects; - -/** - * 类的介绍:防止设备掉线 系统未能调整,做一个定时任务,每天凌晨将所有设备重新接入 - * - * @author xuyang - * @version 1.0.0 - * @createTime 2023/8/28 14:21 - */ -@Component -@Slf4j -public class AccessScheduledTask { - - @Resource - private CsDeviceServiceImpl csDeviceService; - - @Resource - private ICsTopicService csTopicService; - - @Resource - private ICsEquipmentDeliveryService csEquipmentDeliveryService; - - /** - * {秒数} {分钟} {小时} {日期} {月份} {星期} {年份(可为空)} - */ - @Scheduled(cron = "0 0 0 * * ?") - public void executeTask() { - log.info("每日凌晨定时任务执行"); - List list = csEquipmentDeliveryService.getAll(); - list.forEach(item->{ - String version = csTopicService.getVersion(item.getNdid()); - if (!Objects.isNull(version)){ - csDeviceService.devAccess(item.getNdid(),version); - } - }); - } - -} +//package com.njcn.access.runner; +// +//import cn.hutool.core.collection.CollUtil; +//import cn.hutool.core.collection.CollectionUtil; +//import com.njcn.access.service.ICsEquipmentDeliveryService; +//import com.njcn.access.service.ICsTopicService; +//import com.njcn.access.service.impl.CsDeviceServiceImpl; +//import com.njcn.csdevice.pojo.po.CsEquipmentDeliveryPO; +//import lombok.extern.slf4j.Slf4j; +//import org.springframework.scheduling.annotation.Scheduled; +//import org.springframework.stereotype.Component; +// +//import javax.annotation.Resource; +//import java.util.List; +//import java.util.Objects; +// +///** +// * 类的介绍:防止设备掉线 系统未能调整,做一个定时任务,每天凌晨将所有设备重新接入 +// * +// * @author xuyang +// * @version 1.0.0 +// * @createTime 2023/8/28 14:21 +// */ +//@Component +//@Slf4j +//public class AccessScheduledTask { +// +// @Resource +// private CsDeviceServiceImpl csDeviceService; +// +// @Resource +// private ICsTopicService csTopicService; +// +// @Resource +// private ICsEquipmentDeliveryService csEquipmentDeliveryService; +// +// /** +// * {秒数} {分钟} {小时} {日期} {月份} {星期} {年份(可为空)} +// */ +// @Scheduled(cron = "0 0 0 * * ?") +// public void executeTask() { +// log.info("每日凌晨定时任务执行"); +// List list = csEquipmentDeliveryService.getAll(); +// if (CollUtil.isNotEmpty(list)) { +// for (int i = 0; i < list.size(); i++) { +// String version = csTopicService.getVersion(list.get(i).getNdid()); +// if (!Objects.isNull(version)){ +// csDeviceService.devAccess(list.get(i).getNdid(),version,i); +// } +// } +// } +// } +//} diff --git a/iot-access/access-boot/src/main/java/com/njcn/access/service/AskDeviceDataService.java b/iot-access/access-boot/src/main/java/com/njcn/access/service/AskDeviceDataService.java new file mode 100644 index 0000000..49d6a2e --- /dev/null +++ b/iot-access/access-boot/src/main/java/com/njcn/access/service/AskDeviceDataService.java @@ -0,0 +1,19 @@ +package com.njcn.access.service; + +/** + * @author xy + */ +public interface AskDeviceDataService { + + void askDeviceRootPath(String nDid); + + void askDeviceFileOrDir(String nDid, String name); + + boolean downloadFile(String nDid, String name, Integer size, String fileCheck); + + void rebootDevice(String nDid); + + void createFolder(String nDid, String path); + + void deleteFolder(String nDid, String path); +} diff --git a/iot-access/access-boot/src/main/java/com/njcn/access/service/ICsDevModelRelationService.java b/iot-access/access-boot/src/main/java/com/njcn/access/service/ICsDevModelRelationService.java index 64ccca3..c860b88 100644 --- a/iot-access/access-boot/src/main/java/com/njcn/access/service/ICsDevModelRelationService.java +++ b/iot-access/access-boot/src/main/java/com/njcn/access/service/ICsDevModelRelationService.java @@ -27,5 +27,12 @@ public interface ICsDevModelRelationService extends IService入库 * @param file @@ -26,4 +31,11 @@ public interface ICsDevModelService { */ void addDict(MultipartFile file); + /** + * 上传文件至装置 + * 更新程序、更新模板 + */ + void uploadDevFile(MultipartFile file, String id, String path); + + } diff --git a/iot-access/access-boot/src/main/java/com/njcn/access/service/ICsDeviceService.java b/iot-access/access-boot/src/main/java/com/njcn/access/service/ICsDeviceService.java index 7b03746..94088f6 100644 --- a/iot-access/access-boot/src/main/java/com/njcn/access/service/ICsDeviceService.java +++ b/iot-access/access-boot/src/main/java/com/njcn/access/service/ICsDeviceService.java @@ -26,7 +26,7 @@ public interface ICsDeviceService { Object getModel(String nDid); /** - * MQTT连接成功,获取装置所用的模板信息 + * 直连设备接入 * @param devAccessParam */ void devAccess(DevAccessParam devAccessParam); @@ -37,6 +37,12 @@ public interface ICsDeviceService { */ void resetFactory(String nDid); + /** + * 手动发起接入 + * @param nDid + */ + boolean manualAccess(String nDid); + /** * 直连设备注册 * @param nDid 设备识别码 diff --git a/iot-access/access-boot/src/main/java/com/njcn/access/service/impl/AskDeviceDataServiceImpl.java b/iot-access/access-boot/src/main/java/com/njcn/access/service/impl/AskDeviceDataServiceImpl.java new file mode 100644 index 0000000..76d37c7 --- /dev/null +++ b/iot-access/access-boot/src/main/java/com/njcn/access/service/impl/AskDeviceDataServiceImpl.java @@ -0,0 +1,250 @@ +package com.njcn.access.service.impl; + +import com.alibaba.nacos.shaded.com.google.gson.Gson; +import com.github.tocrhz.mqtt.publisher.MqttPublisher; +import com.njcn.access.api.CsTopicFeignClient; +import com.njcn.access.enums.AccessEnum; +import com.njcn.access.enums.TypeEnum; +import com.njcn.access.pojo.dto.ControlDto; +import com.njcn.access.pojo.dto.ReqAndResDto; +import com.njcn.access.pojo.dto.file.FileRedisDto; +import com.njcn.access.service.AskDeviceDataService; +import com.njcn.common.pojo.exception.BusinessException; +import com.njcn.csdevice.enums.AlgorithmResponseEnum; +import com.njcn.redis.pojo.enums.AppRedisKey; +import com.njcn.redis.utils.RedisUtil; +import lombok.RequiredArgsConstructor; +import net.sf.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import java.util.Objects; + +/** + * @author xy + */ +@Service +@RequiredArgsConstructor +public class AskDeviceDataServiceImpl implements AskDeviceDataService { + + private static final Logger log = LoggerFactory.getLogger(AskDeviceDataServiceImpl.class); + private final MqttPublisher publisher; + private final CsTopicFeignClient csTopicFeignClient; + private final RedisUtil redisUtil; + private static Integer mid = 1; + private static Integer range = 51200; + + @Override + public void askDeviceRootPath(String nDid) { + Object object = getDeviceMid(nDid); + if (!Objects.isNull(object)) { + mid = (Integer) object; + } + ReqAndResDto.Req reqAndResParam = new ReqAndResDto.Req(); + reqAndResParam.setMid(mid); + reqAndResParam.setDid(1); + reqAndResParam.setPri(AccessEnum.FIRST_CHANNEL.getCode()); + reqAndResParam.setExpire(-1); + reqAndResParam.setType(Integer.parseInt(TypeEnum.TYPE_13.getCode())); + String version = getVersion(nDid); + publisher.send("/Pfm/DevFileCmd/" + version + "/" + nDid, new Gson().toJson(reqAndResParam), 1, false); + mid = mid + 1; + if (mid > 10000) { + mid = 1; + } + redisUtil.saveByKey(AppRedisKey.DEVICE_MID + nDid,mid); + } + + @Override + public void askDeviceFileOrDir(String nDid, String name) { + Object object = getDeviceMid(nDid); + if (!Objects.isNull(object)) { + mid = (Integer) object; + } + ReqAndResDto.Req reqAndResParam = new ReqAndResDto.Req(); + reqAndResParam.setMid(mid); + reqAndResParam.setDid(1); + reqAndResParam.setPri(AccessEnum.FIRST_CHANNEL.getCode()); + reqAndResParam.setExpire(-1); + reqAndResParam.setType(Integer.parseInt(TypeEnum.TYPE_8.getCode())); + String json = String.format("{\"Name\":\"%s\"}", name); + JSONObject jsonObject = JSONObject.fromObject(json); + reqAndResParam.setMsg(jsonObject); + String version = getVersion(nDid); + publisher.send("/Pfm/DevFileCmd/" + version + "/" + nDid, new Gson().toJson(reqAndResParam), 1, false); + mid = mid + 1; + if (mid > 10000) { + mid = 1; + } + redisUtil.saveByKey(AppRedisKey.DEVICE_MID + nDid,mid); + } + + + @Override + public boolean downloadFile(String nDid, String name, Integer size, String fileCheck) { + boolean result = true; + redisUtil.saveByKeyWithExpire("fileDowning:"+nDid,"fileDowning",300L); + redisUtil.saveByKeyWithExpire("fileCheck"+name,fileCheck,300L); + int length = size/51200 + 1; + try { + for (int i = 0; i < length; i++) { + Object object = getDeviceMid(nDid); + if (!Objects.isNull(object)) { + mid = (Integer) object; + } + ReqAndResDto.Req reqAndResParam = getPojo(mid,name,i); + publisher.send("/Pfm/DevFileCmd/V1/"+nDid,new Gson().toJson(reqAndResParam),1,false); + //判断是否重发 + sendNextStep(name,nDid,mid,i); + FileRedisDto fileRedisDto = (FileRedisDto) redisUtil.getObjectByKey(AppRedisKey.DOWNLOAD + name + mid); + //重发之后判断继续循环还是跳出循环 + if (!Objects.isNull(fileRedisDto) && !Objects.equals(fileRedisDto.getCode(),200)) { + result = false; + break; + } + mid = mid + 1; + if (mid > 10000) { + mid = 1; + } + redisUtil.saveByKey(AppRedisKey.DEVICE_MID + nDid,mid); + } + } catch (Exception e) { + redisUtil.delete("fileDowning:"+nDid); + redisUtil.delete("fileCheck"+name); + throw new BusinessException(AlgorithmResponseEnum.FILE_DOWNLOAD_ERROR); + } + return result; + } + + @Override + public void rebootDevice(String nDid) { + Object object = getDeviceMid(nDid); + if (!Objects.isNull(object)) { + mid = (Integer) object; + } + ReqAndResDto.Req reqAndResParam = new ReqAndResDto.Req(); + reqAndResParam.setMid(mid); + reqAndResParam.setDid(0); + reqAndResParam.setPri(AccessEnum.FIRST_CHANNEL.getCode()); + reqAndResParam.setExpire(-1); + reqAndResParam.setType(Integer.parseInt(TypeEnum.TYPE_31.getCode())); + ControlDto controlDto = new ControlDto(); + controlDto.setClDid(-1); + controlDto.setCmdType("reboot"); + controlDto.setCmdParm("on"); + reqAndResParam.setMsg(controlDto); + publisher.send("/Pfm/DevCmd/V1/" + nDid, new Gson().toJson(reqAndResParam), 1, false); + mid = mid + 1; + if (mid > 10000) { + mid = 1; + } + redisUtil.saveByKey(AppRedisKey.DEVICE_MID + nDid,mid); + } + + @Override + public void createFolder(String nDid, String path) { + Object object = getDeviceMid(nDid); + if (!Objects.isNull(object)) { + mid = (Integer) object; + } + ReqAndResDto.Req reqAndResParam = new ReqAndResDto.Req(); + reqAndResParam.setMid(mid); + reqAndResParam.setDid(0); + reqAndResParam.setPri(AccessEnum.FIRST_CHANNEL.getCode()); + reqAndResParam.setExpire(-1); + reqAndResParam.setType(Integer.parseInt(TypeEnum.TYPE_12.getCode())); + String json = String.format("{\"Name\":\"%s\"}", path); + JSONObject jsonObject = JSONObject.fromObject(json); + reqAndResParam.setMsg(jsonObject); + publisher.send("/Pfm/DevFileCmd/V1/" + nDid, new Gson().toJson(reqAndResParam), 1, false); + mid = mid + 1; + if (mid > 10000) { + mid = 1; + } + redisUtil.saveByKey(AppRedisKey.DEVICE_MID + nDid,mid); + } + + @Override + public void deleteFolder(String nDid, String path) { + Object object = getDeviceMid(nDid); + if (!Objects.isNull(object)) { + mid = (Integer) object; + } + ReqAndResDto.Req reqAndResParam = new ReqAndResDto.Req(); + reqAndResParam.setMid(mid); + reqAndResParam.setDid(0); + reqAndResParam.setPri(AccessEnum.FIRST_CHANNEL.getCode()); + reqAndResParam.setExpire(-1); + reqAndResParam.setType(Integer.parseInt(TypeEnum.TYPE_11.getCode())); + String json = String.format("{\"Name\":\"%s\"}", path); + JSONObject jsonObject = JSONObject.fromObject(json); + reqAndResParam.setMsg(jsonObject); + publisher.send("/Pfm/DevFileCmd/V1/" + nDid, new Gson().toJson(reqAndResParam), 1, false); + mid = mid + 1; + if (mid > 10000) { + mid = 1; + } + redisUtil.saveByKey(AppRedisKey.DEVICE_MID + nDid,mid); + } + + public Object getDeviceMid(String nDid) { + return redisUtil.getObjectByKey(AppRedisKey.DEVICE_MID + nDid); + } + + public String getVersion(String nDid) { + return csTopicFeignClient.find(nDid).getData(); + } + + /** + * 文件下载请求报文 + */ + public ReqAndResDto.Req getPojo(Integer mid, String fileName, Integer step) { + String json; + ReqAndResDto.Req reqAndResParam = new ReqAndResDto.Req(); + reqAndResParam.setMid(mid); + reqAndResParam.setDid(0); + reqAndResParam.setPri(AccessEnum.FIRST_CHANNEL.getCode()); + reqAndResParam.setType(Integer.parseInt(TypeEnum.TYPE_9.getCode())); + reqAndResParam.setExpire(-1); + json = "{Name:\""+fileName+"\",TransferMode:"+1+",Offset:"+(step*range)+",Len:"+range+"}"; + JSONObject jsonObject = JSONObject.fromObject(json); + reqAndResParam.setMsg(jsonObject); + return reqAndResParam; + } + + /** + * 根据装置响应来判断是否询问下一帧数据 + */ + public void sendNextStep(String fileName, String id, int mid,int step) { + try { + for (int i = 1; i < 4; i++) { + if (step == 0 ){ + Thread.sleep(5000); + } else { + Thread.sleep(2000); + } + FileRedisDto fileRedisDto = (FileRedisDto) redisUtil.getObjectByKey(AppRedisKey.DOWNLOAD + fileName + mid); + if (Objects.isNull(fileRedisDto)) { + FileRedisDto failDto = new FileRedisDto(); + failDto.setCode(400); + redisUtil.saveByKeyWithExpire(AppRedisKey.DOWNLOAD + fileName + mid,failDto,10L); + } else { + if (Objects.equals(fileRedisDto.getCode(),200)) { + break; + } else { + log.info("第" +i+"次尝试"); + //尝试失败则设置code为400,如果装置响应了,则会将code置为200 + FileRedisDto failDto = new FileRedisDto(); + failDto.setCode(400); + redisUtil.saveByKeyWithExpire(AppRedisKey.DOWNLOAD + fileName + mid,failDto,10L); + ReqAndResDto.Req req = getPojo(mid,fileName,step); + publisher.send("/Pfm/DevFileCmd/V1" + id, new Gson().toJson(req), 1, false); + } + } + } + } catch (Exception e) { + throw new BusinessException(AlgorithmResponseEnum.ASK_DEVICE_DIR_ERROR); + } + } +} diff --git a/iot-access/access-boot/src/main/java/com/njcn/access/service/impl/CsDevModelRelationServiceImpl.java b/iot-access/access-boot/src/main/java/com/njcn/access/service/impl/CsDevModelRelationServiceImpl.java index b2c1645..cc6b1ac 100644 --- a/iot-access/access-boot/src/main/java/com/njcn/access/service/impl/CsDevModelRelationServiceImpl.java +++ b/iot-access/access-boot/src/main/java/com/njcn/access/service/impl/CsDevModelRelationServiceImpl.java @@ -1,6 +1,9 @@ package com.njcn.access.service.impl; +import cn.hutool.core.util.IdUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.njcn.access.mapper.CsDevModelRelationMapper; import com.njcn.access.service.ICsDevModelRelationService; @@ -12,10 +15,13 @@ import com.njcn.csdevice.pojo.po.CsDevModelRelationPO; import com.njcn.csdevice.pojo.vo.CsDevModelRelationVO; import org.apache.commons.lang.StringUtils; import org.springframework.beans.BeanUtils; +import org.springframework.cloud.commons.util.IdUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDateTime; import java.util.List; +import java.util.UUID; import java.util.stream.Collectors; /** @@ -38,7 +44,7 @@ public class CsDevModelRelationServiceImpl extends ServiceImpl csDevModelRelationVOS = this.queryDevModelRelation (queryParm); - if(csDevModelRelationVOS.size ()>0){ + if(!csDevModelRelationVOS.isEmpty()){ throw new BusinessException (AlgorithmResponseEnum.DATA_ERROR); } CsDevModelRelationPO csDevModelRelationPO = new CsDevModelRelationPO(); @@ -48,6 +54,25 @@ public class CsDevModelRelationServiceImpl extends ServiceImpl wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(CsDevModelRelationPO::getDevId, deviceId) + .eq (CsDevModelRelationPO::getStatus, 1); + this.baseMapper.delete(wrapper); + } + + @Override + public void addRelation(CsDevModelRelationPO po) { + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(CsDevModelRelationPO::getDevId, po.getDevId()) + .eq(CsDevModelRelationPO::getDid, po.getDid()) + .eq(CsDevModelRelationPO::getStatus, 1) + .set(CsDevModelRelationPO::getModelId,po.getModelId()) + .set(CsDevModelRelationPO::getUpdateTime,po.getUpdateTime()); + this.update(wrapper); + } + public List queryDevModelRelation(CsDevModelRelationQueryParm queryParm) { QueryWrapper queryWrapper = new QueryWrapper<> (); queryWrapper.eq (StringUtils.isNotBlank (queryParm.getId ()),"id",queryParm.getId ()). diff --git a/iot-access/access-boot/src/main/java/com/njcn/access/service/impl/CsDevModelServiceImpl.java b/iot-access/access-boot/src/main/java/com/njcn/access/service/impl/CsDevModelServiceImpl.java index ef79c8b..776aebe 100644 --- a/iot-access/access-boot/src/main/java/com/njcn/access/service/impl/CsDevModelServiceImpl.java +++ b/iot-access/access-boot/src/main/java/com/njcn/access/service/impl/CsDevModelServiceImpl.java @@ -2,26 +2,38 @@ package com.njcn.access.service.impl; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; import com.alibaba.nacos.shaded.com.google.gson.Gson; +import com.github.tocrhz.mqtt.publisher.MqttPublisher; +import com.njcn.access.enums.AccessEnum; import com.njcn.access.enums.AccessResponseEnum; import com.njcn.access.enums.DataModel; import com.njcn.access.enums.TypeEnum; +import com.njcn.access.handler.MqttMessageHandler; import com.njcn.access.mapper.CsDevModelMapper; +import com.njcn.access.pojo.dto.ReqAndResDto; +import com.njcn.access.pojo.dto.UploadFileDto; import com.njcn.access.pojo.dto.data.*; import com.njcn.access.pojo.dto.devModel.*; +import com.njcn.access.pojo.dto.file.FileRedisDto; import com.njcn.access.pojo.po.CsLineModel; import com.njcn.access.service.*; +import com.njcn.access.utils.CRC32Utils; import com.njcn.access.utils.JsonUtil; import com.njcn.common.pojo.dto.DeviceLogDTO; +import com.njcn.common.pojo.enums.response.CommonResponseEnum; import com.njcn.common.pojo.exception.BusinessException; import com.njcn.csdevice.api.CsLogsFeignClient; import com.njcn.csdevice.api.DevModelFeignClient; +import com.njcn.csdevice.enums.AlgorithmResponseEnum; import com.njcn.csdevice.pojo.po.*; +import com.njcn.csdevice.pojo.vo.CsEquipmentDeliveryVO; import com.njcn.oss.constant.OssPath; import com.njcn.oss.utils.FileStorageUtil; +import com.njcn.redis.pojo.enums.AppRedisKey; +import com.njcn.redis.utils.RedisUtil; import com.njcn.system.api.*; import com.njcn.system.enums.DicDataEnum; -import com.njcn.system.enums.DicTreeEnum; import com.njcn.system.pojo.param.CsWaveParam; import com.njcn.system.pojo.param.EleEpdPqdParam; import com.njcn.system.pojo.param.EleEvtParam; @@ -79,6 +91,24 @@ public class CsDevModelServiceImpl implements ICsDevModelService { private final DictTreeFeignClient dictTreeFeignClient; + private final MqttPublisher publisher; + + private final ICsTopicService csTopicService; + + private final ICsEquipmentDeliveryService csEquipmentDeliveryService; + + private final RedisUtil redisUtil; + + private final MqttMessageHandler mqttMessageHandler; + + @Override + public void refreshDevModelCache() { + + + + + } + @Override @Transactional(rollbackFor = {Exception.class}) public void addModel(MultipartFile file) { @@ -134,6 +164,151 @@ public class CsDevModelServiceImpl implements ICsDevModelService { } } + @Override + public void uploadDevFile(MultipartFile file,String id,String path) { + Object object = redisUtil.getObjectByKey("uploading"); + if (Objects.nonNull(object)) { + throw new BusinessException(AlgorithmResponseEnum.FILE_UPLOADING); + } + Object object2 = redisUtil.getObjectByKey("fileDowning:" + id); + if (Objects.nonNull(object2)) { + throw new BusinessException(AlgorithmResponseEnum.FILE_BUSY); + } + try { + byte[] bytes = file.getBytes(); + int length = bytes.length; + //生成文件校验码 + int crc = CRC32Utils.calculateCRC32(bytes,length,0xffffffff); + String hexString = String.format("%08X", crc); + //存储文件至文件服务器 + fileStorageUtil.uploadMultipart(file, OssPath.SYSTEM_TO_DEV + file.getOriginalFilename() + "_"); + //获取版本 + String version = csTopicService.getVersion(id); + int cap = 50 * 1024; + //需要分片处理 一帧按50k大小传递 + if (length > cap){ + //需要循环的次数 + int times = bytes.length / cap + 1; + for (int i = 1; i <= times; i++) { + //发送数据给前端 + String json = "{allStep:\""+times+"\",nowStep:"+i+"}"; + publisher.send("/Web/Progress", new Gson().toJson(json), 1, false); + DeviceLogDTO logDto = new DeviceLogDTO(); + byte[] lsBytes; + if (length > 50*1024) { + lsBytes = Arrays.copyOfRange(bytes, (i - 1) * cap, i * cap); + ReqAndResDto.Req req = getPojo(i,path,file,bytes.length,lsBytes,(i-1)*cap,hexString); + publisher.send("/Pfm/DevFileCmd/" + version + "/" + id, new Gson().toJson(req), 1, false); + logDto.setOperate(id + "设备上送文件,这是第" + i + "帧"); + logDto.setResult(1); + length = length - cap; + //判断是否重发 + sendNextStep(logDto,path,file,bytes.length,lsBytes,(i-1)*cap,version,id,i,hexString,false); + FileRedisDto fileRedisDto = (FileRedisDto) redisUtil.getObjectByKey(AppRedisKey.UPLOAD); + //重发之后判断继续循环还是跳出循环 + if (!Objects.isNull(fileRedisDto) && !Objects.equals(fileRedisDto.getCode(),200)) { + redisUtil.delete("uploading"); + break; + } + } else { + lsBytes = Arrays.copyOfRange(bytes, (i - 1) * cap, bytes.length); + ReqAndResDto.Req req = getPojo(i,path,file,bytes.length,lsBytes,(i-1)*cap,hexString); + publisher.send("/Pfm/DevFileCmd/" + version + "/" + id, new Gson().toJson(req), 1, false); + logDto.setOperate(id + "设备上送文件,这是最后一帧,为第" + i + "帧"); + logDto.setResult(1); + //判断是否重发 + sendNextStep(logDto,path,file,bytes.length,lsBytes,(i-1)*cap,version,id,i,hexString,true); + } + csLogsFeignClient.addUserLog(logDto); + } + } else { + String json = "{allStep:\""+1+"\",nowStep:"+1+"}"; + publisher.send("/Web/Progress", new Gson().toJson(json), 1, false); + ReqAndResDto.Req req = getPojo(1,path,file,length,bytes,0,hexString); + publisher.send("/Pfm/DevFileCmd/" + version + "/" + id, new Gson().toJson(req), 1, false); + DeviceLogDTO logDto = new DeviceLogDTO(); + logDto.setOperate(id + "系统上送文件,当前文件只有1帧"); + logDto.setResult(1); + csLogsFeignClient.addUserLog(logDto); + //判断是否重发 + sendNextStep(logDto,path,file,length,bytes,0,version,id,1,hexString,false); + } + } catch (Exception e) { + DeviceLogDTO logDto = new DeviceLogDTO(); + logDto.setResult(0); + logDto.setFailReason(AccessResponseEnum.UPLOAD_ERROR.getMessage()); + csLogsFeignClient.addUserLog(logDto); + throw new BusinessException(AccessResponseEnum.UPLOAD_ERROR); + } + } + + /** + * 上送文件至装置 + */ + public ReqAndResDto.Req getPojo(Integer mid, String path, MultipartFile file, Integer allLength, byte[] bytes, Integer offset, String fileCheck) { + //组装报文 + ReqAndResDto.Req reqAndResParam = new ReqAndResDto.Req(); + reqAndResParam.setMid(mid); + reqAndResParam.setDid(0); + reqAndResParam.setPri(AccessEnum.FIRST_CHANNEL.getCode()); + reqAndResParam.setType(Integer.parseInt(TypeEnum.TYPE_10.getCode())); + reqAndResParam.setExpire(-1); + UploadFileDto uploadFileDto = new UploadFileDto(); + uploadFileDto.setName(path + "/" + file.getOriginalFilename()); + uploadFileDto.setFileSize(allLength); + uploadFileDto.setOffset(offset); + uploadFileDto.setLen(bytes.length); + uploadFileDto.setData(Base64.getEncoder().encodeToString(bytes)); + uploadFileDto.setFileCheck(fileCheck); + //生成当前帧文件校验码 + int crc = CRC32Utils.calculateCRC32(bytes,bytes.length,0xffffffff); + String hexString = String.format("%08X", crc); + uploadFileDto.setStepFileCheck(hexString); + reqAndResParam.setMsg(uploadFileDto); + return reqAndResParam; + } + + /** + * 根据装置响应来判断发送的内容 + */ + public void sendNextStep(DeviceLogDTO logDto, String path, MultipartFile file, int length, byte[] bytes, Integer offset, String version, String id, int mid, String fileCheck, boolean result) { + try { + for (int i = 0; i < 30; i++) { + if (result) { + Thread.sleep(10000); + } else { + Thread.sleep(2000); + } + FileRedisDto fileRedisDto = (FileRedisDto) redisUtil.getObjectByKey(AppRedisKey.UPLOAD); + if (Objects.isNull(fileRedisDto)) { + FileRedisDto fileRedis = new FileRedisDto(); + fileRedis.setCode(400); + redisUtil.saveByKeyWithExpire(AppRedisKey.UPLOAD,fileRedis,10L); + } else { + if (Objects.equals(fileRedisDto.getCode(),200)) { + break; + } else { + FileRedisDto fileRedis = new FileRedisDto(); + fileRedis.setCode(400); + redisUtil.saveByKeyWithExpire(AppRedisKey.UPLOAD,fileRedis,10L); + ReqAndResDto.Req req = getPojo(mid,path,file,length,bytes,offset,fileCheck); + publisher.send("/Pfm/DevFileCmd/" + version + "/" + id, new Gson().toJson(req), 1, false); + logDto.setOperate(id + "系统上送文件,装置响应失败,重新发送,这是第" + (i+1) + "次"); + logDto.setResult(1); + csLogsFeignClient.addUserLog(logDto); + } + } + } + } catch (InterruptedException e) { + assert logDto != null; + logDto.setResult(0); + logDto.setFailReason(AccessResponseEnum.RELOAD_UPLOAD_ERROR.getMessage()); + csLogsFeignClient.addUserLog(logDto); + throw new RuntimeException(e); + } + } + + /** * 新增cs_dev_model数据 */ @@ -689,18 +864,19 @@ public class CsDevModelServiceImpl implements ICsDevModelService { if (CollectionUtil.isNotEmpty(dataSetList)){ dataSetList.forEach(item1->{ String id = IdUtil.fastSimpleUUID(); - CsDataSet CsDataSet = new CsDataSet(); - CsDataSet.setId(id); - CsDataSet.setPid(pId); - CsDataSet.setName(item1.getName()); - CsDataSet.setAnotherName(dataSetName(item1.getName(),code)); - CsDataSet.setIdx(item1.getIdx()); - CsDataSet.setPeriod(item1.getPeriod()); - CsDataSet.setStoreFlag(item1.getStoreFlag()); - CsDataSet.setDataList(String.join(",",templateDto.getDataList())); - CsDataSet.setType(0); - CsDataSet.setClDev(0); - setList.add(CsDataSet); + CsDataSet csDataSet = new CsDataSet(); + csDataSet.setId(id); + csDataSet.setPid(pId); + csDataSet.setName(item1.getName()); + csDataSet.setAnotherName(dataSetName(item1.getName(),code)); + csDataSet.setIdx(item1.getIdx()); + csDataSet.setPeriod(item1.getPeriod()); + csDataSet.setStoreFlag(item1.getStoreFlag()); + csDataSet.setDataList(String.join(",",templateDto.getDataList())); + csDataSet.setType(0); + csDataSet.setClDev(0); + csDataSet.setDataLevel(item1.getDataLevel()); + setList.add(csDataSet); List list = item1.getDataArrayDtoList(); if(CollectionUtil.isNotEmpty(list)) { int i = 0; @@ -724,23 +900,24 @@ public class CsDevModelServiceImpl implements ICsDevModelService { if (CollectionUtil.isNotEmpty(clDataSetList)){ clDataSetList.forEach(item4->{ String id = IdUtil.fastSimpleUUID(); - CsDataSet CsDataSet = new CsDataSet(); - CsDataSet.setId(id); - CsDataSet.setPid(pId); - CsDataSet.setName(item4.getName()); - CsDataSet.setAnotherName(dataSetName(item4.getName(),code)); - CsDataSet.setIdx(item4.getIdx()); - CsDataSet.setPeriod(item4.getPeriod()); - CsDataSet.setStoreFlag(item4.getStoreFlag()); - CsDataSet.setDataList(String.join(",",item3.getDataList())); + CsDataSet csDataSet = new CsDataSet(); + csDataSet.setId(id); + csDataSet.setPid(pId); + csDataSet.setName(item4.getName()); + csDataSet.setAnotherName(dataSetName(item4.getName(),code)); + csDataSet.setIdx(item4.getIdx()); + csDataSet.setPeriod(item4.getPeriod()); + csDataSet.setStoreFlag(item4.getStoreFlag()); + csDataSet.setDataList(String.join(",",item3.getDataList())); + csDataSet.setDataLevel(item4.getDataLevel()); //fixme 先用数据类型来区分模板的类型 if (item3.getDataList().contains("Apf") || item3.getDataList().contains("Dvr")){ - CsDataSet.setType(1); + csDataSet.setType(1); } else { - CsDataSet.setType(2); + csDataSet.setType(2); } - CsDataSet.setClDev(item3.getClDid()); - setList.add(CsDataSet); + csDataSet.setClDev(item3.getClDid()); + setList.add(csDataSet); List list = item4.getDataArrayDtoList(); if(CollectionUtil.isNotEmpty(list)) { @@ -1063,14 +1240,14 @@ public class CsDevModelServiceImpl implements ICsDevModelService { showName = "APF模块8数据"; break; case "Ds$Pqd$Stat$01": - if (Objects.isNull(code)){ + if (Objects.equals(code, DicDataEnum.CONNECT_DEV.getCode()) || Objects.isNull(code)){ showName = "电网侧数据"; } else if (Objects.equals(code, DicDataEnum.PORTABLE.getCode())){ showName = "监测1路数据"; } break; case "Ds$Pqd$Stat$02": - if (Objects.isNull(code)){ + if (Objects.equals(code, DicDataEnum.CONNECT_DEV.getCode()) || Objects.isNull(code)){ showName = "负载侧数据"; } else if (Objects.equals(code, DicDataEnum.PORTABLE.getCode())){ showName = "监测2路数据"; diff --git a/iot-access/access-boot/src/main/java/com/njcn/access/service/impl/CsDeviceServiceImpl.java b/iot-access/access-boot/src/main/java/com/njcn/access/service/impl/CsDeviceServiceImpl.java index 817ab7b..3615f3d 100644 --- a/iot-access/access-boot/src/main/java/com/njcn/access/service/impl/CsDeviceServiceImpl.java +++ b/iot-access/access-boot/src/main/java/com/njcn/access/service/impl/CsDeviceServiceImpl.java @@ -2,6 +2,7 @@ package com.njcn.access.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.IdUtil; import com.alibaba.nacos.client.naming.utils.CollectionUtils; import com.alibaba.nacos.shaded.com.google.gson.Gson; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; @@ -35,16 +36,14 @@ import com.njcn.system.enums.DicDataEnum; import com.njcn.system.pojo.po.SysDicTreePO; import com.njcn.web.utils.RequestUtil; import lombok.AllArgsConstructor; +import net.sf.cglib.core.Local; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.stream.Collectors; /** @@ -103,7 +102,7 @@ public class CsDeviceServiceImpl implements ICsDeviceService { DeviceLogDTO logDto = new DeviceLogDTO(); logDto.setUserName(RequestUtil.getUserNickname()); logDto.setLoginName(RequestUtil.getUsername()); - logDto.setOperate("当前设备"+nDid+"状态判断"); + logDto.setOperate("直连设备"+nDid+"注册"); logDto.setResult(1); //1.判断nDid是否存在 CsEquipmentDeliveryVO csEquipmentDeliveryVO = csEquipmentDeliveryService.queryEquipmentBynDid(nDid); @@ -128,7 +127,14 @@ public class CsDeviceServiceImpl implements ICsDeviceService { csLogsFeignClient.addUserLog(logDto); throw new BusinessException(AccessResponseEnum.DEV_IS_NOT_ZL); } - //3.判断客户端是否在线 + //3.判断是否已经注册过 + if (!Objects.isNull(csEquipmentDeliveryVO.getNdid()) && Objects.equals(type,csEquipmentDeliveryVO.getProcess()) && Objects.equals(AccessEnum.ACCESS.getCode(),csEquipmentDeliveryVO.getStatus())){ + logDto.setResult(0); + logDto.setFailReason(AccessResponseEnum.NDID_SAME_STEP.getMessage()); + csLogsFeignClient.addUserLog(logDto); + throw new BusinessException(AccessResponseEnum.NDID_SAME_STEP); + } + //4.判断客户端是否在线 String clientName = "NJCN-" + nDid.substring(nDid.length() - 6); boolean mqttClient = mqttUtil.judgeClientOnline(clientName); if (!mqttClient){ @@ -137,7 +143,7 @@ public class CsDeviceServiceImpl implements ICsDeviceService { csLogsFeignClient.addUserLog(logDto); throw new BusinessException(AccessResponseEnum.MISSING_CLIENT); } - //4.判断当前流程是否是合法的 + //5.判断当前流程是否是合法的 if (csEquipmentDeliveryVO.getProcess() > type){ logDto.setResult(0); logDto.setFailReason(AccessResponseEnum.PROCESS_SAME_ERROR.getMessage()); @@ -147,10 +153,10 @@ public class CsDeviceServiceImpl implements ICsDeviceService { logDto.setFailReason(AccessResponseEnum.PROCESS_MISSING_ERROR.getMessage()); throw new BusinessException(AccessResponseEnum.PROCESS_MISSING_ERROR); } - //5.询问设备支持的主题信息 + //6.询问设备支持的主题信息 //将支持的主题入库 askTopic(nDid); - //6.MQTT询问装置用的模板,并判断库中是否存在模板 + //7.MQTT询问装置用的模板,并判断库中是否存在模板 //存在则建立关系;不存在则告警出来 SysDicTreePO dictData = dictTreeFeignClient.queryById(csEquipmentDeliveryVO.getDevModel()).getData(); if (Objects.isNull(dictData)){ @@ -174,7 +180,7 @@ public class CsDeviceServiceImpl implements ICsDeviceService { logDto.setResult(1); Object model = null; try { - Thread.sleep(1500); + Thread.sleep(3000); String key = AppRedisKey.LINE + nDid; model = redisUtil.getObjectByKey(key); if (Objects.isNull(model)){ @@ -197,7 +203,7 @@ public class CsDeviceServiceImpl implements ICsDeviceService { DeviceLogDTO logDto = new DeviceLogDTO(); logDto.setUserName(RequestUtil.getUserNickname()); logDto.setLoginName(RequestUtil.getUsername()); - logDto.setOperate("设备"+devAccessParam.getNDid()+"注册"); + logDto.setOperate("设备"+devAccessParam.getNDid()+"接入"); logDto.setResult(1); try { //获取版本 @@ -229,6 +235,7 @@ public class CsDeviceServiceImpl implements ICsDeviceService { CsLedgerParam param = new CsLedgerParam(); AppLineTopologyDiagramPO appLineTopologyDiagramPo = new AppLineTopologyDiagramPO(); po.setName(item.getName()); + po.setDeviceId(vo.getId()); po.setPosition(item.getPosition()); po.setClDid(0); if (Objects.equals(DicDataEnum.GRID_SIDE.getCode(),location)){ @@ -280,8 +287,7 @@ public class CsDeviceServiceImpl implements ICsDeviceService { //6.修改装置状态 csEquipmentDeliveryService.updateStatusBynDid(devAccessParam.getNDid(), AccessEnum.REGISTERED.getCode()); //7.发起自动接入请求 - devAccess(devAccessParam.getNDid(),version); - + devAccessAskTemplate(devAccessParam.getNDid(),version,1); //8.删除redis监测点模板信息 redisUtil.delete(AppRedisKey.MODEL + devAccessParam.getNDid()); redisUtil.delete(AppRedisKey.LINE + devAccessParam.getNDid()); @@ -351,6 +357,13 @@ public class CsDeviceServiceImpl implements ICsDeviceService { } } + @Override + @Transactional(rollbackFor = Exception.class) + public boolean manualAccess(String nDid) { + String version = csTopicService.getVersion(nDid); + return devAccessAskTemplate(nDid,version,new Random().nextInt(10000)); + } + @Override @Transactional(rollbackFor = {Exception.class}) @@ -409,7 +422,7 @@ public class CsDeviceServiceImpl implements ICsDeviceService { //3.修改装置状态为注册状态 csEquipmentDeliveryService.updateStatusBynDid(nDid, AccessEnum.REGISTERED.getCode()); //4.发起自动接入请求 - devAccess(nDid,version); + devAccessAskTemplate(nDid,version,1); //5.存储日志 csLogsFeignClient.addUserLog(logDto); //6.存储设备调试日志表 @@ -443,7 +456,7 @@ public class CsDeviceServiceImpl implements ICsDeviceService { //获取版本 String version = csTopicService.getVersion(nDid); //发起接入请求 - this.devAccess(nDid,version); + this.devAccessAskTemplate(nDid,version,1); } private void checkDeviceStatus(String nDid) { @@ -497,26 +510,47 @@ public class CsDeviceServiceImpl implements ICsDeviceService { throw new BusinessException(responseEnum); } - public void devAccess(String nDid,String version) { - ReqAndResDto.Req reqAndResParam = new ReqAndResDto.Req(); - reqAndResParam.setMid(1); - reqAndResParam.setDid(0); - reqAndResParam.setPri(AccessEnum.FIRST_CHANNEL.getCode()); - reqAndResParam.setType(Integer.parseInt(TypeEnum.TYPE_5.getCode())); - reqAndResParam.setExpire(-1); - logger.info("设备接入报文为:" + new Gson().toJson(reqAndResParam)); - publisher.send("/Pfm/DevCmd/"+version+"/"+nDid, new Gson().toJson(reqAndResParam),1,false); - } - - public void devAccessMid(String nDid,String version,Integer mid) { - ReqAndResDto.Req reqAndResParam = new ReqAndResDto.Req(); - reqAndResParam.setMid(mid); - reqAndResParam.setDid(0); - reqAndResParam.setPri(AccessEnum.FIRST_CHANNEL.getCode()); - reqAndResParam.setType(Integer.parseInt(TypeEnum.TYPE_5.getCode())); - reqAndResParam.setExpire(-1); - logger.info("设备接入报文为:" + new Gson().toJson(reqAndResParam)); - publisher.send("/Pfm/DevCmd/"+version+"/"+nDid, new Gson().toJson(reqAndResParam),1,false); + /** + * 装置重新接入系统,需要校验所用的模板 + * @param nDid + * @param version + */ + @Transactional(rollbackFor = Exception.class) + public boolean devAccessAskTemplate(String nDid,String version,Integer mid) { + boolean result = false; + try { + redisUtil.delete(AppRedisKey.MODEL + nDid); + //询问装置当前所用模板 + ReqAndResDto.Req reqAndResParam = new ReqAndResDto.Req(); + reqAndResParam.setMid(mid); + reqAndResParam.setDid(0); + reqAndResParam.setPri(AccessEnum.FIRST_CHANNEL.getCode()); + reqAndResParam.setType(Integer.parseInt(TypeEnum.TYPE_3.getCode())); + reqAndResParam.setExpire(-1); + publisher.send("/Pfm/DevCmd/"+version+"/"+nDid,new Gson().toJson(reqAndResParam),1,false); + //接收到模板,判断模板是否存在,替换模板,发起接入 + Thread.sleep(2000); + List modelId = objectToList(redisUtil.getObjectByKey(AppRedisKey.MODEL + nDid)); + if (CollUtil.isNotEmpty(modelId)) { + CsEquipmentDeliveryVO vo = equipmentFeignClient.queryEquipmentByndid(nDid).getData(); + //重新录入装置和模板关系信息 + for (CsModelDto item : modelId) { + CsDevModelRelationPO po = new CsDevModelRelationPO(); + po.setDevId(vo.getId()); + po.setModelId(item.getModelId()); + po.setDid(item.getDid()); + po.setUpdateTime(LocalDateTime.now()); + csDevModelRelationService.addRelation(po); + } + //发起接入 + reqAndResParam.setType(Integer.parseInt(TypeEnum.TYPE_5.getCode())); + publisher.send("/Pfm/DevCmd/"+version+"/"+nDid, new Gson().toJson(reqAndResParam),1,false); + result = true; + } + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + return result; } /** @@ -529,7 +563,7 @@ public class CsDeviceServiceImpl implements ICsDeviceService { reqAndResParam.setPri(AccessEnum.FIRST_CHANNEL.getCode()); reqAndResParam.setType(Integer.parseInt(TypeEnum.TYPE_1.getCode())); reqAndResParam.setExpire(-1); - logger.info("询问主题报文为:" + new Gson().toJson(reqAndResParam)); + logger.info("询问主题报文为:{}", new Gson().toJson(reqAndResParam)); publisher.send("/Pfm/DevTopic/"+nDid, new Gson().toJson(reqAndResParam),1,false); } @@ -549,7 +583,7 @@ public class CsDeviceServiceImpl implements ICsDeviceService { accessDto.setNdid(nDid); accessDto.setDevType(devType); reqAndResParam.setMsg(accessDto); - logger.info("注册报文为:" + new Gson().toJson(reqAndResParam)); + logger.info("注册报文为:{}", new Gson().toJson(reqAndResParam)); publisher.send("/Pfm/DevReg/"+nDid, new Gson().toJson(reqAndResParam),1,false); } @@ -564,16 +598,4 @@ public class CsDeviceServiceImpl implements ICsDeviceService { } return urlList; } - - public List objectToList2(Object object) { - List urlList = new ArrayList<>(); - if (object != null) { - if (object instanceof ArrayList) { - for (Object o : (List) object) { - urlList.add((RspDataDto.LdevInfo) o); - } - } - } - return urlList; - } } diff --git a/iot-access/access-boot/src/main/resources/bootstrap.yml b/iot-access/access-boot/src/main/resources/bootstrap.yml index eadfba5..48e3e85 100644 --- a/iot-access/access-boot/src/main/resources/bootstrap.yml +++ b/iot-access/access-boot/src/main/resources/bootstrap.yml @@ -35,6 +35,10 @@ spring: refresh: true main: allow-bean-definition-overriding: true + servlet: + multipart: + max-file-size: 100MB + max-request-size: 100MB #项目日志的配置 logging: diff --git a/iot-analysis/analysis-stat/stat-api/src/main/java/com/njcn/stat/api/WlRecordFeignClient.java b/iot-analysis/analysis-stat/stat-api/src/main/java/com/njcn/stat/api/WlRecordFeignClient.java new file mode 100644 index 0000000..b58111b --- /dev/null +++ b/iot-analysis/analysis-stat/stat-api/src/main/java/com/njcn/stat/api/WlRecordFeignClient.java @@ -0,0 +1,21 @@ +//package com.njcn.stat.api; +// +//import com.njcn.common.pojo.constant.ServerInfo; +//import com.njcn.common.pojo.response.HttpResult; +//import com.njcn.mq.message.AppAutoDataMessage; +//import com.njcn.stat.api.fallback.WlRecordClientFallbackFactory; +//import org.springframework.cloud.openfeign.FeignClient; +//import org.springframework.validation.annotation.Validated; +//import org.springframework.web.bind.annotation.PostMapping; +//import org.springframework.web.bind.annotation.RequestBody; +// +///** +// * @author xy +// */ +//@FeignClient(value = ServerInfo.CS_STAT_BOOT, path = "/record", fallbackFactory = WlRecordClientFallbackFactory.class,contextId = "record") +//public interface WlRecordFeignClient { +// +// @PostMapping("/addOrUpdateBaseData") +// HttpResult addOrUpdateBaseData(@RequestBody @Validated AppAutoDataMessage appAutoDataMessage); +// +//} diff --git a/iot-analysis/analysis-stat/stat-api/src/main/java/com/njcn/stat/api/fallback/WlRecordClientFallbackFactory.java b/iot-analysis/analysis-stat/stat-api/src/main/java/com/njcn/stat/api/fallback/WlRecordClientFallbackFactory.java new file mode 100644 index 0000000..60ae9bf --- /dev/null +++ b/iot-analysis/analysis-stat/stat-api/src/main/java/com/njcn/stat/api/fallback/WlRecordClientFallbackFactory.java @@ -0,0 +1,35 @@ +//package com.njcn.stat.api.fallback; +// +//import com.njcn.common.pojo.enums.response.CommonResponseEnum; +//import com.njcn.common.pojo.exception.BusinessException; +//import com.njcn.common.pojo.response.HttpResult; +//import com.njcn.mq.message.AppAutoDataMessage; +//import com.njcn.stat.api.WlRecordFeignClient; +//import feign.hystrix.FallbackFactory; +//import lombok.extern.slf4j.Slf4j; +//import org.springframework.stereotype.Component; +// +///** +// * @author xy +// */ +//@Slf4j +//@Component +//public class WlRecordClientFallbackFactory implements FallbackFactory { +// @Override +// public WlRecordFeignClient create(Throwable cause) { +// //判断抛出异常是否为解码器抛出的业务异常 +// Enum exceptionEnum = CommonResponseEnum.SERVICE_FALLBACK; +// if (cause.getCause() instanceof BusinessException) { +// BusinessException businessException = (BusinessException) cause.getCause(); +// } +// Enum finalExceptionEnum = exceptionEnum; +// return new WlRecordFeignClient() { +// +// @Override +// public HttpResult addOrUpdateBaseData(AppAutoDataMessage appAutoDataMessage) { +// log.error("{}异常,降级处理,异常为:{}","新增或更新装置基础数据",cause.toString()); +// throw new BusinessException(finalExceptionEnum); +// } +// }; +// } +//} diff --git a/iot-analysis/analysis-stat/stat-boot/src/main/java/com/njcn/stat/controller/WlRecordController.java b/iot-analysis/analysis-stat/stat-boot/src/main/java/com/njcn/stat/controller/WlRecordController.java new file mode 100644 index 0000000..e979405 --- /dev/null +++ b/iot-analysis/analysis-stat/stat-boot/src/main/java/com/njcn/stat/controller/WlRecordController.java @@ -0,0 +1,48 @@ +//package com.njcn.stat.controller; +// +//import com.njcn.common.pojo.annotation.OperateInfo; +//import com.njcn.common.pojo.enums.common.LogEnum; +//import com.njcn.common.pojo.enums.response.CommonResponseEnum; +//import com.njcn.common.pojo.response.HttpResult; +//import com.njcn.common.utils.HttpResultUtil; +//import com.njcn.mq.message.AppAutoDataMessage; +//import com.njcn.stat.service.IWlRecordService; +//import com.njcn.web.controller.BaseController; +//import io.swagger.annotations.Api; +//import io.swagger.annotations.ApiImplicitParam; +//import io.swagger.annotations.ApiOperation; +//import lombok.AllArgsConstructor; +//import lombok.extern.slf4j.Slf4j; +//import org.springframework.validation.annotation.Validated; +//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; +// +///** +// * 类的介绍: +// * +// * @author xuyang +// * @version 1.0.0 +// * @createTime 2024/9/10 9:23 +// */ +//@Slf4j +//@RestController +//@RequestMapping("/record") +//@Api(tags = "便携式基础数据录入") +//@AllArgsConstructor +//public class WlRecordController extends BaseController { +// +// private final IWlRecordService wlRecordService; +// +// @OperateInfo(info = LogEnum.BUSINESS_COMMON) +// @PostMapping("/addOrUpdateBaseData") +// @ApiOperation("新增或更新装置基础数据") +// @ApiImplicitParam(name = "appAutoDataMessage", value = "数据实体", required = true) +// public HttpResult addOrUpdateBaseData(@RequestBody @Validated AppAutoDataMessage appAutoDataMessage){ +// String methodDescribe = getMethodDescribe("addOrUpdateBaseData"); +// wlRecordService.addOrUpdateBaseData(appAutoDataMessage); +// return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe); +// } +// +//} diff --git a/iot-analysis/analysis-stat/stat-boot/src/main/java/com/njcn/stat/listener/RedisKeyExpirationListener.java b/iot-analysis/analysis-stat/stat-boot/src/main/java/com/njcn/stat/listener/RedisKeyExpirationListener.java index ce447c1..6ae17be 100644 --- a/iot-analysis/analysis-stat/stat-boot/src/main/java/com/njcn/stat/listener/RedisKeyExpirationListener.java +++ b/iot-analysis/analysis-stat/stat-boot/src/main/java/com/njcn/stat/listener/RedisKeyExpirationListener.java @@ -1,67 +1,67 @@ -package com.njcn.stat.listener; - -import cn.hutool.core.collection.CollectionUtil; -import com.njcn.common.pojo.exception.BusinessException; -import com.njcn.redis.pojo.enums.AppRedisKey; -import com.njcn.redis.utils.RedisUtil; -import com.njcn.stat.enums.StatResponseEnum; -import com.njcn.system.api.EpdFeignClient; -import com.njcn.system.pojo.dto.EpdDTO; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.core.annotation.Order; -import org.springframework.data.redis.connection.Message; -import org.springframework.data.redis.listener.KeyExpirationEventMessageListener; -import org.springframework.data.redis.listener.RedisMessageListenerContainer; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * @author hongawen - * @version 1.0.0 - * @date 2022年04月02日 14:31 - */ -@Slf4j -@Component -public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener { - - @Resource - private EpdFeignClient epdFeignClient; - - @Resource - private RedisUtil redisUtil; - - public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) { - super(listenerContainer); - } - - - /** - * 针对redis数据失效事件,进行数据处理 - * 注意message.toString()可以获取失效的key - */ - @Override - @Order(0) - public void onMessage(Message message, byte[] pattern) { - if (StringUtils.isBlank(message.toString())) { - return; - } - //判断失效的key - String expiredKey = message.toString(); - if(expiredKey.equals(AppRedisKey.ELE_EPD_PQD)){ - Map map = new HashMap<>(); - List list = epdFeignClient.findAll().getData(); - if (CollectionUtil.isEmpty(list)){ - throw new BusinessException(StatResponseEnum.DICT_NULL); - } - list.forEach(item->{ - map.put(item.getDictName(),item.getTableName()); - }); - redisUtil.saveByKeyWithExpire(AppRedisKey.ELE_EPD_PQD,map,3600L); - } - } -} +//package com.njcn.stat.listener; +// +//import cn.hutool.core.collection.CollectionUtil; +//import com.njcn.common.pojo.exception.BusinessException; +//import com.njcn.redis.pojo.enums.AppRedisKey; +//import com.njcn.redis.utils.RedisUtil; +//import com.njcn.stat.enums.StatResponseEnum; +//import com.njcn.system.api.EpdFeignClient; +//import com.njcn.system.pojo.dto.EpdDTO; +//import lombok.extern.slf4j.Slf4j; +//import org.apache.commons.lang3.StringUtils; +//import org.springframework.core.annotation.Order; +//import org.springframework.data.redis.connection.Message; +//import org.springframework.data.redis.listener.KeyExpirationEventMessageListener; +//import org.springframework.data.redis.listener.RedisMessageListenerContainer; +//import org.springframework.stereotype.Component; +// +//import javax.annotation.Resource; +//import java.util.HashMap; +//import java.util.List; +//import java.util.Map; +// +///** +// * @author hongawen +// * @version 1.0.0 +// * @date 2022年04月02日 14:31 +// */ +//@Slf4j +//@Component +//public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener { +// +// @Resource +// private EpdFeignClient epdFeignClient; +// +// @Resource +// private RedisUtil redisUtil; +// +// public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) { +// super(listenerContainer); +// } +// +// +// /** +// * 针对redis数据失效事件,进行数据处理 +// * 注意message.toString()可以获取失效的key +// */ +// @Override +// @Order(0) +// public void onMessage(Message message, byte[] pattern) { +// if (StringUtils.isBlank(message.toString())) { +// return; +// } +// //判断失效的key +// String expiredKey = message.toString(); +// if(expiredKey.equals(AppRedisKey.ELE_EPD_PQD)){ +// Map map = new HashMap<>(); +// List list = epdFeignClient.findAll().getData(); +// if (CollectionUtil.isEmpty(list)){ +// throw new BusinessException(StatResponseEnum.DICT_NULL); +// } +// list.forEach(item->{ +// map.put(item.getDictName(),item.getTableName()); +// }); +// redisUtil.saveByKeyWithExpire(AppRedisKey.ELE_EPD_PQD,map,3600L); +// } +// } +//} diff --git a/iot-analysis/analysis-stat/stat-boot/src/main/java/com/njcn/stat/service/IWlRecordService.java b/iot-analysis/analysis-stat/stat-boot/src/main/java/com/njcn/stat/service/IWlRecordService.java new file mode 100644 index 0000000..955e025 --- /dev/null +++ b/iot-analysis/analysis-stat/stat-boot/src/main/java/com/njcn/stat/service/IWlRecordService.java @@ -0,0 +1,12 @@ +//package com.njcn.stat.service; +// +//import com.njcn.mq.message.AppAutoDataMessage; +// +///** +// * @author xy +// */ +//public interface IWlRecordService { +// +// void addOrUpdateBaseData(AppAutoDataMessage appAutoDataMessage); +// +//} diff --git a/iot-analysis/analysis-stat/stat-boot/src/main/java/com/njcn/stat/service/impl/StatServiceImpl.java b/iot-analysis/analysis-stat/stat-boot/src/main/java/com/njcn/stat/service/impl/StatServiceImpl.java index 4b55ff0..fafab29 100644 --- a/iot-analysis/analysis-stat/stat-boot/src/main/java/com/njcn/stat/service/impl/StatServiceImpl.java +++ b/iot-analysis/analysis-stat/stat-boot/src/main/java/com/njcn/stat/service/impl/StatServiceImpl.java @@ -94,42 +94,41 @@ public class StatServiceImpl implements IStatService { } //直连设备 else if (Objects.equals(DicDataEnum.CONNECT_DEV.getCode(),code)) { - if (Objects.equals(appAutoDataMessage.getDid(),1)){ - lineId = new Gson().fromJson(String.valueOf(redisUtil.getObjectByKey(AppRedisKey.LINE_POSITION+appAutoDataMessage.getId())), Map.class).get("0").toString(); + if (Objects.equals(appAutoDataMessage.getDid(),1)){lineId = new Gson().fromJson(String.valueOf(redisUtil.getObjectByKey(AppRedisKey.LINE_POSITION+appAutoDataMessage.getId())), Map.class).get("0").toString(); } else if (Objects.equals(appAutoDataMessage.getDid(),2)){ lineId = new Gson().fromJson(String.valueOf(redisUtil.getObjectByKey(AppRedisKey.LINE_POSITION+appAutoDataMessage.getId())), Map.class).get(appAutoDataMessage.getMsg().getClDid().toString()).toString(); } } - //缓存指标和influxDB表关系 - Object object2 = redisUtil.getObjectByKey(AppRedisKey.ELE_EPD_PQD); - if(Objects.isNull(object2)) { - saveData(); - } +// //缓存指标和influxDB表关系 +// Object object2 = redisUtil.getObjectByKey(AppRedisKey.ELE_EPD_PQD); +// if(Objects.isNull(object2)) { +// saveData(); +// } //获取当前设备信息 if (CollectionUtil.isNotEmpty(list)){ List recordList = new ArrayList<>(); for (AppAutoDataMessage.DataArray item : list) { switch (item.getDataAttr()) { case 1: - log.info("处理最大值"); + log.info("{}-->处理最大值", po.getNdid() + appAutoDataMessage.getDid() + appAutoDataMessage.getMsg().getClDid()); dataArrayParam.setStatMethod("max"); break; case 2: - log.info("处理最小值"); + log.info("{}-->处理最小值", po.getNdid() + appAutoDataMessage.getDid() + appAutoDataMessage.getMsg().getClDid()); dataArrayParam.setStatMethod("min"); break; case 3: - log.info("处理avg"); + log.info("{}-->处理avg", po.getNdid() + appAutoDataMessage.getDid() + appAutoDataMessage.getMsg().getClDid()); dataArrayParam.setStatMethod("avg"); break; case 4: - log.info("处理cp95"); + log.info("{}-->处理cp95", po.getNdid() + appAutoDataMessage.getDid() + appAutoDataMessage.getMsg().getClDid()); dataArrayParam.setStatMethod("cp95"); break; default: break; } - Object object = redisUtil.getObjectByKey(appAutoDataMessage.getId() + appAutoDataMessage.getDid() + appAutoDataMessage.getMsg().getClDid()); + Object object = redisUtil.getObjectByKey(appAutoDataMessage.getId() + appAutoDataMessage.getDid() + appAutoDataMessage.getMsg().getClDid() + dataArrayParam.getStatMethod()); List dataArrayList; if (Objects.isNull(object)){ dataArrayList = saveModelData(dataArrayParam); @@ -169,29 +168,29 @@ public class StatServiceImpl implements IStatService { } } } - redisUtil.saveByKeyWithExpire(AppRedisKey.LINE_POSITION+id,map,600L); + redisUtil.saveByKey(AppRedisKey.LINE_POSITION+id,map); } - /** - * 缓存字典和influxDB表关系 - */ - public void saveData() { - Map map = new HashMap<>(); - List list = epdFeignClient.findAll().getData(); - if (CollectionUtil.isEmpty(list)){ - throw new BusinessException(StatResponseEnum.DICT_NULL); - } - list.forEach(item->{ - map.put(item.getDictName(),item.getTableName()); - }); - redisUtil.saveByKeyWithExpire(AppRedisKey.ELE_EPD_PQD,map,3600L); - } +// /** +// * 缓存字典和influxDB表关系 +// */ +// public void saveData() { +// Map map = new HashMap<>(); +// List list = epdFeignClient.findAll().getData(); +// if (CollectionUtil.isEmpty(list)){ +// throw new BusinessException(StatResponseEnum.DICT_NULL); +// } +// list.forEach(item->{ +// map.put(item.getDictName(),item.getTableName()); +// }); +// redisUtil.saveByKeyWithExpire(AppRedisKey.ELE_EPD_PQD,map,3600L); +// } /** * 缓存设备模板信息 */ public List saveModelData(DataArrayParam dataArrayParam) { - String key = dataArrayParam.getId() + dataArrayParam.getDid() + dataArrayParam.getCldId(); + String key = dataArrayParam.getId() + dataArrayParam.getDid() + dataArrayParam.getCldId() + dataArrayParam.getStatMethod(); List dataArrayList = dataArrayFeignClient.findListByParam(dataArrayParam).getData(); if (CollectionUtil.isEmpty(dataArrayList)){ throw new BusinessException(StatResponseEnum.DATA_ARRAY_NULL); @@ -215,10 +214,10 @@ public class StatServiceImpl implements IStatService { if (!Objects.equals(dataArrayList.size(),floats.size())){ throw new BusinessException(StatResponseEnum.ARRAY_DATA_NOT_MATCH); } - //判断字典数据是否存在 - if (Objects.isNull(redisUtil.getObjectByKey(AppRedisKey.ELE_EPD_PQD))){ - saveData(); - } +// //判断字典数据是否存在 +// if (Objects.isNull(redisUtil.getObjectByKey(AppRedisKey.ELE_EPD_PQD))){ +// saveData(); +// } Map map = new Gson().fromJson(String.valueOf(redisUtil.getObjectByKey(AppRedisKey.ELE_EPD_PQD)), Map.class); for (int i = 0; i < dataArrayList.size(); i++) { String tableName = map.get(dataArrayList.get(i).getName()); diff --git a/iot-analysis/analysis-stat/stat-boot/src/main/java/com/njcn/stat/service/impl/WlRecordServiceImpl.java b/iot-analysis/analysis-stat/stat-boot/src/main/java/com/njcn/stat/service/impl/WlRecordServiceImpl.java new file mode 100644 index 0000000..232a475 --- /dev/null +++ b/iot-analysis/analysis-stat/stat-boot/src/main/java/com/njcn/stat/service/impl/WlRecordServiceImpl.java @@ -0,0 +1,44 @@ +//package com.njcn.stat.service.impl; +// +//import com.njcn.csdevice.api.EquipmentFeignClient; +//import com.njcn.csdevice.api.WlRecordFeignClient; +//import com.njcn.csdevice.pojo.po.CsEquipmentDeliveryPO; +//import com.njcn.csdevice.pojo.po.WlRecord; +//import com.njcn.mq.message.AppAutoDataMessage; +//import com.njcn.stat.service.IWlRecordService; +//import lombok.AllArgsConstructor; +//import lombok.extern.slf4j.Slf4j; +//import org.springframework.beans.BeanUtils; +//import org.springframework.stereotype.Service; +// +///** +// * 类的介绍: +// * +// * @author xuyang +// * @version 1.0.0 +// * @createTime 2023/8/14 9:32 +// */ +//@Service +//@Slf4j +//@AllArgsConstructor +//public class WlRecordServiceImpl implements IWlRecordService{ +// +// private final EquipmentFeignClient equipmentFeignClient; +// +// private final WlRecordFeignClient wlRecordFeignClient; +// +// @Override +// public void addOrUpdateBaseData(AppAutoDataMessage appAutoDataMessage) { +// WlRecord wlRecord = new WlRecord(); +// CsEquipmentDeliveryPO po = equipmentFeignClient.findDevByNDid(appAutoDataMessage.getId()).getData(); +// AppAutoDataMessage.DataArray dataArray = appAutoDataMessage.getMsg().getDataArray().get(0); +// BeanUtils.copyProperties(dataArray, wlRecord); +// wlRecord.setDevId(po.getId()); +// wlRecord.setLineId(po.getNdid() + appAutoDataMessage.getMsg().getClDid()); +// wlRecord.setGcDataPath(dataArray.getPrjDataPath()); +// if (dataArray.getPrjTimeEnd() == -1) { +// wlRecord.setEndTime(null); +// } +// wlRecordFeignClient.addBaseData(wlRecord); +// } +//} diff --git a/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/api/EventFeignClient.java b/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/api/EventFeignClient.java index 42a4fad..c9d02b9 100644 --- a/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/api/EventFeignClient.java +++ b/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/api/EventFeignClient.java @@ -15,4 +15,8 @@ public interface EventFeignClient { @PostMapping("/analysis") HttpResult analysis(AppEventMessage appEventMessage); + + @PostMapping("/errorEvent") + HttpResult insertErrorEvent(AppEventMessage appEventMessage); + } diff --git a/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/api/EvtErrorFeignClient.java b/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/api/EvtErrorFeignClient.java new file mode 100644 index 0000000..bdd5de5 --- /dev/null +++ b/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/api/EvtErrorFeignClient.java @@ -0,0 +1,19 @@ +package com.njcn.zlevent.api; + +import com.njcn.common.pojo.constant.ServerInfo; +import com.njcn.common.pojo.response.HttpResult; +import com.njcn.mq.message.AppEventMessage; +import com.njcn.zlevent.api.fallback.EvtErrorClientFallbackFactory; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; + +/** + * @author xy + */ +@FeignClient(value = ServerInfo.CS_ZL_EVENT_BOOT, path = "/csDevErrEvt", fallbackFactory = EvtErrorClientFallbackFactory.class,contextId = "csDevErrEvt") +public interface EvtErrorFeignClient { + + @PostMapping("/errorEvent") + HttpResult insertErrorEvent(AppEventMessage appEventMessage); + +} diff --git a/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/api/WaveFeignClient.java b/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/api/WaveFeignClient.java index 17be4ec..ac8a313 100644 --- a/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/api/WaveFeignClient.java +++ b/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/api/WaveFeignClient.java @@ -3,14 +3,14 @@ package com.njcn.zlevent.api; import com.njcn.common.pojo.constant.ServerInfo; import com.njcn.common.pojo.response.HttpResult; import com.njcn.mq.message.AppEventMessage; -import com.njcn.zlevent.api.fallback.EventClientFallbackFactory; +import com.njcn.zlevent.api.fallback.WaveClientFallbackFactory; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; /** * @author xy */ -@FeignClient(value = ServerInfo.CS_ZL_EVENT_BOOT, path = "/wave", fallbackFactory = EventClientFallbackFactory.class,contextId = "wave") +@FeignClient(value = ServerInfo.CS_ZL_EVENT_BOOT, path = "/wave", fallbackFactory = WaveClientFallbackFactory.class,contextId = "wave") public interface WaveFeignClient { @PostMapping("/analysis") diff --git a/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/api/fallback/EventClientFallbackFactory.java b/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/api/fallback/EventClientFallbackFactory.java index 0be5fa7..495882d 100644 --- a/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/api/fallback/EventClientFallbackFactory.java +++ b/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/api/fallback/EventClientFallbackFactory.java @@ -30,6 +30,12 @@ public class EventClientFallbackFactory implements FallbackFactory insertErrorEvent(AppEventMessage appEventMessage) { + log.error("{}异常,降级处理,异常为:{}","异常事件统计",cause.toString()); + throw new BusinessException(finalExceptionEnum); + } }; } } diff --git a/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/api/fallback/EvtErrorClientFallbackFactory.java b/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/api/fallback/EvtErrorClientFallbackFactory.java new file mode 100644 index 0000000..63222ea --- /dev/null +++ b/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/api/fallback/EvtErrorClientFallbackFactory.java @@ -0,0 +1,35 @@ +package com.njcn.zlevent.api.fallback; + +import com.njcn.common.pojo.enums.response.CommonResponseEnum; +import com.njcn.common.pojo.exception.BusinessException; +import com.njcn.common.pojo.response.HttpResult; +import com.njcn.mq.message.AppEventMessage; +import com.njcn.zlevent.api.EvtErrorFeignClient; +import feign.hystrix.FallbackFactory; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * @author xy + */ +@Slf4j +@Component +public class EvtErrorClientFallbackFactory implements FallbackFactory { + @Override + public EvtErrorFeignClient create(Throwable cause) { + //判断抛出异常是否为解码器抛出的业务异常 + Enum exceptionEnum = CommonResponseEnum.SERVICE_FALLBACK; + if (cause.getCause() instanceof BusinessException) { + BusinessException businessException = (BusinessException) cause.getCause(); + } + Enum finalExceptionEnum = exceptionEnum; + return new EvtErrorFeignClient() { + + @Override + public HttpResult insertErrorEvent(AppEventMessage appEventMessage) { + log.error("{}异常,降级处理,异常为:{}","异常事件统计",cause.toString()); + throw new BusinessException(finalExceptionEnum); + } + }; + } +} diff --git a/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/api/fallback/WaveClientFallbackFactory.java b/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/api/fallback/WaveClientFallbackFactory.java index 2fcdf17..dcd6b8a 100644 --- a/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/api/fallback/WaveClientFallbackFactory.java +++ b/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/api/fallback/WaveClientFallbackFactory.java @@ -5,6 +5,7 @@ import com.njcn.common.pojo.exception.BusinessException; import com.njcn.common.pojo.response.HttpResult; import com.njcn.mq.message.AppEventMessage; import com.njcn.zlevent.api.EventFeignClient; +import com.njcn.zlevent.api.WaveFeignClient; import feign.hystrix.FallbackFactory; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -14,16 +15,16 @@ import org.springframework.stereotype.Component; */ @Slf4j @Component -public class WaveClientFallbackFactory implements FallbackFactory { +public class WaveClientFallbackFactory implements FallbackFactory { @Override - public EventFeignClient create(Throwable cause) { + public WaveFeignClient create(Throwable cause) { //判断抛出异常是否为解码器抛出的业务异常 Enum exceptionEnum = CommonResponseEnum.SERVICE_FALLBACK; if (cause.getCause() instanceof BusinessException) { BusinessException businessException = (BusinessException) cause.getCause(); } Enum finalExceptionEnum = exceptionEnum; - return new EventFeignClient() { + return new WaveFeignClient() { @Override public HttpResult analysis(AppEventMessage appEventMessage) { diff --git a/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/pojo/dto/FileStreamDto.java b/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/pojo/dto/FileStreamDto.java index 3596013..848883e 100644 --- a/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/pojo/dto/FileStreamDto.java +++ b/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/pojo/dto/FileStreamDto.java @@ -5,6 +5,7 @@ import lombok.Data; import java.io.Serializable; import java.util.HashSet; import java.util.List; +import java.util.Set; /** * 类的介绍: @@ -22,6 +23,6 @@ public class FileStreamDto implements Serializable { private Integer frameLen; - private List list; + private Set list; } diff --git a/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/pojo/dto/WaveTimeDto.java b/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/pojo/dto/WaveTimeDto.java index d131772..5abaa5a 100644 --- a/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/pojo/dto/WaveTimeDto.java +++ b/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/pojo/dto/WaveTimeDto.java @@ -12,8 +12,12 @@ import lombok.Data; @Data public class WaveTimeDto { + private String fileName; + private String deviceId; + private String nDid; + private String lineId; private String startTime; diff --git a/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/pojo/po/CsDevErrEvt.java b/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/pojo/po/CsDevErrEvt.java new file mode 100644 index 0000000..300e462 --- /dev/null +++ b/iot-analysis/analysis-zl-event/zl-event-api/src/main/java/com/njcn/zlevent/pojo/po/CsDevErrEvt.java @@ -0,0 +1,43 @@ +package com.njcn.zlevent.pojo.po; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + *

+ * 装置异常事件统计 + *

+ * + * @author xy + * @since 2024-09-12 + */ +@Data +@TableName("cs_dev_err_evt") +public class CsDevErrEvt { + + private static final long serialVersionUID = 1L; + + /** + * id + */ + private String id; + + /** + * 设备识别码 + */ + private String ndid; + + /** + * 事件发生时间 + */ + private LocalDateTime evtTime; + + /** + * 事件code编码 + */ + private String code; + + +} diff --git a/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/controller/CsDevErrEvtController.java b/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/controller/CsDevErrEvtController.java new file mode 100644 index 0000000..46e6d87 --- /dev/null +++ b/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/controller/CsDevErrEvtController.java @@ -0,0 +1,50 @@ +package com.njcn.zlevent.controller; + + +import com.njcn.common.pojo.annotation.OperateInfo; +import com.njcn.common.pojo.enums.common.LogEnum; +import com.njcn.common.pojo.enums.response.CommonResponseEnum; +import com.njcn.common.pojo.response.HttpResult; +import com.njcn.common.utils.HttpResultUtil; +import com.njcn.mq.message.AppEventMessage; +import com.njcn.web.controller.BaseController; +import com.njcn.zlevent.service.ICsDevErrEvtService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +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; + +/** + *

+ * 装置异常事件统计 前端控制器 + *

+ * + * @author xy + * @since 2024-09-12 + */ +@RestController +@Slf4j +@RequestMapping("/csDevErrEvt") +@Api(tags = "装置异常事件处理") +@AllArgsConstructor +public class CsDevErrEvtController extends BaseController { + + private final ICsDevErrEvtService csDevErrEvtService; + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/errorEvent") + @ApiOperation("异常事件统计") + @ApiImplicitParam(name = "appEventMessage", value = "数据实体", required = true) + public HttpResult insertErrorEvent(@RequestBody AppEventMessage appEventMessage){ + String methodDescribe = getMethodDescribe("insertErrorEvent"); + csDevErrEvtService.insertErrorEvent(appEventMessage); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe); + } + +} + diff --git a/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/controller/WaveController.java b/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/controller/WaveController.java index f480bff..d6b41ee 100644 --- a/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/controller/WaveController.java +++ b/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/controller/WaveController.java @@ -36,7 +36,7 @@ public class WaveController extends BaseController { @OperateInfo(info = LogEnum.BUSINESS_COMMON) @PostMapping("/analysis") - @ApiOperation("录波解析") + @ApiOperation("录波事件") @ApiImplicitParam(name = "appEventMessage", value = "数据实体", required = true) public HttpResult analysis(@RequestBody AppEventMessage appEventMessage){ String methodDescribe = getMethodDescribe("analysis"); diff --git a/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/listener/RedisKeyExpirationListener.java b/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/listener/RedisKeyExpirationListener.java index 714623e..f85f94f 100644 --- a/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/listener/RedisKeyExpirationListener.java +++ b/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/listener/RedisKeyExpirationListener.java @@ -8,14 +8,18 @@ import com.njcn.access.api.CsTopicFeignClient; import com.njcn.access.enums.AccessEnum; import com.njcn.access.enums.TypeEnum; import com.njcn.access.pojo.dto.ReqAndResDto; +import com.njcn.access.pojo.dto.file.FileRedisDto; +import com.njcn.access.utils.ChannelObjectUtil; import com.njcn.common.pojo.exception.BusinessException; +import com.njcn.csdevice.enums.AlgorithmResponseEnum; import com.njcn.redis.pojo.enums.AppRedisKey; import com.njcn.redis.utils.RedisUtil; import com.njcn.stat.enums.StatResponseEnum; import com.njcn.system.api.EpdFeignClient; import com.njcn.system.pojo.dto.EpdDTO; import com.njcn.zlevent.pojo.dto.FileStreamDto; -import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer; +import com.njcn.zlevent.pojo.dto.WaveTimeDto; +import com.njcn.zlevent.service.ICsWaveAnalysisService; import lombok.extern.slf4j.Slf4j; import net.sf.json.JSONObject; import org.apache.commons.lang3.StringUtils; @@ -26,10 +30,7 @@ import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.stereotype.Component; import javax.annotation.Resource; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.IntStream; /** @@ -43,21 +44,23 @@ public class RedisKeyExpirationListener extends KeyExpirationEventMessageListene @Resource private EpdFeignClient epdFeignClient; - @Resource private RedisUtil redisUtil; - @Resource private CsTopicFeignClient csTopicFeignClient; - @Resource private MqttPublisher publisher; + @Resource + private ChannelObjectUtil channelObjectUtil; + @Resource + private ICsWaveAnalysisService iCsWaveAnalysisService; + private static Integer mid = 1; + private static Integer range = 51200; public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) { super(listenerContainer); } - /** * 针对redis数据失效事件,进行数据处理 * 注意message.toString()可以获取失效的key @@ -91,35 +94,103 @@ public class RedisKeyExpirationListener extends KeyExpirationEventMessageListene int end = dto.getTotal(); IntStream.rangeClosed(start, end) .filter(i -> !dto.getList().contains(i)) - .forEach(missingNumber -> { - log.info("缺失的数字:{}",missingNumber); - missingList.add(missingNumber); - }); - redisUtil.saveByKey(AppRedisKey.FILE_PART_MISSING.concat(fileName), missingList); - Integer offset = (missingList.get(0) - 1) * dto.getFrameLen(); - askMissingFileStream(dto.getNDid(),missingList.get(0),fileName,offset,dto.getFrameLen()); + .forEach(missingList::add); + Object object = redisUtil.getObjectByKey(AppRedisKey.FILE_PART_MISSING.concat(fileName)); + if (CollectionUtil.isNotEmpty(missingList) && Objects.isNull(object)) { + downloadFile(missingList,dto.getNDid(),fileName); + } } } + //请求缺失的数据 + public void downloadFile( List missingList, String nDid, String name) { + for (Integer missingNumber : missingList) { + int i = missingNumber - 1; + Object object = getDeviceMid(nDid); + if (!Objects.isNull(object)) { + mid = (Integer) object; + } + ReqAndResDto.Req reqAndResParam = getPojo(mid,name,i); + publisher.send("/Pfm/DevFileCmd/V1/"+nDid,new Gson().toJson(reqAndResParam),1,false); + //判断是否重发 + sendNextStep(name,nDid,mid,i,nDid); + FileRedisDto fileRedisDto = (FileRedisDto) redisUtil.getObjectByKey(AppRedisKey.DOWNLOAD + name + mid); + //重发之后判断继续循环还是跳出循环 + if (!Objects.isNull(fileRedisDto) && !Objects.equals(fileRedisDto.getCode(),200)) { + break; + } + mid = mid + 1; + if (mid > 10000) { + mid = 1; + } + redisUtil.saveByKey(AppRedisKey.DEVICE_MID + nDid,mid); + } + } - public void askMissingFileStream(String nDid, Integer mid, String fileName, Integer offset, Integer len) { - String version = csTopicFeignClient.find(nDid).getData(); + public Object getDeviceMid(String nDid) { + return redisUtil.getObjectByKey(AppRedisKey.DEVICE_MID + nDid); + } + + /** + * 文件下载请求报文 + */ + public ReqAndResDto.Req getPojo(Integer mid, String fileName, Integer step) { + String json; ReqAndResDto.Req reqAndResParam = new ReqAndResDto.Req(); reqAndResParam.setMid(mid); reqAndResParam.setDid(0); reqAndResParam.setPri(AccessEnum.FIRST_CHANNEL.getCode()); reqAndResParam.setType(Integer.parseInt(TypeEnum.TYPE_9.getCode())); reqAndResParam.setExpire(-1); - String json = "{Name:\""+fileName+"\",Offset:"+offset+",Len:"+len+"}"; + json = "{Name:\""+fileName+"\",TransferMode:"+1+",Offset:"+(step*range)+",Len:"+range+"}"; JSONObject jsonObject = JSONObject.fromObject(json); reqAndResParam.setMsg(jsonObject); - publisher.send("/Pfm/DevFileCmd/"+version+"/"+nDid,new Gson().toJson(reqAndResParam),1,false); - log.info("请求文件流报文:" + new Gson().toJson(reqAndResParam)); + return reqAndResParam; } - - - - + /** + * 根据装置响应来判断是否询问下一帧数据 + */ + public void sendNextStep(String fileName, String id, int mid,int step, String nDid) { + try { + for (int i = 1; i < 31; i++) { + if (step == 0 ){ + Thread.sleep(5000); + } else { + Thread.sleep(2000); + } + FileRedisDto fileRedisDto = (FileRedisDto) redisUtil.getObjectByKey(AppRedisKey.DOWNLOAD + fileName + mid); + if (Objects.isNull(fileRedisDto)) { + FileRedisDto failDto = new FileRedisDto(); + failDto.setCode(400); + redisUtil.saveByKeyWithExpire(AppRedisKey.DOWNLOAD + fileName + mid,failDto,10L); + } else { + if (Objects.equals(fileRedisDto.getCode(),200)) { + redisUtil.delete("handleEvent:" + nDid); + redisUtil.delete(AppRedisKey.FILE_PART.concat(fileName)); + redisUtil.delete(AppRedisKey.RMQ_FILE_CONSUME_KEY.concat(fileName)); + //删除已经处理完的文件,之后再判断还有是否需要下载的文件 + List fileDto = channelObjectUtil.objectToList(redisUtil.getObjectByKey("eventFile:" + nDid),WaveTimeDto.class); + fileDto.removeIf(item2 -> item2.getFileName().equals(fileName)); + redisUtil.saveByKey("eventFile:" + nDid, fileDto); + if (CollectionUtil.isNotEmpty(fileDto)) { + iCsWaveAnalysisService.channelWave(nDid); + } + break; + } else { + log.info("第" +i+"次尝试"); + //尝试失败则设置code为400,如果装置响应了,则会将code置为200 + FileRedisDto failDto = new FileRedisDto(); + failDto.setCode(400); + redisUtil.saveByKeyWithExpire(AppRedisKey.DOWNLOAD + fileName + mid,failDto,10L); + ReqAndResDto.Req req = getPojo(mid,fileName,step); + publisher.send("/Pfm/DevFileCmd/V1" + id, new Gson().toJson(req), 1, false); + } + } + } + } catch (Exception e) { + throw new BusinessException(AlgorithmResponseEnum.ASK_DEVICE_DIR_ERROR); + } + } } diff --git a/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/mapper/CsDevErrEvtMapper.java b/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/mapper/CsDevErrEvtMapper.java new file mode 100644 index 0000000..ac729dd --- /dev/null +++ b/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/mapper/CsDevErrEvtMapper.java @@ -0,0 +1,16 @@ +package com.njcn.zlevent.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.njcn.zlevent.pojo.po.CsDevErrEvt; + +/** + *

+ * 装置异常事件统计 Mapper 接口 + *

+ * + * @author xy + * @since 2024-09-12 + */ +public interface CsDevErrEvtMapper extends BaseMapper { + +} diff --git a/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/service/ICsDevErrEvtService.java b/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/service/ICsDevErrEvtService.java new file mode 100644 index 0000000..72b9023 --- /dev/null +++ b/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/service/ICsDevErrEvtService.java @@ -0,0 +1,23 @@ +package com.njcn.zlevent.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.njcn.mq.message.AppEventMessage; +import com.njcn.zlevent.pojo.po.CsDevErrEvt; + +/** + *

+ * 装置异常事件统计 服务类 + *

+ * + * @author xy + * @since 2024-09-12 + */ +public interface ICsDevErrEvtService extends IService { + + /** + * 将装置推送的异常事件统计,目前先入库 + * @param appEventMessage + */ + void insertErrorEvent(AppEventMessage appEventMessage); + +} diff --git a/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/service/ICsWaveAnalysisService.java b/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/service/ICsWaveAnalysisService.java index 929cc0b..b09a0c5 100644 --- a/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/service/ICsWaveAnalysisService.java +++ b/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/service/ICsWaveAnalysisService.java @@ -20,4 +20,6 @@ public interface ICsWaveAnalysisService { */ void analysis(AppEventMessage appEventMessage); + void channelWave(String nDid); + } diff --git a/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/service/impl/CsAlarmServiceImpl.java b/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/service/impl/CsAlarmServiceImpl.java index 6d4e51d..e82f7e4 100644 --- a/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/service/impl/CsAlarmServiceImpl.java +++ b/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/service/impl/CsAlarmServiceImpl.java @@ -56,13 +56,13 @@ public class CsAlarmServiceImpl extends ServiceImpl im try { List dataArray = appEventMessage.getMsg().getDataArray(); for (AppEventMessage.DataArray item : dataArray) { + eventTime = eventService.timeFormat(item.getDataTimeSec(),item.getDataTimeUSec()); //事件入库 CsEventPO csEvent = new CsEventPO(); csEvent.setId(id); csEvent.setDeviceId(po.getId()); csEvent.setProcess(po.getProcess()); csEvent.setCode(item.getCode()); - eventTime = eventService.timeFormat(item.getDataTimeSec(),item.getDataTimeUSec()); csEvent.setStartTime(eventTime); tag = item.getName(); csEvent.setTag(tag); @@ -74,19 +74,19 @@ public class CsAlarmServiceImpl extends ServiceImpl im } if (CollectionUtil.isNotEmpty(list1)){ csEventService.saveBatch(list1); - } - //推送事件逻辑处理 && cs_event_user入库 && 修改字典中告警事件的编码 - for (AppEventMessage.DataArray item : dataArray) { - if (Objects.isNull(item.getCode())){ - sendEventUtils.sendUser(2,item.getType(),po.getId(),item.getName(),eventTime,id); - } else { - sendEventUtils.sendUser(2,item.getType(),po.getId(),item.getCode(),eventTime,id); - //更新字典信息 - EleEpdPqd eleEpdPqd = epdFeignClient.findByName(item.getName()).getData(); - EleEpdPqdParam.EleEpdPqdUpdateParam updateParam = new EleEpdPqdParam.EleEpdPqdUpdateParam(); - BeanUtils.copyProperties(eleEpdPqd,updateParam); - updateParam.setDefaultValue(item.getCode()); - epdFeignClient.update(updateParam); + //推送事件逻辑处理 && cs_event_user入库 && 修改字典中告警事件的编码 + for (AppEventMessage.DataArray item : dataArray) { + if (Objects.isNull(item.getCode())){ + sendEventUtils.sendUser(2,item.getType(),po.getId(),item.getName(),eventTime,id); + } else { + sendEventUtils.sendUser(2,item.getType(),po.getId(),item.getCode(),eventTime,id); + //更新字典信息 + EleEpdPqd eleEpdPqd = epdFeignClient.findByName(item.getName()).getData(); + EleEpdPqdParam.EleEpdPqdUpdateParam updateParam = new EleEpdPqdParam.EleEpdPqdUpdateParam(); + BeanUtils.copyProperties(eleEpdPqd,updateParam); + updateParam.setDefaultValue(item.getCode()); + epdFeignClient.update(updateParam); + } } } } catch (Exception e) { diff --git a/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/service/impl/CsDevErrEvtServiceImpl.java b/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/service/impl/CsDevErrEvtServiceImpl.java new file mode 100644 index 0000000..10f4dd5 --- /dev/null +++ b/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/service/impl/CsDevErrEvtServiceImpl.java @@ -0,0 +1,61 @@ +package com.njcn.zlevent.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.njcn.mq.message.AppEventMessage; +import com.njcn.zlevent.mapper.CsDevErrEvtMapper; +import com.njcn.zlevent.pojo.po.CsDevErrEvt; +import com.njcn.zlevent.service.ICsDevErrEvtService; +import org.springframework.stereotype.Service; + +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; + +/** + *

+ * 装置异常事件统计 服务实现类 + *

+ * + * @author xy + * @since 2024-09-12 + */ +@Service +public class CsDevErrEvtServiceImpl extends ServiceImpl implements ICsDevErrEvtService { + + @Override + public void insertErrorEvent(AppEventMessage appEventMessage) { + List list = new ArrayList<>(); + List dataArrayList = appEventMessage.getMsg().getDataArray(); + for (AppEventMessage.DataArray dataArray : dataArrayList) { + CsDevErrEvt evt = new CsDevErrEvt(); + evt.setNdid(appEventMessage.getId()); + evt.setEvtTime(timeFormat(dataArray.getDataTimeSec(),dataArray.getDataTimeUSec())); + evt.setCode(dataArray.getCode()); + list.add(evt); + } + this.saveBatch(list); + } + + /** + * 时间转换 + */ + public LocalDateTime timeFormat(Long time1, Long time2) { + String time; + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + time1 = time1 - 8*3600; + long t1 = time1 * 1000000 + time2; + String time1String = String.valueOf(t1); + String time11 = time1String.substring(time1String.length() - 6); + String time111 = time1String.substring(0,time1String.length() - 6); + String formatTime1 = format.format(Long.parseLong(time111) * 1000); + if (time2 == 0){ + time = formatTime1 + ".000000"; + } else { + time = formatTime1 + "." + time11; + } + DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS"); + return LocalDateTime.parse(time, fmt); + } +} diff --git a/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/service/impl/CsWaveAnalysisServiceImpl.java b/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/service/impl/CsWaveAnalysisServiceImpl.java index 07699e9..8b6f830 100644 --- a/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/service/impl/CsWaveAnalysisServiceImpl.java +++ b/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/service/impl/CsWaveAnalysisServiceImpl.java @@ -7,9 +7,11 @@ import com.njcn.access.api.CsTopicFeignClient; import com.njcn.access.enums.AccessEnum; import com.njcn.access.enums.TypeEnum; import com.njcn.access.pojo.dto.ReqAndResDto; +import com.njcn.access.utils.ChannelObjectUtil; import com.njcn.common.pojo.exception.BusinessException; import com.njcn.csdevice.api.CsLineFeignClient; import com.njcn.csdevice.api.EquipmentFeignClient; +import com.njcn.csdevice.enums.AlgorithmResponseEnum; import com.njcn.csdevice.pojo.po.CsLinePO; import com.njcn.mq.message.AppEventMessage; import com.njcn.redis.pojo.enums.AppRedisKey; @@ -24,6 +26,7 @@ import com.njcn.zlevent.service.ICsWaveAnalysisService; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import net.sf.json.JSONObject; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.text.SimpleDateFormat; @@ -54,11 +57,11 @@ public class CsWaveAnalysisServiceImpl implements ICsWaveAnalysisService { private final DicDataFeignClient dicDataFeignClient; + private final ChannelObjectUtil channelObjectUtil; + @Override public void analysis(AppEventMessage appEventMessage) { - int mid = 1; - //获取监测点 - String lineId = null; + List list = new ArrayList<>(); Object object1 = redisUtil.getObjectByKey(AppRedisKey.LINE_POSITION+appEventMessage.getId()); if (Objects.isNull(object1)){ lineInfo(appEventMessage.getId()); @@ -69,30 +72,43 @@ public class CsWaveAnalysisServiceImpl implements ICsWaveAnalysisService { List dataArrayList = appEventMessage.getMsg().getDataArray(); if (CollectionUtil.isNotEmpty(dataArrayList)){ for (AppEventMessage.DataArray item : dataArrayList) { - //处理mid - if (Objects.equals(mid,10000)){ - mid = 1; - } List paramList = item.getParam(); Object object = paramList.stream().filter(item2 -> ZlConstant.WAVE_NAME.equals(item2.getName())).findFirst().get().getData(); Object object2 = paramList.stream().filter(item2 -> ZlConstant.WAVE_PARAM_RCDKEEPTIME.equals(item2.getName())).findFirst().get().getData(); Object object3 = paramList.stream().filter(item2 -> ZlConstant.WAVE_POSITION.equals(item2.getName())).findFirst().get().getData(); - if (Objects.equals(object3.toString(),ZlConstant.GRID)){ - lineId = new Gson().fromJson(String.valueOf(redisUtil.getObjectByKey(AppRedisKey.LINE_POSITION+appEventMessage.getId())), Map.class).get("1").toString(); - } else if (Objects.equals(object3.toString(),ZlConstant.LOAD)){ - lineId = new Gson().fromJson(String.valueOf(redisUtil.getObjectByKey(AppRedisKey.LINE_POSITION+appEventMessage.getId())), Map.class).get("2").toString(); - } else { - lineId = new Gson().fromJson(String.valueOf(redisUtil.getObjectByKey(AppRedisKey.LINE_POSITION+appEventMessage.getId())), Map.class).get("0").toString(); - } + String lineId = appEventMessage.getId() + appEventMessage.getMsg().getClDid(); String fileName = object.toString().replaceAll("\\[","").replaceAll("]",""); List fileList = Arrays.stream(fileName.split(",")).collect(Collectors.toList()); + //获取到录波文件,将文件信息存储起来 for (String file : fileList) { file = file.trim(); - askFileInfo(appEventMessage.getId(),mid,file); - mid++; - channelTimeRange(file,item.getDataTimeSec(),item.getDataTimeUSec(),(Double)object2,deviceId,lineId,object3.toString()); + WaveTimeDto dto = channelTimeRange(file,appEventMessage.getId(),item.getDataTimeSec(),item.getDataTimeUSec(),(Double)object2,deviceId,lineId,object3.toString()); + list.add(dto); + } + Object obj = redisUtil.getObjectByKey("eventFile:" + appEventMessage.getId()); + if (!Objects.isNull(obj)) { + List oldList = channelObjectUtil.objectToList(obj,WaveTimeDto.class); + oldList.addAll(list); + redisUtil.saveByKey("eventFile:" + appEventMessage.getId(), oldList); + } else { + redisUtil.saveByKey("eventFile:" + appEventMessage.getId(), list); } } + //处理缓存数据 + channelWave(appEventMessage.getId()); + } + } + + @Override + @Async("asyncExecutor") + public void channelWave(String nDid) { + //有相同文件处理时,则不进行数据处理 + Object obj = redisUtil.getObjectByKey("handleEvent:" + nDid); + if (Objects.isNull(obj)) { + WaveTimeDto dto = channelObjectUtil.objectToList( redisUtil.getObjectByKey("eventFile:" + nDid),WaveTimeDto.class).get(0); + askFileInfo(nDid,1,dto.getFileName()); + } else { + throw new BusinessException(AlgorithmResponseEnum.FILE_DOWNLOADING); } } @@ -114,10 +130,11 @@ public class CsWaveAnalysisServiceImpl implements ICsWaveAnalysisService { publisher.send("/Pfm/DevFileCmd/"+version+"/"+nDid,new Gson().toJson(reqAndResParam),1,false); } + /** * 时间处理 */ - public void channelTimeRange(String fileName, long time, long subtleTime, Double millisecond, String deviceId, String lineId, String location) { + public WaveTimeDto channelTimeRange(String fileName,String nDid, long time, long subtleTime, Double millisecond, String deviceId, String lineId, String location) { SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); time = time - 8*3600; // 将millisecond转换为长整型,并乘以1000以获取微秒 @@ -136,12 +153,14 @@ public class CsWaveAnalysisServiceImpl implements ICsWaveAnalysisService { String formatTime2 = format.format(Long.parseLong(time222) * 1000); WaveTimeDto waveTimeDto = new WaveTimeDto(); + waveTimeDto.setFileName(fileName); waveTimeDto.setStartTime(formatTime1 + "." + time11); waveTimeDto.setEndTime(formatTime2 + "." + time22); waveTimeDto.setDeviceId(deviceId); + waveTimeDto.setNDid(nDid); waveTimeDto.setLineId(lineId); waveTimeDto.setLocation(location); - redisUtil.saveByKeyWithExpire(AppRedisKey.TIME+fileName,waveTimeDto,600L); + return waveTimeDto; } /** diff --git a/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/service/impl/EventServiceImpl.java b/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/service/impl/EventServiceImpl.java index 2c8de7e..869d48d 100644 --- a/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/service/impl/EventServiceImpl.java +++ b/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/service/impl/EventServiceImpl.java @@ -92,6 +92,7 @@ public class EventServiceImpl implements IEventService { //处理事件数据 List dataArray = appEventMessage.getMsg().getDataArray(); for (AppEventMessage.DataArray item : dataArray) { + eventTime = timeFormat(item.getDataTimeSec(),item.getDataTimeUSec()); id = IdUtil.fastSimpleUUID(); //事件入库 CsEventPO csEvent = new CsEventPO(); @@ -137,6 +138,10 @@ public class EventServiceImpl implements IEventService { if (Objects.equals(param.getName(),ZlConstant.EVT_PARAM_TM)){ csEvent.setPersistTime(Double.parseDouble(param.getData().toString())); } + lineId = appEventMessage.getId() + appEventMessage.getMsg().getClDid(); + fields.put(param.getName(),appEventMessage.getMsg().getClDid()==1?"电网侧":"负载侧"); + csEvent.setLocation(appEventMessage.getMsg().getClDid()==1?ZlConstant.GRID:ZlConstant.LOAD); + csEvent.setClDid(appEventMessage.getMsg().getClDid()); fields.put(param.getName(),param.getData()); } //fixme 这边前置传递的应该是UTC时间,但是前置说是传递的北京时间,讨论了一下没太理解。这边暂时先这样处理,influx入库处理成北京时间,减去8小时。 @@ -161,15 +166,15 @@ public class EventServiceImpl implements IEventService { //cs_event入库 if (CollectionUtil.isNotEmpty(list1)){ csEventService.saveBatch(list1); + //推送事件逻辑处理 && cs_event_user入库 + for (AppEventMessage.DataArray item : dataArray) { + sendEventUtils.sendUser(1,item.getType(),po.getId(),item.getName(),eventTime,id); + } } //evt_data入库 if (CollectionUtil.isNotEmpty(records)) { influxDbUtils.batchInsert(influxDbUtils.getDbName(), "", InfluxDB.ConsistencyLevel.ALL, TimeUnit.MILLISECONDS, records); } - //推送事件逻辑处理 && cs_event_user入库 - for (AppEventMessage.DataArray item : dataArray) { - sendEventUtils.sendUser(1,item.getType(),po.getId(),item.getName(),eventTime,id); - } } catch (Exception e) { CsEventLogs csEventLogs = new CsEventLogs(); csEventLogs.setLineId(lineId); diff --git a/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/service/impl/FileServiceImpl.java b/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/service/impl/FileServiceImpl.java index 9ad5380..64d98b2 100644 --- a/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/service/impl/FileServiceImpl.java +++ b/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/service/impl/FileServiceImpl.java @@ -4,15 +4,15 @@ import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.text.StrPool; import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSON; -import com.alibaba.nacos.common.utils.ConcurrentHashSet; import com.alibaba.nacos.shaded.com.google.gson.Gson; -import com.alibaba.nacos.shaded.com.google.gson.GsonBuilder; import com.github.tocrhz.mqtt.publisher.MqttPublisher; import com.njcn.access.api.CsTopicFeignClient; import com.njcn.access.enums.AccessEnum; import com.njcn.access.enums.AccessResponseEnum; import com.njcn.access.enums.TypeEnum; import com.njcn.access.pojo.dto.ReqAndResDto; +import com.njcn.access.utils.CRC32Utils; +import com.njcn.access.utils.ChannelObjectUtil; import com.njcn.common.config.GeneralInfo; import com.njcn.common.pojo.exception.BusinessException; import com.njcn.csharmonic.api.WavePicFeignClient; @@ -28,10 +28,7 @@ import com.njcn.zlevent.pojo.dto.FileStreamDto; import com.njcn.zlevent.pojo.dto.WaveTimeDto; import com.njcn.zlevent.pojo.po.CsEventFileLogs; import com.njcn.zlevent.pojo.po.CsWave; -import com.njcn.zlevent.service.ICsEventFileLogsService; -import com.njcn.zlevent.service.ICsEventService; -import com.njcn.zlevent.service.ICsWaveService; -import com.njcn.zlevent.service.IFileService; +import com.njcn.zlevent.service.*; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import net.sf.json.JSONObject; @@ -41,7 +38,6 @@ import java.io.*; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; -import java.util.concurrent.TimeUnit; /** * 类的介绍: @@ -56,22 +52,16 @@ import java.util.concurrent.TimeUnit; public class FileServiceImpl implements IFileService { private final RedisUtil redisUtil; - private final CsTopicFeignClient csTopicFeignClient; - private final MqttPublisher publisher; - private final FileStorageUtil fileStorageUtil; - private final ICsEventService csEventService; - private final ICsEventFileLogsService csEventLogsService; - private final WavePicFeignClient wavePicFeignClient; - private final ICsWaveService csWaveService; - private final GeneralInfo generalInfo; + private final ICsWaveAnalysisService iCsWaveAnalysisService; + private final ChannelObjectUtil channelObjectUtil; @Override public void analysisFileInfo(AppFileMessage appFileMessage) { @@ -82,12 +72,7 @@ public class FileServiceImpl implements IFileService { String fileName = appFileMessage.getMsg().getFileInfo().getName(); //缓存文件信息用于文件流拼接 FileInfoDto fileInfoDto = new FileInfoDto(); - //获取波形文件起始结束时间 - Object fileInfo = redisUtil.getObjectByKey(AppRedisKey.TIME+fileName); - if (Objects.isNull(fileInfo)){ - throw new BusinessException(AccessResponseEnum.WAVE_INFO_MISSING); - } - WaveTimeDto waveTimeDto = JSON.parseObject(JSON.toJSONString(fileInfo), WaveTimeDto.class); + WaveTimeDto waveTimeDto = channelObjectUtil.objectToList(redisUtil.getObjectByKey("eventFile:" + appFileMessage.getId()),WaveTimeDto.class).get(0); fileInfoDto.setStartTime(waveTimeDto.getStartTime()); fileInfoDto.setEndTime(waveTimeDto.getEndTime()); fileInfoDto.setDeviceId(waveTimeDto.getDeviceId()); @@ -123,8 +108,8 @@ public class FileServiceImpl implements IFileService { @Override public void analysisFileStream(AppFileMessage appFileMessage) { DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS"); - String filePath; - List list = new ArrayList<>(); + String filePath = null; + Set list = new HashSet<>(); FileStreamDto fileStreamDto = new FileStreamDto(); //波形文件上传成功后,将文件信息存储一下,方便后期查看 CsWave csWave = new CsWave(); @@ -138,18 +123,54 @@ public class FileServiceImpl implements IFileService { try { //todo 目前文件先只处理波形事件的,后续有其他文件再做处理 String fileName = appFileMessage.getMsg().getName(); - //String lsFileName =fileName.split(StrUtil.SLASH)[fileName.split(StrUtil.SLASH).length - 1]; String lsFileName = generalInfo.getBusinessTempPath() + File.separator + fileName.split(StrUtil.SLASH)[fileName.split(StrUtil.SLASH).length - 1]; + File lsFile =new File(generalInfo.getBusinessTempPath()); + //如果文件夹不存在则创建 + if (!lsFile.exists() && !lsFile.isDirectory()) { + lsFile .mkdirs(); + } //获取缓存的文件信息 Object fileInfo = redisUtil.getObjectByKey(AppRedisKey.RMQ_FILE_CONSUME_KEY.concat(fileName)); FileInfoDto fileInfoDto = JSON.parseObject(JSON.toJSONString(fileInfo), FileInfoDto.class); - //1.判断当前文件是否之前缓存过,没缓存,则先缓存(这边缓存两条记录,一条是用来判断超时的,还有一条记录文件数据,文件数据目前没有过期时间,文件数据收完才会删除) - //2.缓存了判断收到的报文个数是否和总个数一致,一致则解析文件;不一致则更新缓存 - //3.超时判断: 30s分钟未收到相关文件信息,核查文件个数,看丢失哪些帧,重新请求 - if(fileName.contains(".cfg") || fileName.contains(".dat")) { + if (Objects.isNull(fileInfoDto)) { + String fileCheck = redisUtil.getObjectByKey("fileCheck"+fileName).toString(); if (appFileMessage.getMsg().getFrameTotal() == 1){ //解析文件入库 - filePath = fileStream(1,null,appFileMessage.getMsg().getData(),fileName,appFileMessage.getId()); + filePath = fileStream(1,null,appFileMessage.getMsg().getData(),fileName,appFileMessage.getId(),fileCheck,"download"); + } else { + //最后一帧 + if (Objects.equals(appFileMessage.getMsg().getFrameTotal(), appFileMessage.getMsg().getFrameCurr())) { + Map filePartMap = readFile(lsFileName); + filePartMap.put(appFileMessage.getMsg().getFrameCurr(), appFileMessage.getMsg().getData()); + //解析文件 + filePath = fileStream(appFileMessage.getMsg().getFrameTotal(), filePartMap, null, fileName, appFileMessage.getId(),fileCheck,"download"); + //删除临时文件 + File file = new File(lsFileName); + if (file.exists()) { + file.delete(); + } + } + //中间帧 + else { + Map filePartMap = readFile(lsFileName); + if (Objects.isNull(filePartMap.get(appFileMessage.getMsg().getFrameCurr()))) { + appendFile(lsFileName,appFileMessage.getMsg().getFrameCurr(),appFileMessage.getMsg().getData()); + } + } + } + if (!Objects.isNull(filePath)){ + redisUtil.saveByKey("downloadFilePath:"+appFileMessage.getMsg().getName(),filePath); + } + } + //录波文件下载 + //1.判断当前文件是否之前缓存过,没缓存,则先缓存(这边缓存两条记录,一条是用来判断超时的,还有一条记录文件数据,文件数据目前没有过期时间,文件数据收完才会删除) + //2.缓存了判断收到的报文个数是否和总个数一致,一致则解析文件;不一致则更新缓存 + //3.超时判断: 30s未收到相关文件信息,核查文件个数,看丢失哪些帧,重新请求 + else { + redisUtil.saveByKey("handleEvent:" + appFileMessage.getId(),"doing"); + if (appFileMessage.getMsg().getFrameTotal() == 1){ + //解析文件入库 + filePath = fileStream(1,null,appFileMessage.getMsg().getData(),fileName,appFileMessage.getId(),fileInfoDto.getFileCheck(),"event"); csEventLogs.setStatus(1); csEventLogs.setRemark("当前文件1帧,全部收到,解析成功!"); csEventLogs.setNowStep(1); @@ -164,8 +185,18 @@ public class FileServiceImpl implements IFileService { if (CollectionUtil.isNotEmpty(eventList)){ eventList.forEach(wavePicFeignClient::getWavePics); } + redisUtil.delete("handleEvent:" + appFileMessage.getId()); redisUtil.delete(AppRedisKey.RMQ_FILE_CONSUME_KEY.concat(fileInfoDto.getName())); + //删除已经处理完的文件,之后再判断还有是否需要下载的文件 + List fileDto = channelObjectUtil.objectToList(redisUtil.getObjectByKey("eventFile:" + appFileMessage.getId()),WaveTimeDto.class); + fileDto.removeIf(item -> item.getFileName().equals(fileName)); + redisUtil.saveByKey("eventFile:" + appFileMessage.getId(), fileDto); + if (CollectionUtil.isNotEmpty(fileDto)) { + iCsWaveAnalysisService.channelWave(appFileMessage.getId()); + } } else { + //收到数据就刷新缓存值 + redisUtil.saveByKeyWithExpire(AppRedisKey.FILE_PART_TIME.concat(appFileMessage.getMsg().getName()), null, 10L); Object object1 = redisUtil.getObjectByKey(AppRedisKey.FILE_PART.concat(fileName)); if (Objects.isNull(object1)){ fileStreamDto.setTotal(appFileMessage.getMsg().getFrameTotal()); @@ -178,7 +209,6 @@ public class FileServiceImpl implements IFileService { csEventLogs.setNowStep(appFileMessage.getMsg().getFrameCurr()); csEventLogs.setAllStep(appFileMessage.getMsg().getFrameTotal()); csEventLogs.setIsAll(0); - redisUtil.saveByKeyWithExpire(AppRedisKey.FILE_PART_TIME.concat(appFileMessage.getMsg().getName()), null, 30L); redisUtil.saveByKey(AppRedisKey.FILE_PART.concat(appFileMessage.getMsg().getName()), fileStreamDto); //将数据写入临时文件 appendFile(lsFileName,appFileMessage.getMsg().getFrameCurr(),appFileMessage.getMsg().getData()); @@ -192,7 +222,7 @@ public class FileServiceImpl implements IFileService { Map filePartMap = readFile(lsFileName); filePartMap.put(appFileMessage.getMsg().getFrameCurr(), appFileMessage.getMsg().getData()); //解析文件 - filePath = fileStream(appFileMessage.getMsg().getFrameTotal(), filePartMap, null, fileName, appFileMessage.getId()); + filePath = fileStream(appFileMessage.getMsg().getFrameTotal(), filePartMap, null, fileName, appFileMessage.getId(),fileInfoDto.getFileCheck(),"event"); csEventLogs.setStatus(1); csEventLogs.setRemark("当前文件" + appFileMessage.getMsg().getFrameTotal() + "帧,这是第" + appFileMessage.getMsg().getFrameCurr() + "帧,全部收到,解析成功!"); csEventLogs.setNowStep(appFileMessage.getMsg().getFrameCurr()); @@ -212,27 +242,33 @@ public class FileServiceImpl implements IFileService { redisUtil.delete(AppRedisKey.FILE_PART_TIME.concat(appFileMessage.getMsg().getName())); redisUtil.delete(AppRedisKey.FILE_PART.concat(appFileMessage.getMsg().getName())); redisUtil.delete(AppRedisKey.FILE_PART_MISSING.concat(appFileMessage.getMsg().getName())); + redisUtil.delete("handleEvent:" + appFileMessage.getId()); //删除临时文件 File file = new File(lsFileName); if (file.exists()) { file.delete(); } + //删除已经处理完的文件,之后再判断还有是否需要下载的文件 + List fileDto = channelObjectUtil.objectToList(redisUtil.getObjectByKey("eventFile:" + appFileMessage.getId()),WaveTimeDto.class); + fileDto.removeIf(item -> item.getFileName().equals(fileName)); + redisUtil.saveByKey("eventFile:" + appFileMessage.getId(), fileDto); + if (CollectionUtil.isNotEmpty(fileDto)) { + iCsWaveAnalysisService.channelWave(appFileMessage.getId()); + } } else { - Object object2 = redisUtil.getObjectByKey(AppRedisKey.FILE_PART_MISSING.concat(fileName)); csEventLogs.setStatus(1); csEventLogs.setRemark("当前文件" + appFileMessage.getMsg().getFrameTotal() + "帧,这是第" + appFileMessage.getMsg().getFrameCurr() + "帧,记录成功!"); csEventLogs.setNowStep(appFileMessage.getMsg().getFrameCurr()); csEventLogs.setAllStep(appFileMessage.getMsg().getFrameTotal()); csEventLogs.setIsAll(0); - if (Objects.isNull(object2)) { - redisUtil.saveByKeyWithExpire(AppRedisKey.FILE_PART_TIME.concat(appFileMessage.getMsg().getName()), null, 30L); - } else { - redisUtil.saveByKeyWithExpire(AppRedisKey.FILE_PART_TIME.concat(appFileMessage.getMsg().getName()), null, 1L); - } redisUtil.saveByKey(AppRedisKey.FILE_PART.concat(appFileMessage.getMsg().getName()), dto); //将数据写入临时文件 appendFile(lsFileName, appFileMessage.getMsg().getFrameCurr(), appFileMessage.getMsg().getData()); log.info("当前文件 {} 帧,这是第 {} 帧报文,记录成功", appFileMessage.getMsg().getFrameTotal(), appFileMessage.getMsg().getFrameCurr()); + Object object2 = redisUtil.getObjectByKey(AppRedisKey.FILE_PART_MISSING.concat(fileName)); + if (!Objects.isNull(object2)) { + redisUtil.saveByKeyWithExpire(AppRedisKey.FILE_PART_TIME.concat(appFileMessage.getMsg().getName()), null, 1L); + } } } else { csEventLogs.setStatus(1); @@ -249,9 +285,6 @@ public class FileServiceImpl implements IFileService { csEventLogs.setLocation(fileInfoDto.getLocation()); //记录日志 csEventLogsService.save(csEventLogs); - } else { - //todo 处理其他文件 - log.info("暂未做其他文件处理"); } } catch (Exception e){ csEventLogs.setStatus(0); @@ -283,10 +316,10 @@ public class FileServiceImpl implements IFileService { /** * 组装文件 */ - public String fileStream(Integer number, Map map, String data, String fileName, String nDid) { + public String fileStream(Integer number, Map map, String data, String fileName, String nDid, String fileCheck,String type) { String filePath; if (number == 1){ - filePath = stream(true,data,nDid,fileName,null); + filePath = stream(true,data,nDid,fileName,null,fileCheck,type); } else { int lengthByte = 0; for (int i = 1; i <= number; i++) { @@ -300,7 +333,7 @@ public class FileServiceImpl implements IFileService { System.arraycopy(byteArray, 0, allByte, countLength, byteArray.length); countLength += byteArray.length; } - filePath = stream(false,null,nDid,fileName,allByte); + filePath = stream(false,null,nDid,fileName,allByte,fileCheck,type); } return filePath; } @@ -308,7 +341,8 @@ public class FileServiceImpl implements IFileService { /** * 解析存储文件信息 */ - public String stream(boolean bool, String stream, String folder, String fileName, byte[] bytes) { + public String stream(boolean bool, String stream, String folder, String fileName, byte[] bytes, String fileCheck, String type) { + String path; byte[] byteArray = null; //将文件后缀替换成大写 String[] parts = fileName.split(StrUtil.SLASH); @@ -321,9 +355,18 @@ public class FileServiceImpl implements IFileService { } else { byteArray = bytes; } - //todo 此处需要做文件crc校验或者md5校验,目前不知道怎么处理,先放一下 + //文件校验 + int crc = CRC32Utils.calculateCRC32(byteArray,byteArray.length,0xffffffff); + String hexString = String.format("%08X", crc); + if (!Objects.equals(hexString,fileCheck)) { + throw new BusinessException(AccessResponseEnum.FILE_CHECK_ERROR); + } InputStream inputStream = new ByteArrayInputStream(byteArray); - String path = fileStorageUtil.uploadStreamSpecifyName(inputStream, OssPath.WAVE_DIR + folder + StrUtil.SLASH,fileName); + if (Objects.equals(type,"download")) { + path = fileStorageUtil.uploadStreamSpecifyName(inputStream, OssPath.DOWNLOAD_DIR + folder + StrUtil.SLASH,fileName); + } else { + path = fileStorageUtil.uploadStreamSpecifyName(inputStream, OssPath.WAVE_DIR + folder + StrUtil.SLASH,fileName); + } try { inputStream.close(); } catch (IOException e) { diff --git a/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/utils/FileCheckUtils.java b/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/utils/FileCheckUtils.java deleted file mode 100644 index 29fc592..0000000 --- a/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/utils/FileCheckUtils.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.njcn.zlevent.utils; - -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.zip.CRC32; - -/** - * 类的介绍:用于文件校验 - * - * @author xuyang - * @version 1.0.0 - * @createTime 2023/9/8 13:32 - */ - -public class FileCheckUtils { - - /** - * 32位CRC检验 - * @param inputStream 文件流 - * @return - * @throws IOException - */ - public static long calculateCRC32Checksum(InputStream inputStream) throws IOException { - CRC32 crc32 = new CRC32(); - // 用于读取文件内容的缓冲区 - byte[] buffer = new byte[1024]; - int bytesRead; - while ((bytesRead = inputStream.read(buffer)) != -1) { - crc32.update(buffer, 0, bytesRead); - } - return crc32.getValue(); - } - - /** - * MD5检验 - * @param inputStream 文件流 - * @return - * @throws IOException - */ - public static String calculateMD5Checksum(InputStream inputStream) throws IOException, NoSuchAlgorithmException { - MessageDigest md5 = MessageDigest.getInstance("MD5"); - // 用于读取文件内容的缓冲区 - byte[] buffer = new byte[8192]; - int bytesRead; - while ((bytesRead = inputStream.read(buffer)) != -1) { - md5.update(buffer, 0, bytesRead); - } - byte[] digest = md5.digest(); - // 将MD5摘要转换为十六进制字符串 - StringBuilder md5Checksum = new StringBuilder(); - for (byte b : digest) { - md5Checksum.append(String.format("%02x", b)); - } - return md5Checksum.toString(); - } -} diff --git a/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/utils/SendEventUtils.java b/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/utils/SendEventUtils.java index 0fa0c9d..b022dcb 100644 --- a/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/utils/SendEventUtils.java +++ b/iot-analysis/analysis-zl-event/zl-event-boot/src/main/java/com/njcn/zlevent/utils/SendEventUtils.java @@ -2,8 +2,6 @@ package com.njcn.zlevent.utils; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.date.DatePattern; -import cn.hutool.core.date.DateUnit; -import cn.hutool.core.date.DateUtil; import com.alibaba.nacos.shaded.com.google.gson.Gson; import com.njcn.csdevice.api.CsDeviceUserFeignClient; import com.njcn.csdevice.api.CsLedgerFeignClient; @@ -22,7 +20,6 @@ import com.njcn.zlevent.service.ICsEventUserService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; -import org.springframework.util.CollectionUtils; import javax.annotation.Resource; import java.io.BufferedReader; @@ -34,7 +31,6 @@ import java.net.URL; import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; @@ -87,8 +83,9 @@ public class SendEventUtils { public void sendUser(Integer eventType,String type,String devId, String eventName, LocalDateTime eventTime, String id) { int code; List users = new ArrayList<>(); - + List eventUser; List devCodeList; + List userList = new ArrayList<>(); List csEventSendMsgList = new ArrayList<>(); NoticeUserDto noticeUserDto = new NoticeUserDto(); NoticeUserDto.Payload payload = new NoticeUserDto.Payload(); @@ -101,51 +98,65 @@ public class SendEventUtils { case "1": code = 2; //设备自身事件 不推送给用户,推送给业务管理 - users = getAdminUser(); - if (CollectionUtil.isNotEmpty(users)){ - noticeUserDto.setPushClientId(Collections.singletonList(users.get(0).getDevCode())); - noticeUserDto.setTitle("设备事件"); - //记录需要通知的用户和事件关系 - CsEventUserPO csEventUser = new CsEventUserPO(); - csEventUser.setUserId(users.get(0).getId()); - csEventUser.setStatus(0); - csEventUser.setEventId(id); - result.add(csEventUser); + eventUser = getEventUser(devId,false); + if (CollectionUtil.isNotEmpty(eventUser)) { + eventUser.forEach(item->{ + CsEventUserPO csEventUser = new CsEventUserPO(); + csEventUser.setUserId(item); + csEventUser.setStatus(0); + csEventUser.setEventId(id); + result.add(csEventUser); + }); + + users = getSendUser(eventUser,2); + if (CollectionUtil.isNotEmpty(users)){ + for (User user : users){ + userList.add(user.getDevCode()); + } + noticeUserDto.setPushClientId(userList); + noticeUserDto.setTitle("设备事件"); + } } break; case "2": code = 0; //暂态事件 - users = getEventUser(devId,0); - devCodeList = users.stream().map(User::getDevCode).distinct().collect(Collectors.toList()); - noticeUserDto.setPushClientId(devCodeList); - noticeUserDto.setTitle("暂态事件"); - //记录需要通知的用户和事件关系 - List list2 = users.stream().map(User::getId).collect(Collectors.toList()); - list2.forEach(item->{ - CsEventUserPO csEventUser2 = new CsEventUserPO(); - csEventUser2.setUserId(item); - csEventUser2.setStatus(0); - csEventUser2.setEventId(id); - result.add(csEventUser2); - }); + eventUser = getEventUser(devId,true); + if (CollectionUtil.isNotEmpty(eventUser)) { + eventUser.forEach(item->{ + CsEventUserPO csEventUser = new CsEventUserPO(); + csEventUser.setUserId(item); + csEventUser.setStatus(0); + csEventUser.setEventId(id); + result.add(csEventUser); + }); + users = getSendUser(eventUser,0); + if (CollectionUtil.isNotEmpty(users)){ + devCodeList = users.stream().map(User::getDevCode).distinct().collect(Collectors.toList()); + noticeUserDto.setPushClientId(devCodeList); + noticeUserDto.setTitle("暂态事件"); + } + } break; case "3": code = 1; //稳态事件 - users = getEventUser(devId,1); - devCodeList = users.stream().map(User::getDevCode).distinct().collect(Collectors.toList()); - noticeUserDto.setPushClientId(devCodeList); - noticeUserDto.setTitle("稳态事件"); - //记录需要通知的用户和事件关系 - List list3 = users.stream().map(User::getId).collect(Collectors.toList()); - list3.forEach(item->{ - CsEventUserPO csEventUser3 = new CsEventUserPO(); - csEventUser3.setUserId(item); - csEventUser3.setStatus(0); - csEventUser3.setEventId(id); - result.add(csEventUser3); - }); + eventUser = getEventUser(devId,true); + if (CollectionUtil.isNotEmpty(eventUser)) { + eventUser.forEach(item->{ + CsEventUserPO csEventUser = new CsEventUserPO(); + csEventUser.setUserId(item); + csEventUser.setStatus(0); + csEventUser.setEventId(id); + result.add(csEventUser); + }); + users = getSendUser(eventUser,1); + if (CollectionUtil.isNotEmpty(users)){ + devCodeList = users.stream().map(User::getDevCode).distinct().collect(Collectors.toList()); + noticeUserDto.setPushClientId(devCodeList); + noticeUserDto.setTitle("稳态事件"); + } + } break; default: code = 0; @@ -164,36 +175,42 @@ public class SendEventUtils { switch (type) { case "1": //Ⅰ级告警 不推送给用户,推送给业务管理 - users = getAdminUser(); - if (CollectionUtil.isNotEmpty(users)){ - eventName = epdFeignClient.findByName(eventName).getData().getShowName(); - noticeUserDto.setPushClientId(Collections.singletonList(users.get(0).getDevCode())); - //记录需要通知的用户和事件关系 - users.forEach(item->{ + eventUser = getEventUser(devId,false); + if (CollectionUtil.isNotEmpty(eventUser)) { + eventUser.forEach(item->{ CsEventUserPO csEventUser = new CsEventUserPO(); - csEventUser.setUserId(item.getId()); + csEventUser.setUserId(item); csEventUser.setStatus(0); csEventUser.setEventId(id); result.add(csEventUser); }); + users = getSendUser(eventUser,3); + if (CollectionUtil.isNotEmpty(users)){ + eventName = epdFeignClient.findByName(eventName).getData().getShowName(); + devCodeList = users.stream().map(User::getDevCode).distinct().collect(Collectors.toList()); + noticeUserDto.setPushClientId(devCodeList); + } } break; case "2": eventName = epdFeignClient.findByName(eventName).getData().getShowName(); case "3": //Ⅱ、Ⅲ级告警推送相关用户及业务管理员 - users = getEventUser(devId,3); - devCodeList = users.stream().map(User::getDevCode).distinct().collect(Collectors.toList()); - noticeUserDto.setPushClientId(devCodeList); - //记录需要通知的用户和事件关系 - List list4 = users.stream().map(User::getId).collect(Collectors.toList()); - list4.forEach(item->{ - CsEventUserPO csEventUser4 = new CsEventUserPO(); - csEventUser4.setUserId(item); - csEventUser4.setStatus(0); - csEventUser4.setEventId(id); - result.add(csEventUser4); - }); + eventUser = getEventUser(devId,true); + if (CollectionUtil.isNotEmpty(eventUser)) { + eventUser.forEach(item->{ + CsEventUserPO csEventUser = new CsEventUserPO(); + csEventUser.setUserId(item); + csEventUser.setStatus(0); + csEventUser.setEventId(id); + result.add(csEventUser); + }); + users = getSendUser(eventUser,3); + if (CollectionUtil.isNotEmpty(users)){ + devCodeList = users.stream().map(User::getDevCode).distinct().collect(Collectors.toList()); + noticeUserDto.setPushClientId(devCodeList); + } + } break; default: break; @@ -206,7 +223,16 @@ public class SendEventUtils { payload.setPath("/pages/message/message?type="+payload.getType()); noticeUserDto.setPayload(payload); } - sendEventToUser(noticeUserDto); + if (CollectionUtil.isNotEmpty(noticeUserDto.getPushClientId())) { + List filteredList = noticeUserDto.getPushClientId().stream() + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toList()); + if (CollectionUtil.isNotEmpty(filteredList)) { + noticeUserDto.setPushClientId(filteredList); + sendEventToUser(noticeUserDto); + } + } //记录推送日志 for (User item : users) { CsEventSendMsg csEventSendMsg = new CsEventSendMsg(); @@ -235,7 +261,8 @@ public class SendEventUtils { public void sendEventToUser(NoticeUserDto noticeUserDto) { try { // 创建一个URL对象,指定目标HTTPS接口地址 - URL url = new URL("https://fc-mp-b46c4dff-7244-4f7c-ae8b-7c1194d8cce8.next.bspapp.com/push"); + //URL url = new URL("https://fc-mp-b46c4dff-7244-4f7c-ae8b-7c1194d8cce8.next.bspapp.com/push"); + URL url = new URL("https://fc-mp-ff7b310f-94c9-4468-8260-109111c0a6b2.next.bspapp.com/push"); // 打开HTTPS连接 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); // 设置请求方法为POST @@ -271,57 +298,53 @@ public class SendEventUtils { } /** - * 获取需要通知事件的用户 + * 获取所有需要推送的用户id */ - public List getEventUser(String devId,Integer type) { - List users = new ArrayList<>(); - List result = new ArrayList<>(); - //获取设备下主用户和子用户集合 - List list = csDeviceUserFeignClient.findUserById(devId).getData(); + public List getEventUser(String devId,boolean isAdmin) { List adminUser = appUserFeignClient.getAdminInfo().getData(); List adminList = adminUser.stream().map(User::getId).collect(Collectors.toList()); - list.addAll(adminList); - //查询哪些用户打开了事件提示 - if (CollectionUtil.isNotEmpty(list)){ - List appInfoSet = appInfoSetFeignClient.getListById(list).getData(); - switch (type) { - case 0: - result = appInfoSet.stream() - .filter(person -> person.getEventInfo() == 1) - .map(AppInfoSet::getUserId).collect(Collectors.toList()); - break; - case 1: - result = appInfoSet.stream() - .filter(person -> person.getHarmonicInfo() == 1) - .map(AppInfoSet::getUserId).collect(Collectors.toList()); - break; - case 3: - result = appInfoSet.stream() - .filter(person -> person.getAlarmInfo() == 1) - .map(AppInfoSet::getUserId).collect(Collectors.toList()); - break; - default: - break; - } + if (isAdmin) { + List list = csDeviceUserFeignClient.findUserById(devId).getData(); + adminList.addAll(list); } - if (CollectionUtil.isNotEmpty(result)){ - users = userFeignClient.getUserByIdList(result).getData(); - } - return users; + return adminList; } - public List getAdminUser() { - List users = appUserFeignClient.getAdminInfo().getData(); + + /** + * 获取所有打开推送的用户信息 + */ + public List getSendUser(List userList,Integer type) { + List users = new ArrayList<>(); List result = new ArrayList<>(); - List adminList = users.stream().map(User::getId).collect(Collectors.toList()); - if (CollectionUtil.isNotEmpty(adminList)){ - List appInfoSet = appInfoSetFeignClient.getListById(adminList).getData(); - result = appInfoSet.stream() - .filter(person -> person.getRunInfo() == 1) - .map(AppInfoSet::getUserId).collect(Collectors.toList()); + List appInfoSet = appInfoSetFeignClient.getListById(userList).getData(); + + switch (type) { + case 0: + result = appInfoSet.stream() + .filter(person -> person.getEventInfo() == 1) + .map(AppInfoSet::getUserId).collect(Collectors.toList()); + break; + case 1: + result = appInfoSet.stream() + .filter(person -> person.getHarmonicInfo() == 1) + .map(AppInfoSet::getUserId).collect(Collectors.toList()); + break; + case 2: + result = appInfoSet.stream() + .filter(person -> person.getRunInfo() == 1) + .map(AppInfoSet::getUserId).collect(Collectors.toList()); + break; + case 3: + result = appInfoSet.stream() + .filter(person -> person.getAlarmInfo() == 1) + .map(AppInfoSet::getUserId).collect(Collectors.toList()); + break; + default: + break; } if (CollectionUtil.isNotEmpty(result)){ - users = userFeignClient.getUserByIdList(result).getData(); + users = userFeignClient.appuserByIdList(result).getData(); } return users; } diff --git a/iot-message/message-boot/src/main/java/com/njcn/message/consumer/AppAutoDataConsumer.java b/iot-message/message-boot/src/main/java/com/njcn/message/consumer/AppAutoDataConsumer.java index 8afc7fd..150e2f6 100644 --- a/iot-message/message-boot/src/main/java/com/njcn/message/consumer/AppAutoDataConsumer.java +++ b/iot-message/message-boot/src/main/java/com/njcn/message/consumer/AppAutoDataConsumer.java @@ -45,6 +45,8 @@ public class AppAutoDataConsumer extends EnhanceConsumerMessageHandler nexus-releases Nexus Release Repository - http://192.168.1.13:8001/nexus/content/repositories/releases/ + http://192.168.1.22:8001/nexus/content/repositories/releases/ nexus-snapshots Nexus Snapshot Repository - http://192.168.1.13:8001/nexus/content/repositories/snapshots/ + http://192.168.1.22:8001/nexus/content/repositories/snapshots/ @@ -32,16 +32,15 @@ - 192.168.1.13 + 192.168.1.22 127.0.0.1 - 192.168.1.13 + 192.168.1.22 ${middle.server.url}:18848 415a1c87-33aa-47bd-8e25-13cc456c87ed - ${middle.server.url}:8080