diff --git a/src/main/java/com/dreamworks/boditech/controller/TmpController.java b/src/main/java/com/dreamworks/boditech/controller/TmpController.java index 0d20149..8aac4e5 100644 --- a/src/main/java/com/dreamworks/boditech/controller/TmpController.java +++ b/src/main/java/com/dreamworks/boditech/controller/TmpController.java @@ -1,5 +1,10 @@ package com.dreamworks.boditech.controller; import com.dreamworks.boditech.controller.entity.ApiResponse; +import com.dreamworks.boditech.driver.actuator.ActArmXY; +import com.dreamworks.boditech.driver.actuator.ActArmZMotor; +import com.dreamworks.boditech.driver.actuator.ActPipette; +import com.dreamworks.boditech.driver.actuator.ActuatorModule; +import com.dreamworks.boditech.service.DeviceService; import com.dreamworks.boditech.service.WebsocketServerService; import jakarta.annotation.Resource; import org.springframework.beans.factory.annotation.Autowired; @@ -11,19 +16,17 @@ import java.util.Map; @Controller public class TmpController extends BaseController { @Resource - private WebsocketServerService wsServer; + private DeviceService deviceService; @ResponseBody - @PostMapping("/api/tmp/trigger-event") - public ApiResponse triggerEvent(@RequestBody Map params ) { - this.wsServer.send("{\"type\":\"event\",\"name\":\"" + params.get("name") + "\"}"); - return this.success(); - } + @PostMapping("/api/tmp/move-to-liquid-level") + public ApiResponse moveToLiquidLevel(@RequestBody Map params ) { + String name = (String)params.get("name"); + ActArmXY armXY = (ActArmXY)this.deviceService.device.getActuator(ActuatorModule.ARM_XY); + armXY.moveTo("largeBufferTubeAspirationStart"); - @ResponseBody - @PostMapping("/api/tmp/trigger-error") - public ApiResponse triggerError(@RequestBody Map params ) { - this.wsServer.send("{\"type\":\"error\",\"name\":\"" + params.get("name") + "\",\"message\":\"DEMO_ERROR_MESSAGE\"}"); + ActArmZMotor armZ = (ActArmZMotor)this.deviceService.device.getActuator(ActuatorModule.ARM_Z_MOTOR); + armZ.moveToLiquidLevel(name); return this.success(); } } diff --git a/src/main/java/com/dreamworks/boditech/driver/Device.java b/src/main/java/com/dreamworks/boditech/driver/Device.java index fbc1ba5..a439caf 100644 --- a/src/main/java/com/dreamworks/boditech/driver/Device.java +++ b/src/main/java/com/dreamworks/boditech/driver/Device.java @@ -14,6 +14,7 @@ import jakarta.annotation.Resource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -49,6 +50,8 @@ public class Device { public WebsocketServerService websocketServerService; @Resource public IdChipService idChipService; + @Resource + public Environment appEnv; // consumable : test cards public final CsmTestCardManager testCards = new CsmTestCardManager(this); @@ -78,7 +81,7 @@ public class Device { this.appendActuator(new ActMotor(ActuatorModule.TEST_TUBE_SHAKING_ROTATE_MOTOR, this)); this.appendActuator(new ActMotor(ActuatorModule.TEST_TUBE_SHAKING_CLIP_MOTOR, this)); this.appendActuator(new ActArmXY(ActuatorModule.ARM_XY, this)); - this.appendActuator(new ActMotor(ActuatorModule.ARM_Z_MOTOR, this)); + this.appendActuator(new ActArmZMotor(ActuatorModule.ARM_Z_MOTOR, this)); this.appendActuator(new ActPipette(ActuatorModule.ARM_Z_PIPETTE, this)); this.appendActuator(new ActIncubator(ActuatorModule.INCUBATOR_MOTOR, this)); this.appendActuator(new ActMotor(ActuatorModule.ANALYSIS_PUSH_MOTOR, this)); diff --git a/src/main/java/com/dreamworks/boditech/driver/actuator/ActArmZMotor.java b/src/main/java/com/dreamworks/boditech/driver/actuator/ActArmZMotor.java new file mode 100644 index 0000000..4d70397 --- /dev/null +++ b/src/main/java/com/dreamworks/boditech/driver/actuator/ActArmZMotor.java @@ -0,0 +1,35 @@ +package com.dreamworks.boditech.driver.actuator; +import com.dreamworks.boditech.driver.Device; +import com.dreamworks.boditech.utils.AppError; +import com.dreamworks.boditech.utils.AppRuntimeException; + +public class ActArmZMotor extends ActMotor { + // constructor + public ActArmZMotor(Integer mid, Device device) { + super(mid, device); + } + + // move to level of liquid + public void moveToLiquidLevel( String pointName ) { + Device device = this.getDevice(); + ActPipette pipette = (ActPipette)device.getActuator(ActuatorModule.ARM_Z_PIPETTE); + + Integer armZPosition = device.getLocationByName(pointName + "LiquidLevelDetect.start"); + Integer armZStep = device.getLocationByName(pointName + "LiquidLevelDetect.step"); + Integer armZMax = device.getLocationByName(pointName + "LiquidLevelDetect.max"); + Integer aspirationDepth = device.getLocationByName(pointName + "LiquidLevelDetect.aspirationDepth"); + do { + this.moveTo(armZPosition); + if ( pipette.hasTipTouchedLiquidLevel() ) { + break; + } + armZPosition += armZStep; + if ( armZPosition > armZMax ) { + throw new AppRuntimeException(AppError.DEVICE_LIQUID_LEVEL_DETECT_FAILED); + } + } while ( true ); + + armZPosition += aspirationDepth; + this.moveTo(armZPosition); + } +} diff --git a/src/main/java/com/dreamworks/boditech/driver/actuator/ActPipette.java b/src/main/java/com/dreamworks/boditech/driver/actuator/ActPipette.java index 46b48c6..2267364 100644 --- a/src/main/java/com/dreamworks/boditech/driver/actuator/ActPipette.java +++ b/src/main/java/com/dreamworks/boditech/driver/actuator/ActPipette.java @@ -3,6 +3,9 @@ import com.dreamworks.boditech.driver.DeviceCommand; import com.dreamworks.boditech.driver.Device; import com.dreamworks.boditech.driver.consumable.CsmPipetteTip; public class ActPipette extends ActuatorBase { + // register : sensor temperature 0 + public static final Integer REG_PIPETTE_CAPACITANCE_VAL = 4001; + // has tip private Boolean hasTip = false; @@ -50,12 +53,22 @@ public class ActPipette extends ActuatorBase { // move to given position public void aspiration(int volume) { + String enable = this.getDevice().appEnv.getProperty("app.device.pipetteAspirationEnable"); + if ("false".equals(enable)) { + return ; + } + this.call(DeviceCommand.CMD_PIPETTE_CTRL_MOVE_TO_UL, volume); this.waitForFinish(); } // move to given position public void dispense(int volume) { + String enable = this.getDevice().appEnv.getProperty("app.device.pipetteAspirationEnable"); + if ("false".equals(enable)) { + return ; + } + this.call(DeviceCommand.CMD_PIPETTE_CTRL_MOVE_TO_UL, volume); this.waitForFinish(); } @@ -64,4 +77,22 @@ public class ActPipette extends ActuatorBase { public void dispense() { this.dispense(0); } + + // get whether the tip has touched liquid level + public Boolean hasTipTouchedLiquidLevel() { + String enable = this.getDevice().appEnv.getProperty("app.device.pipetteLiquidLevelDetectEnable"); + if ( "false".equals(enable) ) { + return true; + } + + int threshold = this.getDevice().getLocationByName("liquidLevelDetectCapacitanceThreshold"); + int highCount = 0; + for ( int i=0; i<3; i++ ) { + Integer capValue = this.getRegister(ActPipette.REG_PIPETTE_CAPACITANCE_VAL); + if ( capValue > threshold ) { + highCount++; + } + } + return highCount > 1; + } } diff --git a/src/main/java/com/dreamworks/boditech/driver/connection/ClientRequest.java b/src/main/java/com/dreamworks/boditech/driver/connection/ClientRequest.java index 6a3edb7..3ef70a9 100644 --- a/src/main/java/com/dreamworks/boditech/driver/connection/ClientRequest.java +++ b/src/main/java/com/dreamworks/boditech/driver/connection/ClientRequest.java @@ -14,4 +14,6 @@ public class ClientRequest { public Integer errorCode = 0; // is response received public Boolean isResponseReceived = false; + // timeout count + public Integer timeoutCount = 0; } diff --git a/src/main/java/com/dreamworks/boditech/driver/connection/ComSerialPort.java b/src/main/java/com/dreamworks/boditech/driver/connection/ComSerialPort.java index 80205e9..94113c1 100644 --- a/src/main/java/com/dreamworks/boditech/driver/connection/ComSerialPort.java +++ b/src/main/java/com/dreamworks/boditech/driver/connection/ComSerialPort.java @@ -118,6 +118,11 @@ public class ComSerialPort { if ( requestItem.isResponseReceived ) { return ; } + if ( requestItem.timeoutCount < 3 ) { + requestItem.timeoutCount ++; + write(requestItem.parameter); + return ; + } LOG.info("device -- timeout"); requestItem.response = null; requestItem.errorCode = ClientRequest.ERROR_CODE_TIMEOUT; @@ -126,14 +131,12 @@ public class ComSerialPort { } }; Timer timer = new Timer(); - timer.schedule(timerTask, 10000); + timer.schedule(timerTask, 5000); synchronized (requestItem) { String cmd = MyByteBuffer.toHex(request.parameter); LOG.info("device => {}", cmd); - - byte[] bytes = request.parameter.array(); - this.port.writeBytes(bytes, bytes.length); + this.write(request.parameter); try { requestItem.wait(); } catch (InterruptedException e) { @@ -154,4 +157,10 @@ public class ComSerialPort { int numRead = this.port.readBytes(buffer, buffer.length); return ByteBuffer.wrap(buffer, 0, numRead); } + + // write data to serial port + public void write( ByteBuffer data ) { + byte[] bytes = data.array(); + this.port.writeBytes(bytes, bytes.length); + } } diff --git a/src/main/java/com/dreamworks/boditech/driver/task/TaskTest.java b/src/main/java/com/dreamworks/boditech/driver/task/TaskTest.java index 5e2892c..c4d321f 100644 --- a/src/main/java/com/dreamworks/boditech/driver/task/TaskTest.java +++ b/src/main/java/com/dreamworks/boditech/driver/task/TaskTest.java @@ -5,7 +5,9 @@ import com.dreamworks.boditech.driver.entity.IncubatorSlot; import com.dreamworks.boditech.driver.task.step.Step; import com.dreamworks.boditech.entity.Project; public interface TaskTest { - // + // get task type + String getTaskType(); + String getProjectName(); CsmSampleTube getSampleTube(); diff --git a/src/main/java/com/dreamworks/boditech/driver/task/TaskTestBase.java b/src/main/java/com/dreamworks/boditech/driver/task/TaskTestBase.java index a80c65e..a10ccf4 100644 --- a/src/main/java/com/dreamworks/boditech/driver/task/TaskTestBase.java +++ b/src/main/java/com/dreamworks/boditech/driver/task/TaskTestBase.java @@ -45,6 +45,11 @@ abstract public class TaskTestBase extends TaskBase implements TaskTest { private Boolean requestTaskStop = false; @Override + public String getTaskType() { + return this.test.taskType; + } + + @Override public void stop() { if ( null == this.device ) { super.stop(); diff --git a/src/main/java/com/dreamworks/boditech/driver/task/step/StepPretreatment.java b/src/main/java/com/dreamworks/boditech/driver/task/step/StepPretreatment.java index 09e0496..38e368d 100644 --- a/src/main/java/com/dreamworks/boditech/driver/task/step/StepPretreatment.java +++ b/src/main/java/com/dreamworks/boditech/driver/task/step/StepPretreatment.java @@ -79,12 +79,12 @@ public class StepPretreatment extends StepBase { Device device = executor.getDevice(); ActPipette pipette = (ActPipette)device.getActuator(ActuatorModule.ARM_Z_PIPETTE); ActArmXY armXY = (ActArmXY)device.getActuator(ActuatorModule.ARM_XY); - ActMotor armZMotor = (ActMotor)device.getActuator(ActuatorModule.ARM_Z_MOTOR); + ActArmZMotor armZMotor = (ActArmZMotor)device.getActuator(ActuatorModule.ARM_Z_MOTOR); CsmSampleTube sampleTube = this.taskTest.getSampleTube(); pipette.useTip(this.pipetteTip); armXY.moveTo(sampleTube.getLocationX(), sampleTube.getLocationY()); - armZMotor.moveTo(sampleTube.getLocationZ()); + armZMotor.moveToLiquidLevel(sampleTube.type); for ( int i=0; i<5; i++ ) { pipette.aspiration(100); diff --git a/src/main/java/com/dreamworks/boditech/driver/task/step/StepSampling.java b/src/main/java/com/dreamworks/boditech/driver/task/step/StepSampling.java index d3e6bae..3dc8c90 100644 --- a/src/main/java/com/dreamworks/boditech/driver/task/step/StepSampling.java +++ b/src/main/java/com/dreamworks/boditech/driver/task/step/StepSampling.java @@ -6,6 +6,7 @@ import com.dreamworks.boditech.driver.task.Task; import com.dreamworks.boditech.driver.task.Executor; import com.dreamworks.boditech.driver.consumable.*; import com.dreamworks.boditech.driver.task.TaskTest; +import com.dreamworks.boditech.utils.AppRuntimeException; import com.fasterxml.jackson.annotation.JsonProperty; public class StepSampling extends StepBase { @JsonProperty("source") @@ -24,7 +25,7 @@ public class StepSampling extends StepBase { // arm XY private ActArmXY armXY; // arm Z motor - private ActMotor armZMotor; + private ActArmZMotor armZMotor; // pipette private ActPipette pipette; @@ -54,7 +55,7 @@ public class StepSampling extends StepBase { Device device = executor.getDevice(); this.taskTest = (TaskTest)task; this.armXY = (ActArmXY)device.getActuator(ActuatorModule.ARM_XY); - this.armZMotor = (ActMotor)device.getActuator(ActuatorModule.ARM_Z_MOTOR); + this.armZMotor = (ActArmZMotor)device.getActuator(ActuatorModule.ARM_Z_MOTOR); this.pipette = (ActPipette)device.getActuator(ActuatorModule.ARM_Z_PIPETTE); // 准备测试板夹 @@ -126,7 +127,18 @@ public class StepSampling extends StepBase { CsmSampleTube sampleTube = this.taskTest.getSampleTube(); this.armXY.moveTo(sampleTube.getLocationX(), sampleTube.getLocationY()); - this.armZMotor.moveTo(sampleTube.getLocationZ()); + try { + if (CsmSampleTube.TASK_TYPE_EMERGENCY.equals(this.taskTest.getTaskType())) { + this.armZMotor.moveToLiquidLevel("emergencyTube"); + } else { + this.armZMotor.moveToLiquidLevel(sampleTube.type); + } + } catch ( AppRuntimeException e ) { + // @TODO : 额~~~ 这里是个大问题, 样本没有取到的话,我应该当作没发生,继续执行后续操作 ~~~ + // 但是应当标记一下这个测试状态到 ERROR 才行, 顺便发个通知给前台 ~~~ + // 这里先忽略掉, 后面根据 issue 来处理 + } + this.pipette.aspiration(this.amount); this.armZMotor.moveTo(0); } @@ -136,6 +148,7 @@ public class StepSampling extends StepBase { CsmLargeBufferTube tube = this.largeBufferTube; this.armXY.moveTo(tube.getLocationX(), tube.getLocationY()); this.armZMotor.moveTo(tube.getLocationZ()); + this.armZMotor.moveToLiquidLevel("largeBufferTube"); this.pipette.aspiration(this.amount); this.armZMotor.moveTo(0); } diff --git a/src/main/java/com/dreamworks/boditech/utils/AppError.java b/src/main/java/com/dreamworks/boditech/utils/AppError.java index a3ecddf..71733dd 100644 --- a/src/main/java/com/dreamworks/boditech/utils/AppError.java +++ b/src/main/java/com/dreamworks/boditech/utils/AppError.java @@ -29,4 +29,5 @@ public enum AppError { DEVICE_STOP_FAILED, DEVICE_EXECUTING_REQUIRED, DEVICE_START_RESET_EXECUTING, + DEVICE_LIQUID_LEVEL_DETECT_FAILED, } diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index c0fef79..a07ae56 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -12,10 +12,12 @@ app: device: enable : true debug : true - connectionType : SerialPort # SerialPort, WebSocket + connectionType : WebSocket # SerialPort, WebSocket path : COM3 baudrate : 921600 wsuri : ws://192.168.8.10:19005 + pipetteAspirationEnable : false + pipetteLiquidLevelDetectEnable : false websocket: port : 19006 \ No newline at end of file