main
parent
8cc61d099e
commit
cb28159b37
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"running_time": 0,
|
||||||
|
"last_update": "2025-12-23T00:00:00",
|
||||||
|
"version": "1.0"
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -11,15 +11,31 @@ from influxdb_client import InfluxDBClient, Point
|
||||||
from influxdb_client.client.write_api import SYNCHRONOUS
|
from influxdb_client.client.write_api import SYNCHRONOUS
|
||||||
from config_service import ConfigService
|
from config_service import ConfigService
|
||||||
|
|
||||||
def checkValue(data: bytes) -> int:
|
def checkValue(data, little_endian=True):
|
||||||
|
"""
|
||||||
|
计算Modbus CRC16校验和
|
||||||
|
参数:
|
||||||
|
data: 字节串或字节数组
|
||||||
|
little_endian: 是否使用小端字节序,默认为False(大端)
|
||||||
|
返回:
|
||||||
|
CRC16值 (2字节,小端字节序)
|
||||||
|
"""
|
||||||
crc = 0xFFFF
|
crc = 0xFFFF
|
||||||
length = len(data)
|
for byte in data:
|
||||||
if length % 2 != 0:
|
crc ^= byte
|
||||||
return 0
|
for _ in range(8):
|
||||||
for i in range(0, length, 2):
|
if crc & 0x0001:
|
||||||
val = data[i] * 256 + data[i + 1]
|
crc = (crc >> 1) ^ 0xA001
|
||||||
crc = crc ^ val
|
else:
|
||||||
return crc
|
crc = crc >> 1
|
||||||
|
if little_endian:
|
||||||
|
# 小端字节序:低位在前,高位在后
|
||||||
|
low_byte = crc & 0xFF
|
||||||
|
high_byte = (crc >> 8) & 0xFF
|
||||||
|
return (low_byte << 8) | high_byte
|
||||||
|
else:
|
||||||
|
# 大端字节序:高位在前,低位在后
|
||||||
|
return crc & 0xFFFF
|
||||||
|
|
||||||
def nowStr():
|
def nowStr():
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
|
|
@ -216,15 +232,6 @@ class ConfigManager:
|
||||||
# self.observer.stop()
|
# self.observer.stop()
|
||||||
# self.observer.join()
|
# self.observer.join()
|
||||||
|
|
||||||
# class ConfigFileHandler(FileSystemEventHandler):
|
|
||||||
# def __init__(self, config_manager):
|
|
||||||
# self.config_manager = config_manager
|
|
||||||
|
|
||||||
# def on_modified(self, event):
|
|
||||||
# if Path(event.src_path) == self.config_manager.config_file:
|
|
||||||
# logging.info("Config file modified, reloading...")
|
|
||||||
# self.config_manager.load_all_configs()
|
|
||||||
|
|
||||||
class BidirectionalMap:
|
class BidirectionalMap:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.key_to_address = {} # 配置键 -> (地址, 类型)
|
self.key_to_address = {} # 配置键 -> (地址, 类型)
|
||||||
|
|
@ -357,26 +364,55 @@ class ModbusSequentialDataBlockForPCM(ModbusSequentialDataBlock):
|
||||||
super().setValues(address, values)
|
super().setValues(address, values)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
super().setValues(address, values)
|
||||||
|
|
||||||
# Handle client writes
|
# Handle client writes
|
||||||
updated = False
|
updated = False
|
||||||
for i, value in enumerate(values):
|
print(f"*************************address={address}, values={values}*************************")
|
||||||
reg_addr = address-1
|
reg_addr = address - 1
|
||||||
|
# print(f"values = {values}")
|
||||||
|
# path = self.config_manager.mapping.get_config_keys(reg_addr)
|
||||||
|
# print(f"*************************{path}:{reg_addr}:{values}********************")
|
||||||
|
# if self.config_manager.update_config_value(path[0], value[0]):
|
||||||
|
# updated = True
|
||||||
|
|
||||||
|
regCount = len(values)
|
||||||
|
while(regCount > 0):
|
||||||
path = self.config_manager.mapping.get_config_keys(reg_addr)
|
path = self.config_manager.mapping.get_config_keys(reg_addr)
|
||||||
print(f"{path}:{reg_addr}")
|
print(f"*************************{path}, {reg_addr}, {regCount}*************************")
|
||||||
if self.config_manager.update_config_value(path[0], value):
|
dataType = self.config_manager.mapping.key_to_address[path[0]][1]
|
||||||
updated = True
|
print(f"*************************{path}, {dataType}, {regCount}*************************")
|
||||||
|
if len(path) > 0:
|
||||||
super().setValues(address, values)
|
if '16' in dataType:
|
||||||
|
print(f"*************************{path}:{reg_addr}:{values[0]}:{regCount}********************")
|
||||||
|
if dataType in ['int16', 'uint16']:
|
||||||
|
self.config_manager.update_config_value(path[0], int(values[0]))
|
||||||
|
regCount -= 1
|
||||||
|
reg_addr += 1
|
||||||
|
values = values[1:]
|
||||||
|
elif '32' in dataType:
|
||||||
|
print(f"*************************{path}:{reg_addr}:{values[0:2]}:{regCount}********************")
|
||||||
|
if dataType in ['int32', 'uint32']:
|
||||||
|
self.config_manager.update_config_value(path[0], (values[0]<<16)+values[1])
|
||||||
|
elif dataType == 'float32':
|
||||||
|
self.config_manager.update_config_value(path[0], registers_to_float(values))
|
||||||
|
regCount -= 2
|
||||||
|
reg_addr += 2
|
||||||
|
values = values[2:]
|
||||||
|
else:
|
||||||
|
regCount -= 1
|
||||||
|
reg_addr += 1
|
||||||
|
|
||||||
if updated:
|
if updated:
|
||||||
self.config_manager.save_config()
|
self.config_manager.save_config()
|
||||||
self.logger.debug(f"Register {address} update triggered configuration change")
|
self.logger.debug(f"Register {address} update triggered configuration change")
|
||||||
|
|
||||||
def server_set_values(self, address, values):
|
def server_set_values(self, address, values):
|
||||||
"""Server-only write method that won't trigger YAML update"""
|
"""Server-only write method that won't trigger YAML update"""
|
||||||
self._is_client_write = False
|
# self._is_client_write = False
|
||||||
self.setValues(address, values)
|
# self.setValues(address, values)
|
||||||
self._is_client_write = True
|
# self._is_client_write = True
|
||||||
|
super().setValues(address, values)
|
||||||
|
|
||||||
class LSDAQ:
|
class LSDAQ:
|
||||||
def __init__(self, config:dict, logger):
|
def __init__(self, config:dict, logger):
|
||||||
|
|
@ -405,7 +441,7 @@ class LSDAQ:
|
||||||
if self.port != '/dev/ttyLP3':
|
if self.port != '/dev/ttyLP3':
|
||||||
self.status = -201
|
self.status = -201
|
||||||
self.baudrate = config.get('baudrate', 115200)
|
self.baudrate = config.get('baudrate', 115200)
|
||||||
self.timeout = config.get('timeout', 1)
|
self.timeout = config.get('timeout', 50)/1000.0
|
||||||
self.mode = config.get('mode', 0)
|
self.mode = config.get('mode', 0)
|
||||||
self.channels = config.get('channels', 16)
|
self.channels = config.get('channels', 16)
|
||||||
if self.mode not in [0, 1]:
|
if self.mode not in [0, 1]:
|
||||||
|
|
@ -687,12 +723,261 @@ class LSDAQ:
|
||||||
self.close()
|
self.close()
|
||||||
self.logger.info("Modbus Serial TCP Client stopped.")
|
self.logger.info("Modbus Serial TCP Client stopped.")
|
||||||
|
|
||||||
|
class Breaker:
|
||||||
|
def __init__(self, config:dict, logger):
|
||||||
|
# 加载配置参数
|
||||||
|
''' self.errorCode 码表
|
||||||
|
0x0001 打开/dev/ttyUSB0设备失败
|
||||||
|
0x0101 与断路器通讯失败
|
||||||
|
'''
|
||||||
|
self.errorCode = 0
|
||||||
|
''' self.load_status 码表
|
||||||
|
0x00 负载不在线
|
||||||
|
0x0101 负载在线
|
||||||
|
'''
|
||||||
|
self.load_status = 0
|
||||||
|
self.config = config
|
||||||
|
self.logger = logger
|
||||||
|
self.port = config.get('port', '/dev/ttyUSB0')
|
||||||
|
self.baudrate = config.get('baudrate', 9600)
|
||||||
|
self.timeout = config.get('timeout', 50)/1000.0
|
||||||
|
self.task_start_threshold = config.get('task_start_threshold', 2000)
|
||||||
|
self.task_stop_threshold = config.get('task_stop_threshold', 2000)
|
||||||
|
self.locked = 0
|
||||||
|
self.closed = 0x0F # 0x0F:分闸 0xF0:合闸
|
||||||
|
self.reasonForLastOpen = 15 # F:无 1:过流 2:漏电 3:过温 4:过载 5:过压 6:欠压 7:远程 8:模组 9:失压 A:锁扣 B:限电 0: 本地
|
||||||
|
self.active_powers = []
|
||||||
|
self.duration = config.get('duration', 5)
|
||||||
|
self.alarm = 0
|
||||||
|
|
||||||
|
self.active_power = 0 # 有功功率,单位:W
|
||||||
|
|
||||||
|
OVV = config.get('OVV', 275)
|
||||||
|
UVV = config.get('UVV', 150)
|
||||||
|
OCV = config.get('OCV', 10000)
|
||||||
|
LCV = config.get('LCV', 30)
|
||||||
|
OTV = config.get('OTV', 80)
|
||||||
|
OPV = config.get('OPV', 13000)
|
||||||
|
OVT = config.get('OVT', 0)
|
||||||
|
UVT = config.get('UVT', 0)
|
||||||
|
OCT = config.get('OCT', 0)
|
||||||
|
LCT = config.get('LCT', 200)
|
||||||
|
OTT = config.get('OTT', 200)
|
||||||
|
OPT = config.get('OPT', 100)
|
||||||
|
|
||||||
|
self.reg_values = {
|
||||||
|
'locked': 0,
|
||||||
|
'closed': 0x0F,
|
||||||
|
'reasonForLastOpen': 0x0F,
|
||||||
|
'alarm': 0,
|
||||||
|
'active_power': 0,
|
||||||
|
'load_status': 0
|
||||||
|
}
|
||||||
|
# 构建指令集
|
||||||
|
self.cmdList = {
|
||||||
|
# 指令格式:指令描述,指令字符串,回复长度,超时时间,发送校验标志,接收校验标志,重试次数
|
||||||
|
'readAllDatas': ['', f"0204 0000 0027", 83, 300, 1, 1, 3],
|
||||||
|
'readOverLimitValues': ['', f"0203 0002 0006", 17, 200, 1, 1, 3],
|
||||||
|
'readOverLimitActionTime': ['', f"0203 0010 0006", 17, 200, 1, 1, 3],
|
||||||
|
'setOverLimitValues': ['', f"0210 0002 0006 0C {OVV:04X} {UVV:04X} {OCV:04X} {LCV:04X} {OTV:04X} {OPV:04X}", 8, 100, 1, 1, 3],
|
||||||
|
'setOverLimitActionTime': ['', f"0210 0010 0006 0C {OVT:04X} {UVT:04X} {OCT:04X} {LCT:04X} {OTT:04X} {OPT:04X}", 8, 100, 1, 1, 3],
|
||||||
|
'closeBreaker': ['', f"0210 000D 0001 02 FF00", 8, 100, 1, 1, 3],
|
||||||
|
'openBreaker': ['', f"0210 000D 0001 02 0000", 8, 100, 1, 1, 3],
|
||||||
|
'turnOnGreen': ['', f"0105 0002 FF00", 8, 100, 1, 1, 3],
|
||||||
|
'turnOffGreen': ['', f"0105 0002 0000", 8, 100, 1, 1, 3],
|
||||||
|
'turnOnRed': ['', f"0105 0008 FF00", 8, 100, 1, 1, 3],
|
||||||
|
'turnOffRed': ['', f"0105 0000 0000", 8, 100, 1, 1, 3],
|
||||||
|
'turnOnAlarm': ['', f"0105 00A1 FF00", 8, 100, 1, 1, 3],
|
||||||
|
'turnOffAlarm': ['', f"0105 00A1 0000", 8, 100, 1, 1, 3]
|
||||||
|
}
|
||||||
|
|
||||||
|
self.optFlag = 0
|
||||||
|
self.logger.info(f"Breader routine inspection started.")
|
||||||
|
|
||||||
|
def update_config(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def exeCmd(self, cmdName) -> list: # type: ignore
|
||||||
|
try:
|
||||||
|
info = ''
|
||||||
|
cmd = self.cmdList.get(cmdName, None)
|
||||||
|
if cmd is None:
|
||||||
|
return [False, None, f"Command {cmdName} not found in cmdList."]
|
||||||
|
retry = 0
|
||||||
|
data = bytearray().fromhex(cmd[1])
|
||||||
|
if (cmd[4] == 1):
|
||||||
|
data += bytearray(checkValue(data[0:]).to_bytes(2, 'big'))
|
||||||
|
if len(cmd) >= 7:
|
||||||
|
RETRYTIMES = int(cmd[6])
|
||||||
|
else:
|
||||||
|
RETRYTIMES = 1
|
||||||
|
while (retry < RETRYTIMES):
|
||||||
|
info += f"[{nowStr()}] Sent:{wordData2HexStr(data)}\n"
|
||||||
|
recvData = bytearray()
|
||||||
|
self.serial.write(data)
|
||||||
|
time.sleep(int(cmd[3])/1000.0)
|
||||||
|
recvData = self.serial.read(int(cmd[2]))
|
||||||
|
info += (f"[{nowStr()}] Echo:{wordData2HexStr(recvData)}\n")
|
||||||
|
rspLen = int(cmd[2])
|
||||||
|
if len(recvData) >= rspLen:
|
||||||
|
if recvData[0:2] == bytearray().fromhex(cmd[1][0:4]):
|
||||||
|
# info += f"[{nowStr()}] Echo:{wordData2HexStr(recvData[0:rspLen])}\n"
|
||||||
|
recvData = recvData[0:rspLen]
|
||||||
|
if (cmd[5] == 1):
|
||||||
|
crc = int.from_bytes(recvData[rspLen-2:rspLen], byteorder='big')
|
||||||
|
calc_value = checkValue(recvData[0:rspLen-2])
|
||||||
|
# info += f"{crc:04X}, {calc_value:04X}\n"
|
||||||
|
if crc == calc_value:
|
||||||
|
# self.logger.info(info)
|
||||||
|
return [True, recvData, info]
|
||||||
|
else:
|
||||||
|
# self.logger.info(info)
|
||||||
|
return [True, recvData, info]
|
||||||
|
retry += 1
|
||||||
|
# self.logger.info(info)
|
||||||
|
return [False, None, info]
|
||||||
|
except Exception as e:
|
||||||
|
info += f"[{nowStr()}] Error in exeCmd({cmd}): {str(e)}\n" # type: ignore
|
||||||
|
# self.logger.info(info)
|
||||||
|
return [False, None, info]
|
||||||
|
|
||||||
|
def parseData(self, cmdName, rawData):
|
||||||
|
try:
|
||||||
|
match cmdName:
|
||||||
|
case 'readAllDatas':
|
||||||
|
rawData = rawData[3:-2]
|
||||||
|
self.locked = rawData[0]
|
||||||
|
self.closed = rawData[1]
|
||||||
|
self.reasonForLastOpen = (rawData[6]&0xF0)>>4
|
||||||
|
self.active_power = int.from_bytes(rawData[68:70], byteorder='big')
|
||||||
|
self.active_powers.append(self.active_power)
|
||||||
|
|
||||||
|
if len(self.active_powers) > self.duration * 2:
|
||||||
|
self.active_powers = self.active_powers[1:]
|
||||||
|
if np.mean(self.active_powers) > self.task_start_threshold:
|
||||||
|
self.load_status = 1
|
||||||
|
if np.mean(self.active_powers) < self.task_stop_threshold:
|
||||||
|
self.load_status = 0
|
||||||
|
|
||||||
|
|
||||||
|
self.reg_values['locked'] = self.locked
|
||||||
|
self.reg_values['closed'] = self.closed
|
||||||
|
self.reg_values['reasonForLastOpen'] = self.reasonForLastOpen
|
||||||
|
self.reg_values['alarm'] = self.alarm
|
||||||
|
self.reg_values['active_power'] = self.active_power
|
||||||
|
self.reg_values['load_status'] = self.load_status
|
||||||
|
|
||||||
|
print(f"breaker: {self.reg_values}")
|
||||||
|
|
||||||
|
case 'closeBreaker':
|
||||||
|
pass
|
||||||
|
case 'openBreaker':
|
||||||
|
pass
|
||||||
|
case _:
|
||||||
|
pass
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"[{nowStr()}] Error in Breaker: parseData({cmdName}): {str(e)}\n")
|
||||||
|
|
||||||
|
def openBreaker(self):
|
||||||
|
if self.reg_values['closed'] == 0xF0:
|
||||||
|
self.optFlag = 2
|
||||||
|
|
||||||
|
def closeBreaker(self):
|
||||||
|
if self.reg_values['closed'] == 0x0F:
|
||||||
|
self.optFlag = 3
|
||||||
|
|
||||||
|
def alarming(self):
|
||||||
|
if not self.alarm and self.closed & 0xFF == 0xF0:
|
||||||
|
self.exeCmd('turnOffGreen')
|
||||||
|
self.exeCmd('turnOnRed')
|
||||||
|
self.exeCmd('turnOnAlarm')
|
||||||
|
|
||||||
|
def unalarming(self):
|
||||||
|
if self.alarm:
|
||||||
|
if self.closed & 0xFF == 0xF0:
|
||||||
|
self.exeCmd('turnOnGreen')
|
||||||
|
self.exeCmd('turnOffRed')
|
||||||
|
self.exeCmd('turnOffAlarm')
|
||||||
|
else:
|
||||||
|
self.exeCmd('turnOffGreen')
|
||||||
|
self.exeCmd('turnOffRed')
|
||||||
|
self.exeCmd('turnOffAlarm')
|
||||||
|
|
||||||
|
def open(self):
|
||||||
|
"""打开串口连接"""
|
||||||
|
self.serial = serial.Serial(self.port, self.baudrate, timeout=self.timeout)
|
||||||
|
if not self.serial.is_open:
|
||||||
|
self.errorCode = 0x0001
|
||||||
|
return -1
|
||||||
|
else:
|
||||||
|
self.errorCode = 0
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if self.serial.is_open:
|
||||||
|
self.serial.close()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""主运行循环"""
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
# self.logger.info(f"optFlag={self.optFlag}")
|
||||||
|
match self.optFlag:
|
||||||
|
case 0:
|
||||||
|
if self.open() == 0:
|
||||||
|
ret0 = self.exeCmd('openBreaker')
|
||||||
|
# self.logger.info(f"setOverLimitValues ret: {ret0}")
|
||||||
|
ret1 = self.exeCmd('setOverLimitValues')
|
||||||
|
# self.logger.info(f"setOverLimitValues ret: {ret1}")
|
||||||
|
ret2 = self.exeCmd('readOverLimitValues')
|
||||||
|
self.logger.info(f"readOverLimitValues ret: {ret2}")
|
||||||
|
ret3 = self.exeCmd('setOverLimitActionTime')
|
||||||
|
# self.logger.info(f"setOverLimitValues ret: {ret3}")
|
||||||
|
ret4 = self.exeCmd('readOverLimitActionTime')
|
||||||
|
self.logger.info(f"readOverLimitActionTime ret: {ret4}")
|
||||||
|
if ret0[0] and ret1[0] and ret3[0]:
|
||||||
|
self.optFlag = 1
|
||||||
|
continue
|
||||||
|
self.optFlag = -1
|
||||||
|
self.errorCode = 0x0101
|
||||||
|
case 1:
|
||||||
|
time.sleep(0.2)
|
||||||
|
ret = self.exeCmd('readAllDatas')
|
||||||
|
self.logger.info(f"readAllDatas ret: {wordData2HexStr(ret[1])}")
|
||||||
|
if ret[0]:
|
||||||
|
self.parseData('readAllDatas', ret[1])
|
||||||
|
continue
|
||||||
|
self.optFlag = -1
|
||||||
|
self.errorCode = 0x0101
|
||||||
|
case 2:
|
||||||
|
ret = self.exeCmd('openBreaker')
|
||||||
|
if ret[0]:
|
||||||
|
self.optFlag = 1
|
||||||
|
continue
|
||||||
|
self.optFlag = -1
|
||||||
|
self.errorCode = 0x0101
|
||||||
|
case 3:
|
||||||
|
ret = self.exeCmd('closeBreaker')
|
||||||
|
if ret[0]:
|
||||||
|
self.optFlag = 1
|
||||||
|
continue
|
||||||
|
self.optFlag = -1
|
||||||
|
self.errorCode = 0x0101
|
||||||
|
case _:
|
||||||
|
time.sleep(1)
|
||||||
|
self.close()
|
||||||
|
self.optFlag = 0
|
||||||
|
except Exception as e:
|
||||||
|
self.close()
|
||||||
|
self.logger.info(f"Error in Breader: run(), {e}")
|
||||||
|
|
||||||
class GPS:
|
class GPS:
|
||||||
def __init__(self, config:dict, logger):
|
def __init__(self, config:dict, logger):
|
||||||
self.status = -1
|
self.status = -1
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
|
self.config = config
|
||||||
self.port = config.get('port', '/dev/ttyLP4')
|
self.port = config.get('port', '/dev/ttyLP4')
|
||||||
if self.port != '/dev/ttyLP3':
|
if self.port != '/dev/ttyLP4':
|
||||||
self.status = -201
|
self.status = -201
|
||||||
self.baudrate = config.get('baudrate', 9600)
|
self.baudrate = config.get('baudrate', 9600)
|
||||||
self.timeout = config.get('timeout', 1)
|
self.timeout = config.get('timeout', 1)
|
||||||
|
|
@ -780,9 +1065,12 @@ class HSDAQ:
|
||||||
try:
|
try:
|
||||||
self.config = config
|
self.config = config
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
result = subprocess.run(["ip","neigh","add", "192.168.0.2", "lladdr","00:0A:35:01:FE:C0", "dev", "ethernet0"], capture_output=True, text=True, encoding="gbk")
|
result = subprocess.run(["ip","neigh","add", "192.168.0.2", "lladdr","00:0A:35:01:FE:C0", "dev", "ethernet0"], capture_output=True, text=True, encoding="utf-8")
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
self.logger.info(result.stderr)
|
self.logger.info(result.stderr)
|
||||||
|
# result = subprocess.run(["sudo","ethtool","-s", "ethernet0", "speed", "100", "duplex", "full", "autoneg", "off"], capture_output=True, text=True, encoding="utf-8")
|
||||||
|
# if result.returncode != 0:
|
||||||
|
# self.logger.info(result.stderr)
|
||||||
|
|
||||||
# 设置允许强制修改缓存区大小
|
# 设置允许强制修改缓存区大小
|
||||||
self.sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.ntohs(0x0003))
|
self.sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.ntohs(0x0003))
|
||||||
|
|
@ -1041,15 +1329,26 @@ class HSDAQ:
|
||||||
else:
|
else:
|
||||||
_data = datas[:,i]
|
_data = datas[:,i]
|
||||||
|
|
||||||
|
_data = _data[0:20]
|
||||||
log_data = np.log(np.abs(_data) + 1e-300)
|
log_data = np.log(np.abs(_data) + 1e-300)
|
||||||
log_mean_squared = 2 * np.mean(log_data) + np.log(len(_data))
|
log_mean_squared = 2 * np.mean(log_data) + np.log(len(_data))
|
||||||
_rms = np.exp(0.5 * log_mean_squared) / self.scale
|
_rms = np.exp(0.5 * log_mean_squared) / self.scale
|
||||||
|
|
||||||
# _rms = np.sqrt(np.mean(_data**2))/self.scale
|
# _rms = np.sqrt(np.mean(_data**2))/self.scale
|
||||||
|
_min = np.min(_data)
|
||||||
|
_max = np.max(_data)
|
||||||
|
_mean = np.mean(_data)
|
||||||
|
|
||||||
|
# if (_max - _mean) * 5 < (_mean - _min):
|
||||||
|
# _mean = np.mean(_data[0:20])
|
||||||
|
# _min = np.min(_data[0:20])
|
||||||
|
# _max = np.max(_data[0:20])
|
||||||
|
# _rms = np.sqrt(np.mean(np.square(_data[0:20])))
|
||||||
|
|
||||||
self.feature_data[f'CH{j}'] = {
|
self.feature_data[f'CH{j}'] = {
|
||||||
'min': np.min(_data)/self.scale,
|
'min': _min/self.scale,
|
||||||
'max': np.max(_data)/self.scale,
|
'max': _max/self.scale,
|
||||||
'mean': np.mean(_data)/self.scale,
|
'mean': _mean/self.scale,
|
||||||
'std': np.std(_data)/self.scale,
|
'std': np.std(_data)/self.scale,
|
||||||
'rms': _rms,
|
'rms': _rms,
|
||||||
'sr0': 0.0,
|
'sr0': 0.0,
|
||||||
|
|
@ -1331,7 +1630,10 @@ class ModbusGateway:
|
||||||
regs_config_file='src/regs-mapping-1.2-debug.yaml',
|
regs_config_file='src/regs-mapping-1.2-debug.yaml',
|
||||||
config_file=config_file,
|
config_file=config_file,
|
||||||
logger=self.logger)
|
logger=self.logger)
|
||||||
|
|
||||||
|
self.taskInfo = {'status':0x0000, 'running_time':0, 'period':0}
|
||||||
|
self.taskInfo['period'] = self.config_manager.config['task']['period']*60
|
||||||
|
|
||||||
# 初始化influxdb client
|
# 初始化influxdb client
|
||||||
self.influx_client = InfluxDBWriter(
|
self.influx_client = InfluxDBWriter(
|
||||||
url=self.config_manager.config['influxdb'].get('url', 'http://localhost:8086'),
|
url=self.config_manager.config['influxdb'].get('url', 'http://localhost:8086'),
|
||||||
|
|
@ -1342,12 +1644,12 @@ class ModbusGateway:
|
||||||
|
|
||||||
# 连接plc server
|
# 连接plc server
|
||||||
self.plc_host = self.config_manager.config['plc-server'].get('host', '172.22.0.3')
|
self.plc_host = self.config_manager.config['plc-server'].get('host', '172.22.0.3')
|
||||||
self.plc_port = self.config_manager.config['plc-server'].get('port', 502)
|
self.plc_port = self.config_manager.config['plc-server'].get('port', 5020)
|
||||||
self.plc_client = ModbusTcpClient(self.plc_host, port=self.plc_port)
|
self.plc_client = ModbusTcpClient(self.plc_host, port=self.plc_port)
|
||||||
self.plc_measurements = self.config_manager.config['plc-server'].get('measurements', {})
|
self.plc_measurements = self.config_manager.config['plc-server'].get('measurements', {})
|
||||||
|
|
||||||
# 创建本地modbus tcp服务器
|
# 创建本地modbus tcp服务器
|
||||||
self.max_address = 1200
|
self.max_address = 1300
|
||||||
self.host = self.config_manager.config['modbus-server']['host']
|
self.host = self.config_manager.config['modbus-server']['host']
|
||||||
self.port = self.config_manager.config['modbus-server']['port']
|
self.port = self.config_manager.config['modbus-server']['port']
|
||||||
|
|
||||||
|
|
@ -1373,6 +1675,7 @@ class ModbusGateway:
|
||||||
self.logger.info(f"Local modbusTCP service starts, IP={self.host}, port={self.port}")
|
self.logger.info(f"Local modbusTCP service starts, IP={self.host}, port={self.port}")
|
||||||
|
|
||||||
self.gps = GPS(self.config_manager.config['gps'], self.logger)
|
self.gps = GPS(self.config_manager.config['gps'], self.logger)
|
||||||
|
self.breaker = Breaker(self.config_manager.config['breaker'], self.logger)
|
||||||
self.lsdaq = LSDAQ(self.config_manager.config['lsdaq'], self.logger)
|
self.lsdaq = LSDAQ(self.config_manager.config['lsdaq'], self.logger)
|
||||||
self.hsdaq = HSDAQ(self.config_manager.config['hsdaq'], self.logger)
|
self.hsdaq = HSDAQ(self.config_manager.config['hsdaq'], self.logger)
|
||||||
|
|
||||||
|
|
@ -1380,6 +1683,10 @@ class ModbusGateway:
|
||||||
self.gps_thread.daemon = True
|
self.gps_thread.daemon = True
|
||||||
self.gps_thread.start()
|
self.gps_thread.start()
|
||||||
|
|
||||||
|
self.breaker_thread = threading.Thread(target=self.breaker.run)
|
||||||
|
self.breaker_thread.daemon = True
|
||||||
|
self.breaker_thread.start()
|
||||||
|
|
||||||
self.lsdaq_thread = threading.Thread(target=self.lsdaq.run)
|
self.lsdaq_thread = threading.Thread(target=self.lsdaq.run)
|
||||||
self.lsdaq_thread.daemon = True
|
self.lsdaq_thread.daemon = True
|
||||||
self.lsdaq_thread.start()
|
self.lsdaq_thread.start()
|
||||||
|
|
@ -1387,15 +1694,11 @@ class ModbusGateway:
|
||||||
self.hsdaq_thread = threading.Thread(target=self.hsdaq.run)
|
self.hsdaq_thread = threading.Thread(target=self.hsdaq.run)
|
||||||
self.hsdaq_thread.daemon = True
|
self.hsdaq_thread.daemon = True
|
||||||
self.hsdaq_thread.start()
|
self.hsdaq_thread.start()
|
||||||
|
|
||||||
# diagnostics for write calls
|
|
||||||
self._write_call_count = 0
|
|
||||||
self._last_write_ts = None
|
|
||||||
|
|
||||||
# 启动配置服务(HTTP API)
|
# 启动配置服务(HTTP API)
|
||||||
config_service_port = self.config_manager.config.get('config-server', {}).get('port', 5000)
|
config_service_port = self.config_manager.config.get('config-server', {}).get('port', 5000)
|
||||||
config_service_host = self.config_manager.config.get('config-server', {}).get('host', '127.0.0.1')
|
config_service_host = self.config_manager.config.get('config-server', {}).get('host', '127.0.0.1')
|
||||||
config_serial_port = self.config_manager.config.get('config-server', {}).get('serial_port', 'ttyUSB0')
|
|
||||||
# 如果配置文件中指定了配置文件路径,使用它;否则使用默认的YAML配置文件
|
# 如果配置文件中指定了配置文件路径,使用它;否则使用默认的YAML配置文件
|
||||||
config_service_config_path = self.config_manager.config.get('config-server', {}).get('config_path', config_file_temp)
|
config_service_config_path = self.config_manager.config.get('config-server', {}).get('config_path', config_file_temp)
|
||||||
|
|
||||||
|
|
@ -1405,7 +1708,6 @@ class ModbusGateway:
|
||||||
port=config_service_port,
|
port=config_service_port,
|
||||||
debug=False,
|
debug=False,
|
||||||
logger=self.logger,
|
logger=self.logger,
|
||||||
serial_port=config_serial_port
|
|
||||||
)
|
)
|
||||||
self.config_service.start()
|
self.config_service.start()
|
||||||
self.logger.info(f"Config service started on {config_service_host}:{config_service_port}")
|
self.logger.info(f"Config service started on {config_service_host}:{config_service_port}")
|
||||||
|
|
@ -1426,6 +1728,11 @@ class ModbusGateway:
|
||||||
for i in range(len(gps_reg_values)):
|
for i in range(len(gps_reg_values)):
|
||||||
holding_registers.server_set_values(2*i+1, float_to_registers(gps_reg_values[i])) # type: ignore
|
holding_registers.server_set_values(2*i+1, float_to_registers(gps_reg_values[i])) # type: ignore
|
||||||
|
|
||||||
|
breaker_reg_values = list(self.breaker.reg_values.values())
|
||||||
|
# print(f"breaker_reg_values:{breaker_reg_values}")
|
||||||
|
for i in range(len(breaker_reg_values)):
|
||||||
|
holding_registers.server_set_values(1220+i+1, int(breaker_reg_values[i])) # type: ignore
|
||||||
|
|
||||||
lsdaq_reg_values = list(self.lsdaq.reg_values.values())
|
lsdaq_reg_values = list(self.lsdaq.reg_values.values())
|
||||||
# print(f"lsdaq_reg_values:{lsdaq_reg_values}")
|
# print(f"lsdaq_reg_values:{lsdaq_reg_values}")
|
||||||
for i in range(len(lsdaq_reg_values)):
|
for i in range(len(lsdaq_reg_values)):
|
||||||
|
|
@ -1448,19 +1755,19 @@ class ModbusGateway:
|
||||||
holding_registers.server_set_values(1145+i+1, int(hsdaq_warning_values[i])) # type: ignore
|
holding_registers.server_set_values(1145+i+1, int(hsdaq_warning_values[i])) # type: ignore
|
||||||
|
|
||||||
# 更新从plc采集到的数据
|
# 更新从plc采集到的数据
|
||||||
packed_data = bytearray()
|
# packed_data = bytearray()
|
||||||
for k, v in self.plc_measurements.items():
|
# for k, v in self.plc_measurements.items():
|
||||||
packed_data.extend(struct.pack('>fHffH', v['value'], v['warning_param']['enable'], v['warning_param']['lower'], v['warning_param']['upper'], v['warning']))
|
# packed_data.extend(struct.pack('>fHffH', v['value'], v['warning_param']['enable'], v['warning_param']['lower'], v['warning_param']['upper'], v['warning']))
|
||||||
register_values = []
|
# register_values = []
|
||||||
for i in range(0, len(packed_data), 2):
|
# for i in range(0, len(packed_data), 2):
|
||||||
if i + 1 < len(packed_data):
|
# if i + 1 < len(packed_data):
|
||||||
# 组合两个字节为一个16位整数
|
# # 组合两个字节为一个16位整数
|
||||||
value = (packed_data[i] << 8) | packed_data[i + 1]
|
# value = (packed_data[i] << 8) | packed_data[i + 1]
|
||||||
else:
|
# else:
|
||||||
# 如果字节数为奇数,最后一个字节补0
|
# # 如果字节数为奇数,最后一个字节补0
|
||||||
value = packed_data[i] << 8
|
# value = packed_data[i] << 8
|
||||||
register_values.append(value)
|
# register_values.append(value)
|
||||||
holding_registers.server_set_values(1161+1, register_values)
|
# holding_registers.server_set_values(1161+1, register_values)
|
||||||
|
|
||||||
# 读取CPU各温区温度
|
# 读取CPU各温区温度
|
||||||
thermal_zones = 5
|
thermal_zones = 5
|
||||||
|
|
@ -1491,8 +1798,6 @@ class ModbusGateway:
|
||||||
)
|
)
|
||||||
self.write_api = self.influx_client.write_api(write_options=SYNCHRONOUS)
|
self.write_api = self.influx_client.write_api(write_options=SYNCHRONOUS)
|
||||||
try:
|
try:
|
||||||
# 诊断:测量构建 points 与实际写入的耗时
|
|
||||||
t_build_start = time.perf_counter()
|
|
||||||
points = []
|
points = []
|
||||||
# 构建toradex核心板CPU温度
|
# 构建toradex核心板CPU温度
|
||||||
point = Point("PCM_Measurement")
|
point = Point("PCM_Measurement")
|
||||||
|
|
@ -1508,55 +1813,63 @@ class ModbusGateway:
|
||||||
point.field(field_name, float(field_value))
|
point.field(field_name, float(field_value))
|
||||||
points.append(point)
|
points.append(point)
|
||||||
|
|
||||||
|
# 构建Breaker采集数据
|
||||||
|
point = Point("PCM_Measurement")
|
||||||
|
point.tag("data_type", 'Breaker')
|
||||||
|
for field_name, field_value in self.breaker.reg_values.items():
|
||||||
|
point.field(field_name, float(field_value))
|
||||||
|
points.append(point)
|
||||||
|
|
||||||
|
# 构建TaskInfo采集数据
|
||||||
|
point = Point("PCM_Measurement")
|
||||||
|
point.tag("data_type", 'TaskInfo')
|
||||||
|
print(f"taskInfo = {self.taskInfo}")
|
||||||
|
for field_name, field_value in self.taskInfo.items():
|
||||||
|
if field_value == None:
|
||||||
|
field_value = 0
|
||||||
|
point.field(field_name, float(field_value))
|
||||||
|
points.append(point)
|
||||||
|
|
||||||
# 构建低速采集数据
|
# 构建低速采集数据
|
||||||
point = Point("PCM_Measurement")
|
point = Point("PCM_Measurement")
|
||||||
point.tag("data_type", 'LSDAQ')
|
point.tag("data_type", 'LSDAQ')
|
||||||
|
# for field_name, field_value in self.lsdaq.reg_values.items():
|
||||||
|
# point.field(field_name, float(field_value))
|
||||||
|
# for field_name, field_value in self.lsdaq.warning_values.items():
|
||||||
|
# point.field(field_name+'.warning', field_value)
|
||||||
for field_name, field_value in self.lsdaq.reg_values.items():
|
for field_name, field_value in self.lsdaq.reg_values.items():
|
||||||
if field_name in self.lsdaq.alias and self.lsdaq.alias.get(field_name) != '':
|
if field_name in self.lsdaq.alias and self.lsdaq.alias.get(field_name) != '':
|
||||||
point.field(self.lsdaq.alias[field_name], float(field_value))
|
point.field(self.lsdaq.alias[field_name], float(field_value))
|
||||||
for field_name, field_value in self.lsdaq.warning_values.items():
|
for field_name, field_value in self.lsdaq.warning_values.items():
|
||||||
if field_name in self.lsdaq.alias and self.lsdaq.alias.get(field_name) != '':
|
if field_name in self.lsdaq.alias and self.lsdaq.alias.get(field_name) != '':
|
||||||
point.field(self.lsdaq.alias[field_name]+'.warning', field_value)
|
point.field(self.lsdaq.alias[field_name]+'.warning', field_value)
|
||||||
points.append(point)
|
points.append(point)
|
||||||
|
|
||||||
# 构建高速采集数据
|
# 构建高速采集数据
|
||||||
for i in range(16):
|
for i in range(16):
|
||||||
|
# print(str(self.hsdaq.feature_data[f'CH{i}']))
|
||||||
j = i + 1
|
j = i + 1
|
||||||
point = Point("PCM_Measurement")
|
point = Point("PCM_Measurement")
|
||||||
|
# point.tag("data_type", f'HSDAQ_CH{j}')
|
||||||
if self.hsdaq.alias.get(f'CH{j}') != '':
|
if self.hsdaq.alias.get(f'CH{j}') != '':
|
||||||
point.tag("data_type", self.hsdaq.alias[f'CH{j}'])
|
point.tag("data_type", self.hsdaq.alias[f'CH{j}'])
|
||||||
for field_name, field_value in self.hsdaq.feature_data[f'CH{j}'].items():
|
for field_name, field_value in self.hsdaq.feature_data[f'CH{j}'].items():
|
||||||
point.field(field_name, float(field_value))
|
point.field(field_name, float(field_value))
|
||||||
point.field('warning', self.hsdaq.warning_values[f'CH{j}'])
|
point.field('warning', self.hsdaq.warning_values[f'CH{j}'])
|
||||||
points.append(point)
|
points.append(point)
|
||||||
|
|
||||||
# 构建从PLC采集到的数据
|
# 构建从PLC采集到的数据
|
||||||
point = Point("PCM_Measurement")
|
point = Point("PCM_Measurement")
|
||||||
point.tag("data_type", f'PLC')
|
point.tag("data_type", f'PLC')
|
||||||
for k, v in self.plc_measurements.items():
|
for k, v in self.plc_measurements.items():
|
||||||
point.field(k, float(v['value']))
|
point.field(k, float(v['value']))
|
||||||
points.append(point)
|
points.append(point)
|
||||||
t_build = time.perf_counter() - t_build_start
|
|
||||||
|
|
||||||
# 测量写入 API 的耗时
|
# 将数据点写入influxdb
|
||||||
t_write_start = time.perf_counter()
|
|
||||||
self.influx_client.write_batch_data(points)
|
self.influx_client.write_batch_data(points)
|
||||||
t_write_api = time.perf_counter() - t_write_start
|
|
||||||
self.logger.info(f"_write_to_influxdb: build={t_build:.3f}s write_api={t_write_api:.3f}s points={len(points)}")
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error in _write_to_influxdb(): {e}")
|
self.logger.error(f"Error in _write_to_influxdb(): {e}")
|
||||||
finally:
|
|
||||||
# write-call diagnostics: count and delta since last call
|
|
||||||
try:
|
|
||||||
now_ts = time.time()
|
|
||||||
last = getattr(self, "_last_write_ts", None)
|
|
||||||
delta = (now_ts - last) if last else None
|
|
||||||
self._last_write_ts = now_ts
|
|
||||||
self._write_call_count = getattr(self, "_write_call_count", 0) + 1
|
|
||||||
self.logger.info(f"WriteCall #{self._write_call_count} at {datetime.now()} delta_since_last={delta if delta is not None else 0:.3f}s")
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _read_plc_datas(self):
|
def _read_plc_datas(self):
|
||||||
if not self.plc_client:
|
if not self.plc_client:
|
||||||
|
|
@ -1581,45 +1894,59 @@ class ModbusGateway:
|
||||||
def run(self):
|
def run(self):
|
||||||
"""主运行循环"""
|
"""主运行循环"""
|
||||||
timestamp = time.time()
|
timestamp = time.time()
|
||||||
|
task_control_reg_addr = self.config_manager.config['task']['control_reg_addr']
|
||||||
|
task_control_reg_value = 0x0000
|
||||||
|
self.taskInfo['start_time'] = None
|
||||||
|
self.taskInfo['running_time'] = 0
|
||||||
while True:
|
while True:
|
||||||
self.gps.config = self.config_manager.config['gps']
|
self.gps.config = self.config_manager.config['gps']
|
||||||
self.lsdaq.config = self.config_manager.config['lsdaq']
|
self.lsdaq.config = self.config_manager.config['lsdaq']
|
||||||
self.lsdaq.update_config()
|
self.lsdaq.update_config()
|
||||||
self.hsdaq.config = self.config_manager.config['hsdaq']
|
self.hsdaq.config = self.config_manager.config['hsdaq']
|
||||||
self.hsdaq.update_config()
|
self.hsdaq.update_config()
|
||||||
|
self.taskInfo['status'] = self.holding_registers.values[task_control_reg_addr+1]
|
||||||
|
match self.taskInfo['status']:
|
||||||
|
case 0x0000:
|
||||||
|
self.breaker.openBreaker()
|
||||||
|
self.taskInfo['start_time'] = None
|
||||||
|
self.taskInfo['running_time'] = 0
|
||||||
|
case 0x5555:
|
||||||
|
self.breaker.closeBreaker()
|
||||||
|
if self.breaker.reg_values['load_status'] == 1:
|
||||||
|
current_time = time.time()
|
||||||
|
self.taskInfo['running_time'] += ( current_time - self.taskInfo['start_time'] )
|
||||||
|
self.taskInfo['start_time'] = current_time
|
||||||
|
else:
|
||||||
|
self.taskInfo['start_time'] = time.time()
|
||||||
|
|
||||||
|
if self.taskInfo['running_time'] > self.taskInfo['period']:
|
||||||
|
self.holding_registers.values[task_control_reg_addr+1] = 0xFFFF
|
||||||
|
continue
|
||||||
|
|
||||||
|
if 1 in self.lsdaq.warning_values.values() or 1 in self.hsdaq.warning_values.values():
|
||||||
|
self.breaker.alarming()
|
||||||
|
self.holding_registers.values[task_control_reg_addr+1] = 0xFFFF
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
self.breaker.unalarming()
|
||||||
|
case 0xAAAA:
|
||||||
|
self.breaker.openBreaker()
|
||||||
|
self.taskInfo['start_time'] = time.time()
|
||||||
|
case 0xFFFF:
|
||||||
|
self.holding_registers.values[task_control_reg_addr+1] = 0x0000
|
||||||
|
|
||||||
# time.sleep(0.01)
|
# time.sleep(0.01)
|
||||||
nowtime = time.time()
|
nowtime = time.time()
|
||||||
# print(f"{timestamp}, {nowtime}")
|
# print(f"{timestamp}, {nowtime}")
|
||||||
if (nowtime-timestamp) > 1:
|
if (nowtime-timestamp) > 1:
|
||||||
timestamp = nowtime
|
timestamp = nowtime
|
||||||
# 将数据写入influxdb,并测量耗时以便诊断性能问题
|
#将数据写入influxdb
|
||||||
try:
|
self._write_to_influxdb()
|
||||||
t0 = time.perf_counter()
|
# self._read_plc_datas()
|
||||||
self._write_to_influxdb()
|
self._update_modbus_datas()
|
||||||
dt_write = time.perf_counter() - t0
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.error(f"_write_to_influxdb exception: {e}")
|
|
||||||
dt_write = -1.0
|
|
||||||
|
|
||||||
try:
|
# 控制寄存器
|
||||||
t0 = time.perf_counter()
|
|
||||||
self._read_plc_datas()
|
|
||||||
dt_read = time.perf_counter() - t0
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.error(f"_read_plc_datas exception: {e}")
|
|
||||||
dt_read = -1.0
|
|
||||||
|
|
||||||
total_dt = (dt_write if dt_write > 0 else 0.0) + (dt_read if dt_read > 0 else 0.0)
|
|
||||||
self.logger.info(f"Timing: _write_to_influxdb={dt_write:.3f}s _read_plc_datas={dt_read:.3f}s total={total_dt:.3f}s")
|
|
||||||
# 测量 _update_modbus_datas 耗时以定位主循环延迟来源
|
|
||||||
try:
|
|
||||||
t0 = time.perf_counter()
|
|
||||||
self._update_modbus_datas()
|
|
||||||
dt_update = time.perf_counter() - t0
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.error(f"_update_modbus_datas exception: {e}")
|
|
||||||
dt_update = -1.0
|
|
||||||
self.logger.info(f"Timing: _update_modbus_datas={dt_update:.3f}s")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
gateway = ModbusGateway()
|
gateway = ModbusGateway()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue