micrOSDevToolKit 2.8.6__py3-none-any.whl → 2.9.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.

Potentially problematic release.


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

Files changed (104) hide show
  1. env/driver_cp210x/.DS_Store +0 -0
  2. env/driver_cp210x/macOS_VCP_Driver/SiLabsUSBDriverDisk.dmg +0 -0
  3. env/driver_cp210x/macOS_VCP_Driver/macOS_VCP_Driver_Release_Notes.txt +0 -0
  4. micrOS/micropython/esp32-20241129-v1.24.1.bin +0 -0
  5. micrOS/micropython/esp32s3-20241129-v1.24.1.bin +0 -0
  6. micrOS/micropython/esp32s3_spiram_oct-20241129-v1.24.1.bin +0 -0
  7. micrOS/micropython/rpi-pico-w-20241129-v1.24.1.uf2 +0 -0
  8. micrOS/micropython/tinypico-20241129-v1.24.1.bin +0 -0
  9. micrOS/release_info/micrOS_ReleaseInfo/system_analysis_sum.json +40 -40
  10. micrOS/source/Common.py +12 -17
  11. micrOS/source/Config.py +20 -1
  12. micrOS/source/Hooks.py +25 -15
  13. micrOS/source/LM_buzzer.py +16 -9
  14. micrOS/source/LM_cct.py +2 -3
  15. micrOS/source/LM_dimmer.py +1 -2
  16. micrOS/source/LM_distance.py +2 -4
  17. micrOS/source/LM_genIO.py +1 -1
  18. micrOS/source/LM_i2s_mic.py +4 -4
  19. micrOS/source/LM_keychain.py +2 -3
  20. micrOS/source/LM_light_sensor.py +1 -2
  21. micrOS/source/LM_neoeffects.py +22 -7
  22. micrOS/source/LM_neopixel.py +2 -3
  23. micrOS/source/LM_oledui.py +3 -4
  24. micrOS/source/LM_pet_feeder.py +3 -4
  25. micrOS/source/LM_ph_sensor.py +2 -2
  26. micrOS/source/LM_presence.py +2 -3
  27. micrOS/source/LM_rest.py +52 -9
  28. micrOS/source/LM_rgb.py +1 -2
  29. micrOS/source/LM_roboarm.py +1 -2
  30. micrOS/source/LM_sound_event.py +2 -3
  31. micrOS/source/LM_system.py +14 -3
  32. micrOS/source/LM_telegram.py +226 -15
  33. micrOS/source/Logger.py +6 -0
  34. micrOS/source/Notify.py +46 -218
  35. micrOS/source/Scheduler.py +7 -4
  36. micrOS/source/Shell.py +2 -2
  37. micrOS/source/Tasks.py +106 -98
  38. micrOS/source/Types.py +6 -2
  39. micrOS/source/Web.py +32 -8
  40. micrOS/source/dashboard.html +4 -4
  41. micrOS/source/micrOS.py +5 -10
  42. micrOS/source/micrOSloader.py +16 -18
  43. micrOS/source/uapi.js +5 -6
  44. micrOS/source/urequests.py +171 -85
  45. {micrOSDevToolKit-2.8.6.dist-info → micrOSDevToolKit-2.9.0.dist-info}/METADATA +2 -2
  46. {micrOSDevToolKit-2.8.6.dist-info → micrOSDevToolKit-2.9.0.dist-info}/RECORD +95 -97
  47. toolkit/DevEnvUSB.py +1 -6
  48. toolkit/MicrOSDevEnv.py +8 -5
  49. toolkit/simulator_lib/__pycache__/simulator.cpython-312.pyc +0 -0
  50. toolkit/simulator_lib/__pycache__/uasyncio.cpython-312.pyc +0 -0
  51. toolkit/simulator_lib/__pycache__/usocket.cpython-312.pyc +0 -0
  52. toolkit/simulator_lib/simulator.py +14 -0
  53. toolkit/simulator_lib/uasyncio.py +2 -2
  54. toolkit/simulator_lib/usocket.py +5 -1
  55. toolkit/workspace/precompiled/Common.mpy +0 -0
  56. toolkit/workspace/precompiled/Config.mpy +0 -0
  57. toolkit/workspace/precompiled/Hooks.mpy +0 -0
  58. toolkit/workspace/precompiled/LM_buzzer.mpy +0 -0
  59. toolkit/workspace/precompiled/LM_cct.mpy +0 -0
  60. toolkit/workspace/precompiled/LM_dimmer.mpy +0 -0
  61. toolkit/workspace/precompiled/LM_distance.mpy +0 -0
  62. toolkit/workspace/precompiled/LM_genIO.mpy +0 -0
  63. toolkit/workspace/precompiled/LM_i2s_mic.mpy +0 -0
  64. toolkit/workspace/precompiled/LM_keychain.mpy +0 -0
  65. toolkit/workspace/precompiled/LM_light_sensor.mpy +0 -0
  66. toolkit/workspace/precompiled/LM_neoeffects.mpy +0 -0
  67. toolkit/workspace/precompiled/LM_neopixel.mpy +0 -0
  68. toolkit/workspace/precompiled/LM_oledui.mpy +0 -0
  69. toolkit/workspace/precompiled/LM_pet_feeder.py +3 -4
  70. toolkit/workspace/precompiled/LM_ph_sensor.py +2 -2
  71. toolkit/workspace/precompiled/LM_presence.mpy +0 -0
  72. toolkit/workspace/precompiled/LM_rest.mpy +0 -0
  73. toolkit/workspace/precompiled/LM_rgb.mpy +0 -0
  74. toolkit/workspace/precompiled/LM_roboarm.mpy +0 -0
  75. toolkit/workspace/precompiled/LM_sound_event.mpy +0 -0
  76. toolkit/workspace/precompiled/LM_system.mpy +0 -0
  77. toolkit/workspace/precompiled/LM_telegram.mpy +0 -0
  78. toolkit/workspace/precompiled/Logger.mpy +0 -0
  79. toolkit/workspace/precompiled/Notify.mpy +0 -0
  80. toolkit/workspace/precompiled/Scheduler.mpy +0 -0
  81. toolkit/workspace/precompiled/Shell.mpy +0 -0
  82. toolkit/workspace/precompiled/Tasks.mpy +0 -0
  83. toolkit/workspace/precompiled/Types.mpy +0 -0
  84. toolkit/workspace/precompiled/Web.mpy +0 -0
  85. toolkit/workspace/precompiled/dashboard.html +4 -4
  86. toolkit/workspace/precompiled/micrOS.mpy +0 -0
  87. toolkit/workspace/precompiled/micrOSloader.mpy +0 -0
  88. toolkit/workspace/precompiled/node_config.json +1 -1
  89. toolkit/workspace/precompiled/uapi.js +5 -6
  90. toolkit/workspace/precompiled/urequests.mpy +0 -0
  91. env/driver_cp210x/CH34XSER_MAC/CH34X_DRV_INSTALL_INSTRUCTIONS.pdf +0 -0
  92. env/driver_cp210x/CH34XSER_MAC/CH34xVCPDriver.pkg +0 -0
  93. micrOS/micropython/esp32-20231005-v1.21.0.bin +0 -0
  94. micrOS/micropython/esp32-GENERIC-20240602-v1.23.0.bin +0 -0
  95. micrOS/micropython/rpi-pico-w-20240602-v1.23.0.uf2 +0 -0
  96. micrOS/micropython/tinypico-20231005-v1.21.0.bin +0 -0
  97. micrOS/micropython/tinypico_usbc-UM-20240105-v1.22.1.bin +0 -0
  98. /micrOS/micropython/{esp32c3-GENERIC-20240222-v1.22.2.bin → esp32c3-20240222-v1.22.2.bin} +0 -0
  99. /micrOS/micropython/{esp32s2-GENERIC-20240602-v1.23.0.bin → esp32s2-20240602-v1.23.0.bin} +0 -0
  100. /micrOS/micropython/{esp32s3-GENERIC-20240105-v1.22.1.bin → esp32s3-20240105-v1.22.1.bin} +0 -0
  101. {micrOSDevToolKit-2.8.6.data → micrOSDevToolKit-2.9.0.data}/scripts/devToolKit.py +0 -0
  102. {micrOSDevToolKit-2.8.6.dist-info → micrOSDevToolKit-2.9.0.dist-info}/LICENSE +0 -0
  103. {micrOSDevToolKit-2.8.6.dist-info → micrOSDevToolKit-2.9.0.dist-info}/WHEEL +0 -0
  104. {micrOSDevToolKit-2.8.6.dist-info → micrOSDevToolKit-2.9.0.dist-info}/top_level.txt +0 -0
micrOS/source/Tasks.py CHANGED
@@ -19,7 +19,7 @@ from Network import sta_high_avail
19
19
 
20
20
  try:
21
21
  from gc import collect
22
- except:
22
+ except ImportError:
23
23
  console_write("[SIMULATOR MODE GC IMPORT]")
24
24
  from simgc import collect
25
25
 
@@ -38,22 +38,34 @@ class TaskBase:
38
38
  TASKS = {} # TASK OBJ list
39
39
 
40
40
  def __init__(self):
41
- self.task = None # [TASK] Store created async task object
42
- self.done = asyncio.Event() # [TASK] Store done state
43
- self.out = "" # [TASK] Store output
44
- self.tag = None # [TASK] Task tag (identification)
41
+ self.task = None # Store created async task object
42
+ self.tag = None # Task tag (identification)
43
+ self.done = asyncio.Event() # Store task done state
44
+ self.out = "" # Store task output
45
45
 
46
46
  @staticmethod
47
- def is_busy(tag):
47
+ def is_busy(tag) -> bool:
48
48
  """
49
- Check task is busy by tag in TASKS
50
- - exists + running = busy
49
+ Check task is busy by tag
50
+ :param tag: for task selection
51
51
  """
52
52
  task = TaskBase.TASKS.get(tag, None)
53
53
  # return True: busy OR False: not busy (inactive)
54
54
  return bool(task is not None and not task.done.is_set())
55
55
 
56
- def cancel(self):
56
+ @staticmethod
57
+ def task_gc():
58
+ """
59
+ Automatic passive task deletion over QUEUE_SIZE
60
+ """
61
+ keep = TaskBase.QUEUE_SIZE
62
+ passive = tuple((task_tag for task_tag in list(TaskBase.TASKS) if not TaskBase.is_busy(task_tag)))
63
+ if len(passive) >= keep:
64
+ for i in range(0, len(passive)-keep+1):
65
+ del TaskBase.TASKS[passive[i]]
66
+ collect() # GC collect
67
+
68
+ def cancel(self) -> bool:
57
69
  """
58
70
  Cancel task (+cleanup)
59
71
  """
@@ -83,13 +95,13 @@ class TaskBase:
83
95
  collect() # GC collect
84
96
 
85
97
  @staticmethod
86
- def task_gc():
87
- keep = TaskBase.QUEUE_SIZE
88
- passive = tuple((task_tag for task_tag in list(TaskBase.TASKS) if not TaskBase.is_busy(task_tag)))
89
- if len(passive) >= keep:
90
- for i in range(0, len(passive)-keep+1):
91
- del TaskBase.TASKS[passive[i]]
92
- collect() # GC collect
98
+ async def feed(sleep_ms=1):
99
+ """
100
+ Feed event loop
101
+ :param sleep_ms: in millisecond (min: 1)
102
+ """
103
+ # TODO: feed WDT - preemptive cooperative multitasking aka reboot if no feed until X time period
104
+ return await asyncio.sleep_ms(sleep_ms)
93
105
 
94
106
  def __del__(self):
95
107
  try:
@@ -187,9 +199,12 @@ class MagicTask(TaskBase):
187
199
  - self.__inloop: lm call type - one-shot (False) / looped (True)
188
200
  - self.__msg_buf: lm msg object redirect to variable - store lm output
189
201
  """
202
+ jsonify = self.__callback[-1] == '>json'
203
+ if jsonify:
204
+ self.__callback = self.__callback[:-1]
190
205
  while True:
191
- await asyncio.sleep_ms(self.__sleep)
192
- state, self.out = _exec_lm_core(self.__callback)
206
+ await self.feed(self.__sleep)
207
+ state, self.out = _exec_lm_core(self.__callback, jsonify)
193
208
  if not state or not self.__inloop:
194
209
  break
195
210
  self.task_gc() # Task pool cleanup
@@ -265,10 +280,10 @@ class Manager:
265
280
  try:
266
281
  while True:
267
282
  # [0] Just chill
268
- await asyncio.sleep_ms(300)
283
+ await my_task.feed(300)
269
284
  # [1] PROBE SYSTEM LOAD + 300ms
270
285
  t = ticks_ms()
271
- await asyncio.sleep_ms(300)
286
+ await my_task.feed(300)
272
287
  delta_rate = int(((ticks_diff(ticks_ms(), t) / 300) - 1) * 100)
273
288
  Manager.LOAD = int((Manager.LOAD + delta_rate) / 2) # Average - smooth
274
289
  # [2] NETWORK AUTO REPAIR
@@ -353,7 +368,6 @@ class Manager:
353
368
  - by tag: module.function
354
369
  - by tag module.*, kill all for selected module
355
370
  """
356
-
357
371
  def terminate(_tag):
358
372
  to_kill = TaskBase.TASKS.get(_tag, None)
359
373
  try:
@@ -399,85 +413,94 @@ class Manager:
399
413
  #################################################################
400
414
  # LM EXEC CORE functions #
401
415
  #################################################################
402
-
403
- def lm_exec(arg_list):
416
+ def exec_builtins(func):
417
+ """
418
+ Module execution built-in commands and modifiers
419
+ - modules - show active modules list
420
+ - task kill ... - task termination
421
+ show ... - task output dump
422
+ - ... >json - postfix to "jsonize" the output
423
+ """
424
+ def wrapper(arg_list, jsonify=None):
425
+ # Ensure the parameter is a list of strings
426
+ if isinstance(arg_list, list) and arg_list:
427
+ # JSONIFY: [1] >json in arg_list or [2] jsonify True/False
428
+ json_flag = arg_list[-1] == '>json'
429
+ if json_flag:
430
+ arg_list = arg_list[:-1]
431
+ json_flag = jsonify if isinstance(jsonify, bool) else json_flag
432
+ # MODULES
433
+ if arg_list[0] == 'modules':
434
+ return True, list((m.strip().replace('LM_', '') for m in modules if m.startswith('LM_'))) + ['task']
435
+ # Handle task manipulation commands: list, kill, show - return True -> Command handled
436
+ if 'task' == arg_list[0]:
437
+ arg_len = len(arg_list)
438
+ # task list
439
+ if arg_len > 1 and 'list' == arg_list[1]:
440
+ on, off = Manager.list_tasks(json=json_flag)
441
+ # RETURN: JSON mode Human readable mode with cpu & queue info
442
+ return (True, {'active': on[3:], 'inactive': off}) if json_flag else (True, '\n'.join(on) + '\n' + '\n'.join(off) + '\n')
443
+ # task kill <taskID> / task show <taskID>
444
+ if arg_len > 2:
445
+ if 'kill' == arg_list[1]:
446
+ state, msg = Manager.kill(tag=arg_list[2])
447
+ return True, msg
448
+ if 'show' == arg_list[1]:
449
+ return True, Manager.show(tag=arg_list[2])
450
+ return True, "Invalid task cmd! Help: task list / kill <taskID> / show <taskID>"
451
+ # Call the decorated function with the additional flag
452
+ return func(arg_list, json_flag)
453
+ return wrapper
454
+
455
+
456
+ @exec_builtins
457
+ def lm_exec(arg_list, jsonify):
404
458
  """
405
459
  Main LM executor function with
406
460
  - async (background)
407
461
  - sync
408
462
  (single) task execution (_exec_lm_core)
409
463
  :param arg_list: command parameters
410
- Return Bool(OK/NOK), STR(Command output)
464
+ :param jsonify: request json output (controlled by the decorator)
465
+ Return Bool(OK/NOK), "Command output"
411
466
  """
412
467
 
413
- def _exec_task():
414
- nonlocal arg_list, arg_len
415
- # [1] Handle task manipulation commands: list, kill, show - return True -> Command handled
416
- if 'task' == arg_list[0]:
417
- # task list
418
- if arg_len > 1 and 'list' == arg_list[1]:
419
- if arg_len > 2 and arg_list[2].strip() == '>json':
420
- # JSON mode
421
- on, off = Manager.list_tasks(json=True)
422
- return True, {'active': on[3:], 'inactive': off}
423
- on, off = Manager.list_tasks(json=False)
424
- # Human readable mode with cpu & queue info
425
- return True, '\n'.join(on) + '\n' + '\n'.join(off) + '\n' # Show active tasks and passive tasks
426
- # task kill <taskID> / task show <taskID>
427
- if arg_len > 2:
428
- if 'kill' == arg_list[1]:
429
- state, msg = Manager.kill(tag=arg_list[2])
430
- return True, msg
431
- if 'show' == arg_list[1]:
432
- return True, Manager.show(tag=arg_list[2])
433
- return True, "Invalid task cmd! Help: task list / kill <taskID> / show <taskID>"
434
- # [2] Start async task, postfix: &, &&
435
- if arg_len > 2 and '&' in arg_list[-1]:
436
- # Evaluate task mode: loop + delay
437
- mode = arg_list.pop(-1)
438
- loop = mode.count('&') == 2
439
- delay = mode.replace('&', '').strip()
440
- delay = int(delay) if delay.isdigit() else None
441
- # Create and start async lm task
442
- try:
443
- state = Manager.create_task(arg_list, loop=loop, delay=delay)
444
- except Exception as e:
445
- # Valid & handled task command
446
- return True, str(e)
447
- tag = '.'.join(arg_list[0:2])
468
+ # [1] Async "background" task execution, postfix: &, &&
469
+ if len(arg_list) > 2 and '&' in arg_list[-1]:
470
+ # Evaluate task mode: loop + delay
471
+ mode = arg_list.pop(-1)
472
+ loop = mode.count('&') == 2
473
+ delay = mode.replace('&', '').strip()
474
+ delay = int(delay) if delay.isdigit() else None
475
+ # Create and start async lm task
476
+ try:
477
+ state = Manager.create_task(arg_list, loop=loop, delay=delay)
478
+ except Exception as e:
448
479
  # Valid & handled task command
449
- if state:
450
- return True, f"Start {tag}"
451
- return True, f"{tag} is Busy"
452
- # Not valid task command
453
- return False, ''
454
-
455
- # ================ main function ================
456
- # modules built-in function: show loaded LoadModules
457
- arg_len = len(arg_list)
458
- if arg_len > 0 and arg_list[0] == 'modules':
459
- return True, list((m.strip().replace('LM_', '') for m in modules if m.startswith('LM_'))) + ['task']
460
- # [1] Run task command: start (&), list, kill, show
461
- is_task, out = _exec_task()
462
- if is_task:
463
- return True, out
480
+ return True, str(e)
481
+ tag = '.'.join(arg_list[0:2])
482
+ # Valid & handled task command
483
+ if state:
484
+ return True, f"Start {tag}"
485
+ return True, f"{tag} is Busy"
486
+
464
487
  # [2] Sync "realtime" task execution
465
- state, out = _exec_lm_core(arg_list)
488
+ state, out = _exec_lm_core(arg_list, jsonify)
466
489
  return state, out
467
490
 
468
491
 
469
- def _exec_lm_core(cmd_list):
492
+ def _exec_lm_core(cmd_list, jsonify):
470
493
  """
471
494
  [CORE] Single command executor: MODULE.FUNCTION...
472
495
  :param cmd_list: list of string parameters
473
496
  [1] module name (LM)
474
497
  [2] function
475
498
  [3...] parameters (separator: space)
476
- [-1] Built-in json output handler: >json
499
+ :param jsonify: request json output
477
500
  Return Bool(OK/NOK), STR(Command output)
478
501
  """
479
502
 
480
- def __func_params(param):
503
+ def _func_params(param):
481
504
  buf = None
482
505
  if "'" in param or '"' in param:
483
506
  str_index = [i for i, c in enumerate(param) if c in ('"', "'")]
@@ -489,12 +512,9 @@ def _exec_lm_core(cmd_list):
489
512
  param = param.format(*buf)
490
513
  return param
491
514
 
492
- # Check json mode for LM execution
493
- json_mode = cmd_list[-1] == '>json'
494
- cmd_list = cmd_list[0:-1] if json_mode else cmd_list
495
515
  # LoadModule execution
496
516
  if len(cmd_list) >= 2:
497
- lm_mod, lm_func, lm_params = f"LM_{cmd_list[0]}", cmd_list[1], __func_params(' '.join(cmd_list[2:]))
517
+ lm_mod, lm_func, lm_params = f"LM_{cmd_list[0]}", cmd_list[1], _func_params(' '.join(cmd_list[2:]))
498
518
  try:
499
519
  # ------------- LM LOAD & EXECUTE ------------- #
500
520
  # [1] LOAD MODULE - OPTIMIZED by sys.modules
@@ -516,11 +536,11 @@ def _exec_lm_core(cmd_list):
516
536
  # Handle LM output data
517
537
  if isinstance(lm_output, dict):
518
538
  # json True: output->json else Format dict output "human readable"
519
- lm_output = dumps(lm_output) if json_mode else '\n'.join(
539
+ lm_output = dumps(lm_output) if jsonify else '\n'.join(
520
540
  [f" {key}: {value}" for key, value in lm_output.items()])
521
541
  if lm_func == 'help':
522
542
  # Special case for help command: json True: output->json else Format dict output "human readable"
523
- lm_output = dumps(lm_output) if json_mode else '\n'.join([f" {out}," for out in lm_output])
543
+ lm_output = dumps(lm_output) if jsonify else '\n'.join([f" {out}," for out in lm_output])
524
544
  # Return LM exec result
525
545
  return True, str(lm_output)
526
546
  # ---------------------------------------------------------------------- #
@@ -540,7 +560,7 @@ def lm_is_loaded(lm_name):
540
560
  [Auth mode]
541
561
  Check lm_name in enabled modules
542
562
  """
543
- static_keywords = tuple('task')
563
+ static_keywords = ('task', 'modules')
544
564
  loaded_mods = [lm.replace('LM_', '') for lm in modules if lm.startswith('LM_')]
545
565
  return lm_name in static_keywords or lm_name in loaded_mods
546
566
 
@@ -573,7 +593,7 @@ def exec_lm_pipe(taskstr):
573
593
 
574
594
  def exec_lm_pipe_schedule(taskstr):
575
595
  """
576
- Scheduled Wrapper for exec_lm_pipe for IRQs (extIRQ, timIRQ)
596
+ Scheduled Wrapper for exec_lm_pipe for IRQs (extIRQ, timIRQ, cronIRQ)
577
597
  """
578
598
  try:
579
599
  schedule(exec_lm_pipe, taskstr)
@@ -581,15 +601,3 @@ def exec_lm_pipe_schedule(taskstr):
581
601
  except Exception as e:
582
602
  errlog_add(f"[ERR] exec_lm_pipe_schedule: {e}")
583
603
  return False
584
-
585
-
586
- def exec_lm_core_schedule(arg_list):
587
- """
588
- Scheduled Wrapper for lm_exec for cron IRQ
589
- """
590
- try:
591
- schedule(lm_exec, arg_list)
592
- return True
593
- except Exception as e:
594
- errlog_add(f"[ERR] schedule_lm_exec {arg_list}: {e}")
595
- return False
micrOS/source/Types.py CHANGED
@@ -70,8 +70,12 @@ def _generate(type_dict, help_msg):
70
70
  overwrite[value_type] = values
71
71
  valid_params.append(param_str)
72
72
  else:
73
- param_str = f'{p}=:{"range" if "range" in type_dict else "options"}:'
74
- valid_params.append(param_str)
73
+ if p.startswith("&"):
74
+ # Handle special use case - task postfix
75
+ valid_params.append(p)
76
+ else:
77
+ param_str = f'{p}=:{"range" if "range" in type_dict else "options"}:'
78
+ valid_params.append(param_str)
75
79
  type_dict['lm_call'] = f"{func} {' '.join(valid_params)}"
76
80
  return dumps(type_dict | overwrite)
77
81
 
micrOS/source/Web.py CHANGED
@@ -1,3 +1,17 @@
1
+ """
2
+ Module is responsible for webserver environment
3
+ dedicated to micrOS framework.
4
+ Built-in-function:
5
+ - response
6
+ - landing page: index.html
7
+ - rest/ - call load modules, e.x.: system/top
8
+ - file response (.html, .css, .js, .jped) - generic file server feature
9
+ - "virtual" endpoints - to reply from script on a defined endpoint
10
+ - stream - stream data (jpeg) function
11
+
12
+ Designed by Marcell Ban aka BxNxM
13
+ """
14
+
1
15
  from json import dumps, loads
2
16
  import uasyncio as asyncio
3
17
  from Tasks import lm_exec, NativeTask, lm_is_loaded
@@ -60,9 +74,22 @@ class WebEngine:
60
74
  try:
61
75
  # SEND RESOURCE CONTENT: HTML, JS, CSS
62
76
  with open(resource, 'r') as file:
63
- html = file.read()
64
- await self.client.a_send(self.REQ200.format(dtype=WebEngine.file_type(resource), len=len(html), data=html))
65
- except OSError:
77
+ data = file.read()
78
+ response = self.REQ200.format(dtype=WebEngine.file_type(resource), len=len(data), data=data)
79
+ # Send entire response data
80
+ await self.client.a_send(response)
81
+
82
+ # Send chunks of response data (experimental)
83
+ #response_len, chunk_size, position = len(response), 300, 0
84
+ #while position < response_len:
85
+ # # Calculate the size of the next chunk
86
+ # next_chunk_size = min(chunk_size, response_len - position)
87
+ # chunk = response[position:position + next_chunk_size]
88
+ # await self.client.a_send(chunk)
89
+ # position += next_chunk_size
90
+ except Exception as e:
91
+ if 'memory allocation failed' in str(e):
92
+ errlog_add(f"[ERR] WebCli {resource}: {e}")
66
93
  await self.client.a_send(self.REQ404.format(len=13, data='404 Not Found'))
67
94
  return
68
95
  # INVALID/BAD REQUEST
@@ -76,14 +103,11 @@ class WebEngine:
76
103
  # REST sub-parameter handling (rest commands)
77
104
  cmd = (cmd.replace('/', ' ').replace('%22', '"').replace('%E2%80%9C', '"')
78
105
  .replace('%E2%80%9D', '"').replace('-', ' ').strip().split())
79
- # request json format instead of default string output (+ handle & tasks syntax)
80
- cmd.insert(-1, '>json') if cmd[-1].startswith('&') else cmd.append('>json')
81
106
  # EXECUTE COMMAND - LoadModule
82
107
  if WebEngine.AUTH:
83
- state, out = lm_exec(cmd) if lm_is_loaded(cmd[0]) or cmd[0].startswith('modules') else (
84
- True, 'Auth:Protected')
108
+ state, out = lm_exec(cmd, jsonify=True) if lm_is_loaded(cmd[0]) else (True, 'Auth:Protected')
85
109
  else:
86
- state, out = lm_exec(cmd)
110
+ state, out = lm_exec(cmd, jsonify=True)
87
111
  try:
88
112
  resp_schema['result'] = loads(out) # Load again ... hack for embedded shell json converter...
89
113
  except:
@@ -31,10 +31,10 @@
31
31
  transition: border-color 0.3s ease; /* Smooth transition for border color */
32
32
  }
33
33
  </style>
34
- <script src="uapi.js" defer></script>
35
- <script src="uwidgets.js" defer></script>
36
- <script src="uwidgets_pro.js" defer></script>
37
- <script src="udashboard.js" defer></script>
34
+ <script src="uapi.js" ></script>
35
+ <script src="uwidgets.js" ></script>
36
+ <script src="uwidgets_pro.js" ></script>
37
+ <script src="udashboard.js" ></script>
38
38
  </head>
39
39
  <body>
40
40
  <h1> micrOS dashboard </h1>
micrOS/source/micrOS.py CHANGED
@@ -20,15 +20,6 @@ from Debug import errlog_add
20
20
  # INTERRUPT HANDLER INTERFACES / WRAPPERS #
21
21
  #################################################################
22
22
 
23
-
24
- def safe_boot():
25
- try:
26
- bootup()
27
- except Exception as e:
28
- print(f"[micrOS main] Hooks.boot() error: {e}")
29
- errlog_add(f"[ERR] safe_boot: {e}")
30
-
31
-
32
23
  def irq_handler():
33
24
  try:
34
25
  enableInterrupt()
@@ -58,7 +49,11 @@ def micrOS():
58
49
  aio = Manager()
59
50
 
60
51
  # BOOT TASKS: Initial LM executions
61
- safe_boot()
52
+ try:
53
+ bootup()
54
+ except Exception as e:
55
+ print(f"[micrOS main] Hooks.boot() error: {e}")
56
+ errlog_add(f"[ERR] safe_boot: {e}")
62
57
 
63
58
  # NETWORK setup
64
59
  nwmd = auto_nw_config()
@@ -14,11 +14,12 @@ except:
14
14
  try:
15
15
  from Debug import errlog_add
16
16
  except Exception as e:
17
- print(f"Import error: {e}")
17
+ print(f"[loader] Import error: {e}")
18
18
  errlog_add = None
19
+ from machine import reset
19
20
 
20
21
 
21
- def __is_micrOS():
22
+ def _is_micrOS():
22
23
  """
23
24
  Recovery mode for OTA update in case of connection/transfer failure
24
25
  .if_mode can have 2 possible values: webrepl or micros (strings)
@@ -34,7 +35,6 @@ def __is_micrOS():
34
35
  False -> webrepl
35
36
  * EOE (EndOfExecution) -> off
36
37
  """
37
- mode = 'micros'
38
38
  try:
39
39
  with open('.if_mode', 'r') as f:
40
40
  mode = f.read().strip().lower()
@@ -54,7 +54,6 @@ def __is_micrOS():
54
54
  exit(0)
55
55
  # start webrepl
56
56
  print("[loader][if_mode:False] .if_mode:webrepl -> webrepl interface")
57
- print("[loader][recovery mode] - manually selected in .if_mode file")
58
57
  return False
59
58
 
60
59
 
@@ -71,7 +70,14 @@ def __recovery_mode():
71
70
  import webrepl
72
71
  webrepl.start(password=pwd)
73
72
  except Exception as e:
74
- print(f"Webrepl import error: {e}")
73
+ if callable(errlog_add):
74
+ errlog_add(f"[ERR][micrOSloader] webrepl failed: {e}")
75
+ print("[loader] Reset .if_mode to micros and reboot")
76
+ with open('.if_mode', 'w') as f:
77
+ f.write("micros")
78
+ # Reboot machine
79
+ reset()
80
+
75
81
 
76
82
 
77
83
  def __auto_restart_event():
@@ -82,10 +88,6 @@ def __auto_restart_event():
82
88
  - value: micros [update was successful - reboot is necessary]
83
89
  :return:
84
90
  """
85
- from sys import platform
86
- if platform == 'rp2':
87
- print("[loader][ota-rebooter] SKIP on platform: rp2 (webrepl block)")
88
- return
89
91
 
90
92
  from utime import sleep
91
93
  trigger_is_active = False
@@ -93,7 +95,7 @@ def __auto_restart_event():
93
95
  # Wait after webrepl started for possible ota updates (~3s*5= 15sec)
94
96
  while wait_update_tout > 0:
95
97
  # Wait for micros turns to webrepl until timeout
96
- if __is_micrOS():
98
+ if _is_micrOS():
97
99
  # micrOS mode
98
100
  print(f"[loader][ota-rebooter][micros][{wait_update_tout}] Wait for OTA update possible start")
99
101
  wait_update_tout -= 1
@@ -102,19 +104,18 @@ def __auto_restart_event():
102
104
  # Set trigger - if_mode changed to webrepl - ota update started - trigger wait
103
105
  trigger_is_active = True
104
106
  # Restart if trigger was activated
105
- if trigger_is_active and __is_micrOS():
107
+ if trigger_is_active and _is_micrOS():
106
108
  print("[loader][ota-rebooter][micros][trigger: True] OTA was finished - reboot")
107
109
  # Create cleanup indicator file for ConfigHandler
108
110
  with open('cleanup.pds', 'w') as f:
109
111
  f.write('')
110
112
  # Reboot machine
111
- from machine import reset
112
113
  reset()
113
114
  sleep(3)
114
115
 
115
116
 
116
117
  def main():
117
- if __is_micrOS():
118
+ if _is_micrOS():
118
119
  # Main mode
119
120
  try:
120
121
  print("[loader][main mode] Start micrOS (default)")
@@ -126,11 +127,8 @@ def main():
126
127
  # Handle micrOS system crash (never happened...but) -> webrepl mode default pwd: ADmin123
127
128
  print(f"[loader][main mode] micrOS start failed: {e}")
128
129
  print("[loader][main mode] -> [recovery mode]")
129
- if errlog_add is not None:
130
- try:
131
- errlog_add(f"[ERR][micrOSloader] start failed: {e}")
132
- except:
133
- pass
130
+ if callable(errlog_add):
131
+ errlog_add(f"[ERR][micrOSloader] start failed: {e}")
134
132
  # Recovery aka webrepl mode
135
133
  __recovery_mode()
136
134
  __auto_restart_event()
micrOS/source/uapi.js CHANGED
@@ -1,9 +1,9 @@
1
- // Get the current hostname and create the REST API URL
2
- const currentHostname = window.location.hostname;
3
- const port = window.location.port ? `:${window.location.port}` : "";
1
+ // CORE MICROS BACKEND INTERFACE
2
+
3
+ const BASE_URL = `http://${window.location.hostname}${window.location.port ? `:${window.location.port}` : ""}`;
4
4
 
5
5
  function restAPICore(cmd, timeout=5000) {
6
- const query = `http://${currentHostname}${port}/rest/${cmd.trim().replace(/\s+/g, '/')}`;
6
+ const query = `${BASE_URL}/rest/${cmd.trim().replace(/\s+/g, '/')}`;
7
7
  const controller = new AbortController();
8
8
  const timeoutId = setTimeout(() => controller.abort(), timeout);
9
9
  const startTime = performance.now();
@@ -59,5 +59,4 @@ function restInfo() {
59
59
  });
60
60
  }
61
61
 
62
- // Init basic info from board
63
- // restInfo();
62
+ // Designed by BxNxM |/|/|/|/