Read power ports' status
This commit is contained in:
parent
164a768d13
commit
16ca41bd5d
1 changed files with 264 additions and 3 deletions
267
stabiliti.py
267
stabiliti.py
|
|
@ -2,11 +2,175 @@ import easymodbus.modbusClient
|
|||
from datetime import datetime as dt
|
||||
from functools import reduce
|
||||
|
||||
nRegsPortStatus = 32
|
||||
|
||||
addrControlAC1 = 64
|
||||
addrStatusAC1 = 96
|
||||
|
||||
addrControlDC2 = 128
|
||||
addrStatusDC2 = 160
|
||||
|
||||
addrControlDC3 = 192
|
||||
addrStatusDC3 = 224
|
||||
|
||||
addrControlSystem = 0x0100
|
||||
addrStatusSystem = 0x0120
|
||||
|
||||
addrStatusThermal = 0x0160
|
||||
|
||||
addrStatusDiagnostic = 0x0180
|
||||
|
||||
p1_port_status = {0: "AC1 port is real power soft-limiting",
|
||||
1: "AC1 port is current soft-limiting",
|
||||
2: "AC1 port is reactive power soft-limiting",
|
||||
3: "AC1 port is power derated (limited) due to high temperature",
|
||||
4: "Reserved",
|
||||
5: "AC1 port is throttling back on port DC2 due to soft-limiting",
|
||||
6: "Reserved",
|
||||
7: "AC1 port is throttling back on port DC3 due to soft-limiting",
|
||||
8: "AC1 port has the seamless transfer feature enabled when in FPWR control",
|
||||
9: "The PCS SEL-547 interface transfer switch HW is indicating the PCS is islanded (islanding contactor is commanded to open). If enabled, the PCS is able to form a microgrid in FPWR control.",
|
||||
10: "The PCS SEL-547 interface transfer switch HW is indicating the PCS is islanded (islanding contactor has successfully opened). If enabled, the PCS is able to form a microgrid in FPWR control.",
|
||||
11: "Reserved",
|
||||
12: "Reserved",
|
||||
13: "Reserved",
|
||||
14: "Reserved",
|
||||
15: "Reserved"}
|
||||
|
||||
p2_port_status = {0: "DC2 port is power soft-limiting",
|
||||
1: "DC2 port is current soft-limiting",
|
||||
2: "Reserved",
|
||||
3: "DC2 port is power derated (limited) due to high temperature",
|
||||
4: "Reserved",
|
||||
5: "DC2 port is throttling back on port AC1 due to soft-limiting",
|
||||
6: "Reserved",
|
||||
7: "DC2 port is throttling back on port DC3 due to soft-limiting",
|
||||
8: "Reserved",
|
||||
9: "Reserved",
|
||||
10: "Reserved",
|
||||
11: "Reserved",
|
||||
12: "Reserved",
|
||||
13: "Reserved",
|
||||
14: "Reserved",
|
||||
15: "Reserved"}
|
||||
|
||||
p3_port_status = {0: "DC3 port is power soft-limiting",
|
||||
1: "DC3 port is current soft-limiting",
|
||||
2: "Reserved",
|
||||
3: "DC3 port is power derated (limited) due to high temperature",
|
||||
4: "Reserved",
|
||||
5: "DC3 port is throttling back on port AC1 due to soft-limiting",
|
||||
6: "Reserved",
|
||||
7: "DC3 port is throttling back on port DC2 due to soft-limiting",
|
||||
8: "Reserved",
|
||||
9: "Reserved",
|
||||
10: "Reserved",
|
||||
11: "Reserved",
|
||||
12: "Reserved",
|
||||
13: "Reserved",
|
||||
14: "Reserved",
|
||||
15: "Reserved"}
|
||||
|
||||
|
||||
def toPower(x):
|
||||
return x * 10
|
||||
|
||||
def toVar(x):
|
||||
return x * 10
|
||||
|
||||
def toVa(x):
|
||||
return x * 10
|
||||
|
||||
def toPwrFactor(x):
|
||||
return x * 0.01
|
||||
|
||||
def toCurrent(x):
|
||||
return x * 0.1
|
||||
|
||||
def toVoltage(x):
|
||||
return x
|
||||
|
||||
def toFreq(x):
|
||||
return x * 0.001
|
||||
|
||||
def toTemperature(x):
|
||||
return x * 0.1
|
||||
|
||||
def toRpm(x):
|
||||
return x
|
||||
|
||||
def toMinutes(x):
|
||||
return x
|
||||
|
||||
def toTime(x):
|
||||
return x // 60, x % 60
|
||||
|
||||
def toBaud(x):
|
||||
return x * 100
|
||||
|
||||
def toString(r):
|
||||
ms = r >> 8
|
||||
ls = (r & 0xff)
|
||||
return chr(ms) + chr(ls)
|
||||
|
||||
def toBitString(r):
|
||||
return bin(r)[2:].zfill(16)
|
||||
|
||||
def selectMessages (bits, msgDict):
|
||||
msgs = []
|
||||
for i, bit in enumerate(bits[::-1]):
|
||||
if bit == '1':
|
||||
msgs.append(msgDict[i])
|
||||
return msgs
|
||||
|
||||
def parse_address (addr_string):
|
||||
import re
|
||||
m = re.findall(r'0x([0-9A-F]+)', addr_string)
|
||||
|
||||
if len(m) > 1:
|
||||
start, end = m
|
||||
elif len(m) > 0:
|
||||
start = m[0]
|
||||
end = start
|
||||
else:
|
||||
raise ValueError()
|
||||
|
||||
nregisters = int(end, 16) - int(start, 16) + 1
|
||||
|
||||
return int(start, 16), nregisters
|
||||
|
||||
|
||||
class StabilitiRegister(object):
|
||||
def __init__(self, name, proplist):
|
||||
self.name = name
|
||||
self.index = proplist[0]
|
||||
self.address, self.size = parse_address(proplist[1])
|
||||
self.access = proplist[2]
|
||||
self.dtype = proplist[3]
|
||||
self.vrange = proplist[4]
|
||||
self.vdefault = proplist[5]
|
||||
|
||||
def isreadonly(self):
|
||||
return self.is_readable() and (not self.is_writable())
|
||||
|
||||
def is_readable(self):
|
||||
return 'R' in self.access
|
||||
|
||||
def is_writable(self):
|
||||
return 'W' in self.access
|
||||
|
||||
def getdefaultvalue(self):
|
||||
if self.vdefault = 'NA':
|
||||
return None
|
||||
else:
|
||||
return self.vdefault
|
||||
|
||||
class StabilitiController(object):
|
||||
''''''
|
||||
def __init__(self, ipaddr, port=502, uid=240):
|
||||
self.client = easymodbus.modbusClient.ModbusClient(ipaddr, port)
|
||||
self.client.unitidentifier = uid
|
||||
self.reg_info = {}
|
||||
|
||||
def checkConnect(self):
|
||||
if not self.client.is_connected():
|
||||
|
|
@ -17,9 +181,7 @@ class StabilitiController(object):
|
|||
self.client.close()
|
||||
|
||||
def reg2str(self, r):
|
||||
ms = (r & 0xff00) >> 8
|
||||
ls = (r & 0xff)
|
||||
return chr(ms) + chr(ls)
|
||||
return toString(r)
|
||||
|
||||
def getNetworkConfigs(self, config=None):
|
||||
self.checkConnect()
|
||||
|
|
@ -128,5 +290,104 @@ b3-b4: The status of the fault.
|
|||
pass
|
||||
|
||||
def getSystemStatus(self):
|
||||
'''Get PCS Status'''
|
||||
'''\
|
||||
system_status[0] – PCS in self-test mode
|
||||
system_status[1] – PCS reconnect timer 0 counting down from last disconnect due to ABORT-0 fault
|
||||
system_status[2] – PCS reconnect timer 1 counting down from last disconnect due to ABORT-1 fault
|
||||
system_status[3] – PCS reconnect timer 2 counting down from last disconnect due to ABORT-2 fault
|
||||
system_status[4] – PCS DC2 port pre-charge operation is active
|
||||
system_status[5] – Invalid control method programmed in PCS
|
||||
system_status[6] – AC rotation indication ( 0 – AC1 port wired as A-B-C | 1 – AC1 port wired as C-B-A )
|
||||
system_status[7] – PV/MPPT low-voltage indication ( 0 – PV/MPPT is disabled due to low voltage at port | 1 – PV/MPPT is enabled with voltage above minimum PV/MPPT limit set for port )
|
||||
system_status[8] – PV/MPPT time-of-day indication ( 0 – System time-of-day outside PV/MPPT operational range | 1 – System time-of-day satisfies start-time and stop-time limits set for PV/MPPT port, PV/MPPT port allowed to convert power )
|
||||
system_status[9] – PCS power conversion is active
|
||||
system_status[10] – PCS hardware shutdown function is active (power conversion disabled)
|
||||
system_status[11] – PCS lockdown active due to GFDI fault, IMI fault, or fan fault
|
||||
system_status[12] – PCS fault of severity level ABORT-0 active
|
||||
system_status[13] – PCS fault of severity level ABORT-1 active
|
||||
system_status[14] – PCS fault of severity level ABORT-2 active
|
||||
system_status[15] – PCS GFDI fault or IMI fault detected
|
||||
'''
|
||||
regs = self.client.read_holdingregisters(addrStatusSystem, nRegsPortStatus)
|
||||
pass
|
||||
|
||||
def getPortStatusAC1(self):
|
||||
'''Get Status of AC1 Power Port'''
|
||||
|
||||
def relAddr(x):
|
||||
return x - addrStatusAC1
|
||||
|
||||
regs = self.client.read_holdingregisters(addrStatusAC1, nRegsPortStatus)
|
||||
|
||||
p1_all_status = {"p1_port_status": toBitString(regs[relAddr(96)]),
|
||||
"p1_real_pwr_ramped": toPower(regs[relAddr(100)]),
|
||||
"p1_reactive_pwr_ramped": toVar(regs[relAddr(101)]),
|
||||
"p1_frequency": toFreq(regs[relAddr(105)]),
|
||||
"p1_v_ab_rms": toVoltage(regs[relAddr(109)]),
|
||||
"p1_v_bc_rms": toVoltage(regs[relAddr(110)]),
|
||||
"p1_v_ca_rms": toVoltage(regs[relAddr(111)]),
|
||||
"p1_v_an_rms": toVoltage(regs[relAddr(112)]),
|
||||
"p1_v_bn_rms": toVoltage(regs[relAddr(113)]),
|
||||
"p1_v_cn_rms": toVoltage(regs[relAddr(114)]),
|
||||
"p1_power_factor": toPwrFactor(regs[relAddr(118)]),
|
||||
"p1_real_power": toPower(regs[relAddr(119)]),
|
||||
"p1_reactive_power": toVar(regs[relAddr(120)]),
|
||||
"p1_apparent_power": toVa(regs[relAddr(121)]),
|
||||
"p1_i_a_int_rms": toCurrent(regs[relAddr(122)]),
|
||||
"p1_i_b_int_rms": toCurrent(regs[relAddr(123)]),
|
||||
"p1_i_c_int_rms": toCurrent(regs[relAddr(124)]),
|
||||
"p1_i_a_ext_rms": toCurrent(regs[relAddr(125)]),
|
||||
"p1_i_b_ext_rms": toCurrent(regs[relAddr(126)]),
|
||||
"p1_i_c_ext_rms": toCurrent(regs[relAddr(127)])}
|
||||
|
||||
status = p1_all_status['p1_port_status']
|
||||
|
||||
return selectMessages(status, p1_port_status), p1_all_status
|
||||
|
||||
def getPortStatusDC2(self):
|
||||
'''Get Status of DC2 Power Port'''
|
||||
|
||||
def relAddr(x):
|
||||
return x - addrStatusDC2
|
||||
|
||||
regs = self.client.read_holdingregisters(addrStatusDC2, nRegsPortStatus)
|
||||
|
||||
p2_all_status = {'p2_port_status': toBitString(regs[relAddr(160)]),
|
||||
'p2_current_ramped': toCurrent(regs[relAddr(164)]),
|
||||
'p2_power_ramped': toPower(regs[relAddr(165)]),
|
||||
'p2_voltage_ramped': toVoltage(regs[relAddr(166)]),
|
||||
'pv_tod_stat': toMinutes(regs[relAddr(167)]),
|
||||
'p2_pv_restart_stat': regs[relAddr(168)],
|
||||
'p2_v_pn': toVoltage(regs[relAddr(173)]),
|
||||
'p2_v_pg': toVoltage(regs[relAddr(176)]),
|
||||
'dc_com_voltage': toVoltage(regs[relAddr(177)]),
|
||||
'p2_power': toPower(regs[relAddr(185)]),
|
||||
'p2_current': toCurrent(regs[relAddr(186)])}
|
||||
|
||||
status = p2_all_status['p2_port_status']
|
||||
|
||||
return selectMessages(status, p2_port_status), p2_all_status
|
||||
|
||||
def getPortStatusDC3(self):
|
||||
'''Get Status of DC3 Power Port'''
|
||||
|
||||
def relAddr(x):
|
||||
return x - addrStatusDC3
|
||||
|
||||
regs = self.client.read_holdingregisters(addrStatusDC3, nRegsPortStatus)
|
||||
|
||||
p3_all_status = {'p3_port_status': toBitString(regs[relAddr(224)]),
|
||||
'p3_current_ramped': toCurrent(regs[relAddr(228)]),
|
||||
'p3_power_ramped': toPower(regs[relAddr(229)]),
|
||||
'p3_voltage_ramped': toVoltage(regs[relAddr(230)]),
|
||||
'p3_pv_restart_stat': regs[relAddr(232)],
|
||||
'p3_v_pn': toVoltage(regs[relAddr(237)]),
|
||||
'p3_v_pg': toVoltage(regs[relAddr(240)]),
|
||||
'p3_power': toPower(regs[relAddr(249)]),
|
||||
'p3_current': toCurrent(regs[relAddr(250)])}
|
||||
|
||||
status = p3_all_status['p3_port_status']
|
||||
|
||||
return selectMessages(status, p3_port_status), p3_all_status
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue