You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
180 lines
7.9 KiB
180 lines
7.9 KiB
package com.iflytop.gd.app.service;
|
|
|
|
|
|
import cn.hutool.json.JSONObject;
|
|
import com.iflytop.gd.app.core.DebugGenerator;
|
|
import com.iflytop.gd.common.cmd.CommandFuture;
|
|
import com.iflytop.gd.common.cmd.CyclicNumberGenerator;
|
|
import com.iflytop.gd.common.cmd.DeviceCommandBundle;
|
|
import com.iflytop.gd.common.constant.CommandStatus;
|
|
import com.iflytop.gd.hardware.HardwareService;
|
|
import jakarta.annotation.PostConstruct;
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import org.springframework.stereotype.Service;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.concurrent.BlockingQueue;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.concurrent.ConcurrentMap;
|
|
import java.util.concurrent.LinkedBlockingQueue;
|
|
|
|
@Slf4j
|
|
@Service
|
|
@RequiredArgsConstructor
|
|
public class DeviceCommandService {
|
|
private final HardwareService hardwareService;
|
|
private final WebSocketService webSocketService;
|
|
private final DeviceStateService deviceStateService;
|
|
/**
|
|
* 需要等待加液区空闲的龙门架机械臂指令
|
|
*/
|
|
private final ConcurrentMap<Integer, CommandFuture> sendCommandFutureMap = new ConcurrentHashMap<>();
|
|
private final BlockingQueue<CommandFuture[]> gantryCommandQueue = new LinkedBlockingQueue<>();
|
|
|
|
@PostConstruct
|
|
private void initExecutorThread() {
|
|
new Thread(this::executeCommands).start();
|
|
}
|
|
|
|
private void executeCommands() {
|
|
while (true) {
|
|
try {
|
|
deviceStateService.setGantryArmStateIdle(true);
|
|
CommandFuture[] commandFutureArray = gantryCommandQueue.take();
|
|
for (CommandFuture commandFuture : commandFutureArray) {
|
|
executeCommand(commandFuture);
|
|
}
|
|
} catch (Exception e) {
|
|
Thread.currentThread().interrupt();
|
|
} finally {
|
|
deviceStateService.setGantryArmStateIdle(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
public synchronized CommandFuture[] sendCommandGantryQueue(DeviceCommandBundle... deviceCommandBundles) {
|
|
return sendCommandGantryQueue(null, null, deviceCommandBundles);
|
|
}
|
|
|
|
public synchronized CommandFuture[] sendCommandGantryQueue(String cmdId, String cmdCode, DeviceCommandBundle... deviceCommandBundles) {
|
|
List<CommandFuture> commandFutureList = new ArrayList<>();
|
|
for (DeviceCommandBundle deviceCommandBundle : deviceCommandBundles) {
|
|
commandFutureList.add(createDeviceCommandFuture(cmdId, cmdCode, deviceCommandBundle));
|
|
}
|
|
CommandFuture[] commandFutureArray = commandFutureList.toArray(new CommandFuture[0]);
|
|
try {
|
|
gantryCommandQueue.put(commandFutureArray);
|
|
} catch (Exception e) {
|
|
log.error("设备指令入队列失败", e);
|
|
throw new RuntimeException(e);
|
|
}
|
|
return commandFutureArray;
|
|
}
|
|
|
|
/**
|
|
* 根据 DeviceCommand 创建 CommandFuture
|
|
*/
|
|
private CommandFuture createDeviceCommandFuture(String cmdId, String cmdCode, DeviceCommandBundle deviceCommandBundle) {
|
|
CommandFuture commandFuture = createDeviceCommandFuture(deviceCommandBundle);
|
|
commandFuture.setCmdId(cmdId);
|
|
commandFuture.setCmdCode(cmdCode);
|
|
return commandFuture;
|
|
}
|
|
|
|
/**
|
|
* 根据 DeviceCommand 创建 CommandFuture
|
|
*/
|
|
private CommandFuture createDeviceCommandFuture(DeviceCommandBundle deviceCommandBundle) {
|
|
CommandFuture commandFuture = new CommandFuture();
|
|
commandFuture.setDeviceCommandBundle(deviceCommandBundle);
|
|
commandFuture.getResponseFuture().whenComplete((result, ex) -> {
|
|
sendCommandFutureMap.remove(deviceCommandBundle.getDeviceCommand().getCmdId());
|
|
});
|
|
return commandFuture;
|
|
}
|
|
|
|
public void executeCommand(CommandFuture commandFuture) {
|
|
int cmdId = CyclicNumberGenerator.getInstance().generateNumber();
|
|
commandFuture.getDeviceCommandBundle().getDeviceCommand().setCmdId(cmdId);
|
|
sendCommandFutureMap.put(cmdId, commandFuture);
|
|
commandFuture.setStartSendTime(System.currentTimeMillis());
|
|
if (!deviceStateService.getDeviceState().isVirtual()) {
|
|
if (!hardwareService.sendCommand(commandFuture.getDeviceCommandBundle().getDeviceCommand())) {
|
|
sendCommandFutureMap.remove(commandFuture.getDeviceCommandBundle().getDeviceCommand().getCmdId());
|
|
throw new RuntimeException("向设备发送指令失败");
|
|
}
|
|
} else {
|
|
//虚拟模式
|
|
new Thread(() -> {
|
|
try {
|
|
String actionName = commandFuture.getDeviceCommandBundle().getDeviceCommand().getAction().name();
|
|
if (actionName.contains("move") || actionName.contains("origin")) {
|
|
Thread.sleep(300);
|
|
}
|
|
JSONObject jsonObject = new JSONObject();
|
|
jsonObject.putOnce("cmdId", cmdId);
|
|
jsonObject.putOnce("success", true);
|
|
completeCommandResponse(jsonObject);
|
|
} catch (InterruptedException e) {
|
|
// 处理中断异常
|
|
Thread.currentThread().interrupt();
|
|
}
|
|
}).start();
|
|
}
|
|
if (commandFuture.getCmdId() != null) {
|
|
webSocketService.pushDebug(DebugGenerator.generateJson(commandFuture.getCmdId(), commandFuture.getCmdCode(), CommandStatus.DEVICE_SEND, commandFuture.getDeviceCommandBundle().getCmdName() + "指令,已发给设备", commandFuture.getDeviceCommandBundle()));
|
|
}
|
|
|
|
}
|
|
|
|
public CommandFuture sendCommand(DeviceCommandBundle deviceCommand) {
|
|
CommandFuture commandFuture = createDeviceCommandFuture(deviceCommand);
|
|
executeCommand(commandFuture);
|
|
return commandFuture;
|
|
}
|
|
|
|
public CommandFuture sendCommand(String cmdId, String cmdCode, DeviceCommandBundle deviceCommandBundle) {
|
|
CommandFuture commandFuture = createDeviceCommandFuture(cmdId, cmdCode, deviceCommandBundle);
|
|
executeCommand(commandFuture);
|
|
return commandFuture;
|
|
}
|
|
|
|
public void completeCommandResponse(JSONObject deviceResult) {
|
|
Integer cmdId = deviceResult.getInt("cmdId");
|
|
if (cmdId != null) {
|
|
CommandFuture future = sendCommandFutureMap.get(cmdId);
|
|
if (future != null) {
|
|
future.setEndSendTime(System.currentTimeMillis());
|
|
Boolean success = deviceResult.getBool("success"); //数据验证
|
|
if (success == null || !success) { //response失败
|
|
if (future.getCmdId() != null) {
|
|
webSocketService.pushDebug(DebugGenerator.generateJson(future.getCmdId(), future.getCmdCode(), CommandStatus.DEVICE_ERROR,
|
|
future.getDeviceCommandBundle().getCmdName() + "指令,设备response错误,耗时:" + (future.getEndSendTime() - future.getStartSendTime()), deviceResult));
|
|
}
|
|
future.completeResponseExceptionally(new RuntimeException("response失败:" + deviceResult));
|
|
} else {
|
|
if (future.getCmdId() != null) {
|
|
webSocketService.pushDebug(DebugGenerator.generateJson(future.getCmdId(), future.getCmdCode(), CommandStatus.DEVICE_RESULT,
|
|
future.getDeviceCommandBundle().getCmdName() + "指令,设备response正常,耗时:" + (future.getEndSendTime() - future.getStartSendTime()), deviceResult));
|
|
}
|
|
future.completeResponse(deviceResult);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 取消等待中的future并从map中移除
|
|
*/
|
|
public synchronized void releaseAllCommandFutures() {
|
|
for (Integer key : sendCommandFutureMap.keySet()) {
|
|
CommandFuture future = sendCommandFutureMap.remove(key);
|
|
if (future != null) {
|
|
future.getResponseFuture().cancel(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|