调整以适应esp32
This commit is contained in:
@@ -7,37 +7,35 @@ RS485 总线管理器实现
|
||||
此模块实现了 IBusManager 接口,用于管理 RS485 总线通信。
|
||||
"""
|
||||
|
||||
from .bus_interface import IBusManager
|
||||
from typing import Dict, Any
|
||||
from main.logs.logger import log
|
||||
from ..logs.logger import log
|
||||
|
||||
# 导入 MicroPython 的 UART 和 Pin 库
|
||||
from machine import UART, Pin
|
||||
import time # 用于添加延时,确保RS485方向切换
|
||||
import _thread # 用于线程同步
|
||||
import struct # 用于浮点数转换
|
||||
import struct # 用于浮点数转换
|
||||
|
||||
|
||||
class RS485Manager(IBusManager):
|
||||
class RS485Manager:
|
||||
"""
|
||||
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 管理器。
|
||||
|
||||
Args:
|
||||
bus_config (Dict[int, Dict[str, Any]]): 包含所有总线配置的字典。
|
||||
bus_config: 包含所有总线配置的字典。
|
||||
键是总线ID,值是该总线的详细配置。
|
||||
default_timeouts (Dict[str, int]): 包含各种默认超时设置的字典。
|
||||
default_timeouts: 包含各种默认超时设置的字典。
|
||||
"""
|
||||
self.bus_config = bus_config
|
||||
self.default_timeouts = default_timeouts
|
||||
# 存储以总线号为key的UART管理器实例、RTS引脚和锁
|
||||
self.bus_ports: Dict[int, Dict[str, Any]] = {}
|
||||
self.bus_ports = {}
|
||||
|
||||
log("RS485Manager 已使用配置初始化。")
|
||||
log(f"总线配置: {self.bus_config}")
|
||||
@@ -77,7 +75,8 @@ class RS485Manager(IBusManager):
|
||||
else:
|
||||
log(f"总线 {bus_id} 的协议不是 RS485,跳过初始化。")
|
||||
|
||||
def _calculate_crc16_modbus(self, data: bytes) -> int:
|
||||
@staticmethod
|
||||
def _calculate_crc16_modbus(data):
|
||||
"""
|
||||
计算 Modbus RTU 的 CRC16 校验码。
|
||||
"""
|
||||
@@ -85,23 +84,24 @@ class RS485Manager(IBusManager):
|
||||
for byte in data:
|
||||
crc ^= byte
|
||||
for _ in range(8):
|
||||
if (crc & 0x0001):
|
||||
if crc & 0x0001:
|
||||
crc >>= 1
|
||||
crc ^= 0xA001
|
||||
else:
|
||||
crc >>= 1
|
||||
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 单精度浮点数。
|
||||
假定为大端 (ABCD) 字节序。
|
||||
"""
|
||||
# 最小预期长度: 从站ID(1) + 功能码(1) + 字节计数(1) + 4字节数据 + CRC(2) = 9字节
|
||||
MIN_RESPONSE_LEN = 9
|
||||
EXPECTED_DATA_BYTE_COUNT = 4 # 32位浮点数占用4字节
|
||||
min_response_len = 9
|
||||
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'}")
|
||||
return None
|
||||
|
||||
@@ -110,18 +110,17 @@ class RS485Manager(IBusManager):
|
||||
# response_bytes[:-2] 是用于CRC计算的数据部分
|
||||
# response_bytes[-2:] 是CRC本身
|
||||
data_for_crc = response_bytes[:-2]
|
||||
received_crc = (response_bytes[-1] << 8) | response_bytes[-2] # CRC的低字节在前,高字节在后
|
||||
received_crc = (response_bytes[-1] << 8) | response_bytes[-2] # 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:
|
||||
log(f"错误: CRC校验失败。接收CRC: {received_crc:04X}, 计算CRC: {calculated_crc:04X}. 响应: {response_bytes.hex()}")
|
||||
return None
|
||||
|
||||
slave_id = response_bytes[0]
|
||||
function_code = response_bytes[1]
|
||||
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)
|
||||
if function_code not in [0x03, 0x04]:
|
||||
@@ -129,13 +128,13 @@ class RS485Manager(IBusManager):
|
||||
return None
|
||||
|
||||
# 3. 字节计数检查
|
||||
if byte_count != EXPECTED_DATA_BYTE_COUNT:
|
||||
log(f"警告: 响应字节计数 {byte_count} 不符合预期 (期望{EXPECTED_DATA_BYTE_COUNT})。响应: {response_bytes.hex()}")
|
||||
if byte_count != expected_data_byte_count:
|
||||
log(f"警告: 响应字节计数 {byte_count} 不符合预期 (期望{expected_data_byte_count})。响应: {response_bytes.hex()}")
|
||||
return None
|
||||
|
||||
# 4. 提取的数据字节长度检查 (与字节计数检查有重叠,但更安全)
|
||||
if len(data_bytes) != EXPECTED_DATA_BYTE_COUNT:
|
||||
log(f"错误: 提取的数据字节长度不正确。期望{EXPECTED_DATA_BYTE_COUNT}, 实际{len(data_bytes)}. 响应: {response_bytes.hex()}")
|
||||
if len(data_bytes) != expected_data_byte_count:
|
||||
log(f"错误: 提取的数据字节长度不正确。期望{expected_data_byte_count}, 实际{len(data_bytes)}. 响应: {response_bytes.hex()}")
|
||||
return None
|
||||
|
||||
# 5. 转换为浮点数 (大端, ABCD)
|
||||
@@ -147,7 +146,7 @@ class RS485Manager(IBusManager):
|
||||
log(f"错误: 浮点数转换失败: {e}. 数据字节: {data_bytes.hex()}. 响应: {response_bytes.hex()}")
|
||||
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:
|
||||
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。
|
||||
|
||||
Args:
|
||||
task (dict): 从Protobuf解码出的单个CollectTask消息字典。
|
||||
task: 从Protobuf解码出的单个CollectTask消息字典。
|
||||
期望结构: {"command": {"bus_number": int, "command_bytes": bytes}}
|
||||
|
||||
Returns:
|
||||
float | None: 成功解析则返回数值,否则返回None。
|
||||
成功解析则返回数值,否则返回None。
|
||||
"""
|
||||
# I. 任务参数解析与初步验证
|
||||
try:
|
||||
@@ -248,7 +247,7 @@ class RS485Manager(IBusManager):
|
||||
# IV. 响应解析与数据提取 (默认 Modbus RTU 浮点数)
|
||||
# TODO: 根据CollectTask的Protobuf定义,此处需要根据parser_type来选择具体的解析逻辑和类型。
|
||||
# 目前默认使用Modbus RTU大端浮点数解析。
|
||||
parsed_value = self._parse_modbus_rtu_float_default(response_bytes)
|
||||
|
||||
parsed_value = RS485Manager._parse_modbus_rtu_float_default(response_bytes)
|
||||
|
||||
# V. 返回结果
|
||||
return parsed_value
|
||||
@@ -22,8 +22,8 @@ LORA_CONFIG = {
|
||||
|
||||
# LoRa模块连接的GPIO引脚
|
||||
'pins': {
|
||||
'tx': 17, # UART TX
|
||||
'rx': 16, # UART RX
|
||||
'tx': 5, # UART TX
|
||||
'rx': 4, # UART RX
|
||||
},
|
||||
|
||||
# LoRa Mesh 模块发送模式(EC: 透传; ED: 完整数据包)
|
||||
@@ -60,9 +60,9 @@ BUS_CONFIG = {
|
||||
|
||||
# 该总线使用的GPIO引脚
|
||||
'pins': {
|
||||
'tx': 4, # RS485 TX
|
||||
'rx': 5, # RS485 RX
|
||||
'rts': 2, # RS485 DE/RE 方向控制引脚
|
||||
'tx': 16, # RS485 TX
|
||||
'rx': 17, # RS485 RX
|
||||
'rts': 15, # RS485 DE/RE 方向控制引脚
|
||||
}
|
||||
},
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
一个简单的、可配置的日志记录器模块。
|
||||
"""
|
||||
|
||||
from main.config.config import *
|
||||
from app.config.config import *
|
||||
|
||||
|
||||
def log(message: str):
|
||||
@@ -8,13 +8,12 @@ LoRa模块的具体实现 (UART Passthrough for LoRa Mesh)
|
||||
这个实现针对的是通过UART进行透传的LoRa Mesh模块。
|
||||
"""
|
||||
|
||||
from .lora_interface import ILoraManager
|
||||
from main.logs.logger import log
|
||||
from ..logs.logger import log
|
||||
from machine import UART
|
||||
import time
|
||||
|
||||
|
||||
class LoRaMeshUartPassthroughManager(ILoraManager):
|
||||
class LoRaMeshUartPassthroughManager:
|
||||
"""
|
||||
通过UART与LoRa Mesh模块通信的处理器实现 (ED模式)。
|
||||
实现了自动分片与重组逻辑。
|
||||
@@ -8,15 +8,12 @@
|
||||
- 编排业务流程:解码指令,并将业务任务分发给相应的管理器。
|
||||
- 完全不关心总线通信和数据解析的技术实现细节。
|
||||
"""
|
||||
|
||||
# 导入我们定义的“契约”(接口)
|
||||
from lora.lora_interface import ILoraManager
|
||||
from bus.bus_interface import IBusManager
|
||||
|
||||
from app.lora.lora_mesh_uart_passthrough_manager import LoRaMeshUartPassthroughManager
|
||||
from app.bus.rs485_manager import RS485Manager
|
||||
# 导入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:
|
||||
@@ -25,7 +22,7 @@ class Processor:
|
||||
它依赖于抽象的、面向业务的接口。
|
||||
"""
|
||||
|
||||
def __init__(self, lora_handler: ILoraManager, bus_manager: IBusManager):
|
||||
def __init__(self, lora_handler: LoRaMeshUartPassthroughManager, bus_manager: RS485Manager):
|
||||
"""
|
||||
构造函数 (依赖注入)。
|
||||
|
||||
@@ -10,12 +10,12 @@
|
||||
- 从队列中取出任务,并交给Processor进行耗时处理。
|
||||
"""
|
||||
|
||||
import uqueue
|
||||
from processor import Processor
|
||||
from logs.logger import log
|
||||
from app.uqueue import Queue
|
||||
from app.processor import Processor
|
||||
from app.logs.logger import log
|
||||
|
||||
|
||||
def worker_task(task_queue: uqueue.Queue, processor: Processor):
|
||||
def worker_task(task_queue: Queue, processor: Processor):
|
||||
"""
|
||||
工作线程的主函数。
|
||||
|
||||
@@ -15,25 +15,23 @@
|
||||
|
||||
import time
|
||||
import _thread
|
||||
from config import config
|
||||
import uqueue # 导入我们自己创建的本地uqueue模块
|
||||
from app.config import config
|
||||
from app.uqueue import Queue # 导入我们自己创建的本地uqueue模块
|
||||
|
||||
# 导入接口和实现
|
||||
from lora.lora_interface import ILoraManager
|
||||
from bus.bus_interface import IBusManager
|
||||
from lora.lora_mesh_uart_passthrough_manager import LoRaMeshUartPassthroughManager
|
||||
from bus.rs485_manager import RS485Manager
|
||||
from processor import Processor
|
||||
from app.lora.lora_mesh_uart_passthrough_manager import LoRaMeshUartPassthroughManager
|
||||
from app.bus.rs485_manager import RS485Manager
|
||||
from app.processor import Processor
|
||||
|
||||
# 导入工作线程的执行函数
|
||||
from worker import worker_task
|
||||
from logs.logger import log
|
||||
from app.worker import worker_task
|
||||
from app.logs.logger import log
|
||||
|
||||
# --- 模块级变量定义 (带有类型提示) ---
|
||||
lora_manager: ILoraManager | None = None
|
||||
bus_manager: IBusManager | None = None
|
||||
lora_manager: LoRaMeshUartPassthroughManager | None = None
|
||||
bus_manager: RS485Manager | None = None
|
||||
processor: Processor | None = None
|
||||
task_queue: uqueue.Queue | None = None
|
||||
task_queue: Queue | None = None
|
||||
|
||||
|
||||
def setup():
|
||||
@@ -52,7 +50,7 @@ def setup():
|
||||
|
||||
# 2. 从配置文件读取队列长度,并创建线程安全的队列
|
||||
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}")
|
||||
|
||||
# 3. 启动工作线程
|
||||
Reference in New Issue
Block a user