11 changed files with 347 additions and 12 deletions
-
21src/main/java/a8k/app/hardware/driver/PipetteCtrlDriverV2.java
-
13src/main/java/a8k/app/hardware/type/A8kPacket.java
-
1src/main/java/a8k/app/hardware/type/CmdId.java
-
1src/main/java/a8k/app/hardware/type/pipette_module/cfg/PipetteCommonConfigIndex.java
-
10src/main/java/a8k/app/hardware/type/pipette_module/cfgbean/PipetteCommonConfig.java
-
84src/main/java/a8k/app/service/module/TurntableAndOptScannerCtrlModule.java
-
64src/main/java/a8k/app/service/statemgr/ReactionPlateContainerStateMgr.java
-
100src/main/java/a8k/app/utils/ZWorkThread.java
-
7src/main/java/a8k/extui/page/driver/pipette_module/PipetteGunCommonConfigPage.java
-
36src/main/java/a8k/extui/page/driver/pipette_module/PipetteGunOperationCtrlPage.java
-
22src/main/java/a8k/extui/page/driver/pipette_module/PipetteGunTestCtrlPage.java
@ -0,0 +1,84 @@ |
|||||
|
package a8k.app.service.module; |
||||
|
|
||||
|
import a8k.app.service.data.ReactionRecordMgrService; |
||||
|
import a8k.app.service.lowerctrl.OptScanModuleCtrlService; |
||||
|
import a8k.app.service.statemgr.IncubationPlateStateMgrService; |
||||
|
import a8k.app.service.statemgr.OptScanModuleStateMgrService; |
||||
|
import a8k.app.teststate.VirtualDevice; |
||||
|
import a8k.app.utils.ZWorkThread; |
||||
|
import jakarta.annotation.PostConstruct; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
@Component |
||||
|
@Slf4j |
||||
|
@RequiredArgsConstructor |
||||
|
public class TurntableAndOptScannerCtrlModule { |
||||
|
public enum State { |
||||
|
idle, |
||||
|
starting, |
||||
|
working, |
||||
|
stopping, |
||||
|
} |
||||
|
|
||||
|
// |
||||
|
//状态 |
||||
|
// |
||||
|
private final VirtualDevice virtualDevice; |
||||
|
private final IncubationPlateStateMgrService incubationPlateStateMgrService; |
||||
|
private final OptScanModuleStateMgrService optScanModuleStateMgrService; |
||||
|
// |
||||
|
// 数据库 |
||||
|
// |
||||
|
private final ReactionRecordMgrService reactionRecordMgrService; |
||||
|
// |
||||
|
// 控制 |
||||
|
// |
||||
|
private final OptScanModuleCtrlService optScanModuleCtrlService; |
||||
|
|
||||
|
|
||||
|
private ZWorkThread workThread; |
||||
|
private State state = State.idle; |
||||
|
private final Object stateLock = new Object(); |
||||
|
private final Object turntableLock = new Object(); |
||||
|
|
||||
|
@PostConstruct |
||||
|
public void init() { |
||||
|
state = State.idle; |
||||
|
workThread = new ZWorkThread(); |
||||
|
workThread.init("TurntableAndOptScannerCtrlModule-WorkThread"); |
||||
|
} |
||||
|
|
||||
|
public void workThreadFn() { |
||||
|
// while (!workThread.isStopping()) { |
||||
|
// } |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// |
||||
|
// utils |
||||
|
// |
||||
|
private void setState(State state) { |
||||
|
synchronized (stateLock) { |
||||
|
this.state = state; |
||||
|
log.info("TurntableAndOptScannerCtrlModule state changed to {}", state); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private State getState() { |
||||
|
synchronized (stateLock) { |
||||
|
return state; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void waitForState(State state) { |
||||
|
while (!getState().equals(state)) { |
||||
|
try { |
||||
|
Thread.sleep(50); |
||||
|
} catch (InterruptedException ignored) { |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,64 @@ |
|||||
|
package a8k.app.service.statemgr; |
||||
|
|
||||
|
import a8k.app.constant.AppConstant; |
||||
|
import a8k.app.type.a8k.ConsumableGroup; |
||||
|
import a8k.app.type.a8k.container.ReactionPlateContainer; |
||||
|
import jakarta.annotation.PostConstruct; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
@Component |
||||
|
@Slf4j |
||||
|
@RequiredArgsConstructor |
||||
|
public class ReactionPlateContainerStateMgr { |
||||
|
|
||||
|
public ReactionPlateContainer[] reactionPlateGroup = new ReactionPlateContainer[AppConstant.CONSUMABLE_CHANNEL_NUM]; |
||||
|
|
||||
|
@PostConstruct |
||||
|
void init() { |
||||
|
for (int i = 0; i < reactionPlateGroup.length; i++) { |
||||
|
reactionPlateGroup[i] = new ReactionPlateContainer(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
synchronized void installReactionPlateContainer(ConsumableGroup group, Integer projId, String projName, String projShortName, String lotId, String color, Integer num) { |
||||
|
ReactionPlateContainer reactionPlateContainer = reactionPlateGroup[group.off]; |
||||
|
reactionPlateContainer.projId = projId; |
||||
|
reactionPlateContainer.projName = projName; |
||||
|
reactionPlateContainer.projShortName = projShortName; |
||||
|
reactionPlateContainer.lotId = lotId; |
||||
|
reactionPlateContainer.color = color; |
||||
|
reactionPlateContainer.num = num; |
||||
|
reactionPlateContainer.isInstall = true; |
||||
|
reactionPlateContainer.reserveNum = 0; |
||||
|
} |
||||
|
|
||||
|
synchronized void setReactionPlateNum(ConsumableGroup group, Integer num) { |
||||
|
log.debug("setReactionPlateNum: group={}, num={}", group, num); |
||||
|
ReactionPlateContainer reactionPlateContainer = reactionPlateGroup[group.off]; |
||||
|
if (reactionPlateContainer.isInstall) { |
||||
|
reactionPlateContainer.num = num; |
||||
|
} else { |
||||
|
log.warn("setReactionPlateNum: Reaction plate container is not installed for group: {}", group); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
synchronized Boolean popOneReactionPlate(ConsumableGroup group) { |
||||
|
log.debug("popOneReactionPlate: group={}", group); |
||||
|
ReactionPlateContainer reactionPlateContainer = reactionPlateGroup[group.off]; |
||||
|
if (reactionPlateContainer.isInstall && reactionPlateContainer.num > 0) { |
||||
|
reactionPlateContainer.num--; |
||||
|
return true; |
||||
|
} else { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
synchronized void uninstallReactionPlateContainer(ConsumableGroup group) { |
||||
|
log.debug("uninstallReactionPlateContainer: group={}", group); |
||||
|
ReactionPlateContainer reactionPlateContainer = reactionPlateGroup[group.off]; |
||||
|
reactionPlateContainer.reset(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,100 @@ |
|||||
|
package a8k.app.utils; |
||||
|
|
||||
|
import org.springframework.util.Assert; |
||||
|
|
||||
|
import java.util.concurrent.atomic.AtomicBoolean; |
||||
|
|
||||
|
public class ZWorkThread { |
||||
|
Thread thread; |
||||
|
Runnable pendingRunnable; |
||||
|
AtomicBoolean workingFlag = new AtomicBoolean(false); |
||||
|
AtomicBoolean stopPendingFlag = new AtomicBoolean(false); |
||||
|
|
||||
|
|
||||
|
public void init(String name) { |
||||
|
thread = new Thread(this::workThread); |
||||
|
thread.setName(name); |
||||
|
thread.start(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
synchronized public void start(Runnable runnable) { |
||||
|
Assert.isTrue(thread != null, "Thread not initialized. Call init() first."); |
||||
|
|
||||
|
if (workingFlag.get()) { |
||||
|
stop(); |
||||
|
} |
||||
|
pendingRunnable = runnable; |
||||
|
} |
||||
|
|
||||
|
synchronized public void stop() { |
||||
|
Assert.isTrue(thread != null, "Thread not initialized. Call init() first."); |
||||
|
|
||||
|
stopPendingFlag.set(true); |
||||
|
while (workingFlag.get()) { |
||||
|
threadSleep(50); |
||||
|
} |
||||
|
stopPendingFlag.set(false); |
||||
|
} |
||||
|
|
||||
|
public Boolean isStopping() { |
||||
|
return stopPendingFlag.get(); |
||||
|
} |
||||
|
|
||||
|
public Boolean isWorking() { |
||||
|
return workingFlag.get(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
private void workThread() { |
||||
|
while (true) { |
||||
|
Runnable runnable = null; |
||||
|
if (pendingRunnable != null) { |
||||
|
runnable = pendingRunnable; |
||||
|
pendingRunnable = null; |
||||
|
} |
||||
|
|
||||
|
if (runnable == null) { |
||||
|
threadSleep(50); |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
workingFlag.set(true); |
||||
|
runnable.run(); |
||||
|
workingFlag.set(false); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public void threadSleep(int ms) { |
||||
|
try { |
||||
|
Thread.sleep(ms); |
||||
|
} catch (InterruptedException ignored) { |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static void main(String[] args) throws InterruptedException { |
||||
|
ZWorkThread workThread = new ZWorkThread(); |
||||
|
workThread.init("TestWorkThread"); |
||||
|
|
||||
|
workThread.start(() -> { |
||||
|
while (!workThread.isStopping()) { |
||||
|
System.out.println("Task 1 is running"); |
||||
|
workThread.threadSleep(100); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
Thread.sleep(3000); |
||||
|
workThread.stop(); |
||||
|
|
||||
|
workThread.start(() -> { |
||||
|
while (!workThread.isStopping()) { |
||||
|
System.out.println("Task 2 is running"); |
||||
|
workThread.threadSleep(100); |
||||
|
} |
||||
|
}); |
||||
|
Thread.sleep(3000); |
||||
|
workThread.stop(); |
||||
|
} |
||||
|
|
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue