diff --git a/Alif/AppKit-E7_USB/Board/Corstone-300/vsi/python/arm_vsi3.py b/Alif/AppKit-E7_USB/Board/Corstone-300/vsi/python/arm_vsi3.py index 22b4d0a..6250c88 100644 --- a/Alif/AppKit-E7_USB/Board/Corstone-300/vsi/python/arm_vsi3.py +++ b/Alif/AppKit-E7_USB/Board/Corstone-300/vsi/python/arm_vsi3.py @@ -25,6 +25,7 @@ #More details. import os +import atexit import logging import logging.handlers from os import path @@ -214,6 +215,7 @@ def _build_sdsio_request(command: int, sid: int = 0, argument: int = 0, data: by Stream = sdsio_manager( work_dir=_work_dir, auto_playback=_auto_playback, + exit_after_playback=True, play_list=_play_list, mon_port=None, write_flush_records=_write_flush_records, @@ -222,6 +224,15 @@ def _build_sdsio_request(command: int, sid: int = 0, argument: int = 0, data: by control_input_factory=False, ) +# Shutdown SDSIO manager on exit +def shutdown(): + try: + Stream.shutdown() + except Exception: + logger.error("Failed to shutdown SDSIO manager.") + +# Register the shutdown function to be called on exit +atexit.register(shutdown) ## Process command # @param command requested SDSIO command diff --git a/Alif/AppKit-E7_USB/Board/Corstone-300/vsi/python/sdsio.py b/Alif/AppKit-E7_USB/Board/Corstone-300/vsi/python/sdsio.py index b5c3809..cefe648 100644 --- a/Alif/AppKit-E7_USB/Board/Corstone-300/vsi/python/sdsio.py +++ b/Alif/AppKit-E7_USB/Board/Corstone-300/vsi/python/sdsio.py @@ -14,6 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import asyncio import os import os.path as path import threading @@ -26,7 +27,7 @@ # ---------------------------------------------------------------------------- # # SDSIO server-compatible stream implementation # # ---------------------------------------------------------------------------- # -SDSIO_VSI_VERSION = "3.0.0" +SDSIO_VSI_VERSION = "3.1.0" class StreamInfo(NamedTuple): name: str = None @@ -133,8 +134,12 @@ def __init__(self, auto_playback=False): self._set = SDS_FLAG_MASK_PLAYBACK_MODE | SDS_FLAG_MASK_START self._auto_start_pending = True - def apply(self, set_mask: int, clear_mask: int): + def apply(self, set_mask: int, clear_mask: int, auto_playback: Optional[bool] = None): with self._lock: + if auto_playback is not None: + self._auto_playback = auto_playback + self._auto_start_pending = auto_playback and bool(set_mask & SDS_FLAG_MASK_START) + self._auto_terminate_pending = False self._set = (self._set | set_mask) & ~clear_mask self._clear = (self._clear | clear_mask) & ~set_mask if set_mask & SDS_FLAG_MASK_PLAYBACK_MODE: @@ -166,14 +171,17 @@ def request_auto_playback_start(self) -> bool: self._auto_start_pending = True return True - def request_auto_playback_terminate(self) -> bool: + def request_auto_playback_terminate(self, _force=False) -> bool: with self._lock: if not self._auto_playback: return False - if self._auto_start_pending or self._auto_terminate_pending: - return False - if self._target_flags & SDS_FLAG_MASK_START: + if self._auto_terminate_pending: return False + if not _force: + if self._auto_start_pending: + return False + if self._target_flags & SDS_FLAG_MASK_START: + return False self._set |= SDS_FLAG_MASK_CI_TERMINATE self._clear &= ~SDS_FLAG_MASK_CI_TERMINATE self._auto_terminate_pending = True @@ -216,7 +224,10 @@ def __init__( self, work_dir, auto_playback=False, + exit_after_playback=False, + no_progress_info=False, play_list: Optional[list] = None, + play_step: Optional[int] = None, mon_port: Optional[int] = None, write_flush_records: Optional[int] = None, status_bar_factory=None, @@ -224,7 +235,7 @@ def __init__( control_input_factory=None, ): self._stream_id = 0 - self._play_step_index = 0 + self._play_step = 0 self._rec_index = None # recording session index (None = not yet determined) self._work_dir = path.normpath(work_dir) self._rec_dir = self._work_dir @@ -239,19 +250,23 @@ def __init__( self._read_buffers = {} # sid -> ByteStreamBuffer self._read_threads = {} # sid -> Thread self._read_stop = {} # sid -> Event - # lock to protect stream_id increment and open checks - self._manager_lock = threading.Lock() + # lock to protect playback selection and stream state transitions + self._manager_lock = threading.RLock() # timestamp of last stream read or write command self.time_last_rw = time.time() # status bar self._status = None - if status_bar_factory is None: + if status_bar_factory is None and not no_progress_info: status_bar_factory = StatusBar if status_bar_factory: self._status = status_bar_factory(self) self._playback_mode = False + self._exit_after_playback = exit_after_playback + self._send_ci_terminate_on_shutdown = False self._play_list = play_list + self._play_step_limit = len(play_list) if play_list else None + self._single_play_step_selected = False self._mon_port = mon_port self._write_flush_records = write_flush_records # SDS Control Flags @@ -262,9 +277,9 @@ def __init__( if monitor_factory is None: monitor_factory = sdsMonitorInterface if monitor_factory: - self._monitor = monitor_factory(self._mon_port, self._flags) + self._monitor = monitor_factory(self._mon_port, self._flags, self.select_play_step) self._ctrl_input = None - if control_input_factory is not False: + if control_input_factory is not False and sys.stdin.isatty(): if control_input_factory is None: control_input_factory = sdsControlInput if control_input_factory: @@ -275,6 +290,15 @@ def __init__( self._info_IdleRate: int = 0 self._last_async_time = time.time() self._last_playback_stream_name = None + try: + self._loop = asyncio.get_running_loop() + self._main_task = asyncio.current_task() + except RuntimeError: + self._loop = None + self._main_task = None + if play_step is not None: + if play_step < 0 or not self.select_play_step(play_step): + raise ValueError(f"Invalid play step: {play_step}") def shutdown(self): self.shutdown_requested.set() @@ -459,40 +483,93 @@ def _file_read_worker(self, sid, name, buf: ByteStreamBuffer, stop_evt): finally: buf.set_eof() + def _get_play_step_limit(self): + if not self._play_list: + return None + if self._play_step_limit is None: + return len(self._play_list) + return min(self._play_step_limit, len(self._play_list)) + + def _is_single_play_step_selected(self) -> bool: + return self._single_play_step_selected + + def select_play_step(self, play_step: Optional[int]) -> bool: + with self._manager_lock: + if self.opened_streams: + logger.error("Play step selection failed: streams are currently open.") + return False + if not self._play_list: + logger.error("Play step selection failed: no play steps are configured.") + return False + + if play_step is not None and (play_step < 0 or play_step >= len(self._play_list)): + logger.error(f"Play step selection failed: {play_step} is outside 0-{len(self._play_list) - 1}.") + return False + + if play_step is None: + self._play_step = 0 + self._play_step_limit = len(self._play_list) + self._single_play_step_selected = False + logger.debug(f"Selected all playback steps 0-{len(self._play_list) - 1}.") + else: + self._play_step = play_step + self._play_step_limit = play_step + 1 + self._single_play_step_selected = True + logger.debug(f"Selected playback step {play_step}.") + self._label_list.clear() + self._timestamp_boundaries.clear() + return True + def _create_play_label_list(self, name) -> list[str]: _labels = [] - if self._play_list and self._play_step_index < len(self._play_list): - _step = self._play_list[self._play_step_index] + _play_step_limit = self._get_play_step_limit() + if self._play_list and self._play_step < _play_step_limit: + _step = self._play_list[self._play_step] _labels = list(_step.get('labels', [])) else: - # No playlist: one file per open, indexed by play_step_index - _candidate = path.join(self._work_dir, f"{name}.{self._play_step_index}.sds") + # No playlist: one file per open, selected by play_step + _candidate = path.join(self._work_dir, f"{name}.{self._play_step}.sds") if path.exists(_candidate): - _labels.append(str(self._play_step_index)) + _labels.append(str(self._play_step)) return _labels def _has_next_auto_playback_step(self) -> bool: if not self._flags.auto_playback or self.opened_streams: return False if self._play_list: - return self._play_step_index < len(self._play_list) + return self._play_step < self._get_play_step_limit() if self._last_playback_stream_name: return bool(self._create_play_label_list(self._last_playback_stream_name)) return False def _request_auto_playback_if_needed(self, target_flags: Optional[int] = None): - _target_flags = self._flags.target_flags if target_flags is None else target_flags - if _target_flags & SDS_FLAG_MASK_START: + with self._manager_lock: + _target_flags = self._flags.target_flags if target_flags is None else target_flags + if _target_flags & SDS_FLAG_MASK_START: + return + if self.opened_streams: + return + if self._has_next_auto_playback_step(): + self._flags.request_auto_playback_start() + elif self._flags.auto_playback and self._last_playback_stream_name: + if self._flags.request_auto_playback_terminate(): + _complete_msg = "Playback complete - no more steps remaining." if self._play_list else "Playback complete." + logger.info(_complete_msg) + self._request_exit_after_playback("playback complete") + + def _request_exit_after_playback(self, _reason: str): + if not self._exit_after_playback: return - if self.opened_streams: - return - if self._has_next_auto_playback_step(): - self._flags.request_auto_playback_start() - elif self._flags.auto_playback and self._last_playback_stream_name: - if self._flags.request_auto_playback_terminate(): - logger.info("Playback complete - no more steps remaining.") - + logger.info(f"SDSIO-Server terminating ({_reason}).") + self._send_ci_terminate_on_shutdown = True + self.shutdown_requested.set() + if self._loop and self._main_task: + self._loop.call_soon_threadsafe(self._main_task.cancel) def _open(self, mode, name): + with self._manager_lock: + return self._open_locked(mode, name) + + def _open_locked(self, mode, name): _cmd = CMD_OPEN # prepare error response _resp_err = bytearray() @@ -523,12 +600,13 @@ def _open(self, mode, name): if self._playback_mode: if not self._label_list: # Get flags, Set working dir + _index_based_playback = False if self._play_list: - if self._play_step_index < len(self._play_list): - _step = self._play_list[self._play_step_index] + if self._play_step < self._get_play_step_limit(): + _step = self._play_list[self._play_step] _step_desc = _step.get('step', '') _desc_suffix = f": {_step_desc}" if _step_desc else "" - logger.info(f"Playback step {self._play_step_index + 1}/{len(self._play_list)}{_desc_suffix}.") + logger.info(f"Playback step {self._play_step}{_desc_suffix}.") _set_flags = _step.get('setflags', 0) _clear_flags = _step.get('clearflags', 0) _recdir = _step.get('recdir', None) @@ -538,10 +616,13 @@ def _open(self, mode, name): self._rec_dir = self._work_dir else: logger.error(f"Open Failed. End of playlist. No more steps available for playback stream '{name}'.") + if self._exit_after_playback and self._flags.request_auto_playback_terminate(_force=True): + self._request_exit_after_playback("playback data unavailable") return _resp_err else: _set_flags = 0 _clear_flags = 0 + _index_based_playback = True if _set_flags or _clear_flags: logger.debug(f"Applying flags for playback stream '{name}': set=0x{_set_flags:08X}, clear=0x{_clear_flags:08X}.") @@ -550,12 +631,16 @@ def _open(self, mode, name): # Create label list _play_label_list = self._create_play_label_list(name) if not _play_label_list: - if not self._play_list and self._play_step_index > 0: + if not self._play_list and self._play_step > 0: logger.error(f"Open Failed. No more files available for playback stream '{name}'.") else: logger.error(f"Open Failed. No files found for playback stream '{name}'.") + if self._exit_after_playback and self._flags.request_auto_playback_terminate(_force=True): + self._request_exit_after_playback("playback data unavailable") return _resp_err self._label_list = _play_label_list + if _index_based_playback and self._play_step == 0: + logger.info("No play steps, index based playback started.") else: if mode == 0: @@ -614,6 +699,8 @@ def _open(self, mode, name): for _sds_file_path in _file_paths: if not path.exists(_sds_file_path): logger.error(f"Missing file for playback stream '{name}': {self._format_path(_sds_file_path)}") + if self._exit_after_playback and self._flags.request_auto_playback_terminate(_force=True): + self._request_exit_after_playback("playback data unavailable") return _resp_err if not self._timestamp_boundaries: @@ -657,6 +744,10 @@ def _open(self, mode, name): return _resp def _close(self, sid): + with self._manager_lock: + return self._close_locked(sid) + + def _close_locked(self, sid): _resp = bytearray() _stream = self.opened_streams[sid] _name = _stream.name @@ -699,7 +790,7 @@ def _close(self, sid): if not self.opened_streams: if self._playback_mode: - self._play_step_index += 1 + self._play_step += 1 self._label_list.clear() self._timestamp_boundaries.clear() self._request_auto_playback_if_needed() @@ -809,7 +900,7 @@ def _info(self, flags: int, idle_rate: int, err_data: bytes): logger.info(f"{idle_rate}% idle.") self._info_IdleRate = idle_rate if err_data: - _status = int.from_bytes(err_data[0:4],'little') + _status = int.from_bytes(err_data[0:4], 'little', signed=True) _line = int.from_bytes(err_data[4:8],'little') _err_mgs = err_data[8:] if _status == 0: @@ -850,9 +941,14 @@ def get_async_response(self): def get_shutdown_flags(self): _resp = bytearray() _cmd = CMD_FLAGS + if self._send_ci_terminate_on_shutdown: + _set_mask = SDS_FLAG_MASK_CI_TERMINATE + else: + _set_mask = 0 + _clear_mask = SDS_FLAG_MASK_ALIVE _resp.extend(_cmd.to_bytes(4,'little')) - _resp.extend((0).to_bytes(4,'little')) - _resp.extend((1 << 28).to_bytes(4,'little')) + _resp.extend(_set_mask.to_bytes(4,'little')) + _resp.extend(_clear_mask.to_bytes(4,'little')) _resp.extend((0).to_bytes(4,'little')) return _resp diff --git a/Alif/AppKit-E7_USB/README.md b/Alif/AppKit-E7_USB/README.md index b3dda58..99180e2 100644 --- a/Alif/AppKit-E7_USB/README.md +++ b/Alif/AppKit-E7_USB/README.md @@ -111,7 +111,7 @@ To perform a recording, follow these steps: ```txt >sdsio-server usb -SDSIO-Server v3.0.0 +SDSIO-Server v3.1.0 Press 'Ctrl+C' or 'X' to exit. Working directory: ...\Arm-Examples\SDS-Examples\Alif\AppKit-E7_USB SDSIO command input: R=Record, P=playback, S/s=stop, T/t=reset, X/x=exit, A-H=set flags 0-7, a-h=clear flags 0-7. @@ -174,7 +174,7 @@ The SDS file `Test_Out..p.sds` created during playback should be identical to ```txt >sdsio-server usb -SDSIO-Server v3.0.0 +SDSIO-Server v3.1.0 Press 'Ctrl+C' or 'X' to exit. Working directory: ...\Arm-Examples\SDS-Examples\Alif\AppKit-E7_USB SDSIO command input: R=Record, P=playback, S/s=stop, T/t=reset, X/x=exit, A-H=set flags 0-7, a-h=clear flags 0-7. @@ -279,7 +279,7 @@ To perform a recording, follow these steps: ```txt >sdsio-server usb -SDSIO-Server v3.0.0 +SDSIO-Server v3.1.0 Press 'Ctrl+C' or 'X' to exit. Working directory: ...\Arm-Examples\SDS-Examples\Alif\AppKit-E7_USB SDSIO command input: R=Record, P=playback, S/s=stop, T/t=reset, X/x=exit, A-H=set flags 0-7, a-h=clear flags 0-7. @@ -345,7 +345,7 @@ The SDS file `ML_Out..p.sds` created during playback should be identical to t ```txt >sdsio-server usb -SDSIO-Server v3.0.0 +SDSIO-Server v3.1.0 Press 'Ctrl+C' or 'X' to exit. Working directory: ...\Arm-Examples\SDS-Examples\Alif\AppKit-E7_USB SDSIO command input: R=Record, P=playback, S/s=stop, T/t=reset, X/x=exit, A-H=set flags 0-7, a-h=clear flags 0-7. @@ -432,7 +432,7 @@ Detected objects :: [x=109, y=69, w=43, h=58] ```txt Created by ...\Arm-Examples\SDS-Examples\Alif\AppKit-E7_USB\Board\Corstone-300\vsi\python\arm_vsi3.py -SDSIO VSI version 3.0.0 +SDSIO VSI version 3.1.0 SDSIO_FVP environment variable not set. Working directory: ...\Arm-Examples\SDS-Examples\Alif\AppKit-E7_USB\algorithm\SDS Recordings. SDSIO configuration YAML: ...\Arm-Examples\SDS-Examples\Alif\AppKit-E7_USB\algorithm.sdsio.yml. diff --git a/Alif/AppKit-E7_USB/SDS.csolution.yml b/Alif/AppKit-E7_USB/SDS.csolution.yml index cc3f077..42392de 100644 --- a/Alif/AppKit-E7_USB/SDS.csolution.yml +++ b/Alif/AppKit-E7_USB/SDS.csolution.yml @@ -15,7 +15,7 @@ solution: # Refer to https://open-cmsis-pack.github.io/cmsis-toolbox/ReferenceApplications/ for more information packs: - - pack: ARM::SDS@^3.0.0-0 + - pack: ARM::SDS@^3.1.0-0 - pack: AlifSemiconductor::Ensemble@^2.2.0-0 - pack: ARM::V2M_MPS3_SSE_300_BSP@^1.5.0 diff --git a/Alif/AppKit-E7_USB/algorithm.sdsio.yml b/Alif/AppKit-E7_USB/algorithm.sdsio.yml index 6e9f962..1aac288 100644 --- a/Alif/AppKit-E7_USB/algorithm.sdsio.yml +++ b/Alif/AppKit-E7_USB/algorithm.sdsio.yml @@ -7,7 +7,7 @@ sdsio: # Data stream information used by VS Code extension streams: - name: ML_In - view: video + view: image - name: ML_Out view: signal diff --git a/Alif/AppKit-E7_USB/algorithm/SDS Recordings/ML_In.0.mp4 b/Alif/AppKit-E7_USB/algorithm/SDS Recordings/ML_In.0.mp4 index 2ce23d1..59e12f1 100644 Binary files a/Alif/AppKit-E7_USB/algorithm/SDS Recordings/ML_In.0.mp4 and b/Alif/AppKit-E7_USB/algorithm/SDS Recordings/ML_In.0.mp4 differ diff --git a/Alif/AppKit-E7_USB/algorithm/SDS Recordings/ML_In.sds.yml b/Alif/AppKit-E7_USB/algorithm/SDS Recordings/ML_In.sds.yml index b9d44e9..19c2657 100644 --- a/Alif/AppKit-E7_USB/algorithm/SDS Recordings/ML_In.sds.yml +++ b/Alif/AppKit-E7_USB/algorithm/SDS Recordings/ML_In.sds.yml @@ -1,11 +1,9 @@ sds: name: ML input description: RGB888 video frames from camera - frequency: 12.5 + sample-frequency: 12.5 content: - - value: Frame - type: uint8_t - image: + - image: pixel_format: RGB888 width: 192 height: 192 diff --git a/Alif/AppKit-E7_USB/algorithm/SDS Recordings/ML_Out.sds.yml b/Alif/AppKit-E7_USB/algorithm/SDS Recordings/ML_Out.sds.yml index be4d56a..62256e5 100644 --- a/Alif/AppKit-E7_USB/algorithm/SDS Recordings/ML_Out.sds.yml +++ b/Alif/AppKit-E7_USB/algorithm/SDS Recordings/ML_Out.sds.yml @@ -1,7 +1,7 @@ sds: name: ML output description: Results of object detection - frequency: 12.5 + sample-frequency: 12.5 content: - value: confidence type: double @@ -9,7 +9,7 @@ sds: type: uint32_t - value: y type: uint32_t - - value: w + - value: width type: uint32_t - - value: h + - value: height type: uint32_t diff --git a/Alif/AppKit-E7_USB/algorithm/sds_control.c b/Alif/AppKit-E7_USB/algorithm/sds_control.c index 6a11479..60ec0fb 100644 --- a/Alif/AppKit-E7_USB/algorithm/sds_control.c +++ b/Alif/AppKit-E7_USB/algorithm/sds_control.c @@ -34,7 +34,8 @@ osThreadAttr_t attrAlgorithmThread = { // sdsControlThread thread attributes osThreadAttr_t attr_sdsControlThread = { - .name = "sdsControl" + .name = "sdsControl", + .priority = osPriorityNormal1 }; // Idle time counter diff --git a/Alif/AppKit-E7_USB/algorithm/sds_main.c b/Alif/AppKit-E7_USB/algorithm/sds_main.c index bdc1e65..27cc504 100644 --- a/Alif/AppKit-E7_USB/algorithm/sds_main.c +++ b/Alif/AppKit-E7_USB/algorithm/sds_main.c @@ -164,7 +164,7 @@ __NO_RETURN void AlgorithmThread (void *argument) { do { ret = sdsRead(sds_data_in_id, ×lot, algo_data_in_buf, sizeof(algo_data_in_buf)); if (ret == SDS_NO_DATA) { - osDelay(10U); + osDelay(1U); DiscardInputData(); } } while (ret == SDS_NO_DATA); @@ -190,7 +190,7 @@ __NO_RETURN void AlgorithmThread (void *argument) { do { ret = sdsWrite(sds_data_in_id, timeslot, algo_data_in_buf, sizeof(algo_data_in_buf)); if (ret == SDS_NO_SPACE) { - osDelay(10U); + osDelay(1U); } } while (ret == SDS_NO_SPACE); SDS_ASSERT(ret == sizeof(algo_data_in_buf)); @@ -208,7 +208,7 @@ __NO_RETURN void AlgorithmThread (void *argument) { do { ret = sdsWrite(sds_data_out_id, timeslot, algo_data_out_buf, sizeof(algo_data_out_buf)); if (ret == SDS_NO_SPACE) { - osDelay(10U); + osDelay(1U); } } while (ret == SDS_NO_SPACE); SDS_ASSERT(ret == sizeof(algo_data_out_buf)); diff --git a/Alif/AppKit-E7_USB/datatest/SDS Recordings/Test_In.sds.yml b/Alif/AppKit-E7_USB/datatest/SDS Recordings/Test_In.sds.yml index 6909a4e..be56e4c 100644 --- a/Alif/AppKit-E7_USB/datatest/SDS Recordings/Test_In.sds.yml +++ b/Alif/AppKit-E7_USB/datatest/SDS Recordings/Test_In.sds.yml @@ -1,17 +1,17 @@ sds: name: Test Input Data description: Generated accelerometer samples at 16600 Hz - frequency: 16600 + sample-frequency: 16600 content: - - value: x - type: uint16_t - scale: 0.001 - unit: G - - value: y - type: uint16_t - scale: 0.001 - unit: G - - value: z - type: uint16_t - scale: 0.001 - unit: G + - value: x + type: uint16_t + scale: 0.001 + unit: G + - value: y + type: uint16_t + scale: 0.001 + unit: G + - value: z + type: uint16_t + scale: 0.001 + unit: G diff --git a/Alif/AppKit-E7_USB/datatest/SDS Recordings/Test_Out.sds.yml b/Alif/AppKit-E7_USB/datatest/SDS Recordings/Test_Out.sds.yml index 2727caa..f1a085b 100644 --- a/Alif/AppKit-E7_USB/datatest/SDS Recordings/Test_Out.sds.yml +++ b/Alif/AppKit-E7_USB/datatest/SDS Recordings/Test_Out.sds.yml @@ -1,9 +1,9 @@ sds: name: Test Output Data description: Results of algorithm processing - frequency: 1000 + sample-frequency: 1000 content: - - value: x - type: uint16_t - - value: y - type: uint16_t + - value: x + type: uint16_t + - value: y + type: uint16_t diff --git a/Alif/AppKit-E7_USB/datatest/sds_control.c b/Alif/AppKit-E7_USB/datatest/sds_control.c index 6a11479..60ec0fb 100644 --- a/Alif/AppKit-E7_USB/datatest/sds_control.c +++ b/Alif/AppKit-E7_USB/datatest/sds_control.c @@ -34,7 +34,8 @@ osThreadAttr_t attrAlgorithmThread = { // sdsControlThread thread attributes osThreadAttr_t attr_sdsControlThread = { - .name = "sdsControl" + .name = "sdsControl", + .priority = osPriorityNormal1 }; // Idle time counter diff --git a/Alif/AppKit-E7_USB/datatest/sds_main.c b/Alif/AppKit-E7_USB/datatest/sds_main.c index b2e38c1..32de570 100644 --- a/Alif/AppKit-E7_USB/datatest/sds_main.c +++ b/Alif/AppKit-E7_USB/datatest/sds_main.c @@ -164,7 +164,7 @@ __NO_RETURN void AlgorithmThread (void *argument) { do { ret = sdsRead(sds_data_in_id, ×lot, algo_data_in_buf, sizeof(algo_data_in_buf)); if (ret == SDS_NO_DATA) { - osDelay(10U); + osDelay(1U); DiscardInputData(); } } while (ret == SDS_NO_DATA); @@ -190,7 +190,7 @@ __NO_RETURN void AlgorithmThread (void *argument) { do { ret = sdsWrite(sds_data_in_id, timeslot, algo_data_in_buf, sizeof(algo_data_in_buf)); if (ret == SDS_NO_SPACE) { - osDelay(10U); + osDelay(1U); } } while (ret == SDS_NO_SPACE); SDS_ASSERT(ret == sizeof(algo_data_in_buf)); @@ -208,7 +208,7 @@ __NO_RETURN void AlgorithmThread (void *argument) { do { ret = sdsWrite(sds_data_out_id, timeslot, algo_data_out_buf, sizeof(algo_data_out_buf)); if (ret == SDS_NO_SPACE) { - osDelay(10U); + osDelay(1U); } } while (ret == SDS_NO_SPACE); SDS_ASSERT(ret == sizeof(algo_data_out_buf)); diff --git a/Alif/AppKit-E7_USB/sdsio/fvp/RTE/SDS/sds_config.h b/Alif/AppKit-E7_USB/sdsio/fvp/RTE/SDS/sds_config.h index 8d0b50c..ecd50a5 100644 --- a/Alif/AppKit-E7_USB/sdsio/fvp/RTE/SDS/sds_config.h +++ b/Alif/AppKit-E7_USB/sdsio/fvp/RTE/SDS/sds_config.h @@ -17,7 +17,7 @@ * * Name: sds_config.h * Purpose: SDS configuration options - * Rev.: V3.0.0 + * Rev.: V3.1.0 */ //-------- <<< Use Configuration Wizard in Context Menu >>> -------------------- @@ -40,7 +40,7 @@ #define SDS_THREAD_STACK_SIZE 1024 // SDS system thread priority -#define SDS_THREAD_PRIORITY osPriorityNormal +#define SDS_THREAD_PRIORITY osPriorityNormal1 // SDS stream open timeout in kernel ticks #define SDS_OPEN_TIMEOUT 3000U diff --git a/Alif/AppKit-E7_USB/sdsio/fvp/RTE/SDS/sds_config.h.base@3.0.0 b/Alif/AppKit-E7_USB/sdsio/fvp/RTE/SDS/sds_config.h.base@3.1.0 similarity index 95% rename from Alif/AppKit-E7_USB/sdsio/fvp/RTE/SDS/sds_config.h.base@3.0.0 rename to Alif/AppKit-E7_USB/sdsio/fvp/RTE/SDS/sds_config.h.base@3.1.0 index 8d0b50c..ecd50a5 100644 --- a/Alif/AppKit-E7_USB/sdsio/fvp/RTE/SDS/sds_config.h.base@3.0.0 +++ b/Alif/AppKit-E7_USB/sdsio/fvp/RTE/SDS/sds_config.h.base@3.1.0 @@ -17,7 +17,7 @@ * * Name: sds_config.h * Purpose: SDS configuration options - * Rev.: V3.0.0 + * Rev.: V3.1.0 */ //-------- <<< Use Configuration Wizard in Context Menu >>> -------------------- @@ -40,7 +40,7 @@ #define SDS_THREAD_STACK_SIZE 1024 // SDS system thread priority -#define SDS_THREAD_PRIORITY osPriorityNormal +#define SDS_THREAD_PRIORITY osPriorityNormal1 // SDS stream open timeout in kernel ticks #define SDS_OPEN_TIMEOUT 3000U diff --git a/Alif/AppKit-E7_USB/sdsio/fvp/sdsio_fvp.clayer.yml b/Alif/AppKit-E7_USB/sdsio/fvp/sdsio_fvp.clayer.yml index f4634a7..5c2f55a 100644 --- a/Alif/AppKit-E7_USB/sdsio/fvp/sdsio_fvp.clayer.yml +++ b/Alif/AppKit-E7_USB/sdsio/fvp/sdsio_fvp.clayer.yml @@ -3,7 +3,7 @@ layer: description: Layer with SDS and SDSIO using VSI packs: - - pack: ARM::SDS@^3.0.0-0 + - pack: ARM::SDS@^3.1.0-0 connections: - connect: SDS diff --git a/Alif/AppKit-E7_USB/sdsio/usb/RTE/SDS/sds_config.h b/Alif/AppKit-E7_USB/sdsio/usb/RTE/SDS/sds_config.h index 8d0b50c..ecd50a5 100644 --- a/Alif/AppKit-E7_USB/sdsio/usb/RTE/SDS/sds_config.h +++ b/Alif/AppKit-E7_USB/sdsio/usb/RTE/SDS/sds_config.h @@ -17,7 +17,7 @@ * * Name: sds_config.h * Purpose: SDS configuration options - * Rev.: V3.0.0 + * Rev.: V3.1.0 */ //-------- <<< Use Configuration Wizard in Context Menu >>> -------------------- @@ -40,7 +40,7 @@ #define SDS_THREAD_STACK_SIZE 1024 // SDS system thread priority -#define SDS_THREAD_PRIORITY osPriorityNormal +#define SDS_THREAD_PRIORITY osPriorityNormal1 // SDS stream open timeout in kernel ticks #define SDS_OPEN_TIMEOUT 3000U diff --git a/Alif/AppKit-E7_USB/sdsio/usb/RTE/SDS/sds_config.h.base@3.0.0 b/Alif/AppKit-E7_USB/sdsio/usb/RTE/SDS/sds_config.h.base@3.1.0 similarity index 95% rename from Alif/AppKit-E7_USB/sdsio/usb/RTE/SDS/sds_config.h.base@3.0.0 rename to Alif/AppKit-E7_USB/sdsio/usb/RTE/SDS/sds_config.h.base@3.1.0 index 8d0b50c..ecd50a5 100644 --- a/Alif/AppKit-E7_USB/sdsio/usb/RTE/SDS/sds_config.h.base@3.0.0 +++ b/Alif/AppKit-E7_USB/sdsio/usb/RTE/SDS/sds_config.h.base@3.1.0 @@ -17,7 +17,7 @@ * * Name: sds_config.h * Purpose: SDS configuration options - * Rev.: V3.0.0 + * Rev.: V3.1.0 */ //-------- <<< Use Configuration Wizard in Context Menu >>> -------------------- @@ -40,7 +40,7 @@ #define SDS_THREAD_STACK_SIZE 1024 // SDS system thread priority -#define SDS_THREAD_PRIORITY osPriorityNormal +#define SDS_THREAD_PRIORITY osPriorityNormal1 // SDS stream open timeout in kernel ticks #define SDS_OPEN_TIMEOUT 3000U diff --git a/Alif/AppKit-E7_USB/sdsio/usb/sdsio_usb.clayer.yml b/Alif/AppKit-E7_USB/sdsio/usb/sdsio_usb.clayer.yml index 23e8729..5b36837 100644 --- a/Alif/AppKit-E7_USB/sdsio/usb/sdsio_usb.clayer.yml +++ b/Alif/AppKit-E7_USB/sdsio/usb/sdsio_usb.clayer.yml @@ -3,7 +3,7 @@ layer: description: Layer with SDS and SDSIO using USB interface to the SDSIO-Server packs: - - pack: ARM::SDS@^3.0.0-0 + - pack: ARM::SDS@^3.1.0-0 - pack: Keil::MDK-Middleware@^8.0.0 connections: diff --git a/Alif/DevKit-E8_ETH/Board/Corstone-320/vsi/python/arm_vsi3.py b/Alif/DevKit-E8_ETH/Board/Corstone-320/vsi/python/arm_vsi3.py index 22b4d0a..6250c88 100644 --- a/Alif/DevKit-E8_ETH/Board/Corstone-320/vsi/python/arm_vsi3.py +++ b/Alif/DevKit-E8_ETH/Board/Corstone-320/vsi/python/arm_vsi3.py @@ -25,6 +25,7 @@ #More details. import os +import atexit import logging import logging.handlers from os import path @@ -214,6 +215,7 @@ def _build_sdsio_request(command: int, sid: int = 0, argument: int = 0, data: by Stream = sdsio_manager( work_dir=_work_dir, auto_playback=_auto_playback, + exit_after_playback=True, play_list=_play_list, mon_port=None, write_flush_records=_write_flush_records, @@ -222,6 +224,15 @@ def _build_sdsio_request(command: int, sid: int = 0, argument: int = 0, data: by control_input_factory=False, ) +# Shutdown SDSIO manager on exit +def shutdown(): + try: + Stream.shutdown() + except Exception: + logger.error("Failed to shutdown SDSIO manager.") + +# Register the shutdown function to be called on exit +atexit.register(shutdown) ## Process command # @param command requested SDSIO command diff --git a/Alif/DevKit-E8_ETH/Board/Corstone-320/vsi/python/sdsio.py b/Alif/DevKit-E8_ETH/Board/Corstone-320/vsi/python/sdsio.py index e6d8581..0d18f5d 100644 --- a/Alif/DevKit-E8_ETH/Board/Corstone-320/vsi/python/sdsio.py +++ b/Alif/DevKit-E8_ETH/Board/Corstone-320/vsi/python/sdsio.py @@ -14,6 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import asyncio import os import os.path as path import threading @@ -26,7 +27,7 @@ # ---------------------------------------------------------------------------- # # SDSIO server-compatible stream implementation # # ---------------------------------------------------------------------------- # -SDSIO_VSI_VERSION = "3.0.0" +SDSIO_VSI_VERSION = "3.1.0" class StreamInfo(NamedTuple): name: str = None @@ -133,8 +134,12 @@ def __init__(self, auto_playback=False): self._set = SDS_FLAG_MASK_PLAYBACK_MODE | SDS_FLAG_MASK_START self._auto_start_pending = True - def apply(self, set_mask: int, clear_mask: int): + def apply(self, set_mask: int, clear_mask: int, auto_playback: Optional[bool] = None): with self._lock: + if auto_playback is not None: + self._auto_playback = auto_playback + self._auto_start_pending = auto_playback and bool(set_mask & SDS_FLAG_MASK_START) + self._auto_terminate_pending = False self._set = (self._set | set_mask) & ~clear_mask self._clear = (self._clear | clear_mask) & ~set_mask if set_mask & SDS_FLAG_MASK_PLAYBACK_MODE: @@ -166,14 +171,17 @@ def request_auto_playback_start(self) -> bool: self._auto_start_pending = True return True - def request_auto_playback_terminate(self) -> bool: + def request_auto_playback_terminate(self, _force=False) -> bool: with self._lock: if not self._auto_playback: return False - if self._auto_start_pending or self._auto_terminate_pending: - return False - if self._target_flags & SDS_FLAG_MASK_START: + if self._auto_terminate_pending: return False + if not _force: + if self._auto_start_pending: + return False + if self._target_flags & SDS_FLAG_MASK_START: + return False self._set |= SDS_FLAG_MASK_CI_TERMINATE self._clear &= ~SDS_FLAG_MASK_CI_TERMINATE self._auto_terminate_pending = True @@ -216,7 +224,10 @@ def __init__( self, work_dir, auto_playback=False, + exit_after_playback=False, + no_progress_info=False, play_list: Optional[list] = None, + play_step: Optional[int] = None, mon_port: Optional[int] = None, write_flush_records: Optional[int] = None, status_bar_factory=None, @@ -224,7 +235,7 @@ def __init__( control_input_factory=None, ): self._stream_id = 0 - self._play_step_index = 0 + self._play_step = 0 self._rec_index = None # recording session index (None = not yet determined) self._work_dir = path.normpath(work_dir) self._rec_dir = self._work_dir @@ -239,19 +250,23 @@ def __init__( self._read_buffers = {} # sid -> ByteStreamBuffer self._read_threads = {} # sid -> Thread self._read_stop = {} # sid -> Event - # lock to protect stream_id increment and open checks - self._manager_lock = threading.Lock() + # lock to protect playback selection and stream state transitions + self._manager_lock = threading.RLock() # timestamp of last stream read or write command self.time_last_rw = time.time() # status bar self._status = None - if status_bar_factory is None: + if status_bar_factory is None and not no_progress_info: status_bar_factory = StatusBar if status_bar_factory: self._status = status_bar_factory(self) self._playback_mode = False + self._exit_after_playback = exit_after_playback + self._send_ci_terminate_on_shutdown = False self._play_list = play_list + self._play_step_limit = len(play_list) if play_list else None + self._single_play_step_selected = False self._mon_port = mon_port self._write_flush_records = write_flush_records # SDS Control Flags @@ -262,9 +277,9 @@ def __init__( if monitor_factory is None: monitor_factory = sdsMonitorInterface if monitor_factory: - self._monitor = monitor_factory(self._mon_port, self._flags) + self._monitor = monitor_factory(self._mon_port, self._flags, self.select_play_step) self._ctrl_input = None - if control_input_factory is not False: + if control_input_factory is not False and sys.stdin.isatty(): if control_input_factory is None: control_input_factory = sdsControlInput if control_input_factory: @@ -275,6 +290,15 @@ def __init__( self._info_IdleRate: int = 0 self._last_async_time = time.time() self._last_playback_stream_name = None + try: + self._loop = asyncio.get_running_loop() + self._main_task = asyncio.current_task() + except RuntimeError: + self._loop = None + self._main_task = None + if play_step is not None: + if play_step < 0 or not self.select_play_step(play_step): + raise ValueError(f"Invalid play step: {play_step}") def shutdown(self): self.shutdown_requested.set() @@ -459,40 +483,93 @@ def _file_read_worker(self, sid, name, buf: ByteStreamBuffer, stop_evt): finally: buf.set_eof() + def _get_play_step_limit(self): + if not self._play_list: + return None + if self._play_step_limit is None: + return len(self._play_list) + return min(self._play_step_limit, len(self._play_list)) + + def _is_single_play_step_selected(self) -> bool: + return self._single_play_step_selected + + def select_play_step(self, play_step: Optional[int]) -> bool: + with self._manager_lock: + if self.opened_streams: + logger.error("Play step selection failed: streams are currently open.") + return False + if not self._play_list: + logger.error("Play step selection failed: no play steps are configured.") + return False + + if play_step is not None and (play_step < 0 or play_step >= len(self._play_list)): + logger.error(f"Play step selection failed: {play_step} is outside 0-{len(self._play_list) - 1}.") + return False + + if play_step is None: + self._play_step = 0 + self._play_step_limit = len(self._play_list) + self._single_play_step_selected = False + logger.debug(f"Selected all playback steps 0-{len(self._play_list) - 1}.") + else: + self._play_step = play_step + self._play_step_limit = play_step + 1 + self._single_play_step_selected = True + logger.debug(f"Selected playback step {play_step}.") + self._label_list.clear() + self._timestamp_boundaries.clear() + return True + def _create_play_label_list(self, name) -> list[str]: _labels = [] - if self._play_list and self._play_step_index < len(self._play_list): - _step = self._play_list[self._play_step_index] + _play_step_limit = self._get_play_step_limit() + if self._play_list and self._play_step < _play_step_limit: + _step = self._play_list[self._play_step] _labels = list(_step.get('labels', [])) else: - # No playlist: one file per open, indexed by play_step_index - _candidate = path.join(self._work_dir, f"{name}.{self._play_step_index}.sds") + # No playlist: one file per open, selected by play_step + _candidate = path.join(self._work_dir, f"{name}.{self._play_step}.sds") if path.exists(_candidate): - _labels.append(str(self._play_step_index)) + _labels.append(str(self._play_step)) return _labels def _has_next_auto_playback_step(self) -> bool: if not self._flags.auto_playback or self.opened_streams: return False if self._play_list: - return self._play_step_index < len(self._play_list) + return self._play_step < self._get_play_step_limit() if self._last_playback_stream_name: return bool(self._create_play_label_list(self._last_playback_stream_name)) return False def _request_auto_playback_if_needed(self, target_flags: Optional[int] = None): - _target_flags = self._flags.target_flags if target_flags is None else target_flags - if _target_flags & SDS_FLAG_MASK_START: + with self._manager_lock: + _target_flags = self._flags.target_flags if target_flags is None else target_flags + if _target_flags & SDS_FLAG_MASK_START: + return + if self.opened_streams: + return + if self._has_next_auto_playback_step(): + self._flags.request_auto_playback_start() + elif self._flags.auto_playback and self._last_playback_stream_name: + if self._flags.request_auto_playback_terminate(): + _complete_msg = "Playback complete - no more steps remaining." if self._play_list else "Playback complete." + logger.info(_complete_msg) + self._request_exit_after_playback("playback complete") + + def _request_exit_after_playback(self, _reason: str): + if not self._exit_after_playback: return - if self.opened_streams: - return - if self._has_next_auto_playback_step(): - self._flags.request_auto_playback_start() - elif self._flags.auto_playback and self._last_playback_stream_name: - if self._flags.request_auto_playback_terminate(): - logger.info("Playback complete - no more steps remaining.") - + logger.info(f"SDSIO-Server terminating ({_reason}).") + self._send_ci_terminate_on_shutdown = True + self.shutdown_requested.set() + if self._loop and self._main_task: + self._loop.call_soon_threadsafe(self._main_task.cancel) def _open(self, mode, name): + with self._manager_lock: + return self._open_locked(mode, name) + + def _open_locked(self, mode, name): _cmd = CMD_OPEN # prepare error response _resp_err = bytearray() @@ -523,12 +600,13 @@ def _open(self, mode, name): if self._playback_mode: if not self._label_list: # Get flags, Set working dir + _index_based_playback = False if self._play_list: - if self._play_step_index < len(self._play_list): - _step = self._play_list[self._play_step_index] + if self._play_step < self._get_play_step_limit(): + _step = self._play_list[self._play_step] _step_desc = _step.get('step', '') _desc_suffix = f": {_step_desc}" if _step_desc else "" - logger.info(f"Playback step {self._play_step_index + 1}/{len(self._play_list)}{_desc_suffix}.") + logger.info(f"Playback step {self._play_step}{_desc_suffix}.") _set_flags = _step.get('setflags', 0) _clear_flags = _step.get('clearflags', 0) _recdir = _step.get('recdir', None) @@ -538,10 +616,13 @@ def _open(self, mode, name): self._rec_dir = self._work_dir else: logger.error(f"Open Failed. End of playlist. No more steps available for playback stream '{name}'.") + if self._exit_after_playback and self._flags.request_auto_playback_terminate(_force=True): + self._request_exit_after_playback("playback data unavailable") return _resp_err else: _set_flags = 0 _clear_flags = 0 + _index_based_playback = True if _set_flags or _clear_flags: logger.debug(f"Applying flags for playback stream '{name}': set=0x{_set_flags:08X}, clear=0x{_clear_flags:08X}.") @@ -550,12 +631,16 @@ def _open(self, mode, name): # Create label list _play_label_list = self._create_play_label_list(name) if not _play_label_list: - if not self._play_list and self._play_step_index > 0: + if not self._play_list and self._play_step > 0: logger.error(f"Open Failed. No more files available for playback stream '{name}'.") else: logger.error(f"Open Failed. No files found for playback stream '{name}'.") + if self._exit_after_playback and self._flags.request_auto_playback_terminate(_force=True): + self._request_exit_after_playback("playback data unavailable") return _resp_err self._label_list = _play_label_list + if _index_based_playback and self._play_step == 0: + logger.info("No play steps, index based playback started.") else: if mode == 0: @@ -614,6 +699,8 @@ def _open(self, mode, name): for _sds_file_path in _file_paths: if not path.exists(_sds_file_path): logger.error(f"Missing file for playback stream '{name}': {self._format_path(_sds_file_path)}") + if self._exit_after_playback and self._flags.request_auto_playback_terminate(_force=True): + self._request_exit_after_playback("playback data unavailable") return _resp_err if not self._timestamp_boundaries: @@ -657,6 +744,10 @@ def _open(self, mode, name): return _resp def _close(self, sid): + with self._manager_lock: + return self._close_locked(sid) + + def _close_locked(self, sid): _resp = bytearray() _stream = self.opened_streams[sid] _name = _stream.name @@ -699,7 +790,7 @@ def _close(self, sid): if not self.opened_streams: if self._playback_mode: - self._play_step_index += 1 + self._play_step += 1 self._label_list.clear() self._timestamp_boundaries.clear() self._request_auto_playback_if_needed() @@ -809,7 +900,7 @@ def _info(self, flags: int, idle_rate: int, err_data: bytes): logger.info(f"{idle_rate}% idle.") self._info_IdleRate = idle_rate if err_data: - _status = int.from_bytes(err_data[0:4],'little') + _status = int.from_bytes(err_data[0:4], 'little', signed=True) _line = int.from_bytes(err_data[4:8],'little') _err_mgs = err_data[8:] if _status == 0: @@ -850,9 +941,14 @@ def get_async_response(self): def get_shutdown_flags(self): _resp = bytearray() _cmd = CMD_FLAGS + if self._send_ci_terminate_on_shutdown: + _set_mask = SDS_FLAG_MASK_CI_TERMINATE + else: + _set_mask = 0 + _clear_mask = SDS_FLAG_MASK_ALIVE _resp.extend(_cmd.to_bytes(4,'little')) - _resp.extend((0).to_bytes(4,'little')) - _resp.extend((1 << 28).to_bytes(4,'little')) + _resp.extend(_set_mask.to_bytes(4,'little')) + _resp.extend(_clear_mask.to_bytes(4,'little')) _resp.extend((0).to_bytes(4,'little')) return _resp diff --git a/Alif/DevKit-E8_ETH/README.md b/Alif/DevKit-E8_ETH/README.md index 0ff9857..4233136 100644 --- a/Alif/DevKit-E8_ETH/README.md +++ b/Alif/DevKit-E8_ETH/README.md @@ -124,7 +124,7 @@ To perform a recording, follow these steps: ```txt >sdsio-server socket -SDSIO-Server v3.0.0 +SDSIO-Server v3.1.0 Press 'Ctrl+C' or 'X' to exit. Working directory: ...\Arm-Examples\SDS-Examples\Alif\DevKit-E8_ETH SDSIO command input: R=Record, P=playback, S/s=stop, T/t=reset, X/x=exit, A-H=set flags 0-7, a-h=clear flags 0-7. @@ -187,7 +187,7 @@ The SDS file `Test_Out..p.sds` created during playback should be identical to ```txt >sdsio-server socket -SDSIO-Server v3.0.0 +SDSIO-Server v3.1.0 Press 'Ctrl+C' or 'X' to exit. Working directory: ...\Arm-Examples\SDS-Examples\Alif\DevKit-E8_ETH SDSIO command input: R=Record, P=playback, S/s=stop, T/t=reset, X/x=exit, A-H=set flags 0-7, a-h=clear flags 0-7. @@ -294,7 +294,7 @@ To perform a recording, follow these steps: ```txt >sdsio-server socket -SDSIO-Server v3.0.0 +SDSIO-Server v3.1.0 Press 'Ctrl+C' or 'X' to exit. Working directory: ...\Arm-Examples\SDS-Examples\Alif\DevKit-E8_ETH SDSIO command input: R=Record, P=playback, S/s=stop, T/t=reset, X/x=exit, A-H=set flags 0-7, a-h=clear flags 0-7. @@ -360,7 +360,7 @@ The SDS file `ML_Out..p.sds` created during playback should be identical to t ```txt >sdsio-server socket -SDSIO-Server v3.0.0 +SDSIO-Server v3.1.0 Press 'Ctrl+C' or 'X' to exit. Working directory: ...\Arm-Examples\SDS-Examples\Alif\DevKit-E8_ETH SDSIO command input: R=Record, P=playback, S/s=stop, T/t=reset, X/x=exit, A-H=set flags 0-7, a-h=clear flags 0-7. @@ -446,7 +446,7 @@ Detected objects :: [x=109, y=69, w=43, h=58] ```txt Created by ...\Arm-Examples\SDS-Examples\Alif\DevKit-E8_ETH\Board\Corstone-320\vsi\python\arm_vsi3.py -SDSIO VSI version 3.0.0 +SDSIO VSI version 3.1.0 SDSIO_FVP environment variable not set. Working directory: ...\Arm-Examples\SDS-Examples\Alif\DevKit-E8_ETH\algorithm\SDS Recordings. SDSIO configuration YAML: ...\Arm-Examples\SDS-Examples\Alif\DevKit-E8_ETH\algorithm.sdsio.yml. diff --git a/Alif/DevKit-E8_ETH/SDS.csolution.yml b/Alif/DevKit-E8_ETH/SDS.csolution.yml index 12b971a..0bb1a6c 100644 --- a/Alif/DevKit-E8_ETH/SDS.csolution.yml +++ b/Alif/DevKit-E8_ETH/SDS.csolution.yml @@ -15,7 +15,7 @@ solution: # Refer to https://open-cmsis-pack.github.io/cmsis-toolbox/ReferenceApplications/ for more information packs: - - pack: ARM::SDS@^3.0.0-0 + - pack: ARM::SDS@^3.1.0-0 - pack: AlifSemiconductor::Ensemble@^2.2.0-0 - pack: ARM::SSE_320_BSP@1.1.0 diff --git a/Alif/DevKit-E8_ETH/algorithm.sdsio.yml b/Alif/DevKit-E8_ETH/algorithm.sdsio.yml index 6e9f962..1aac288 100644 --- a/Alif/DevKit-E8_ETH/algorithm.sdsio.yml +++ b/Alif/DevKit-E8_ETH/algorithm.sdsio.yml @@ -7,7 +7,7 @@ sdsio: # Data stream information used by VS Code extension streams: - name: ML_In - view: video + view: image - name: ML_Out view: signal diff --git a/Alif/DevKit-E8_ETH/algorithm/SDS Recordings/ML_In.sds.yml b/Alif/DevKit-E8_ETH/algorithm/SDS Recordings/ML_In.sds.yml index b9d44e9..19c2657 100644 --- a/Alif/DevKit-E8_ETH/algorithm/SDS Recordings/ML_In.sds.yml +++ b/Alif/DevKit-E8_ETH/algorithm/SDS Recordings/ML_In.sds.yml @@ -1,11 +1,9 @@ sds: name: ML input description: RGB888 video frames from camera - frequency: 12.5 + sample-frequency: 12.5 content: - - value: Frame - type: uint8_t - image: + - image: pixel_format: RGB888 width: 192 height: 192 diff --git a/Alif/DevKit-E8_ETH/algorithm/SDS Recordings/ML_Out.sds.yml b/Alif/DevKit-E8_ETH/algorithm/SDS Recordings/ML_Out.sds.yml index be4d56a..62256e5 100644 --- a/Alif/DevKit-E8_ETH/algorithm/SDS Recordings/ML_Out.sds.yml +++ b/Alif/DevKit-E8_ETH/algorithm/SDS Recordings/ML_Out.sds.yml @@ -1,7 +1,7 @@ sds: name: ML output description: Results of object detection - frequency: 12.5 + sample-frequency: 12.5 content: - value: confidence type: double @@ -9,7 +9,7 @@ sds: type: uint32_t - value: y type: uint32_t - - value: w + - value: width type: uint32_t - - value: h + - value: height type: uint32_t diff --git a/Alif/DevKit-E8_ETH/algorithm/sds_control.c b/Alif/DevKit-E8_ETH/algorithm/sds_control.c index 6a11479..60ec0fb 100644 --- a/Alif/DevKit-E8_ETH/algorithm/sds_control.c +++ b/Alif/DevKit-E8_ETH/algorithm/sds_control.c @@ -34,7 +34,8 @@ osThreadAttr_t attrAlgorithmThread = { // sdsControlThread thread attributes osThreadAttr_t attr_sdsControlThread = { - .name = "sdsControl" + .name = "sdsControl", + .priority = osPriorityNormal1 }; // Idle time counter diff --git a/Alif/DevKit-E8_ETH/algorithm/sds_main.c b/Alif/DevKit-E8_ETH/algorithm/sds_main.c index 325cc77..48ba2d7 100644 --- a/Alif/DevKit-E8_ETH/algorithm/sds_main.c +++ b/Alif/DevKit-E8_ETH/algorithm/sds_main.c @@ -164,7 +164,7 @@ __NO_RETURN void AlgorithmThread (void *argument) { do { ret = sdsRead(sds_data_in_id, ×lot, algo_data_in_buf, sizeof(algo_data_in_buf)); if (ret == SDS_NO_DATA) { - osDelay(10U); + osDelay(1U); DiscardInputData(); } } while (ret == SDS_NO_DATA); @@ -190,7 +190,7 @@ __NO_RETURN void AlgorithmThread (void *argument) { do { ret = sdsWrite(sds_data_in_id, timeslot, algo_data_in_buf, sizeof(algo_data_in_buf)); if (ret == SDS_NO_SPACE) { - osDelay(10U); + osDelay(1U); } } while (ret == SDS_NO_SPACE); SDS_ASSERT(ret == sizeof(algo_data_in_buf)); @@ -208,7 +208,7 @@ __NO_RETURN void AlgorithmThread (void *argument) { do { ret = sdsWrite(sds_data_out_id, timeslot, algo_data_out_buf, sizeof(algo_data_out_buf)); if (ret == SDS_NO_SPACE) { - osDelay(10U); + osDelay(1U); } } while (ret == SDS_NO_SPACE); SDS_ASSERT(ret == sizeof(algo_data_out_buf)); diff --git a/Alif/DevKit-E8_ETH/datatest/SDS Recordings/Test_In.sds.yml b/Alif/DevKit-E8_ETH/datatest/SDS Recordings/Test_In.sds.yml index 6909a4e..be56e4c 100644 --- a/Alif/DevKit-E8_ETH/datatest/SDS Recordings/Test_In.sds.yml +++ b/Alif/DevKit-E8_ETH/datatest/SDS Recordings/Test_In.sds.yml @@ -1,17 +1,17 @@ sds: name: Test Input Data description: Generated accelerometer samples at 16600 Hz - frequency: 16600 + sample-frequency: 16600 content: - - value: x - type: uint16_t - scale: 0.001 - unit: G - - value: y - type: uint16_t - scale: 0.001 - unit: G - - value: z - type: uint16_t - scale: 0.001 - unit: G + - value: x + type: uint16_t + scale: 0.001 + unit: G + - value: y + type: uint16_t + scale: 0.001 + unit: G + - value: z + type: uint16_t + scale: 0.001 + unit: G diff --git a/Alif/DevKit-E8_ETH/datatest/SDS Recordings/Test_Out.sds.yml b/Alif/DevKit-E8_ETH/datatest/SDS Recordings/Test_Out.sds.yml index 2727caa..f1a085b 100644 --- a/Alif/DevKit-E8_ETH/datatest/SDS Recordings/Test_Out.sds.yml +++ b/Alif/DevKit-E8_ETH/datatest/SDS Recordings/Test_Out.sds.yml @@ -1,9 +1,9 @@ sds: name: Test Output Data description: Results of algorithm processing - frequency: 1000 + sample-frequency: 1000 content: - - value: x - type: uint16_t - - value: y - type: uint16_t + - value: x + type: uint16_t + - value: y + type: uint16_t diff --git a/Alif/DevKit-E8_ETH/datatest/sds_control.c b/Alif/DevKit-E8_ETH/datatest/sds_control.c index 6a11479..60ec0fb 100644 --- a/Alif/DevKit-E8_ETH/datatest/sds_control.c +++ b/Alif/DevKit-E8_ETH/datatest/sds_control.c @@ -34,7 +34,8 @@ osThreadAttr_t attrAlgorithmThread = { // sdsControlThread thread attributes osThreadAttr_t attr_sdsControlThread = { - .name = "sdsControl" + .name = "sdsControl", + .priority = osPriorityNormal1 }; // Idle time counter diff --git a/Alif/DevKit-E8_ETH/datatest/sds_main.c b/Alif/DevKit-E8_ETH/datatest/sds_main.c index e9eb3fc..60c61fc 100644 --- a/Alif/DevKit-E8_ETH/datatest/sds_main.c +++ b/Alif/DevKit-E8_ETH/datatest/sds_main.c @@ -164,7 +164,7 @@ __NO_RETURN void AlgorithmThread (void *argument) { do { ret = sdsRead(sds_data_in_id, ×lot, algo_data_in_buf, sizeof(algo_data_in_buf)); if (ret == SDS_NO_DATA) { - osDelay(10U); + osDelay(1U); DiscardInputData(); } } while (ret == SDS_NO_DATA); @@ -190,7 +190,7 @@ __NO_RETURN void AlgorithmThread (void *argument) { do { ret = sdsWrite(sds_data_in_id, timeslot, algo_data_in_buf, sizeof(algo_data_in_buf)); if (ret == SDS_NO_SPACE) { - osDelay(10U); + osDelay(1U); } } while (ret == SDS_NO_SPACE); SDS_ASSERT(ret == sizeof(algo_data_in_buf)); @@ -208,7 +208,7 @@ __NO_RETURN void AlgorithmThread (void *argument) { do { ret = sdsWrite(sds_data_out_id, timeslot, algo_data_out_buf, sizeof(algo_data_out_buf)); if (ret == SDS_NO_SPACE) { - osDelay(10U); + osDelay(1U); } } while (ret == SDS_NO_SPACE); SDS_ASSERT(ret == sizeof(algo_data_out_buf)); diff --git a/Alif/DevKit-E8_ETH/sdsio/fvp/RTE/SDS/sds_config.h b/Alif/DevKit-E8_ETH/sdsio/fvp/RTE/SDS/sds_config.h index 8d0b50c..ecd50a5 100644 --- a/Alif/DevKit-E8_ETH/sdsio/fvp/RTE/SDS/sds_config.h +++ b/Alif/DevKit-E8_ETH/sdsio/fvp/RTE/SDS/sds_config.h @@ -17,7 +17,7 @@ * * Name: sds_config.h * Purpose: SDS configuration options - * Rev.: V3.0.0 + * Rev.: V3.1.0 */ //-------- <<< Use Configuration Wizard in Context Menu >>> -------------------- @@ -40,7 +40,7 @@ #define SDS_THREAD_STACK_SIZE 1024 // SDS system thread priority -#define SDS_THREAD_PRIORITY osPriorityNormal +#define SDS_THREAD_PRIORITY osPriorityNormal1 // SDS stream open timeout in kernel ticks #define SDS_OPEN_TIMEOUT 3000U diff --git a/Alif/DevKit-E8_ETH/sdsio/fvp/RTE/SDS/sds_config.h.base@3.0.0 b/Alif/DevKit-E8_ETH/sdsio/fvp/RTE/SDS/sds_config.h.base@3.1.0 similarity index 95% rename from Alif/DevKit-E8_ETH/sdsio/fvp/RTE/SDS/sds_config.h.base@3.0.0 rename to Alif/DevKit-E8_ETH/sdsio/fvp/RTE/SDS/sds_config.h.base@3.1.0 index 8d0b50c..ecd50a5 100644 --- a/Alif/DevKit-E8_ETH/sdsio/fvp/RTE/SDS/sds_config.h.base@3.0.0 +++ b/Alif/DevKit-E8_ETH/sdsio/fvp/RTE/SDS/sds_config.h.base@3.1.0 @@ -17,7 +17,7 @@ * * Name: sds_config.h * Purpose: SDS configuration options - * Rev.: V3.0.0 + * Rev.: V3.1.0 */ //-------- <<< Use Configuration Wizard in Context Menu >>> -------------------- @@ -40,7 +40,7 @@ #define SDS_THREAD_STACK_SIZE 1024 // SDS system thread priority -#define SDS_THREAD_PRIORITY osPriorityNormal +#define SDS_THREAD_PRIORITY osPriorityNormal1 // SDS stream open timeout in kernel ticks #define SDS_OPEN_TIMEOUT 3000U diff --git a/Alif/DevKit-E8_ETH/sdsio/fvp/sdsio_fvp.clayer.yml b/Alif/DevKit-E8_ETH/sdsio/fvp/sdsio_fvp.clayer.yml index f4634a7..5c2f55a 100644 --- a/Alif/DevKit-E8_ETH/sdsio/fvp/sdsio_fvp.clayer.yml +++ b/Alif/DevKit-E8_ETH/sdsio/fvp/sdsio_fvp.clayer.yml @@ -3,7 +3,7 @@ layer: description: Layer with SDS and SDSIO using VSI packs: - - pack: ARM::SDS@^3.0.0-0 + - pack: ARM::SDS@^3.1.0-0 connections: - connect: SDS diff --git a/Alif/DevKit-E8_ETH/sdsio/network/RTE/SDS/sds_config.h b/Alif/DevKit-E8_ETH/sdsio/network/RTE/SDS/sds_config.h index 8d0b50c..ecd50a5 100644 --- a/Alif/DevKit-E8_ETH/sdsio/network/RTE/SDS/sds_config.h +++ b/Alif/DevKit-E8_ETH/sdsio/network/RTE/SDS/sds_config.h @@ -17,7 +17,7 @@ * * Name: sds_config.h * Purpose: SDS configuration options - * Rev.: V3.0.0 + * Rev.: V3.1.0 */ //-------- <<< Use Configuration Wizard in Context Menu >>> -------------------- @@ -40,7 +40,7 @@ #define SDS_THREAD_STACK_SIZE 1024 // SDS system thread priority -#define SDS_THREAD_PRIORITY osPriorityNormal +#define SDS_THREAD_PRIORITY osPriorityNormal1 // SDS stream open timeout in kernel ticks #define SDS_OPEN_TIMEOUT 3000U diff --git a/Alif/DevKit-E8_ETH/sdsio/network/RTE/SDS/sds_config.h.base@3.0.0 b/Alif/DevKit-E8_ETH/sdsio/network/RTE/SDS/sds_config.h.base@3.1.0 similarity index 95% rename from Alif/DevKit-E8_ETH/sdsio/network/RTE/SDS/sds_config.h.base@3.0.0 rename to Alif/DevKit-E8_ETH/sdsio/network/RTE/SDS/sds_config.h.base@3.1.0 index 8d0b50c..ecd50a5 100644 --- a/Alif/DevKit-E8_ETH/sdsio/network/RTE/SDS/sds_config.h.base@3.0.0 +++ b/Alif/DevKit-E8_ETH/sdsio/network/RTE/SDS/sds_config.h.base@3.1.0 @@ -17,7 +17,7 @@ * * Name: sds_config.h * Purpose: SDS configuration options - * Rev.: V3.0.0 + * Rev.: V3.1.0 */ //-------- <<< Use Configuration Wizard in Context Menu >>> -------------------- @@ -40,7 +40,7 @@ #define SDS_THREAD_STACK_SIZE 1024 // SDS system thread priority -#define SDS_THREAD_PRIORITY osPriorityNormal +#define SDS_THREAD_PRIORITY osPriorityNormal1 // SDS stream open timeout in kernel ticks #define SDS_OPEN_TIMEOUT 3000U diff --git a/Alif/DevKit-E8_ETH/sdsio/network/sdsio_network.clayer.yml b/Alif/DevKit-E8_ETH/sdsio/network/sdsio_network.clayer.yml index dd6b6d9..d4b51e5 100644 --- a/Alif/DevKit-E8_ETH/sdsio/network/sdsio_network.clayer.yml +++ b/Alif/DevKit-E8_ETH/sdsio/network/sdsio_network.clayer.yml @@ -3,7 +3,7 @@ layer: description: Layer with SDS and SDSIO using Ethernet interface to the SDSIO-Server packs: - - pack: ARM::SDS@^3.0.0-0 + - pack: ARM::SDS@^3.1.0-0 - pack: MDK-Packs::IoT_Socket@^1.4.0 - pack: Keil::MDK-Middleware@^8.0.0 diff --git a/ST/B-U585I-IOT02A/KeywordSpotting/Board/Corstone-300/vsi/python/arm_vsi3.py b/ST/B-U585I-IOT02A/KeywordSpotting/Board/Corstone-300/vsi/python/arm_vsi3.py index 22b4d0a..6250c88 100644 --- a/ST/B-U585I-IOT02A/KeywordSpotting/Board/Corstone-300/vsi/python/arm_vsi3.py +++ b/ST/B-U585I-IOT02A/KeywordSpotting/Board/Corstone-300/vsi/python/arm_vsi3.py @@ -25,6 +25,7 @@ #More details. import os +import atexit import logging import logging.handlers from os import path @@ -214,6 +215,7 @@ def _build_sdsio_request(command: int, sid: int = 0, argument: int = 0, data: by Stream = sdsio_manager( work_dir=_work_dir, auto_playback=_auto_playback, + exit_after_playback=True, play_list=_play_list, mon_port=None, write_flush_records=_write_flush_records, @@ -222,6 +224,15 @@ def _build_sdsio_request(command: int, sid: int = 0, argument: int = 0, data: by control_input_factory=False, ) +# Shutdown SDSIO manager on exit +def shutdown(): + try: + Stream.shutdown() + except Exception: + logger.error("Failed to shutdown SDSIO manager.") + +# Register the shutdown function to be called on exit +atexit.register(shutdown) ## Process command # @param command requested SDSIO command diff --git a/ST/B-U585I-IOT02A/KeywordSpotting/Board/Corstone-300/vsi/python/sdsio.py b/ST/B-U585I-IOT02A/KeywordSpotting/Board/Corstone-300/vsi/python/sdsio.py index b5c3809..cefe648 100644 --- a/ST/B-U585I-IOT02A/KeywordSpotting/Board/Corstone-300/vsi/python/sdsio.py +++ b/ST/B-U585I-IOT02A/KeywordSpotting/Board/Corstone-300/vsi/python/sdsio.py @@ -14,6 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import asyncio import os import os.path as path import threading @@ -26,7 +27,7 @@ # ---------------------------------------------------------------------------- # # SDSIO server-compatible stream implementation # # ---------------------------------------------------------------------------- # -SDSIO_VSI_VERSION = "3.0.0" +SDSIO_VSI_VERSION = "3.1.0" class StreamInfo(NamedTuple): name: str = None @@ -133,8 +134,12 @@ def __init__(self, auto_playback=False): self._set = SDS_FLAG_MASK_PLAYBACK_MODE | SDS_FLAG_MASK_START self._auto_start_pending = True - def apply(self, set_mask: int, clear_mask: int): + def apply(self, set_mask: int, clear_mask: int, auto_playback: Optional[bool] = None): with self._lock: + if auto_playback is not None: + self._auto_playback = auto_playback + self._auto_start_pending = auto_playback and bool(set_mask & SDS_FLAG_MASK_START) + self._auto_terminate_pending = False self._set = (self._set | set_mask) & ~clear_mask self._clear = (self._clear | clear_mask) & ~set_mask if set_mask & SDS_FLAG_MASK_PLAYBACK_MODE: @@ -166,14 +171,17 @@ def request_auto_playback_start(self) -> bool: self._auto_start_pending = True return True - def request_auto_playback_terminate(self) -> bool: + def request_auto_playback_terminate(self, _force=False) -> bool: with self._lock: if not self._auto_playback: return False - if self._auto_start_pending or self._auto_terminate_pending: - return False - if self._target_flags & SDS_FLAG_MASK_START: + if self._auto_terminate_pending: return False + if not _force: + if self._auto_start_pending: + return False + if self._target_flags & SDS_FLAG_MASK_START: + return False self._set |= SDS_FLAG_MASK_CI_TERMINATE self._clear &= ~SDS_FLAG_MASK_CI_TERMINATE self._auto_terminate_pending = True @@ -216,7 +224,10 @@ def __init__( self, work_dir, auto_playback=False, + exit_after_playback=False, + no_progress_info=False, play_list: Optional[list] = None, + play_step: Optional[int] = None, mon_port: Optional[int] = None, write_flush_records: Optional[int] = None, status_bar_factory=None, @@ -224,7 +235,7 @@ def __init__( control_input_factory=None, ): self._stream_id = 0 - self._play_step_index = 0 + self._play_step = 0 self._rec_index = None # recording session index (None = not yet determined) self._work_dir = path.normpath(work_dir) self._rec_dir = self._work_dir @@ -239,19 +250,23 @@ def __init__( self._read_buffers = {} # sid -> ByteStreamBuffer self._read_threads = {} # sid -> Thread self._read_stop = {} # sid -> Event - # lock to protect stream_id increment and open checks - self._manager_lock = threading.Lock() + # lock to protect playback selection and stream state transitions + self._manager_lock = threading.RLock() # timestamp of last stream read or write command self.time_last_rw = time.time() # status bar self._status = None - if status_bar_factory is None: + if status_bar_factory is None and not no_progress_info: status_bar_factory = StatusBar if status_bar_factory: self._status = status_bar_factory(self) self._playback_mode = False + self._exit_after_playback = exit_after_playback + self._send_ci_terminate_on_shutdown = False self._play_list = play_list + self._play_step_limit = len(play_list) if play_list else None + self._single_play_step_selected = False self._mon_port = mon_port self._write_flush_records = write_flush_records # SDS Control Flags @@ -262,9 +277,9 @@ def __init__( if monitor_factory is None: monitor_factory = sdsMonitorInterface if monitor_factory: - self._monitor = monitor_factory(self._mon_port, self._flags) + self._monitor = monitor_factory(self._mon_port, self._flags, self.select_play_step) self._ctrl_input = None - if control_input_factory is not False: + if control_input_factory is not False and sys.stdin.isatty(): if control_input_factory is None: control_input_factory = sdsControlInput if control_input_factory: @@ -275,6 +290,15 @@ def __init__( self._info_IdleRate: int = 0 self._last_async_time = time.time() self._last_playback_stream_name = None + try: + self._loop = asyncio.get_running_loop() + self._main_task = asyncio.current_task() + except RuntimeError: + self._loop = None + self._main_task = None + if play_step is not None: + if play_step < 0 or not self.select_play_step(play_step): + raise ValueError(f"Invalid play step: {play_step}") def shutdown(self): self.shutdown_requested.set() @@ -459,40 +483,93 @@ def _file_read_worker(self, sid, name, buf: ByteStreamBuffer, stop_evt): finally: buf.set_eof() + def _get_play_step_limit(self): + if not self._play_list: + return None + if self._play_step_limit is None: + return len(self._play_list) + return min(self._play_step_limit, len(self._play_list)) + + def _is_single_play_step_selected(self) -> bool: + return self._single_play_step_selected + + def select_play_step(self, play_step: Optional[int]) -> bool: + with self._manager_lock: + if self.opened_streams: + logger.error("Play step selection failed: streams are currently open.") + return False + if not self._play_list: + logger.error("Play step selection failed: no play steps are configured.") + return False + + if play_step is not None and (play_step < 0 or play_step >= len(self._play_list)): + logger.error(f"Play step selection failed: {play_step} is outside 0-{len(self._play_list) - 1}.") + return False + + if play_step is None: + self._play_step = 0 + self._play_step_limit = len(self._play_list) + self._single_play_step_selected = False + logger.debug(f"Selected all playback steps 0-{len(self._play_list) - 1}.") + else: + self._play_step = play_step + self._play_step_limit = play_step + 1 + self._single_play_step_selected = True + logger.debug(f"Selected playback step {play_step}.") + self._label_list.clear() + self._timestamp_boundaries.clear() + return True + def _create_play_label_list(self, name) -> list[str]: _labels = [] - if self._play_list and self._play_step_index < len(self._play_list): - _step = self._play_list[self._play_step_index] + _play_step_limit = self._get_play_step_limit() + if self._play_list and self._play_step < _play_step_limit: + _step = self._play_list[self._play_step] _labels = list(_step.get('labels', [])) else: - # No playlist: one file per open, indexed by play_step_index - _candidate = path.join(self._work_dir, f"{name}.{self._play_step_index}.sds") + # No playlist: one file per open, selected by play_step + _candidate = path.join(self._work_dir, f"{name}.{self._play_step}.sds") if path.exists(_candidate): - _labels.append(str(self._play_step_index)) + _labels.append(str(self._play_step)) return _labels def _has_next_auto_playback_step(self) -> bool: if not self._flags.auto_playback or self.opened_streams: return False if self._play_list: - return self._play_step_index < len(self._play_list) + return self._play_step < self._get_play_step_limit() if self._last_playback_stream_name: return bool(self._create_play_label_list(self._last_playback_stream_name)) return False def _request_auto_playback_if_needed(self, target_flags: Optional[int] = None): - _target_flags = self._flags.target_flags if target_flags is None else target_flags - if _target_flags & SDS_FLAG_MASK_START: + with self._manager_lock: + _target_flags = self._flags.target_flags if target_flags is None else target_flags + if _target_flags & SDS_FLAG_MASK_START: + return + if self.opened_streams: + return + if self._has_next_auto_playback_step(): + self._flags.request_auto_playback_start() + elif self._flags.auto_playback and self._last_playback_stream_name: + if self._flags.request_auto_playback_terminate(): + _complete_msg = "Playback complete - no more steps remaining." if self._play_list else "Playback complete." + logger.info(_complete_msg) + self._request_exit_after_playback("playback complete") + + def _request_exit_after_playback(self, _reason: str): + if not self._exit_after_playback: return - if self.opened_streams: - return - if self._has_next_auto_playback_step(): - self._flags.request_auto_playback_start() - elif self._flags.auto_playback and self._last_playback_stream_name: - if self._flags.request_auto_playback_terminate(): - logger.info("Playback complete - no more steps remaining.") - + logger.info(f"SDSIO-Server terminating ({_reason}).") + self._send_ci_terminate_on_shutdown = True + self.shutdown_requested.set() + if self._loop and self._main_task: + self._loop.call_soon_threadsafe(self._main_task.cancel) def _open(self, mode, name): + with self._manager_lock: + return self._open_locked(mode, name) + + def _open_locked(self, mode, name): _cmd = CMD_OPEN # prepare error response _resp_err = bytearray() @@ -523,12 +600,13 @@ def _open(self, mode, name): if self._playback_mode: if not self._label_list: # Get flags, Set working dir + _index_based_playback = False if self._play_list: - if self._play_step_index < len(self._play_list): - _step = self._play_list[self._play_step_index] + if self._play_step < self._get_play_step_limit(): + _step = self._play_list[self._play_step] _step_desc = _step.get('step', '') _desc_suffix = f": {_step_desc}" if _step_desc else "" - logger.info(f"Playback step {self._play_step_index + 1}/{len(self._play_list)}{_desc_suffix}.") + logger.info(f"Playback step {self._play_step}{_desc_suffix}.") _set_flags = _step.get('setflags', 0) _clear_flags = _step.get('clearflags', 0) _recdir = _step.get('recdir', None) @@ -538,10 +616,13 @@ def _open(self, mode, name): self._rec_dir = self._work_dir else: logger.error(f"Open Failed. End of playlist. No more steps available for playback stream '{name}'.") + if self._exit_after_playback and self._flags.request_auto_playback_terminate(_force=True): + self._request_exit_after_playback("playback data unavailable") return _resp_err else: _set_flags = 0 _clear_flags = 0 + _index_based_playback = True if _set_flags or _clear_flags: logger.debug(f"Applying flags for playback stream '{name}': set=0x{_set_flags:08X}, clear=0x{_clear_flags:08X}.") @@ -550,12 +631,16 @@ def _open(self, mode, name): # Create label list _play_label_list = self._create_play_label_list(name) if not _play_label_list: - if not self._play_list and self._play_step_index > 0: + if not self._play_list and self._play_step > 0: logger.error(f"Open Failed. No more files available for playback stream '{name}'.") else: logger.error(f"Open Failed. No files found for playback stream '{name}'.") + if self._exit_after_playback and self._flags.request_auto_playback_terminate(_force=True): + self._request_exit_after_playback("playback data unavailable") return _resp_err self._label_list = _play_label_list + if _index_based_playback and self._play_step == 0: + logger.info("No play steps, index based playback started.") else: if mode == 0: @@ -614,6 +699,8 @@ def _open(self, mode, name): for _sds_file_path in _file_paths: if not path.exists(_sds_file_path): logger.error(f"Missing file for playback stream '{name}': {self._format_path(_sds_file_path)}") + if self._exit_after_playback and self._flags.request_auto_playback_terminate(_force=True): + self._request_exit_after_playback("playback data unavailable") return _resp_err if not self._timestamp_boundaries: @@ -657,6 +744,10 @@ def _open(self, mode, name): return _resp def _close(self, sid): + with self._manager_lock: + return self._close_locked(sid) + + def _close_locked(self, sid): _resp = bytearray() _stream = self.opened_streams[sid] _name = _stream.name @@ -699,7 +790,7 @@ def _close(self, sid): if not self.opened_streams: if self._playback_mode: - self._play_step_index += 1 + self._play_step += 1 self._label_list.clear() self._timestamp_boundaries.clear() self._request_auto_playback_if_needed() @@ -809,7 +900,7 @@ def _info(self, flags: int, idle_rate: int, err_data: bytes): logger.info(f"{idle_rate}% idle.") self._info_IdleRate = idle_rate if err_data: - _status = int.from_bytes(err_data[0:4],'little') + _status = int.from_bytes(err_data[0:4], 'little', signed=True) _line = int.from_bytes(err_data[4:8],'little') _err_mgs = err_data[8:] if _status == 0: @@ -850,9 +941,14 @@ def get_async_response(self): def get_shutdown_flags(self): _resp = bytearray() _cmd = CMD_FLAGS + if self._send_ci_terminate_on_shutdown: + _set_mask = SDS_FLAG_MASK_CI_TERMINATE + else: + _set_mask = 0 + _clear_mask = SDS_FLAG_MASK_ALIVE _resp.extend(_cmd.to_bytes(4,'little')) - _resp.extend((0).to_bytes(4,'little')) - _resp.extend((1 << 28).to_bytes(4,'little')) + _resp.extend(_set_mask.to_bytes(4,'little')) + _resp.extend(_clear_mask.to_bytes(4,'little')) _resp.extend((0).to_bytes(4,'little')) return _resp diff --git a/ST/B-U585I-IOT02A/KeywordSpotting/README.md b/ST/B-U585I-IOT02A/KeywordSpotting/README.md index 9aa7272..f907abc 100644 --- a/ST/B-U585I-IOT02A/KeywordSpotting/README.md +++ b/ST/B-U585I-IOT02A/KeywordSpotting/README.md @@ -118,7 +118,7 @@ To perform a recording, follow these steps: ```txt >sdsio-server usb -SDSIO-Server v3.0.0 +SDSIO-Server v3.1.0 Press 'Ctrl+C' or 'X' to exit. Working directory: ...\Arm-Examples\SDS-Examples\ST\B-U585I-IOT02A\KeywordSpotting SDSIO command input: R=Record, P=playback, S/s=stop, T/t=reset, X/x=exit, A-H=set flags 0-7, a-h=clear flags 0-7. @@ -183,7 +183,7 @@ The SDS file `Test_Out..p.sds` created during playback should be identical to ```txt >sdsio-server usb -SDSIO-Server v3.0.0 +SDSIO-Server v3.1.0 Press 'Ctrl+C' or 'X' to exit. Working directory: ...\Arm-Examples\SDS-Examples\ST\B-U585I-IOT02A\KeywordSpotting SDSIO command input: R=Record, P=playback, S/s=stop, T/t=reset, X/x=exit, A-H=set flags 0-7, a-h=clear flags 0-7. @@ -288,7 +288,7 @@ To perform a recording, follow these steps: ```txt >sdsio-server usb -SDSIO-Server v3.0.0 +SDSIO-Server v3.1.0 Press 'Ctrl+C' or 'X' to exit. Working directory: ...\Arm-Examples\SDS-Examples\ST\B-U585I-IOT02A\KeywordSpotting SDSIO command input: R=Record, P=playback, S/s=stop, T/t=reset, X/x=exit, A-H=set flags 0-7, a-h=clear flags 0-7. @@ -348,7 +348,7 @@ The SDS file `ML_Out..p.sds` created during playback should be identical to t ```txt >sdsio-server usb -SDSIO-Server v3.0.0 +SDSIO-Server v3.1.0 Press 'Ctrl+C' or 'X' to exit. Working directory: ...\Arm-Examples\SDS-Examples\ST\B-U585I-IOT02A\KeywordSpotting SDSIO command input: R=Record, P=playback, S/s=stop, T/t=reset, X/x=exit, A-H=set flags 0-7, a-h=clear flags 0-7. @@ -427,7 +427,7 @@ SDSIO VSI interface initialized successfully ```txt Created by ...\Arm-Examples\SDS-Examples\ST\B-U585I-IOT02A\KeywordSpotting\Board\Corstone-300\vsi\python\arm_vsi3.py -SDSIO VSI version 3.0.0 +SDSIO VSI version 3.1.0 SDSIO_FVP environment variable not set. Working directory: ...\Arm-Examples\SDS-Examples\ST\B-U585I-IOT02A\KeywordSpotting\algorithm\SDS Recordings. SDSIO configuration YAML: ...\Arm-Examples\SDS-Examples\ST\B-U585I-IOT02A\KeywordSpotting\algorithm.sdsio.yml. diff --git a/ST/B-U585I-IOT02A/KeywordSpotting/SDS.csolution.yml b/ST/B-U585I-IOT02A/KeywordSpotting/SDS.csolution.yml index a500dce..f69536c 100644 --- a/ST/B-U585I-IOT02A/KeywordSpotting/SDS.csolution.yml +++ b/ST/B-U585I-IOT02A/KeywordSpotting/SDS.csolution.yml @@ -15,7 +15,7 @@ solution: # Refer to https://open-cmsis-pack.github.io/cmsis-toolbox/ReferenceApplications/ for more information packs: - - pack: ARM::SDS@^3.0.0-0 + - pack: ARM::SDS@^3.1.0-0 - pack: Keil::B-U585I-IOT02A_BSP - pack: Keil::STM32U5xx_DFP - pack: ARM::V2M_MPS3_SSE_300_BSP@^1.5.0 diff --git a/ST/B-U585I-IOT02A/KeywordSpotting/algorithm.sdsio.yml b/ST/B-U585I-IOT02A/KeywordSpotting/algorithm.sdsio.yml index cce8142..ff03682 100644 --- a/ST/B-U585I-IOT02A/KeywordSpotting/algorithm.sdsio.yml +++ b/ST/B-U585I-IOT02A/KeywordSpotting/algorithm.sdsio.yml @@ -7,7 +7,7 @@ sdsio: # Data stream information used by VS Code extension streams: - name: ML_In - view: audio + view: wav - name: ML_Out view: signal diff --git a/ST/B-U585I-IOT02A/KeywordSpotting/algorithm/SDS Recordings/ML_In.0.wav b/ST/B-U585I-IOT02A/KeywordSpotting/algorithm/SDS Recordings/ML_In.0.wav new file mode 100644 index 0000000..5e056c2 Binary files /dev/null and b/ST/B-U585I-IOT02A/KeywordSpotting/algorithm/SDS Recordings/ML_In.0.wav differ diff --git a/ST/B-U585I-IOT02A/KeywordSpotting/algorithm/SDS Recordings/ML_In.sds.yml b/ST/B-U585I-IOT02A/KeywordSpotting/algorithm/SDS Recordings/ML_In.sds.yml index 0a9b957..b6fedec 100644 --- a/ST/B-U585I-IOT02A/KeywordSpotting/algorithm/SDS Recordings/ML_In.sds.yml +++ b/ST/B-U585I-IOT02A/KeywordSpotting/algorithm/SDS Recordings/ML_In.sds.yml @@ -1,7 +1,9 @@ sds: - name: ML Model Data Input - description: Mono microphone with 16kHz sample rate - frequency: 16000 + name: ML input + description: Mono audio from microphone + sample-frequency: 16000 content: - - value: Mono - type: int16_t + - audio: + bit-depth: 16 + channels: 1 + format: pcm diff --git a/ST/B-U585I-IOT02A/KeywordSpotting/algorithm/SDS Recordings/ML_Out.sds.yml b/ST/B-U585I-IOT02A/KeywordSpotting/algorithm/SDS Recordings/ML_Out.sds.yml index 72d220e..75948ba 100644 --- a/ST/B-U585I-IOT02A/KeywordSpotting/algorithm/SDS Recordings/ML_Out.sds.yml +++ b/ST/B-U585I-IOT02A/KeywordSpotting/algorithm/SDS Recordings/ML_Out.sds.yml @@ -1,11 +1,11 @@ sds: - name: ML Model Data Output + name: ML output description: Classification results - frequency: 4.0 + sample-frequency: 4.0 content: - - value: helloworld - type: float - - value: noise - type: float - - value: unknown - type: float + - value: helloworld + type: float + - value: noise + type: float + - value: unknown + type: float diff --git a/ST/B-U585I-IOT02A/KeywordSpotting/algorithm/sds_control.c b/ST/B-U585I-IOT02A/KeywordSpotting/algorithm/sds_control.c index 6a11479..60ec0fb 100644 --- a/ST/B-U585I-IOT02A/KeywordSpotting/algorithm/sds_control.c +++ b/ST/B-U585I-IOT02A/KeywordSpotting/algorithm/sds_control.c @@ -34,7 +34,8 @@ osThreadAttr_t attrAlgorithmThread = { // sdsControlThread thread attributes osThreadAttr_t attr_sdsControlThread = { - .name = "sdsControl" + .name = "sdsControl", + .priority = osPriorityNormal1 }; // Idle time counter diff --git a/ST/B-U585I-IOT02A/KeywordSpotting/algorithm/sds_main.c b/ST/B-U585I-IOT02A/KeywordSpotting/algorithm/sds_main.c index df1855d..50ac5fc 100644 --- a/ST/B-U585I-IOT02A/KeywordSpotting/algorithm/sds_main.c +++ b/ST/B-U585I-IOT02A/KeywordSpotting/algorithm/sds_main.c @@ -164,7 +164,7 @@ __NO_RETURN void AlgorithmThread (void *argument) { do { ret = sdsRead(sds_data_in_id, ×lot, algo_data_in_buf, sizeof(algo_data_in_buf)); if (ret == SDS_NO_DATA) { - osDelay(10U); + osDelay(1U); DiscardInputData(); } } while (ret == SDS_NO_DATA); @@ -190,7 +190,7 @@ __NO_RETURN void AlgorithmThread (void *argument) { do { ret = sdsWrite(sds_data_in_id, timeslot, algo_data_in_buf, sizeof(algo_data_in_buf)); if (ret == SDS_NO_SPACE) { - osDelay(10U); + osDelay(1U); } } while (ret == SDS_NO_SPACE); SDS_ASSERT(ret == sizeof(algo_data_in_buf)); @@ -208,7 +208,7 @@ __NO_RETURN void AlgorithmThread (void *argument) { do { ret = sdsWrite(sds_data_out_id, timeslot, algo_data_out_buf, sizeof(algo_data_out_buf)); if (ret == SDS_NO_SPACE) { - osDelay(10U); + osDelay(1U); } } while (ret == SDS_NO_SPACE); SDS_ASSERT(ret == sizeof(algo_data_out_buf)); diff --git a/ST/B-U585I-IOT02A/KeywordSpotting/datatest/SDS Recordings/Test_In.sds.yml b/ST/B-U585I-IOT02A/KeywordSpotting/datatest/SDS Recordings/Test_In.sds.yml index 6909a4e..be56e4c 100644 --- a/ST/B-U585I-IOT02A/KeywordSpotting/datatest/SDS Recordings/Test_In.sds.yml +++ b/ST/B-U585I-IOT02A/KeywordSpotting/datatest/SDS Recordings/Test_In.sds.yml @@ -1,17 +1,17 @@ sds: name: Test Input Data description: Generated accelerometer samples at 16600 Hz - frequency: 16600 + sample-frequency: 16600 content: - - value: x - type: uint16_t - scale: 0.001 - unit: G - - value: y - type: uint16_t - scale: 0.001 - unit: G - - value: z - type: uint16_t - scale: 0.001 - unit: G + - value: x + type: uint16_t + scale: 0.001 + unit: G + - value: y + type: uint16_t + scale: 0.001 + unit: G + - value: z + type: uint16_t + scale: 0.001 + unit: G diff --git a/ST/B-U585I-IOT02A/KeywordSpotting/datatest/SDS Recordings/Test_Out.sds.yml b/ST/B-U585I-IOT02A/KeywordSpotting/datatest/SDS Recordings/Test_Out.sds.yml index 2727caa..f1a085b 100644 --- a/ST/B-U585I-IOT02A/KeywordSpotting/datatest/SDS Recordings/Test_Out.sds.yml +++ b/ST/B-U585I-IOT02A/KeywordSpotting/datatest/SDS Recordings/Test_Out.sds.yml @@ -1,9 +1,9 @@ sds: name: Test Output Data description: Results of algorithm processing - frequency: 1000 + sample-frequency: 1000 content: - - value: x - type: uint16_t - - value: y - type: uint16_t + - value: x + type: uint16_t + - value: y + type: uint16_t diff --git a/ST/B-U585I-IOT02A/KeywordSpotting/datatest/sds_control.c b/ST/B-U585I-IOT02A/KeywordSpotting/datatest/sds_control.c index 6a11479..60ec0fb 100644 --- a/ST/B-U585I-IOT02A/KeywordSpotting/datatest/sds_control.c +++ b/ST/B-U585I-IOT02A/KeywordSpotting/datatest/sds_control.c @@ -34,7 +34,8 @@ osThreadAttr_t attrAlgorithmThread = { // sdsControlThread thread attributes osThreadAttr_t attr_sdsControlThread = { - .name = "sdsControl" + .name = "sdsControl", + .priority = osPriorityNormal1 }; // Idle time counter diff --git a/ST/B-U585I-IOT02A/KeywordSpotting/datatest/sds_main.c b/ST/B-U585I-IOT02A/KeywordSpotting/datatest/sds_main.c index b2e38c1..32de570 100644 --- a/ST/B-U585I-IOT02A/KeywordSpotting/datatest/sds_main.c +++ b/ST/B-U585I-IOT02A/KeywordSpotting/datatest/sds_main.c @@ -164,7 +164,7 @@ __NO_RETURN void AlgorithmThread (void *argument) { do { ret = sdsRead(sds_data_in_id, ×lot, algo_data_in_buf, sizeof(algo_data_in_buf)); if (ret == SDS_NO_DATA) { - osDelay(10U); + osDelay(1U); DiscardInputData(); } } while (ret == SDS_NO_DATA); @@ -190,7 +190,7 @@ __NO_RETURN void AlgorithmThread (void *argument) { do { ret = sdsWrite(sds_data_in_id, timeslot, algo_data_in_buf, sizeof(algo_data_in_buf)); if (ret == SDS_NO_SPACE) { - osDelay(10U); + osDelay(1U); } } while (ret == SDS_NO_SPACE); SDS_ASSERT(ret == sizeof(algo_data_in_buf)); @@ -208,7 +208,7 @@ __NO_RETURN void AlgorithmThread (void *argument) { do { ret = sdsWrite(sds_data_out_id, timeslot, algo_data_out_buf, sizeof(algo_data_out_buf)); if (ret == SDS_NO_SPACE) { - osDelay(10U); + osDelay(1U); } } while (ret == SDS_NO_SPACE); SDS_ASSERT(ret == sizeof(algo_data_out_buf)); diff --git a/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/fvp/RTE/SDS/sds_config.h b/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/fvp/RTE/SDS/sds_config.h index 8d0b50c..ecd50a5 100644 --- a/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/fvp/RTE/SDS/sds_config.h +++ b/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/fvp/RTE/SDS/sds_config.h @@ -17,7 +17,7 @@ * * Name: sds_config.h * Purpose: SDS configuration options - * Rev.: V3.0.0 + * Rev.: V3.1.0 */ //-------- <<< Use Configuration Wizard in Context Menu >>> -------------------- @@ -40,7 +40,7 @@ #define SDS_THREAD_STACK_SIZE 1024 // SDS system thread priority -#define SDS_THREAD_PRIORITY osPriorityNormal +#define SDS_THREAD_PRIORITY osPriorityNormal1 // SDS stream open timeout in kernel ticks #define SDS_OPEN_TIMEOUT 3000U diff --git a/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/fvp/RTE/SDS/sds_config.h.base@3.0.0 b/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/fvp/RTE/SDS/sds_config.h.base@3.0.0 deleted file mode 100644 index 8d0b50c..0000000 --- a/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/fvp/RTE/SDS/sds_config.h.base@3.0.0 +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2025-2026 Arm Limited. All rights reserved. - * - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the License); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an AS IS BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Name: sds_config.h - * Purpose: SDS configuration options - * Rev.: V3.0.0 - */ - -//-------- <<< Use Configuration Wizard in Context Menu >>> -------------------- - -// SDS System Configuration - -// Maximum concurrent streams <1-31> -// Default: 16 -#define SDS_MAX_STREAMS 16U - -// Internal buffer size for I/O transfers -// Default: 8192 -#define SDS_BUF_SIZE 8192U - -// - -//------------- <<< end of configuration section >>> --------------------------- - -// SDS system thread stack size -#define SDS_THREAD_STACK_SIZE 1024 - -// SDS system thread priority -#define SDS_THREAD_PRIORITY osPriorityNormal - -// SDS stream open timeout in kernel ticks -#define SDS_OPEN_TIMEOUT 3000U - -// SDS stream close timeout in kernel ticks -#define SDS_CLOSE_TIMEOUT 3000U - -// Optimal I/O transfer size (read/write) -// Default: 8192 -// Select a value appropriate for the underlying I/O interface (e.g., socket, USART, VCOM, file system) -// to ensure efficient read/write performance -#define SDS_IO_TRANSFER_SIZE 8192U diff --git a/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/fvp/RTE/SDS/sds_config.h.base@3.1.0 b/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/fvp/RTE/SDS/sds_config.h.base@3.1.0 new file mode 100644 index 0000000..ecd50a5 --- /dev/null +++ b/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/fvp/RTE/SDS/sds_config.h.base@3.1.0 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2025-2026 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Name: sds_config.h + * Purpose: SDS configuration options + * Rev.: V3.1.0 + */ + +//-------- <<< Use Configuration Wizard in Context Menu >>> -------------------- + +// SDS System Configuration + +// Maximum concurrent streams <1-31> +// Default: 16 +#define SDS_MAX_STREAMS 16U + +// Internal buffer size for I/O transfers +// Default: 8192 +#define SDS_BUF_SIZE 8192U + +// + +//------------- <<< end of configuration section >>> --------------------------- + +// SDS system thread stack size +#define SDS_THREAD_STACK_SIZE 1024 + +// SDS system thread priority +#define SDS_THREAD_PRIORITY osPriorityNormal1 + +// SDS stream open timeout in kernel ticks +#define SDS_OPEN_TIMEOUT 3000U + +// SDS stream close timeout in kernel ticks +#define SDS_CLOSE_TIMEOUT 3000U + +// Optimal I/O transfer size (read/write) +// Default: 8192 +// Select a value appropriate for the underlying I/O interface (e.g., socket, USART, VCOM, file system) +// to ensure efficient read/write performance +#define SDS_IO_TRANSFER_SIZE 8192U diff --git a/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/fvp/sdsio_fvp.clayer.yml b/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/fvp/sdsio_fvp.clayer.yml index f4634a7..5c2f55a 100644 --- a/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/fvp/sdsio_fvp.clayer.yml +++ b/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/fvp/sdsio_fvp.clayer.yml @@ -3,7 +3,7 @@ layer: description: Layer with SDS and SDSIO using VSI packs: - - pack: ARM::SDS@^3.0.0-0 + - pack: ARM::SDS@^3.1.0-0 connections: - connect: SDS diff --git a/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/usb/RTE/SDS/sds_config.h b/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/usb/RTE/SDS/sds_config.h index 8d0b50c..ecd50a5 100644 --- a/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/usb/RTE/SDS/sds_config.h +++ b/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/usb/RTE/SDS/sds_config.h @@ -17,7 +17,7 @@ * * Name: sds_config.h * Purpose: SDS configuration options - * Rev.: V3.0.0 + * Rev.: V3.1.0 */ //-------- <<< Use Configuration Wizard in Context Menu >>> -------------------- @@ -40,7 +40,7 @@ #define SDS_THREAD_STACK_SIZE 1024 // SDS system thread priority -#define SDS_THREAD_PRIORITY osPriorityNormal +#define SDS_THREAD_PRIORITY osPriorityNormal1 // SDS stream open timeout in kernel ticks #define SDS_OPEN_TIMEOUT 3000U diff --git a/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/usb/RTE/SDS/sds_config.h.base@3.0.0 b/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/usb/RTE/SDS/sds_config.h.base@3.0.0 deleted file mode 100644 index 8d0b50c..0000000 --- a/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/usb/RTE/SDS/sds_config.h.base@3.0.0 +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2025-2026 Arm Limited. All rights reserved. - * - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the License); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an AS IS BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Name: sds_config.h - * Purpose: SDS configuration options - * Rev.: V3.0.0 - */ - -//-------- <<< Use Configuration Wizard in Context Menu >>> -------------------- - -// SDS System Configuration - -// Maximum concurrent streams <1-31> -// Default: 16 -#define SDS_MAX_STREAMS 16U - -// Internal buffer size for I/O transfers -// Default: 8192 -#define SDS_BUF_SIZE 8192U - -// - -//------------- <<< end of configuration section >>> --------------------------- - -// SDS system thread stack size -#define SDS_THREAD_STACK_SIZE 1024 - -// SDS system thread priority -#define SDS_THREAD_PRIORITY osPriorityNormal - -// SDS stream open timeout in kernel ticks -#define SDS_OPEN_TIMEOUT 3000U - -// SDS stream close timeout in kernel ticks -#define SDS_CLOSE_TIMEOUT 3000U - -// Optimal I/O transfer size (read/write) -// Default: 8192 -// Select a value appropriate for the underlying I/O interface (e.g., socket, USART, VCOM, file system) -// to ensure efficient read/write performance -#define SDS_IO_TRANSFER_SIZE 8192U diff --git a/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/usb/RTE/SDS/sds_config.h.base@3.1.0 b/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/usb/RTE/SDS/sds_config.h.base@3.1.0 new file mode 100644 index 0000000..ecd50a5 --- /dev/null +++ b/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/usb/RTE/SDS/sds_config.h.base@3.1.0 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2025-2026 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Name: sds_config.h + * Purpose: SDS configuration options + * Rev.: V3.1.0 + */ + +//-------- <<< Use Configuration Wizard in Context Menu >>> -------------------- + +// SDS System Configuration + +// Maximum concurrent streams <1-31> +// Default: 16 +#define SDS_MAX_STREAMS 16U + +// Internal buffer size for I/O transfers +// Default: 8192 +#define SDS_BUF_SIZE 8192U + +// + +//------------- <<< end of configuration section >>> --------------------------- + +// SDS system thread stack size +#define SDS_THREAD_STACK_SIZE 1024 + +// SDS system thread priority +#define SDS_THREAD_PRIORITY osPriorityNormal1 + +// SDS stream open timeout in kernel ticks +#define SDS_OPEN_TIMEOUT 3000U + +// SDS stream close timeout in kernel ticks +#define SDS_CLOSE_TIMEOUT 3000U + +// Optimal I/O transfer size (read/write) +// Default: 8192 +// Select a value appropriate for the underlying I/O interface (e.g., socket, USART, VCOM, file system) +// to ensure efficient read/write performance +#define SDS_IO_TRANSFER_SIZE 8192U diff --git a/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/usb/sdsio_usb.clayer.yml b/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/usb/sdsio_usb.clayer.yml index 23e8729..5b36837 100644 --- a/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/usb/sdsio_usb.clayer.yml +++ b/ST/B-U585I-IOT02A/KeywordSpotting/sdsio/usb/sdsio_usb.clayer.yml @@ -3,7 +3,7 @@ layer: description: Layer with SDS and SDSIO using USB interface to the SDSIO-Server packs: - - pack: ARM::SDS@^3.0.0-0 + - pack: ARM::SDS@^3.1.0-0 - pack: Keil::MDK-Middleware@^8.0.0 connections: diff --git a/ST/B-U585I-IOT02A/MotionRecognition/Board/Corstone-300/vsi/python/arm_vsi3.py b/ST/B-U585I-IOT02A/MotionRecognition/Board/Corstone-300/vsi/python/arm_vsi3.py index 144ab12..c1df180 100644 --- a/ST/B-U585I-IOT02A/MotionRecognition/Board/Corstone-300/vsi/python/arm_vsi3.py +++ b/ST/B-U585I-IOT02A/MotionRecognition/Board/Corstone-300/vsi/python/arm_vsi3.py @@ -25,6 +25,7 @@ #More details. import os +import atexit import logging import logging.handlers from os import path @@ -214,6 +215,7 @@ def _build_sdsio_request(command: int, sid: int = 0, argument: int = 0, data: by Stream = sdsio_manager( work_dir=_work_dir, auto_playback=_auto_playback, + exit_after_playback=True, play_list=_play_list, mon_port=None, write_flush_records=_write_flush_records, @@ -222,6 +224,15 @@ def _build_sdsio_request(command: int, sid: int = 0, argument: int = 0, data: by control_input_factory=False, ) +# Shutdown SDSIO manager on exit +def shutdown(): + try: + Stream.shutdown() + except Exception: + logger.error("Failed to shutdown SDSIO manager.") + +# Register the shutdown function to be called on exit +atexit.register(shutdown) ## Process command # @param command requested SDSIO command diff --git a/ST/B-U585I-IOT02A/MotionRecognition/Board/Corstone-300/vsi/python/sdsio.py b/ST/B-U585I-IOT02A/MotionRecognition/Board/Corstone-300/vsi/python/sdsio.py index b5c3809..cefe648 100644 --- a/ST/B-U585I-IOT02A/MotionRecognition/Board/Corstone-300/vsi/python/sdsio.py +++ b/ST/B-U585I-IOT02A/MotionRecognition/Board/Corstone-300/vsi/python/sdsio.py @@ -14,6 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import asyncio import os import os.path as path import threading @@ -26,7 +27,7 @@ # ---------------------------------------------------------------------------- # # SDSIO server-compatible stream implementation # # ---------------------------------------------------------------------------- # -SDSIO_VSI_VERSION = "3.0.0" +SDSIO_VSI_VERSION = "3.1.0" class StreamInfo(NamedTuple): name: str = None @@ -133,8 +134,12 @@ def __init__(self, auto_playback=False): self._set = SDS_FLAG_MASK_PLAYBACK_MODE | SDS_FLAG_MASK_START self._auto_start_pending = True - def apply(self, set_mask: int, clear_mask: int): + def apply(self, set_mask: int, clear_mask: int, auto_playback: Optional[bool] = None): with self._lock: + if auto_playback is not None: + self._auto_playback = auto_playback + self._auto_start_pending = auto_playback and bool(set_mask & SDS_FLAG_MASK_START) + self._auto_terminate_pending = False self._set = (self._set | set_mask) & ~clear_mask self._clear = (self._clear | clear_mask) & ~set_mask if set_mask & SDS_FLAG_MASK_PLAYBACK_MODE: @@ -166,14 +171,17 @@ def request_auto_playback_start(self) -> bool: self._auto_start_pending = True return True - def request_auto_playback_terminate(self) -> bool: + def request_auto_playback_terminate(self, _force=False) -> bool: with self._lock: if not self._auto_playback: return False - if self._auto_start_pending or self._auto_terminate_pending: - return False - if self._target_flags & SDS_FLAG_MASK_START: + if self._auto_terminate_pending: return False + if not _force: + if self._auto_start_pending: + return False + if self._target_flags & SDS_FLAG_MASK_START: + return False self._set |= SDS_FLAG_MASK_CI_TERMINATE self._clear &= ~SDS_FLAG_MASK_CI_TERMINATE self._auto_terminate_pending = True @@ -216,7 +224,10 @@ def __init__( self, work_dir, auto_playback=False, + exit_after_playback=False, + no_progress_info=False, play_list: Optional[list] = None, + play_step: Optional[int] = None, mon_port: Optional[int] = None, write_flush_records: Optional[int] = None, status_bar_factory=None, @@ -224,7 +235,7 @@ def __init__( control_input_factory=None, ): self._stream_id = 0 - self._play_step_index = 0 + self._play_step = 0 self._rec_index = None # recording session index (None = not yet determined) self._work_dir = path.normpath(work_dir) self._rec_dir = self._work_dir @@ -239,19 +250,23 @@ def __init__( self._read_buffers = {} # sid -> ByteStreamBuffer self._read_threads = {} # sid -> Thread self._read_stop = {} # sid -> Event - # lock to protect stream_id increment and open checks - self._manager_lock = threading.Lock() + # lock to protect playback selection and stream state transitions + self._manager_lock = threading.RLock() # timestamp of last stream read or write command self.time_last_rw = time.time() # status bar self._status = None - if status_bar_factory is None: + if status_bar_factory is None and not no_progress_info: status_bar_factory = StatusBar if status_bar_factory: self._status = status_bar_factory(self) self._playback_mode = False + self._exit_after_playback = exit_after_playback + self._send_ci_terminate_on_shutdown = False self._play_list = play_list + self._play_step_limit = len(play_list) if play_list else None + self._single_play_step_selected = False self._mon_port = mon_port self._write_flush_records = write_flush_records # SDS Control Flags @@ -262,9 +277,9 @@ def __init__( if monitor_factory is None: monitor_factory = sdsMonitorInterface if monitor_factory: - self._monitor = monitor_factory(self._mon_port, self._flags) + self._monitor = monitor_factory(self._mon_port, self._flags, self.select_play_step) self._ctrl_input = None - if control_input_factory is not False: + if control_input_factory is not False and sys.stdin.isatty(): if control_input_factory is None: control_input_factory = sdsControlInput if control_input_factory: @@ -275,6 +290,15 @@ def __init__( self._info_IdleRate: int = 0 self._last_async_time = time.time() self._last_playback_stream_name = None + try: + self._loop = asyncio.get_running_loop() + self._main_task = asyncio.current_task() + except RuntimeError: + self._loop = None + self._main_task = None + if play_step is not None: + if play_step < 0 or not self.select_play_step(play_step): + raise ValueError(f"Invalid play step: {play_step}") def shutdown(self): self.shutdown_requested.set() @@ -459,40 +483,93 @@ def _file_read_worker(self, sid, name, buf: ByteStreamBuffer, stop_evt): finally: buf.set_eof() + def _get_play_step_limit(self): + if not self._play_list: + return None + if self._play_step_limit is None: + return len(self._play_list) + return min(self._play_step_limit, len(self._play_list)) + + def _is_single_play_step_selected(self) -> bool: + return self._single_play_step_selected + + def select_play_step(self, play_step: Optional[int]) -> bool: + with self._manager_lock: + if self.opened_streams: + logger.error("Play step selection failed: streams are currently open.") + return False + if not self._play_list: + logger.error("Play step selection failed: no play steps are configured.") + return False + + if play_step is not None and (play_step < 0 or play_step >= len(self._play_list)): + logger.error(f"Play step selection failed: {play_step} is outside 0-{len(self._play_list) - 1}.") + return False + + if play_step is None: + self._play_step = 0 + self._play_step_limit = len(self._play_list) + self._single_play_step_selected = False + logger.debug(f"Selected all playback steps 0-{len(self._play_list) - 1}.") + else: + self._play_step = play_step + self._play_step_limit = play_step + 1 + self._single_play_step_selected = True + logger.debug(f"Selected playback step {play_step}.") + self._label_list.clear() + self._timestamp_boundaries.clear() + return True + def _create_play_label_list(self, name) -> list[str]: _labels = [] - if self._play_list and self._play_step_index < len(self._play_list): - _step = self._play_list[self._play_step_index] + _play_step_limit = self._get_play_step_limit() + if self._play_list and self._play_step < _play_step_limit: + _step = self._play_list[self._play_step] _labels = list(_step.get('labels', [])) else: - # No playlist: one file per open, indexed by play_step_index - _candidate = path.join(self._work_dir, f"{name}.{self._play_step_index}.sds") + # No playlist: one file per open, selected by play_step + _candidate = path.join(self._work_dir, f"{name}.{self._play_step}.sds") if path.exists(_candidate): - _labels.append(str(self._play_step_index)) + _labels.append(str(self._play_step)) return _labels def _has_next_auto_playback_step(self) -> bool: if not self._flags.auto_playback or self.opened_streams: return False if self._play_list: - return self._play_step_index < len(self._play_list) + return self._play_step < self._get_play_step_limit() if self._last_playback_stream_name: return bool(self._create_play_label_list(self._last_playback_stream_name)) return False def _request_auto_playback_if_needed(self, target_flags: Optional[int] = None): - _target_flags = self._flags.target_flags if target_flags is None else target_flags - if _target_flags & SDS_FLAG_MASK_START: + with self._manager_lock: + _target_flags = self._flags.target_flags if target_flags is None else target_flags + if _target_flags & SDS_FLAG_MASK_START: + return + if self.opened_streams: + return + if self._has_next_auto_playback_step(): + self._flags.request_auto_playback_start() + elif self._flags.auto_playback and self._last_playback_stream_name: + if self._flags.request_auto_playback_terminate(): + _complete_msg = "Playback complete - no more steps remaining." if self._play_list else "Playback complete." + logger.info(_complete_msg) + self._request_exit_after_playback("playback complete") + + def _request_exit_after_playback(self, _reason: str): + if not self._exit_after_playback: return - if self.opened_streams: - return - if self._has_next_auto_playback_step(): - self._flags.request_auto_playback_start() - elif self._flags.auto_playback and self._last_playback_stream_name: - if self._flags.request_auto_playback_terminate(): - logger.info("Playback complete - no more steps remaining.") - + logger.info(f"SDSIO-Server terminating ({_reason}).") + self._send_ci_terminate_on_shutdown = True + self.shutdown_requested.set() + if self._loop and self._main_task: + self._loop.call_soon_threadsafe(self._main_task.cancel) def _open(self, mode, name): + with self._manager_lock: + return self._open_locked(mode, name) + + def _open_locked(self, mode, name): _cmd = CMD_OPEN # prepare error response _resp_err = bytearray() @@ -523,12 +600,13 @@ def _open(self, mode, name): if self._playback_mode: if not self._label_list: # Get flags, Set working dir + _index_based_playback = False if self._play_list: - if self._play_step_index < len(self._play_list): - _step = self._play_list[self._play_step_index] + if self._play_step < self._get_play_step_limit(): + _step = self._play_list[self._play_step] _step_desc = _step.get('step', '') _desc_suffix = f": {_step_desc}" if _step_desc else "" - logger.info(f"Playback step {self._play_step_index + 1}/{len(self._play_list)}{_desc_suffix}.") + logger.info(f"Playback step {self._play_step}{_desc_suffix}.") _set_flags = _step.get('setflags', 0) _clear_flags = _step.get('clearflags', 0) _recdir = _step.get('recdir', None) @@ -538,10 +616,13 @@ def _open(self, mode, name): self._rec_dir = self._work_dir else: logger.error(f"Open Failed. End of playlist. No more steps available for playback stream '{name}'.") + if self._exit_after_playback and self._flags.request_auto_playback_terminate(_force=True): + self._request_exit_after_playback("playback data unavailable") return _resp_err else: _set_flags = 0 _clear_flags = 0 + _index_based_playback = True if _set_flags or _clear_flags: logger.debug(f"Applying flags for playback stream '{name}': set=0x{_set_flags:08X}, clear=0x{_clear_flags:08X}.") @@ -550,12 +631,16 @@ def _open(self, mode, name): # Create label list _play_label_list = self._create_play_label_list(name) if not _play_label_list: - if not self._play_list and self._play_step_index > 0: + if not self._play_list and self._play_step > 0: logger.error(f"Open Failed. No more files available for playback stream '{name}'.") else: logger.error(f"Open Failed. No files found for playback stream '{name}'.") + if self._exit_after_playback and self._flags.request_auto_playback_terminate(_force=True): + self._request_exit_after_playback("playback data unavailable") return _resp_err self._label_list = _play_label_list + if _index_based_playback and self._play_step == 0: + logger.info("No play steps, index based playback started.") else: if mode == 0: @@ -614,6 +699,8 @@ def _open(self, mode, name): for _sds_file_path in _file_paths: if not path.exists(_sds_file_path): logger.error(f"Missing file for playback stream '{name}': {self._format_path(_sds_file_path)}") + if self._exit_after_playback and self._flags.request_auto_playback_terminate(_force=True): + self._request_exit_after_playback("playback data unavailable") return _resp_err if not self._timestamp_boundaries: @@ -657,6 +744,10 @@ def _open(self, mode, name): return _resp def _close(self, sid): + with self._manager_lock: + return self._close_locked(sid) + + def _close_locked(self, sid): _resp = bytearray() _stream = self.opened_streams[sid] _name = _stream.name @@ -699,7 +790,7 @@ def _close(self, sid): if not self.opened_streams: if self._playback_mode: - self._play_step_index += 1 + self._play_step += 1 self._label_list.clear() self._timestamp_boundaries.clear() self._request_auto_playback_if_needed() @@ -809,7 +900,7 @@ def _info(self, flags: int, idle_rate: int, err_data: bytes): logger.info(f"{idle_rate}% idle.") self._info_IdleRate = idle_rate if err_data: - _status = int.from_bytes(err_data[0:4],'little') + _status = int.from_bytes(err_data[0:4], 'little', signed=True) _line = int.from_bytes(err_data[4:8],'little') _err_mgs = err_data[8:] if _status == 0: @@ -850,9 +941,14 @@ def get_async_response(self): def get_shutdown_flags(self): _resp = bytearray() _cmd = CMD_FLAGS + if self._send_ci_terminate_on_shutdown: + _set_mask = SDS_FLAG_MASK_CI_TERMINATE + else: + _set_mask = 0 + _clear_mask = SDS_FLAG_MASK_ALIVE _resp.extend(_cmd.to_bytes(4,'little')) - _resp.extend((0).to_bytes(4,'little')) - _resp.extend((1 << 28).to_bytes(4,'little')) + _resp.extend(_set_mask.to_bytes(4,'little')) + _resp.extend(_clear_mask.to_bytes(4,'little')) _resp.extend((0).to_bytes(4,'little')) return _resp diff --git a/ST/B-U585I-IOT02A/MotionRecognition/README.md b/ST/B-U585I-IOT02A/MotionRecognition/README.md index d40c1ec..5670721 100644 --- a/ST/B-U585I-IOT02A/MotionRecognition/README.md +++ b/ST/B-U585I-IOT02A/MotionRecognition/README.md @@ -118,7 +118,7 @@ To perform a recording, follow these steps: ```txt >sdsio-server usb -SDSIO-Server v3.0.0 +SDSIO-Server v3.1.0 Press 'Ctrl+C' or 'X' to exit. Working directory: ...\Arm-Examples\SDS-Examples\ST\B-U585I-IOT02A\MotionRecognition SDSIO command input: R=Record, P=playback, S/s=stop, T/t=reset, X/x=exit, A-H=set flags 0-7, a-h=clear flags 0-7. @@ -183,7 +183,7 @@ The SDS file `Test_Out..p.sds` created during playback should be identical to ```txt >sdsio-server usb -SDSIO-Server v3.0.0 +SDSIO-Server v3.1.0 Press 'Ctrl+C' or 'X' to exit. Working directory: ...\Arm-Examples\SDS-Examples\ST\B-U585I-IOT02A\MotionRecognition SDSIO command input: R=Record, P=playback, S/s=stop, T/t=reset, X/x=exit, A-H=set flags 0-7, a-h=clear flags 0-7. @@ -286,7 +286,7 @@ To perform a recording, follow these steps: ```txt >sdsio-server usb -SDSIO-Server v3.0.0 +SDSIO-Server v3.1.0 Press 'Ctrl+C' or 'X' to exit. Working directory: ...\Arm-Examples\SDS-Examples\ST\B-U585I-IOT02A\MotionRecognition SDSIO command input: R=Record, P=playback, S/s=stop, T/t=reset, X/x=exit, A-H=set flags 0-7, a-h=clear flags 0-7. @@ -349,7 +349,7 @@ The SDS file `ML_Out..p.sds` created during playback should be identical to t ```txt >sdsio-server usb -SDSIO-Server v3.0.0 +SDSIO-Server v3.1.0 Press 'Ctrl+C' or 'X' to exit. Working directory: ...\Arm-Examples\SDS-Examples\ST\B-U585I-IOT02A\MotionRecognition SDSIO command input: R=Record, P=playback, S/s=stop, T/t=reset, X/x=exit, A-H=set flags 0-7, a-h=clear flags 0-7. @@ -455,7 +455,7 @@ Anomaly prediction: -0.180526 ```txt Created by ...\Arm-Examples\SDS-Examples\ST\B-U585I-IOT02A\MotionRecognition\Board\Corstone-300\vsi\python\arm_vsi3.py -SDSIO VSI version 3.0.0 +SDSIO VSI version 3.1.0 SDSIO_FVP environment variable not set. Working directory: ...\Arm-Examples\SDS-Examples\ST\B-U585I-IOT02A\MotionRecognition\algorithm\SDS Recordings. SDSIO configuration YAML: ...\Arm-Examples\SDS-Examples\ST\B-U585I-IOT02A\MotionRecognition\algorithm.sdsio.yml. diff --git a/ST/B-U585I-IOT02A/MotionRecognition/SDS.csolution.yml b/ST/B-U585I-IOT02A/MotionRecognition/SDS.csolution.yml index a500dce..f69536c 100644 --- a/ST/B-U585I-IOT02A/MotionRecognition/SDS.csolution.yml +++ b/ST/B-U585I-IOT02A/MotionRecognition/SDS.csolution.yml @@ -15,7 +15,7 @@ solution: # Refer to https://open-cmsis-pack.github.io/cmsis-toolbox/ReferenceApplications/ for more information packs: - - pack: ARM::SDS@^3.0.0-0 + - pack: ARM::SDS@^3.1.0-0 - pack: Keil::B-U585I-IOT02A_BSP - pack: Keil::STM32U5xx_DFP - pack: ARM::V2M_MPS3_SSE_300_BSP@^1.5.0 diff --git a/ST/B-U585I-IOT02A/MotionRecognition/algorithm/SDS Recordings/ML_In.sds.yml b/ST/B-U585I-IOT02A/MotionRecognition/algorithm/SDS Recordings/ML_In.sds.yml index 87dc445..6a09908 100644 --- a/ST/B-U585I-IOT02A/MotionRecognition/algorithm/SDS Recordings/ML_In.sds.yml +++ b/ST/B-U585I-IOT02A/MotionRecognition/algorithm/SDS Recordings/ML_In.sds.yml @@ -1,17 +1,17 @@ sds: - name: ML Model Data Input - description: Accelerometer with 3 axes (sampling at 52.0Hz) - frequency: 52.0 + name: ML input + description: Conditioned 3 axes accelerometer data (sampled at 52.0Hz) + sample-frequency: 52.0 content: - - value: x - type: float - scale: 0.1 - unit: G - - value: y - type: float - scale: 0.1 - unit: G - - value: z - type: float - scale: 0.1 - unit: G + - value: x + type: float + scale: 0.1 + unit: G + - value: y + type: float + scale: 0.1 + unit: G + - value: z + type: float + scale: 0.1 + unit: G diff --git a/ST/B-U585I-IOT02A/MotionRecognition/algorithm/SDS Recordings/ML_Out.sds.yml b/ST/B-U585I-IOT02A/MotionRecognition/algorithm/SDS Recordings/ML_Out.sds.yml index d1fe1cc..022e7a5 100644 --- a/ST/B-U585I-IOT02A/MotionRecognition/algorithm/SDS Recordings/ML_Out.sds.yml +++ b/ST/B-U585I-IOT02A/MotionRecognition/algorithm/SDS Recordings/ML_Out.sds.yml @@ -1,13 +1,13 @@ sds: - name: ML Model Data Output + name: ML output description: Classification results - frequency: 0.417 + sample-frequency: 0.417 content: - - value: idle - type: float - - value: snake - type: float - - value: updown - type: float - - value: wave - type: float + - value: idle + type: float + - value: snake + type: float + - value: updown + type: float + - value: wave + type: float diff --git a/ST/B-U585I-IOT02A/MotionRecognition/algorithm/sds_control.c b/ST/B-U585I-IOT02A/MotionRecognition/algorithm/sds_control.c index 6a11479..60ec0fb 100644 --- a/ST/B-U585I-IOT02A/MotionRecognition/algorithm/sds_control.c +++ b/ST/B-U585I-IOT02A/MotionRecognition/algorithm/sds_control.c @@ -34,7 +34,8 @@ osThreadAttr_t attrAlgorithmThread = { // sdsControlThread thread attributes osThreadAttr_t attr_sdsControlThread = { - .name = "sdsControl" + .name = "sdsControl", + .priority = osPriorityNormal1 }; // Idle time counter diff --git a/ST/B-U585I-IOT02A/MotionRecognition/algorithm/sds_main.c b/ST/B-U585I-IOT02A/MotionRecognition/algorithm/sds_main.c index df1855d..50ac5fc 100644 --- a/ST/B-U585I-IOT02A/MotionRecognition/algorithm/sds_main.c +++ b/ST/B-U585I-IOT02A/MotionRecognition/algorithm/sds_main.c @@ -164,7 +164,7 @@ __NO_RETURN void AlgorithmThread (void *argument) { do { ret = sdsRead(sds_data_in_id, ×lot, algo_data_in_buf, sizeof(algo_data_in_buf)); if (ret == SDS_NO_DATA) { - osDelay(10U); + osDelay(1U); DiscardInputData(); } } while (ret == SDS_NO_DATA); @@ -190,7 +190,7 @@ __NO_RETURN void AlgorithmThread (void *argument) { do { ret = sdsWrite(sds_data_in_id, timeslot, algo_data_in_buf, sizeof(algo_data_in_buf)); if (ret == SDS_NO_SPACE) { - osDelay(10U); + osDelay(1U); } } while (ret == SDS_NO_SPACE); SDS_ASSERT(ret == sizeof(algo_data_in_buf)); @@ -208,7 +208,7 @@ __NO_RETURN void AlgorithmThread (void *argument) { do { ret = sdsWrite(sds_data_out_id, timeslot, algo_data_out_buf, sizeof(algo_data_out_buf)); if (ret == SDS_NO_SPACE) { - osDelay(10U); + osDelay(1U); } } while (ret == SDS_NO_SPACE); SDS_ASSERT(ret == sizeof(algo_data_out_buf)); diff --git a/ST/B-U585I-IOT02A/MotionRecognition/datatest/SDS Recordings/Test_In.sds.yml b/ST/B-U585I-IOT02A/MotionRecognition/datatest/SDS Recordings/Test_In.sds.yml index 6909a4e..be56e4c 100644 --- a/ST/B-U585I-IOT02A/MotionRecognition/datatest/SDS Recordings/Test_In.sds.yml +++ b/ST/B-U585I-IOT02A/MotionRecognition/datatest/SDS Recordings/Test_In.sds.yml @@ -1,17 +1,17 @@ sds: name: Test Input Data description: Generated accelerometer samples at 16600 Hz - frequency: 16600 + sample-frequency: 16600 content: - - value: x - type: uint16_t - scale: 0.001 - unit: G - - value: y - type: uint16_t - scale: 0.001 - unit: G - - value: z - type: uint16_t - scale: 0.001 - unit: G + - value: x + type: uint16_t + scale: 0.001 + unit: G + - value: y + type: uint16_t + scale: 0.001 + unit: G + - value: z + type: uint16_t + scale: 0.001 + unit: G diff --git a/ST/B-U585I-IOT02A/MotionRecognition/datatest/SDS Recordings/Test_Out.sds.yml b/ST/B-U585I-IOT02A/MotionRecognition/datatest/SDS Recordings/Test_Out.sds.yml index 2727caa..f1a085b 100644 --- a/ST/B-U585I-IOT02A/MotionRecognition/datatest/SDS Recordings/Test_Out.sds.yml +++ b/ST/B-U585I-IOT02A/MotionRecognition/datatest/SDS Recordings/Test_Out.sds.yml @@ -1,9 +1,9 @@ sds: name: Test Output Data description: Results of algorithm processing - frequency: 1000 + sample-frequency: 1000 content: - - value: x - type: uint16_t - - value: y - type: uint16_t + - value: x + type: uint16_t + - value: y + type: uint16_t diff --git a/ST/B-U585I-IOT02A/MotionRecognition/datatest/sds_control.c b/ST/B-U585I-IOT02A/MotionRecognition/datatest/sds_control.c index 6a11479..60ec0fb 100644 --- a/ST/B-U585I-IOT02A/MotionRecognition/datatest/sds_control.c +++ b/ST/B-U585I-IOT02A/MotionRecognition/datatest/sds_control.c @@ -34,7 +34,8 @@ osThreadAttr_t attrAlgorithmThread = { // sdsControlThread thread attributes osThreadAttr_t attr_sdsControlThread = { - .name = "sdsControl" + .name = "sdsControl", + .priority = osPriorityNormal1 }; // Idle time counter diff --git a/ST/B-U585I-IOT02A/MotionRecognition/datatest/sds_main.c b/ST/B-U585I-IOT02A/MotionRecognition/datatest/sds_main.c index b2e38c1..32de570 100644 --- a/ST/B-U585I-IOT02A/MotionRecognition/datatest/sds_main.c +++ b/ST/B-U585I-IOT02A/MotionRecognition/datatest/sds_main.c @@ -164,7 +164,7 @@ __NO_RETURN void AlgorithmThread (void *argument) { do { ret = sdsRead(sds_data_in_id, ×lot, algo_data_in_buf, sizeof(algo_data_in_buf)); if (ret == SDS_NO_DATA) { - osDelay(10U); + osDelay(1U); DiscardInputData(); } } while (ret == SDS_NO_DATA); @@ -190,7 +190,7 @@ __NO_RETURN void AlgorithmThread (void *argument) { do { ret = sdsWrite(sds_data_in_id, timeslot, algo_data_in_buf, sizeof(algo_data_in_buf)); if (ret == SDS_NO_SPACE) { - osDelay(10U); + osDelay(1U); } } while (ret == SDS_NO_SPACE); SDS_ASSERT(ret == sizeof(algo_data_in_buf)); @@ -208,7 +208,7 @@ __NO_RETURN void AlgorithmThread (void *argument) { do { ret = sdsWrite(sds_data_out_id, timeslot, algo_data_out_buf, sizeof(algo_data_out_buf)); if (ret == SDS_NO_SPACE) { - osDelay(10U); + osDelay(1U); } } while (ret == SDS_NO_SPACE); SDS_ASSERT(ret == sizeof(algo_data_out_buf)); diff --git a/ST/B-U585I-IOT02A/MotionRecognition/sdsio/fvp/RTE/SDS/sds_config.h b/ST/B-U585I-IOT02A/MotionRecognition/sdsio/fvp/RTE/SDS/sds_config.h index 8d0b50c..ecd50a5 100644 --- a/ST/B-U585I-IOT02A/MotionRecognition/sdsio/fvp/RTE/SDS/sds_config.h +++ b/ST/B-U585I-IOT02A/MotionRecognition/sdsio/fvp/RTE/SDS/sds_config.h @@ -17,7 +17,7 @@ * * Name: sds_config.h * Purpose: SDS configuration options - * Rev.: V3.0.0 + * Rev.: V3.1.0 */ //-------- <<< Use Configuration Wizard in Context Menu >>> -------------------- @@ -40,7 +40,7 @@ #define SDS_THREAD_STACK_SIZE 1024 // SDS system thread priority -#define SDS_THREAD_PRIORITY osPriorityNormal +#define SDS_THREAD_PRIORITY osPriorityNormal1 // SDS stream open timeout in kernel ticks #define SDS_OPEN_TIMEOUT 3000U diff --git a/ST/B-U585I-IOT02A/MotionRecognition/sdsio/fvp/RTE/SDS/sds_config.h.base@3.0.0 b/ST/B-U585I-IOT02A/MotionRecognition/sdsio/fvp/RTE/SDS/sds_config.h.base@3.0.0 deleted file mode 100644 index 8d0b50c..0000000 --- a/ST/B-U585I-IOT02A/MotionRecognition/sdsio/fvp/RTE/SDS/sds_config.h.base@3.0.0 +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2025-2026 Arm Limited. All rights reserved. - * - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the License); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an AS IS BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Name: sds_config.h - * Purpose: SDS configuration options - * Rev.: V3.0.0 - */ - -//-------- <<< Use Configuration Wizard in Context Menu >>> -------------------- - -// SDS System Configuration - -// Maximum concurrent streams <1-31> -// Default: 16 -#define SDS_MAX_STREAMS 16U - -// Internal buffer size for I/O transfers -// Default: 8192 -#define SDS_BUF_SIZE 8192U - -// - -//------------- <<< end of configuration section >>> --------------------------- - -// SDS system thread stack size -#define SDS_THREAD_STACK_SIZE 1024 - -// SDS system thread priority -#define SDS_THREAD_PRIORITY osPriorityNormal - -// SDS stream open timeout in kernel ticks -#define SDS_OPEN_TIMEOUT 3000U - -// SDS stream close timeout in kernel ticks -#define SDS_CLOSE_TIMEOUT 3000U - -// Optimal I/O transfer size (read/write) -// Default: 8192 -// Select a value appropriate for the underlying I/O interface (e.g., socket, USART, VCOM, file system) -// to ensure efficient read/write performance -#define SDS_IO_TRANSFER_SIZE 8192U diff --git a/ST/B-U585I-IOT02A/MotionRecognition/sdsio/fvp/RTE/SDS/sds_config.h.base@3.1.0 b/ST/B-U585I-IOT02A/MotionRecognition/sdsio/fvp/RTE/SDS/sds_config.h.base@3.1.0 new file mode 100644 index 0000000..ecd50a5 --- /dev/null +++ b/ST/B-U585I-IOT02A/MotionRecognition/sdsio/fvp/RTE/SDS/sds_config.h.base@3.1.0 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2025-2026 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Name: sds_config.h + * Purpose: SDS configuration options + * Rev.: V3.1.0 + */ + +//-------- <<< Use Configuration Wizard in Context Menu >>> -------------------- + +// SDS System Configuration + +// Maximum concurrent streams <1-31> +// Default: 16 +#define SDS_MAX_STREAMS 16U + +// Internal buffer size for I/O transfers +// Default: 8192 +#define SDS_BUF_SIZE 8192U + +// + +//------------- <<< end of configuration section >>> --------------------------- + +// SDS system thread stack size +#define SDS_THREAD_STACK_SIZE 1024 + +// SDS system thread priority +#define SDS_THREAD_PRIORITY osPriorityNormal1 + +// SDS stream open timeout in kernel ticks +#define SDS_OPEN_TIMEOUT 3000U + +// SDS stream close timeout in kernel ticks +#define SDS_CLOSE_TIMEOUT 3000U + +// Optimal I/O transfer size (read/write) +// Default: 8192 +// Select a value appropriate for the underlying I/O interface (e.g., socket, USART, VCOM, file system) +// to ensure efficient read/write performance +#define SDS_IO_TRANSFER_SIZE 8192U diff --git a/ST/B-U585I-IOT02A/MotionRecognition/sdsio/fvp/sdsio_fvp.clayer.yml b/ST/B-U585I-IOT02A/MotionRecognition/sdsio/fvp/sdsio_fvp.clayer.yml index f4634a7..5c2f55a 100644 --- a/ST/B-U585I-IOT02A/MotionRecognition/sdsio/fvp/sdsio_fvp.clayer.yml +++ b/ST/B-U585I-IOT02A/MotionRecognition/sdsio/fvp/sdsio_fvp.clayer.yml @@ -3,7 +3,7 @@ layer: description: Layer with SDS and SDSIO using VSI packs: - - pack: ARM::SDS@^3.0.0-0 + - pack: ARM::SDS@^3.1.0-0 connections: - connect: SDS diff --git a/ST/B-U585I-IOT02A/MotionRecognition/sdsio/usb/RTE/SDS/sds_config.h b/ST/B-U585I-IOT02A/MotionRecognition/sdsio/usb/RTE/SDS/sds_config.h index 8d0b50c..ecd50a5 100644 --- a/ST/B-U585I-IOT02A/MotionRecognition/sdsio/usb/RTE/SDS/sds_config.h +++ b/ST/B-U585I-IOT02A/MotionRecognition/sdsio/usb/RTE/SDS/sds_config.h @@ -17,7 +17,7 @@ * * Name: sds_config.h * Purpose: SDS configuration options - * Rev.: V3.0.0 + * Rev.: V3.1.0 */ //-------- <<< Use Configuration Wizard in Context Menu >>> -------------------- @@ -40,7 +40,7 @@ #define SDS_THREAD_STACK_SIZE 1024 // SDS system thread priority -#define SDS_THREAD_PRIORITY osPriorityNormal +#define SDS_THREAD_PRIORITY osPriorityNormal1 // SDS stream open timeout in kernel ticks #define SDS_OPEN_TIMEOUT 3000U diff --git a/ST/B-U585I-IOT02A/MotionRecognition/sdsio/usb/RTE/SDS/sds_config.h.base@3.0.0 b/ST/B-U585I-IOT02A/MotionRecognition/sdsio/usb/RTE/SDS/sds_config.h.base@3.0.0 deleted file mode 100644 index 8d0b50c..0000000 --- a/ST/B-U585I-IOT02A/MotionRecognition/sdsio/usb/RTE/SDS/sds_config.h.base@3.0.0 +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2025-2026 Arm Limited. All rights reserved. - * - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the License); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an AS IS BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Name: sds_config.h - * Purpose: SDS configuration options - * Rev.: V3.0.0 - */ - -//-------- <<< Use Configuration Wizard in Context Menu >>> -------------------- - -// SDS System Configuration - -// Maximum concurrent streams <1-31> -// Default: 16 -#define SDS_MAX_STREAMS 16U - -// Internal buffer size for I/O transfers -// Default: 8192 -#define SDS_BUF_SIZE 8192U - -// - -//------------- <<< end of configuration section >>> --------------------------- - -// SDS system thread stack size -#define SDS_THREAD_STACK_SIZE 1024 - -// SDS system thread priority -#define SDS_THREAD_PRIORITY osPriorityNormal - -// SDS stream open timeout in kernel ticks -#define SDS_OPEN_TIMEOUT 3000U - -// SDS stream close timeout in kernel ticks -#define SDS_CLOSE_TIMEOUT 3000U - -// Optimal I/O transfer size (read/write) -// Default: 8192 -// Select a value appropriate for the underlying I/O interface (e.g., socket, USART, VCOM, file system) -// to ensure efficient read/write performance -#define SDS_IO_TRANSFER_SIZE 8192U diff --git a/ST/B-U585I-IOT02A/MotionRecognition/sdsio/usb/RTE/SDS/sds_config.h.base@3.1.0 b/ST/B-U585I-IOT02A/MotionRecognition/sdsio/usb/RTE/SDS/sds_config.h.base@3.1.0 new file mode 100644 index 0000000..ecd50a5 --- /dev/null +++ b/ST/B-U585I-IOT02A/MotionRecognition/sdsio/usb/RTE/SDS/sds_config.h.base@3.1.0 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2025-2026 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Name: sds_config.h + * Purpose: SDS configuration options + * Rev.: V3.1.0 + */ + +//-------- <<< Use Configuration Wizard in Context Menu >>> -------------------- + +// SDS System Configuration + +// Maximum concurrent streams <1-31> +// Default: 16 +#define SDS_MAX_STREAMS 16U + +// Internal buffer size for I/O transfers +// Default: 8192 +#define SDS_BUF_SIZE 8192U + +// + +//------------- <<< end of configuration section >>> --------------------------- + +// SDS system thread stack size +#define SDS_THREAD_STACK_SIZE 1024 + +// SDS system thread priority +#define SDS_THREAD_PRIORITY osPriorityNormal1 + +// SDS stream open timeout in kernel ticks +#define SDS_OPEN_TIMEOUT 3000U + +// SDS stream close timeout in kernel ticks +#define SDS_CLOSE_TIMEOUT 3000U + +// Optimal I/O transfer size (read/write) +// Default: 8192 +// Select a value appropriate for the underlying I/O interface (e.g., socket, USART, VCOM, file system) +// to ensure efficient read/write performance +#define SDS_IO_TRANSFER_SIZE 8192U diff --git a/ST/B-U585I-IOT02A/MotionRecognition/sdsio/usb/sdsio_usb.clayer.yml b/ST/B-U585I-IOT02A/MotionRecognition/sdsio/usb/sdsio_usb.clayer.yml index 23e8729..5b36837 100644 --- a/ST/B-U585I-IOT02A/MotionRecognition/sdsio/usb/sdsio_usb.clayer.yml +++ b/ST/B-U585I-IOT02A/MotionRecognition/sdsio/usb/sdsio_usb.clayer.yml @@ -3,7 +3,7 @@ layer: description: Layer with SDS and SDSIO using USB interface to the SDSIO-Server packs: - - pack: ARM::SDS@^3.0.0-0 + - pack: ARM::SDS@^3.1.0-0 - pack: Keil::MDK-Middleware@^8.0.0 connections: