TG-PlatformPlus/UserScripts/sapcs.py

725 lines
26 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import os, time, csv, struct, sys
import numpy as np
sys.path.append(r'.\UserScripts')
from user_common import wordData2HexStr, nowStr, nowStr1, checkValue, params_to_int
from CRC import crc16 as checkValue
SENDHEADER = ''
RSPHEADER = ''
RETRYTIMES = 3
CYCLES = 1
TIMEOUT = 0.2
DEBUG = True
# 启动函数,命令开始会调用此函数
def start():
global recvData, deviceData
try:
log_i(f"[{devInfo['name']}] {cmdInfo['name']}".center(80, '-'))
deviceData = _G.get(f"{devInfo['name']}")
if not deviceData:
deviceData = {}
if 'data' not in deviceData:
deviceData['data'] = {}
if 'info' not in deviceData:
deviceData['info'] = {}
if 'status' not in deviceData:
deviceData['status'] = {}
if 'config' not in deviceData:
deviceData['config'] = {}
if 'channelsInfoTable' not in deviceData:
channelsInfoTable = {
'channel1':{
'name':'channel1',
'powerStatus':{
'value':0,
'address':0x0054,
'mask':0x0001,
'len':1
},
'signalVoltage':{
'value':1,
'address':0x0020,
'len':2
},
'powerCurrent':{
'value':1,
'address':0x0030,
'len':2
},
'powerVoltage':{
'value':1,
'address':0x0032,
'len':2
},
'signalVoltageCalibParams':{
'scale':{
'value':1,
'address':0x0060,
'len':1
},
'b':{
'value':0,
'address':0x0068,
'len':1
},
'unit':'uV'
},
'powerCurrentCalibParams':{
'scale':{
'value':1,
'address':0x0070,
'len':1
},
'b':{
'value':0,
'address':0x0080,
'len':1
},
'unit':'uA'
},
'powerVoltageCalibParams':{
'scale':{
'value':1,
'address':0x0071,
'len':1
},
'b':{
'value':0,
'address':0x0081,
'len':1
},
'unit':'mV',
}
},
'channel2':{
'name':'channel2',
'powerStatus':{
'value':0,
'address':0x0054,
'mask':0x0002,
'len':1
},
'signalVoltage':{
'value':1,
'address':0x0022,
'len':2
},
'powerCurrent':{
'value':1,
'address':0x0034,
'len':2
},
'powerVoltage':{
'value':1,
'address':0x0036,
'len':2
},
'signalVoltageCalibParams':{
'scale':{
'value':1,
'address':0x0061,
'len':1
},
'b':{
'value':0,
'address':0x0069,
'len':1
},
'unit':'uV'
},
'powerCurrentCalibParams':{
'scale':{
'value':1,
'address':0x0072,
'len':1
},
'b':{
'value':0,
'address':0x0082,
'len':1
},
'unit':'uA'
},
'powerVoltageCalibParams':{
'scale':{
'value':1,
'address':0x0073,
'len':1
},
'b':{
'value':0,
'address':0x0083,
'len':1
},
'unit':'mV',
}
},
'channel3':{
'name':'channel3',
'powerStatus':{
'value':0,
'address':0x0054,
'mask':0x0004,
'len':1
},
'signalVoltage':{
'value':1,
'address':0x0024,
'len':2
},
'powerCurrent':{
'value':1,
'address':0x0038,
'len':2
},
'powerVoltage':{
'value':1,
'address':0x003A,
'len':2
},
'signalVoltageCalibParams':{
'scale':{
'value':1,
'address':0x0062,
'len':1
},
'b':{
'value':0,
'address':0x006A,
'len':1
},
'unit':'uV'
},
'powerCurrentCalibParams':{
'scale':{
'value':1,
'address':0x0074,
'len':1
},
'b':{
'value':0,
'address':0x0084,
'len':1
},
'unit':'uA'
},
'powerVoltageCalibParams':{
'scale':{
'value':1,
'address':0x0075,
'len':1
},
'b':{
'value':0,
'address':0x0085,
'len':1
},
'unit':'mV',
}
},
'channel4':{
'name':'channel4',
'powerStatus':{
'value':0,
'address':0x0054,
'mask':0x0008,
'len':1
},
'signalVoltage':{
'value':1,
'address':0x0026,
'len':2
},
'powerCurrent':{
'value':1,
'address':0x003C,
'len':2
},
'powerVoltage':{
'value':1,
'address':0x003E,
'len':2
},
'signalVoltageCalibParams':{
'scale':{
'value':1,
'address':0x0063,
'len':1
},
'b':{
'value':0,
'address':0x006B,
'len':1
},
'unit':'uV'
},
'powerCurrentCalibParams':{
'scale':{
'value':1,
'address':0x0076,
'len':1
},
'b':{
'value':0,
'address':0x0086,
'len':1
},
'unit':'uA'
},
'powerVoltageCalibParams':{
'scale':{
'value':1,
'address':0x0077,
'len':1
},
'b':{
'value':0,
'address':0x0087,
'len':1
},
'unit':'mV',
}
},
'channel5':{
'name':'channel5',
'powerStatus':{
'value':0,
'address':0x0054,
'mask':0x0010,
'len':1
},
'signalVoltage':{
'value':1,
'address':0x0028,
'len':2
},
'powerCurrent':{
'value':1,
'address':0x0040,
'len':2
},
'powerVoltage':{
'value':1,
'address':0x0042,
'len':2
},
'signalVoltageCalibParams':{
'scale':{
'value':1,
'address':0x0064,
'len':1
},
'b':{
'value':0,
'address':0x006C,
'len':1
},
'unit':'uV'
},
'powerCurrentCalibParams':{
'scale':{
'value':1,
'address':0x0078,
'len':1
},
'b':{
'value':0,
'address':0x0088,
'len':1
},
'unit':'uA'
},
'powerVoltageCalibParams':{
'scale':{
'value':1,
'address':0x0079,
'len':1
},
'b':{
'value':0,
'address':0x0089,
'len':1
},
'unit':'mV',
}
},
'channel6':{
'name':'channel6',
'powerStatus':{
'value':0,
'address':0x0054,
'mask':0x0020,
'len':1
},
'signalVoltage':{
'value':1,
'address':0x002A,
'len':2
},
'powerCurrent':{
'value':1,
'address':0x0044,
'len':2
},
'powerVoltage':{
'value':1,
'address':0x0046,
'len':2
},
'signalVoltageCalibParams':{
'scale':{
'value':1,
'address':0x0065,
'len':1
},
'b':{
'value':0,
'address':0x006D,
'len':1
},
'unit':'uV'
},
'powerCurrentCalibParams':{
'scale':{
'value':1,
'address':0x007A,
'len':1
},
'b':{
'value':0,
'address':0x008A,
'len':1
},
'unit':'uA'
},
'powerVoltageCalibParams':{
'scale':{
'value':1,
'address':0x007B,
'len':1
},
'b':{
'value':0,
'address':0x008B,
'len':1
},
'unit':'mV',
}
},
'channel7':{
'name':'channel7',
'powerStatus':{
'value':0,
'address':0x0054,
'mask':0x0040,
'len':1
},
'signalVoltage':{
'value':1,
'address':0x002C,
'len':2
},
'powerCurrent':{
'value':1,
'address':0x0048,
'len':2
},
'powerVoltage':{
'value':1,
'address':0x004A,
'len':2
},
'signalVoltageCalibParams':{
'scale':{
'value':1,
'address':0x0066,
'len':1
},
'b':{
'value':0,
'address':0x006E,
'len':1
},
'unit':'uV'
},
'powerCurrentCalibParams':{
'scale':{
'value':1,
'address':0x007C,
'len':1
},
'b':{
'value':0,
'address':0x008C,
'len':1
},
'unit':'uA'
},
'powerVoltageCalibParams':{
'scale':{
'value':1,
'address':0x007D,
'len':1
},
'b':{
'value':0,
'address':0x008D,
'len':1
},
'unit':'mV',
}
},
'channel8':{
'name':'channel8',
'powerStatus':{
'value':0,
'address':0x0054,
'mask':0x0080,
'len':1
},
'signalVoltage':{
'value':1,
'address':0x002E,
'len':2
},
'powerCurrent':{
'value':1,
'address':0x004C,
'len':2
},
'powerVoltage':{
'value':1,
'address':0x004E,
'len':2
},
'signalVoltageCalibParams':{
'scale':{
'value':1,
'address':0x0067,
'len':1
},
'b':{
'value':0,
'address':0x006F,
'len':1
},
'unit':'uV'
},
'powerCurrentCalibParams':{
'scale':{
'value':1,
'address':0x007E,
'len':1
},
'b':{
'value':0,
'address':0x008E,
'len':1
},
'unit':'uA'
},
'powerVoltageCalibParams':{
'scale':{
'value':1,
'address':0x007F,
'len':1
},
'b':{
'value':0,
'address':0x008F,
'len':1
},
'unit':'mV',
}
}
}
deviceData['channelsInfoTable'] = channelsInfoTable
else:
channelsInfoTable = deviceData['channelsInfoTable']
deviceData['status']['StopAll'] = False
_G.set(devInfo['name'], deviceData)
except Exception as e:
log_e(f"Error in start. {str(e)}")
finish()
# 此函数会被重复调用间隔10毫秒直到finish()
def loop():
global recvData, deviceData
try:
read_device_info()
finish()
except Exception as e:
log_e(f"Error in loop. {str(e)}")
finish()
# 接收数据处理函数,当收到数据会调用此函数
def recvDataHandler(data):
global recvData
try:
recvData = recvData + data
except Exception as e:
log_e(f"Error in recvDataHandler. {str(e)}")
finish()
def log_du(str):
try:
deviceData = _G.get(f"{devInfo['name']}")
if (deviceData and 'config' in deviceData and 'flagDebug' in deviceData['config']):
debug = deviceData['config']['flagDebug']
else:
debug = False
if debug or DEBUG:
log_d(str)
except Exception as e:
raise Exception(f"Error in log_du(): {str(e)}")
def set_flagDebug(flag):
try:
deviceData = _G.get(f"{devInfo['name']}")
if not deviceData:
deviceData = {}
if 'config' not in deviceData:
deviceData['config'] = {}
deviceData['config']['flagDebug'] = flag
_G.set(devInfo['name'], deviceData)
return True
except Exception as e:
log_e(f"Error in set_flagDebug(): {str(e)}")
return False
def exeCmd(cmd):
try:
global recvData
data = bytearray().fromhex(cmd[0])
data += bytearray(checkValue(data).to_bytes(2, 'little'))
for i in range(RETRYTIMES):
log_du(f"[{nowStr()}] Sent:{wordData2HexStr(data)}")
recvData = bytearray()
send(data)
time.sleep(TIMEOUT)
rspLen = int(cmd[1])
if len(recvData) >= rspLen:
log_du(f"[{nowStr()}] Echo:{wordData2HexStr(recvData[0:rspLen])}")
crc = int.from_bytes(recvData[rspLen-2:rspLen], byteorder='little')
calc_value = checkValue(recvData[0:rspLen-2])
# log(f"{crc:04X}, {calc_value:04X}")
if crc == calc_value:
return [True, recvData[0:rspLen]]
return [False, None]
except Exception as e:
raise Exception(f"Error in exeCmd({cmd}): {str(e)}")
def read_registers(startAddress, count):
try:
ret = exeCmd([f"0103 {startAddress:04X} {count:04X}", count*2+6])
if ret[0]:
registers = {}
# log(str(ret[1]))
for i in range(count):
registers[f"0x{startAddress+i:04X}"] = ret[1][4+2*i]*256 + ret[1][4+2*i+1]
return [0, registers]
else:
return [-1, None]
except Exception as e:
raise Exception(f"Error in read_registers(): {str(e)}")
def update_dict_from_registers(dictData, registers):
try:
if not dictData or not registers:
return False
# log(str(dictData))
# log(str(registers))
for key, value in dictData.items():
# log(f"{str(key)}, {str(value)}")
if isinstance(value, dict):
if 'address' in value:
if f"0x{value['address']:04X}" in registers:
value['value'] = 0
for i in range(value['len']):
value['value'] = (value['value']<<16) + registers[f"0x{value['address']+i:04X}"]
# log(f"0x{value['address']+i:04X}")
# log(str(registers[f"0x{value['address']+i:04X}"]))
if 'mask' in value:
value['value'] = value['value'] & value['mask']
# if key == 'powerStatus':
# log(f"{value['value']}, {value['mask']}, {i}")
else:
update_dict_from_registers(value, registers)
else:
pass
# log(str(dictData))
return True
except Exception as e:
raise Exception(f"Error in update_dict_from_registers(): {str(e)}")
def read_device_info():
try:
deviceData = _G.get(f"{devInfo['name']}")
if not deviceData or 'channelsInfoTable' not in deviceData:
return False
else:
channelsInfoTable = deviceData['channelsInfoTable']
registers = {}
#读取0x0020~0x004F寄存器并存为字典
registers1 = read_registers(0x0020, 0x0030)
registers2 = read_registers(0x0054, 0x0001)
#读取0x0060~0x008F寄存器并存为字典
registers3 = read_registers(0x0060, 0x0020)
if registers1[0] == 0 and registers2[0] == 0 and registers3[0] == 0:
registers = {**registers1[1], **registers2[1], **registers3[1]}
log(str(registers))
#从寄存器数据字典中提取数据存储至channelsInfoTable中
update_dict_from_registers(channelsInfoTable, registers)
deviceData['channelsInfoTable'] = channelsInfoTable
_G.set(devInfo['name'], deviceData)
log(str(deviceData))
#显示channelsInfoTable中的数据
except Exception as e:
log_e(f"Error in read_device_info(): {str(e)}")
return False
def write_registers_from_dict(dictData, registers):
try:
if not dictData or not registers:
return False
# log(str(dictData))
# log(str(registers))
for key, value in dictData.items():
# log(f"{str(key)}, {str(value)}")
if isinstance(value, dict):
if 'address' in value:
if f"0x{value['address']:04X}" in registers:
if 'mask' in value: #执行指令读取当前配置根据mask, 将要写入的位与当前配置做运算,再执行写配置指令
if value['len'] >= 1:
ret = exeCmd([f"0103 {value['address']:04X} {value['len']:04X}", 6+value['len']*2])
if ret[0]:
data = 0
for i in range(value['len']):
data = data<<16 + int(ret[1][4+2*i:6+2*i],16)
ret = exeCmd([f"0110 {value['address']:04X} {value['len']:04X} {data|value['mask']:0{value['len']*4}X}", 6+value['len']*2])
if not ret[0]:
raise Exception(f"fail to write registers({value['address']:04X}, {value['len']}).")
else:
raise Exception(f"fail to read registers({value['address']:04X}, {value['len']}).")
else:
raise Exception(f"len={str(value['len'])} is error. ")
else: #执行写入指令
if value['len'] >= 1:
ret = exeCmd([f"0110 {value['address']:04X} {value['len']:04X} {value['value']:0{value['len']*4}X}", 6+value['len']*2])
if not ret[0]:
raise Exception(f"fail to write registers({value['address']:04X}, {value['len']}).")
else:
raise Exception(f"len={str(value['len'])} is error. ")
else:
write_registers_from_dict(value, registers)
else:
pass
# log(str(dictData))
return True
except Exception as e:
raise Exception(f"Error in write_registers_from_dict(): {str(e)}")