314 lines
11 KiB
Python
314 lines
11 KiB
Python
# uncompyle6 version 3.9.2
|
|
# Python bytecode version base 3.7.0 (3394)
|
|
# Decompiled from: Python 3.8.19 (default, Mar 20 2024, 15:27:52)
|
|
# [Clang 14.0.6 ]
|
|
# Embedded file name: /var/user/app/device_supervisorbak/device_supervisor/lib/mcprotocol/comfx.py
|
|
# Compiled at: 2024-04-18 03:12:58
|
|
# Size of source mod 2**32: 11460 bytes
|
|
"""
|
|
mcprotocol
|
|
Created on 2021/3/31
|
|
@author: Lius
|
|
"""
|
|
import re
|
|
from typing import NoReturn
|
|
from .mcformat import SerialModel
|
|
DEVICE_READ = 48
|
|
DEVICE_WRITE = 49
|
|
FORCE_ON = 55
|
|
FORCE_OFF = 56
|
|
ENQ = 5
|
|
STX = 2
|
|
ETX = 3
|
|
ACK = 6
|
|
NAK = 21
|
|
|
|
class ComFx:
|
|
|
|
def __init__(self, channel):
|
|
self.channel = channel
|
|
self._wordsize = 4
|
|
|
|
def _get_sum_check_code(self, calc_data):
|
|
""" Get sum check code.
|
|
"""
|
|
res = 0
|
|
for byte in calc_data:
|
|
res += byte
|
|
|
|
return res
|
|
|
|
def _encode_value(self, value, mode='short', isSigned=False):
|
|
"""encode com protocol value data to byte.
|
|
"""
|
|
try:
|
|
if mode == "byte":
|
|
value = value & 255
|
|
value.to_bytes(1, "little", signed=isSigned)
|
|
value_byte = format(value, "x").rjust(2, "0").upper().encode()
|
|
else:
|
|
if mode == "short":
|
|
value = value & 65535
|
|
value.to_bytes(2, "little", signed=isSigned)
|
|
value_byte = format(value, "x").rjust(4, "0").upper().encode()
|
|
else:
|
|
if mode == "long":
|
|
value = value & 4294967295L
|
|
value.to_bytes(4, "little", signed=isSigned)
|
|
value_byte = format(value, "x").rjust(8, "0").upper().encode()
|
|
else:
|
|
raise ValueError("Please input value type")
|
|
except:
|
|
raise ValueError("Exceeeded Device value range")
|
|
|
|
return value_byte
|
|
|
|
def _decode_value(self, byte, isSigned=False):
|
|
"""decode byte to value
|
|
"""
|
|
try:
|
|
value = int(byte.decode(), 16)
|
|
if isSigned:
|
|
bit = 16
|
|
if byte & 1 << bit - 1 != 0:
|
|
value = byte - (1 << bit)
|
|
except:
|
|
raise ValueError("Could not decode byte to value")
|
|
|
|
return value
|
|
|
|
def _check_cmdanswer(self, recv_data):
|
|
"""check command answer.
|
|
"""
|
|
if recv_data == b'' or recv_data[0] == ACK:
|
|
return "No response data."
|
|
if recv_data[0] == NAK:
|
|
return "Illegal request data."
|
|
|
|
def _x_or_y_offset(self, devicenum):
|
|
return int(devicenum / 10)
|
|
|
|
def _s_or_m_offset(self, devicenum):
|
|
return int(devicenum / 8)
|
|
|
|
def _get_read_address(self, devicecode, devicenum):
|
|
if devicecode == "Y":
|
|
return self._x_or_y_offset(int(devicenum)) + 160
|
|
if devicecode == "X":
|
|
return self._x_or_y_offset(int(devicenum)) + 128
|
|
if devicecode == "M":
|
|
return self._s_or_m_offset(int(devicenum)) + 256
|
|
if devicecode == "S":
|
|
return self._s_or_m_offset(int(devicenum))
|
|
if devicecode == "D":
|
|
return int(devicenum) * 2 + 4096
|
|
if devicecode.startswith("CN"):
|
|
return int(devicenum) * 2 + 2560
|
|
if devicecode.startswith("TN"):
|
|
return int(devicenum) * 2 + 2048
|
|
|
|
def _device_read(self, headdevice, readsize):
|
|
"""read in byte units.
|
|
"""
|
|
request_data = bytes()
|
|
request_data += STX.to_bytes(1, "big")
|
|
request_data += DEVICE_READ.to_bytes(1, "big")
|
|
devicecode = re.search("\\D+", headdevice).group(0)
|
|
devicenum = re.search("\\d.*", headdevice).group(0)
|
|
address = "{:x}".format(self._get_read_address(devicecode, devicenum))
|
|
request_data += address.rjust(4, "0").upper().encode()
|
|
size_value = readsize & 65535
|
|
size_value.to_bytes(2, "little", signed=False)
|
|
request_data += format(size_value, "x").rjust(2, "0").upper().encode()
|
|
request_data += ETX.to_bytes(1, "big")
|
|
request_data += self._encode_value(self._get_sum_check_code(request_data[1[:None]]), "byte")
|
|
self.channel._send(request_data)
|
|
recv_data = self.channel._recv()
|
|
check_res = self._check_cmdanswer(recv_data)
|
|
if check_res:
|
|
return ([], check_res)
|
|
return (
|
|
recv_data[1[:readsize * 2 + 1]], None)
|
|
|
|
def batchread_wordunits(self, headdevice, readsize):
|
|
"""batch read in word units.
|
|
"""
|
|
recv_data, error = self._device_read(headdevice, readsize * 2)
|
|
if error:
|
|
return (
|
|
recv_data, error)
|
|
word_values = []
|
|
_wordsize = 4
|
|
data_index = 0
|
|
for _ in range(readsize):
|
|
wordvalue = self._decode_value(recv_data[data_index[:data_index + _wordsize]])
|
|
word_values.append(wordvalue >> 8 | (wordvalue & 255) << 8)
|
|
data_index += _wordsize
|
|
|
|
return (
|
|
word_values, None)
|
|
|
|
def is_octal_device(self, headdevice):
|
|
devicecode = re.search("\\D+", headdevice).group(0)
|
|
if devicecode in ('X', 'Y'):
|
|
return True
|
|
return False
|
|
|
|
def _read_bit_device(self, headdevice, readsize):
|
|
"""read in byte units.
|
|
"""
|
|
request_data = bytes()
|
|
request_data += STX.to_bytes(1, "big")
|
|
request_data += DEVICE_READ.to_bytes(1, "big")
|
|
devicecode = re.search("\\D+", headdevice).group(0)
|
|
devicenum = re.search("\\d.*", headdevice).group(0)
|
|
address = "{:x}".format(self._get_read_address(devicecode, devicenum))
|
|
request_data += address.rjust(4, "0").upper().encode()
|
|
size_value = readsize & 65535
|
|
size_value.to_bytes(2, "little", signed=False)
|
|
request_data += format(size_value, "x").rjust(2, "0").upper().encode()
|
|
request_data += ETX.to_bytes(1, "big")
|
|
request_data += self._encode_value(self._get_sum_check_code(request_data[1[:None]]), "byte")
|
|
self.channel._send(request_data)
|
|
recv_data = self.channel._recv()
|
|
check_res = self._check_cmdanswer(recv_data)
|
|
if check_res:
|
|
return ([], check_res)
|
|
return (
|
|
recv_data[1[:readsize * 2 + 1]], None)
|
|
|
|
def batchread_bitunits(self, headdevice, readsize):
|
|
"""batch read in bit units.
|
|
"""
|
|
bit = 16
|
|
if self.is_octal_device(headdevice):
|
|
bit = 8
|
|
elif readsize % bit == 0:
|
|
read_word_size = int(readsize / bit)
|
|
else:
|
|
read_word_size = int(readsize / bit) + 1
|
|
_recv, error = self._read_bit_device(headdevice, readsize * 2)
|
|
if error:
|
|
return (
|
|
_recv, error)
|
|
recv_data = []
|
|
_wordsize = 4
|
|
data_index = 0
|
|
for _ in range(readsize):
|
|
wordvalue = self._decode_value(_recv[data_index[:data_index + _wordsize]])
|
|
recv_data.append(wordvalue >> 8 | (wordvalue & 255) << 8)
|
|
data_index += _wordsize
|
|
|
|
bit_values = []
|
|
data_index = 0
|
|
for i in range(read_word_size):
|
|
bit_list_of_word = [int(x) for x in str(bin(recv_data[i]))[2[:None]].rjust(bit, "0")]
|
|
bit_list_of_word.reverse()
|
|
bit_values += bit_list_of_word
|
|
data_index += 1
|
|
|
|
sidx = int(re.search("\\d.*", headdevice).group(0))
|
|
bit_values = bit_values[sidx[:sidx + readsize]]
|
|
return (bit_values, None)
|
|
|
|
def _device_write(self, headdevice, values):
|
|
"""write in byte units.
|
|
"""
|
|
request_data = bytes()
|
|
request_data += STX.to_bytes(1, "big")
|
|
request_data += DEVICE_WRITE.to_bytes(1, "big")
|
|
devicecode = re.search("\\D+", headdevice).group(0)
|
|
devicenum = re.search("\\d.*", headdevice).group(0)
|
|
address = "{:x}".format(self._get_read_address(devicecode, devicenum))
|
|
request_data += address.rjust(4, "0").upper().encode()
|
|
size_value = len(values)
|
|
size_value.to_bytes(2, "little", signed=False)
|
|
request_data += format(size_value, "x").rjust(2, "0").upper().encode()
|
|
for value in values:
|
|
value.to_bytes(1, "little", signed=False)
|
|
request_data += format(value, "x").rjust(2, "0").upper().encode()
|
|
|
|
request_data += ETX.to_bytes(1, "big")
|
|
request_data += self._encode_value(self._get_sum_check_code(request_data[1[:None]]), "byte")
|
|
self.channel._send(request_data)
|
|
recv_data = self.channel._recv()
|
|
if recv_data != b'':
|
|
check_res = self._check_cmdanswer(recv_data)
|
|
if check_res:
|
|
return ([], check_res)
|
|
return (None, None)
|
|
|
|
def batchwrite_wordunits(self, headdevice, values):
|
|
"""batch write in word units.
|
|
"""
|
|
wirte_values = list()
|
|
for value in values:
|
|
value = value & 65535
|
|
wirte_values.append(value & 255)
|
|
wirte_values.append(value >> 8)
|
|
|
|
print(wirte_values)
|
|
recv_data, error = self._device_write(headdevice, wirte_values)
|
|
if error:
|
|
if error != "No response data.":
|
|
return (
|
|
recv_data, error)
|
|
return (None, None)
|
|
|
|
def _get_write_bit_addr(self, headdevice, bit):
|
|
devicecode = re.search("\\D+", headdevice).group(0)
|
|
devicenum = re.search("\\d.*", headdevice).group(0)
|
|
if devicecode == "Y":
|
|
return int(devicenum, 8) << 8 | 5
|
|
if devicecode == "X":
|
|
return int(devicenum, 8) << 8 | 4
|
|
if devicecode == "M":
|
|
return int(devicenum, 8) << 8 | 8
|
|
if devicecode == "S":
|
|
return int(devicenum, 8) << 8
|
|
|
|
def _write_bit_device(self, cmd, addr):
|
|
"""write in byte units.
|
|
"""
|
|
request_data = bytes()
|
|
request_data += STX.to_bytes(1, "big")
|
|
request_data += cmd.to_bytes(1, "big")
|
|
address = "{:x}".format(addr)
|
|
request_data += address.rjust(4, "0").upper().encode()
|
|
request_data += ETX.to_bytes(1, "big")
|
|
request_data += self._encode_value(self._get_sum_check_code(request_data[1[:None]]), "byte")
|
|
self.channel._send(request_data)
|
|
recv_data = self.channel._recv()
|
|
if recv_data != b'':
|
|
check_res = self._check_cmdanswer(recv_data)
|
|
if check_res:
|
|
return ([], check_res)
|
|
return (None, None)
|
|
|
|
def batchwrite_bitunits(self, headdevice, values):
|
|
"""batch read in bit units.
|
|
"""
|
|
for index, value in enumerate(values):
|
|
if not value == 0:
|
|
if not value == 1:
|
|
raise ValueError("Each value must be 0 or 1. 0 is OFF, 1 is ON.")
|
|
write_cmd = 55 if value == 1 else 56
|
|
addess = self._get_write_bit_addr(headdevice, index)
|
|
|
|
recv_data, error = self._write_bit_device(write_cmd, addess)
|
|
if error:
|
|
if error != "No response data.":
|
|
return (
|
|
recv_data, error)
|
|
return (None, None)
|
|
|
|
def get_plc_status(self):
|
|
request_data = bytes()
|
|
request_data += ENQ.to_bytes(1, "big")
|
|
self.channel._send(request_data)
|
|
recv_data = self.channel._recv()
|
|
if recv_data != b'':
|
|
if recv_data[0] == ACK:
|
|
return True
|
|
return False
|