feat(ft546): adapt lift conveyor and charger protocols
This commit is contained in:
@@ -0,0 +1,309 @@
|
||||
package com.ruoyi.project.ft546.device;
|
||||
|
||||
import com.ruoyi.common.domain.PlcConnectInfo;
|
||||
import com.ruoyi.device.cdz.domain.DeviceCdz;
|
||||
import com.ruoyi.device.srgv.domain.DeviceSrgv;
|
||||
import com.ruoyi.device.ssj.domain.DeviceSsj;
|
||||
import com.ruoyi.device.tsj.domain.DeviceTsj;
|
||||
import com.ruoyi.project.ft546.config.Ft546DeviceProfile;
|
||||
import com.ruoyi.project.ft546.device.charger.Ft546ChargerAdapter;
|
||||
import com.ruoyi.project.ft546.device.conveyor.Ft546ConveyorAdapter;
|
||||
import com.ruoyi.project.ft546.device.lift.Ft546LiftAdapter;
|
||||
import com.ruoyi.schedule.plcRead.cdz.PlcCdzRead;
|
||||
import com.ruoyi.schedule.plcRead.ssj.PlcSsjRead;
|
||||
import com.ruoyi.schedule.plcRead.srgv.PlcSrgvRead;
|
||||
import com.ruoyi.schedule.plcRead.tsj.PlcTsjRead;
|
||||
import com.ruoyi.wcs.application.device.DeviceCommandEnvelope;
|
||||
import com.ruoyi.wcs.application.device.SendReceipt;
|
||||
import com.ruoyi.wcs.domain.execution.DeviceCommandType;
|
||||
import com.ruoyi.wcs.domain.execution.DeviceSnapshot;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
|
||||
class Ft546EquipmentAdaptersTest {
|
||||
private static final Instant NOW = Instant.parse("2026-08-11T05:00:00Z");
|
||||
|
||||
@Test
|
||||
void liftAdapterReportsFactsWithoutReleasingCoreResources() {
|
||||
DeviceTsj device = liftDevice();
|
||||
device.setDqcs(3);
|
||||
device.setJdbz(2);
|
||||
device.setHczt(1);
|
||||
device.setYxxf(1);
|
||||
device.setSbzt(2);
|
||||
device.setCzfs(2);
|
||||
device.setRwh(30001L);
|
||||
FakeTsjRead runtime = new FakeTsjRead("1", device);
|
||||
Ft546LiftAdapter adapter = new Ft546LiftAdapter(runtime,
|
||||
profile("1", "LIFT", "LIFT_TRANSFER", "DB602.0"),
|
||||
(deviceId, commandId) -> false, contextProvider());
|
||||
|
||||
DeviceSnapshot snapshot = adapter.readSnapshot("1");
|
||||
|
||||
assertEquals("3", snapshot.getFacts().get("floor"));
|
||||
assertEquals("OPEN", snapshot.getFacts().get("doorState"));
|
||||
assertEquals("READY", snapshot.getFacts().get("portState"));
|
||||
assertEquals("1", snapshot.getFacts().get("load"));
|
||||
assertEquals("2", snapshot.getFacts().get("status"));
|
||||
assertEquals("2", snapshot.getFacts().get("mode"));
|
||||
assertFalse(snapshot.getFacts().containsKey("releaseLiftResource"));
|
||||
assertFalse(snapshot.hasOrderedDeviceSequence());
|
||||
assertFalse(snapshot.hasDeviceBootSession());
|
||||
assertFalse(snapshot.isCommandCompleted());
|
||||
}
|
||||
|
||||
@Test
|
||||
void liftCommandKeepsTheLegacyByteLayout() {
|
||||
FakeTsjRead runtime = new FakeTsjRead("1", liftDevice());
|
||||
Ft546LiftAdapter adapter = new Ft546LiftAdapter(runtime,
|
||||
profile("1", "LIFT", "LIFT_TRANSFER", "DB602.0"),
|
||||
(deviceId, commandId) -> false, contextProvider());
|
||||
|
||||
SendReceipt receipt = adapter.send(envelope("LIFT-1", "1",
|
||||
DeviceCommandType.LIFT_TRANSFER,
|
||||
"taskNo=30001|taskType=1|sourceFloor=3|targetFloor=7"));
|
||||
|
||||
assertEquals(SendReceipt.Status.ACCEPTED, receipt.getStatus());
|
||||
assertEquals("DB602.0", runtime.addresses.get(0));
|
||||
assertArrayEquals(new byte[] {0, 0, 117, 49, 0, 1, 0, 3,
|
||||
0, 7, 0, 1, 0, (byte) 178}, runtime.payloads.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void conveyorAdapterMapsGoodsModeAndReadyFactsAndLegacyCommand() {
|
||||
DeviceSsj device = conveyorDevice();
|
||||
device.setTaskNo(30002);
|
||||
device.setDw(1);
|
||||
device.setKx(1);
|
||||
device.setModel(1);
|
||||
device.setTrayCode("TRAY-001");
|
||||
device.setErrorCode(0);
|
||||
FakeSsjRead runtime = new FakeSsjRead("GS1002", device);
|
||||
Ft546ConveyorAdapter adapter = new Ft546ConveyorAdapter(runtime,
|
||||
profile("GS1002", "CONVEYOR", "CONVEYOR_TRANSFER",
|
||||
"DB41.0"),
|
||||
(deviceId, commandId) -> false, contextProvider());
|
||||
|
||||
DeviceSnapshot snapshot = adapter.readSnapshot("GS1002");
|
||||
SendReceipt receipt = adapter.send(envelope("CONV-1", "GS1002",
|
||||
DeviceCommandType.CONVEYOR_TRANSFER,
|
||||
"taskNo=30001|destination=2|completed=1"));
|
||||
|
||||
assertEquals("PRESENT", snapshot.getFacts().get("goodsState"));
|
||||
assertEquals("1", snapshot.getFacts().get("mode"));
|
||||
assertEquals("READY", snapshot.getFacts().get("readyState"));
|
||||
assertEquals("TRAY-001", snapshot.getFacts().get("trayCode"));
|
||||
assertEquals("0", snapshot.getFacts().get("faultCode"));
|
||||
assertEquals(SendReceipt.Status.ACCEPTED, receipt.getStatus());
|
||||
assertEquals("DB41.0", runtime.addresses.get(0));
|
||||
assertArrayEquals(new byte[] {0, 0, 117, 49, 0, 2, 0, 1,
|
||||
0, 0, 0, (byte) 169}, runtime.payloads.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void chargerAdapterMapsOccupancyChargingAndFaultFactsAndLegacyCommand() {
|
||||
DeviceCdz device = chargerDevice();
|
||||
device.setRwh(30003);
|
||||
device.setStatus(1);
|
||||
device.setModel(1);
|
||||
device.setSpare1("1");
|
||||
FakeCdzRead runtime = new FakeCdzRead("1", device);
|
||||
Ft546ChargerAdapter adapter = new Ft546ChargerAdapter(runtime,
|
||||
profile("1", "CHARGER", "CHARGE", "DB701.0"),
|
||||
(deviceId, commandId) -> false, contextProvider());
|
||||
|
||||
DeviceSnapshot snapshot = adapter.readSnapshot("1");
|
||||
SendReceipt receipt = adapter.send(envelope("CHARGE-1", "1",
|
||||
DeviceCommandType.CHARGE, "enabled=1"));
|
||||
|
||||
assertEquals("OCCUPIED", snapshot.getFacts().get("occupancy"));
|
||||
assertEquals("ON", snapshot.getFacts().get("charging"));
|
||||
assertEquals("UNAVAILABLE", snapshot.getFacts().get("fault"));
|
||||
assertEquals("070501", snapshot.getFacts().get("site"));
|
||||
assertEquals(SendReceipt.Status.ACCEPTED, receipt.getStatus());
|
||||
assertEquals("DB701.0", runtime.addresses.get(0));
|
||||
assertArrayEquals(new byte[] {0, 1}, runtime.payloads.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void attemptedEquipmentWriteFailureIsUnknownAndBlocksRetry() {
|
||||
FakeCdzRead runtime = new FakeCdzRead("1", chargerDevice());
|
||||
runtime.writeResult = 0;
|
||||
Ft546ChargerAdapter adapter = new Ft546ChargerAdapter(runtime,
|
||||
profile("1", "CHARGER", "CHARGE", "DB701.0"),
|
||||
(deviceId, commandId) -> false, contextProvider());
|
||||
|
||||
SendReceipt first = adapter.send(envelope("CHARGE-1", "1",
|
||||
DeviceCommandType.CHARGE, "enabled=1"));
|
||||
SendReceipt retry = adapter.send(envelope("CHARGE-2", "1",
|
||||
DeviceCommandType.CHARGE, "enabled=1"));
|
||||
|
||||
assertEquals(SendReceipt.Status.ACCEPTED_UNKNOWN,
|
||||
first.getStatus());
|
||||
assertEquals(SendReceipt.Status.REJECTED, retry.getStatus());
|
||||
assertEquals(1, runtime.payloads.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void factReadersPublishAConnectionDropAfterTheyWereConnected() {
|
||||
FakeTsjRead lift = new FakeTsjRead("1", liftDevice());
|
||||
FakeSsjRead conveyor = new FakeSsjRead("GS1002", conveyorDevice());
|
||||
FakeCdzRead charger = new FakeCdzRead("1", chargerDevice());
|
||||
DeviceSrgv shuttleDevice = new DeviceSrgv();
|
||||
shuttleDevice.setDeviceId(1);
|
||||
PlcSrgvRead shuttle = new PlcSrgvRead(shuttleDevice,
|
||||
new PlcConnectInfo("127.0.0.1", 502));
|
||||
shuttle.setIsConnect(true);
|
||||
|
||||
lift.setIsConnect(false);
|
||||
conveyor.setIsConnect(false);
|
||||
charger.setIsConnect(false);
|
||||
shuttle.setIsConnect(false);
|
||||
|
||||
assertFalse(lift.IsConnect());
|
||||
assertFalse(conveyor.IsConnect());
|
||||
assertFalse(charger.IsConnect());
|
||||
assertFalse(shuttle.IsConnect());
|
||||
}
|
||||
|
||||
private static Ft546DeviceProfile profile(String deviceId,
|
||||
String deviceType,
|
||||
String capability,
|
||||
String commandAddress) {
|
||||
Ft546DeviceProfile profile = new Ft546DeviceProfile();
|
||||
profile.setDeviceId(deviceId);
|
||||
profile.setDeviceType(deviceType);
|
||||
profile.setAdapterType("FT546_LEGACY_" + deviceType);
|
||||
profile.setProfileVersion(1L);
|
||||
profile.getCapabilities().add(capability);
|
||||
profile.getRegisters().put("command", commandAddress);
|
||||
return profile;
|
||||
}
|
||||
|
||||
private static Ft546SnapshotContextProvider contextProvider() {
|
||||
return deviceId -> new Ft546SnapshotContext("SNAP-" + deviceId,
|
||||
"KEY-" + deviceId, 9L, 17L, NOW);
|
||||
}
|
||||
|
||||
private static DeviceCommandEnvelope envelope(String commandId,
|
||||
String deviceId,
|
||||
DeviceCommandType type,
|
||||
String payload) {
|
||||
return new DeviceCommandEnvelope(commandId, "KEY-" + commandId,
|
||||
deviceId, type, payload, 1, 9L, 17L);
|
||||
}
|
||||
|
||||
private static DeviceTsj liftDevice() {
|
||||
DeviceTsj device = new DeviceTsj();
|
||||
device.setId(1L);
|
||||
return device;
|
||||
}
|
||||
|
||||
private static DeviceSsj conveyorDevice() {
|
||||
DeviceSsj device = new DeviceSsj();
|
||||
device.setDeviceCode("GS1002");
|
||||
return device;
|
||||
}
|
||||
|
||||
private static DeviceCdz chargerDevice() {
|
||||
DeviceCdz device = new DeviceCdz();
|
||||
device.setId(1);
|
||||
device.setSiteId("070501");
|
||||
return device;
|
||||
}
|
||||
|
||||
private abstract static class RecordingRuntime {
|
||||
private final List<String> addresses = new ArrayList<String>();
|
||||
private final List<byte[]> payloads = new ArrayList<byte[]>();
|
||||
private int writeResult = 1;
|
||||
|
||||
final int record(String address, byte[] payload) {
|
||||
addresses.add(address);
|
||||
payloads.add(payload.clone());
|
||||
return writeResult;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class FakeTsjRead extends PlcTsjRead {
|
||||
private final RecordingRuntime recording = new RecordingRuntime() { };
|
||||
private final List<String> addresses = recording.addresses;
|
||||
private final List<byte[]> payloads = recording.payloads;
|
||||
|
||||
private FakeTsjRead(String id, DeviceTsj device) {
|
||||
super(map(id, device), new PlcConnectInfo("127.0.0.1", 102));
|
||||
setIsConnect(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write(String address, byte[] payload) {
|
||||
return recording.record(address, payload);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class FakeSsjRead extends PlcSsjRead {
|
||||
private final RecordingRuntime recording = new RecordingRuntime() { };
|
||||
private final List<String> addresses = recording.addresses;
|
||||
private final List<byte[]> payloads = recording.payloads;
|
||||
|
||||
private FakeSsjRead(String id, DeviceSsj device) {
|
||||
super(map(id, device), new PlcConnectInfo("127.0.0.1", 102));
|
||||
setIsConnect(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write(String address, byte[] payload) {
|
||||
return recording.record(address, payload);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class FakeCdzRead extends PlcCdzRead {
|
||||
private final List<String> addresses = new ArrayList<String>();
|
||||
private final List<byte[]> payloads = new ArrayList<byte[]>();
|
||||
private int writeResult = 1;
|
||||
|
||||
private FakeCdzRead(String id, DeviceCdz device) {
|
||||
super(map(id, device), new PlcConnectInfo("127.0.0.1", 102));
|
||||
setIsConnect(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write(String address, byte[] payload) {
|
||||
addresses.add(address);
|
||||
payloads.add(payload.clone());
|
||||
return writeResult;
|
||||
}
|
||||
}
|
||||
|
||||
private static ConcurrentHashMap<String, DeviceTsj> map(
|
||||
String id, DeviceTsj device) {
|
||||
ConcurrentHashMap<String, DeviceTsj> map =
|
||||
new ConcurrentHashMap<String, DeviceTsj>();
|
||||
map.put(id, device);
|
||||
return map;
|
||||
}
|
||||
|
||||
private static ConcurrentHashMap<String, DeviceSsj> map(
|
||||
String id, DeviceSsj device) {
|
||||
ConcurrentHashMap<String, DeviceSsj> map =
|
||||
new ConcurrentHashMap<String, DeviceSsj>();
|
||||
map.put(id, device);
|
||||
return map;
|
||||
}
|
||||
|
||||
private static ConcurrentHashMap<String, DeviceCdz> map(
|
||||
String id, DeviceCdz device) {
|
||||
ConcurrentHashMap<String, DeviceCdz> map =
|
||||
new ConcurrentHashMap<String, DeviceCdz>();
|
||||
map.put(id, device);
|
||||
return map;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import com.ruoyi.project.ft546.config.Ft546CapabilityEvidence;
|
||||
import com.ruoyi.project.ft546.config.Ft546CapabilityGate;
|
||||
import com.ruoyi.project.ft546.config.Ft546DeviceProfile;
|
||||
import com.ruoyi.project.ft546.device.Ft546DeviceRuntimeRegistry;
|
||||
import com.ruoyi.project.ft546.device.Ft546MotionCommandGuard;
|
||||
import com.ruoyi.schedule.plcRead.srgv.PlcSrgvRead;
|
||||
import com.ruoyi.wcs.application.device.DeviceCommandEnvelope;
|
||||
import com.ruoyi.wcs.application.device.SendReceipt;
|
||||
@@ -241,7 +242,7 @@ class Ft546LegacySrgvAdapterTest {
|
||||
private static Ft546LegacySrgvAdapter adapter(
|
||||
FakePlcSrgvRead runtime, Ft546DeviceProfile profile,
|
||||
Ft546CapabilityEvidence evidence,
|
||||
Ft546LegacySrgvAdapter.MotionCommandGuard motionGuard) {
|
||||
Ft546MotionCommandGuard motionGuard) {
|
||||
Ft546DeviceRuntimeRegistry registry =
|
||||
new Ft546DeviceRuntimeRegistry();
|
||||
registry.register("1", runtime);
|
||||
|
||||
Reference in New Issue
Block a user