基于python控制继电器开关操作以及串口轮询

        本次流水线继电器一拖多资源支撑脚本初衷是为了节约成本,实现手机与继电器串口绑定。在实验过程中由于硬件不适配,处处碰壁,也是我第一次接触硬件方面的内容在此做些总结与记录。(由于部分内容不太清楚是否触及公司的保密协议,可能会打码)

关于继电器的说明:USB继电器模块搭载性能稳定的USB转串口芯片和微控制器,可在电脑端用串口调试软件发送串口指令来控制继电器的开和关。

方案:

  1. 1个手机链接1个双通道继电器,分别切换USB和供电,为1组继电器 (绑定手机与继电器组映射关系o轮询后映射)
  2. A手机测试时,BCD..手机供电继电器断开,USB切换继电器保持,BCD..手机无法通信,BCD不在线,
  3. 无手机测试时,所有设备供电,USB均可通信,LDMS中设备均在线
  4. 多个项目在同1个节点,当前任务A独占pc设备 (不能因手机断线而直接导致BCD排队中的任务失败),其余任务需排队

        继电器的串口1中usb连接电脑,typc连接手机(故定义为通信串口),串口2连接电源充当供电串口。        

        代码案例如下:(暂未优化,还需添加当前继电器状态的定义,以及每次切换开关时回显转换前与转换后的状态对比等等。。。。)

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# coding=utf-8

import logging
import sys
import time
import serial
from serial.tools import list_ports
import os


class _RelayChannel:
    """T20双通道USB继电器通道名称定义
    """
    CONSTANT_PART = "A0"  # 常量

    lineone = "01"  # 第一路

    linetwo = "02"  # 第二路

    lineall = "0F"  # 所有路

    switchon = "01"  # 打开

    switchoff = "00"  # 关闭

    switchcheck = "02"  # 查询

    "打开第一路"
    oneon = int(CONSTANT_PART, 16) + int(lineone, 16) + int(switchon, 16)
    one_on = bytes.fromhex(CONSTANT_PART + lineone + switchon + f"{oneon:02X}")

    "关闭第一路"
    oneoff = int(CONSTANT_PART, 16) + int(lineone, 16) + int(switchoff, 16)
    one_off = bytes.fromhex(CONSTANT_PART + lineone + switchoff + f"{oneoff:02X}")

    "打开第二路"
    twoon = int(CONSTANT_PART, 16) + int(linetwo, 16) + int(switchon, 16)
    two_on = bytes.fromhex(CONSTANT_PART + linetwo + switchon + f"{twoon:02X}")

    "关闭第二路"
    twooff = int(CONSTANT_PART, 16) + int(linetwo, 16) + int(switchoff, 16)
    two_off = bytes.fromhex(CONSTANT_PART + linetwo + switchoff + f"{twooff:02X}")

    "打开所有路"
    allon = int(CONSTANT_PART, 16) + int(lineall, 16) + int(switchon, 16)
    all_on = bytes.fromhex(CONSTANT_PART + lineall + switchon + f"{allon:02X}")

    "关闭所有路"
    alloff = int(CONSTANT_PART, 16) + int(lineall, 16) + int(switchoff, 16)
    all_off = bytes.fromhex(CONSTANT_PART + lineall + switchoff + f"{alloff:02X}")

    "查询第一路状态"
    checkone = int(CONSTANT_PART, 16) + int(lineone, 16) + int(switchcheck, 16)
    check_one = bytes.fromhex(CONSTANT_PART + lineone + switchcheck + f"{checkone:02X}")

    "查询第二路状态"
    checktwo = int(CONSTANT_PART, 16) + int(linetwo, 16) + int(switchcheck, 16)
    check_two = bytes.fromhex(CONSTANT_PART + linetwo + switchcheck + f"{checktwo:02X}")

    "查询所有开关状态"
    checkall = int(CONSTANT_PART, 16) + int(lineall, 16) + int(switchcheck, 16)
    check_all = bytes.fromhex(CONSTANT_PART + lineall + switchcheck + f"{checkall:02X}")

class Relay(_RelayChannel):

    def alloff(self):  # 关闭所有开关
        self.write_data(_RelayChannel.all_off)

    def poweron(self):  # 打开继电器
        self.write_data(_RelayChannel.one_on)

    def poweroff(self):  # 关闭继电器
        self.write_data(_RelayChannel.one_off)

    def usbon(self):  # 打开usb
        self.write_data(_RelayChannel.two_on)

    def usboff(self):  # 关闭usb
        self.write_data(_RelayChannel.two_off)

    def allon(self):  # 打开所有开关
        self.write_data(_RelayChannel.all_on)

    def querypower(self):  # 查询供电状态
        self.write_data(_RelayChannel.check_one)
        pass

    def queryusb(self):  # 查询usb状态
        self.serial.write(_RelayChannel.check_two)
        pass

    def queryall(self):  # 查询所有状态
        self.serial.write(_RelayChannel.check_all)
        pass


class FuncRelayChannel(_RelayChannel):
    """功能通道映射关系
    """
    """查询是否存在DSTUR-T20双通道USB继电器
    """
    ports = serial.tools.list_ports.comports()

    def __init__(self, port: str = None, baudrate: int = 115200, bytesize: int = 8, timeout: float = 0.1):
        """
        初始化串口通信对象。

        :param port: 串口名称,如 'COM3' 或 '/dev/ttyUSB0'
        :param baudrate: 波特率,默认为 115200
        :param bytesize: 数据位,默认为 8 (常用值:7, 8)
        :param timeout: 串口超时时间,默认为 0.1 秒
        """
        self.port = port
        self.baudrate = baudrate
        self.bytesize = bytesize
        self.timeout = timeout
        self.serial_ports = []  # 串口列表
        self.adb_device_list = []  # adb设备列表
        self.serial_adb = {}  # 手机与串口绑定关系列表
        self._initialize_ports()  # 初始化并打开所有串口
        self.inquiry_serialPort()  # 识别是否为指定串口
        self.inquiry_adb()  # 识别adb设备
        self.close_and_reopen_ports()  # 绑定设备
        # self.check_power()

    def _initialize_ports(self):
        """
        等待测试模式(测试结束和测试前应该进入的模式)
        初始化所有识别到的串口并打开。
        """

        for port_info in FuncRelayChannel.ports:
            try:
                serial_connection = serial.Serial(port_info.device, self.baudrate, bytesize=self.bytesize,
                                                  timeout=self.timeout)
                serial_connection.write(_RelayChannel.all_on)
                # print(f"串口 {port_info.device} 已初始化并打开。")
            except serial.SerialException as e:
                print(f"串口初始化失败{e}")

    def inquiry_serialPort(self):
        """
        识别是否为指定串口设备
        """

        # 存储串口列表
        self.serial_ports = []
        # 指定厂商ID
        vid = 0x0588
        # 指定设备ID
        pid = 0x6470
        for port_info in serial.tools.list_ports.comports():

            # 检查设备是否匹配指定的VID和PID
            if hasattr(port_info, 'vid') and hasattr(port_info, 'pid'):
                if port_info.vid == vid and port_info.pid == pid:
                    self.serial_ports.append((port_info.device))
                    print(f"当前指定的串口设备: {port_info.device},VID={hex(port_info.vid)}, PID={hex(port_info.pid)}")
                    print()
                else:
                    return False
                    # print("暂未发现其余指定串口设备。")

    def inquiry_adb(self):
        """
        识别所有的adb设备
        """

        time.sleep(2)
        # 执行adb devices命令并获取输出
        self.adb_output = os.popen('adb devices').read()

        # 存储安卓设备名称(SN号码)
        self.adb_device_list = [line.split('\t')[0] for line in self.adb_output.splitlines()[1:] if 'device' in line]
        # print(self.adb_device_list)
        return set(self.adb_device_list) if self.adb_device_list else None

    def close_and_reopen_ports(self):
        """
        轮询串口
        关闭一个串口,检查ADB设备,然后重新打开所有串口。(即绑定串口与设备)
        """

        for port in self.serial_ports:
            try:
                adb_list = set(self.adb_device_list)
                # print(f"正在关闭串口: {port}")
                serial_connection = serial.Serial(port, self.baudrate, timeout=self.timeout)
                serial_connection.write(_RelayChannel.two_off)
                serial_connection.close()
                # print(f"串口 {port} 已关闭")
                self.inquiry_adb()
                inquiry_adbs = self.inquiry_adb()

                if inquiry_adbs is not None:
                    current_adb_devices = set(inquiry_adbs)  # 更新ADB设备列表
                    missing_adb = list(adb_list - current_adb_devices)
                    if len(missing_adb) > 1:
                        print(f"串口 {port} 匹配到多个手机: {missing_adb}")

                    elif len(missing_adb) == 0:
                        print(f"串口 {port} 未匹配到手机设备")

                    else:
                        print(f"串口 {port} 匹配到单个手机: {missing_adb}")
                        phone_sn = missing_adb.pop()
                        self.serial_adb.update({port: phone_sn})
                else:
                    print("inquiry_adbs 返回了 None,请查看是否已连接adb设备")

                print(f'继电器与手机绑定关系:{self.serial_adb}')
                # 重新打开所有串口
                print("正在重新打开所有通信串口...")
                self.close_all()
                self._initialize_ports()
                time.sleep(2)
                self.inquiry_serialPort()
                self.inquiry_adb()
                print("所有串口已重新打开。")

            except serial.SerialException as e:
                print(f"{port}: {e}")
                print()

    def close_all(self):
        """
        关闭所有已打开的通信串口。
        """

        for port in self.serial_ports:
            try:
                serial_connection = serial.Serial(port, self.baudrate, bytesize=self.bytesize,
                                                  timeout=self.timeout)
                serial_connection.write(_RelayChannel.two_off)
                # print(f"串口 {port} 已关闭。")
            except serial.SerialException as e:
                print(f"无法关闭串口 {port}: {e}")

    def alloff(self):  # 关闭所有开关
        self.serial.write(_RelayChannel.all_off)

    def poweron(self):  # 打开继电器
        self.serial.write(_RelayChannel.one_on)

    def poweroff(self):  # 关闭继电器
        self.serial.write(_RelayChannel.one_off)

    def usbon(self):  # 打开usb
        self.serial.write(_RelayChannel.two_on)

    def usboff(self):  # 关闭usb
        self.serial.write(_RelayChannel.two_off)

    def allon(self):  # 打开所有开关
        self.serial.write(_RelayChannel.all_on)

    def querypower(self):  # 查询供电状态
        self.serial.write(_RelayChannel.check_one)
        pass

    def queryusb(self):  # 查询usb状态
        self.serial.write(_RelayChannel.check_two)
        pass

    def queryall(self):  # 查询所有状态
        self.serial.write(_RelayChannel.check_all)
        pass


class BusinessFunctions():
    """
    具体业务功能映射关系
    """
    excluded_port = 'COM5'  # 指定独活串口

    # 测试串口独活
    def liveAlone(self):
        # 列出所有可用的串口
        ports = FuncRelayChannel.ports
        self.channel = FuncRelayChannel()
        serial.Serial
        time.sleep(1)
        # 遍历所有串口
        for port, desc, hwid in ports:
            if port == self.excluded_port:
                # 如果是指定的排除串口,则跳过
                continue
            try:
                # 尝试打开串口
                with serial.Serial(port, 115200, timeout=1) as ser:
                    # 写入数据
                    self.channel.serial = ser
                    self.channel.usbon()
                    time.sleep(0.1)
                    self.channel.usboff()
                    # print(f'Written to {port}')
            except serial.SerialException as e:
                # 处理串口错误,例如串口被占用或不存在
                print(f'Error writing to {port}: {e},处理串口错误,可能该串口不属于指定厂商或串口被占用或不存在')

    def restoreInitialization(self):  # 恢复初始化
        self.channel._initialize_ports()


if __name__ == '__main__':
    business = BusinessFunctions()
    business.liveAlone()  # 独活
    time.sleep(2)
    business.restoreInitialization()  # 恢复初始化


  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值