从 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:
2026-09-01 17:03:26 +08:00
parent abab25326a
commit 5888b4ccce
10 changed files with 507 additions and 374 deletions
+92 -191
View File
@@ -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 客户端 ----------
# ---------- PLCEasy521Modbus 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_max1000)坐标(死区由 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 客户端类(含报文打印) ----------