【无标题】

*************

Python

topic: 电机状态和计算机通信

*************

The topics done in 力扣 are causing me distress. While doing the 力扣 and some other exercises, I didn't really understand what the TOPIC of this question was actually doing. In actual engineering applications, it is rare to use such awesome algorithms, which are basically simple additions and subtractions, adding something new to the previous code.

企业级的文件都是分工明确的。不同的文件只做一件事,这样方便维护。每个模块应该以独立的文件出现。其目录结构大致如下:

motor_control_system/
├── main.py
├── config/
│   ├── __init__.py
│   ├── settings.py
│   └── logging_config.py
├── communication/
│   ├── __init__.py
│   ├── protocol.py
│   └── client.py
├── motor/
│   ├── __init__.py
│   ├── state.py
│   └── controller.py
├── utils/
│   ├── __init__.py
│   ├── exceptions.py
│   └── validator.py
├── tests/
│   ├── __init__.py
│   └── test_controller.py
└── requirements.txt

假如你是研发总监,那么你就可以说,每个人负责一个模块。然后你就可以组织一个会议,将功能的实现任务分发。我不是很了解具体的流程,因为我不是研发总监哈哈哈啊哈。

1、配置文件模块

# settings.py - 全局配置参数

class Settings:
    """电机通信系统的全局配置"""
    MOTOR_IP: str = "192.168.1.100"      # 电机控制器IP地址
    MOTOR_PORT: int = 502                # Modbus TCP默认端口
    SERIAL_PORT: str = "/dev/ttyUSB0"    # 备用串口路径
    BAUD_RATE: int = 9600                # 串口波特率
    TIMEOUT: float = 3.0                 # 通信超时(秒)
    RETRY_ATTEMPTS: int = 3              # 通信重试次数

    @classmethod
    def validate(cls):
        """验证配置有效性"""
        if not cls.MOTOR_IP:
            raise ValueError("IP地址不能为空")
        if cls.TIMEOUT < 0:
            raise ValueError("超时时间必须为正数")
# logging_config.py - 日志配置

import logging
from pathlib import Path

def configure_logging(log_dir: str = "logs") -> None:
    """配置日志系统和文件输出
    
    Args:
        log_dir: 日志文件存储目录
    """
    Path(log_dir).mkdir(exist_ok=True)
    
    logging.basicConfig(
        level=logging.INFO,
        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
        handlers=[
            logging.FileHandler(f"{log_dir}/motor_system.log"),
            logging.StreamHandler()
        ]
    )
    logging.captureWarnings(True)  # 捕获警告日志

2、通信协议模块

# protocol.py - 通信协议定义

from typing import Tuple
from utils.exceptions import ProtocolError

class ModbusProtocol:
    """Modbus RTU/TCP协议处理"""

    @staticmethod
    def pack_read_command(slave_id: int, register: int) -> bytes:
        """打包读取寄存器命令
        
        Args:
            slave_id: 从站地址
            register: 寄存器地址
            
        Returns:
            完整的Modbus命令帧
        """
        if not 0 <= slave_id <= 247:
            raise ProtocolError("从站地址超出范围")
        return bytes([
            slave_id, 0x03, 
            register >> 8, register & 0xFF, 
            0x00, 0x01  # 默认读取1个寄存器
        ])

    @staticmethod
    def unpack_response(data: bytes) -> Tuple[int, int]:
        """解包Modbus响应数据
        
        Returns:
            (register_value, crc_check_result)
        """
        if len(data) < 6:
            raise ProtocolError("响应数据长度不足")
        return (int.from_bytes(data[3:5], 'big'), 1)  # 示例简化CRC校验
# client.py - 通信客户端实现

import socket
from typing import Optional
from config.settings import Settings
from utils.exceptions import CommunicationError

class MotorCommunicationClient:
    """电机通信客户端 (TCP实现)"""

    def __init__(self):
        self.socket: Optional[socket.socket] = None
        self._connect()

    def _connect(self) -> None:
        """建立TCP连接"""
        try:
            self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.socket.settimeout(Settings.TIMEOUT)
            self.socket.connect((Settings.MOTOR_IP, Settings.MOTOR_PORT))
        except socket.error as e:
            raise CommunicationError(f"连接失败: {str(e)}")

    def send_command(self, command: bytes) -> bytes:
        """发送命令并获取响应"""
        for attempt in range(Settings.RETRY_ATTEMPTS):
            try:
                self.socket.sendall(command)
                return self.socket.recv(1024)
            except socket.timeout:
                if attempt == Settings.RETRY_ATTEMPTS - 1:
                    raise CommunicationError("通信超时")
            except socket.error as e:
                self._connect()  # 尝试重连
                raise CommunicationError(f"通信错误: {str(e)}")

    def __del__(self):
        if self.socket:
            self.socket.close()

3、电机控制模块

# state.py - 电机状态模型

from dataclasses import dataclass
from enum import IntEnum

class MotorErrorCode(IntEnum):
    """电机错误代码枚举"""
    NORMAL = 0
    OVER_TEMPERATURE = 1
    OVER_CURRENT = 2
    ENCODER_ERROR = 3

@dataclass(frozen=True)
class MotorState:
    """电机实时状态数据类"""
    speed_rpm: float
    temperature_c: float
    current_a: float
    error_code: MotorErrorCode = MotorErrorCode.NORMAL

    def is_safe(self) -> bool:
        """检查电机状态是否安全"""
        return (
            self.error_code == MotorErrorCode.NORMAL
            and self.temperature_c < 80.0
            and self.current_a < 10.0
        )
# controller.py - 电机控制逻辑

from typing import Optional
from communication.client import MotorCommunicationClient
from communication.protocol import ModbusProtocol
from motor.state import MotorState, MotorErrorCode
from utils.exceptions import MotorFaultError

class MotorController:
    """电机控制核心类"""

    def __init__(self):
        self.client = MotorCommunicationClient()
        self._current_state: Optional[MotorState] = None

    def get_state(self) -> MotorState:
        """获取当前电机状态"""
        cmd = ModbusProtocol.pack_read_command(1, 0x4000)
        resp = self.client.send_command(cmd)
        value, _ = ModbusProtocol.unpack_response(resp)
        
        # 模拟数据解析 (实际根据协议调整)
        self._current_state = MotorState(
            speed_rpm=float(value & 0xFFFF),
            temperature_c=float((value >> 16) & 0xFF),
            current_a=float((value >> 24) & 0xFF) / 10.0,
            error_code=MotorErrorCode(value >> 32)
        )
        
        if not self._current_state.is_safe():
            raise MotorFaultError("电机处于不安全状态")
        return self._current_state

    def set_speed(self, rpm: float) -> bool:
        """设置电机转速"""
        if not 0 <= rpm <= 3000:
            raise ValueError("转速超出有效范围")
        # 实际实现需要根据协议发送控制命令
        return True

4、工具模块

# exceptions.py - 自定义异常体系

class MotorSystemError(Exception):
    """系统基础异常"""
    pass

class CommunicationError(MotorSystemError):
    """通信相关异常"""
    pass

class ProtocolError(MotorSystemError):
    """协议解析异常"""
    pass

class MotorFaultError(MotorSystemError):
    """电机故障异常"""
    pass

5、主程序入口

# main.py - 系统入口

from config.logging_config import configure_logging
from config.settings import Settings
from motor.controller import MotorController

def main():
    # 初始化系统
    Settings.validate()
    configure_logging()
    
    # 创建控制器实例
    controller = MotorController()
    
    try:
        # 示例操作
        state = controller.get_state()
        print(f"当前状态: {state}")
        
        controller.set_speed(1500.0)
        print("转速设置成功")
        
    except Exception as e:
        print(f"系统错误: {str(e)}")

if __name__ == "__main__":
    main()

6、单元测试

# test_controller.py - 控制器单元测试

import pytest
from unittest.mock import MagicMock
from motor.controller import MotorController
from motor.state import MotorState

class TestMotorController:
    @pytest.fixture
    def mock_client(self):
        client = MagicMock()
        client.send_command.return_value = bytes([1, 3, 2, 0, 100, 0, 0])
        return client

    def test_get_state(self, mock_client):
        controller = MotorController()
        controller.client = mock_client
        
        state = controller.get_state()
        assert isinstance(state, MotorState)
        assert state.speed_rpm == 100.0

事实上,一个完整的工程文件大概就是这么个结构,可能整体文件量更多,对应实现的功能更多。但是结构大概就是这么个结构。

所以我还是认为,在完整的工程类文件中,要学会写函数,更要学会调用函数。有很多时候是多层嵌套的。所以在不同的类,命名空间中调用函数得一直学习。实际的工程文件中更多用到的还是初中知识,加加减减的。调用,调用,还是调用。明天再重新学习一下调用吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值