Browse Source

增加步骤盖上试管帽

master
sige 2 years ago
parent
commit
5bad92fb5d
  1. BIN
      doc/通讯协议.pdf
  2. 14
      src/main/java/com/dreamworks/boditech/BoditechApplication.java
  3. 37
      src/main/java/com/dreamworks/boditech/driver/Device.java
  4. 12
      src/main/java/com/dreamworks/boditech/driver/connection/ClientRequest.java
  5. 47
      src/main/java/com/dreamworks/boditech/driver/connection/ComWebsocket.java
  6. 124
      src/main/java/com/dreamworks/boditech/driver/connection/ComWebsocketClient.java
  7. 22
      src/main/java/com/dreamworks/boditech/driver/consumable/CsmSampleTube.java
  8. 28
      src/main/java/com/dreamworks/boditech/driver/task/step/StepCapTube.java
  9. 1
      src/main/java/com/dreamworks/boditech/driver/task/step/StepManager.java
  10. 2
      src/main/java/com/dreamworks/boditech/driver/task/step/StepPretreatment.java
  11. 63
      src/main/java/com/dreamworks/boditech/utils/BoditechDeviceClient.java
  12. 14
      src/main/java/com/dreamworks/boditech/utils/MyByteBuffer.java
  13. 4
      src/main/resources/application.yml

BIN
doc/通讯协议.pdf

14
src/main/java/com/dreamworks/boditech/BoditechApplication.java

@ -1,6 +1,5 @@
package com.dreamworks.boditech;
import com.dreamworks.boditech.utils.BoditechDeviceClient;
import org.java_websocket.client.WebSocketClient;
import com.dreamworks.boditech.driver.connection.ComWebsocketClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@ -11,22 +10,11 @@ import java.net.URISyntaxException;
@SpringBootApplication
public class BoditechApplication {
@Value("${app.device.wsurl}")
private String deviceWsUrl;
public static void main(String[] args) {
SpringApplication.run(BoditechApplication.class, args);
}
@Bean
public BoditechDeviceClient boditechDeviceClient() throws URISyntaxException {
URI uri = new URI(this.deviceWsUrl);
BoditechDeviceClient client = new BoditechDeviceClient(uri);
client.connect();
return client;
}
@Bean
public ServerEndpointExporter websocketServerEndpointExporter() {
return new ServerEndpointExporter();
}

37
src/main/java/com/dreamworks/boditech/driver/Device.java

@ -1,6 +1,8 @@
package com.dreamworks.boditech.driver;
import com.dreamworks.boditech.driver.actuator.*;
import com.dreamworks.boditech.driver.connection.ClientRequest;
import com.dreamworks.boditech.driver.connection.ComSerialPort;
import com.dreamworks.boditech.driver.connection.ComWebsocket;
import com.dreamworks.boditech.driver.consumable.*;
import com.dreamworks.boditech.driver.entity.DeviceLogEntry;
import com.dreamworks.boditech.mapper.DeviceLogMapper;
@ -26,12 +28,15 @@ import java.util.Map;
@Component
public class Device {
private static final Logger LOG = LoggerFactory.getLogger(Device.class);
@Value("${app.device.connectionType}")
private String connectionType;
@Value("${app.device.debug}")
private Boolean debug;
@Resource
private ComSerialPort serialPort;
@Resource
private ComWebsocket websocket;
@Resource
private RuntimeOptionService runtimeOptionService;
@Resource
public ProjectService projectService;
@ -115,16 +120,18 @@ public class Device {
int mainCmdId = (cmd >> 8) & 0xFFFF;
int moduleId = mid;
ByteBuffer request = ByteBuffer.allocate(length);
request.order(ByteOrder.LITTLE_ENDIAN);
request.putShort(this.messageIndex);
request.putShort((short)mainCmdId); // main cmd id
request.put((byte)subCmdId); // sub cmd id
request.put((byte)0); // directive type
request.putShort((short)moduleId); // mid
ClientRequest request = new ClientRequest();
request.id = this.messageIndex;
request.parameter = ByteBuffer.allocate(length);
request.parameter.order(ByteOrder.LITTLE_ENDIAN);
request.parameter.putShort(this.messageIndex);
request.parameter.putShort((short)mainCmdId); // main cmd id
request.parameter.put((byte)subCmdId); // sub cmd id
request.parameter.put((byte)0); // directive type
request.parameter.putShort((short)moduleId); // mid
for ( Object param : params ) {
if ( param instanceof Integer ) {
request.putInt((Integer)param);
request.parameter.putInt((Integer)param);
}
}
@ -133,10 +140,11 @@ public class Device {
this.messageIndex = 0;
}
if ( this.debug ) {
return this.serialPortCall(request);
if ("SerialPort".equals(this.connectionType)) {
return this.serialPortCall(request.parameter);
}
return this.prodCall(cmd, mid, params);
return this.websocket.call(request);
}
// call device command in debug mode
@ -152,11 +160,6 @@ public class Device {
return response;
}
private ByteBuffer prodCall(Integer cmd, Integer mid, Object[] params) {
System.out.println("Device prodCall");
return null;
}
// log device message
public void logMessage( Integer direction, ByteBuffer message ) {
String messageHex = MyByteBuffer.toHex(message);

12
src/main/java/com/dreamworks/boditech/driver/connection/ClientRequest.java

@ -0,0 +1,12 @@
package com.dreamworks.boditech.driver.connection;
import java.nio.ByteBuffer;
public class ClientRequest {
// message id
public short id;
// parameter
public ByteBuffer parameter;
// response
public ByteBuffer response;
// error code
public Integer errorCode = 0;
}

47
src/main/java/com/dreamworks/boditech/driver/connection/ComWebsocket.java

@ -0,0 +1,47 @@
package com.dreamworks.boditech.driver.connection;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
@Component
public class ComWebsocket {
@Value("${app.device.connectionType}")
private String connectionType;
@Value("${app.device.wsuri}")
private String deviceWsUrl;
// websocket client
private ComWebsocketClient client;
@PostConstruct
public void init() {
if ( !this.connectionType.equals("WebSocket") ) {
return ;
}
this.open();
}
// open websocket connection
public void open() {
URI uri = null;
try {
uri = new URI(this.deviceWsUrl);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
this.client = new ComWebsocketClient(uri);
this.client.connect();
}
// call device method and wait for response
public ByteBuffer call(ClientRequest request ) {
this.client.call(request);
if ( 0 != request.errorCode ) {
throw new RuntimeException("device error : " + request.errorCode);
}
return request.response;
}
}

124
src/main/java/com/dreamworks/boditech/driver/connection/ComWebsocketClient.java

@ -0,0 +1,124 @@
package com.dreamworks.boditech.driver.connection;
import com.dreamworks.boditech.utils.MyByteBuffer;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class ComWebsocketClient extends WebSocketClient {
// message type : ack
private static final byte MESSAGE_TYPE_ACK = 0x01;
// message type : error ack
private static final byte MESSAGE_TYPE_ERROR_ACK = 0x02;
// message type : event
private static final byte MESSAGE_TYPE_EVENT = 0x03;
// logger
private static final Logger LOG = LoggerFactory.getLogger(ComWebsocketClient.class);
// request list
private final List<ClientRequest> requestList = new ArrayList<>();
// constructor
public ComWebsocketClient(URI uri ) {
super(uri);
}
/**
* call device method and wait for response
* @param request request
*/
public void call( ClientRequest request ) {
this.requestList.add(request);
int index = this.requestList.indexOf(request);
ClientRequest requestItem = this.requestList.get(index);
synchronized (requestItem) {
String cmd = MyByteBuffer.toHex(request.parameter);
cmd = cmd.replace(" ","");
LOG.info("device => {}", cmd);
this.send(cmd);
try {
requestItem.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
/**
* event handler for receiving text message from device
* @param text message from device
*/
@Override
public void onMessage(String text) {
LOG.info("device <= {}", text);
ByteBuffer message = MyByteBuffer.fromHex(text);
message.order(ByteOrder.LITTLE_ENDIAN);
byte messageType = message.get(5);
if ( ComWebsocketClient.MESSAGE_TYPE_ACK == messageType ) {
this.onMessageHandleAck(message);
} else if ( ComWebsocketClient.MESSAGE_TYPE_ERROR_ACK == messageType ) {
this.onMessageHandleErrorAck(message);
} else if ( ComWebsocketClient.MESSAGE_TYPE_EVENT == messageType ) {
this.onMessageHandleEvent(message);
} else {
throw new RuntimeException("unknown message type");
}
}
// handle ack message
private void onMessageHandleAck(ByteBuffer message) {
ClientRequest request = this.getRequestMessage(message);
synchronized (Objects.requireNonNull(request)) {
request.response = message;
request.notify();
}
}
// handle error ack message
private void onMessageHandleErrorAck(ByteBuffer message) {
ClientRequest request = this.getRequestMessage(message);
synchronized (Objects.requireNonNull(request)) {
request.response = null;
request.errorCode = message.getInt(8);
request.notify();
}
}
// get request message
private ClientRequest getRequestMessage( ByteBuffer message ) {
short messageId = message.getShort(0);
for ( ClientRequest requestItem : this.requestList ) {
if ( requestItem.id == messageId ) {
return requestItem;
}
}
return null;
}
// handle event message
private void onMessageHandleEvent( ByteBuffer message ) {
// @TODO : handle event
throw new RuntimeException("not implemented");
}
@Override
public void onOpen(ServerHandshake serverHandshake) {
LOG.info("device connected");
}
@Override
public void onClose(int i, String s, boolean b) {
LOG.info("close");
throw new RuntimeException("device connection closed");
}
@Override
public void onError(Exception e) {
LOG.error("error", e);
throw new RuntimeException(e);
}
}

22
src/main/java/com/dreamworks/boditech/driver/consumable/CsmSampleTube.java

@ -2,9 +2,15 @@ package com.dreamworks.boditech.driver.consumable;
import com.dreamworks.boditech.driver.Device;
import java.util.Objects;
public class CsmSampleTube {
// tube type : Epp 0.5
public static final String TYPE_EPP05 = "Epp0.5";
// tube type : blood tube 3ml
public static final String TYPE_BLOOD_TUBE_3ML = "BloodTube3ml";
// tube type : blood tube 5ml
public static final String TYPE_BLOOD_TUBE_5ML = "BloodTube5ml";
// task type : regular
public static final String TASK_TYPE_REGULAR = "regular";
// task type : emergency
@ -21,6 +27,7 @@ public class CsmSampleTube {
// device
private final Device device;
// constructor
public CsmSampleTube(Device device) {
this.device = device;
}
@ -35,14 +42,23 @@ public class CsmSampleTube {
return this.locationY;
}
// check if tube has cap
public Boolean hasCap() {
return Objects.equals(this.type, CsmSampleTube.TYPE_BLOOD_TUBE_3ML)
|| Objects.equals(this.type, CsmSampleTube.TYPE_BLOOD_TUBE_5ML);
}
// get location z for pipette to start aspiration.
public Integer getLocationZ() {
if ( CsmSampleTube.TASK_TYPE_EMERGENCY.equals(this.taskType) ) {
return this.device.getLocationByName("emergencyTubeDepth");
} else if ( CsmSampleTube.TYPE_BLOOD_TUBE_5ML.equals(this.type) ) {
return this.device.getLocationByName("regularBloodTube5mlDepth");
} else if ( CsmSampleTube.TYPE_BLOOD_TUBE_3ML.equals(this.type) ) {
return this.device.getLocationByName("regularBloodTube3mlDepth");
} else {
throw new RuntimeException("unknown tube type " + this.type);
}
// @TODO : get location z for regular tube
return 1400;
}
public Boolean isShakable() {

28
src/main/java/com/dreamworks/boditech/driver/task/step/StepCapTube.java

@ -0,0 +1,28 @@
package com.dreamworks.boditech.driver.task.step;
import com.dreamworks.boditech.driver.Device;
import com.dreamworks.boditech.driver.actuator.ActMotor;
import com.dreamworks.boditech.driver.actuator.ActuatorModule;
import com.dreamworks.boditech.driver.task.Executor;
import com.dreamworks.boditech.driver.task.Task;
import com.dreamworks.boditech.driver.task.TaskTest;
public class StepCapTube extends StepBase {
@Override
public void execute(Executor executor, Task task) {
TaskTest taskTest = (TaskTest)task;
if ( !taskTest.getSampleTube().hasCap() ) {
return ;
}
Device device = executor.getDevice();
ActMotor testTubeMoveMotor = (ActMotor)device.getActuator(ActuatorModule.TEST_TUBE_SHAKING_MOVE_MOTOR);
ActMotor testTubeCapClipMotor = (ActMotor)device.getActuator(ActuatorModule.TEST_TUBE_SHAKING_CAP_CLIP_MOTOR);
ActMotor testTubeClipMotor = (ActMotor)device.getActuator(ActuatorModule.TEST_TUBE_SHAKING_CLIP_MOTOR);
testTubeMoveMotor.moveTo("shakeTestTubeMoveCapClip");
testTubeCapClipMotor.moveTo("shakeTestTubeCapClipOpen");
testTubeMoveMotor.moveTo("shakeTestTubeMoveStandby");
testTubeCapClipMotor.moveTo("shakeTestTubeCapClipClose");
testTubeClipMotor.rotate(ActMotor.ROTATE_DIRECTION_COUNTER_CLOCKWISE);
testTubeClipMotor.stop();
}
}

1
src/main/java/com/dreamworks/boditech/driver/task/step/StepManager.java

@ -20,6 +20,7 @@ public class StepManager {
StepManager.stepMap.put("sampling", StepSampling.class);
StepManager.stepMap.put("analysis", StepAnalysis.class);
StepManager.stepMap.put("wait", StepWait.class);
StepManager.stepMap.put("cap-tube", StepCapTube.class);
}
// build step by given name

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

@ -40,8 +40,6 @@ public class StepPretreatment extends StepBase {
if ( this.uncap ) {
this.uncap("none");
}
this.taskTest.postStepPush(this);
}
// shake

63
src/main/java/com/dreamworks/boditech/utils/BoditechDeviceClient.java

@ -1,63 +0,0 @@
package com.dreamworks.boditech.utils;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.nio.ByteBuffer;
public class BoditechDeviceClient extends WebSocketClient {
public interface OnMessageCallback {
void onCallback(ByteBuffer buffer);
}
private static final Logger LOG = LoggerFactory.getLogger(BoditechDeviceClient.class);
private OnMessageCallback onMessageCallback;
public BoditechDeviceClient(URI uri ) {
super(uri);
}
public void setOnMessageCallback( OnMessageCallback callback ) {
this.onMessageCallback = callback;
}
@Override
public void onOpen(ServerHandshake serverHandshake) {
LOG.info("device connected");
}
/**
* event handler for receiving binary message from device
* @param buffer message from device
*/
@Override
public void onMessage(ByteBuffer buffer) {
LOG.info("on message (binary) :{}", buffer);
if ( this.onMessageCallback != null ) {
this.onMessageCallback.onCallback(buffer);
}
}
/**
* event handler for receiving text message from device
* @param text message from device
*/
@Override
public void onMessage(String text) {
LOG.info("on message (text) : {}", text);
if ( this.onMessageCallback != null ) {
this.onMessageCallback.onCallback(ByteBuffer.wrap(text.getBytes()));
}
}
@Override
public void onClose(int i, String s, boolean b) {
LOG.info("close");
}
@Override
public void onError(Exception e) {
LOG.error("error", e);
}
}

14
src/main/java/com/dreamworks/boditech/utils/MyByteBuffer.java

@ -9,4 +9,18 @@ public class MyByteBuffer {
}
return sb.toString();
}
// convert hex string to byte buffer
public static ByteBuffer fromHex ( String hex ) {
// remove space
hex = hex.replaceAll(" ", "");
// convert hex string to byte array
byte[] bytes = new byte[hex.length() / 2];
for (int i = 0; i < hex.length(); i += 2) {
String sub = hex.substring(i, i + 2);
bytes[i / 2] = (byte) Integer.parseInt(sub, 16);
}
// convert byte array to byte buffer
return ByteBuffer.wrap(bytes);
}
}

4
src/main/resources/application.yml

@ -8,7 +8,7 @@ spring:
app:
device:
debug : true
connectionType : WebSocket # SerialPort, WebSocket
path : COM3
baudrate : 921600
# wsurl : wss://ws.postman-echo.com/raw
wsurl : ws://127.0.0.1:8899/device
wsuri : ws://192.168.8.10:19005
Loading…
Cancel
Save