sige 1 year ago
parent
commit
cab084f03f
  1. BIN
      app.db
  2. 2
      pom.xml
  3. 53
      src/main/java/com/iflytop/a800/ResourceLockManager.java
  4. 5
      src/main/java/com/iflytop/a800/Task.java
  5. 36
      src/main/java/com/iflytop/a800/TaskBase.java
  6. 28
      src/main/java/com/iflytop/a800/TaskManager.java
  7. 25
      src/main/java/com/iflytop/a800/controller/DemoController.java
  8. 22
      src/main/java/com/iflytop/a800/controller/TaskController.java
  9. 14
      src/main/java/com/iflytop/a800/device/Device.java
  10. 42
      src/main/java/com/iflytop/a800/device/Feeder.java
  11. 4
      src/main/java/com/iflytop/a800/device/Incubator.java
  12. 117
      src/main/java/com/iflytop/a800/device/Pipette.java
  13. 4
      src/main/java/com/iflytop/a800/device/Scanner.java
  14. 4
      src/main/java/com/iflytop/a800/device/Shaker.java
  15. 4
      src/main/java/com/iflytop/a800/device/TestCardWarehouse.java
  16. 10
      src/main/java/com/iflytop/a800/resource/TestTube.java
  17. 8
      src/main/java/com/iflytop/a800/resource/TestTubeRack.java
  18. 73
      src/main/java/com/iflytop/a800/task/TubeRackTask.java
  19. 52
      src/main/java/com/iflytop/a800/task/TubeTestTask.java

BIN
app.db

2
pom.xml

@ -20,7 +20,7 @@
<dependency> <dependency>
<groupId>com.iflytop</groupId> <groupId>com.iflytop</groupId>
<artifactId>uf</artifactId> <artifactId>uf</artifactId>
<version>0.0.12</version>
<version>0.0.19</version>
</dependency> </dependency>
</dependencies> </dependencies>

53
src/main/java/com/iflytop/a800/ResourceLockManager.java

@ -0,0 +1,53 @@
package com.iflytop.a800;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ResourceLockManager {
// lock map
private final Map<String, List<Object>> locks = new HashMap<>();
// singleton
private static final ResourceLockManager instance = new ResourceLockManager();
// get instance
public static ResourceLockManager getInstance() {
return instance;
}
// lock
public Object lock( String resName ) {
Object lock = new Object();
synchronized ( this.locks ) {
var locks = this.locks.computeIfAbsent(resName, k -> new ArrayList<>());
if ( locks.isEmpty() ) {
locks.add(lock);
return lock;
}
locks.add(lock);
}
synchronized ( lock ) {
try {
lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
return lock;
}
// unlock
public void unlock( String resName, Object lock ) {
Object nextLock = null;
synchronized ( this.locks ) {
var locks = this.locks.get(resName);
if ( null == locks ) {
return ;
}
locks.remove(lock);
nextLock = locks.isEmpty() ? null : locks.get(0);
}
synchronized ( nextLock ) {
nextLock.notify();
}
}
}

5
src/main/java/com/iflytop/a800/Task.java

@ -0,0 +1,5 @@
package com.iflytop.a800;
public interface Task {
// start this task
void start();
}

36
src/main/java/com/iflytop/a800/TaskBase.java

@ -0,0 +1,36 @@
package com.iflytop.a800;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
abstract public class TaskBase extends Thread implements Task {
// 事件回调
public interface EventCallback {
void callback( Task task, List<Object> args );
}
// 事件回调列表
public Map<String,List<EventCallback>> eventCallbacks = new HashMap<>();
// 追加事件回调
public void on( String name, EventCallback callback ) {
if ( !eventCallbacks.containsKey(name) ) {
eventCallbacks.put(name, new ArrayList<>());
}
eventCallbacks.get(name).add(callback);
}
// 触发事件
public void emit( String name, List<Object> args ) {
if ( !eventCallbacks.containsKey(name) ) {
return;
}
for ( var callback : eventCallbacks.get(name) ) {
callback.callback(this, args);
}
}
public void lockRes() {}
public void unlockRes() {}
}

28
src/main/java/com/iflytop/a800/TaskManager.java

@ -0,0 +1,28 @@
package com.iflytop.a800;
import jakarta.annotation.PostConstruct;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
public class TaskManager {
// list of tasks
private final List<Task> tasks = new ArrayList<Task>();
// singleton instance
private static TaskManager instance;
// get instance
public static TaskManager getInstance() {
return instance;
}
@PostConstruct
public void init() {
instance = this;
}
// append task
public void append( Task task ) {
tasks.add(task);
task.start();
}
}

25
src/main/java/com/iflytop/a800/controller/DemoController.java

@ -0,0 +1,25 @@
package com.iflytop.a800.controller;
import com.iflytop.a800.device.Device;
import com.iflytop.uf.controller.UfApiControllerBase;
import com.iflytop.uf.controller.UfApiResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class DemoController extends UfApiControllerBase {
@PostMapping("/api/demo/pipette-tip-pick-up")
@ResponseBody
public UfApiResponse pipetteTipPickUp() {
var pipette = Device.getInstance().pipette;
pipette.tipPickUp();
return this.success();
}
@PostMapping("/api/demo/pipette-tip-drop")
@ResponseBody
public UfApiResponse pipetteTipDrop() {
var pipette = Device.getInstance().pipette;
pipette.tipDrop();
return this.success();
}
}

22
src/main/java/com/iflytop/a800/controller/TaskController.java

@ -0,0 +1,22 @@
package com.iflytop.a800.controller;
import com.iflytop.a800.TaskManager;
import com.iflytop.a800.task.TubeRackTask;
import com.iflytop.uf.controller.UfApiControllerBase;
import com.iflytop.uf.controller.UfApiResponse;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class TaskController extends UfApiControllerBase {
@Resource
private TaskManager taskManager;
@PostMapping("/api/task/tube-rack-append")
@ResponseBody
public UfApiResponse tubeRackAppend() {
var task = new TubeRackTask();
this.taskManager.append(task);
return this.success();
}
}

14
src/main/java/com/iflytop/a800/device/Device.java

@ -0,0 +1,14 @@
package com.iflytop.a800.device;
public class Device {
// singleton instance
private static final Device instance = new Device();
// feeder
public final Feeder feeder = new Feeder();
// pipette
public final Pipette pipette = new Pipette();
// get instance
public static Device getInstance() {
return instance;
}
}

42
src/main/java/com/iflytop/a800/device/Feeder.java

@ -0,0 +1,42 @@
package com.iflytop.a800.device;
import com.iflytop.uf.UfCmdSnippetExecutor;
public class Feeder {
// 试管架进料
public void feed() {
UfCmdSnippetExecutor.execute("FeedTubeRackFeed");
}
// 试管架出料
public void exit() {
UfCmdSnippetExecutor.execute("FeedTubeRackExit");
}
// 读取试管架类型
public Number readTubeRackType() {
UfCmdSnippetExecutor.execute("FeedTubeRackTypeReadPrepare");
return 1;
}
// 读取试管是否存在
public Boolean readIsTestTubeExisted(int i) {
UfCmdSnippetExecutor.execute("FeedTubeRackTubeExistsCheckPrepare");
return false;
}
// 读取是否为5ml管
public Boolean readIsWb5ml(int i) {
UfCmdSnippetExecutor.execute("FeedTubeRackTubeIsWb5mlCheckPrepare");
return true;
}
// 读取条码
public String readBarCode(int i) {
UfCmdSnippetExecutor.execute("FeedTubeRackTubeBarCodeScan");
return "1222";
}
// 准备
public void prepareForTesting() {
UfCmdSnippetExecutor.execute("FeedTubeRackPrepareForTesting");
}
}

4
src/main/java/com/iflytop/a800/device/Incubator.java

@ -0,0 +1,4 @@
package com.iflytop.a800.device;
public class Incubator {
}

117
src/main/java/com/iflytop/a800/device/Pipette.java

@ -0,0 +1,117 @@
package com.iflytop.a800.device;
import com.iflytop.uf.UfActuatorCmdExecutor;
import com.iflytop.uf.UfCmdSnippetExecutor;
import com.iflytop.uf.model.UfMdbOption;
import com.iflytop.uf.util.UfCommon;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class Pipette {
// logger
public static final Logger LOG = LoggerFactory.getLogger(Pipette.class);
// if pipette has tip
private Boolean hasTip = false;
// tip amount list
private final List<Integer> tipAmountList = new ArrayList<>();
//
public Pipette() {
// @TODO : 删除下面三行测试用
this.tipAmountList.add(0);
this.tipAmountList.add(120-29);
this.tipAmountList.add(120);
}
// set tip amount
public void setTipAmount( Integer index, Integer amount ) {
this.tipAmountList.set(index, amount);
}
// pick up tip
public void tipPickUp() {
if ( this.hasTip ) {
return ;
}
int zoneIndex = -1;
int tipIndex = -1;
for ( int i = 0; i < this.tipAmountList.size(); i++ ) {
if ( this.tipAmountList.get(i) > 0 ) {
zoneIndex = i;
tipIndex = 120 - this.tipAmountList.get(i);
break;
}
}
if ( -1 == zoneIndex ) {
throw new RuntimeException("No tip available");
}
this.tipAmountList.set(zoneIndex, 120 - tipIndex -1);
Integer zoneStartX = UfMdbOption.getInteger(String.format("PipetteTipZoneStartX.%d", zoneIndex), 0);
Integer zoneStartY = UfMdbOption.getInteger(String.format("PipetteTipZoneStartY.%d", zoneIndex), 0);
Integer zoneZ = UfMdbOption.getInteger(String.format("PipetteTipZoneZ.%d", zoneIndex), 0);
Integer distanceX = UfMdbOption.getInteger("PipetteTipDistanceX", 0);
Integer distanceY = UfMdbOption.getInteger("PipetteTipDistanceY", 0);
Integer indexX = tipIndex % 12;
Integer indexY = tipIndex / 12;
Integer x = zoneStartX + indexX * distanceX;
Integer y = zoneStartY + indexY * distanceY;
Map<String,Object> pickUpParams = Map.of("tipX", x, "tipY", y, "tipZ", zoneZ);
// 尝试三次拾取枪头
for ( int i=0; i<3; i++ ) {
LOG.info("[Pipette] Pick up tip at [{},{}](x={}, y={}, z={})", indexX, indexY, x, y, zoneZ);
UfCmdSnippetExecutor.execute("PipetteTipPickUp", pickUpParams);
String tipState = UfActuatorCmdExecutor.execute("Pipette", "read_pipette_tip_state");
if ("1".equals(tipState)) {
break ;
}
}
String tipState = UfActuatorCmdExecutor.execute("Pipette", "read_pipette_tip_state");
if ("0".equals(tipState)) {
this.tipPickUp();
return ; // 如果拾取失败则跳过这个TIP取下一个
}
this.hasTip = true;
}
// 丢弃枪头
public void tipDrop() {
if ( !this.hasTip ) {
return ;
}
LOG.info("[Pipette] Drop tip");
UfCmdSnippetExecutor.execute("PipetteTipDrop");
for ( int i=0; i<10; i++ ) {
if ( !this.isTipExistOnPipette() ) {
break;
}
UfCommon.delay(500);
UfActuatorCmdExecutor.execute("Pipette", "pipette_ctrl_put_tip");
LOG.info("[Pipette] Drop tip : retry {}", i);
}
if ( this.isTipExistOnPipette() ) {
throw new RuntimeException("Failed to drop tip");
}
UfCmdSnippetExecutor.execute("ArmReset");
this.hasTip = false;
}
// check if tip exists on pipette by reading tip state
private Boolean isTipExistOnPipette() {
int existsCount = 0;
for ( int i=0; i<3; i++ ) {
String tipState = UfActuatorCmdExecutor.execute("Pipette", "read_pipette_tip_state");
if ("1".equals(tipState)) {
existsCount++;
}
UfCommon.delay(100);
}
return existsCount > 1;
}
}

4
src/main/java/com/iflytop/a800/device/Scanner.java

@ -0,0 +1,4 @@
package com.iflytop.a800.device;
public class Scanner {
}

4
src/main/java/com/iflytop/a800/device/Shaker.java

@ -0,0 +1,4 @@
package com.iflytop.a800.device;
public class Shaker {
}

4
src/main/java/com/iflytop/a800/device/TestCardWarehouse.java

@ -0,0 +1,4 @@
package com.iflytop.a800.device;
public class TestCardWarehouse {
}

10
src/main/java/com/iflytop/a800/resource/TestTube.java

@ -0,0 +1,10 @@
package com.iflytop.a800.resource;
import com.iflytop.a800.task.TubeTestTask;
public class TestTube {
// is tube existed
public Boolean isExisted = false;
// tube type
public Number type;
// tube bar code
public String barCode;
}

8
src/main/java/com/iflytop/a800/resource/TestTubeRack.java

@ -0,0 +1,8 @@
package com.iflytop.a800.resource;
import java.util.List;
public class TestTubeRack {
// tube rack type
public Number type;
// list of test tubes
public List<TestTube> tubes;
}

73
src/main/java/com/iflytop/a800/task/TubeRackTask.java

@ -0,0 +1,73 @@
package com.iflytop.a800.task;
import com.iflytop.a800.Task;
import com.iflytop.a800.TaskBase;
import com.iflytop.a800.TaskManager;
import com.iflytop.a800.device.Device;
import com.iflytop.a800.resource.TestTube;
import com.iflytop.a800.resource.TestTubeRack;
import java.util.ArrayList;
import java.util.List;
public class TubeRackTask extends TaskBase {
// sampling tubes
private List<TestTube> samplingTubes;
@Override
public void run() {
var device = Device.getInstance();
var feeder = device.feeder;
var taskMan = TaskManager.getInstance();
feeder.feed();
this.samplingTubes = new ArrayList<>();
// test tube rack
TestTubeRack tubeRack = new TestTubeRack();
tubeRack.type = feeder.readTubeRackType();
tubeRack.tubes = new ArrayList<>();
for (int i = 0; i < 10; i++) {
var tube = new TestTube();
tubeRack.tubes.add(tube);
tube.isExisted = feeder.readIsTestTubeExisted(i);
if ( !tube.isExisted ) {
continue ;
}
this.samplingTubes.add(tube);
tube.type = tubeRack.type;
if ( !tube.type.equals(1) ) {
continue;
}
Boolean isWb5ml = feeder.readIsWb5ml(i);
if ( isWb5ml ) {
tube.type = 2;
}
tube.barCode = feeder.readBarCode(i);
var testTask = new TubeTestTask();
testTask.tubeRack = tubeRack;
testTask.tube = tube;
testTask.on("StartIncubating", this::onTubeTaskStartIncubating);
taskMan.append(testTask);
}
feeder.prepareForTesting();
}
// tube task finish callback
private void onTubeTaskStartIncubating(Task task, List<Object> args ) {
TubeTestTask tubeTask = null;
if ( !(task instanceof TubeTestTask) ) {
return ;
}
tubeTask = (TubeTestTask)task;
var tube = tubeTask.tube;
this.samplingTubes.remove(tube);
if ( !this.samplingTubes.isEmpty() ) {
return ;
}
Device.getInstance().feeder.exit();
}
}

52
src/main/java/com/iflytop/a800/task/TubeTestTask.java

@ -0,0 +1,52 @@
package com.iflytop.a800.task;
import com.iflytop.a800.TaskBase;
import com.iflytop.a800.resource.TestTube;
import com.iflytop.a800.resource.TestTubeRack;
import com.iflytop.uf.UfCmdSnippetExecutor;
public class TubeTestTask extends TaskBase {
public TestTubeRack tubeRack;
public TestTube tube;
public String status;
@Override
public void run() {
this.shake();
this.uncap();
this.sampling();
this.cap();
this.incubate();
this.scan();
}
// 摇匀
private void shake() {
UfCmdSnippetExecutor.execute("SampleTestShake");
}
// 取盖
private void uncap() {
UfCmdSnippetExecutor.execute("SampleTestUnCap");
}
// 取样
private void sampling() {
UfCmdSnippetExecutor.execute("SampleTestSamplingFromWb5ml");
UfCmdSnippetExecutor.execute("SampleTestSamplingFromBufferTube");
UfCmdSnippetExecutor.execute("SampleTestSamplingFromBufferTube");
}
// 盖回
private void cap() {
UfCmdSnippetExecutor.execute("SampleTestCap");
}
// 孵育
private void incubate() {
UfCmdSnippetExecutor.execute("SampleTestIncubate");
}
// 扫描
private void scan() {
UfCmdSnippetExecutor.execute("SampleTestScanResult");
}
}
Loading…
Cancel
Save