diff --git a/package.xml b/package.xml
new file mode 100644
index 0000000..6e51ca4
--- /dev/null
+++ b/package.xml
@@ -0,0 +1,67 @@
+
+ bin
+
+
+ tar.gz
+
+
+
+
+
+
+ false
+ lib
+ false
+
+
+
+
+
+
+ ${project.basedir}
+
+
+ README*
+ LICENSE*
+ NOTICE*
+
+
+
+
+
+ ${project.basedir}/src/main/resources/
+ config
+
+
+
+
+ ${project.basedir}/src/main/script/
+ script
+
+ *.bat
+ *.sh
+
+
+
+
+ ${project.basedir}/excel
+ excel
+
+ *.*
+
+
+
+
+ ${project.build.directory}
+
+
+ *.jar
+
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..2b66978
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,210 @@
+
+
+ 4.0.0
+
+ com.goats
+ changhaoshiye
+ 1.0-SNAPSHOT
+ 昌昊加工工时采集/分析
+
+
+ 11
+ 11
+ UTF-8
+
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.7.0
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ 2.7.0
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+ com.alibaba
+ druid-spring-boot-starter
+ 1.2.11
+
+
+
+
+ mysql
+ mysql-connector-java
+ 8.0.30
+
+
+
+
+
+ org.mybatis.spring.boot
+ mybatis-spring-boot-starter
+ 1.3.2
+
+
+
+
+ org.dromara.mica-mqtt
+ mica-mqtt-client-spring-boot-starter
+ 2.4.1
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ 5.7.1
+ compile
+
+
+
+
+
+
+
+
+
+
+ net.sf.json-lib
+ json-lib
+ 0.9
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+
+
+ false
+
+
+
+ true
+
+ lib/
+
+ com.goats.WebApplication
+
+
+ config
+
+
+
+
+ *.properties
+ *.yaml
+ *.config
+ config/**
+
+
+
+
+ maven-resources-plugin
+ 3.1.0
+
+
+ copy-resources
+ package
+
+ copy-resources
+
+
+ ${project.build.directory}/maven-archiver/resources
+
+
+ ${basedir}/src/main/resources
+ *.properties
+ *.config
+ true
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+ ${project.basedir}/package.xml
+
+ ${project.artifactId}
+ false
+
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ true
+
+
+
+ org.mybatis.generator
+ mybatis-generator-maven-plugin
+ 1.3.2
+
+ true
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 11
+ 11
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/com/goats/WebApplication.java b/src/main/java/com/goats/WebApplication.java
new file mode 100644
index 0000000..32bb462
--- /dev/null
+++ b/src/main/java/com/goats/WebApplication.java
@@ -0,0 +1,18 @@
+package com.goats;
+
+import lombok.extern.slf4j.Slf4j;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+@SpringBootApplication
+@Slf4j
+@MapperScan("com.goats.mapper")
+@EnableScheduling //开启定时任务
+public class WebApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(WebApplication.class, args);
+ log.info("项目启动成功");
+ }
+}
diff --git a/src/main/java/com/goats/mapper/MqttDataMapper.java b/src/main/java/com/goats/mapper/MqttDataMapper.java
new file mode 100644
index 0000000..4042c26
--- /dev/null
+++ b/src/main/java/com/goats/mapper/MqttDataMapper.java
@@ -0,0 +1,10 @@
+package com.goats.mapper;
+
+import com.goats.pojo.MqttData;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface MqttDataMapper {
+
+ void insertMqttData(MqttData mqttData);
+}
diff --git a/src/main/java/com/goats/mqtt/MqttClientConnectListener.java b/src/main/java/com/goats/mqtt/MqttClientConnectListener.java
new file mode 100644
index 0000000..0a10a0f
--- /dev/null
+++ b/src/main/java/com/goats/mqtt/MqttClientConnectListener.java
@@ -0,0 +1,40 @@
+package com.goats.mqtt;
+
+import org.dromara.mica.mqtt.core.client.MqttClientCreator;
+import org.dromara.mica.mqtt.spring.client.event.MqttConnectedEvent;
+import org.dromara.mica.mqtt.spring.client.event.MqttDisconnectEvent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.event.EventListener;
+import org.springframework.stereotype.Service;
+
+
+/**
+ * 示例:客户端连接状态监听
+ *
+ * @author L.cm
+ */
+@Service
+public class MqttClientConnectListener {
+ private static final Logger logger = LoggerFactory.getLogger(MqttClientConnectListener.class);
+
+ @Autowired
+ private MqttClientCreator mqttClientCreator;
+
+ @EventListener
+ public void onConnected(MqttConnectedEvent event) {
+ logger.info("MqttConnectedEvent:{}", event);
+ }
+
+ @EventListener
+ public void onDisconnect(MqttDisconnectEvent event) {
+ // 离线时更新重连时的密码,适用于类似阿里云 mqtt clientId 连接带时间戳的方式
+ logger.info("MqttDisconnectEvent:{}", event);
+ // 在断线时更新 clientId、username、password
+ mqttClientCreator.clientId("newClient" + System.currentTimeMillis())
+ .username("newUserName")
+ .password("newPassword");
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/goats/mqtt/MqttClientGlobalMessageListener.java b/src/main/java/com/goats/mqtt/MqttClientGlobalMessageListener.java
new file mode 100644
index 0000000..3d76c06
--- /dev/null
+++ b/src/main/java/com/goats/mqtt/MqttClientGlobalMessageListener.java
@@ -0,0 +1,203 @@
+package com.goats.mqtt;
+
+import com.goats.mapper.MqttDataMapper;
+import com.goats.pojo.DataPacket;
+import com.goats.pojo.MqttData;
+import net.sf.json.JSONObject;
+import org.dromara.mica.mqtt.codec.MqttPublishMessage;
+import org.dromara.mica.mqtt.core.client.IMqttClientGlobalMessageListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+import org.tio.core.ChannelContext;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Service
+public class MqttClientGlobalMessageListener implements IMqttClientGlobalMessageListener {
+
+ private static final Logger logger = LoggerFactory.getLogger(MqttClientGlobalMessageListener.class);
+
+ @Autowired
+ private MqttDataMapper mqttDataMapper;
+ // 超时时间,单位为毫秒
+ private static final long TIMEOUT = 20000; // 20秒
+
+ // 模拟接收到的数据包 Map,使用设备ID和数据时间戳组合来唯一标识
+ //key 设备主键 value 三个包信息
+ private static ConcurrentHashMap messageMap = new ConcurrentHashMap<>();
+
+ @Override
+ public void onMessage(ChannelContext context, String topic, MqttPublishMessage message, byte[] payload) {
+ // 在这里处理收到的消息
+ try {
+ String str = new String(payload, StandardCharsets.UTF_8);
+ JSONObject jsonObject = new JSONObject(str);
+ logger.info("收到的消息订阅是{}", topic);
+ String deviceKey = jsonObject.optString("DeviceID"); //设备Id
+ String deviceDate = jsonObject.optString("Date"); //网关时间
+ Integer seq = jsonObject.optInt("Seq"); //包编号
+ if (topic.equals("v1/devices")&& deviceKey.equals("1016")) {
+ /**
+ * 1、根据每条发送过来的消息,存储的Map集合中(选择合适的key 比如设备id 时间戳不同不能作为联合主键)
+ * 2、依次接收1、2、3存入,每次接收到消息判断是否全部接收到3条消息,如果已全部接收到,执行下面逻辑,插入数据库
+ * 3、如果包顺序是按照老狗网关配置顺序(不管按不按顺序),一旦出现重复的SEQ消息,那么直接丢弃上一个对象,重新存储map集合。
+ *
+ * 改造:
+ * 1、超时处理(只收到了 2 个包,第 3 个包丢失了、缓存中的数据会一直存在)
+ * 2、重复包的处理逻辑 是丢失上个整个对象,还是丢弃上次的包,还是丢弃本次的包。==== 丢弃本次发来的包
+ */
+ //从map缓存对象中判断是否存在包信息
+ DataPacket existingPacket = messageMap.get(deviceKey);
+ //如果不存在包信息,那么是第一条信息,创建包对象
+ if (existingPacket == null) {
+ //添加包编号与JSON数据
+ existingPacket = new DataPacket(deviceKey, System.currentTimeMillis(), seq, str);
+ } else {
+ // 如果存在包信息,检查是否重复
+ if (existingPacket.isSeqReceived(seq)) {
+ // 重复包,丢弃当前包
+ System.out.println("重复包,设备ID: " + deviceKey + ", 包编号: " + seq);
+ return;
+ } else {
+ // 添加新包
+ existingPacket.addPacket(seq, str);
+ }
+ }
+ //判断如何缓存中已存在三个包消息,那么进行业务逻辑处理
+ if (existingPacket.isComplete()) {
+ //合并三个JSON串
+ String mergeJsonStrings = existingPacket.getMergedData();
+ logger.info("设备ID是->{},合并后JSON串->{}", deviceKey, mergeJsonStrings);
+ String DeviceID = jsonObject.optString("DeviceID"); //设备Id
+ messageMap.remove(DeviceID); // 数据保存后,移除此数据包记录
+ logger.info("-------------------执行业务逻辑在下面-------------------------");
+ jsonObject = new JSONObject(mergeJsonStrings);
+ //数据添加到数据库
+ MqttData mqttData = new MqttData();
+ mqttData.setRid(String.valueOf(System.currentTimeMillis()));
+ mqttData.setId(jsonObject.optString("DeviceID"));
+ mqttData.setA001(jsonObject.optDouble("A001"));
+ mqttData.setA002(jsonObject.optDouble("A002"));
+ mqttData.setA003(jsonObject.optDouble("A003"));
+ mqttData.setA004(jsonObject.optInt("A004"));
+ mqttData.setA005(jsonObject.optInt("A005"));
+ mqttData.setA006(jsonObject.optInt("A006"));
+ mqttData.setA007(jsonObject.optInt("A007"));
+ mqttData.setA008(jsonObject.optInt("A008"));
+ mqttData.setA009(jsonObject.optInt("A009"));
+ mqttData.setA010(jsonObject.optInt("A010"));
+ mqttData.setA011(jsonObject.optInt("A011"));
+ mqttData.setA012(jsonObject.optInt("A012"));
+ mqttData.setA013(jsonObject.optInt("A013"));
+ mqttData.setA014(jsonObject.optInt("A014"));
+ mqttData.setA015(jsonObject.optInt("A015"));
+ mqttData.setA016(jsonObject.optInt("A016"));
+ mqttData.setA017(jsonObject.optInt("A017"));
+ mqttData.setA018(jsonObject.optDouble("A018"));
+ mqttData.setA019(jsonObject.optDouble("A019"));
+ mqttData.setA020(jsonObject.optDouble("A020"));
+ mqttData.setA021(jsonObject.optInt("A021"));
+ mqttData.setA022(jsonObject.optInt("A022"));
+ mqttData.setA023(jsonObject.optInt("A023"));
+ mqttData.setA024(jsonObject.optInt("A024"));
+ mqttData.setA025(jsonObject.optInt("A025"));
+ mqttData.setA026(jsonObject.optInt("A026"));
+ mqttData.setA027(jsonObject.optInt("A027"));
+ mqttData.setA028(jsonObject.optDouble("A028"));
+ mqttData.setA029(jsonObject.optDouble("A029"));
+ mqttData.setA030(jsonObject.optDouble("A030"));
+ mqttData.setA031(jsonObject.optInt("A031"));
+ mqttData.setA032(jsonObject.optInt("A032"));
+ mqttData.setA033(jsonObject.optInt("A033"));
+ mqttData.setA034(jsonObject.optInt("A034"));
+ mqttData.setA035(jsonObject.optInt("A035"));
+ mqttData.setA036(jsonObject.optInt("A036"));
+ mqttData.setA037(jsonObject.optInt("A037"));
+ mqttData.setA038(jsonObject.optInt("A038"));
+ mqttData.setA039(jsonObject.optString("A039"));
+ mqttData.setA040(jsonObject.optInt("A040"));
+ mqttData.setA041(jsonObject.optInt("A041"));
+ mqttData.setA042(jsonObject.optInt("A042"));
+ mqttData.setA043(jsonObject.optInt("A043"));
+ mqttData.setA044(jsonObject.optInt("A044"));
+ mqttData.setA045(jsonObject.optInt("A045"));
+ mqttData.setA046(jsonObject.optInt("A046"));
+ mqttData.setA047(jsonObject.optInt("A047"));
+ mqttData.setA048(jsonObject.optDouble("A048"));
+ mqttData.setA049(jsonObject.optDouble("A049"));
+ mqttData.setA050(jsonObject.optDouble("A050"));
+ mqttData.setA051(jsonObject.optDouble("A051"));
+ mqttData.setA052(jsonObject.optDouble("A052"));
+ mqttData.setA053(jsonObject.optDouble("A053"));
+ mqttData.setA054(jsonObject.optDouble("A054"));
+ mqttData.setA055(jsonObject.optDouble("A055"));
+ mqttData.setA056(jsonObject.optDouble("A056"));
+ mqttData.setA057(jsonObject.optDouble("A057"));
+ mqttData.setA058(jsonObject.optDouble("A058"));
+ mqttData.setA059(jsonObject.optDouble("A059"));
+ mqttData.setA060(jsonObject.optDouble("A060"));
+ mqttData.setA061(jsonObject.optDouble("A061"));
+ mqttData.setA062(jsonObject.optDouble("A062"));
+ mqttData.setA063(jsonObject.optDouble("A063"));
+ mqttData.setA064(jsonObject.optDouble("A064"));
+ mqttData.setA065(jsonObject.optDouble("A065"));
+ mqttData.setA066(jsonObject.optDouble("A066"));
+ mqttData.setA067(jsonObject.optDouble("A067"));
+ mqttData.setA068(jsonObject.optDouble("A068"));
+ mqttData.setA069(jsonObject.optDouble("A069"));
+ mqttData.setA070(jsonObject.optDouble("A070"));
+ mqttData.setA071(jsonObject.optDouble("A071"));
+ mqttData.setA072(jsonObject.optDouble("A072"));
+ mqttData.setA073(jsonObject.optDouble("A073"));
+ mqttData.setA074(jsonObject.optDouble("A074"));
+ mqttData.setA075(jsonObject.optDouble("A075"));
+ mqttData.setA076(jsonObject.optDouble("A076"));
+ mqttData.setA077(jsonObject.optDouble("A077"));
+ mqttData.setA078(jsonObject.optDouble("A078"));
+ mqttData.setA079(jsonObject.optDouble("A079"));
+ mqttData.setA080(jsonObject.optDouble("A080"));
+ mqttData.setA081(jsonObject.optInt("A081"));
+ mqttData.setA082(jsonObject.optInt("A082"));
+ mqttData.setA083(jsonObject.optInt("A083"));
+ mqttData.setA084(jsonObject.optInt("A084"));
+ mqttData.setA085(jsonObject.optInt("A085"));
+ mqttDataMapper.insertMqttData(mqttData);
+ } else {
+ logger.info("设备ID是->{},不满足三个包信息,进行PUT操作,保持对象信息是{}", deviceKey, existingPacket);
+ messageMap.put(deviceKey, existingPacket);
+ System.out.println("messageMap = " + messageMap);
+ return;
+ }
+
+ }else {
+ logger.info("次信息是1015设备,不做处理,{}",jsonObject);
+ }
+ } catch (Exception e) {
+ e.getMessage();
+ }
+ }
+
+
+ @Scheduled(fixedRate = 5000)
+ public void checkTimeout() {
+ long currentTime = System.currentTimeMillis();
+ logger.info("定时任务正在执行,{}",messageMap);
+ for (Iterator> it = messageMap.entrySet().iterator(); it.hasNext(); ) {
+ Map.Entry entry = it.next();
+ DataPacket packet = entry.getValue();
+ long timeDiff = currentTime - packet.getLastUpdateTime();
+ logger.info("定时任务正在执行,设备ID: {}, 时间差: {} 毫秒", packet.getDeviceId(), timeDiff);
+ if (timeDiff > TIMEOUT) {
+ it.remove();
+ logger.info("定时任务正在执行,超时移除,设备ID: {}", packet.getDeviceId());
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/com/goats/mqtt/MqttClientSubscribeListener.java b/src/main/java/com/goats/mqtt/MqttClientSubscribeListener.java
new file mode 100644
index 0000000..b528dbd
--- /dev/null
+++ b/src/main/java/com/goats/mqtt/MqttClientSubscribeListener.java
@@ -0,0 +1,32 @@
+package com.goats.mqtt;
+
+import org.dromara.mica.mqtt.codec.MqttQoS;
+import org.dromara.mica.mqtt.spring.client.MqttClientSubscribe;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import java.nio.charset.StandardCharsets;
+
+@Service
+public class MqttClientSubscribeListener {
+ private static final Logger logger = LoggerFactory.getLogger(MqttClientSubscribeListener.class);
+
+ @MqttClientSubscribe("v1/devices")
+ public void subQos0(String topic, byte[] payload) {
+ logger.info("topic:{} payload:{}", topic, new String(payload, StandardCharsets.UTF_8));
+ }
+
+ /*@MqttClientSubscribe(value = "/qos1/#", qos = MqttQoS.QOS1)
+ public void subQos1(String topic, byte[] payload) {
+ logger.info("topic:{} payload:{}", topic, new String(payload, StandardCharsets.UTF_8));
+ }
+
+ @MqttClientSubscribe("/sys/${productKey}/${deviceName}/thing/sub/register")
+ public void thingSubRegister(String topic, byte[] payload) {
+ // 1.3.8 开始支持,@MqttClientSubscribe 注解支持 ${} 变量替换,会默认替换成 +
+ // 注意:mica-mqtt 会先从 Spring boot 配置中替换参数 ${},如果存在配置会优先被替换。
+ logger.info("topic:{} payload:{}", topic, new String(payload, StandardCharsets.UTF_8));
+ }
+*/
+}
\ No newline at end of file
diff --git a/src/main/java/com/goats/pojo/DataPacket.java b/src/main/java/com/goats/pojo/DataPacket.java
new file mode 100644
index 0000000..e901b81
--- /dev/null
+++ b/src/main/java/com/goats/pojo/DataPacket.java
@@ -0,0 +1,111 @@
+package com.goats.pojo;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+/**
+ * 缓存数据结构,用于存储每个设备的数据包
+ */
+public class DataPacket {
+ private String deviceId; // 设备ID
+ private String[] dataParts = new String[3]; // 用来存储三部分数据
+ private boolean[] receivedSeqs = new boolean[3]; // 记录哪些包已经收到
+ private long lastUpdateTime; // 最后一次更新时间
+
+ public DataPacket(String deviceId, Long timestamp, int seq, String data) {
+ this.deviceId = deviceId;
+ addPacket(seq, data);
+ this.lastUpdateTime = System.currentTimeMillis();
+ }
+
+ // 添加数据包
+ public void addPacket(int seq, String data) {
+ if (seq >= 1 && seq <= 3) {
+ this.dataParts[seq - 1] = data;
+ this.receivedSeqs[seq - 1] = true;
+ this.lastUpdateTime = System.currentTimeMillis();
+ }
+ }
+
+ // 判断是否已经接收到所有数据包
+ public boolean isComplete() {
+ for (boolean received : receivedSeqs) {
+ if (!received) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // 判断是否已经接收到某个包
+ public boolean isSeqReceived(int seq) {
+ return seq >= 1 && seq <= 3 && receivedSeqs[seq - 1];
+ }
+
+ /**
+ * 获取合并后的数据
+ * @return 合并后的 JSON 字符串
+ * @throws IOException 如果 JSON 解析失败
+ */
+ public String getMergedData() throws IOException {
+ // 调用 mergeJsonStrings 方法合并 JSON 数据
+ return mergeJsonStrings(dataParts[0], dataParts[1], dataParts[2]);
+ }
+
+ // 获取最后一次更新时间
+ public long getLastUpdateTime() {
+ return lastUpdateTime;
+ }
+
+ /**
+ * 合并多个 JSON 字符串
+ * @param jsonStrings 多个 JSON 字符串
+ * @return 合并后的 JSON 字符串
+ * @throws IOException 如果 JSON 解析失败
+ */
+ public static String mergeJsonStrings(String... jsonStrings) throws IOException {
+ // 创建 ObjectMapper 实例
+ ObjectMapper mapper = new ObjectMapper();
+
+ // 创建一个空的 ObjectNode 来存放合并后的内容
+ ObjectNode mergedNode = mapper.createObjectNode();
+
+ // 遍历传入的所有 JSON 字符串
+ for (String jsonString : jsonStrings) {
+ // 解析当前 JSON 字符串为 JsonNode
+ JsonNode jsonNode = mapper.readTree(jsonString);
+
+ // 合并字段
+ jsonNode.fieldNames().forEachRemaining(fieldName -> {
+ // 忽略重复的字段 seq, Date 和 DeviceID,只保留一个
+ if (!fieldName.equals("seq") && !fieldName.equals("Date")) {
+ mergedNode.set(fieldName, jsonNode.get(fieldName));
+ }
+ });
+ }
+
+ // 返回合并后的 JSON 字符串
+ return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(mergedNode);
+ }
+
+ public String getDeviceId() {
+ return deviceId;
+ }
+
+ public void setDeviceId(String deviceId) {
+ this.deviceId = deviceId;
+ }
+
+ @Override
+ public String toString() {
+ return "DataPacket{" +
+ "deviceId='" + deviceId + '\'' +
+ ", dataParts=" + Arrays.toString(dataParts) +
+ ", receivedSeqs=" + Arrays.toString(receivedSeqs) +
+ '}';
+ }
+}
diff --git a/src/main/java/com/goats/pojo/MqttData.java b/src/main/java/com/goats/pojo/MqttData.java
new file mode 100644
index 0000000..beae74d
--- /dev/null
+++ b/src/main/java/com/goats/pojo/MqttData.java
@@ -0,0 +1,102 @@
+package com.goats.pojo;
+
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * mqtt原始数据
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class MqttData {
+ private String Rid;
+ private String id;
+ private Double A001;
+ private Double A002;
+ private Double A003;
+ private Integer A004;
+ private Integer A005;
+ private Integer A006;
+ private Integer A007;
+ private Integer A008;
+ private Integer A009;
+ private Integer A010;
+ private Integer A011;
+ private Integer A012;
+ private Integer A013;
+ private Integer A014;
+ private Integer A015;
+ private Integer A016;
+ private Integer A017;
+ private Double A018;
+ private Double A019;
+ private Double A020;
+ private Integer A021;
+ private Integer A022;
+ private Integer A023;
+ private Integer A024;
+ private Integer A025;
+ private Integer A026;
+ private Integer A027;
+ private Double A028;
+ private Double A029;
+ private Double A030;
+ private Integer A031;
+ private Integer A032;
+ private Integer A033;
+ private Integer A034;
+ private Integer A035;
+ private Integer A036;
+ private Integer A037;
+ private Integer A038;
+ private String A039;
+ private Integer A040;
+ private Integer A041;
+ private Integer A042;
+ private Integer A043;
+ private Integer A044;
+ private Integer A045;
+ private Integer A046;
+ private Integer A047;
+ private Double A048;
+ private Double A049;
+ private Double A050;
+ private Double A051;
+ private Double A052;
+ private Double A053;
+ private Double A054;
+ private Double A055;
+ private Double A056;
+ private Double A057;
+ private Double A058;
+ private Double A059;
+ private Double A060;
+ private Double A061;
+ private Double A062;
+ private Double A063;
+ private Double A064;
+ private Double A065;
+ private Double A066;
+ private Double A067;
+ private Double A068;
+ private Double A069;
+ private Double A070;
+ private Double A071;
+ private Double A072;
+ private Double A073;
+ private Double A074;
+ private Double A075;
+ private Double A076;
+ private Double A077;
+ private Double A078;
+ private Double A079;
+ private Double A080;
+ private Integer A081;
+ private Integer A082;
+ private Integer A083;
+ private Integer A084;
+ private Integer A085;
+}
diff --git a/src/main/java/com/goats/utils/MergeJson.java b/src/main/java/com/goats/utils/MergeJson.java
new file mode 100644
index 0000000..1c76f7f
--- /dev/null
+++ b/src/main/java/com/goats/utils/MergeJson.java
@@ -0,0 +1,53 @@
+package com.goats.utils;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import java.io.IOException;
+
+/**
+ * 合并json串方法
+ */
+public class MergeJson {
+
+
+ public static void main(String[] args) {
+ // 示例多个JSON字符串
+ String jsonString1 = "{ \"DeviceID\": \"1016\", \"seq\": \"12345\", \"Date\": \"2025-02-11\", \"A049\": 12121122.1, \"A050\": 12121122.11 }";
+ String jsonString2 = "{ \"DeviceID\": \"1016\", \"seq\": \"67890\", \"Date\": \"2025-02-12\", \"A051\": 12121122.11, \"A052\": 12121122.11 }";
+ String jsonString3 = "{ \"DeviceID\": \"1016\", \"seq\": \"98765\", \"Date\": \"2025-02-13\", \"A053\": 12121122.22, \"A054\": 12121122.33 }";
+
+ }
+
+ /**
+ * 合并多个json串
+ * @param jsonStrings
+ * @return
+ * @throws IOException
+ */
+ public static String mergeJsonStrings2(String... jsonStrings) throws IOException {
+ // 创建 ObjectMapper 实例
+ ObjectMapper mapper = new ObjectMapper();
+
+ // 创建一个空的 ObjectNode 来存放合并后的内容
+ ObjectNode mergedNode = mapper.createObjectNode();
+
+ // 遍历传入的所有 JSON 字符串
+ for (String jsonString : jsonStrings) {
+ // 解析当前 JSON 字符串为 JsonNode
+ JsonNode jsonNode = mapper.readTree(jsonString);
+
+ // 合并字段
+ jsonNode.fieldNames().forEachRemaining(fieldName -> {
+ // 忽略重复的字段 seq, Date 和 DeviceID,只保留一个
+ if (!fieldName.equals("seq") && !fieldName.equals("Date")) {
+ mergedNode.set(fieldName, jsonNode.get(fieldName));
+ }
+ });
+ }
+
+ // 返回合并后的 JSON 字符串
+ return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(mergedNode);
+ }
+}
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
new file mode 100644
index 0000000..876c8ca
--- /dev/null
+++ b/src/main/resources/application-dev.yml
@@ -0,0 +1,23 @@
+################ 开发环境配置 ################
+# 端口
+server:
+ port: 9020
+
+spring:
+ # 数据源配置
+ datasource:
+ type: com.alibaba.druid.pool.DruidDataSource
+ url: jdbc:mysql://8.130.165.100:33060/goats_ch?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
+ username: root
+ password: goats@2023mysql
+ # 是否打开sql监控台 (生产环境请务必关闭此选项)
+ druid:
+ stat-view-servlet:
+ enabled: true
+ web-stat-filter:
+ enabled: true
+ filter:
+ config:
+ enabled: true
+
+
diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml
new file mode 100644
index 0000000..397767d
--- /dev/null
+++ b/src/main/resources/application-prod.yml
@@ -0,0 +1,21 @@
+################ 生产环境配置 ################
+# 端口
+server:
+ port: 9020
+
+spring:
+ # 数据源配置
+ datasource:
+ type: com.alibaba.druid.pool.DruidDataSource
+ url: jdbc:mysql://192.168.15.20:3306/goats_chupdate?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+ username: root
+ password: 123456
+ # 是否打开sql监控台 (生产环境请务必关闭此选项)
+ druid:
+ stat-view-servlet:
+ enabled: false
+ web-stat-filter:
+ enabled: false
+ filter:
+ config:
+ enabled: false
\ No newline at end of file
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
new file mode 100644
index 0000000..9a1b65f
--- /dev/null
+++ b/src/main/resources/application.yml
@@ -0,0 +1,42 @@
+spring:
+ # 应用名称
+ application.name: changhaoshiye
+ profiles:
+ # 启动环境加载,同时加载两个的写法:common,prod 优先级左>右
+# active: dev
+# active: test
+ active: prod
+mqtt:
+ client:
+ enabled: true # 是否开启客户端,默认:true
+# ip: 127.0.0.1 # 连接的服务端 ip ,默认:127.0.0.1
+ ip: 192.168.15.20 # 连接的服务端 ip ,默认:127.0.0.1
+ port: 1883 # 端口:默认:1883
+ name: Java-Mqtt-Client # 名称,默认:Mica-Mqtt-Client
+ client-id: 000001 # 客户端Id(非常重要,一般为设备 sn,不可重复)
+ user-name: admin # 认证的用户名
+ password: public # 认证的密码
+ global-subscribe: # 全局订阅的 topic,可被全局监听到,保留 session 停机重启,依然可以接受到消息。(2.2.9开始支持)
+ timeout: 5 # 超时时间,单位:秒,默认:5秒
+ reconnect: true # 是否重连,默认:true
+ re-interval: 5000 # 重连时间,默认 5000 毫秒
+ version: mqtt_3_1_1 # mqtt 协议版本,可选 MQTT_3_1、mqtt_3_1_1、mqtt_5,默认:mqtt_3_1_1
+ read-buffer-size: 8KB # 接收数据的 buffer size,默认:8k
+ max-bytes-in-message: 10MB # 消息解析最大 bytes 长度,默认:10M
+ keep-alive-secs: 60 # keep-alive 时间,单位:秒
+ clean-session: true # mqtt clean session,默认:true
+ ssl:
+ enabled: false # 是否开启 ssl 认证,2.1.0 开始支持双向认证
+ keystore-path: # 可选参数:ssl 双向认证 keystore 目录,支持 classpath:/ 路径。
+ keystore-pass: # 可选参数:ssl 双向认证 keystore 密码
+ truststore-path: # 可选参数:ssl 双向认证 truststore 目录,支持 classpath:/ 路径。
+ truststore-pass: # 可选参数:ssl 双向认证 truststore 密码
+
+# mybatis配置
+mybatis:
+ #标注待解析的mapper的xml文件位置
+ mapper-locations: classpath*:mapper/*.xml
+ #标注实体类位置
+ type-aliases-package: com.goats.pojo
+ configuration:
+ log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
new file mode 100644
index 0000000..446a61a
--- /dev/null
+++ b/src/main/resources/logback.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+ %-25d{yyyy-MM-dd HH:mm:ss.SSS} %green(%-5level) %boldBlue(-->) %msg%n
+
+
+
+
+
+
+
+
+
+ ${LOG_HOME}/%d{yyyy_MM}/%d{yyyy_MM_dd}/%d{yyyy_MM_dd_HH}(%i).log
+
+ 1MB
+
+ 3650
+
+
+ %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] --> : %msg%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/mapper/MqttDataMapper.xml b/src/main/resources/mapper/MqttDataMapper.xml
new file mode 100644
index 0000000..5195785
--- /dev/null
+++ b/src/main/resources/mapper/MqttDataMapper.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+ insert into s_gateway(Rid, id, A001, A002, A003, A004, A005, A006, A007, A008, A009, A010,
+ A011, A012, A013, A014, A015, A016, A017, A018, A019, A020, A021, A022,
+ A023, A024, A025, A026, A027, A028, A029, A030, A031, A032, A033, A034,
+ A035, A036, A037, A038, A039, A040, A041, A042, A043, A044, A045, A046,
+ A047, A048, A049, A050, A051, A052, A053, A054, A055, A056, A057, A058,
+ A059, A060, A061, A062, A063, A064, A065, A066, A067, A068, A069, A070,
+ A071, A072, A073, A074, A075, A076, A077, A078, A079, A080, A081, A082,
+ A083, A084, A085)
+ values (#{Rid}, #{id}, #{A001}, #{A002}, #{A003}, #{A004}, #{A005}, #{A006}, #{A007},
+ #{A008}, #{A009}, #{A010}, #{A011}, #{A012}, #{A013}, #{A014}, #{A015}, #{A016},
+ #{A017}, #{A018}, #{A019}, #{A020}, #{A021}, #{A022}, #{A023}, #{A024}, #{A025},
+ #{A026}, #{A027}, #{A028}, #{A029}, #{A030}, #{A031}, #{A032}, #{A033}, #{A034},
+ #{A035}, #{A036}, #{A037}, #{A038}, #{A039}, #{A040}, #{A041}, #{A042}, #{A043},
+ #{A044}, #{A045}, #{A046}, #{A047}, #{A048}, #{A049}, #{A050}, #{A051}, #{A052},
+ #{A053}, #{A054}, #{A055}, #{A056}, #{A057}, #{A058}, #{A059}, #{A060}, #{A061},
+ #{A062}, #{A063}, #{A064}, #{A065}, #{A066}, #{A067}, #{A068}, #{A069}, #{A070},
+ #{A071}, #{A072}, #{A073}, #{A074}, #{A075}, #{A076}, #{A077}, #{A078}, #{A079},
+ #{A080}, #{A081}, #{A082}, #{A083}, #{A084}, #{A085});
+
+
+
\ No newline at end of file
diff --git a/src/test/java/com/goats/ProcessTimeCalculator.java b/src/test/java/com/goats/ProcessTimeCalculator.java
new file mode 100644
index 0000000..a3df2d2
--- /dev/null
+++ b/src/test/java/com/goats/ProcessTimeCalculator.java
@@ -0,0 +1,88 @@
+package com.goats;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ProcessTimeCalculator {
+ public static void main(String[] args) {
+ String filePath = "D:\\GOATS\\昌昊项目资料\\sql\\s_data_202502271336.txt";
+ List records = new ArrayList<>();
+ List results = new ArrayList<>();
+
+ // 读取数据
+ try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
+ String line;
+ boolean isHeader = true;
+ while ((line = br.readLine()) != null) {
+ if (isHeader) { // 跳过表头
+ isHeader = false;
+ continue;
+ }
+ String[] fields = line.split("\\|");
+ if (fields.length < 8) continue;
+
+ ProductionRecord record = new ProductionRecord();
+ record.rid = Long.parseLong(fields[1].trim());
+ record.id = Integer.parseInt(fields[2].trim());
+ record.a002CycleTime = Double.parseDouble(fields[4].trim().replace(",", "")); //循环时间
+ record.a003RunningTime = Double.parseDouble(fields[5].trim().replace(",", ""));//运行时间
+ record.a005Quantity = Integer.parseInt(fields[7].trim().replace(",", ""));//加工数量
+
+ records.add(record);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ // 计算加工工时
+ int previousQuantity = 0;
+ double previousRunningTime = 0;
+ for (ProductionRecord record : records) {
+ if (record.a005Quantity > previousQuantity) {
+ if (previousQuantity != 0) { // 忽略初始值
+ double cycleTime = record.a003RunningTime - previousRunningTime;
+ results.add(new ProcessTimeResult(
+ record.a005Quantity,
+ cycleTime,
+ record.a002CycleTime
+ ));
+ }
+ previousQuantity = record.a005Quantity;
+ previousRunningTime = record.a003RunningTime;
+ }
+ }
+
+ // 打印结果
+ System.out.println("零件编号 | 加工工时 (分钟) | A002循环时间 (秒)");
+ for (ProcessTimeResult result : results) {
+ System.out.printf("%-8d | %-12.2f | %-15.2f%n",
+ result.partNumber,
+ result.processTime,
+ result.a002CycleTime
+ );
+ }
+ }
+
+ static class ProductionRecord {
+ long rid;
+ int id;
+ double a002CycleTime;
+ double a003RunningTime;
+ int a005Quantity;
+ }
+
+ static class ProcessTimeResult {
+ int partNumber;
+ double processTime;
+ double a002CycleTime;
+
+ ProcessTimeResult(int partNumber, double processTime, double a002CycleTime) {
+ this.partNumber = partNumber;
+ this.processTime = processTime;
+ this.a002CycleTime = a002CycleTime;
+ }
+ }
+}