From e2f3ad1ccda638fbc22d0850b96fc044b46ac2e5 Mon Sep 17 00:00:00 2001 From: Yeongdo Park Date: Wed, 19 Feb 2020 14:30:17 -0800 Subject: [PATCH] All register view and operation control working. --- .gitignore | 3 +- FaultMonitor.py | 107 +++++++- FaultView.py | 151 ++++++++++-- faultView.wxg | 642 +++++++++++++++++++++++++++++++++++++++--------- stabiliti.py | 163 +++++++++--- 5 files changed, 889 insertions(+), 177 deletions(-) diff --git a/.gitignore b/.gitignore index 0727d3b..121df58 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /venv/ *.bak -/__pycache__/ \ No newline at end of file +/__pycache__/ +/#~wxg.autosave~faultView.wxg# diff --git a/FaultMonitor.py b/FaultMonitor.py index c4dc8d5..84aabde 100644 --- a/FaultMonitor.py +++ b/FaultMonitor.py @@ -6,12 +6,17 @@ import wx import wx.grid +import wx.propgrid import gettext gettext.install(None) from pubsub import pub +import re + +from registers import regdict + print('pubsub API version', pub.VERSION_API) from pubsub.utils.notification import useNotifyByWriteFile @@ -31,6 +36,7 @@ class Controller: self.view = view self.timer = wx.Timer() self.timer.Bind(wx.EVT_TIMER, self.OnTimer) + self.refresh_rate = 2000 self.mode = False pub.subscribe(self.getFaultInfo, 'fault_selected') @@ -40,9 +46,19 @@ class Controller: pub.subscribe(self.resetNetworkConfigs, 'reset_network_configs') pub.subscribe(self.resetPcsRemote, 'reset_pcs_remote') pub.subscribe(self.checkSysOpMode, 'check_system_op_mode') + pub.subscribe(self.startSystemOp, "start_system_op_manual") + pub.subscribe(self.stopSystemOp, "stop_system_op_manual") + + def startSystemOp(self, mode, val): + self.sc.setPortModes(mode) + self.sc.setPortSetpoints(val) + self.sc.setUserStart() + + def stopSystemOp(self): + self.sc.setUserStop() def startTimer(self): - self.timer.Start(2000) + self.timer.Start(self.refresh_rate) def stopTimer(self): self.timer.Stop() @@ -50,6 +66,7 @@ class Controller: def applyNetworkConfigs(self, ncDict): if self.sc: self.sc.checkClose() + self.refresh_rate = ncDict.pop("refresh") self.sc = SC(**ncDict) def resetNetworkConfigs(self): @@ -84,14 +101,73 @@ class Controller: self.view.text_fault_status.SetValue(status_text) def OnTimer(self, event): + # Read registers + self.sc.readAllRegisters() + # Process registers + # Show Registers factivity = self.sc.readFaultActivity() foccurence = self.sc.readFaultOccurence() self.view.SetFaultGrid(factivity, foccurence) - event.Skip() + + + + stats = list(range(4)) + stats[0], _ = (self.sc.getSystemStatus()) + stats[1], _ = (self.sc.getPortStatusAC1()) + stats[2], _ = (self.sc.getPortStatusDC2()) + stats[3], _ = (self.sc.getPortStatusDC3()) + + self.view.update_status(list(map("\n".join, stats))) + + ''' + for item in self.writeQueue: + name, value = item + self.sc.write(name, value) + ''' + class MyFrame(FaultFrame): def __init__(self, *args, **kwds): FaultFrame.__init__(self, *args, **kwds) + self.property_page = self.property_grid_1.AddPage("Ports") + self.property_page_etc = self.property_grid_1.AddPage("Etc") + for k in regdict: + prop = wx.propgrid.StringProperty(label=k, value="") + prop.SetHelpString(str(regdict[k])) + if re.search(r'^p[1-3]_', k): + self.property_page.Append(prop) + else: + self.property_page_etc.Append(prop) + self.property_page.Sort() + self.property_page_etc.Sort() + # self.property_page.SetPropertyAttributeAll("Raw", 0) + # self.property_page_etc.SetPropertyAttributeAll("Raw", 0) + self.property_grid_1.SetPropertyAttributeAll("Units", 0) + self.property_grid_1.SetColumnCount(3, self.property_page.GetIndex()) + self.property_grid_1.SetColumnCount(3, self.property_page_etc.GetIndex()) + self.property_grid_1.ShowHeader() + + pub.subscribe(self.update_display, 'read_all_registers') + + def update_status(self, stats): + self.text_ctrl_system_status.SetValue(stats[0]) + self.text_ctrl_ac1_status.SetValue(stats[1]) + self.text_ctrl_dc2_status.SetValue(stats[2]) + self.text_ctrl_dc3_status.SetValue(stats[3]) + + def update_display(self, reg, info): + ports = {k:v for (k,v) in info.items() if re.search(r'^p[1-3]_', k)} + etc = {k:v for (k,v) in info.items() if not re.search(r'^p[1-3]_', k)} + + for k, v in ports.items(): + self.property_page.SetPropertyValue(k, str(v.convertValue(reg[v.address]))) + self.property_page.SetPropertyAttribute(k, "Units", str((reg[v.address]))) + + for k, v in etc.items(): + self.property_page_etc.SetPropertyValue(k, str(v.convertValue(reg[v.address]))) + self.property_page_etc.SetPropertyAttribute(k, "Units", str((reg[v.address]))) + + def OnFaultSelect(self, event): # wxGlade: MyFrame. pub.sendMessage("fault_selected", pos=(event.GetRow(), event.GetCol())) @@ -103,8 +179,12 @@ class MyFrame(FaultFrame): for j in range(8): if fActivity[i*8+j] == '1': self.fault_grid.SetCellBackgroundColour(i, j, wx.LIGHT_GREY) + else: + self.fault_grid.SetCellBackgroundColour(i, j, wx.WHITE) if fOccurence[i*8+j] == '1': self.fault_grid.SetCellValue(i, j, "O") + else: + self.fault_grid.SetCellValue(i, j, "") def ToggleMonitoring(self, event): # wxGlade: MyFrame. btnLabel = self.button_1.GetLabel() @@ -115,9 +195,6 @@ class MyFrame(FaultFrame): pub.sendMessage("monitoring_stopped") self.button_1.SetLabel("Start") - if event: - event.Skip() - def OnApply(self, event): if self.button_1.Enabled: @@ -132,6 +209,7 @@ class MyFrame(FaultFrame): "ipaddr": self.input_ip.GetValue(), "port": int(self.input_port.GetValue()), "uid": int(self.input_uid.GetValue()), + "refresh": int(self.input_refresh.GetValue()), } pub.sendMessage("apply_network_configs", ncDict=ncDict) @@ -161,6 +239,25 @@ class MyFrame(FaultFrame): def OnCheckSysOpMode(self, event): pub.sendMessage("check_system_op_mode") + def OnOpStart(self, event): + mode = (self.choice_ac1_control.GetStringSelection(), + self.choice_dc2_control.GetStringSelection(), + self.choice_dc3_control.GetStringSelection()) + inputs = { + "p1_real_pwr_setpt": self.text_ctrl_ac1_realpwr.Value, + "p1_react_pwr_setpt": self.text_ctrl_ac1_reactpwr.Value, + "p2_current_setpt": self.text_ctrl_dc2_current.Value, + "p2_power_setpt": self.text_ctrl_dc2_power.Value, + "p2_voltage_setpt": self.text_ctrl_dc2_voltage.Value, + "p3_current_setpt": self.text_ctrl_dc3_current.Value, + "p3_power_setpt": self.text_ctrl_dc3_power.Value, + "p3_voltage_setpt": self.text_ctrl_dc3_voltage.Value, + } + pub.sendMessage("start_system_op_manual", mode=mode, val=inputs) + + def OnOpStop(self, event): + pub.sendMessage("stop_system_op_manual") + # end of class MyFrame class MyApp(wx.App): diff --git a/FaultView.py b/FaultView.py index e0552b4..33e42c6 100644 --- a/FaultView.py +++ b/FaultView.py @@ -1,10 +1,11 @@ # -*- coding: UTF-8 -*- # -# generated by wxGlade 0.9.5 on Thu Feb 13 18:40:11 2020 +# generated by wxGlade 0.9.5 on Tue Feb 18 18:22:15 2020 # import wx import wx.grid +import wx.propgrid # begin wxGlade: dependencies import gettext @@ -19,7 +20,7 @@ class FaultFrame(wx.Frame): # 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.SetSize((1099, 729)) 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")) @@ -27,13 +28,35 @@ class FaultFrame(wx.Frame): self.reset_button = wx.Button(self, wx.ID_ANY, _("Reset Connection")) self.apply_button = wx.Button(self, wx.ID_ANY, _("Apply")) self.rreset_button = wx.Button(self, wx.ID_ANY, _("Remote PCS Reset")) - self.some_button = wx.Button(self, wx.ID_STOP, "") + self.button_1 = wx.ToggleButton(self, wx.ID_ANY, _("Start")) self.op_mode_button = wx.Button(self, wx.ID_ANY, _("System OP Mode")) self.text_op_mode = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY) + self.text_ctrl_system_status = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_MULTILINE | wx.TE_READONLY) + self.text_ctrl_ac1_status = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_MULTILINE | wx.TE_READONLY) + self.text_ctrl_dc2_status = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_MULTILINE | wx.TE_READONLY) + self.text_ctrl_dc3_status = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_MULTILINE | wx.TE_READONLY) 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.panel_1 = wx.Panel(self, wx.ID_ANY) + self.button_2 = wx.Button(self.panel_1, wx.ID_ANY, _("Start")) + self.button_3 = wx.Button(self.panel_1, wx.ID_ANY, _("Stop")) + self.choice_ac1_control = wx.Choice(self.panel_1, wx.ID_ANY, choices=[_("IDLE"), _("NET"), _("GPWR"), _("FPWR")]) + self.text_ctrl_ac1_realpwr = wx.TextCtrl(self.panel_1, wx.ID_ANY, _("0")) + self.text_ctrl_ac1_reactpwr = wx.TextCtrl(self.panel_1, wx.ID_ANY, _("0")) + self.choice_dc2_control = wx.Choice(self.panel_1, wx.ID_ANY, choices=[_("IDLE"), _("NET"), _("PV/MPPT"), _("CURR"), _("PWR"), _("VOLT")]) + self.text_ctrl_dc2_current = wx.TextCtrl(self.panel_1, wx.ID_ANY, _("0")) + self.text_ctrl_dc2_power = wx.TextCtrl(self.panel_1, wx.ID_ANY, _("0")) + self.text_ctrl_dc2_voltage = wx.TextCtrl(self.panel_1, wx.ID_ANY, _("0")) + self.text_ctrl_7 = wx.TextCtrl(self.panel_1, wx.ID_ANY, "") + self.text_ctrl_8 = wx.TextCtrl(self.panel_1, wx.ID_ANY, "") + self.button_4 = wx.Button(self.panel_1, wx.ID_ANY, _("Enable Precharge")) + self.button_5 = wx.Button(self.panel_1, wx.ID_ANY, _("Disable Precharge")) + self.choice_dc3_control = wx.Choice(self.panel_1, wx.ID_ANY, choices=[_("IDLE"), _("NET"), _("PV/MPPT"), _("CURR"), _("PWR"), _("VOLT")]) + self.text_ctrl_dc3_current = wx.TextCtrl(self.panel_1, wx.ID_ANY, _("0")) + self.text_ctrl_dc3_power = wx.TextCtrl(self.panel_1, wx.ID_ANY, _("0")) + self.text_ctrl_dc3_voltage = wx.TextCtrl(self.panel_1, wx.ID_ANY, _("0")) + self.property_grid_1 = wx.propgrid.PropertyGridManager(self, wx.ID_ANY, style=wx.propgrid.PG_DESCRIPTION | wx.propgrid.PG_TOOLBAR) self.__set_properties() self.__do_layout() @@ -41,14 +64,18 @@ class FaultFrame(wx.Frame): self.Bind(wx.EVT_BUTTON, self.OnReset, self.reset_button) self.Bind(wx.EVT_BUTTON, self.OnApply, self.apply_button) self.Bind(wx.EVT_BUTTON, self.OnButtonRemoteReset, self.rreset_button) + self.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleMonitoring, self.button_1) self.Bind(wx.EVT_BUTTON, self.OnCheckSysOpMode, self.op_mode_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) + self.Bind(wx.EVT_BUTTON, self.OnOpStart, self.button_2) + self.Bind(wx.EVT_BUTTON, self.OnOpStop, self.button_3) # end wxGlade def __set_properties(self): # begin wxGlade: FaultFrame.__set_properties - self.SetTitle(_("frame")) + self.SetTitle(_("PCS Controller")) + self.rreset_button.Enable(False) + self.button_1.Enable(False) self.fault_grid.CreateGrid(8, 8) self.fault_grid.SetRowLabelSize(30) self.fault_grid.SetColLabelSize(30) @@ -73,16 +100,25 @@ class FaultFrame(wx.Frame): 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)) + self.choice_ac1_control.SetSelection(0) + self.choice_dc2_control.SetSelection(0) + self.choice_dc3_control.SetSelection(0) # end wxGlade def __do_layout(self): # begin wxGlade: FaultFrame.__do_layout sizer_1 = wx.BoxSizer(wx.HORIZONTAL) + sizer_2 = wx.BoxSizer(wx.VERTICAL) + sizer_11 = wx.StaticBoxSizer(wx.StaticBox(self.panel_1, wx.ID_ANY, _("DC3 Port")), wx.HORIZONTAL) + grid_sizer_2 = wx.GridSizer(4, 2, 0, 0) + sizer_10 = wx.StaticBoxSizer(wx.StaticBox(self.panel_1, wx.ID_ANY, _("DC2 Port")), wx.HORIZONTAL) + grid_sizer_1 = wx.GridSizer(7, 2, 0, 0) + sizer_9 = wx.StaticBoxSizer(wx.StaticBox(self.panel_1, wx.ID_ANY, _("AC1 Port")), wx.HORIZONTAL) + grid_sizer_3 = wx.GridSizer(3, 2, 0, 0) + sizer_5 = wx.StaticBoxSizer(wx.StaticBox(self.panel_1, wx.ID_ANY, _("Manual Mode Control")), wx.HORIZONTAL) sizer_3 = wx.BoxSizer(wx.VERTICAL) - sizer_2 = wx.BoxSizer(wx.HORIZONTAL) + sizer_7 = wx.BoxSizer(wx.VERTICAL) + sizer_8 = wx.BoxSizer(wx.VERTICAL) sizer_4 = wx.GridSizer(7, 2, 0, 0) label_1 = wx.StaticText(self, wx.ID_ANY, _("IP Address")) sizer_4.Add(label_1, 0, 0, 0) @@ -99,16 +135,85 @@ class FaultFrame(wx.Frame): sizer_4.Add(self.reset_button, 0, 0, 0) sizer_4.Add(self.apply_button, 0, 0, 0) sizer_4.Add(self.rreset_button, 0, 0, 0) - sizer_4.Add(self.some_button, 0, 0, 0) + sizer_4.Add(self.button_1, 0, 0, 0) sizer_4.Add(self.op_mode_button, 0, 0, 0) sizer_4.Add(self.text_op_mode, 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_7.Add(sizer_4, 0, wx.EXPAND, 0) + label_21 = wx.StaticText(self, wx.ID_ANY, _("System Status")) + sizer_8.Add(label_21, 0, 0, 0) + sizer_8.Add(self.text_ctrl_system_status, 1, wx.EXPAND, 0) + label_22 = wx.StaticText(self, wx.ID_ANY, _("AC1 Status")) + sizer_8.Add(label_22, 0, 0, 0) + sizer_8.Add(self.text_ctrl_ac1_status, 1, wx.EXPAND, 0) + label_23 = wx.StaticText(self, wx.ID_ANY, _("DC2 Statu")) + sizer_8.Add(label_23, 0, 0, 0) + sizer_8.Add(self.text_ctrl_dc2_status, 1, wx.EXPAND, 0) + label_24 = wx.StaticText(self, wx.ID_ANY, _("DC3 Status")) + sizer_8.Add(label_24, 0, 0, 0) + sizer_8.Add(self.text_ctrl_dc3_status, 1, wx.EXPAND, 0) + sizer_7.Add(sizer_8, 1, wx.EXPAND, 0) + sizer_1.Add(sizer_7, 1, wx.EXPAND, 0) + sizer_3.Add(self.fault_grid, 0, wx.ALIGN_CENTER, 0) + label_25 = wx.StaticText(self, wx.ID_ANY, _("Fault Info (Documented)")) + sizer_3.Add(label_25, 0, 0, 0) + sizer_3.Add(self.text_fault_doc, 1, wx.EXPAND, 0) + label_26 = wx.StaticText(self, wx.ID_ANY, _("Fault Status")) + sizer_3.Add(label_26, 0, 0, 0) + sizer_3.Add(self.text_fault_status, 1, wx.EXPAND, 0) sizer_1.Add(sizer_3, 1, wx.EXPAND, 0) + sizer_5.Add(self.button_2, 0, 0, 0) + sizer_5.Add(self.button_3, 0, 0, 0) + sizer_2.Add(sizer_5, 0, 0, 0) + label_8 = wx.StaticText(self.panel_1, wx.ID_ANY, _("Control Method")) + grid_sizer_3.Add(label_8, 0, 0, 0) + grid_sizer_3.Add(self.choice_ac1_control, 0, 0, 0) + label_11 = wx.StaticText(self.panel_1, wx.ID_ANY, _("Real Power Setpoint")) + grid_sizer_3.Add(label_11, 0, 0, 0) + grid_sizer_3.Add(self.text_ctrl_ac1_realpwr, 0, 0, 0) + label_12 = wx.StaticText(self.panel_1, wx.ID_ANY, _("Reactive Power Setpoint")) + grid_sizer_3.Add(label_12, 0, 0, 0) + grid_sizer_3.Add(self.text_ctrl_ac1_reactpwr, 0, 0, 0) + sizer_9.Add(grid_sizer_3, 1, wx.EXPAND, 0) + sizer_2.Add(sizer_9, 1, wx.EXPAND, 0) + label_9 = wx.StaticText(self.panel_1, wx.ID_ANY, _("Contol Method")) + grid_sizer_1.Add(label_9, 0, 0, 0) + grid_sizer_1.Add(self.choice_dc2_control, 0, 0, 0) + label_19 = wx.StaticText(self.panel_1, wx.ID_ANY, _("Current Setpoint")) + grid_sizer_1.Add(label_19, 0, 0, 0) + grid_sizer_1.Add(self.text_ctrl_dc2_current, 0, 0, 0) + label_13 = wx.StaticText(self.panel_1, wx.ID_ANY, _("Power Setpoint")) + grid_sizer_1.Add(label_13, 0, 0, 0) + grid_sizer_1.Add(self.text_ctrl_dc2_power, 0, 0, 0) + label_14 = wx.StaticText(self.panel_1, wx.ID_ANY, _("Voltage Setpoint")) + grid_sizer_1.Add(label_14, 0, 0, 0) + grid_sizer_1.Add(self.text_ctrl_dc2_voltage, 0, 0, 0) + label_17 = wx.StaticText(self.panel_1, wx.ID_ANY, _("Precharge Limit Low")) + grid_sizer_1.Add(label_17, 0, 0, 0) + label_18 = wx.StaticText(self.panel_1, wx.ID_ANY, _("Precharge Limit High")) + grid_sizer_1.Add(label_18, 0, 0, 0) + grid_sizer_1.Add(self.text_ctrl_7, 0, 0, 0) + grid_sizer_1.Add(self.text_ctrl_8, 0, 0, 0) + grid_sizer_1.Add(self.button_4, 0, 0, 0) + grid_sizer_1.Add(self.button_5, 0, 0, 0) + sizer_10.Add(grid_sizer_1, 1, wx.EXPAND, 0) + sizer_2.Add(sizer_10, 1, wx.EXPAND, 0) + label_10 = wx.StaticText(self.panel_1, wx.ID_ANY, _("Control Method")) + grid_sizer_2.Add(label_10, 0, 0, 0) + grid_sizer_2.Add(self.choice_dc3_control, 0, 0, 0) + label_20 = wx.StaticText(self.panel_1, wx.ID_ANY, _("Current Setpoint")) + grid_sizer_2.Add(label_20, 0, 0, 0) + grid_sizer_2.Add(self.text_ctrl_dc3_current, 0, 0, 0) + label_15 = wx.StaticText(self.panel_1, wx.ID_ANY, _("Power Setpoint")) + grid_sizer_2.Add(label_15, 0, 0, 0) + grid_sizer_2.Add(self.text_ctrl_dc3_power, 0, 0, 0) + label_16 = wx.StaticText(self.panel_1, wx.ID_ANY, _("Voltage Setpoint")) + grid_sizer_2.Add(label_16, 0, 0, 0) + grid_sizer_2.Add(self.text_ctrl_dc3_voltage, 0, 0, 0) + sizer_11.Add(grid_sizer_2, 1, wx.ALL | wx.EXPAND, 1) + sizer_2.Add(sizer_11, 1, wx.EXPAND, 0) + self.panel_1.SetSizer(sizer_2) + sizer_1.Add(self.panel_1, 1, wx.EXPAND, 0) + sizer_1.Add(self.property_grid_1, 1, wx.EXPAND, 0) self.SetSizer(sizer_1) self.Layout() # end wxGlade @@ -125,6 +230,10 @@ class FaultFrame(wx.Frame): print("Event handler 'OnButtonRemoteReset' not implemented!") event.Skip() + def ToggleMonitoring(self, event): # wxGlade: FaultFrame. + print("Event handler 'ToggleMonitoring' not implemented!") + event.Skip() + def OnCheckSysOpMode(self, event): # wxGlade: FaultFrame. print("Event handler 'OnCheckSysOpMode' not implemented!") event.Skip() @@ -133,8 +242,12 @@ class FaultFrame(wx.Frame): print("Event handler 'OnFaultSelect' not implemented!") event.Skip() - def ToggleMonitoring(self, event): # wxGlade: FaultFrame. - print("Event handler 'ToggleMonitoring' not implemented!") + def OnOpStart(self, event): # wxGlade: FaultFrame. + print("Event handler 'OnOpStart' not implemented!") + event.Skip() + + def OnOpStop(self, event): # wxGlade: FaultFrame. + print("Event handler 'OnOpStop' not implemented!") event.Skip() # end of class FaultFrame diff --git a/faultView.wxg b/faultView.wxg index deb6b97..1b25dde 100644 --- a/faultView.wxg +++ b/faultView.wxg @@ -1,10 +1,10 @@ - + - 558, 556 - frame + 1099, 729 + PCS Controller wxHORIZONTAL @@ -12,123 +12,200 @@ 0 wxEXPAND - - 7 - 2 - 0 - 0 + + wxVERTICAL 0 - - + wxEXPAND + + 7 + 2 + 0 + 0 + + + 0 + + + + + + + 0 + + 192.168.1.21 + + + + + 0 + + + + + + + 0 + + 502 + + + + + 0 + + + + + + + 0 + + 240 + + + + + 0 + + + + + + + 0 + + 2000 + + + + + 0 + + + OnReset + + + + + + + 0 + + + OnApply + + + + + + + 0 + + + OnButtonRemoteReset + + 1 + + + + + + 0 + + + ToggleMonitoring + + 1 + + + + + + 0 + + + OnCheckSysOpMode + + + + + + + 0 + + + + - + 0 - - 192.168.1.21 - - - - - 0 - - - - - - - 0 - - 502 - - - - - 0 - - - - - - - 0 - - 240 - - - - - 0 - - - - - - - 0 - - 2000 - - - - - 0 - - - OnReset - - - - - - - 0 - - - OnApply - - - - - - - 0 - - - OnButtonRemoteReset - - - - - - - 0 - - - ToggleMonitoring - - 1 - - - - - - 0 - - - OnCheckSysOpMode - - - - - - - 0 - - + wxEXPAND + + wxVERTICAL + + + 0 + + + + + + + 0 + wxEXPAND + + + + + + + 0 + + + + + + + 0 + wxEXPAND + + + + + + + 0 + + + + + + + 0 + wxEXPAND + + + + + + + 0 + + + + + + + 0 + wxEXPAND + + + + @@ -180,23 +257,358 @@ 0 + + + + + + + 0 wxEXPAND - -1, 120 0 + + + + + + + 0 wxEXPAND - -1, 120 + + + 0 + wxEXPAND + + + wxVERTICAL + + + 0 + + wxHORIZONTAL + + + + 0 + + + OnOpStart + + + + + + + 0 + + + OnOpStop + + + + + + + + + 0 + wxEXPAND + + wxHORIZONTAL + + + + 0 + wxEXPAND + + 3 + 2 + 0 + 0 + + + 0 + + + + + + + 0 + + 0 + + IDLE + NET + GPWR + FPWR + + + + + + 0 + + + + + + + 0 + + 0 + + + + + 0 + + + + + + + 0 + + 0 + + + + + + + + + 0 + wxEXPAND + + wxHORIZONTAL + + + + 0 + wxEXPAND + + 7 + 2 + 0 + 0 + + + 0 + + + + + + + 0 + + 0 + + IDLE + NET + PV/MPPT + CURR + PWR + VOLT + + + + + + 0 + + + + + + + 0 + + 0 + + + + + 0 + + + + + + + 0 + + 0 + + + + + 0 + + + + + + + 0 + + 0 + + + + + 0 + + + + + + + 0 + + + + + + + 0 + + + + + + 0 + + + + + + 0 + + + + + + + 0 + + + + + + + + + + + 0 + wxEXPAND + + wxHORIZONTAL + + + + 1 + wxALL|wxEXPAND + + 4 + 2 + 0 + 0 + + + 0 + + + + + + + 0 + + 0 + + IDLE + NET + PV/MPPT + CURR + PWR + VOLT + + + + + + 0 + + + + + + + 0 + + 0 + + + + + 0 + + + + + + + 0 + + 0 + + + + + 0 + + + + + + + 0 + + 0 + + + + + + + + + + + + 0 + wxEXPAND + + self.property_grid_1.ShowHeader() + + + diff --git a/stabiliti.py b/stabiliti.py index 0bc71aa..8886155 100644 --- a/stabiliti.py +++ b/stabiliti.py @@ -1,6 +1,10 @@ import easymodbus.modbusClient from datetime import datetime as dt from functools import reduce +from registers import regdict +import numpy as np + +from pubsub import pub nRegsPortStatus = 32 @@ -20,6 +24,27 @@ addrStatusThermal = 0x0160 addrStatusDiagnostic = 0x0180 +system_status = ["" for i in range(16)] +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" + 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", @@ -29,8 +54,12 @@ p1_port_status = {0: "AC1 port is real power 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.", + 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", @@ -71,30 +100,40 @@ p3_port_status = {0: "DC3 port is power soft-limiting", 14: "Reserved", 15: "Reserved"} +mode_dict = { + "IDLE": 0x0000, + "NET": 0x0001, + "GPWR": 0x0402, + "FPWR": 0x0502, + "PV/MPPT": 0x0002, + "CURR": 0x0301, + "PWR": 0x0401, + "VOLT": 0x0501, +} def toPower(x): - return x * 10 + return np.int16(x) * 10 def toVar(x): - return x * 10 + return np.int16(x) * 10 def toVa(x): - return x * 10 + return np.int16(x) * 10 def toPwrFactor(x): - return x * 0.01 + return np.int16(x) * 0.01 def toCurrent(x): - return x * 0.1 + return np.int16(x) * 0.1 def toVoltage(x): - return x + return np.int16(x) def toFreq(x): - return x * 0.001 + return np.uint16(x) * 0.001 def toTemperature(x): - return x * 0.1 + return np.uint16(x) * 0.1 def toRpm(x): return x @@ -139,11 +178,27 @@ def parse_address (addr_string): return int(start, 16), nregisters +valueConversion = {"U16": np.uint16, + "S16": np.int16, + "HEX4": toBitString, + "HEX": toBitString, + "POWER": toPower, + "VAR": toVar, + "VA": toVa, + "PF": toPwrFactor, + "CURRENT": toCurrent, + "VOLTAGE": toVoltage, + "FREQ": toFreq, + "TEMP": toTemperature, + "RPM": toRpm, + "MINUTES": toMinutes, + "BAUD": toBaud, + "STRING": toString, } class StabilitiRegister(object): def __init__(self, name, proplist): self.name = name - self.index = proplist[0] + self.index = list(map(int, proplist[0])) self.address, self.size = parse_address(proplist[1]) self.access = proplist[2] self.dtype = proplist[3] @@ -159,8 +214,11 @@ class StabilitiRegister(object): def is_writable(self): return 'W' in self.access + def convertValue(self, raw): + return valueConversion[self.dtype](raw) + def getdefaultvalue(self): - if self.vdefault = 'NA': + if self.vdefault == 'NA': return None else: return self.vdefault @@ -170,7 +228,8 @@ 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 = {} + self.reg_info = {k:StabilitiRegister(k, v) for k, v in regdict.items()} + self.registers = np.zeros(4000, dtype=np.int) def checkConnect(self): if not self.client.is_connected(): @@ -180,6 +239,39 @@ class StabilitiController(object): if self.client.is_connected(): self.client.close() + def setPortModes(self, modes): + self.client.write_single_register(self.reg_info["p1_control_method"].address, mode_dict[modes[0]]) + self.client.write_single_register(self.reg_info["p2_control_method"].address, mode_dict[modes[1]]) + self.client.write_single_register(self.reg_info["p3_control_method"].address, mode_dict[modes[2]]) + + def setPortSetpoints(self, val): + for k,v in val.items(): + self.client.write_single_register(self.reg_info[k].address, int(float(v))) + + def setUserStart(self): + self.client.write_single_register(self.reg_info["user_start"].address, 1) + + def setUserStop(self): + self.client.write_single_register(self.reg_info["user_stop"].address, 1) + + def readAllRegisters(self): + + print("read all registers") + + self.checkConnect() + + message_size = 64 + for i in range(0, 654, message_size): + holdingRegisters = self.client.read_holdingregisters(i,message_size) + self.registers[i:i+message_size] = np.asarray(holdingRegisters) + + for i in range(2000, 2069, message_size): + holdingRegisters = self.client.read_holdingregisters(i,message_size) + self.registers[i:i+message_size] = np.asarray(holdingRegisters) + + pub.sendMessage("read_all_registers", reg=self.registers, info=self.reg_info) + + def reg2str(self, r): return toString(r) @@ -291,26 +383,18 @@ b3-b4: The status of the fault. 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 + regs = self.registers + + sys_dict = {k:v for k,v in self.reg_info.items() if v.index[0] == 4 and v.index[1] == 10} + + system_all_status = { + k: v.convertValue(regs[v.address]) for k, v in sys_dict.items() + } + + status = system_all_status['system_status'] + + return selectMessages(status, system_status), system_all_status + def getPortStatusAC1(self): '''Get Status of AC1 Power Port''' @@ -318,7 +402,8 @@ system_status[15] – PCS GFDI fault or IMI fault detected def relAddr(x): return x - addrStatusAC1 - regs = self.client.read_holdingregisters(addrStatusAC1, nRegsPortStatus) + # regs = self.client.read_holdingregisters(addrStatusAC1, nRegsPortStatus) + regs = self.registers[addrStatusAC1:addrStatusAC1+nRegsPortStatus] p1_all_status = {"p1_port_status": toBitString(regs[relAddr(96)]), "p1_real_pwr_ramped": toPower(regs[relAddr(100)]), @@ -351,7 +436,8 @@ system_status[15] – PCS GFDI fault or IMI fault detected def relAddr(x): return x - addrStatusDC2 - regs = self.client.read_holdingregisters(addrStatusDC2, nRegsPortStatus) + # regs = self.client.read_holdingregisters(addrStatusDC2, nRegsPortStatus) + regs = self.registers[addrStatusDC2:addrStatusDC2+nRegsPortStatus] p2_all_status = {'p2_port_status': toBitString(regs[relAddr(160)]), 'p2_current_ramped': toCurrent(regs[relAddr(164)]), @@ -375,7 +461,11 @@ system_status[15] – PCS GFDI fault or IMI fault detected def relAddr(x): return x - addrStatusDC3 - regs = self.client.read_holdingregisters(addrStatusDC3, nRegsPortStatus) + regs = self.registers[addrStatusDC3:addrStatusDC3+nRegsPortStatus] + + # regs = self.client.read_holdingregisters(addrStatusDC3, nRegsPortStatus) + + # self.registers[addrStatusDC3, nRegsPortStatus] = regs p3_all_status = {'p3_port_status': toBitString(regs[relAddr(224)]), 'p3_current_ramped': toCurrent(regs[relAddr(228)]), @@ -390,4 +480,3 @@ system_status[15] – PCS GFDI fault or IMI fault detected status = p3_all_status['p3_port_status'] return selectMessages(status, p3_port_status), p3_all_status -