36-cell (3-slave) voltage monitoring/logging version
This commit is contained in:
parent
915ca498fb
commit
b155a58374
6 changed files with 354 additions and 52 deletions
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.7 (wxGladeTest)" project-jdk-type="Python SDK" />
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.7 (bms-can-ui)" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
2
.idea/wxGladeTest.iml
generated
2
.idea/wxGladeTest.iml
generated
|
|
@ -4,7 +4,7 @@
|
|||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="jdk" jdkName="Python 3.7 (bms-can-ui)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
127
BmsApp.py
127
BmsApp.py
|
|
@ -1,6 +1,10 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
import datetime
|
||||
import csv
|
||||
import win32api
|
||||
|
||||
import wx
|
||||
import wx.propgrid
|
||||
|
||||
|
|
@ -13,13 +17,22 @@ from enum import Enum
|
|||
import can
|
||||
import cantools
|
||||
|
||||
from BmsView import BmsMonitorFrame
|
||||
from VoltageView import BmsMonitorFrame
|
||||
|
||||
startTime = datetime.datetime.now()
|
||||
winUptime_ms = win32api.GetTickCount()
|
||||
winUptime_dt = datetime.timedelta(milliseconds=winUptime_ms)
|
||||
bootTime = startTime-winUptime_dt
|
||||
|
||||
db = cantools.database.load_file('res/EBUS_Host.dbc')
|
||||
|
||||
nModule = 22
|
||||
nModule = 3
|
||||
nCell = 12
|
||||
|
||||
completeKey = "isModuleDataComplete"
|
||||
timestampKey = "MessageTimestamp"
|
||||
lastUpdateKey = timestampKey + ".old"
|
||||
lastMsgKey = "LastMessageId"
|
||||
|
||||
def parse_can_id(arbitration_id):
|
||||
mprefix = (arbitration_id & 0xfffff000) >> 12
|
||||
|
|
@ -48,7 +61,7 @@ def find_category(imsg, isignal):
|
|||
|
||||
if 0x00 <= msg_type <= 0x06:
|
||||
return BMS_MSG_CAT.BMS_PROFILE
|
||||
elif 0x07 <= msg_type <= 0x0B:
|
||||
elif 0x07 <= msg_type <= 0x0C:
|
||||
return BMS_MSG_CAT.MODULE_DATA
|
||||
elif msg_type == 0x10:
|
||||
if isignal < 4:
|
||||
|
|
@ -84,7 +97,7 @@ class BmsListener (can.Listener):
|
|||
self.parent = parent
|
||||
|
||||
def on_message_received(self, msg):
|
||||
print("{:#X}".format(msg.arbitration_id))
|
||||
# print("{:#X}".format(msg.arbitration_id))
|
||||
self.parent.process_message(msg)
|
||||
|
||||
|
||||
|
|
@ -98,8 +111,31 @@ class Controller:
|
|||
|
||||
pub.subscribe (self.view.update_battery_module_grid, "bms.module.update")
|
||||
|
||||
pub.subscribe(self.start_write, "view.start")
|
||||
pub.subscribe(self.stop_write, "view.stop")
|
||||
pub.subscribe (self.write_battery_module, "bms.module.update")
|
||||
|
||||
pub.subscribe (self.model.setter, "view.id_selector")
|
||||
|
||||
def start_write(self):
|
||||
filename = self.view.text_ctrl_1.GetValue()
|
||||
self.output_files = [open(filename+"-M{}.csv".format(i+1), 'w') for i in range(nModule)]
|
||||
self.csv_writers = [csv.writer(output_file, delimiter=',') for output_file in self.output_files]
|
||||
for csv_writer in self.csv_writers:
|
||||
csv_writer.writerow(["time"] + ["CV{:02d}".format(i) for i in range(nCell)])
|
||||
|
||||
def stop_write(self):
|
||||
for output_file in self.output_files:
|
||||
output_file.close()
|
||||
|
||||
def write_battery_module(self, state, i_module):
|
||||
module_dict = state[i_module]
|
||||
if module_dict[lastMsgKey] & 0xff == 0x0c:
|
||||
keyString = 'BatS_Cell{}_V'
|
||||
voltages = [module_dict.get(keyString.format(x), -1) for x in range(nCell)]
|
||||
dt = str(bootTime+datetime.timedelta(seconds=module_dict.get(timestampKey, 0.0)))
|
||||
self.csv_writers[i_module].writerow([dt]+voltages)
|
||||
|
||||
|
||||
class PmgrowBms:
|
||||
def __init__(self, add_printer=True):
|
||||
|
|
@ -108,7 +144,7 @@ class PmgrowBms:
|
|||
self.notifier = None
|
||||
self.listeners = [can.Printer()] if add_printer else []
|
||||
self.listeners.append(BmsListener(self))
|
||||
self.master_id = 0x02
|
||||
self.master_id = 0x09
|
||||
|
||||
def setter (self, id):
|
||||
print ("updating master id")
|
||||
|
|
@ -128,17 +164,28 @@ class PmgrowBms:
|
|||
self.notifier = None
|
||||
self.bus = None
|
||||
|
||||
def __update_bm_state(self, dmsg):
|
||||
imodule = dmsg['BatS_Cell_ModuleNo']
|
||||
def __update_bm_state(self, dmsg, msg):
|
||||
i_module = dmsg['BatS_Cell_ModuleNo']
|
||||
self.battery_module_state[i_module][lastUpdateKey] = self.battery_module_state[i_module].get(timestampKey,0.0)
|
||||
self.battery_module_state[i_module][timestampKey] = msg.timestamp
|
||||
self.battery_module_state[i_module][lastMsgKey] = msg.arbitration_id
|
||||
|
||||
if self.battery_module_state[i_module][timestampKey] - self.battery_module_state[i_module][lastUpdateKey] > 0.1:
|
||||
self.battery_module_state[i_module][completeKey] = 4
|
||||
else:
|
||||
self.battery_module_state[i_module][completeKey] = self.battery_module_state[i_module][completeKey] - 1
|
||||
|
||||
for k in dmsg:
|
||||
self.battery_module_state[imodule][k] = dmsg[k]
|
||||
self.battery_module_state[i_module][k] = dmsg[k]
|
||||
|
||||
return i_module
|
||||
|
||||
def process_message(self, msg):
|
||||
msg_prefix, master_id, msg_type = parse_can_id(msg.arbitration_id)
|
||||
full_id = (msg_prefix, master_id, msg_type)
|
||||
db_msg = db.get_message_by_frame_id(msg.arbitration_id)
|
||||
|
||||
print(master_id, self.master_id)
|
||||
# print(master_id, self.master_id)
|
||||
if master_id == self.master_id:
|
||||
if find_category(full_id, 0) == BMS_MSG_CAT.MODULE_DATA:
|
||||
try:
|
||||
|
|
@ -146,8 +193,8 @@ class PmgrowBms:
|
|||
except KeyError as key:
|
||||
print(key, " does not exist")
|
||||
else:
|
||||
self.__update_bm_state(decoded)
|
||||
pub.sendMessage("bms.module.update", state=self.battery_module_state)
|
||||
i_module = self.__update_bm_state(decoded, msg)
|
||||
pub.sendMessage("bms.module.update", state=self.battery_module_state, i_module=i_module)
|
||||
|
||||
elif find_category(full_id, 0) == BMS_MSG_CAT.BMS_PROFILE:
|
||||
pass
|
||||
|
|
@ -184,36 +231,6 @@ class BmsMonitorView(BmsMonitorFrame):
|
|||
def __init__(self, *args, **kwds):
|
||||
BmsMonitorFrame.__init__(self, *args, **kwds)
|
||||
|
||||
self.property_grid_page_info = self.property_grid_1.AddPage("Info")
|
||||
|
||||
for c in pgrid_category:
|
||||
cat_name = c.name
|
||||
cat = wx.propgrid.PropertyCategory(label=cat_name)
|
||||
self.property_grid_page_info.Append(cat)
|
||||
|
||||
# bms_model = wx.propgrid.StringProperty(label="Model", name="bms_model", value="B3DWER32EW")
|
||||
# self.property_grid_page_info.AppendIn("packdata", bms_model)
|
||||
|
||||
for msg in db.messages:
|
||||
prefix, id, type = parse_can_id(msg.frame_id)
|
||||
id_tuple = prefix, id, type
|
||||
|
||||
if id == 0x02:
|
||||
|
||||
if find_category(id_tuple, 0) == BMS_MSG_CAT.BMS_PROFILE:
|
||||
prop = wx.propgrid.StringProperty(label=msg.name)
|
||||
self.property_grid_page_info.AppendIn(BMS_MSG_CAT.BMS_PROFILE.name, prop)
|
||||
|
||||
for isig, signal in enumerate(msg.signals):
|
||||
plabel = str_hex(type) + signal.name
|
||||
prop = wx.propgrid.StringProperty(label=plabel)
|
||||
prop.SetHelpString(signal.comment)
|
||||
|
||||
cat = find_category(id_tuple, isig)
|
||||
|
||||
if cat in pgrid_category:
|
||||
self.property_grid_page_info.AppendIn(cat.name, prop)
|
||||
|
||||
def GetModuleVoltage(self, module_dict):
|
||||
keyString = 'BatS_Cell{}_V'
|
||||
return [module_dict.get(keyString.format(x), -1) for x in range(nCell)]
|
||||
|
|
@ -222,18 +239,28 @@ class BmsMonitorView(BmsMonitorFrame):
|
|||
keyString = 'BatS_Cell{}_BalState'
|
||||
return [module_dict.get(keyString.format(x), -1) for x in range(nCell)]
|
||||
|
||||
def update_battery_module_grid(self, state):
|
||||
i_module = self.spin_ctrl_1.GetValue()
|
||||
def update_battery_module_grid(self, state, i_module):
|
||||
# grids = [self.grid_1, self.grid_2, self.grid_3]
|
||||
ts_ctrls = [self.timestamp_1, self.timestamp_2, self.timestamp_3, ]
|
||||
grid = self.grid_1
|
||||
ts_ctrl = ts_ctrls[i_module]
|
||||
module_dict = state[i_module]
|
||||
voltages = self.GetModuleVoltage(module_dict)
|
||||
bal_states = self.GetModuleBalState(module_dict)
|
||||
for i in range(nCell):
|
||||
self.grid_1.SetCellValue(i, 0, str(voltages[i]))
|
||||
self.grid_1.SetCellValue(i, 2, str(bal_states[i]))
|
||||
self.grid_1.ForceRefresh()
|
||||
if module_dict[lastMsgKey] & 0xff == 0x0c:
|
||||
voltages = self.GetModuleVoltage(module_dict)
|
||||
bal_states = self.GetModuleBalState(module_dict)
|
||||
dt = datetime.datetime.fromtimestamp(module_dict.get(timestampKey, 0.0))
|
||||
msg_id = module_dict.get(lastMsgKey, 0)
|
||||
ts_ctrl.SetValue(str(bootTime+datetime.timedelta(seconds=dt.timestamp())))
|
||||
for i in range(nCell):
|
||||
grid.SetCellValue(i, i_module, str(voltages[i]))
|
||||
print("refreshing module ", i_module, "latest update: ", bootTime+datetime.timedelta(seconds=dt.timestamp()),
|
||||
dt.timestamp(), "{:x}".format(msg_id))
|
||||
grid.ForceRefresh()
|
||||
|
||||
def OnIdChoice(self, event):
|
||||
print ("sending update request")
|
||||
# event.Get
|
||||
# wx.Event.GetEventObject()
|
||||
pub.sendMessage("view.id_selector", id=int(self.choice_1.GetStringSelection(), 16))
|
||||
|
||||
event.Skip()
|
||||
|
|
@ -253,7 +280,7 @@ class BmsMonitorView(BmsMonitorFrame):
|
|||
|
||||
class BmsApp(wx.App):
|
||||
def OnInit(self):
|
||||
self.bms = PmgrowBms()
|
||||
self.bms = PmgrowBms(add_printer=True)
|
||||
self.frame = BmsMonitorView(None, wx.ID_ANY, "")
|
||||
self.bms_controller = Controller(self.bms, self.frame)
|
||||
self.SetTopWindow(self.frame)
|
||||
|
|
|
|||
115
VoltageView.py
Normal file
115
VoltageView.py
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: UTF-8 -*-
|
||||
#
|
||||
# generated by wxGlade 0.9.6 on Sun Nov 29 07:00:24 2020
|
||||
#
|
||||
|
||||
import wx
|
||||
import wx.grid
|
||||
|
||||
# begin wxGlade: dependencies
|
||||
import gettext
|
||||
# end wxGlade
|
||||
|
||||
# begin wxGlade: extracode
|
||||
# end wxGlade
|
||||
|
||||
|
||||
class BmsMonitorFrame(wx.Frame):
|
||||
def __init__(self, *args, **kwds):
|
||||
# begin wxGlade: BmsMonitorFrame.__init__
|
||||
kwds["style"] = kwds.get("style", 0) | wx.CAPTION | wx.CLIP_CHILDREN | wx.CLOSE_BOX | wx.MAXIMIZE_BOX | wx.MINIMIZE_BOX | wx.SYSTEM_MENU
|
||||
wx.Frame.__init__(self, *args, **kwds)
|
||||
self.SetSize((338, 393))
|
||||
self.button_1 = wx.ToggleButton(self, wx.ID_ANY, _("Start"))
|
||||
self.text_ctrl_1 = wx.TextCtrl(self, wx.ID_ANY, _("prefix"))
|
||||
self.timestamp_1 = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
|
||||
self.timestamp_2 = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
|
||||
self.timestamp_3 = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
|
||||
self.grid_1 = wx.grid.Grid(self, wx.ID_ANY, size=(1, 1))
|
||||
|
||||
self.__set_properties()
|
||||
self.__do_layout()
|
||||
|
||||
self.Bind(wx.EVT_TOGGLEBUTTON, self.OnToggle, self.button_1)
|
||||
# end wxGlade
|
||||
|
||||
def __set_properties(self):
|
||||
# begin wxGlade: BmsMonitorFrame.__set_properties
|
||||
self.SetTitle(_("BMS Monitor"))
|
||||
_icon = wx.NullIcon
|
||||
_icon.CopyFromBitmap(wx.Bitmap("res\\repurpose-energy-ci-icon.ico", wx.BITMAP_TYPE_ANY))
|
||||
self.SetIcon(_icon)
|
||||
self.grid_1.CreateGrid(12, 3)
|
||||
self.grid_1.EnableEditing(0)
|
||||
self.grid_1.EnableDragColSize(0)
|
||||
self.grid_1.EnableDragRowSize(0)
|
||||
self.grid_1.EnableDragGridSize(0)
|
||||
self.grid_1.SetColLabelValue(0, _("Slave 1 (V)"))
|
||||
self.grid_1.SetColLabelValue(1, _("Slave 2 (V)"))
|
||||
self.grid_1.SetColLabelValue(2, _("Slave 3 (V)"))
|
||||
self.grid_1.SetRowLabelValue(0, _("Cell 1"))
|
||||
self.grid_1.SetRowLabelValue(1, _("Cell 2"))
|
||||
self.grid_1.SetRowLabelValue(2, _("Cell 3"))
|
||||
self.grid_1.SetRowLabelValue(3, _("Cell 4"))
|
||||
self.grid_1.SetRowLabelValue(4, _("Cell 5"))
|
||||
self.grid_1.SetRowLabelValue(5, _("Cell 6"))
|
||||
self.grid_1.SetRowSize(5, 18)
|
||||
self.grid_1.SetRowLabelValue(6, _("Cell 7"))
|
||||
self.grid_1.SetRowLabelValue(7, _("Cell 8"))
|
||||
self.grid_1.SetRowLabelValue(8, _("Cell 9"))
|
||||
self.grid_1.SetRowLabelValue(9, _("Cell 10"))
|
||||
self.grid_1.SetRowLabelValue(10, _("Cell 11"))
|
||||
self.grid_1.SetRowLabelValue(11, _("Cell 12"))
|
||||
# end wxGlade
|
||||
|
||||
def __do_layout(self):
|
||||
# begin wxGlade: BmsMonitorFrame.__do_layout
|
||||
sizer_1 = wx.BoxSizer(wx.VERTICAL)
|
||||
sizer_4 = wx.BoxSizer(wx.HORIZONTAL)
|
||||
sizer_3 = wx.BoxSizer(wx.HORIZONTAL)
|
||||
sizer_2 = wx.BoxSizer(wx.HORIZONTAL)
|
||||
sizer_6 = wx.BoxSizer(wx.HORIZONTAL)
|
||||
sizer_6.Add(self.button_1, 0, 0, 0)
|
||||
sizer_6.Add((20, 20), 0, 0, 0)
|
||||
label_4 = wx.StaticText(self, wx.ID_ANY, _("Output: "))
|
||||
sizer_6.Add(label_4, 0, 0, 0)
|
||||
sizer_6.Add(self.text_ctrl_1, 0, 0, 0)
|
||||
sizer_1.Add(sizer_6, 0, wx.EXPAND, 0)
|
||||
label_1 = wx.StaticText(self, wx.ID_ANY, _("Slave 1 Update "))
|
||||
sizer_2.Add(label_1, 0, 0, 0)
|
||||
sizer_2.Add(self.timestamp_1, 1, 0, 0)
|
||||
sizer_1.Add(sizer_2, 0, wx.EXPAND, 0)
|
||||
label_2 = wx.StaticText(self, wx.ID_ANY, _("Slave 2 Update "))
|
||||
sizer_3.Add(label_2, 0, 0, 0)
|
||||
sizer_3.Add(self.timestamp_2, 1, wx.EXPAND, 0)
|
||||
sizer_1.Add(sizer_3, 0, wx.EXPAND, 0)
|
||||
label_3 = wx.StaticText(self, wx.ID_ANY, _("Slave 3 Update "))
|
||||
sizer_4.Add(label_3, 0, 0, 0)
|
||||
sizer_4.Add(self.timestamp_3, 2, wx.EXPAND, 0)
|
||||
sizer_1.Add(sizer_4, 0, wx.EXPAND, 0)
|
||||
sizer_1.Add(self.grid_1, 1, wx.EXPAND, 0)
|
||||
self.SetSizer(sizer_1)
|
||||
self.Layout()
|
||||
# end wxGlade
|
||||
|
||||
def OnToggle(self, event): # wxGlade: BmsMonitorFrame.<event_handler>
|
||||
print("Event handler 'OnToggle' not implemented!")
|
||||
event.Skip()
|
||||
|
||||
# end of class BmsMonitorFrame
|
||||
|
||||
class BmsApp(wx.App):
|
||||
def OnInit(self):
|
||||
self.frame = BmsMonitorFrame(None, wx.ID_ANY, "")
|
||||
self.SetTopWindow(self.frame)
|
||||
self.frame.Show()
|
||||
return True
|
||||
|
||||
# end of class BmsApp
|
||||
|
||||
if __name__ == "__main__":
|
||||
gettext.install("app") # replace with the appropriate catalog name
|
||||
|
||||
app = BmsApp(0)
|
||||
app.MainLoop()
|
||||
155
VoltageView.wxg
Normal file
155
VoltageView.wxg
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- generated by wxGlade 0.9.6 on Sun Nov 29 07:00:21 2020 -->
|
||||
|
||||
<application class="BmsApp" encoding="UTF-8" for_version="3.0" header_extension=".h" indent_amount="4" indent_symbol="space" is_template="0" language="python" mark_blocks="1" name="app" option="0" overwrite="1" path="./VoltageView.py" source_extension=".cpp" top_window="frame" use_gettext="1" use_new_namespace="1">
|
||||
<object class="BmsMonitorFrame" name="frame" base="EditFrame">
|
||||
<size>338, 393</size>
|
||||
<title>BMS Monitor</title>
|
||||
<style>wxCAPTION|wxMINIMIZE_BOX|wxCLOSE_BOX|wxMAXIMIZE_BOX|wxSYSTEM_MENU|wxCLIP_CHILDREN</style>
|
||||
<icon>res\repurpose-energy-ci-icon.ico</icon>
|
||||
<object class="wxBoxSizer" name="sizer_1" base="EditBoxSizer">
|
||||
<orient>wxVERTICAL</orient>
|
||||
<object class="sizeritem">
|
||||
<option>0</option>
|
||||
<border>0</border>
|
||||
<flag>wxEXPAND</flag>
|
||||
<object class="wxBoxSizer" name="sizer_6" base="EditBoxSizer">
|
||||
<orient>wxHORIZONTAL</orient>
|
||||
<object class="sizeritem">
|
||||
<option>0</option>
|
||||
<border>0</border>
|
||||
<object class="wxToggleButton" name="button_1" base="EditToggleButton">
|
||||
<events>
|
||||
<handler event="EVT_TOGGLEBUTTON">OnToggle</handler>
|
||||
</events>
|
||||
<label>Start</label>
|
||||
</object>
|
||||
</object>
|
||||
<object class="sizeritem">
|
||||
<option>0</option>
|
||||
<border>0</border>
|
||||
<object class="spacer" name="spacer" base="EditSpacer">
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</object>
|
||||
</object>
|
||||
<object class="sizeritem">
|
||||
<option>0</option>
|
||||
<border>0</border>
|
||||
<object class="wxStaticText" name="label_4" base="EditStaticText">
|
||||
<label>Output: </label>
|
||||
</object>
|
||||
</object>
|
||||
<object class="sizeritem">
|
||||
<option>0</option>
|
||||
<border>0</border>
|
||||
<object class="wxTextCtrl" name="text_ctrl_1" base="EditTextCtrl">
|
||||
<value>prefix</value>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
<object class="sizeritem">
|
||||
<option>0</option>
|
||||
<border>0</border>
|
||||
<flag>wxEXPAND</flag>
|
||||
<object class="wxBoxSizer" name="sizer_2" base="EditBoxSizer">
|
||||
<orient>wxHORIZONTAL</orient>
|
||||
<object class="sizeritem">
|
||||
<option>0</option>
|
||||
<border>0</border>
|
||||
<object class="wxStaticText" name="label_1" base="EditStaticText">
|
||||
<label>Slave 1 Update </label>
|
||||
</object>
|
||||
</object>
|
||||
<object class="sizeritem">
|
||||
<option>1</option>
|
||||
<border>0</border>
|
||||
<object class="wxTextCtrl" name="timestamp_1" base="EditTextCtrl">
|
||||
<style>wxTE_READONLY</style>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
<object class="sizeritem">
|
||||
<option>0</option>
|
||||
<border>0</border>
|
||||
<flag>wxEXPAND</flag>
|
||||
<object class="wxBoxSizer" name="sizer_3" base="EditBoxSizer">
|
||||
<orient>wxHORIZONTAL</orient>
|
||||
<object class="sizeritem">
|
||||
<option>0</option>
|
||||
<border>0</border>
|
||||
<object class="wxStaticText" name="label_2" base="EditStaticText">
|
||||
<label>Slave 2 Update </label>
|
||||
</object>
|
||||
</object>
|
||||
<object class="sizeritem">
|
||||
<option>1</option>
|
||||
<border>0</border>
|
||||
<flag>wxEXPAND</flag>
|
||||
<object class="wxTextCtrl" name="timestamp_2" base="EditTextCtrl">
|
||||
<style>wxTE_READONLY</style>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
<object class="sizeritem">
|
||||
<option>0</option>
|
||||
<border>0</border>
|
||||
<flag>wxEXPAND</flag>
|
||||
<object class="wxBoxSizer" name="sizer_4" base="EditBoxSizer">
|
||||
<orient>wxHORIZONTAL</orient>
|
||||
<object class="sizeritem">
|
||||
<option>0</option>
|
||||
<border>0</border>
|
||||
<object class="wxStaticText" name="label_3" base="EditStaticText">
|
||||
<label>Slave 3 Update </label>
|
||||
</object>
|
||||
</object>
|
||||
<object class="sizeritem">
|
||||
<option>2</option>
|
||||
<border>0</border>
|
||||
<flag>wxEXPAND</flag>
|
||||
<object class="wxTextCtrl" name="timestamp_3" base="EditTextCtrl">
|
||||
<style>wxTE_READONLY</style>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
<object class="sizeritem">
|
||||
<option>1</option>
|
||||
<border>0</border>
|
||||
<flag>wxEXPAND</flag>
|
||||
<object class="wxGrid" name="grid_1" base="EditGrid">
|
||||
<create_grid>1</create_grid>
|
||||
<columns>
|
||||
<column size="-1">Slave 1 (V)</column>
|
||||
<column size="-1">Slave 2 (V)</column>
|
||||
<column size="-1">Slave 3 (V)</column>
|
||||
</columns>
|
||||
<rows>
|
||||
<row size="-1">Cell 1</row>
|
||||
<row size="-1">Cell 2</row>
|
||||
<row size="-1">Cell 3</row>
|
||||
<row size="-1">Cell 4</row>
|
||||
<row size="-1">Cell 5</row>
|
||||
<row size="18">Cell 6</row>
|
||||
<row size="-1">Cell 7</row>
|
||||
<row size="-1">Cell 8</row>
|
||||
<row size="-1">Cell 9</row>
|
||||
<row size="-1">Cell 10</row>
|
||||
<row size="-1">Cell 11</row>
|
||||
<row size="-1">Cell 12</row>
|
||||
</rows>
|
||||
<enable_editing>0</enable_editing>
|
||||
<enable_grid_lines>1</enable_grid_lines>
|
||||
<enable_col_resize>0</enable_col_resize>
|
||||
<enable_row_resize>0</enable_row_resize>
|
||||
<enable_grid_resize>0</enable_grid_resize>
|
||||
<selection_mode>wxGrid.wxGridSelectCells</selection_mode>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</application>
|
||||
5
requirements.txt
Normal file
5
requirements.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
wxPython~=4.1.0
|
||||
python-can~=3.3.3
|
||||
cantools~=35.3.0
|
||||
pywin32~=300
|
||||
Pypubsub~=4.0.3
|
||||
Loading…
Add table
Reference in a new issue