调整以适应esp32

This commit is contained in:
2025-10-13 14:35:20 +08:00
parent 961b0c170b
commit 902c90cf19
15 changed files with 56 additions and 63 deletions

View File

@@ -7,9 +7,7 @@ RS485 总线管理器实现
此模块实现了 IBusManager 接口用于管理 RS485 总线通信 此模块实现了 IBusManager 接口用于管理 RS485 总线通信
""" """
from .bus_interface import IBusManager from ..logs.logger import log
from typing import Dict, Any
from main.logs.logger import log
# 导入 MicroPython 的 UART 和 Pin 库 # 导入 MicroPython 的 UART 和 Pin 库
from machine import UART, Pin from machine import UART, Pin
@@ -18,26 +16,26 @@ import _thread # 用于线程同步
import struct # 用于浮点数转换 import struct # 用于浮点数转换
class RS485Manager(IBusManager): class RS485Manager:
""" """
RS485 总线管理器 RS485 总线管理器
负责 RS485 设备的指令发送响应接收和数据解析 负责 RS485 设备的指令发送响应接收和数据解析
""" """
def __init__(self, bus_config: Dict[int, Dict[str, Any]], default_timeouts: Dict[str, int]): def __init__(self, bus_config, default_timeouts):
""" """
构造函数注入配置 构造函数注入配置
根据传入的配置初始化 RS485 总线对应的 UART 管理器 根据传入的配置初始化 RS485 总线对应的 UART 管理器
Args: Args:
bus_config (Dict[int, Dict[str, Any]]): 包含所有总线配置的字典 bus_config: 包含所有总线配置的字典
键是总线ID值是该总线的详细配置 键是总线ID值是该总线的详细配置
default_timeouts (Dict[str, int]): 包含各种默认超时设置的字典 default_timeouts: 包含各种默认超时设置的字典
""" """
self.bus_config = bus_config self.bus_config = bus_config
self.default_timeouts = default_timeouts self.default_timeouts = default_timeouts
# 存储以总线号为key的UART管理器实例、RTS引脚和锁 # 存储以总线号为key的UART管理器实例、RTS引脚和锁
self.bus_ports: Dict[int, Dict[str, Any]] = {} self.bus_ports = {}
log("RS485Manager 已使用配置初始化。") log("RS485Manager 已使用配置初始化。")
log(f"总线配置: {self.bus_config}") log(f"总线配置: {self.bus_config}")
@@ -77,7 +75,8 @@ class RS485Manager(IBusManager):
else: else:
log(f"总线 {bus_id} 的协议不是 RS485跳过初始化。") log(f"总线 {bus_id} 的协议不是 RS485跳过初始化。")
def _calculate_crc16_modbus(self, data: bytes) -> int: @staticmethod
def _calculate_crc16_modbus(data):
""" """
计算 Modbus RTU CRC16 校验码 计算 Modbus RTU CRC16 校验码
""" """
@@ -85,23 +84,24 @@ class RS485Manager(IBusManager):
for byte in data: for byte in data:
crc ^= byte crc ^= byte
for _ in range(8): for _ in range(8):
if (crc & 0x0001): if crc & 0x0001:
crc >>= 1 crc >>= 1
crc ^= 0xA001 crc ^= 0xA001
else: else:
crc >>= 1 crc >>= 1
return crc return crc
def _parse_modbus_rtu_float_default(self, response_bytes: bytes) -> float | None: @staticmethod
def _parse_modbus_rtu_float_default(response_bytes):
""" """
默认解析 Modbus RTU 响应中的 32 IEEE 754 单精度浮点数 默认解析 Modbus RTU 响应中的 32 IEEE 754 单精度浮点数
假定为大端 (ABCD) 字节序 假定为大端 (ABCD) 字节序
""" """
# 最小预期长度: 从站ID(1) + 功能码(1) + 字节计数(1) + 4字节数据 + CRC(2) = 9字节 # 最小预期长度: 从站ID(1) + 功能码(1) + 字节计数(1) + 4字节数据 + CRC(2) = 9字节
MIN_RESPONSE_LEN = 9 min_response_len = 9
EXPECTED_DATA_BYTE_COUNT = 4 # 32位浮点数占用4字节 expected_data_byte_count = 4 # 32位浮点数占用4字节
if not response_bytes or len(response_bytes) < MIN_RESPONSE_LEN: if not response_bytes or len(response_bytes) < min_response_len:
log(f"警告: 响应字节过短或为空,无法解析为浮点数。响应: {response_bytes.hex() if response_bytes else 'None'}") log(f"警告: 响应字节过短或为空,无法解析为浮点数。响应: {response_bytes.hex() if response_bytes else 'None'}")
return None return None
@@ -113,15 +113,14 @@ class RS485Manager(IBusManager):
received_crc = (response_bytes[-1] << 8) | response_bytes[-2] # CRC的低字节在前高字节在后 received_crc = (response_bytes[-1] << 8) | response_bytes[-2] # CRC的低字节在前高字节在后
# 1. CRC 校验 # 1. CRC 校验
calculated_crc = self._calculate_crc16_modbus(data_for_crc) calculated_crc = RS485Manager._calculate_crc16_modbus(data_for_crc)
if calculated_crc != received_crc: if calculated_crc != received_crc:
log(f"错误: CRC校验失败。接收CRC: {received_crc:04X}, 计算CRC: {calculated_crc:04X}. 响应: {response_bytes.hex()}") log(f"错误: CRC校验失败。接收CRC: {received_crc:04X}, 计算CRC: {calculated_crc:04X}. 响应: {response_bytes.hex()}")
return None return None
slave_id = response_bytes[0]
function_code = response_bytes[1] function_code = response_bytes[1]
byte_count = response_bytes[2] byte_count = response_bytes[2]
data_bytes = response_bytes[3:3 + EXPECTED_DATA_BYTE_COUNT] data_bytes = response_bytes[3:3 + expected_data_byte_count]
# 2. 功能码检查 (假设读取保持寄存器0x03或输入寄存器0x04) # 2. 功能码检查 (假设读取保持寄存器0x03或输入寄存器0x04)
if function_code not in [0x03, 0x04]: if function_code not in [0x03, 0x04]:
@@ -129,13 +128,13 @@ class RS485Manager(IBusManager):
return None return None
# 3. 字节计数检查 # 3. 字节计数检查
if byte_count != EXPECTED_DATA_BYTE_COUNT: if byte_count != expected_data_byte_count:
log(f"警告: 响应字节计数 {byte_count} 不符合预期 (期望{EXPECTED_DATA_BYTE_COUNT})。响应: {response_bytes.hex()}") log(f"警告: 响应字节计数 {byte_count} 不符合预期 (期望{expected_data_byte_count})。响应: {response_bytes.hex()}")
return None return None
# 4. 提取的数据字节长度检查 (与字节计数检查有重叠,但更安全) # 4. 提取的数据字节长度检查 (与字节计数检查有重叠,但更安全)
if len(data_bytes) != EXPECTED_DATA_BYTE_COUNT: if len(data_bytes) != expected_data_byte_count:
log(f"错误: 提取的数据字节长度不正确。期望{EXPECTED_DATA_BYTE_COUNT}, 实际{len(data_bytes)}. 响应: {response_bytes.hex()}") log(f"错误: 提取的数据字节长度不正确。期望{expected_data_byte_count}, 实际{len(data_bytes)}. 响应: {response_bytes.hex()}")
return None return None
# 5. 转换为浮点数 (大端, ABCD) # 5. 转换为浮点数 (大端, ABCD)
@@ -147,7 +146,7 @@ class RS485Manager(IBusManager):
log(f"错误: 浮点数转换失败: {e}. 数据字节: {data_bytes.hex()}. 响应: {response_bytes.hex()}") log(f"错误: 浮点数转换失败: {e}. 数据字节: {data_bytes.hex()}. 响应: {response_bytes.hex()}")
return None return None
def execute_raw_command(self, bus_id: int, command: bytes) -> None: def execute_raw_command(self, bus_id, command):
""" """
契约执行一个发后不理的原始指令 契约执行一个发后不理的原始指令
@@ -176,7 +175,7 @@ class RS485Manager(IBusManager):
except Exception as e: except Exception as e:
log(f"错误: 在总线 {bus_id} 上执行原始命令失败: {e}") log(f"错误: 在总线 {bus_id} 上执行原始命令失败: {e}")
def execute_collect_task(self, task: dict) -> float | None: def execute_collect_task(self, task):
""" """
契约执行一个完整的采集任务并直接返回最终的数值 契约执行一个完整的采集任务并直接返回最终的数值
@@ -188,11 +187,11 @@ class RS485Manager(IBusManager):
- 返回最终的float数值或在任何失败情况下返回None - 返回最终的float数值或在任何失败情况下返回None
Args: Args:
task (dict): 从Protobuf解码出的单个CollectTask消息字典 task: 从Protobuf解码出的单个CollectTask消息字典
期望结构: {"command": {"bus_number": int, "command_bytes": bytes}} 期望结构: {"command": {"bus_number": int, "command_bytes": bytes}}
Returns: Returns:
float | None: 成功解析则返回数值否则返回None 成功解析则返回数值否则返回None
""" """
# I. 任务参数解析与初步验证 # I. 任务参数解析与初步验证
try: try:
@@ -248,7 +247,7 @@ class RS485Manager(IBusManager):
# IV. 响应解析与数据提取 (默认 Modbus RTU 浮点数) # IV. 响应解析与数据提取 (默认 Modbus RTU 浮点数)
# TODO: 根据CollectTask的Protobuf定义此处需要根据parser_type来选择具体的解析逻辑和类型。 # TODO: 根据CollectTask的Protobuf定义此处需要根据parser_type来选择具体的解析逻辑和类型。
# 目前默认使用Modbus RTU大端浮点数解析。 # 目前默认使用Modbus RTU大端浮点数解析。
parsed_value = self._parse_modbus_rtu_float_default(response_bytes) parsed_value = RS485Manager._parse_modbus_rtu_float_default(response_bytes)
# V. 返回结果 # V. 返回结果
return parsed_value return parsed_value

View File

@@ -22,8 +22,8 @@ LORA_CONFIG = {
# LoRa模块连接的GPIO引脚 # LoRa模块连接的GPIO引脚
'pins': { 'pins': {
'tx': 17, # UART TX 'tx': 5, # UART TX
'rx': 16, # UART RX 'rx': 4, # UART RX
}, },
# LoRa Mesh 模块发送模式(EC: 透传; ED: 完整数据包) # LoRa Mesh 模块发送模式(EC: 透传; ED: 完整数据包)
@@ -60,9 +60,9 @@ BUS_CONFIG = {
# 该总线使用的GPIO引脚 # 该总线使用的GPIO引脚
'pins': { 'pins': {
'tx': 4, # RS485 TX 'tx': 16, # RS485 TX
'rx': 5, # RS485 RX 'rx': 17, # RS485 RX
'rts': 2, # RS485 DE/RE 方向控制引脚 'rts': 15, # RS485 DE/RE 方向控制引脚
} }
}, },

View File

@@ -5,7 +5,7 @@
一个简单的可配置的日志记录器模块 一个简单的可配置的日志记录器模块
""" """
from main.config.config import * from app.config.config import *
def log(message: str): def log(message: str):

View File

@@ -8,13 +8,12 @@ LoRa模块的具体实现 (UART Passthrough for LoRa Mesh)
这个实现针对的是通过UART进行透传的LoRa Mesh模块 这个实现针对的是通过UART进行透传的LoRa Mesh模块
""" """
from .lora_interface import ILoraManager from ..logs.logger import log
from main.logs.logger import log
from machine import UART from machine import UART
import time import time
class LoRaMeshUartPassthroughManager(ILoraManager): class LoRaMeshUartPassthroughManager:
""" """
通过UART与LoRa Mesh模块通信的处理器实现 (ED模式) 通过UART与LoRa Mesh模块通信的处理器实现 (ED模式)
实现了自动分片与重组逻辑 实现了自动分片与重组逻辑

View File

@@ -8,15 +8,12 @@
- 编排业务流程解码指令并将业务任务分发给相应的管理器 - 编排业务流程解码指令并将业务任务分发给相应的管理器
- 完全不关心总线通信和数据解析的技术实现细节 - 完全不关心总线通信和数据解析的技术实现细节
""" """
from app.lora.lora_mesh_uart_passthrough_manager import LoRaMeshUartPassthroughManager
# 导入我们定义的“契约”(接口) from app.bus.rs485_manager import RS485Manager
from lora.lora_interface import ILoraManager
from bus.bus_interface import IBusManager
# 导入Protobuf解析代码 # 导入Protobuf解析代码
from proto import client_pb from app.proto import client_pb
from logs.logger import log from app.logs.logger import log
class Processor: class Processor:
@@ -25,7 +22,7 @@ class Processor:
它依赖于抽象的面向业务的接口 它依赖于抽象的面向业务的接口
""" """
def __init__(self, lora_handler: ILoraManager, bus_manager: IBusManager): def __init__(self, lora_handler: LoRaMeshUartPassthroughManager, bus_manager: RS485Manager):
""" """
构造函数 (依赖注入) 构造函数 (依赖注入)

View File

@@ -10,12 +10,12 @@
- 从队列中取出任务并交给Processor进行耗时处理 - 从队列中取出任务并交给Processor进行耗时处理
""" """
import uqueue from app.uqueue import Queue
from processor import Processor from app.processor import Processor
from logs.logger import log from app.logs.logger import log
def worker_task(task_queue: uqueue.Queue, processor: Processor): def worker_task(task_queue: Queue, processor: Processor):
""" """
工作线程的主函数 工作线程的主函数

View File

@@ -15,25 +15,23 @@
import time import time
import _thread import _thread
from config import config from app.config import config
import uqueue # 导入我们自己创建的本地uqueue模块 from app.uqueue import Queue # 导入我们自己创建的本地uqueue模块
# 导入接口和实现 # 导入接口和实现
from lora.lora_interface import ILoraManager from app.lora.lora_mesh_uart_passthrough_manager import LoRaMeshUartPassthroughManager
from bus.bus_interface import IBusManager from app.bus.rs485_manager import RS485Manager
from lora.lora_mesh_uart_passthrough_manager import LoRaMeshUartPassthroughManager from app.processor import Processor
from bus.rs485_manager import RS485Manager
from processor import Processor
# 导入工作线程的执行函数 # 导入工作线程的执行函数
from worker import worker_task from app.worker import worker_task
from logs.logger import log from app.logs.logger import log
# --- 模块级变量定义 (带有类型提示) --- # --- 模块级变量定义 (带有类型提示) ---
lora_manager: ILoraManager | None = None lora_manager: LoRaMeshUartPassthroughManager | None = None
bus_manager: IBusManager | None = None bus_manager: RS485Manager | None = None
processor: Processor | None = None processor: Processor | None = None
task_queue: uqueue.Queue | None = None task_queue: Queue | None = None
def setup(): def setup():
@@ -52,7 +50,7 @@ def setup():
# 2. 从配置文件读取队列长度,并创建线程安全的队列 # 2. 从配置文件读取队列长度,并创建线程安全的队列
queue_size = config.SYSTEM_PARAMS.get('task_queue_max_size', 10) queue_size = config.SYSTEM_PARAMS.get('task_queue_max_size', 10)
task_queue = uqueue.Queue(maxsize=queue_size) task_queue = Queue(maxsize=queue_size)
log(f"任务队列已创建,最大容量: {queue_size}") log(f"任务队列已创建,最大容量: {queue_size}")
# 3. 启动工作线程 # 3. 启动工作线程