#本例程序演示如何使用Python控制Modbus协议通过UDP/IP或者串口协议发送数据到Modbus RTU设备。
#可通过修改protocol = 'serial'  选择对应的通信模式
#可设置电压电流值并返回一次电压和电流值
#需要安装pymodbus库，可以使用pip进行安装：pip install pymodbus
#运行前请先安装相关依赖库

    
import time
import struct
from pymodbus.client import ModbusSerialClient, ModbusTcpClient
from pymodbus.exceptions import ModbusIOException

# 配置串口参数
serial_port = 'com3'  # 根据实际情况修改串口号
serial_baudrate = 9600
serial_bytesize = 8
serial_parity = 'N'
serial_stopbits = 1
serial_timeout = 1

# 配置TCP参数
tcp_host = '192.168.1.100'  # 根据实际情况修改IP地址
tcp_port = 502              # Modbus TCP默认端口

# 选择协议类型：'serial' 或 'tcp'
protocol = 'serial'  # 可以更改为 'serial' 或 'tcp'

def float_to_registers(value):
    """
    将浮点数转换为两个16位寄存器值。
    
    :param value: 浮点数值
    :return: 高16位寄存器值, 低16位寄存器值
    """
    # 将浮点数转换为32位单精度浮点数的字节表示
    packed = struct.pack('>f', value)
    # 将字节表示拆分为两个16位寄存器值
    high, low = struct.unpack('>HH', packed)
    return high, low

def registers_to_float(high, low):
    """
    将两个16位寄存器值重新组合成32位浮点数。
    
    :param high: 高16位寄存器值
    :param low: 低16位寄存器值
    :return: 浮点数值
    """
    # 将两个16位寄存器值重新组合成32位浮点数的字节表示
    packed = struct.pack('>HH', high, low)
    # 将字节表示转换为浮点数
    value = struct.unpack('>f', packed)[0]
    return value

def create_client(protocol):
    """
    根据协议类型创建Modbus客户端。
    
    :param protocol: 'serial' 或 'tcp'
    :return: Modbus客户端实例
    """
    if protocol == 'serial':
        return ModbusSerialClient(
            port=serial_port,
            baudrate=serial_baudrate,
            bytesize=serial_bytesize,
            parity=serial_parity,
            stopbits=serial_stopbits,
            timeout=serial_timeout
        )
    elif protocol == 'tcp':
        return ModbusTcpClient(
            host=tcp_host,
            port=tcp_port
        )
    else:
        raise ValueError("不支持的协议类型")

def main():
    try:
        # 创建Modbus客户端
        client = create_client(protocol)

        # 连接客户端
        if not client.connect():
            raise Exception("无法连接到设备")

        try:
            # 设置电压值 (5V)
            voltage_register = 0x0208  # 电压值寄存器地址
            voltage_value = 2.0        # 5V
            voltage_high, voltage_low = float_to_registers(voltage_value)
            response = client.write_registers(address=voltage_register, values=[voltage_high, voltage_low], slave=1)
            if isinstance(response, ModbusIOException):
                print("设置电压值失败: ", response)
            else:
                print("设置电压值成功")

            # 设置电流值 (5A)
            current_register = 0x020A  # 电流值寄存器地址
            current_value = 5.0        # 5A
            current_high, current_low = float_to_registers(current_value)
            response = client.write_registers(address=current_register, values=[current_high, current_low], slave=1)
            if isinstance(response, ModbusIOException):
                print("设置电流值失败: ", response)
            else:
                print("设置电流值成功")

            # 启动输出
            output_register = 0x0200  # 输出控制寄存器地址
            output_value = 1          # 1表示启动输出
            response = client.write_register(address=output_register, value=output_value, slave=1)
            if isinstance(response, ModbusIOException):
                print("启动输出失败: ", response)
            else:
                print("启动输出成功")

            # 等待一段时间
            time.sleep(2)

            # 读取当前电压值
            read_voltage_register = 0x0202  # 读取电压值寄存器地址
            response = client.read_holding_registers(address=read_voltage_register, count=2, slave=1)
            if isinstance(response, ModbusIOException):
                print("读取电压值失败: ", response)
            else:
                voltage_high, voltage_low = response.registers
                voltage_value = registers_to_float(voltage_high, voltage_low)
                print(f"当前电压值: {voltage_value} V")

            # 读取当前电流值
            read_current_register = 0x0204  # 读取电流值寄存器地址
            response = client.read_holding_registers(address=read_current_register, count=2, slave=1)
            if isinstance(response, ModbusIOException):
                print("读取电流值失败: ", response)
            else:
                current_high, current_low = response.registers
                current_value = registers_to_float(current_high, current_low)
                print(f"当前电流值: {current_value} A")

        except Exception as e:
            print(f"发生错误: {e}")

    finally:
        # 断开连接
        client.close()

if __name__ == "__main__":
    main()