33 changed files with 1670 additions and 236 deletions
@ -0,0 +1,111 @@ |
|||||||
|
package com.mh.user.constants; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author LJF |
||||||
|
* @version 1.0 |
||||||
|
* @project TAD_Server |
||||||
|
* @description 4G设备常量值 |
||||||
|
* @date 2023/7/3 18:14:12 |
||||||
|
*/ |
||||||
|
public enum FourthGEnum { |
||||||
|
|
||||||
|
LOGIN_HEART_RESPONSE("0D", "登录和心跳确认控制码"), |
||||||
|
|
||||||
|
LOGIN_HEART_REQUEST("8D", "登录和心跳请求控制码"), |
||||||
|
|
||||||
|
LOGIN_HEART_EXCEPTION("CD", "登录和心跳异常应答控制码"), |
||||||
|
|
||||||
|
LOGIN_TYPE("02", "登录类型"), |
||||||
|
|
||||||
|
HEART_TYPE("01", "心跳类型"), |
||||||
|
|
||||||
|
CLOSE_CONTROL_TYPE("1C", "合闸指令"), |
||||||
|
|
||||||
|
OPEN_CONTROL_TYPE("1A", "跳闸指令"), |
||||||
|
|
||||||
|
DATA_ACTIVE_UPLOAD("8E", "数据主动上报控制码"), |
||||||
|
|
||||||
|
PULL_AND_CLOSE_DEVICE_REQUEST("1C", "跳合闸请求控制码"), |
||||||
|
|
||||||
|
PULL_AND_CLOSE_DEVICE_RESPONSE("9C", "跳合闸应答控制码"), |
||||||
|
|
||||||
|
PULL_AND_CLOSE_DEVICE_EXCEPTION("DC", "跳合闸应答异常控制码"), |
||||||
|
|
||||||
|
CLEAR_DEVICE_REQUEST("1A", "电表清零请求控制码"), |
||||||
|
|
||||||
|
CLEAR_DEVICE_RESPONSE("9A", "电表清零应答控制码"), |
||||||
|
|
||||||
|
CLEAR_DEVICE_EXCEPTION("DA", "电表清零应答异常控制码"), |
||||||
|
|
||||||
|
READ_DEVICE_REQUEST("11", "读电表请求控制码"), |
||||||
|
|
||||||
|
READ_DEVICE_RESPONSE("91", "读电表应答控制码"), |
||||||
|
|
||||||
|
READ_DEVICE_EXCEPTION("D1", "读电表应答异常控制码"), |
||||||
|
|
||||||
|
WRITE_DEVICE_REQUEST("14", "写电表请求控制码"), |
||||||
|
|
||||||
|
WRITE_DEVICE_RESPONSE("94", "读电表应答控制码"), |
||||||
|
WRITE_DEVICE_IP("94", "写电表IP应答控制码"), |
||||||
|
|
||||||
|
WRITE_DEVICE_EXCEPTION("D4", "读电表应答异常控制码"), |
||||||
|
|
||||||
|
CHARGE_DEVICE_REQUEST("0F", "充值指令"), |
||||||
|
|
||||||
|
CHARGE_DEVICE_RESPONSE("8F", "充值正确应答控制码"), |
||||||
|
|
||||||
|
CHARGE_DEVICE_EXCEPTION("CF", "充值异常应答控制码"), |
||||||
|
|
||||||
|
READ_TOTAL_CHARGE_IDENTIFY_CODE("00000000", "当前组合有功总电能"), |
||||||
|
|
||||||
|
READ_STATUS_IDENTIFY_CODE("03050004", "读电表运行状态"), |
||||||
|
READ_UPLOAD_CODE("02008104", "读取上报报文"), |
||||||
|
|
||||||
|
TY_UPLOAD_CODE("55C2", "腾越水表上报标识"), |
||||||
|
|
||||||
|
WRITE_CHARGE_TIME_IDENTIFY_CODE("07008104", "写入充值次数"), |
||||||
|
|
||||||
|
WRITE_LIMIT_POWER("10008104","写入限容功率"), |
||||||
|
READ_LIMIT_POWER("10008104","读取限容功率"), |
||||||
|
READ_SOFT_VERSION("01008004","读软件版本"), |
||||||
|
READ_HARD_VERSION("02008004","读硬件版本"), |
||||||
|
READ_ICCID("05008104","读取ICCID"), |
||||||
|
READ_IMEI("04008104","读取IMEI"), |
||||||
|
WRITE_IP("0B008104","设置IP端口号"), |
||||||
|
READ_IP("0B008104","读取IP端口号"), |
||||||
|
UPGRADES("13008104","触发升级,写1触发升级") |
||||||
|
; |
||||||
|
|
||||||
|
private String code; |
||||||
|
|
||||||
|
private String desc; |
||||||
|
|
||||||
|
FourthGEnum(String code, String desc) { |
||||||
|
this.code = code; |
||||||
|
this.desc = desc; |
||||||
|
} |
||||||
|
|
||||||
|
public String getCode() { |
||||||
|
return code; |
||||||
|
} |
||||||
|
|
||||||
|
public void setCode(String code) { |
||||||
|
this.code = code; |
||||||
|
} |
||||||
|
|
||||||
|
public String getDesc() { |
||||||
|
return desc; |
||||||
|
} |
||||||
|
|
||||||
|
public void setDesc(String desc) { |
||||||
|
this.desc = desc; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
return "FourthGEnum{" + |
||||||
|
"code='" + code + '\'' + |
||||||
|
", desc='" + desc + '\'' + |
||||||
|
'}'; |
||||||
|
} |
||||||
|
} |
||||||
@ -1,159 +1,159 @@ |
|||||||
package com.mh.user.job; |
//package com.mh.user.job;
|
||||||
|
|
||||||
import com.mh.user.constants.SocketMessage; |
|
||||||
import com.mh.user.entity.GatewayManageEntity; |
|
||||||
import com.mh.user.serialport.SerialPortThread; |
|
||||||
import com.mh.user.service.DeviceCodeParamService; |
|
||||||
import com.mh.user.service.impl.DeviceDisplayServiceImpl; |
|
||||||
import com.mh.user.utils.GetReadOrder485; |
|
||||||
import lombok.SneakyThrows; |
|
||||||
import lombok.extern.slf4j.Slf4j; |
|
||||||
import org.quartz.DisallowConcurrentExecution; |
|
||||||
import org.quartz.Job; |
|
||||||
import org.quartz.JobExecutionContext; |
|
||||||
import org.quartz.JobExecutionException; |
|
||||||
import org.springframework.beans.factory.annotation.Autowired; |
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
/** |
|
||||||
* @author ljf |
|
||||||
* @title : |
|
||||||
* @description : 定时采集冷量计任务 |
|
||||||
* @updateTime 2020-05-18 |
|
||||||
* @throws : |
|
||||||
*/ |
|
||||||
/** |
|
||||||
* :@DisallowConcurrentExecution : 此标记用在实现Job的类上面,意思是不允许并发执行. |
|
||||||
* :注意org.quartz.threadPool.threadCount线程池中线程的数量至少要多个,否则@DisallowConcurrentExecution不生效 |
|
||||||
* :假如Job的设置时间间隔为3秒,但Job执行时间是5秒,设置@DisallowConcurrentExecution以后程序会等任务执行完毕以后再去执行, |
|
||||||
* 否则会在3秒时再启用新的线程执行 |
|
||||||
*/ |
|
||||||
@DisallowConcurrentExecution |
|
||||||
@Slf4j |
|
||||||
public class JobCloudAndMeter implements Job { |
|
||||||
|
|
||||||
@Autowired |
|
||||||
private SocketMessage socketMessage; |
|
||||||
|
|
||||||
@Autowired |
|
||||||
DeviceDisplayServiceImpl.GatewayManageService gatewayManageService; |
|
||||||
|
|
||||||
@Autowired |
|
||||||
DeviceCodeParamService deviceCodeParamService; |
|
||||||
|
|
||||||
private static int taskTimes = 1; |
|
||||||
List<GatewayManageEntity> gatewayDtoList=new ArrayList<>(); |
|
||||||
GetReadOrder485 getReadOrder485=new GetReadOrder485(); |
|
||||||
|
|
||||||
@SneakyThrows |
|
||||||
@Override |
|
||||||
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { |
|
||||||
log.info("定时采集开始>>>>>>"); |
|
||||||
if(taskTimes==2){//2
|
|
||||||
int r=deviceCodeParamService.queryCount2(); //查询记录数
|
|
||||||
if (r==0){ |
|
||||||
getReadOrder485.createOrderParam2(); //生成采集参数
|
|
||||||
} |
|
||||||
|
|
||||||
SerialPortThread myThread = new SerialPortThread(); |
|
||||||
Thread thread = new Thread(myThread); |
|
||||||
myThread.setName("2","1"); |
|
||||||
thread.start(); |
|
||||||
System.out.println("当前活动线程数:"+Thread.activeCount()); |
|
||||||
Thread.currentThread().sleep(3000);//毫秒
|
|
||||||
|
|
||||||
// SerialPortThread myThread2 = new SerialPortThread();
|
|
||||||
// Thread thread2 = new Thread(myThread2);
|
|
||||||
// myThread2.setName("2","2");
|
|
||||||
// thread2.start();
|
|
||||||
// System.out.println("当前活动线程数:"+Thread.activeCount());
|
|
||||||
// Thread.currentThread().sleep(3000);//毫秒
|
|
||||||
//
|
//
|
||||||
// SerialPortThread myThread3 = new SerialPortThread();
|
//import com.mh.user.constants.SocketMessage;
|
||||||
// Thread thread3 = new Thread(myThread3);
|
//import com.mh.user.entity.GatewayManageEntity;
|
||||||
// myThread3.setName("2","3");
|
//import com.mh.user.serialport.SerialPortThread;
|
||||||
// thread3.start();
|
//import com.mh.user.service.DeviceCodeParamService;
|
||||||
// System.out.println("当前活动线程数:"+Thread.activeCount());
|
//import com.mh.user.service.GatewayManageService;
|
||||||
// Thread.currentThread().sleep(3000);//毫秒
|
//import com.mh.user.utils.GetReadOrder485;
|
||||||
|
//import lombok.SneakyThrows;
|
||||||
|
//import lombok.extern.slf4j.Slf4j;
|
||||||
|
//import org.quartz.DisallowConcurrentExecution;
|
||||||
|
//import org.quartz.Job;
|
||||||
|
//import org.quartz.JobExecutionContext;
|
||||||
|
//import org.quartz.JobExecutionException;
|
||||||
|
//import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
//import java.util.ArrayList;
|
||||||
|
//import java.util.List;
|
||||||
//
|
//
|
||||||
// SerialPortThread myThread4= new SerialPortThread();
|
///**
|
||||||
// Thread thread4 = new Thread(myThread4);
|
// * @author ljf
|
||||||
// myThread4.setName("2","4");
|
// * @title :
|
||||||
// thread4.start();
|
// * @description : 定时采集冷量计任务
|
||||||
// System.out.println("当前活动线程数:"+Thread.activeCount());
|
// * @updateTime 2020-05-18
|
||||||
// Thread.currentThread().sleep(3000);//毫秒
|
// * @throws :
|
||||||
System.out.println("采集水、电、运行状态!"+taskTimes); |
// */
|
||||||
taskTimes++; |
///**
|
||||||
}else if(taskTimes==3){//5
|
// * :@DisallowConcurrentExecution : 此标记用在实现Job的类上面,意思是不允许并发执行.
|
||||||
int r=deviceCodeParamService.queryCount3();//查询记录数
|
// * :注意org.quartz.threadPool.threadCount线程池中线程的数量至少要多个,否则@DisallowConcurrentExecution不生效
|
||||||
if (r==0){ |
// * :假如Job的设置时间间隔为3秒,但Job执行时间是5秒,设置@DisallowConcurrentExecution以后程序会等任务执行完毕以后再去执行,
|
||||||
getReadOrder485.createOrderParam3(); //生成采集参数
|
// * 否则会在3秒时再启用新的线程执行
|
||||||
} |
// */
|
||||||
|
//@DisallowConcurrentExecution
|
||||||
SerialPortThread myThread = new SerialPortThread(); |
//@Slf4j
|
||||||
Thread thread = new Thread(myThread); |
//public class JobCloudAndMeter implements Job {
|
||||||
myThread.setName("3","1"); |
|
||||||
thread.start(); |
|
||||||
System.out.println("当前活动线程数:"+Thread.activeCount()); |
|
||||||
Thread.currentThread().sleep(3000);//毫秒
|
|
||||||
|
|
||||||
// SerialPortThread myThread2 = new SerialPortThread();
|
|
||||||
// Thread thread2 = new Thread(myThread2);
|
|
||||||
// myThread2.setName("3","2");
|
|
||||||
// thread2.start();
|
|
||||||
// System.out.println("当前活动线程数:"+Thread.activeCount());
|
|
||||||
// Thread.currentThread().sleep(3000);//毫秒
|
|
||||||
//
|
//
|
||||||
// SerialPortThread myThread3 = new SerialPortThread();
|
// @Autowired
|
||||||
// Thread thread3 = new Thread(myThread3);
|
// private SocketMessage socketMessage;
|
||||||
// myThread3.setName("3","3");
|
|
||||||
// thread3.start();
|
|
||||||
// System.out.println("当前活动线程数:"+Thread.activeCount());
|
|
||||||
// Thread.currentThread().sleep(3000);//毫秒
|
|
||||||
//
|
//
|
||||||
// SerialPortThread myThread4= new SerialPortThread();
|
// @Autowired
|
||||||
// Thread thread4 = new Thread(myThread4);
|
// GatewayManageService gatewayManageService;
|
||||||
// myThread4.setName("3","4");
|
//
|
||||||
// thread4.start();
|
// @Autowired
|
||||||
// System.out.println("当前活动线程数:"+Thread.activeCount());
|
// DeviceCodeParamService deviceCodeParamService;
|
||||||
// Thread.currentThread().sleep(3000);//毫秒
|
//
|
||||||
System.out.println("采集设定温度、设定水位、故障状态!"+taskTimes); |
// private static int taskTimes = 1;
|
||||||
taskTimes=1; |
// List<GatewayManageEntity> gatewayDtoList=new ArrayList<>();
|
||||||
}else if(taskTimes==1){ |
// GetReadOrder485 getReadOrder485=new GetReadOrder485();
|
||||||
int r=deviceCodeParamService.queryCount(); //查询记录数
|
//
|
||||||
if (r==0){ |
// @SneakyThrows
|
||||||
getReadOrder485.createOrderParam(); //生成采集参数
|
// @Override
|
||||||
} |
// public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
|
||||||
SerialPortThread myThread = new SerialPortThread(); |
// log.info("定时采集开始>>>>>>");
|
||||||
Thread thread = new Thread(myThread); |
// if(taskTimes==2){//2
|
||||||
myThread.setName("1","1"); |
// int r=deviceCodeParamService.queryCount2(); //查询记录数
|
||||||
thread.start(); |
// if (r==0){
|
||||||
System.out.println("当前活动线程数:"+Thread.activeCount()); |
// getReadOrder485.createOrderParam2(); //生成采集参数
|
||||||
Thread.currentThread().sleep(3000);//毫秒
|
// }
|
||||||
|
//
|
||||||
// SerialPortThread myThread2 = new SerialPortThread();
|
// SerialPortThread myThread = new SerialPortThread();
|
||||||
// Thread thread2 = new Thread(myThread2);
|
// Thread thread = new Thread(myThread);
|
||||||
// myThread2.setName("1","2");
|
// myThread.setName("2","1");
|
||||||
// thread2.start();
|
// thread.start();
|
||||||
// System.out.println("当前活动线程数:"+Thread.activeCount());
|
// System.out.println("当前活动线程数:"+Thread.activeCount());
|
||||||
// Thread.currentThread().sleep(3000);//毫秒
|
// Thread.currentThread().sleep(3000);//毫秒
|
||||||
//
|
//
|
||||||
// SerialPortThread myThread3 = new SerialPortThread();
|
//// SerialPortThread myThread2 = new SerialPortThread();
|
||||||
// Thread thread3 = new Thread(myThread3);
|
//// Thread thread2 = new Thread(myThread2);
|
||||||
// myThread3.setName("1","3");
|
//// myThread2.setName("2","2");
|
||||||
// thread3.start();
|
//// thread2.start();
|
||||||
|
//// System.out.println("当前活动线程数:"+Thread.activeCount());
|
||||||
|
//// Thread.currentThread().sleep(3000);//毫秒
|
||||||
|
////
|
||||||
|
//// SerialPortThread myThread3 = new SerialPortThread();
|
||||||
|
//// Thread thread3 = new Thread(myThread3);
|
||||||
|
//// myThread3.setName("2","3");
|
||||||
|
//// thread3.start();
|
||||||
|
//// System.out.println("当前活动线程数:"+Thread.activeCount());
|
||||||
|
//// Thread.currentThread().sleep(3000);//毫秒
|
||||||
|
////
|
||||||
|
//// SerialPortThread myThread4= new SerialPortThread();
|
||||||
|
//// Thread thread4 = new Thread(myThread4);
|
||||||
|
//// myThread4.setName("2","4");
|
||||||
|
//// thread4.start();
|
||||||
|
//// System.out.println("当前活动线程数:"+Thread.activeCount());
|
||||||
|
//// Thread.currentThread().sleep(3000);//毫秒
|
||||||
|
// System.out.println("采集水、电、运行状态!"+taskTimes);
|
||||||
|
// taskTimes++;
|
||||||
|
// }else if(taskTimes==3){//5
|
||||||
|
// int r=deviceCodeParamService.queryCount3();//查询记录数
|
||||||
|
// if (r==0){
|
||||||
|
// getReadOrder485.createOrderParam3(); //生成采集参数
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// SerialPortThread myThread = new SerialPortThread();
|
||||||
|
// Thread thread = new Thread(myThread);
|
||||||
|
// myThread.setName("3","1");
|
||||||
|
// thread.start();
|
||||||
// System.out.println("当前活动线程数:"+Thread.activeCount());
|
// System.out.println("当前活动线程数:"+Thread.activeCount());
|
||||||
// Thread.currentThread().sleep(3000);//毫秒
|
// Thread.currentThread().sleep(3000);//毫秒
|
||||||
//
|
//
|
||||||
// SerialPortThread myThread4= new SerialPortThread();
|
//// SerialPortThread myThread2 = new SerialPortThread();
|
||||||
// Thread thread4 = new Thread(myThread4);
|
//// Thread thread2 = new Thread(myThread2);
|
||||||
// myThread4.setName("1","4");
|
//// myThread2.setName("3","2");
|
||||||
// thread4.start();
|
//// thread2.start();
|
||||||
|
//// System.out.println("当前活动线程数:"+Thread.activeCount());
|
||||||
|
//// Thread.currentThread().sleep(3000);//毫秒
|
||||||
|
////
|
||||||
|
//// SerialPortThread myThread3 = new SerialPortThread();
|
||||||
|
//// Thread thread3 = new Thread(myThread3);
|
||||||
|
//// myThread3.setName("3","3");
|
||||||
|
//// thread3.start();
|
||||||
|
//// System.out.println("当前活动线程数:"+Thread.activeCount());
|
||||||
|
//// Thread.currentThread().sleep(3000);//毫秒
|
||||||
|
////
|
||||||
|
//// SerialPortThread myThread4= new SerialPortThread();
|
||||||
|
//// Thread thread4 = new Thread(myThread4);
|
||||||
|
//// myThread4.setName("3","4");
|
||||||
|
//// thread4.start();
|
||||||
|
//// System.out.println("当前活动线程数:"+Thread.activeCount());
|
||||||
|
//// Thread.currentThread().sleep(3000);//毫秒
|
||||||
|
// System.out.println("采集设定温度、设定水位、故障状态!"+taskTimes);
|
||||||
|
// taskTimes=1;
|
||||||
|
// }else if(taskTimes==1){
|
||||||
|
// int r=deviceCodeParamService.queryCount(); //查询记录数
|
||||||
|
// if (r==0){
|
||||||
|
// getReadOrder485.createOrderParam(); //生成采集参数
|
||||||
|
// }
|
||||||
|
// SerialPortThread myThread = new SerialPortThread();
|
||||||
|
// Thread thread = new Thread(myThread);
|
||||||
|
// myThread.setName("1","1");
|
||||||
|
// thread.start();
|
||||||
// System.out.println("当前活动线程数:"+Thread.activeCount());
|
// System.out.println("当前活动线程数:"+Thread.activeCount());
|
||||||
// Thread.currentThread().sleep(3000);//毫秒
|
// Thread.currentThread().sleep(3000);//毫秒
|
||||||
System.out.println("采集水位、水温!"+taskTimes); |
//
|
||||||
taskTimes++; |
//// SerialPortThread myThread2 = new SerialPortThread();
|
||||||
} |
//// Thread thread2 = new Thread(myThread2);
|
||||||
} |
//// myThread2.setName("1","2");
|
||||||
} |
//// thread2.start();
|
||||||
|
//// System.out.println("当前活动线程数:"+Thread.activeCount());
|
||||||
|
//// Thread.currentThread().sleep(3000);//毫秒
|
||||||
|
////
|
||||||
|
//// SerialPortThread myThread3 = new SerialPortThread();
|
||||||
|
//// Thread thread3 = new Thread(myThread3);
|
||||||
|
//// myThread3.setName("1","3");
|
||||||
|
//// thread3.start();
|
||||||
|
//// System.out.println("当前活动线程数:"+Thread.activeCount());
|
||||||
|
//// Thread.currentThread().sleep(3000);//毫秒
|
||||||
|
////
|
||||||
|
//// SerialPortThread myThread4= new SerialPortThread();
|
||||||
|
//// Thread thread4 = new Thread(myThread4);
|
||||||
|
//// myThread4.setName("1","4");
|
||||||
|
//// thread4.start();
|
||||||
|
//// System.out.println("当前活动线程数:"+Thread.activeCount());
|
||||||
|
//// Thread.currentThread().sleep(3000);//毫秒
|
||||||
|
// System.out.println("采集水位、水温!"+taskTimes);
|
||||||
|
// taskTimes++;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|||||||
@ -0,0 +1,53 @@ |
|||||||
|
package com.mh.user.netty; |
||||||
|
|
||||||
|
import com.mh.user.netty.handle.FourthChannelInitializer; |
||||||
|
import io.netty.bootstrap.ServerBootstrap; |
||||||
|
import io.netty.channel.*; |
||||||
|
import io.netty.channel.nio.NioEventLoopGroup; |
||||||
|
import io.netty.channel.socket.nio.NioServerSocketChannel; |
||||||
|
import io.netty.handler.logging.LogLevel; |
||||||
|
import io.netty.handler.logging.LoggingHandler; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @author LJF |
||||||
|
* @title :Netty |
||||||
|
* @description :netty 使用 |
||||||
|
* @updateTime 2020-04-21 |
||||||
|
* @throws : |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public class NettyEchoServer { |
||||||
|
|
||||||
|
public void bind(int port) throws Exception { |
||||||
|
// accept线程组,用来接收连接
|
||||||
|
EventLoopGroup bossGroup = new NioEventLoopGroup(2); |
||||||
|
// IO 线程组,用来处理业务逻辑
|
||||||
|
EventLoopGroup workerGroup = new NioEventLoopGroup(4); |
||||||
|
|
||||||
|
try { |
||||||
|
// 服务端启动引导
|
||||||
|
ServerBootstrap serverBootstrap = new ServerBootstrap(); |
||||||
|
serverBootstrap.group(bossGroup,workerGroup) // 绑定两个线程
|
||||||
|
.channel(NioServerSocketChannel.class) // 指定通道类型
|
||||||
|
.option(ChannelOption.SO_BACKLOG, 1024) // 设置TCP连接的缓冲区
|
||||||
|
.option(ChannelOption.SO_KEEPALIVE, true) |
||||||
|
.handler(new LoggingHandler(LogLevel.INFO)) // 设置日志级别
|
||||||
|
.childHandler(new FourthChannelInitializer()); // 初始化handle
|
||||||
|
// 通过bind启动服务
|
||||||
|
ChannelFuture f = serverBootstrap.bind(port).sync(); |
||||||
|
// 阻塞主线程,知道网络服务被关闭
|
||||||
|
//f.channel().closeFuture().sync();
|
||||||
|
f.channel().closeFuture().addListener(future -> { |
||||||
|
log.info("Netty服务器已关闭"); |
||||||
|
workerGroup.shutdownGracefully(); |
||||||
|
bossGroup.shutdownGracefully(); |
||||||
|
}); |
||||||
|
} catch (Exception e){ |
||||||
|
log.error("netty服务器异常", e); |
||||||
|
workerGroup.shutdownGracefully(); |
||||||
|
bossGroup.shutdownGracefully(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,63 @@ |
|||||||
|
package com.mh.user.netty.decoder; |
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf; |
||||||
|
import io.netty.channel.ChannelHandlerContext; |
||||||
|
import io.netty.handler.codec.MessageToByteEncoder; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
|
||||||
|
@Slf4j |
||||||
|
public class CustomFrameEncoder extends MessageToByteEncoder<String> { |
||||||
|
private static final byte FRAME_START = 0x68; |
||||||
|
private static final byte FRAME_END = 0x16; |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void encode(ChannelHandlerContext ctx, String msg, ByteBuf out) throws Exception { |
||||||
|
log.info("数据到达:"+msg); |
||||||
|
// Write first frame start
|
||||||
|
out.writeByte(FRAME_START); |
||||||
|
|
||||||
|
// Write meter number
|
||||||
|
byte[] meterNumberBytes = hexStringToByteArray(msg.substring(2, 14)); // Extract meter number from message
|
||||||
|
out.writeBytes(meterNumberBytes); |
||||||
|
|
||||||
|
// Write second frame start
|
||||||
|
out.writeByte(FRAME_START); |
||||||
|
|
||||||
|
// Write control code
|
||||||
|
byte controlCode = hexStringToByteArray(msg.substring(14, 16))[0]; // Extract control code from message
|
||||||
|
out.writeByte(controlCode); |
||||||
|
|
||||||
|
// Write data length
|
||||||
|
int dataLength = Integer.parseInt(msg.substring(16, 18), 16); // Extract data length from message
|
||||||
|
out.writeByte(dataLength); |
||||||
|
|
||||||
|
// Write data
|
||||||
|
byte[] dataBytes = hexStringToByteArray(msg.substring(18, 18 + (dataLength * 2))); // Extract data from message
|
||||||
|
out.writeBytes(dataBytes); |
||||||
|
|
||||||
|
// Calculate and write checksum
|
||||||
|
byte checksum = calculateChecksum(msg.substring(14, 18 + (dataLength * 2))); // Calculate checksum
|
||||||
|
out.writeByte(checksum); |
||||||
|
|
||||||
|
// Write frame end
|
||||||
|
out.writeByte(FRAME_END); |
||||||
|
} |
||||||
|
|
||||||
|
private byte[] hexStringToByteArray(String hexString) { |
||||||
|
int len = hexString.length(); |
||||||
|
byte[] data = new byte[len / 2]; |
||||||
|
for (int i = 0; i < len; i += 2) { |
||||||
|
data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) |
||||||
|
+ Character.digit(hexString.charAt(i + 1), 16)); |
||||||
|
} |
||||||
|
return data; |
||||||
|
} |
||||||
|
|
||||||
|
private byte calculateChecksum(String message) { |
||||||
|
byte checksum = 0; |
||||||
|
for (int i = 0; i < message.length(); i += 2) { |
||||||
|
checksum += Integer.parseInt(message.substring(i, i + 2), 16); |
||||||
|
} |
||||||
|
return (byte) (checksum & 0xFF); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,50 @@ |
|||||||
|
package com.mh.user.netty.handle; |
||||||
|
|
||||||
|
import com.github.benmanes.caffeine.cache.Cache; |
||||||
|
import com.mh.user.constants.FourthGEnum; |
||||||
|
import com.mh.user.entity.GatewayManageEntity; |
||||||
|
import com.mh.user.netty.session.ServerSession; |
||||||
|
import com.mh.user.netty.session.SessionMap; |
||||||
|
import com.mh.user.utils.CacheUtil; |
||||||
|
import com.mh.user.utils.ExchangeStringUtil; |
||||||
|
import com.mh.user.utils.NettyTools; |
||||||
|
import com.mh.user.utils.SpringBeanUtil; |
||||||
|
import io.netty.channel.ChannelHandlerContext; |
||||||
|
import io.netty.channel.ChannelInboundHandlerAdapter; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.context.ApplicationContext; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
@Slf4j |
||||||
|
public class DataUploadServerHandler extends ChannelInboundHandlerAdapter { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { |
||||||
|
//将接收到的数据转为字符串,此字符串就是客户端发送的字符串
|
||||||
|
String receiveStr = ExchangeStringUtil.bytesToHexString((byte[]) msg); |
||||||
|
// 判断当前报文是否是上报数据报文
|
||||||
|
if (receiveStr != null) { |
||||||
|
// 判断属于哪一个DTU网关上报的数据
|
||||||
|
CacheUtil instance = CacheUtil.getInstance(); |
||||||
|
List<GatewayManageEntity> gwList = instance.getGatewayInfo(); |
||||||
|
if (gwList != null && !gwList.isEmpty()) { |
||||||
|
for (GatewayManageEntity gw : gwList) { |
||||||
|
if (receiveStr.startsWith(ExchangeStringUtil.str2HexStr(gw.getSn()))) { |
||||||
|
// 直接设置对应值
|
||||||
|
NettyTools.setReceiveMsg(gw.getHeartBeat(), receiveStr.substring(gw.getSn().length())); |
||||||
|
} else { |
||||||
|
// 判断是否登录,没有登录立马断开
|
||||||
|
String deviceCode = gw.getHeartBeat(); |
||||||
|
if (!SessionMap.inst().hasLogin(deviceCode+ctx.channel().remoteAddress())) { |
||||||
|
ServerSession.closeSession(ctx); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
super.channelRead(ctx, msg); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,50 @@ |
|||||||
|
package com.mh.user.netty.handle; |
||||||
|
|
||||||
|
import com.mh.user.netty.session.ServerSession; |
||||||
|
import io.netty.channel.ChannelHandlerContext; |
||||||
|
import io.netty.channel.ChannelInboundHandlerAdapter; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author LJF |
||||||
|
* @version 1.0 |
||||||
|
* @project TAD_Server |
||||||
|
* @description 异常处理 |
||||||
|
* @date 2023/7/5 09:02:03 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public class ExceptionServerHandler extends ChannelInboundHandlerAdapter { |
||||||
|
//private final ApplicationContext applicationContext = ApplicationContextProvider.getApplicationContext();
|
||||||
|
//private final MeterInfoService meterInfoService = applicationContext.getBean(MeterInfoService.class);
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { |
||||||
|
ctx.flush(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void channelInactive(ChannelHandlerContext ctx) throws Exception { |
||||||
|
log.info("{}客户端断开连接!", ctx.channel().remoteAddress()); |
||||||
|
//String meterNumIp = ServerSession.closeSession(ctx);
|
||||||
|
//String[] split = meterNumIp.split("/");
|
||||||
|
//String meterNum = split[0];
|
||||||
|
//if (meterNum != null) {
|
||||||
|
// meterInfoService.insertTestDetail(meterNum,"offline");
|
||||||
|
//}
|
||||||
|
ServerSession.closeSession(ctx); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { |
||||||
|
if (cause instanceof UnsupportedOperationException) { |
||||||
|
ServerSession.closeSession(ctx); |
||||||
|
} else { |
||||||
|
log.info("获得已知异常!",cause); |
||||||
|
ctx.close(); |
||||||
|
} |
||||||
|
log.info("获得已知异常!{}", cause.getMessage()); |
||||||
|
log.info("获得已知异常!", cause); |
||||||
|
ServerSession.closeSession(ctx); |
||||||
|
ctx.close(); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,34 @@ |
|||||||
|
package com.mh.user.netty.handle; |
||||||
|
|
||||||
|
import com.mh.user.netty.decoder.CustomFrameEncoder; |
||||||
|
import io.netty.channel.Channel; |
||||||
|
import io.netty.channel.ChannelInitializer; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author LJF |
||||||
|
* @version 1.0 |
||||||
|
* @project TAD_Server |
||||||
|
* @description 初始换一个服务端 |
||||||
|
* @date 2023/7/3 15:58:05 |
||||||
|
*/ |
||||||
|
public class FourthChannelInitializer extends ChannelInitializer<Channel> { |
||||||
|
@Override |
||||||
|
protected void initChannel(Channel channel) throws Exception { |
||||||
|
// 电表报文分隔符,一般都是16结尾
|
||||||
|
//ByteBuf delimiter = Unpooled.copiedBuffer(new byte[]{22});
|
||||||
|
//打印分隔符
|
||||||
|
//channel.pipeline().addLast("frameDecoder", new DelimiterBasedFrameDecoder(1024,false, delimiter));
|
||||||
|
//分割报文处理解码器,防止数据粘包
|
||||||
|
// channel.pipeline().addLast(new CustomFrameEncoder());
|
||||||
|
// 处理登录
|
||||||
|
channel.pipeline().addLast(new LoginRequestHandler()); |
||||||
|
// 处理心跳
|
||||||
|
channel.pipeline().addLast(new HeartBeatServerHandler()); |
||||||
|
// 处理数据上报数据
|
||||||
|
channel.pipeline().addLast(new DataUploadServerHandler()); |
||||||
|
// 异常处理
|
||||||
|
channel.pipeline().addLast(new ExceptionServerHandler()); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,102 @@ |
|||||||
|
package com.mh.user.netty.handle; |
||||||
|
|
||||||
|
import com.mh.user.model.SysLog; |
||||||
|
import com.mh.user.netty.session.ServerSession; |
||||||
|
import com.mh.user.service.GatewayManageService; |
||||||
|
import com.mh.user.service.SysLogService; |
||||||
|
import com.mh.user.utils.ExchangeStringUtil; |
||||||
|
import com.mh.user.utils.SpringContextUtils; |
||||||
|
import io.netty.channel.ChannelHandlerContext; |
||||||
|
import io.netty.handler.timeout.IdleStateEvent; |
||||||
|
import io.netty.handler.timeout.IdleStateHandler; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.context.ApplicationContext; |
||||||
|
|
||||||
|
import java.util.Date; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @author LJF |
||||||
|
* @version 1.0 |
||||||
|
* @project TAD_Server |
||||||
|
* @description 心跳包设置 |
||||||
|
* @date 2023/7/4 13:54:27 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public class HeartBeatServerHandler extends IdleStateHandler { |
||||||
|
|
||||||
|
private final ApplicationContext applicationContext = SpringContextUtils.getApplicationContext(); |
||||||
|
private final SysLogService sysLogService = applicationContext.getBean(SysLogService.class); |
||||||
|
private final GatewayManageService gatewayManageService = applicationContext.getBean(GatewayManageService.class); |
||||||
|
|
||||||
|
//目前网关默认心跳30秒,多长时间没有心跳,就关闭连接
|
||||||
|
private static final int READ_IDLE_GAP = 30; |
||||||
|
|
||||||
|
/** |
||||||
|
* @param readerIdleTimeSeconds 最长 没有 read到心跳的时间 |
||||||
|
* @param writerIdleTimeSeconds 心跳包我们只是进行接收心跳,没有所谓的写操作,我们将其设置为0 |
||||||
|
* @param allIdleTimeSeconds 如果我们定义 多长时间没有 读或写操作,我们使用这个参数。 |
||||||
|
*/ |
||||||
|
public HeartBeatServerHandler(int readerIdleTimeSeconds, int writerIdleTimeSeconds, int allIdleTimeSeconds) { |
||||||
|
super(readerIdleTimeSeconds, writerIdleTimeSeconds, allIdleTimeSeconds); |
||||||
|
} |
||||||
|
|
||||||
|
public HeartBeatServerHandler(long readerIdleTime, long writerIdleTime, long allIdleTime, TimeUnit unit) { |
||||||
|
super(readerIdleTime, writerIdleTime, allIdleTime, unit); |
||||||
|
} |
||||||
|
|
||||||
|
public HeartBeatServerHandler(boolean observeOutput, long readerIdleTime, long writerIdleTime, long allIdleTime, TimeUnit unit) { |
||||||
|
super(observeOutput, readerIdleTime, writerIdleTime, allIdleTime, unit); |
||||||
|
} |
||||||
|
|
||||||
|
public HeartBeatServerHandler() { |
||||||
|
super(READ_IDLE_GAP, 0, 0, TimeUnit.SECONDS); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { |
||||||
|
|
||||||
|
//将接收到的数据转为字符串,此字符串就是客户端发送的字符串
|
||||||
|
String receiveStr = ExchangeStringUtil.bytesToHexString((byte[]) msg); |
||||||
|
// 判断当前报文是否是登录报文
|
||||||
|
if (receiveStr != null && receiveStr.startsWith("2400")) { |
||||||
|
if (receiveStr.length() != 8) { |
||||||
|
super.channelRead(ctx, msg); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// 通过对应的心跳包码进行判断,然后更新网关在线情况
|
||||||
|
gatewayManageService.updateGatewayManageOnlineByHeartBeatCode(receiveStr, 0); |
||||||
|
|
||||||
|
// //进行心跳包的处理
|
||||||
|
// FutureTaskScheduler.add(() -> {
|
||||||
|
// if (ctx.channel().isActive()) {
|
||||||
|
// // 开始发送第一条采集数据报文
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
} |
||||||
|
super.channelRead(ctx, msg); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception { |
||||||
|
log.info("{} 已超过 " + READ_IDLE_GAP + " 秒没有接收到心跳包,已断开链接", ctx.channel().remoteAddress()); |
||||||
|
String imeiIP = ServerSession.closeSession(ctx); |
||||||
|
String[] split = imeiIP.split("/"); |
||||||
|
String imei = split[0]; |
||||||
|
if (imei != null) { |
||||||
|
// 添加到日志表,下线了
|
||||||
|
SysLog sysLog = new SysLog(); |
||||||
|
sysLog.setOperation(imei + "下线了"); |
||||||
|
sysLog.setCreateBy("开发者"); |
||||||
|
sysLog.setIp(imeiIP); |
||||||
|
sysLog.setCreateTime(new Date()); |
||||||
|
sysLogService.insertLog(sysLog); |
||||||
|
// 更新网关在线情况
|
||||||
|
gatewayManageService.updateGatewayManageOnlineByImei(imei, 1); |
||||||
|
} |
||||||
|
ctx.close(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,101 @@ |
|||||||
|
package com.mh.user.netty.handle; |
||||||
|
|
||||||
|
import com.mh.common.utils.StringUtils; |
||||||
|
import com.mh.user.netty.session.ServerSession; |
||||||
|
import com.mh.user.netty.session.SessionMap; |
||||||
|
import com.mh.user.netty.task.CallbackTask; |
||||||
|
import com.mh.user.netty.task.CallbackTaskScheduler; |
||||||
|
import com.mh.user.utils.ExchangeStringUtil; |
||||||
|
import io.netty.channel.ChannelHandlerContext; |
||||||
|
import io.netty.channel.ChannelInboundHandlerAdapter; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author LJF |
||||||
|
* @version 1.0 |
||||||
|
* @project TAD_Server |
||||||
|
* @description 登录报文处理 |
||||||
|
* @date 2023/7/3 15:34:11 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public class LoginRequestHandler extends ChannelInboundHandlerAdapter { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { |
||||||
|
//将接收到的数据转为字符串,此字符串就是客户端发送的字符串
|
||||||
|
String receiveStr = ExchangeStringUtil.bytesToHexString((byte[]) msg); |
||||||
|
if (StringUtils.isBlank(receiveStr)) { |
||||||
|
super.channelRead(ctx, msg); |
||||||
|
return; |
||||||
|
} |
||||||
|
// 判断当前报文是否是心跳包上线: 869530073040186
|
||||||
|
log.info("接收到的心跳报文 <== {}", receiveStr); |
||||||
|
// 获取IMEI号
|
||||||
|
String deviceCode = receiveStr; |
||||||
|
String meterNum = deviceCode; |
||||||
|
deviceCode = deviceCode + ctx.channel().remoteAddress(); |
||||||
|
//新的session的创建
|
||||||
|
ServerSession session = new ServerSession(ctx.channel(), deviceCode); |
||||||
|
|
||||||
|
//进行登录逻辑处理,异步进行处理。并且需要知道 处理的结果。 callbacktask就要
|
||||||
|
//派上用场了
|
||||||
|
String finalDeviceCode = deviceCode; |
||||||
|
CallbackTaskScheduler.add(new CallbackTask<Boolean>() { |
||||||
|
@Override |
||||||
|
public Boolean execute() throws Exception { |
||||||
|
//进行 login 逻辑的处理
|
||||||
|
return action(session, finalDeviceCode, ctx, meterNum); |
||||||
|
} |
||||||
|
|
||||||
|
//没有异常的话,我们进行处理
|
||||||
|
@Override |
||||||
|
public void onBack(Boolean result) { |
||||||
|
if (result) { |
||||||
|
log.info("设备登录成功: 设备号 = " + session.getSessionId()); |
||||||
|
//ctx.pipeline().remove(LoginRequestHandler.class); //压测需要放开
|
||||||
|
} else { |
||||||
|
log.info("设备刷新登录: 设备号 = " + session.getSessionId()); |
||||||
|
SessionMap.inst().updateSession(finalDeviceCode, session, meterNum); |
||||||
|
//log.info("设备登录失败: 设备号 = " + session.getSessionId());
|
||||||
|
//ServerSession.closeSession(ctx);
|
||||||
|
// 假如说已经在会话中了,直接断开连接
|
||||||
|
//ctx.close();
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//有异常的话,我们进行处理
|
||||||
|
@Override |
||||||
|
public void onException(Throwable t) { |
||||||
|
log.info("设备登录异常: 设备号 = " + session.getSessionId()); |
||||||
|
ServerSession.closeSession(ctx); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private boolean action(ServerSession session, String deviceCode, ChannelHandlerContext ctx, String meterNum) { |
||||||
|
//user验证
|
||||||
|
boolean isValidUser = checkUser(deviceCode, session); |
||||||
|
if (!isValidUser) { |
||||||
|
//我们发送登录成功的报文给 客户端
|
||||||
|
session.bind(); |
||||||
|
return false; |
||||||
|
} |
||||||
|
//我们发送登录成功的报文给 客户端
|
||||||
|
session.bind(); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean checkUser(String deviceCode, ServerSession session) { |
||||||
|
//当前用户已经登录
|
||||||
|
if (SessionMap.inst().hasLogin(deviceCode)) { |
||||||
|
log.info("设备已经登录: 设备号 = " + deviceCode); |
||||||
|
return false; |
||||||
|
} |
||||||
|
//一般情况下,我们会将 user存储到 DB中,然后对user的用户名和密码进行校验
|
||||||
|
//但是,我们这边没有进行db的集成,所以我们想一个别的办法进行user的校验。在我们的sessionMap进行以下校验
|
||||||
|
//为什么选sessionmap,因为我们user的会话,都是存储到sessionmap中的,sessionmap中只要有这个user的会话,说明就是ok的
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,66 @@ |
|||||||
|
package com.mh.user.netty.session; |
||||||
|
|
||||||
|
import io.netty.channel.Channel; |
||||||
|
import io.netty.channel.ChannelFuture; |
||||||
|
import io.netty.channel.ChannelFutureListener; |
||||||
|
import io.netty.channel.ChannelHandlerContext; |
||||||
|
import io.netty.util.AttributeKey; |
||||||
|
import lombok.Data; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
|
||||||
|
@Data |
||||||
|
@Slf4j |
||||||
|
public class ServerSession { |
||||||
|
public static final AttributeKey<ServerSession> SESSION_KEY = |
||||||
|
AttributeKey.valueOf("SESSION_KEY"); |
||||||
|
//通道
|
||||||
|
private Channel channel; |
||||||
|
private final String sessionId; |
||||||
|
private boolean isLogin = false; |
||||||
|
|
||||||
|
public ServerSession(Channel channel, String deviceCode){ |
||||||
|
this.channel = channel; |
||||||
|
this.sessionId = deviceCode; |
||||||
|
} |
||||||
|
|
||||||
|
//session需要和通道进行一定的关联,他是在构造函数中关联上的;
|
||||||
|
//session还需要通过sessionkey和channel进行再次的关联;channel.attr方法.set当前的
|
||||||
|
// serverSession
|
||||||
|
//session需要被添加到我们的SessionMap中
|
||||||
|
public void bind(){ |
||||||
|
log.info("server Session 会话进行绑定 :" + channel.remoteAddress()); |
||||||
|
channel.attr(SESSION_KEY).set(this); |
||||||
|
SessionMap.inst().addSession(sessionId, this); |
||||||
|
this.isLogin = true; |
||||||
|
} |
||||||
|
|
||||||
|
//通过channel获取session
|
||||||
|
public static ServerSession getSession(ChannelHandlerContext ctx){ |
||||||
|
Channel channel = ctx.channel(); |
||||||
|
return channel.attr(SESSION_KEY).get(); |
||||||
|
} |
||||||
|
|
||||||
|
//关闭session,新增返回一个meterNum用于纪录设备下线时间2024-05-08
|
||||||
|
public static String closeSession(ChannelHandlerContext ctx){ |
||||||
|
String meterNum = null; |
||||||
|
ServerSession serverSession = ctx.channel().attr(SESSION_KEY).get(); |
||||||
|
if(serverSession != null && serverSession.getSessionId() != null) { |
||||||
|
ChannelFuture future = serverSession.channel.close(); |
||||||
|
future.addListener((ChannelFutureListener) future1 -> { |
||||||
|
if(!future1.isSuccess()) { |
||||||
|
log.info("Channel close error!"); |
||||||
|
} |
||||||
|
}); |
||||||
|
ctx.close(); |
||||||
|
meterNum = serverSession.sessionId; |
||||||
|
SessionMap.inst().removeSession(serverSession.sessionId); |
||||||
|
log.info(ctx.channel().remoteAddress()+" "+serverSession.sessionId + "==>移除会话"); |
||||||
|
} |
||||||
|
return meterNum; |
||||||
|
} |
||||||
|
|
||||||
|
//写消息
|
||||||
|
public void writeAndFlush(Object msg) { |
||||||
|
channel.writeAndFlush(msg); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,96 @@ |
|||||||
|
package com.mh.user.netty.session; |
||||||
|
|
||||||
|
import lombok.Data; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
|
||||||
|
import java.util.Iterator; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
@Data |
||||||
|
@Slf4j |
||||||
|
public class SessionMap { |
||||||
|
|
||||||
|
private ThreadLocal<Boolean> sceneThreadLocal = new ThreadLocal<>(); |
||||||
|
|
||||||
|
//用单例模式进行sessionMap的创建
|
||||||
|
private SessionMap(){} |
||||||
|
|
||||||
|
private static SessionMap singleInstance = new SessionMap(); |
||||||
|
|
||||||
|
public static SessionMap inst() { |
||||||
|
return singleInstance; |
||||||
|
} |
||||||
|
|
||||||
|
//进行会话的保存
|
||||||
|
//key 我们使用 sessionId;value 需要是 serverSession
|
||||||
|
private ConcurrentHashMap<String, ServerSession> map = new ConcurrentHashMap<>(256); |
||||||
|
//添加session
|
||||||
|
public void addSession(String sessionId, ServerSession s) { |
||||||
|
map.put(sessionId, s); |
||||||
|
log.info("IP地址:"+s.getChannel().remoteAddress()+" "+ sessionId + " 表具上线,总共表具:" + map.size()); |
||||||
|
} |
||||||
|
|
||||||
|
//删除session
|
||||||
|
public void removeSession(String sessionId) { |
||||||
|
if(map.containsKey(sessionId)) { |
||||||
|
ServerSession s = map.get(sessionId); |
||||||
|
map.remove(sessionId); |
||||||
|
log.info("设备id下线:{},在线设备:{}", s.getSessionId(), map.size() ); |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean hasLogin(String sessionId) { |
||||||
|
Iterator<Map.Entry<String, ServerSession>> iterator = map.entrySet().iterator(); |
||||||
|
while(iterator.hasNext()) { |
||||||
|
Map.Entry<String, ServerSession> next = iterator.next(); |
||||||
|
if(sessionId != null && sessionId.equalsIgnoreCase(next.getValue().getSessionId())) { |
||||||
|
return true ; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
//如果在线,肯定有sessionMap里保存的 serverSession
|
||||||
|
//如果不在线,serverSession也没有。用这个来判断是否在线
|
||||||
|
public List<ServerSession> getSessionBy(String sessionId) { |
||||||
|
return map.values().stream(). |
||||||
|
filter(s -> s.getSessionId().equals(sessionId)). |
||||||
|
collect(Collectors.toList()); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean getScene() { |
||||||
|
return sceneThreadLocal.get(); |
||||||
|
} |
||||||
|
|
||||||
|
public void initScene(Boolean status) { |
||||||
|
if (sceneThreadLocal == null) { |
||||||
|
log.info("======创建ThreadLocal======"); |
||||||
|
sceneThreadLocal = new ThreadLocal<>(); |
||||||
|
} |
||||||
|
log.info("设置状态==>" + status); |
||||||
|
sceneThreadLocal.set(status); |
||||||
|
} |
||||||
|
|
||||||
|
public void clearScene() { |
||||||
|
initScene(null); |
||||||
|
sceneThreadLocal.remove(); |
||||||
|
} |
||||||
|
|
||||||
|
public void updateSession(String sessionId, ServerSession session, String meterNum) { |
||||||
|
Iterator<Map.Entry<String, ServerSession>> iterator = map.entrySet().iterator(); |
||||||
|
while(iterator.hasNext()) { |
||||||
|
Map.Entry<String, ServerSession> next = iterator.next(); |
||||||
|
if (next.getKey().contains(meterNum)){ |
||||||
|
iterator.remove(); |
||||||
|
} |
||||||
|
if(sessionId != null && sessionId.equalsIgnoreCase(next.getValue().getSessionId())) { |
||||||
|
next.setValue(session); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,20 @@ |
|||||||
|
package com.mh.user.netty.task; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author LJF |
||||||
|
* @version 1.0 |
||||||
|
* @project TAD_Server |
||||||
|
* @description 回调任务 |
||||||
|
* @date 2023/7/3 15:34:11 |
||||||
|
*/ |
||||||
|
public interface CallbackTask<T> { |
||||||
|
T execute() throws Exception; |
||||||
|
|
||||||
|
/** |
||||||
|
* // 执行没有 异常的情况下的 返回值
|
||||||
|
* @param t |
||||||
|
*/ |
||||||
|
void onBack(T t); |
||||||
|
|
||||||
|
void onException(Throwable t); |
||||||
|
} |
||||||
@ -0,0 +1,80 @@ |
|||||||
|
package com.mh.user.netty.task; |
||||||
|
|
||||||
|
import com.google.common.util.concurrent.*; |
||||||
|
|
||||||
|
import java.util.concurrent.Callable; |
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue; |
||||||
|
import java.util.concurrent.ExecutorService; |
||||||
|
import java.util.concurrent.Executors; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author LJF |
||||||
|
* @version 1.0 |
||||||
|
* @project TAD_Server |
||||||
|
* @description 回调任务 |
||||||
|
* @date 2023/7/3 15:34:11 |
||||||
|
*/ |
||||||
|
public class CallbackTaskScheduler extends Thread { |
||||||
|
private ConcurrentLinkedQueue<CallbackTask> executeTaskQueue = |
||||||
|
new ConcurrentLinkedQueue<>(); |
||||||
|
private long sleepTime = 1000 * 10; |
||||||
|
private final ExecutorService pool = Executors.newCachedThreadPool(); |
||||||
|
ListeningExecutorService lpool = MoreExecutors.listeningDecorator(pool); |
||||||
|
private static CallbackTaskScheduler inst = new CallbackTaskScheduler(); |
||||||
|
|
||||||
|
private CallbackTaskScheduler() { |
||||||
|
this.start(); |
||||||
|
} |
||||||
|
|
||||||
|
//add task
|
||||||
|
public static <T> void add(CallbackTask<T> executeTask) { |
||||||
|
inst.executeTaskQueue.add(executeTask); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
while (true) { |
||||||
|
handleTask(); |
||||||
|
//为了避免频繁连接服务器,但是当前连接服务器过长导致失败
|
||||||
|
//threadSleep(sleepTime);
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void threadSleep(long sleepTime) { |
||||||
|
try { |
||||||
|
Thread.sleep(sleepTime); |
||||||
|
} catch (Exception e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//任务执行
|
||||||
|
private void handleTask() { |
||||||
|
CallbackTask executeTask = null; |
||||||
|
while (executeTaskQueue.peek() != null) { |
||||||
|
executeTask = executeTaskQueue.poll(); |
||||||
|
handleTask(executeTask); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private <T> void handleTask(CallbackTask<T> executeTask) { |
||||||
|
ListenableFuture<T> future = lpool.submit(new Callable<T>() { |
||||||
|
public T call() throws Exception { |
||||||
|
return executeTask.execute(); |
||||||
|
} |
||||||
|
}); |
||||||
|
Futures.addCallback(future, new FutureCallback<T>() { |
||||||
|
@Override |
||||||
|
public void onSuccess(T t) { |
||||||
|
executeTask.onBack(t); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onFailure(Throwable throwable) { |
||||||
|
executeTask.onException(throwable); |
||||||
|
} |
||||||
|
}, lpool); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
@ -0,0 +1,6 @@ |
|||||||
|
package com.mh.user.netty.task; |
||||||
|
|
||||||
|
//不需要知道异步线程的 返回值
|
||||||
|
public interface ExecuteTask { |
||||||
|
void execute(); |
||||||
|
} |
||||||
@ -0,0 +1,67 @@ |
|||||||
|
package com.mh.user.netty.task; |
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue; |
||||||
|
import java.util.concurrent.ExecutorService; |
||||||
|
import java.util.concurrent.Executors; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author LJF |
||||||
|
* @version 1.0 |
||||||
|
* @project TAD_Server |
||||||
|
* @description 任务定时 |
||||||
|
* @date 2023/7/3 15:34:11 |
||||||
|
*/ |
||||||
|
public class FutureTaskScheduler extends Thread{ |
||||||
|
private ConcurrentLinkedQueue<ExecuteTask> executeTaskQueue = |
||||||
|
new ConcurrentLinkedQueue<>(); |
||||||
|
private long sleepTime = 200; |
||||||
|
private ExecutorService pool = Executors.newFixedThreadPool(10); |
||||||
|
private static FutureTaskScheduler inst = new FutureTaskScheduler(); |
||||||
|
public FutureTaskScheduler() { |
||||||
|
this.start(); |
||||||
|
} |
||||||
|
//任务添加
|
||||||
|
public static void add(ExecuteTask executeTask) { |
||||||
|
inst.executeTaskQueue.add(executeTask); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
while (true) { |
||||||
|
handleTask(); |
||||||
|
//threadSleep(sleepTime);
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void threadSleep(long sleepTime) { |
||||||
|
try { |
||||||
|
Thread.sleep(sleepTime); |
||||||
|
} catch (InterruptedException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//执行任务
|
||||||
|
private void handleTask() { |
||||||
|
ExecuteTask executeTask; |
||||||
|
while (executeTaskQueue.peek() != null) { |
||||||
|
executeTask = executeTaskQueue.poll(); |
||||||
|
handleTask(executeTask); |
||||||
|
} |
||||||
|
//刷新心跳时间
|
||||||
|
} |
||||||
|
private void handleTask(ExecuteTask executeTask) { |
||||||
|
pool.execute(new ExecuteRunnable(executeTask)); |
||||||
|
} |
||||||
|
|
||||||
|
class ExecuteRunnable implements Runnable { |
||||||
|
ExecuteTask executeTask; |
||||||
|
public ExecuteRunnable(ExecuteTask executeTask) { |
||||||
|
this.executeTask = executeTask; |
||||||
|
} |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
executeTask.execute(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,204 @@ |
|||||||
|
package com.mh.user.tcp; |
||||||
|
|
||||||
|
import com.mh.common.utils.StringUtils; |
||||||
|
import com.mh.user.constants.Constant; |
||||||
|
import com.mh.user.entity.DeviceCodeParamEntity; |
||||||
|
import com.mh.user.factory.Device; |
||||||
|
import com.mh.user.factory.DeviceFactory; |
||||||
|
import com.mh.user.netty.session.ServerSession; |
||||||
|
import com.mh.user.netty.session.SessionMap; |
||||||
|
import com.mh.user.serialport.SerialTool; |
||||||
|
import com.mh.user.service.BuildingService; |
||||||
|
import com.mh.user.service.DeviceInstallService; |
||||||
|
import com.mh.user.service.NowDataService; |
||||||
|
import com.mh.user.strategy.DeviceStrategy; |
||||||
|
import com.mh.user.strategy.DeviceStrategyFactory; |
||||||
|
import com.mh.user.utils.*; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.springframework.context.ApplicationContext; |
||||||
|
import purejavacomm.SerialPort; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author LJF |
||||||
|
* @version 1.0 |
||||||
|
* @project CHWS |
||||||
|
* @description 通过tcp发送和接收数据 |
||||||
|
* @date 2024-03-18 14:56:32 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public class SendAndReceiveByTcp { |
||||||
|
|
||||||
|
List<DeviceCodeParamEntity> deviceManageEntityList; |
||||||
|
|
||||||
|
ApplicationContext context = SpringBeanUtil.getApplicationContext(); |
||||||
|
DeviceInstallService deviceInstallService = context.getBean(DeviceInstallService.class); |
||||||
|
NowDataService nowDataService = context.getBean(NowDataService.class); |
||||||
|
BuildingService buildingService = context.getBean(BuildingService.class); |
||||||
|
|
||||||
|
public void sendAndReceive(String sort, String thread, String heartBeat) { |
||||||
|
CacheUtil cacheUtil = CacheUtil.getInstance(); |
||||||
|
try { |
||||||
|
//生成对应的采集指令
|
||||||
|
List<DeviceCodeParamEntity> deviceParamsByType = cacheUtil.getDeviceParamsByType(sort); |
||||||
|
deviceManageEntityList = deviceParamsByType |
||||||
|
.parallelStream() |
||||||
|
.filter(value -> value.getThread().equals(thread)) |
||||||
|
.sorted(Comparator.comparing(DeviceCodeParamEntity::getDataCom)) |
||||||
|
.collect(Collectors.toList()); |
||||||
|
int size = deviceManageEntityList.size(); |
||||||
|
for (int i = 0; i < size; i++) { |
||||||
|
//判断网页端是否有操作设备的
|
||||||
|
if (Constant.WEB_FLAG) { |
||||||
|
log.info("有指令下发退出定时采集"); |
||||||
|
break; |
||||||
|
} |
||||||
|
String deviceAddr = deviceManageEntityList.get(i).getDeviceAddr(); |
||||||
|
String deviceType = deviceManageEntityList.get(i).getDeviceType(); |
||||||
|
String registerAddr = deviceManageEntityList.get(i).getRegisterAddr(); |
||||||
|
String brand = deviceManageEntityList.get(i).getBrand();//品牌
|
||||||
|
String buildingId = deviceManageEntityList.get(i).getBuildingId(); |
||||||
|
String buildingName = buildingService.queryBuildingName(buildingId); //查询楼栋名称
|
||||||
|
// 创建设备报文
|
||||||
|
Device device = DeviceFactory.createDevice(deviceType); |
||||||
|
DeviceStrategy strategy = DeviceStrategyFactory.createStrategy(deviceType); |
||||||
|
if (null == strategy) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
device.setStrategy(strategy); |
||||||
|
String sendStr = device.createOrders(deviceManageEntityList.get(i)); |
||||||
|
|
||||||
|
try { |
||||||
|
//向串口发送指令
|
||||||
|
if (StringUtils.isBlank(sendStr)) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
if (deviceType.equals("热泵")) { |
||||||
|
for (int j = 0; j < 4; j++) { |
||||||
|
Thread.sleep(1000); |
||||||
|
// 判断网页端是否有操作设备的
|
||||||
|
if (Constant.WEB_FLAG) { |
||||||
|
log.info("有指令下发退出定时采集"); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
Thread.sleep(2000); |
||||||
|
} |
||||||
|
//从获取响应数据
|
||||||
|
ConcurrentHashMap<String, ServerSession> map = SessionMap.inst().getMap(); |
||||||
|
Set<Map.Entry<String, ServerSession>> entries = map.entrySet(); |
||||||
|
boolean flag = false; |
||||||
|
String keyVal = null; |
||||||
|
for (Map.Entry<String, ServerSession> entry : entries) { |
||||||
|
String key = entry.getKey(); |
||||||
|
//log.info("当前session:{},当前sessionId:{},当前在线设备数:{}",entry,entry.getValue().getSessionId(),map.size());
|
||||||
|
if (key.contains(heartBeat)) { |
||||||
|
flag = true; |
||||||
|
keyVal = key; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
if (flag) { |
||||||
|
ServerSession serverSession = map.get(keyVal); |
||||||
|
serverSession.getChannel().writeAndFlush(NettyTools.createByteBuf(sendStr)); |
||||||
|
NettyTools.initReceiveMsg(heartBeat); |
||||||
|
String receiveMsg = NettyTools.waitReceiveMsg(heartBeat); |
||||||
|
Date date1 = new Date(); |
||||||
|
String dateStr = DateUtil.dateToString(date1, "yyyy-MM-dd HH:mm:ss"); |
||||||
|
if (StringUtils.isBlank(receiveMsg)) { |
||||||
|
log.info("TCP客户端:{},没有数据返回!{}", keyVal, i); |
||||||
|
printLog(deviceAddr, deviceType, buildingId, buildingName, dateStr, log, deviceInstallService, nowDataService); |
||||||
|
continue; |
||||||
|
} |
||||||
|
// 处理返回来的数据报文
|
||||||
|
dealReceiveData(dateStr, keyVal, i, deviceAddr, deviceType, registerAddr, brand, buildingId, buildingName, receiveMsg, device); |
||||||
|
} else { |
||||||
|
log.error("没有找到心跳包数据:{}", heartBeat); |
||||||
|
} |
||||||
|
} catch (Exception e) { |
||||||
|
log.error("发送窗口数据异常==>", e); |
||||||
|
} |
||||||
|
} |
||||||
|
} catch (Exception e) { |
||||||
|
log.error("-------------串口采集异常!----------->>", e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static void printLog(String deviceAddr, String deviceType, String buildingId, String buildingName, String dateStr, Logger log, DeviceInstallService deviceInstallService, NowDataService nowDataService) { |
||||||
|
log.info("----------------{}离线,设备号:{},所属楼栋:{}----------------", deviceType, deviceAddr, buildingName); |
||||||
|
String time1 = deviceInstallService.selectLastDate(deviceType, deviceAddr, buildingId); |
||||||
|
if (time1 == null) { |
||||||
|
time1 = dateStr; |
||||||
|
} |
||||||
|
int d = ExchangeStringUtil.compareCopyTime(time1, dateStr); |
||||||
|
if (d == 1) { |
||||||
|
deviceInstallService.updateNotOnline(deviceAddr, deviceType, buildingId, "离线"); //所有设备离线
|
||||||
|
if (deviceType.equals("热泵")) { |
||||||
|
nowDataService.updateRunState(buildingId, deviceAddr, "离线", buildingName); //监控界面状态表热泵在线状态
|
||||||
|
} |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 处理返回来的数据 |
||||||
|
* |
||||||
|
* @param dateStr |
||||||
|
* @param serialPort |
||||||
|
* @param i |
||||||
|
* @param deviceAddr |
||||||
|
* @param deviceType |
||||||
|
* @param registerAddr |
||||||
|
* @param brand |
||||||
|
* @param buildingId |
||||||
|
* @param buildingName |
||||||
|
* @param receiveStr |
||||||
|
* @throws InterruptedException |
||||||
|
*/ |
||||||
|
private void dealReceiveData(String dateStr, |
||||||
|
String serialPort, |
||||||
|
int i, |
||||||
|
String deviceAddr, |
||||||
|
String deviceType, |
||||||
|
String registerAddr, |
||||||
|
String brand, |
||||||
|
String buildingId, |
||||||
|
String buildingName, String receiveStr, Device device) { |
||||||
|
try { |
||||||
|
//去掉空格和null
|
||||||
|
receiveStr = receiveStr.replace("null", ""); |
||||||
|
receiveStr = receiveStr.replace(" ", ""); |
||||||
|
log.info("TCP客户端:{},接受第{}数据:{},大小: {}", serialPort, i, receiveStr, receiveStr.length()); |
||||||
|
//返回值全部变成大写
|
||||||
|
String receiveData = receiveStr.toUpperCase(); |
||||||
|
//截取去掉FE
|
||||||
|
String dataStr; |
||||||
|
if (receiveData.length() > 8) { |
||||||
|
String str1 = receiveData.substring(0, 8); |
||||||
|
String str2 = receiveData.substring(8); |
||||||
|
dataStr = str1.replace("FE", "") + str2; |
||||||
|
} else { |
||||||
|
dataStr = receiveData.replace("FE", ""); |
||||||
|
} |
||||||
|
deviceInstallService.updateOnline(deviceAddr, deviceType, buildingId, "在线"); //设备在线
|
||||||
|
log.info("----------------{}在线,设备号:{},所属楼栋:{}----------------", deviceType, deviceAddr, buildingName); |
||||||
|
if (deviceType.equals("热泵")) { |
||||||
|
String strState = nowDataService.selectState(buildingId, deviceAddr); |
||||||
|
if (strState != null && strState.equals("离线")) { //采集到数据
|
||||||
|
nowDataService.updateRunState(buildingId, deviceAddr, "不运行", buildingName); //监控界面状态表热泵在线状态
|
||||||
|
} |
||||||
|
} |
||||||
|
// 解析返回来的数据
|
||||||
|
device.analysisReceiveData(dateStr, deviceType, registerAddr, brand, buildingId, buildingName, dataStr); |
||||||
|
|
||||||
|
} catch (Exception e) { |
||||||
|
log.error("楼栋:{}设备类型:{}保存数据库失败!{}", buildingName, deviceType, i, e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,126 @@ |
|||||||
|
package com.mh.user.tcp; |
||||||
|
|
||||||
|
import com.mh.common.utils.StringUtils; |
||||||
|
import com.mh.user.constants.Constant; |
||||||
|
import com.mh.user.entity.DeviceCodeParamEntity; |
||||||
|
import com.mh.user.entity.GatewayManageEntity; |
||||||
|
import com.mh.user.factory.Device; |
||||||
|
import com.mh.user.factory.DeviceFactory; |
||||||
|
import com.mh.user.netty.session.ServerSession; |
||||||
|
import com.mh.user.netty.session.SessionMap; |
||||||
|
import com.mh.user.serialport.SerialTool; |
||||||
|
import com.mh.user.service.BuildingService; |
||||||
|
import com.mh.user.service.DeviceInstallService; |
||||||
|
import com.mh.user.service.NowDataService; |
||||||
|
import com.mh.user.strategy.DeviceStrategy; |
||||||
|
import com.mh.user.strategy.DeviceStrategyFactory; |
||||||
|
import com.mh.user.utils.*; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.context.ApplicationContext; |
||||||
|
import purejavacomm.SerialPort; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.util.Date; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Set; |
||||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||||
|
|
||||||
|
import static com.mh.user.tcp.SendAndReceiveByTcp.printLog; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author nxr |
||||||
|
* @title : |
||||||
|
* @description : 串口发送和接收处理,操作类 |
||||||
|
* @updateTime 2022-08-10 |
||||||
|
* @throws : |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public class TcpSingle { |
||||||
|
|
||||||
|
// 调用service
|
||||||
|
ApplicationContext context = SpringBeanUtil.getApplicationContext(); |
||||||
|
DeviceInstallService deviceInstallService = context.getBean(DeviceInstallService.class); |
||||||
|
NowDataService nowDataService = context.getBean(NowDataService.class); |
||||||
|
BuildingService buildingService = context.getBean(BuildingService.class); |
||||||
|
|
||||||
|
public String serialPortSend(DeviceCodeParamEntity deviceCodeParamEntity, GatewayManageEntity gatewayManageEntity) { |
||||||
|
String rtData = "fail"; |
||||||
|
String comName = deviceCodeParamEntity.getDataCom().toUpperCase(); |
||||||
|
if (StringUtils.isBlank(comName)) { |
||||||
|
return rtData; |
||||||
|
} |
||||||
|
try { |
||||||
|
// 创建设备报文
|
||||||
|
Device device = DeviceFactory.createDevice(deviceCodeParamEntity.getDeviceType()); |
||||||
|
DeviceStrategy strategy = DeviceStrategyFactory.createStrategy(deviceCodeParamEntity.getDeviceType()); |
||||||
|
if (null == strategy) { |
||||||
|
return rtData; |
||||||
|
} |
||||||
|
device.setStrategy(strategy); |
||||||
|
String sendStr = device.createOrders(deviceCodeParamEntity); |
||||||
|
//从获取响应数据
|
||||||
|
ConcurrentHashMap<String, ServerSession> map = SessionMap.inst().getMap(); |
||||||
|
Set<Map.Entry<String, ServerSession>> entries = map.entrySet(); |
||||||
|
boolean flag = false; |
||||||
|
String keyVal = null; |
||||||
|
String heartBeat = gatewayManageEntity.getHeartBeat().toUpperCase(); |
||||||
|
for (Map.Entry<String, ServerSession> entry : entries) { |
||||||
|
String key = entry.getKey(); |
||||||
|
//log.info("当前session:{},当前sessionId:{},当前在线设备数:{}",entry,entry.getValue().getSessionId(),map.size());
|
||||||
|
if (key.contains(heartBeat)) { |
||||||
|
flag = true; |
||||||
|
keyVal = key; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
if (flag) { |
||||||
|
ServerSession serverSession = map.get(keyVal); |
||||||
|
serverSession.getChannel().writeAndFlush(NettyTools.createByteBuf(sendStr)); |
||||||
|
NettyTools.initReceiveMsg(heartBeat); |
||||||
|
String receiveStr = ""; |
||||||
|
if (Constant.WEB_FLAG) { |
||||||
|
receiveStr = NettyTools.waitReceiveMsg(heartBeat); |
||||||
|
} |
||||||
|
if (StringUtils.isBlank(receiveStr)) { |
||||||
|
return Constant.FAIL; |
||||||
|
} |
||||||
|
receiveStr = receiveStr.replace("null", "").replace(" ", ""); |
||||||
|
log.info("TCP客户端" + heartBeat + "接收数据:" + receiveStr + ",大小: " + receiveStr.length()); |
||||||
|
//返回值全部变成大写
|
||||||
|
String receiveData = receiveStr.toUpperCase(); |
||||||
|
//截取去掉FE
|
||||||
|
String deviceType = deviceCodeParamEntity.getDeviceType(); |
||||||
|
String deviceAddr = deviceCodeParamEntity.getDeviceAddr(); |
||||||
|
String dataStr = ""; |
||||||
|
if (receiveData.length() > 8 && ("水表".equals(deviceType) || "电表".equals(deviceType))) { |
||||||
|
String str1 = receiveData.substring(0, 8); |
||||||
|
String str2 = receiveData.substring(8); |
||||||
|
dataStr = str1.replace("FE", "") + str2; |
||||||
|
} else { |
||||||
|
dataStr = receiveData; |
||||||
|
} |
||||||
|
String registerAddr = deviceCodeParamEntity.getRegisterAddr(); |
||||||
|
String brand = deviceCodeParamEntity.getBrand(); |
||||||
|
String buildingId = deviceCodeParamEntity.getBuildingId(); |
||||||
|
String buildingName = buildingService.queryBuildingName(buildingId); //查询楼栋名称
|
||||||
|
|
||||||
|
deviceInstallService.updateOnline(deviceAddr, deviceType, buildingId, "在线"); //设备在线
|
||||||
|
log.info("{}在线,设备号:{},所属楼栋:{}", deviceType, deviceAddr, buildingName); |
||||||
|
if (deviceType.equals("热泵")) { |
||||||
|
String strState = nowDataService.selectState(buildingId, deviceAddr); |
||||||
|
if (strState != null && strState.equals("离线")) { //采集到数据
|
||||||
|
nowDataService.updateRunState(buildingId, deviceAddr, "不运行", buildingName); //监控界面状态表热泵在线状态
|
||||||
|
} |
||||||
|
} |
||||||
|
rtData = device.analysisReceiveData(DateUtil.dateToString(new Date(), "yyyy-MM-dd HH:mm:ss"), |
||||||
|
deviceType, registerAddr, brand, buildingId, buildingName, dataStr); |
||||||
|
return rtData; |
||||||
|
} else { |
||||||
|
log.error("没有找到心跳包数据:{}", heartBeat); |
||||||
|
} |
||||||
|
} catch (Exception e) { |
||||||
|
log.info("单抄TCP客户端:{}异常,没有数据返回!关闭串口", gatewayManageEntity.getHeartBeat(), e); |
||||||
|
} |
||||||
|
return Constant.FAIL; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,101 @@ |
|||||||
|
package com.mh.user.utils; |
||||||
|
|
||||||
|
|
||||||
|
import com.google.common.cache.Cache; |
||||||
|
import com.google.common.cache.CacheBuilder; |
||||||
|
import com.mh.common.utils.StringUtils; |
||||||
|
import com.mh.user.constants.Constant; |
||||||
|
import io.netty.buffer.ByteBuf; |
||||||
|
import io.netty.buffer.Unpooled; |
||||||
|
import io.netty.channel.ChannelHandlerContext; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
|
||||||
|
import java.util.Objects; |
||||||
|
import java.util.concurrent.BlockingQueue; |
||||||
|
import java.util.concurrent.LinkedBlockingQueue; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author LJF |
||||||
|
* @version 1.0 |
||||||
|
* @project TAD_Server |
||||||
|
* @description 缓存等待数据 |
||||||
|
* @date 2023/7/4 08:45:16 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public class NettyTools { |
||||||
|
|
||||||
|
public static ByteBuf getByteBuf(ChannelHandlerContext ctx, String sendStr) { |
||||||
|
// byte类型的数据
|
||||||
|
// String sendStr = "5803004900021914"; // 冷量计
|
||||||
|
// 申请一个数据结构存储信息
|
||||||
|
ByteBuf buffer = ctx.alloc().buffer(); |
||||||
|
// 将信息放入数据结构中
|
||||||
|
buffer.writeBytes(ExchangeStringUtil.hexStrToBinaryStr(sendStr));//对接需要16进制
|
||||||
|
return buffer; |
||||||
|
} |
||||||
|
|
||||||
|
public static ByteBuf createByteBuf(String sendStr) { |
||||||
|
// byte类型的数据
|
||||||
|
// String sendStr = "5803004900021914"; // 冷量计
|
||||||
|
// 申请一个数据结构存储信息
|
||||||
|
ByteBuf buffer = Unpooled.buffer(); |
||||||
|
// 将信息放入数据结构中
|
||||||
|
buffer.writeBytes(ExchangeStringUtil.hexStrToBinaryStr(sendStr));//对接需要16进制
|
||||||
|
return buffer; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 响应消息缓存 |
||||||
|
*/ |
||||||
|
private static final Cache<String, BlockingQueue<String>> responseMsgCache = CacheBuilder.newBuilder() |
||||||
|
.maximumSize(50000) |
||||||
|
.expireAfterWrite(1000, TimeUnit.SECONDS) |
||||||
|
.build(); |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 等待响应消息 |
||||||
|
* @param key 消息唯一标识 |
||||||
|
* @return ReceiveDdcMsgVo |
||||||
|
*/ |
||||||
|
public static String waitReceiveMsg(String key) { |
||||||
|
|
||||||
|
try { |
||||||
|
//设置超时时间
|
||||||
|
String vo = Objects.requireNonNull(responseMsgCache.getIfPresent(key)) |
||||||
|
.poll(1000 * 10, TimeUnit.MILLISECONDS); |
||||||
|
|
||||||
|
//删除key
|
||||||
|
responseMsgCache.invalidate(key); |
||||||
|
return vo; |
||||||
|
} catch (Exception e) { |
||||||
|
log.error("获取数据异常,sn={},msg=null",key); |
||||||
|
return Constant.FAIL; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 初始化响应消息的队列 |
||||||
|
* @param key 消息唯一标识 |
||||||
|
*/ |
||||||
|
public static void initReceiveMsg(String key) { |
||||||
|
responseMsgCache.put(key,new LinkedBlockingQueue<String>(1)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 设置响应消息 |
||||||
|
* @param key 消息唯一标识 |
||||||
|
*/ |
||||||
|
public static void setReceiveMsg(String key, String msg) { |
||||||
|
|
||||||
|
if(responseMsgCache.getIfPresent(key) != null){ |
||||||
|
responseMsgCache.getIfPresent(key).add(msg); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
log.warn("sn {}不存在",key); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue