risingLee 2025-12-23 16:13:19 +08:00
parent 8cc61d099e
commit cb28159b37
3 changed files with 2492 additions and 104 deletions

5
.task_state.json Normal file
View File

@ -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

View File

@ -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()