451 lines
16 KiB
Python
Raw Normal View History

2025-09-29 09:35:08 +08:00
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
VWED Fins 模块
提供 Fins 协议通信相关的内置函数
"""
from typing import Optional
from utils.logger import get_logger
import socket
import struct
logger = get_logger("services.script_fins_module")
class VWEDFinsModule:
"""VWED Fins 模块类"""
def __init__(self, script_id: str):
self.script_id = script_id
logger.debug(f"初始化 Fins 模块脚本ID: {script_id}")
def read_fins_string(self, ip: str, port: int, area: int, fins_io_addr: int, bit_offset: int, word_length: int) -> Optional[str]:
"""
Fins 读取 string
Args:
ip: 从机 IP
port: 从机端口
area: 读取的存储区域代码16进制值如0x82
fins_io_addr: 读取的地址位
bit_offset: 读取的偏移量
word_length: 读取的长度
Returns:
Optional[str]: 读取成功的返回值失败返回 None
"""
try:
logger.info(f"Fins 读取 string - IP: {ip}, Port: {port}, Area: 0x{area:02X}, Addr: {fins_io_addr}, Offset: {bit_offset}, Length: {word_length}")
# 建立 TCP 连接
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5.0) # 5秒超时
sock.connect((ip, port))
try:
# 构造 Fins 读取命令
command = self._build_fins_read_command(area, fins_io_addr, bit_offset, word_length)
# 发送命令
sock.send(command)
# 接收响应
response = sock.recv(1024)
# 解析响应并提取字符串数据
result = self._parse_fins_string_response(response, word_length)
logger.info(f"Fins 读取 string 成功: {result}")
return result
finally:
sock.close()
except Exception as e:
logger.error(f"Fins 读取 string 失败: {str(e)}")
return None
def read_fins_word(self, ip: str, port: int, area: int, fins_io_addr: int, bit_offset: int) -> Optional[int]:
"""
Fins 读取 word
Args:
ip: 从机 IP
port: 从机端口
area: 读取的存储区域代码16进制值如0x82
fins_io_addr: 读取的地址位
bit_offset: 读取的偏移量
Returns:
Optional[int]: 读取成功的返回值失败返回 None
"""
try:
logger.info(f"Fins 读取 word - IP: {ip}, Port: {port}, Area: 0x{area:02X}, Addr: {fins_io_addr}, Offset: {bit_offset}")
# 建立 TCP 连接
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5.0)
sock.connect((ip, port))
try:
# 构造 Fins 读取命令(读取 1 个 word
command = self._build_fins_read_command(area, fins_io_addr, bit_offset, 1)
# 发送命令
sock.send(command)
# 接收响应
response = sock.recv(1024)
# 解析响应并提取 word 数据
result = self._parse_fins_word_response(response)
logger.info(f"Fins 读取 word 成功: {result}")
return result
finally:
sock.close()
except Exception as e:
logger.error(f"Fins 读取 word 失败: {str(e)}")
return None
def read_fins_bit(self, ip: str, port: int, area: int, fins_io_addr: int, bit_offset: int) -> Optional[int]:
"""
Fins 读取 bit
Args:
ip: 从机 IP
port: 从机端口
area: 读取的存储区域代码16进制值如0x82
fins_io_addr: 读取的地址位
bit_offset: 读取的偏移量
Returns:
Optional[int]: 读取成功的返回值失败返回 None
"""
try:
logger.info(f"Fins 读取 bit - IP: {ip}, Port: {port}, Area: 0x{area:02X}, Addr: {fins_io_addr}, Offset: {bit_offset}")
# 建立 TCP 连接
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5.0)
sock.connect((ip, port))
try:
# 构造 Fins 位读取命令
command = self._build_fins_bit_read_command(area, fins_io_addr, bit_offset)
# 发送命令
sock.send(command)
# 接收响应
response = sock.recv(1024)
# 解析响应并提取 bit 数据
result = self._parse_fins_bit_response(response)
logger.info(f"Fins 读取 bit 成功: {result}")
return result
finally:
sock.close()
except Exception as e:
logger.error(f"Fins 读取 bit 失败: {str(e)}")
return None
def write_fins_word(self, ip: str, port: int, area: int, fins_io_addr: int, bit_offset: int, value: int) -> None:
"""
Fins 写入 word
Args:
ip: 从机 IP
port: 从机端口
area: 写入的存储区域代码16进制值如0x82
fins_io_addr: 写入的地址位
bit_offset: 写入的偏移量
value: 写入的值
"""
try:
logger.info(f"Fins 写入 word - IP: {ip}, Port: {port}, Area: 0x{area:02X}, Addr: {fins_io_addr}, Offset: {bit_offset}, Value: {value}")
# 建立 TCP 连接
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5.0)
sock.connect((ip, port))
try:
# 构造 Fins 写入命令
command = self._build_fins_write_command(area, fins_io_addr, bit_offset, value)
# 发送命令
sock.send(command)
# 接收响应
response = sock.recv(1024)
# 检查响应状态
if self._check_fins_write_response(response):
logger.info(f"Fins 写入 word 成功")
else:
logger.error(f"Fins 写入 word 失败: 响应状态错误")
finally:
sock.close()
except Exception as e:
logger.error(f"Fins 写入 word 失败: {str(e)}")
def write_fins_bit(self, ip: str, port: int, area: int, fins_io_addr: int, bit_offset: int, value: bool) -> None:
"""
Fins 写入 bit
Args:
ip: 从机 IP
port: 从机端口
area: 写入的存储区域代码16进制值如0x82
fins_io_addr: 写入的地址位
bit_offset: 写入的偏移量
value: 写入的值
"""
try:
logger.info(f"Fins 写入 bit - IP: {ip}, Port: {port}, Area: 0x{area:02X}, Addr: {fins_io_addr}, Offset: {bit_offset}, Value: {value}")
# 建立 TCP 连接
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5.0)
sock.connect((ip, port))
try:
# 构造 Fins 位写入命令
command = self._build_fins_bit_write_command(area, fins_io_addr, bit_offset, value)
# 发送命令
sock.send(command)
# 接收响应
response = sock.recv(1024)
# 检查响应状态
if self._check_fins_write_response(response):
logger.info(f"Fins 写入 bit 成功")
else:
logger.error(f"Fins 写入 bit 失败: 响应状态错误")
finally:
sock.close()
except Exception as e:
logger.error(f"Fins 写入 bit 失败: {str(e)}")
def _build_fins_read_command(self, area: int, address: int, bit_offset: int, word_count: int) -> bytes:
"""构造 Fins 读取命令"""
# Fins TCP 头部 (16 字节)
header = b'FINS' + b'\x00' * 12
# Fins 命令头部
icf = 0x80 # Information Control Field
rsv = 0x00 # Reserved
gct = 0x02 # Gateway Count
dna = 0x00 # Destination Network Address
da1 = 0x00 # Destination Unit Address
da2 = 0x00 # Destination Node Address
sna = 0x00 # Source Network Address
sa1 = 0x00 # Source Unit Address
sa2 = 0x00 # Source Node Address
sid = 0x00 # Service ID
# 读取命令
cmd = 0x0101 # Memory Area Read
# 内存区域指定
area_code = area & 0xFF
addr_high = (address >> 8) & 0xFF
addr_low = address & 0xFF
bit_addr = bit_offset & 0x0F
count_high = (word_count >> 8) & 0xFF
count_low = word_count & 0xFF
# 组装命令
command = struct.pack('>BBBBBBBBBBHBBBBBB',
icf, rsv, gct, dna, da1, da2, sna, sa1, sa2, sid,
cmd, area_code, addr_high, addr_low, bit_addr, count_high, count_low)
# 添加长度到头部
length = len(command)
header = header[:8] + struct.pack('>I', length) + header[12:]
return header + command
def _build_fins_bit_read_command(self, area: int, address: int, bit_offset: int) -> bytes:
"""构造 Fins 位读取命令"""
# 使用读取命令,但指定位地址
return self._build_fins_read_command(area, address, bit_offset, 1)
def _build_fins_write_command(self, area: int, address: int, bit_offset: int, value: int) -> bytes:
"""构造 Fins 写入命令"""
# Fins TCP 头部
header = b'FINS' + b'\x00' * 12
# Fins 命令头部
icf = 0x80
rsv = 0x00
gct = 0x02
dna = 0x00
da1 = 0x00
da2 = 0x00
sna = 0x00
sa1 = 0x00
sa2 = 0x00
sid = 0x00
# 写入命令
cmd = 0x0102 # Memory Area Write
# 内存区域指定
area_code = area & 0xFF
addr_high = (address >> 8) & 0xFF
addr_low = address & 0xFF
bit_addr = bit_offset & 0x0F
count_high = 0x00
count_low = 0x01 # 写入 1 个 word
# 数据部分
data_high = (value >> 8) & 0xFF
data_low = value & 0xFF
# 组装命令
command = struct.pack('>BBBBBBBBBBHBBBBBBB',
icf, rsv, gct, dna, da1, da2, sna, sa1, sa2, sid,
cmd, area_code, addr_high, addr_low, bit_addr, count_high, count_low,
data_high, data_low)
# 添加长度到头部
length = len(command)
header = header[:8] + struct.pack('>I', length) + header[12:]
return header + command
def _build_fins_bit_write_command(self, area: int, address: int, bit_offset: int, value: bool) -> bytes:
"""构造 Fins 位写入命令"""
# 使用位写入命令
header = b'FINS' + b'\x00' * 12
# Fins 命令头部
icf = 0x80
rsv = 0x00
gct = 0x02
dna = 0x00
da1 = 0x00
da2 = 0x00
sna = 0x00
sa1 = 0x00
sa2 = 0x00
sid = 0x00
# 位写入命令
cmd = 0x0102 # Memory Area Write (bit)
# 内存区域指定
area_code = area & 0xFF
addr_high = (address >> 8) & 0xFF
addr_low = address & 0xFF
bit_addr = bit_offset & 0x0F
count_high = 0x00
count_low = 0x01
# 位数据
bit_data = 0x01 if value else 0x00
# 组装命令
command = struct.pack('>BBBBBBBBBBHBBBBBB',
icf, rsv, gct, dna, da1, da2, sna, sa1, sa2, sid,
cmd, area_code, addr_high, addr_low, bit_addr, count_high, count_low,
bit_data)
# 添加长度到头部
length = len(command)
header = header[:8] + struct.pack('>I', length) + header[12:]
return header + command
def _parse_fins_string_response(self, response: bytes, word_length: int) -> Optional[str]:
"""解析 Fins 字符串响应"""
try:
# 跳过头部,获取数据部分
if len(response) < 20:
return None
# 检查响应码
if len(response) > 28 and response[27] != 0x00:
logger.error(f"Fins 响应错误码: 0x{response[27]:02X}")
return None
# 提取字符串数据
data_start = 28 # 跳过头部和响应码
data_bytes = response[data_start:data_start + word_length * 2]
# 转换为字符串(移除空字符)
result = data_bytes.decode('ascii', errors='ignore').rstrip('\x00')
return result
except Exception as e:
logger.error(f"解析 Fins 字符串响应失败: {str(e)}")
return None
def _parse_fins_word_response(self, response: bytes) -> Optional[int]:
"""解析 Fins word 响应"""
try:
# 跳过头部,获取数据部分
if len(response) < 30:
return None
# 检查响应码
if response[27] != 0x00:
logger.error(f"Fins 响应错误码: 0x{response[27]:02X}")
return None
# 提取 word 数据
data_start = 28
word_data = struct.unpack('>H', response[data_start:data_start + 2])[0]
return word_data
except Exception as e:
logger.error(f"解析 Fins word 响应失败: {str(e)}")
return None
def _parse_fins_bit_response(self, response: bytes) -> Optional[int]:
"""解析 Fins bit 响应"""
try:
# 跳过头部,获取数据部分
if len(response) < 29:
return None
# 检查响应码
if response[27] != 0x00:
logger.error(f"Fins 响应错误码: 0x{response[27]:02X}")
return None
# 提取 bit 数据
bit_data = response[28] & 0x01
return bit_data
except Exception as e:
logger.error(f"解析 Fins bit 响应失败: {str(e)}")
return None
def _check_fins_write_response(self, response: bytes) -> bool:
"""检查 Fins 写入响应"""
try:
if len(response) < 28:
return False
# 检查响应码
return response[27] == 0x00
except Exception as e:
logger.error(f"检查 Fins 写入响应失败: {str(e)}")
return False