package com.ruoyi.web.controller.modbus;
import com.ruoyi.system.domain.GmModbus;
import com.ruoyi.system.domain.vo.GmGroundwaterModbusVO;
import com.ruoyi.system.service.IGmDeviceService;
import com.ruoyi.web.controller.mqtt.SpringUtilsAuTo;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class HJ212Server {
// 协议常量
private static final String HEADER = "##";
private static final String TAIL = "\r\n";
private static final int LENGTH_FIELD_SIZE = 4;
private static final int CRC_FIELD_SIZE = 4;
// 配置参数
private static final int SERVER_PORT = 6001;
private static final int THREAD_POOL_SIZE = 10;
private static final String CHAR_SET = "GB2312";
private static IGmDeviceService gmDeviceService = SpringUtilsAuTo.getBean(IGmDeviceService.class);
private static volatile boolean isRunning = true;
private static ExecutorService threadPool;
public static void main(String[] args) {
startServer();
// 添加关闭钩子
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
stopServer();
System.out.println("HJ212服务已安全关闭");
}));
// String rawData = "##0527QN=20250610150010165;ST=24;CN=2061;PW=123456;MN=88888880000001;Flag=5;CP=&&DataTime=20250610140000;w21003-Min=0.03,w21003-Max=0.03,w21003-Avg=0.03,w21003-Cou=0,w21003-Flag=N;e79902-Min=0,e79902-Max=0,e79902-Avg=0,e79902-Flag=D;w01001-Min=7.85,w01001-Max=7.87,w01001-Avg=7.8613,w01001-Flag=N;w40016-Min=270.588,w40016-Max=271.412,w40016-Avg=271.002,w40016-Flag=N;w21007-Min=1.7739,w21007-Max=2.8038,w21007-Avg=2.2626,w21007-Cou=0,w21007-Flag=N;w01006-Min=32.7687,w01006-Max=35.668,w01006-Avg=34.1145,w01006-Cou=0,w01006-Flag=N&&0500\n";
//
// // 解析数据
// GmGroundwaterModbusVO data = parseData(rawData);
// System.out.println(data);
}
/**
* 启动HJ212协议接收服务
*/
public static void startServer() {
System.out.println("正在启动HJ212数据接收服务...");
threadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
new Thread(() -> {
try (ServerSocket serverSocket = new ServerSocket(SERVER_PORT)) {
System.out.println("服务启动成功,监听端口: " + SERVER_PORT);
System.out.println("等待环保监测设备连接...");
while (isRunning) {
try {
Socket clientSocket = serverSocket.accept();
threadPool.execute(new ClientHandler(clientSocket));
} catch (SocketException e) {
if (isRunning) System.err.println("Socket异常: " + e.getMessage());
}
}
} catch (IOException e) {
System.err.println("服务启动失败: " + e.getMessage());
stopServer();
}
}, "HJ212-Server-Thread").start();
}
/**
* 停止HJ212服务
*/
public static void stopServer() {
System.out.println("正在停止HJ212服务...");
isRunning = false;
if (threadPool != null) {
threadPool.shutdownNow();
}
}
/**
* 客户端连接处理器
*/
static class ClientHandler implements Runnable {
private final Socket clientSocket;
// 根据 CPU 核心数动态计算线程池参数
int corePoolSize = Runtime.getRuntime().availableProcessors();
int maxPoolSize = corePoolSize * 2; // IO 密集型任务,线程数可适当增加
long keepAliveTime = 60L; // 空闲线程存活时间
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(1000); // 有界队列,防止内存溢出
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maxPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
workQueue,
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略:调用者线程执行(主流程仍可继续)
);
ClientHandler(Socket socket) {
this.clientSocket = socket;
}
@Override
public void run() {
String clientAddr = clientSocket.getInetAddress().getHostAddress();
System.out.println("设备连接: " + clientAddr);
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream(), CHAR_SET))) {
while (isRunning && !clientSocket.isClosed()) {
String rawData = readHJ212Packet(reader);
if (rawData == null) break; // 连接断开
Date currentDate1 = new Date();
SimpleDateFormat dateFormat1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String formattedDate1 = dateFormat1.format(currentDate1);
System.out.println("modbus数据:::::::::"+rawData);
GmModbus gmModbus = new GmModbus();
gmModbus.setReceiveTime(formattedDate1);
gmModbus.setMsg(rawData);
gmDeviceService.insertGmModbus(gmModbus);
// 提交解析任务到线程池(异步处理)
executor.submit(() -> {
try {
processHJ212Data(rawData); // 解析逻辑
} catch (Exception e) {
// 记录解析错误日志,但不中断主流程
System.err.println("解析失败: " + e.getMessage());
}
});
}
} catch (Exception e) {
if (isRunning) System.err.println("处理错误: " + e.getMessage());
} finally {
try {
clientSocket.close();
} catch (IOException e) {
// 忽略关闭异常
}
System.out.println("设备断开: " + clientAddr);
}
}
// 读取完整数据包
private String readHJ212Packet(BufferedReader reader) throws IOException {
StringBuilder packet = new StringBuilder();
char[] buffer = new char[1024];
int bytesRead;
long startTime = System.currentTimeMillis();
while (isRunning && (bytesRead = reader.read(buffer)) != -1) {
packet.append(buffer, 0, bytesRead);
String data = packet.toString();
// 检查完整包
if (data.startsWith(HEADER)) {
if (data.endsWith(TAIL)) {
return data;
}
// 包未完整,继续读取
} else if (System.currentTimeMillis() - startTime > 5000) {
throw new SocketTimeoutException("数据包接收超时");
}
}
return null;
}
}
private static void parseAndSetDateTime(GmGroundwaterModbusVO data, String rawTime) {
try {
DateTimeFormatter inputFormat = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
DateTimeFormatter outputFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime dateTime = LocalDateTime.parse(rawTime, inputFormat);
String formattedTime = dateTime.format(outputFormat);
data.setDatetimes(formattedTime); // 假设 GmDeviceData 有 setDatetimes 方法
} catch (Exception e) {
System.err.println("时间解析失败: " + rawTime + ", 错误: " + e.getMessage());
}
}
private static GmGroundwaterModbusVO parseData(String rawData) {
GmGroundwaterModbusVO data = new GmGroundwaterModbusVO();
// 提取时间戳(DataTime)
extractAndSetTimestamp(rawData, data);
// 提取 IMEI(MN)
extractAndSetImei(rawData, data);
// 定义传感器列表
String[] sensors = {"w21003", "e79902", "w01001", "w40016", "w21007", "w01010", "w01006"};
// 为每个传感器提取 Rtd, Min, Max, Avg
for (String sensor : sensors) {
extractAndSetValue(rawData, sensor, "Rtd=", 4,
value -> {
if (sensor.equals("w21003")) data.setAnRtd(value);
else if (sensor.equals("e79902")) data.setWaterRtd(value);
else if (sensor.equals("w01001")) data.setPhRtd(value);
else if (sensor.equals("w40016")) data.setHardRtd(value);
else if (sensor.equals("w21007")) data.setAcidRtd(value);
else if (sensor.equals("w01010")) data.setTempRtd(value);
else if (sensor.equals("w01006")) data.setDissolveRtd(value);
}
);
extractAndSetValue(rawData, sensor, "Min=", 4,
value -> {
if (sensor.equals("w21003")) data.setAnMin(value);
else if (sensor.equals("e79902")) data.setWaterMin(value);
else if (sensor.equals("w01001")) data.setPhMin(value);
else if (sensor.equals("w40016")) data.setHardMin(value);
else if (sensor.equals("w21007")) data.setAcidMin(value);
else if (sensor.equals("w01010")) data.setTempMin(value);
else if (sensor.equals("w01006")) data.setDissolveMin(value);
}
);
extractAndSetValue(rawData, sensor, "Max=", 4,
value -> {
if (sensor.equals("w21003")) data.setAnMax(value);
else if (sensor.equals("e79902")) data.setWaterMax(value);
else if (sensor.equals("w01001")) data.setPhMax(value);
else if (sensor.equals("w40016")) data.setHardMax(value);
else if (sensor.equals("w21007")) data.setAcidMax(value);
else if (sensor.equals("w01010")) data.setTempMax(value);
else if (sensor.equals("w01006")) data.setDissolveMax(value);
}
);
extractAndSetValue(rawData, sensor, "Avg=", 4,
value -> {
if (sensor.equals("w21003")) data.setAnAvg(value);
else if (sensor.equals("e79902")) data.setWaterAvg(value);
else if (sensor.equals("w01001")) data.setPhAvg(value);
else if (sensor.equals("w40016")) data.setHardAvg(value);
else if (sensor.equals("w21007")) data.setAcidAvg(value);
else if (sensor.equals("w01010")) data.setTempAvg(value);
else if (sensor.equals("w01006")) data.setDissolveAvg(value);
}
);
}
return data;
}
private static void extractAndSetTimestamp(String rawData, GmGroundwaterModbusVO data) {
int dtStart = rawData.indexOf("DataTime=");
if (dtStart == -1) return;
dtStart += 9; // "DataTime=".length()
int dtEnd = rawData.indexOf(";", dtStart);
if (dtEnd == -1) dtEnd = rawData.length();
String rawTime = rawData.substring(dtStart, dtEnd);
try {
SimpleDateFormat inputFormat = new SimpleDateFormat("yyyyMMddHHmmss");
Date date = inputFormat.parse(rawTime);
data.setDatetimes(String.valueOf(date.getTime()));
} catch (ParseException e) {
System.err.println("时间格式错误: " + rawTime);
}
}
private static void extractAndSetImei(String rawData, GmGroundwaterModbusVO data) {
int mnStart = rawData.indexOf("MN=");
if (mnStart == -1) return;
mnStart += 3; // "MN=".length()
int mnEnd = rawData.indexOf(";", mnStart);
if (mnEnd == -1) mnEnd = rawData.length();
data.setImei(rawData.substring(mnStart, mnEnd));
}
private static void extractAndSetValue(String rawData, String sensor, String field,
int fieldLength, Consumer<String> setter) {
String prefix = sensor + "-" + field;
int start = rawData.indexOf(prefix);
if (start == -1) return;
start += prefix.length();
int end = rawData.indexOf(",", start);
if (end == -1) end = rawData.length();
String value = rawData.substring(start, end).trim();
setter.accept(value); // 调用 accept
}
private static void setField(GmGroundwaterModbusVO data, String fieldName, String value) {
if (value == null) return;
try {
Field field = GmGroundwaterModbusVO.class.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(data, value);
} catch (Exception e) {
System.err.println("Failed to set field: " + fieldName + " with value: " + value);
}
}
// 处理HJ212协议数据
private static void processHJ212Data(String rawData) {
try {
if(rawData.contains("Avg")||rawData.contains("Min")||rawData.contains("Max")||rawData.contains("Rtd")){
GmGroundwaterModbusVO data = parseData(rawData);
System.out.println(data);
// 插入数据库
gmDeviceService.insertGmDeviceData(data);
}
// 1. 基础格式校验
if (!rawData.startsWith(HEADER)){
throw new IllegalArgumentException("无效数据包头");
}
if (!rawData.endsWith(TAIL)) {
throw new IllegalArgumentException("无效数据包尾");
}
// 2. 提取长度字段
String lengthStr = rawData.substring(HEADER.length(), HEADER.length() + LENGTH_FIELD_SIZE);
int dataLength;
try {
dataLength = Integer.parseInt(lengthStr);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("长度字段格式错误: " + lengthStr);
}
// 3. 提取数据段和CRC
int dataStart = HEADER.length() + LENGTH_FIELD_SIZE;
int dataEnd = dataStart + dataLength;
if (rawData.length() < dataEnd + CRC_FIELD_SIZE) {
throw new IllegalArgumentException("数据包长度不足");
}
String dataSegment = rawData.substring(dataStart, dataEnd);
String receivedCRC = rawData.substring(dataEnd, dataEnd + CRC_FIELD_SIZE);
// 4. CRC校验
String calculatedCRC = calculateCRC(dataSegment);
if (!receivedCRC.equalsIgnoreCase(calculatedCRC)) {
throw new SecurityException("CRC校验失败,收到: " + receivedCRC + " 计算: " + calculatedCRC);
}
// 5. 解析数据段
PollutionData pollutionData = parseDataSegment(dataSegment);
// 6. 存储到数据库
saveToDatabase(pollutionData);
System.out.println("数据处理成功: " + pollutionData.getDeviceId());
} catch (Exception e) {
System.err.println("数据处理失败: " + e.getMessage());
}
}
// CRC16校验算法
private static String calculateCRC(String data) {
int crc = 0xFFFF;
for (char c : data.toCharArray()) {
crc ^= (int) c;
for (int i = 0; i < 8; i++) {
if ((crc & 0x0001) == 1) {
crc = (crc >>> 1) ^ 0xA001;
} else {
crc >>>= 1;
}
}
}
return String.format("%04X", crc & 0xFFFF);
}
// 解析数据段
private static PollutionData parseDataSegment(String dataSegment) {
PollutionData data = new PollutionData();
// 提取设备ID
Matcher matcher = Pattern.compile("MN=([^;]+);").matcher(dataSegment);
if (matcher.find()) data.setDeviceId(matcher.group(1));
// 提取时间戳
matcher = Pattern.compile("DataTime=(\\d{14})").matcher(dataSegment);
if (matcher.find()) data.setTimestamp(parseDateTime(matcher.group(1)));
// 解析污染因子
matcher = Pattern.compile("(a\\d{5})-Rtd=([\\d.]+)").matcher(dataSegment);
while (matcher.find()) {
String factorCode = matcher.group(1);
double value = Double.parseDouble(matcher.group(2));
data.addPollutionFactor(factorCode, value);
}
return data;
}
private static LocalDateTime parseDateTime(String dtStr) {
return LocalDateTime.of(
Integer.parseInt(dtStr.substring(0, 4)),
Integer.parseInt(dtStr.substring(4, 6)),
Integer.parseInt(dtStr.substring(6, 8)),
Integer.parseInt(dtStr.substring(8, 10)),
Integer.parseInt(dtStr.substring(10, 12)),
Integer.parseInt(dtStr.substring(12, 14))
);
}
private static void saveToDatabase(PollutionData data) {
// 数据库存储实现 (示例)
try {
// 实际实现应替换为JDBC、MyBatis等持久化方案
System.out.printf("[存储] 设备: %s, 时间: %s, 数据: %s%n",
data.getDeviceId(), data.getTimestamp(), data.getFactors());
// 模拟数据库操作
Thread.sleep(50);
} catch (Exception e) {
System.err.println("存储异常: " + e.getMessage());
}
}
// 污染数据实体类
static class PollutionData {
private String deviceId;
private LocalDateTime timestamp;
private final Map<String, Double> factors = new HashMap<>();
public void addPollutionFactor(String code, double value) {
factors.put(code, value);
}
// getters
public String getDeviceId() { return deviceId; }
public LocalDateTime getTimestamp() { return timestamp; }
public Map<String, Double> getFactors() { return factors; }
public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
public void setTimestamp(LocalDateTime timestamp) { this.timestamp = timestamp; }
}
}
我这给代码如何改呢
最新发布