diff --git a/app.db b/app.db index 36558e4..0fd5b47 100644 Binary files a/app.db and b/app.db differ diff --git a/src/main/java/com/iflytop/digester/DigestionTaskThread.java b/src/main/java/com/iflytop/digester/DigestionTaskThread.java index 18b8a43..8cbc01a 100644 --- a/src/main/java/com/iflytop/digester/DigestionTaskThread.java +++ b/src/main/java/com/iflytop/digester/DigestionTaskThread.java @@ -162,8 +162,25 @@ public class DigestionTaskThread extends Thread { device.transferArm.moveTubeRackToLiquidPlate(this.heatingSlot.index); // 拍照检查异常试管 this.takeShotAndCheckErrorTubes(); + + // 申请异常处理位 + var errorSlot = device.heatingTurntable.getErrorSlot(); + var emptyTubeIndexes = errorSlot.getEmptyTubeIndexes(this.errorTubeIndexes.size()); + var errorTubeIndexes = this.errorTubeIndexes; + if ( emptyTubeIndexes.size() < this.errorTubeIndexes.size() ) { + errorTubeIndexes = this.errorTubeIndexes.subList(0, emptyTubeIndexes.size()); + } + // 将异常试管放入异常处理区域 - device.transferArm.moveTubesToErrorSlot(this.errorTubeIndexes); + device.transferArm.moveTubesToErrorSlot(errorTubeIndexes, emptyTubeIndexes); + + // 更新试管状态 + for ( var tubeIndex : errorTubeIndexes ) { + var tube = this.heatingSlot.tubes.get(tubeIndex); + errorSlot.tubes.set(emptyTubeIndexes.get(0), tube); + this.heatingSlot.tubes.set(tubeIndex, null); + } + // 将正常试管放入加热转盘 device.transferArm.moveTubeRackToHeatingTurntable(this.heatingSlot.index); } diff --git a/src/main/java/com/iflytop/digester/deviceinstance/HeatingTurntableInstance.java b/src/main/java/com/iflytop/digester/deviceinstance/HeatingTurntableInstance.java index ceb6ad5..2d1a646 100644 --- a/src/main/java/com/iflytop/digester/deviceinstance/HeatingTurntableInstance.java +++ b/src/main/java/com/iflytop/digester/deviceinstance/HeatingTurntableInstance.java @@ -1,12 +1,15 @@ package com.iflytop.digester.deviceinstance; import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; import java.util.TimerTask; - @Component public class HeatingTurntableInstance { + @Value("${app.errorSlotIndex}") + private Integer errorSlotIndex; + // 槽位列表 private final List slots = new ArrayList<>(); @@ -15,6 +18,9 @@ public class HeatingTurntableInstance { for (int i = 0; i < 5; i++) { HeatingTurntableSlot slot = new HeatingTurntableSlot(); slot.index = i; + if (i == errorSlotIndex) { + slot.isErrorSlot = true; + } slots.add(slot); } } @@ -24,6 +30,11 @@ public class HeatingTurntableInstance { return slots; } + // 获取异常槽位 + public HeatingTurntableSlot getErrorSlot() { + return slots.get(errorSlotIndex); + } + // 根据索引获取槽位 public HeatingTurntableSlot getSlotByIndex(Integer index) { return slots.get(index); diff --git a/src/main/java/com/iflytop/digester/deviceinstance/HeatingTurntableSlot.java b/src/main/java/com/iflytop/digester/deviceinstance/HeatingTurntableSlot.java index a05638e..2e5f3a8 100644 --- a/src/main/java/com/iflytop/digester/deviceinstance/HeatingTurntableSlot.java +++ b/src/main/java/com/iflytop/digester/deviceinstance/HeatingTurntableSlot.java @@ -23,6 +23,8 @@ public class HeatingTurntableSlot { public List tubes = new ArrayList<>(); // 试管架位置 public String tubeRackLocation = null; // heating-turntable:加热转盘 liquid-plate :加液盘 + // 是否异常槽位 + public Boolean isErrorSlot = false; // Constructor public HeatingTurntableSlot() { @@ -43,6 +45,17 @@ public class HeatingTurntableSlot { } } + // 获取空试管索引列表 + public List getEmptyTubeIndexes( Integer limit ) { + List indexes = new ArrayList<>(); + for (int i = 0; i < tubes.size(); i++) { + if ( null == tubes.get(i) && indexes.size() < limit ) { + indexes.add(i); + } + } + return indexes; + } + // 获取非空试管索引列表 public List getExistTubeIndexes() { List indexes = new ArrayList<>(); @@ -60,8 +73,11 @@ public class HeatingTurntableSlot { this.heatingDuration = duration; this.heatingStatus = "on"; - var snippetName = "HeatingTurntableSlotHeating.Start." + this.index; - Map snippetParams = Map.of("temperature", temperature, "duration", duration); + var snippetName = "HeatingTurntableSlotHeating." + this.index; + Map snippetParams = Map.of( + "temperature", temperature, + "duration", duration * 60 * 1000 + ); UfCmdSnippetExecutor.execute(snippetName, snippetParams); // 加热完成 diff --git a/src/main/java/com/iflytop/digester/deviceinstance/TransferRobotArmInstance.java b/src/main/java/com/iflytop/digester/deviceinstance/TransferRobotArmInstance.java index 4c5d01c..130235b 100644 --- a/src/main/java/com/iflytop/digester/deviceinstance/TransferRobotArmInstance.java +++ b/src/main/java/com/iflytop/digester/deviceinstance/TransferRobotArmInstance.java @@ -1,7 +1,9 @@ package com.iflytop.digester.deviceinstance; import com.iflytop.digester.underframework.UfCmdSnippetExecutor; +import com.iflytop.digester.underframework.dao.model.UfMdbOption; import org.springframework.stereotype.Component; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -16,37 +18,33 @@ public class TransferRobotArmInstance { this.lockArm(lock); // 执行从异常槽位取出试管 - UfCmdSnippetExecutor.execute("TubeTakeOutFromErrorSlotStart"); for (Integer tubeIndex : tubeIndexes) { - UfCmdSnippetExecutor.execute("TubeTakeOutFromErrorSlot", Map.of( - "MotorLiquidPlatePos", 100, - "MotorArmLeftRightSrcPos", 100, - "MotorArmLeftRightDstPos", 100, - "MotorHeatingTurntablePos", 100 - )); + var snippetKey = "TubeTakeOutFromErrorSlot"; + var snippetParams = new HashMap(); + snippetParams.put("LiquidPlatePos", UfMdbOption.getInteger("LiquidPlateTube." + tubeIndex, 0)); + snippetParams.put("TransLrSrcPos", UfMdbOption.getInteger("HeatPlateErrorSlotTube." + tubeIndex, 0)); + snippetParams.put("HeatingPlatePos", UfMdbOption.getInteger("TransLrMotorLiquidPlateTube." + tubeIndex, 0)); + snippetParams.put("TransLrDestPos", UfMdbOption.getInteger("TransLrMotorHeatingPlateTube." + tubeIndex, 0)); + UfCmdSnippetExecutor.execute(snippetKey, snippetParams); } - UfCmdSnippetExecutor.execute("TubeTakeOutFromErrorSlotEnd"); - this.unlockArm(lock); } // 移动试管到异常槽位 - public void moveTubesToErrorSlot(List tubeIndexes) { + public void moveTubesToErrorSlot(List srcIndexes, List destIndexes) { var lock = new Object(); this.lockArm(lock); // 执行移动试管到异常槽位 - UfCmdSnippetExecutor.execute("TubeMoveToErrorSlotStart"); - for (Integer tubeIndex : tubeIndexes) { - UfCmdSnippetExecutor.execute("TubeMoveToErrorSlot", Map.of( - "MotorLiquidPlatePos", 100, - "MotorArmLeftRightSrcPos", 100, - "MotorArmLeftRightDstPos", 100, - "MotorHeatingTurntablePos", 100 - )); + for (Integer tubeIndex : srcIndexes) { + String snippetKey = "TubeMoveToErrorSlot"; + Map snippetParams = new HashMap<>(); + snippetParams.put("LiquidPlatePos", UfMdbOption.getInteger("LiquidPlateTube." + tubeIndex, 0)); + snippetParams.put("TransLrSrcPos", UfMdbOption.getInteger("TransLrMotorLiquidPlateTube." + tubeIndex, 0)); + snippetParams.put("HeatingPlatePos", UfMdbOption.getInteger("HeatPlateErrorSlotTube." + tubeIndex, 0)); + snippetParams.put("TransLrDestPos", UfMdbOption.getInteger("TransLrMotorHeatingPlateTube." + tubeIndex, 0)); + UfCmdSnippetExecutor.execute(snippetKey, snippetParams); } - UfCmdSnippetExecutor.execute("TubeMoveToErrorSlotEnd"); - this.unlockArm(lock); } diff --git a/src/main/java/com/iflytop/digester/model/MdbDigestionTaskLog.java b/src/main/java/com/iflytop/digester/model/MdbDigestionTaskLog.java new file mode 100644 index 0000000..845c65f --- /dev/null +++ b/src/main/java/com/iflytop/digester/model/MdbDigestionTaskLog.java @@ -0,0 +1,31 @@ +package com.iflytop.digester.model; +import com.iflytop.digester.underframework.dao.record.UfActiveRecord; +import com.iflytop.digester.underframework.dao.record.UfActiveRecordField; +public class MdbDigestionTaskLog extends UfActiveRecord { + @UfActiveRecordField + public String taskId; + + @UfActiveRecordField + public String action; + + @UfActiveRecordField + public String content; + + @UfActiveRecordField + public Long timestamp; + + // get table name + public static String getTableName() { + return "app_digestion_task_logs"; + } + + // log + public static void log(String taskId, String action, String content) { + var log = new MdbDigestionTaskLog(); + log.taskId = taskId; + log.action = action; + log.content = content; + log.timestamp = System.currentTimeMillis(); + log.save(); + } +} diff --git a/src/main/java/com/iflytop/digester/underframework/UfCmdSnippetExecutor.java b/src/main/java/com/iflytop/digester/underframework/UfCmdSnippetExecutor.java index 00719d9..a1a0d9c 100644 --- a/src/main/java/com/iflytop/digester/underframework/UfCmdSnippetExecutor.java +++ b/src/main/java/com/iflytop/digester/underframework/UfCmdSnippetExecutor.java @@ -2,6 +2,8 @@ package com.iflytop.digester.underframework; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.iflytop.digester.underframework.command.UfCommand; +import com.iflytop.digester.underframework.util.UfClassHelper; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateException; @@ -14,6 +16,7 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.io.StringWriter; public class UfCmdSnippetExecutor { @@ -79,15 +82,36 @@ public class UfCmdSnippetExecutor { public void executeStep(String cmdId, String cmdParams) { var cmd = UfActiveRecord.findOne(UfMdbActuatorCmd.class, cmdId); if (null == cmd) { + this.executeBuildInCmd(cmdId, cmdParams); + return; + } + + cmd.parameters = this.compileParameterString(cmdParams); + UfActuatorCmdExecutor.execute(cmd); + } + + // execute build-in command + private void executeBuildInCmd( String cmdId, String cmdParams ) { + var buildInCmdClassName = this.getClass().getPackageName() + ".command.UfCmd" + cmdId; + UfCommand buildInCmd = null; + try { + buildInCmd = (UfCommand)UfClassHelper.newInstance(buildInCmdClassName); + } catch ( Exception e ) { throw new RuntimeException("无效的指令ID : " + cmdId); } - // 编译参数为模板 + cmdParams = this.compileParameterString(cmdParams); + List args = List.of(cmdParams.split(",")); + buildInCmd.execute(args); + } + + // 编译参数字符串 + private String compileParameterString( String paramText ) { freemarker.template.Configuration cfg = new Configuration(Configuration.VERSION_2_3_31); cfg.setNumberFormat("0"); Template template = null; try { - template = new Template("", cmdParams, cfg); + template = new Template("", paramText, cfg); } catch (IOException e) { throw new RuntimeException(e); } @@ -97,7 +121,6 @@ public class UfCmdSnippetExecutor { } catch (TemplateException | IOException e) { throw new RuntimeException(e); } - cmd.parameters = paramStrWriter.toString(); - UfActuatorCmdExecutor.execute(cmd); + return paramStrWriter.toString(); } } diff --git a/src/main/java/com/iflytop/digester/underframework/resourcecontrol/systemcommand/TsCmdDelay.java b/src/main/java/com/iflytop/digester/underframework/command/UfCmdDelay.java similarity index 73% rename from src/main/java/com/iflytop/digester/underframework/resourcecontrol/systemcommand/TsCmdDelay.java rename to src/main/java/com/iflytop/digester/underframework/command/UfCmdDelay.java index 7424340..53c7c6c 100644 --- a/src/main/java/com/iflytop/digester/underframework/resourcecontrol/systemcommand/TsCmdDelay.java +++ b/src/main/java/com/iflytop/digester/underframework/command/UfCmdDelay.java @@ -1,6 +1,6 @@ -package com.iflytop.digester.underframework.resourcecontrol.systemcommand; +package com.iflytop.digester.underframework.command; import java.util.List; -public class TsCmdDelay implements TsCommand { +public class UfCmdDelay implements UfCommand { @Override public void execute( List args ) { // get delay time diff --git a/src/main/java/com/iflytop/digester/underframework/command/UfCommand.java b/src/main/java/com/iflytop/digester/underframework/command/UfCommand.java new file mode 100644 index 0000000..9417462 --- /dev/null +++ b/src/main/java/com/iflytop/digester/underframework/command/UfCommand.java @@ -0,0 +1,6 @@ +package com.iflytop.digester.underframework.command; +import java.util.List; +public interface UfCommand { + // execute + void execute( List args ); +} diff --git a/src/main/java/com/iflytop/digester/underframework/connection/UfModbusRTUOverTCP.java b/src/main/java/com/iflytop/digester/underframework/connection/UfModbusRTUOverTCP.java index bb2402e..310c93e 100644 --- a/src/main/java/com/iflytop/digester/underframework/connection/UfModbusRTUOverTCP.java +++ b/src/main/java/com/iflytop/digester/underframework/connection/UfModbusRTUOverTCP.java @@ -38,14 +38,14 @@ public class UfModbusRTUOverTCP extends UfConnectionBase { synchronized public String execute(UfMdbActuatorCmd command) { if ( "03".equals(command.cmdId) ) { Integer slaveId = Integer.parseInt(command.cmdFlags); - Integer address = Integer.parseInt(command.parameters); + Integer address = Integer.parseInt(command.parameters.trim()); Integer value = this.readHoldingRegister(slaveId, address); return value.toString(); } else if ( "06".equals(command.cmdId) ) { Integer slaveId = Integer.parseInt(command.cmdFlags); String[] params = command.parameters.split(","); - Integer address = Integer.parseInt(params[0]); - Integer value = Integer.parseInt(params[1]); + Integer address = Integer.parseInt(params[0].trim()); + Integer value = Integer.parseInt(params[1].trim()); this.writeHoldingRegister(slaveId, address, value); return ""; } else { diff --git a/src/main/java/com/iflytop/digester/underframework/resourcecontrol/systemcommand/TsCommand.java b/src/main/java/com/iflytop/digester/underframework/resourcecontrol/systemcommand/TsCommand.java deleted file mode 100644 index 9eb36f6..0000000 --- a/src/main/java/com/iflytop/digester/underframework/resourcecontrol/systemcommand/TsCommand.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.iflytop.digester.underframework.resourcecontrol.systemcommand; -import java.util.List; -public interface TsCommand { - // execute - void execute( List args ); -} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index b2c3a9c..06258ed 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -13,16 +13,16 @@ device: connections: - name : zcancmder key : zcancmder - enable : false + enable : true type : ZcancmderWebsocket -# uri: ws://192.168.8.10:19005 - uri : ws://127.0.0.1:19005 + uri: ws://192.168.8.10:19005 +# uri : ws://127.0.0.1:19005 - name : modbus key : modbus enable : true type : ModbusRTUOverTCP - host: 127.0.0.1 - port: 502 + host: 192.168.8.10 + port: 20000 mqtt-broker: uri: tcp://broker.emqx.io:1883 diff --git a/web b/web index 1513f7b..4641118 160000 --- a/web +++ b/web @@ -1 +1 @@ -Subproject commit 1513f7b9d26f452c6ded8c1d7fe43c047fab162a +Subproject commit 464111807ce66ce1d0f0652455cc17689d12ab7e