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