import net from 'net';
|
import { fanhuiData, fanhuiState, fanhuiEErrer, fanhuiCommLog } from './index'
|
import logger from '../../utils/logger';
|
import aliyunIot from 'aliyun-iot-device-sdk'
|
|
|
export default class TcpClient {
|
public host: string;
|
public port: number;
|
public productKey: string;
|
public deviceSecret: string;
|
public deviceName: string;
|
/**定时多少毫秒 */
|
private retryInterval: number;
|
/**客户端连接对象 */
|
private client: net.Socket;
|
private iotDevice: aliyunIot.device;
|
/**是否启动重新连接 */
|
private shouldReconnect: boolean;
|
public masgData: Buffer;
|
public disposed: boolean = false;
|
private attemptCount: number;
|
private maxAttempts: number;
|
// 定时重新发送
|
private retryIntervalSund: number;
|
// 是否回应了ack06
|
private waitingForAck: boolean;
|
|
/**
|
*
|
* @param host ip
|
* @param port 端口
|
* @param productKey iot产品key
|
* @param deviceName iot设备名称
|
* @param deviceSecret iot秘钥
|
* @param retryInterval
|
*/
|
constructor(host: string, port: number, productKey: string, deviceName: string, deviceSecret: string, retryInterval: number = 10000) {
|
logger.info('新增clientSocket链接对象---' + host)
|
this.host = host;
|
this.port = port;
|
this.deviceName = deviceName;
|
this.productKey = productKey;
|
this.deviceSecret = deviceSecret;
|
this.disposed = false;
|
this.retryInterval = retryInterval;
|
this.client = new net.Socket();
|
this.shouldReconnect = true;
|
this.setupClient();
|
this.connect();
|
this.attemptCount = 0
|
this.maxAttempts = 10,
|
this.retryIntervalSund = 10 * 1000
|
this.waitingForAck = false
|
|
|
|
}
|
/**
|
*
|
* @returns 注销对象
|
*/
|
public dispose(): void {
|
if (this.disposed) {
|
console.log('Already disposed.');
|
return;
|
}
|
this.disposed = true;
|
console.log('Resources have been disposed.');
|
}
|
/**
|
* 创建物联网链接
|
*/
|
public setIotDevice() {
|
try {
|
logger.info('新增iot链接对象---' + this.deviceName)
|
this.iotDevice = undefined;
|
let devConfig = {
|
deviceName: this.deviceName,
|
deviceSecret: this.deviceSecret,
|
productKey: this.productKey
|
}
|
console.log(devConfig)
|
this.iotDevice = aliyunIot.device(devConfig);
|
this.iotDevice.on('connect', () => {
|
console.log('阿里链接成功----')
|
});
|
// this.onIotConnect.bind(this)
|
this.iotDevice.on('error', this.onIotError.bind(this));
|
}
|
catch (error) {
|
console.log('--------------2232323' + error)
|
logger.info('新增iot链接对象异常---' + this.deviceName)
|
const listData2 = { ip: this.host, prot: this.port, data: error.toString() }
|
fanhuiEErrer(listData2)
|
}
|
|
|
|
}
|
private setupClient() {
|
this.client.on('connect', this.onConnect.bind(this));
|
this.client.on('data', this.onData.bind(this));
|
this.client.on('close', this.onClose.bind(this));
|
this.client.on('error', this.onError.bind(this));
|
}
|
/**
|
* 连接成功方法
|
*/
|
public connect() {
|
this.shouldReconnect = true
|
// 监测连接对象是否存在
|
if (this.client) {
|
this.disposed = false
|
// 尝试连接tcp服务
|
logger.info(`开始创建链接${this.host}`)
|
console.log('Attempting to connect to the TCP server...');
|
const listData = { ip: this.host, prot: this.port, data: `创建连接${this.host}` }
|
fanhuiCommLog(listData)
|
this.client.connect(this.port, this.host);
|
|
}
|
|
}
|
/**
|
* 发送数据到服务端
|
*/
|
public fasongData(str: string) {
|
// 检查下连接断开没有
|
if (!this.client.destroyed) {
|
const buffer = Buffer.from(str, 'utf-8');
|
this.client.write(buffer);
|
} else {
|
return
|
}
|
}
|
/**发送其他数据 需要加如数据和校验码的 */
|
public fasongDataJiaoyan(str: string) {
|
// 查看连接对象是否正常连接
|
if (!this.client.destroyed) {
|
// 原始字符串
|
const originalString = str;
|
// 计算CRC-16校验码
|
const crcResult = crc16(originalString);
|
// 将结果转换为字节数组
|
const crcBytes = [
|
(crcResult >> 8) & 0xFF, // 高字节
|
crcResult & 0xFF // 低字节
|
];
|
// 创建包含校验码的数据包(先转换为Buffer)
|
const messageBuffer = Buffer.concat([
|
Buffer.from(originalString), // 原始数据部分
|
Buffer.from(crcBytes), // CRC校验码部分
|
Buffer.from([0x0D,0x0A])
|
]);
|
console.log('ping hao d jiao yan ma',messageBuffer.length)
|
if( this.waitingForAck){
|
console.log('londion-------zhong')
|
const listData = { ip: this.host, prot: this.port, data: '正在等待应答中,会自动尝试10秒后发送' }
|
fanhuiCommLog(listData)
|
}else{
|
console.log('fa song xue ya shu ju ')
|
this.sendAndAwaitAck(messageBuffer)
|
}
|
} else {
|
return
|
}
|
|
}
|
|
|
// 发送数据等待相应ACK06或者发送10次自动关闭
|
private sendAndAwaitAck = (str: Buffer) => {
|
console.log(`chang shi fa song shu ju,cahng shi ci shu: ${this.attemptCount + 1}`);
|
const listData = { ip: this.host, prot: this.port, data: str.toString() }
|
fanhuiCommLog(listData)
|
this.client.write(str);
|
|
// // 标记为正在等待 ACK
|
this.waitingForAck = true;
|
// 设置一个超时来等待 ACK
|
setTimeout(() => {
|
if (this.waitingForAck && this.attemptCount < this.maxAttempts) {
|
this.attemptCount++;
|
console.log('wei shou dao yin da ---未收到应答,jiang zai shi miao hou chong shi');
|
this.sendAndAwaitAck(str);
|
} else {
|
if (this.waitingForAck) {
|
this.waitingForAck = false
|
this.attemptCount=0
|
console.log('yi ji cao guo 10 ci l ,fang qi ding shi fa songnpm ');
|
const listData = { ip: this.host, prot: this.port, data: '已经超过了10次发送尝试,都没有等到回应,请重新点击发送' }
|
fanhuiCommLog(listData)
|
}
|
}
|
}, this.retryInterval);
|
// 开启发送定时任务
|
|
}
|
/**物联网连接异常 */
|
private onIotError(error) {
|
console.log('阿里物联网连接异常,', this.host);
|
logger.info('iot链接对象失败---' + this.deviceName + 'ip' + this.host)
|
logger.error(error)
|
const listData2 = { ip: this.host, prot: this.port, data: `errer物联网链接实拍${error.toString()}` }
|
fanhuiEErrer(listData2)
|
}
|
/**
|
* 建立连接成功
|
*/
|
private onConnect() {
|
console.log('Connected to the TCP server');
|
logger.info(`创建链接${this.host}成功`)
|
// 输出调试日志
|
const listData3 = { ip: this.host, prot: this.port, data: '建立连接成功' }
|
fanhuiCommLog(listData3)
|
// 给ui返回连接状态
|
const listData = { ip: this.host, prot: this.port, data: 1 }
|
fanhuiState(listData)
|
const listData2 = { ip: this.host, prot: this.port, data: 'client链接成功' }
|
fanhuiEErrer(listData2)
|
this.setIotDevice()
|
// 向服务器发送消息
|
if (this.client) {
|
//定时发送
|
// this.dinshiFasong()
|
}
|
|
}
|
/**
|
* 收到返回的数据
|
* @param data
|
*/
|
private onData(data: Buffer) {
|
console.log(`Received from server: `);
|
logger.info(`收到服务端数据${this.host}`)
|
const listData = { ip: this.host, prot: this.port, data: data }
|
//计算校验码
|
const jyma = calculateChecksum(data)
|
console.log(data)
|
console.log(data[85], '-----', jyma)
|
//数据头 67 89 长度86 做校验 贝朗的透析机
|
if (data[0] == 103 && data[1] === 137 && data.length === 86 && jyma === data[85]) {
|
try {
|
fanhuiData(listData)
|
const mode = funToModel(data)
|
this.iotDevice?.postProps(mode)
|
} catch (error) {
|
console.log('数据解析有误')
|
}
|
}
|
//其他数据东丽的
|
else {
|
if (data[0] === 6 && data.length === 1) {
|
this.waitingForAck = false;
|
}
|
console.log('qi ta shu ju---------')
|
fanhuiCommLog(listData)
|
// fanhuiData(listData)
|
}
|
|
}
|
|
|
private onClose() {
|
console.log('22222')
|
// 给ui返回连接状态
|
const listData = { ip: this.host, data: 0 }
|
fanhuiState(listData)
|
if (this.shouldReconnect && this.disposed === false) {
|
const listData = { ip: this.host, prot: this.port, data: `${this.retryInterval / 1000}s 后重连` }
|
fanhuiCommLog(listData)
|
console.log(`Connection closed, retrying in ${this.retryInterval / 1000} seconds...`);
|
const listData2 = { ip: this.host, prot: this.port, data: `errer链接断开, ${this.retryInterval / 1000} 秒后重新连接...` }
|
fanhuiEErrer(listData2)
|
setTimeout(() => this.connect(), this.retryInterval);
|
} else {
|
console.log('Connection closed by client.');
|
}
|
}
|
|
private onError(err: Error) {
|
console.log('lian jie chu cuo ')
|
console.error(`Connection error: ${err.message}`);
|
const listData = { ip: this.host, prot: this.port, data: '建立连接错误' }
|
fanhuiCommLog(listData)
|
logger.info(`链接错误Connection error: ${err.message} ${this.host}`)
|
}
|
|
public close() {
|
console.log('close--client--')
|
console.log(this.client)
|
this.disposed = true
|
this.client.destroy();
|
this.client = null
|
this.iotDevice = null
|
// 给ui返回连接状态
|
const listData = { ip: this.host, prot: this.port, data: 0 }
|
fanhuiState(listData)
|
const listData2 = { ip: this.host, prot: this.port, data: '注销链接对象' }
|
fanhuiCommLog(listData2)
|
console.log('guanbi------')
|
}
|
// 定时时间间隔发送
|
private dinshiFasong() {
|
setTimeout(() => {
|
console.log(this.host)
|
console.log('xu huan fa song key---')
|
console.log(this.client.destroyed)
|
// 检查下连接断开没有
|
if (!this.client.destroyed) {
|
let buffer1 = Buffer.from([0xFF, 0x55, 0x00, 0X50, 0XEE, 0X42]);
|
this.client.write(buffer1);
|
this.dinshiFasong()
|
} else {
|
return
|
}
|
}, 10000)
|
}
|
}
|
|
/**数据校验 校验贝恩透析器 */
|
function calculateChecksum(buf: Uint8Array): number {
|
let checkSum = 0;
|
for (let i = 0; i <= 84; i++) {
|
if (i !== 2 && i !== 3 && i !== 4) {
|
checkSum += buf[i];
|
}
|
}
|
return checkSum & 0xFF;
|
}
|
/**数据校验 发送数据加 校验 血压发送给贝恩血压计 */
|
// 计算校验和
|
function calculateChecksum2(buf) {
|
let sum = 0;
|
for (let i = 0; i < buf.length; i++) {
|
sum += buf[i];
|
}
|
// 如果需要,可以对 sum 取模以适应单个字节
|
return sum & 0xFF;
|
}
|
/**
|
* CRC-16-CCITT(多项式 0x1021)来计算校验码。
|
*/
|
function crc16(data) {
|
let crc = 0xFFFF; // 初始值
|
let polynomial = 0x1021; // 使用的多项式
|
for (let i = 0; i < data.length; i++) {
|
let byte = data.charCodeAt(i);
|
crc ^= byte << 8;
|
for (let j = 0; j < 8; j++) {
|
if ((crc & 0x8000) > 0) {
|
crc = (crc << 1) ^ polynomial;
|
} else {
|
crc = crc << 1;
|
}
|
}
|
crc &= 0xFFFF; // 确保结果为16位
|
}
|
|
return crc >>> 0; // 转换为无符号整数
|
}
|
/**解析数据1 */
|
function parseData(buf: Uint8Array, index1: number, index2: number, index3: number, index4: number): number {
|
let data5: number = (buf[index1] << 24) | (buf[index2] << 16) | (buf[index3] << 8) | buf[index4];
|
return data5;
|
}
|
|
function funToModel(data: Buffer) {
|
const row = data
|
const msgBody = {
|
clmb: parseData(row, 5, 6, 7, 8),
|
ycll: parseData(row, 9, 10, 11, 12),
|
//脱水速率
|
clsl: parseData(row, 13, 14, 15, 16),
|
//血泵流量设定
|
xlllsd: parseData(row, 17, 18, 19, 20),
|
//透析液流量
|
txyll: parseData(row, 21, 22, 23, 24),
|
//透析液温度
|
txywd: parseData(row, 25, 26, 27, 28),
|
//电导度
|
ddd: parseData(row, 29, 30, 31, 32),
|
//静脉压
|
jmy: parseData(row, 33, 34, 35, 36),
|
//透析液压
|
txyy: parseData(row, 37, 38, 39, 40),
|
//跨膜压
|
kmy: parseData(row, 41, 42, 43, 44),
|
//透析时间
|
txsc: parseData(row, 45, 46, 47, 48),
|
//肝素泵流量
|
gslll: parseData(row, 49, 50, 51, 52),
|
//累计注射量
|
ljzsl: parseData(row, 53, 54, 55, 56),
|
//报警编号
|
bjbh: parseData(row, 57, 58, 59, 60),
|
//剩余时间
|
sysj: parseData(row, 61, 62, 63, 64),
|
//血流量
|
xll: parseData(row, 65, 66, 67, 68),
|
//动脉压
|
dmy: parseData(row, 69, 70, 71, 72),
|
//收缩压
|
ssy: parseData(row, 73, 74, 75, 76),
|
//舒张压
|
szy: parseData(row, 77, 78, 79, 80),
|
//脉搏
|
mb: parseData(row, 81, 82, 83, 84),
|
}
|
return msgBody
|
|
}
|