mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2026-02-05 05:45:10 +00:00
Update workstation code for YB4 0107
This commit is contained in:
@@ -4,8 +4,7 @@ import traceback
|
||||
from typing import Any, Union, List, Dict, Callable, Optional, Tuple
|
||||
from pydantic import BaseModel
|
||||
|
||||
from pymodbus.client import ModbusSerialClient, ModbusTcpClient
|
||||
from pymodbus.framer import FramerType
|
||||
from pymodbus.client.sync import ModbusSerialClient, ModbusTcpClient
|
||||
from typing import TypedDict
|
||||
|
||||
from unilabos.device_comms.modbus_plc.modbus import DeviceType, HoldRegister, Coil, InputRegister, DiscreteInputs, DataType, WorderOrder
|
||||
@@ -403,7 +402,7 @@ class TCPClient(BaseClient):
|
||||
class RTUClient(BaseClient):
|
||||
def __init__(self, port: str, baudrate: int, timeout: int):
|
||||
super().__init__()
|
||||
self._set_client(ModbusSerialClient(framer=FramerType.RTU, port=port, baudrate=baudrate, timeout=timeout))
|
||||
self._set_client(ModbusSerialClient(method='rtu', port=port, baudrate=baudrate, timeout=timeout))
|
||||
self._connect()
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -1,12 +1,26 @@
|
||||
# coding=utf-8
|
||||
from enum import Enum
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Tuple, Union, Optional, TYPE_CHECKING
|
||||
from pymodbus.payload import BinaryPayloadDecoder, BinaryPayloadBuilder
|
||||
from pymodbus.constants import Endian
|
||||
|
||||
from pymodbus.client import ModbusBaseSyncClient
|
||||
from pymodbus.client.mixin import ModbusClientMixin
|
||||
from typing import Tuple, Union, Optional
|
||||
if TYPE_CHECKING:
|
||||
from pymodbus.client.sync import ModbusSerialClient, ModbusTcpClient
|
||||
|
||||
# Define DataType enum for pymodbus 2.5.3 compatibility
|
||||
class DataType(Enum):
|
||||
INT16 = "int16"
|
||||
UINT16 = "uint16"
|
||||
INT32 = "int32"
|
||||
UINT32 = "uint32"
|
||||
INT64 = "int64"
|
||||
UINT64 = "uint64"
|
||||
FLOAT32 = "float32"
|
||||
FLOAT64 = "float64"
|
||||
STRING = "string"
|
||||
BOOL = "bool"
|
||||
|
||||
DataType = ModbusClientMixin.DATATYPE
|
||||
|
||||
class WorderOrder(Enum):
|
||||
BIG = "big"
|
||||
@@ -19,8 +33,96 @@ class DeviceType(Enum):
|
||||
INPUT_REGISTER = 'input_register'
|
||||
|
||||
|
||||
def _convert_from_registers(registers, data_type: DataType, word_order: str = 'big'):
|
||||
"""Convert registers to a value using BinaryPayloadDecoder.
|
||||
|
||||
Args:
|
||||
registers: List of register values
|
||||
data_type: DataType enum specifying the target data type
|
||||
word_order: 'big' or 'little' endian
|
||||
|
||||
Returns:
|
||||
Converted value
|
||||
"""
|
||||
# Determine byte and word order based on word_order parameter
|
||||
if word_order == 'little':
|
||||
byte_order = Endian.Little
|
||||
word_order_enum = Endian.Little
|
||||
else:
|
||||
byte_order = Endian.Big
|
||||
word_order_enum = Endian.Big
|
||||
|
||||
decoder = BinaryPayloadDecoder.fromRegisters(registers, byteorder=byte_order, wordorder=word_order_enum)
|
||||
|
||||
if data_type == DataType.INT16:
|
||||
return decoder.decode_16bit_int()
|
||||
elif data_type == DataType.UINT16:
|
||||
return decoder.decode_16bit_uint()
|
||||
elif data_type == DataType.INT32:
|
||||
return decoder.decode_32bit_int()
|
||||
elif data_type == DataType.UINT32:
|
||||
return decoder.decode_32bit_uint()
|
||||
elif data_type == DataType.INT64:
|
||||
return decoder.decode_64bit_int()
|
||||
elif data_type == DataType.UINT64:
|
||||
return decoder.decode_64bit_uint()
|
||||
elif data_type == DataType.FLOAT32:
|
||||
return decoder.decode_32bit_float()
|
||||
elif data_type == DataType.FLOAT64:
|
||||
return decoder.decode_64bit_float()
|
||||
elif data_type == DataType.STRING:
|
||||
return decoder.decode_string(len(registers) * 2)
|
||||
else:
|
||||
raise ValueError(f"Unsupported data type: {data_type}")
|
||||
|
||||
|
||||
def _convert_to_registers(value, data_type: DataType, word_order: str = 'little'):
|
||||
"""Convert a value to registers using BinaryPayloadBuilder.
|
||||
|
||||
Args:
|
||||
value: Value to convert
|
||||
data_type: DataType enum specifying the source data type
|
||||
word_order: 'big' or 'little' endian
|
||||
|
||||
Returns:
|
||||
List of register values
|
||||
"""
|
||||
# Determine byte and word order based on word_order parameter
|
||||
if word_order == 'little':
|
||||
byte_order = Endian.Little
|
||||
word_order_enum = Endian.Little
|
||||
else:
|
||||
byte_order = Endian.Big
|
||||
word_order_enum = Endian.Big
|
||||
|
||||
builder = BinaryPayloadBuilder(byteorder=byte_order, wordorder=word_order_enum)
|
||||
|
||||
if data_type == DataType.INT16:
|
||||
builder.add_16bit_int(value)
|
||||
elif data_type == DataType.UINT16:
|
||||
builder.add_16bit_uint(value)
|
||||
elif data_type == DataType.INT32:
|
||||
builder.add_32bit_int(value)
|
||||
elif data_type == DataType.UINT32:
|
||||
builder.add_32bit_uint(value)
|
||||
elif data_type == DataType.INT64:
|
||||
builder.add_64bit_int(value)
|
||||
elif data_type == DataType.UINT64:
|
||||
builder.add_64bit_uint(value)
|
||||
elif data_type == DataType.FLOAT32:
|
||||
builder.add_32bit_float(value)
|
||||
elif data_type == DataType.FLOAT64:
|
||||
builder.add_64bit_float(value)
|
||||
elif data_type == DataType.STRING:
|
||||
builder.add_string(value)
|
||||
else:
|
||||
raise ValueError(f"Unsupported data type: {data_type}")
|
||||
|
||||
return builder.to_registers()
|
||||
|
||||
|
||||
class Base(ABC):
|
||||
def __init__(self, client: ModbusBaseSyncClient, name: str, address: int, typ: DeviceType, data_type: DataType):
|
||||
def __init__(self, client, name: str, address: int, typ: DeviceType, data_type):
|
||||
self._address: int = address
|
||||
self._client = client
|
||||
self._name = name
|
||||
@@ -58,7 +160,11 @@ class Coil(Base):
|
||||
count = value,
|
||||
slave = slave)
|
||||
|
||||
return resp.bits, resp.isError()
|
||||
# 检查是否读取出错
|
||||
if resp.isError():
|
||||
return [], True
|
||||
|
||||
return resp.bits, False
|
||||
|
||||
def write(self,value: Union[int, float, bool, str, list[bool], list[int], list[float]], data_type: Optional[DataType ]= None, word_order: WorderOrder = WorderOrder.LITTLE, slave = 1) -> bool:
|
||||
if isinstance(value, list):
|
||||
@@ -91,8 +197,18 @@ class DiscreteInputs(Base):
|
||||
count = value,
|
||||
slave = slave)
|
||||
|
||||
# 检查是否读取出错
|
||||
if resp.isError():
|
||||
# 根据数据类型返回默认值
|
||||
if data_type in [DataType.FLOAT32, DataType.FLOAT64]:
|
||||
return 0.0, True
|
||||
elif data_type == DataType.STRING:
|
||||
return "", True
|
||||
else:
|
||||
return 0, True
|
||||
|
||||
# noinspection PyTypeChecker
|
||||
return self._client.convert_from_registers(resp.registers, data_type, word_order=word_order.value), resp.isError()
|
||||
return _convert_from_registers(resp.registers, data_type, word_order=word_order.value), False
|
||||
|
||||
def write(self,value: Union[int, float, bool, str, list[bool], list[int], list[float]], data_type: Optional[DataType ]= None, word_order: WorderOrder = WorderOrder.LITTLE, slave = 1) -> bool:
|
||||
raise ValueError('discrete inputs only support read')
|
||||
@@ -112,8 +228,19 @@ class HoldRegister(Base):
|
||||
address = self.address,
|
||||
count = value,
|
||||
slave = slave)
|
||||
|
||||
# 检查是否读取出错
|
||||
if resp.isError():
|
||||
# 根据数据类型返回默认值
|
||||
if data_type in [DataType.FLOAT32, DataType.FLOAT64]:
|
||||
return 0.0, True
|
||||
elif data_type == DataType.STRING:
|
||||
return "", True
|
||||
else:
|
||||
return 0, True
|
||||
|
||||
# noinspection PyTypeChecker
|
||||
return self._client.convert_from_registers(resp.registers, data_type, word_order=word_order.value), resp.isError()
|
||||
return _convert_from_registers(resp.registers, data_type, word_order=word_order.value), False
|
||||
|
||||
|
||||
def write(self,value: Union[int, float, bool, str, list[bool], list[int], list[float]], data_type: Optional[DataType ]= None, word_order: WorderOrder = WorderOrder.LITTLE, slave = 1) -> bool:
|
||||
@@ -132,7 +259,7 @@ class HoldRegister(Base):
|
||||
return self._client.write_register(self.address, value, slave= slave).isError()
|
||||
else:
|
||||
# noinspection PyTypeChecker
|
||||
encoder_resp = self._client.convert_to_registers(value, data_type=data_type, word_order=word_order.value)
|
||||
encoder_resp = _convert_to_registers(value, data_type=data_type, word_order=word_order.value)
|
||||
return self._client.write_registers(self.address, encoder_resp, slave=slave).isError()
|
||||
|
||||
|
||||
@@ -153,8 +280,19 @@ class InputRegister(Base):
|
||||
address = self.address,
|
||||
count = value,
|
||||
slave = slave)
|
||||
|
||||
# 检查是否读取出错
|
||||
if resp.isError():
|
||||
# 根据数据类型返回默认值
|
||||
if data_type in [DataType.FLOAT32, DataType.FLOAT64]:
|
||||
return 0.0, True
|
||||
elif data_type == DataType.STRING:
|
||||
return "", True
|
||||
else:
|
||||
return 0, True
|
||||
|
||||
# noinspection PyTypeChecker
|
||||
return self._client.convert_from_registers(resp.registers, data_type, word_order=word_order.value), resp.isError()
|
||||
return _convert_from_registers(resp.registers, data_type, word_order=word_order.value), False
|
||||
|
||||
def write(self,value: Union[int, float, bool, str, list[bool], list[int], list[float]], data_type: Optional[DataType ]= None, word_order: WorderOrder = WorderOrder.LITTLE, slave = 1) -> bool:
|
||||
raise ValueError('input register only support read')
|
||||
|
||||
Reference in New Issue
Block a user