715 lines
26 KiB
Python
715 lines
26 KiB
Python
|
|
#!/usr/bin/env python
|
|||
|
|
# -*- coding: utf-8 -*-
|
|||
|
|
|
|||
|
|
"""
|
|||
|
|
VWED Modbus TCP模块
|
|||
|
|
提供Modbus TCP通信相关的内置函数
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
from utils.logger import get_logger
|
|||
|
|
from pymodbus.client.tcp import ModbusTcpClient
|
|||
|
|
|
|||
|
|
logger = get_logger("services.modbus_module")
|
|||
|
|
|
|||
|
|
|
|||
|
|
class VWEDModbusModule:
|
|||
|
|
"""VWED Modbus TCP模块"""
|
|||
|
|
|
|||
|
|
def __init__(self, script_id: str):
|
|||
|
|
self.script_id = script_id
|
|||
|
|
self._modbus_instances = {} # 存储配置的Modbus实例
|
|||
|
|
logger.debug(f"初始化 VWEDModbusModule, script_id: {script_id}")
|
|||
|
|
|
|||
|
|
def _create_client(self, ip: str, port: int, timeout: float = 3.0) -> ModbusTcpClient:
|
|||
|
|
"""创建Modbus TCP客户端"""
|
|||
|
|
return ModbusTcpClient(host=ip, port=port, timeout=timeout)
|
|||
|
|
|
|||
|
|
def _get_modbus_instance(self, name: str):
|
|||
|
|
"""获取配置的Modbus实例"""
|
|||
|
|
if name not in self._modbus_instances:
|
|||
|
|
raise Exception(f"Modbus实例 '{name}' 未配置")
|
|||
|
|
return self._modbus_instances[name]
|
|||
|
|
|
|||
|
|
def configure_modbus_instance(self, name: str, ip: str, port: int, slave_id: int, type_str: str = '4x'):
|
|||
|
|
"""配置Modbus实例"""
|
|||
|
|
self._modbus_instances[name] = {
|
|||
|
|
'ip': ip,
|
|||
|
|
'port': port,
|
|||
|
|
'slave_id': slave_id,
|
|||
|
|
'type_str': type_str
|
|||
|
|
}
|
|||
|
|
logger.debug(f"配置Modbus实例: {name} -> IP={ip}, Port={port}, SlaveID={slave_id}, Type={type_str}")
|
|||
|
|
|
|||
|
|
def read_coil_status(self, ip: str, port: int, slave_id: int, offset: int) -> bool | None:
|
|||
|
|
"""
|
|||
|
|
Modbus 读取线圈量,功能码 01
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
ip: 从机 IP
|
|||
|
|
port: 从机端口
|
|||
|
|
slave_id: 从机 slave ID
|
|||
|
|
offset: Modbus 地址
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
bool | None: 读取成功返回布尔值,失败返回None
|
|||
|
|
"""
|
|||
|
|
client = None
|
|||
|
|
try:
|
|||
|
|
client = self._create_client(ip, port)
|
|||
|
|
client.connect()
|
|||
|
|
|
|||
|
|
result = client.read_coils(offset, count=1, slave=slave_id)
|
|||
|
|
if result.isError():
|
|||
|
|
logger.error(f"读取线圈状态失败: {result}")
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
logger.debug(f"读取线圈状态成功: IP={ip}, Port={port}, SlaveID={slave_id}, Offset={offset}, Value={result.bits[0]}")
|
|||
|
|
return result.bits[0]
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"读取线圈状态异常: {e}")
|
|||
|
|
return None
|
|||
|
|
finally:
|
|||
|
|
if client:
|
|||
|
|
client.close()
|
|||
|
|
|
|||
|
|
def read_input_status(self, ip: str, port: int, slave_id: int, offset: int) -> bool | None:
|
|||
|
|
"""
|
|||
|
|
Modbus 读取离散输入,功能码 02
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
ip: 从机 IP
|
|||
|
|
port: 从机端口
|
|||
|
|
slave_id: 从机 slave ID
|
|||
|
|
offset: Modbus 地址
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
bool | None: 读取成功返回布尔值,失败返回None
|
|||
|
|
"""
|
|||
|
|
client = None
|
|||
|
|
try:
|
|||
|
|
client = self._create_client(ip, port)
|
|||
|
|
client.connect()
|
|||
|
|
|
|||
|
|
result = client.read_discrete_inputs(offset, count=1, slave=slave_id)
|
|||
|
|
if result.isError():
|
|||
|
|
logger.error(f"读取离散输入失败: {result}")
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
logger.debug(f"读取离散输入成功: IP={ip}, Port={port}, SlaveID={slave_id}, Offset={offset}, Value={result.bits[0]}")
|
|||
|
|
return result.bits[0]
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"读取离散输入异常: {e}")
|
|||
|
|
return None
|
|||
|
|
finally:
|
|||
|
|
if client:
|
|||
|
|
client.close()
|
|||
|
|
|
|||
|
|
def read_holding_register(self, ip: str, port: int, slave_id: int, offset: int, data_type: int) -> int | None:
|
|||
|
|
"""
|
|||
|
|
Modbus 读取保持寄存器,功能码 03
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
ip: 从机 IP
|
|||
|
|
port: 从机端口
|
|||
|
|
slave_id: 从机 slave ID
|
|||
|
|
offset: Modbus 地址
|
|||
|
|
data_type: 数据类型,2:无符号整形,3:有符号整形
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
int | None: 读取成功返回整数值,失败返回None
|
|||
|
|
"""
|
|||
|
|
client = None
|
|||
|
|
try:
|
|||
|
|
client = self._create_client(ip, port)
|
|||
|
|
client.connect()
|
|||
|
|
|
|||
|
|
result = client.read_holding_registers(offset, count=1, slave=slave_id)
|
|||
|
|
if result.isError():
|
|||
|
|
logger.error(f"读取保持寄存器失败: {result}")
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
value = result.registers[0]
|
|||
|
|
# 根据数据类型进行转换
|
|||
|
|
if data_type == 3: # 有符号整形
|
|||
|
|
if value > 32767:
|
|||
|
|
value = value - 65536
|
|||
|
|
|
|||
|
|
logger.debug(f"读取保持寄存器成功: IP={ip}, Port={port}, SlaveID={slave_id}, Offset={offset}, Value={value}")
|
|||
|
|
return value
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"读取保持寄存器异常: {e}")
|
|||
|
|
return None
|
|||
|
|
finally:
|
|||
|
|
if client:
|
|||
|
|
client.close()
|
|||
|
|
|
|||
|
|
def read_input_register(self, ip: str, port: int, slave_id: int, offset: int, data_type: int) -> int | None:
|
|||
|
|
"""
|
|||
|
|
Modbus 读取输入寄存器,功能码 04
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
ip: 从机 IP
|
|||
|
|
port: 从机端口
|
|||
|
|
slave_id: 从机 slave ID
|
|||
|
|
offset: Modbus 地址
|
|||
|
|
data_type: 数据类型,2:无符号整形,3:有符号整形
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
int | None: 读取成功返回整数值,失败返回None
|
|||
|
|
"""
|
|||
|
|
client = None
|
|||
|
|
try:
|
|||
|
|
client = self._create_client(ip, port)
|
|||
|
|
client.connect()
|
|||
|
|
|
|||
|
|
result = client.read_input_registers(offset, count=1, slave=slave_id)
|
|||
|
|
if result.isError():
|
|||
|
|
logger.error(f"读取输入寄存器失败: {result}")
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
value = result.registers[0]
|
|||
|
|
# 根据数据类型进行转换
|
|||
|
|
if data_type == 3: # 有符号整形
|
|||
|
|
if value > 32767:
|
|||
|
|
value = value - 65536
|
|||
|
|
|
|||
|
|
logger.debug(f"读取输入寄存器成功: IP={ip}, Port={port}, SlaveID={slave_id}, Offset={offset}, Value={value}")
|
|||
|
|
return value
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"读取输入寄存器异常: {e}")
|
|||
|
|
return None
|
|||
|
|
finally:
|
|||
|
|
if client:
|
|||
|
|
client.close()
|
|||
|
|
|
|||
|
|
def batch_read_coil_status(self, ip: str, port: int, slave_id: int, offset: int, length: int) -> list[bool] | None:
|
|||
|
|
"""
|
|||
|
|
Modbus 批量读取线圈量,功能码 01
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
ip: 从机 IP
|
|||
|
|
port: 从机端口
|
|||
|
|
slave_id: 从机 slave ID
|
|||
|
|
offset: Modbus 首地址
|
|||
|
|
length: Modbus 地址数量
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
list[bool] | None: 读取成功返回布尔值列表,失败返回None
|
|||
|
|
"""
|
|||
|
|
client = None
|
|||
|
|
try:
|
|||
|
|
client = self._create_client(ip, port)
|
|||
|
|
client.connect()
|
|||
|
|
|
|||
|
|
result = client.read_coils(offset, count=length, slave=slave_id)
|
|||
|
|
if result.isError():
|
|||
|
|
logger.error(f"批量读取线圈状态失败: {result}")
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
values = result.bits[:length] # 只返回请求的数量
|
|||
|
|
logger.debug(f"批量读取线圈状态成功: IP={ip}, Port={port}, SlaveID={slave_id}, Offset={offset}, Length={length}")
|
|||
|
|
return values
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"批量读取线圈状态异常: {e}")
|
|||
|
|
return None
|
|||
|
|
finally:
|
|||
|
|
if client:
|
|||
|
|
client.close()
|
|||
|
|
|
|||
|
|
def batch_read_input_status(self, ip: str, port: int, slave_id: int, offset: int, length: int) -> list[bool] | None:
|
|||
|
|
"""
|
|||
|
|
Modbus 批量读取离散输入,功能码 02
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
ip: 从机 IP
|
|||
|
|
port: 从机端口
|
|||
|
|
slave_id: 从机 slave ID
|
|||
|
|
offset: Modbus 首地址
|
|||
|
|
length: Modbus 地址数量
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
list[bool] | None: 读取成功返回布尔值列表,失败返回None
|
|||
|
|
"""
|
|||
|
|
client = None
|
|||
|
|
try:
|
|||
|
|
client = self._create_client(ip, port)
|
|||
|
|
client.connect()
|
|||
|
|
|
|||
|
|
result = client.read_discrete_inputs(offset, count=length, slave=slave_id)
|
|||
|
|
if result.isError():
|
|||
|
|
logger.error(f"批量读取离散输入失败: {result}")
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
values = result.bits[:length] # 只返回请求的数量
|
|||
|
|
logger.debug(f"批量读取离散输入成功: IP={ip}, Port={port}, SlaveID={slave_id}, Offset={offset}, Length={length}")
|
|||
|
|
return values
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"批量读取离散输入异常: {e}")
|
|||
|
|
return None
|
|||
|
|
finally:
|
|||
|
|
if client:
|
|||
|
|
client.close()
|
|||
|
|
|
|||
|
|
def batch_read_holding_registers(self, ip: str, port: int, slave_id: int, offset: int, length: int) -> list[int] | None:
|
|||
|
|
"""
|
|||
|
|
Modbus 批量读取保持寄存器,功能码 03
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
ip: 从机 IP
|
|||
|
|
port: 从机端口
|
|||
|
|
slave_id: 从机 slave ID
|
|||
|
|
offset: Modbus 首地址
|
|||
|
|
length: Modbus 地址数量
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
list[int] | None: 读取成功返回整数值列表,失败返回None
|
|||
|
|
"""
|
|||
|
|
client = None
|
|||
|
|
try:
|
|||
|
|
client = self._create_client(ip, port)
|
|||
|
|
client.connect()
|
|||
|
|
|
|||
|
|
result = client.read_holding_registers(offset, count=length, slave=slave_id)
|
|||
|
|
if result.isError():
|
|||
|
|
logger.error(f"批量读取保持寄存器失败: {result}")
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
values = list(result.registers)
|
|||
|
|
logger.debug(f"批量读取保持寄存器成功: IP={ip}, Port={port}, SlaveID={slave_id}, Offset={offset}, Length={length}")
|
|||
|
|
return values
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"批量读取保持寄存器异常: {e}")
|
|||
|
|
return None
|
|||
|
|
finally:
|
|||
|
|
if client:
|
|||
|
|
client.close()
|
|||
|
|
|
|||
|
|
def batch_read_input_registers(self, ip: str, port: int, slave_id: int, offset: int, length: int) -> list[int] | None:
|
|||
|
|
"""
|
|||
|
|
Modbus 批量读取输入寄存器,功能码 04
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
ip: 从机 IP
|
|||
|
|
port: 从机端口
|
|||
|
|
slave_id: 从机 slave ID
|
|||
|
|
offset: Modbus 首地址
|
|||
|
|
length: Modbus 地址数量
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
list[int] | None: 读取成功返回整数值列表,失败返回None
|
|||
|
|
"""
|
|||
|
|
client = None
|
|||
|
|
try:
|
|||
|
|
client = self._create_client(ip, port)
|
|||
|
|
client.connect()
|
|||
|
|
|
|||
|
|
result = client.read_input_registers(offset, count=length, slave=slave_id)
|
|||
|
|
if result.isError():
|
|||
|
|
logger.error(f"批量读取输入寄存器失败: {result}")
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
values = list(result.registers)
|
|||
|
|
logger.debug(f"批量读取输入寄存器成功: IP={ip}, Port={port}, SlaveID={slave_id}, Offset={offset}, Length={length}")
|
|||
|
|
return values
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"批量读取输入寄存器异常: {e}")
|
|||
|
|
return None
|
|||
|
|
finally:
|
|||
|
|
if client:
|
|||
|
|
client.close()
|
|||
|
|
|
|||
|
|
def write_coil_status(self, ip: str, port: int, slave_id: int, offset: int, value: bool) -> bool:
|
|||
|
|
"""
|
|||
|
|
Modbus 写入线圈量,功能码 05
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
ip: 从机 IP
|
|||
|
|
port: 从机端口
|
|||
|
|
slave_id: 从机 slave ID
|
|||
|
|
offset: Modbus 地址
|
|||
|
|
value: 写入的值
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
bool: 写入成功返回True,失败返回False
|
|||
|
|
"""
|
|||
|
|
client = None
|
|||
|
|
try:
|
|||
|
|
client = self._create_client(ip, port)
|
|||
|
|
client.connect()
|
|||
|
|
|
|||
|
|
result = client.write_coil(offset, value, slave=slave_id)
|
|||
|
|
if result.isError():
|
|||
|
|
logger.error(f"写入线圈状态失败: {result}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
logger.debug(f"写入线圈状态成功: IP={ip}, Port={port}, SlaveID={slave_id}, Offset={offset}, Value={value}")
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"写入线圈状态异常: {e}")
|
|||
|
|
return False
|
|||
|
|
finally:
|
|||
|
|
if client:
|
|||
|
|
client.close()
|
|||
|
|
|
|||
|
|
def write_holding_register(self, ip: str, port: int, slave_id: int, offset: int, data_type: int, value: int) -> bool:
|
|||
|
|
"""
|
|||
|
|
Modbus 写入保持寄存器,功能码 06
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
ip: 从机 IP
|
|||
|
|
port: 从机端口
|
|||
|
|
slave_id: 从机 slave ID
|
|||
|
|
offset: Modbus 地址
|
|||
|
|
data_type: 数据类型,2:无符号整形,3:有符号整形
|
|||
|
|
value: 写入的值
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
bool: 写入成功返回True,失败返回False
|
|||
|
|
"""
|
|||
|
|
client = None
|
|||
|
|
try:
|
|||
|
|
client = self._create_client(ip, port)
|
|||
|
|
client.connect()
|
|||
|
|
|
|||
|
|
# 根据数据类型进行转换
|
|||
|
|
write_value = value
|
|||
|
|
if data_type == 3 and value < 0: # 有符号整形且为负数
|
|||
|
|
write_value = value + 65536
|
|||
|
|
|
|||
|
|
result = client.write_register(offset, write_value, slave=slave_id)
|
|||
|
|
if result.isError():
|
|||
|
|
logger.error(f"写入保持寄存器失败: {result}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
logger.debug(f"写入保持寄存器成功: IP={ip}, Port={port}, SlaveID={slave_id}, Offset={offset}, Value={value}")
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"写入保持寄存器异常: {e}")
|
|||
|
|
return False
|
|||
|
|
finally:
|
|||
|
|
if client:
|
|||
|
|
client.close()
|
|||
|
|
|
|||
|
|
def batch_write_coil_status(self, ip: str, port: int, slave_id: int, offset: int, values: list[bool]) -> bool:
|
|||
|
|
"""
|
|||
|
|
Modbus 批量写入线圈量,功能码 0f
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
ip: 从机 IP
|
|||
|
|
port: 从机端口
|
|||
|
|
slave_id: 从机 slave ID
|
|||
|
|
offset: Modbus 地址
|
|||
|
|
values: 多个需写入的值
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
bool: 写入成功返回True,失败返回False
|
|||
|
|
"""
|
|||
|
|
client = None
|
|||
|
|
try:
|
|||
|
|
client = self._create_client(ip, port)
|
|||
|
|
client.connect()
|
|||
|
|
|
|||
|
|
result = client.write_coils(offset, values, slave=slave_id)
|
|||
|
|
if result.isError():
|
|||
|
|
logger.error(f"批量写入线圈状态失败: {result}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
logger.debug(f"批量写入线圈状态成功: IP={ip}, Port={port}, SlaveID={slave_id}, Offset={offset}, Count={len(values)}")
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"批量写入线圈状态异常: {e}")
|
|||
|
|
return False
|
|||
|
|
finally:
|
|||
|
|
if client:
|
|||
|
|
client.close()
|
|||
|
|
|
|||
|
|
def batch_write_holding_register(self, ip: str, port: int, slave_id: int, offset: int, values: list[int]) -> bool:
|
|||
|
|
"""
|
|||
|
|
Modbus 批量写入保持寄存器,功能码 10
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
ip: 从机 IP
|
|||
|
|
port: 从机端口
|
|||
|
|
slave_id: 从机 slave ID
|
|||
|
|
offset: Modbus 地址
|
|||
|
|
values: 多个需写入的值
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
bool: 写入成功返回True,失败返回False
|
|||
|
|
"""
|
|||
|
|
client = None
|
|||
|
|
try:
|
|||
|
|
client = self._create_client(ip, port)
|
|||
|
|
client.connect()
|
|||
|
|
|
|||
|
|
result = client.write_registers(offset, values, slave=slave_id)
|
|||
|
|
if result.isError():
|
|||
|
|
logger.error(f"批量写入保持寄存器失败: {result}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
logger.debug(f"批量写入保持寄存器成功: IP={ip}, Port={port}, SlaveID={slave_id}, Offset={offset}, Count={len(values)}")
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"批量写入保持寄存器异常: {e}")
|
|||
|
|
return False
|
|||
|
|
finally:
|
|||
|
|
if client:
|
|||
|
|
client.close()
|
|||
|
|
|
|||
|
|
def write_single_modbus_value(self, ip: str, port: int, slave_id: int, type_str: str, offset: int, value: int) -> bool:
|
|||
|
|
"""
|
|||
|
|
Modbus 通用写入单个地址
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
ip: 从机 IP
|
|||
|
|
port: 从机端口
|
|||
|
|
slave_id: 从机 slave ID
|
|||
|
|
type_str: 地址类型,可选择'0x','1x','3x','4x'
|
|||
|
|
offset: Modbus 地址
|
|||
|
|
value: 写入的值
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
bool: 写入成功返回True,失败返回False
|
|||
|
|
"""
|
|||
|
|
try:
|
|||
|
|
if type_str == '0x': # 线圈,只支持写入
|
|||
|
|
return self.write_coil_status(ip, port, slave_id, offset, bool(value))
|
|||
|
|
elif type_str == '1x': # 离散输入,不支持写入
|
|||
|
|
logger.error(f"离散输入(1x)不支持写入操作")
|
|||
|
|
return False
|
|||
|
|
elif type_str == '3x': # 输入寄存器,不支持写入
|
|||
|
|
logger.error(f"输入寄存器(3x)不支持写入操作")
|
|||
|
|
return False
|
|||
|
|
elif type_str == '4x': # 保持寄存器
|
|||
|
|
return self.write_holding_register(ip, port, slave_id, offset, 2, value) # 默认无符号
|
|||
|
|
else:
|
|||
|
|
logger.error(f"不支持的地址类型: {type_str}")
|
|||
|
|
return False
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"通用写入单个地址异常: {e}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def write_batch_modbus_value(self, ip: str, port: int, slave_id: int, type_str: str, offset: int, values: list[int]) -> bool:
|
|||
|
|
"""
|
|||
|
|
Modbus 通用写入多个地址
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
ip: 从机 IP
|
|||
|
|
port: 从机端口
|
|||
|
|
slave_id: 从机 slave ID
|
|||
|
|
type_str: 地址类型,可选择'0x','1x','3x','4x'
|
|||
|
|
offset: Modbus 地址
|
|||
|
|
values: 写入的值
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
bool: 写入成功返回True,失败返回False
|
|||
|
|
"""
|
|||
|
|
try:
|
|||
|
|
if type_str == '0x': # 线圈,只支持写入
|
|||
|
|
bool_values = [bool(v) for v in values]
|
|||
|
|
return self.batch_write_coil_status(ip, port, slave_id, offset, bool_values)
|
|||
|
|
elif type_str == '1x': # 离散输入,不支持写入
|
|||
|
|
logger.error(f"离散输入(1x)不支持写入操作")
|
|||
|
|
return False
|
|||
|
|
elif type_str == '3x': # 输入寄存器,不支持写入
|
|||
|
|
logger.error(f"输入寄存器(3x)不支持写入操作")
|
|||
|
|
return False
|
|||
|
|
elif type_str == '4x': # 保持寄存器
|
|||
|
|
return self.batch_write_holding_register(ip, port, slave_id, offset, values)
|
|||
|
|
else:
|
|||
|
|
logger.error(f"不支持的地址类型: {type_str}")
|
|||
|
|
return False
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"通用写入多个地址异常: {e}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def write_single_modbus_value_by_instance_name(self, name: str, offset: int, value: int) -> bool:
|
|||
|
|
"""
|
|||
|
|
通过实例名称写入单个地址
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
name: 已经配置的实例名称
|
|||
|
|
offset: Modbus 地址
|
|||
|
|
value: 写入的值
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
bool: 写入成功返回True,失败返回False
|
|||
|
|
|
|||
|
|
Raises:
|
|||
|
|
Exception: 如果没有配置 modbus 实例,则会抛出初始化异常
|
|||
|
|
"""
|
|||
|
|
try:
|
|||
|
|
instance = self._get_modbus_instance(name)
|
|||
|
|
return self.write_single_modbus_value(
|
|||
|
|
instance['ip'],
|
|||
|
|
instance['port'],
|
|||
|
|
instance['slave_id'],
|
|||
|
|
instance['type_str'],
|
|||
|
|
offset,
|
|||
|
|
value
|
|||
|
|
)
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"通过实例名称写入单个地址失败: {e}")
|
|||
|
|
raise e
|
|||
|
|
|
|||
|
|
def write_batch_modbus_value_by_instance_name(self, name: str, offset: int, values: list[int]) -> bool:
|
|||
|
|
"""
|
|||
|
|
通过实例名称写入多个地址
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
name: 已经配置的实例名称
|
|||
|
|
offset: Modbus 地址
|
|||
|
|
values: 写入的值
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
bool: 写入成功返回True,失败返回False
|
|||
|
|
|
|||
|
|
Raises:
|
|||
|
|
Exception: 如果没有配置 modbus 实例,则会抛出初始化异常
|
|||
|
|
"""
|
|||
|
|
try:
|
|||
|
|
instance = self._get_modbus_instance(name)
|
|||
|
|
return self.write_batch_modbus_value(
|
|||
|
|
instance['ip'],
|
|||
|
|
instance['port'],
|
|||
|
|
instance['slave_id'],
|
|||
|
|
instance['type_str'],
|
|||
|
|
offset,
|
|||
|
|
values
|
|||
|
|
)
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"通过实例名称写入多个地址失败: {e}")
|
|||
|
|
raise e
|
|||
|
|
|
|||
|
|
def read_single_modbus_value(self, ip: str, port: int, slave_id: int, type_str: str, offset: int) -> int | None:
|
|||
|
|
"""
|
|||
|
|
Modbus 通用读取单个地址
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
ip: 从机 IP
|
|||
|
|
port: 从机端口
|
|||
|
|
slave_id: 从机 slave ID
|
|||
|
|
type_str: 地址类型,可选择'0x','1x','3x','4x'
|
|||
|
|
offset: Modbus 地址
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
int | None: 读取成功返回整数值,失败返回None
|
|||
|
|
"""
|
|||
|
|
try:
|
|||
|
|
if type_str == '0x': # 线圈
|
|||
|
|
result = self.read_coil_status(ip, port, slave_id, offset)
|
|||
|
|
return 1 if result else 0 if result is not None else None
|
|||
|
|
elif type_str == '1x': # 离散输入
|
|||
|
|
result = self.read_input_status(ip, port, slave_id, offset)
|
|||
|
|
return 1 if result else 0 if result is not None else None
|
|||
|
|
elif type_str == '3x': # 输入寄存器
|
|||
|
|
return self.read_input_register(ip, port, slave_id, offset, 2) # 默认无符号
|
|||
|
|
elif type_str == '4x': # 保持寄存器
|
|||
|
|
return self.read_holding_register(ip, port, slave_id, offset, 2) # 默认无符号
|
|||
|
|
else:
|
|||
|
|
logger.error(f"不支持的地址类型: {type_str}")
|
|||
|
|
return None
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"通用读取单个地址异常: {e}")
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
def read_batch_modbus_value(self, ip: str, port: int, slave_id: int, type_str: str, offset: int, length: int) -> list[int] | None:
|
|||
|
|
"""
|
|||
|
|
Modbus 通用读取多个地址
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
ip: 从机 IP
|
|||
|
|
port: 从机端口
|
|||
|
|
slave_id: 从机 slave ID
|
|||
|
|
type_str: 地址类型,可选择'0x','1x','3x','4x'
|
|||
|
|
offset: Modbus 地址
|
|||
|
|
length: 读取的数据长度
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
list[int] | None: 读取成功返回整数值列表,失败返回None
|
|||
|
|
"""
|
|||
|
|
try:
|
|||
|
|
if type_str == '0x': # 线圈
|
|||
|
|
result = self.batch_read_coil_status(ip, port, slave_id, offset, length)
|
|||
|
|
return [1 if b else 0 for b in result] if result is not None else None
|
|||
|
|
elif type_str == '1x': # 离散输入
|
|||
|
|
result = self.batch_read_input_status(ip, port, slave_id, offset, length)
|
|||
|
|
return [1 if b else 0 for b in result] if result is not None else None
|
|||
|
|
elif type_str == '3x': # 输入寄存器
|
|||
|
|
return self.batch_read_input_registers(ip, port, slave_id, offset, length)
|
|||
|
|
elif type_str == '4x': # 保持寄存器
|
|||
|
|
return self.batch_read_holding_registers(ip, port, slave_id, offset, length)
|
|||
|
|
else:
|
|||
|
|
logger.error(f"不支持的地址类型: {type_str}")
|
|||
|
|
return None
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"通用读取多个地址异常: {e}")
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
def read_single_modbus_value_by_instance_name(self, name: str, offset: int) -> int | None:
|
|||
|
|
"""
|
|||
|
|
通过实例名称读取单个地址
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
name: 配置的唯一实例名称
|
|||
|
|
offset: Modbus 地址
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
int | None: 读取成功返回整数值,失败返回None
|
|||
|
|
|
|||
|
|
Raises:
|
|||
|
|
Exception: 如果没有配置 modbus 实例,则会抛出初始化异常
|
|||
|
|
"""
|
|||
|
|
try:
|
|||
|
|
instance = self._get_modbus_instance(name)
|
|||
|
|
return self.read_single_modbus_value(
|
|||
|
|
instance['ip'],
|
|||
|
|
instance['port'],
|
|||
|
|
instance['slave_id'],
|
|||
|
|
instance['type_str'],
|
|||
|
|
offset
|
|||
|
|
)
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"通过实例名称读取单个地址失败: {e}")
|
|||
|
|
raise e
|
|||
|
|
|
|||
|
|
def read_batch_modbus_value_by_instance_name(self, name: str, offset: int, length: int) -> list[int] | None:
|
|||
|
|
"""
|
|||
|
|
通过实例名称读取多个地址
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
name: 配置的唯一实例名称
|
|||
|
|
offset: Modbus 地址
|
|||
|
|
length: 读取的数据长度
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
list[int] | None: 读取成功返回整数值列表,失败返回None
|
|||
|
|
|
|||
|
|
Raises:
|
|||
|
|
Exception: 如果没有配置 modbus 实例,则会抛出初始化异常
|
|||
|
|
"""
|
|||
|
|
try:
|
|||
|
|
instance = self._get_modbus_instance(name)
|
|||
|
|
return self.read_batch_modbus_value(
|
|||
|
|
instance['ip'],
|
|||
|
|
instance['port'],
|
|||
|
|
instance['slave_id'],
|
|||
|
|
instance['type_str'],
|
|||
|
|
offset,
|
|||
|
|
length
|
|||
|
|
)
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"通过实例名称读取多个地址失败: {e}")
|
|||
|
|
raise e
|