2025-09-30 13:52:36 +08:00
|
|
|
|
"""
|
|
|
|
|
S7 PLC通信模块
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import logging
|
|
|
|
|
import snap7
|
|
|
|
|
from snap7.type import *
|
|
|
|
|
from snap7.util import *
|
|
|
|
|
from typing import Optional, Any
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
|
|
# 配置日志
|
|
|
|
|
logging.basicConfig(level=logging.INFO,
|
|
|
|
|
format='%(asctime)s - %(name)s - %(funcName)s - %(levelname)s - %(message)s')
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VWEDS7Module:
|
|
|
|
|
"""
|
|
|
|
|
S7 PLC通信模块类
|
2025-10-01 15:20:55 +08:00
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, script_id: str):
|
|
|
|
|
self.script_id = script_id
|
|
|
|
|
logger.debug(f"初始化 S7 模块,脚本ID: {script_id}")
|
2025-09-30 13:52:36 +08:00
|
|
|
|
|
|
|
|
|
def _get_rack_slot(self, plc_type: str) -> tuple:
|
|
|
|
|
"""
|
|
|
|
|
根据PLC类型获取默认的rack和slot值
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
plc_type (str): PLC类型
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
tuple: (rack, slot) 默认值
|
|
|
|
|
"""
|
|
|
|
|
# 根据常见默认值设置
|
|
|
|
|
# S7-200 SMART: rack=0, slot=1
|
|
|
|
|
# S7-300: rack=0, slot=2
|
|
|
|
|
# S7-400: rack=0, slot=3
|
|
|
|
|
# S7-1200: rack=0, slot=1
|
|
|
|
|
# S7-1500: rack=0, slot=1
|
|
|
|
|
# S7-200: rack=0, slot=1
|
|
|
|
|
|
|
|
|
|
defaults = {
|
|
|
|
|
'S200Smart': (0, 1),
|
|
|
|
|
'S300': (0, 2),
|
|
|
|
|
'S400': (0, 3),
|
|
|
|
|
'S1200': (0, 1),
|
|
|
|
|
'S1500': (0, 1),
|
|
|
|
|
'S200': (0, 1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return defaults.get(plc_type, (0, 1))
|
|
|
|
|
|
|
|
|
|
def _parse_address(self, block_and_offset: str) -> tuple:
|
|
|
|
|
"""
|
|
|
|
|
解析地址字符串,返回区域、DB号、字节地址和位地址
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
block_and_offset (str): 地址字符串,如M100, DB1.100, DB1.100.7等
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
tuple: (area, db_number, byte_addr, bit_addr)
|
|
|
|
|
"""
|
|
|
|
|
area = None
|
|
|
|
|
db_number = 0
|
|
|
|
|
byte_addr = 0
|
|
|
|
|
bit_addr = None
|
|
|
|
|
|
|
|
|
|
# 移除空格
|
|
|
|
|
block_and_offset = block_and_offset.replace(' ', '')
|
|
|
|
|
|
|
|
|
|
# 处理DB块地址
|
|
|
|
|
if block_and_offset.startswith('DB'):
|
|
|
|
|
parts = block_and_offset[2:].split('.')
|
|
|
|
|
if len(parts) >= 2:
|
|
|
|
|
db_number = int(parts[0])
|
|
|
|
|
byte_addr = int(parts[1])
|
|
|
|
|
if len(parts) == 3:
|
|
|
|
|
bit_addr = int(parts[2])
|
|
|
|
|
area = Areas.DB
|
|
|
|
|
# 处理其他区域地址
|
|
|
|
|
else:
|
|
|
|
|
if block_and_offset[0] == 'M':
|
|
|
|
|
area = Areas.MK
|
|
|
|
|
addr_part = block_and_offset[1:]
|
|
|
|
|
elif block_and_offset[0] == 'I':
|
|
|
|
|
area = Areas.PE
|
|
|
|
|
addr_part = block_and_offset[1:]
|
|
|
|
|
elif block_and_offset[0] == 'Q':
|
|
|
|
|
area = Areas.PA
|
|
|
|
|
addr_part = block_and_offset[1:]
|
|
|
|
|
elif block_and_offset[0] == 'V':
|
|
|
|
|
# V区通常映射到M区
|
|
|
|
|
area = Areas.MK
|
|
|
|
|
addr_part = block_and_offset[1:]
|
|
|
|
|
elif block_and_offset[0] == 'T':
|
|
|
|
|
area = Areas.TM
|
|
|
|
|
addr_part = block_and_offset[1:]
|
|
|
|
|
elif block_and_offset[0] == 'C':
|
|
|
|
|
area = Areas.CT
|
|
|
|
|
addr_part = block_and_offset[1:]
|
|
|
|
|
elif block_and_offset[0] == 'AI':
|
|
|
|
|
area = Areas.AI
|
|
|
|
|
addr_part = block_and_offset[2:]
|
|
|
|
|
elif block_and_offset[0] == 'AQ':
|
|
|
|
|
area = Areas.AQ
|
|
|
|
|
addr_part = block_and_offset[2:]
|
|
|
|
|
else:
|
|
|
|
|
raise ValueError(f"不支持的地址区域: {block_and_offset[0]}")
|
|
|
|
|
|
|
|
|
|
# 处理地址中的位信息
|
|
|
|
|
if '.' in addr_part:
|
|
|
|
|
parts = addr_part.split('.')
|
|
|
|
|
byte_addr = int(parts[0])
|
|
|
|
|
bit_addr = int(parts[1])
|
|
|
|
|
else:
|
|
|
|
|
byte_addr = int(addr_part)
|
|
|
|
|
|
|
|
|
|
return area, db_number, byte_addr, bit_addr
|
|
|
|
|
|
|
|
|
|
def read_s7_int(self, plc_type: str, ip: str, block_and_offset: str) -> Optional[int]:
|
|
|
|
|
"""
|
|
|
|
|
S7 读取 Int。
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
plc_type (str): PLC 类型,可选值:S1200/S300/S400/S1500/S200Smart/S200
|
|
|
|
|
ip (str): PLC IP地址
|
|
|
|
|
block_and_offset (str): 读取的地址,如M100, DB1.100等
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Optional[int]: 读取到的整数值,失败则返回None
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取rack和slot
|
|
|
|
|
rack, slot = self._get_rack_slot(plc_type)
|
|
|
|
|
|
|
|
|
|
# 解析地址
|
|
|
|
|
area, db_number, byte_addr, bit_addr = self._parse_address(block_and_offset)
|
|
|
|
|
|
|
|
|
|
# 创建客户端并连接
|
|
|
|
|
client = snap7.client.Client()
|
|
|
|
|
client.connect(ip, rack, slot, 1102)
|
|
|
|
|
|
|
|
|
|
# 读取数据
|
|
|
|
|
if area == Areas.DB:
|
|
|
|
|
data = client.db_read(db_number, byte_addr, 2) # Int是2个字节
|
|
|
|
|
else:
|
|
|
|
|
data = client.read_area(area, 0, byte_addr, 2)
|
|
|
|
|
|
|
|
|
|
# 断开连接
|
|
|
|
|
client.disconnect()
|
|
|
|
|
|
|
|
|
|
# 解析数据
|
|
|
|
|
value = snap7.util.get_int(data, 0)
|
|
|
|
|
logger.info(f"成功读取 {plc_type} {ip} 的 {block_and_offset} 地址的Int值: {value}")
|
|
|
|
|
return value
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"{datetime.now()} - VWEDS7Module - read_s7_int - 读取Int值时发生错误: {e}")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def read_s7_dint(self, plc_type: str, ip: str, block_and_offset: str) -> Optional[int]:
|
|
|
|
|
"""
|
|
|
|
|
S7 读取 DInt。
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
plc_type (str): PLC 类型,可选值:S1200/S300/S400/S1500/S200Smart/S200
|
|
|
|
|
ip (str): PLC IP地址
|
|
|
|
|
block_and_offset (str): 读取的地址,如M100, DB1.100等
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Optional[int]: 读取到的双整数值,失败则返回None
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取rack和slot
|
|
|
|
|
rack, slot = self._get_rack_slot(plc_type)
|
|
|
|
|
|
|
|
|
|
# 解析地址
|
|
|
|
|
area, db_number, byte_addr, bit_addr = self._parse_address(block_and_offset)
|
|
|
|
|
|
|
|
|
|
# 创建客户端并连接
|
|
|
|
|
client = snap7.client.Client()
|
|
|
|
|
client.connect(ip, rack, slot, 1102)
|
|
|
|
|
|
|
|
|
|
# 读取数据
|
|
|
|
|
if area == Areas.DB:
|
|
|
|
|
data = client.db_read(db_number, byte_addr, 4) # DInt是4个字节
|
|
|
|
|
else:
|
|
|
|
|
data = client.read_area(area, 0, byte_addr, 4)
|
|
|
|
|
|
|
|
|
|
# 断开连接
|
|
|
|
|
client.disconnect()
|
|
|
|
|
|
|
|
|
|
# 解析数据
|
|
|
|
|
value = snap7.util.get_dint(data, 0)
|
|
|
|
|
logger.info(f"成功读取 {plc_type} {ip} 的 {block_and_offset} 地址的DInt值: {value}")
|
|
|
|
|
return value
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"{datetime.now()} - VWEDS7Module - read_s7_dint - 读取DInt值时发生错误: {e}")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def read_s7_word(self, plc_type: str, ip: str, block_and_offset: str) -> Optional[int]:
|
|
|
|
|
"""
|
|
|
|
|
S7 读取 Word。
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
plc_type (str): PLC 类型,可选值:S1200/S300/S400/S1500/S200Smart/S200
|
|
|
|
|
ip (str): PLC IP地址
|
|
|
|
|
block_and_offset (str): 读取的地址,如M100, DB1.100等
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Optional[int]: 读取到的字值,失败则返回None
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取rack和slot
|
|
|
|
|
rack, slot = self._get_rack_slot(plc_type)
|
|
|
|
|
|
|
|
|
|
# 解析地址
|
|
|
|
|
area, db_number, byte_addr, bit_addr = self._parse_address(block_and_offset)
|
|
|
|
|
|
|
|
|
|
# 创建客户端并连接
|
|
|
|
|
client = snap7.client.Client()
|
|
|
|
|
client.connect(ip, rack, slot, 1102)
|
|
|
|
|
|
|
|
|
|
# 读取数据
|
|
|
|
|
if area == Areas.DB:
|
|
|
|
|
data = client.db_read(db_number, byte_addr, 2) # Word是2个字节
|
|
|
|
|
else:
|
|
|
|
|
data = client.read_area(area, 0, byte_addr, 2)
|
|
|
|
|
|
|
|
|
|
# 断开连接
|
|
|
|
|
client.disconnect()
|
|
|
|
|
|
|
|
|
|
# 解析数据
|
|
|
|
|
value = snap7.util.get_word(data, 0)
|
|
|
|
|
logger.info(f"成功读取 {plc_type} {ip} 的 {block_and_offset} 地址的Word值: {value}")
|
|
|
|
|
return value
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"{datetime.now()} - VWEDS7Module - read_s7_word - 读取Word值时发生错误: {e}")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def read_s7_dword(self, plc_type: str, ip: str, block_and_offset: str) -> Optional[int]:
|
|
|
|
|
"""
|
|
|
|
|
S7 读取 DWord。
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
plc_type (str): PLC 类型,可选值:S1200/S300/S400/S1500/S200Smart/S200
|
|
|
|
|
ip (str): PLC IP地址
|
|
|
|
|
block_and_offset (str): 读取的地址,如M100, DB1.100等
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Optional[int]: 读取到的双字值,失败则返回None
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取rack和slot
|
|
|
|
|
rack, slot = self._get_rack_slot(plc_type)
|
|
|
|
|
|
|
|
|
|
# 解析地址
|
|
|
|
|
area, db_number, byte_addr, bit_addr = self._parse_address(block_and_offset)
|
|
|
|
|
|
|
|
|
|
# 创建客户端并连接
|
|
|
|
|
client = snap7.client.Client()
|
|
|
|
|
client.connect(ip, rack, slot, 1102)
|
|
|
|
|
|
|
|
|
|
# 读取数据
|
|
|
|
|
if area == Areas.DB:
|
|
|
|
|
data = client.db_read(db_number, byte_addr, 4) # DWord是4个字节
|
|
|
|
|
else:
|
|
|
|
|
data = client.read_area(area, 0, byte_addr, 4)
|
|
|
|
|
|
|
|
|
|
# 断开连接
|
|
|
|
|
client.disconnect()
|
|
|
|
|
|
|
|
|
|
# 解析数据
|
|
|
|
|
value = snap7.util.get_dword(data, 0)
|
|
|
|
|
logger.info(f"成功读取 {plc_type} {ip} 的 {block_and_offset} 地址的DWord值: {value}")
|
|
|
|
|
return value
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"{datetime.now()} - VWEDS7Module - read_s7_dword - 读取DWord值时发生错误: {e}")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def read_s7_string(self, plc_type: str, ip: str, block_and_offset: str) -> Optional[str]:
|
|
|
|
|
"""
|
|
|
|
|
S7 读取 String。
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
plc_type (str): PLC 类型,可选值:S1200/S300/S400/S1500/S200Smart/S200
|
|
|
|
|
ip (str): PLC IP地址
|
|
|
|
|
block_and_offset (str): 读取的地址,如M100, DB1.100等
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Optional[str]: 读取到的字符串值,失败则返回None
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取rack和slot
|
|
|
|
|
rack, slot = self._get_rack_slot(plc_type)
|
|
|
|
|
|
|
|
|
|
# 解析地址
|
|
|
|
|
area, db_number, byte_addr, bit_addr = self._parse_address(block_and_offset)
|
|
|
|
|
|
|
|
|
|
# 创建客户端并连接
|
|
|
|
|
client = snap7.client.Client()
|
|
|
|
|
client.connect(ip, rack, slot, 1102)
|
|
|
|
|
|
|
|
|
|
# 先读取字符串的长度信息(前2个字节)
|
|
|
|
|
# 第一个字节是最大长度,第二个字节是实际长度
|
|
|
|
|
if area == Areas.DB:
|
|
|
|
|
length_data = client.db_read(db_number, byte_addr, 2)
|
|
|
|
|
else:
|
|
|
|
|
length_data = client.read_area(area, 0, byte_addr, 2)
|
|
|
|
|
|
|
|
|
|
# 获取字符串的实际长度
|
|
|
|
|
max_length = length_data[0] # 最大长度
|
|
|
|
|
actual_length = length_data[1] # 实际长度
|
|
|
|
|
|
|
|
|
|
# 读取字符串数据(根据实际长度读取)
|
|
|
|
|
if area == Areas.DB:
|
|
|
|
|
data = client.db_read(db_number, byte_addr + 2, actual_length)
|
|
|
|
|
else:
|
|
|
|
|
data = client.read_area(area, 0, byte_addr + 2, actual_length)
|
|
|
|
|
|
|
|
|
|
# 断开连接
|
|
|
|
|
client.disconnect()
|
|
|
|
|
|
|
|
|
|
# 解析数据
|
|
|
|
|
value = data.decode('utf-8').rstrip('\x00').rstrip() # 移除可能的空字符和空格
|
|
|
|
|
logger.info(f"成功读取 {plc_type} {ip} 的 {block_and_offset} 地址的String值: {value}")
|
|
|
|
|
return value
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"{datetime.now()} - VWEDS7Module - read_s7_string - 读取String值时发生错误: {e}")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def read_s7_boolean(self, plc_type: str, ip: str, block_and_offset: str) -> Optional[bool]:
|
|
|
|
|
"""
|
|
|
|
|
S7 读取 Bool。
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
plc_type (str): PLC 类型,可选值:S1200/S300/S400/S1500/S200Smart/S200
|
|
|
|
|
ip (str): PLC IP地址
|
|
|
|
|
block_and_offset (str): 读取的地址,如M100.0, DB1.100.7等
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Optional[bool]: 读取到的布尔值,失败则返回None
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取rack和slot
|
|
|
|
|
rack, slot = self._get_rack_slot(plc_type)
|
|
|
|
|
|
|
|
|
|
# 解析地址
|
|
|
|
|
area, db_number, byte_addr, bit_addr = self._parse_address(block_and_offset)
|
|
|
|
|
|
|
|
|
|
# 检查是否有位地址
|
|
|
|
|
if bit_addr is None:
|
|
|
|
|
raise ValueError("读取布尔值需要指定位地址,如M100.0")
|
|
|
|
|
|
|
|
|
|
# 创建客户端并连接
|
|
|
|
|
client = snap7.client.Client()
|
|
|
|
|
client.connect(ip, rack, slot, 1102)
|
|
|
|
|
|
|
|
|
|
# 读取数据
|
|
|
|
|
if area == Areas.DB:
|
|
|
|
|
data = client.db_read(db_number, byte_addr, 1) # 读取1个字节
|
|
|
|
|
else:
|
|
|
|
|
data = client.read_area(area, 0, byte_addr, 1)
|
|
|
|
|
|
|
|
|
|
# 断开连接
|
|
|
|
|
client.disconnect()
|
|
|
|
|
|
|
|
|
|
# 解析数据
|
|
|
|
|
value = snap7.util.get_bool(data, 0, bit_addr)
|
|
|
|
|
logger.info(f"成功读取 {plc_type} {ip} 的 {block_and_offset} 地址的Boolean值: {value}")
|
|
|
|
|
return value
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"{datetime.now()} - VWEDS7Module - read_s7_boolean - 读取Boolean值时发生错误: {e}")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def write_s7_int(self, plc_type: str, ip: str, block_and_offset: str, value: int) -> bool:
|
|
|
|
|
"""
|
|
|
|
|
S7 写入 Int。
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
plc_type (str): PLC 类型,可选值:S1200/S300/S400/S1500/S200Smart/S200
|
|
|
|
|
ip (str): PLC IP地址
|
|
|
|
|
block_and_offset (str): 写入的地址,如M100, DB1.100等
|
|
|
|
|
value (int): 要写入的整数值
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
bool: 写入成功返回True,失败返回False
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取rack和slot
|
|
|
|
|
rack, slot = self._get_rack_slot(plc_type)
|
|
|
|
|
|
|
|
|
|
# 解析地址
|
|
|
|
|
area, db_number, byte_addr, bit_addr = self._parse_address(block_and_offset)
|
|
|
|
|
|
|
|
|
|
# 创建客户端并连接
|
|
|
|
|
client = snap7.client.Client()
|
|
|
|
|
client.connect(ip, rack, slot, 1102)
|
|
|
|
|
|
|
|
|
|
# 准备数据
|
|
|
|
|
data = bytearray(2)
|
|
|
|
|
snap7.util.set_word(data, 0, value)
|
|
|
|
|
|
|
|
|
|
# 写入数据
|
|
|
|
|
logger.info(f"准备写入数据到DB{db_number}, 偏移量{byte_addr}, 数据: {data}")
|
|
|
|
|
if area == Areas.DB:
|
|
|
|
|
client.db_write(db_number, byte_addr, data)
|
|
|
|
|
else:
|
|
|
|
|
client.write_area(area, 0, byte_addr, data)
|
|
|
|
|
logger.info(f"数据写入完成")
|
|
|
|
|
|
|
|
|
|
# 断开连接
|
|
|
|
|
client.disconnect()
|
|
|
|
|
|
|
|
|
|
logger.info(f"成功向 {plc_type} {ip} 的 {block_and_offset} 地址写入Int值 {value}")
|
|
|
|
|
return True
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"{datetime.now()} - VWEDS7Module - write_s7_int - 写入Int值时发生错误: {e}")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def write_s7_dint(self, plc_type: str, ip: str, block_and_offset: str, value: int) -> bool:
|
|
|
|
|
"""
|
|
|
|
|
S7 写入 DInt。
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
plc_type (str): PLC 类型,可选值:S1200/S300/S400/S1500/S200Smart/S200
|
|
|
|
|
ip (str): PLC IP地址
|
|
|
|
|
block_and_offset (str): 写入的地址,如M100, DB1.100等
|
|
|
|
|
value (int): 要写入的双整数值
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
bool: 写入成功返回True,失败返回False
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取rack和slot
|
|
|
|
|
rack, slot = self._get_rack_slot(plc_type)
|
|
|
|
|
|
|
|
|
|
# 解析地址
|
|
|
|
|
area, db_number, byte_addr, bit_addr = self._parse_address(block_and_offset)
|
|
|
|
|
|
|
|
|
|
# 创建客户端并连接
|
|
|
|
|
client = snap7.client.Client()
|
|
|
|
|
client.connect(ip, rack, slot, 1102)
|
|
|
|
|
|
|
|
|
|
# 准备数据
|
|
|
|
|
data = bytearray(4)
|
|
|
|
|
snap7.util.set_dint(data, 0, value)
|
|
|
|
|
|
|
|
|
|
# 写入数据
|
|
|
|
|
if area == Areas.DB:
|
|
|
|
|
client.db_write(db_number, byte_addr, data)
|
|
|
|
|
else:
|
|
|
|
|
client.write_area(area, 0, byte_addr, data)
|
|
|
|
|
|
|
|
|
|
# 断开连接
|
|
|
|
|
client.disconnect()
|
|
|
|
|
|
|
|
|
|
logger.info(f"成功向 {plc_type} {ip} 的 {block_and_offset} 地址写入DInt值 {value}")
|
|
|
|
|
return True
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"{datetime.now()} - VWEDS7Module - write_s7_dint - 写入DInt值时发生错误: {e}")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def write_s7_word(self, plc_type: str, ip: str, block_and_offset: str, value: int) -> bool:
|
|
|
|
|
"""
|
|
|
|
|
S7 写入 Word。
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
plc_type (str): PLC 类型,可选值:S1200/S300/S400/S1500/S200Smart/S200
|
|
|
|
|
ip (str): PLC IP地址
|
|
|
|
|
block_and_offset (str): 写入的地址,如M100, DB1.100等
|
|
|
|
|
value (int): 要写入的字值
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
bool: 写入成功返回True,失败返回False
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取rack和slot
|
|
|
|
|
rack, slot = self._get_rack_slot(plc_type)
|
|
|
|
|
|
|
|
|
|
# 解析地址
|
|
|
|
|
area, db_number, byte_addr, bit_addr = self._parse_address(block_and_offset)
|
|
|
|
|
|
|
|
|
|
# 创建客户端并连接
|
|
|
|
|
client = snap7.client.Client()
|
|
|
|
|
client.connect(ip, rack, slot, 1102)
|
|
|
|
|
|
|
|
|
|
# 准备数据
|
|
|
|
|
data = bytearray(2)
|
|
|
|
|
snap7.util.set_word(data, 0, value)
|
|
|
|
|
|
|
|
|
|
# 写入数据
|
|
|
|
|
if area == Areas.DB:
|
|
|
|
|
client.db_write(db_number, byte_addr, data)
|
|
|
|
|
else:
|
|
|
|
|
client.write_area(area, 0, byte_addr, data)
|
|
|
|
|
|
|
|
|
|
# 断开连接
|
|
|
|
|
client.disconnect()
|
|
|
|
|
|
|
|
|
|
logger.info(f"成功向 {plc_type} {ip} 的 {block_and_offset} 地址写入Word值 {value}")
|
|
|
|
|
return True
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"{datetime.now()} - VWEDS7Module - write_s7_word - 写入Word值时发生错误: {e}")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def write_s7_dword(self, plc_type: str, ip: str, block_and_offset: str, value: int) -> bool:
|
|
|
|
|
"""
|
|
|
|
|
S7 写入 DWord。
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
plc_type (str): PLC 类型,可选值:S1200/S300/S400/S1500/S200Smart/S200
|
|
|
|
|
ip (str): PLC IP地址
|
|
|
|
|
block_and_offset (str): 写入的地址,如M100, DB1.100等
|
|
|
|
|
value (int): 要写入的双字值
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
bool: 写入成功返回True,失败返回False
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取rack和slot
|
|
|
|
|
rack, slot = self._get_rack_slot(plc_type)
|
|
|
|
|
|
|
|
|
|
# 解析地址
|
|
|
|
|
area, db_number, byte_addr, bit_addr = self._parse_address(block_and_offset)
|
|
|
|
|
|
|
|
|
|
# 创建客户端并连接
|
|
|
|
|
client = snap7.client.Client()
|
|
|
|
|
client.connect(ip, rack, slot, 1102)
|
|
|
|
|
|
|
|
|
|
# 准备数据
|
|
|
|
|
data = bytearray(4)
|
|
|
|
|
snap7.util.set_dword(data, 0, value)
|
|
|
|
|
|
|
|
|
|
# 写入数据
|
|
|
|
|
if area == Areas.DB:
|
|
|
|
|
client.db_write(db_number, byte_addr, data)
|
|
|
|
|
else:
|
|
|
|
|
client.write_area(area, 0, byte_addr, data)
|
|
|
|
|
|
|
|
|
|
# 断开连接
|
|
|
|
|
client.disconnect()
|
|
|
|
|
|
|
|
|
|
logger.info(f"成功向 {plc_type} {ip} 的 {block_and_offset} 地址写入DWord值 {value}")
|
|
|
|
|
return True
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"{datetime.now()} - VWEDS7Module - write_s7_dword - 写入DWord值时发生错误: {e}")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def write_s7_string(self, plc_type: str, ip: str, block_and_offset: str, value: str) -> bool:
|
|
|
|
|
"""
|
|
|
|
|
S7 写入 String。
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
plc_type (str): PLC 类型,可选值:S1200/S300/S400/S1500/S200Smart/S200
|
|
|
|
|
ip (str): PLC IP地址
|
|
|
|
|
block_and_offset (str): 写入的地址,如M100, DB1.100等
|
|
|
|
|
value (str): 要写入的字符串值
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
bool: 写入成功返回True,失败返回False
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取rack和slot
|
|
|
|
|
rack, slot = self._get_rack_slot(plc_type)
|
|
|
|
|
|
|
|
|
|
# 解析地址
|
|
|
|
|
area, db_number, byte_addr, bit_addr = self._parse_address(block_and_offset)
|
|
|
|
|
|
|
|
|
|
# 创建客户端并连接
|
|
|
|
|
client = snap7.client.Client()
|
|
|
|
|
client.connect(ip, rack, slot, 1102)
|
|
|
|
|
|
|
|
|
|
# 准备数据
|
|
|
|
|
# 字符串格式:前2个字节为长度,后面是字符串内容
|
|
|
|
|
data = bytearray(256) # 最大256字节
|
|
|
|
|
encoded_value = value.encode('utf-8')
|
|
|
|
|
length = min(len(encoded_value), 254) # 最大长度254
|
|
|
|
|
snap7.util.set_word(data, 0, length) # 设置长度
|
|
|
|
|
data[2:2+length] = encoded_value[:length] # 设置字符串内容
|
|
|
|
|
|
|
|
|
|
# 写入数据
|
|
|
|
|
if area == Areas.DB:
|
|
|
|
|
client.db_write(db_number, byte_addr, data)
|
|
|
|
|
else:
|
|
|
|
|
client.write_area(area, 0, byte_addr, data)
|
|
|
|
|
|
|
|
|
|
# 断开连接
|
|
|
|
|
client.disconnect()
|
|
|
|
|
|
|
|
|
|
logger.info(f"成功向 {plc_type} {ip} 的 {block_and_offset} 地址写入String值 {value}")
|
|
|
|
|
return True
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"{datetime.now()} - VWEDS7Module - write_s7_string - 写入String值时发生错误: {e}")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def write_s7_boolean(self, plc_type: str, ip: str, block_and_offset: str, value: bool) -> bool:
|
|
|
|
|
"""
|
|
|
|
|
S7 写入 Bool。
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
plc_type (str): PLC 类型,可选值:S1200/S300/S400/S1500/S200Smart/S200
|
|
|
|
|
ip (str): PLC IP地址
|
|
|
|
|
block_and_offset (str): 写入的地址,如M100.0, DB1.100.7等
|
|
|
|
|
value (bool): 要写入的布尔值
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
bool: 写入成功返回True,失败返回False
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取rack和slot
|
|
|
|
|
rack, slot = self._get_rack_slot(plc_type)
|
|
|
|
|
|
|
|
|
|
# 解析地址
|
|
|
|
|
area, db_number, byte_addr, bit_addr = self._parse_address(block_and_offset)
|
|
|
|
|
|
|
|
|
|
# 检查是否有位地址
|
|
|
|
|
if bit_addr is None:
|
|
|
|
|
raise ValueError("写入布尔值需要指定位地址,如M100.0")
|
|
|
|
|
|
|
|
|
|
# 创建客户端并连接
|
|
|
|
|
client = snap7.client.Client()
|
|
|
|
|
client.connect(ip, rack, slot, 1102)
|
|
|
|
|
|
|
|
|
|
# 读取当前字节数据
|
|
|
|
|
if area == Areas.DB:
|
|
|
|
|
data = client.db_read(db_number, byte_addr, 1)
|
|
|
|
|
else:
|
|
|
|
|
data = client.read_area(area, 0, byte_addr, 1)
|
|
|
|
|
|
|
|
|
|
# 设置布尔值
|
|
|
|
|
snap7.util.set_bool(data, 0, bit_addr, value)
|
|
|
|
|
|
|
|
|
|
# 写入数据
|
|
|
|
|
if area == Areas.DB:
|
|
|
|
|
client.db_write(db_number, byte_addr, data)
|
|
|
|
|
else:
|
|
|
|
|
client.write_area(area, 0, byte_addr, data)
|
|
|
|
|
|
|
|
|
|
# 断开连接
|
|
|
|
|
client.disconnect()
|
|
|
|
|
|
|
|
|
|
logger.info(f"成功向 {plc_type} {ip} 的 {block_and_offset} 地址写入Boolean值 {value}")
|
|
|
|
|
return True
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"{datetime.now()} - VWEDS7Module - write_s7_boolean - 写入Boolean值时发生错误: {e}")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def write_s7(self, plc_type: str, ip: str, block_and_offset: str, value: bytes, rack: int = 0, slot: int = 1) -> bool:
|
|
|
|
|
"""
|
|
|
|
|
S7 通用写入。
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
plc_type (str): PLC 类型,可选值:S1200/S300/S400/S1500/S200Smart/S200
|
|
|
|
|
ip (str): PLC IP地址
|
|
|
|
|
block_and_offset (str): 写入的地址,如M100, DB1.100等
|
|
|
|
|
value (bytes): 要写入的字节数据
|
|
|
|
|
rack (int): 中央机架,默认为0
|
|
|
|
|
slot (int): CPU模块插槽号,默认为1
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
bool: 写入成功返回True,失败返回False
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取rack和slot的默认值
|
|
|
|
|
default_rack, default_slot = self._get_rack_slot(plc_type)
|
|
|
|
|
if rack == 0 and slot == 1:
|
|
|
|
|
rack, slot = default_rack, default_slot
|
|
|
|
|
|
|
|
|
|
# 解析地址
|
|
|
|
|
area, db_number, byte_addr, bit_addr = self._parse_address(block_and_offset)
|
|
|
|
|
|
|
|
|
|
# 创建客户端并连接
|
|
|
|
|
client = snap7.client.Client()
|
|
|
|
|
client.connect(ip, rack, slot, 1102)
|
|
|
|
|
|
|
|
|
|
# 写入数据
|
|
|
|
|
if area == Areas.DB:
|
|
|
|
|
client.db_write(db_number, byte_addr, value)
|
|
|
|
|
else:
|
|
|
|
|
client.write_area(area, 0, byte_addr, value)
|
|
|
|
|
|
|
|
|
|
# 断开连接
|
|
|
|
|
client.disconnect()
|
|
|
|
|
|
|
|
|
|
logger.info(f"成功向 {plc_type} {ip} 的 {block_and_offset} 地址写入数据 {value}")
|
|
|
|
|
return True
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"{datetime.now()} - VWEDS7Module - write_s7 - 通用写入时发生错误: {e}")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def read_s7(self, plc_type: str, ip: str, block_and_offset: str, size: int, rack: int = 0, slot: int = 1) -> Optional[bytes]:
|
|
|
|
|
"""
|
|
|
|
|
S7 通用读取。
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
plc_type (str): PLC 类型,可选值:S1200/S300/S400/S1500/S200Smart/S200
|
|
|
|
|
ip (str): PLC IP地址
|
|
|
|
|
block_and_offset (str): 读取的地址,如M100, DB1.100等
|
|
|
|
|
size (int): 读取的数据大小(字节数)
|
|
|
|
|
rack (int): 中央机架,默认为0
|
|
|
|
|
slot (int): CPU模块插槽号,默认为1
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Optional[bytes]: 读取到的字节数据,失败则返回None
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取rack和slot的默认值
|
|
|
|
|
default_rack, default_slot = self._get_rack_slot(plc_type)
|
|
|
|
|
if rack == 0 and slot == 1:
|
|
|
|
|
rack, slot = default_rack, default_slot
|
|
|
|
|
|
|
|
|
|
# 解析地址
|
|
|
|
|
area, db_number, byte_addr, bit_addr = self._parse_address(block_and_offset)
|
|
|
|
|
|
|
|
|
|
# 创建客户端并连接
|
|
|
|
|
client = snap7.client.Client()
|
|
|
|
|
client.connect(ip, rack, slot, 1102)
|
|
|
|
|
|
|
|
|
|
# 读取数据
|
|
|
|
|
if area == Areas.DB:
|
|
|
|
|
data = client.db_read(db_number, byte_addr, size)
|
|
|
|
|
else:
|
|
|
|
|
data = client.read_area(area, 0, byte_addr, size)
|
|
|
|
|
|
|
|
|
|
# 断开连接
|
|
|
|
|
client.disconnect()
|
|
|
|
|
|
|
|
|
|
logger.info(f"成功读取 {plc_type} {ip} 的 {block_and_offset} 地址的 {size} 字节数据")
|
|
|
|
|
return data
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"{datetime.now()} - VWEDS7Module - read_s7 - 通用读取时发生错误: {e}")
|
|
|
|
|
return None
|