diff --git a/app.db b/app.db
index ce753d5..6809d9b 100644
Binary files a/app.db and b/app.db differ
diff --git a/src/main/java/com/iflytop/digester/StartResetTaskThread.java b/src/main/java/com/iflytop/digester/StartResetTaskThread.java
index 5810e11..e27e21a 100644
--- a/src/main/java/com/iflytop/digester/StartResetTaskThread.java
+++ b/src/main/java/com/iflytop/digester/StartResetTaskThread.java
@@ -1,9 +1,24 @@
package com.iflytop.digester;
import com.iflytop.digester.deviceinstance.Device;
+import com.iflytop.digester.underframework.UfActuatorCmdExecutor;
+import com.iflytop.digester.underframework.UfApplication;
+import com.iflytop.digester.underframework.UfCmdSnippetExecutor;
+import com.iflytop.digester.underframework.dao.model.UfMdbNotification;
+import com.iflytop.digester.underframework.dao.model.UfMdbOption;
import com.iflytop.digester.underframework.dao.model.UfMdbRuntimeVariable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
public class StartResetTaskThread extends Thread {
+ // logger
+ public static final Logger LOG = LoggerFactory.getLogger(StartResetTaskThread.class);
// 是否需要放置异常处理试管架
private Boolean isErrorTubeRackRequired = true;
+ // pause lock
+ private final Object pauseLock = new Object();
@Override
public void run() {
@@ -11,57 +26,15 @@ public class StartResetTaskThread extends Thread {
device.setStatus("preparing");
this.setProgressMessage("设备初始化...");
+ UfCmdSnippetExecutor.execute("StartResetInitDevices");
+
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
-// this.getDevice().getIO().setValue("LightRed", 1);
-//
-// this.setProgressMessage("设备初始化 : 关闭设备门");
-// DiActMotor doorMotor = this.getActuator(MyDevice.ACT_DOOR_MOTOR, DiActMotor.class);
-// doorMotor.setEnable(true);
-// MoveDoorClose.execute(this.getDevice());
-//
-// this.setProgressMessage("设备初始化 : 加液臂复位");
-// DiActMotor liquidMotor = this.getActuator(MyDevice.ACT_LIQUID_MOTOR, DiActMotor.class);
-// liquidMotor.setEnable(true);
-// liquidMotor.reset();
-// liquidMotor.moveTo("LiquidArmStandby");
-//
-// this.setProgressMessage("设备初始化 : 关闭夹爪");
-// DiActServo transferClipServo = this.getActuator(MyDevice.ACT_TRANSFER_CLIP_SERVO, DiActServo.class);
-// transferClipServo.setEnable(true);
-// transferClipServo.moveTo("TransClipServoClose");
-//
-// this.setProgressMessage("设备初始化 : 搬运机械臂上下移动复位");
-// DiActMotor transUdMotor = this.getActuator(MyDevice.ACT_TRANSFER_UD_MOTOR, DiActMotor.class);
-// transUdMotor.setEnable(true);
-// transUdMotor.reset();
-// transUdMotor.moveTo("TransUdMotorStandby");
-//
-// this.setProgressMessage("设备初始化 : 搬运机械臂左右移动复位");
-// DiActMotor transLrMotor = this.getActuator(MyDevice.ACT_TRANSFER_LR_MOTOR, DiActMotor.class);
-// transLrMotor.setEnable(true);
-// transLrMotor.reset();
-// transLrMotor.setMoveOffset("TransLrMotorGlobalOffset");
-// transLrMotor.moveTo("TransLrMotorStandby");
-//
-// // @TODO : http://127.0.0.1:5566/issues/39
-// this.setProgressMessage("设备初始化 : 加液转盘复位");
-// DiActMotor liquidPlateMotor = this.getActuator(MyDevice.ACT_LIQUID_PLATE_MOTOR, DiActMotor.class);
-// liquidPlateMotor.setEnable(true);
-// liquidPlateMotor.reset();
-//
-// // @TODO : http://127.0.0.1:5566/issues/40
-// this.setProgressMessage("设备初始化 : 加热盘转盘复位");
-// DiActMotor heatPlateMotor = this.getActuator(MyDevice.ACT_HEAT_PLATE_MOTOR, DiActMotor.class);
-// heatPlateMotor.setEnable(true);
-// heatPlateMotor.reset();
-// heatPlateMotor.moveTo("HeatPlateStandby");
-//
-// this.setupHeatingSlotCover();
+ this.setupHeatingSlotCover();
// this.setupPeristalticPump();
//
// this.setProgressMessage("设备初始化 : 相机");
@@ -146,63 +119,60 @@ public class StartResetTaskThread extends Thread {
// MoveDoorClose.execute(this.getDevice());
// }
//
-// /**
-// * 清理试管架
-// * @issue : 无法获取加液位置是否存在试管架
-// */
-// private void setupHeatingSlotCover() {
-// boolean isDoorOpen = false;
-// Integer errorSlotIndex = this.getDevice().getEnv().getProperty("app.errorSlotIndex",Integer.class);
-//
-// for ( int i=0; i<5; i++ ) {
-// this.setProgressMessage("设备初始化 : 检查试管架 " + (i+1) + "/5");
-// String key = "HeatingPlateMotorSlotCover_" + i;
-// DiActServo heatSlotCoverServo = this.getActuator(key, DiActServo.class);
-// heatSlotCoverServo.setEnable(true);
-// var currentPos = heatSlotCoverServo.getCurrentPos();
-// LOG.info("HeatingPlateMotorSlotCover #{} : current pos = {}", i, currentPos);
-//
-// // 如果是空的,则初始化位置后继续下一步
-// var rackExistsPos = heatSlotCoverServo.getLocationValue("HeatingPlateMotorSlotCoverRackExists");
-// if ( currentPos > rackExistsPos ) {
-// heatSlotCoverServo.setCurrentPosAsMiddle();
-// continue;
-// }
-//
-// // 如果是空试管架, 又是异常区域则不处理, 因为异常区域始终要有个试管架
-// var emptyRackExistsPos = heatSlotCoverServo.getLocationValue("HeatingPlateMotorSlotCoverEmptyRackExists");
-// if ( currentPos > emptyRackExistsPos && null != errorSlotIndex && errorSlotIndex == i ) { // 异常区域存在空试管架
-// this.isErrorTubeRackRequired = false;
-// continue;
-// }
-//
-// var moveToLiquidPlate = new MoveMoveTubeRackFromHeatPlateToLiquidPlate();
-// moveToLiquidPlate.setDevice(this.getDevice());
-// moveToLiquidPlate.slotIndex = i;
-// moveToLiquidPlate.updateSlotStatus = false;
-// moveToLiquidPlate.run();
-//
-// if ( !isDoorOpen ) {
-// MoveDoorOpen.execute(this.getDevice());
-// isDoorOpen = true;
-// }
-//
-// heatSlotCoverServo.moveTo("HeatingPlateMotorSlotCoverEmpty");
-// heatSlotCoverServo.setEnable(false);
-//
-// // 等待取出试管架
-// DiMdbNotification.taskAction(this, "TaskStartResetErrorTubeRackTakeOut");
-// this.waitAction("TaskStartResetErrorTubeRackTakeOut");
-// LOG.info("HeatingPlateMotorSlotCover #{} : take out", i);
-//
-// heatSlotCoverServo.setEnable(true);
-// heatSlotCoverServo.setCurrentPosAsMiddle();
-// }
-//
-// if ( isDoorOpen ) {
-// MoveDoorClose.execute(this.getDevice());
-// }
-// }
+ /**
+ * 清理试管架
+ * @issue : 无法获取加液位置是否存在试管架
+ */
+ private void setupHeatingSlotCover() {
+ boolean isDoorOpen = false;
+ Integer errorSlotIndex = UfApplication.getApp().getEnv().getProperty("app.errorSlotIndex", Integer.class);
+
+ for ( int i=0; i<5; i++ ) {
+ this.setProgressMessage("设备初始化 : 检查试管架 " + (i+1) + "/5");
+ UfCmdSnippetExecutor.execute(String.format("HeatingSlotCoverEnable.%d",i));
+ var actuatorKey = String.format("HeatingPlateMotorSlotCover_%d", i);
+ var currentPosStr = UfActuatorCmdExecutor.execute(actuatorKey, "motor_read_pos");
+ var currentPos = Integer.parseInt(currentPosStr);
+ LOG.info("HeatingPlateMotorSlotCover #{} : current pos = {}", i, currentPos);
+
+ // 如果是空的,则初始化位置后继续下一步
+ var rackExistsPos = UfMdbOption.getInteger("HeatingPlateMotorSlotCoverRackExists", 0);
+ if ( currentPos > rackExistsPos ) {
+ UfActuatorCmdExecutor.execute(actuatorKey, "motor_easy_set_current_pos", "2047");
+ continue;
+ }
+
+ // 如果是空试管架, 又是异常区域则不处理, 因为异常区域始终要有个试管架
+ var emptyRackExistsPos = UfMdbOption.getInteger("HeatingPlateMotorSlotCoverEmptyRackExists", 0);
+ if ( currentPos > emptyRackExistsPos && null != errorSlotIndex && errorSlotIndex == i ) { // 异常区域存在空试管架
+ this.isErrorTubeRackRequired = false;
+ continue;
+ }
+
+ // 将试管架移至加液盘
+ UfCmdSnippetExecutor.execute(String.format("TubeRackMoveToLiquidPlate.%d", i));
+ // 打开柜门
+ if ( !isDoorOpen ) {
+ UfCmdSnippetExecutor.execute("DoorOpen");
+ isDoorOpen = true;
+ }
+ // 禁用该区位密封盖,用于后续重置状态
+ UfCmdSnippetExecutor.execute(String.format("HeatingSlotCoverDisable.%d", i));
+
+ // 等待取出试管架
+ UfMdbNotification.action("StartResetTubeRackTakeOut", new HashMap<>());
+ this.waitAction("TaskStartResetErrorTubeRackTakeOut");
+ LOG.info("HeatingPlateMotorSlotCover #{} : take out", i);
+
+ // 重新启用密封盖
+ UfCmdSnippetExecutor.execute(String.format("HeatingSlotCoverEnable.%d", i));
+ UfCmdSnippetExecutor.execute(String.format("HeatingSlotCoverServoSetCurPosAsMiddle.%d", i));
+ }
+
+ if ( isDoorOpen ) {
+ UfCmdSnippetExecutor.execute("DoorClose");
+ }
+ }
//
// // 异常处理位试管架取出
// public void actionErrorTubeRackTakeOut() {
@@ -241,6 +211,26 @@ public class StartResetTaskThread extends Thread {
// action.finish();
// }
+ // wait for continue
+ private void waitAction( String key ) {
+ LOG.info("Start reset wait for action : {}", key);
+ synchronized (pauseLock) {
+ try {
+ pauseLock.wait();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ // execute action
+ public void executeAction( String key ) {
+ LOG.info("Start reset execute action : {}", key);
+ synchronized (pauseLock) {
+ pauseLock.notifyAll();
+ }
+ }
+
// set progress message
private void setProgressMessage(String message) {
UfMdbRuntimeVariable.setString(message, "device.start.message");
diff --git a/src/main/java/com/iflytop/digester/controller/DeviceController.java b/src/main/java/com/iflytop/digester/controller/DeviceController.java
index cd66146..1497044 100644
--- a/src/main/java/com/iflytop/digester/controller/DeviceController.java
+++ b/src/main/java/com/iflytop/digester/controller/DeviceController.java
@@ -6,11 +6,18 @@ import com.iflytop.digester.underframework.controller.UfApiResponse;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.Map;
+
@Controller
public class DeviceController extends UfApiControllerBase {
@Resource
private Device device;
+ // start reset task thread
+ private StartResetTaskThread startReset = null;
+
@ResponseBody
@PostMapping("/api/device/start")
@@ -18,8 +25,16 @@ public class DeviceController extends UfApiControllerBase {
if (!"new".equals(this.device.getStatus())) {
return this.error("当前设备无法执行启动操作");
}
- var startReset = new StartResetTaskThread();
- startReset.start();
+ this.startReset = new StartResetTaskThread();
+ this.startReset.start();
+ return this.success();
+ }
+
+ @ResponseBody
+ @PostMapping("/api/device/start-action-execute")
+ public UfApiResponse startActionExecute(@RequestBody Map params) {
+ String action = (String)params.get("action");
+ this.startReset.executeAction(action);
return this.success();
}
diff --git a/src/main/java/com/iflytop/digester/underframework/UfActuatorCmdExecutor.java b/src/main/java/com/iflytop/digester/underframework/UfActuatorCmdExecutor.java
index cd39c85..c40697e 100644
--- a/src/main/java/com/iflytop/digester/underframework/UfActuatorCmdExecutor.java
+++ b/src/main/java/com/iflytop/digester/underframework/UfActuatorCmdExecutor.java
@@ -1,9 +1,33 @@
package com.iflytop.digester.underframework;
+import com.iflytop.digester.underframework.dao.model.UfMdbActuator;
import com.iflytop.digester.underframework.dao.model.UfMdbActuatorCmd;
+import com.iflytop.digester.underframework.dao.record.UfActiveRecord;
+import java.util.Map;
public class UfActuatorCmdExecutor {
// execute cmd
public static String execute(UfMdbActuatorCmd cmd) {
var con = UfApplication.getApp().connections.get(cmd.connectionKey);
return con.execute(cmd);
}
+
+ // execute cmd
+ public static String execute(String actuatorKey, String cmdKey, String cmdParams ) {
+ var actuator = UfActiveRecord.findOne(UfMdbActuator.class, Map.of("key", actuatorKey));
+ if ( null == actuator ) {
+ throw new RuntimeException("无效的设备KEY :" + actuatorKey);
+ }
+ var command = UfActiveRecord.findOne(UfMdbActuatorCmd.class, Map.of("cmdKey", cmdKey, "actuatorId", actuator.id));
+ if ( null == command ) {
+ throw new RuntimeException("无效的命令KEY :" + cmdKey);
+ }
+ if ( null != cmdParams ) {
+ command.parameters = cmdParams;
+ }
+ return execute(command);
+ }
+
+ // execute cmd
+ public static String execute(String actuatorKey, String cmdKey) {
+ return execute(actuatorKey, cmdKey, null);
+ }
}
diff --git a/src/main/java/com/iflytop/digester/underframework/connection/UfZcancmderWebsocket.java b/src/main/java/com/iflytop/digester/underframework/connection/UfZcancmderWebsocket.java
index 43efb43..525f7a9 100644
--- a/src/main/java/com/iflytop/digester/underframework/connection/UfZcancmderWebsocket.java
+++ b/src/main/java/com/iflytop/digester/underframework/connection/UfZcancmderWebsocket.java
@@ -1,5 +1,5 @@
package com.iflytop.digester.underframework.connection;
-import com.iflytop.digester.underframework.dao.model.TsMdbActuator;
+import com.iflytop.digester.underframework.dao.model.UfMdbActuator;
import com.iflytop.digester.underframework.dao.model.UfMdbActuatorCmd;
import com.iflytop.digester.underframework.dao.record.UfActiveRecord;
import com.iflytop.digester.underframework.util.TsByteBuffer;
@@ -109,7 +109,7 @@ public class UfZcancmderWebsocket extends UfConnectionBase {
}
if ( null == this.response ) {
- var actuator = UfActiveRecord.findOne(TsMdbActuator.class, actuatorCmd.actuatorId);
+ var actuator = UfActiveRecord.findOne(UfMdbActuator.class, actuatorCmd.actuatorId);
throw new RuntimeException(String.format("设备 [%s] 响应超时: %s", actuator.name, actuatorCmd.cmdKey));
}
@@ -153,7 +153,7 @@ public class UfZcancmderWebsocket extends UfConnectionBase {
private void sendCommandRequest(UfMdbActuatorCmd actuatorCmd) {
String cmd = this.buildCommand(actuatorCmd);
- TsMdbActuator actuator = TsMdbActuator.findOne(TsMdbActuator.class, actuatorCmd.actuatorId);
+ UfMdbActuator actuator = UfMdbActuator.findOne(UfMdbActuator.class, actuatorCmd.actuatorId);
LOG.info("[Command-Executor:{}] {}({}) => {}", actuator.name, actuatorCmd.cmdKey, actuatorCmd.parameters, cmd);
if ( !this.client.isOpen() ) {
throw new RuntimeException("ZCanCmder 连接已断开");
@@ -177,7 +177,7 @@ public class UfZcancmderWebsocket extends UfConnectionBase {
paramList = List.of(params.split(","));
}
- TsMdbActuator actuator = TsMdbActuator.findOne(TsMdbActuator.class, actuatorCmd.actuatorId);
+ UfMdbActuator actuator = UfMdbActuator.findOne(UfMdbActuator.class, actuatorCmd.actuatorId);
int moduleId = Integer.parseInt(actuatorCmd.cmdFlags);
int cmdId = Integer.parseInt(actuatorCmd.cmdId, 16);
@@ -283,7 +283,7 @@ public class UfZcancmderWebsocket extends UfConnectionBase {
this.executeDeviceCommand(command);
this.waitForActuatorFinish(command);
- var actuator = UfActiveRecord.findOne(TsMdbActuator.class, command.actuatorId);
+ var actuator = UfActiveRecord.findOne(UfMdbActuator.class, command.actuatorId);
var encoderAvailable = actuator.getProperty("encoderAvailable");
if ( null == encoderAvailable || !encoderAvailable.asBoolean() ) {
return ;
diff --git a/src/main/java/com/iflytop/digester/underframework/controller/TsApiActuator.java b/src/main/java/com/iflytop/digester/underframework/controller/TsApiActuator.java
index e4b2c98..ac2d1fa 100644
--- a/src/main/java/com/iflytop/digester/underframework/controller/TsApiActuator.java
+++ b/src/main/java/com/iflytop/digester/underframework/controller/TsApiActuator.java
@@ -1,7 +1,7 @@
package com.iflytop.digester.underframework.controller;
import com.iflytop.digester.underframework.dao.record.UfActiveRecord;
import com.iflytop.digester.underframework.dao.record.UfActiveRecordCriteria;
-import com.iflytop.digester.underframework.dao.model.TsMdbActuator;
+import com.iflytop.digester.underframework.dao.model.UfMdbActuator;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@@ -15,13 +15,13 @@ public class TsApiActuator extends UfApiControllerBase {
String moduleId = (String) params.get("moduleId");
var criteria = new UfActiveRecordCriteria();
criteria.conditions = Map.of("moduleId", moduleId);
- var items = UfActiveRecord.find(TsMdbActuator.class, criteria);
+ var items = UfActiveRecord.find(UfMdbActuator.class, criteria);
return success(items);
}
@PostMapping("/api/actuator/save")
@ResponseBody
- public UfApiResponse save(@RequestBody TsMdbActuator actuator ) {
+ public UfApiResponse save(@RequestBody UfMdbActuator actuator ) {
if ( null != actuator.id ) {
actuator.isNewRecord = false;
}
@@ -31,7 +31,7 @@ public class TsApiActuator extends UfApiControllerBase {
@PostMapping("/api/actuator/delete")
@ResponseBody
- public UfApiResponse delete(@RequestBody TsMdbActuator actuator ) {
+ public UfApiResponse delete(@RequestBody UfMdbActuator actuator ) {
actuator.delete();
return success();
}
diff --git a/src/main/java/com/iflytop/digester/underframework/dao/model/TsMdbActuator.java b/src/main/java/com/iflytop/digester/underframework/dao/model/UfMdbActuator.java
similarity index 90%
rename from src/main/java/com/iflytop/digester/underframework/dao/model/TsMdbActuator.java
rename to src/main/java/com/iflytop/digester/underframework/dao/model/UfMdbActuator.java
index c6b81d1..d2135ae 100644
--- a/src/main/java/com/iflytop/digester/underframework/dao/model/TsMdbActuator.java
+++ b/src/main/java/com/iflytop/digester/underframework/dao/model/UfMdbActuator.java
@@ -2,13 +2,12 @@ package com.iflytop.digester.underframework.dao.model;
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.underframework.dao.record.UfActiveRecord;
import com.iflytop.digester.underframework.dao.record.UfActiveRecordField;
/*
底层资源,比如电机、传感器等
*/
-public class TsMdbActuator extends UfActiveRecord {
+public class UfMdbActuator extends UfActiveRecord {
@UfActiveRecordField
public String moduleId;
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index ac7c199..997974a 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -31,3 +31,5 @@ mqtt-broker:
my-topic : "stw-a80"
transbot-topic : "transbot"
+app :
+ errorSlotIndex : 4
\ No newline at end of file
diff --git a/web b/web
index e2b8752..2b86529 160000
--- a/web
+++ b/web
@@ -1 +1 @@
-Subproject commit e2b87526304961b2bac163b58e372fd54ebffdb3
+Subproject commit 2b865293c1311ba5bcefddcb1df3ac83a97799ef