石墨消解仪后端服务
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.

134 lines
5.2 KiB

package com.iflytop.gd.infrastructure.drivers;
import cn.hutool.core.util.ObjectUtil;
import com.iflytop.gd.system.drivers.CommandBus;
import com.iflytop.gd.system.exceptions.CommandExecTimeoutException;
import com.iflytop.gd.system.exceptions.HardwareErrorException;
import com.iflytop.gd.system.models.DataPacket;
import com.iflytop.gd.system.utils.ByteArray;
import jakarta.annotation.PostConstruct;
import jakarta.websocket.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.net.URI;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* 使用WebSocket实现命令总线功能
*/
@Slf4j
@Component
@ClientEndpoint
@EnableScheduling
public class WebSocketCommandBusImpl implements CommandBus {
private Session session;
private CountDownLatch countDownLatch;
private DataPacket lastDataPacket;
private Integer packetIndex = 0;
private final String COMMAND_BUS_WEBSOCKET_URL;
public static final int PACKET_TYPE_CMD = 0xA0;
public static final int PACKET_TYPE_ACK = 0xA1;
public static final int PACKET_TYPE_ERROR_ACK = 0xA2;
public static final int PACKET_TYPE_EVENT = 0xA3;
//TODO 配置硬件服务段ws链接
public WebSocketCommandBusImpl(@Value("${command_bus.websocket_server_url}") String websocketServerUrl) throws DeploymentException, IOException {
this.COMMAND_BUS_WEBSOCKET_URL = websocketServerUrl;
}
@Scheduled(fixedRate = 5000)
@PostConstruct
public void connectToCommandBusWebSocketServer() throws DeploymentException, IOException {
if (this.session == null || !this.session.isOpen()) {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
URI endpointURI = URI.create(COMMAND_BUS_WEBSOCKET_URL);
try {
this.session = container.connectToServer(this, endpointURI);
} catch (Exception e) {
// log.error(e.getMessage(), e);
}
}
}
@Override
public synchronized DataPacket waitForCommandExec(DataPacket commandPacket, Integer timeout, TimeUnit unit)
throws CommandExecTimeoutException, HardwareErrorException, IOException, InterruptedException {
try {
if (!this.session.isOpen()) {
log.error("Session state={}", this.session.isOpen());
throw new IOException("Session is not open");
}
packetIndex = packetIndex + 1;
if (packetIndex > 30000) {
packetIndex = 1;
}
commandPacket.setPacketIndex(packetIndex);
this.countDownLatch = new CountDownLatch(1);
String byteString = commandPacket.toByteString();
log.info("ModuleId={}, CommandId={}, 数据包二进制字符内容={}", commandPacket.getModuleId(), commandPacket.getCmdId(), byteString);
this.session.getBasicRemote().sendText(byteString);
boolean isTimeout = !this.countDownLatch.await(timeout, unit);
// 命令返回或者超时了
if (isTimeout) {
log.error("Command exec timeout, moduleId={}, commandId={}, timeoutInMilSeconds={}",
commandPacket.getModuleId(), commandPacket.getCmdId(), unit.toMillis(timeout));
throw new CommandExecTimeoutException();
}
log.debug("收到数据包{}", this.lastDataPacket);
// 在指定的时间内得到了响应
if (this.lastDataPacket.getPacketType() == PACKET_TYPE_ERROR_ACK) {
log.error("moduleId={}执行command={}发送硬件错误", this.lastDataPacket.getModuleId(), this.lastDataPacket.getCmdId());
throw new HardwareErrorException();
}
return ObjectUtil.cloneByStream(this.lastDataPacket);
} catch (IOException e) {
log.error("发送指令发生异常", e);
throw e;
} catch (InterruptedException e) {
log.error("Thread: {}被中断", Thread.currentThread().getName());
throw e;
}
}
@OnOpen
public void onOpen(Session session) {
this.session = session;
log.info("WebSocket connection established");
}
@OnMessage
public void onMessage(String message) {
byte[] bytes = ByteArray.hexStringToBytes(message);
log.info("New packet arrived: {}", message);
this.lastDataPacket = new DataPacket(bytes);
if (this.lastDataPacket.getPacketType() == DataPacket.PACKET_TYPE_ACK ||
this.lastDataPacket.getPacketType() == DataPacket.PACKET_TYPE_ERROR_ACK) {
this.countDownLatch.countDown();
}
}
@OnClose
public void onClose() {
if (this.countDownLatch != null) {
this.countDownLatch.countDown();
}
}
@OnError
public void onError(Session session, Throwable throwable) {
if (this.countDownLatch != null) {
this.countDownLatch.countDown();
}
}
}