diff --git a/app.db b/app.db
index 03bb6b7..fe9d44e 100644
Binary files a/app.db and b/app.db differ
diff --git a/pom.xml b/pom.xml
index ad29e25..cb6b004 100644
--- a/pom.xml
+++ b/pom.xml
@@ -42,6 +42,11 @@
Java-WebSocket
1.5.4
+
+ org.eclipse.paho
+ org.eclipse.paho.client.mqttv3
+ 1.2.5
+
diff --git a/src/main/java/com/iflytop/digester/DigestionTaskTheadManager.java b/src/main/java/com/iflytop/digester/DigestionTaskTheadManager.java
index c8f2b84..5b72641 100644
--- a/src/main/java/com/iflytop/digester/DigestionTaskTheadManager.java
+++ b/src/main/java/com/iflytop/digester/DigestionTaskTheadManager.java
@@ -1,16 +1,40 @@
package com.iflytop.digester;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.iflytop.digester.deviceinstance.HeatingTurntableSlotTube;
import com.iflytop.digester.model.MdbDigestionTask;
import jakarta.annotation.PostConstruct;
+import org.eclipse.paho.client.mqttv3.*;
+import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
-
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+
@Component
public class DigestionTaskTheadManager {
// Singleton instance
private static DigestionTaskTheadManager instance = null;
// Task list
private List tasks;
+ // Mqtt client
+ private MqttClient client;
+
+ @Value("${mqtt-broker.uri}")
+ private String mqttBrokerUri;
+ @Value("${mqtt-broker.username}")
+ private String mqttBrokerUsername;
+ @Value("${mqtt-broker.password}")
+ private String mqttBrokerPassword;
+ @Value("${mqtt-broker.clientId}")
+ private String mqttBrokerClientId;
+ @Value("${mqtt-broker.my-topic}")
+ private String mqttBrokerMyTopic;
+ @Value("${mqtt-broker.transbot-topic}")
+ private String mqttBrokerTransbotTopic;
// Get instance
public static DigestionTaskTheadManager getInstance() {
@@ -21,12 +45,101 @@ public class DigestionTaskTheadManager {
public void init() {
this.tasks = new ArrayList<>();
instance = this;
+ this.setupMqttBroker();
+ }
+
+ // setup mqtt broker
+ private void setupMqttBroker() {
+ this.client = null;
+ try {
+ this.client = new MqttClient(this.mqttBrokerUri, this.mqttBrokerClientId, new MemoryPersistence());
+ } catch (MqttException e) {
+ throw new RuntimeException(e);
+ }
+ this.client.setCallback(new MqttCallback() {
+ public void connectionLost(Throwable cause) {
+ System.out.println("connectionLost: " + cause.getMessage());
+ }
+ public void messageArrived(String topic, MqttMessage message) {
+ DigestionTaskTheadManager.this.handleOnMessageArrived(topic, message);
+ }
+ public void deliveryComplete(IMqttDeliveryToken token) {
+ System.out.println("deliveryComplete---------" + token.isComplete());
+ }
+ });
+ try {
+ MqttConnectOptions options = new MqttConnectOptions();
+ options.setUserName(this.mqttBrokerUsername);
+ options.setPassword(this.mqttBrokerPassword.toCharArray());
+ client.connect(options);
+ } catch (MqttException e) {
+ throw new RuntimeException(e);
+ }
+
+ try {
+ client.subscribe(this.mqttBrokerMyTopic, 2);
+ } catch (MqttException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ // Handle on message arrived
+ private void handleOnMessageArrived(String topic, MqttMessage message) {
+ String content = new String(message.getPayload());
+ JsonNode actionJsonTree = null;
+ try {
+ ObjectMapper jsonMapper = new ObjectMapper();
+ actionJsonTree = jsonMapper.readTree(content);
+ } catch (JsonProcessingException e) {
+ // ignore invalid message
+ }
+
+ assert actionJsonTree != null;
+ String taskId = actionJsonTree.get("taskId").asText();
+ String actionName = actionJsonTree.get("action").asText();
+ Map actionParams = new HashMap<>();
+ if ( actionJsonTree.has("params") ) {
+ var paramsJsonTree = actionJsonTree.get("params");
+ for ( JsonNode paramNode : paramsJsonTree ) {
+ actionParams.put(paramNode.get("key").asText(), paramNode.get("value").asText());
+ }
+ }
+
+ DigestionTaskThread task = null;
+ for ( var t : this.tasks ) {
+ if ( t.getTaskId().equals(taskId) ) {
+ task = t;
+ break;
+ }
+ }
+ if ( null == task ) {
+ // ignore invalid task
+ return ;
+ }
+ task.executeAction(actionName, actionParams);
+ }
+
+ // Send message to trans bot
+ public void sendMessageToTransBot(String message) {
+ try {
+ MqttMessage mqttMessage = new MqttMessage(message.getBytes());
+ mqttMessage.setQos(2);
+ this.client.publish(this.mqttBrokerTransbotTopic, mqttMessage);
+ } catch (MqttException e) {
+ throw new RuntimeException(e);
+ }
}
// Start task
public void startTask( MdbDigestionTask taskModel ) {
var task = new DigestionTaskThread(taskModel);
- tasks.add(task);
+ task.setFinishCallback(this::handleOnTaskFinished);
+ this.tasks.add(task);
task.start();
}
+
+ // Handle task finished
+ private void handleOnTaskFinished( DigestionTaskThread task ) {
+ this.tasks.remove(task);
+ }
}
diff --git a/src/main/java/com/iflytop/digester/DigestionTaskThread.java b/src/main/java/com/iflytop/digester/DigestionTaskThread.java
index c0d8a3d..eec3710 100644
--- a/src/main/java/com/iflytop/digester/DigestionTaskThread.java
+++ b/src/main/java/com/iflytop/digester/DigestionTaskThread.java
@@ -8,7 +8,14 @@ import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
+
public class DigestionTaskThread extends Thread {
+ // 任务完成回调
+ public interface FinishCallback {
+ void callback( DigestionTaskThread task );
+ }
+
// logger
public static final Logger LOG = LoggerFactory.getLogger(DigestionTaskThread.class);
// 消解任务Model
@@ -19,6 +26,14 @@ public class DigestionTaskThread extends Thread {
private HeatingTurntableSlot heatingSlot;
// 异常试管索引列表
private List errorTubeIndexes;
+ // 任务完成回调
+ private FinishCallback finishCallback;
+ // 试管架放入等待锁
+ private final Object tubeRackPutInWaitLock = new Object();
+ // 试管架取出等待锁
+ private final Object tubeRackTakeOutWaitLock = new Object();
+ // 异常处理线程
+ private Thread errorProcessThread;
// 消解任务
public DigestionTaskThread(MdbDigestionTask taskModel) {
@@ -26,6 +41,29 @@ public class DigestionTaskThread extends Thread {
this.solution = UfActiveRecord.findOne(MdbDigestionSolution.class, taskModel.digestionId);
}
+ // 设置任务完成回调
+ public void setFinishCallback(FinishCallback finishCallback) {
+ this.finishCallback = finishCallback;
+ }
+
+ // 获取任务ID
+ public String getTaskId() {
+ return this.taskModel.taskId;
+ }
+
+ // 执行动作
+ public void executeAction(String name, Map params) {
+ if ( "TubeRackPutInDone".equals(name) ) {
+ synchronized ( this.tubeRackPutInWaitLock ) {
+ this.tubeRackPutInWaitLock.notifyAll();
+ }
+ } else if ( "TubeRackTakeOutDone".equals(name) ) {
+ synchronized ( this.tubeRackTakeOutWaitLock ) {
+ this.tubeRackTakeOutWaitLock.notifyAll();
+ }
+ }
+ }
+
@Override
public void run() {
try {
@@ -44,30 +82,27 @@ public class DigestionTaskThread extends Thread {
this.executeRound(lastRound);
// 检查试管
this.tubeCheck();
-
- // 异常处理执行
- var errorRound = this.solution.getErrorRounds();
- for ( int i=0; i {
+ try {
+ // 异常处理执行
+ var errorRound = this.solution.getErrorRounds();
+ for ( int i=0; i tubeExistsMap = new ArrayList<>();
+ public String heatingStatus = "off"; // off:未加热 on:加热中
+ // 试管列表
+ public List tubes = new ArrayList<>();
+
+ // Constructor
+ public HeatingTurntableSlot() {
+ for (int i = 0; i < 16; i++) {
+ tubes.add(null);
+ }
+ }
// 设置试管架编号
public void setTubeRackNo( String tubeRackNo ) {
this.tubeRackNo = tubeRackNo;
}
+ // 设置试管
+ public void setTubes( List tubes ) {
+ for (int i = 0; i < tubes.size(); i++) {
+ this.tubes.set(i, tubes.get(i));
+ }
+ }
+
// 获取非空试管索引列表
public List getExistTubeIndexes() {
List indexes = new ArrayList<>();
- for (int i = 0; i < tubeExistsMap.size(); i++) {
- if (tubeExistsMap.get(i)) {
+ for (int i = 0; i < tubes.size(); i++) {
+ if ( null != tubes.get(i) ) {
indexes.add(i);
}
}
return indexes;
}
+ // 执行加热
public void heating( Integer temperature, Integer duration ) {
- // 执行加热
+ this.destTemperature = temperature;
+ this.heatingDuration = duration;
+ this.heatingStatus = "on";
+
+ var snippetName = "HeatingTurntableSlotHeating.Start." + this.index;
+ Map snippetParams = Map.of("temperature", temperature, "duration", duration);
+ UfCmdSnippetExecutor.execute(snippetName, snippetParams);
+
+ // 加热完成
+ this.destTemperature = 0;
+ this.heatingDuration = 0;
+ this.heatingStatus = "off";
}
}
diff --git a/src/main/java/com/iflytop/digester/deviceinstance/HeatingTurntableSlotTube.java b/src/main/java/com/iflytop/digester/deviceinstance/HeatingTurntableSlotTube.java
new file mode 100644
index 0000000..55dad04
--- /dev/null
+++ b/src/main/java/com/iflytop/digester/deviceinstance/HeatingTurntableSlotTube.java
@@ -0,0 +1,7 @@
+package com.iflytop.digester.deviceinstance;
+public class HeatingTurntableSlotTube {
+ // tube index
+ public Integer index = -1;
+ // tube no
+ public String no = "";
+}
diff --git a/src/main/java/com/iflytop/digester/deviceinstance/LiquidAdditionInstance.java b/src/main/java/com/iflytop/digester/deviceinstance/LiquidAdditionInstance.java
index 81fb89d..cdfe016 100644
--- a/src/main/java/com/iflytop/digester/deviceinstance/LiquidAdditionInstance.java
+++ b/src/main/java/com/iflytop/digester/deviceinstance/LiquidAdditionInstance.java
@@ -5,7 +5,6 @@ import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-
@Component
public class LiquidAdditionInstance {
// list of liquids
@@ -27,43 +26,58 @@ public class LiquidAdditionInstance {
// 针对试管加液
public void addLiquidToTubes(List tubes, String type, int volume ) {
- var outerIndexes = List.of(0,4, 3,2, 15,11, 12,13);
- var innerIndexes = List.of(1,5, 6,7, 14,10, 9,8);
var pumpIndexes = this.getPumpIndexForGroupOutAndIn(type);
var pumpGroupOutIndex = pumpIndexes.get(0);
var pumpGroupInIndex = pumpIndexes.get(1);
-
- for ( int i=0; i<4; i++ ) {
- // @TODO: 这里要计算坐标, 但是还没想好, 等一下 ~~~
-
+ for ( int batchIndex=0; batchIndex<4; batchIndex++ ) {
// 外圈加液
- UfCmdSnippetExecutor.execute("LiquidAdditionGroupOutPrepare");
- UfCmdSnippetExecutor.execute(
- "LiquidAdditionPump" + pumpGroupOutIndex,
- Map.of("volume", volume)
- );
- UfCmdSnippetExecutor.execute(
- "LiquidAdditionPump" + pumpGroupInIndex,
- Map.of("volume", volume)
- );
+ UfCmdSnippetExecutor.execute("LiquidAdditionPrepare.Out." + batchIndex);
+ if ( this.checkTubeExists("GOUT", batchIndex, "TOUT", tubes) ) { // 外圈,外侧试管加液
+ UfCmdSnippetExecutor.execute(
+ "LiquidAdditionPump" + pumpGroupOutIndex,
+ Map.of("volume", volume)
+ );
+ }
+ if ( this.checkTubeExists("GOUT", batchIndex, "TIN", tubes) ) { // 外圈,内侧试管加液
+ UfCmdSnippetExecutor.execute(
+ "LiquidAdditionPump" + pumpGroupInIndex,
+ Map.of("volume", volume)
+ );
+ }
// 内圈加液
- UfCmdSnippetExecutor.execute("LiquidAdditionGroupInPrepare");
- UfCmdSnippetExecutor.execute(
- "LiquidAdditionPump" + pumpGroupOutIndex,
- Map.of("volume", volume)
- );
- UfCmdSnippetExecutor.execute(
- "LiquidAdditionPump" + pumpGroupInIndex,
- Map.of("volume", volume)
- );
+ UfCmdSnippetExecutor.execute("LiquidAdditionPrepare.In." + batchIndex);
+ if ( this.checkTubeExists("GIN", batchIndex, "TOUT", tubes) ) { // 内圈,外侧试管加液
+ UfCmdSnippetExecutor.execute(
+ "LiquidAdditionPump" + pumpGroupOutIndex,
+ Map.of("volume", volume)
+ );
+ }
+ if ( this.checkTubeExists("GIN", batchIndex, "TIN", tubes) ) { // 内圈,内侧试管加液
+ UfCmdSnippetExecutor.execute(
+ "LiquidAdditionPump" + pumpGroupInIndex,
+ Map.of("volume", volume)
+ );
+ }
}
// 加液复位
UfCmdSnippetExecutor.execute("LiquidAdditionReset");
}
+ // 检查试管是否存在
+ private Boolean checkTubeExists(String groupType, Integer batchIndex, String slotType, List tubes) {
+ var slotIndexes = Map.of(
+ "GIN", List.of(1,5, 6,7, 14,10, 9,8),
+ "GOUT", List.of(0,4, 3,2, 15,11, 12,13)
+ );
+ var groupSlotIndexes = slotIndexes.get(groupType);
+ var batchSlotIndexes = groupSlotIndexes.subList(batchIndex*2, batchIndex*2+2);
+ var slotIndexesToCheck = batchSlotIndexes.get("TOUT".equals(slotType) ? 0 : 1);
+ return tubes.contains(slotIndexesToCheck);
+ }
+
// 获取蠕动泵索引
private List getPumpIndexForGroupOutAndIn( String type ) {
for ( int i=0; i<8; i++ ) {
@@ -73,16 +87,4 @@ public class LiquidAdditionInstance {
}
throw new RuntimeException("未找到对应的液体类型");
}
-
-
- //当前有几种酸
- //各种酸目前的状态,是否缺液
- //指挥加酸机械臂运动 ,实际上调用的是 片段
-
-
-
- //调用蠕动泵加酸,实际上调用的是 片段
-
-
-
}
diff --git a/src/main/java/com/iflytop/digester/deviceinstance/TransferRobotArmInstance.java b/src/main/java/com/iflytop/digester/deviceinstance/TransferRobotArmInstance.java
index b09d6ad..63e5bda 100644
--- a/src/main/java/com/iflytop/digester/deviceinstance/TransferRobotArmInstance.java
+++ b/src/main/java/com/iflytop/digester/deviceinstance/TransferRobotArmInstance.java
@@ -1,35 +1,86 @@
package com.iflytop.digester.deviceinstance;
+import com.iflytop.digester.underframework.UfCmdSnippetExecutor;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
public class TransferRobotArmInstance {
-
+ // 等待锁队列
private final List