Browse Source

与设备TCP通信基础逻辑

master
白凤吉 6 months ago
parent
commit
43fca46111
  1. 1
      build.gradle
  2. 87
      src/main/java/com/qyft/gd/device/client/TcpClient.java
  3. 7
      src/main/java/com/qyft/gd/device/common/constant/DeviceCommands.java
  4. 32
      src/main/java/com/qyft/gd/device/config/TcpConfig.java
  5. 37
      src/main/java/com/qyft/gd/device/handler/DeviceMessageHandler.java
  6. 10
      src/main/java/com/qyft/gd/device/model/bo/DeviceStatus.java
  7. 24
      src/main/java/com/qyft/gd/device/service/DeviceStateService.java
  8. 9
      src/main/resources/application.yml

1
build.gradle

@ -39,6 +39,7 @@ dependencies {
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.12.6'
implementation group: 'com.alibaba', name: 'fastjson', version: '2.0.54'
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-websocket', version: '3.4.2'
implementation group: 'io.netty', name: 'netty-all', version: '4.1.117.Final'
//++++++++++++++++

87
src/main/java/com/qyft/gd/device/client/TcpClient.java

@ -0,0 +1,87 @@
package com.qyft.gd.device.client;
import com.qyft.gd.device.config.TcpConfig;
import com.qyft.gd.device.handler.DeviceMessageHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;
@Component
@RequiredArgsConstructor
public class TcpClient {
private static final Logger log = LoggerFactory.getLogger(TcpClient.class);
private final TcpConfig tcpConfig;
private final DeviceMessageHandler deviceMessageHandler;
private final EventLoopGroup group = new NioEventLoopGroup();
private Channel channel;
private Bootstrap bootstrap;
@PostConstruct
public void init() {
if (tcpConfig.isEnable()) {
connect();
}
}
public void connect() {
try {
if (bootstrap == null) {
bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, tcpConfig.getTimeout())
.handler(new ChannelInitializer<>() {
@Override
protected void initChannel(Channel ch) {
ch.pipeline().addLast(deviceMessageHandler);
}
});
}
log.info("尝试连接到TCP服务 {}:{}", tcpConfig.getHost(), tcpConfig.getPort());
ChannelFuture future = bootstrap.connect(new InetSocketAddress(tcpConfig.getHost(), tcpConfig.getPort()));
future.addListener((ChannelFutureListener) f -> {
if (f.isSuccess()) {
channel = f.channel();
log.info("已链接到TCP服务");
} else {
log.error("无法连接到TCP服务. {}ms后重试...", tcpConfig.getReconnect());
f.channel().eventLoop().schedule(this::connect, tcpConfig.getReconnect(), TimeUnit.MILLISECONDS);
}
});
} catch (Exception e) {
log.error("尝试连接到TCP服务发生意外错误: {}", e.getMessage(), e);
}
}
@Scheduled(fixedRateString = "${tcp.reconnect}")
public void checkConnection() {
if (channel == null || !channel.isActive()) {
log.error("TCP服务链接丢失");
connect();
}
}
public void sendCommand(String command) {
if (channel != null && channel.isActive()) {
channel.writeAndFlush(command);
} else {
log.error("TCP服务未连接,无法发送指令: {}", command);
}
}
}

7
src/main/java/com/qyft/gd/device/common/constant/DeviceCommands.java

@ -0,0 +1,7 @@
package com.qyft.gd.device.common.constant;
/**
* 设备指令
*/
public class DeviceCommands {
}

32
src/main/java/com/qyft/gd/device/config/TcpConfig.java

@ -0,0 +1,32 @@
package com.qyft.gd.device.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "tcp")
public class TcpConfig {
/**
* 是否启用 TCP 连接
*/
private boolean enable;
/**
* TCP 链接地址
*/
private String host;
/**
* TCP 端口
*/
private int port;
/**
* 断线重连间隔毫秒
*/
private int reconnect;
/**
* 连接超时时间毫秒
*/
private int timeout;
}

37
src/main/java/com/qyft/gd/device/handler/DeviceMessageHandler.java

@ -0,0 +1,37 @@
package com.qyft.gd.device.handler;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.qyft.gd.device.model.bo.DeviceStatus;
import com.qyft.gd.device.service.DeviceStateService;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
@ChannelHandler.Sharable
@RequiredArgsConstructor
public class DeviceMessageHandler extends SimpleChannelInboundHandler<String> {
private static final Logger log = LoggerFactory.getLogger(DeviceMessageHandler.class);
private final DeviceStateService deviceStateService;
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) {
try {
// 解析 JSON
JSONObject json = JSONUtil.parseObj(msg);
DeviceStatus deviceStatus = json.toBean(DeviceStatus.class);
// 更新设备状态
deviceStateService.updateDeviceStatus(deviceStatus);
log.info("设备状态已更新: {}", json.toStringPretty());
} catch (Exception e) {
log.error("设备状态更新错误: {}, error: {}", msg, e.getMessage(), e);
}
}
}

10
src/main/java/com/qyft/gd/device/model/bo/DeviceStatus.java

@ -0,0 +1,10 @@
package com.qyft.gd.device.model.bo;
import lombok.Data;
/**
* 设备当前状态
*/
@Data
public class DeviceStatus {
}

24
src/main/java/com/qyft/gd/device/service/DeviceStateService.java

@ -0,0 +1,24 @@
package com.qyft.gd.device.service;
import cn.hutool.core.bean.BeanUtil;
import com.qyft.gd.device.model.bo.DeviceStatus;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class DeviceStateService {
private final DeviceStatus deviceStatus = new DeviceStatus();
public void updateDeviceStatus(DeviceStatus newStatus) {
synchronized (deviceStatus) {
BeanUtil.copyProperties(newStatus, deviceStatus);
}
}
public DeviceStatus getDeviceStatus() {
synchronized (deviceStatus) {
return BeanUtil.copyProperties(deviceStatus, DeviceStatus.class);
}
}
}

9
src/main/resources/application.yml

@ -31,3 +31,12 @@ springdoc:
jwt:
enabled: false # 是否启用权限认证,设置为 true 启用,false 禁用
#与设备TCP链接
tcp:
enable: false # 是否开启 TCP 连接
host: 192.168.1.100
port: 9000
reconnect: 5000 # 断线重连间隔(单位:毫秒)
timeout: 10000 # 连接超时时间(单位:毫秒)
Loading…
Cancel
Save