233 lines
8.1 KiB
Python
233 lines
8.1 KiB
Python
#!/usr/bin/env python
|
||
# -*- coding: utf-8 -*-
|
||
|
||
"""
|
||
VWED.melsec 模块 - Melsec PLC通信相关功能
|
||
"""
|
||
|
||
from typing import Optional, Union
|
||
from utils.logger import get_logger
|
||
import pymcprotocol
|
||
|
||
|
||
|
||
|
||
class VWEDMelsecModule:
|
||
"""VWED.melsec 模块 - Melsec PLC通信相关功能"""
|
||
|
||
def __init__(self, script_id: str):
|
||
self.script_id = script_id
|
||
self.logger = get_logger(f"script.{script_id}.melsec")
|
||
|
||
def read_boolean(self, ip: str, port: int, address: str) -> Optional[bool]:
|
||
"""Melsec 读取 boolean
|
||
|
||
Args:
|
||
ip (str): 从机 IP
|
||
port (int): 从机端口
|
||
address (str): 读取的地址位
|
||
|
||
Returns:
|
||
Optional[bool]: 读取成功的返回值,失败返回 None
|
||
"""
|
||
|
||
try:
|
||
# 创建Melsec连接
|
||
plc = pymcprotocol.Type3E()
|
||
plc.setaccessopt(commtype="binary")
|
||
plc.connect(ip, port)
|
||
|
||
# 读取布尔值
|
||
result = plc.batchread_bitunits(address, 1)
|
||
plc.close()
|
||
|
||
if result and len(result) > 0:
|
||
value = bool(result[0])
|
||
self.logger.info(f"读取Melsec boolean成功: {ip}:{port} 地址: {address} 值: {value}")
|
||
return value
|
||
else:
|
||
self.logger.warning(f"读取Melsec boolean无数据: {ip}:{port} 地址: {address}")
|
||
return None
|
||
|
||
except Exception as e:
|
||
self.logger.error(f"读取Melsec boolean失败: {ip}:{port} 地址: {address} 错误: {e}")
|
||
return None
|
||
|
||
def read_number(self, ip: str, port: int, address: str) -> Optional[float]:
|
||
"""Melsec 读取 number
|
||
|
||
Args:
|
||
ip (str): 从机 IP
|
||
port (int): 从机端口
|
||
address (str): 读取的地址位
|
||
|
||
Returns:
|
||
Optional[float]: 读取成功的返回值,失败返回 None
|
||
"""
|
||
|
||
try:
|
||
# 创建Melsec连接
|
||
plc = pymcprotocol.Type3E()
|
||
plc.setaccessopt(commtype="binary")
|
||
plc.connect(ip, port)
|
||
|
||
# 读取数值(假设为16位整数)
|
||
result = plc.batchread_wordunits(address, 1)
|
||
plc.close()
|
||
|
||
if result and len(result) > 0:
|
||
value = float(result[0])
|
||
self.logger.info(f"读取Melsec number成功: {ip}:{port} 地址: {address} 值: {value}")
|
||
return value
|
||
else:
|
||
self.logger.warning(f"读取Melsec number无数据: {ip}:{port} 地址: {address}")
|
||
return None
|
||
|
||
except Exception as e:
|
||
self.logger.error(f"读取Melsec number失败: {ip}:{port} 地址: {address} 错误: {e}")
|
||
return None
|
||
|
||
def read_string(self, ip: str, port: int, address: str, length: int) -> Optional[str]:
|
||
"""Melsec 读取 string
|
||
|
||
Args:
|
||
ip (str): 从机 IP
|
||
port (int): 从机端口
|
||
address (str): 读取的地址位
|
||
length (int): 读取的长度
|
||
|
||
Returns:
|
||
Optional[str]: 读取成功的返回值,失败返回 None
|
||
"""
|
||
|
||
try:
|
||
# 创建Melsec连接
|
||
plc = pymcprotocol.Type3E()
|
||
plc.setaccessopt(commtype="binary")
|
||
plc.connect(ip, port)
|
||
|
||
# 读取字符串(每个字符占用2字节)
|
||
word_count = (length + 1) // 2 # 计算需要读取的字数
|
||
result = plc.batchread_wordunits(address, word_count)
|
||
plc.close()
|
||
|
||
if result and len(result) > 0:
|
||
# 将字数据转换为字符串
|
||
byte_data = []
|
||
for word in result:
|
||
byte_data.extend([word & 0xFF, (word >> 8) & 0xFF])
|
||
|
||
# 转换为字符串并去除空字符
|
||
value = bytes(byte_data[:length]).decode('ascii', errors='ignore').rstrip('\x00')
|
||
self.logger.info(f"读取Melsec string成功: {ip}:{port} 地址: {address} 长度: {length} 值: {value}")
|
||
return value
|
||
else:
|
||
self.logger.warning(f"读取Melsec string无数据: {ip}:{port} 地址: {address}")
|
||
return None
|
||
|
||
except Exception as e:
|
||
self.logger.error(f"读取Melsec string失败: {ip}:{port} 地址: {address} 错误: {e}")
|
||
return None
|
||
|
||
def write_boolean(self, ip: str, port: int, address: str, value: bool) -> bool:
|
||
"""Melsec 写入 boolean
|
||
|
||
Args:
|
||
ip (str): 从机 IP
|
||
port (int): 从机端口
|
||
address (str): 写入的地址位
|
||
value (bool): 写入的值
|
||
|
||
Returns:
|
||
bool: 成功返回 True,失败返回 False
|
||
"""
|
||
|
||
try:
|
||
# 创建Melsec连接
|
||
plc = pymcprotocol.Type3E()
|
||
plc.setaccessopt(commtype="binary")
|
||
plc.connect(ip, port)
|
||
|
||
# 写入布尔值
|
||
plc.batchwrite_bitunits(address, [1 if value else 0])
|
||
plc.close()
|
||
|
||
self.logger.info(f"写入Melsec boolean成功: {ip}:{port} 地址: {address} 值: {value}")
|
||
return True
|
||
|
||
except Exception as e:
|
||
self.logger.error(f"写入Melsec boolean失败: {ip}:{port} 地址: {address} 值: {value} 错误: {e}")
|
||
return False
|
||
|
||
def write_number(self, ip: str, port: int, address: str, value: float) -> bool:
|
||
"""Melsec 写入 number
|
||
|
||
Args:
|
||
ip (str): 从机 IP
|
||
port (int): 从机端口
|
||
address (str): 写入的地址位
|
||
value (float): 写入的值
|
||
|
||
Returns:
|
||
bool: 成功返回 True,失败返回 False
|
||
"""
|
||
|
||
try:
|
||
# 创建Melsec连接
|
||
plc = pymcprotocol.Type3E()
|
||
plc.setaccessopt(commtype="binary")
|
||
plc.connect(ip, port)
|
||
|
||
# 写入数值(转换为整数)
|
||
int_value = int(value)
|
||
plc.batchwrite_wordunits(address, [int_value])
|
||
plc.close()
|
||
|
||
self.logger.info(f"写入Melsec number成功: {ip}:{port} 地址: {address} 值: {value}")
|
||
return True
|
||
|
||
except Exception as e:
|
||
self.logger.error(f"写入Melsec number失败: {ip}:{port} 地址: {address} 值: {value} 错误: {e}")
|
||
return False
|
||
|
||
def write_string(self, ip: str, port: int, address: str, value: str) -> bool:
|
||
"""Melsec 写入 string
|
||
|
||
Args:
|
||
ip (str): 从机 IP
|
||
port (int): 从机端口
|
||
address (str): 写入的地址位
|
||
value (str): 写入的值
|
||
|
||
Returns:
|
||
bool: 成功返回 True,失败返回 False
|
||
"""
|
||
|
||
try:
|
||
# 创建Melsec连接
|
||
plc = pymcprotocol.Type3E()
|
||
plc.setaccessopt(commtype="binary")
|
||
plc.connect(ip, port)
|
||
|
||
# 准备字符串数据
|
||
byte_data = value.encode('ascii')[:254] # 限制长度并编码为ASCII
|
||
# 补齐到偶数长度
|
||
if len(byte_data) % 2 == 1:
|
||
byte_data += b'\x00'
|
||
|
||
# 转换为字数据
|
||
word_data = []
|
||
for i in range(0, len(byte_data), 2):
|
||
word = byte_data[i] | (byte_data[i+1] << 8)
|
||
word_data.append(word)
|
||
|
||
# 写入字符串
|
||
plc.batchwrite_wordunits(address, word_data)
|
||
plc.close()
|
||
|
||
self.logger.info(f"写入Melsec string成功: {ip}:{port} 地址: {address} 值: {value}")
|
||
return True
|
||
|
||
except Exception as e:
|
||
self.logger.error(f"写入Melsec string失败: {ip}:{port} 地址: {address} 值: {value} 错误: {e}")
|
||
return False |