commit 64580f8f8a181c5ab82d5bc02c9948a68002ab99 Author: Yeongdo Park Date: Mon Feb 10 20:18:39 2020 -0800 Initial Commit - Fault Monitoring diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0727d3b --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/venv/ +*.bak +/__pycache__/ \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..5c98b42 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,2 @@ +# Default ignored files +/workspace.xml \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..6a89c15 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modbus-test.iml b/.idea/modbus-test.iml new file mode 100644 index 0000000..74d515a --- /dev/null +++ b/.idea/modbus-test.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..4afdc56 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..8306744 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/FaultMonitor.py b/FaultMonitor.py new file mode 100644 index 0000000..fb39801 --- /dev/null +++ b/FaultMonitor.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +# +# generated by wxGlade 0.9.5 on Fri Feb 7 20:10:41 2020 +# + +import wx +import wx.grid + +import gettext +gettext.install(None) + +from pubsub import pub + +print('pubsub API version', pub.VERSION_API) + +from pubsub.utils.notification import useNotifyByWriteFile +import sys + +useNotifyByWriteFile(sys.stdout) + +import pprint + +from stabiliti import StabilitiController as SC +from faults import faultInfo +from FaultView import FaultFrame + +class Controller: + def __init__(self, view): + self.sc = None + self.view = view + self.timer = wx.Timer() + self.timer.Bind(wx.EVT_TIMER, self.OnTimer) + + pub.subscribe(self.getFaultInfo, 'fault_selected') + pub.subscribe(self.startTimer, 'monitoring_started') + pub.subscribe(self.stopTimer, 'monitoring_stopped') + pub.subscribe(self.applyNetworkConfigs, 'apply_network_configs') + pub.subscribe(self.resetNetworkConfigs, 'reset_network_configs') + + def startTimer(self): + self.timer.Start(2000) + + def stopTimer(self): + self.timer.Stop() + + def applyNetworkConfigs(self, ncDict): + if self.sc: + self.sc.checkClose() + self.sc = SC(**ncDict) + + def resetNetworkConfigs(self): + if self.sc: + self.sc.checkClose() + self.sc = None + + def getFaultInfo(self, pos): + y, x = pos + fid = x + y*8 + if 0 <= fid <= 63: + try: + info_text = faultInfo[fid] + except IndexError: + info_text = "Fault Number: {} (Reserved)".format(fid) + self.view.text_fault_doc.SetValue(info_text) + + status_text = "Not Connected" + if self.sc: + fDict = self.sc.readFaultDetail(fid) + status_text = pprint.pformat(fDict) + self.view.text_fault_status.SetValue(status_text) + + def OnTimer(self, event): + factivity = self.sc.readFaultActivity() + foccurence = self.sc.readFaultOccurence() + self.view.SetFaultGrid(factivity, foccurence) + event.Skip() + +class MyFrame(FaultFrame): + def __init__(self, *args, **kwds): + FaultFrame.__init__(self, *args, **kwds) + + def OnFaultSelect(self, event): # wxGlade: MyFrame. + pub.sendMessage("fault_selected", pos=(event.GetRow(), event.GetCol())) + event.Skip() + + def SetFaultGrid(self, fActivity, fOccurence): + if len(fActivity) == len(fOccurence) == 64: + for i in range(8): + for j in range(8): + if fActivity[i*8+j] == '1': + self.fault_grid.SetCellBackgroundColour(i, j, wx.LIGHT_GREY) + if fOccurence[i*8+j] == '1': + self.fault_grid.SetCellValue(i, j, "O") + + def ToggleMonitoring(self, event): # wxGlade: MyFrame. + btnLabel = self.button_1.GetLabel() + if self.button_1.Value: + pub.sendMessage("monitoring_started") + self.button_1.SetLabel("Stop") + else: + pub.sendMessage("monitoring_stopped") + self.button_1.SetLabel("Start") + + if event: + event.Skip() + + def OnApply(self, event): + + if self.button_1.Enabled: + return + + self.input_uid.Disable() + self.input_port.Disable() + self.input_ip.Disable() + self.input_refresh.Disable() + + ncDict = { + "ipaddr": self.input_ip.GetValue(), + "port": int(self.input_port.GetValue()), + "uid": int(self.input_uid.GetValue()), + } + + pub.sendMessage("apply_network_configs", ncDict=ncDict) + + self.button_1.Enable() + + def OnReset(self, event): + + if not self.button_1.Enabled: + return + + pub.sendMessage("reset_network_configs") + self.button_1.SetValue(False) + self.ToggleMonitoring(None) + + self.button_1.Disable() + + self.input_uid.Enable() + self.input_port.Enable() + self.input_ip.Enable() + self.input_refresh.Enable() + + +# end of class MyFrame + +class MyApp(wx.App): + def OnInit(self): + self.frame = MyFrame(None, wx.ID_ANY, "") + self.c = Controller(self.frame) + self.SetTopWindow(self.frame) + self.frame.Show() + return True + +# end of class MyApp + +if __name__ == "__main__": + app = MyApp(0) + app.MainLoop() + app.c.sc.checkClose() diff --git a/FaultView.py b/FaultView.py new file mode 100644 index 0000000..757bc37 --- /dev/null +++ b/FaultView.py @@ -0,0 +1,122 @@ +# -*- coding: UTF-8 -*- +# +# generated by wxGlade 0.9.5 on Mon Feb 10 19:42:48 2020 +# + +import wx +import wx.grid + +# begin wxGlade: dependencies +import gettext +# end wxGlade + +# begin wxGlade: extracode +# end wxGlade + + +class FaultFrame(wx.Frame): + def __init__(self, *args, **kwds): + # begin wxGlade: FaultFrame.__init__ + kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE + wx.Frame.__init__(self, *args, **kwds) + self.SetSize((632, 556)) + self.input_ip = wx.TextCtrl(self, wx.ID_ANY, _("192.168.1.21")) + self.input_port = wx.TextCtrl(self, wx.ID_ANY, _("502")) + self.input_uid = wx.TextCtrl(self, wx.ID_ANY, _("240")) + self.input_refresh = wx.TextCtrl(self, wx.ID_ANY, _("2000")) + self.reset_button = wx.Button(self, wx.ID_ANY, _("Reset")) + self.apply_button = wx.Button(self, wx.ID_ANY, _("Apply")) + self.fault_grid = wx.grid.Grid(self, wx.ID_ANY, size=(1, 1)) + self.button_1 = wx.ToggleButton(self, wx.ID_ANY, _("Start")) + self.text_fault_doc = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_MULTILINE | wx.TE_READONLY) + self.text_fault_status = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_MULTILINE | wx.TE_READONLY) + + self.__set_properties() + self.__do_layout() + + self.Bind(wx.EVT_BUTTON, self.OnReset, self.reset_button) + self.Bind(wx.EVT_BUTTON, self.OnApply, self.apply_button) + self.Bind(wx.grid.EVT_GRID_CMD_CELL_LEFT_CLICK, self.OnFaultSelect, self.fault_grid) + self.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleMonitoring, self.button_1) + # end wxGlade + + def __set_properties(self): + # begin wxGlade: FaultFrame.__set_properties + self.SetTitle(_("frame")) + self.fault_grid.CreateGrid(8, 8) + self.fault_grid.SetRowLabelSize(30) + self.fault_grid.SetColLabelSize(30) + self.fault_grid.EnableEditing(0) + self.fault_grid.EnableDragColSize(0) + self.fault_grid.EnableDragRowSize(0) + self.fault_grid.EnableDragGridSize(0) + self.fault_grid.SetColSize(0, 30) + self.fault_grid.SetColSize(1, 30) + self.fault_grid.SetColSize(2, 30) + self.fault_grid.SetColSize(3, 30) + self.fault_grid.SetColSize(4, 30) + self.fault_grid.SetColSize(5, 30) + self.fault_grid.SetColSize(6, 30) + self.fault_grid.SetColSize(7, 30) + self.fault_grid.SetRowSize(0, 30) + self.fault_grid.SetRowSize(1, 30) + self.fault_grid.SetRowSize(2, 30) + self.fault_grid.SetRowSize(3, 30) + self.fault_grid.SetRowSize(4, 30) + self.fault_grid.SetRowSize(5, 30) + self.fault_grid.SetRowSize(6, 30) + self.fault_grid.SetRowSize(7, 30) + self.fault_grid.SetMinSize((270, 270)) + self.button_1.Enable(False) + self.text_fault_doc.SetMinSize((-1, 120)) + self.text_fault_status.SetMinSize((-1, 120)) + # end wxGlade + + def __do_layout(self): + # begin wxGlade: FaultFrame.__do_layout + sizer_1 = wx.BoxSizer(wx.HORIZONTAL) + sizer_3 = wx.BoxSizer(wx.VERTICAL) + sizer_2 = wx.BoxSizer(wx.HORIZONTAL) + sizer_4 = wx.GridSizer(5, 2, 0, 0) + label_1 = wx.StaticText(self, wx.ID_ANY, _("IP Address")) + sizer_4.Add(label_1, 0, 0, 0) + sizer_4.Add(self.input_ip, 0, 0, 0) + label_2 = wx.StaticText(self, wx.ID_ANY, _("Port")) + sizer_4.Add(label_2, 0, 0, 0) + sizer_4.Add(self.input_port, 0, 0, 0) + label_3 = wx.StaticText(self, wx.ID_ANY, _("Unit ID")) + sizer_4.Add(label_3, 0, 0, 0) + sizer_4.Add(self.input_uid, 0, 0, 0) + label_4 = wx.StaticText(self, wx.ID_ANY, _("Refresh Period (ms)")) + sizer_4.Add(label_4, 0, 0, 0) + sizer_4.Add(self.input_refresh, 0, 0, 0) + sizer_4.Add(self.reset_button, 0, 0, 0) + sizer_4.Add(self.apply_button, 0, 0, 0) + sizer_1.Add(sizer_4, 1, wx.EXPAND, 0) + sizer_2.Add(self.fault_grid, 0, wx.ALIGN_CENTER, 0) + sizer_2.Add(self.button_1, 0, wx.ALIGN_CENTER, 0) + sizer_3.Add(sizer_2, 1, wx.ALIGN_CENTER, 0) + sizer_3.Add(self.text_fault_doc, 0, wx.EXPAND, 0) + sizer_3.Add(self.text_fault_status, 0, wx.EXPAND, 0) + sizer_1.Add(sizer_3, 1, wx.EXPAND, 0) + self.SetSizer(sizer_1) + self.Layout() + # end wxGlade + + def OnReset(self, event): # wxGlade: FaultFrame. + print("Event handler 'OnReset' not implemented!") + event.Skip() + + def OnApply(self, event): # wxGlade: FaultFrame. + print("Event handler 'OnApply' not implemented!") + event.Skip() + + def OnFaultSelect(self, event): # wxGlade: FaultFrame. + print("Event handler 'OnFaultSelect' not implemented!") + event.Skip() + + def ToggleMonitoring(self, event): # wxGlade: FaultFrame. + print("Event handler 'ToggleMonitoring' not implemented!") + event.Skip() + +# end of class FaultFrame diff --git a/faultView.wxg b/faultView.wxg new file mode 100644 index 0000000..8473ff6 --- /dev/null +++ b/faultView.wxg @@ -0,0 +1,184 @@ + + + + + + 632, 556 + frame + + + wxHORIZONTAL + + + 0 + wxEXPAND + + 5 + 2 + 0 + 0 + + + 0 + + + + + + + 0 + + 192.168.1.21 + + + + + 0 + + + + + + + 0 + + 502 + + + + + 0 + + + + + + + 0 + + 240 + + + + + 0 + + + + + + + 0 + + 2000 + + + + + 0 + + + OnReset + + + + + + + 0 + + + OnApply + + + + + + + + + 0 + wxEXPAND + + wxVERTICAL + + + 0 + wxALIGN_CENTER + + wxHORIZONTAL + + + 0 + wxALIGN_CENTER + + + OnFaultSelect + + 270, 270 + 1 + + A + B + C + D + E + F + G + H + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + + 0 + 1 + 0 + 0 + 0 + wxGrid.wxGridSelectCells + + + + + 0 + wxALIGN_CENTER + + + ToggleMonitoring + + 1 + + + + + + + + 0 + wxEXPAND + + -1, 120 + + + + + + 0 + wxEXPAND + + -1, 120 + + + + + + + + diff --git a/faults.py b/faults.py new file mode 100644 index 0000000..57d58c8 --- /dev/null +++ b/faults.py @@ -0,0 +1,372 @@ +faultInfo = [ +'''\ +Fault Number: 0 +Description: GFDI fault (grounded DC) +Selector Values: +0x01 = GFDI fault detected +0x10 = Loss of communication +Severity: 0x110 = Lockdown +''', +'''\ +Fault Number: 1 +Description: IMI fault (floating DC) +Selector Values: NA +Severity: 0x110 = Lockdown +''', +'''\ +Fault Number: 2 +Description: Power module heatsink temperature fault +Selector Values: +0x00 = Over temperature +0x01 = Under temperature +Severity: 0x100 Abort 1 +''', +'''\ +Fault Number: 3 +Description: Control board temperature fault +Selector Values: +0x00 = Over temperature +0x01 = Under temperature +Severity: 0x100 Abort 1 +''', +'''\ +Fault Number: 4 +Description: 24V auxiliary supply under voltage +Selector Values: NA +Severity: 0x100 Abort 1 +''', +'''\ +Fault Number: 5 +Description: Fan fault +Selector Values: +0x00 = Fan not spinning +0x01 = Bad tach reading +Severity: 0x110 = Lockdown +''', +'''\ +Fault Number: 6 +Description: DC differential over voltage +Selector Values: +0x00 = Port 2 +0x01 = Port 3 +Severity: 0x100 Abort 1 +''', +'''\ +Fault Number: 7 +Description: DC differential under voltage +Selector Values: +0x00 = Port 2 +0x01 = Port 3 +Severity: 0x100 Abort 1 +''', +'''\ +Fault Number: 8 +Description: Link over voltage +Selector Values: NA +Severity: 0x101 Abort 2 +''', +'''\ +Fault Number: 9 +Description: Link starving +Selector Values: NA +Severity: 0x101 Abort 2 +''', +'''\ +Fault Number: 10 +Description: Link over current +Selector Values: NA +Severity: 0x101 Abort 2 +''', +'''\ +Fault Number: 11 +Description: IGBT VCES over voltage 1 +Selector Values: +0x00 = Link positive to phase A +0x01 = Link positive to phase B +0x10 = Link positive to phase C +Severity: 0x101 Abort 2 +''', +'''\ +Fault Number: 12 +Description: IGBT VCES over voltage 2 +Selector Values: +0x00 = Link negative to phase A +0x01 = Link negative to phase B +0x10 = Link negative to phase C +Severity: 0x101 Abort 2 +''', +'''\ +Fault Number: 13 +Description: IGBT VCES over voltage 3 +Selector Values: +0x00 = Link positive to DC port 2 +0x01 = Link positive to DC port 3 +0x10 = Link positive to DC common +Severity: 0x101 Abort 2 +''', +'''\ +Fault Number: 14 +Description: IGBT VCES over voltage 4 +Selector Values: +0x00 = Link positive to DC port 2 +0x01 = Link positive to DC port 3 +0x10 = Link positive to DC common +Severity: 0x101 Abort 2 +''', +'''\ +Fault Number: 15 +Description: AC A-B hard switch +Selector Values: 0x00, 0x01, 0x10, 0x11 +Severity: 0x101 Abort 2 +''', +'''\ +Fault Number: 16 +Description: AC B-C hard switch +Selector Values: 0x00, 0x01, 0x10, 0x11 +Severity: 0x101 Abort 2 +''', +'''\ +Fault Number: 17 +Description: AC C-A hard switch +Selector Values: 0x00, 0x01, 0x10, 0x11 +Severity: 0x101 Abort 2 +''', +'''\ +Fault Number: 18 +Description: DC2 input hard switch +Selector Values: 0x00, 0x01, 0x10, 0x11 +Severity: 0x101 Abort 2 +''', +'''\ +Fault Number: 19 +Description: DC2 output hard switch +Selector Values: 0x00, 0x01, 0x10, 0x11 +Severity: 0x101 Abort 2 +''', +'''\ +Fault Number: 20 +Description: DC3 input hard switch +Selector Values: 0x00, 0x01, 0x10, 0x11 +Severity: 0x101 Abort 2 +''', +'''\ +Fault Number: 21 +Description: DC3 output hard switch +Selector Values: 0x00, 0x01, 0x10, 0x11 +Severity: 0x101 Abort 2 +''', +'''\ +Fault Number: 22 +Description: Link state timer check +Selector Values: NA +Severity: 0x101 Abort 2 +''', +'''\ +Fault Number: 23 +Description: Bad link start +Selector Values: 0x00, 0x01, 0x10, 0x11 +Severity: 0x101 Abort 2 +''', +'''\ +Fault Number: 24 +Description: Invalid method/settings +Selector Values: +0x00 = Invalid port1 method/setting +0x01 = Invalid port2 method/setting +0x10 = Invalid port3 method/setting +Severity: 0x101 Abort 2 +''', +'''\ +Fault Number: 25 +Description: Island detected +Selector Values: NA +Severity: 0x011 Abort 0 +''', +'''\ +Fault Number: 26 +Description: AC under voltage level 1 trip +Selector Values: NA +Severity: 0x011 Abort 0 +''', +'''\ +Fault Number: 27 +Description: AC under voltage level 2 trip +Selector Values: NA +Severity: 0x011 Abort 0 +''', +'''\ +Fault Number: 28 +Description: AC under voltage level 3 trip +Selector Values: NA +Severity: 0x011 Abort 0 +''', +'''\ +Fault Number: 29 +Description: AC under voltage level 4 trip +Selector Values: NA +Severity: 0x011 Abort 0 +''', +'''\ +Fault Number: 30 +Description: AC over voltage level 1 trip +Selector Values: NA +Severity: 0x011 Abort 0 +''', +'''\ +Fault Number: 31 +Description: AC over voltage level 2 trip +Selector Values: NA +Severity: 0x011 Abort 0 +''', +'''\ +Fault Number: 32 +Description: AC over voltage level 3 trip +Selector Values: NA +Severity: 0x011 Abort 0 +''', +'''\ +Fault Number: 33 +Description: AC over voltage level 4 trip +Selector Values: NA +Severity: 0x011 Abort 0 +''', +'''\ +Fault Number: 34 +Description: AC under frequency level 1 trip +Selector Values: NA +Severity: 0x011 Abort 0 +''', +'''\ +Fault Number: 35 +Description: AC under frequency level 2 trip +Selector Values: NA +Severity: 0x011 Abort 0 +''', +'''\ +Fault Number: 36 +Description: AC under frequency level 3 trip +Selector Values: NA +Severity: 0x011 Abort 0 +''', +'''\ +Fault Number: 37 +Description: AC under frequency level 4 trip +Selector Values: NA +Severity: 0x011 Abort 0 +''', +'''\ +Fault Number: 38 +Description: AC over frequency level 1 trip +Selector Values: NA +Severity: 0x011 Abort 0 +''', +'''\ +Fault Number: 39 +Description: AC over frequency level 2 trip +Selector Values: NA +Severity: 0x011 Abort 0 +''', +'''\ +Fault Number: 40 +Description: AC over frequency level 3 trip +Selector Values: NA +Severity: 0x011 Abort 0 +''', +'''\ +Fault Number: 41 +Description: AC over frequency level 4 trip +Selector Values: NA +Severity: 0x011 Abort 0 +''', +'''\ +Fault Number: 42 +Description: Watchdog timeout +Selector Values: 0x00, 0x01 +Severity: 0x101 Abort 2 +''', +'''\ +Fault Number: 43 +Description: Emergency stop active +Selector Values: NA +Severity: 0x011 = Abort 0 +''', +'''\ +Fault Number: 44 +Description: Sensing fault +Selector Values: NA +Severity: 0x101 Abort 2 +''', +'''\ +Fault Number: 45 +Description: Arc fault +Selector Values: NA +Severity: 0x011 Abort 0 +''', +'''\ +Fault Number: 46 +Description: Comms processor initiated shutdown +Selector Values: NA +Severity: 0x101 Abort 2 +''', +'''\ +Fault Number: 47 +Description: Surge detect +Selector Values: +0x00 = Phase A +0x01 = Phase B +0x10 = Phase C +Severity: 0x100 Abort 1 +''', +'''\ +Fault Number: 48 +Description: AC switch breakdown voltage exceeded +Selector Values: +0x00 = Phase A to phase B +0x01 = Phase B to phase C +0x10 = Phase C to phase A +Severity: 0x101 Abort 2 +''', +'''\ +Fault Number: 49 +Description: DC switch breakdown voltage exceeded +Selector Values: +0x00 = DC2 +0x01 = DC3 +Severity: 0x101 Abort 2 +''', +'''\ +Fault Number: 50 +Description: Precharge timeout +Selector Values: NA +Severity: 0x101 Abort 2 +''', +'''\ +Fault Number: 51 +Description: AC to link primary high voltage +Selector Values: + 0x000 = Positive to ground + 0x001 = Negative to ground +Severity: 0x101 Abort 2 +''', +'''\ +Fault Number: 52 +Description: AC line to line instantaneous over voltage +Selector Values: + 0x000 = Phase A to phase B + 0x001 = Phase B to phase C + 0x010 = Phase C to phase A +Severity: 0x100 Abort 1 +''', +'''\ +Fault Number: 53 +Description: License fault +Selector Values: NA +Severity: 0x101 Abort 2 +''', +'''\ +Fault Number: 54 +Description: Grid_Ack timeout +Selector Values: NA +Severity: 0x100 Abort 1 +''' +] \ No newline at end of file diff --git a/stabiliti.py b/stabiliti.py new file mode 100644 index 0000000..da83ab1 --- /dev/null +++ b/stabiliti.py @@ -0,0 +1,122 @@ +import easymodbus.modbusClient +from datetime import datetime as dt +from functools import reduce + +class StabilitiController(object): + '''''' + def __init__(self, ipaddr, port=502, uid=240): + self.client = easymodbus.modbusClient.ModbusClient(ipaddr, port) + self.client.unitidentifier = uid + + def checkConnect(self): + if not self.client.is_connected(): + self.client.connect() + + def checkClose(self): + if self.client.is_connected(): + self.client.close() + + def reg2str(self, r): + ms = (r & 0xff00) >> 8 + ls = (r & 0xff) + return chr(ms) + chr(ls) + + def getNetworkConfigs(self, config=None): + self.checkConnect() + configDict = {} + from functools import reduce + if config: + pass + else: + holdingRegisters = self.client.read_holdingregisters(2030,8) + configDict["ipaddr"] = (reduce (lambda a, b: str(a)+str(b), map(self.reg2str, holdingRegisters))) + holdingRegisters = self.client.read_holdingregisters(2038,8) + configDict["netmask"] = (reduce (lambda a, b: str(a)+str(b), map(self.reg2str, holdingRegisters))) + + return configDict + + def reg2utc(self, timel, timeu): + return dt.utcfromtimestamp((timeu << 16) + timel) + + def reg2bits(self, reg): + return bin(reg)[2:].zfill(16) + + def readFaultDetail(self, findex): + if not (0<=findex<=63): + return None + + self.checkConnect() + + self.client.write_single_register(0, findex) + regs = self.client.read_holdingregisters(1, 9) + flimit, fval, fcount, ftimel, ftimeu = regs[:5] + fselector, fstatus = regs[-2:] + fselector = bin(fselector)[2:].zfill(16)[-3:] + print(bin(fstatus)[2:].zfill(16)) + fseverity = bin(fstatus)[2:].zfill(16)[-3:] + ''' +b0-b2: The severity of the fault. +0x000 = Info: increments the fault counter only. +0x001 = Alert: increments the fault counter only. +0x010 = Alarm: fault is logged. +0x011 = Abort 0: fault is logged and unit is stopped. Reconnect timer 0 is used for restart. +0x100 = Abort 1: fault is logged and unit is stopped. Reconnect timer 1 is used for restart. +0x101 = Abort 2: fault is logged and unit is stopped. Reconnect timer 2 is used for restart. +0x110 = Lockdown: fault is logged, unit stops processing power and requires a reset. +0x111 = Reserved +''' + fstatus = bin(fstatus)[2:].zfill(16)[-5:-3] + ''' +b3-b4: The status of the fault. +0x01 = No fault +0x10 = Active +0x11 = Occurred + ''' + print(flimit, fval, fcount, + self.reg2utc(ftimel, ftimeu), + fselector, + fstatus, + fseverity + ) + + return { + "Number": findex, + "Limit": flimit, + "Value": fval, + "Occurence": fcount, + "TimeStamp": self.reg2utc(ftimel, ftimeu), + "Selector": fselector, + "Severity": fseverity, + "Status": fstatus, + } + + def readFaultArray(self, start): + + import operator as op + self.checkConnect() + + faults = self.client.read_holdingregisters(start,4) + faultStrings = map(lambda x: bin(x)[2:].zfill(16)[::-1], faults) + return reduce(op.add, faultStrings) + + def readFaultActivity(self): + return self.readFaultArray(16) + + def readFaultOccurence(self): + return self.readFaultArray(24) + + def printAllFaults(self, faults): + fmt = " {} || {} | {} | {} | {} | {} | {} | {} | {} " + sep = " ================================== " + print (fmt.format(" ", *list(range(8)))) + print (sep) + for i in range(8): + idx = i * 8 + print (fmt.format(i, *faults[idx:idx+8])) + + def reconnectTimer(self): + pass + + def getSystemStatus(self): + pass + diff --git a/test1.py b/test1.py new file mode 100644 index 0000000..5bc61a9 --- /dev/null +++ b/test1.py @@ -0,0 +1,45 @@ +import easymodbus.modbusClient + +ipaddr, port = ('192.168.1.21', 502) +uid = 240 + +modbusclient = easymodbus.modbusClient.ModbusClient(ipaddr, port) +# modbusclient.timeout = 5000 +modbusclient.unitidentifier = uid +modbusclient.connect() + +modbusclient.is_connected() + +# holdingRegisters = easymodbus.modbusClient.convert_registers_to_float(modbusclient.read_holdingregisters(2038,8)) +# print (holdingRegisters) + +def reg2str (r): + ms = (r & 0xff00) >> 8 + ls = (r & 0xff) + return chr(ms) + chr(ls) + +from functools import reduce + +holdingRegisters = modbusclient.read_holdingregisters(296,1) +print(hex(holdingRegisters[0])) + +holdingRegisters = modbusclient.read_holdingregisters(297,1) +print(holdingRegisters) + +modbusclient.close() + +from stabiliti import StabilitiController as SC + +sc = SC(ipaddr) + +sc.getNetworkConfigs() + +sc.printAllFaults(sc.readFaultActivity()) + +sc.printAllFaults(sc.readFaultOccurence()) + +print ( + list(filter (lambda x: x[1] == '1', enumerate(sc.readFaultActivity()))) +) + +sc.checkClose()