From 1ab10ea539672ebfec5099a065065274d6c80b86 Mon Sep 17 00:00:00 2001 From: 18210258040 Date: Tue, 27 Jan 2026 09:25:37 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=A8=E4=BA=8ESIS=E6=AD=A6=E6=B1=89?= =?UTF-8?q?=E5=88=86=E5=85=AC=E5=8F=B8PCM=E4=BA=A7=E5=93=81=EF=BC=8C?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/PCM_Service.py | 1204 +++++++++++++++++++++++++++++++++++++++ src/config.yaml | 233 ++++++++ src/default_config.yaml | 226 ++++++++ src/docker-compose.yml | 41 ++ src/logging_config.json | 35 ++ src/regs_mapping.yaml | 556 ++++++++++++++++++ 6 files changed, 2295 insertions(+) create mode 100644 src/PCM_Service.py create mode 100644 src/config.yaml create mode 100644 src/default_config.yaml create mode 100644 src/docker-compose.yml create mode 100644 src/logging_config.json create mode 100644 src/regs_mapping.yaml diff --git a/src/PCM_Service.py b/src/PCM_Service.py new file mode 100644 index 0000000..e84356b --- /dev/null +++ b/src/PCM_Service.py @@ -0,0 +1,1204 @@ +import threading, pynmea2, time, struct, serial, socket, yaml, os, logging.config, json, subprocess, shutil, time, copy, gc, glob +from pymodbus.server import StartTcpServer +from pymodbus.datastore import ModbusSequentialDataBlock +from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext +from threading import Lock +import numpy as np +from datetime import datetime +from pathlib import Path + +def checkValue(data: bytes) -> int: + 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 + +def nowStr(): + now = datetime.now() + ret = now.strftime('%Y/%m/%d %H:%M:%S.') + f"{now.microsecond // 1000:03d}" + return ret + +def wordData2HexStr(data): + if data: + ret = ' '.join(data[i:i+2].hex() for i in range(0, len(data), 2)) + else: + ret = '' + return ret.upper() + +def float_to_registers(value): + packed = struct.pack('>f', value) + return [struct.unpack('>H', packed[0:2])[0], struct.unpack('>H', packed[2:4])[0]] + +class ConfigManager: + def __init__(self, regs_config_file, config_file, logger): + self.config_file = Path(config_file) + self.regs_config_file = Path(regs_config_file) + self.lock = threading.Lock() + self.config = {} + self.regs_config = {} + self.logger = logger + self.mapping = BidirectionalMap() + + self.load_all_configs() + + # 设置文件监视器 + # self.observer = Observer() + # self.event_handler = ConfigFileHandler(self) + # self.observer.schedule(self.event_handler, path=str(self.config_file.parent)) + # self.observer.start() + + def load_all_configs(self): + """加载主配置和寄存器配置""" + with self.lock: + if not os.path.exists(self.config_file): + self.logger.warning(f"Config file {self.config_file} not found") + + if not os.path.exists(self.regs_config_file): + self.logger.warning(f"Regsister mapping file {self.regs_config_file} not found") + + # 加载主配置 + with open(self.config_file, 'r') as f: + self.config = yaml.safe_load(f) + # 低速采集sensor_type处理 + self.config['lsdaq']['sensor_type'] = self.config['lsdaq'].get('sensor_type').replace(' ', '') + if len(self.config['lsdaq']['sensor_type']) != 16 or not all(c in '01' for c in self.config['lsdaq']['sensor_type']): + self.config['lsdaq']['sensor_type'] = '1111111111111111' + self.config['lsdaq']['sensor_type'] = int(self.config['lsdaq']['sensor_type'][::-1], 2) + + # 高速采集sensor_type处理 + self.config['hsdaq']['sensor_type'] = self.config['hsdaq'].get('sensor_type').replace(' ', '') + if len(self.config['hsdaq']['sensor_type']) != 32 or not all(c in '01' for c in self.config['hsdaq']['sensor_type']): + self.config['hsdaq']['sensor_type'] = '11111111111111111111111111111111' + _s = self.config['hsdaq']['sensor_type'] + self.config['hsdaq']['sensor_type'] = int(''.join([_s[2*i:2*i+2] for i in range(len(_s)//2-1, -1, -1)]), 2) + + # 高速采集save_flag处理 + self.config['hsdaq']['save_flag'] = self.config['hsdaq'].get('save_flag').replace(' ', '') + if len(self.config['hsdaq']['save_flag']) != 16 or not all(c in '01' for c in self.config['hsdaq']['save_flag']): + self.config['hsdaq']['save_flag'] = '1111111111111111' + self.config['hsdaq']['save_flag'] = int(self.config['hsdaq']['save_flag'][::-1], 2) + + with open(self.regs_config_file, 'r') as f: + self.regs_config = yaml.safe_load(f) + + # 构建映射关系 + self._build_mappings() + + def _build_mappings(self): + """构建配置键到地址的双向映射""" + # 处理value_regs + # if 'value_regs' in self.regs_config: + # self._process_registers_section(self.regs_config['value_regs'], '', 'value') + + # 处理control_regs + if 'control_regs' in self.regs_config: + self._process_registers_section(self.regs_config['control_regs'], '', 'control') + + def _process_registers_section(self, section, current_path, reg_type): + """处理寄存器配置部分""" + def traverse(node, current_path=""): + # print(f"node={node}, current_path={current_path}") + for key, value in node.items(): + new_path = f"{current_path}.{key}" if current_path else key + if isinstance(value, dict): + if all(isinstance(k, str) and isinstance(v, int) for k, v in value.items()): + # 这是叶子节点,包含寄存器地址 + for sub_key, address in value.items(): + full_path = f"{new_path}.{sub_key}" + self.mapping.add_mapping(full_path, address, reg_type) + else: + traverse(value, new_path) + else: + # 直接映射 + self.mapping.add_mapping(new_path, value[0], value[1]) + + traverse(section, current_path) + # print(f"key_to_address={self.mapping.key_to_address}") + # print(f"address_to_keys={self.mapping.address_to_keys}") + # print(f"key_to_data_type={self.mapping.key_to_data_type}") + # print(f"address_to_data_type={self.mapping.address_to_data_type}") + + def get_config_value(self, config_path): + """通过配置路径获取配置值""" + keys = config_path.split('.') + node = self.config + for key in keys: + if isinstance(node, dict) and key in node: + node = node[key] + else: + return None + return node + + def update_config_value(self, config_path, value): + """更新配置值并保存""" + with self.lock: + # print(config_path) + keys = config_path.split('.') + node = self.config + for key in keys[:-1]: + if key not in node: + node[key] = {} + node = node[key] + node[keys[-1]] = value + + # 保存到文件 + self._save_config() + + def _save_config(self): + """保存配置到文件""" + _config = copy.deepcopy(self.config) + _config['lsdaq']['sensor_type'] = f"{_config['lsdaq']['sensor_type']:016b}"[::-1] + _s = f"{_config['hsdaq']['sensor_type']:032b}" + _config['hsdaq']['sensor_type'] = ''.join([_s[2*i:2*i+2] for i in range(len(_s)//2-1, -1, -1)]) + _config['hsdaq']['save_flag'] = f"{_config['hsdaq']['save_flag']:016b}"[::-1] + + with open(self.config_file, 'w') as f: + yaml.dump(_config, f, sort_keys=False, default_flow_style=False) + + # def close(self): + # 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 = {} # 配置键 -> (地址, 类型) + self.address_to_keys = {} # 地址 -> [配置键] + self.key_to_data_type = {} # 配置键 -> 数据类型 + self.address_to_data_type = {} # 地址 -> 数据类型 + + def add_mapping(self, config_key, address, reg_type, data_type='uint16'): + """添加映射关系""" + self.key_to_address[config_key] = (address, reg_type) + self.address_to_keys.setdefault(address, []).append(config_key) + self.key_to_data_type[config_key] = data_type + self.address_to_data_type[address] = data_type + + def get_address(self, config_key): + """通过配置键获取地址和类型""" + print(self.key_to_address) + return self.key_to_address.get(config_key, (None, None)) + + def get_config_keys(self, address): + """通过地址获取配置键列表""" + # print(self.address_to_keys) + return self.address_to_keys.get(address, []) + + def get_data_type(self, identifier): + """获取数据类型,identifier可以是地址或配置键""" + if isinstance(identifier, int): + return self.address_to_data_type.get(identifier) + else: + return self.key_to_data_type.get(identifier) + +class DataTypeValidator: + @staticmethod + def validate(value, data_type): + try: + if data_type == 'float32': + return float(value) + elif data_type in ('uint16', 'uint32'): + val = int(value) + if data_type == 'uint16' and not (0 <= val <= 65535): + raise ValueError("Value out of range for uint16") + elif data_type == 'uint32' and not (0 <= val <= 4294967295): + raise ValueError("Value out of range for uint32") + return val + elif data_type == 'int32': + val = int(value) + if not (-2147483648 <= val <= 2147483647): + raise ValueError("Value out of range for int32") + return val + elif data_type == 'string': + return str(value) + else: + return int(value) # 默认处理为uint16 + except (ValueError, TypeError) as e: + logging.error(f"Validation failed for {value} as {data_type}: {str(e)}") + return None + +class RegisterConfigEnhancer: + def __init__(self, register_config): + self.register_config = register_config + self.data_type_mapping = self._create_data_type_mapping() + + def _create_data_type_mapping(self): + """为寄存器分配适当的数据类型""" + mapping = {} + + # GPS数据通常需要浮点数 + if 'value_regs' in self.register_config and 'gps' in self.register_config['value_regs']: + for field in ['latitude', 'longitude', 'altitude', 'speed']: + if field in self.register_config['value_regs']['gps']: + addr = self.register_config['value_regs']['gps'][field] + mapping[addr] = 'float32' + + # 传感器校准参数需要浮点数 + for dev in ['lsdaq', 'hsdaq']: + if dev in self.register_config.get('control_regs', {}): + for param_type in ['sensor_Tmp_CalibParam', 'sensor_Cur_CalibParam', + 'sensor_Vol_CalibParam', 'sensor_Vib_CalibParam']: + if param_type in self.register_config['control_regs'][dev]: + for ch in self.register_config['control_regs'][dev][param_type]: + for param in ['K2', 'K', 'B']: + addr = self.register_config['control_regs'][dev][param_type][ch][param] + mapping[addr] = 'float32' + + return mapping + + def get_data_type(self, address): + return self.data_type_mapping.get(address, 'uint16') + +class ModbusSequentialDataBlockForPCM(ModbusSequentialDataBlock): + def __init__(self, config_manager, logger, *args, **kwargs): + super().__init__(*args, **kwargs) + self.config_manager = config_manager + self._is_client_write = True + self.logger = logger + self._initialize_registers() + + def _initialize_registers(self): + """Initialize register values from configuration""" + for key, value in self.config_manager.regs_config['control_regs'].items(): + config_value = self.config_manager.get_config_value(key) + # print(f"{key}:{value[0]}:{config_value}") + if config_value is not None and ('w' in value[2] or 'W' in value[2]): + match value[1]: + case 'float32': + config_value = float(config_value) + self.server_set_values(value[0]+1, float_to_registers(config_value)) + case 'uint32': + config_value = int(config_value) + # self.server_set_values(value[0]+1, [config_value & 0xFFFF, (config_value >> 16) & 0xFFFF]) + self.server_set_values(value[0]+1, [(config_value >> 16) & 0xFFFF, config_value & 0xFFFF]) + case 'int32': + config_value = int(config_value) + # self.server_set_values(value[0]+1, [config_value & 0xFFFF, (config_value >> 16) & 0xFFFF]) + self.server_set_values(value[0]+1, [(config_value >> 16) & 0xFFFF, config_value & 0xFFFF]) + case 'uint16': + config_value = int(config_value) + # print(f"{key}:{value[0]}:{config_value}:{[struct.pack('>H', config_value)[0]]}") + self.server_set_values(value[0]+1, [config_value & 0xFFFF]) + case 'int16': + config_value = int(config_value) + self.server_set_values(value[0]+1, [config_value & 0xFFFF]) + case _: + pass + self.logger.info("Register initialization completed") + + def setValues(self, address, values): + """Override setValues method""" + if not self._is_client_write: + super().setValues(address, values) + return + + # Handle client writes + updated = False + for i, value in enumerate(values): + reg_addr = address-1 + 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) + + 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 + +class ModbusGateway: + def __init__(self): + # 初始化logger + config_file = 'src/config_1.2.yaml' + with open('src/logging_config.json', 'r') as f: + logging.config.dictConfig(json.load(f)) + self.logger = logging.getLogger('PCM') + + self.config_manager = ConfigManager( + regs_config_file='src/regs_mapping_1.2.yaml', + config_file=config_file, + logger=self.logger) + + self.max_address = 1000 + self.host = self.config_manager.config['modbus-server']['host'] + self.port = self.config_manager.config['modbus-server']['port'] + + self.holding_registers = ModbusSequentialDataBlockForPCM(self.config_manager, self.logger, 0x00, [0]*(self.max_address+1)) + + # 创建数据存储 + store = ModbusSlaveContext( + di=ModbusSequentialDataBlock(0, [0]*1), + co=ModbusSequentialDataBlock(0, [0]*1), + # hr=ModbusSequentialDataBlock(0, [0]*1000), + hr=self.holding_registers, + ir=ModbusSequentialDataBlock(0, [0]*1)) + + self.context = ModbusServerContext(slaves={1:store}, single=False) + + # 启动服务器线程 + self.modbus_sever = threading.Thread( + target=StartTcpServer, + # kwargs={"context": self.context, "address": (self.host, self.port)}) + kwargs={"context": self.context, "address": ('0.0.0.0', self.port)}) + self.modbus_sever.daemon = True + self.modbus_sever.start() + 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.lsdaq = LSDAQ(self.config_manager.config['lsdaq'], self.logger) + self.hsdaq = HSDAQ(self.config_manager.config['hsdaq'], self.logger) + + self.gps_thread = threading.Thread(target=self.gps.run) + self.gps_thread.daemon = True + self.gps_thread.start() + + self.lsdaq_thread = threading.Thread(target=self.lsdaq.run) + self.lsdaq_thread.daemon = True + self.lsdaq_thread.start() + + self.hsdaq_thread = threading.Thread(target=self.hsdaq.run) + self.hsdaq_thread.daemon = True + self.hsdaq_thread.start() + + def _update_modbus_datas(self): + """更新本地Modbus服务器的寄存器""" + if not hasattr(self, 'context'): + self.logger.error("Local modbus tcp service isn't initilized.") + return + + try: + # 获取本地服务器的slave上下文 + slave = self.context[1] + holding_registers = self.holding_registers + + gps_reg_values = list(self.gps.gps_data.values()) + # print(f"gps_reg_values:{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 + + 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)): + holding_registers.server_set_values(8+2*i+1, float_to_registers(lsdaq_reg_values[i])) # type: ignore + + hsdaq_reg_values = self.hsdaq.reg_values + # print(f"hsdaq_reg_values:{hsdaq_reg_values}") + # print(f"len = {len(hsdaq_reg_values)}") + for i in range(len(hsdaq_reg_values)): + holding_registers.server_set_values(60+2*i+1, float_to_registers(hsdaq_reg_values[i])) # type: ignore + + # 读取CPU各温区温度 + thermal_zones = 5 + for i in range(thermal_zones): + with open(f"/sys/class/thermal/thermal_zone{i}/temp", 'r') as f: + # 读取温度值(毫摄氏度) + temp_millic = int(f.read().strip()) + # 转换为摄氏度 + temp_c = temp_millic / 1000.0 + + holding_registers.server_set_values(50+2*i+1, float_to_registers(temp_c)) # type: ignore + + except Exception as e: + self.logger.error(f"Error in _update_modbus_datas(): {e}") + + def run(self): + """主运行循环""" + while True: + self._update_modbus_datas() + 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() + time.sleep(0.1) + +class LSDAQ: + def __init__(self, config:dict, logger): + # 加载配置参数 + ''' self.status 码表 + -200: 配置信息错误 + -201: 串口号错误 + -202: 传感器类型错误 + -203: 工作模式错误 + -100: 设备关闭 + -101: 设备未连接 + -1: 多次执行指令失败 + 0: 正常 + 100: 连接失败 + 200: 命令执行失败 + 202: 读取命令错误 + 203: 响应超时 + 204: 报头错误 + 205: 校验错误 + 206: 数据解析错误 + ''' + self.status = -1 + self.config = config + self.logger = logger + self.port = config.get('port', '/dev/ttyLP3') + if self.port != '/dev/ttyLP3': + self.status = -201 + self.baudrate = config.get('baudrate', 115200) + self.timeout = config.get('timeout', 1) + self.mode = config.get('mode', 0) + self.channels = config.get('channels', 16) + if self.mode not in [0, 1]: + self.mode = 0 + self.status = -203 + self.frameNo = 0 + self.sensor_type = config.get('sensor_type', 0xffff) + + self.reg_values = { + 'CH0': 0.0, + 'CH1': 0.0, + 'CH2': 0.0, + 'CH3': 0.0, + 'CH4': 0.0, + 'CH5': 0.0, + 'CH6': 0.0, + 'CH7': 0.0, + 'CH8': 0.0, + 'CH9': 0.0, + 'CH10': 0.0, + 'CH11': 0.0, + 'CH12': 0.0, + 'CH13': 0.0, + 'CH14': 0.0, + 'CH15': 0.0, + 'OFFSET': 0.0, + 'POWERVOL': 0.0, + 'TEMP': 0.0, + 'GAIN': 0.0, + 'REF': 0.0, + 'STATUS': 0.0 + } + _sensor_Tmp_CalibParam = { + 'CH0': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH1': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH2': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH3': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH4': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH5': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH6': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH7': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH8': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH9': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH10': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH11': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH12': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH13': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH14': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH15': {'K2':0.0, 'K': 1.0, 'B': 0.0} + } + _sensor_Cur_CalibParam = { + 'CH0': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH1': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH2': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH3': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH4': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH5': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH6': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH7': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH8': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH9': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH10': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH11': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH12': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH13': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH14': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH15': {'K2':0.0, 'K': 1.0, 'B': 0.0} + } + self.sensor_Tmp_CalibParam = config.get('sensor_Tmp_CalibParam', _sensor_Tmp_CalibParam) + self.sensor_Cur_CalibParam = config.get('sensor_Cur_CalibParam', _sensor_Cur_CalibParam) + + # 构建指令集 + self.cmdList = { + # 查询所有通道采集数据 + # 指令格式:指令字符串,回复长度,超时时间,发送校验标志,接收校验标志,指令描述,重试次数 + 'readAllADs': ['', f"0000 0000 0006 0103 0008 0017", 55, 200, 0, 0, 5] + } + self.optFlag = 0 + def update_config(self): + self.mode = self.config.get('mode') + if self.mode not in [0, 1]: + self.mode = 0 + self.sensor_type = self.config.get('sensor_type', 0xffff) + self.sensor_Tmp_CalibParam = self.config.get('sensor_Tmp_CalibParam') + self.sensor_Cur_CalibParam = self.config.get('sensor_Cur_CalibParam') + + def exeCmd(self, cmdName:str='readAllADs') -> list: # type: ignore + try: + info = '' + cmd = self.cmdList.get(cmdName, None) + self.status = 0 + if cmd is None: + self.status = 202 + 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[2:]).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(f"0000"): + # info += f"[{nowStr()}] Echo:{wordData2HexStr(recvData[0:rspLen])}\n" + rspLen = len(recvData) + 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.status = 205 + else: + self.logger.info(info) + return [True, recvData, info] + else: + self.status = 204 + recvData = recvData[1:] + else: + self.status = 203 + retry += 1 + if retry == RETRYTIMES: + self.status = -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): + _sensor_type = f"{self.sensor_type:016b}"[::-1] + match cmdName: + case 'readAllADs': + datas = struct.unpack('>21H', rawData[9:51]) + if self.mode == 1: + # 校准模式下,直接返回原始数据 + for i in range(self.channels): + self.reg_values[f'CH{i}'] = datas[i] + else: + # 工作模式下,进行数据转换 + for i in range(self.channels): + if _sensor_type[i] == '0': + # 温度传感器 + # self.logger.info(str(self.reg_values)) + self.reg_values[f'CH{i}'] = (datas[i]**2*self.sensor_Tmp_CalibParam[f'CH{i}']['K2'] + datas[i]*self.sensor_Tmp_CalibParam[f'CH{i}']['K'] + self.sensor_Tmp_CalibParam[f'CH{i}']['B']) + elif _sensor_type[i] == '1': + # 电流传感器 + self.reg_values[f'CH{i}'] = (datas[i]**2*self.sensor_Cur_CalibParam[f'CH{i}']['K2'] + datas[i]*self.sensor_Cur_CalibParam[f'CH{i}']['K'] + self.sensor_Cur_CalibParam[f'CH{i}']['B']) + self.reg_values['OFFSET'] = datas[16]/7864 + self.reg_values['POWERVOL'] = datas[17]/7864 + self.reg_values['TEMP'] = datas[18]/98-2734 + self.reg_values['GAIN'] = datas[19]/7864 + self.reg_values['REF'] = datas[20]/7864 + self.reg_values['STATUS'] = self.status + case _: + self.status = 206 + + def open(self): + """打开串口连接""" + self.serial = serial.Serial(self.port, self.baudrate, timeout=self.timeout) + if not self.serial.is_open: + self.status = -101 + return -1 + else: + self.status = 0 + return 0 + + def close(self): + self.serial.close() + self.status = -100 + + def run(self): + """主运行循环""" + try: + while True: + match self.optFlag: + case 0: + if self.open() == 0: + self.optFlag = 1 + else: + self.optFlag = -1 + case 1: + ret = self.exeCmd('readAllADs') + if ret[0]: + self.parseData('readAllADs', ret[1]) + # self.logger.info(str(self.reg_values)) + self.frameNo += 1 + if self.frameNo > 0xFFFF: + self.frameNo = 0 + time.sleep(1) + if self.status == -1: + self.optFlag = -1 + case _: + time.sleep(5) + self.close() + self.optFlag = 0 + except KeyboardInterrupt: + self.close() + self.logger.info("Modbus Serial TCP Client stopped.") + +class GPS: + def __init__(self, config:dict, logger): + self.status = -1 + self.logger = logger + self.port = config.get('port', '/dev/ttyLP4') + if self.port != '/dev/ttyLP3': + self.status = -201 + self.baudrate = config.get('baudrate', 9600) + self.timeout = config.get('timeout', 1) + self.optFlag = 0 + self.gps_data = {'latitude': 0.0, 'longitude': 0.0, 'altitude': 0.0, 'speed': 0.0} + + def read_data(self): + """从串口读取GPS数据""" + if not self.serial or not self.serial.is_open: + return -1 + try: + # 读取NMEA数据 (简化示例,实际需要解析NMEA语句) + line = self.serial.readline().decode('ascii', errors='ignore').strip() + if line.startswith('$GNGGA') or line.startswith('$GPGGA') or line.startswith('$BDGGA'): + # 示例解析GPGGA语句 (实际应用中需要更健壮的解析) + parts = line.split(',') + if len(parts) > 9: + try: + # 纬度格式转换: ddmm.mmmm -> 十进制 + lat = (float(parts[2][:2]) if parts[2] else 0.0) + (float(parts[2][2:]) if parts[2] else 0.0)/60.0 + if parts[3] == 'S': + lat = -lat + + # 经度格式转换: dddmm.mmmm -> 十进制 + lon = (float(parts[4][:3]) if parts[4] else 0.0) + (float(parts[4][3:]) if parts[4] else 0.0)/60.0 + if parts[5] == 'W': + lon = -lon + + # 海拔高度 + alt = float(parts[9]) if parts[9] else 0.0 + + self.gps_data = { + 'latitude': lat, + 'longitude': lon, + 'altitude': alt, + 'speed': 0.0 # GPGGA不包含速度,需要从GPRMC获取 + } + return 0 + except (ValueError, IndexError) as e: + raise Exception(f"Error in parse GPS data: {e}") + except Exception as e: + self.logger.error(f"Error in read_gps_data(): {e}") + + def open(self): + """打开串口连接""" + self.serial = serial.Serial(self.port, self.baudrate, timeout=self.timeout) + if not self.serial.is_open: + self.status = -101 + return -1 + else: + self.status = 0 + return 0 + + def close(self): + self.serial.close() + self.status = -100 + + def run(self): + """主运行循环""" + try: + while True: + match self.optFlag: + case 0: + if self.open() == 0: + self.optFlag = 1 + else: + self.optFlag = -1 + case 1: + ret = self.read_data() + if ret != 0: + self.optFlag = -1 + continue + self.logger.info(str(self.gps_data)) + case _: + time.sleep(5) + self.close() + self.open() + self.optFlag = 0 + except KeyboardInterrupt: + self.close() + self.logger.info("Modbus Serial TCP Client stopped.") + +class HSDAQ: + def __init__(self, config:dict, logger): + 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") + if result.returncode != 0: + self.logger.info(result.stderr) + + # 设置允许强制修改缓存区大小 + self.sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.ntohs(0x0003)) + SO_RCVBUFORCE = 33 + self.sock.setsockopt(socket.SOL_SOCKET, SO_RCVBUFORCE, 1024 * 1024 * 25) + # 设置 SO_NO_CHECK 选项,使用整数值 11 + SO_NO_CHECK = 11 + self.sock.setsockopt(socket.SOL_SOCKET, SO_NO_CHECK, 0) + actual_buf_size = self.sock.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF) + self.logger.info(f"Requested UDP buffer: 50MB, Actual UDP buffer: {actual_buf_size/1024/1024:.2f}MB") + self.sock.bind(('ethernet0', 0)) + + self.dataFileDir = self.config['output_dir'] + self.file_type = self.config.get('file_type', 1) + if self.file_type not in [0, 1]: + self.file_type = 1 + + self.save_flag = self.config.get('save_flag', 0xffff) + self.channels = self.config.get('channels', 16) + + if not os.path.exists(self.dataFileDir): + os.makedirs(self.dataFileDir) + for i in range(self.channels): + os.makedirs(os.path.join(self.config['output_dir'], f"{i+1:02}"), exist_ok=True) + + + self.buffer = b'' + self.feature_data = {} + self.frequency = [0]*16 + self.reg_values = [] + + self.daqBoardNo = self.config.get('daq_board_no', 'XXXXXXXXXX') + self.sensor_type = self.config.get('sensor_type', 0xffffffff) + + self.feature_type = self.config.get('feature_type', '加速度rms') + self.min_vol_cur_phy_value = self.config.get('min_vol_cur_phy_value', 0.0) + self.max_vol_cur_phy_value = self.config.get('max_vol_cur_phy_value', 160.0) + self.scale = self.config.get('vol_cur_phy_scale', 1) + self.sample_rate = self.config.get('sample_rate', 100000) + self.sample_time = self.config.get('sample_time', 1000) + self.sample_period = self.config.get('sample_period', 4000) + self.sample_points = int(self.sample_rate*self.sample_time/1000) + self.one_sample_time = self.config.get('one_sample_time', 10) + self.mode = self.config.get('mode', 0) + + self.cmdList = {'startDAQ': bytes.fromhex(f"DDDD 0001 {self.sample_time*1000:08X} {(self.sample_period-self.sample_time)*1000:08X} {self.one_sample_time:08X} {self.sensor_type:08X}"), + 'stopDAQ': bytes.fromhex(f"DDDD 0000 {self.sample_time*1000:08X} {(self.sample_period-self.sample_time)*1000:08X} {self.one_sample_time:08X} {self.sensor_type:08X}")} + + _sensor_Vol_CalibParam = { + 'CH0': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH1': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH2': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH3': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH4': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH5': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH6': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH7': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH8': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH9': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH10': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH11': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH12': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH13': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH14': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH15': {'K2':0.0, 'K': 1.0, 'B': 0.0} + } + _sensor_Cur_CalibParam = { + 'CH0': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH1': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH2': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH3': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH4': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH5': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH6': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH7': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH8': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH9': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH10': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH11': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH12': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH13': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH14': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH15': {'K2':0.0, 'K': 1.0, 'B': 0.0} + } + _sensor_Vib_CalibParam = { + 'CH0': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH1': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH2': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH3': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH4': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH5': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH6': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH7': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH8': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH9': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH10': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH11': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH12': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH13': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH14': {'K2':0.0, 'K': 1.0, 'B': 0.0}, + 'CH15': {'K2':0.0, 'K': 1.0, 'B': 0.0} + } + self.sensor_Vol_CalibParam = self.config.get('sensor_Vol_CalibParam', _sensor_Vol_CalibParam) + self.sensor_Cur_CalibParam = self.config.get('sensor_Cur_CalibParam', _sensor_Cur_CalibParam) + self.sensor_Vib_CalibParam = self.config.get('sensor_Vib_CalibParam', _sensor_Vib_CalibParam) + self.logger.info(f"DAQ thread starts. Address of DAQ board: IP={self.config['host']}, port={self.config['port']}") + except Exception as e: + self.logger.error(f"Error in __init__(): {e}") + time.sleep(5) + self.__init__() + + def update_config(self): + self.file_type = self.config.get('file_type') + if self.file_type not in [0, 1]: + self.file_type = 1 + self.save_flag = self.config.get('save_flag') + self.sensor_type = self.config.get('sensor_type') + self.sample_rate = self.config.get('sample_rate') + self.sample_time = self.config.get('sample_time') + self.sample_period = self.config.get('sample_period') + self.sample_points = int(self.sample_rate*self.sample_time/1000) + self.one_sample_time = self.config.get('one_sample_time') + self.mode = self.config.get('mode') + if self.mode not in [0, 1]: + self.mode = 1 + + self.cmdList = {'startDAQ': bytes.fromhex(f"DDDD 0001 {self.sample_time*1000:08X} {(self.sample_period-self.sample_time)*1000:08X} {self.one_sample_time:08X} {self.sensor_type:08X}"), + 'stopDAQ': bytes.fromhex(f"DDDD 0000 {self.sample_time*1000:08X} {(self.sample_period-self.sample_time)*1000:08X} {self.one_sample_time:08X} {self.sensor_type:08X}")} + self.sensor_Vol_CalibParam = self.config.get('sensor_Vol_CalibParam') + self.sensor_Cur_CalibParam = self.config.get('sensor_Cur_CalibParam') + self.sensor_Vib_CalibParam = self.config.get('sensor_Vib_CalibParam') + + def start_DAQ(self): + """发送启动采集指令""" + try: + self.buffer = bytearray() + if hasattr(self, 'udpsock') and self.udpsock: + self.udpsock.close() + self.udpsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + # self.udpsock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1024 * 25) + self.udpsock.bind((self.config['local_host'], self.config['local_port'])) + self.udpsock.sendto(self.cmdList['startDAQ'], (self.config['host'], self.config['port'])) + self.udpsock.close() + self.logger.info(f"Send start command to DAQ board. {self.cmdList['startDAQ'].hex()}") + except Exception as e: + self.logger.error(f"Error in start_DAQ(): {e}") + + def stop_DAQ(self): + """发送停止采集指令""" + try: + self.buffer = bytearray() + if hasattr(self, 'udpsock') and self.udpsock: + self.udpsock.close() + self.udpsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + # self.udpsock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1024 * 25) + self.udpsock.bind((self.config['local_host'], self.config['local_port'])) + self.udpsock.sendto(self.cmdList['stopDAQ'], (self.config['host'], self.config['port'])) + self.udpsock.close() + self.logger.info(f"Send stop command to DAQ board. {self.cmdList['stopDAQ'].hex()}") + except Exception as e: + self.logger.error(f"Error in stop_DAQ(): {e}") + + def _get_dir_size(self, path: Path) -> int: + """利用 Linux 的 du,返回目录本身已占用字节数,毫秒级""" + return int(subprocess.check_output( + ['du', '-sb', str(path)], text=True).split()[0]) + + def _oldest_file(self, path: Path): + """返回目录中最旧的普通文件 Path 对象,没有则返回 None""" + with os.scandir(path) as it: + files = [e for e in it if e.is_file()] + if not files: + return None + # 按修改时间升序 + return Path(min(files, key=lambda e: e.stat().st_mtime).path) + + def save_data(self): + """保存数据到文件""" + try: + #判断磁盘剩余空间是否小于1G,如果是从16通道的旧文件目录中删除文件 + channels = self.channels + # usage = shutil.disk_usage("C:/") + # while usage.free < self.config['daq']['min_free_gb']*1024*1024*1024: + # #获取目录下文件列表,并按照降序排序,如果硬盘空间小于阈值,删除旧的文件 + # for i in range(channels): + # os.makedirs(os.path.join(self.config['daq']['output_dir'], f"{i+1:02}"), exist_ok=True) + # fileList = os.listdir(f"C:/users/Administrator/PCM/data/{i+1:02}") + # fileList.sort(reverse=False) + # if os.path.exists(f"C:/users/Administrator/PCM/data/{i+1:02}/{fileList[0]}"): + # os.remove(f"C:/users/Administrator/PCM/data/{i+1:02}/{fileList[0]}") + # # os.remove(f"C:/users/Administrator/PCM/data/{fileList[1]}") + # usage = shutil.disk_usage("C:/") + + target_dir = Path(self.dataFileDir) + max_usage_gb = 5 + max_usage_bytes = max_usage_gb * 1024**3 + channels = self.channels + while True: + if self._get_dir_size(target_dir) <= max_usage_bytes: + break + for ch in range(channels): + ch_path = target_dir / f'{ch+1:02d}' + ch_path.mkdir(parents=True, exist_ok=True) + victim = self._oldest_file(ch_path) + if victim: + victim.unlink() + self.reg_values = [] + timestamp = time.time() + timeStr = datetime.fromtimestamp(timestamp).strftime("%Y%m%d%H%M%S") + # with open(filename, 'wb') as f: + # f.write(self.buffer) + # f.close() + print(f"Length of buffer: {len(self.buffer)}") + datas = np.frombuffer(self.buffer, dtype='>h') + print(f"Length of datas: {len(datas)}") + datas = datas[:int(len(datas)/channels)*channels] + datas = datas.reshape(-1, channels) + data = None + _s = f"{self.sensor_type:032b}" + _sensor_type = ''.join([_s[2*i:2*i+2] for i in range(channels-1, -1, -1)]) + print(f"save_flag = {self.save_flag}") + _save_flag = f"{self.save_flag:016b}"[::-1] + if 'calib_params' in self.config and 'vibration' in self.config['calib_params']: + _fre = self.config['calib_params']['vibration'].get('frequency', -1) + else: + _fre = -1 + + for i in range(channels): + if _fre != -1 and _fre > 0: + _len = len(datas[:,i])//_fre*_fre + _data = datas[0:_len,i] + else: + _data = datas[:,i] + _rms = np.sqrt(np.mean(_data**2))/self.scale + self.feature_data[f'CH{i}'] = { + 'min': np.min(_data)/self.scale, + 'max': np.max(_data)/self.scale, + 'mean': np.mean(_data)/self.scale, + 'std': np.std(_data)/self.scale, + 'rms': _rms, + 'sr0': 0.0, + 'sr1': 0.0, + 'sr2': 0.0, + 'sr3': 0.0, + 'sr4': 0.0 + } + rms = self.feature_data[f'CH{i}']['rms'] + mean = self.feature_data[f'CH{i}']['mean'] + + filename = '' + match _sensor_type[2*i:2*i+2]: + case '00': + # 计算频率,以Hz为单位 + self.feature_data[f'CH{i}']['sr0'] = self.calculateFrequency(datas[:, i], self.one_sample_time) + self.reg_values.extend(list(self.feature_data[f'CH{i}'].values())) + data = datas[:, i] + filename = os.path.join(self.dataFileDir, f"{i+1:02}/{timeStr}_{rms:.3f}_{self.feature_type}_{self.daqBoardNo}_{i+1:02}_N_{self.sample_rate}_{self.sample_points}_0.000_{mean:.3f}_{self.min_vol_cur_phy_value}_{self.max_vol_cur_phy_value}_{self.scale}") + case '01': + # 计算声音大小 + self.reg_values.extend(list(self.feature_data[f'CH{i}'].values())) + data = datas[:, i] + filename = os.path.join(self.dataFileDir, f"{i+1:02}/{timeStr}_{rms:.3f}_{self.feature_type}_{self.daqBoardNo}_{i+1:02}_N_{self.sample_rate}_{self.sample_points}_0.000_{mean:.3f}_{self.min_vol_cur_phy_value}_{self.max_vol_cur_phy_value}_{self.scale}") + case '10': + # 计算电流大小 + data = datas[:, i] + if self.mode != 1: + for k, v in self.feature_data[f'CH{i}'].items(): + self.feature_data[f'CH{i}'][k] = v**2*self.sensor_Cur_CalibParam[f'CH{i}']['K2'] + v*self.sensor_Cur_CalibParam[f'CH{i}']['K'] + self.sensor_Cur_CalibParam[f'CH{i}']['B'] + data = (datas[:, i]**2*self.sensor_Cur_CalibParam[f'CH{i}']['K2'] + datas[:, i]*self.sensor_Cur_CalibParam[f'CH{i}']['K']+self.sensor_Cur_CalibParam[f'CH{i}']['B']).astype(np.float32) + self.reg_values.extend(list(self.feature_data[f'CH{i}'].values())) + filename = os.path.join(self.dataFileDir, f"{i+1:02}/{timeStr}_{rms:.3f}_{self.feature_type}_{self.daqBoardNo}_{i+1:02}_N_{self.sample_rate}_{self.sample_points}_0.000_{mean:.3f}_{self.min_vol_cur_phy_value}_{self.max_vol_cur_phy_value}_{self.scale}") + case '11': + # 计算振动大小 + data = datas[:, i] + if self.mode != 1: + for k, v in self.feature_data[f'CH{i}'].items(): + self.feature_data[f'CH{i}'][k] = v**2*self.sensor_Vib_CalibParam[f'CH{i}']['K2'] + v*self.sensor_Vib_CalibParam[f'CH{i}']['K'] + self.sensor_Vib_CalibParam[f'CH{i}']['B'] + data = (datas[:, i]**2*self.sensor_Vib_CalibParam[f'CH{i}']['K2'] + datas[:, i]*self.sensor_Vib_CalibParam[f'CH{i}']['K']+self.sensor_Vib_CalibParam[f'CH{i}']['B']).astype(np.float32) + self.feature_data[f'CH{i}']['rms'] = np.sqrt(np.mean(data**2)) + self.feature_data[f'CH{i}']['sr0'] = self.feature_data[f'CH{i}']['rms']*np.sqrt(2) + else: + self.feature_data[f'CH{i}']['sr0'] = self.feature_data[f'CH{i}']['std']*np.sqrt(2) + self.reg_values.extend(list(self.feature_data[f'CH{i}'].values())) + filename = os.path.join(self.dataFileDir, f"{i+1:02}/{timeStr}_{rms:.3f}_{self.feature_type}_{self.daqBoardNo}_{i+1:02}_N_{self.sample_rate}_{self.sample_points}_0.000_{mean:.3f}_{self.min_vol_cur_phy_value}_{self.max_vol_cur_phy_value}_{self.scale}") + case _: + pass + + # 将数据写入文件 + if self.file_type == 1: + filename += '.bin' + if _save_flag[i] == '1': + if isinstance(data, np.ndarray): + try: + temp_data = data.astype('>f4') + bytes_data = temp_data.tobytes() + with open(filename, 'wb', buffering=0) as f: + # 1. 正常写 + f.write(bytes_data) + + # 2. 告诉内核:整个文件以后大概率不读,页 cache 可以立即回收 + fd = os.open(filename, os.O_RDONLY) + try: + # POSIX_FADV_DONTNEED = 4 + os.posix_fadvise(fd, 0, 0, os.POSIX_FADV_DONTNEED) + finally: + os.close(fd) + del temp_data, bytes_data, data + finally: + if 'temp_data' in locals(): + del temp_data + if 'bytes_data' in locals(): + del bytes_data + self.logger.debug(f"Success to save data to {filename}.") + else: + filename += '.csv' + if _save_flag[i] == '1': + if isinstance(data, np.ndarray): + with open(filename, 'w') as f: + # 使用生成器表达式避免创建巨大列表 + lines = (f"{num:.4f}\n" for num in data) + f.writelines(lines) + self.logger.debug(f"Saved data to {filename}.") + del lines + data = None + datas = None + self.buffer = bytearray() + self._force_memory_cleanup() + except Exception as e: + self.logger.error(f"Error in save_data(): {e}") + + def _force_memory_cleanup(self): + """强制内存清理""" + import gc + # 清除各种缓存 + if hasattr(np, 'getbufsize'): + np.setbufsize(32768) + + # 强制垃圾回收 + gc.collect() + gc.collect() # 两次确保回收 + + # 稍微等待让系统处理 + import time + time.sleep(0.01) + + def calculateFrequency(self, signal, oneSampleTime): + '''计算0-1变换的数组中0-1变化的次数, 并计算其频率''' + # oneSampleTime 单位为us + if len(signal) < 2: + return 0.0 # 信号太短无法计算频率 + transitions = 0 # 跳变次数计数器 + # 遍历数组计算跳变次数 + for i in range(1, len(signal)): + if signal[i] != signal[i-1]: + transitions += 1 + # 计算频率: + # 每个周期有2次跳变(0→1和1→0) + # 总时间 = 采样点数 / 采样率 + # 频率 = (跳变次数 / 2) / (总时间) + total_time = len(signal) * oneSampleTime / 1000000 + frequency = (transitions // 2) / total_time if total_time > 0 else 0.0 + return frequency + + def run(self): + """主运行循环""" + self.logger.info(f"Start DAQ thread.") + frame_size = self.config.get('frame_size_max', 1464) + FILESIZE = self.config.get('file_size', 32000000) + DATA_DIR = self.config.get('output_dir', 'data') + optFlag = 0 + # 清空接收缓存,并向DAQ模块发送启动采集指令 + self.buffer = b'' + self.sampleNum = 0 + self.stop_DAQ() + time.sleep(0.01) + self.start_DAQ() + lastFrameNo = 0 + cycles = 0 + while(True): + try: + data, addr = self.sock.recvfrom(frame_size+42) + # 如何返回了数据,数据起始符正确,包号正确,则存储数据 + if data: + data = data[42:] + nowFrameNo = int.from_bytes(data[4:8], 'big') + if nowFrameNo != lastFrameNo + 1: + print(f"Received data: len={len(data)}, lastFrameNo={lastFrameNo}, nowFrameNo={nowFrameNo}") + # self.logger.info(f"Received data: len={len(data)}, frame NO.={int.from_bytes(data[4:8], 'big')}") + else: + continue + # self.logger.debug(f"Head Data:(20 byte) ={' '.join(data[i:i+2].hex() for i in range(0, 20, 2))}") + + if optFlag == 0: + if data and data[0:4] == bytearray([0xa5, 0x5a, 0xa5, 0x5a]) and len(data) == frame_size and data[4:8] == bytearray([0x00, 0x00, 0x00, 0x01]): + self.buffer += data[24:] + self.sampleNum += ((len(data)-24)/32) + # if self.sensorType != int.from_bytes(data[8:12], 'big'): + # self.logger.error(f"In daq_thread(): SensorType in return data doesn't match with config.") + optFlag = 1 + elif optFlag == 1: + if data and data[0:4] == bytearray([0xa5, 0x5a, 0xa5, 0x5a]): + if len(data) != frame_size or data[5:8] == bytearray([0x00, 0x00, 0x01]): + if data[4:8] != bytearray([0x00, 0x00, 0x00, 0x01]): + self.buffer += data[24:] + self.sampleNum += ((len(data)-24)/32) + + cycles += 1 + self.save_data() + self.buffer = bytearray() + self.sampleNum = 0 + optFlag = 0 + else: + self.buffer += data[24:] + self.sampleNum += ((len(data)-24)/32) + + if nowFrameNo - lastFrameNo != 1 and lastFrameNo < nowFrameNo: + if lastFrameNo != 0: + pass + self.logger.warning(f"cycles= {cycles}, lastFrameNo={lastFrameNo}, nowFrameNo={nowFrameNo}") + # raise Exception() + if nowFrameNo != 0: + lastFrameNo = nowFrameNo + except Exception as e: + self.stop_DAQ() + self.sock.close() + self.running = False + self.buffer = bytearray() + self.logger.error(f"Error in daq_thread(): {e}") + self.logger.info(f"Stop DAQ thread.") + break + +if __name__ == "__main__": + gateway = ModbusGateway() + gateway.run() \ No newline at end of file diff --git a/src/config.yaml b/src/config.yaml new file mode 100644 index 0000000..8ad6ee2 --- /dev/null +++ b/src/config.yaml @@ -0,0 +1,233 @@ +# gps 配置 +gps: + port: '/dev/ttyLP4' + baudrate: 9600 + timeout: 50 + +# lsdaq 配置 +lsdaq: + # Modbus-RTU 配置 + port: '/dev/ttyLP3' # 串口号,如COM3或'/dev/ttyLP3' + baudrate: 115200 # 波特率 + timeout: 1 # 超时时间(秒) + mode: 0 # 工作模式,'work'-0 或 'calib'-1 + # 配置采集通道传感器类型 + # 用1位标识传感器类型,16通道16位组成1个uint16数据。CH0->CH15 + # 0:PT100; 1: 4-20mA电流型传感器; + sensor_type: '0000 0000 0000 0011' # 低频采集通道传感器类型 '0000 0000 0000 0000' + sensor_Tmp_CalibParam: + # PT100传感器对应的K值和T值,未调整通道时 + # 'CH0': {'K2':0, 'K': 0.030257902, 'B': -517.0348658} + # 'CH1': {'K2':0, 'K': 0.030303351, 'B': -516.9375391} + # 'CH2': {'K2':0, 'K': 0.030314082, 'B': -518.0912602} + # 'CH3': {'K2':0, 'K': 0.030221791, 'B': -516.7059355} + # 'CH4': {'K2':0, 'K': 0.030112226, 'B': -513.7697071} + # 'CH5': {'K2':0, 'K': 0.030417324, 'B': -518.5073587} + # 'CH6': {'K2':0, 'K': 0.030322627, 'B': -517.4944564} + # 'CH7': {'K2':0, 'K':0.030109526, 'B': -512.9024069} + # 'CH8': {'K2':0, 'K': 0.030482308, 'B': -519.7812926} + # 'CH9': {'K2':0.0, 'K': 0.030154755, 'B': -514.9594939} + # 'CH10': {'K2':0.0, 'K': 0.030060306, 'B': -515.1610909} + # 'CH11': {'K2':0.0, 'K': 0.030218679, 'B': -516.1381104} + # 'CH12': {'K2':0.0, 'K': 0.030651258, 'B': -524.4282223} + # 'CH13': {'K2':0.0, 'K': 0.03044716, 'B': -521.8728551} + # 'CH14': {'K2':0.0, 'K': 0.030796257, 'B': -526.6423943} + # 'CH15': {'K2':0.0, 'K':0.030278378, 'B': -516.9205772} + + # PT100传感器对应的K值和T值,调整通道后 + 'CH0': {'K2':0, 'K': 0.030257902, 'B': -517.0348658} + 'CH1': {'K2':0, 'K': 0.030303351, 'B': -516.9375391} + 'CH2': {'K2':0, 'K': 0.030314082, 'B': -518.0912602} + 'CH3': {'K2':0, 'K': 0.030221791, 'B': -516.7059355} + 'CH4': {'K2':0, 'K': 0.030112226, 'B': -513.7697071} + 'CH5': {'K2':0, 'K': 0.030417324, 'B': -518.5073587} + 'CH6': {'K2':0, 'K': 0.030322627, 'B': -517.4944564} + 'CH7': {'K2':0, 'K':0.030109526, 'B': -512.9024069} + 'CH8': {'K2':0.0, 'K':0.030278378, 'B': -516.9205772} + 'CH9': {'K2':0.0, 'K': 0.030796257, 'B': -526.6423943} + 'CH10': {'K2':0.0, 'K': 0.03044716, 'B': -521.8728551} + 'CH11': {'K2':0.0, 'K': 0.030651258, 'B': -524.4282223} + 'CH12': {'K2':0.0, 'K': 0.030218679, 'B': -516.1381104} + 'CH13': {'K2':0.0, 'K': 0.030060306, 'B': -515.1610909} + 'CH14': {'K2':0.0, 'K': 0.030154755, 'B': -514.9594939} + 'CH15': {'K2':0, 'K': 0.030482308, 'B': -519.7812926} + + sensor_Cur_CalibParam: + # 4~20mA传感器对应的K值和T值,未调整通道时 + # 'CH0': {'K2':0.0, 'K': -0.001491968, 'B': 25.26246964} + # 'CH1': {'K2':0.0, 'K': -0.001493628, 'B': 25.28923241} + # 'CH2': {'K2':0.0, 'K': -0.001494113, 'B': 25.30278378} + # 'CH3': {'K2':0.0, 'K': -0.001492948, 'B': 25.28846151} + # 'CH4': {'K2':0.0, 'K': -0.001490842, 'B': 25.25241575} + # 'CH5': {'K2':0.0, 'K': -0.001491645, 'B': 25.26877665} + # 'CH6': {'K2':0.0, 'K': -0.00149083, 'B': 25.24721242} + # 'CH7': {'K2':0.0, 'K': -0.001492758, 'B': 25.27288297} + # 'CH8': {'K2':0.0, 'K': -0.001493109, 'B': 25.28743775} + # 'CH9': {'K2':0.0, 'K': -0.001484187, 'B': 25.13392988} + # 'CH10': {'K2':0.0, 'K': -0.001486631, 'B': 25.17767163} + # 'CH11': {'K2':0.0, 'K': -0.001493103, 'B': 25.28584646} + # 'CH12': {'K2':0.0, 'K': -0.001491895, 'B': 25.26601835} + # 'CH13': {'K2':0.0, 'K': -0.001490194, 'B': 25.24431484} + # 'CH14': {'K2':0.0, 'K': -0.001493638, 'B': 25.29401713} + # 'CH15': {'K2':0.0, 'K': -0.001491885, 'B': 25.25890993} + + # 4~20mA传感器对应的K值和T值,调整通道后 + 'CH0': {'K2':0.0, 'K': -0.001491968, 'B': 25.26246964} + 'CH1': {'K2':0.0, 'K': -0.001493628, 'B': 25.28923241} + 'CH2': {'K2':0.0, 'K': -0.001494113, 'B': 25.30278378} + 'CH3': {'K2':0.0, 'K': -0.001492948, 'B': 25.28846151} + 'CH4': {'K2':0.0, 'K': -0.001490842, 'B': 25.25241575} + 'CH5': {'K2':0.0, 'K': -0.001491645, 'B': 25.26877665} + 'CH6': {'K2':0.0, 'K': -0.00149083, 'B': 25.24721242} + 'CH7': {'K2':0.0, 'K': -0.001492758, 'B': 25.27288297} + 'CH8': {'K2':0.0, 'K': -0.001491885, 'B': 25.25890993} + 'CH9': {'K2':0.0, 'K': -0.001493638, 'B': 25.29401713} + 'CH10': {'K2':0.0, 'K': -0.001490194, 'B': 25.24431484} + 'CH11': {'K2':0.0, 'K': -0.001491895, 'B': 25.26601835} + 'CH12': {'K2':0.0, 'K': -0.001493103, 'B': 25.28584646} + 'CH13': {'K2':0.0, 'K': -0.001486631, 'B': 25.17767163} + 'CH14': {'K2':0.0, 'K': -0.001484187, 'B': 25.13392988} + 'CH15': {'K2':0.0, 'K': -0.001493109, 'B': 25.28743775} + +# hsdaq 配置 +hsdaq: + host: 192.168.0.2 + port: 8080 + local_host: 192.168.0.3 + local_port: 8080 + timeout: 50 + channels: 16 + sample_time: 1000 # 单位ms + sample_period: 5000 # 单位ms + one_sample_time: 10 # 单位us + sample_rate: 100000 # 单位Hz + # 配置高频采集通道传感器类型 + # 用2位标识传感器类型,16通道32位组成1个uint32数据。CH1->CH16 + # 00:NPN型开关量; 01:PNP型开关量; 10: 4-20mA电流型传感器; 11:振动传感器 + sensor_type: '0111 1111 1111 1111 1111 1111 1111 1010' + # sensor_type: '0000 0000 0000 0000 0000 0000 0000 0000' + # sensor_type: '0101 0101 0101 0101 0101 0101 0101 0101' + # sensor_type: '1010 1010 1010 1010 1010 1010 1010 1010' + + frame_size_max: 1464 + file_size: 32000000 + file_type: 1 # csv-0 或 bin-1 + output_dir: data + min_free_gb: 1 + channels: 16 + save_flag: '1011 1111 1100 0000' #'1011 1111 1100 0000' #'1111 1111 1111 1111' # 选择保存数据的通道,1-保存数据, 0-不保存,CH1->CH16 + daq_board_no: '2504210001' + feature_type: '加速度rms' + min_vol_cur_phy_value: 0.0 + max_vol_cur_phy_value: 160.0 + vol_cur_phy_scale: 1 + mode: 0 # 工作模式,'work'-0 或 'calib'-1 + sensor_Cur_CalibParam: + # 4~20mA传感器对应的K值和T值,未调整通道时 + # 'CH0': {'K2':0.0, 'K': 0.002574779, 'B': 0.001034803} + # 'CH1': {'K2':0.0, 'K': 0.002577854, 'B': 0.003481423} + # 'CH2': {'K2':0.0, 'K': 0.002572228, 'B': 0.000180365} + # 'CH3': {'K2':0.0, 'K': 0.00258263, 'B': 0.001601482} + # 'CH4': {'K2':0.0, 'K': 0.002578148, 'B': 0.001619703} + # 'CH5': {'K2':0.0, 'K': 0.002580244, 'B': 0.001414032} + # 'CH6': {'K2':0.0, 'K': 0.002573372, 'B': -5.67175E-06} + # 'CH7': {'K2':0.0, 'K': 0.002563052, 'B': 0.000328752} + # 'CH8': {'K2':0.0, 'K': 0.002573487, 'B': 0.000873064} + # 'CH9': {'K2':0.0, 'K': 0.002580323, 'B': 0.000544915} + # 'CH10': {'K2':0.0, 'K': 0.002577332, 'B': -0.000845715} + # 'CH11': {'K2':0.0, 'K': 0.002574748, 'B': 0.001037011} + # 'CH12': {'K2':0.0, 'K': 0.002581746, 'B': 0.000375208} + # 'CH13': {'K2':0.0, 'K': 0.002576609, 'B': 0.001021399} + # 'CH14': {'K2':0.0, 'K': 0.002572029, 'B': 0.000148142} + # 'CH15': {'K2':0.0, 'K': 0.002582191, 'B': 0.000300086} + + # 4~20mA传感器对应的K值和T值,调整通道后 + 'CH0': {'K2':0.0, 'K': 0.00258263, 'B': 0.001601482} + 'CH1': {'K2':0.0, 'K': 0.002572228, 'B': 0.000180365} + 'CH2': {'K2':0.0, 'K': 0.002577854, 'B': 0.003481423} + 'CH3': {'K2':0.0, 'K': 0.002574779, 'B': 0.001034803} + 'CH4': {'K2':0.0, 'K': 0.002563052, 'B': 0.000328752} + 'CH5': {'K2':0.0, 'K': 0.002573372, 'B': -5.67175E-06} + 'CH6': {'K2':0.0, 'K': 0.002580244, 'B': 0.001414032} + 'CH7': {'K2':0.0, 'K': 0.002578148, 'B': 0.001619703} + 'CH8': {'K2':0.0, 'K': 0.002582191, 'B': 0.000300086} + 'CH9': {'K2':0.0, 'K': 0.002572029, 'B': 0.000148142} + 'CH10': {'K2':0.0, 'K': 0.002576609, 'B': 0.001021399} + 'CH11': {'K2':0.0, 'K': 0.002574748, 'B': 0.001037011} + 'CH12': {'K2':0.0, 'K': 0.002581746, 'B': 0.000375208} + 'CH13': {'K2':0.0, 'K': 0.002577332, 'B': -0.000845715} + 'CH14': {'K2':0.0, 'K': 0.002580323, 'B': 0.000544915} + 'CH15': {'K2':0.0, 'K': 0.002573487, 'B': 0.000873064} + + sensor_Vol_CalibParam: + # 电压传感器对应的K值和T值 + 'CH0': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH1': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH2': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH3': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH4': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH5': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH6': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH7': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH8': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH9': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH10': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH11': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH12': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH13': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH14': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH15': {'K2':0.0, 'K': 1.0, 'B': 0.0} + + sensor_Vib_CalibParam: + # 振动传感器对应的K值和T值,未调整通道时 + # 'CH0': {'K2':0.0, 'K': 0.001290249, 'B': 0.93594657} + # 'CH1': {'K2':0.0, 'K': 0.001276318, 'B': 1.123980037} + # 'CH2': {'K2':0.0, 'K': 0.001269578, 'B': 0.908196807} + # 'CH3': {'K2':0.0, 'K': 0.001273944, 'B': 1.067762112} + # 'CH4': {'K2':0.0, 'K': 0.001278641, 'B': 0.963362219} + # 'CH5': {'K2':0.0, 'K': 0.001279707, 'B': 0.975354671} + # 'CH6': {'K2':0.0, 'K': 0.001276854, 'B': 1.06327067} + # 'CH7': {'K2':0.0, 'K': 0.001268516, 'B': 0.887745169} + # 'CH8': {'K2':0.0, 'K': 0.001271303, 'B': 0.939062555} + # 'CH9': {'K2':0.0, 'K': 0.001276319, 'B': 0.827555233} + # 'CH10': {'K2':0.0, 'K': 0.001273497, 'B': 1.051533787} + # 'CH11': {'K2':0.0, 'K': 0.001275122, 'B': 0.796252288} + # 'CH12': {'K2':0.0, 'K': 0.00127781, 'B': 0.938569861} + # 'CH13': {'K2':0.0, 'K': 0.001274617, 'B': 1.052574207} + # 'CH14': {'K2':0.0, 'K': 0.001272706, 'B': 0.947889708} + # 'CH15': {'K2':0.0, 'K': 0.001278097, 'B': 0.985136254} + + # 振动传感器对应的K值和T值,调整通道后 + 'CH0': {'K2':0.0, 'K': 0.001273944, 'B': 1.067762112} + 'CH1': {'K2':0.0, 'K': 0.001269578, 'B': 0.908196807} + 'CH2': {'K2':0.0, 'K': 0.001276318, 'B': 1.123980037} + 'CH3': {'K2':0.0, 'K': 0.001290249, 'B': 0.93594657} + 'CH4': {'K2':0.0, 'K': 0.001268516, 'B': 0.887745169} + 'CH5': {'K2':0.0, 'K': 0.001276854, 'B': 1.06327067} + 'CH6': {'K2':0.0, 'K': 0.001279707, 'B': 0.975354671} + 'CH7': {'K2':0.0, 'K': 0.001278641, 'B': 0.963362219} + 'CH8': {'K2':0.0, 'K': 0.001278097, 'B': 0.985136254} + 'CH9': {'K2':0.0, 'K': 0.001272706, 'B': 0.947889708} + 'CH10': {'K2':0.0, 'K': 0.001274617, 'B': 1.052574207} + 'CH11': {'K2':0.0, 'K': 0.001275122, 'B': 0.796252288} + 'CH12': {'K2':0.0, 'K': 0.00127781, 'B': 0.938569861} + 'CH13': {'K2':0.0, 'K': 0.001273497, 'B': 1.051533787} + 'CH14': {'K2':0.0, 'K': 0.001276319, 'B': 0.827555233} + 'CH15': {'K2':0.0, 'K': 0.001271303, 'B': 0.939062555} + + calib_params: + vibration: + frequency: 500 #单位Hz + +modbus-server: + # 通过Modbus TCP协议提供gps、lsdaq和hsdaq特征值数据 + host: 192.168.100.10 + port: 5020 + timeout: 50 + +influxdb: + url: "http://localhost:8086" + token: "EzmJXjf8DJq8ehT-2VsRdE8NRbu9gOvogO0fJywoDnkKWV2EAMIOoDWVz4Dlam_8VQvPzsEUEm2FrmAk14kRMA==" + org: "MEASCON" + active: true + bucket: PCM \ No newline at end of file diff --git a/src/default_config.yaml b/src/default_config.yaml new file mode 100644 index 0000000..204607d --- /dev/null +++ b/src/default_config.yaml @@ -0,0 +1,226 @@ +# gps 配置 +gps: + port: '/dev/ttyLP4' + baudrate: 9600 + timeout: 50 + +# lsdaq 配置 +lsdaq: + # Modbus-RTU 配置 + port: '/dev/ttyLP3' # 串口号,如COM3或'/dev/ttyLP3' + baudrate: 115200 # 波特率 + timeout: 1 # 超时时间(秒) + mode: 0 # 工作模式,'work'-0 或 'calib'-1 + # 配置采集通道传感器类型 + # 用1位标识传感器类型,16通道16位组成1个uint16数据。CH0->CH15 + # 0:PT100; 1: 4-20mA电流型传感器; + sensor_type: '0000 0000 0000 0011' # 低频采集通道传感器类型 '0000 0000 0000 0000' + sensor_Tmp_CalibParam: + # PT100传感器对应的K值和T值,未调整通道时 + # 'CH0': {'K2':0, 'K': 0.030257902, 'B': -517.0348658} + # 'CH1': {'K2':0, 'K': 0.030303351, 'B': -516.9375391} + # 'CH2': {'K2':0, 'K': 0.030314082, 'B': -518.0912602} + # 'CH3': {'K2':0, 'K': 0.030221791, 'B': -516.7059355} + # 'CH4': {'K2':0, 'K': 0.030112226, 'B': -513.7697071} + # 'CH5': {'K2':0, 'K': 0.030417324, 'B': -518.5073587} + # 'CH6': {'K2':0, 'K': 0.030322627, 'B': -517.4944564} + # 'CH7': {'K2':0, 'K':0.030109526, 'B': -512.9024069} + # 'CH8': {'K2':0, 'K': 0.030482308, 'B': -519.7812926} + # 'CH9': {'K2':0.0, 'K': 0.030154755, 'B': -514.9594939} + # 'CH10': {'K2':0.0, 'K': 0.030060306, 'B': -515.1610909} + # 'CH11': {'K2':0.0, 'K': 0.030218679, 'B': -516.1381104} + # 'CH12': {'K2':0.0, 'K': 0.030651258, 'B': -524.4282223} + # 'CH13': {'K2':0.0, 'K': 0.03044716, 'B': -521.8728551} + # 'CH14': {'K2':0.0, 'K': 0.030796257, 'B': -526.6423943} + # 'CH15': {'K2':0.0, 'K':0.030278378, 'B': -516.9205772} + + # PT100传感器对应的K值和T值,调整通道后 + 'CH0': {'K2':0, 'K': 0.030257902, 'B': -517.0348658} + 'CH1': {'K2':0, 'K': 0.030303351, 'B': -516.9375391} + 'CH2': {'K2':0, 'K': 0.030314082, 'B': -518.0912602} + 'CH3': {'K2':0, 'K': 0.030221791, 'B': -516.7059355} + 'CH4': {'K2':0, 'K': 0.030112226, 'B': -513.7697071} + 'CH5': {'K2':0, 'K': 0.030417324, 'B': -518.5073587} + 'CH6': {'K2':0, 'K': 0.030322627, 'B': -517.4944564} + 'CH7': {'K2':0, 'K':0.030109526, 'B': -512.9024069} + 'CH8': {'K2':0.0, 'K':0.030278378, 'B': -516.9205772} + 'CH9': {'K2':0.0, 'K': 0.030796257, 'B': -526.6423943} + 'CH10': {'K2':0.0, 'K': 0.03044716, 'B': -521.8728551} + 'CH11': {'K2':0.0, 'K': 0.030651258, 'B': -524.4282223} + 'CH12': {'K2':0.0, 'K': 0.030218679, 'B': -516.1381104} + 'CH13': {'K2':0.0, 'K': 0.030060306, 'B': -515.1610909} + 'CH14': {'K2':0.0, 'K': 0.030154755, 'B': -514.9594939} + 'CH15': {'K2':0, 'K': 0.030482308, 'B': -519.7812926} + + sensor_Cur_CalibParam: + # 4~20mA传感器对应的K值和T值,未调整通道时 + # 'CH0': {'K2':0.0, 'K': -0.001491968, 'B': 25.26246964} + # 'CH1': {'K2':0.0, 'K': -0.001493628, 'B': 25.28923241} + # 'CH2': {'K2':0.0, 'K': -0.001494113, 'B': 25.30278378} + # 'CH3': {'K2':0.0, 'K': -0.001492948, 'B': 25.28846151} + # 'CH4': {'K2':0.0, 'K': -0.001490842, 'B': 25.25241575} + # 'CH5': {'K2':0.0, 'K': -0.001491645, 'B': 25.26877665} + # 'CH6': {'K2':0.0, 'K': -0.00149083, 'B': 25.24721242} + # 'CH7': {'K2':0.0, 'K': -0.001492758, 'B': 25.27288297} + # 'CH8': {'K2':0.0, 'K': -0.001493109, 'B': 25.28743775} + # 'CH9': {'K2':0.0, 'K': -0.001484187, 'B': 25.13392988} + # 'CH10': {'K2':0.0, 'K': -0.001486631, 'B': 25.17767163} + # 'CH11': {'K2':0.0, 'K': -0.001493103, 'B': 25.28584646} + # 'CH12': {'K2':0.0, 'K': -0.001491895, 'B': 25.26601835} + # 'CH13': {'K2':0.0, 'K': -0.001490194, 'B': 25.24431484} + # 'CH14': {'K2':0.0, 'K': -0.001493638, 'B': 25.29401713} + # 'CH15': {'K2':0.0, 'K': -0.001491885, 'B': 25.25890993} + + # 4~20mA传感器对应的K值和T值,调整通道后 + 'CH0': {'K2':0.0, 'K': -0.001491968, 'B': 25.26246964} + 'CH1': {'K2':0.0, 'K': -0.001493628, 'B': 25.28923241} + 'CH2': {'K2':0.0, 'K': -0.001494113, 'B': 25.30278378} + 'CH3': {'K2':0.0, 'K': -0.001492948, 'B': 25.28846151} + 'CH4': {'K2':0.0, 'K': -0.001490842, 'B': 25.25241575} + 'CH5': {'K2':0.0, 'K': -0.001491645, 'B': 25.26877665} + 'CH6': {'K2':0.0, 'K': -0.00149083, 'B': 25.24721242} + 'CH7': {'K2':0.0, 'K': -0.001492758, 'B': 25.27288297} + 'CH8': {'K2':0.0, 'K': -0.001491885, 'B': 25.25890993} + 'CH9': {'K2':0.0, 'K': -0.001493638, 'B': 25.29401713} + 'CH10': {'K2':0.0, 'K': -0.001490194, 'B': 25.24431484} + 'CH11': {'K2':0.0, 'K': -0.001491895, 'B': 25.26601835} + 'CH12': {'K2':0.0, 'K': -0.001493103, 'B': 25.28584646} + 'CH13': {'K2':0.0, 'K': -0.001486631, 'B': 25.17767163} + 'CH14': {'K2':0.0, 'K': -0.001484187, 'B': 25.13392988} + 'CH15': {'K2':0.0, 'K': -0.001493109, 'B': 25.28743775} + +# hsdaq 配置 +hsdaq: + host: 192.168.0.2 + port: 8080 + local_host: 192.168.0.3 + local_port: 8080 + timeout: 50 + channels: 16 + sample_time: 1000 # 单位ms + sample_period: 4000 # 单位ms + one_sample_time: 10 # 单位us + sample_rate: 100000 # 单位Hz + # 配置高频采集通道传感器类型 + # 用2位标识传感器类型,16通道32位组成1个uint32数据。CH1->CH16 + # 00:NPN型开关量; 01:PNP型开关量; 10: 4-20mA电流型传感器; 11:振动传感器 + sensor_type: '0111 1111 1111 1111 1111 1111 1111 1010' + # sensor_type: '0000 0000 0000 0000 0000 0000 0000 0000' + # sensor_type: '0101 0101 0101 0101 0101 0101 0101 0101' + # sensor_type: '1010 1010 1010 1010 1010 1010 1010 1010' + + frame_size_max: 1464 + file_size: 32000000 + file_type: 0 # csv-0 或 bin-1 + output_dir: data + min_free_gb: 1 + channels: 16 + save_flag: '0000 0000 0000 0000' #'1111 1111 1111 1111' # 选择保存数据的通道,1-保存数据, 0-不保存,CH1->CH16 + daq_board_no: '2504210001' + feature_type: '加速度rms' + min_vol_cur_phy_value: 0.0 + max_vol_cur_phy_value: 160.0 + vol_cur_phy_scale: 1 + mode: 0 # 工作模式,'work'-0 或 'calib'-1 + sensor_Cur_CalibParam: + # 4~20mA传感器对应的K值和T值,未调整通道时 + # 'CH0': {'K2':0.0, 'K': 0.002574779, 'B': 0.001034803} + # 'CH1': {'K2':0.0, 'K': 0.002577854, 'B': 0.003481423} + # 'CH2': {'K2':0.0, 'K': 0.002572228, 'B': 0.000180365} + # 'CH3': {'K2':0.0, 'K': 0.00258263, 'B': 0.001601482} + # 'CH4': {'K2':0.0, 'K': 0.002578148, 'B': 0.001619703} + # 'CH5': {'K2':0.0, 'K': 0.002580244, 'B': 0.001414032} + # 'CH6': {'K2':0.0, 'K': 0.002573372, 'B': -5.67175E-06} + # 'CH7': {'K2':0.0, 'K': 0.002563052, 'B': 0.000328752} + # 'CH8': {'K2':0.0, 'K': 0.002573487, 'B': 0.000873064} + # 'CH9': {'K2':0.0, 'K': 0.002580323, 'B': 0.000544915} + # 'CH10': {'K2':0.0, 'K': 0.002577332, 'B': -0.000845715} + # 'CH11': {'K2':0.0, 'K': 0.002574748, 'B': 0.001037011} + # 'CH12': {'K2':0.0, 'K': 0.002581746, 'B': 0.000375208} + # 'CH13': {'K2':0.0, 'K': 0.002576609, 'B': 0.001021399} + # 'CH14': {'K2':0.0, 'K': 0.002572029, 'B': 0.000148142} + # 'CH15': {'K2':0.0, 'K': 0.002582191, 'B': 0.000300086} + + # 4~20mA传感器对应的K值和T值,调整通道后 + 'CH0': {'K2':0.0, 'K': 0.00258263, 'B': 0.001601482} + 'CH1': {'K2':0.0, 'K': 0.002572228, 'B': 0.000180365} + 'CH2': {'K2':0.0, 'K': 0.002577854, 'B': 0.003481423} + 'CH3': {'K2':0.0, 'K': 0.002574779, 'B': 0.001034803} + 'CH4': {'K2':0.0, 'K': 0.002563052, 'B': 0.000328752} + 'CH5': {'K2':0.0, 'K': 0.002573372, 'B': -5.67175E-06} + 'CH6': {'K2':0.0, 'K': 0.002580244, 'B': 0.001414032} + 'CH7': {'K2':0.0, 'K': 0.002578148, 'B': 0.001619703} + 'CH8': {'K2':0.0, 'K': 0.002582191, 'B': 0.000300086} + 'CH9': {'K2':0.0, 'K': 0.002572029, 'B': 0.000148142} + 'CH10': {'K2':0.0, 'K': 0.002576609, 'B': 0.001021399} + 'CH11': {'K2':0.0, 'K': 0.002574748, 'B': 0.001037011} + 'CH12': {'K2':0.0, 'K': 0.002581746, 'B': 0.000375208} + 'CH13': {'K2':0.0, 'K': 0.002577332, 'B': -0.000845715} + 'CH14': {'K2':0.0, 'K': 0.002580323, 'B': 0.000544915} + 'CH15': {'K2':0.0, 'K': 0.002573487, 'B': 0.000873064} + + sensor_Vol_CalibParam: + # 电压传感器对应的K值和T值 + 'CH0': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH1': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH2': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH3': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH4': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH5': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH6': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH7': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH8': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH9': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH10': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH11': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH12': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH13': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH14': {'K2':0.0, 'K': 1.0, 'B': 0.0} + 'CH15': {'K2':0.0, 'K': 1.0, 'B': 0.0} + + sensor_Vib_CalibParam: + # 振动传感器对应的K值和T值,未调整通道时 + # 'CH0': {'K2':0.0, 'K': 0.001290249, 'B': 0.93594657} + # 'CH1': {'K2':0.0, 'K': 0.001276318, 'B': 1.123980037} + # 'CH2': {'K2':0.0, 'K': 0.001269578, 'B': 0.908196807} + # 'CH3': {'K2':0.0, 'K': 0.001273944, 'B': 1.067762112} + # 'CH4': {'K2':0.0, 'K': 0.001278641, 'B': 0.963362219} + # 'CH5': {'K2':0.0, 'K': 0.001279707, 'B': 0.975354671} + # 'CH6': {'K2':0.0, 'K': 0.001276854, 'B': 1.06327067} + # 'CH7': {'K2':0.0, 'K': 0.001268516, 'B': 0.887745169} + # 'CH8': {'K2':0.0, 'K': 0.001271303, 'B': 0.939062555} + # 'CH9': {'K2':0.0, 'K': 0.001276319, 'B': 0.827555233} + # 'CH10': {'K2':0.0, 'K': 0.001273497, 'B': 1.051533787} + # 'CH11': {'K2':0.0, 'K': 0.001275122, 'B': 0.796252288} + # 'CH12': {'K2':0.0, 'K': 0.00127781, 'B': 0.938569861} + # 'CH13': {'K2':0.0, 'K': 0.001274617, 'B': 1.052574207} + # 'CH14': {'K2':0.0, 'K': 0.001272706, 'B': 0.947889708} + # 'CH15': {'K2':0.0, 'K': 0.001278097, 'B': 0.985136254} + + # 振动传感器对应的K值和T值,调整通道后 + 'CH0': {'K2':0.0, 'K': 0.001273944, 'B': 1.067762112} + 'CH1': {'K2':0.0, 'K': 0.001269578, 'B': 0.908196807} + 'CH2': {'K2':0.0, 'K': 0.001276318, 'B': 1.123980037} + 'CH3': {'K2':0.0, 'K': 0.001290249, 'B': 0.93594657} + 'CH4': {'K2':0.0, 'K': 0.001268516, 'B': 0.887745169} + 'CH5': {'K2':0.0, 'K': 0.001276854, 'B': 1.06327067} + 'CH6': {'K2':0.0, 'K': 0.001279707, 'B': 0.975354671} + 'CH7': {'K2':0.0, 'K': 0.001278641, 'B': 0.963362219} + 'CH8': {'K2':0.0, 'K': 0.001278097, 'B': 0.985136254} + 'CH9': {'K2':0.0, 'K': 0.001272706, 'B': 0.947889708} + 'CH10': {'K2':0.0, 'K': 0.001274617, 'B': 1.052574207} + 'CH11': {'K2':0.0, 'K': 0.001275122, 'B': 0.796252288} + 'CH12': {'K2':0.0, 'K': 0.00127781, 'B': 0.938569861} + 'CH13': {'K2':0.0, 'K': 0.001273497, 'B': 1.051533787} + 'CH14': {'K2':0.0, 'K': 0.001276319, 'B': 0.827555233} + 'CH15': {'K2':0.0, 'K': 0.001271303, 'B': 0.939062555} + + calib_params: + vibration: + frequency: 500 #单位Hz + +modbus-server: + # 通过Modbus TCP协议提供gps、lsdaq和hsdaq特征值数据 + host: 192.168.100.10 + port: 5020 + timeout: 50 diff --git a/src/docker-compose.yml b/src/docker-compose.yml new file mode 100644 index 0000000..09383b6 --- /dev/null +++ b/src/docker-compose.yml @@ -0,0 +1,41 @@ +version: '1.0' +services: + pcmv1: + image: pcmv1:latest + container_name: pcmv1 + command: ["/bin/bash", "-c", "source .venv/bin/activate && python3 src/PCM_Service_1.4.py"] + network_mode: host + privileged: true + restart: unless-stopped + mem_limit: 256M + cpuset: "0" + volumes: + - /home/torizon/src/pcmv1:/pcmv1/src + - /home/torizon/data/pcmv1:/pcmv1/data + tty: true + stdin_open: true + environment: + - TZ=Asia/Shanghai + deploy: + mode: replicated + replicas: 1 + + pcmv1_flask: + image: pcm_flask_v1:latest + container_name: pcm_flask_v1 + command: python src/app.pyc + network_mode: host + privileged: true + restart: unless-stopped + mem_limit: 512M + cpuset: "1-3" + volumes: + - /home/torizon/app:/app + - /home/torizon/data/pcmv1:/app/data + tty: true + stdin_open: true + environment: + - TZ=Asia/Shanghai + deploy: + mode: replicated + replicas: 1 \ No newline at end of file diff --git a/src/logging_config.json b/src/logging_config.json new file mode 100644 index 0000000..eb59cdf --- /dev/null +++ b/src/logging_config.json @@ -0,0 +1,35 @@ +{ + "disable_existing_loggers": false, + "formatters": { + "simple": { + "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + } + }, + "handlers": { + "console": { + "class": "logging.StreamHandler", + "formatter": "simple", + "level": "INFO", + "stream": "ext://sys.stdout" + }, + "file": { + "class": "logging.handlers.RotatingFileHandler", + "filename": "src/app.log", + "formatter": "simple", + "level": "DEBUG", + "maxBytes": 10485760, + "backupCount": 3, + "encoding": "utf-8" + } + }, + "loggers": { + "PCM": { + "handlers": [ + "console", "file" + ], + "level": "INFO", + "propagate": false + } + }, + "version": 1 +} \ No newline at end of file diff --git a/src/regs_mapping.yaml b/src/regs_mapping.yaml new file mode 100644 index 0000000..1b92c38 --- /dev/null +++ b/src/regs_mapping.yaml @@ -0,0 +1,556 @@ +# 寄存器完整映射表 +# 格式说明:配置路径: [地址, 数据类型, 访问权限] +# 访问权限:RO-只读,RW-读写 + +value_regs: + # GPS特征值数据 + gps.latitude: [0, float32, RO] + gps.longitude: [2, float32, RO] + gps.altitude: [4, float32, RO] + gps.speed: [6, float32, RO] + + # lsdaq特征值数据 + lsdaq.CH0: [8, float32, RO] + lsdaq.CH1: [10, float32, RO] + lsdaq.CH2: [12, float32, RO] + lsdaq.CH3: [14, float32, RO] + lsdaq.CH4: [16, float32, RO] + lsdaq.CH5: [18, float32, RO] + lsdaq.CH6: [20, float32, RO] + lsdaq.CH7: [22, float32, RO] + lsdaq.CH8: [24, float32, RO] + lsdaq.CH9: [26, float32, RO] + lsdaq.CH10: [28, float32, RO] + lsdaq.CH11: [30, float32, RO] + lsdaq.CH12: [32, float32, RO] + lsdaq.CH13: [34, float32, RO] + lsdaq.CH14: [36, float32, RO] + lsdaq.CH15: [38, float32, RO] + lsdaq.OFFSET: [40, float32, RO] + lsdaq.POWERVOL: [42, float32, RO] + lsdaq.TEMP: [44, float32, RO] + lsdaq.GAIN: [46, float32, RO] + lsdaq.REF: [48, float32, RO] + + # CPU各温区温度 + mcu.zone1.temp: [50, float32, RO] + mcu.zone2.temp: [52, float32, RO] + mcu.zone3.temp: [54, float32, RO] + mcu.zone4.temp: [56, float32, RO] + mcu.zone5.temp: [58, float32, RO] + + # hsdaq特征值数据 + hsdaq.CH0.min: [60, float32, RO] + hsdaq.CH0.max: [62, float32, RO] + hsdaq.CH0.mean: [64, float32, RO] + hsdaq.CH0.std: [66, float32, RO] + hsdaq.CH0.rms: [68, float32, RO] + hsdaq.CH0.sr0: [70, float32, RO] + hsdaq.CH0.sr1: [72, float32, RO] + hsdaq.CH0.sr2: [74, float32, RO] + hsdaq.CH0.sr3: [76, float32, RO] + hsdaq.CH0.sr4: [78, float32, RO] + + hsdaq.CH1.min: [80, float32, RO] + hsdaq.CH1.max: [82, float32, RO] + hsdaq.CH1.mean: [84, float32, RO] + hsdaq.CH1.std: [86, float32, RO] + hsdaq.CH1.rms: [88, float32, RO] + hsdaq.CH1.sr0: [90, float32, RO] + hsdaq.CH1.sr1: [92, float32, RO] + hsdaq.CH1.sr2: [94, float32, RO] + hsdaq.CH1.sr3: [96, float32, RO] + hsdaq.CH1.sr4: [98, float32, RO] + + hsdaq.CH2.min: [100, float32, RO] + hsdaq.CH2.max: [102, float32, RO] + hsdaq.CH2.mean: [104, float32, RO] + hsdaq.CH2.std: [106, float32, RO] + hsdaq.CH2.rms: [108, float32, RO] + hsdaq.CH2.sr0: [110, float32, RO] + hsdaq.CH2.sr1: [112, float32, RO] + hsdaq.CH2.sr2: [114, float32, RO] + hsdaq.CH2.sr3: [116, float32, RO] + hsdaq.CH2.sr4: [118, float32, RO] + + hsdaq.CH3.min: [120, float32, RO] + hsdaq.CH3.max: [122, float32, RO] + hsdaq.CH3.mean: [124, float32, RO] + hsdaq.CH3.std: [126, float32, RO] + hsdaq.CH3.rms: [128, float32, RO] + hsdaq.CH3.sr0: [130, float32, RO] + hsdaq.CH3.sr1: [132, float32, RO] + hsdaq.CH3.sr2: [134, float32, RO] + hsdaq.CH3.sr3: [136, float32, RO] + hsdaq.CH3.sr4: [138, float32, RO] + + hsdaq.CH4.min: [140, float32, RO] + hsdaq.CH4.max: [142, float32, RO] + hsdaq.CH4.mean: [144, float32, RO] + hsdaq.CH4.std: [146, float32, RO] + hsdaq.CH4.rms: [148, float32, RO] + hsdaq.CH4.sr0: [150, float32, RO] + hsdaq.CH4.sr1: [152, float32, RO] + hsdaq.CH4.sr2: [154, float32, RO] + hsdaq.CH4.sr3: [156, float32, RO] + hsdaq.CH4.sr4: [158, float32, RO] + + hsdaq.CH5.min: [160, float32, RO] + hsdaq.CH5.max: [162, float32, RO] + hsdaq.CH5.mean: [164, float32, RO] + hsdaq.CH5.std: [166, float32, RO] + hsdaq.CH5.rms: [168, float32, RO] + hsdaq.CH5.sr0: [170, float32, RO] + hsdaq.CH5.sr1: [172, float32, RO] + hsdaq.CH5.sr2: [174, float32, RO] + hsdaq.CH5.sr3: [176, float32, RO] + hsdaq.CH5.sr4: [178, float32, RO] + + hsdaq.CH6.min: [180, float32, RO] + hsdaq.CH6.max: [182, float32, RO] + hsdaq.CH6.mean: [184, float32, RO] + hsdaq.CH6.std: [186, float32, RO] + hsdaq.CH6.rms: [188, float32, RO] + hsdaq.CH6.sr0: [190, float32, RO] + hsdaq.CH6.sr1: [192, float32, RO] + hsdaq.CH6.sr2: [194, float32, RO] + hsdaq.CH6.sr3: [196, float32, RO] + hsdaq.CH6.sr4: [198, float32, RO] + + hsdaq.CH7.min: [200, float32, RO] + hsdaq.CH7.max: [202, float32, RO] + hsdaq.CH7.mean: [204, float32, RO] + hsdaq.CH7.std: [206, float32, RO] + hsdaq.CH7.rms: [208, float32, RO] + hsdaq.CH7.sr0: [210, float32, RO] + hsdaq.CH7.sr1: [212, float32, RO] + hsdaq.CH7.sr2: [214, float32, RO] + hsdaq.CH7.sr3: [216, float32, RO] + hsdaq.CH7.sr4: [218, float32, RO] + + hsdaq.CH8.min: [220, float32, RO] + hsdaq.CH8.max: [222, float32, RO] + hsdaq.CH8.mean: [224, float32, RO] + hsdaq.CH8.std: [226, float32, RO] + hsdaq.CH8.rms: [228, float32, RO] + hsdaq.CH8.sr0: [230, float32, RO] + hsdaq.CH8.sr1: [232, float32, RO] + hsdaq.CH8.sr2: [234, float32, RO] + hsdaq.CH8.sr3: [236, float32, RO] + hsdaq.CH8.sr4: [238, float32, RO] + + hsdaq.CH9.min: [240, float32, RO] + hsdaq.CH9.max: [242, float32, RO] + hsdaq.CH9.mean: [244, float32, RO] + hsdaq.CH9.std: [246, float32, RO] + hsdaq.CH9.rms: [248, float32, RO] + hsdaq.CH9.sr0: [250, float32, RO] + hsdaq.CH9.sr1: [252, float32, RO] + hsdaq.CH9.sr2: [254, float32, RO] + hsdaq.CH9.sr3: [256, float32, RO] + hsdaq.CH9.sr4: [258, float32, RO] + + hsdaq.CH10.min: [260, float32, RO] + hsdaq.CH10.max: [262, float32, RO] + hsdaq.CH10.mean: [264, float32, RO] + hsdaq.CH10.std: [266, float32, RO] + hsdaq.CH10.rms: [268, float32, RO] + hsdaq.CH10.sr0: [270, float32, RO] + hsdaq.CH10.sr1: [272, float32, RO] + hsdaq.CH10.sr2: [274, float32, RO] + hsdaq.CH10.sr3: [276, float32, RO] + hsdaq.CH10.sr4: [278, float32, RO] + + hsdaq.CH11.min: [280, float32, RO] + hsdaq.CH11.max: [282, float32, RO] + hsdaq.CH11.mean: [284, float32, RO] + hsdaq.CH11.std: [286, float32, RO] + hsdaq.CH11.rms: [288, float32, RO] + hsdaq.CH11.sr0: [290, float32, RO] + hsdaq.CH11.sr1: [292, float32, RO] + hsdaq.CH11.sr2: [294, float32, RO] + hsdaq.CH11.sr3: [296, float32, RO] + hsdaq.CH11.sr4: [298, float32, RO] + + hsdaq.CH12.min: [300, float32, RO] + hsdaq.CH12.max: [302, float32, RO] + hsdaq.CH12.mean: [304, float32, RO] + hsdaq.CH12.std: [306, float32, RO] + hsdaq.CH12.rms: [308, float32, RO] + hsdaq.CH12.sr0: [310, float32, RO] + hsdaq.CH12.sr1: [312, float32, RO] + hsdaq.CH12.sr2: [314, float32, RO] + hsdaq.CH12.sr3: [316, float32, RO] + hsdaq.CH12.sr4: [318, float32, RO] + + hsdaq.CH13.min: [320, float32, RO] + hsdaq.CH13.max: [322, float32, RO] + hsdaq.CH13.mean: [324, float32, RO] + hsdaq.CH13.std: [326, float32, RO] + hsdaq.CH13.rms: [328, float32, RO] + hsdaq.CH13.sr0: [330, float32, RO] + hsdaq.CH13.sr1: [332, float32, RO] + hsdaq.CH13.sr2: [334, float32, RO] + hsdaq.CH13.sr3: [336, float32, RO] + hsdaq.CH13.sr4: [338, float32, RO] + + hsdaq.CH14.min: [340, float32, RO] + hsdaq.CH14.max: [342, float32, RO] + hsdaq.CH14.mean: [344, float32, RO] + hsdaq.CH14.std: [346, float32, RO] + hsdaq.CH14.rms: [348, float32, RO] + hsdaq.CH14.sr0: [350, float32, RO] + hsdaq.CH14.sr1: [352, float32, RO] + hsdaq.CH14.sr2: [354, float32, RO] + hsdaq.CH14.sr3: [356, float32, RO] + hsdaq.CH14.sr4: [358, float32, RO] + + hsdaq.CH15.min: [360, float32, RO] + hsdaq.CH15.max: [362, float32, RO] + hsdaq.CH15.mean: [364, float32, RO] + hsdaq.CH15.std: [366, float32, RO] + hsdaq.CH15.rms: [368, float32, RO] + hsdaq.CH15.sr0: [370, float32, RO] + hsdaq.CH15.sr1: [372, float32, RO] + hsdaq.CH15.sr2: [374, float32, RO] + hsdaq.CH15.sr3: [376, float32, RO] + hsdaq.CH15.sr4: [378, float32, RO] + +control_regs: + # lsdaq控制寄存器 + lsdaq.mode: [400, uint16, RW] + lsdaq.sensor_type: [401, uint16, RW] + + # PT100传感器校准参数 + lsdaq.sensor_Tmp_CalibParam.CH0.K2: [402, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH0.K: [404, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH0.B: [406, float32, RW] + + lsdaq.sensor_Tmp_CalibParam.CH1.K2: [408, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH1.K: [410, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH1.B: [412, float32, RW] + + lsdaq.sensor_Tmp_CalibParam.CH2.K2: [414, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH2.K: [416, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH2.B: [418, float32, RW] + + lsdaq.sensor_Tmp_CalibParam.CH3.K2: [420, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH3.K: [422, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH3.B: [424, float32, RW] + + lsdaq.sensor_Tmp_CalibParam.CH4.K2: [426, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH4.K: [428, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH4.B: [430, float32, RW] + + lsdaq.sensor_Tmp_CalibParam.CH5.K2: [432, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH5.K: [434, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH5.B: [436, float32, RW] + + lsdaq.sensor_Tmp_CalibParam.CH6.K2: [438, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH6.K: [440, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH6.B: [442, float32, RW] + + lsdaq.sensor_Tmp_CalibParam.CH7.K2: [444, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH7.K: [446, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH7.B: [448, float32, RW] + + lsdaq.sensor_Tmp_CalibParam.CH8.K2: [450, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH8.K: [452, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH8.B: [454, float32, RW] + + lsdaq.sensor_Tmp_CalibParam.CH9.K2: [456, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH9.K: [458, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH9.B: [460, float32, RW] + + lsdaq.sensor_Tmp_CalibParam.CH10.K2: [462, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH10.K: [464, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH10.B: [466, float32, RW] + + lsdaq.sensor_Tmp_CalibParam.CH11.K2: [468, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH11.K: [470, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH11.B: [472, float32, RW] + + lsdaq.sensor_Tmp_CalibParam.CH12.K2: [474, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH12.K: [476, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH12.B: [478, float32, RW] + + lsdaq.sensor_Tmp_CalibParam.CH13.K2: [480, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH13.K: [482, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH13.B: [484, float32, RW] + + lsdaq.sensor_Tmp_CalibParam.CH14.K2: [486, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH14.K: [488, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH14.B: [490, float32, RW] + + lsdaq.sensor_Tmp_CalibParam.CH15.K2: [492, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH15.K: [494, float32, RW] + lsdaq.sensor_Tmp_CalibParam.CH15.B: [496, float32, RW] + + # 4-20mA传感器校准参数 + lsdaq.sensor_Cur_CalibParam.CH0.K2: [498, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH0.K: [500, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH0.B: [502, float32, RW] + + lsdaq.sensor_Cur_CalibParam.CH1.K2: [504, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH1.K: [506, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH1.B: [508, float32, RW] + + lsdaq.sensor_Cur_CalibParam.CH2.K2: [510, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH2.K: [512, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH2.B: [514, float32, RW] + + lsdaq.sensor_Cur_CalibParam.CH3.K2: [516, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH3.K: [518, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH3.B: [520, float32, RW] + + lsdaq.sensor_Cur_CalibParam.CH4.K2: [522, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH4.K: [524, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH4.B: [526, float32, RW] + + lsdaq.sensor_Cur_CalibParam.CH5.K2: [528, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH5.K: [530, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH5.B: [532, float32, RW] + + lsdaq.sensor_Cur_CalibParam.CH6.K2: [534, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH6.K: [536, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH6.B: [538, float32, RW] + + lsdaq.sensor_Cur_CalibParam.CH7.K2: [540, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH7.K: [542, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH7.B: [544, float32, RW] + + lsdaq.sensor_Cur_CalibParam.CH8.K2: [546, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH8.K: [548, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH8.B: [550, float32, RW] + + lsdaq.sensor_Cur_CalibParam.CH9.K2: [552, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH9.K: [554, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH9.B: [556, float32, RW] + + lsdaq.sensor_Cur_CalibParam.CH10.K2: [558, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH10.K: [560, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH10.B: [562, float32, RW] + + lsdaq.sensor_Cur_CalibParam.CH11.K2: [564, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH11.K: [566, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH11.B: [568, float32, RW] + + lsdaq.sensor_Cur_CalibParam.CH12.K2: [570, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH12.K: [572, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH12.B: [574, float32, RW] + + lsdaq.sensor_Cur_CalibParam.CH13.K2: [576, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH13.K: [578, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH13.B: [580, float32, RW] + + lsdaq.sensor_Cur_CalibParam.CH14.K2: [582, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH14.K: [584, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH14.B: [586, float32, RW] + + lsdaq.sensor_Cur_CalibParam.CH15.K2: [588, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH15.K: [590, float32, RW] + lsdaq.sensor_Cur_CalibParam.CH15.B: [592, float32, RW] + + # hsdaq控制寄存器 + hsdaq.sample_time: [600, uint32, RW] + hsdaq.sample_period: [602, uint32, RW] + hsdaq.one_sample_time: [604, uint32, RW] + hsdaq.sensor_type: [606, uint32, RW] + hsdaq.mode: [608, uint16, RW] + hsdaq.save_flag: [609, uint16, RW] + hsdaq.file_type: [610, uint16, RW] + + # hsdaq 4-20mA传感器校准参数 + hsdaq.sensor_Cur_CalibParam.CH0.K2: [611, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH0.K: [613, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH0.B: [615, float32, RW] + + hsdaq.sensor_Cur_CalibParam.CH1.K2: [617, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH1.K: [619, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH1.B: [621, float32, RW] + + hsdaq.sensor_Cur_CalibParam.CH2.K2: [623, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH2.K: [625, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH2.B: [627, float32, RW] + + hsdaq.sensor_Cur_CalibParam.CH3.K2: [629, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH3.K: [631, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH3.B: [633, float32, RW] + + hsdaq.sensor_Cur_CalibParam.CH4.K2: [635, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH4.K: [637, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH4.B: [639, float32, RW] + + hsdaq.sensor_Cur_CalibParam.CH5.K2: [641, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH5.K: [643, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH5.B: [645, float32, RW] + + hsdaq.sensor_Cur_CalibParam.CH6.K2: [647, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH6.K: [649, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH6.B: [651, float32, RW] + + hsdaq.sensor_Cur_CalibParam.CH7.K2: [653, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH7.K: [655, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH7.B: [657, float32, RW] + + hsdaq.sensor_Cur_CalibParam.CH8.K2: [659, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH8.K: [661, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH8.B: [663, float32, RW] + + hsdaq.sensor_Cur_CalibParam.CH9.K2: [665, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH9.K: [667, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH9.B: [669, float32, RW] + + hsdaq.sensor_Cur_CalibParam.CH10.K2: [671, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH10.K: [673, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH10.B: [675, float32, RW] + + hsdaq.sensor_Cur_CalibParam.CH11.K2: [677, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH11.K: [679, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH11.B: [681, float32, RW] + + hsdaq.sensor_Cur_CalibParam.CH12.K2: [683, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH12.K: [685, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH12.B: [687, float32, RW] + + hsdaq.sensor_Cur_CalibParam.CH13.K2: [689, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH13.K: [691, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH13.B: [693, float32, RW] + + hsdaq.sensor_Cur_CalibParam.CH14.K2: [695, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH14.K: [697, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH14.B: [699, float32, RW] + + hsdaq.sensor_Cur_CalibParam.CH15.K2: [701, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH15.K: [703, float32, RW] + hsdaq.sensor_Cur_CalibParam.CH15.B: [705, float32, RW] + + # hsdaq 电压传感器校准参数 + hsdaq.sensor_Vol_CalibParam.CH0.K2: [707, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH0.K: [709, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH0.B: [711, float32, RW] + + hsdaq.sensor_Vol_CalibParam.CH1.K2: [713, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH1.K: [715, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH1.B: [717, float32, RW] + + hsdaq.sensor_Vol_CalibParam.CH2.K2: [719, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH2.K: [721, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH2.B: [723, float32, RW] + + hsdaq.sensor_Vol_CalibParam.CH3.K2: [725, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH3.K: [727, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH3.B: [729, float32, RW] + + hsdaq.sensor_Vol_CalibParam.CH4.K2: [731, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH4.K: [733, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH4.B: [735, float32, RW] + + hsdaq.sensor_Vol_CalibParam.CH5.K2: [737, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH5.K: [739, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH5.B: [741, float32, RW] + + hsdaq.sensor_Vol_CalibParam.CH6.K2: [743, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH6.K: [745, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH6.B: [747, float32, RW] + + hsdaq.sensor_Vol_CalibParam.CH7.K2: [749, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH7.K: [751, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH7.B: [753, float32, RW] + + hsdaq.sensor_Vol_CalibParam.CH8.K2: [755, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH8.K: [757, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH8.B: [759, float32, RW] + + hsdaq.sensor_Vol_CalibParam.CH9.K2: [761, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH9.K: [763, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH9.B: [765, float32, RW] + + hsdaq.sensor_Vol_CalibParam.CH10.K2: [767, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH10.K: [769, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH10.B: [771, float32, RW] + + hsdaq.sensor_Vol_CalibParam.CH11.K2: [773, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH11.K: [775, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH11.B: [777, float32, RW] + + hsdaq.sensor_Vol_CalibParam.CH12.K2: [779, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH12.K: [781, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH12.B: [783, float32, RW] + + hsdaq.sensor_Vol_CalibParam.CH13.K2: [785, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH13.K: [787, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH13.B: [789, float32, RW] + + hsdaq.sensor_Vol_CalibParam.CH14.K2: [791, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH14.K: [793, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH14.B: [795, float32, RW] + + hsdaq.sensor_Vol_CalibParam.CH15.K2: [797, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH15.K: [799, float32, RW] + hsdaq.sensor_Vol_CalibParam.CH15.B: [801, float32, RW] + + # hsdaq 振动传感器校准参数 + hsdaq.sensor_Vib_CalibParam.CH0.K2: [803, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH0.K: [805, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH0.B: [807, float32, RW] + + hsdaq.sensor_Vib_CalibParam.CH1.K2: [809, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH1.K: [811, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH1.B: [813, float32, RW] + + hsdaq.sensor_Vib_CalibParam.CH2.K2: [815, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH2.K: [817, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH2.B: [819, float32, RW] + + hsdaq.sensor_Vib_CalibParam.CH3.K2: [821, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH3.K: [823, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH3.B: [825, float32, RW] + + hsdaq.sensor_Vib_CalibParam.CH4.K2: [827, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH4.K: [829, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH4.B: [831, float32, RW] + + hsdaq.sensor_Vib_CalibParam.CH5.K2: [833, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH5.K: [835, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH5.B: [837, float32, RW] + + hsdaq.sensor_Vib_CalibParam.CH6.K2: [839, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH6.K: [841, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH6.B: [843, float32, RW] + + hsdaq.sensor_Vib_CalibParam.CH7.K2: [845, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH7.K: [847, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH7.B: [849, float32, RW] + + hsdaq.sensor_Vib_CalibParam.CH8.K2: [851, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH8.K: [853, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH8.B: [855, float32, RW] + + hsdaq.sensor_Vib_CalibParam.CH9.K2: [857, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH9.K: [859, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH9.B: [861, float32, RW] + + hsdaq.sensor_Vib_CalibParam.CH10.K2: [863, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH10.K: [865, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH10.B: [867, float32, RW] + + hsdaq.sensor_Vib_CalibParam.CH11.K2: [869, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH11.K: [871, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH11.B: [873, float32, RW] + + hsdaq.sensor_Vib_CalibParam.CH12.K2: [875, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH12.K: [877, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH12.B: [879, float32, RW] + + hsdaq.sensor_Vib_CalibParam.CH13.K2: [881, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH13.K: [883, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH13.B: [885, float32, RW] + + hsdaq.sensor_Vib_CalibParam.CH14.K2: [887, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH14.K: [889, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH14.B: [891, float32, RW] + + hsdaq.sensor_Vib_CalibParam.CH15.K2: [893, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH15.K: [895, float32, RW] + hsdaq.sensor_Vib_CalibParam.CH15.B: [897, float32, RW] \ No newline at end of file