调整以适应esp32
This commit is contained in:
@@ -7,37 +7,35 @@ 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
|
||||||
import time # 用于添加延时,确保RS485方向切换
|
import time # 用于添加延时,确保RS485方向切换
|
||||||
import _thread # 用于线程同步
|
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
|
||||||
|
|
||||||
@@ -110,18 +110,17 @@ class RS485Manager(IBusManager):
|
|||||||
# response_bytes[:-2] 是用于CRC计算的数据部分
|
# response_bytes[:-2] 是用于CRC计算的数据部分
|
||||||
# response_bytes[-2:] 是CRC本身
|
# response_bytes[-2:] 是CRC本身
|
||||||
data_for_crc = response_bytes[:-2]
|
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 校验
|
# 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
|
||||||
@@ -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 方向控制引脚
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
一个简单的、可配置的日志记录器模块。
|
一个简单的、可配置的日志记录器模块。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from main.config.config import *
|
from app.config.config import *
|
||||||
|
|
||||||
|
|
||||||
def log(message: str):
|
def log(message: str):
|
||||||
@@ -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模式)。
|
||||||
实现了自动分片与重组逻辑。
|
实现了自动分片与重组逻辑。
|
||||||
@@ -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):
|
||||||
"""
|
"""
|
||||||
构造函数 (依赖注入)。
|
构造函数 (依赖注入)。
|
||||||
|
|
||||||
@@ -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):
|
||||||
"""
|
"""
|
||||||
工作线程的主函数。
|
工作线程的主函数。
|
||||||
|
|
||||||
@@ -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. 启动工作线程
|
||||||
Reference in New Issue
Block a user