Browse Source

#24 液面位置检测

master
sige 2 years ago
parent
commit
912e7ccf94
  1. 23
      src/main/java/com/dreamworks/boditech/controller/TmpController.java
  2. 5
      src/main/java/com/dreamworks/boditech/driver/Device.java
  3. 35
      src/main/java/com/dreamworks/boditech/driver/actuator/ActArmZMotor.java
  4. 31
      src/main/java/com/dreamworks/boditech/driver/actuator/ActPipette.java
  5. 2
      src/main/java/com/dreamworks/boditech/driver/connection/ClientRequest.java
  6. 17
      src/main/java/com/dreamworks/boditech/driver/connection/ComSerialPort.java
  7. 4
      src/main/java/com/dreamworks/boditech/driver/task/TaskTest.java
  8. 5
      src/main/java/com/dreamworks/boditech/driver/task/TaskTestBase.java
  9. 4
      src/main/java/com/dreamworks/boditech/driver/task/step/StepPretreatment.java
  10. 19
      src/main/java/com/dreamworks/boditech/driver/task/step/StepSampling.java
  11. 1
      src/main/java/com/dreamworks/boditech/utils/AppError.java
  12. 4
      src/main/resources/application-dev.yml

23
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<String,Object> 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<String,Object> 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<String,Object> 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();
}
}

5
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));

35
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);
}
}

31
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;
}
}

2
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;
}

17
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);
}
}

4
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();

5
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();

4
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);

19
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);
}

1
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,
}

4
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
Loading…
Cancel
Save