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 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
|
||||
length = len(data)
|
||||
if length % 2 != 0:
|
||||
return 0
|
||||
for i in range(0, length, 2):
|
||||
val = data[i] * 256 + data[i + 1]
|
||||
crc = crc ^ val
|
||||
return crc
|
||||
for byte in data:
|
||||
crc ^= byte
|
||||
for _ in range(8):
|
||||
if crc & 0x0001:
|
||||
crc = (crc >> 1) ^ 0xA001
|
||||
else:
|
||||
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():
|
||||
now = datetime.now()
|
||||
|
|
@ -216,15 +232,6 @@ class ConfigManager:
|
|||
# self.observer.stop()
|
||||
# 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:
|
||||
def __init__(self):
|
||||
self.key_to_address = {} # 配置键 -> (地址, 类型)
|
||||
|
|
@ -357,26 +364,55 @@ class ModbusSequentialDataBlockForPCM(ModbusSequentialDataBlock):
|
|||
super().setValues(address, values)
|
||||
return
|
||||
|
||||
super().setValues(address, values)
|
||||
|
||||
# Handle client writes
|
||||
updated = False
|
||||
for i, value in enumerate(values):
|
||||
reg_addr = address-1
|
||||
print(f"*************************address={address}, values={values}*************************")
|
||||
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)
|
||||
print(f"{path}:{reg_addr}")
|
||||
if self.config_manager.update_config_value(path[0], value):
|
||||
updated = True
|
||||
|
||||
super().setValues(address, values)
|
||||
|
||||
print(f"*************************{path}, {reg_addr}, {regCount}*************************")
|
||||
dataType = self.config_manager.mapping.key_to_address[path[0]][1]
|
||||
print(f"*************************{path}, {dataType}, {regCount}*************************")
|
||||
if len(path) > 0:
|
||||
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:
|
||||
self.config_manager.save_config()
|
||||
self.logger.debug(f"Register {address} update triggered configuration change")
|
||||
|
||||
def server_set_values(self, address, values):
|
||||
"""Server-only write method that won't trigger YAML update"""
|
||||
self._is_client_write = False
|
||||
self.setValues(address, values)
|
||||
self._is_client_write = True
|
||||
# self._is_client_write = False
|
||||
# self.setValues(address, values)
|
||||
# self._is_client_write = True
|
||||
super().setValues(address, values)
|
||||
|
||||
class LSDAQ:
|
||||
def __init__(self, config:dict, logger):
|
||||
|
|
@ -405,7 +441,7 @@ class LSDAQ:
|
|||
if self.port != '/dev/ttyLP3':
|
||||
self.status = -201
|
||||
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.channels = config.get('channels', 16)
|
||||
if self.mode not in [0, 1]:
|
||||
|
|
@ -687,12 +723,261 @@ class LSDAQ:
|
|||
self.close()
|
||||
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:
|
||||
def __init__(self, config:dict, logger):
|
||||
self.status = -1
|
||||
self.logger = logger
|
||||
self.config = config
|
||||
self.port = config.get('port', '/dev/ttyLP4')
|
||||
if self.port != '/dev/ttyLP3':
|
||||
if self.port != '/dev/ttyLP4':
|
||||
self.status = -201
|
||||
self.baudrate = config.get('baudrate', 9600)
|
||||
self.timeout = config.get('timeout', 1)
|
||||
|
|
@ -780,9 +1065,12 @@ class HSDAQ:
|
|||
try:
|
||||
self.config = config
|
||||
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:
|
||||
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))
|
||||
|
|
@ -1041,15 +1329,26 @@ class HSDAQ:
|
|||
else:
|
||||
_data = datas[:,i]
|
||||
|
||||
_data = _data[0:20]
|
||||
log_data = np.log(np.abs(_data) + 1e-300)
|
||||
log_mean_squared = 2 * np.mean(log_data) + np.log(len(_data))
|
||||
_rms = np.exp(0.5 * log_mean_squared) / 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}'] = {
|
||||
'min': np.min(_data)/self.scale,
|
||||
'max': np.max(_data)/self.scale,
|
||||
'mean': np.mean(_data)/self.scale,
|
||||
'min': _min/self.scale,
|
||||
'max': _max/self.scale,
|
||||
'mean': _mean/self.scale,
|
||||
'std': np.std(_data)/self.scale,
|
||||
'rms': _rms,
|
||||
'sr0': 0.0,
|
||||
|
|
@ -1331,7 +1630,10 @@ class ModbusGateway:
|
|||
regs_config_file='src/regs-mapping-1.2-debug.yaml',
|
||||
config_file=config_file,
|
||||
logger=self.logger)
|
||||
|
||||
|
||||
self.taskInfo = {'status':0x0000, 'running_time':0, 'period':0}
|
||||
self.taskInfo['period'] = self.config_manager.config['task']['period']*60
|
||||
|
||||
# 初始化influxdb client
|
||||
self.influx_client = InfluxDBWriter(
|
||||
url=self.config_manager.config['influxdb'].get('url', 'http://localhost:8086'),
|
||||
|
|
@ -1342,12 +1644,12 @@ class ModbusGateway:
|
|||
|
||||
# 连接plc server
|
||||
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_measurements = self.config_manager.config['plc-server'].get('measurements', {})
|
||||
|
||||
# 创建本地modbus tcp服务器
|
||||
self.max_address = 1200
|
||||
self.max_address = 1300
|
||||
self.host = self.config_manager.config['modbus-server']['host']
|
||||
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.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.hsdaq = HSDAQ(self.config_manager.config['hsdaq'], self.logger)
|
||||
|
||||
|
|
@ -1380,6 +1683,10 @@ class ModbusGateway:
|
|||
self.gps_thread.daemon = True
|
||||
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.daemon = True
|
||||
self.lsdaq_thread.start()
|
||||
|
|
@ -1387,15 +1694,11 @@ class ModbusGateway:
|
|||
self.hsdaq_thread = threading.Thread(target=self.hsdaq.run)
|
||||
self.hsdaq_thread.daemon = True
|
||||
self.hsdaq_thread.start()
|
||||
|
||||
# diagnostics for write calls
|
||||
self._write_call_count = 0
|
||||
self._last_write_ts = None
|
||||
|
||||
# 启动配置服务(HTTP API)
|
||||
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_serial_port = self.config_manager.config.get('config-server', {}).get('serial_port', 'ttyUSB0')
|
||||
|
||||
# 如果配置文件中指定了配置文件路径,使用它;否则使用默认的YAML配置文件
|
||||
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,
|
||||
debug=False,
|
||||
logger=self.logger,
|
||||
serial_port=config_serial_port
|
||||
)
|
||||
self.config_service.start()
|
||||
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)):
|
||||
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())
|
||||
# print(f"lsdaq_reg_values:{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
|
||||
|
||||
# 更新从plc采集到的数据
|
||||
packed_data = bytearray()
|
||||
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']))
|
||||
register_values = []
|
||||
for i in range(0, len(packed_data), 2):
|
||||
if i + 1 < len(packed_data):
|
||||
# 组合两个字节为一个16位整数
|
||||
value = (packed_data[i] << 8) | packed_data[i + 1]
|
||||
else:
|
||||
# 如果字节数为奇数,最后一个字节补0
|
||||
value = packed_data[i] << 8
|
||||
register_values.append(value)
|
||||
holding_registers.server_set_values(1161+1, register_values)
|
||||
# packed_data = bytearray()
|
||||
# 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']))
|
||||
# register_values = []
|
||||
# for i in range(0, len(packed_data), 2):
|
||||
# if i + 1 < len(packed_data):
|
||||
# # 组合两个字节为一个16位整数
|
||||
# value = (packed_data[i] << 8) | packed_data[i + 1]
|
||||
# else:
|
||||
# # 如果字节数为奇数,最后一个字节补0
|
||||
# value = packed_data[i] << 8
|
||||
# register_values.append(value)
|
||||
# holding_registers.server_set_values(1161+1, register_values)
|
||||
|
||||
# 读取CPU各温区温度
|
||||
thermal_zones = 5
|
||||
|
|
@ -1491,8 +1798,6 @@ class ModbusGateway:
|
|||
)
|
||||
self.write_api = self.influx_client.write_api(write_options=SYNCHRONOUS)
|
||||
try:
|
||||
# 诊断:测量构建 points 与实际写入的耗时
|
||||
t_build_start = time.perf_counter()
|
||||
points = []
|
||||
# 构建toradex核心板CPU温度
|
||||
point = Point("PCM_Measurement")
|
||||
|
|
@ -1508,55 +1813,63 @@ class ModbusGateway:
|
|||
point.field(field_name, float(field_value))
|
||||
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.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():
|
||||
if field_name in self.lsdaq.alias and self.lsdaq.alias.get(field_name) != '':
|
||||
point.field(self.lsdaq.alias[field_name], float(field_value))
|
||||
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) != '':
|
||||
point.field(self.lsdaq.alias[field_name]+'.warning', field_value)
|
||||
points.append(point)
|
||||
|
||||
points.append(point)
|
||||
|
||||
# 构建高速采集数据
|
||||
for i in range(16):
|
||||
# print(str(self.hsdaq.feature_data[f'CH{i}']))
|
||||
j = i + 1
|
||||
point = Point("PCM_Measurement")
|
||||
# point.tag("data_type", f'HSDAQ_CH{j}')
|
||||
if self.hsdaq.alias.get(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():
|
||||
point.field(field_name, float(field_value))
|
||||
point.field('warning', self.hsdaq.warning_values[f'CH{j}'])
|
||||
points.append(point)
|
||||
|
||||
|
||||
# 构建从PLC采集到的数据
|
||||
point = Point("PCM_Measurement")
|
||||
point.tag("data_type", f'PLC')
|
||||
for k, v in self.plc_measurements.items():
|
||||
point.field(k, float(v['value']))
|
||||
points.append(point)
|
||||
t_build = time.perf_counter() - t_build_start
|
||||
|
||||
# 测量写入 API 的耗时
|
||||
t_write_start = time.perf_counter()
|
||||
# 将数据点写入influxdb
|
||||
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:
|
||||
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):
|
||||
if not self.plc_client:
|
||||
|
|
@ -1581,45 +1894,59 @@ class ModbusGateway:
|
|||
def run(self):
|
||||
"""主运行循环"""
|
||||
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:
|
||||
self.gps.config = self.config_manager.config['gps']
|
||||
self.lsdaq.config = self.config_manager.config['lsdaq']
|
||||
self.lsdaq.update_config()
|
||||
self.hsdaq.config = self.config_manager.config['hsdaq']
|
||||
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)
|
||||
nowtime = time.time()
|
||||
# print(f"{timestamp}, {nowtime}")
|
||||
if (nowtime-timestamp) > 1:
|
||||
timestamp = nowtime
|
||||
# 将数据写入influxdb,并测量耗时以便诊断性能问题
|
||||
try:
|
||||
t0 = time.perf_counter()
|
||||
self._write_to_influxdb()
|
||||
dt_write = time.perf_counter() - t0
|
||||
except Exception as e:
|
||||
self.logger.error(f"_write_to_influxdb exception: {e}")
|
||||
dt_write = -1.0
|
||||
#将数据写入influxdb
|
||||
self._write_to_influxdb()
|
||||
# self._read_plc_datas()
|
||||
self._update_modbus_datas()
|
||||
|
||||
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__":
|
||||
gateway = ModbusGateway()
|
||||
|
|
|
|||
Loading…
Reference in New Issue