micrOSDevToolKit 2.10.2__py3-none-any.whl → 2.10.5__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.

Potentially problematic release.


This version of micrOSDevToolKit might be problematic. Click here for more details.

Files changed (34) hide show
  1. micrOS/release_info/micrOS_ReleaseInfo/system_analysis_sum.json +26 -18
  2. micrOS/source/Common.py +2 -2
  3. micrOS/source/Espnow.py +245 -123
  4. micrOS/source/Files.py +101 -0
  5. micrOS/source/LM_espnow.py +10 -7
  6. micrOS/source/LM_mqtt_pro.py +211 -0
  7. micrOS/source/LM_pacman.py +37 -57
  8. micrOS/source/LM_system.py +1 -1
  9. micrOS/source/Logger.py +5 -11
  10. micrOS/source/Shell.py +23 -19
  11. micrOS/source/Tasks.py +2 -2
  12. micrOS/source/__pycache__/Common.cpython-312.pyc +0 -0
  13. micrOS/source/__pycache__/Logger.cpython-312.pyc +0 -0
  14. micrOS/source/microIO.py +3 -2
  15. micrOS/source/urequests.py +10 -1
  16. {micrOSDevToolKit-2.10.2.dist-info → microsdevtoolkit-2.10.5.dist-info}/METADATA +3 -2
  17. {micrOSDevToolKit-2.10.2.dist-info → microsdevtoolkit-2.10.5.dist-info}/RECORD +34 -30
  18. {micrOSDevToolKit-2.10.2.dist-info → microsdevtoolkit-2.10.5.dist-info}/WHEEL +1 -1
  19. toolkit/simulator_lib/__pycache__/uos.cpython-312.pyc +0 -0
  20. toolkit/simulator_lib/uos.py +5 -5
  21. toolkit/workspace/precompiled/Espnow.mpy +0 -0
  22. toolkit/workspace/precompiled/Files.mpy +0 -0
  23. toolkit/workspace/precompiled/LM_espnow.py +10 -7
  24. toolkit/workspace/precompiled/LM_mqtt_pro.py +211 -0
  25. toolkit/workspace/precompiled/LM_pacman.mpy +0 -0
  26. toolkit/workspace/precompiled/LM_system.mpy +0 -0
  27. toolkit/workspace/precompiled/Logger.mpy +0 -0
  28. toolkit/workspace/precompiled/Shell.mpy +0 -0
  29. toolkit/workspace/precompiled/Tasks.mpy +0 -0
  30. toolkit/workspace/precompiled/microIO.mpy +0 -0
  31. toolkit/workspace/precompiled/urequests.mpy +0 -0
  32. {micrOSDevToolKit-2.10.2.data → microsdevtoolkit-2.10.5.data}/scripts/devToolKit.py +0 -0
  33. {micrOSDevToolKit-2.10.2.dist-info → microsdevtoolkit-2.10.5.dist-info/licenses}/LICENSE +0 -0
  34. {micrOSDevToolKit-2.10.2.dist-info → microsdevtoolkit-2.10.5.dist-info}/top_level.txt +0 -0
@@ -6,13 +6,14 @@ def load():
6
6
  """
7
7
  return Espnow.initialize()
8
8
 
9
- def send(peer, msg='modules'):
9
+ def send(peer:bytes|str, msg:str='modules'):
10
10
  """
11
11
  Send message to peer (by mac address)
12
12
  :param peer: mac address of espnow device
13
13
  :param msg: message string/load module call
14
14
  """
15
- return Espnow.espnow_send(peer, msg)
15
+ now = Espnow.initialize()
16
+ return now.send(peer, msg)
16
17
 
17
18
  def start_server():
18
19
  """
@@ -20,21 +21,23 @@ def start_server():
20
21
  - this can receive espnow messages
21
22
  - it includes Load Module execution logic (beta)
22
23
  """
23
- return Espnow.espnow_server()
24
+ now = Espnow.initialize()
25
+ return now.start_server()
24
26
 
25
27
  def stats():
26
28
  """
27
29
  Get ESPNOW stats
28
30
  """
29
- return Espnow.stats()
31
+ now = Espnow.initialize()
32
+ return now.stats()
30
33
 
31
- def add_peer(peer):
34
+ def add_peer(peer:bytes, dev_name:str=None):
32
35
  """
33
36
  Add ESPNOW peer to known hosts
34
37
  - It is needed before first send(...)
35
38
  """
36
39
  now = Espnow.initialize()
37
- return Espnow.add_peer(now, peer)
40
+ return now.add_peer(peer, dev_name)
38
41
 
39
42
  def mac_address():
40
43
  """
@@ -46,4 +49,4 @@ def help():
46
49
  """
47
50
  [beta] ESPNOW sender/receiver with LM execution
48
51
  """
49
- return 'load', 'send <peer> "ping"', 'start_server', 'add_peer <peer>', 'stats', 'mac_address'
52
+ return 'load', 'send <peer> "ping"', 'start_server', 'add_peer <peer> dev_name=None', 'stats', 'mac_address'
@@ -0,0 +1,211 @@
1
+ # async_message.py Test of asynchronous mqtt client with async Broker class
2
+ # (C) Copyright Peter Hinch 2024.
3
+ # Released under the MIT licence.
4
+ # Public brokers https://github.com/mqtt/mqtt.github.io/wiki/public_brokers
5
+ # mip command: ???
6
+
7
+ from Config import cfgget
8
+ from mqtt_as import MQTTClient, config
9
+ from Common import micro_task, console, syslog, exec_cmd
10
+
11
+
12
+ # Set up MQTT
13
+ class MQTT:
14
+ CLIENT:MQTTClient = None # MQTT Client (broker) instance
15
+ TOPIC = "micros" # Default topic
16
+ TOPIC_COMMAND_LUT = {} # Lookup table for command/topic pairs
17
+ # Example:
18
+ # {"topic1": ["mod func", "mod2 func"], "topic2": []}
19
+
20
+
21
+ async def _receiver(task):
22
+ """
23
+ MQTT AS receiver loop
24
+ """
25
+ async for topic, msg, retained in MQTT.CLIENT.queue:
26
+ topic, msg = topic.decode(), msg.decode()
27
+ console(f'Topic: "{topic}" Message: "{msg}" Retained: {retained}')
28
+
29
+ # Command execution... use MQTT.TOPIC_COMMAND_LUT
30
+ topic_commands:list = MQTT.TOPIC_COMMAND_LUT.get(topic, None)
31
+ output_struct:list = []
32
+ if topic_commands is None:
33
+ syslog(f"[WARN] mqtt Unknown topic: {topic}")
34
+ elif len(topic_commands) == 0:
35
+ syslog(f"[WARN] mqtt No commands for {topic}")
36
+ else:
37
+ task.out = f"Handle topic: {topic}"
38
+ for cmd in topic_commands:
39
+ single_command = cmd.split()
40
+ if len(single_command) > 0:
41
+ state, output = exec_cmd(single_command, jsonify=True, skip_check=True)
42
+ output_struct.append({"state": state, "result": output, "cmd": cmd})
43
+ if len(output_struct) > 0:
44
+ console(f'\tMQTT Publish: {output_struct}')
45
+ task.out = f"Publish {topic}"
46
+ MQTT.CLIENT.publish(topic, str(output_struct))
47
+ else:
48
+ task.out = f"Nothing to publish {topic}"
49
+ task.feed()
50
+
51
+
52
+ async def _subscribe():
53
+ """
54
+ MQTT AS Topic subscribe towards server
55
+ """
56
+ with micro_task(tag="mqtt.subscribe") as my_task:
57
+ my_task.out = "Started"
58
+ try:
59
+ for t in MQTT.TOPIC_COMMAND_LUT:
60
+ console(f"Subscribe topic: {t}")
61
+ await MQTT.CLIENT.subscribe(t, 1)
62
+ my_task.out = "Done"
63
+ except Exception as e:
64
+ my_task.out = f"Error: {e}"
65
+
66
+
67
+ async def _publish(message, topic):
68
+ """
69
+ Send message to topic with mqtt
70
+ """
71
+ tag = f"mqtt.publish.{topic}"
72
+ with micro_task(tag=tag) as my_task:
73
+ console(f"mqtt send: [{topic}] {message}")
74
+ await MQTT.CLIENT.publish(topic, message, qos=1)
75
+ my_task.out = "Sent"
76
+
77
+
78
+ async def _up():
79
+ """
80
+ UP Listener - resubscribe
81
+ """
82
+ with micro_task(tag="mqtt.up") as my_task:
83
+ while True:
84
+ # Wait for UP Event - (re)subscribe
85
+ my_task.out = "Wait"
86
+ await MQTT.CLIENT.up.wait()
87
+ MQTT.CLIENT.up.clear()
88
+ micro_task(tag="mqtt.subscribe", task=_subscribe())
89
+ my_task.out = "Re-Subscription"
90
+ my_task.feed()
91
+
92
+
93
+ async def _init_client(topic:str=None, commands:str=None, raw_dict:dict|None=None):
94
+ """
95
+ Initialize main mqtt receiver and topics
96
+ :param topic: topic string, ex.: 'lights'
97
+ :param commands: semicolon separated commands. ex.: 'rgb toggle; cct toggle'
98
+ OR
99
+ :param raw_dict: python dict string for multi topic subscription, ex.: {"lights": ["rgb toggle", "cct toggle"], ...}
100
+ """
101
+ with micro_task(tag="mqtt.client") as my_task:
102
+ try:
103
+ await MQTT.CLIENT.connect()
104
+ my_task.out = "Connection successful."
105
+ except OSError:
106
+ my_task.out = "Connection failed."
107
+ return
108
+ # Wait for mqtt client connected successfully
109
+ await MQTT.CLIENT.up.wait()
110
+ MQTT.CLIENT.up.clear()
111
+ # Initialize mqtt topics, ha
112
+ subscribe(topic, commands, raw_dict)
113
+ micro_task(tag="mqtt.up", task=_up())
114
+ # Async listener loop
115
+ await _receiver(my_task)
116
+ my_task.out = "Receiver closed"
117
+ # Close when listener exits
118
+ MQTT.CLIENT.close()
119
+
120
+ #########################################
121
+ # PUBLIC FUNCTIONS #
122
+ #########################################
123
+
124
+ def publish(message:str, topic:str=MQTT.TOPIC):
125
+ """
126
+ Publish message
127
+ :param message: string to be sent
128
+ :param topic: topic for message
129
+ """
130
+ state = micro_task(tag=f"mqtt.publish.{topic}", task=_publish(message, topic))
131
+ state = "starting" if state else "already running"
132
+ return f"Message send, {state}"
133
+
134
+
135
+ def subscribe(topic:str=None, commands:str=None, raw_dict:dict|None=None):
136
+ """
137
+ Subscribe for single topics and set callback function(s) aka command(s)
138
+ :param topic: topic string, ex.: 'lights'
139
+ :param commands: semicolon separated commands. ex.: 'rgb toggle; cct toggle'
140
+ OR
141
+ :param raw_dict: python dict string for multi topic subscription, ex.: {"lights": ["rgb toggle", "cct toggle"], ...}
142
+
143
+ return: all or selected topics command
144
+ """
145
+ updated = False
146
+ topic = topic.strip()
147
+ # Register single topic
148
+ if topic and commands:
149
+ # raw commands structure: 'rgb toggle; cct toggle'
150
+ commands = [ c.strip() for c in commands.split(";") ]
151
+ # commands: Topic LUT structure: {'topic': ['mod func'], ..., 'lights': ['rgb toggle', 'cct toggle']}
152
+ updated = True if MQTT.TOPIC_COMMAND_LUT.get(topic, None) is None else False
153
+ MQTT.TOPIC_COMMAND_LUT[topic] = commands
154
+ # Register multiple topics at once
155
+ elif isinstance(raw_dict, dict):
156
+ updated = True
157
+ MQTT.TOPIC_COMMAND_LUT.update(raw_dict)
158
+ # Start subscribe task
159
+ if updated:
160
+ state = micro_task(tag="mqtt.subscribe", task=_subscribe())
161
+ state = "starting" if state else "already running"
162
+ return f"Subscribe, {state}"
163
+
164
+ # Return handling
165
+ if topic is not None:
166
+ # Return selected topic commands
167
+ return MQTT.TOPIC_COMMAND_LUT.get(topic, None)
168
+ # Return registered topics
169
+ return MQTT.TOPIC_COMMAND_LUT
170
+
171
+
172
+ def _configure(server_ip:str, username:str, password:str):
173
+ # Define configuration
174
+ config["keepalive"] = 120
175
+ config["queue_len"] = 1 # Use event interface with default queue
176
+ # Define configuration
177
+ config['client_id'] = cfgget("devfid")
178
+ config['ssid'] = cfgget("staessid")
179
+ config['wifi_pw'] = cfgget("stapwd")
180
+ config['port'] = 1883 # expose????
181
+ config['server'] = server_ip # '172.20.10.2'
182
+ config['user'] = username # test
183
+ config['password'] = password # '12345'
184
+ return config
185
+
186
+
187
+ def load(server_ip:str, username:str, password:str, topic:str=None, commands:str=None, raw_dict:dict|None=None):
188
+ """
189
+ Load MQTT_AS receiver...
190
+ :param server_ip: server IP address
191
+ :param username: server user
192
+ :param password: server user password
193
+
194
+ :param topic: topic string, ex.: 'lights'
195
+ :param commands: semicolon separated commands. ex.: 'rgb toggle; cct toggle'
196
+ OR
197
+ :param raw_dict: python dict string for multi topic subscription, ex.: {"lights": ["rgb toggle", "cct toggle"], ...}
198
+ """
199
+ MQTTClient.DEBUG = True
200
+ MQTT.CLIENT = MQTTClient(_configure(server_ip, username, password))
201
+
202
+ state = micro_task(tag="mqtt.client", task=_init_client(topic, commands, raw_dict))
203
+ return "Starting" if state else "Already running"
204
+
205
+
206
+ def help():
207
+ return ("load <server_ip> <username> <password> topic='micros', commands='rgb toggle; cct toggle'",
208
+ "subscribe topic='micros', commands='rgb toggle; cct toggle'",
209
+ "subscribe #without params dumps the topic-command data structure",
210
+ "publish message='hello' topic='micros'",
211
+ "HINT: task show mqtt.*")
@@ -1,73 +1,56 @@
1
- from uos import listdir, remove, stat
2
1
  from sys import modules
3
2
  from Common import socket_stream
4
-
5
- WEB_EXT = ('html', 'js', 'css')
6
- DATA_TYPES = ('log', 'pds', 'dat')
7
-
8
- def _is_app_resource(path='/'):
9
- if stat(path)[0] & 0x4000: # Dir check
10
- return True, 'd'
11
- file_name = path.split("/")[-1]
12
- if file_name.startswith('LM_') or file_name.split('.')[-1] in WEB_EXT + DATA_TYPES:
13
- return True, 'f'
14
- return False, '?'
3
+ from Files import _is_module, list_fs, ilist_fs, remove_fs
15
4
 
16
5
 
17
6
  #############################################
18
7
  # Safe file system handler functions #
19
8
  #############################################
20
9
 
21
- def ls(path="/", content='*', raw=False):
10
+ def ls(path="/", content='*', raw=False, select='*'):
22
11
  """
23
12
  Linux like ls command - list app resources and app folders
24
13
  :param path: path to list, default: /
25
14
  :param content: content type, default all, f-file, d-dir can be selected
26
15
  :param raw: keep raw output [(is_app, type), ...]
16
+ :param select: select specific app resource: LM or IO, default: all
27
17
  """
28
- path = path if path.endswith('/') else f"{path}/"
29
- items = []
30
- for item in listdir(path):
31
- is_app, item_type = _is_app_resource(path + item)
32
- if is_app and (content == "*" or item_type == content):
33
- items.append((item_type, item))
18
+ items = list_fs(path, content, select=select)
34
19
  if raw:
35
20
  return items
36
- formatted_output = ""
37
- i = 0
38
- for f in items:
39
- i += 1
40
- spacer = " " * (4 - len(str(i)))
41
- formatted_output += f"{i}{spacer}{f[0]} {f[1]}\n"
42
- return formatted_output
21
+
22
+ # Build a formatted output (just like `ls -l` style index)
23
+ lines = ""
24
+ for i, f in enumerate(items):
25
+ spacer = " " * (4 - len(str(i+1)))
26
+ if content == "*":
27
+ lines += f"{i+1}{spacer}{f[0]} {f[1]}\n"
28
+ else:
29
+ lines += f"{i + 1}{spacer}{f}\n"
30
+ return lines
43
31
 
44
32
 
45
- def rm(path):
33
+ def rm(path, allow_dir=False):
46
34
  """
47
35
  Linux like rm command - delete app resources and folders
48
36
  :param path: app resource name/path, ex.: LM_robustness.py
37
+ :param allow_dir: enable directory deletion, default: False
49
38
  """
50
- if 'pacman.' in path or 'system.' in path or "/" == path.strip():
51
- return f'Load module {path} is protected, skip delete.'
52
- is_app, item_type = _is_app_resource(path)
53
- if is_app:
54
- remove(path)
55
- return f"Remove: {path} {'dir' if item_type == 'd' else 'file'}"
56
- return f"Invalid path {path}"
39
+ return remove_fs(path, allow_dir)
57
40
 
58
41
 
59
42
  def dirtree(path="/", raw=False):
60
43
  """Return only directories from a given path."""
61
44
  path = path if path.endswith('/') else f"{path}/"
62
- folders = [f"{path}/{item}" for item in listdir(path) if _is_app_resource(f"{path}{item}")[1] == 'd']
63
- folder_contents = {folder:listdir(folder) for folder in folders}
45
+ folders = [f"{path}/{item}" for item in ilist_fs(path, type_filter='d')]
46
+ folder_contents = {folder:list_fs(folder) for folder in folders}
64
47
  if raw:
65
48
  return folder_contents
66
49
  formatted_output = ""
67
50
  for k, v in folder_contents.items():
68
51
  formatted_output += f"{k}\n"
69
52
  for val in v:
70
- formatted_output += f"\t{val}\n"
53
+ formatted_output += f"\t{val[0]} {val[1]}\n"
71
54
  return formatted_output
72
55
 
73
56
 
@@ -108,18 +91,20 @@ def del_duplicates():
108
91
  - delete duplicated .mpy and .py resources, keep .mpy resource!
109
92
  """
110
93
  msg_buf = []
111
- py = list((res.split('.')[0] for res in listdir() if res.endswith('.py'))) # Normally smaller list
112
- mpy = (res.split('.')[0] for res in listdir() if res.endswith('.mpy'))
94
+ files = list_fs(type_filter='f', select='LM')
95
+ py = list((res.split('.')[0] for res in files if res.endswith('.py'))) # Normally smaller list
96
+ mpy = (res.split('.')[0] for res in files if res.endswith('.mpy'))
113
97
  for m in mpy:
114
98
  # Iterate over mpy resources
115
99
  state = True
116
100
  if m in py and m != 'main':
117
101
  to_delete = f'{m}.py'
118
102
  try:
119
- remove(to_delete)
103
+ verdict = remove_fs(to_delete)
120
104
  except:
105
+ verdict = "n/a"
121
106
  state = False
122
- msg_buf.append(f' Delete {to_delete} {state}')
107
+ msg_buf.append(f' Delete {to_delete} {state} - {verdict}')
123
108
  return '\n'.join(msg_buf) if len(msg_buf) > 0 else 'Nothing to delete.'
124
109
 
125
110
 
@@ -147,7 +132,7 @@ def cachedump(delpds=None, msgobj=None):
147
132
  if delpds is None:
148
133
  # List pds files aka application cache
149
134
  msg_buf = []
150
- for pds in (_pds for _pds in listdir() if _pds.endswith('.pds')):
135
+ for pds in (_pds for _pds in ilist_fs(type_filter='f') if _pds.endswith('.pds')):
151
136
  with open(pds, 'r') as f:
152
137
  if msgobj is None:
153
138
  msg_buf.append(f'{pds}: {f.read()}')
@@ -156,8 +141,8 @@ def cachedump(delpds=None, msgobj=None):
156
141
  return msg_buf if len(msg_buf) > 0 else ''
157
142
  # Remove given pds file
158
143
  try:
159
- remove(f'{delpds}.pds')
160
- return f'{delpds}.pds delete done.'
144
+ verdict = remove_fs(f'{delpds}.pds')
145
+ return f'{delpds}.pds delete done.: {verdict}'
161
146
  except:
162
147
  return f'{delpds}.pds not exists'
163
148
 
@@ -168,7 +153,7 @@ def dat_dump():
168
153
  - logged data from LMs, sensor datat, etc...
169
154
  """
170
155
  logs_dir = "/logs/"
171
- dats = (f for f in listdir(logs_dir) if f.endswith('.dat'))
156
+ dats = (f for f in ilist_fs(type_filter='f') if f.endswith('.dat'))
172
157
  out = {}
173
158
  for dat in dats:
174
159
  with open(f"{logs_dir}{dat}", 'r') as f:
@@ -188,8 +173,7 @@ def listmods(msgobj=None):
188
173
  """
189
174
  # Dump available LMs
190
175
  msg_buf = []
191
- for k in (res.replace('LM_', '') for res in listdir("/")
192
- if res.startswith('LM_') or res.split('.')[-1] in WEB_EXT):
176
+ for k in (res.replace('LM_', '') for res in ilist_fs(type_filter='f', select='LM')):
193
177
  if msgobj is None:
194
178
  msg_buf.append(f' {k}')
195
179
  else:
@@ -197,21 +181,17 @@ def listmods(msgobj=None):
197
181
  return msg_buf if len(msg_buf) > 0 else ''
198
182
 
199
183
 
200
- def delmod(mod=None):
184
+ def delmod(mod):
201
185
  """
202
186
  Module package manager
203
187
  :param mod:
204
188
  Delete Load Module with full name: module.py or module.mpy
205
189
  OR delete any web resource: *.js, *.css, *.html
206
190
  """
207
- if mod is not None and (mod.endswith('py') or mod.split('.')[-1] in WEB_EXT):
208
- # LM exception list - system and pacman cannot be deleted
209
- if 'pacman.' in mod or 'system.' in mod:
210
- return f'Load module {mod} is in use, skip delete.'
191
+ if mod.endswith('py') or _is_module(mod):
211
192
  try:
212
- to_remove = mod if mod.split('.')[-1] in WEB_EXT else f'LM_{mod}'
213
- remove(to_remove)
214
- return f'Delete module: {mod}'
193
+ to_remove = f'LM_{mod}' if mod.endswith('py') else mod
194
+ return remove_fs(to_remove)
215
195
  except Exception as e:
216
196
  return f'Cannot delete: {mod}: {e}'
217
197
  return f'Invalid value: {mod}'
@@ -223,7 +203,7 @@ def micros_checksum(msgobj=None):
223
203
  from binascii import hexlify
224
204
  from Config import cfgget
225
205
 
226
- for f_name in (_pds for _pds in listdir() if _pds.endswith('py')):
206
+ for f_name in ilist_fs(type_filter='f', select='LM'):
227
207
  with open(f_name, 'rb') as f:
228
208
  cs = hexlify(sha1(f.read()).digest()).decode('utf-8')
229
209
  msgobj(f"{cs} {f_name}")
@@ -244,5 +224,5 @@ def help(widgets=False):
244
224
  'dat_dump',
245
225
  'download url="BxNxM/micrOS/master/toolkit/workspace/precompiled/LM_robustness.py"',
246
226
  'micros_checksum',
247
- 'ls path="/" content="*/f/d"',
227
+ 'ls path="/" content="*/f/d" select="*/LM/IO"',
248
228
  'rm <path>', 'dirtree path="/"')
@@ -1,4 +1,4 @@
1
- from uos import statvfs, getcwd, listdir, uname
1
+ from uos import statvfs, getcwd, uname
2
2
  from utime import localtime
3
3
  from network import WLAN, STA_IF, AP_IF
4
4
  from binascii import hexlify
micrOS/source/Logger.py CHANGED
@@ -6,7 +6,8 @@ Designed by Marcell Ban aka BxNxM
6
6
  """
7
7
  from time import localtime
8
8
  from re import match
9
- from uos import listdir, remove, stat, mkdir, getcwd
9
+ from uos import remove, mkdir, getcwd
10
+ from Files import ilist_fs, is_dir
10
11
 
11
12
  #############################################
12
13
  # LOGGING WITH DATA ROTATION #
@@ -18,14 +19,7 @@ def _init_logger():
18
19
  global LOG_FOLDER
19
20
  if LOG_FOLDER is None:
20
21
  LOG_FOLDER = f"{getcwd()}logs"
21
- do_create = True
22
- try:
23
- if stat(LOG_FOLDER)[0] & 0x4000:
24
- # Dir exists - skip create
25
- do_create = False
26
- except:
27
- pass
28
- if do_create:
22
+ if not is_dir(LOG_FOLDER):
29
23
  try:
30
24
  mkdir(LOG_FOLDER)
31
25
  syslog(f"[BOOT] log dir {LOG_FOLDER} init")
@@ -107,7 +101,7 @@ def log_get(f_name, msgobj=None):
107
101
 
108
102
  def syslog(data=None, msgobj=None):
109
103
  if data is None:
110
- err_cnt = sum([log_get(f, msgobj) for f in listdir(LOG_FOLDER) if f.endswith(".sys.log")])
104
+ err_cnt = sum([log_get(f, msgobj) for f in ilist_fs(LOG_FOLDER, type_filter='f') if f.endswith(".sys.log")])
111
105
  return err_cnt
112
106
 
113
107
  _match = match(r"^\[([^\[\]]+)\]", data)
@@ -117,7 +111,7 @@ def syslog(data=None, msgobj=None):
117
111
 
118
112
 
119
113
  def log_clean(msgobj=None):
120
- to_del = [file for file in listdir(LOG_FOLDER) if file.endswith('.log')]
114
+ to_del = [file for file in ilist_fs(LOG_FOLDER, type_filter='f') if file.endswith('.log')]
121
115
  for _del in to_del:
122
116
  _del = f"{LOG_FOLDER}/{_del}"
123
117
  if msgobj is not None:
micrOS/source/Shell.py CHANGED
@@ -12,9 +12,9 @@ Designed by Marcell Ban aka BxNxM
12
12
  # IMPORTS #
13
13
  #################################################################
14
14
  from sys import modules
15
- from uos import listdir
16
15
  from machine import reset as hard_reset, soft_reset
17
16
  from Config import cfgget, cfgput
17
+ from Files import ilist_fs
18
18
  from Tasks import lm_exec
19
19
  from Debug import errlog_add
20
20
 
@@ -25,7 +25,7 @@ from Debug import errlog_add
25
25
 
26
26
  class Shell:
27
27
  __slots__ = ['__devfid', '__auth_mode', '__hwuid', '__auth_ok', '__conf_mode']
28
- MICROS_VERSION = '2.10.2-0'
28
+ MICROS_VERSION = '2.10.5-0'
29
29
 
30
30
  def __init__(self):
31
31
  """
@@ -155,27 +155,30 @@ class Shell:
155
155
 
156
156
  # HELP MSG
157
157
  if local_cmd and msg_list[0] == "help":
158
- await self.a_send("[MICROS] - built-in shell commands")
158
+ await self.a_send("[MICROS]")
159
159
  await self.a_send(" hello - hello msg - for device identification")
160
160
  await self.a_send(" modules - show active Load Modules")
161
161
  await self.a_send(" version - returns micrOS version")
162
- await self.a_send(" exit - exit from shell socket prompt")
162
+ await self.a_send(" exit - exit shell session")
163
163
  await self.a_send(" reboot - system soft reboot (vm), hard reboot (hw): reboot -h")
164
164
  await self.a_send(" webrepl - start webrepl, for file transfers use with --update")
165
- await self.a_send("[CONF] Configure mode - built-in shell commands")
165
+ await self.a_send("[CONF] Configuration mode")
166
166
  await self.a_send(" conf - Enter conf mode")
167
- await self.a_send(" dump - Dump all data")
167
+ await self.a_send(" dump - Dump all data, filter: dump <str>")
168
168
  await self.a_send(" key - Get value")
169
169
  await self.a_send(" key value - Set value")
170
170
  await self.a_send(" noconf - Exit conf mode")
171
- await self.a_send("[TASK] postfix: ...&x - one-time, ...&&x - periodic, x: wait ms [x min: 20ms]")
172
- await self.a_send(" task list - list tasks by <tag>s")
171
+ await self.a_send("[TASK] Task operations")
172
+ await self.a_send(" task list - list tasks by tags")
173
173
  await self.a_send(" task kill <tag> - stop task")
174
174
  await self.a_send(" task show <tag> - show task output")
175
- await self.a_send("[EXEC] Command mode (LMs):")
176
- await self.a_send(" >>node01.local - INTERCON postfix, execute command on remote device")
177
- await self.a_send(" >json - JSON postfix, request json formatted output")
178
- await self.a_send(" help lm - list ALL LoadModules")
175
+ await self.a_send("[EXEC] Command mode, syntax(...): <module> <function> <params> <postfix>")
176
+ await self.a_send(" Postfix hints:")
177
+ await self.a_send(" ... &<x> - start one-shot task")
178
+ await self.a_send(" ... &&<x> - start periodic task, where <x>: delay ms [x min: 20ms]")
179
+ await self.a_send(" ... >>hostname - remote command execution (intercon)")
180
+ await self.a_send(" ... >json - request json formatted output")
181
+ await self.a_send(" help lm - list ALL available LoadModules")
179
182
  if "lm" in str(msg_list):
180
183
  return await Shell._show_lm_funcs(msg_obj=self.a_send)
181
184
  return await Shell._show_lm_funcs(msg_obj=self.a_send, active_only=True)
@@ -252,13 +255,13 @@ class Shell:
252
255
  Dump LM modules with functions - in case of [py] files
253
256
  Dump LM module with help function call - in case of [mpy] files
254
257
  """
255
- async def _help(mod):
256
- for lm_path in (i for i in mod if i.startswith('LM_') and (i.endswith('py'))):
258
+ async def _help(mods):
259
+ for lm_path in mods:
257
260
  lm_name = lm_path.replace('LM_', '').split('.')[0]
258
261
  try:
259
- await msg_obj(f" {lm_name}")
262
+ await msg_obj(f" {lm_name}")
260
263
  if lm_path.endswith('.mpy'):
261
- await msg_obj(f" {' ' * len(lm_path.replace('LM_', '').split('.')[0])}help")
264
+ await msg_obj(f" {' ' * len(lm_path.replace('LM_', '').split('.')[0])}help")
262
265
  continue
263
266
  with open(lm_path, 'r') as f:
264
267
  line = "micrOSisTheBest"
@@ -266,19 +269,20 @@ class Shell:
266
269
  line = f.readline()
267
270
  ldata = line.strip()
268
271
  if ldata.startswith('def ') and not ldata.split()[1].startswith("_") and 'self' not in ldata:
269
- await msg_obj(f" {' ' * len(lm_name)}{ldata.replace('def ', '').split('(')[0]}")
272
+ await msg_obj(f" {' ' * len(lm_name)}{ldata.replace('def ', '').split('(')[0]}")
270
273
  except Exception as e:
271
274
  await msg_obj(f"[{lm_path}] SHOW LM PARSER WARNING: {e}")
272
275
  return False
273
276
  return True
274
277
 
278
+ await msg_obj("")
275
279
  # [1] list active modules (default in shell)
276
280
  if active_only:
277
281
  mod_keys = modules.keys()
278
- active_modules = (dir_mod for dir_mod in listdir() if dir_mod.split('.')[0] in mod_keys)
282
+ active_modules = (dir_mod for dir_mod in ilist_fs(type_filter='f', select="LM") if dir_mod.split('.')[0] in mod_keys)
279
283
  return await _help(active_modules)
280
284
  # [2] list all LMs on file system (ALL - help lm) - manual
281
- return await _help(listdir())
285
+ return await _help(ilist_fs(type_filter='f', select="LM"))
282
286
 
283
287
  @staticmethod
284
288
  async def webrepl(msg_obj, update=False):
micrOS/source/Tasks.py CHANGED
@@ -44,7 +44,7 @@ class TaskBase:
44
44
  self.out = "" # Store task output
45
45
 
46
46
  @staticmethod
47
- def is_busy(tag) -> bool:
47
+ def is_busy(tag:str) -> bool:
48
48
  """
49
49
  Check task is busy by tag
50
50
  :param tag: for task selection
@@ -335,7 +335,7 @@ class Manager:
335
335
  _tasks = []
336
336
  tag_parts = tag.split('.')
337
337
  for t in TaskBase.TASKS:
338
- if t.startswith(tag_parts[0]) and len(tag_parts) > 1 and tag_parts[1] == '*':
338
+ if len(tag_parts) > 1 and t.startswith('.'.join(tag_parts[0:-1])) and tag_parts[-1] == '*':
339
339
  _tasks.append(t)
340
340
  if len(_tasks) == 0:
341
341
  return []
micrOS/source/microIO.py CHANGED
@@ -10,7 +10,8 @@ Designed by Marcell Ban aka BxNxM
10
10
  # IMPORTS #
11
11
  #################################################################
12
12
  from sys import platform
13
- from uos import listdir, uname
13
+ from uos import uname
14
+ from Files import ilist_fs
14
15
  from Logger import syslog
15
16
 
16
17
  #################################################################
@@ -65,7 +66,7 @@ def set_pinmap(map_data=None):
65
66
 
66
67
  # SELECT LOOKUP TABLE BASED ON PLATFORM / User input
67
68
  if isinstance(io_file, str) and io_file != 'n/a':
68
- if f"IO_{io_file}" in [io.split('.')[0] for io in listdir() if io.startswith('IO_')]:
69
+ if f"IO_{io_file}" in [io.split('.')[0] for io in ilist_fs(type_filter='f', select="IO")]:
69
70
  PinMap.MAPPING_LUT = io_file
70
71
  return PinMap.MAPPING_LUT
71
72
  PinMap.MAPPING_LUT = detect_platform()