从 MT2-AM8 模拟量模块迁移到 Easy521 PLC(Modbus TCP)
- 读取 D700(阀后流量)、D710(阀后压力)、D720(阀前压力), 读用功能码 03,0~32000 线性映射到各自量程(300 SLM / 400 kPa / 300 kPa) - 输出 D730(电机位置,0~10V),写用功能码 06,行程仍为 0~1000、死区 800 - 复用 MT2 骨架,保留 get_flow/get_pressure/set_motor_position 接口, channel 参数改为直接 Modbus 地址 - config.py 新增 PLC 设置,保留 MT2AM8 设置为死代码(MT2AM8Client 类保留) - 同步更新 README.md 中的硬件接线、配置默认值与模块职责描述
This commit is contained in:
+92
-191
@@ -20,9 +20,10 @@ def _motor_log(msg: str):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
volthege_min = 819 # 模拟量映射最小值(对应 0V/4mA)
|
||||
volthege_max = 4095 # 模拟量映射最大值(对应 10V/20mA)
|
||||
x_max = 1000 # 最大行程
|
||||
volthege_min = 819 # 模拟量映射最小值(对应 0V/4mA)—— MT2 旧用
|
||||
volthege_max = 4095 # 模拟量映射最大值(对应 10V/20mA)—— MT2 旧用
|
||||
x_max = 1000 # 电机最大行程(PLC 沿用 0~1000 坐标,死区由 config 给定)
|
||||
raw_max = 32000 # PLC 模拟量映射最大值(0~32000,对应量程满量程)
|
||||
impulse_max = 163840 # 电机脉冲最大值(对应 x_max)
|
||||
|
||||
|
||||
@@ -42,49 +43,60 @@ def set_motor_limits(volthege_min_val=None, volthege_max_val=None, x_max_val=Non
|
||||
if x_max_val is not None:
|
||||
x_max = x_max_val
|
||||
|
||||
# ---------- 原有的 PLC Modbus TCP 客户端类 ----------
|
||||
# ---------- PLC(Easy521)Modbus TCP 客户端 ----------
|
||||
class Easy521ModbusClient:
|
||||
# 参数来源(GUI 页面1 Modbus TCP 区):
|
||||
# host <- PLC地址 (默认 192.168.1.88)
|
||||
# port <- 端口 (默认 502)
|
||||
# current_p_addr <- 读取压力寄存器地址 (默认 504)
|
||||
def __init__(self, host="192.168.1.88", port=502, slave_id=1, current_p_addr=504):
|
||||
"""
|
||||
信捷 Easy521 PLC 的 Modbus TCP 通讯类。
|
||||
|
||||
- D 寄存器为直接 Modbus 地址:读用功能码 03(保持寄存器),写用功能码 06。
|
||||
- 模拟量映射:0~raw_max(32000)线性对应各量程满量程。
|
||||
- 电机行程仍沿用 0~x_max(1000)坐标(死区由 config 给定),映射到 0~raw_max 输出。
|
||||
|
||||
对外接口沿用 MT2AM8Client 的 get_flow / get_pressure / set_motor_position,
|
||||
只是这里的 ``channel`` 参数是寄存器地址。
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
host="192.168.1.88",
|
||||
port=502,
|
||||
slave_id=1,
|
||||
flow_addr=700,
|
||||
flow_range=300.0,
|
||||
pressure_before_addr=720,
|
||||
pressure_before_range=300.0,
|
||||
pressure_after_addr=710,
|
||||
pressure_after_range=400.0,
|
||||
motor_output_addr=730,
|
||||
):
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.slave_id = slave_id
|
||||
self.flow_addr = int(flow_addr)
|
||||
self.flow_range = float(flow_range)
|
||||
self.pressure_before_addr = int(pressure_before_addr)
|
||||
self.pressure_before_range = float(pressure_before_range)
|
||||
self.pressure_after_addr = int(pressure_after_addr)
|
||||
self.pressure_after_range = float(pressure_after_range)
|
||||
self.motor_output_addr = int(motor_output_addr)
|
||||
self.client = ModbusTcpClient(
|
||||
host=host,
|
||||
port=port,
|
||||
timeout=3,
|
||||
retries=3
|
||||
retries=3,
|
||||
)
|
||||
self.connected = False
|
||||
self.current_p_addr0 = current_p_addr
|
||||
self.current_p_addr = current_p_addr
|
||||
# self.current_p_addr = 18
|
||||
self.target_p_addr = 42
|
||||
self.u_addr = 514
|
||||
# self.u_addr = 40
|
||||
# self.output_postion = 514
|
||||
self.control_flag_addr = 100
|
||||
self.M901_ADDR = 901
|
||||
self.M902_ADDR = 902
|
||||
self.M903_ADDR = 903
|
||||
self.M904_ADDR = 904
|
||||
self.M905_ADDR = 905
|
||||
self.M906_ADDR = 906
|
||||
self.q_addr = 512
|
||||
|
||||
def connect(self):
|
||||
try:
|
||||
connection = self.client.connect()
|
||||
if connection:
|
||||
print(f"成功连接到 {self.host}:{self.port}")
|
||||
conn = self.client.connect()
|
||||
if conn:
|
||||
print(f"成功连接到 PLC {self.host}:{self.port}")
|
||||
self.connected = True
|
||||
else:
|
||||
print(f"无法连接到 {self.host}:{self.port}")
|
||||
print(f"无法连接到 PLC {self.host}:{self.port}")
|
||||
self.connected = False
|
||||
return connection
|
||||
return conn
|
||||
except Exception as e:
|
||||
print(f"连接错误: {e}")
|
||||
self.connected = False
|
||||
@@ -95,181 +107,70 @@ class Easy521ModbusClient:
|
||||
self.connected = False
|
||||
print("连接已关闭")
|
||||
|
||||
def read_float(self, address):
|
||||
def _read_register(self, address):
|
||||
"""读单个 16 位保持寄存器(功能码 03),返回原始整数,失败返回 None。"""
|
||||
try:
|
||||
address = int(address)
|
||||
result = self.client.read_input_registers(
|
||||
address=address,
|
||||
count=2,
|
||||
slave=self.slave_id
|
||||
result = self.client.read_holding_registers(
|
||||
address=int(address),
|
||||
count=1,
|
||||
slave=self.slave_id,
|
||||
)
|
||||
if not result.isError():
|
||||
decoder = BinaryPayloadDecoder.fromRegisters(
|
||||
result.registers,
|
||||
byteorder=Endian.BIG,
|
||||
wordorder=Endian.LITTLE
|
||||
)
|
||||
return decoder.decode_32bit_float()
|
||||
else:
|
||||
print(f"读取寄存器错误: {result}")
|
||||
return None
|
||||
return result.registers[0]
|
||||
print(f"读取寄存器 D{int(address)} 错误: {result}")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"读取浮点数时发生错误: {e}")
|
||||
print(f"读取寄存器 D{int(address)} 异常: {e}")
|
||||
return None
|
||||
|
||||
def write_float(self, address, float_value):
|
||||
def _write_register(self, address, value):
|
||||
"""写单个 16 位保持寄存器(功能码 06)。"""
|
||||
try:
|
||||
address = int(address)
|
||||
builder = BinaryPayloadBuilder(byteorder=Endian.LITTLE, wordorder=Endian.BIG)
|
||||
builder.add_32bit_float(float_value)
|
||||
payload = builder.to_registers()
|
||||
result = self.client.write_registers(
|
||||
address=address,
|
||||
values=payload,
|
||||
slave=self.slave_id
|
||||
result = self.client.write_register(
|
||||
address=int(address),
|
||||
value=int(value),
|
||||
slave=self.slave_id,
|
||||
)
|
||||
return not result.isError()
|
||||
except Exception as e:
|
||||
print(f"写入浮点数时发生错误: {e}")
|
||||
return False
|
||||
|
||||
def write_coil(self, address, value):
|
||||
try:
|
||||
address = int(address)
|
||||
result = self.client.write_coil(address=address, value=value, slave=self.slave_id)
|
||||
return not result.isError()
|
||||
except Exception as e:
|
||||
print(f"写入线圈时发生错误: {e}")
|
||||
return False
|
||||
|
||||
def get_current_p(self):
|
||||
return self.read_float(self.current_p_addr)
|
||||
|
||||
def get_current_p0(self):
|
||||
return self.read_float(self.current_p_addr0)
|
||||
|
||||
def get_target_p(self):
|
||||
return self.read_float(self.target_p_addr)
|
||||
|
||||
def get_current_q(self):
|
||||
return self.read_float(self.q_addr)
|
||||
|
||||
def write_u(self, float_value):
|
||||
try:
|
||||
address = int(self.u_addr)
|
||||
builder = BinaryPayloadBuilder(byteorder=Endian.BIG, wordorder=Endian.LITTLE)
|
||||
builder.add_32bit_float(float_value)
|
||||
payload = builder.to_registers()
|
||||
result = self.client.write_registers(
|
||||
address=address,
|
||||
values=payload,
|
||||
slave=self.slave_id
|
||||
)
|
||||
return not result.isError()
|
||||
except Exception as e:
|
||||
print(f"写入浮点数时发生错误: {e}")
|
||||
return False
|
||||
|
||||
def start_control(self):
|
||||
success = self.write_coil(self.control_flag_addr, True)
|
||||
if success:
|
||||
print("成功写入控制标志位True")
|
||||
else:
|
||||
print("写入控制标志位失败")
|
||||
|
||||
def stop_control(self):
|
||||
success = self.write_coil(self.control_flag_addr, False)
|
||||
if success:
|
||||
print("成功写入控制标志位False")
|
||||
else:
|
||||
print("写入控制标志位失败")
|
||||
|
||||
def read_rtu_flow(self, port='COM3', slave_id=2, baudrate=9600, bytesize=8, parity='N', stopbits=1):
|
||||
client = ModbusSerialClient(
|
||||
port=port,
|
||||
baudrate=baudrate,
|
||||
bytesize=bytesize,
|
||||
parity=parity,
|
||||
stopbits=stopbits,
|
||||
timeout=3
|
||||
)
|
||||
if not client.connect():
|
||||
print(f"无法连接到串口 {port}")
|
||||
return None
|
||||
|
||||
try:
|
||||
result = client.read_holding_registers(address=22, count=2, slave=slave_id)
|
||||
if result.isError():
|
||||
print(f"RTU 读取寄存器错误: {result}")
|
||||
return None
|
||||
decoder = BinaryPayloadDecoder.fromRegisters(
|
||||
result.registers,
|
||||
byteorder=Endian.BIG,
|
||||
wordorder=Endian.BIG
|
||||
)
|
||||
value = decoder.decode_32bit_uint() / 100
|
||||
return value
|
||||
print(f"写入寄存器 D{int(address)} 错误: {result}")
|
||||
return False
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"写入寄存器 D{int(address)} 异常: {e}")
|
||||
return False
|
||||
|
||||
def get_flow(self, address):
|
||||
"""读阀后流量,raw / raw_max * flow_range -> SLM。"""
|
||||
raw = self._read_register(address)
|
||||
if raw is None:
|
||||
return None
|
||||
finally:
|
||||
client.close()
|
||||
return raw / raw_max * self.flow_range
|
||||
|
||||
def start_up(self):
|
||||
if self.write_coil(self.M901_ADDR, True):
|
||||
print("M901 置位 TRUE")
|
||||
time.sleep(1)
|
||||
if self.write_coil(self.M901_ADDR, False):
|
||||
print("M901 复位 FALSE")
|
||||
else:
|
||||
print("警告:M901 复位失败")
|
||||
def get_pressure(self, address):
|
||||
"""读压力,按寄存器地址匹配量程(阀前 300 / 阀后 400)-> kPa。"""
|
||||
address = int(address)
|
||||
if address == self.pressure_before_addr:
|
||||
rng = self.pressure_before_range
|
||||
elif address == self.pressure_after_addr:
|
||||
rng = self.pressure_after_range
|
||||
else:
|
||||
print("警告:M901 置位失败")
|
||||
time.sleep(1)
|
||||
print(f"未知压力寄存器 D{address},按阀后量程兜底")
|
||||
rng = self.pressure_after_range
|
||||
raw = self._read_register(address)
|
||||
if raw is None:
|
||||
return None
|
||||
return raw / raw_max * rng
|
||||
|
||||
if self.write_coil(self.M902_ADDR, True):
|
||||
print("M902 置位 TRUE")
|
||||
time.sleep(1)
|
||||
if self.write_coil(self.M902_ADDR, False):
|
||||
print("M902 复位 FALSE")
|
||||
else:
|
||||
print("警告:M902 复位失败")
|
||||
else:
|
||||
print("警告:M902 置位失败")
|
||||
time.sleep(1)
|
||||
|
||||
if self.write_coil(self.M905_ADDR, True):
|
||||
print("M905 (初始开度) 置位 TRUE")
|
||||
time.sleep(1)
|
||||
if self.write_coil(self.M905_ADDR, False):
|
||||
print("M905 (初始开度) 复位 FALSE")
|
||||
else:
|
||||
print("警告:M905 (初始开度) 复位失败")
|
||||
else:
|
||||
print("警告:M905 (初始开度) 置位失败")
|
||||
time.sleep(1)
|
||||
|
||||
if self.write_coil(self.M903_ADDR, True):
|
||||
print("M903 置位 TRUE(持续)")
|
||||
else:
|
||||
print("警告:M903 置位失败")
|
||||
|
||||
if self.write_coil(self.M904_ADDR, True):
|
||||
print("M904 置位 TRUE(持续)")
|
||||
else:
|
||||
print("警告:M904 置位失败")
|
||||
time.sleep(1)
|
||||
|
||||
if self.write_coil(self.M906_ADDR, True):
|
||||
print("M906 (归零) 置位 TRUE")
|
||||
time.sleep(1)
|
||||
if self.write_coil(self.M906_ADDR, False):
|
||||
print("M906 (归零) 复位 FALSE")
|
||||
else:
|
||||
print("警告:M906 (归零) 复位失败")
|
||||
else:
|
||||
print("警告:M906 (归零) 置位失败")
|
||||
|
||||
print("初始化完成,M903 和 M904 已保持为 TRUE。")
|
||||
def set_motor_position(self, position, channel=None):
|
||||
"""把 0~x_max 的行程映射到 0~raw_max 后写入电机输出寄存器。"""
|
||||
if channel is None:
|
||||
channel = self.motor_output_addr
|
||||
position = float(position)
|
||||
if not 0 <= position <= x_max:
|
||||
print(f"行程需在 0~{x_max:.0f} 之间")
|
||||
return False
|
||||
raw_value = int(position / x_max * raw_max)
|
||||
return self._write_register(channel, raw_value)
|
||||
|
||||
|
||||
# ---------- 新增:独立的电机 Modbus RTU 客户端类(含报文打印) ----------
|
||||
|
||||
Reference in New Issue
Block a user