enode-host 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
enode_host/__init__.py ADDED
@@ -0,0 +1 @@
1
+ """Host-side utilities for interacting with the ESP-IDF firmware."""
@@ -0,0 +1,165 @@
1
+ import asyncio
2
+ import struct
3
+ from dataclasses import dataclass
4
+ from typing import Dict, Optional
5
+
6
+ from .protocol import (
7
+ MsgType,
8
+ build_ack,
9
+ format_mac,
10
+ parse_acc_batch,
11
+ parse_pps,
12
+ parse_sd_chunk,
13
+ parse_sd_done,
14
+ parse_status,
15
+ )
16
+
17
+
18
+ @dataclass
19
+ class SocketResponse:
20
+ payload: bytes
21
+
22
+
23
+ async def send_message(host: str, port: int, payload: bytes, timeout: float = 5.0) -> SocketResponse:
24
+ reader: asyncio.StreamReader
25
+ writer: asyncio.StreamWriter
26
+
27
+ try:
28
+ reader, writer = await asyncio.wait_for(
29
+ asyncio.open_connection(host, port), timeout=timeout
30
+ )
31
+ except asyncio.TimeoutError as exc:
32
+ raise TimeoutError(f"Timed out connecting to {host}:{port}") from exc
33
+
34
+ try:
35
+ writer.write(payload)
36
+ await writer.drain()
37
+ data = await asyncio.wait_for(reader.read(4096), timeout=timeout)
38
+ return SocketResponse(payload=data)
39
+ finally:
40
+ writer.close()
41
+ await writer.wait_closed()
42
+
43
+
44
+ def run_client(host: str, port: int, message: str, timeout: float) -> int:
45
+ payload = message.encode("utf-8")
46
+ response = asyncio.run(send_message(host, port, payload, timeout))
47
+ if response.payload:
48
+ print(response.payload.decode("utf-8", errors="replace"))
49
+ return 0
50
+
51
+
52
+ async def read_frame(reader: asyncio.StreamReader) -> Optional[tuple[int, bytes]]:
53
+ header = await reader.readexactly(3)
54
+ msg_type, length = struct.unpack(">BH", header)
55
+ if length == 0:
56
+ return msg_type, b""
57
+ payload = await reader.readexactly(length)
58
+ return msg_type, payload
59
+
60
+
61
+ async def handle_client(reader: asyncio.StreamReader, writer: asyncio.StreamWriter, clients: Dict[str, asyncio.StreamWriter]) -> None:
62
+ peer = writer.get_extra_info("peername")
63
+ peer_id = f"{peer[0]}:{peer[1]}" if peer else "unknown"
64
+ clients[peer_id] = writer
65
+ print(f"[server] connected {peer_id} (clients={len(clients)})")
66
+
67
+ try:
68
+ while True:
69
+ msg_type, payload = await read_frame(reader)
70
+ if msg_type == MsgType.STATUS:
71
+ status = parse_status(payload)
72
+ print(
73
+ "[status] "
74
+ f"node={status.node_type}:{status.node_number} "
75
+ f"level={status.level} "
76
+ f"parent={format_mac(status.parent_mac)} "
77
+ f"self={format_mac(status.self_mac)} "
78
+ f"rssi={status.rssi}"
79
+ )
80
+ writer.write(build_ack(MsgType.STATUS, 0, "ok"))
81
+ await writer.drain()
82
+ elif msg_type == MsgType.DATA:
83
+ batch = parse_acc_batch(payload)
84
+ print(f"[data] node={batch.node_type}:{batch.node_number} samples={len(batch.samples)}")
85
+ writer.write(build_ack(MsgType.DATA, 0, "ok"))
86
+ await writer.drain()
87
+ elif msg_type == MsgType.PPS:
88
+ pps = parse_pps(payload)
89
+ print(f"[pps] node={pps.node_type}:{pps.node_number} cc={pps.cc} epoch={pps.epoch}")
90
+ writer.write(build_ack(MsgType.PPS, 0, "ok"))
91
+ await writer.drain()
92
+ elif msg_type == MsgType.SD_STREAM:
93
+ chunk = parse_sd_chunk(payload)
94
+ print(
95
+ f"[sd] node={chunk.node_type}:{chunk.node_number} "
96
+ f"file_time={chunk.file_time} offset={chunk.offset} size={len(chunk.data)}"
97
+ )
98
+ writer.write(build_ack(MsgType.SD_STREAM, 0, "ok"))
99
+ await writer.drain()
100
+ elif msg_type == MsgType.SD_DONE:
101
+ done = parse_sd_done(payload)
102
+ print(
103
+ f"[sd] node={done.node_type}:{done.node_number} "
104
+ f"file_time={done.file_time} status={done.status}"
105
+ )
106
+ writer.write(build_ack(MsgType.SD_DONE, 0, "ok"))
107
+ await writer.drain()
108
+ else:
109
+ print(f"[server] msg_type=0x{msg_type:02X} len={len(payload)}")
110
+ writer.write(build_ack(msg_type, 0, "ok"))
111
+ await writer.drain()
112
+ except asyncio.IncompleteReadError:
113
+ pass
114
+ finally:
115
+ clients.pop(peer_id, None)
116
+ writer.close()
117
+ await writer.wait_closed()
118
+ print(f"[server] disconnected {peer_id} (clients={len(clients)})")
119
+
120
+
121
+ async def broadcast_messages(
122
+ queue: asyncio.Queue[bytes],
123
+ clients: Dict[str, asyncio.StreamWriter],
124
+ ) -> None:
125
+ while True:
126
+ payload = await queue.get()
127
+ if not clients:
128
+ print("[server] no clients to broadcast to")
129
+ continue
130
+ for peer_id, writer in list(clients.items()):
131
+ try:
132
+ writer.write(payload)
133
+ await writer.drain()
134
+ except ConnectionError:
135
+ clients.pop(peer_id, None)
136
+ print(f"[server] broadcast to {len(clients)} clients")
137
+
138
+
139
+ async def run_server(
140
+ host: str,
141
+ port: int,
142
+ on_connect_payload: Optional[bytes],
143
+ command_queue: Optional[asyncio.Queue[bytes]] = None,
144
+ ) -> None:
145
+ clients: Dict[str, asyncio.StreamWriter] = {}
146
+
147
+ async def _client_handler(reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
148
+ if on_connect_payload:
149
+ writer.write(on_connect_payload)
150
+ await writer.drain()
151
+ await handle_client(reader, writer, clients)
152
+
153
+ server = await asyncio.start_server(_client_handler, host=host, port=port)
154
+ addrs = ", ".join(str(sock.getsockname()) for sock in server.sockets or [])
155
+ print(f"[server] listening on {addrs}")
156
+
157
+ async with server:
158
+ if command_queue is None:
159
+ await server.serve_forever()
160
+ else:
161
+ broadcaster = asyncio.create_task(broadcast_messages(command_queue, clients))
162
+ try:
163
+ await server.serve_forever()
164
+ finally:
165
+ broadcaster.cancel()
@@ -0,0 +1,226 @@
1
+ #!/usr/local/bin/python3
2
+
3
+ #----------------------------------------------
4
+ # NOTE
5
+ # 1. Ask Ki about the definition of local axes for M352, or see the line # 397 of model.py
6
+ # when you look at the screen of the node,
7
+ # the direction to the left hand-side is the X-axis
8
+ # the downward direction is the Y-axis
9
+ # the direction perpendicular to the screen and coming towards yourself is Z-axis
10
+ #
11
+ # this is the right-hand rule:
12
+ # the direction of you index finger is the x-axis,
13
+ # that of the middle finger is the y-axis
14
+ # that of the thumb is the z-axis
15
+ #
16
+ #---------------------------------------------
17
+ # HOW TO USE THE SYSTEM
18
+ #
19
+ # 1. please execute this file first, so that the server is ready to receive data from ndoes
20
+ # 2. turn on the node(s)
21
+ # - the PPS LED on the display can be believed
22
+ # - please don't believe RSSI, Lv, BAT %, as they are not completed yet.
23
+ # 3. Files are saved under data2 folder in HDF
24
+ # 4. Please read example.ipynb for how to load data file and display it
25
+ # - Please notice the time is in UTC to avoid any issues regarding British Summer Time
26
+ # 5. For time-sync, never reset the acc node, but power-cycle it.
27
+ # - there are two buttons on the sensor node:
28
+ # - one for power next to the USB-c port
29
+ # - the other for reset next to the SD slot
30
+ # - pressing the pwr button for 5 sec will shutdown the node, and pressing it for 1 sec will start the node
31
+
32
+
33
+
34
+ import wx
35
+ import view
36
+ import model
37
+ import importlib
38
+ importlib.reload(view)
39
+ importlib.reload(model)
40
+
41
+ import logging
42
+ import threading
43
+ import queues
44
+ from numpy import array
45
+ import shm_sigproc
46
+ import glob
47
+ import os
48
+ import esp_mesh
49
+ import pandas as pd
50
+ from model import CmdType, ChannelType
51
+ from enum import IntEnum
52
+
53
+ os.remove('log/my_log_file.log')
54
+
55
+ logging.basicConfig(level=logging.INFO, # Set the minimum log level
56
+ #format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
57
+ format='%(name)s - %(levelname)s - %(message)s',
58
+ handlers=[logging.FileHandler('log/my_log_file.log'), logging.StreamHandler()])
59
+ logger = logging.getLogger(__name__)
60
+
61
+
62
+ os.makedirs("raw-data", exist_ok = True)
63
+
64
+
65
+ class CMD_TYPE(IntEnum):
66
+ SET_MODE = 0
67
+ CLEAR_STORAGE = 1
68
+ NOTIFY_PARENT = 2
69
+ NOTIFY_CHILDREN = 3
70
+ SHUTDOWN = 4
71
+
72
+ class OPERATION_MODE(IntEnum):
73
+ ONLINE_DAQ = 0
74
+ STANDALONE_DATA_PEEKING = 1
75
+ POST_DAQ_DATA_STREAMING = 2
76
+ MAINTENANCE = 3
77
+
78
+ class Controller:
79
+
80
+ def __init__(self):
81
+
82
+ self.model = model.Model(self)
83
+ self.view = view.View(self)
84
+ self.view.Show()
85
+ self.mesh = esp_mesh.Esp_Mesh(self.model)
86
+
87
+ # mesh.begin()
88
+ # shm_sigproc.begin()
89
+
90
+ #
91
+ # BINDINGS
92
+ #
93
+
94
+ # node_num_txt
95
+ self.view.m_textCtrl2.Bind(wx.EVT_TEXT, self.on_change_node_nums)
96
+
97
+ # Menu File>Exit
98
+ self.view.Bind(wx.EVT_MENU, self.on_exit, self.view.m_menu_file_quit)
99
+
100
+ # Menu Data>Export
101
+ self.view.Bind(wx.EVT_MENU, self.on_data_export, self.view.m_menu_data_export)
102
+
103
+ # Menu CMD>SD clear
104
+ self.view.Bind(wx.EVT_MENU, self.on_cmd_sdclear, self.view.m_menu_cmd_sdclear)
105
+
106
+ # Menu CMD>Shutdown
107
+ self.view.Bind(wx.EVT_MENU, self.on_cmd_shutdown, self.view.m_menu_cmd_shutdown)
108
+
109
+ # Menu Tool>clf
110
+ self.view.Bind(wx.EVT_MENU, self.on_view_clf, self.view.m_menu_view_clf)
111
+
112
+ # Start DAQ Button
113
+ self.view.m_button1.Bind(wx.EVT_BUTTON, self.Start_Button_onclick)
114
+
115
+ # Stop DAQ Button
116
+ self.view.m_button2.Bind(wx.EVT_BUTTON, self.Stop_Button_onclick)
117
+
118
+ #
119
+ self.view.m_choice_OperationMode.Bind(wx.EVT_CHOICE, self.choice_OperationMode_onchange)
120
+
121
+ def on_change_node_nums(self, event):
122
+
123
+ self.model.options['node_nums_txt'] = self.view.m_textCtrl2.GetValue()
124
+ self.model.parse_node_nums_txt()
125
+ self.model.save_config()
126
+ self.model.init_mesh_status_data()
127
+ self.model.init_other_data()
128
+ self.view.mesh_status_data_view()
129
+ self.view.m_grid2.ForceRefresh()
130
+ # Init the time history and PSD plots
131
+ self.view.init_plot()
132
+
133
+ def on_exit(self, event):
134
+
135
+ self.view.Close()
136
+
137
+ def on_data_export(self, event):
138
+
139
+ self.model.export()
140
+
141
+ def Start_Button_onclick(self, event):
142
+
143
+ map = {
144
+ OPERATION_MODE.ONLINE_DAQ: 1,
145
+ OPERATION_MODE.STANDALONE_DATA_PEEKING: 5,
146
+ OPERATION_MODE.POST_DAQ_DATA_STREAMING: 7,
147
+ }
148
+ operation_Mode = self.view.m_choice_OperationMode.GetSelection()
149
+
150
+ self.model.init_other_data()
151
+ self.view.init_plot()
152
+ self.view.figure_update()
153
+ for inode in self.mesh.sockets.keys():
154
+ self.mesh.send_cmd(inode, CMD_TYPE.SET_MODE, data0 = map[operation_Mode])
155
+ self.model.update_mesh_status_data(inode, 'CMD', "sent")
156
+ self.view.table_update()
157
+ logger.info('command sent: {} start'.format(OPERATION_MODE(operation_Mode).name))
158
+
159
+ def Stop_Button_onclick(self, event):
160
+
161
+ map = {
162
+ OPERATION_MODE.ONLINE_DAQ: 0,
163
+ OPERATION_MODE.STANDALONE_DATA_PEEKING: 4,
164
+ OPERATION_MODE.POST_DAQ_DATA_STREAMING: 2,
165
+ }
166
+
167
+ operation_Mode = self.view.m_choice_OperationMode.GetSelection()
168
+
169
+ for inode in self.mesh.sockets.keys():
170
+ self.mesh.send_cmd(inode, CMD_TYPE.SET_MODE, data0 = map[operation_Mode])
171
+ self.model.update_mesh_status_data(inode, 'CMD', "sent")
172
+ self.view.table_update()
173
+
174
+ logger.info('command sent: {} stop'.format(OPERATION_MODE(operation_Mode).name))
175
+ # self.view.gui_update(1)
176
+
177
+ def choice_OperationMode_onchange(self, event):
178
+ operation_Mode = self.view.m_choice_OperationMode.GetSelection()
179
+ if operation_Mode == OPERATION_MODE.POST_DAQ_DATA_STREAMING:
180
+ for inode in self.mesh.sockets.keys():
181
+ self.mesh.send_cmd(inode, CMD_TYPE.SET_MODE, data0 = 2)
182
+ self.model.update_mesh_status_data(inode, 'CMD', "sent")
183
+ self.view.table_update()
184
+
185
+ if operation_Mode == OPERATION_MODE.ONLINE_DAQ:
186
+ self.PPS_outdate_check = True
187
+ else:
188
+ self.PPS_outdate_check = False
189
+
190
+
191
+ def on_view_clf(self, event):
192
+ self.model.init_other_data()
193
+ self.view.init_plot()
194
+ self.view.figure_update()
195
+ return
196
+
197
+ def on_cmd_sdclear(self, event):
198
+
199
+ for inode in self.mesh.sockets.keys():
200
+ self.mesh.send_cmd(inode, CMD_TYPE.CLEAR_STORAGE)
201
+ self.model.update_mesh_status_data(inode, 'CMD', "sent")
202
+ self.view.table_update()
203
+ logger.info('command sent: SD Clear sent')
204
+
205
+ def on_cmd_shutdown(self, event):
206
+
207
+ for inode in self.mesh.sockets.keys():
208
+ self.mesh.send_cmd(inode, CMD_TYPE.SHUTDOWN)
209
+ self.model.update_mesh_status_data(inode, 'CMD', "sent")
210
+ self.view.table_update()
211
+ logger.info('command sent: SD Clear sent')
212
+
213
+ return
214
+
215
+
216
+
217
+ if __name__ == '__main__':
218
+ app = wx.App()
219
+ controller = Controller()
220
+ # controller.view.gui_update(1)
221
+ app.MainLoop()
222
+
223
+
224
+
225
+
226
+
enode_host/backup/c.py ADDED
@@ -0,0 +1,226 @@
1
+ #!/usr/local/bin/python3
2
+
3
+ #----------------------------------------------
4
+ # NOTE
5
+ # 1. Ask Ki about the definition of local axes for M352, or see the line # 397 of model.py
6
+ # when you look at the screen of the node,
7
+ # the direction to the left hand-side is the X-axis
8
+ # the downward direction is the Y-axis
9
+ # the direction perpendicular to the screen and coming towards yourself is Z-axis
10
+ #
11
+ # this is the right-hand rule:
12
+ # the direction of you index finger is the x-axis,
13
+ # that of the middle finger is the y-axis
14
+ # that of the thumb is the z-axis
15
+ #
16
+ #---------------------------------------------
17
+ # HOW TO USE THE SYSTEM
18
+ #
19
+ # 1. please execute this file first, so that the server is ready to receive data from ndoes
20
+ # 2. turn on the node(s)
21
+ # - the PPS LED on the display can be believed
22
+ # - please don't believe RSSI, Lv, BAT %, as they are not completed yet.
23
+ # 3. Files are saved under data2 folder in HDF
24
+ # 4. Please read example.ipynb for how to load data file and display it
25
+ # - Please notice the time is in UTC to avoid any issues regarding British Summer Time
26
+ # 5. For time-sync, never reset the acc node, but power-cycle it.
27
+ # - there are two buttons on the sensor node:
28
+ # - one for power next to the USB-c port
29
+ # - the other for reset next to the SD slot
30
+ # - pressing the pwr button for 5 sec will shutdown the node, and pressing it for 1 sec will start the node
31
+
32
+
33
+
34
+ import wx
35
+ import view
36
+ import model
37
+ import importlib
38
+ importlib.reload(view)
39
+ importlib.reload(model)
40
+
41
+ import logging
42
+ import threading
43
+ import queues
44
+ from numpy import array
45
+ import shm_sigproc
46
+ import glob
47
+ import os
48
+ import esp_mesh
49
+ import pandas as pd
50
+ from model import CmdType, ChannelType
51
+ from enum import IntEnum
52
+
53
+ os.remove('log/my_log_file.log')
54
+
55
+ logging.basicConfig(level=logging.INFO, # Set the minimum log level
56
+ #format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
57
+ format='%(name)s - %(levelname)s - %(message)s',
58
+ handlers=[logging.FileHandler('log/my_log_file.log'), logging.StreamHandler()])
59
+ logger = logging.getLogger(__name__)
60
+
61
+
62
+ os.makedirs("raw-data", exist_ok = True)
63
+
64
+
65
+ class CMD_TYPE(IntEnum):
66
+ SET_MODE = 0
67
+ CLEAR_STORAGE = 1
68
+ NOTIFY_PARENT = 2
69
+ NOTIFY_CHILDREN = 3
70
+ SHUTDOWN = 4
71
+
72
+ class OPERATION_MODE(IntEnum):
73
+ ONLINE_DAQ = 0
74
+ STANDALONE_DATA_PEEKING = 1
75
+ POST_DAQ_DATA_STREAMING = 2
76
+ MAINTENANCE = 3
77
+
78
+ class Controller:
79
+
80
+ def __init__(self):
81
+
82
+ self.model = model.Model(self)
83
+ self.view = view.View(self)
84
+ self.view.Show()
85
+ self.mesh = esp_mesh.Esp_Mesh(self.model)
86
+
87
+ # mesh.begin()
88
+ # shm_sigproc.begin()
89
+
90
+ #
91
+ # BINDINGS
92
+ #
93
+
94
+ # node_num_txt
95
+ self.view.m_button_nodes_update.Bind(wx.EVT_BUTTON, self.on_change_node_nums)
96
+
97
+ # Menu File>Exit
98
+ self.view.Bind(wx.EVT_MENU, self.on_exit, self.view.m_menu_file_quit)
99
+
100
+ # Menu Data>Export
101
+ self.view.Bind(wx.EVT_MENU, self.on_data_export, self.view.m_menu_data_export)
102
+
103
+ # Menu CMD>SD clear
104
+ self.view.Bind(wx.EVT_MENU, self.on_cmd_sdclear, self.view.m_menu_cmd_sdclear)
105
+
106
+ # Menu CMD>Shutdown
107
+ self.view.Bind(wx.EVT_MENU, self.on_cmd_shutdown, self.view.m_menu_cmd_shutdown)
108
+
109
+ # Menu Tool>clf
110
+ self.view.Bind(wx.EVT_MENU, self.on_view_clf, self.view.m_menu_view_clf)
111
+
112
+ # Start DAQ Button
113
+ self.view.m_button1.Bind(wx.EVT_BUTTON, self.Start_Button_onclick)
114
+
115
+ # Stop DAQ Button
116
+ self.view.m_button2.Bind(wx.EVT_BUTTON, self.Stop_Button_onclick)
117
+
118
+ #
119
+ self.view.m_choice_OperationMode.Bind(wx.EVT_CHOICE, self.choice_OperationMode_onchange)
120
+
121
+ def on_change_node_nums(self, event):
122
+
123
+ self.model.options['node_nums_txt'] = self.view.m_textCtrl2.GetValue()
124
+ self.model.parse_node_nums_txt()
125
+ self.model.save_config()
126
+ self.model.init_mesh_status_data()
127
+ self.model.init_other_data()
128
+ self.view.mesh_status_data_view()
129
+ self.view.m_grid2.ForceRefresh()
130
+ # Init the time history and PSD plots
131
+ self.view.init_plot()
132
+
133
+ def on_exit(self, event):
134
+
135
+ self.view.Close()
136
+
137
+ def on_data_export(self, event):
138
+
139
+ self.model.export()
140
+
141
+ def Start_Button_onclick(self, event):
142
+
143
+ map = {
144
+ OPERATION_MODE.ONLINE_DAQ: 1,
145
+ OPERATION_MODE.STANDALONE_DATA_PEEKING: 5,
146
+ OPERATION_MODE.POST_DAQ_DATA_STREAMING: 7,
147
+ }
148
+ operation_Mode = self.view.m_choice_OperationMode.GetSelection()
149
+
150
+ self.model.init_other_data()
151
+ self.view.init_plot()
152
+ self.view.figure_update()
153
+ for inode in self.mesh.sockets.keys():
154
+ self.mesh.send_cmd(inode, CMD_TYPE.SET_MODE, data0 = map[operation_Mode])
155
+ self.model.update_mesh_status_data(inode, 'CMD', "sent")
156
+ self.view.table_update()
157
+ logger.info('command sent: {} start'.format(OPERATION_MODE(operation_Mode).name))
158
+
159
+ def Stop_Button_onclick(self, event):
160
+
161
+ map = {
162
+ OPERATION_MODE.ONLINE_DAQ: 0,
163
+ OPERATION_MODE.STANDALONE_DATA_PEEKING: 4,
164
+ OPERATION_MODE.POST_DAQ_DATA_STREAMING: 2,
165
+ }
166
+
167
+ operation_Mode = self.view.m_choice_OperationMode.GetSelection()
168
+
169
+ for inode in self.mesh.sockets.keys():
170
+ self.mesh.send_cmd(inode, CMD_TYPE.SET_MODE, data0 = map[operation_Mode])
171
+ self.model.update_mesh_status_data(inode, 'CMD', "sent")
172
+ self.view.table_update()
173
+
174
+ logger.info('command sent: {} stop'.format(OPERATION_MODE(operation_Mode).name))
175
+ # self.view.gui_update(1)
176
+
177
+ def choice_OperationMode_onchange(self, event):
178
+ operation_Mode = self.view.m_choice_OperationMode.GetSelection()
179
+ if operation_Mode == OPERATION_MODE.POST_DAQ_DATA_STREAMING:
180
+ for inode in self.mesh.sockets.keys():
181
+ self.mesh.send_cmd(inode, CMD_TYPE.SET_MODE, data0 = 2)
182
+ self.model.update_mesh_status_data(inode, 'CMD', "sent")
183
+ self.view.table_update()
184
+
185
+ if operation_Mode == OPERATION_MODE.ONLINE_DAQ:
186
+ self.PPS_outdate_check = True
187
+ else:
188
+ self.PPS_outdate_check = False
189
+
190
+
191
+ def on_view_clf(self, event):
192
+ self.model.init_other_data()
193
+ self.view.init_plot()
194
+ self.view.figure_update()
195
+ return
196
+
197
+ def on_cmd_sdclear(self, event):
198
+
199
+ for inode in self.mesh.sockets.keys():
200
+ self.mesh.send_cmd(inode, CMD_TYPE.CLEAR_STORAGE)
201
+ self.model.update_mesh_status_data(inode, 'CMD', "sent")
202
+ self.view.table_update()
203
+ logger.info('command sent: SD Clear sent')
204
+
205
+ def on_cmd_shutdown(self, event):
206
+
207
+ for inode in self.mesh.sockets.keys():
208
+ self.mesh.send_cmd(inode, CMD_TYPE.SHUTDOWN)
209
+ self.model.update_mesh_status_data(inode, 'CMD', "sent")
210
+ self.view.table_update()
211
+ logger.info('command sent: SD Clear sent')
212
+
213
+ return
214
+
215
+
216
+
217
+ if __name__ == '__main__':
218
+ app = wx.App()
219
+ controller = Controller()
220
+ # controller.view.gui_update(1)
221
+ app.MainLoop()
222
+
223
+
224
+
225
+
226
+