feat(ft546): adapt lift conveyor and charger protocols
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
package com.ruoyi.project.ft546.device;
|
||||
|
||||
import com.ruoyi.project.ft546.config.Ft546ConfigurationValidator;
|
||||
import com.ruoyi.project.ft546.config.Ft546DeviceProfile;
|
||||
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 com.ruoyi.wcs.port.out.DeviceCommandGateway;
|
||||
import com.ruoyi.wcs.port.out.DeviceSnapshotGateway;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public abstract class AbstractFt546EquipmentAdapter
|
||||
implements DeviceCommandGateway, DeviceSnapshotGateway {
|
||||
private final Ft546DeviceProfile profile;
|
||||
private final DeviceCommandType commandType;
|
||||
private final Ft546MotionCommandGuard motionCommandGuard;
|
||||
private final Ft546SnapshotContextProvider contextProvider;
|
||||
private final Set<String> locallyUnknownDevices =
|
||||
Collections.newSetFromMap(
|
||||
new ConcurrentHashMap<String, Boolean>());
|
||||
|
||||
protected AbstractFt546EquipmentAdapter(
|
||||
Ft546DeviceProfile sourceProfile, String expectedDeviceType,
|
||||
String requiredCapability, DeviceCommandType commandType,
|
||||
Ft546MotionCommandGuard motionCommandGuard,
|
||||
Ft546SnapshotContextProvider contextProvider) {
|
||||
this.profile = copy(Objects.requireNonNull(sourceProfile,
|
||||
"profile"));
|
||||
new Ft546ConfigurationValidator().validate(profile);
|
||||
if (!expectedDeviceType.equals(profile.getDeviceType())) {
|
||||
throw new IllegalArgumentException(
|
||||
"profile device type must be " + expectedDeviceType);
|
||||
}
|
||||
if (!profile.getCapabilities().contains(requiredCapability)) {
|
||||
throw new IllegalStateException(
|
||||
requiredCapability + " is not enabled");
|
||||
}
|
||||
commandAddress();
|
||||
this.commandType = Objects.requireNonNull(commandType, "commandType");
|
||||
this.motionCommandGuard = Objects.requireNonNull(motionCommandGuard,
|
||||
"motionCommandGuard");
|
||||
this.contextProvider = Objects.requireNonNull(contextProvider,
|
||||
"contextProvider");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final SendReceipt send(DeviceCommandEnvelope envelope) {
|
||||
Objects.requireNonNull(envelope, "envelope");
|
||||
String receiptId = receiptId(envelope);
|
||||
if (!profile.getDeviceId().equals(envelope.getDeviceId())) {
|
||||
return SendReceipt.rejected(receiptId,
|
||||
"profile belongs to another device");
|
||||
}
|
||||
if (envelope.getType() != commandType) {
|
||||
return SendReceipt.rejected(receiptId,
|
||||
"command type is not supported by this adapter");
|
||||
}
|
||||
if (locallyUnknownDevices.contains(envelope.getDeviceId())
|
||||
|| motionCommandGuard.hasBlockingMotionCommand(
|
||||
envelope.getDeviceId(), envelope.getCommandId())) {
|
||||
return SendReceipt.rejected(receiptId,
|
||||
"device has another active command or unresolved UNKNOWN");
|
||||
}
|
||||
final byte[] payload;
|
||||
try {
|
||||
payload = encode(envelope.getPayload());
|
||||
} catch (IllegalArgumentException rejected) {
|
||||
return SendReceipt.rejected(receiptId, rejected.getMessage());
|
||||
}
|
||||
if (!isConnected()) {
|
||||
return SendReceipt.notSent("equipment PLC is disconnected");
|
||||
}
|
||||
try {
|
||||
int result = write(commandAddress(), payload);
|
||||
if (result <= 0) {
|
||||
return markUnknown(envelope.getDeviceId(), receiptId,
|
||||
"equipment write result is ambiguous");
|
||||
}
|
||||
return SendReceipt.accepted(receiptId);
|
||||
} catch (RuntimeException ambiguous) {
|
||||
return markUnknown(envelope.getDeviceId(), receiptId,
|
||||
"equipment write raised "
|
||||
+ ambiguous.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DeviceSnapshot readSnapshot(String deviceId) {
|
||||
if (!profile.getDeviceId().equals(deviceId)) {
|
||||
throw new IllegalArgumentException(
|
||||
"profile belongs to another device");
|
||||
}
|
||||
return snapshot(deviceId);
|
||||
}
|
||||
|
||||
protected abstract boolean isConnected();
|
||||
protected abstract int write(String address, byte[] payload);
|
||||
protected abstract byte[] encode(String payload);
|
||||
protected abstract DeviceSnapshot snapshot(String deviceId);
|
||||
|
||||
protected final DeviceSnapshot unorderedSnapshot(
|
||||
String deviceId, String taskNumber, String currentNode,
|
||||
Map<String, String> facts) {
|
||||
Ft546SnapshotContext context = Objects.requireNonNull(
|
||||
contextProvider.contextFor(deviceId), "snapshot context");
|
||||
return DeviceSnapshot.unorderedWithoutBootSession(
|
||||
context.getSnapshotId(), deviceId, context.getCommandKey(),
|
||||
fact(taskNumber), context.getObservedAt(), isConnected(),
|
||||
context.getTopologyVersion(), context.getFencingToken(),
|
||||
fact(currentNode), Collections.singletonList(fact(currentNode)),
|
||||
false, new LinkedHashMap<String, String>(facts));
|
||||
}
|
||||
|
||||
protected final String commandAddress() {
|
||||
String address = profile.getRegisters().get("command");
|
||||
if (address == null || address.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException(
|
||||
"profile register is missing: command");
|
||||
}
|
||||
return address.trim();
|
||||
}
|
||||
|
||||
protected static String value(Object value) {
|
||||
return value == null ? "UNKNOWN" : String.valueOf(value);
|
||||
}
|
||||
|
||||
protected static String fact(String value) {
|
||||
return value == null || value.trim().isEmpty()
|
||||
? "UNKNOWN" : value.trim();
|
||||
}
|
||||
|
||||
private SendReceipt markUnknown(String deviceId, String receiptId,
|
||||
String detail) {
|
||||
locallyUnknownDevices.add(deviceId);
|
||||
return SendReceipt.acceptedUnknown(receiptId, detail);
|
||||
}
|
||||
|
||||
private static String receiptId(DeviceCommandEnvelope envelope) {
|
||||
return "FT546:" + envelope.getCommandId() + ":"
|
||||
+ envelope.getAttempt();
|
||||
}
|
||||
|
||||
private static Ft546DeviceProfile copy(Ft546DeviceProfile source) {
|
||||
Ft546DeviceProfile copy = new Ft546DeviceProfile();
|
||||
copy.setDeviceId(source.getDeviceId());
|
||||
copy.setDeviceType(source.getDeviceType());
|
||||
copy.setAdapterType(source.getAdapterType());
|
||||
copy.setIp(source.getIp());
|
||||
copy.setPort(source.getPort());
|
||||
copy.setStation(source.getStation());
|
||||
copy.setCapabilities(source.getCapabilities());
|
||||
copy.setRegisters(source.getRegisters());
|
||||
copy.setProfileVersion(source.getProfileVersion());
|
||||
copy.setProtocolCorpusHash(source.getProtocolCorpusHash());
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.ruoyi.project.ft546.device;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class Ft546LegacyPayloadCodec {
|
||||
private Ft546LegacyPayloadCodec() { }
|
||||
|
||||
public static Map<String, String> parse(String payload) {
|
||||
if (payload == null || payload.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("payload must not be blank");
|
||||
}
|
||||
Map<String, String> values = new LinkedHashMap<String, String>();
|
||||
for (String segment : payload.split("\\|")) {
|
||||
int separator = segment.indexOf('=');
|
||||
if (separator <= 0 || separator == segment.length() - 1) {
|
||||
throw new IllegalArgumentException(
|
||||
"invalid command payload segment: " + segment);
|
||||
}
|
||||
String key = segment.substring(0, separator).trim();
|
||||
String value = segment.substring(separator + 1).trim();
|
||||
if (values.put(key, value) != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"duplicate command payload field: " + key);
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
public static int positiveInt(Map<String, String> values, String field) {
|
||||
int value = integer(values, field);
|
||||
if (value <= 0) {
|
||||
throw new IllegalArgumentException(field + " must be positive");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public static int binaryFlag(Map<String, String> values, String field) {
|
||||
int value = integer(values, field);
|
||||
if (value != 0 && value != 1) {
|
||||
throw new IllegalArgumentException(field + " must be 0 or 1");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public static int integer(Map<String, String> values, String field) {
|
||||
String value = values.get(field);
|
||||
if (value == null || value.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException(field + " is required");
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(value);
|
||||
} catch (NumberFormatException invalid) {
|
||||
throw new IllegalArgumentException(field + " is not an integer",
|
||||
invalid);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] fields(Field... fields) {
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
for (Field field : fields) {
|
||||
for (int shift = (field.width - 1) * 8; shift >= 0; shift -= 8) {
|
||||
output.write((field.value >>> shift) & 0xff);
|
||||
}
|
||||
}
|
||||
return output.toByteArray();
|
||||
}
|
||||
|
||||
public static int checksum(int... values) {
|
||||
int sum = 0;
|
||||
for (int value : values) {
|
||||
sum += (value >>> 24) & 0xff;
|
||||
sum += (value >>> 16) & 0xff;
|
||||
sum += (value >>> 8) & 0xff;
|
||||
sum += value & 0xff;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
public static Field int32(int value) { return new Field(4, value); }
|
||||
public static Field int16(int value) {
|
||||
if (value < Short.MIN_VALUE || value > 0xffff) {
|
||||
throw new IllegalArgumentException("int16 value is out of range");
|
||||
}
|
||||
return new Field(2, value);
|
||||
}
|
||||
|
||||
public static final class Field {
|
||||
private final int width;
|
||||
private final int value;
|
||||
|
||||
private Field(int width, int value) {
|
||||
this.width = width;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.ruoyi.project.ft546.device;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Ft546MotionCommandGuard {
|
||||
boolean hasBlockingMotionCommand(String deviceId, String commandId);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.ruoyi.project.ft546.device;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class Ft546SnapshotContext {
|
||||
private final String snapshotId;
|
||||
private final String commandKey;
|
||||
private final long topologyVersion;
|
||||
private final long fencingToken;
|
||||
private final Instant observedAt;
|
||||
|
||||
public Ft546SnapshotContext(String snapshotId, String commandKey,
|
||||
long topologyVersion, long fencingToken,
|
||||
Instant observedAt) {
|
||||
this.snapshotId = requireText(snapshotId, "snapshotId");
|
||||
this.commandKey = requireText(commandKey, "commandKey");
|
||||
if (topologyVersion <= 0L || fencingToken <= 0L) {
|
||||
throw new IllegalArgumentException(
|
||||
"snapshot context versions are invalid");
|
||||
}
|
||||
this.topologyVersion = topologyVersion;
|
||||
this.fencingToken = fencingToken;
|
||||
this.observedAt = Objects.requireNonNull(observedAt, "observedAt");
|
||||
}
|
||||
|
||||
public String getSnapshotId() { return snapshotId; }
|
||||
public String getCommandKey() { return commandKey; }
|
||||
public long getTopologyVersion() { return topologyVersion; }
|
||||
public long getFencingToken() { return fencingToken; }
|
||||
public Instant getObservedAt() { return observedAt; }
|
||||
|
||||
private static String requireText(String value, String field) {
|
||||
if (value == null || value.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException(field + " must not be blank");
|
||||
}
|
||||
return value.trim();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.ruoyi.project.ft546.device;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Ft546SnapshotContextProvider {
|
||||
Ft546SnapshotContext contextFor(String deviceId);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.ruoyi.project.ft546.device.charger;
|
||||
|
||||
import com.ruoyi.device.cdz.domain.DeviceCdz;
|
||||
import com.ruoyi.project.ft546.config.Ft546DeviceProfile;
|
||||
import com.ruoyi.project.ft546.device.AbstractFt546EquipmentAdapter;
|
||||
import com.ruoyi.project.ft546.device.Ft546LegacyPayloadCodec;
|
||||
import com.ruoyi.project.ft546.device.Ft546MotionCommandGuard;
|
||||
import com.ruoyi.project.ft546.device.Ft546SnapshotContextProvider;
|
||||
import com.ruoyi.schedule.plcRead.cdz.PlcCdzRead;
|
||||
import com.ruoyi.wcs.domain.execution.DeviceCommandType;
|
||||
import com.ruoyi.wcs.domain.execution.DeviceSnapshot;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class Ft546ChargerAdapter
|
||||
extends AbstractFt546EquipmentAdapter {
|
||||
private final PlcCdzRead runtime;
|
||||
|
||||
public Ft546ChargerAdapter(PlcCdzRead runtime,
|
||||
Ft546DeviceProfile profile,
|
||||
Ft546MotionCommandGuard motionCommandGuard,
|
||||
Ft546SnapshotContextProvider contextProvider) {
|
||||
super(profile, "CHARGER", "CHARGE", DeviceCommandType.CHARGE,
|
||||
motionCommandGuard, contextProvider);
|
||||
this.runtime = Objects.requireNonNull(runtime, "runtime");
|
||||
runtime.requireDevice(profile.getDeviceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isConnected() { return runtime.IsConnect(); }
|
||||
|
||||
@Override
|
||||
protected int write(String address, byte[] payload) {
|
||||
return runtime.write(address, payload);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] encode(String payload) {
|
||||
Map<String, String> values = Ft546LegacyPayloadCodec.parse(payload);
|
||||
int enabled = Ft546LegacyPayloadCodec.binaryFlag(values, "enabled");
|
||||
return Ft546LegacyPayloadCodec.fields(
|
||||
Ft546LegacyPayloadCodec.int16(enabled));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DeviceSnapshot snapshot(String deviceId) {
|
||||
DeviceCdz device = runtime.requireDevice(deviceId);
|
||||
Map<String, String> facts = new LinkedHashMap<String, String>();
|
||||
facts.put("connection", isConnected() ? "ONLINE" : "OFFLINE");
|
||||
facts.put("occupancy", isBlank(device.getSpare1())
|
||||
? "FREE" : "OCCUPIED");
|
||||
facts.put("occupyingVehicle", fact(device.getSpare1()));
|
||||
facts.put("charging", Integer.valueOf(1).equals(device.getStatus())
|
||||
? "ON" : "OFF");
|
||||
facts.put("fault", "UNAVAILABLE");
|
||||
facts.put("status", value(device.getStatus()));
|
||||
facts.put("mode", value(device.getModel()));
|
||||
facts.put("site", fact(device.getSiteId()));
|
||||
facts.put("floor", value(device.getSzcs()));
|
||||
facts.put("taskNumber", value(device.getRwh()));
|
||||
return unorderedSnapshot(deviceId, value(device.getRwh()),
|
||||
fact(device.getSiteId()), facts);
|
||||
}
|
||||
|
||||
private static boolean isBlank(String value) {
|
||||
return value == null || value.trim().isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.ruoyi.project.ft546.device.conveyor;
|
||||
|
||||
import com.ruoyi.device.ssj.domain.DeviceSsj;
|
||||
import com.ruoyi.project.ft546.config.Ft546DeviceProfile;
|
||||
import com.ruoyi.project.ft546.device.AbstractFt546EquipmentAdapter;
|
||||
import com.ruoyi.project.ft546.device.Ft546LegacyPayloadCodec;
|
||||
import com.ruoyi.project.ft546.device.Ft546MotionCommandGuard;
|
||||
import com.ruoyi.project.ft546.device.Ft546SnapshotContextProvider;
|
||||
import com.ruoyi.schedule.plcRead.ssj.PlcSsjRead;
|
||||
import com.ruoyi.wcs.domain.execution.DeviceCommandType;
|
||||
import com.ruoyi.wcs.domain.execution.DeviceSnapshot;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class Ft546ConveyorAdapter
|
||||
extends AbstractFt546EquipmentAdapter {
|
||||
private final PlcSsjRead runtime;
|
||||
|
||||
public Ft546ConveyorAdapter(PlcSsjRead runtime,
|
||||
Ft546DeviceProfile profile,
|
||||
Ft546MotionCommandGuard motionCommandGuard,
|
||||
Ft546SnapshotContextProvider contextProvider) {
|
||||
super(profile, "CONVEYOR", "CONVEYOR_TRANSFER",
|
||||
DeviceCommandType.CONVEYOR_TRANSFER, motionCommandGuard,
|
||||
contextProvider);
|
||||
this.runtime = Objects.requireNonNull(runtime, "runtime");
|
||||
runtime.requireDevice(profile.getDeviceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isConnected() { return runtime.IsConnect(); }
|
||||
|
||||
@Override
|
||||
protected int write(String address, byte[] payload) {
|
||||
return runtime.write(address, payload);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] encode(String payload) {
|
||||
Map<String, String> values = Ft546LegacyPayloadCodec.parse(payload);
|
||||
int taskNo = Ft546LegacyPayloadCodec.positiveInt(values, "taskNo");
|
||||
int destination = Ft546LegacyPayloadCodec.positiveInt(values,
|
||||
"destination");
|
||||
int completed = Ft546LegacyPayloadCodec.binaryFlag(values,
|
||||
"completed");
|
||||
int checksum = Ft546LegacyPayloadCodec.checksum(taskNo, destination,
|
||||
completed);
|
||||
return Ft546LegacyPayloadCodec.fields(
|
||||
Ft546LegacyPayloadCodec.int32(taskNo),
|
||||
Ft546LegacyPayloadCodec.int16(destination),
|
||||
Ft546LegacyPayloadCodec.int16(completed),
|
||||
Ft546LegacyPayloadCodec.int32(checksum));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DeviceSnapshot snapshot(String deviceId) {
|
||||
DeviceSsj device = runtime.requireDevice(deviceId);
|
||||
Map<String, String> facts = new LinkedHashMap<String, String>();
|
||||
facts.put("connection", isConnected() ? "ONLINE" : "OFFLINE");
|
||||
facts.put("goodsState", device.getDw() == 1
|
||||
? "PRESENT" : "ABSENT");
|
||||
facts.put("mode", value(device.getModel()));
|
||||
facts.put("readyState", device.getKx() == 1
|
||||
? "READY" : "BLOCKED");
|
||||
facts.put("trayCode", fact(device.getTrayCode()));
|
||||
facts.put("taskNumber", value(device.getTaskNo()));
|
||||
facts.put("request", value(device.getRkqq()));
|
||||
facts.put("faultCode", value(device.getErrorCode()));
|
||||
return unorderedSnapshot(deviceId, value(device.getTaskNo()),
|
||||
fact(device.getDeviceCode()), facts);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.ruoyi.project.ft546.device.lift;
|
||||
|
||||
import com.ruoyi.device.tsj.domain.DeviceTsj;
|
||||
import com.ruoyi.project.ft546.config.Ft546DeviceProfile;
|
||||
import com.ruoyi.project.ft546.device.AbstractFt546EquipmentAdapter;
|
||||
import com.ruoyi.project.ft546.device.Ft546LegacyPayloadCodec;
|
||||
import com.ruoyi.project.ft546.device.Ft546MotionCommandGuard;
|
||||
import com.ruoyi.project.ft546.device.Ft546SnapshotContextProvider;
|
||||
import com.ruoyi.schedule.plcRead.tsj.PlcTsjRead;
|
||||
import com.ruoyi.wcs.domain.execution.DeviceCommandType;
|
||||
import com.ruoyi.wcs.domain.execution.DeviceSnapshot;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class Ft546LiftAdapter extends AbstractFt546EquipmentAdapter {
|
||||
private final PlcTsjRead runtime;
|
||||
|
||||
public Ft546LiftAdapter(PlcTsjRead runtime, Ft546DeviceProfile profile,
|
||||
Ft546MotionCommandGuard motionCommandGuard,
|
||||
Ft546SnapshotContextProvider contextProvider) {
|
||||
super(profile, "LIFT", "LIFT_TRANSFER",
|
||||
DeviceCommandType.LIFT_TRANSFER, motionCommandGuard,
|
||||
contextProvider);
|
||||
this.runtime = Objects.requireNonNull(runtime, "runtime");
|
||||
runtime.requireDevice(profile.getDeviceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isConnected() { return runtime.IsConnect(); }
|
||||
|
||||
@Override
|
||||
protected int write(String address, byte[] payload) {
|
||||
return runtime.write(address, payload);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] encode(String payload) {
|
||||
Map<String, String> values = Ft546LegacyPayloadCodec.parse(payload);
|
||||
int taskNo = Ft546LegacyPayloadCodec.positiveInt(values, "taskNo");
|
||||
int taskType = Ft546LegacyPayloadCodec.positiveInt(values,
|
||||
"taskType");
|
||||
int sourceFloor = Ft546LegacyPayloadCodec.positiveInt(values,
|
||||
"sourceFloor");
|
||||
int targetFloor = Ft546LegacyPayloadCodec.positiveInt(values,
|
||||
"targetFloor");
|
||||
int completed = 1;
|
||||
int checksum = Ft546LegacyPayloadCodec.checksum(taskNo, taskType,
|
||||
sourceFloor, targetFloor, completed);
|
||||
return Ft546LegacyPayloadCodec.fields(
|
||||
Ft546LegacyPayloadCodec.int32(taskNo),
|
||||
Ft546LegacyPayloadCodec.int16(taskType),
|
||||
Ft546LegacyPayloadCodec.int16(sourceFloor),
|
||||
Ft546LegacyPayloadCodec.int16(targetFloor),
|
||||
Ft546LegacyPayloadCodec.int16(completed),
|
||||
Ft546LegacyPayloadCodec.int16(checksum));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DeviceSnapshot snapshot(String deviceId) {
|
||||
DeviceTsj device = runtime.requireDevice(deviceId);
|
||||
Map<String, String> facts = new LinkedHashMap<String, String>();
|
||||
facts.put("connection", isConnected() ? "ONLINE" : "OFFLINE");
|
||||
facts.put("floor", value(device.getDqcs()));
|
||||
facts.put("doorState", doorState(device.getJdbz()));
|
||||
facts.put("doorStateSource", "EXECUTION_PHASE_DERIVED");
|
||||
facts.put("portState", Integer.valueOf(1).equals(device.getYxxf())
|
||||
? "READY" : "BLOCKED");
|
||||
facts.put("load", value(device.getHczt()));
|
||||
facts.put("status", value(device.getSbzt()));
|
||||
facts.put("mode", value(device.getCzfs()));
|
||||
facts.put("executionStage", value(device.getJdbz()));
|
||||
facts.put("taskNumber", value(device.getRwh()));
|
||||
return unorderedSnapshot(deviceId, value(device.getRwh()),
|
||||
value(device.getDqcs()), facts);
|
||||
}
|
||||
|
||||
private static String doorState(Integer executionStage) {
|
||||
if (executionStage == null) {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
return executionStage == 2 || executionStage == 4
|
||||
? "OPEN" : "CLOSED";
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,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;
|
||||
@@ -22,7 +23,7 @@ public final class Ft546LegacySrgvAdapter
|
||||
private final Ft546DeviceRuntimeRegistry registry;
|
||||
private final Ft546DeviceProfile profile;
|
||||
private final CapabilityGateResult capabilities;
|
||||
private final MotionCommandGuard motionCommandGuard;
|
||||
private final Ft546MotionCommandGuard motionCommandGuard;
|
||||
private final SnapshotContextProvider snapshotContextProvider;
|
||||
private final Ft546SrgvCommandEncoder encoder =
|
||||
new Ft546SrgvCommandEncoder();
|
||||
@@ -36,7 +37,7 @@ public final class Ft546LegacySrgvAdapter
|
||||
Ft546DeviceProfile profile,
|
||||
Ft546CapabilityEvidence evidence,
|
||||
Ft546CapabilityGate capabilityGate,
|
||||
MotionCommandGuard motionCommandGuard,
|
||||
Ft546MotionCommandGuard motionCommandGuard,
|
||||
SnapshotContextProvider contextProvider) {
|
||||
this.registry = Objects.requireNonNull(registry, "registry");
|
||||
this.profile = copyProfile(Objects.requireNonNull(profile,
|
||||
@@ -148,11 +149,6 @@ public final class Ft546LegacySrgvAdapter
|
||||
return copy;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface MotionCommandGuard {
|
||||
boolean hasBlockingMotionCommand(String deviceId, String commandId);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface SnapshotContextProvider {
|
||||
Ft546SrgvSnapshotMapper.Context contextFor(String deviceId);
|
||||
|
||||
@@ -42,6 +42,20 @@ public class PlcCdzRead implements Runnable {
|
||||
return a;
|
||||
}
|
||||
|
||||
public int write(String address, byte[] data) {
|
||||
return WriteToCdz(address, data);
|
||||
}
|
||||
|
||||
public DeviceCdz requireDevice(String deviceId) {
|
||||
DeviceCdz device = deviceCdzMap == null
|
||||
? null : deviceCdzMap.get(deviceId);
|
||||
if (device == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"charger device not found: " + deviceId);
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (isRun) {
|
||||
@@ -99,16 +113,11 @@ public class PlcCdzRead implements Runnable {
|
||||
}
|
||||
|
||||
public void setIsConnect(boolean bflag) {
|
||||
if (!success) {
|
||||
success = bflag;
|
||||
}
|
||||
success = bflag;
|
||||
}
|
||||
|
||||
public boolean IsConnect() {
|
||||
if (success) {
|
||||
return success;
|
||||
}
|
||||
return false;
|
||||
return success;
|
||||
}
|
||||
|
||||
void reConnect() {
|
||||
|
||||
@@ -237,16 +237,11 @@ public class PlcSrgvRead implements Runnable {
|
||||
}
|
||||
|
||||
public void setIsConnect(boolean bflag) {
|
||||
if (!success) {
|
||||
success = bflag;
|
||||
}
|
||||
success = bflag;
|
||||
}
|
||||
|
||||
public boolean IsConnect() {
|
||||
if (success) {
|
||||
return success;
|
||||
}
|
||||
return false;
|
||||
return success;
|
||||
}
|
||||
|
||||
void reConnect() {
|
||||
|
||||
@@ -47,6 +47,20 @@ public class PlcSsjRead implements Runnable {
|
||||
return a;
|
||||
}
|
||||
|
||||
public int write(String address, byte[] data) {
|
||||
return WriteToSSJ(address, data);
|
||||
}
|
||||
|
||||
public DeviceSsj requireDevice(String deviceId) {
|
||||
DeviceSsj device = deviceSsjMap == null
|
||||
? null : deviceSsjMap.get(deviceId);
|
||||
if (device == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"conveyor device not found: " + deviceId);
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (isRun) {
|
||||
@@ -223,16 +237,11 @@ public class PlcSsjRead implements Runnable {
|
||||
}
|
||||
|
||||
public void setIsConnect(boolean bflag) {
|
||||
if (!success) {
|
||||
success = bflag;
|
||||
}
|
||||
success = bflag;
|
||||
}
|
||||
|
||||
public boolean IsConnect() {
|
||||
if (success) {
|
||||
return success;
|
||||
}
|
||||
return false;
|
||||
return success;
|
||||
}
|
||||
|
||||
void reConnect() {
|
||||
|
||||
@@ -43,6 +43,20 @@ public class PlcTsjRead implements Runnable {
|
||||
return a;
|
||||
}
|
||||
|
||||
public int write(String address, byte[] data) {
|
||||
return WriteToTSJ(address, data);
|
||||
}
|
||||
|
||||
public DeviceTsj requireDevice(String deviceId) {
|
||||
DeviceTsj device = deviceTsjMap == null
|
||||
? null : deviceTsjMap.get(deviceId);
|
||||
if (device == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"lift device not found: " + deviceId);
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (isRun) {
|
||||
@@ -107,16 +121,11 @@ public class PlcTsjRead implements Runnable {
|
||||
}
|
||||
|
||||
public void setIsConnect(boolean bflag) {
|
||||
if (!success) {
|
||||
success = bflag;
|
||||
}
|
||||
success = bflag;
|
||||
}
|
||||
|
||||
public boolean IsConnect() {
|
||||
if (success) {
|
||||
return success;
|
||||
}
|
||||
return false;
|
||||
return success;
|
||||
}
|
||||
|
||||
void reConnect() {
|
||||
|
||||
@@ -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