fix(ft546): serialize shuttle reader lifecycle
This commit is contained in:
@@ -38,6 +38,9 @@ public class Ft546SrgvRuntimeFactory {
|
||||
}
|
||||
|
||||
public PlcSrgvRead create(DeviceSrgv device) {
|
||||
if (device == null) {
|
||||
throw new IllegalArgumentException("device identity is required");
|
||||
}
|
||||
Ft546DeviceProfile profile = new Ft546DeviceProfile();
|
||||
profile.setDeviceId(String.valueOf(device.getDeviceId()));
|
||||
profile.setDeviceType("SHUTTLE");
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -66,8 +67,10 @@ public class ScheduleThread {
|
||||
@Autowired
|
||||
private Ft546SrgvRuntimeFactory srgvRuntimeFactory;
|
||||
|
||||
private boolean isRun = false;
|
||||
private boolean isRun2 = false;
|
||||
private volatile boolean isRun = false;
|
||||
private volatile boolean isRun2 = false;
|
||||
|
||||
private static final long THREAD_STOP_TIMEOUT_MS = 5000L;
|
||||
|
||||
/**
|
||||
* 项目初始化启动
|
||||
@@ -171,16 +174,21 @@ public class ScheduleThread {
|
||||
/**
|
||||
* 开始线程
|
||||
*/
|
||||
public void startAllThread() {
|
||||
plcSsjReadCustomThread.getThread().start();
|
||||
for (CustomThread<PlcSrgvRead> reader
|
||||
: plcSrgvReadCustomThreads.values()) {
|
||||
reader.getThread().start();
|
||||
public synchronized void startAllThread() {
|
||||
if (isRun) {
|
||||
return;
|
||||
}
|
||||
List<Thread> readerThreads = requireFactReaderThreads();
|
||||
for (Thread readerThread : readerThreads) {
|
||||
if (readerThread.getState() != Thread.State.NEW) {
|
||||
throw new IllegalStateException(
|
||||
"PLC fact reader cannot be restarted: "
|
||||
+ readerThread.getName());
|
||||
}
|
||||
}
|
||||
for (Thread readerThread : readerThreads) {
|
||||
readerThread.start();
|
||||
}
|
||||
plcTsjReadCustomThread.getThread().start();
|
||||
plcCdzReadCustomThread.getThread().start();
|
||||
|
||||
|
||||
isRun = true;
|
||||
}
|
||||
|
||||
@@ -200,14 +208,18 @@ public class ScheduleThread {
|
||||
/**
|
||||
* 停止线程
|
||||
*/
|
||||
public void stopAllThread() {
|
||||
plcSsjReadCustomThread.runnable.isRun = false;
|
||||
for (CustomThread<PlcSrgvRead> reader
|
||||
: plcSrgvReadCustomThreads.values()) {
|
||||
reader.runnable.isRun = false;
|
||||
public synchronized void stopAllThread() {
|
||||
if (!isRun) {
|
||||
return;
|
||||
}
|
||||
List<Thread> readerThreads = currentFactReaderThreads();
|
||||
signalFactReadersToStop();
|
||||
isRun = false;
|
||||
awaitFactReadersStopped(readerThreads);
|
||||
plcSrgvReadCustomThreads.clear();
|
||||
deviceRuntimeRegistry.clear();
|
||||
if (deviceRuntimeRegistry != null) {
|
||||
deviceRuntimeRegistry.clear();
|
||||
}
|
||||
// plcDdj1ReadCustomThread.runnable.isRun = false;
|
||||
/* plcDdj2ReadCustomThread.runnable.isRun = false;
|
||||
plcDdj3ReadCustomThread.runnable.isRun = false;
|
||||
@@ -215,7 +227,95 @@ public class ScheduleThread {
|
||||
plcDdj5ReadCustomThread.runnable.isRun = false;
|
||||
plcDdj6ReadCustomThread.runnable.isRun = false;*/
|
||||
plcReadInit();
|
||||
isRun = false;
|
||||
}
|
||||
|
||||
private List<Thread> requireFactReaderThreads() {
|
||||
List<Thread> threads = new ArrayList<Thread>();
|
||||
threads.add(requireThread(plcSsjReadCustomThread,
|
||||
"conveyor fact reader"));
|
||||
for (Map.Entry<String, CustomThread<PlcSrgvRead>> entry
|
||||
: plcSrgvReadCustomThreads.entrySet()) {
|
||||
threads.add(requireThread(entry.getValue(),
|
||||
"shuttle fact reader " + entry.getKey()));
|
||||
}
|
||||
threads.add(requireThread(plcTsjReadCustomThread,
|
||||
"lift fact reader"));
|
||||
threads.add(requireThread(plcCdzReadCustomThread,
|
||||
"charger fact reader"));
|
||||
return threads;
|
||||
}
|
||||
|
||||
private List<Thread> currentFactReaderThreads() {
|
||||
List<Thread> threads = new ArrayList<Thread>();
|
||||
addThreadIfPresent(threads, plcSsjReadCustomThread);
|
||||
for (CustomThread<PlcSrgvRead> reader
|
||||
: plcSrgvReadCustomThreads.values()) {
|
||||
addThreadIfPresent(threads, reader);
|
||||
}
|
||||
addThreadIfPresent(threads, plcTsjReadCustomThread);
|
||||
addThreadIfPresent(threads, plcCdzReadCustomThread);
|
||||
return threads;
|
||||
}
|
||||
|
||||
private void signalFactReadersToStop() {
|
||||
if (plcSsjReadCustomThread != null
|
||||
&& plcSsjReadCustomThread.runnable != null) {
|
||||
plcSsjReadCustomThread.runnable.isRun = false;
|
||||
}
|
||||
for (CustomThread<PlcSrgvRead> reader
|
||||
: plcSrgvReadCustomThreads.values()) {
|
||||
if (reader != null && reader.runnable != null) {
|
||||
reader.runnable.isRun = false;
|
||||
}
|
||||
}
|
||||
if (plcTsjReadCustomThread != null
|
||||
&& plcTsjReadCustomThread.runnable != null) {
|
||||
plcTsjReadCustomThread.runnable.isRun = false;
|
||||
}
|
||||
if (plcCdzReadCustomThread != null
|
||||
&& plcCdzReadCustomThread.runnable != null) {
|
||||
plcCdzReadCustomThread.runnable.isRun = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void awaitFactReadersStopped(List<Thread> readerThreads) {
|
||||
long deadline = System.currentTimeMillis() + THREAD_STOP_TIMEOUT_MS;
|
||||
for (Thread readerThread : readerThreads) {
|
||||
long remaining = deadline - System.currentTimeMillis();
|
||||
if (remaining <= 0L) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
readerThread.join(remaining);
|
||||
} catch (InterruptedException interrupted) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new IllegalStateException(
|
||||
"interrupted while stopping PLC fact readers",
|
||||
interrupted);
|
||||
}
|
||||
}
|
||||
for (Thread readerThread : readerThreads) {
|
||||
if (readerThread.isAlive()) {
|
||||
throw new IllegalStateException(
|
||||
"PLC fact reader did not stop: "
|
||||
+ readerThread.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Thread requireThread(CustomThread<?> customThread,
|
||||
String name) {
|
||||
if (customThread == null || customThread.getThread() == null) {
|
||||
throw new IllegalStateException(name + " is not initialized");
|
||||
}
|
||||
return customThread.getThread();
|
||||
}
|
||||
|
||||
private static void addThreadIfPresent(List<Thread> threads,
|
||||
CustomThread<?> customThread) {
|
||||
if (customThread != null && customThread.getThread() != null) {
|
||||
threads.add(customThread.getThread());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package com.ruoyi.project.ft546.device;
|
||||
|
||||
import com.ruoyi.common.domain.PlcConnectInfo;
|
||||
import com.ruoyi.common.domain.CustomThread;
|
||||
import com.ruoyi.device.srgv.domain.DeviceSrgv;
|
||||
import com.ruoyi.project.ft546.config.Ft546DeviceProfile;
|
||||
import com.ruoyi.project.ft546.device.shuttle.Ft546SrgvRuntimeFactory;
|
||||
import com.ruoyi.schedule.ScheduleThread;
|
||||
import com.ruoyi.schedule.plcRead.srgv.PlcSrgvRead;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -14,6 +16,7 @@ import java.nio.file.Paths;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
@@ -55,6 +58,29 @@ class Ft546DeviceRuntimeRegistryTest {
|
||||
() -> new Ft546SrgvRuntimeFactory().create(device, profile));
|
||||
}
|
||||
|
||||
@Test
|
||||
void factoryRejectsMissingDeviceWithTheDeclaredValidationError() {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new Ft546SrgvRuntimeFactory().create(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void startingFactReadersTwiceIsIdempotent() {
|
||||
ScheduleThread schedule = new ScheduleThread();
|
||||
schedule.plcSsjReadCustomThread = noOpThread();
|
||||
schedule.plcTsjReadCustomThread = noOpThread();
|
||||
schedule.plcCdzReadCustomThread = noOpThread();
|
||||
|
||||
schedule.startAllThread();
|
||||
|
||||
assertDoesNotThrow(schedule::startAllThread);
|
||||
}
|
||||
|
||||
@Test
|
||||
void stoppingBeforeInitializationIsANoOp() {
|
||||
assertDoesNotThrow(() -> new ScheduleThread().stopAllThread());
|
||||
}
|
||||
|
||||
@Test
|
||||
void schedulingAndLegacySendPathsDoNotSelectThreeFixedReaders()
|
||||
throws Exception {
|
||||
@@ -75,6 +101,10 @@ class Ft546DeviceRuntimeRegistryTest {
|
||||
new PlcConnectInfo(device.getIp(), device.getPort()));
|
||||
}
|
||||
|
||||
private static <T> CustomThread<T> noOpThread() {
|
||||
return new CustomThread<T>(new Thread(() -> { }), null);
|
||||
}
|
||||
|
||||
private static DeviceSrgv device(String id) {
|
||||
DeviceSrgv device = new DeviceSrgv();
|
||||
device.setDeviceId(Integer.valueOf(id));
|
||||
|
||||
Reference in New Issue
Block a user