451 lines
16 KiB
Python
451 lines
16 KiB
Python
#!/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 |