Browse Source

耗材不足时通知前台

master
sige 2 years ago
parent
commit
5a9c1bdb15
  1. 15
      src/main/java/com/dreamworks/boditech/controller/DeviceController.java
  2. 7
      src/main/java/com/dreamworks/boditech/driver/consumable/CsmBufferTubeManager.java
  3. 58
      src/main/java/com/dreamworks/boditech/driver/consumable/CsmLargeBufferTubeManager.java
  4. 32
      src/main/java/com/dreamworks/boditech/driver/task/Executor.java
  5. 8
      src/main/java/com/dreamworks/boditech/driver/task/TaskLoad.java
  6. 11
      src/main/java/com/dreamworks/boditech/driver/task/TaskStartReset.java
  7. 43
      src/main/java/com/dreamworks/boditech/driver/task/TaskTestBase.java
  8. 2
      src/main/java/com/dreamworks/boditech/driver/task/step/Step.java
  9. 7
      src/main/java/com/dreamworks/boditech/driver/task/step/StepBase.java
  10. 18
      src/main/java/com/dreamworks/boditech/driver/task/step/StepPretreatment.java
  11. 13
      src/main/java/com/dreamworks/boditech/driver/task/step/StepPuncture.java
  12. 33
      src/main/java/com/dreamworks/boditech/driver/task/step/StepSampling.java
  13. 24
      src/main/java/com/dreamworks/boditech/service/WebsocketServerService.java
  14. 5
      src/main/java/com/dreamworks/boditech/utils/AppError.java

15
src/main/java/com/dreamworks/boditech/controller/DeviceController.java

@ -167,7 +167,20 @@ public class DeviceController extends BaseController {
status = executor.getWorkingStatus();
statusName = I18n.t("device.status." + status);
}
return this.success(Map.of("status", status, "statusName", statusName));
return this.success(Map.of(
"status", status,
"statusName", statusName
));
}
@ResponseBody
@PostMapping("/api/device/executor-status-get")
public ApiResponse executorStatusGet() {
Executor executor = this.deviceService.getTaskExecutor();
if ( null == executor ) {
return this.success(Map.of("status", 0));
}
return this.success(Map.of("status",executor.getStatus()));
}
@ResponseBody

7
src/main/java/com/dreamworks/boditech/driver/consumable/CsmBufferTubeManager.java

@ -2,6 +2,9 @@ package com.dreamworks.boditech.driver.consumable;
import com.dreamworks.boditech.driver.Device;
import com.dreamworks.boditech.driver.entity.ParamBufferTubeUpdateByBox;
import com.dreamworks.boditech.entity.Project;
import com.dreamworks.boditech.utils.AppError;
import com.dreamworks.boditech.utils.AppRuntimeException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@ -82,7 +85,7 @@ public class CsmBufferTubeManager {
// tube alloc by project name
public CsmBufferTube tubeAllocByProject(Project project) {
for ( CsmBufferTubeBox box : this.bufferTubeBoxes ) {
if ( !box.isProjectMatched(project) ) {
if ( 0 >= box.tubeAmount || !box.isProjectMatched(project) ) {
continue;
}
box.tubeAmount --;
@ -92,7 +95,7 @@ public class CsmBufferTubeManager {
tube.index = box.tubeAmount;
return tube;
}
throw new RuntimeException("no buffer tube available for project " + project.name);
throw new AppRuntimeException(AppError.TASK_BUFFER_TUBE_ALLOC_FAILED);
}
// get device

58
src/main/java/com/dreamworks/boditech/driver/consumable/CsmLargeBufferTubeManager.java

@ -3,6 +3,9 @@ import com.dreamworks.boditech.driver.Device;
import com.dreamworks.boditech.driver.entity.ParamBufferTubeUpdateByBox;
import com.dreamworks.boditech.driver.entity.ParamLargeBufferTubeUpdate;
import com.dreamworks.boditech.entity.Project;
import com.dreamworks.boditech.utils.AppError;
import com.dreamworks.boditech.utils.AppRuntimeException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@ -39,6 +42,7 @@ public class CsmLargeBufferTubeManager {
for ( int i=0; i<6; i++ ) {
CsmLargeBufferTube tube = new CsmLargeBufferTube(this);
tube.index = i;
tube.amount = 0;
this.largeBufferTubes.add(tube);
}
}
@ -91,54 +95,14 @@ public class CsmLargeBufferTubeManager {
tube.amount = param.tubeAmount;
}
// get large buffer tube
public CsmLargeBufferTube getTubeByProjectName( String projectName ) {
CsmLargeBufferTube tube = null;
for ( CsmLargeBufferTube tmp : this.largeBufferTubes ) {
// if ( !tmp.projectName.equals(projectName) ) {
// continue ;
// }
if ( tmp.isEmpty() ) {
continue;
// large buffer tube update
public CsmLargeBufferTube allocByProject( Project project ) {
for ( CsmLargeBufferTube tube : this.largeBufferTubes ) {
if ( 0 >= tube.amount || !tube.isProjectMatched(project) ) {
continue ;
}
tube = tmp;
break;
}
if ( null == tube ) {
throw new RuntimeException("no large buffer tube");
return tube;
}
return tube;
throw new AppRuntimeException(AppError.TASK_LARGE_BUFFER_TUBE_ALLOC_FAILED);
}
}

32
src/main/java/com/dreamworks/boditech/driver/task/Executor.java

@ -31,6 +31,8 @@ public class Executor implements Runnable {
public String workingStatus = "WAIT_FOR_TUBE_RACK";
// incubator wait task
private Task incubatorWaitTask = null;
// resource wait task
private Task resourceWaitTask = null;
/**
* execute single task on device
@ -55,6 +57,8 @@ public class Executor implements Runnable {
public void run() {
LOG.info("task executor start");
this.status = Executor.STATUS_RUNNING;
this.workingStatus = "EXECUTING";
this.device.websocketServerService.log("Task Executor Started");
while (!Objects.equals(this.status, Executor.STATUS_STOP_REQUEST)) {
Task task = this.findExecutableTask();
@ -86,6 +90,7 @@ public class Executor implements Runnable {
this.status = Executor.STATUS_STOPPED;
this.workingStatus = "STOPPED";
LOG.info("task executor stop");
this.device.websocketServerService.log("Task Executor Stopped");
}
/**
@ -166,6 +171,8 @@ public class Executor implements Runnable {
// stop executor
public void stop() {
LOG.info("task executor stop : start");
this.device.websocketServerService.log("Task Executor Stopping");
for ( Task task : this.tasks ) {
task.stop();
}
@ -181,6 +188,8 @@ public class Executor implements Runnable {
*/
public void pause() {
LOG.info("task executor pause : start");
this.device.websocketServerService.log("Task Executor Pausing");
this.status = Executor.STATUS_PAUSE_REQUEST;
synchronized (this.tasks) {
this.tasks.notifyAll();
@ -192,17 +201,28 @@ public class Executor implements Runnable {
throw new RuntimeException(e);
}
}
this.workingStatus = "PAUSED";
LOG.info("task executor pause : end");
this.device.websocketServerService.log("Task Executor Paused");
}
// resume executor
public void resume() {
LOG.info("task executor resume : start");
this.device.websocketServerService.log("Task Executor Resuming");
if ( null != this.resourceWaitTask ) {
this.resourceWaitTask.setStatus(Task.STATUS_READY);
this.resourceWaitTask = null;
}
this.status = Executor.STATUS_RUNNING;
synchronized (this.tasks) {
this.tasks.notifyAll();
}
LOG.info("task executor resume : end");
this.device.websocketServerService.log("Task Executor Resumed");
}
// append task
@ -231,6 +251,7 @@ public class Executor implements Runnable {
// wait for incubator slot
public void waitForIncubatorSlot( Task task ) {
LOG.info("task executor wait for incubator slot");
this.device.websocketServerService.log("Task Executor Waiting For Incubator Slot");
this.status = Executor.STATUS_PAUSED;
this.incubatorWaitTask = task;
}
@ -241,10 +262,19 @@ public class Executor implements Runnable {
return ;
}
LOG.info("task executor incubator slot available");
this.device.websocketServerService.log("Task Executor Incubator Slot Available");
this.status = Executor.STATUS_RUNNING;
this.incubatorWaitTask.setStatus(Task.STATUS_READY);
this.incubatorWaitTask = null;
}
// public void waitForConsumable( Task task );
// wait for consumable resource
public void waitForConsumableResource( Task task, Exception e ) {
LOG.info("task executor wait for consumable resource");
this.device.websocketServerService.log("Task Executor Waiting For Consumable Resource");
this.workingStatus = "WAIT_FOR_CONSUMABLE_RESOURCE";
this.status = Executor.STATUS_PAUSED;
this.resourceWaitTask = task;
this.device.websocketServerService.emit("deviceWaitForConsumableResource", e);
}
}

8
src/main/java/com/dreamworks/boditech/driver/task/TaskLoad.java

@ -1,9 +1,6 @@
package com.dreamworks.boditech.driver.task;
import com.dreamworks.boditech.driver.Device;
import com.dreamworks.boditech.driver.actuator.ActArmXY;
import com.dreamworks.boditech.driver.actuator.ActCodeScanner;
import com.dreamworks.boditech.driver.actuator.ActModuleTestCardBoxCase;
import com.dreamworks.boditech.driver.actuator.ActuatorModule;
import com.dreamworks.boditech.driver.actuator.*;
import com.dreamworks.boditech.entity.Project;
import com.dreamworks.boditech.utils.AppError;
import com.dreamworks.boditech.utils.AppRuntimeException;
@ -25,6 +22,9 @@ public class TaskLoad extends TaskBase {
this.armXY = (ActArmXY)device.getActuator(ActuatorModule.ARM_XY);
this.codeScanner = (ActCodeScanner)device.getActuator(ActuatorModule.ARM_Z_SCANNER);
ActMotor testCardBoxMotor = (ActMotor)device.getActuator(ActuatorModule.TEST_CARD_BOX_MOTOR);
testCardBoxMotor.reset();
try {
this.testCardLoad(executor);
this.bufferTubeLoad(executor);

11
src/main/java/com/dreamworks/boditech/driver/task/TaskStartReset.java

@ -5,6 +5,8 @@ import com.dreamworks.boditech.driver.actuator.*;
import com.dreamworks.boditech.driver.entity.IncubatorSlot;
import com.dreamworks.boditech.driver.entity.WsMessageEvent;
import com.dreamworks.boditech.service.AccountService;
import com.dreamworks.boditech.utils.AppError;
import com.dreamworks.boditech.utils.AppRuntimeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -18,6 +20,8 @@ public class TaskStartReset extends TaskBase {
public static final Integer MODE_ERROR = 1;
// logger
private static final Logger LOG = LoggerFactory.getLogger(TaskStartReset.class);
// is executing
private static Boolean isExecuting = false;
// reset mode
public Integer mode = MODE_NORMAL;
// device instance
@ -31,8 +35,12 @@ public class TaskStartReset extends TaskBase {
@Override
public void execute(Executor executor) {
long startTime = System.currentTimeMillis();
if ( TaskStartReset.isExecuting ) {
throw new AppRuntimeException(AppError.DEVICE_START_RESET_EXECUTING);
}
TaskStartReset.isExecuting = true;
long startTime = System.currentTimeMillis();
this.device = executor.getDevice();
this.analysisPushMotor = (ActMotor)device.getActuator(ActuatorModule.ANALYSIS_PUSH_MOTOR);
this.analysisScanMotor = (ActMotor)device.getActuator(ActuatorModule.ANALYSIS_SCAN_MOTOR);
@ -117,6 +125,7 @@ public class TaskStartReset extends TaskBase {
this.device.testService.cancelAllTasks();
this.device.setIsReady(true);
TaskStartReset.isExecuting = false;
LOG.info("[reset] task finished, cost {}s", (System.currentTimeMillis() - startTime)/1000.00);
}

43
src/main/java/com/dreamworks/boditech/driver/task/TaskTestBase.java

@ -10,6 +10,7 @@ import com.dreamworks.boditech.driver.task.step.StepManager;
import com.dreamworks.boditech.entity.Project;
import com.dreamworks.boditech.entity.MdbTest;
import com.dreamworks.boditech.entity.TestStepLogEntry;
import com.dreamworks.boditech.utils.AppRuntimeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -114,10 +115,6 @@ abstract public class TaskTestBase extends TaskBase implements TaskTest {
return true;
}
if ( !this.waitForResource() ) {
return false;
}
Device device = this.taskExecutor.getDevice();
if ( null == this.projectId ) {
throw new RuntimeException("project id must be set before execute");
@ -126,16 +123,32 @@ abstract public class TaskTestBase extends TaskBase implements TaskTest {
this.steps = StepManager.buildSteps(this.project.steps);
this.stepIndex = 0;
this.postSteps = new ArrayList<>();
this.reactionTube = device.bufferTubes.tubeAllocByProject(this.project);
this.test = new MdbTest();
this.beforeTaskExecute(this.test);
if ( !this.taskResourceAlloc() ) {
return false;
}
device.testService.insert(this.test);
return true;
}
// wait for resource
private Boolean waitForResource() {
private Boolean taskResourceAlloc() {
// 小缓冲液
try {
this.reactionTube = device.bufferTubes.tubeAllocByProject(this.project);
} catch ( AppRuntimeException e ) {
this.steps.clear();
this.steps = null;
this.taskExecutor.waitForConsumableResource(this, e);
this.setStatus(Task.STATUS_WAITING);
return false;
}
// 孵育盘卡位
ActIncubator incubator = (ActIncubator) this.device.getActuator(ActuatorModule.INCUBATOR_MOTOR);
if ( !incubator.hasFreeSlot() ) {
this.taskExecutor.waitForIncubatorSlot(this);
@ -143,6 +156,18 @@ abstract public class TaskTestBase extends TaskBase implements TaskTest {
return false;
}
// 其他资源
for ( Step step : this.steps ) {
try {
step.resourcePreAlloc(this.taskExecutor, this);
} catch ( Exception e ) {
this.steps.clear();
this.steps = null;
this.taskExecutor.waitForConsumableResource(this, e);
this.setStatus(Task.STATUS_WAITING);
return false;
}
}
return true;
}
@ -157,7 +182,11 @@ abstract public class TaskTestBase extends TaskBase implements TaskTest {
TestStepLogEntry logEntry = new TestStepLogEntry();
logEntry.stepName = step.getActionName();
logEntry.stepOptions = step.getStepNode().toString();
LOG.info("step start: ({}) => {}", logEntry.stepName, logEntry.stepOptions);
String logMsg = String.format("[task #%d] step (%s) : %s", this.test.id, logEntry.stepName, logEntry.stepOptions);
LOG.info(logMsg);
this.device.websocketServerService.log(logMsg);
this.device.testService.logStepStart(this.test, logEntry);
step.execute(this.taskExecutor, this);
this.device.testService.logStepEnd(logEntry);

2
src/main/java/com/dreamworks/boditech/driver/task/step/Step.java

@ -19,4 +19,6 @@ public interface Step {
JsonNode getStepNode();
void stop();
void resourcePreAlloc(Executor executor, Task task);
}

7
src/main/java/com/dreamworks/boditech/driver/task/step/StepBase.java

@ -1,4 +1,6 @@
package com.dreamworks.boditech.driver.task.step;
import com.dreamworks.boditech.driver.task.Executor;
import com.dreamworks.boditech.driver.task.Task;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
@ -46,4 +48,9 @@ abstract public class StepBase implements Step {
public void stop() {
// nothing to do here as default, override this method if needed
}
// check resource requirement
public void resourcePreAlloc(Executor executor, Task task) {
// nothing to do here as default, override this method if needed
}
}

18
src/main/java/com/dreamworks/boditech/driver/task/step/StepPretreatment.java

@ -30,6 +30,21 @@ public class StepPretreatment extends StepBase {
// has uncapped
private boolean hasUncapped = false;
// pipette tip to mix sample
private CsmPipetteTip pipetteTip;
@Override
public void resourcePreAlloc(Executor executor, Task task) {
super.resourcePreAlloc(executor,task);
TaskTest taskTest = (TaskTest)task;
CsmSampleTube tube = taskTest.getSampleTube();
MdbTestTube testTube = executor.getDevice().testService.testTubeFindByKey(tube.type);
if ( this.shaking && Objects.equals(MdbTestTube.MIX_TYPE_PIPETTE, testTube.mixType) ) {
this.pipetteTip = executor.getDevice().pipetteTips.tipAlloc();
}
}
@Override
public void execute(Executor executor, Task task) {
this.taskTest = (TaskTest)task;
@ -67,8 +82,7 @@ public class StepPretreatment extends StepBase {
ActMotor armZMotor = (ActMotor)device.getActuator(ActuatorModule.ARM_Z_MOTOR);
CsmSampleTube sampleTube = this.taskTest.getSampleTube();
CsmPipetteTip tip = device.pipetteTips.tipAlloc();
pipette.useTip(tip);
pipette.useTip(this.pipetteTip);
armXY.moveTo(sampleTube.getLocationX(), sampleTube.getLocationY());
armZMotor.moveTo(sampleTube.getLocationZ());

13
src/main/java/com/dreamworks/boditech/driver/task/step/StepPuncture.java

@ -14,6 +14,15 @@ public class StepPuncture extends StepBase {
@JsonProperty("tubeType")
public String tubeType;
// pipette tip to puncture
private CsmPipetteTip pipetteTip;
@Override
public void resourcePreAlloc(Executor executor, Task task) {
super.resourcePreAlloc(executor, task);
this.pipetteTip = executor.getDevice().pipetteTips.tipAlloc();
}
@Override
public void execute(Executor executor, Task task) {
Device device = executor.getDevice();
@ -23,8 +32,7 @@ public class StepPuncture extends StepBase {
TaskTest taskTest = (TaskTest)task;
// 01. 拾取TIP头
CsmPipetteTip tip = device.pipetteTips.tipAlloc();
pipette.useTip(tip);
pipette.useTip(this.pipetteTip);
// 04. 移动到穿刺区域
CsmBufferTube bufferTube = taskTest.getReactionTube();
@ -35,5 +43,6 @@ public class StepPuncture extends StepBase {
// 06. 上升到准备区域
armZMotor.moveTo(0);
pipette.dropTip();
}
}

33
src/main/java/com/dreamworks/boditech/driver/task/step/StepSampling.java

@ -28,6 +28,27 @@ public class StepSampling extends StepBase {
// pipette
private ActPipette pipette;
// resource : pipette tip
private CsmPipetteTip pipetteTip;
// resource : test card
private CsmTestCard testcard;
// resource : large buffer tube
private CsmLargeBufferTube largeBufferTube;
@Override
public void resourcePreAlloc(Executor executor, Task task) {
super.resourcePreAlloc(executor, task);
TaskTest taskTest = (TaskTest)task;
this.pipetteTip = executor.getDevice().pipetteTips.tipAlloc();
if ( this.submit ) {
this.testcard = executor.getDevice().testCards.cardAllocByProject(taskTest.getProject());
}
if ( "LargeBufferTube".equals(this.source) ) {
this.largeBufferTube = executor.getDevice().largeBufferTubes.allocByProject(taskTest.getProject());
}
}
@Override
public void execute(Executor executor, Task task) {
Device device = executor.getDevice();
@ -41,11 +62,7 @@ public class StepSampling extends StepBase {
this.setupTestcard(device);
}
// 如果没有TIP头则拾取TIP头
if ( !this.pipette.getHasTip() ) {
CsmPipetteTip tip = device.pipetteTips.tipAlloc();
this.pipette.useTip(tip);
}
this.pipette.useTip(this.pipetteTip);
// 1. 获取样本
if ( "Sample".equals(this.source) ) {
@ -95,8 +112,7 @@ public class StepSampling extends StepBase {
incubator.lockSlot(testCardSlot.getIndex());
// 获取测试板
CsmTestCard testcard = device.testCards.cardAllocByProject(this.taskTest.getProject());
testCardBoxMotor.moveTo(testcard.getLocation());
testCardBoxMotor.moveTo(this.testcard.getLocation());
testCardFeedMotor.moveTo("testCardPushDepth");
testCardFeedMotor.moveTo("testCardPushStandby");
@ -117,8 +133,7 @@ public class StepSampling extends StepBase {
// get reagent from large buffer tube
private void getReagentFromLargeBufferTube( Device device ) {
CsmLargeBufferTube tube = device.largeBufferTubes.getTubeByProjectName(this.taskTest.getProjectName());
CsmLargeBufferTube tube = this.largeBufferTube;
this.armXY.moveTo(tube.getLocationX(), tube.getLocationY());
this.armZMotor.moveTo(tube.getLocationZ());
this.pipette.aspiration(this.amount);

24
src/main/java/com/dreamworks/boditech/service/WebsocketServerService.java

@ -1,4 +1,5 @@
package com.dreamworks.boditech.service;
import com.dreamworks.boditech.utils.MyCommon;
import com.dreamworks.boditech.utils.MyWsServerConnection;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
@ -6,6 +7,8 @@ import jakarta.annotation.PostConstruct;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Service
public class WebsocketServerService {
// connections
@ -38,4 +41,25 @@ public class WebsocketServerService {
}
this.send(json);
}
// emit event
public void emit( String name, Object data ) {
Map<String,Object> event = Map.of(
"type", "event",
"name", name,
"data", data
);
this.send(event);
}
// emit event
public void emit( String name ) {
this.emit(name, false);
}
// log to frontend
public void log( String message, Object ... params) {
Map<String,Object> event = Map.of("type", "log", "data", message);
this.send(event);
}
}

5
src/main/java/com/dreamworks/boditech/utils/AppError.java

@ -16,6 +16,10 @@ public enum AppError {
INCUBATOR_NO_FREE_SLOT,
TEST_TUBE_RACK_TYPE_NOT_SUPPORTED,
// task
TASK_BUFFER_TUBE_ALLOC_FAILED,
TASK_LARGE_BUFFER_TUBE_ALLOC_FAILED,
// device
DEVICE_BUSY,
DEVICE_ALREADY_STARTED,
@ -24,4 +28,5 @@ public enum AppError {
DEVICE_NOT_STARTED,
DEVICE_STOP_FAILED,
DEVICE_EXECUTING_REQUIRED,
DEVICE_START_RESET_EXECUTING,
}
Loading…
Cancel
Save