451 lines
16 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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