diff --git a/.gitignore b/.gitignore index 9437b76c..ecb31737 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,5 @@ Thumbs.db *.mov *.wmv +# VS Code IDE +.vscode/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..4cb07366 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "wasm-micro-runtime"] + path = wasm-micro-runtime + url = https://github.com/bytecodealliance/wasm-micro-runtime.git diff --git a/CMakeLists.txt b/CMakeLists.txt index b1a9c34d..269a731f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,94 +1,52 @@ cmake_minimum_required(VERSION 3.20.0) -find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +# Allow user to set CMAKE_SYSTEM_NAME via -DCMAKE_SYSTEM_NAME=Zephyr or Linux +if(NOT DEFINED TARGET_PLATFORM_NAME) + set(TARGET_PLATFORM_NAME "Zephyr") +endif() -project(ocre VERSION ${APP_VERSION_MAJOR}.${APP_VERSION_MINOR}.${APP_PATCHLEVEL}.${APP_VERSION_TWEAK} LANGUAGES C ASM) +if(TARGET_PLATFORM_NAME STREQUAL "Linux") + project(ocre LANGUAGES C ASM) +elseif(TARGET_PLATFORM_NAME STREQUAL "Zephyr") + find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + project(ocre VERSION ${APP_VERSION_MAJOR}.${APP_VERSION_MINOR}.${APP_PATCHLEVEL}.${APP_VERSION_TWEAK} LANGUAGES C ASM) +else() + message(FATAL_ERROR "Unsupported TARGET_PLATFORM_NAME: ${TARGET_PLATFORM_NAME}") +endif() +# ----------- COMMON SECTION ----------- set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_C_STANDARD 99) +set(CMAKE_CXX_STANDARD 17) +# Version and build info (common) execute_process( - COMMAND git describe --long --dirty --always - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - OUTPUT_VARIABLE RAW_GIT_VERSION - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - + COMMAND git describe --long --dirty --always + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE RAW_GIT_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE +) string(REPLACE "-dirty" "+" BUILD_INFO ${RAW_GIT_VERSION}) - string(TIMESTAMP BUILD_DATE "%d-%m-%Y, %H:%M" UTC ) - cmake_host_system_information(RESULT BUILD_MACHINE QUERY HOSTNAME) - string(PREPEND BUILD_INFO dev-) - string(REPLACE "-" ";" GIT_INFO_LIST ${RAW_GIT_VERSION}) list(GET GIT_INFO_LIST 0 VERSION_NUMBER) -message("VERSION NUMBER: ${VERSION_NUMBER}") -zephyr_compile_options(-DVERSION_INFO="${VERSION_NUMBER}") -message("BUILD DATE: ${BUILD_DATE}") -zephyr_compile_options(-DVERSION_BUILD_DATE="${BUILD_DATE}") -message("BUILD MACHINE: ${BUILD_MACHINE}") -zephyr_compile_options(-DVERSION_BUILD_MACHINE="${BUILD_MACHINE}") -message("BUILD_INFO: ${BUILD_INFO}") -zephyr_compile_options(-DVERSION_BUILD_INFO="${BUILD_INFO}") - -# Determine the ISA of the target and set appropriately -if (DEFINED CONFIG_ISA_THUMB2) -set (TARGET_ISA THUMB) -elseif (DEFINED CONFIG_ISA_ARM) -set (TARGET_ISA ARM) -elseif (DEFINED CONFIG_X86) -set (TARGET_ISA X86_32) -elseif (DEFINED CONFIG_XTENSA) -set (TARGET_ISA XTENSA) -elseif (DEFINED CONFIG_RISCV) -set (TARGET_ISA RISCV32) -elseif (DEFINED CONFIG_ARCH_POSIX) -# Technically, this is not correct as the CPU architecture is not set. This assumes POSIX is x86 32-bit -set (TARGET_ISA X86_32) -else () -message (FATAL_ERROR "Unsupported ISA: ${CONFIG_ARCH}") -endif () -message("TARGET ISA: ${TARGET_ISA}") - -add_compile_options(-O0 -Wno-unknown-attributes) - - -################## -# WAMR Options # -################## -set (WAMR_BUILD_PLATFORM "zephyr") -set (WAMR_BUILD_TARGET ${TARGET_ISA}) -set (WAMR_BUILD_INTERP 1) -set (WAMR_BUILD_FAST_INTERP 0) -set (WAMR_BUILD_AOT 0) -set (WAMR_BUILD_JIT 0) -set (WAMR_BUILD_LIBC_BUILTIN 0) -set (WAMR_BUILD_LIBC_WASI 1) -set (WAMR_BUILD_LIB_PTHREAD 1) -set (WAMR_BUILD_REF_TYPES 1) -set (WASM_ENABLE_LOG 1) - -# Override the global heap usage -if (NOT DEFINED WAMR_BUILD_GLOBAL_HEAP_POOL) - set (WAMR_BUILD_GLOBAL_HEAP_POOL 1) -endif () - -# Override the global heap size for small devices -if (NOT DEFINED WAMR_BUILD_GLOBAL_HEAP_SIZE) - #set (WAMR_BUILD_GLOBAL_HEAP_SIZE 131072) # 128 KB - set (WAMR_BUILD_GLOBAL_HEAP_SIZE 32767) # 32 KB -endif () - -# Include WAMR build script -set (WAMR_ROOT_DIR ${ZEPHYR_WASM_MICRO_RUNTIME_MODULE_DIR}) -include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) -# Generate the messages header file +# Message generation (common) set(OCRE_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}) set(MSG_INPUT_FILES ${OCRE_ROOT_DIR}/src/ocre/components/container_supervisor/component_supervisor.yaml ) set(MSG_GENERATED_FILE ${CMAKE_CURRENT_LIST_DIR}/src/messaging/messages.g) +# Zephyr toolchain usually provides PYTHON_EXECUTABLE +if(NOT DEFINED PYTHON_EXECUTABLE) + find_package(Python3 QUIET REQUIRED Interpreter Development) + if(NOT Python3_FOUND) + message(FATAL_ERROR "Python3 interpreter not found. Please install Python3 or set PYTHON_EXECUTABLE to the path of your Python interpreter.") + endif() + set(PYTHON_EXECUTABLE ${Python3_EXECUTABLE}) +endif() + add_custom_command( OUTPUT ${MSG_GENERATED_FILE} COMMAND ${PYTHON_EXECUTABLE} ${OCRE_ROOT_DIR}/tools/automsg ${MSG_INPUT_FILES} ${MSG_GENERATED_FILE} @@ -97,53 +55,22 @@ add_custom_command( ) add_custom_target(generate_messages DEPENDS ${MSG_GENERATED_FILE}) -zephyr_include_directories( - src/ - src/ocre -) - -set(lib_sources - # Libraries - src/ocre/sm/sm.c - src/ocre/fs/fs.c - src/ocre/ocre_timers/ocre_timer.c - src/ocre/container_healthcheck/ocre_container_healthcheck.c - - # Ocre APIs - src/ocre/api/ocre_api.c -) - -# Compile in sensors framework if enabled. -if(CONFIG_OCRE_SENSORS) - set(ocre_sources ${ocre_sources} ${OCRE_ROOT_DIR}/src/ocre/ocre_sensors/ocre_sensors.c) -endif() +if(NOT "${OCRE_INPUT_FILE}" STREQUAL "") + message("Using input file: ${OCRE_INPUT_FILE}") + add_custom_command( + OUTPUT ${CMAKE_CURRENT_LIST_DIR}/src/ocre/ocre_input_file.g + COMMAND xxd -n wasm_binary -i ${OCRE_INPUT_FILE} > ${CMAKE_CURRENT_LIST_DIR}/src/ocre/ocre_input_file.g + DEPENDS ${OCRE_INPUT_FILE} + COMMENT "Generating C header from ${OCRE_INPUT_FILE}" + ) -# Compile random sensor if enabled. -if(CONFIG_RNG_SENSOR) - set(ocre_sources ${ocre_sources} ${OCRE_ROOT_DIR}/src/ocre/ocre_sensors/rng_sensor.c) -endif() + add_definitions(-DHAS_GENERATED_INPUT) -if(DEFINED CONFIG_OCRE_GPIO) - set(lib_sources ${lib_sources} - src/ocre/ocre_gpio/ocre_gpio.c - ) + add_custom_target(generate_ocre_file DEPENDS ${CMAKE_CURRENT_LIST_DIR}/src/ocre/ocre_input_file.g) endif() -# Compile container messaging if enabled. -if(CONFIG_OCRE_CONTAINER_MESSAGING) - set(lib_sources ${lib_sources} src/ocre/container_messaging/messaging.c) +if(TARGET_PLATFORM_NAME STREQUAL "Linux") + include(${CMAKE_CURRENT_LIST_DIR}/src/shared/platform/posix/ocre_internal.cmake) +elseif(TARGET_PLATFORM_NAME STREQUAL "Zephyr") + include(${CMAKE_CURRENT_LIST_DIR}/src/shared/platform/zephyr/ocre_internal.cmake) endif() - -set(component_sources - # Component support - src/ocre/component/component.c - - # Components - src/ocre/ocre_container_runtime/ocre_container_runtime.c - src/ocre/components/container_supervisor/cs_main.c - src/ocre/components/container_supervisor/cs_sm.c - src/ocre/components/container_supervisor/cs_sm_impl.c -) - -target_sources(app PRIVATE ${WAMR_RUNTIME_LIB_SOURCE} ${lib_sources} ${component_sources} src/main.c) -add_dependencies(app generate_messages) diff --git a/Kconfig b/Kconfig index a38b82d5..1e282c82 100644 --- a/Kconfig +++ b/Kconfig @@ -115,6 +115,12 @@ config OCRE_CONTAINER_DEFAULT_STACK_SIZE help The default value used for a container's stack size. +config MAX_CONTAINERS + int "Maximum concurrent containers" + default 10 + help + The default value for maximum number of container's. + config MAX_TIMERS int "Maximum number of timers" default 5 @@ -146,6 +152,14 @@ config OCRE_GPIO help Enable the OCRE GPIO driver that provides a portable API layer for GPIO operations across different hardware platforms. + + +config OCRE_TIMER + bool "OCRE Timer Driver" + default y + help + Enable the OCRE Timer driver that provides a portable API layer + for Timer operations across different hardware platforms. config OCRE_GPIO_MAX_PINS int "Maximum number of GPIO pins" @@ -179,4 +193,17 @@ config MESSAGING_MAX_SUBSCRIPTIONS help Number of maximum subscriptions for Container Messaging +config OCRE_SHELL + bool "Enable OCRE Shell" + default y + help + Enable the OCRE Shell for dynamic configuration management. + +config IMU_SENSOR + bool "IMU Sensor" + default n + depends on OCRE_SENSORS + help + Enable support for the custom IMU sensor. + endmenu diff --git a/README.md b/README.md index cffaf36c..133e2c46 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ ![Ocre logo](ocre_logo.jpg "Ocre") + # Ocre + [![Build](https://github.com/project-ocre/ocre-runtime/actions/workflows/build.yml/badge.svg)](https://github.com/project-ocre/ocre-runtime/actions/workflows/build.yml) [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/9691/badge)](https://www.bestpractices.dev/projects/9691) [![License](https://img.shields.io/github/license/project-ocre/ocre-runtime?color=blue)](LICENSE) @@ -8,75 +10,179 @@ Ocre is a container runtime for constrained devices. It leverages [WebAssembly](https://www.webassembly.org) and [Zephyr](https://www.zephyrproject.org/) to support OCI-type application containers in a footprint up to 2,000 times smaller than traditional Linux-based container runtimes. Our mission is to modernize the embedded applications by making it as easy to develop and securely deploy apps on constrained edge devices as it is in the cloud. -## Getting Started -This guide walks you through building and running Ocre on a simulated device using Zephyr's `native_sim` target. For instructions on building and flashing Ocre to physical hardware, please refer to our [documentation](https://docs.project-ocre.org/quickstart/firmware/hardware/). +--- -The application in `./src/main.c` demonstrates basic Ocre runtime usage by writing a hello world application to flash and executing it. +## Features & Platform Support +Ocre supports a range of features depending on the platform. The table below summarizes the current support: -1. **Install Dependencies and Zephyr SDK** +| Feature | Zephyr (native_sim, b_u585i_iot02a) | Linux x86_64 | +|------------------------|:-----------------------------------:|:-------------:| +| Ocre Runtime | ✅ | ✅ | +| Container Messaging | ✅ | ❌ | +| GPIO | ✅ | ❌ | +| Timers | ✅ | ❌ | +| Sensors | ✅ | ❌ | +| Networking | ❌ | ✅ | +| Interactive Shell | ✅ | ❌ | -Complete the [Install dependencies](https://docs.zephyrproject.org/3.7.0/develop/getting_started/index.html#install-dependencies) and the [Install the Zephyr SDK](https://docs.zephyrproject.org/3.7.0/develop/getting_started/index.html#install-the-zephyr-sdk) sections for your host operating system from the Zephyr (v3.7.0) [Getting Started Guide](https://docs.zephyrproject.org/3.7.0/develop/getting_started/index.html#getting-started-guide). +- **Zephyr**: Full feature set, including hardware integration and shell. +- **Linux x86_64**: Core runtime and networking only; hardware and shell features are not available. -2. Create a Virtual Python Environment (`venv`) +--- -``` +## Getting Started + +This guide will help you build and run Ocre on both Zephyr (simulated and hardware) and Linux. + +### Prerequisites + +- **Zephyr SDK** (for Zephyr targets) +- **Python 3** (for build tooling) +- **CMake** and **make** (for Linux builds) +- **west** (Zephyr meta-tool) +- **git** + +--- + +### Setup + +#### Zephyr + +Follow the [Zephyr Getting Started Guide](https://docs.zephyrproject.org/3.7.0/develop/getting_started/index.html) for your OS, including: +- [Install dependencies](https://docs.zephyrproject.org/3.7.0/develop/getting_started/index.html#install-dependencies) +- [Install the Zephyr SDK](https://docs.zephyrproject.org/3.7.0/develop/getting_started/index.html#install-the-zephyr-sdk) + +#### Python Virtual Environment (Recommended) + +```sh mkdir runtime && cd runtime python3 -m venv .venv source .venv/bin/activate ``` +You may need to install `python3-venv` on your system. -**Note:** You may need to install the `python3-venv` package (or equivalent) on your host system beforehand. - -3. **Install WEST** +#### Install West Install the [west](https://docs.zephyrproject.org/latest/develop/west/index.html) CLI tool, which is needed to build, run and manage Zephyr applications. -``` +```sh pip install west ``` -4. **Initialize the workspace** +#### Initialize and Update Zephyr Workspace -This will checkout the Ocre runtime code and initalize the workspace. -``` +```sh west init -m git@github.com:project-ocre/ocre-runtime.git +west update ``` -5. **Update West** +#### Install Zephyr Python Requirements -Next, we need to update the workspace with the latest Zephyr and WASM Micro Runtime code. +```sh +pip install -r zephyr/scripts/requirements.txt +``` + +--- +#### Linux + +#### Clone the Repository + +```sh +git clone git@github.com:project-ocre/ocre-runtime.git +cd ocre-runtime ``` -west update + +#### Initialize submodules +```sh +git submodule update --init --recursive ``` -6. **Install Additional Zephyr (pip) requirements** +--- + +## Building and Running + +### Using the `build.sh` Script (Recommended) -In order to build the Ocre runtime properly, you'll need to install a few remaining requirements for Zephyr. +Ocre provides a convenient `build.sh` script to simplify building and running for both Zephyr and Linux targets. +#### Usage + +```sh +./build.sh -t [-r] [-f [file2 ...]] [-b] [-h] ``` -pip install -r zephyr/scripts/requirements.txt + +- `-t `: **Required**. `z` for Zephyr, `l` for Linux. +- `-r`: Run after build (optional). +- `-f `: Specify one or more input files (optional). +- `-b`: (Zephyr only) Build for `b_u585i_iot02a` board instead of `native_sim`. +- `-h`: Show help. + +#### Examples + +**Build and run for Zephyr native_sim:** +```sh +./build.sh -t z -r ``` -7. **Build the application** +**Build for Zephyr b_u585i_iot02a board:** +```sh +./build.sh -t z -b +``` -The following will build the firmware for the *virtual*, `native_sim` target which will allow you to run the Ocre runtime on a simulated device, rather than a physical board. +**Build and run for Linux with a WASM file:** +```sh +./build.sh -t l -r -f your_file.wasm ``` + +--- + +### Manual Build Steps + +#### Zephyr (native_sim) + +```sh west build -b native_sim ./application -d build -- -DMODULE_EXT_ROOT=`pwd`/application +west flash ``` -8. **Run the application** -Run the following command: +#### Zephyr (b_u585i_iot02a) + +```sh +west build -b b_u585i_iot02a ./application -d build -- -DMODULE_EXT_ROOT=`pwd`/application +# Flash to board (requires hardware) +west flash ``` -./build/zephyr/zephyr.exe + +#### Linux x86_64 + +```sh +mkdir -p build +cd build +cmake .. -DTARGET_PLATFORM_NAME=Linux +make +./app your_file.wasm ``` --- + +## Additional Notes + +- The `build.sh` script will automatically detect the target and handle build/run steps, including passing input files and selecting the correct Zephyr board. +- For Zephyr, only the first file specified with `-f` is passed as the input file (see script for details). +- For Linux, you can pass multiple WASM files as arguments to the built application. +- The script checks build success and only runs the application if the build completes successfully. +- The `mini-samples` directory contains a "Hello World" container, which is hardcoded as a C array. When Ocre is run without input file arguments, it executes this sample container by default. If an input file is provided, Zephyr will convert that file into a C array at build time and run it as a container. On Linux, this conversion does not occur — instead, Ocre simply opens and reads the provided file(s) directly from the filesystem. + +--- + ## License + Distributed under the Apache License 2.0. See [LICENSE](https://github.com/project-ocre/ocre-runtime/blob/main/LICENSE) for more information. --- + ## More Info * **[Website](https://lfedge.org/projects/ocre/)**: For a high-level overview of the Ocre project, and its goals, visit our website. * **[Docs](https://docs.project-ocre.org/)**: For more detailed information about the Ocre runtime, visit Ocre docs. diff --git a/boards/b_u585i_iot02a.conf b/boards/b_u585i_iot02a.conf index 58b0fae9..f33910e9 100644 --- a/boards/b_u585i_iot02a.conf +++ b/boards/b_u585i_iot02a.conf @@ -10,4 +10,10 @@ CONFIG_NET_L2_ETHERNET=y # hts221 sensor config CONFIG_I2C=y CONFIG_SENSOR=y -CONFIG_CBPRINTF_FP_SUPPORT=y \ No newline at end of file +CONFIG_CBPRINTF_FP_SUPPORT=y + +# Ocre GPIO Support +CONFIG_OCRE_GPIO=y +CONFIG_OCRE_GPIO_MAX_PORTS=8 +CONFIG_OCRE_GPIO_PINS_PER_PORT=16 +CONFIG_OCRE_GPIO_MAX_PINS=256 diff --git a/boards/b_u585i_iot02a.overlay b/boards/b_u585i_iot02a.overlay index e6e5fa8c..ebb6b858 100644 --- a/boards/b_u585i_iot02a.overlay +++ b/boards/b_u585i_iot02a.overlay @@ -44,4 +44,4 @@ &vbat4 { status = "disabled"; -}; \ No newline at end of file +}; diff --git a/build.sh b/build.sh new file mode 100755 index 00000000..44d53c05 --- /dev/null +++ b/build.sh @@ -0,0 +1,94 @@ +#!/bin/bash + +# Function to display help +show_help() { + echo "Usage: $0 -t [-r] [-f [file2 ...]]" + echo " -t (Required) Specify the target. z for Zephyr and l for Linux" + echo " -r (Optional) Specify whether run after the build" + echo " -f (Optional) Specify one or more input files" + echo " -b (Optional) Only Zephyr: build for b_u585i_iot02a instead of native_sim" + echo " -h Display help" + exit 0 +} + +RUN_MODE=false # Default: Run mode disabled +INPUT_FILES=() +ZEPHYR_BOARD="native_sim" + +# Parse arguments +while [[ $# -gt 0 ]]; do + case "$1" in + -t) + TARGET="$2" + shift 2 + ;; + -r) + RUN_MODE=true + shift + ;; + -f) + shift + while [[ $# -gt 0 && ! "$1" =~ ^- ]]; do + INPUT_FILES+=("$1") + shift + done + ;; + -b) + ZEPHYR_BOARD=b_u585i_iot02a + shift + ;; + -h) + show_help + ;; + *) + echo "Invalid option: $1" >&2 + show_help + ;; + esac +done + +# Check if required argument is provided +if [[ "$TARGET" == "z" ]]; then + echo "Target is: Zephyr's $ZEPHYR_BOARD" + cd .. + if [[ ${#INPUT_FILES[@]} -gt 0 ]]; then + echo "Input files provided: ${INPUT_FILES[*]}" + rm flash.bin + west build -p -b $ZEPHYR_BOARD ./application -d build -- \ + -DMODULE_EXT_ROOT=`pwd`/application -DOCRE_INPUT_FILE="${INPUT_FILES[0]}" -DTARGET_PLATFORM_NAME=Zephyr + # Note: Only the first file is passed to OCRE_INPUT_FILE, adapt as needed for multiple files + else + rm flash.bin + west build -p -b $ZEPHYR_BOARD ./application -d build -- \ + -DMODULE_EXT_ROOT=`pwd`/application -DTARGET_PLATFORM_NAME=Zephyr + fi +elif [[ "$TARGET" == "l" ]]; then + echo "Target is: Linux x86_64" + if [[ ! -d "build" ]]; then + echo "build folder does not exist. Creating: build" + mkdir -p "build" + fi + cd build + cmake .. -DTARGET_PLATFORM_NAME=Linux -DWAMR_DISABLE_STACK_HW_BOUND_CHECK=1 + # Capture make output to a file + make | tee build.log + BUILD_SUCCESS=false + if [[ $(tail -n 1 build.log) == "[100%] Built target app" ]]; then + BUILD_SUCCESS=true + fi +else + echo "Target does not contain 'z' or 'l': exit" + exit +fi + +# Execute run mode if -r flag is set and build was successful +if [[ "$TARGET" == "z" && "$RUN_MODE" = true ]]; then + west flash +elif [[ "$TARGET" == "l" && "$RUN_MODE" = true && "$BUILD_SUCCESS" = true ]]; then + if [[ ${#INPUT_FILES[@]} -gt 0 ]]; then + echo "Input files provided: ${INPUT_FILES[*]}" + ./app "${INPUT_FILES[@]}" + else + ./app + fi +fi diff --git a/ocre.code-workspace b/ocre.code-workspace index 7f5d2825..2a13006e 100644 --- a/ocre.code-workspace +++ b/ocre.code-workspace @@ -48,7 +48,20 @@ "functional": "c", "tuple": "c", "type_traits": "c", - "utility": "c" + "utility": "c", + "__hash_table": "c", + "__split_buffer": "c", + "bitset": "c", + "deque": "c", + "initializer_list": "c", + "queue": "c", + "stack": "c", + "string": "c", + "string_view": "c", + "unordered_map": "c", + "vector": "c", + "__config": "c", + "__node_handle": "c" }, "editor.formatOnType": true, }, @@ -148,4 +161,4 @@ } ] } -} \ No newline at end of file +} diff --git a/prj.conf b/prj.conf index 480e0a91..9ba8c6e6 100644 --- a/prj.conf +++ b/prj.conf @@ -56,7 +56,7 @@ CONFIG_POSIX_API=y CONFIG_POSIX_MAX_FDS=10 # Memory / malloc optopns -CONFIG_HEAP_MEM_POOL_SIZE=8192 +CONFIG_HEAP_MEM_POOL_SIZE=65536 CONFIG_MAIN_STACK_SIZE=8192 CONFIG_SHELL_STACK_SIZE=8192 CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=8192 @@ -85,5 +85,6 @@ CONFIG_MAX_SENSORS=10 CONFIG_MAX_CHANNELS_PER_SENSOR=5 CONFIG_OCRE_MEMORY_CHECK_ENABLED=n -CONFIG_OCRE_GPIO=n +CONFIG_OCRE_GPIO=y +CONFIG_OCRE_LOG_DEBUG=y diff --git a/src/main.c b/src/main.c deleted file mode 100644 index 42f79885..00000000 --- a/src/main.c +++ /dev/null @@ -1,880 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include - -#include -#include -#include - -void create_sample_container(); - -int main(int argc, char *argv[]) { - ocre_cs_ctx ctx; - ocre_container_init_arguments_t args; - char *container_filename = "hello"; - - ocre_app_storage_init(); - - create_sample_container(container_filename); - - // Step 1: Initialize the Ocre runtime - ocre_container_runtime_status_t ret = ocre_container_runtime_init(&ctx, &args); - - if (ret == RUNTIME_STATUS_INITIALIZED) { - printf("\n\nOcre runtime started\n"); - - // Step 2: Create the container, this allocates and loads the container binary - ocre_container_data_t ocre_container_data; - int container_ID; - ocre_container_runtime_cb callback; - - ocre_container_data.heap_size = 0; - snprintf(ocre_container_data.name, sizeof(ocre_container_data.name), "Hello World"); - snprintf(ocre_container_data.sha256, sizeof(ocre_container_data.sha256), "%s", container_filename); - ocre_container_data.timers = 0; - ocre_container_data.watchdog_interval = 0; - ocre_container_runtime_create_container(&ctx, &ocre_container_data, &container_ID, callback); - - // Step 3: Execute the container - ocre_container_runtime_run_container(&ctx, container_ID, callback); - - // Loop forever, without this the application will exit and stop all execution - while (true) { - k_msleep(1000); - } - - } else { - printf("\n\nOcre runtime failed to start.\n"); - } -} - -/** - * Creates a container image file using the sample "hello-word" WASM module - * This is for demostration purposes only and does not perform any error checking. - * - * @param file_name a string containing the name of the file to create - */ - -void create_sample_container(char *file_name) { - struct fs_file_t f; - static char file_path[64]; - snprintf((char *)&file_path, 64, "/lfs/ocre/images/%s.bin", file_name); - int res; - - fs_file_t_init(&f); - res = fs_open(&f, file_path, FS_O_CREATE | FS_O_RDWR); - - unsigned char wasm_binary[] = { - 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x01, 0x3C, 0x0A, 0x60, 0x03, 0x7F, 0x7F, 0x7F, 0x01, 0x7F, - 0x60, 0x03, 0x7F, 0x7E, 0x7F, 0x01, 0x7E, 0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F, 0x60, 0x01, 0x7F, 0x01, 0x7F, - 0x60, 0x04, 0x7F, 0x7E, 0x7F, 0x7F, 0x01, 0x7F, 0x60, 0x04, 0x7F, 0x7F, 0x7F, 0x7F, 0x01, 0x7F, 0x60, 0x01, - 0x7F, 0x00, 0x60, 0x00, 0x01, 0x7F, 0x60, 0x00, 0x00, 0x60, 0x04, 0x7F, 0x7F, 0x7F, 0x7F, 0x00, 0x02, 0xAE, - 0x02, 0x09, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x6D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0x02, 0x03, 0x02, 0x02, 0x16, - 0x77, 0x61, 0x73, 0x69, 0x5F, 0x73, 0x6E, 0x61, 0x70, 0x73, 0x68, 0x6F, 0x74, 0x5F, 0x70, 0x72, 0x65, 0x76, - 0x69, 0x65, 0x77, 0x31, 0x08, 0x61, 0x72, 0x67, 0x73, 0x5F, 0x67, 0x65, 0x74, 0x00, 0x02, 0x16, 0x77, 0x61, - 0x73, 0x69, 0x5F, 0x73, 0x6E, 0x61, 0x70, 0x73, 0x68, 0x6F, 0x74, 0x5F, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, - 0x77, 0x31, 0x0E, 0x61, 0x72, 0x67, 0x73, 0x5F, 0x73, 0x69, 0x7A, 0x65, 0x73, 0x5F, 0x67, 0x65, 0x74, 0x00, - 0x02, 0x16, 0x77, 0x61, 0x73, 0x69, 0x5F, 0x73, 0x6E, 0x61, 0x70, 0x73, 0x68, 0x6F, 0x74, 0x5F, 0x70, 0x72, - 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x08, 0x66, 0x64, 0x5F, 0x63, 0x6C, 0x6F, 0x73, 0x65, 0x00, 0x03, 0x16, - 0x77, 0x61, 0x73, 0x69, 0x5F, 0x73, 0x6E, 0x61, 0x70, 0x73, 0x68, 0x6F, 0x74, 0x5F, 0x70, 0x72, 0x65, 0x76, - 0x69, 0x65, 0x77, 0x31, 0x0D, 0x66, 0x64, 0x5F, 0x66, 0x64, 0x73, 0x74, 0x61, 0x74, 0x5F, 0x67, 0x65, 0x74, - 0x00, 0x02, 0x16, 0x77, 0x61, 0x73, 0x69, 0x5F, 0x73, 0x6E, 0x61, 0x70, 0x73, 0x68, 0x6F, 0x74, 0x5F, 0x70, - 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x07, 0x66, 0x64, 0x5F, 0x73, 0x65, 0x65, 0x6B, 0x00, 0x04, 0x16, - 0x77, 0x61, 0x73, 0x69, 0x5F, 0x73, 0x6E, 0x61, 0x70, 0x73, 0x68, 0x6F, 0x74, 0x5F, 0x70, 0x72, 0x65, 0x76, - 0x69, 0x65, 0x77, 0x31, 0x08, 0x66, 0x64, 0x5F, 0x77, 0x72, 0x69, 0x74, 0x65, 0x00, 0x05, 0x16, 0x77, 0x61, - 0x73, 0x69, 0x5F, 0x73, 0x6E, 0x61, 0x70, 0x73, 0x68, 0x6F, 0x74, 0x5F, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, - 0x77, 0x31, 0x09, 0x70, 0x72, 0x6F, 0x63, 0x5F, 0x65, 0x78, 0x69, 0x74, 0x00, 0x06, 0x16, 0x77, 0x61, 0x73, - 0x69, 0x5F, 0x73, 0x6E, 0x61, 0x70, 0x73, 0x68, 0x6F, 0x74, 0x5F, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, - 0x31, 0x0B, 0x73, 0x63, 0x68, 0x65, 0x64, 0x5F, 0x79, 0x69, 0x65, 0x6C, 0x64, 0x00, 0x07, 0x03, 0x31, 0x30, - 0x08, 0x08, 0x08, 0x02, 0x07, 0x03, 0x03, 0x00, 0x06, 0x06, 0x02, 0x06, 0x07, 0x02, 0x02, 0x03, 0x02, 0x04, - 0x05, 0x06, 0x07, 0x08, 0x03, 0x08, 0x08, 0x08, 0x03, 0x06, 0x07, 0x08, 0x03, 0x05, 0x02, 0x02, 0x03, 0x08, - 0x03, 0x03, 0x03, 0x00, 0x00, 0x03, 0x00, 0x01, 0x01, 0x03, 0x06, 0x09, 0x04, 0x05, 0x01, 0x70, 0x01, 0x05, - 0x05, 0x06, 0x12, 0x03, 0x7F, 0x01, 0x41, 0xD0, 0x96, 0x04, 0x0B, 0x7F, 0x01, 0x41, 0x00, 0x0B, 0x7F, 0x00, - 0x41, 0x00, 0x0B, 0x07, 0x13, 0x02, 0x06, 0x6D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0x02, 0x00, 0x06, 0x5F, 0x73, - 0x74, 0x61, 0x72, 0x74, 0x00, 0x0A, 0x08, 0x01, 0x09, 0x09, 0x0A, 0x01, 0x00, 0x41, 0x01, 0x0B, 0x04, 0x30, - 0x2E, 0x32, 0x34, 0x0C, 0x01, 0x03, 0x0A, 0xA7, 0x6A, 0x30, 0x02, 0x00, 0x0B, 0x75, 0x00, 0x02, 0x40, 0x02, - 0x40, 0x02, 0x40, 0x41, 0xC8, 0x16, 0x41, 0x00, 0x41, 0x01, 0xFE, 0x48, 0x02, 0x00, 0x0E, 0x02, 0x00, 0x01, - 0x02, 0x0B, 0x41, 0x80, 0x08, 0x41, 0x80, 0x08, 0x24, 0x01, 0x41, 0x00, 0x41, 0xF0, 0x00, 0xFC, 0x08, 0x00, - 0x00, 0x41, 0xF0, 0x08, 0x41, 0x00, 0x41, 0x17, 0xFC, 0x08, 0x01, 0x00, 0x41, 0x88, 0x09, 0x41, 0x00, 0x41, - 0x88, 0x01, 0xFC, 0x08, 0x02, 0x00, 0x41, 0x90, 0x0A, 0x41, 0x00, 0x41, 0xB8, 0x0C, 0xFC, 0x0B, 0x00, 0x41, - 0xC8, 0x16, 0x41, 0x02, 0xFE, 0x17, 0x02, 0x00, 0x41, 0xC8, 0x16, 0x41, 0x7F, 0xFE, 0x00, 0x02, 0x00, 0x1A, - 0x0C, 0x01, 0x0B, 0x41, 0xC8, 0x16, 0x41, 0x01, 0x42, 0x7F, 0xFE, 0x01, 0x02, 0x00, 0x1A, 0x0B, 0xFC, 0x09, - 0x01, 0xFC, 0x09, 0x02, 0x0B, 0x4A, 0x01, 0x01, 0x7F, 0x02, 0x40, 0x02, 0x40, 0x23, 0x82, 0x80, 0x80, 0x80, - 0x00, 0x41, 0x90, 0x8A, 0x80, 0x80, 0x00, 0x6A, 0x41, 0x00, 0x41, 0x01, 0xFE, 0x48, 0x02, 0x00, 0x0D, 0x00, - 0x10, 0x9F, 0x80, 0x80, 0x80, 0x00, 0x10, 0x88, 0x80, 0x80, 0x80, 0x00, 0x10, 0x94, 0x80, 0x80, 0x80, 0x00, - 0x21, 0x00, 0x10, 0xA1, 0x80, 0x80, 0x80, 0x00, 0x20, 0x00, 0x0D, 0x01, 0x0F, 0x0B, 0x00, 0x0B, 0x20, 0x00, - 0x10, 0x9B, 0x80, 0x80, 0x80, 0x00, 0x00, 0x0B, 0x11, 0x00, 0x41, 0xF0, 0x88, 0x80, 0x80, 0x00, 0x10, 0xAA, - 0x80, 0x80, 0x80, 0x00, 0x1A, 0x41, 0x00, 0x0B, 0x28, 0x01, 0x01, 0x7F, 0x02, 0x40, 0x10, 0x9C, 0x80, 0x80, - 0x80, 0x00, 0x22, 0x00, 0x0D, 0x00, 0x41, 0x00, 0x0F, 0x0B, 0x23, 0x81, 0x80, 0x80, 0x80, 0x00, 0x41, 0x80, - 0x80, 0x80, 0x80, 0x00, 0x6A, 0x20, 0x00, 0x36, 0x02, 0x00, 0x41, 0x7F, 0x0B, 0x0A, 0x00, 0x20, 0x00, 0x10, - 0x8E, 0x80, 0x80, 0x80, 0x00, 0x0B, 0xC7, 0x30, 0x01, 0x0B, 0x7F, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, - 0x10, 0x6B, 0x22, 0x01, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0x94, 0x8A, - 0x80, 0x80, 0x00, 0x0D, 0x00, 0x02, 0x40, 0x41, 0x00, 0x41, 0x01, 0xFE, 0x41, 0x02, 0x88, 0x8E, 0x80, 0x80, - 0x00, 0x45, 0x0D, 0x00, 0x41, 0x01, 0x21, 0x02, 0x03, 0x40, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0x88, 0x8E, - 0x80, 0x80, 0x00, 0x0D, 0x00, 0x41, 0x00, 0x41, 0x01, 0xFE, 0x41, 0x02, 0x88, 0x8E, 0x80, 0x80, 0x00, 0x45, - 0x0D, 0x02, 0x0B, 0x02, 0x40, 0x20, 0x02, 0x41, 0x3F, 0x71, 0x0D, 0x00, 0x10, 0x8C, 0x80, 0x80, 0x80, 0x00, - 0x1A, 0x0B, 0x20, 0x02, 0x41, 0x01, 0x6A, 0x21, 0x02, 0x0C, 0x00, 0x0B, 0x0B, 0x02, 0x40, 0x41, 0x00, 0x28, - 0x02, 0x94, 0x8A, 0x80, 0x80, 0x00, 0x0D, 0x00, 0x41, 0x00, 0x41, 0x02, 0x36, 0x02, 0xA8, 0x8A, 0x80, 0x80, - 0x00, 0x41, 0x00, 0x42, 0x7F, 0x37, 0x02, 0xA0, 0x8A, 0x80, 0x80, 0x00, 0x41, 0x00, 0x42, 0x80, 0x80, 0x84, - 0x80, 0x80, 0x80, 0xC0, 0x00, 0x37, 0x02, 0x98, 0x8A, 0x80, 0x80, 0x00, 0x41, 0x00, 0x42, 0x02, 0x37, 0x02, - 0xE8, 0x8D, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x01, 0x41, 0x04, 0x6A, 0x41, 0x70, 0x71, 0x41, 0xD8, 0xAA, - 0xD5, 0xAA, 0x05, 0x73, 0x36, 0x02, 0x94, 0x8A, 0x80, 0x80, 0x00, 0x0B, 0x41, 0x00, 0x41, 0x00, 0xFE, 0x17, - 0x02, 0x88, 0x8E, 0x80, 0x80, 0x00, 0x0B, 0x02, 0x40, 0x41, 0x00, 0x2D, 0x00, 0xE8, 0x8D, 0x80, 0x80, 0x00, - 0x41, 0x02, 0x71, 0x45, 0x0D, 0x00, 0x41, 0x00, 0x41, 0x01, 0xFE, 0x41, 0x02, 0xEC, 0x8D, 0x80, 0x80, 0x00, - 0x45, 0x0D, 0x00, 0x41, 0x01, 0x21, 0x02, 0x03, 0x40, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xEC, 0x8D, 0x80, - 0x80, 0x00, 0x0D, 0x00, 0x41, 0x00, 0x41, 0x01, 0xFE, 0x41, 0x02, 0xEC, 0x8D, 0x80, 0x80, 0x00, 0x45, 0x0D, - 0x02, 0x0B, 0x02, 0x40, 0x20, 0x02, 0x41, 0x3F, 0x71, 0x0D, 0x00, 0x10, 0x8C, 0x80, 0x80, 0x80, 0x00, 0x1A, - 0x0B, 0x20, 0x02, 0x41, 0x01, 0x6A, 0x21, 0x02, 0x0C, 0x00, 0x0B, 0x0B, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, - 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x41, 0x00, - 0x28, 0x02, 0xC4, 0x8A, 0x80, 0x80, 0x00, 0x0D, 0x00, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0x94, 0x8A, 0x80, - 0x80, 0x00, 0x22, 0x03, 0x0D, 0x00, 0x02, 0x40, 0x41, 0x00, 0x41, 0x01, 0xFE, 0x41, 0x02, 0x88, 0x8E, 0x80, - 0x80, 0x00, 0x45, 0x0D, 0x00, 0x41, 0x01, 0x21, 0x02, 0x03, 0x40, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0x88, - 0x8E, 0x80, 0x80, 0x00, 0x0D, 0x00, 0x41, 0x00, 0x41, 0x01, 0xFE, 0x41, 0x02, 0x88, 0x8E, 0x80, 0x80, 0x00, - 0x45, 0x0D, 0x02, 0x0B, 0x02, 0x40, 0x20, 0x02, 0x41, 0x3F, 0x71, 0x0D, 0x00, 0x10, 0x8C, 0x80, 0x80, 0x80, - 0x00, 0x1A, 0x0B, 0x20, 0x02, 0x41, 0x01, 0x6A, 0x21, 0x02, 0x0C, 0x00, 0x0B, 0x0B, 0x02, 0x40, 0x41, 0x00, - 0x28, 0x02, 0x94, 0x8A, 0x80, 0x80, 0x00, 0x22, 0x03, 0x0D, 0x00, 0x41, 0x00, 0x41, 0x02, 0x36, 0x02, 0xA8, - 0x8A, 0x80, 0x80, 0x00, 0x41, 0x00, 0x42, 0x7F, 0x37, 0x02, 0xA0, 0x8A, 0x80, 0x80, 0x00, 0x41, 0x00, 0x42, - 0x80, 0x80, 0x84, 0x80, 0x80, 0x80, 0xC0, 0x00, 0x37, 0x02, 0x98, 0x8A, 0x80, 0x80, 0x00, 0x41, 0x00, 0x42, - 0x02, 0x37, 0x02, 0xE8, 0x8D, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x01, 0x41, 0x08, 0x6A, 0x41, 0x70, 0x71, - 0x41, 0xD8, 0xAA, 0xD5, 0xAA, 0x05, 0x73, 0x22, 0x03, 0x36, 0x02, 0x94, 0x8A, 0x80, 0x80, 0x00, 0x0B, 0x41, - 0x00, 0x41, 0x00, 0xFE, 0x17, 0x02, 0x88, 0x8E, 0x80, 0x80, 0x00, 0x0B, 0x41, 0x80, 0x80, 0x88, 0x80, 0x00, - 0x41, 0xD0, 0x96, 0x84, 0x80, 0x00, 0x49, 0x0D, 0x01, 0x41, 0x80, 0x80, 0x88, 0x80, 0x00, 0x41, 0xD0, 0x96, - 0x84, 0x80, 0x00, 0x6B, 0x41, 0xD9, 0x00, 0x49, 0x0D, 0x00, 0x41, 0x00, 0x21, 0x02, 0x41, 0x00, 0x41, 0xD0, - 0x96, 0x84, 0x80, 0x00, 0x36, 0x02, 0xF0, 0x8D, 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, 0xD0, 0x96, 0x84, 0x80, - 0x00, 0x36, 0x02, 0xBC, 0x8A, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x03, 0x36, 0x02, 0xD0, 0x8A, 0x80, 0x80, - 0x00, 0x41, 0x00, 0x41, 0x7F, 0x36, 0x02, 0xCC, 0x8A, 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, 0x80, 0x80, 0x88, - 0x80, 0x00, 0x41, 0xD0, 0x96, 0x84, 0x80, 0x00, 0x6B, 0x22, 0x03, 0x36, 0x02, 0xF4, 0x8D, 0x80, 0x80, 0x00, - 0x41, 0x00, 0x20, 0x03, 0x36, 0x02, 0xE0, 0x8D, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x03, 0x36, 0x02, 0xDC, - 0x8D, 0x80, 0x80, 0x00, 0x03, 0x40, 0x20, 0x02, 0x41, 0xE8, 0x8A, 0x80, 0x80, 0x00, 0x6A, 0x20, 0x02, 0x41, - 0xDC, 0x8A, 0x80, 0x80, 0x00, 0x6A, 0x22, 0x03, 0x36, 0x02, 0x00, 0x20, 0x03, 0x20, 0x02, 0x41, 0xD4, 0x8A, - 0x80, 0x80, 0x00, 0x6A, 0x22, 0x04, 0x36, 0x02, 0x00, 0x20, 0x02, 0x41, 0xE0, 0x8A, 0x80, 0x80, 0x00, 0x6A, - 0x20, 0x04, 0x36, 0x02, 0x00, 0x20, 0x02, 0x41, 0xF0, 0x8A, 0x80, 0x80, 0x00, 0x6A, 0x20, 0x02, 0x41, 0xE4, - 0x8A, 0x80, 0x80, 0x00, 0x6A, 0x22, 0x04, 0x36, 0x02, 0x00, 0x20, 0x04, 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, - 0x02, 0x41, 0xF8, 0x8A, 0x80, 0x80, 0x00, 0x6A, 0x20, 0x02, 0x41, 0xEC, 0x8A, 0x80, 0x80, 0x00, 0x6A, 0x22, - 0x03, 0x36, 0x02, 0x00, 0x20, 0x03, 0x20, 0x04, 0x36, 0x02, 0x00, 0x20, 0x02, 0x41, 0xF4, 0x8A, 0x80, 0x80, - 0x00, 0x6A, 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, 0x02, 0x41, 0x20, 0x6A, 0x22, 0x02, 0x41, 0x80, 0x02, 0x47, - 0x0D, 0x00, 0x0B, 0x41, 0x80, 0x80, 0x88, 0x80, 0x00, 0x41, 0x4C, 0x6A, 0x41, 0x38, 0x36, 0x02, 0x00, 0x41, - 0x00, 0x41, 0x00, 0x28, 0x02, 0xA4, 0x8A, 0x80, 0x80, 0x00, 0x36, 0x02, 0xC8, 0x8A, 0x80, 0x80, 0x00, 0x41, - 0x00, 0x41, 0xD0, 0x96, 0x84, 0x80, 0x00, 0x41, 0x78, 0x41, 0xD0, 0x96, 0x84, 0x80, 0x00, 0x6B, 0x41, 0x0F, - 0x71, 0x22, 0x02, 0x6A, 0x22, 0x03, 0x36, 0x02, 0xC4, 0x8A, 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, 0x80, 0x80, - 0x88, 0x80, 0x00, 0x41, 0xD0, 0x96, 0x84, 0x80, 0x00, 0x6B, 0x20, 0x02, 0x6B, 0x41, 0x48, 0x6A, 0x22, 0x02, - 0x36, 0x02, 0xB8, 0x8A, 0x80, 0x80, 0x00, 0x20, 0x03, 0x20, 0x02, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x0B, - 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x41, 0xEC, 0x01, 0x4B, 0x0D, 0x00, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, - 0xAC, 0x8A, 0x80, 0x80, 0x00, 0x22, 0x05, 0x41, 0x10, 0x20, 0x00, 0x41, 0x13, 0x6A, 0x41, 0xF0, 0x03, 0x71, - 0x20, 0x00, 0x41, 0x0B, 0x49, 0x1B, 0x22, 0x04, 0x41, 0x03, 0x76, 0x22, 0x03, 0x76, 0x22, 0x02, 0x41, 0x03, - 0x71, 0x45, 0x0D, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x02, 0x41, 0x01, 0x71, 0x20, 0x03, 0x72, 0x41, 0x01, - 0x73, 0x22, 0x04, 0x41, 0x03, 0x74, 0x22, 0x03, 0x41, 0xD4, 0x8A, 0x80, 0x80, 0x00, 0x6A, 0x22, 0x02, 0x20, - 0x03, 0x41, 0xDC, 0x8A, 0x80, 0x80, 0x00, 0x6A, 0x28, 0x02, 0x00, 0x22, 0x03, 0x28, 0x02, 0x08, 0x22, 0x00, - 0x47, 0x0D, 0x00, 0x41, 0x00, 0x20, 0x05, 0x41, 0x7E, 0x20, 0x04, 0x77, 0x71, 0x36, 0x02, 0xAC, 0x8A, 0x80, - 0x80, 0x00, 0x0C, 0x01, 0x0B, 0x20, 0x02, 0x20, 0x00, 0x36, 0x02, 0x08, 0x20, 0x00, 0x20, 0x02, 0x36, 0x02, - 0x0C, 0x0B, 0x20, 0x03, 0x41, 0x08, 0x6A, 0x21, 0x02, 0x20, 0x03, 0x20, 0x04, 0x41, 0x03, 0x74, 0x22, 0x04, - 0x41, 0x03, 0x72, 0x36, 0x02, 0x04, 0x20, 0x03, 0x20, 0x04, 0x6A, 0x22, 0x03, 0x20, 0x03, 0x28, 0x02, 0x04, - 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x0C, 0x0C, 0x0B, 0x20, 0x04, 0x41, 0x00, 0x28, 0x02, 0xB4, 0x8A, 0x80, - 0x80, 0x00, 0x22, 0x06, 0x4D, 0x0D, 0x01, 0x02, 0x40, 0x20, 0x02, 0x45, 0x0D, 0x00, 0x02, 0x40, 0x02, 0x40, - 0x20, 0x02, 0x20, 0x03, 0x74, 0x41, 0x02, 0x20, 0x03, 0x74, 0x22, 0x02, 0x41, 0x00, 0x20, 0x02, 0x6B, 0x72, - 0x71, 0x68, 0x22, 0x03, 0x41, 0x03, 0x74, 0x22, 0x02, 0x41, 0xD4, 0x8A, 0x80, 0x80, 0x00, 0x6A, 0x22, 0x00, - 0x20, 0x02, 0x41, 0xDC, 0x8A, 0x80, 0x80, 0x00, 0x6A, 0x28, 0x02, 0x00, 0x22, 0x02, 0x28, 0x02, 0x08, 0x22, - 0x07, 0x47, 0x0D, 0x00, 0x41, 0x00, 0x20, 0x05, 0x41, 0x7E, 0x20, 0x03, 0x77, 0x71, 0x22, 0x05, 0x36, 0x02, - 0xAC, 0x8A, 0x80, 0x80, 0x00, 0x0C, 0x01, 0x0B, 0x20, 0x00, 0x20, 0x07, 0x36, 0x02, 0x08, 0x20, 0x07, 0x20, - 0x00, 0x36, 0x02, 0x0C, 0x0B, 0x20, 0x02, 0x20, 0x04, 0x41, 0x03, 0x72, 0x36, 0x02, 0x04, 0x20, 0x02, 0x20, - 0x03, 0x41, 0x03, 0x74, 0x22, 0x03, 0x6A, 0x20, 0x03, 0x20, 0x04, 0x6B, 0x22, 0x00, 0x36, 0x02, 0x00, 0x20, - 0x02, 0x20, 0x04, 0x6A, 0x22, 0x07, 0x20, 0x00, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x02, 0x40, 0x20, 0x06, - 0x45, 0x0D, 0x00, 0x20, 0x06, 0x41, 0x78, 0x71, 0x41, 0xD4, 0x8A, 0x80, 0x80, 0x00, 0x6A, 0x21, 0x04, 0x41, - 0x00, 0x28, 0x02, 0xC0, 0x8A, 0x80, 0x80, 0x00, 0x21, 0x03, 0x02, 0x40, 0x02, 0x40, 0x20, 0x05, 0x41, 0x01, - 0x20, 0x06, 0x41, 0x03, 0x76, 0x74, 0x22, 0x08, 0x71, 0x0D, 0x00, 0x41, 0x00, 0x20, 0x05, 0x20, 0x08, 0x72, - 0x36, 0x02, 0xAC, 0x8A, 0x80, 0x80, 0x00, 0x20, 0x04, 0x21, 0x08, 0x0C, 0x01, 0x0B, 0x20, 0x04, 0x28, 0x02, - 0x08, 0x21, 0x08, 0x0B, 0x20, 0x08, 0x20, 0x03, 0x36, 0x02, 0x0C, 0x20, 0x04, 0x20, 0x03, 0x36, 0x02, 0x08, - 0x20, 0x03, 0x20, 0x04, 0x36, 0x02, 0x0C, 0x20, 0x03, 0x20, 0x08, 0x36, 0x02, 0x08, 0x0B, 0x20, 0x02, 0x41, - 0x08, 0x6A, 0x21, 0x02, 0x41, 0x00, 0x20, 0x07, 0x36, 0x02, 0xC0, 0x8A, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, - 0x00, 0x36, 0x02, 0xB4, 0x8A, 0x80, 0x80, 0x00, 0x0C, 0x0C, 0x0B, 0x41, 0x00, 0x28, 0x02, 0xB0, 0x8A, 0x80, - 0x80, 0x00, 0x22, 0x09, 0x45, 0x0D, 0x01, 0x20, 0x09, 0x68, 0x41, 0x02, 0x74, 0x41, 0xDC, 0x8C, 0x80, 0x80, - 0x00, 0x6A, 0x28, 0x02, 0x00, 0x22, 0x07, 0x28, 0x02, 0x04, 0x41, 0x78, 0x71, 0x20, 0x04, 0x6B, 0x21, 0x03, - 0x20, 0x07, 0x21, 0x00, 0x02, 0x40, 0x03, 0x40, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x10, 0x22, 0x02, 0x0D, - 0x00, 0x20, 0x00, 0x28, 0x02, 0x14, 0x22, 0x02, 0x45, 0x0D, 0x02, 0x0B, 0x20, 0x02, 0x28, 0x02, 0x04, 0x41, - 0x78, 0x71, 0x20, 0x04, 0x6B, 0x22, 0x00, 0x20, 0x03, 0x20, 0x00, 0x20, 0x03, 0x49, 0x22, 0x00, 0x1B, 0x21, - 0x03, 0x20, 0x02, 0x20, 0x07, 0x20, 0x00, 0x1B, 0x21, 0x07, 0x20, 0x02, 0x21, 0x00, 0x0C, 0x00, 0x0B, 0x0B, - 0x20, 0x07, 0x28, 0x02, 0x18, 0x21, 0x0A, 0x02, 0x40, 0x20, 0x07, 0x28, 0x02, 0x0C, 0x22, 0x02, 0x20, 0x07, - 0x46, 0x0D, 0x00, 0x20, 0x07, 0x28, 0x02, 0x08, 0x22, 0x00, 0x20, 0x02, 0x36, 0x02, 0x0C, 0x20, 0x02, 0x20, - 0x00, 0x36, 0x02, 0x08, 0x0C, 0x0B, 0x0B, 0x02, 0x40, 0x02, 0x40, 0x20, 0x07, 0x28, 0x02, 0x14, 0x22, 0x00, - 0x45, 0x0D, 0x00, 0x20, 0x07, 0x41, 0x14, 0x6A, 0x21, 0x08, 0x0C, 0x01, 0x0B, 0x20, 0x07, 0x28, 0x02, 0x10, - 0x22, 0x00, 0x45, 0x0D, 0x04, 0x20, 0x07, 0x41, 0x10, 0x6A, 0x21, 0x08, 0x0B, 0x03, 0x40, 0x20, 0x08, 0x21, - 0x0B, 0x20, 0x00, 0x22, 0x02, 0x41, 0x14, 0x6A, 0x21, 0x08, 0x20, 0x02, 0x28, 0x02, 0x14, 0x22, 0x00, 0x0D, - 0x00, 0x20, 0x02, 0x41, 0x10, 0x6A, 0x21, 0x08, 0x20, 0x02, 0x28, 0x02, 0x10, 0x22, 0x00, 0x0D, 0x00, 0x0B, - 0x20, 0x0B, 0x41, 0x00, 0x36, 0x02, 0x00, 0x0C, 0x0A, 0x0B, 0x41, 0x7F, 0x21, 0x04, 0x20, 0x00, 0x41, 0xBF, - 0x7F, 0x4B, 0x0D, 0x00, 0x20, 0x00, 0x41, 0x13, 0x6A, 0x22, 0x02, 0x41, 0x70, 0x71, 0x21, 0x04, 0x41, 0x00, - 0x28, 0x02, 0xB0, 0x8A, 0x80, 0x80, 0x00, 0x22, 0x0A, 0x45, 0x0D, 0x00, 0x41, 0x1F, 0x21, 0x06, 0x02, 0x40, - 0x20, 0x00, 0x41, 0xEC, 0xFF, 0xFF, 0x07, 0x4B, 0x0D, 0x00, 0x20, 0x04, 0x41, 0x26, 0x20, 0x02, 0x41, 0x08, - 0x76, 0x67, 0x22, 0x02, 0x6B, 0x76, 0x41, 0x01, 0x71, 0x20, 0x02, 0x41, 0x01, 0x74, 0x6B, 0x41, 0x3E, 0x6A, - 0x21, 0x06, 0x0B, 0x41, 0x00, 0x20, 0x04, 0x6B, 0x21, 0x03, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, - 0x20, 0x06, 0x41, 0x02, 0x74, 0x41, 0xDC, 0x8C, 0x80, 0x80, 0x00, 0x6A, 0x28, 0x02, 0x00, 0x22, 0x00, 0x0D, - 0x00, 0x41, 0x00, 0x21, 0x02, 0x41, 0x00, 0x21, 0x08, 0x0C, 0x01, 0x0B, 0x41, 0x00, 0x21, 0x02, 0x20, 0x04, - 0x41, 0x00, 0x41, 0x19, 0x20, 0x06, 0x41, 0x01, 0x76, 0x6B, 0x20, 0x06, 0x41, 0x1F, 0x46, 0x1B, 0x74, 0x21, - 0x07, 0x41, 0x00, 0x21, 0x08, 0x03, 0x40, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x04, 0x41, 0x78, 0x71, 0x20, - 0x04, 0x6B, 0x22, 0x05, 0x20, 0x03, 0x4F, 0x0D, 0x00, 0x20, 0x05, 0x21, 0x03, 0x20, 0x00, 0x21, 0x08, 0x20, - 0x05, 0x0D, 0x00, 0x41, 0x00, 0x21, 0x03, 0x20, 0x00, 0x21, 0x08, 0x20, 0x00, 0x21, 0x02, 0x0C, 0x03, 0x0B, - 0x20, 0x02, 0x20, 0x00, 0x28, 0x02, 0x14, 0x22, 0x05, 0x20, 0x05, 0x20, 0x00, 0x20, 0x07, 0x41, 0x1D, 0x76, - 0x41, 0x04, 0x71, 0x6A, 0x41, 0x10, 0x6A, 0x28, 0x02, 0x00, 0x22, 0x0B, 0x46, 0x1B, 0x20, 0x02, 0x20, 0x05, - 0x1B, 0x21, 0x02, 0x20, 0x07, 0x41, 0x01, 0x74, 0x21, 0x07, 0x20, 0x0B, 0x21, 0x00, 0x20, 0x0B, 0x0D, 0x00, - 0x0B, 0x0B, 0x02, 0x40, 0x20, 0x02, 0x20, 0x08, 0x72, 0x0D, 0x00, 0x41, 0x00, 0x21, 0x08, 0x41, 0x02, 0x20, - 0x06, 0x74, 0x22, 0x02, 0x41, 0x00, 0x20, 0x02, 0x6B, 0x72, 0x20, 0x0A, 0x71, 0x22, 0x02, 0x45, 0x0D, 0x03, - 0x20, 0x02, 0x68, 0x41, 0x02, 0x74, 0x41, 0xDC, 0x8C, 0x80, 0x80, 0x00, 0x6A, 0x28, 0x02, 0x00, 0x21, 0x02, - 0x0B, 0x20, 0x02, 0x45, 0x0D, 0x01, 0x0B, 0x03, 0x40, 0x20, 0x02, 0x28, 0x02, 0x04, 0x41, 0x78, 0x71, 0x20, - 0x04, 0x6B, 0x22, 0x05, 0x20, 0x03, 0x49, 0x21, 0x07, 0x02, 0x40, 0x20, 0x02, 0x28, 0x02, 0x10, 0x22, 0x00, - 0x0D, 0x00, 0x20, 0x02, 0x28, 0x02, 0x14, 0x21, 0x00, 0x0B, 0x20, 0x05, 0x20, 0x03, 0x20, 0x07, 0x1B, 0x21, - 0x03, 0x20, 0x02, 0x20, 0x08, 0x20, 0x07, 0x1B, 0x21, 0x08, 0x20, 0x00, 0x21, 0x02, 0x20, 0x00, 0x0D, 0x00, - 0x0B, 0x0B, 0x20, 0x08, 0x45, 0x0D, 0x00, 0x20, 0x03, 0x41, 0x00, 0x28, 0x02, 0xB4, 0x8A, 0x80, 0x80, 0x00, - 0x20, 0x04, 0x6B, 0x4F, 0x0D, 0x00, 0x20, 0x08, 0x28, 0x02, 0x18, 0x21, 0x0B, 0x02, 0x40, 0x20, 0x08, 0x28, - 0x02, 0x0C, 0x22, 0x02, 0x20, 0x08, 0x46, 0x0D, 0x00, 0x20, 0x08, 0x28, 0x02, 0x08, 0x22, 0x00, 0x20, 0x02, - 0x36, 0x02, 0x0C, 0x20, 0x02, 0x20, 0x00, 0x36, 0x02, 0x08, 0x0C, 0x09, 0x0B, 0x02, 0x40, 0x02, 0x40, 0x20, - 0x08, 0x28, 0x02, 0x14, 0x22, 0x00, 0x45, 0x0D, 0x00, 0x20, 0x08, 0x41, 0x14, 0x6A, 0x21, 0x07, 0x0C, 0x01, - 0x0B, 0x20, 0x08, 0x28, 0x02, 0x10, 0x22, 0x00, 0x45, 0x0D, 0x04, 0x20, 0x08, 0x41, 0x10, 0x6A, 0x21, 0x07, - 0x0B, 0x03, 0x40, 0x20, 0x07, 0x21, 0x05, 0x20, 0x00, 0x22, 0x02, 0x41, 0x14, 0x6A, 0x21, 0x07, 0x20, 0x02, - 0x28, 0x02, 0x14, 0x22, 0x00, 0x0D, 0x00, 0x20, 0x02, 0x41, 0x10, 0x6A, 0x21, 0x07, 0x20, 0x02, 0x28, 0x02, - 0x10, 0x22, 0x00, 0x0D, 0x00, 0x0B, 0x20, 0x05, 0x41, 0x00, 0x36, 0x02, 0x00, 0x0C, 0x08, 0x0B, 0x02, 0x40, - 0x41, 0x00, 0x28, 0x02, 0xB4, 0x8A, 0x80, 0x80, 0x00, 0x22, 0x02, 0x20, 0x04, 0x49, 0x0D, 0x00, 0x41, 0x00, - 0x28, 0x02, 0xC0, 0x8A, 0x80, 0x80, 0x00, 0x21, 0x03, 0x02, 0x40, 0x02, 0x40, 0x20, 0x02, 0x20, 0x04, 0x6B, - 0x22, 0x00, 0x41, 0x10, 0x49, 0x0D, 0x00, 0x20, 0x03, 0x20, 0x04, 0x6A, 0x22, 0x07, 0x20, 0x00, 0x41, 0x01, - 0x72, 0x36, 0x02, 0x04, 0x20, 0x03, 0x20, 0x02, 0x6A, 0x20, 0x00, 0x36, 0x02, 0x00, 0x20, 0x03, 0x20, 0x04, - 0x41, 0x03, 0x72, 0x36, 0x02, 0x04, 0x0C, 0x01, 0x0B, 0x20, 0x03, 0x20, 0x02, 0x41, 0x03, 0x72, 0x36, 0x02, - 0x04, 0x20, 0x03, 0x20, 0x02, 0x6A, 0x22, 0x02, 0x20, 0x02, 0x28, 0x02, 0x04, 0x41, 0x01, 0x72, 0x36, 0x02, - 0x04, 0x41, 0x00, 0x21, 0x07, 0x41, 0x00, 0x21, 0x00, 0x0B, 0x41, 0x00, 0x20, 0x00, 0x36, 0x02, 0xB4, 0x8A, - 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x07, 0x36, 0x02, 0xC0, 0x8A, 0x80, 0x80, 0x00, 0x20, 0x03, 0x41, 0x08, - 0x6A, 0x21, 0x02, 0x0C, 0x0A, 0x0B, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xB8, 0x8A, 0x80, 0x80, 0x00, 0x22, - 0x02, 0x20, 0x04, 0x4D, 0x0D, 0x00, 0x41, 0x00, 0x28, 0x02, 0xC4, 0x8A, 0x80, 0x80, 0x00, 0x22, 0x03, 0x20, - 0x04, 0x6A, 0x22, 0x00, 0x20, 0x02, 0x20, 0x04, 0x6B, 0x22, 0x02, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x41, - 0x00, 0x20, 0x02, 0x36, 0x02, 0xB8, 0x8A, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x00, 0x36, 0x02, 0xC4, 0x8A, - 0x80, 0x80, 0x00, 0x20, 0x03, 0x20, 0x04, 0x41, 0x03, 0x72, 0x36, 0x02, 0x04, 0x20, 0x03, 0x41, 0x08, 0x6A, - 0x21, 0x02, 0x0C, 0x0A, 0x0B, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0x94, 0x8A, 0x80, 0x80, 0x00, 0x0D, 0x00, - 0x02, 0x40, 0x41, 0x00, 0x41, 0x01, 0xFE, 0x41, 0x02, 0x88, 0x8E, 0x80, 0x80, 0x00, 0x45, 0x0D, 0x00, 0x41, - 0x01, 0x21, 0x02, 0x03, 0x40, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0x88, 0x8E, 0x80, 0x80, 0x00, 0x0D, 0x00, - 0x41, 0x00, 0x41, 0x01, 0xFE, 0x41, 0x02, 0x88, 0x8E, 0x80, 0x80, 0x00, 0x45, 0x0D, 0x02, 0x0B, 0x02, 0x40, - 0x20, 0x02, 0x41, 0x3F, 0x71, 0x0D, 0x00, 0x10, 0x8C, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x0B, 0x20, 0x02, 0x41, - 0x01, 0x6A, 0x21, 0x02, 0x0C, 0x00, 0x0B, 0x0B, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0x94, 0x8A, 0x80, 0x80, - 0x00, 0x0D, 0x00, 0x41, 0x00, 0x41, 0x02, 0x36, 0x02, 0xA8, 0x8A, 0x80, 0x80, 0x00, 0x41, 0x00, 0x42, 0x7F, - 0x37, 0x02, 0xA0, 0x8A, 0x80, 0x80, 0x00, 0x41, 0x00, 0x42, 0x80, 0x80, 0x84, 0x80, 0x80, 0x80, 0xC0, 0x00, - 0x37, 0x02, 0x98, 0x8A, 0x80, 0x80, 0x00, 0x41, 0x00, 0x42, 0x02, 0x37, 0x02, 0xE8, 0x8D, 0x80, 0x80, 0x00, - 0x41, 0x00, 0x20, 0x01, 0x41, 0x0C, 0x6A, 0x41, 0x70, 0x71, 0x41, 0xD8, 0xAA, 0xD5, 0xAA, 0x05, 0x73, 0x36, - 0x02, 0x94, 0x8A, 0x80, 0x80, 0x00, 0x0B, 0x41, 0x00, 0x41, 0x00, 0xFE, 0x17, 0x02, 0x88, 0x8E, 0x80, 0x80, - 0x00, 0x0B, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0x9C, 0x8A, 0x80, 0x80, 0x00, 0x22, 0x02, 0x20, 0x04, 0x41, - 0xC7, 0x00, 0x6A, 0x22, 0x0B, 0x6A, 0x41, 0x00, 0x20, 0x02, 0x6B, 0x71, 0x22, 0x08, 0x20, 0x04, 0x4B, 0x0D, - 0x00, 0x23, 0x81, 0x80, 0x80, 0x80, 0x00, 0x41, 0x80, 0x80, 0x80, 0x80, 0x00, 0x6A, 0x41, 0x30, 0x36, 0x02, - 0x00, 0x41, 0x00, 0x21, 0x02, 0x0C, 0x0A, 0x0B, 0x41, 0x00, 0x21, 0x05, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, - 0xE4, 0x8D, 0x80, 0x80, 0x00, 0x22, 0x02, 0x45, 0x0D, 0x00, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xDC, 0x8D, - 0x80, 0x80, 0x00, 0x22, 0x03, 0x20, 0x08, 0x6A, 0x22, 0x00, 0x20, 0x03, 0x4D, 0x0D, 0x00, 0x20, 0x00, 0x20, - 0x02, 0x4D, 0x0D, 0x01, 0x0B, 0x23, 0x81, 0x80, 0x80, 0x80, 0x00, 0x41, 0x80, 0x80, 0x80, 0x80, 0x00, 0x6A, - 0x41, 0x30, 0x36, 0x02, 0x00, 0x41, 0x00, 0x21, 0x02, 0x0C, 0x0A, 0x0B, 0x41, 0x7F, 0x21, 0x07, 0x41, 0x00, - 0x2D, 0x00, 0xE8, 0x8D, 0x80, 0x80, 0x00, 0x41, 0x04, 0x71, 0x0D, 0x06, 0x41, 0x00, 0x21, 0x03, 0x02, 0x40, - 0x41, 0x00, 0x28, 0x02, 0xC4, 0x8A, 0x80, 0x80, 0x00, 0x22, 0x02, 0x45, 0x0D, 0x00, 0x41, 0xF0, 0x8D, 0x80, - 0x80, 0x00, 0x21, 0x03, 0x03, 0x40, 0x02, 0x40, 0x20, 0x03, 0x28, 0x02, 0x00, 0x22, 0x00, 0x20, 0x02, 0x4B, - 0x0D, 0x00, 0x20, 0x00, 0x20, 0x03, 0x28, 0x02, 0x04, 0x6A, 0x20, 0x02, 0x4B, 0x0D, 0x02, 0x0B, 0x20, 0x03, - 0x28, 0x02, 0x08, 0x22, 0x03, 0x0D, 0x00, 0x0B, 0x41, 0x00, 0x21, 0x03, 0x0B, 0x02, 0x40, 0x41, 0x00, 0x41, - 0x01, 0xFE, 0x41, 0x02, 0x88, 0x8E, 0x80, 0x80, 0x00, 0x45, 0x0D, 0x00, 0x41, 0x01, 0x21, 0x02, 0x03, 0x40, - 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0x88, 0x8E, 0x80, 0x80, 0x00, 0x0D, 0x00, 0x41, 0x00, 0x41, 0x01, 0xFE, - 0x41, 0x02, 0x88, 0x8E, 0x80, 0x80, 0x00, 0x45, 0x0D, 0x02, 0x0B, 0x02, 0x40, 0x20, 0x02, 0x41, 0x3F, 0x71, - 0x0D, 0x00, 0x10, 0x8C, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x0B, 0x20, 0x02, 0x41, 0x01, 0x6A, 0x21, 0x02, 0x0C, - 0x00, 0x0B, 0x0B, 0x02, 0x40, 0x02, 0x40, 0x20, 0x03, 0x0D, 0x00, 0x41, 0x00, 0x21, 0x02, 0x41, 0x00, 0x10, - 0x9E, 0x80, 0x80, 0x80, 0x00, 0x22, 0x07, 0x41, 0x7F, 0x46, 0x0D, 0x06, 0x20, 0x08, 0x21, 0x05, 0x02, 0x40, - 0x41, 0x00, 0x28, 0x02, 0x98, 0x8A, 0x80, 0x80, 0x00, 0x22, 0x02, 0x41, 0x7F, 0x6A, 0x22, 0x03, 0x20, 0x07, - 0x71, 0x45, 0x0D, 0x00, 0x20, 0x08, 0x20, 0x07, 0x6B, 0x20, 0x03, 0x20, 0x07, 0x6A, 0x41, 0x00, 0x20, 0x02, - 0x6B, 0x71, 0x6A, 0x21, 0x05, 0x0B, 0x02, 0x40, 0x20, 0x05, 0x20, 0x04, 0x4B, 0x0D, 0x00, 0x41, 0x00, 0x21, - 0x02, 0x0C, 0x07, 0x0B, 0x02, 0x40, 0x20, 0x05, 0x41, 0xFE, 0xFF, 0xFF, 0xFF, 0x07, 0x4D, 0x0D, 0x00, 0x41, - 0x00, 0x21, 0x02, 0x0C, 0x07, 0x0B, 0x41, 0x00, 0x21, 0x02, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xE4, 0x8D, - 0x80, 0x80, 0x00, 0x22, 0x03, 0x45, 0x0D, 0x00, 0x41, 0x00, 0x28, 0x02, 0xDC, 0x8D, 0x80, 0x80, 0x00, 0x22, - 0x00, 0x20, 0x05, 0x6A, 0x22, 0x06, 0x20, 0x00, 0x4D, 0x0D, 0x07, 0x20, 0x06, 0x20, 0x03, 0x4B, 0x0D, 0x07, - 0x0B, 0x20, 0x05, 0x10, 0x9E, 0x80, 0x80, 0x80, 0x00, 0x22, 0x03, 0x20, 0x07, 0x47, 0x0D, 0x01, 0x0C, 0x07, - 0x0B, 0x41, 0x00, 0x21, 0x02, 0x20, 0x0B, 0x41, 0x00, 0x28, 0x02, 0xB8, 0x8A, 0x80, 0x80, 0x00, 0x6B, 0x41, - 0x00, 0x28, 0x02, 0x9C, 0x8A, 0x80, 0x80, 0x00, 0x22, 0x00, 0x6A, 0x41, 0x00, 0x20, 0x00, 0x6B, 0x71, 0x22, - 0x05, 0x41, 0xFE, 0xFF, 0xFF, 0xFF, 0x07, 0x4B, 0x0D, 0x05, 0x20, 0x05, 0x10, 0x9E, 0x80, 0x80, 0x80, 0x00, - 0x22, 0x07, 0x20, 0x03, 0x28, 0x02, 0x00, 0x20, 0x03, 0x28, 0x02, 0x04, 0x6A, 0x46, 0x0D, 0x04, 0x20, 0x07, - 0x21, 0x03, 0x0B, 0x41, 0x00, 0x21, 0x02, 0x02, 0x40, 0x20, 0x05, 0x20, 0x04, 0x41, 0xC8, 0x00, 0x6A, 0x4F, - 0x0D, 0x00, 0x20, 0x03, 0x41, 0x7F, 0x46, 0x0D, 0x00, 0x02, 0x40, 0x20, 0x0B, 0x20, 0x05, 0x6B, 0x41, 0x00, - 0x28, 0x02, 0x9C, 0x8A, 0x80, 0x80, 0x00, 0x22, 0x02, 0x6A, 0x41, 0x00, 0x20, 0x02, 0x6B, 0x71, 0x22, 0x02, - 0x41, 0xFE, 0xFF, 0xFF, 0xFF, 0x07, 0x4D, 0x0D, 0x00, 0x20, 0x03, 0x21, 0x07, 0x0C, 0x07, 0x0B, 0x02, 0x40, - 0x20, 0x02, 0x10, 0x9E, 0x80, 0x80, 0x80, 0x00, 0x41, 0x7F, 0x46, 0x0D, 0x00, 0x20, 0x02, 0x20, 0x05, 0x6A, - 0x21, 0x05, 0x20, 0x03, 0x21, 0x07, 0x0C, 0x07, 0x0B, 0x41, 0x00, 0x20, 0x05, 0x6B, 0x10, 0x9E, 0x80, 0x80, - 0x80, 0x00, 0x1A, 0x41, 0x00, 0x21, 0x02, 0x0C, 0x05, 0x0B, 0x20, 0x03, 0x21, 0x07, 0x20, 0x03, 0x41, 0x7F, - 0x47, 0x0D, 0x05, 0x0C, 0x04, 0x0B, 0x00, 0x0B, 0x41, 0x00, 0x21, 0x02, 0x0C, 0x06, 0x0B, 0x41, 0x00, 0x21, - 0x02, 0x0C, 0x04, 0x0B, 0x20, 0x05, 0x21, 0x02, 0x20, 0x07, 0x41, 0x7F, 0x47, 0x0D, 0x01, 0x0B, 0x41, 0x00, - 0x41, 0x00, 0x28, 0x02, 0xE8, 0x8D, 0x80, 0x80, 0x00, 0x41, 0x04, 0x72, 0x36, 0x02, 0xE8, 0x8D, 0x80, 0x80, - 0x00, 0x41, 0x7F, 0x21, 0x07, 0x20, 0x02, 0x21, 0x05, 0x0B, 0x41, 0x00, 0x41, 0x00, 0xFE, 0x17, 0x02, 0x88, - 0x8E, 0x80, 0x80, 0x00, 0x0B, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x08, 0x41, 0xFE, 0xFF, 0xFF, 0xFF, - 0x07, 0x4B, 0x0D, 0x00, 0x20, 0x07, 0x41, 0x7F, 0x47, 0x0D, 0x00, 0x02, 0x40, 0x41, 0x00, 0x41, 0x01, 0xFE, - 0x41, 0x02, 0x88, 0x8E, 0x80, 0x80, 0x00, 0x45, 0x0D, 0x00, 0x41, 0x01, 0x21, 0x02, 0x03, 0x40, 0x02, 0x40, - 0x41, 0x00, 0x28, 0x02, 0x88, 0x8E, 0x80, 0x80, 0x00, 0x0D, 0x00, 0x41, 0x00, 0x41, 0x01, 0xFE, 0x41, 0x02, - 0x88, 0x8E, 0x80, 0x80, 0x00, 0x45, 0x0D, 0x02, 0x0B, 0x02, 0x40, 0x20, 0x02, 0x41, 0x3F, 0x71, 0x0D, 0x00, - 0x10, 0x8C, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x0B, 0x20, 0x02, 0x41, 0x01, 0x6A, 0x21, 0x02, 0x0C, 0x00, 0x0B, - 0x0B, 0x20, 0x08, 0x10, 0x9E, 0x80, 0x80, 0x80, 0x00, 0x21, 0x07, 0x41, 0x00, 0x10, 0x9E, 0x80, 0x80, 0x80, - 0x00, 0x21, 0x02, 0x41, 0x00, 0x41, 0x00, 0xFE, 0x17, 0x02, 0x88, 0x8E, 0x80, 0x80, 0x00, 0x20, 0x07, 0x41, - 0x7F, 0x46, 0x0D, 0x02, 0x20, 0x02, 0x41, 0x7F, 0x46, 0x0D, 0x02, 0x20, 0x07, 0x20, 0x02, 0x4F, 0x0D, 0x02, - 0x20, 0x02, 0x20, 0x07, 0x6B, 0x22, 0x05, 0x20, 0x04, 0x41, 0x38, 0x6A, 0x4B, 0x0D, 0x01, 0x0C, 0x02, 0x0B, - 0x20, 0x07, 0x41, 0x7F, 0x46, 0x0D, 0x01, 0x0B, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0xDC, 0x8D, 0x80, 0x80, - 0x00, 0x20, 0x05, 0x6A, 0x22, 0x02, 0x36, 0x02, 0xDC, 0x8D, 0x80, 0x80, 0x00, 0x02, 0x40, 0x20, 0x02, 0x41, - 0x00, 0x28, 0x02, 0xE0, 0x8D, 0x80, 0x80, 0x00, 0x4D, 0x0D, 0x00, 0x41, 0x00, 0x20, 0x02, 0x36, 0x02, 0xE0, - 0x8D, 0x80, 0x80, 0x00, 0x0B, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x41, 0x00, 0x28, - 0x02, 0xC4, 0x8A, 0x80, 0x80, 0x00, 0x22, 0x03, 0x45, 0x0D, 0x00, 0x41, 0xF0, 0x8D, 0x80, 0x80, 0x00, 0x21, - 0x02, 0x03, 0x40, 0x20, 0x07, 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, 0x00, 0x20, 0x02, 0x28, 0x02, 0x04, 0x22, - 0x08, 0x6A, 0x46, 0x0D, 0x02, 0x20, 0x02, 0x28, 0x02, 0x08, 0x22, 0x02, 0x0D, 0x00, 0x0C, 0x03, 0x0B, 0x0B, - 0x02, 0x40, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xBC, 0x8A, 0x80, 0x80, 0x00, 0x22, 0x02, 0x45, 0x0D, 0x00, - 0x20, 0x07, 0x20, 0x02, 0x4F, 0x0D, 0x01, 0x0B, 0x41, 0x00, 0x20, 0x07, 0x36, 0x02, 0xBC, 0x8A, 0x80, 0x80, - 0x00, 0x0B, 0x41, 0x00, 0x21, 0x02, 0x41, 0x00, 0x20, 0x05, 0x36, 0x02, 0xF4, 0x8D, 0x80, 0x80, 0x00, 0x41, - 0x00, 0x20, 0x07, 0x36, 0x02, 0xF0, 0x8D, 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, 0x7F, 0x36, 0x02, 0xCC, 0x8A, - 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0x94, 0x8A, 0x80, 0x80, 0x00, 0x36, 0x02, 0xD0, 0x8A, - 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, 0x00, 0x36, 0x02, 0xFC, 0x8D, 0x80, 0x80, 0x00, 0x03, 0x40, 0x20, 0x02, - 0x41, 0xE8, 0x8A, 0x80, 0x80, 0x00, 0x6A, 0x20, 0x02, 0x41, 0xDC, 0x8A, 0x80, 0x80, 0x00, 0x6A, 0x22, 0x03, - 0x36, 0x02, 0x00, 0x20, 0x03, 0x20, 0x02, 0x41, 0xD4, 0x8A, 0x80, 0x80, 0x00, 0x6A, 0x22, 0x00, 0x36, 0x02, - 0x00, 0x20, 0x02, 0x41, 0xE0, 0x8A, 0x80, 0x80, 0x00, 0x6A, 0x20, 0x00, 0x36, 0x02, 0x00, 0x20, 0x02, 0x41, - 0xF0, 0x8A, 0x80, 0x80, 0x00, 0x6A, 0x20, 0x02, 0x41, 0xE4, 0x8A, 0x80, 0x80, 0x00, 0x6A, 0x22, 0x00, 0x36, - 0x02, 0x00, 0x20, 0x00, 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, 0x02, 0x41, 0xF8, 0x8A, 0x80, 0x80, 0x00, 0x6A, - 0x20, 0x02, 0x41, 0xEC, 0x8A, 0x80, 0x80, 0x00, 0x6A, 0x22, 0x03, 0x36, 0x02, 0x00, 0x20, 0x03, 0x20, 0x00, - 0x36, 0x02, 0x00, 0x20, 0x02, 0x41, 0xF4, 0x8A, 0x80, 0x80, 0x00, 0x6A, 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, - 0x02, 0x41, 0x20, 0x6A, 0x22, 0x02, 0x41, 0x80, 0x02, 0x47, 0x0D, 0x00, 0x0B, 0x20, 0x07, 0x41, 0x78, 0x20, - 0x07, 0x6B, 0x41, 0x0F, 0x71, 0x22, 0x02, 0x6A, 0x22, 0x03, 0x20, 0x05, 0x41, 0x48, 0x6A, 0x22, 0x00, 0x20, - 0x02, 0x6B, 0x22, 0x02, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0xA4, 0x8A, - 0x80, 0x80, 0x00, 0x36, 0x02, 0xC8, 0x8A, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x02, 0x36, 0x02, 0xB8, 0x8A, - 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x03, 0x36, 0x02, 0xC4, 0x8A, 0x80, 0x80, 0x00, 0x20, 0x07, 0x20, 0x00, - 0x6A, 0x41, 0x38, 0x36, 0x02, 0x04, 0x0C, 0x02, 0x0B, 0x20, 0x03, 0x20, 0x07, 0x4F, 0x0D, 0x00, 0x20, 0x03, - 0x20, 0x00, 0x49, 0x0D, 0x00, 0x20, 0x02, 0x28, 0x02, 0x0C, 0x41, 0x08, 0x71, 0x0D, 0x00, 0x20, 0x03, 0x41, - 0x78, 0x20, 0x03, 0x6B, 0x41, 0x0F, 0x71, 0x22, 0x00, 0x6A, 0x22, 0x07, 0x41, 0x00, 0x28, 0x02, 0xB8, 0x8A, - 0x80, 0x80, 0x00, 0x20, 0x05, 0x6A, 0x22, 0x0B, 0x20, 0x00, 0x6B, 0x22, 0x00, 0x41, 0x01, 0x72, 0x36, 0x02, - 0x04, 0x20, 0x02, 0x20, 0x08, 0x20, 0x05, 0x6A, 0x36, 0x02, 0x04, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0xA4, - 0x8A, 0x80, 0x80, 0x00, 0x36, 0x02, 0xC8, 0x8A, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x00, 0x36, 0x02, 0xB8, - 0x8A, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x07, 0x36, 0x02, 0xC4, 0x8A, 0x80, 0x80, 0x00, 0x20, 0x03, 0x20, - 0x0B, 0x6A, 0x41, 0x38, 0x36, 0x02, 0x04, 0x0C, 0x01, 0x0B, 0x02, 0x40, 0x20, 0x07, 0x41, 0x00, 0x28, 0x02, - 0xBC, 0x8A, 0x80, 0x80, 0x00, 0x4F, 0x0D, 0x00, 0x41, 0x00, 0x20, 0x07, 0x36, 0x02, 0xBC, 0x8A, 0x80, 0x80, - 0x00, 0x0B, 0x20, 0x07, 0x20, 0x05, 0x6A, 0x21, 0x00, 0x41, 0xF0, 0x8D, 0x80, 0x80, 0x00, 0x21, 0x02, 0x02, - 0x40, 0x02, 0x40, 0x03, 0x40, 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, 0x08, 0x20, 0x00, 0x46, 0x0D, 0x01, 0x20, - 0x02, 0x28, 0x02, 0x08, 0x22, 0x02, 0x0D, 0x00, 0x0C, 0x02, 0x0B, 0x0B, 0x20, 0x02, 0x2D, 0x00, 0x0C, 0x41, - 0x08, 0x71, 0x45, 0x0D, 0x02, 0x0B, 0x41, 0xF0, 0x8D, 0x80, 0x80, 0x00, 0x21, 0x02, 0x02, 0x40, 0x03, 0x40, - 0x02, 0x40, 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, 0x00, 0x20, 0x03, 0x4B, 0x0D, 0x00, 0x20, 0x00, 0x20, 0x02, - 0x28, 0x02, 0x04, 0x6A, 0x22, 0x00, 0x20, 0x03, 0x4B, 0x0D, 0x02, 0x0B, 0x20, 0x02, 0x28, 0x02, 0x08, 0x21, - 0x02, 0x0C, 0x00, 0x0B, 0x0B, 0x20, 0x07, 0x41, 0x78, 0x20, 0x07, 0x6B, 0x41, 0x0F, 0x71, 0x22, 0x02, 0x6A, - 0x22, 0x0B, 0x20, 0x05, 0x41, 0x48, 0x6A, 0x22, 0x08, 0x20, 0x02, 0x6B, 0x22, 0x02, 0x41, 0x01, 0x72, 0x36, - 0x02, 0x04, 0x20, 0x07, 0x20, 0x08, 0x6A, 0x41, 0x38, 0x36, 0x02, 0x04, 0x20, 0x03, 0x20, 0x00, 0x41, 0x37, - 0x20, 0x00, 0x6B, 0x41, 0x0F, 0x71, 0x6A, 0x41, 0x41, 0x6A, 0x22, 0x08, 0x20, 0x08, 0x20, 0x03, 0x41, 0x10, - 0x6A, 0x49, 0x1B, 0x22, 0x08, 0x41, 0x23, 0x36, 0x02, 0x04, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0xA4, 0x8A, - 0x80, 0x80, 0x00, 0x36, 0x02, 0xC8, 0x8A, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x02, 0x36, 0x02, 0xB8, 0x8A, - 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x0B, 0x36, 0x02, 0xC4, 0x8A, 0x80, 0x80, 0x00, 0x20, 0x08, 0x41, 0x10, - 0x6A, 0x41, 0x00, 0x29, 0x02, 0xF8, 0x8D, 0x80, 0x80, 0x00, 0x37, 0x02, 0x00, 0x20, 0x08, 0x41, 0x00, 0x29, - 0x02, 0xF0, 0x8D, 0x80, 0x80, 0x00, 0x37, 0x02, 0x08, 0x41, 0x00, 0x20, 0x08, 0x41, 0x08, 0x6A, 0x36, 0x02, - 0xF8, 0x8D, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x05, 0x36, 0x02, 0xF4, 0x8D, 0x80, 0x80, 0x00, 0x41, 0x00, - 0x20, 0x07, 0x36, 0x02, 0xF0, 0x8D, 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, 0x00, 0x36, 0x02, 0xFC, 0x8D, 0x80, - 0x80, 0x00, 0x20, 0x08, 0x41, 0x24, 0x6A, 0x21, 0x02, 0x03, 0x40, 0x20, 0x02, 0x41, 0x07, 0x36, 0x02, 0x00, - 0x20, 0x02, 0x41, 0x04, 0x6A, 0x22, 0x02, 0x20, 0x00, 0x49, 0x0D, 0x00, 0x0B, 0x20, 0x08, 0x20, 0x03, 0x46, - 0x0D, 0x00, 0x20, 0x08, 0x20, 0x08, 0x28, 0x02, 0x04, 0x41, 0x7E, 0x71, 0x36, 0x02, 0x04, 0x20, 0x08, 0x20, - 0x08, 0x20, 0x03, 0x6B, 0x22, 0x07, 0x36, 0x02, 0x00, 0x20, 0x03, 0x20, 0x07, 0x41, 0x01, 0x72, 0x36, 0x02, - 0x04, 0x02, 0x40, 0x02, 0x40, 0x20, 0x07, 0x41, 0xFF, 0x01, 0x4B, 0x0D, 0x00, 0x20, 0x07, 0x41, 0x78, 0x71, - 0x41, 0xD4, 0x8A, 0x80, 0x80, 0x00, 0x6A, 0x21, 0x02, 0x02, 0x40, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xAC, - 0x8A, 0x80, 0x80, 0x00, 0x22, 0x00, 0x41, 0x01, 0x20, 0x07, 0x41, 0x03, 0x76, 0x74, 0x22, 0x07, 0x71, 0x0D, - 0x00, 0x41, 0x00, 0x20, 0x00, 0x20, 0x07, 0x72, 0x36, 0x02, 0xAC, 0x8A, 0x80, 0x80, 0x00, 0x20, 0x02, 0x21, - 0x00, 0x0C, 0x01, 0x0B, 0x20, 0x02, 0x28, 0x02, 0x08, 0x21, 0x00, 0x0B, 0x20, 0x00, 0x20, 0x03, 0x36, 0x02, - 0x0C, 0x20, 0x02, 0x20, 0x03, 0x36, 0x02, 0x08, 0x41, 0x0C, 0x21, 0x07, 0x41, 0x08, 0x21, 0x08, 0x0C, 0x01, - 0x0B, 0x41, 0x1F, 0x21, 0x02, 0x02, 0x40, 0x20, 0x07, 0x41, 0xFF, 0xFF, 0xFF, 0x07, 0x4B, 0x0D, 0x00, 0x20, - 0x07, 0x41, 0x26, 0x20, 0x07, 0x41, 0x08, 0x76, 0x67, 0x22, 0x02, 0x6B, 0x76, 0x41, 0x01, 0x71, 0x20, 0x02, - 0x41, 0x01, 0x74, 0x6B, 0x41, 0x3E, 0x6A, 0x21, 0x02, 0x0B, 0x20, 0x03, 0x20, 0x02, 0x36, 0x02, 0x1C, 0x20, - 0x03, 0x42, 0x00, 0x37, 0x02, 0x10, 0x20, 0x02, 0x41, 0x02, 0x74, 0x41, 0xDC, 0x8C, 0x80, 0x80, 0x00, 0x6A, - 0x21, 0x00, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xB0, 0x8A, 0x80, 0x80, 0x00, 0x22, - 0x08, 0x41, 0x01, 0x20, 0x02, 0x74, 0x22, 0x05, 0x71, 0x0D, 0x00, 0x20, 0x00, 0x20, 0x03, 0x36, 0x02, 0x00, - 0x41, 0x00, 0x20, 0x08, 0x20, 0x05, 0x72, 0x36, 0x02, 0xB0, 0x8A, 0x80, 0x80, 0x00, 0x20, 0x03, 0x20, 0x00, - 0x36, 0x02, 0x18, 0x0C, 0x01, 0x0B, 0x20, 0x07, 0x41, 0x00, 0x41, 0x19, 0x20, 0x02, 0x41, 0x01, 0x76, 0x6B, - 0x20, 0x02, 0x41, 0x1F, 0x46, 0x1B, 0x74, 0x21, 0x02, 0x20, 0x00, 0x28, 0x02, 0x00, 0x21, 0x08, 0x03, 0x40, - 0x20, 0x08, 0x22, 0x00, 0x28, 0x02, 0x04, 0x41, 0x78, 0x71, 0x20, 0x07, 0x46, 0x0D, 0x02, 0x20, 0x02, 0x41, - 0x1D, 0x76, 0x21, 0x08, 0x20, 0x02, 0x41, 0x01, 0x74, 0x21, 0x02, 0x20, 0x00, 0x20, 0x08, 0x41, 0x04, 0x71, - 0x6A, 0x41, 0x10, 0x6A, 0x22, 0x05, 0x28, 0x02, 0x00, 0x22, 0x08, 0x0D, 0x00, 0x0B, 0x20, 0x05, 0x20, 0x03, - 0x36, 0x02, 0x00, 0x20, 0x03, 0x20, 0x00, 0x36, 0x02, 0x18, 0x0B, 0x41, 0x08, 0x21, 0x07, 0x41, 0x0C, 0x21, - 0x08, 0x20, 0x03, 0x21, 0x00, 0x20, 0x03, 0x21, 0x02, 0x0C, 0x01, 0x0B, 0x20, 0x00, 0x28, 0x02, 0x08, 0x21, - 0x02, 0x20, 0x00, 0x20, 0x03, 0x36, 0x02, 0x08, 0x20, 0x02, 0x20, 0x03, 0x36, 0x02, 0x0C, 0x20, 0x03, 0x20, - 0x02, 0x36, 0x02, 0x08, 0x41, 0x00, 0x21, 0x02, 0x41, 0x18, 0x21, 0x07, 0x41, 0x0C, 0x21, 0x08, 0x0B, 0x20, - 0x03, 0x20, 0x08, 0x6A, 0x20, 0x00, 0x36, 0x02, 0x00, 0x20, 0x03, 0x20, 0x07, 0x6A, 0x20, 0x02, 0x36, 0x02, - 0x00, 0x0B, 0x41, 0x00, 0x28, 0x02, 0xB8, 0x8A, 0x80, 0x80, 0x00, 0x22, 0x02, 0x20, 0x04, 0x4D, 0x0D, 0x01, - 0x41, 0x00, 0x28, 0x02, 0xC4, 0x8A, 0x80, 0x80, 0x00, 0x22, 0x03, 0x20, 0x04, 0x6A, 0x22, 0x00, 0x20, 0x02, - 0x20, 0x04, 0x6B, 0x22, 0x02, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x41, 0x00, 0x20, 0x02, 0x36, 0x02, 0xB8, - 0x8A, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x00, 0x36, 0x02, 0xC4, 0x8A, 0x80, 0x80, 0x00, 0x20, 0x03, 0x20, - 0x04, 0x41, 0x03, 0x72, 0x36, 0x02, 0x04, 0x20, 0x03, 0x41, 0x08, 0x6A, 0x21, 0x02, 0x0C, 0x04, 0x0B, 0x20, - 0x02, 0x20, 0x07, 0x36, 0x02, 0x00, 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, 0x04, 0x20, 0x05, 0x6A, 0x36, 0x02, - 0x04, 0x20, 0x07, 0x20, 0x08, 0x20, 0x04, 0x10, 0x8F, 0x80, 0x80, 0x80, 0x00, 0x21, 0x02, 0x0C, 0x03, 0x0B, - 0x23, 0x81, 0x80, 0x80, 0x80, 0x00, 0x41, 0x80, 0x80, 0x80, 0x80, 0x00, 0x6A, 0x41, 0x30, 0x36, 0x02, 0x00, - 0x41, 0x00, 0x21, 0x02, 0x0C, 0x02, 0x0B, 0x02, 0x40, 0x20, 0x0B, 0x45, 0x0D, 0x00, 0x02, 0x40, 0x02, 0x40, - 0x20, 0x08, 0x20, 0x08, 0x28, 0x02, 0x1C, 0x22, 0x07, 0x41, 0x02, 0x74, 0x41, 0xDC, 0x8C, 0x80, 0x80, 0x00, - 0x6A, 0x22, 0x00, 0x28, 0x02, 0x00, 0x47, 0x0D, 0x00, 0x20, 0x00, 0x20, 0x02, 0x36, 0x02, 0x00, 0x20, 0x02, - 0x0D, 0x01, 0x41, 0x00, 0x20, 0x0A, 0x41, 0x7E, 0x20, 0x07, 0x77, 0x71, 0x22, 0x0A, 0x36, 0x02, 0xB0, 0x8A, - 0x80, 0x80, 0x00, 0x0C, 0x02, 0x0B, 0x20, 0x0B, 0x41, 0x10, 0x41, 0x14, 0x20, 0x0B, 0x28, 0x02, 0x10, 0x20, - 0x08, 0x46, 0x1B, 0x6A, 0x20, 0x02, 0x36, 0x02, 0x00, 0x20, 0x02, 0x45, 0x0D, 0x01, 0x0B, 0x20, 0x02, 0x20, - 0x0B, 0x36, 0x02, 0x18, 0x02, 0x40, 0x20, 0x08, 0x28, 0x02, 0x10, 0x22, 0x00, 0x45, 0x0D, 0x00, 0x20, 0x02, - 0x20, 0x00, 0x36, 0x02, 0x10, 0x20, 0x00, 0x20, 0x02, 0x36, 0x02, 0x18, 0x0B, 0x20, 0x08, 0x28, 0x02, 0x14, - 0x22, 0x00, 0x45, 0x0D, 0x00, 0x20, 0x02, 0x20, 0x00, 0x36, 0x02, 0x14, 0x20, 0x00, 0x20, 0x02, 0x36, 0x02, - 0x18, 0x0B, 0x02, 0x40, 0x02, 0x40, 0x20, 0x03, 0x41, 0x0F, 0x4B, 0x0D, 0x00, 0x20, 0x08, 0x20, 0x03, 0x20, - 0x04, 0x72, 0x22, 0x02, 0x41, 0x03, 0x72, 0x36, 0x02, 0x04, 0x20, 0x08, 0x20, 0x02, 0x6A, 0x22, 0x02, 0x20, - 0x02, 0x28, 0x02, 0x04, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x0C, 0x01, 0x0B, 0x20, 0x08, 0x20, 0x04, 0x6A, - 0x22, 0x07, 0x20, 0x03, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x20, 0x08, 0x20, 0x04, 0x41, 0x03, 0x72, 0x36, - 0x02, 0x04, 0x20, 0x07, 0x20, 0x03, 0x6A, 0x20, 0x03, 0x36, 0x02, 0x00, 0x02, 0x40, 0x20, 0x03, 0x41, 0xFF, - 0x01, 0x4B, 0x0D, 0x00, 0x20, 0x03, 0x41, 0x78, 0x71, 0x41, 0xD4, 0x8A, 0x80, 0x80, 0x00, 0x6A, 0x21, 0x02, - 0x02, 0x40, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xAC, 0x8A, 0x80, 0x80, 0x00, 0x22, 0x04, 0x41, 0x01, 0x20, - 0x03, 0x41, 0x03, 0x76, 0x74, 0x22, 0x03, 0x71, 0x0D, 0x00, 0x41, 0x00, 0x20, 0x04, 0x20, 0x03, 0x72, 0x36, - 0x02, 0xAC, 0x8A, 0x80, 0x80, 0x00, 0x20, 0x02, 0x21, 0x03, 0x0C, 0x01, 0x0B, 0x20, 0x02, 0x28, 0x02, 0x08, - 0x21, 0x03, 0x0B, 0x20, 0x03, 0x20, 0x07, 0x36, 0x02, 0x0C, 0x20, 0x02, 0x20, 0x07, 0x36, 0x02, 0x08, 0x20, - 0x07, 0x20, 0x02, 0x36, 0x02, 0x0C, 0x20, 0x07, 0x20, 0x03, 0x36, 0x02, 0x08, 0x0C, 0x01, 0x0B, 0x41, 0x1F, - 0x21, 0x02, 0x02, 0x40, 0x20, 0x03, 0x41, 0xFF, 0xFF, 0xFF, 0x07, 0x4B, 0x0D, 0x00, 0x20, 0x03, 0x41, 0x26, - 0x20, 0x03, 0x41, 0x08, 0x76, 0x67, 0x22, 0x02, 0x6B, 0x76, 0x41, 0x01, 0x71, 0x20, 0x02, 0x41, 0x01, 0x74, - 0x6B, 0x41, 0x3E, 0x6A, 0x21, 0x02, 0x0B, 0x20, 0x07, 0x20, 0x02, 0x36, 0x02, 0x1C, 0x20, 0x07, 0x42, 0x00, - 0x37, 0x02, 0x10, 0x20, 0x02, 0x41, 0x02, 0x74, 0x41, 0xDC, 0x8C, 0x80, 0x80, 0x00, 0x6A, 0x21, 0x04, 0x02, - 0x40, 0x20, 0x0A, 0x41, 0x01, 0x20, 0x02, 0x74, 0x22, 0x00, 0x71, 0x0D, 0x00, 0x20, 0x04, 0x20, 0x07, 0x36, - 0x02, 0x00, 0x41, 0x00, 0x20, 0x0A, 0x20, 0x00, 0x72, 0x36, 0x02, 0xB0, 0x8A, 0x80, 0x80, 0x00, 0x20, 0x07, - 0x20, 0x04, 0x36, 0x02, 0x18, 0x20, 0x07, 0x20, 0x07, 0x36, 0x02, 0x08, 0x20, 0x07, 0x20, 0x07, 0x36, 0x02, - 0x0C, 0x0C, 0x01, 0x0B, 0x20, 0x03, 0x41, 0x00, 0x41, 0x19, 0x20, 0x02, 0x41, 0x01, 0x76, 0x6B, 0x20, 0x02, - 0x41, 0x1F, 0x46, 0x1B, 0x74, 0x21, 0x02, 0x20, 0x04, 0x28, 0x02, 0x00, 0x21, 0x00, 0x02, 0x40, 0x03, 0x40, - 0x20, 0x00, 0x22, 0x04, 0x28, 0x02, 0x04, 0x41, 0x78, 0x71, 0x20, 0x03, 0x46, 0x0D, 0x01, 0x20, 0x02, 0x41, - 0x1D, 0x76, 0x21, 0x00, 0x20, 0x02, 0x41, 0x01, 0x74, 0x21, 0x02, 0x20, 0x04, 0x20, 0x00, 0x41, 0x04, 0x71, - 0x6A, 0x41, 0x10, 0x6A, 0x22, 0x05, 0x28, 0x02, 0x00, 0x22, 0x00, 0x0D, 0x00, 0x0B, 0x20, 0x05, 0x20, 0x07, - 0x36, 0x02, 0x00, 0x20, 0x07, 0x20, 0x04, 0x36, 0x02, 0x18, 0x20, 0x07, 0x20, 0x07, 0x36, 0x02, 0x0C, 0x20, - 0x07, 0x20, 0x07, 0x36, 0x02, 0x08, 0x0C, 0x01, 0x0B, 0x20, 0x04, 0x28, 0x02, 0x08, 0x22, 0x02, 0x20, 0x07, - 0x36, 0x02, 0x0C, 0x20, 0x04, 0x20, 0x07, 0x36, 0x02, 0x08, 0x20, 0x07, 0x41, 0x00, 0x36, 0x02, 0x18, 0x20, - 0x07, 0x20, 0x04, 0x36, 0x02, 0x0C, 0x20, 0x07, 0x20, 0x02, 0x36, 0x02, 0x08, 0x0B, 0x20, 0x08, 0x41, 0x08, - 0x6A, 0x21, 0x02, 0x0C, 0x01, 0x0B, 0x02, 0x40, 0x20, 0x0A, 0x45, 0x0D, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, - 0x07, 0x20, 0x07, 0x28, 0x02, 0x1C, 0x22, 0x08, 0x41, 0x02, 0x74, 0x41, 0xDC, 0x8C, 0x80, 0x80, 0x00, 0x6A, - 0x22, 0x00, 0x28, 0x02, 0x00, 0x47, 0x0D, 0x00, 0x20, 0x00, 0x20, 0x02, 0x36, 0x02, 0x00, 0x20, 0x02, 0x0D, - 0x01, 0x41, 0x00, 0x20, 0x09, 0x41, 0x7E, 0x20, 0x08, 0x77, 0x71, 0x36, 0x02, 0xB0, 0x8A, 0x80, 0x80, 0x00, - 0x0C, 0x02, 0x0B, 0x20, 0x0A, 0x41, 0x10, 0x41, 0x14, 0x20, 0x0A, 0x28, 0x02, 0x10, 0x20, 0x07, 0x46, 0x1B, - 0x6A, 0x20, 0x02, 0x36, 0x02, 0x00, 0x20, 0x02, 0x45, 0x0D, 0x01, 0x0B, 0x20, 0x02, 0x20, 0x0A, 0x36, 0x02, - 0x18, 0x02, 0x40, 0x20, 0x07, 0x28, 0x02, 0x10, 0x22, 0x00, 0x45, 0x0D, 0x00, 0x20, 0x02, 0x20, 0x00, 0x36, - 0x02, 0x10, 0x20, 0x00, 0x20, 0x02, 0x36, 0x02, 0x18, 0x0B, 0x20, 0x07, 0x28, 0x02, 0x14, 0x22, 0x00, 0x45, - 0x0D, 0x00, 0x20, 0x02, 0x20, 0x00, 0x36, 0x02, 0x14, 0x20, 0x00, 0x20, 0x02, 0x36, 0x02, 0x18, 0x0B, 0x02, - 0x40, 0x02, 0x40, 0x20, 0x03, 0x41, 0x0F, 0x4B, 0x0D, 0x00, 0x20, 0x07, 0x20, 0x03, 0x20, 0x04, 0x72, 0x22, - 0x02, 0x41, 0x03, 0x72, 0x36, 0x02, 0x04, 0x20, 0x07, 0x20, 0x02, 0x6A, 0x22, 0x02, 0x20, 0x02, 0x28, 0x02, - 0x04, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x0C, 0x01, 0x0B, 0x20, 0x07, 0x20, 0x04, 0x6A, 0x22, 0x00, 0x20, - 0x03, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x20, 0x07, 0x20, 0x04, 0x41, 0x03, 0x72, 0x36, 0x02, 0x04, 0x20, - 0x00, 0x20, 0x03, 0x6A, 0x20, 0x03, 0x36, 0x02, 0x00, 0x02, 0x40, 0x20, 0x06, 0x45, 0x0D, 0x00, 0x20, 0x06, - 0x41, 0x78, 0x71, 0x41, 0xD4, 0x8A, 0x80, 0x80, 0x00, 0x6A, 0x21, 0x04, 0x41, 0x00, 0x28, 0x02, 0xC0, 0x8A, - 0x80, 0x80, 0x00, 0x21, 0x02, 0x02, 0x40, 0x02, 0x40, 0x41, 0x01, 0x20, 0x06, 0x41, 0x03, 0x76, 0x74, 0x22, - 0x08, 0x20, 0x05, 0x71, 0x0D, 0x00, 0x41, 0x00, 0x20, 0x08, 0x20, 0x05, 0x72, 0x36, 0x02, 0xAC, 0x8A, 0x80, - 0x80, 0x00, 0x20, 0x04, 0x21, 0x08, 0x0C, 0x01, 0x0B, 0x20, 0x04, 0x28, 0x02, 0x08, 0x21, 0x08, 0x0B, 0x20, - 0x08, 0x20, 0x02, 0x36, 0x02, 0x0C, 0x20, 0x04, 0x20, 0x02, 0x36, 0x02, 0x08, 0x20, 0x02, 0x20, 0x04, 0x36, - 0x02, 0x0C, 0x20, 0x02, 0x20, 0x08, 0x36, 0x02, 0x08, 0x0B, 0x41, 0x00, 0x20, 0x00, 0x36, 0x02, 0xC0, 0x8A, - 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x03, 0x36, 0x02, 0xB4, 0x8A, 0x80, 0x80, 0x00, 0x0B, 0x20, 0x07, 0x41, - 0x08, 0x6A, 0x21, 0x02, 0x0B, 0x02, 0x40, 0x41, 0x00, 0x2D, 0x00, 0xE8, 0x8D, 0x80, 0x80, 0x00, 0x41, 0x02, - 0x71, 0x45, 0x0D, 0x00, 0x41, 0x00, 0x41, 0x00, 0xFE, 0x17, 0x02, 0xEC, 0x8D, 0x80, 0x80, 0x00, 0x0B, 0x20, - 0x01, 0x41, 0x10, 0x6A, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x02, 0x0B, 0x9C, 0x08, 0x01, 0x07, 0x7F, - 0x20, 0x00, 0x41, 0x78, 0x20, 0x00, 0x6B, 0x41, 0x0F, 0x71, 0x6A, 0x22, 0x03, 0x20, 0x02, 0x41, 0x03, 0x72, - 0x36, 0x02, 0x04, 0x20, 0x01, 0x41, 0x78, 0x20, 0x01, 0x6B, 0x41, 0x0F, 0x71, 0x6A, 0x22, 0x04, 0x20, 0x03, - 0x20, 0x02, 0x6A, 0x22, 0x05, 0x6B, 0x21, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x04, 0x41, 0x00, 0x28, 0x02, - 0xC4, 0x8A, 0x80, 0x80, 0x00, 0x47, 0x0D, 0x00, 0x41, 0x00, 0x20, 0x05, 0x36, 0x02, 0xC4, 0x8A, 0x80, 0x80, - 0x00, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0xB8, 0x8A, 0x80, 0x80, 0x00, 0x20, 0x00, 0x6A, 0x22, 0x02, 0x36, - 0x02, 0xB8, 0x8A, 0x80, 0x80, 0x00, 0x20, 0x05, 0x20, 0x02, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x0C, 0x01, - 0x0B, 0x02, 0x40, 0x20, 0x04, 0x41, 0x00, 0x28, 0x02, 0xC0, 0x8A, 0x80, 0x80, 0x00, 0x47, 0x0D, 0x00, 0x41, - 0x00, 0x20, 0x05, 0x36, 0x02, 0xC0, 0x8A, 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0xB4, 0x8A, - 0x80, 0x80, 0x00, 0x20, 0x00, 0x6A, 0x22, 0x02, 0x36, 0x02, 0xB4, 0x8A, 0x80, 0x80, 0x00, 0x20, 0x05, 0x20, - 0x02, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x20, 0x05, 0x20, 0x02, 0x6A, 0x20, 0x02, 0x36, 0x02, 0x00, 0x0C, - 0x01, 0x0B, 0x02, 0x40, 0x20, 0x04, 0x28, 0x02, 0x04, 0x22, 0x01, 0x41, 0x03, 0x71, 0x41, 0x01, 0x47, 0x0D, - 0x00, 0x20, 0x01, 0x41, 0x78, 0x71, 0x21, 0x06, 0x20, 0x04, 0x28, 0x02, 0x0C, 0x21, 0x02, 0x02, 0x40, 0x02, - 0x40, 0x20, 0x01, 0x41, 0xFF, 0x01, 0x4B, 0x0D, 0x00, 0x02, 0x40, 0x20, 0x02, 0x20, 0x04, 0x28, 0x02, 0x08, - 0x22, 0x07, 0x47, 0x0D, 0x00, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0xAC, 0x8A, 0x80, 0x80, 0x00, 0x41, 0x7E, - 0x20, 0x01, 0x41, 0x03, 0x76, 0x77, 0x71, 0x36, 0x02, 0xAC, 0x8A, 0x80, 0x80, 0x00, 0x0C, 0x02, 0x0B, 0x20, - 0x02, 0x20, 0x07, 0x36, 0x02, 0x08, 0x20, 0x07, 0x20, 0x02, 0x36, 0x02, 0x0C, 0x0C, 0x01, 0x0B, 0x20, 0x04, - 0x28, 0x02, 0x18, 0x21, 0x08, 0x02, 0x40, 0x02, 0x40, 0x20, 0x02, 0x20, 0x04, 0x46, 0x0D, 0x00, 0x20, 0x04, - 0x28, 0x02, 0x08, 0x22, 0x01, 0x20, 0x02, 0x36, 0x02, 0x0C, 0x20, 0x02, 0x20, 0x01, 0x36, 0x02, 0x08, 0x0C, - 0x01, 0x0B, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x04, 0x28, 0x02, 0x14, 0x22, 0x01, 0x45, 0x0D, 0x00, - 0x20, 0x04, 0x41, 0x14, 0x6A, 0x21, 0x07, 0x0C, 0x01, 0x0B, 0x20, 0x04, 0x28, 0x02, 0x10, 0x22, 0x01, 0x45, - 0x0D, 0x01, 0x20, 0x04, 0x41, 0x10, 0x6A, 0x21, 0x07, 0x0B, 0x03, 0x40, 0x20, 0x07, 0x21, 0x09, 0x20, 0x01, - 0x22, 0x02, 0x41, 0x14, 0x6A, 0x21, 0x07, 0x20, 0x02, 0x28, 0x02, 0x14, 0x22, 0x01, 0x0D, 0x00, 0x20, 0x02, - 0x41, 0x10, 0x6A, 0x21, 0x07, 0x20, 0x02, 0x28, 0x02, 0x10, 0x22, 0x01, 0x0D, 0x00, 0x0B, 0x20, 0x09, 0x41, - 0x00, 0x36, 0x02, 0x00, 0x0C, 0x01, 0x0B, 0x41, 0x00, 0x21, 0x02, 0x0B, 0x20, 0x08, 0x45, 0x0D, 0x00, 0x02, - 0x40, 0x02, 0x40, 0x20, 0x04, 0x20, 0x04, 0x28, 0x02, 0x1C, 0x22, 0x07, 0x41, 0x02, 0x74, 0x41, 0xDC, 0x8C, - 0x80, 0x80, 0x00, 0x6A, 0x22, 0x01, 0x28, 0x02, 0x00, 0x47, 0x0D, 0x00, 0x20, 0x01, 0x20, 0x02, 0x36, 0x02, - 0x00, 0x20, 0x02, 0x0D, 0x01, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0xB0, 0x8A, 0x80, 0x80, 0x00, 0x41, 0x7E, - 0x20, 0x07, 0x77, 0x71, 0x36, 0x02, 0xB0, 0x8A, 0x80, 0x80, 0x00, 0x0C, 0x02, 0x0B, 0x20, 0x08, 0x41, 0x10, - 0x41, 0x14, 0x20, 0x08, 0x28, 0x02, 0x10, 0x20, 0x04, 0x46, 0x1B, 0x6A, 0x20, 0x02, 0x36, 0x02, 0x00, 0x20, - 0x02, 0x45, 0x0D, 0x01, 0x0B, 0x20, 0x02, 0x20, 0x08, 0x36, 0x02, 0x18, 0x02, 0x40, 0x20, 0x04, 0x28, 0x02, - 0x10, 0x22, 0x01, 0x45, 0x0D, 0x00, 0x20, 0x02, 0x20, 0x01, 0x36, 0x02, 0x10, 0x20, 0x01, 0x20, 0x02, 0x36, - 0x02, 0x18, 0x0B, 0x20, 0x04, 0x28, 0x02, 0x14, 0x22, 0x01, 0x45, 0x0D, 0x00, 0x20, 0x02, 0x20, 0x01, 0x36, - 0x02, 0x14, 0x20, 0x01, 0x20, 0x02, 0x36, 0x02, 0x18, 0x0B, 0x20, 0x06, 0x20, 0x00, 0x6A, 0x21, 0x00, 0x20, - 0x04, 0x20, 0x06, 0x6A, 0x22, 0x04, 0x28, 0x02, 0x04, 0x21, 0x01, 0x0B, 0x20, 0x04, 0x20, 0x01, 0x41, 0x7E, - 0x71, 0x36, 0x02, 0x04, 0x20, 0x05, 0x20, 0x00, 0x6A, 0x20, 0x00, 0x36, 0x02, 0x00, 0x20, 0x05, 0x20, 0x00, - 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x02, 0x40, 0x20, 0x00, 0x41, 0xFF, 0x01, 0x4B, 0x0D, 0x00, 0x20, 0x00, - 0x41, 0x78, 0x71, 0x41, 0xD4, 0x8A, 0x80, 0x80, 0x00, 0x6A, 0x21, 0x02, 0x02, 0x40, 0x02, 0x40, 0x41, 0x00, - 0x28, 0x02, 0xAC, 0x8A, 0x80, 0x80, 0x00, 0x22, 0x01, 0x41, 0x01, 0x20, 0x00, 0x41, 0x03, 0x76, 0x74, 0x22, - 0x00, 0x71, 0x0D, 0x00, 0x41, 0x00, 0x20, 0x01, 0x20, 0x00, 0x72, 0x36, 0x02, 0xAC, 0x8A, 0x80, 0x80, 0x00, - 0x20, 0x02, 0x21, 0x00, 0x0C, 0x01, 0x0B, 0x20, 0x02, 0x28, 0x02, 0x08, 0x21, 0x00, 0x0B, 0x20, 0x00, 0x20, - 0x05, 0x36, 0x02, 0x0C, 0x20, 0x02, 0x20, 0x05, 0x36, 0x02, 0x08, 0x20, 0x05, 0x20, 0x02, 0x36, 0x02, 0x0C, - 0x20, 0x05, 0x20, 0x00, 0x36, 0x02, 0x08, 0x0C, 0x01, 0x0B, 0x41, 0x1F, 0x21, 0x02, 0x02, 0x40, 0x20, 0x00, - 0x41, 0xFF, 0xFF, 0xFF, 0x07, 0x4B, 0x0D, 0x00, 0x20, 0x00, 0x41, 0x26, 0x20, 0x00, 0x41, 0x08, 0x76, 0x67, - 0x22, 0x02, 0x6B, 0x76, 0x41, 0x01, 0x71, 0x20, 0x02, 0x41, 0x01, 0x74, 0x6B, 0x41, 0x3E, 0x6A, 0x21, 0x02, - 0x0B, 0x20, 0x05, 0x20, 0x02, 0x36, 0x02, 0x1C, 0x20, 0x05, 0x42, 0x00, 0x37, 0x02, 0x10, 0x20, 0x02, 0x41, - 0x02, 0x74, 0x41, 0xDC, 0x8C, 0x80, 0x80, 0x00, 0x6A, 0x21, 0x01, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xB0, - 0x8A, 0x80, 0x80, 0x00, 0x22, 0x07, 0x41, 0x01, 0x20, 0x02, 0x74, 0x22, 0x04, 0x71, 0x0D, 0x00, 0x20, 0x01, - 0x20, 0x05, 0x36, 0x02, 0x00, 0x41, 0x00, 0x20, 0x07, 0x20, 0x04, 0x72, 0x36, 0x02, 0xB0, 0x8A, 0x80, 0x80, - 0x00, 0x20, 0x05, 0x20, 0x01, 0x36, 0x02, 0x18, 0x20, 0x05, 0x20, 0x05, 0x36, 0x02, 0x08, 0x20, 0x05, 0x20, - 0x05, 0x36, 0x02, 0x0C, 0x0C, 0x01, 0x0B, 0x20, 0x00, 0x41, 0x00, 0x41, 0x19, 0x20, 0x02, 0x41, 0x01, 0x76, - 0x6B, 0x20, 0x02, 0x41, 0x1F, 0x46, 0x1B, 0x74, 0x21, 0x02, 0x20, 0x01, 0x28, 0x02, 0x00, 0x21, 0x07, 0x02, - 0x40, 0x03, 0x40, 0x20, 0x07, 0x22, 0x01, 0x28, 0x02, 0x04, 0x41, 0x78, 0x71, 0x20, 0x00, 0x46, 0x0D, 0x01, - 0x20, 0x02, 0x41, 0x1D, 0x76, 0x21, 0x07, 0x20, 0x02, 0x41, 0x01, 0x74, 0x21, 0x02, 0x20, 0x01, 0x20, 0x07, - 0x41, 0x04, 0x71, 0x6A, 0x41, 0x10, 0x6A, 0x22, 0x04, 0x28, 0x02, 0x00, 0x22, 0x07, 0x0D, 0x00, 0x0B, 0x20, - 0x04, 0x20, 0x05, 0x36, 0x02, 0x00, 0x20, 0x05, 0x20, 0x01, 0x36, 0x02, 0x18, 0x20, 0x05, 0x20, 0x05, 0x36, - 0x02, 0x0C, 0x20, 0x05, 0x20, 0x05, 0x36, 0x02, 0x08, 0x0C, 0x01, 0x0B, 0x20, 0x01, 0x28, 0x02, 0x08, 0x22, - 0x02, 0x20, 0x05, 0x36, 0x02, 0x0C, 0x20, 0x01, 0x20, 0x05, 0x36, 0x02, 0x08, 0x20, 0x05, 0x41, 0x00, 0x36, - 0x02, 0x18, 0x20, 0x05, 0x20, 0x01, 0x36, 0x02, 0x0C, 0x20, 0x05, 0x20, 0x02, 0x36, 0x02, 0x08, 0x0B, 0x20, - 0x03, 0x41, 0x08, 0x6A, 0x0B, 0x0A, 0x00, 0x20, 0x00, 0x10, 0x91, 0x80, 0x80, 0x80, 0x00, 0x0B, 0xF1, 0x0D, - 0x01, 0x07, 0x7F, 0x02, 0x40, 0x20, 0x00, 0x45, 0x0D, 0x00, 0x20, 0x00, 0x41, 0x78, 0x6A, 0x21, 0x01, 0x02, - 0x40, 0x41, 0x00, 0x2D, 0x00, 0xE8, 0x8D, 0x80, 0x80, 0x00, 0x41, 0x02, 0x71, 0x45, 0x0D, 0x00, 0x41, 0x00, - 0x41, 0x01, 0xFE, 0x41, 0x02, 0xEC, 0x8D, 0x80, 0x80, 0x00, 0x45, 0x0D, 0x00, 0x41, 0x01, 0x21, 0x02, 0x03, - 0x40, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xEC, 0x8D, 0x80, 0x80, 0x00, 0x0D, 0x00, 0x41, 0x00, 0x41, 0x01, - 0xFE, 0x41, 0x02, 0xEC, 0x8D, 0x80, 0x80, 0x00, 0x45, 0x0D, 0x02, 0x0B, 0x02, 0x40, 0x20, 0x02, 0x41, 0x3F, - 0x71, 0x0D, 0x00, 0x10, 0x8C, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x0B, 0x20, 0x02, 0x41, 0x01, 0x6A, 0x21, 0x02, - 0x0C, 0x00, 0x0B, 0x0B, 0x20, 0x01, 0x20, 0x00, 0x41, 0x7C, 0x6A, 0x28, 0x02, 0x00, 0x22, 0x00, 0x41, 0x78, - 0x71, 0x22, 0x02, 0x6A, 0x21, 0x03, 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x41, 0x01, 0x71, 0x0D, 0x00, 0x20, - 0x00, 0x41, 0x02, 0x71, 0x45, 0x0D, 0x01, 0x20, 0x01, 0x20, 0x01, 0x28, 0x02, 0x00, 0x22, 0x04, 0x6B, 0x22, - 0x01, 0x41, 0x00, 0x28, 0x02, 0xBC, 0x8A, 0x80, 0x80, 0x00, 0x49, 0x0D, 0x01, 0x20, 0x04, 0x20, 0x02, 0x6A, - 0x21, 0x02, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x01, 0x41, 0x00, 0x28, 0x02, 0xC0, 0x8A, - 0x80, 0x80, 0x00, 0x46, 0x0D, 0x00, 0x20, 0x01, 0x28, 0x02, 0x0C, 0x21, 0x00, 0x02, 0x40, 0x20, 0x04, 0x41, - 0xFF, 0x01, 0x4B, 0x0D, 0x00, 0x20, 0x00, 0x20, 0x01, 0x28, 0x02, 0x08, 0x22, 0x05, 0x47, 0x0D, 0x02, 0x41, - 0x00, 0x41, 0x00, 0x28, 0x02, 0xAC, 0x8A, 0x80, 0x80, 0x00, 0x41, 0x7E, 0x20, 0x04, 0x41, 0x03, 0x76, 0x77, - 0x71, 0x36, 0x02, 0xAC, 0x8A, 0x80, 0x80, 0x00, 0x0C, 0x05, 0x0B, 0x20, 0x01, 0x28, 0x02, 0x18, 0x21, 0x06, - 0x02, 0x40, 0x20, 0x00, 0x20, 0x01, 0x46, 0x0D, 0x00, 0x20, 0x01, 0x28, 0x02, 0x08, 0x22, 0x04, 0x20, 0x00, - 0x36, 0x02, 0x0C, 0x20, 0x00, 0x20, 0x04, 0x36, 0x02, 0x08, 0x0C, 0x04, 0x0B, 0x02, 0x40, 0x02, 0x40, 0x20, - 0x01, 0x28, 0x02, 0x14, 0x22, 0x04, 0x45, 0x0D, 0x00, 0x20, 0x01, 0x41, 0x14, 0x6A, 0x21, 0x05, 0x0C, 0x01, - 0x0B, 0x20, 0x01, 0x28, 0x02, 0x10, 0x22, 0x04, 0x45, 0x0D, 0x03, 0x20, 0x01, 0x41, 0x10, 0x6A, 0x21, 0x05, - 0x0B, 0x03, 0x40, 0x20, 0x05, 0x21, 0x07, 0x20, 0x04, 0x22, 0x00, 0x41, 0x14, 0x6A, 0x21, 0x05, 0x20, 0x00, - 0x28, 0x02, 0x14, 0x22, 0x04, 0x0D, 0x00, 0x20, 0x00, 0x41, 0x10, 0x6A, 0x21, 0x05, 0x20, 0x00, 0x28, 0x02, - 0x10, 0x22, 0x04, 0x0D, 0x00, 0x0B, 0x20, 0x07, 0x41, 0x00, 0x36, 0x02, 0x00, 0x0C, 0x03, 0x0B, 0x20, 0x03, - 0x28, 0x02, 0x04, 0x22, 0x00, 0x41, 0x03, 0x71, 0x41, 0x03, 0x47, 0x0D, 0x03, 0x20, 0x03, 0x20, 0x00, 0x41, - 0x7E, 0x71, 0x36, 0x02, 0x04, 0x41, 0x00, 0x20, 0x02, 0x36, 0x02, 0xB4, 0x8A, 0x80, 0x80, 0x00, 0x20, 0x03, - 0x20, 0x02, 0x36, 0x02, 0x00, 0x20, 0x01, 0x20, 0x02, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x0C, 0x04, 0x0B, - 0x20, 0x00, 0x20, 0x05, 0x36, 0x02, 0x08, 0x20, 0x05, 0x20, 0x00, 0x36, 0x02, 0x0C, 0x0C, 0x02, 0x0B, 0x41, - 0x00, 0x21, 0x00, 0x0B, 0x20, 0x06, 0x45, 0x0D, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x01, 0x20, 0x01, 0x28, - 0x02, 0x1C, 0x22, 0x05, 0x41, 0x02, 0x74, 0x41, 0xDC, 0x8C, 0x80, 0x80, 0x00, 0x6A, 0x22, 0x04, 0x28, 0x02, - 0x00, 0x47, 0x0D, 0x00, 0x20, 0x04, 0x20, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x0D, 0x01, 0x41, 0x00, 0x41, - 0x00, 0x28, 0x02, 0xB0, 0x8A, 0x80, 0x80, 0x00, 0x41, 0x7E, 0x20, 0x05, 0x77, 0x71, 0x36, 0x02, 0xB0, 0x8A, - 0x80, 0x80, 0x00, 0x0C, 0x02, 0x0B, 0x20, 0x06, 0x41, 0x10, 0x41, 0x14, 0x20, 0x06, 0x28, 0x02, 0x10, 0x20, - 0x01, 0x46, 0x1B, 0x6A, 0x20, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x45, 0x0D, 0x01, 0x0B, 0x20, 0x00, 0x20, - 0x06, 0x36, 0x02, 0x18, 0x02, 0x40, 0x20, 0x01, 0x28, 0x02, 0x10, 0x22, 0x04, 0x45, 0x0D, 0x00, 0x20, 0x00, - 0x20, 0x04, 0x36, 0x02, 0x10, 0x20, 0x04, 0x20, 0x00, 0x36, 0x02, 0x18, 0x0B, 0x20, 0x01, 0x28, 0x02, 0x14, - 0x22, 0x04, 0x45, 0x0D, 0x00, 0x20, 0x00, 0x20, 0x04, 0x36, 0x02, 0x14, 0x20, 0x04, 0x20, 0x00, 0x36, 0x02, - 0x18, 0x0B, 0x20, 0x01, 0x20, 0x03, 0x4F, 0x0D, 0x00, 0x20, 0x03, 0x28, 0x02, 0x04, 0x22, 0x04, 0x41, 0x01, - 0x71, 0x45, 0x0D, 0x00, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x04, 0x41, 0x02, - 0x71, 0x0D, 0x00, 0x02, 0x40, 0x20, 0x03, 0x41, 0x00, 0x28, 0x02, 0xC4, 0x8A, 0x80, 0x80, 0x00, 0x47, 0x0D, - 0x00, 0x41, 0x00, 0x20, 0x01, 0x36, 0x02, 0xC4, 0x8A, 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, - 0xB8, 0x8A, 0x80, 0x80, 0x00, 0x20, 0x02, 0x6A, 0x22, 0x02, 0x36, 0x02, 0xB8, 0x8A, 0x80, 0x80, 0x00, 0x20, - 0x01, 0x20, 0x02, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x20, 0x01, 0x41, 0x00, 0x28, 0x02, 0xC0, 0x8A, 0x80, - 0x80, 0x00, 0x47, 0x0D, 0x06, 0x41, 0x00, 0x41, 0x00, 0x36, 0x02, 0xB4, 0x8A, 0x80, 0x80, 0x00, 0x41, 0x00, - 0x41, 0x00, 0x36, 0x02, 0xC0, 0x8A, 0x80, 0x80, 0x00, 0x0C, 0x06, 0x0B, 0x02, 0x40, 0x20, 0x03, 0x41, 0x00, - 0x28, 0x02, 0xC0, 0x8A, 0x80, 0x80, 0x00, 0x47, 0x0D, 0x00, 0x41, 0x00, 0x20, 0x01, 0x36, 0x02, 0xC0, 0x8A, - 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0xB4, 0x8A, 0x80, 0x80, 0x00, 0x20, 0x02, 0x6A, 0x22, - 0x02, 0x36, 0x02, 0xB4, 0x8A, 0x80, 0x80, 0x00, 0x20, 0x01, 0x20, 0x02, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, - 0x20, 0x01, 0x20, 0x02, 0x6A, 0x20, 0x02, 0x36, 0x02, 0x00, 0x0C, 0x06, 0x0B, 0x20, 0x04, 0x41, 0x78, 0x71, - 0x20, 0x02, 0x6A, 0x21, 0x02, 0x20, 0x03, 0x28, 0x02, 0x0C, 0x21, 0x00, 0x02, 0x40, 0x20, 0x04, 0x41, 0xFF, - 0x01, 0x4B, 0x0D, 0x00, 0x02, 0x40, 0x20, 0x00, 0x20, 0x03, 0x28, 0x02, 0x08, 0x22, 0x05, 0x47, 0x0D, 0x00, - 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0xAC, 0x8A, 0x80, 0x80, 0x00, 0x41, 0x7E, 0x20, 0x04, 0x41, 0x03, 0x76, - 0x77, 0x71, 0x36, 0x02, 0xAC, 0x8A, 0x80, 0x80, 0x00, 0x0C, 0x05, 0x0B, 0x20, 0x00, 0x20, 0x05, 0x36, 0x02, - 0x08, 0x20, 0x05, 0x20, 0x00, 0x36, 0x02, 0x0C, 0x0C, 0x04, 0x0B, 0x20, 0x03, 0x28, 0x02, 0x18, 0x21, 0x06, - 0x02, 0x40, 0x20, 0x00, 0x20, 0x03, 0x46, 0x0D, 0x00, 0x20, 0x03, 0x28, 0x02, 0x08, 0x22, 0x04, 0x20, 0x00, - 0x36, 0x02, 0x0C, 0x20, 0x00, 0x20, 0x04, 0x36, 0x02, 0x08, 0x0C, 0x03, 0x0B, 0x02, 0x40, 0x02, 0x40, 0x20, - 0x03, 0x28, 0x02, 0x14, 0x22, 0x04, 0x45, 0x0D, 0x00, 0x20, 0x03, 0x41, 0x14, 0x6A, 0x21, 0x05, 0x0C, 0x01, - 0x0B, 0x20, 0x03, 0x28, 0x02, 0x10, 0x22, 0x04, 0x45, 0x0D, 0x02, 0x20, 0x03, 0x41, 0x10, 0x6A, 0x21, 0x05, - 0x0B, 0x03, 0x40, 0x20, 0x05, 0x21, 0x07, 0x20, 0x04, 0x22, 0x00, 0x41, 0x14, 0x6A, 0x21, 0x05, 0x20, 0x00, - 0x28, 0x02, 0x14, 0x22, 0x04, 0x0D, 0x00, 0x20, 0x00, 0x41, 0x10, 0x6A, 0x21, 0x05, 0x20, 0x00, 0x28, 0x02, - 0x10, 0x22, 0x04, 0x0D, 0x00, 0x0B, 0x20, 0x07, 0x41, 0x00, 0x36, 0x02, 0x00, 0x0C, 0x02, 0x0B, 0x20, 0x03, - 0x20, 0x04, 0x41, 0x7E, 0x71, 0x36, 0x02, 0x04, 0x20, 0x01, 0x20, 0x02, 0x6A, 0x20, 0x02, 0x36, 0x02, 0x00, - 0x20, 0x01, 0x20, 0x02, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x0C, 0x03, 0x0B, 0x41, 0x00, 0x21, 0x00, 0x0B, - 0x20, 0x06, 0x45, 0x0D, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x03, 0x20, 0x03, 0x28, 0x02, 0x1C, 0x22, 0x05, - 0x41, 0x02, 0x74, 0x41, 0xDC, 0x8C, 0x80, 0x80, 0x00, 0x6A, 0x22, 0x04, 0x28, 0x02, 0x00, 0x47, 0x0D, 0x00, - 0x20, 0x04, 0x20, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x0D, 0x01, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0xB0, - 0x8A, 0x80, 0x80, 0x00, 0x41, 0x7E, 0x20, 0x05, 0x77, 0x71, 0x36, 0x02, 0xB0, 0x8A, 0x80, 0x80, 0x00, 0x0C, - 0x02, 0x0B, 0x20, 0x06, 0x41, 0x10, 0x41, 0x14, 0x20, 0x06, 0x28, 0x02, 0x10, 0x20, 0x03, 0x46, 0x1B, 0x6A, - 0x20, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x45, 0x0D, 0x01, 0x0B, 0x20, 0x00, 0x20, 0x06, 0x36, 0x02, 0x18, - 0x02, 0x40, 0x20, 0x03, 0x28, 0x02, 0x10, 0x22, 0x04, 0x45, 0x0D, 0x00, 0x20, 0x00, 0x20, 0x04, 0x36, 0x02, - 0x10, 0x20, 0x04, 0x20, 0x00, 0x36, 0x02, 0x18, 0x0B, 0x20, 0x03, 0x28, 0x02, 0x14, 0x22, 0x04, 0x45, 0x0D, - 0x00, 0x20, 0x00, 0x20, 0x04, 0x36, 0x02, 0x14, 0x20, 0x04, 0x20, 0x00, 0x36, 0x02, 0x18, 0x0B, 0x20, 0x01, - 0x20, 0x02, 0x6A, 0x20, 0x02, 0x36, 0x02, 0x00, 0x20, 0x01, 0x20, 0x02, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, - 0x20, 0x01, 0x41, 0x00, 0x28, 0x02, 0xC0, 0x8A, 0x80, 0x80, 0x00, 0x47, 0x0D, 0x00, 0x41, 0x00, 0x20, 0x02, - 0x36, 0x02, 0xB4, 0x8A, 0x80, 0x80, 0x00, 0x0C, 0x01, 0x0B, 0x02, 0x40, 0x20, 0x02, 0x41, 0xFF, 0x01, 0x4B, - 0x0D, 0x00, 0x20, 0x02, 0x41, 0x78, 0x71, 0x41, 0xD4, 0x8A, 0x80, 0x80, 0x00, 0x6A, 0x21, 0x00, 0x02, 0x40, - 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xAC, 0x8A, 0x80, 0x80, 0x00, 0x22, 0x04, 0x41, 0x01, 0x20, 0x02, 0x41, - 0x03, 0x76, 0x74, 0x22, 0x02, 0x71, 0x0D, 0x00, 0x41, 0x00, 0x20, 0x04, 0x20, 0x02, 0x72, 0x36, 0x02, 0xAC, - 0x8A, 0x80, 0x80, 0x00, 0x20, 0x00, 0x21, 0x02, 0x0C, 0x01, 0x0B, 0x20, 0x00, 0x28, 0x02, 0x08, 0x21, 0x02, - 0x0B, 0x20, 0x02, 0x20, 0x01, 0x36, 0x02, 0x0C, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x08, 0x20, 0x01, 0x20, - 0x00, 0x36, 0x02, 0x0C, 0x20, 0x01, 0x20, 0x02, 0x36, 0x02, 0x08, 0x0C, 0x01, 0x0B, 0x41, 0x1F, 0x21, 0x00, - 0x02, 0x40, 0x20, 0x02, 0x41, 0xFF, 0xFF, 0xFF, 0x07, 0x4B, 0x0D, 0x00, 0x20, 0x02, 0x41, 0x26, 0x20, 0x02, - 0x41, 0x08, 0x76, 0x67, 0x22, 0x00, 0x6B, 0x76, 0x41, 0x01, 0x71, 0x20, 0x00, 0x41, 0x01, 0x74, 0x6B, 0x41, - 0x3E, 0x6A, 0x21, 0x00, 0x0B, 0x20, 0x01, 0x20, 0x00, 0x36, 0x02, 0x1C, 0x20, 0x01, 0x42, 0x00, 0x37, 0x02, - 0x10, 0x20, 0x00, 0x41, 0x02, 0x74, 0x41, 0xDC, 0x8C, 0x80, 0x80, 0x00, 0x6A, 0x21, 0x03, 0x02, 0x40, 0x02, - 0x40, 0x02, 0x40, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xB0, 0x8A, 0x80, 0x80, 0x00, 0x22, 0x04, 0x41, 0x01, - 0x20, 0x00, 0x74, 0x22, 0x05, 0x71, 0x0D, 0x00, 0x41, 0x00, 0x20, 0x04, 0x20, 0x05, 0x72, 0x36, 0x02, 0xB0, - 0x8A, 0x80, 0x80, 0x00, 0x41, 0x08, 0x21, 0x02, 0x41, 0x18, 0x21, 0x00, 0x20, 0x03, 0x21, 0x05, 0x0C, 0x01, - 0x0B, 0x20, 0x02, 0x41, 0x00, 0x41, 0x19, 0x20, 0x00, 0x41, 0x01, 0x76, 0x6B, 0x20, 0x00, 0x41, 0x1F, 0x46, - 0x1B, 0x74, 0x21, 0x00, 0x20, 0x03, 0x28, 0x02, 0x00, 0x21, 0x05, 0x03, 0x40, 0x20, 0x05, 0x22, 0x04, 0x28, - 0x02, 0x04, 0x41, 0x78, 0x71, 0x20, 0x02, 0x46, 0x0D, 0x02, 0x20, 0x00, 0x41, 0x1D, 0x76, 0x21, 0x05, 0x20, - 0x00, 0x41, 0x01, 0x74, 0x21, 0x00, 0x20, 0x04, 0x20, 0x05, 0x41, 0x04, 0x71, 0x6A, 0x41, 0x10, 0x6A, 0x22, - 0x03, 0x28, 0x02, 0x00, 0x22, 0x05, 0x0D, 0x00, 0x0B, 0x41, 0x08, 0x21, 0x02, 0x41, 0x18, 0x21, 0x00, 0x20, - 0x04, 0x21, 0x05, 0x0B, 0x20, 0x01, 0x21, 0x04, 0x20, 0x01, 0x21, 0x07, 0x0C, 0x01, 0x0B, 0x20, 0x04, 0x28, - 0x02, 0x08, 0x22, 0x05, 0x20, 0x01, 0x36, 0x02, 0x0C, 0x41, 0x08, 0x21, 0x00, 0x20, 0x04, 0x41, 0x08, 0x6A, - 0x21, 0x03, 0x41, 0x00, 0x21, 0x07, 0x41, 0x18, 0x21, 0x02, 0x0B, 0x20, 0x03, 0x20, 0x01, 0x36, 0x02, 0x00, - 0x20, 0x01, 0x20, 0x00, 0x6A, 0x20, 0x05, 0x36, 0x02, 0x00, 0x20, 0x01, 0x20, 0x04, 0x36, 0x02, 0x0C, 0x20, - 0x01, 0x20, 0x02, 0x6A, 0x20, 0x07, 0x36, 0x02, 0x00, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0xCC, 0x8A, 0x80, - 0x80, 0x00, 0x41, 0x7F, 0x6A, 0x22, 0x02, 0x41, 0x7F, 0x20, 0x02, 0x1B, 0x36, 0x02, 0xCC, 0x8A, 0x80, 0x80, - 0x00, 0x0B, 0x41, 0x00, 0x2D, 0x00, 0xE8, 0x8D, 0x80, 0x80, 0x00, 0x41, 0x02, 0x71, 0x45, 0x0D, 0x00, 0x41, - 0x00, 0x41, 0x00, 0xFE, 0x17, 0x02, 0xEC, 0x8D, 0x80, 0x80, 0x00, 0x0B, 0x0B, 0x67, 0x02, 0x01, 0x7F, 0x01, - 0x7E, 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x0D, 0x00, 0x41, 0x00, 0x21, 0x02, 0x0C, 0x01, 0x0B, 0x20, 0x00, - 0xAD, 0x20, 0x01, 0xAD, 0x7E, 0x22, 0x03, 0xA7, 0x21, 0x02, 0x20, 0x01, 0x20, 0x00, 0x72, 0x41, 0x80, 0x80, - 0x04, 0x49, 0x0D, 0x00, 0x41, 0x7F, 0x20, 0x02, 0x20, 0x03, 0x42, 0x20, 0x88, 0xA7, 0x41, 0x00, 0x47, 0x1B, - 0x21, 0x02, 0x0B, 0x02, 0x40, 0x20, 0x02, 0x10, 0x8E, 0x80, 0x80, 0x80, 0x00, 0x22, 0x00, 0x45, 0x0D, 0x00, - 0x20, 0x00, 0x41, 0x7C, 0x6A, 0x2D, 0x00, 0x00, 0x41, 0x03, 0x71, 0x45, 0x0D, 0x00, 0x20, 0x00, 0x41, 0x00, - 0x20, 0x02, 0xFC, 0x0B, 0x00, 0x0B, 0x20, 0x00, 0x0B, 0x0B, 0x00, 0x20, 0x00, 0x10, 0x9B, 0x80, 0x80, 0x80, - 0x00, 0x00, 0x0B, 0xD5, 0x01, 0x01, 0x03, 0x7F, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x10, 0x6B, 0x22, - 0x00, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, - 0x00, 0x41, 0x08, 0x6A, 0x20, 0x00, 0x41, 0x0C, 0x6A, 0x10, 0x96, 0x80, 0x80, 0x80, 0x00, 0x0D, 0x00, 0x20, - 0x00, 0x28, 0x02, 0x08, 0x41, 0x01, 0x6A, 0x22, 0x01, 0x45, 0x0D, 0x01, 0x20, 0x00, 0x28, 0x02, 0x0C, 0x10, - 0x8D, 0x80, 0x80, 0x80, 0x00, 0x22, 0x02, 0x45, 0x0D, 0x02, 0x20, 0x01, 0x41, 0x04, 0x10, 0x92, 0x80, 0x80, - 0x80, 0x00, 0x22, 0x01, 0x45, 0x0D, 0x03, 0x20, 0x01, 0x20, 0x02, 0x10, 0x95, 0x80, 0x80, 0x80, 0x00, 0x0D, - 0x04, 0x20, 0x00, 0x28, 0x02, 0x08, 0x20, 0x01, 0x10, 0x8B, 0x80, 0x80, 0x80, 0x00, 0x21, 0x01, 0x20, 0x00, - 0x41, 0x10, 0x6A, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x01, 0x0F, 0x0B, 0x41, 0xC7, 0x00, 0x10, 0x93, - 0x80, 0x80, 0x80, 0x00, 0x00, 0x0B, 0x41, 0xC6, 0x00, 0x10, 0x93, 0x80, 0x80, 0x80, 0x00, 0x00, 0x0B, 0x41, - 0xC6, 0x00, 0x10, 0x93, 0x80, 0x80, 0x80, 0x00, 0x00, 0x0B, 0x20, 0x02, 0x10, 0x90, 0x80, 0x80, 0x80, 0x00, - 0x41, 0xC6, 0x00, 0x10, 0x93, 0x80, 0x80, 0x80, 0x00, 0x00, 0x0B, 0x20, 0x02, 0x10, 0x90, 0x80, 0x80, 0x80, - 0x00, 0x20, 0x01, 0x10, 0x90, 0x80, 0x80, 0x80, 0x00, 0x41, 0xC7, 0x00, 0x10, 0x93, 0x80, 0x80, 0x80, 0x00, - 0x00, 0x0B, 0x11, 0x00, 0x20, 0x00, 0x20, 0x01, 0x10, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0xFF, 0xFF, 0x03, - 0x71, 0x0B, 0x11, 0x00, 0x20, 0x00, 0x20, 0x01, 0x10, 0x81, 0x80, 0x80, 0x80, 0x00, 0x41, 0xFF, 0xFF, 0x03, - 0x71, 0x0B, 0x0F, 0x00, 0x20, 0x00, 0x10, 0x82, 0x80, 0x80, 0x80, 0x00, 0x41, 0xFF, 0xFF, 0x03, 0x71, 0x0B, - 0x11, 0x00, 0x20, 0x00, 0x20, 0x01, 0x10, 0x83, 0x80, 0x80, 0x80, 0x00, 0x41, 0xFF, 0xFF, 0x03, 0x71, 0x0B, - 0x15, 0x00, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x10, 0x84, 0x80, 0x80, 0x80, 0x00, 0x41, 0xFF, - 0xFF, 0x03, 0x71, 0x0B, 0x15, 0x00, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x10, 0x85, 0x80, 0x80, - 0x80, 0x00, 0x41, 0xFF, 0xFF, 0x03, 0x71, 0x0B, 0x0B, 0x00, 0x20, 0x00, 0x10, 0x86, 0x80, 0x80, 0x80, 0x00, - 0x00, 0x0B, 0x0D, 0x00, 0x10, 0x87, 0x80, 0x80, 0x80, 0x00, 0x41, 0xFF, 0xFF, 0x03, 0x71, 0x0B, 0x03, 0x00, - 0x00, 0x0B, 0x55, 0x00, 0x02, 0x40, 0x20, 0x00, 0x0D, 0x00, 0x3F, 0x00, 0x41, 0x10, 0x74, 0x0F, 0x0B, 0x02, - 0x40, 0x20, 0x00, 0x41, 0xFF, 0xFF, 0x03, 0x71, 0x0D, 0x00, 0x20, 0x00, 0x41, 0x7F, 0x4C, 0x0D, 0x00, 0x02, - 0x40, 0x20, 0x00, 0x41, 0x10, 0x76, 0x40, 0x00, 0x22, 0x00, 0x41, 0x7F, 0x47, 0x0D, 0x00, 0x23, 0x81, 0x80, - 0x80, 0x80, 0x00, 0x41, 0x80, 0x80, 0x80, 0x80, 0x00, 0x6A, 0x41, 0x30, 0x36, 0x02, 0x00, 0x41, 0x7F, 0x0F, - 0x0B, 0x20, 0x00, 0x41, 0x10, 0x74, 0x0F, 0x0B, 0x10, 0x9D, 0x80, 0x80, 0x80, 0x00, 0x00, 0x0B, 0xEC, 0x01, - 0x01, 0x03, 0x7F, 0x23, 0x81, 0x80, 0x80, 0x80, 0x00, 0x41, 0x84, 0x80, 0x80, 0x80, 0x00, 0x6A, 0x22, 0x00, - 0x20, 0x00, 0x36, 0x02, 0x00, 0x41, 0xD0, 0x96, 0x84, 0x80, 0x00, 0x21, 0x01, 0x02, 0x40, 0x02, 0x40, 0x41, - 0xD0, 0x96, 0x84, 0x80, 0x00, 0x45, 0x0D, 0x00, 0x41, 0xD0, 0x96, 0x84, 0x80, 0x00, 0x41, 0xD0, 0x96, 0x80, - 0x80, 0x00, 0x6B, 0x21, 0x02, 0x0C, 0x01, 0x0B, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x21, 0x02, 0x41, 0xD0, - 0x96, 0x84, 0x80, 0x00, 0x41, 0xCC, 0x96, 0x80, 0x80, 0x00, 0x6B, 0x41, 0x80, 0x88, 0x80, 0x80, 0x00, 0x20, - 0x02, 0x41, 0x80, 0x88, 0x80, 0x80, 0x00, 0x4B, 0x22, 0x01, 0x1B, 0x21, 0x02, 0x41, 0xD0, 0x96, 0x84, 0x80, - 0x00, 0x41, 0x80, 0x88, 0x80, 0x80, 0x00, 0x20, 0x01, 0x1B, 0x21, 0x01, 0x0B, 0x20, 0x00, 0x41, 0x02, 0x36, - 0x02, 0x1C, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x38, 0x20, 0x00, 0x20, 0x02, 0x36, 0x02, 0x34, 0x20, 0x00, - 0x20, 0x01, 0x36, 0x02, 0x30, 0x20, 0x00, 0x41, 0x98, 0x8E, 0x80, 0x80, 0x00, 0x36, 0x02, 0x5C, 0x20, 0x00, - 0x41, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x36, 0x02, 0x14, 0x20, 0x00, 0x20, 0x00, 0x41, 0xC8, 0x00, 0x6A, 0x36, - 0x02, 0x48, 0x20, 0x00, 0x41, 0x00, 0x28, 0x02, 0x8C, 0x8E, 0x80, 0x80, 0x00, 0x36, 0x02, 0x0C, 0x41, 0x00, - 0x20, 0x02, 0x41, 0x80, 0x80, 0x80, 0x04, 0x20, 0x02, 0x41, 0x80, 0x80, 0x80, 0x04, 0x49, 0x1B, 0x36, 0x02, - 0x8C, 0x8A, 0x80, 0x80, 0x00, 0x20, 0x00, 0x20, 0x00, 0x36, 0x02, 0x08, 0x20, 0x00, 0x20, 0x00, 0x36, 0x02, - 0x04, 0x0B, 0x02, 0x00, 0x0B, 0x0E, 0x00, 0x10, 0xA0, 0x80, 0x80, 0x80, 0x00, 0x10, 0xA5, 0x80, 0x80, 0x80, - 0x00, 0x0B, 0xB5, 0x01, 0x01, 0x04, 0x7F, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x44, 0x41, 0xFF, 0xFF, 0xFF, - 0xFF, 0x7B, 0x71, 0x23, 0x81, 0x80, 0x80, 0x80, 0x00, 0x41, 0x84, 0x80, 0x80, 0x80, 0x00, 0x6A, 0x28, 0x02, - 0x14, 0x22, 0x01, 0x47, 0x0D, 0x00, 0x41, 0x00, 0x0F, 0x0B, 0x41, 0x01, 0x21, 0x02, 0x02, 0x40, 0x20, 0x00, - 0x41, 0xC4, 0x00, 0x6A, 0x22, 0x03, 0x41, 0x00, 0x20, 0x01, 0xFE, 0x48, 0x02, 0x00, 0x45, 0x0D, 0x00, 0x20, - 0x03, 0x41, 0x00, 0x20, 0x01, 0x41, 0x80, 0x80, 0x80, 0x80, 0x04, 0x72, 0x22, 0x04, 0xFE, 0x48, 0x02, 0x00, - 0x22, 0x00, 0x45, 0x0D, 0x00, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x41, 0x80, 0x80, - 0x80, 0x80, 0x04, 0x71, 0x45, 0x0D, 0x00, 0x20, 0x00, 0x21, 0x01, 0x0C, 0x01, 0x0B, 0x20, 0x03, 0x20, 0x00, - 0x20, 0x00, 0x41, 0x80, 0x80, 0x80, 0x80, 0x04, 0x72, 0x22, 0x01, 0xFE, 0x48, 0x02, 0x00, 0x20, 0x00, 0x47, - 0x0D, 0x01, 0x0B, 0x20, 0x03, 0x41, 0x00, 0x20, 0x01, 0x41, 0x01, 0x10, 0xB7, 0x80, 0x80, 0x80, 0x00, 0x0B, - 0x20, 0x03, 0x41, 0x00, 0x20, 0x04, 0xFE, 0x48, 0x02, 0x00, 0x22, 0x00, 0x0D, 0x00, 0x0B, 0x41, 0x01, 0x21, - 0x02, 0x0B, 0x20, 0x02, 0x0B, 0x3B, 0x01, 0x01, 0x7F, 0x20, 0x00, 0x41, 0xC4, 0x00, 0x6A, 0x21, 0x00, 0x03, - 0x40, 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, 0x01, 0x20, 0x00, 0x20, 0x01, 0x41, 0x00, 0xFE, 0x48, 0x02, 0x00, - 0x47, 0x0D, 0x00, 0x0B, 0x02, 0x40, 0x20, 0x01, 0x41, 0x80, 0x80, 0x80, 0x80, 0x04, 0x71, 0x45, 0x0D, 0x00, - 0x20, 0x00, 0x41, 0x01, 0xFE, 0x00, 0x02, 0x00, 0x1A, 0x0B, 0x0B, 0x14, 0x00, 0x41, 0xB0, 0x8E, 0x80, 0x80, - 0x00, 0x10, 0xB6, 0x80, 0x80, 0x80, 0x00, 0x41, 0xB4, 0x8E, 0x80, 0x80, 0x00, 0x0B, 0xFB, 0x03, 0x01, 0x03, - 0x7F, 0x02, 0x40, 0x10, 0xA4, 0x80, 0x80, 0x80, 0x00, 0x28, 0x02, 0x00, 0x22, 0x00, 0x45, 0x0D, 0x00, 0x03, - 0x40, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x44, 0x41, 0x00, 0x48, 0x0D, 0x00, 0x20, 0x00, 0x10, 0xA2, 0x80, - 0x80, 0x80, 0x00, 0x1A, 0x0B, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x14, 0x20, 0x00, 0x28, 0x02, 0x18, 0x46, - 0x0D, 0x00, 0x20, 0x00, 0x41, 0x00, 0x41, 0x00, 0x20, 0x00, 0x28, 0x02, 0x20, 0x11, 0x80, 0x80, 0x80, 0x80, - 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x0B, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x04, 0x22, 0x01, 0x20, - 0x00, 0x28, 0x02, 0x08, 0x22, 0x02, 0x46, 0x0D, 0x00, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x6B, 0xAC, 0x41, - 0x01, 0x20, 0x00, 0x28, 0x02, 0x24, 0x11, 0x81, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x1A, - 0x0B, 0x20, 0x00, 0x28, 0x02, 0x34, 0x22, 0x00, 0x0D, 0x00, 0x0B, 0x0B, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, - 0xB8, 0x8E, 0x80, 0x80, 0x00, 0x22, 0x00, 0x45, 0x0D, 0x00, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x44, 0x41, - 0x00, 0x48, 0x0D, 0x00, 0x20, 0x00, 0x10, 0xA2, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x0B, 0x02, 0x40, 0x20, 0x00, - 0x28, 0x02, 0x14, 0x20, 0x00, 0x28, 0x02, 0x18, 0x46, 0x0D, 0x00, 0x20, 0x00, 0x41, 0x00, 0x41, 0x00, 0x20, - 0x00, 0x28, 0x02, 0x20, 0x11, 0x80, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x0B, 0x20, - 0x00, 0x28, 0x02, 0x04, 0x22, 0x01, 0x20, 0x00, 0x28, 0x02, 0x08, 0x22, 0x02, 0x46, 0x0D, 0x00, 0x20, 0x00, - 0x20, 0x01, 0x20, 0x02, 0x6B, 0xAC, 0x41, 0x01, 0x20, 0x00, 0x28, 0x02, 0x24, 0x11, 0x81, 0x80, 0x80, 0x80, - 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x0B, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0x88, 0x8A, 0x80, 0x80, - 0x00, 0x22, 0x00, 0x45, 0x0D, 0x00, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x44, 0x41, 0x00, 0x48, 0x0D, 0x00, - 0x20, 0x00, 0x10, 0xA2, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x0B, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x14, 0x20, - 0x00, 0x28, 0x02, 0x18, 0x46, 0x0D, 0x00, 0x20, 0x00, 0x41, 0x00, 0x41, 0x00, 0x20, 0x00, 0x28, 0x02, 0x20, - 0x11, 0x80, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x0B, 0x20, 0x00, 0x28, 0x02, 0x04, - 0x22, 0x01, 0x20, 0x00, 0x28, 0x02, 0x08, 0x22, 0x02, 0x46, 0x0D, 0x00, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, - 0x6B, 0xAC, 0x41, 0x01, 0x20, 0x00, 0x28, 0x02, 0x24, 0x11, 0x81, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, - 0x80, 0x00, 0x1A, 0x0B, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xB8, 0x8E, 0x80, 0x80, 0x00, 0x22, 0x00, 0x45, - 0x0D, 0x00, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x44, 0x41, 0x00, 0x48, 0x0D, 0x00, 0x20, 0x00, 0x10, 0xA2, - 0x80, 0x80, 0x80, 0x00, 0x1A, 0x0B, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x14, 0x20, 0x00, 0x28, 0x02, 0x18, - 0x46, 0x0D, 0x00, 0x20, 0x00, 0x41, 0x00, 0x41, 0x00, 0x20, 0x00, 0x28, 0x02, 0x20, 0x11, 0x80, 0x80, 0x80, - 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x0B, 0x20, 0x00, 0x28, 0x02, 0x04, 0x22, 0x01, 0x20, 0x00, - 0x28, 0x02, 0x08, 0x22, 0x02, 0x46, 0x0D, 0x00, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x6B, 0xAC, 0x41, 0x01, - 0x20, 0x00, 0x28, 0x02, 0x24, 0x11, 0x81, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x0B, - 0x0B, 0x5C, 0x01, 0x01, 0x7F, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0x40, 0x22, 0x01, 0x41, 0x7F, 0x6A, 0x20, - 0x01, 0x72, 0x36, 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, 0x01, 0x41, 0x08, 0x71, 0x45, - 0x0D, 0x00, 0x20, 0x00, 0x20, 0x01, 0x41, 0x20, 0x72, 0x36, 0x02, 0x00, 0x41, 0x7F, 0x0F, 0x0B, 0x20, 0x00, - 0x42, 0x00, 0x37, 0x02, 0x04, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0x28, 0x22, 0x01, 0x36, 0x02, 0x18, 0x20, - 0x00, 0x20, 0x01, 0x36, 0x02, 0x14, 0x20, 0x00, 0x20, 0x01, 0x20, 0x00, 0x28, 0x02, 0x2C, 0x6A, 0x36, 0x02, - 0x10, 0x41, 0x00, 0x0B, 0xD5, 0x02, 0x01, 0x06, 0x7F, 0x02, 0x40, 0x02, 0x40, 0x20, 0x03, 0x28, 0x02, 0x44, - 0x41, 0x00, 0x4E, 0x0D, 0x00, 0x41, 0x01, 0x21, 0x04, 0x0C, 0x01, 0x0B, 0x20, 0x03, 0x10, 0xA2, 0x80, 0x80, - 0x80, 0x00, 0x45, 0x21, 0x04, 0x0B, 0x20, 0x02, 0x20, 0x01, 0x6C, 0x21, 0x05, 0x02, 0x40, 0x02, 0x40, 0x20, - 0x03, 0x28, 0x02, 0x10, 0x22, 0x06, 0x0D, 0x00, 0x41, 0x00, 0x21, 0x07, 0x20, 0x03, 0x10, 0xA6, 0x80, 0x80, - 0x80, 0x00, 0x0D, 0x01, 0x20, 0x03, 0x28, 0x02, 0x10, 0x21, 0x06, 0x0B, 0x02, 0x40, 0x20, 0x06, 0x20, 0x03, - 0x28, 0x02, 0x14, 0x22, 0x08, 0x6B, 0x20, 0x05, 0x4F, 0x0D, 0x00, 0x20, 0x03, 0x20, 0x00, 0x20, 0x05, 0x20, - 0x03, 0x28, 0x02, 0x20, 0x11, 0x80, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x21, 0x07, 0x0C, - 0x01, 0x0B, 0x41, 0x00, 0x21, 0x09, 0x02, 0x40, 0x02, 0x40, 0x20, 0x05, 0x0D, 0x00, 0x20, 0x05, 0x21, 0x06, - 0x0C, 0x01, 0x0B, 0x41, 0x00, 0x21, 0x06, 0x02, 0x40, 0x20, 0x03, 0x28, 0x02, 0x48, 0x41, 0x00, 0x4E, 0x0D, - 0x00, 0x20, 0x05, 0x21, 0x06, 0x0C, 0x01, 0x0B, 0x20, 0x00, 0x20, 0x05, 0x6A, 0x21, 0x07, 0x02, 0x40, 0x03, - 0x40, 0x20, 0x07, 0x20, 0x06, 0x6A, 0x41, 0x7F, 0x6A, 0x2D, 0x00, 0x00, 0x41, 0x0A, 0x46, 0x0D, 0x01, 0x20, - 0x05, 0x20, 0x06, 0x41, 0x7F, 0x6A, 0x22, 0x06, 0x6A, 0x0D, 0x00, 0x0B, 0x41, 0x00, 0x21, 0x09, 0x20, 0x05, - 0x21, 0x06, 0x0C, 0x01, 0x0B, 0x20, 0x03, 0x20, 0x00, 0x20, 0x05, 0x20, 0x06, 0x6A, 0x22, 0x09, 0x20, 0x03, - 0x28, 0x02, 0x20, 0x11, 0x80, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x22, 0x07, 0x20, 0x09, - 0x49, 0x0D, 0x01, 0x20, 0x09, 0x20, 0x00, 0x6A, 0x21, 0x00, 0x41, 0x00, 0x20, 0x06, 0x6B, 0x21, 0x06, 0x20, - 0x03, 0x28, 0x02, 0x14, 0x21, 0x08, 0x0B, 0x20, 0x08, 0x20, 0x00, 0x20, 0x06, 0xFC, 0x0A, 0x00, 0x00, 0x20, - 0x03, 0x20, 0x03, 0x28, 0x02, 0x14, 0x20, 0x06, 0x6A, 0x36, 0x02, 0x14, 0x20, 0x09, 0x20, 0x06, 0x6A, 0x21, - 0x07, 0x0B, 0x02, 0x40, 0x20, 0x04, 0x0D, 0x00, 0x20, 0x03, 0x10, 0xA3, 0x80, 0x80, 0x80, 0x00, 0x0B, 0x02, - 0x40, 0x20, 0x07, 0x20, 0x05, 0x47, 0x0D, 0x00, 0x20, 0x02, 0x41, 0x00, 0x20, 0x01, 0x1B, 0x0F, 0x0B, 0x20, - 0x07, 0x20, 0x01, 0x6E, 0x0B, 0x24, 0x01, 0x01, 0x7F, 0x20, 0x00, 0x10, 0xB5, 0x80, 0x80, 0x80, 0x00, 0x21, - 0x02, 0x41, 0x7F, 0x41, 0x00, 0x20, 0x02, 0x20, 0x00, 0x41, 0x01, 0x20, 0x02, 0x20, 0x01, 0x10, 0xA7, 0x80, - 0x80, 0x80, 0x00, 0x47, 0x1B, 0x0B, 0xB3, 0x01, 0x01, 0x03, 0x7F, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, - 0x10, 0x6B, 0x22, 0x02, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x02, 0x20, 0x01, 0x3A, 0x00, 0x0F, 0x02, - 0x40, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x10, 0x22, 0x03, 0x0D, 0x00, 0x02, 0x40, 0x20, 0x00, 0x10, 0xA6, - 0x80, 0x80, 0x80, 0x00, 0x45, 0x0D, 0x00, 0x41, 0x7F, 0x21, 0x03, 0x0C, 0x02, 0x0B, 0x20, 0x00, 0x28, 0x02, - 0x10, 0x21, 0x03, 0x0B, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x14, 0x22, 0x04, 0x20, 0x03, 0x46, 0x0D, 0x00, - 0x20, 0x00, 0x28, 0x02, 0x48, 0x20, 0x01, 0x41, 0xFF, 0x01, 0x71, 0x22, 0x03, 0x46, 0x0D, 0x00, 0x20, 0x00, - 0x20, 0x04, 0x41, 0x01, 0x6A, 0x36, 0x02, 0x14, 0x20, 0x04, 0x20, 0x01, 0x3A, 0x00, 0x00, 0x0C, 0x01, 0x0B, - 0x02, 0x40, 0x20, 0x00, 0x20, 0x02, 0x41, 0x0F, 0x6A, 0x41, 0x01, 0x20, 0x00, 0x28, 0x02, 0x20, 0x11, 0x80, - 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x01, 0x46, 0x0D, 0x00, 0x41, 0x7F, 0x21, 0x03, - 0x0C, 0x01, 0x0B, 0x20, 0x02, 0x2D, 0x00, 0x0F, 0x21, 0x03, 0x0B, 0x20, 0x02, 0x41, 0x10, 0x6A, 0x24, 0x80, - 0x80, 0x80, 0x80, 0x00, 0x20, 0x03, 0x0B, 0xB7, 0x01, 0x01, 0x02, 0x7F, 0x02, 0x40, 0x02, 0x40, 0x41, 0x00, - 0x28, 0x02, 0xCC, 0x89, 0x80, 0x80, 0x00, 0x41, 0x00, 0x4E, 0x0D, 0x00, 0x41, 0x01, 0x21, 0x01, 0x0C, 0x01, - 0x0B, 0x41, 0x88, 0x89, 0x80, 0x80, 0x00, 0x10, 0xA2, 0x80, 0x80, 0x80, 0x00, 0x45, 0x21, 0x01, 0x0B, 0x02, - 0x40, 0x02, 0x40, 0x20, 0x00, 0x41, 0x88, 0x89, 0x80, 0x80, 0x00, 0x10, 0xA8, 0x80, 0x80, 0x80, 0x00, 0x41, - 0x00, 0x4E, 0x0D, 0x00, 0x41, 0x7F, 0x21, 0x00, 0x0C, 0x01, 0x0B, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xD0, - 0x89, 0x80, 0x80, 0x00, 0x41, 0x0A, 0x46, 0x0D, 0x00, 0x41, 0x00, 0x28, 0x02, 0x9C, 0x89, 0x80, 0x80, 0x00, - 0x22, 0x02, 0x41, 0x00, 0x28, 0x02, 0x98, 0x89, 0x80, 0x80, 0x00, 0x46, 0x0D, 0x00, 0x41, 0x00, 0x21, 0x00, - 0x41, 0x00, 0x20, 0x02, 0x41, 0x01, 0x6A, 0x36, 0x02, 0x9C, 0x89, 0x80, 0x80, 0x00, 0x20, 0x02, 0x41, 0x0A, - 0x3A, 0x00, 0x00, 0x0C, 0x01, 0x0B, 0x41, 0x88, 0x89, 0x80, 0x80, 0x00, 0x41, 0x0A, 0x10, 0xA9, 0x80, 0x80, - 0x80, 0x00, 0x41, 0x1F, 0x75, 0x21, 0x00, 0x0B, 0x02, 0x40, 0x20, 0x01, 0x0D, 0x00, 0x41, 0x88, 0x89, 0x80, - 0x80, 0x00, 0x10, 0xA3, 0x80, 0x80, 0x80, 0x00, 0x0B, 0x20, 0x00, 0x0B, 0x02, 0x00, 0x0B, 0x2E, 0x00, 0x10, - 0xAB, 0x80, 0x80, 0x80, 0x00, 0x02, 0x40, 0x20, 0x00, 0x10, 0x97, 0x80, 0x80, 0x80, 0x00, 0x22, 0x00, 0x0D, - 0x00, 0x41, 0x00, 0x0F, 0x0B, 0x23, 0x81, 0x80, 0x80, 0x80, 0x00, 0x41, 0x80, 0x80, 0x80, 0x80, 0x00, 0x6A, - 0x20, 0x00, 0x36, 0x02, 0x00, 0x41, 0x7F, 0x0B, 0x04, 0x00, 0x20, 0x00, 0x0B, 0x13, 0x00, 0x20, 0x00, 0x28, - 0x02, 0x38, 0x10, 0xAD, 0x80, 0x80, 0x80, 0x00, 0x10, 0xAC, 0x80, 0x80, 0x80, 0x00, 0x0B, 0x7F, 0x01, 0x02, - 0x7F, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x10, 0x6B, 0x22, 0x03, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, - 0x41, 0x7F, 0x21, 0x04, 0x02, 0x40, 0x02, 0x40, 0x20, 0x02, 0x41, 0x7F, 0x4A, 0x0D, 0x00, 0x23, 0x81, 0x80, - 0x80, 0x80, 0x00, 0x41, 0x80, 0x80, 0x80, 0x80, 0x00, 0x6A, 0x41, 0x1C, 0x36, 0x02, 0x00, 0x0C, 0x01, 0x0B, - 0x02, 0x40, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x41, 0x0C, 0x6A, 0x10, 0x9A, 0x80, 0x80, 0x80, - 0x00, 0x22, 0x02, 0x45, 0x0D, 0x00, 0x23, 0x81, 0x80, 0x80, 0x80, 0x00, 0x41, 0x80, 0x80, 0x80, 0x80, 0x00, - 0x6A, 0x20, 0x02, 0x36, 0x02, 0x00, 0x41, 0x7F, 0x21, 0x04, 0x0C, 0x01, 0x0B, 0x20, 0x03, 0x28, 0x02, 0x0C, - 0x21, 0x04, 0x0B, 0x20, 0x03, 0x41, 0x10, 0x6A, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x04, 0x0B, 0xBB, - 0x02, 0x01, 0x07, 0x7F, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x10, 0x6B, 0x22, 0x03, 0x24, 0x80, 0x80, - 0x80, 0x80, 0x00, 0x20, 0x03, 0x20, 0x02, 0x36, 0x02, 0x0C, 0x20, 0x03, 0x20, 0x01, 0x36, 0x02, 0x08, 0x20, - 0x03, 0x20, 0x00, 0x28, 0x02, 0x18, 0x22, 0x01, 0x36, 0x02, 0x00, 0x20, 0x03, 0x20, 0x00, 0x28, 0x02, 0x14, - 0x20, 0x01, 0x6B, 0x22, 0x04, 0x36, 0x02, 0x04, 0x41, 0x02, 0x21, 0x05, 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, - 0x28, 0x02, 0x38, 0x20, 0x03, 0x41, 0x02, 0x10, 0xAF, 0x80, 0x80, 0x80, 0x00, 0x22, 0x01, 0x20, 0x04, 0x20, - 0x02, 0x6A, 0x22, 0x06, 0x46, 0x0D, 0x00, 0x20, 0x03, 0x21, 0x04, 0x03, 0x40, 0x02, 0x40, 0x20, 0x01, 0x41, - 0x7F, 0x4A, 0x0D, 0x00, 0x41, 0x00, 0x21, 0x01, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x18, 0x20, 0x00, 0x42, - 0x00, 0x37, 0x03, 0x10, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, 0x41, 0x20, 0x72, 0x36, 0x02, 0x00, 0x20, - 0x05, 0x41, 0x02, 0x46, 0x0D, 0x03, 0x20, 0x02, 0x20, 0x04, 0x28, 0x02, 0x04, 0x6B, 0x21, 0x01, 0x0C, 0x03, - 0x0B, 0x20, 0x04, 0x20, 0x01, 0x20, 0x04, 0x28, 0x02, 0x04, 0x22, 0x07, 0x4B, 0x22, 0x08, 0x41, 0x03, 0x74, - 0x6A, 0x22, 0x09, 0x20, 0x09, 0x28, 0x02, 0x00, 0x20, 0x01, 0x20, 0x07, 0x41, 0x00, 0x20, 0x08, 0x1B, 0x6B, - 0x22, 0x07, 0x6A, 0x36, 0x02, 0x00, 0x20, 0x04, 0x41, 0x0C, 0x41, 0x04, 0x20, 0x08, 0x1B, 0x6A, 0x22, 0x04, - 0x20, 0x04, 0x28, 0x02, 0x00, 0x20, 0x07, 0x6B, 0x36, 0x02, 0x00, 0x20, 0x09, 0x21, 0x04, 0x20, 0x06, 0x20, - 0x01, 0x6B, 0x22, 0x06, 0x20, 0x00, 0x28, 0x02, 0x38, 0x20, 0x09, 0x20, 0x05, 0x20, 0x08, 0x6B, 0x22, 0x05, - 0x10, 0xAF, 0x80, 0x80, 0x80, 0x00, 0x22, 0x01, 0x47, 0x0D, 0x00, 0x0B, 0x0B, 0x20, 0x00, 0x20, 0x00, 0x28, - 0x02, 0x28, 0x22, 0x01, 0x36, 0x02, 0x18, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x14, 0x20, 0x00, 0x20, 0x01, - 0x20, 0x00, 0x28, 0x02, 0x2C, 0x6A, 0x36, 0x02, 0x10, 0x20, 0x02, 0x21, 0x01, 0x0B, 0x20, 0x03, 0x41, 0x10, - 0x6A, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x01, 0x0B, 0x6D, 0x01, 0x01, 0x7F, 0x23, 0x80, 0x80, 0x80, - 0x80, 0x00, 0x41, 0x20, 0x6B, 0x22, 0x01, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, - 0x00, 0x20, 0x01, 0x41, 0x08, 0x6A, 0x10, 0x98, 0x80, 0x80, 0x80, 0x00, 0x22, 0x00, 0x0D, 0x00, 0x41, 0x3B, - 0x21, 0x00, 0x20, 0x01, 0x2D, 0x00, 0x08, 0x41, 0x02, 0x47, 0x0D, 0x00, 0x20, 0x01, 0x2D, 0x00, 0x10, 0x41, - 0x24, 0x71, 0x0D, 0x00, 0x41, 0x01, 0x21, 0x00, 0x0C, 0x01, 0x0B, 0x23, 0x81, 0x80, 0x80, 0x80, 0x00, 0x41, - 0x80, 0x80, 0x80, 0x80, 0x00, 0x6A, 0x20, 0x00, 0x36, 0x02, 0x00, 0x41, 0x00, 0x21, 0x00, 0x0B, 0x20, 0x01, - 0x41, 0x20, 0x6A, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x00, 0x0B, 0x3B, 0x00, 0x20, 0x00, 0x41, 0x81, - 0x80, 0x80, 0x80, 0x00, 0x36, 0x02, 0x20, 0x02, 0x40, 0x20, 0x00, 0x2D, 0x00, 0x00, 0x41, 0xC0, 0x00, 0x71, - 0x0D, 0x00, 0x20, 0x00, 0x28, 0x02, 0x38, 0x10, 0xB1, 0x80, 0x80, 0x80, 0x00, 0x0D, 0x00, 0x20, 0x00, 0x41, - 0x7F, 0x36, 0x02, 0x48, 0x0B, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x10, 0xB0, 0x80, 0x80, 0x80, 0x00, 0x0B, - 0x6B, 0x01, 0x01, 0x7F, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x10, 0x6B, 0x22, 0x03, 0x24, 0x80, 0x80, - 0x80, 0x80, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x41, 0xFF, 0x01, 0x71, 0x20, - 0x03, 0x41, 0x08, 0x6A, 0x10, 0x99, 0x80, 0x80, 0x80, 0x00, 0x22, 0x02, 0x45, 0x0D, 0x00, 0x23, 0x81, 0x80, - 0x80, 0x80, 0x00, 0x41, 0x80, 0x80, 0x80, 0x80, 0x00, 0x6A, 0x41, 0xC6, 0x00, 0x20, 0x02, 0x20, 0x02, 0x41, - 0xCC, 0x00, 0x46, 0x1B, 0x36, 0x02, 0x00, 0x42, 0x7F, 0x21, 0x01, 0x0C, 0x01, 0x0B, 0x20, 0x03, 0x29, 0x03, - 0x08, 0x21, 0x01, 0x0B, 0x20, 0x03, 0x41, 0x10, 0x6A, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x01, 0x0B, - 0x11, 0x00, 0x20, 0x00, 0x28, 0x02, 0x38, 0x20, 0x01, 0x20, 0x02, 0x10, 0xB3, 0x80, 0x80, 0x80, 0x00, 0x0B, - 0xCF, 0x01, 0x01, 0x03, 0x7F, 0x20, 0x00, 0x21, 0x01, 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x41, 0x03, 0x71, - 0x45, 0x0D, 0x00, 0x02, 0x40, 0x20, 0x00, 0x2D, 0x00, 0x00, 0x0D, 0x00, 0x20, 0x00, 0x20, 0x00, 0x6B, 0x0F, - 0x0B, 0x20, 0x00, 0x41, 0x01, 0x6A, 0x22, 0x01, 0x41, 0x03, 0x71, 0x45, 0x0D, 0x00, 0x20, 0x01, 0x2D, 0x00, - 0x00, 0x45, 0x0D, 0x01, 0x20, 0x00, 0x41, 0x02, 0x6A, 0x22, 0x01, 0x41, 0x03, 0x71, 0x45, 0x0D, 0x00, 0x20, - 0x01, 0x2D, 0x00, 0x00, 0x45, 0x0D, 0x01, 0x20, 0x00, 0x41, 0x03, 0x6A, 0x22, 0x01, 0x41, 0x03, 0x71, 0x45, - 0x0D, 0x00, 0x20, 0x01, 0x2D, 0x00, 0x00, 0x45, 0x0D, 0x01, 0x20, 0x00, 0x41, 0x04, 0x6A, 0x22, 0x01, 0x41, - 0x03, 0x71, 0x0D, 0x01, 0x0B, 0x20, 0x01, 0x41, 0x7C, 0x6A, 0x21, 0x02, 0x20, 0x01, 0x41, 0x7B, 0x6A, 0x21, - 0x01, 0x03, 0x40, 0x20, 0x01, 0x41, 0x04, 0x6A, 0x21, 0x01, 0x41, 0x80, 0x82, 0x84, 0x08, 0x20, 0x02, 0x41, - 0x04, 0x6A, 0x22, 0x02, 0x28, 0x02, 0x00, 0x22, 0x03, 0x6B, 0x20, 0x03, 0x72, 0x41, 0x80, 0x81, 0x82, 0x84, - 0x78, 0x71, 0x41, 0x80, 0x81, 0x82, 0x84, 0x78, 0x46, 0x0D, 0x00, 0x0B, 0x03, 0x40, 0x20, 0x01, 0x41, 0x01, - 0x6A, 0x21, 0x01, 0x20, 0x02, 0x2D, 0x00, 0x00, 0x21, 0x03, 0x20, 0x02, 0x41, 0x01, 0x6A, 0x21, 0x02, 0x20, - 0x03, 0x0D, 0x00, 0x0B, 0x0B, 0x20, 0x01, 0x20, 0x00, 0x6B, 0x0B, 0xBC, 0x04, 0x01, 0x03, 0x7F, 0x02, 0x40, - 0x41, 0x00, 0x2C, 0x00, 0x91, 0x8E, 0x80, 0x80, 0x00, 0x22, 0x01, 0x45, 0x0D, 0x00, 0x20, 0x00, 0x41, 0x00, - 0x41, 0x81, 0x80, 0x80, 0x80, 0x78, 0xFE, 0x48, 0x02, 0x00, 0x22, 0x02, 0x45, 0x21, 0x03, 0x02, 0x40, 0x20, - 0x01, 0x41, 0x7F, 0x4A, 0x0D, 0x00, 0x41, 0x00, 0x41, 0x00, 0x3A, 0x00, 0x91, 0x8E, 0x80, 0x80, 0x00, 0x0B, - 0x20, 0x03, 0x0D, 0x00, 0x20, 0x00, 0x20, 0x02, 0x41, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x6A, 0x20, 0x02, 0x20, - 0x02, 0x41, 0x00, 0x48, 0x1B, 0x22, 0x01, 0x20, 0x01, 0x41, 0x81, 0x80, 0x80, 0x80, 0x78, 0x6A, 0xFE, 0x48, - 0x02, 0x00, 0x22, 0x02, 0x20, 0x01, 0x46, 0x0D, 0x00, 0x20, 0x00, 0x20, 0x02, 0x41, 0xFF, 0xFF, 0xFF, 0xFF, - 0x07, 0x6A, 0x20, 0x02, 0x20, 0x02, 0x41, 0x00, 0x48, 0x1B, 0x22, 0x01, 0x20, 0x01, 0x41, 0x81, 0x80, 0x80, - 0x80, 0x78, 0x6A, 0xFE, 0x48, 0x02, 0x00, 0x22, 0x02, 0x20, 0x01, 0x46, 0x0D, 0x00, 0x20, 0x00, 0x20, 0x02, - 0x41, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x6A, 0x20, 0x02, 0x20, 0x02, 0x41, 0x00, 0x48, 0x1B, 0x22, 0x01, 0x20, - 0x01, 0x41, 0x81, 0x80, 0x80, 0x80, 0x78, 0x6A, 0xFE, 0x48, 0x02, 0x00, 0x22, 0x02, 0x20, 0x01, 0x46, 0x0D, - 0x00, 0x20, 0x00, 0x20, 0x02, 0x41, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x6A, 0x20, 0x02, 0x20, 0x02, 0x41, 0x00, - 0x48, 0x1B, 0x22, 0x01, 0x20, 0x01, 0x41, 0x81, 0x80, 0x80, 0x80, 0x78, 0x6A, 0xFE, 0x48, 0x02, 0x00, 0x22, - 0x02, 0x20, 0x01, 0x46, 0x0D, 0x00, 0x20, 0x00, 0x20, 0x02, 0x41, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x6A, 0x20, - 0x02, 0x20, 0x02, 0x41, 0x00, 0x48, 0x1B, 0x22, 0x01, 0x20, 0x01, 0x41, 0x81, 0x80, 0x80, 0x80, 0x78, 0x6A, - 0xFE, 0x48, 0x02, 0x00, 0x22, 0x02, 0x20, 0x01, 0x46, 0x0D, 0x00, 0x20, 0x00, 0x20, 0x02, 0x41, 0xFF, 0xFF, - 0xFF, 0xFF, 0x07, 0x6A, 0x20, 0x02, 0x20, 0x02, 0x41, 0x00, 0x48, 0x1B, 0x22, 0x01, 0x20, 0x01, 0x41, 0x81, - 0x80, 0x80, 0x80, 0x78, 0x6A, 0xFE, 0x48, 0x02, 0x00, 0x22, 0x02, 0x20, 0x01, 0x46, 0x0D, 0x00, 0x20, 0x00, - 0x20, 0x02, 0x41, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x6A, 0x20, 0x02, 0x20, 0x02, 0x41, 0x00, 0x48, 0x1B, 0x22, - 0x01, 0x20, 0x01, 0x41, 0x81, 0x80, 0x80, 0x80, 0x78, 0x6A, 0xFE, 0x48, 0x02, 0x00, 0x22, 0x02, 0x20, 0x01, - 0x46, 0x0D, 0x00, 0x20, 0x00, 0x20, 0x02, 0x41, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x6A, 0x20, 0x02, 0x20, 0x02, - 0x41, 0x00, 0x48, 0x1B, 0x22, 0x01, 0x20, 0x01, 0x41, 0x81, 0x80, 0x80, 0x80, 0x78, 0x6A, 0xFE, 0x48, 0x02, - 0x00, 0x22, 0x02, 0x20, 0x01, 0x46, 0x0D, 0x00, 0x20, 0x00, 0x20, 0x02, 0x41, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, - 0x6A, 0x20, 0x02, 0x20, 0x02, 0x41, 0x00, 0x48, 0x1B, 0x22, 0x01, 0x20, 0x01, 0x41, 0x81, 0x80, 0x80, 0x80, - 0x78, 0x6A, 0xFE, 0x48, 0x02, 0x00, 0x22, 0x02, 0x20, 0x01, 0x46, 0x0D, 0x00, 0x20, 0x00, 0x20, 0x02, 0x41, - 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x6A, 0x20, 0x02, 0x20, 0x02, 0x41, 0x00, 0x48, 0x1B, 0x22, 0x01, 0x20, 0x01, - 0x41, 0x81, 0x80, 0x80, 0x80, 0x78, 0x6A, 0xFE, 0x48, 0x02, 0x00, 0x20, 0x01, 0x46, 0x0D, 0x00, 0x03, 0x40, - 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, 0x02, 0x20, 0x00, 0x20, 0x02, 0x20, 0x02, 0x41, 0x01, 0x6A, 0x22, 0x01, - 0xFE, 0x48, 0x02, 0x00, 0x47, 0x0D, 0x00, 0x0B, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x01, 0x41, 0x7F, - 0x4C, 0x0D, 0x00, 0x20, 0x01, 0x21, 0x02, 0x0C, 0x01, 0x0B, 0x20, 0x00, 0x41, 0x00, 0x20, 0x01, 0x41, 0x01, - 0x10, 0xB7, 0x80, 0x80, 0x80, 0x00, 0x20, 0x01, 0x41, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x6A, 0x21, 0x02, 0x0B, - 0x20, 0x00, 0x20, 0x02, 0x20, 0x02, 0x41, 0x80, 0x80, 0x80, 0x80, 0x78, 0x72, 0xFE, 0x48, 0x02, 0x00, 0x22, - 0x01, 0x20, 0x02, 0x47, 0x0D, 0x00, 0x0B, 0x0B, 0x0B, 0xF9, 0x01, 0x01, 0x03, 0x7F, 0x23, 0x80, 0x80, 0x80, - 0x80, 0x00, 0x41, 0x10, 0x6B, 0x21, 0x04, 0x41, 0x9C, 0x7F, 0x21, 0x05, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, - 0x03, 0x40, 0x02, 0x40, 0x20, 0x01, 0x45, 0x0D, 0x00, 0x20, 0x01, 0x28, 0x02, 0x00, 0x0D, 0x02, 0x0B, 0x20, - 0x00, 0x28, 0x02, 0x00, 0x20, 0x02, 0x47, 0x0D, 0x03, 0x20, 0x04, 0x41, 0x00, 0x36, 0x02, 0x0C, 0x20, 0x04, - 0x41, 0x00, 0x41, 0x00, 0xFE, 0x48, 0x02, 0x0C, 0x1A, 0x02, 0x40, 0x20, 0x01, 0x45, 0x0D, 0x00, 0x20, 0x01, - 0x28, 0x02, 0x00, 0x0D, 0x02, 0x0B, 0x20, 0x00, 0x28, 0x02, 0x00, 0x20, 0x02, 0x47, 0x0D, 0x03, 0x20, 0x04, - 0x41, 0x00, 0x36, 0x02, 0x0C, 0x20, 0x04, 0x41, 0x00, 0x41, 0x00, 0xFE, 0x48, 0x02, 0x0C, 0x1A, 0x20, 0x05, - 0x41, 0x02, 0x6A, 0x22, 0x05, 0x0D, 0x00, 0x0B, 0x20, 0x01, 0x0D, 0x00, 0x41, 0x01, 0x21, 0x06, 0x0C, 0x01, - 0x0B, 0x03, 0x40, 0x20, 0x01, 0x28, 0x02, 0x00, 0x22, 0x04, 0x20, 0x01, 0x20, 0x04, 0x20, 0x04, 0x41, 0x01, - 0x6A, 0xFE, 0x48, 0x02, 0x00, 0x47, 0x0D, 0x00, 0x0B, 0x41, 0x00, 0x21, 0x06, 0x0B, 0x02, 0x40, 0x20, 0x00, - 0x28, 0x02, 0x00, 0x20, 0x02, 0x47, 0x0D, 0x00, 0x20, 0x00, 0x41, 0x03, 0x71, 0x21, 0x04, 0x03, 0x40, 0x02, - 0x40, 0x20, 0x04, 0x0D, 0x00, 0x20, 0x00, 0x20, 0x02, 0x42, 0x7F, 0xFE, 0x01, 0x02, 0x00, 0x21, 0x05, 0x0B, - 0x20, 0x00, 0x28, 0x02, 0x00, 0x20, 0x02, 0x46, 0x0D, 0x00, 0x0B, 0x0B, 0x20, 0x06, 0x0D, 0x00, 0x03, 0x40, - 0x20, 0x01, 0x28, 0x02, 0x00, 0x22, 0x04, 0x20, 0x01, 0x20, 0x04, 0x20, 0x04, 0x41, 0x7F, 0x6A, 0xFE, 0x48, - 0x02, 0x00, 0x47, 0x0D, 0x00, 0x0B, 0x0B, 0x0B, 0x0B, 0x97, 0x02, 0x03, 0x01, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x17, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x20, 0x66, 0x72, 0x6F, 0x6D, - 0x20, 0x4F, 0x63, 0x72, 0x65, 0x2E, 0x00, 0x01, 0x88, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x07, 0x00, 0x00, - 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x04, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x00}; - - int wasm_binary_len = sizeof(wasm_binary); - - fs_write(&f, &wasm_binary, wasm_binary_len); - - fs_close(&f); -} \ No newline at end of file diff --git a/src/ocre/api/ocre_api.c b/src/ocre/api/ocre_api.c index fb05514f..8bab51ff 100644 --- a/src/ocre/api/ocre_api.c +++ b/src/ocre/api/ocre_api.c @@ -11,25 +11,42 @@ #include #include #include -#include -#include + +#include "ocre_core_external.h" #include "bh_platform.h" #include "ocre_api.h" + +#ifdef CONFIG_OCRE_TIMER #include "../ocre_timers/ocre_timer.h" +#endif +#include "ocre/utils/utils.h" +#include "../container_messaging/messaging.h" + +#if defined(CONFIG_OCRE_TIMER) || defined(CONFIG_OCRE_GPIO) || defined(CONFIG_OCRE_SENSORS) +#include "ocre_common.h" +#endif + +#ifdef CONFIG_OCRE_SENSORS #include "../ocre_sensors/ocre_sensors.h" +#endif + +#ifdef CONFIG_OCRE_GPIO #include "../ocre_gpio/ocre_gpio.h" -#include "../container_messaging/messaging.h" +#endif -int _ocre_posix_uname(wasm_exec_env_t exec_env, struct _ocre_posix_utsname *name) { - struct utsname info; - wasm_module_inst_t module_inst = get_module_inst(exec_env); +int _ocre_posix_uname(wasm_exec_env_t exec_env, struct _ocre_posix_utsname *name) { + wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); + if (!module_inst) { + return -1; + } if (!wasm_runtime_validate_native_addr(module_inst, name, sizeof(struct _ocre_posix_utsname))) { return -1; } + struct utsname info; if (uname(&info) != 0) { return -1; } @@ -40,7 +57,6 @@ int _ocre_posix_uname(wasm_exec_env_t exec_env, struct _ocre_posix_utsname *name snprintf(name->release, OCRE_API_POSIX_BUF_SIZE, "%s (%s)", APP_VERSION_STRING, info.release); snprintf(name->version, OCRE_API_POSIX_BUF_SIZE, "%s", info.version); -// ARM Processors are special cased as they are so popular #ifdef CONFIG_ARM #ifdef CONFIG_CPU_CORTEX_M0 strlcat(name->machine, "ARM Cortex-M0", OCRE_API_POSIX_BUF_SIZE); @@ -58,7 +74,6 @@ int _ocre_posix_uname(wasm_exec_env_t exec_env, struct _ocre_posix_utsname *name strlcat(name->machine, "ARM Cortex-M55", OCRE_API_POSIX_BUF_SIZE); #endif #else - // Other processors, use value returned from uname() strlcat(name->machine, info.machine, OCRE_API_POSIX_BUF_SIZE); #endif @@ -66,34 +81,28 @@ int _ocre_posix_uname(wasm_exec_env_t exec_env, struct _ocre_posix_utsname *name return 0; } -/** - * @brief Pause execution for a specified time - * - * @param exec_env WASM execution environment - * @param milliseconds Number of milliseconds to sleep - * @return 0 on success - */ + int ocre_sleep(wasm_exec_env_t exec_env, int milliseconds) { - k_msleep(milliseconds); + core_sleep_ms(milliseconds); return 0; } // Ocre Runtime API NativeSymbol ocre_api_table[] = { {"uname", _ocre_posix_uname, "(*)i", NULL}, - {"ocre_sleep", ocre_sleep, "(i)i", NULL}, - +#if defined(CONFIG_OCRE_TIMER) || defined(CONFIG_OCRE_GPIO) || defined(CONFIG_OCRE_SENSORS) + {"ocre_get_event", ocre_get_event, "(iiii)i", NULL}, + {"ocre_register_dispatcher", ocre_register_dispatcher, "(i$)i", NULL}, +#endif // Container Messaging API #ifdef CONFIG_OCRE_CONTAINER_MESSAGING {"ocre_msg_system_init", ocre_msg_system_init, "()", NULL}, {"ocre_publish_message", ocre_publish_message, "(***i)i", NULL}, {"ocre_subscribe_message", ocre_subscribe_message, "(**)i", NULL}, #endif - // Sensor API #ifdef CONFIG_OCRE_SENSORS - // Sensor API {"ocre_sensors_init", ocre_sensors_init, "()i", NULL}, {"ocre_sensors_discover", ocre_sensors_discover, "()i", NULL}, {"ocre_sensors_open", ocre_sensors_open, "(i)i", NULL}, @@ -101,17 +110,23 @@ NativeSymbol ocre_api_table[] = { {"ocre_sensors_get_channel_count", ocre_sensors_get_channel_count, "(i)i", NULL}, {"ocre_sensors_get_channel_type", ocre_sensors_get_channel_type, "(ii)i", NULL}, {"ocre_sensors_read", ocre_sensors_read, "(ii)i", NULL}, + {"ocre_sensors_open_by_name", ocre_sensors_open_by_name, "($)i", NULL}, + {"ocre_sensors_get_handle_by_name", ocre_sensors_get_handle_by_name, "($)i", NULL}, + {"ocre_sensors_get_channel_count_by_name", ocre_sensors_get_channel_count_by_name, "($)i", NULL}, + {"ocre_sensors_get_channel_type_by_name", ocre_sensors_get_channel_type_by_name, "($i)i", NULL}, + {"ocre_sensors_read_by_name", ocre_sensors_read_by_name, "($i)i", NULL}, + {"ocre_sensors_get_list", ocre_sensors_get_list, "($i)i", NULL}, #endif - // Timer API +// Timer API +#ifdef CONFIG_OCRE_TIMER {"ocre_timer_create", ocre_timer_create, "(i)i", NULL}, {"ocre_timer_start", ocre_timer_start, "(iii)i", NULL}, {"ocre_timer_stop", ocre_timer_stop, "(i)i", NULL}, {"ocre_timer_delete", ocre_timer_delete, "(i)i", NULL}, {"ocre_timer_get_remaining", ocre_timer_get_remaining, "(i)i", NULL}, - {"ocre_timer_set_dispatcher", ocre_timer_set_dispatcher, "(i)v", NULL}, - +#endif +// GPIO API #ifdef CONFIG_OCRE_GPIO - // GPIO API {"ocre_gpio_init", ocre_gpio_wasm_init, "()i", NULL}, {"ocre_gpio_configure", ocre_gpio_wasm_configure, "(iii)i", NULL}, {"ocre_gpio_pin_set", ocre_gpio_wasm_set, "(iii)i", NULL}, @@ -122,4 +137,4 @@ NativeSymbol ocre_api_table[] = { #endif }; -int ocre_api_table_size = sizeof(ocre_api_table) / sizeof(NativeSymbol); \ No newline at end of file +int ocre_api_table_size = sizeof(ocre_api_table) / sizeof(NativeSymbol); diff --git a/src/ocre/api/ocre_common.c b/src/ocre/api/ocre_common.c new file mode 100644 index 00000000..e76d22d6 --- /dev/null +++ b/src/ocre/api/ocre_common.c @@ -0,0 +1,430 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre, a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "ocre_core_external.h" +#include +#include +#include +#include +#include +#include +LOG_MODULE_DECLARE(ocre_cs_component, OCRE_LOG_LEVEL); +#include +#include "../../../../../wasm-micro-runtime/core/iwasm/include/lib_export.h" +#include "bh_log.h" +#include "../ocre_timers/ocre_timer.h" +#include "../ocre_gpio/ocre_gpio.h" +#include "ocre_common.h" +#include + +// Place queue buffer in a dedicated section with alignment +__attribute__((section(".noinit.wasm_event_queue"), + aligned(8))) char wasm_event_queue_buffer[64 * sizeof(wasm_event_t)]; + +char *wasm_event_queue_buffer_ptr = wasm_event_queue_buffer; // Pointer for validation +K_MSGQ_DEFINE(wasm_event_queue, sizeof(wasm_event_t), 64, 4); +bool wasm_event_queue_initialized = false; +struct k_spinlock wasm_event_queue_lock; + +#define EVENT_BUFFER_SIZE 512 +static uint8_t event_buffer[EVENT_BUFFER_SIZE]; +static struct ring_buf event_ring; + +typedef struct module_node { + ocre_module_context_t ctx; + sys_snode_t node; +} module_node_t; + +static sys_slist_t module_registry; +static struct k_mutex registry_mutex; + +static struct cleanup_handler { + ocre_resource_type_t type; + ocre_cleanup_handler_t handler; +} cleanup_handlers[OCRE_RESOURCE_TYPE_COUNT]; + +/* Thread-Local Storage */ +__thread wasm_module_inst_t *current_module_tls = NULL; + +bool common_initialized = false; + +static struct k_sem event_sem; + +#define EVENT_THREAD_POOL_SIZE 2 +static struct core_thread event_threads[EVENT_THREAD_POOL_SIZE]; + +// Flag to signal event threads to exit gracefully +static volatile bool event_threads_exit = false; + +// Arguments for event threads +struct event_thread_args { + int index; // Thread index for identification or logging +}; + +static struct event_thread_args event_args[EVENT_THREAD_POOL_SIZE]; + +// Event thread function to process events from the ring buffer +static void event_thread_fn(void *arg1) { + struct event_thread_args *args = (struct event_thread_args *)arg1; + int index = args->index; // Can be used for logging or debugging + // Initialize WASM runtime thread environment + wasm_runtime_init_thread_env(); + + wasm_event_t event_batch[10]; + uint32_t bytes_read; + + // Main event processing loop + while (!event_threads_exit) { + k_sem_take(&event_sem, K_FOREVER); + if (event_threads_exit) { + break; // Exit if shutdown is signaled + } + bytes_read = ring_buf_get(&event_ring, (uint8_t *)event_batch, sizeof(event_buffer)); + for (size_t i = 0; i < bytes_read / sizeof(wasm_event_t); i++) { + wasm_event_t *event = &event_batch[i]; + if (!event) { + LOG_ERR("Null event in batch"); + continue; + } + if (event->type >= OCRE_RESOURCE_TYPE_COUNT) { + LOG_ERR("Invalid event type: %d", event->type); + continue; + } + wasm_module_inst_t module_inst = current_module_tls ? *current_module_tls : NULL; + if (!module_inst) { + LOG_ERR("No module instance for event type %d", event->type); + continue; + } + switch (event->type) { + case OCRE_RESOURCE_TYPE_TIMER: + LOG_INF("Timer event: id=%d, owner=%p", event->id, (void *)module_inst); + break; + case OCRE_RESOURCE_TYPE_GPIO: + LOG_INF("GPIO event: id=%d, state=%d, owner=%p", event->id, event->state, (void *)module_inst); + break; + case OCRE_RESOURCE_TYPE_SENSOR: + LOG_INF("Sensor event: id=%d, channel=%d, value=%d, owner=%p", event->id, event->port, event->state, + (void *)module_inst); + break; + default: + LOG_ERR("Unhandled event type: %d", event->type); + continue; + } + k_mutex_lock(®istry_mutex, K_FOREVER); + module_node_t *node; + bool found = false; + SYS_SLIST_FOR_EACH_CONTAINER(&module_registry, node, node) { + if (node->ctx.inst == module_inst) { + found = true; + wasm_function_inst_t dispatcher = node->ctx.dispatchers[event->type]; + if (!dispatcher) { + LOG_ERR("No dispatcher for event type %d, module %p", event->type, (void *)module_inst); + break; + } + if (!node->ctx.exec_env) { + LOG_ERR("Null exec_env for module %p", (void *)module_inst); + break; + } + // Array to store arguments for wasm_event_t handler. Size is 3 to hold: + // 1. Event type identifier + // 2. Event data pointer + // 3. Additional context or flags + uint32_t args[3] = {0}; + bool result = false; + current_module_tls = &node->ctx.inst; + switch (event->type) { + case OCRE_RESOURCE_TYPE_TIMER: + args[0] = event->id; + LOG_INF("Dispatching timer event: ID=%d", args[0]); + result = wasm_runtime_call_wasm(node->ctx.exec_env, dispatcher, 1, args); + break; + case OCRE_RESOURCE_TYPE_GPIO: + args[0] = event->id; + args[1] = event->state; + LOG_INF("Dispatching GPIO event: pin=%d, state=%d", args[0], args[1]); + result = wasm_runtime_call_wasm(node->ctx.exec_env, dispatcher, 2, args); + break; + case OCRE_RESOURCE_TYPE_SENSOR: + args[0] = event->id; + args[1] = event->port; + args[2] = event->state; + LOG_INF("Dispatching sensor event: ID=%d, channel=%d, value=%d", args[0], args[1], args[2]); + result = wasm_runtime_call_wasm(node->ctx.exec_env, dispatcher, 3, args); + break; + default: + LOG_ERR("Unknown event type in dispatcher"); + break; + } + if (!result) { + LOG_ERR("WASM call failed: %s", wasm_runtime_get_exception(module_inst)); + } + current_module_tls = NULL; + node->ctx.last_activity = k_uptime_get_32(); + break; + } + } + k_mutex_unlock(®istry_mutex); + if (!found) { + LOG_ERR("Module instance %p not found in registry", (void *)module_inst); + } + } + } + + // Clean up WASM runtime thread environment + wasm_runtime_destroy_thread_env(); +} + +int ocre_get_event(wasm_exec_env_t exec_env, uint32_t type_offset, uint32_t id_offset, uint32_t port_offset, + uint32_t state_offset) { + wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); + if (!module_inst) { + LOG_ERR("No module instance for exec_env"); + return -EINVAL; + } + + int32_t *type_native = (int32_t *)wasm_runtime_addr_app_to_native(module_inst, type_offset); + int32_t *id_native = (int32_t *)wasm_runtime_addr_app_to_native(module_inst, id_offset); + int32_t *port_native = (int32_t *)wasm_runtime_addr_app_to_native(module_inst, port_offset); + int32_t *state_native = (int32_t *)wasm_runtime_addr_app_to_native(module_inst, state_offset); + + if (!type_native || !id_native || !port_native || !state_native) { + LOG_ERR("Invalid offsets provided"); + return -EINVAL; + } + + wasm_event_t event; + k_spinlock_key_t key = k_spin_lock(&wasm_event_queue_lock); + int ret = k_msgq_get(&wasm_event_queue, &event, K_NO_WAIT); + if (ret != 0) { + k_spin_unlock(&wasm_event_queue_lock, key); + return -ENOENT; + } + + *type_native = event.type; + *id_native = event.id; + *port_native = event.port; + *state_native = event.state; + + LOG_INF("Retrieved event: type=%d, id=%d, port=%d, state=%d", event.type, event.id, event.port, event.state); + k_spin_unlock(&wasm_event_queue_lock, key); + return 0; +} + +int ocre_common_init(void) { + static bool initialized = false; + if (initialized) { + LOG_INF("Common system already initialized"); + return 0; + } + k_mutex_init(®istry_mutex); + sys_slist_init(&module_registry); + k_sem_init(&event_sem, 0, UINT_MAX); + ring_buf_init(&event_ring, EVENT_BUFFER_SIZE, event_buffer); + if ((uintptr_t)wasm_event_queue_buffer_ptr % 4 != 0) { + LOG_ERR("wasm_event_queue_buffer misaligned: %p", (void *)wasm_event_queue_buffer_ptr); + return -EINVAL; + } + k_msgq_init(&wasm_event_queue, wasm_event_queue_buffer, sizeof(wasm_event_t), 64); + wasm_event_t dummy; + while (k_msgq_get(&wasm_event_queue, &dummy, K_NO_WAIT) == 0) { + LOG_INF("Purged stale event from queue"); + } + wasm_event_queue_initialized = true; + LOG_INF("wasm_event_queue initialized at %p, size=%d, buffer=%p", (void *)&wasm_event_queue, sizeof(wasm_event_t), + (void *)wasm_event_queue_buffer_ptr); + for (int i = 0; i < EVENT_THREAD_POOL_SIZE; i++) { + event_args[i].index = i; + char thread_name[16]; + snprintf(thread_name, sizeof(thread_name), "event_thread_%d", i); + int ret = core_thread_create(&event_threads[i], event_thread_fn, &event_args[i], thread_name, EVENT_THREAD_STACK_SIZE, 5); + if (ret != 0) { + LOG_ERR("Failed to create thread for event %d", i); + return -1; + } + LOG_INF("Started event thread %s", thread_name); + } + initialized = true; + common_initialized = true; + LOG_INF("OCRE common initialized successfully"); + return 0; +} + +// Signal event threads to exit gracefully +void ocre_common_shutdown(void) { + event_threads_exit = true; + for (int i = 0; i < EVENT_THREAD_POOL_SIZE; i++) { + k_sem_give(&event_sem); + } +} + +int ocre_register_cleanup_handler(ocre_resource_type_t type, ocre_cleanup_handler_t handler) { + if (type >= OCRE_RESOURCE_TYPE_COUNT) { + LOG_ERR("Invalid resource type: %d", type); + return -EINVAL; + } + cleanup_handlers[type] = (struct cleanup_handler){type, handler}; + LOG_INF("Registered cleanup handler for type %d", type); + return 0; +} + +int ocre_register_module(wasm_module_inst_t module_inst) { + if (!module_inst) { + LOG_ERR("Null module instance"); + return -EINVAL; + } + module_node_t *node = k_malloc(sizeof(module_node_t)); + if (!node) { + LOG_ERR("Failed to allocate module node"); + return -ENOMEM; + } + node->ctx.inst = module_inst; + node->ctx.exec_env = wasm_runtime_create_exec_env(module_inst, OCRE_WASM_STACK_SIZE); + if (!node->ctx.exec_env) { + LOG_ERR("Failed to create exec env for module %p", (void *)module_inst); + k_free(node); + return -ENOMEM; + } + node->ctx.in_use = true; + node->ctx.last_activity = k_uptime_get_32(); + memset(node->ctx.resource_count, 0, sizeof(node->ctx.resource_count)); + memset(node->ctx.dispatchers, 0, sizeof(node->ctx.dispatchers)); + k_mutex_lock(®istry_mutex, K_FOREVER); + sys_slist_append(&module_registry, &node->node); + k_mutex_unlock(®istry_mutex); + LOG_INF("Module registered: %p", (void *)module_inst); + return 0; +} + +void ocre_unregister_module(wasm_module_inst_t module_inst) { + if (!module_inst) { + LOG_ERR("Null module instance"); + return; + } + k_mutex_lock(®istry_mutex, K_FOREVER); + module_node_t *node, *tmp; + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&module_registry, node, tmp, node) { + if (node->ctx.inst == module_inst) { + ocre_cleanup_module_resources(module_inst); + if (node->ctx.exec_env) { + wasm_runtime_destroy_exec_env(node->ctx.exec_env); + } + sys_slist_remove(&module_registry, NULL, &node->node); + k_free(node); + LOG_INF("Module unregistered: %p", (void *)module_inst); + break; + } + } + k_mutex_unlock(®istry_mutex); +} + +ocre_module_context_t *ocre_get_module_context(wasm_module_inst_t module_inst) { + if (!module_inst) { + LOG_ERR("Null module instance"); + return NULL; + } + k_mutex_lock(®istry_mutex, K_FOREVER); + module_node_t *node; + SYS_SLIST_FOR_EACH_CONTAINER(&module_registry, node, node) { + if (node->ctx.inst == module_inst) { + node->ctx.last_activity = k_uptime_get_32(); + k_mutex_unlock(®istry_mutex); + return &node->ctx; + } + } + k_mutex_unlock(®istry_mutex); + LOG_ERR("Module context not found for %p", (void *)module_inst); + return NULL; +} + +int ocre_register_dispatcher(wasm_exec_env_t exec_env, ocre_resource_type_t type, const char *function_name) { + if (!exec_env || !function_name || type >= OCRE_RESOURCE_TYPE_COUNT) { + LOG_ERR("Invalid dispatcher params: exec_env=%p, type=%d, func=%s", (void *)exec_env, type, + function_name ? function_name : "null"); + return -EINVAL; + } + wasm_module_inst_t module_inst = current_module_tls ? *current_module_tls : wasm_runtime_get_module_inst(exec_env); + if (!module_inst) { + LOG_ERR("No module instance for event type %d", type); + return -EINVAL; + } + ocre_module_context_t *ctx = ocre_get_module_context(module_inst); + if (!ctx) { + LOG_ERR("Module context not found for %p", (void *)module_inst); + return -EINVAL; + } + LOG_DBG("Attempting to lookup function '%s' in module %p", function_name, (void *)module_inst); + wasm_function_inst_t func = wasm_runtime_lookup_function(module_inst, function_name); + if (!func) { + LOG_ERR("Function %s not found in module %p", function_name, (void *)module_inst); + return -EINVAL; + } + k_mutex_lock(®istry_mutex, K_FOREVER); + ctx->dispatchers[type] = func; + k_mutex_unlock(®istry_mutex); + LOG_INF("Registered dispatcher for type %d: %s", type, function_name); + return 0; +} + +int ocre_post_event(ocre_event_t *event) { + if (!event) { + LOG_ERR("Null event"); + return -EINVAL; + } + if (event->type >= OCRE_RESOURCE_TYPE_COUNT) { + LOG_ERR("Invalid event type: %d", event->type); + return -EINVAL; + } + if (ring_buf_space_get(&event_ring) < sizeof(ocre_event_t)) { + LOG_ERR("Event ring buffer full"); + return -ENOMEM; + } + if (ring_buf_put(&event_ring, (uint8_t *)event, sizeof(ocre_event_t)) != sizeof(ocre_event_t)) { + LOG_ERR("Failed to post event to ring buffer"); + return -ENOMEM; + } + k_sem_give(&event_sem); + LOG_INF("Posted event: type=%d", event->type); + return 0; +} + +uint32_t ocre_get_resource_count(wasm_module_inst_t module_inst, ocre_resource_type_t type) { + ocre_module_context_t *ctx = ocre_get_module_context(module_inst); + return ctx ? ctx->resource_count[type] : 0; +} + +void ocre_increment_resource_count(wasm_module_inst_t module_inst, ocre_resource_type_t type) { + ocre_module_context_t *ctx = ocre_get_module_context(module_inst); + if (ctx && type < OCRE_RESOURCE_TYPE_COUNT) { + k_mutex_lock(®istry_mutex, K_FOREVER); + ctx->resource_count[type]++; + k_mutex_unlock(®istry_mutex); + LOG_INF("Incremented resource count: type=%d, count=%d", type, ctx->resource_count[type]); + } +} + +void ocre_decrement_resource_count(wasm_module_inst_t module_inst, ocre_resource_type_t type) { + ocre_module_context_t *ctx = ocre_get_module_context(module_inst); + if (ctx && type < OCRE_RESOURCE_TYPE_COUNT && ctx->resource_count[type] > 0) { + k_mutex_lock(®istry_mutex, K_FOREVER); + ctx->resource_count[type]--; + k_mutex_unlock(®istry_mutex); + LOG_INF("Decremented resource count: type=%d, count=%d", type, ctx->resource_count[type]); + } +} + +void ocre_cleanup_module_resources(wasm_module_inst_t module_inst) { + for (int i = 0; i < OCRE_RESOURCE_TYPE_COUNT; i++) { + if (cleanup_handlers[i].handler) { + cleanup_handlers[i].handler(module_inst); + } + } +} + +wasm_module_inst_t ocre_get_current_module(void) { + return current_module_tls ? *current_module_tls : NULL; +} diff --git a/src/ocre/api/ocre_common.h b/src/ocre/api/ocre_common.h new file mode 100644 index 00000000..0371c717 --- /dev/null +++ b/src/ocre/api/ocre_common.h @@ -0,0 +1,199 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-License: Apache-2.0 + */ + +#ifndef OCRE_COMMON_H +#define OCRE_COMMON_H + +#include +#include +#include + +#define OCRE_EVENT_THREAD_STACK_SIZE 2048 +#define OCRE_EVENT_THREAD_PRIORITY 5 +#define OCRE_WASM_STACK_SIZE 16384 + +extern bool common_initialized; +extern bool wasm_event_queue_initialized; +extern __thread wasm_module_inst_t *current_module_tls; + +/** + * @brief Enumeration of OCRE resource types. + */ +typedef enum { + OCRE_RESOURCE_TYPE_TIMER, ///< Timer resource + OCRE_RESOURCE_TYPE_GPIO, ///< GPIO resource + OCRE_RESOURCE_TYPE_SENSOR, ///< Sensor resource + OCRE_RESOURCE_TYPE_COUNT ///< Total number of resource types +} ocre_resource_type_t; + +/** + * @brief Structure representing the context of an OCRE module. + */ +typedef struct { + wasm_module_inst_t inst; ///< WASM module instance + wasm_exec_env_t exec_env; ///< WASM execution environment + bool in_use; ///< Flag indicating if the module is in use + uint32_t last_activity; ///< Timestamp of the last activity + uint32_t resource_count[OCRE_RESOURCE_TYPE_COUNT]; ///< Count of resources per type + wasm_function_inst_t dispatchers[OCRE_RESOURCE_TYPE_COUNT]; ///< Event dispatchers per resource type +} ocre_module_context_t; + +/** + * @brief Type definition for cleanup handler function. + * + * @param module_inst The WASM module instance to clean up. + */ +typedef void (*ocre_cleanup_handler_t)(wasm_module_inst_t module_inst); + +/** + * @brief Structure representing a WASM event for the event queue. + */ +typedef struct { + uint32_t type; ///< Event type + uint32_t id; ///< Event ID + uint32_t port; ///< Port associated with the event + uint32_t state; ///< State associated with the event +} wasm_event_t; + +/** + * @brief Structure representing an OCRE event for dispatching. + */ +typedef struct { + union { + struct { + uint32_t timer_id; ///< Timer ID + wasm_module_inst_t owner; ///< Owner module instance + } timer_event; ///< Timer event data + struct { + uint32_t pin_id; ///< GPIO pin ID + uint32_t state; ///< GPIO state + wasm_module_inst_t owner; ///< Owner module instance + } gpio_event; ///< GPIO event data + struct { + uint32_t sensor_id; ///< Sensor ID + uint32_t channel; ///< Sensor channel + uint32_t value; ///< Sensor value + wasm_module_inst_t owner; ///< Owner module instance + } sensor_event; ///< Sensor event data + } data; ///< Union of event data + ocre_resource_type_t type; ///< Type of the event +} ocre_event_t; + +/** + * @brief Initialize the OCRE common system. + * + * This function initializes the common components required for OCRE operations. + * + * @return 0 on success, negative error code on failure. + */ +int ocre_common_init(void); + +/** + * @brief Register a WASM module with the OCRE system. + * + * @param module_inst The WASM module instance to register. + * @return 0 on success, negative error code on failure. + */ +int ocre_register_module(wasm_module_inst_t module_inst); + +/** + * @brief Unregister a WASM module from the OCRE system. + * + * @param module_inst The WASM module instance to unregister. + */ +void ocre_unregister_module(wasm_module_inst_t module_inst); + +/** + * @brief Get the context of a registered WASM module. + * + * @param module_inst The WASM module instance. + * @return Pointer to the module context, or NULL if not found. + */ +ocre_module_context_t *ocre_get_module_context(wasm_module_inst_t module_inst); + +/** + * @brief Register an event dispatcher for a specific resource type. + * + * @param exec_env WASM execution environment. + * @param type Resource type. + * @param function_name Name of the WASM function to use as dispatcher. + * @return 0 on success, negative error code on failure. + */ +int ocre_register_dispatcher(wasm_exec_env_t exec_env, ocre_resource_type_t type, const char *function_name); + +/** + * @brief Post an event to the OCRE event queue. + * + * @param event Pointer to the event to post. + * @return 0 on success, negative error code on failure. + */ +int ocre_post_event(ocre_event_t *event); + +/** + * @brief Get the count of resources of a specific type for a module. + * + * @param module_inst The WASM module instance. + * @param type Resource type. + * @return Number of resources, or 0 if module not found. + */ +uint32_t ocre_get_resource_count(wasm_module_inst_t module_inst, ocre_resource_type_t type); + +/** + * @brief Increment the resource count for a specific type. + * + * @param module_inst The WASM module instance. + * @param type Resource type. + */ +void ocre_increment_resource_count(wasm_module_inst_t module_inst, ocre_resource_type_t type); + +/** + * @brief Decrement the resource count for a specific type. + * + * @param module_inst The WASM module instance. + * @param type Resource type. + */ +void ocre_decrement_resource_count(wasm_module_inst_t module_inst, ocre_resource_type_t type); + +/** + * @brief Clean up resources for a module. + * + * @param module_inst The WASM module instance. + */ +void ocre_cleanup_module_resources(wasm_module_inst_t module_inst); + +/** + * @brief Register a cleanup handler for a resource type. + * + * @param type Resource type. + * @param handler Cleanup handler function. + * @return 0 on success, negative error code on failure. + */ +int ocre_register_cleanup_handler(ocre_resource_type_t type, ocre_cleanup_handler_t handler); + +/** + * @brief Get the current WASM module instance. + * + * @return The current WASM module instance, or NULL if not set. + */ +wasm_module_inst_t ocre_get_current_module(void); + +/** + * @brief Get an event from the WASM event queue. + * + * @param exec_env WASM execution environment. + * @param type_offset Offset in WASM memory for event type. + * @param id_offset Offset in WASM memory for event ID. + * @param port_offset Offset in WASM memory for event port. + * @param state_offset Offset in WASM memory for event state. + * @return 0 on success, negative error code on failure. + */ +int ocre_get_event(wasm_exec_env_t exec_env, uint32_t type_offset, uint32_t id_offset, uint32_t port_offset, + uint32_t state_offset); + +void ocre_common_shutdown(void); + +#endif /* OCRE_COMMON_H */ \ No newline at end of file diff --git a/src/ocre/component/component.c b/src/ocre/component/component.c index 9d0599d5..daa6121b 100644 --- a/src/ocre/component/component.c +++ b/src/ocre/component/component.c @@ -6,21 +6,16 @@ */ #include -#include -LOG_MODULE_DECLARE(device_manager_component, OCRE_LOG_LEVEL); +#include "ocre_core_external.h" #include "component.h" void ocre_component_init(struct ocre_component *component) { - k_msgq_init(&component->msgq, component->msgq_buffer, sizeof(struct ocre_message), MSG_QUEUE_DEPTH); + core_mq_init(&component->msgq, "/ocre_component_msgq", sizeof(struct ocre_message), MSG_QUEUE_DEPTH); } int ocre_component_send(struct ocre_component *component, struct ocre_message *msg) { - int ret = k_msgq_put(&component->msgq, msg, K_NO_WAIT); - - if (ret != 0) { - LOG_HEXDUMP_DBG(msg, sizeof(struct ocre_message), "message"); - } + int ret = core_mq_send(&component->msgq, msg, sizeof(struct ocre_message)); return ret; -} \ No newline at end of file +} diff --git a/src/ocre/component/component.h b/src/ocre/component/component.h index decd77d0..b6d96e7a 100644 --- a/src/ocre/component/component.h +++ b/src/ocre/component/component.h @@ -8,10 +8,11 @@ #ifndef OCRE_COMPONENT_H #define OCRE_COMPONENT_H -#include +#include "ocre_core_external.h" #include -#define MSG_QUEUE_DEPTH 16 +// can't be higher than /proc/sys/fs/mqueue/msg_max which is typically 10 +#define MSG_QUEUE_DEPTH 10 #define COMPONENT_SEND_SIMPLE(c, e) \ struct ocre_message _msg = {.event = e}; \ @@ -19,12 +20,11 @@ struct ocre_component { struct ocre_message msg; /*!< Message struct for reading messages into */ - struct k_msgq msgq; /*!< Message queue to read from */ - char __aligned(4) msgq_buffer[MSG_QUEUE_DEPTH * sizeof(struct ocre_message)]; /*!< Message queue buffer */ + core_mq_t msgq; /*!< Message queue to read from */ }; void ocre_component_init(struct ocre_component *component); int ocre_component_send(struct ocre_component *component, struct ocre_message *msg); -#endif \ No newline at end of file +#endif // OCRE_COMPONENT_H diff --git a/src/ocre/components/container_supervisor/cs_main.c b/src/ocre/components/container_supervisor/cs_main.c index 25f3f8c5..2727572f 100644 --- a/src/ocre/components/container_supervisor/cs_main.c +++ b/src/ocre/components/container_supervisor/cs_main.c @@ -5,33 +5,44 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include - #include #include "cs_sm.h" +#include "ocre_core_external.h" LOG_MODULE_REGISTER(ocre_cs_component, OCRE_LOG_LEVEL); -#define OCRE_CS_THREAD_STACK_SIZE 4096 #define OCRE_CS_THREAD_PRIORITY 0 -K_THREAD_STACK_DEFINE(ocre_cs_stack, OCRE_CS_THREAD_STACK_SIZE); -k_tid_t ocre_cs_tid = NULL; -struct k_thread ocre_cs_thread; +static core_thread_t ocre_cs_thread; +static int ocre_cs_thread_initialized = 0; -static void ocre_cs_main(void *ctx, void *arg1, void *arg2) { +static void ocre_cs_main(void *ctx) { + wasm_runtime_init_thread_env(); LOG_INF("Container Supervisor started."); int ret = _ocre_cs_run(ctx); LOG_ERR("Container Supervisor exited: %d", ret); + wasm_runtime_destroy_thread_env(); } // Function to start the thread void start_ocre_cs_thread(ocre_cs_ctx *ctx) { - ocre_cs_tid = k_thread_create(&ocre_cs_thread, ocre_cs_stack, OCRE_CS_THREAD_STACK_SIZE, ocre_cs_main, ctx, NULL, NULL, - OCRE_CS_THREAD_PRIORITY, 0, K_NO_WAIT); - k_thread_name_set(ocre_cs_tid, "Ocre Container Supervisor"); + if (ocre_cs_thread_initialized) { + LOG_WRN("Container Supervisor thread is already running."); + return; + } + int ret = core_thread_create(&ocre_cs_thread, ocre_cs_main, ctx, "Ocre Container Supervisor", OCRE_CS_THREAD_STACK_SIZE, OCRE_CS_THREAD_PRIORITY); + if (ret != 0) { + LOG_ERR("Failed to create Container Supervisor thread: %d", ret); + return; + } + ocre_cs_thread_initialized = 1; } void destroy_ocre_cs_thread(void) { - k_thread_abort(&ocre_cs_thread); -} \ No newline at end of file + if (!ocre_cs_thread_initialized) { + LOG_ERR("Container Supervisor thread is not running.\n"); + return; + } + core_thread_destroy(&ocre_cs_thread); + ocre_cs_thread_initialized = 0; +} diff --git a/src/ocre/components/container_supervisor/cs_sm.c b/src/ocre/components/container_supervisor/cs_sm.c index e78a705a..47e42e85 100644 --- a/src/ocre/components/container_supervisor/cs_sm.c +++ b/src/ocre/components/container_supervisor/cs_sm.c @@ -6,15 +6,15 @@ */ #include -#include +#include "ocre_core_external.h" LOG_MODULE_DECLARE(ocre_cs_component, OCRE_LOG_LEVEL); - +#ifdef CONFIG_OCRE_SENSORS +#include "../../ocre_sensors/ocre_sensors.h" +#endif #include "cs_sm.h" #include "cs_sm_impl.h" #include -#include "../../ocre_sensors/ocre_sensors.h" - // Define state machine and component struct ocre_component ocre_cs_component; state_machine_t ocre_cs_state_machine; @@ -64,35 +64,76 @@ static void runtime_running_run(void *o) { switch (msg->event) { case EVENT_CREATE_CONTAINER: { LOG_INF("EVENT_CREATE_CONTAINER"); - if (CS_create_container(ctx, msg->containerId) == CONTAINER_STATUS_CREATED) { + + if (msg->containerId < 0 || msg->containerId >= CONFIG_MAX_CONTAINERS) { + LOG_ERR("Invalid container ID: %d", msg->containerId); + break; + } + + if (CS_create_container(&ctx->containers[msg->containerId]) == CONTAINER_STATUS_CREATED) { LOG_INF("Created container in slot:%d", msg->containerId); } else { - LOG_INF("Failed to create container in slot:%d", msg->containerId); + LOG_ERR("Failed to create container in slot:%d", msg->containerId); } break; } case EVENT_RUN_CONTAINER: { LOG_INF("EVENT_RUN_CONTAINER"); - if (CS_run_container(ctx, &msg->containerId) == CONTAINER_STATUS_RUNNING) { + + if (msg->containerId < 0 || msg->containerId >= CONFIG_MAX_CONTAINERS) { + LOG_ERR("Invalid container ID: %d", msg->containerId); + break; + } + + if (CS_run_container(&ctx->containers[msg->containerId]) == CONTAINER_STATUS_RUNNING) { LOG_INF("Started container in slot:%d", msg->containerId); } else { - LOG_INF("Failed to run container in slot:%d", msg->containerId); + LOG_ERR("Failed to run container in slot:%d", msg->containerId); } break; } case EVENT_STOP_CONTAINER: { LOG_INF("EVENT_STOP_CONTAINER"); - CS_stop_container(ctx, msg->containerId, callback); + + if (msg->containerId < 0 || msg->containerId >= CONFIG_MAX_CONTAINERS) { + LOG_ERR("Invalid container ID: %d", msg->containerId); + break; + } + + if (CS_stop_container(&ctx->containers[msg->containerId], callback) == CONTAINER_STATUS_STOPPED) { + LOG_INF("Stopped container in slot:%d", msg->containerId); + } else { + LOG_ERR("Failed to stop container in slot:%d", msg->containerId); + } break; } case EVENT_DESTROY_CONTAINER: { LOG_INF("EVENT_DESTROY_CONTAINER"); - CS_destroy_container(ctx, msg->containerId, callback); + + if (msg->containerId < 0 || msg->containerId >= CONFIG_MAX_CONTAINERS) { + LOG_ERR("Invalid container ID: %d", msg->containerId); + break; + } + + if (CS_destroy_container(&ctx->containers[msg->containerId], callback) == CONTAINER_STATUS_DESTROYED) { + LOG_INF("Destroyed container in slot:%d", msg->containerId); + } else { + LOG_ERR("Failed to destroy container in slot:%d", msg->containerId); + } break; } case EVENT_RESTART_CONTAINER: { LOG_INF("EVENT_RESTART_CONTAINER"); - CS_restart_container(ctx, msg->containerId, callback); + if (msg->containerId < 0 || msg->containerId >= CONFIG_MAX_CONTAINERS) { + LOG_ERR("Invalid container ID: %d", msg->containerId); + break; + } + + if (CS_restart_container(&ctx->containers[msg->containerId], callback) == CONTAINER_STATUS_RUNNING) { + LOG_INF("Container in slot:%d restarted successfully", msg->containerId); + } else { + LOG_ERR("Failed to restart container in slot:%d", msg->containerId); + } break; } case EVENT_CS_DESTROY: @@ -121,8 +162,6 @@ static const struct smf_state hsm[] = { // Entry point for running the state machine int _ocre_cs_run(ocre_cs_ctx *ctx) { ocre_component_init(&ocre_cs_component); - sm_init(&ocre_cs_state_machine, &ocre_cs_component.msgq, &ocre_cs_component.msg, ctx, hsm); - return sm_run(&ocre_cs_state_machine, STATE_RUNTIME_UNINITIALIZED); } diff --git a/src/ocre/components/container_supervisor/cs_sm.h b/src/ocre/components/container_supervisor/cs_sm.h index dfa5f50c..4ba02bc3 100644 --- a/src/ocre/components/container_supervisor/cs_sm.h +++ b/src/ocre/components/container_supervisor/cs_sm.h @@ -8,8 +8,7 @@ #ifndef OCRE_IWASM_H #define OCRE_IWASM_H -#include -#include +#include "ocre_core_external.h" #include #include diff --git a/src/ocre/components/container_supervisor/cs_sm_impl.c b/src/ocre/components/container_supervisor/cs_sm_impl.c index eb2cdedd..9658b8e8 100644 --- a/src/ocre/components/container_supervisor/cs_sm_impl.c +++ b/src/ocre/components/container_supervisor/cs_sm_impl.c @@ -4,31 +4,48 @@ * * SPDX-License-Identifier: Apache-2.0 */ - #include -#include -#include -#include -#include +#include "ocre_core_external.h" + +#ifdef CONFIG_OCRE_TIMER +#include "ocre_timers/ocre_timer.h" +#endif +#ifdef CONFIG_OCRE_GPIO +#include "ocre_gpio/ocre_gpio.h" +#endif +#ifdef CONFIG_OCRE_CONTAINER_MESSAGING +#include "container_messaging/messaging.h" +#endif +#if defined(CONFIG_OCRE_TIMER) || defined(CONFIG_OCRE_GPIO) || defined(CONFIG_OCRE_SENSORS) +#include "api/ocre_common.h" +#endif + +#ifdef CONFIG_OCRE_SHELL +#include "ocre/shell/ocre_shell.h" +#endif + LOG_MODULE_DECLARE(ocre_cs_component, OCRE_LOG_LEVEL); -#include + #include "../../../../../wasm-micro-runtime/core/iwasm/include/lib_export.h" #include "bh_log.h" - +#include #include "cs_sm.h" #include "cs_sm_impl.h" -#include "../../ocre_timers/ocre_timer.h" -#include "../../container_messaging/messaging.h" static char filepath[FILE_PATH_MAX]; static char wamr_heap_buf[CONFIG_OCRE_WAMR_HEAP_BUFFER_SIZE] = {0}; -// Thread stacks for containers -#define CONTAINER_THREAD_STACK_SIZE 4096 -static K_THREAD_STACK_ARRAY_DEFINE(container_thread_stacks, MAX_CONTAINERS, CONTAINER_THREAD_STACK_SIZE); -static struct k_thread container_threads[MAX_CONTAINERS]; -static k_tid_t container_thread_ids[MAX_CONTAINERS]; -static bool container_thread_active[MAX_CONTAINERS] = {false}; +// Thread pool for container execution +#define CONTAINER_THREAD_POOL_SIZE 4 +static core_thread_t container_threads[CONTAINER_THREAD_POOL_SIZE]; +static bool container_thread_active[CONTAINER_THREAD_POOL_SIZE] = {false}; + +static core_mutex_t container_mutex; + +// Arguments for container threads +struct container_thread_args { + ocre_container_t *container; +}; #ifdef CONFIG_OCRE_MEMORY_CHECK_ENABLED static size_t ocre_get_available_memory(void) { @@ -48,92 +65,163 @@ static size_t ocre_get_available_memory(void) { } #endif -// Thread entry point for container execution -static void container_thread_entry(void *container_id_ptr, void *ctx_ptr, void *unused) { - int container_id = *((int *)container_id_ptr); - ocre_cs_ctx *ctx = (ocre_cs_ctx *)ctx_ptr; +static bool validate_container_memory(ocre_container_t *container) { +#ifdef CONFIG_OCRE_MEMORY_CHECK_ENABLED + size_t requested_heap = container->ocre_container_data.heap_size; + size_t requested_stack = container->ocre_container_data.stack_size; - LOG_INF("Container thread %d started", container_id); - if (wasm_application_execute_main(ctx->containers[container_id].ocre_runtime_arguments.module_inst, 0, NULL)) { - LOG_INF("Container %d main function completed successfully", container_id); - } else { - LOG_ERR("ERROR calling main for container %d: %s", container_id, - wasm_runtime_get_exception(ctx->containers[container_id].ocre_runtime_arguments.module_inst)); - ctx->containers[container_id].container_runtime_status = CONTAINER_STATUS_ERROR; + if (requested_heap == 0 || requested_heap > CONFIG_OCRE_CONTAINER_DEFAULT_HEAP_SIZE) { + LOG_ERR("Invalid heap size requested: %zu bytes", requested_heap); + return false; + } + + if (requested_stack == 0 || requested_stack > CONFIG_OCRE_CONTAINER_DEFAULT_STACK_SIZE) { + LOG_ERR("Invalid stack size requested: %zu bytes", requested_stack); + return false; } - // Clean up timers when container exits (whether normally or due to error) - ocre_timer_cleanup_container(ctx->containers[container_id].ocre_runtime_arguments.module_inst); + size_t available_memory = ocre_get_available_memory(); + size_t required_memory = requested_heap + requested_stack + sizeof(WASMExecEnv); + + if (available_memory < required_memory) { + LOG_ERR("Insufficient memory for container %d: required %zu bytes, available %zu bytes", + container->container_ID, required_memory, available_memory); + return false; + } + LOG_INF("Memory check passed: %zu bytes required, %zu bytes available", required_memory, available_memory); + + container->ocre_runtime_arguments.stack_size = requested_stack; + container->ocre_runtime_arguments.heap_size = requested_heap; +#else + container->ocre_runtime_arguments.stack_size = CONFIG_OCRE_CONTAINER_DEFAULT_STACK_SIZE; + container->ocre_runtime_arguments.heap_size = CONFIG_OCRE_CONTAINER_DEFAULT_HEAP_SIZE; +#endif + return true; +} + +// Container thread entry function +static void container_thread_entry(struct container_thread_args *args) { + ocre_container_t *container = args->container; + wasm_module_inst_t module_inst = container->ocre_runtime_arguments.module_inst; + + // Initialize WASM runtime thread environment + wasm_runtime_init_thread_env(); - LOG_INF("Container thread %d exiting", container_id); - container_thread_active[container_id] = false; + LOG_INF("Container thread %d started", container->container_ID); + +#if defined(CONFIG_OCRE_TIMER) || defined(CONFIG_OCRE_GPIO) || defined(CONFIG_OCRE_SENSORS) + // Set TLS for the container's WASM module + current_module_tls = &module_inst; +#endif + // Run the WASM main function + bool success = wasm_application_execute_main(module_inst, 0, NULL); + + // Update container status + container->container_runtime_status = success ? CONTAINER_STATUS_STOPPED : CONTAINER_STATUS_ERROR; + + // Cleanup sequence + core_mutex_lock(&container->lock); + { + // Cleanup timers and GPIO resources +#ifdef CONFIG_OCRE_TIMER + ocre_timer_cleanup_container(module_inst); +#endif +#ifdef CONFIG_OCRE_GPIO + ocre_gpio_cleanup_container(module_inst); +#endif + + // Clear thread tracking + container_thread_active[container->container_ID] = false; +#if defined(CONFIG_OCRE_TIMER) || defined(CONFIG_OCRE_GPIO) || defined(CONFIG_OCRE_SENSORS) + // Clear TLS + current_module_tls = NULL; +#endif - if (ctx->containers[container_id].container_runtime_status == CONTAINER_STATUS_RUNNING) { - ctx->containers[container_id].container_runtime_status = CONTAINER_STATUS_STOPPED; - LOG_INF("Container %d marked as STOPPED after main exit", container_id); } + core_mutex_unlock(&container->lock); + + LOG_INF("Container thread %d exited cleanly", container->container_ID); + + // Clean up WASM runtime thread environment + wasm_runtime_destroy_thread_env(); + core_free(args); // Free the dynamically allocated args } -static int load_binary_to_buffer_fs(ocre_cs_ctx *ctx, int container_id, ocre_container_data_t *container_data) { - int ret = 0; - struct fs_file_t app_file; - struct fs_dirent entry; +static int get_available_thread(void) { + for (int i = 0; i < CONTAINER_THREAD_POOL_SIZE; i++) { + if (!container_thread_active[i]) { + return i; + } + } + return -1; +} - snprintf(filepath, sizeof(filepath), "/lfs/ocre/images/%s.bin", container_data->sha256); +static int load_binary_to_buffer_fs(ocre_runtime_arguments_t *container_arguments, + ocre_container_data_t *container_data) { + int ret = 0; + size_t file_size = 0; + void *file_handle = NULL; + char filepath[FILE_PATH_MAX]; - ret = fs_stat(filepath, &entry); + ret = core_construct_filepath(filepath, sizeof(filepath), container_data->sha256); + if (ret < 0) { + LOG_ERR("Failed to construct filepath for %s: %d", container_data->sha256, ret); + return ret; + } + ret = core_filestat(filepath, &file_size); if (ret < 0) { LOG_ERR("Failed to get file status for %s: %d", filepath, ret); return ret; } - ctx->containers[container_id].ocre_runtime_arguments.size = entry.size; - LOG_INF("Allocating memory for container %d", container_id); - ctx->containers[container_id].ocre_runtime_arguments.buffer = (char *)malloc(entry.size * sizeof(char)); - if (ctx->containers[container_id].ocre_runtime_arguments.buffer == NULL) { + container_arguments->size = file_size; + container_arguments->buffer = malloc(file_size); + if (!container_arguments->buffer) { LOG_ERR("Failed to allocate memory for container binary."); return -ENOMEM; } - LOG_INF("File path: %s, size: %d", filepath, entry.size); - fs_file_t_init(&app_file); + LOG_INF("File path: %s, size: %d", filepath, file_size); - ret = fs_open(&app_file, filepath, FS_O_READ); + ret = core_fileopen(filepath, &file_handle); if (ret < 0) { LOG_ERR("Failed to open file %s: %d", filepath, ret); + core_free(container_arguments->buffer); return ret; } - ret = fs_read(&app_file, ctx->containers[container_id].ocre_runtime_arguments.buffer, - ctx->containers[container_id].ocre_runtime_arguments.size); + ret = core_fileread(file_handle, container_arguments->buffer, file_size); if (ret < 0) { LOG_ERR("Failed to read file %s: %d", filepath, ret); - fs_close(&app_file); + core_fileclose(file_handle); + core_free(container_arguments->buffer); return ret; } - ret = fs_close(&app_file); + ret = core_fileclose(file_handle); if (ret < 0) { LOG_ERR("Failed to close file %s: %d", filepath, ret); + core_free(container_arguments->buffer); return ret; } - return 0; } int CS_ctx_init(ocre_cs_ctx *ctx) { - for (int i = 0; i < MAX_CONTAINERS; i++) { + for (int i = 0; i < CONFIG_MAX_CONTAINERS; i++) { + core_mutex_init(&ctx->containers[i].lock); ctx->containers[i].container_runtime_status = CONTAINER_STATUS_UNKNOWN; ctx->containers[i].ocre_container_data.heap_size = CONFIG_OCRE_CONTAINER_DEFAULT_HEAP_SIZE; ctx->containers[i].ocre_container_data.stack_size = CONFIG_OCRE_CONTAINER_DEFAULT_STACK_SIZE; memset(ctx->containers[i].ocre_container_data.name, 0, sizeof(ctx->containers[i].ocre_container_data.name)); memset(ctx->containers[i].ocre_container_data.sha256, 0, sizeof(ctx->containers[i].ocre_container_data.sha256)); ctx->containers[i].ocre_container_data.timers = 0; - ctx->containers[i].ocre_container_data.watchdog_interval = 0; - container_thread_active[i] = false; - container_thread_ids[i] = NULL; + ctx->containers[i].container_ID = i; } - +#ifdef CONFIG_OCRE_SHELL + register_ocre_shell(ctx); +#endif + core_mutex_init(&container_mutex); return 0; } @@ -152,307 +240,264 @@ ocre_container_runtime_status_t CS_runtime_init(ocre_cs_ctx *ctx, ocre_container return RUNTIME_STATUS_ERROR; } - // Configure WAMR logging bh_log_set_verbose_level(BH_LOG_LEVEL_WARNING); if (!wasm_runtime_register_natives("env", ocre_api_table, ocre_api_table_size)) { LOG_ERR("Failed to register the API's"); return RUNTIME_STATUS_ERROR; } +#ifdef CONFIG_OCRE_TIMER ocre_timer_init(); +#endif +#ifdef CONFIG_OCRE_CONTAINER_MESSAGING ocre_msg_system_init(); +#endif return RUNTIME_STATUS_INITIALIZED; } ocre_container_runtime_status_t CS_runtime_destroy(void) { - for (int i = 0; i < MAX_CONTAINERS; i++) { - if (container_thread_active[i] && container_thread_ids[i] != NULL) { - k_thread_abort(container_thread_ids[i]); + // Signal event threads to exit gracefully +#if defined(CONFIG_OCRE_TIMER) || defined(CONFIG_OCRE_GPIO) || defined(CONFIG_OCRE_SENSORS) + ocre_common_shutdown(); +#endif + + // Abort any active container threads + for (int i = 0; i < CONTAINER_THREAD_POOL_SIZE; i++) { + if (container_thread_active[i]) { + core_thread_destroy(&container_threads[i]); container_thread_active[i] = false; - container_thread_ids[i] = NULL; } } return RUNTIME_STATUS_DESTROYED; } -ocre_container_status_t CS_create_container(ocre_cs_ctx *ctx, int container_id) { - if (ctx->containers[container_id].container_runtime_status == CONTAINER_STATUS_STOPPED) { - LOG_WRN("Container %d is in STOPPED state, destroying it before reuse", container_id); - CS_destroy_container(ctx, container_id, NULL); - } - - if (ctx->containers[container_id].container_runtime_status == CONTAINER_STATUS_DESTROYED || - ctx->containers[container_id].container_runtime_status == CONTAINER_STATUS_UNKNOWN) { +ocre_container_status_t CS_create_container(ocre_container_t *container) { + uint32_t curr_container_ID = container->container_ID; + ocre_container_data_t *curr_container_data = &container->ocre_container_data; + ocre_runtime_arguments_t *curr_container_arguments = &container->ocre_runtime_arguments; -#ifdef CONFIG_OCRE_MEMORY_CHECK_ENABLED - size_t requested_heap = ctx->containers[container_id].ocre_container_data.heap_size; - size_t requested_stack = ctx->containers[container_id].ocre_container_data.stack_size; + if (container->container_runtime_status == CONTAINER_STATUS_STOPPED) { + LOG_WRN("Container %d is in STOPPED state, destroying it before reuse", curr_container_ID); + CS_destroy_container(container, NULL); + } - if (requested_heap == 0 || requested_heap > CONFIG_OCRE_CONTAINER_DEFAULT_HEAP_SIZE) { - LOG_ERR("Invalid heap size requested: %zu bytes", requested_heap); - return CONTAINER_STATUS_ERROR; - } + if (container->container_runtime_status != CONTAINER_STATUS_UNKNOWN && + container->container_runtime_status != CONTAINER_STATUS_DESTROYED) { + LOG_ERR("Cannot create container again container with ID: %d, already exists", curr_container_ID); + return RUNTIME_STATUS_ERROR; + } - if (requested_stack == 0 || requested_stack > CONFIG_OCRE_CONTAINER_DEFAULT_STACK_SIZE) { - LOG_ERR("Invalid stack size requested: %zu bytes", requested_stack); - return CONTAINER_STATUS_ERROR; - } + if (!validate_container_memory(container)) { + LOG_ERR("Memory check not passed"); + return CONTAINER_STATUS_ERROR; + } - size_t available_memory = ocre_get_available_memory(); - size_t required_memory = requested_heap + requested_stack + sizeof(WASMExecEnv); + LOG_INF("Allocating memory for container %d", curr_container_ID); + int ret = load_binary_to_buffer_fs(curr_container_arguments, curr_container_data); + if (ret < 0) { + LOG_ERR("Failed to load binary to buffer: %d", ret); + return CONTAINER_STATUS_ERROR; + } + LOG_INF("Loaded binary to buffer for container %d", curr_container_ID); - if (available_memory < required_memory) { - LOG_ERR("Insufficient memory for container %d: required %zu bytes, available %zu bytes", container_id, - required_memory, available_memory); - return CONTAINER_STATUS_ERROR; - } - LOG_INF("Memory check passed: %zu bytes required, %zu bytes available", required_memory, available_memory); -#endif + curr_container_arguments->module = + wasm_runtime_load(curr_container_arguments->buffer, curr_container_arguments->size, + curr_container_arguments->error_buf, sizeof(curr_container_arguments->error_buf)); + if (!curr_container_arguments->module) { + LOG_ERR("Failed to load WASM module: %s", curr_container_arguments->error_buf); + free(curr_container_arguments->buffer); + return CONTAINER_STATUS_ERROR; + } - ctx->containers[container_id].ocre_runtime_arguments.stack_size = - ctx->containers[container_id].ocre_container_data.stack_size; - ctx->containers[container_id].ocre_runtime_arguments.heap_size = - ctx->containers[container_id].ocre_container_data.heap_size; -#ifndef CONFIG_OCRE_MEMORY_CHECK_ENABLED - ctx->containers[container_id].ocre_runtime_arguments.stack_size = CONFIG_OCRE_CONTAINER_DEFAULT_STACK_SIZE; - ctx->containers[container_id].ocre_runtime_arguments.heap_size = CONFIG_OCRE_CONTAINER_DEFAULT_HEAP_SIZE; -#endif + container->container_runtime_status = CONTAINER_STATUS_CREATED; + LOG_WRN("Created container:%d", curr_container_ID); + return container->container_runtime_status; +} - int ret = load_binary_to_buffer_fs(ctx, container_id, &ctx->containers[container_id].ocre_container_data); - if (ret < 0) { - LOG_ERR("Failed to load binary to buffer: %d", ret); - return CONTAINER_STATUS_ERROR; - } - LOG_WRN("Loaded binary to buffer for container %d", container_id); - if (ctx->containers[container_id].ocre_container_data.watchdog_interval > 0) { - ocre_healthcheck_init(&ctx->containers[container_id].ocre_container_data.WDT, - ctx->containers[container_id].ocre_container_data.watchdog_interval); - } - ctx->containers[container_id].ocre_runtime_arguments.module = - wasm_runtime_load(ctx->containers[container_id].ocre_runtime_arguments.buffer, - ctx->containers[container_id].ocre_runtime_arguments.size, - ctx->containers[container_id].ocre_runtime_arguments.error_buf, - sizeof(ctx->containers[container_id].ocre_runtime_arguments.error_buf)); - - if (!ctx->containers[container_id].ocre_runtime_arguments.module) { - LOG_ERR("Failed to load WASM module: %s", ctx->containers[container_id].ocre_runtime_arguments.error_buf); - return CONTAINER_STATUS_ERROR; - } - if (ctx->containers[container_id].ocre_runtime_arguments.module_inst) { - LOG_INF("WASM runtime allready instantiated for container:%d", container_id); - } else { - LOG_INF("instantiating WASM runtime for container:%d", container_id); - ctx->containers[container_id].ocre_runtime_arguments.module_inst = - wasm_runtime_instantiate(ctx->containers[container_id].ocre_runtime_arguments.module, - ctx->containers[container_id].ocre_runtime_arguments.stack_size, - ctx->containers[container_id].ocre_runtime_arguments.heap_size, - ctx->containers[container_id].ocre_runtime_arguments.error_buf, - sizeof(ctx->containers[container_id].ocre_runtime_arguments.error_buf)); - if (!ctx->containers[container_id].ocre_runtime_arguments.module_inst) { - LOG_ERR("Failed to instantiate WASM module: %s, for containerID= %d", - ctx->containers[container_id].ocre_runtime_arguments.error_buf, container_id); - return CONTAINER_STATUS_ERROR; - } - } +ocre_container_status_t CS_run_container(ocre_container_t *container) { + uint32_t curr_container_ID = container->container_ID; + ocre_runtime_arguments_t *curr_container_arguments = &container->ocre_runtime_arguments; - ctx->containers[container_id].container_runtime_status = CONTAINER_STATUS_CREATED; - LOG_WRN("Created container:%d", container_id); - return ctx->containers[container_id].container_runtime_status; - } else { - LOG_ERR("Cannot create container again container with ID: %d, already exists", container_id); - return RUNTIME_STATUS_ERROR; + if (container->container_runtime_status == CONTAINER_STATUS_RUNNING) { + LOG_WRN("Container status is already in RUNNING state"); + return CONTAINER_STATUS_RUNNING; } -} -ocre_container_status_t CS_run_container(ocre_cs_ctx *ctx, int *container_id) { - char current_sha256[70 + 1]; +#ifdef CONFIG_OCRE_NETWORKING + #define ADDRESS_POOL_SIZE 1 + const char *addr_pool[ADDRESS_POOL_SIZE] = { + "0.0.0.0/0", + }; + wasm_runtime_set_wasi_addr_pool(curr_container_arguments->module, addr_pool, ADDRESS_POOL_SIZE); +#endif - if (ctx->containers[*container_id].container_runtime_status == CONTAINER_STATUS_STOPPED) { - strcpy(current_sha256, ctx->containers[*container_id].ocre_container_data.sha256); - if (strcmp(current_sha256, ctx->containers[*container_id].ocre_container_data.sha256) != 0) { - LOG_WRN("Running different app in container %d, performing full cleanup", *container_id); - CS_destroy_container(ctx, *container_id, NULL); - CS_create_container(ctx, *container_id); - } + if (container->container_runtime_status != CONTAINER_STATUS_CREATED && + container->container_runtime_status != CONTAINER_STATUS_STOPPED) { + LOG_ERR("Container (ID: %d), is not in a valid state to run", curr_container_ID); + container->container_runtime_status = CONTAINER_STATUS_ERROR; + return CONTAINER_STATUS_ERROR; } - if (ctx->containers[*container_id].container_runtime_status == CONTAINER_STATUS_CREATED || - ctx->containers[*container_id].container_runtime_status == CONTAINER_STATUS_STOPPED) { - ocre_timer_set_module_inst(ctx->containers[*container_id].ocre_runtime_arguments.module_inst); -#ifdef CONFIG_OCRE_GPIO - ocre_gpio_set_module_inst(ctx->containers[*container_id].ocre_runtime_arguments.module_inst); -#endif - ctx->containers[*container_id].ocre_runtime_arguments.exec_env = - wasm_runtime_create_exec_env(ctx->containers[*container_id].ocre_runtime_arguments.module_inst, - ctx->containers[*container_id].ocre_runtime_arguments.stack_size); - if (NULL == ctx->containers[*container_id].ocre_runtime_arguments.exec_env) { - LOG_ERR("ERROR creating executive environment for container %d", *container_id); - ctx->containers[*container_id].container_runtime_status = CONTAINER_STATUS_ERROR; - return CONTAINER_STATUS_ERROR; - } - if (container_thread_active[*container_id]) { - LOG_ERR("Container %d thread is already active", *container_id); - return CONTAINER_STATUS_ERROR; - } - container_thread_ids[*container_id] = k_thread_create( - &container_threads[*container_id], container_thread_stacks[*container_id], CONTAINER_THREAD_STACK_SIZE, - container_thread_entry, container_id, ctx, NULL, K_PRIO_PREEMPT(5), 0, K_NO_WAIT); - if (container_thread_ids[*container_id] == NULL) { - LOG_ERR("Failed to create thread for container %d", *container_id); - ctx->containers[*container_id].container_runtime_status = CONTAINER_STATUS_ERROR; + if (curr_container_arguments->module_inst) { + LOG_INF("WASM runtime already instantiated for container:%d", curr_container_ID); + } else { + LOG_INF("Instantiating WASM runtime for container:%d", curr_container_ID); + curr_container_arguments->module_inst = + wasm_runtime_instantiate(curr_container_arguments->module, curr_container_arguments->stack_size, + curr_container_arguments->heap_size, curr_container_arguments->error_buf, + sizeof(curr_container_arguments->error_buf)); + if (!curr_container_arguments->module_inst) { + LOG_ERR("Failed to instantiate WASM module: %s, for containerID= %d", curr_container_arguments->error_buf, + curr_container_ID); + wasm_runtime_unload(curr_container_arguments->module); + free(curr_container_arguments->buffer); return CONTAINER_STATUS_ERROR; } - container_thread_active[*container_id] = true; - ctx->containers[*container_id].container_runtime_status = CONTAINER_STATUS_RUNNING; - char thread_name[16]; - snprintf(thread_name, sizeof(thread_name), "container_%d", *container_id); - k_thread_name_set(container_thread_ids[*container_id], thread_name); - LOG_WRN("Running container:%d in dedicated thread", *container_id); - return CONTAINER_STATUS_RUNNING; - } else { - LOG_ERR("Container (ID: %d), does not exist to run it", *container_id); - ctx->containers[*container_id].container_runtime_status = CONTAINER_STATUS_ERROR; - return CONTAINER_STATUS_ERROR; +#if defined(CONFIG_OCRE_TIMER) || defined(CONFIG_OCRE_GPIO) || defined(CONFIG_OCRE_SENSORS) + ocre_register_module(curr_container_arguments->module_inst); +#endif } -} -ocre_container_status_t CS_get_container_status(ocre_cs_ctx *ctx, int container_id) { - if (container_id < 0 || container_id >= MAX_CONTAINERS) { - LOG_ERR("Invalid container ID: %d", container_id); + core_mutex_lock(&container_mutex); + int thread_idx = get_available_thread(); + if (thread_idx == -1) { + LOG_ERR("No available threads for container %d", curr_container_ID); + container->container_runtime_status = CONTAINER_STATUS_ERROR; + core_mutex_unlock(&container_mutex); return CONTAINER_STATUS_ERROR; } - return ctx->containers[container_id].container_runtime_status; -} - -ocre_container_status_t CS_stop_container(ocre_cs_ctx *ctx, int container_id, ocre_container_runtime_cb callback) { - if (container_id < 0 || container_id >= MAX_CONTAINERS) { - LOG_ERR("Invalid container ID: %d", container_id); + // Allocate thread arguments dynamically + struct container_thread_args *args = core_malloc(sizeof(struct container_thread_args)); + if (!args) { + LOG_ERR("Failed to allocate thread args for container %d", curr_container_ID); + container->container_runtime_status = CONTAINER_STATUS_ERROR; + core_mutex_unlock(&container_mutex); return CONTAINER_STATUS_ERROR; } + args->container = container; - if (ctx->containers[container_id].container_runtime_status == CONTAINER_STATUS_RUNNING) { - if (container_thread_active[container_id] && container_thread_ids[container_id] != NULL) { - LOG_INF("Aborting thread for container %d", container_id); - k_thread_abort(container_thread_ids[container_id]); - container_thread_active[container_id] = false; - container_thread_ids[container_id] = NULL; - } + // Create and start a new thread for the container + char thread_name[16]; + snprintf(thread_name, sizeof(thread_name), "container_%d", curr_container_ID); + container_threads[thread_idx].user_options = curr_container_ID; + int ret = core_thread_create(&container_threads[thread_idx], container_thread_entry, args, thread_name, + CONTAINER_THREAD_STACK_SIZE, 5); - if (ctx->containers[container_id].ocre_runtime_arguments.exec_env) { - wasm_runtime_destroy_exec_env(ctx->containers[container_id].ocre_runtime_arguments.exec_env); - ctx->containers[container_id].ocre_runtime_arguments.exec_env = NULL; - } - - wasm_runtime_deinstantiate(ctx->containers[container_id].ocre_runtime_arguments.module_inst); - ctx->containers[container_id].container_runtime_status = CONTAINER_STATUS_STOPPED; - LOG_WRN("Stopped container:%d", container_id); + if (ret != 0) { + LOG_ERR("Failed to create thread for container %d", curr_container_ID); + container->container_runtime_status = CONTAINER_STATUS_ERROR; - if (callback) { - callback(); - } + core_free(args); // Free the dynamically allocated args - return CONTAINER_STATUS_STOPPED; - } else { - LOG_ERR("Container %d, is not running to stop it", container_id); - ctx->containers[container_id].container_runtime_status = CONTAINER_STATUS_ERROR; + core_mutex_unlock(&container_mutex); return CONTAINER_STATUS_ERROR; } + + container_thread_active[thread_idx] = true; + core_mutex_unlock(&container_mutex); + + container->container_runtime_status = CONTAINER_STATUS_RUNNING; + LOG_WRN("Running container:%d in dedicated thread", curr_container_ID); + return CONTAINER_STATUS_RUNNING; } -ocre_container_status_t CS_destroy_container(ocre_cs_ctx *ctx, int container_id, ocre_container_runtime_cb callback) { - if (container_id < 0 || container_id >= MAX_CONTAINERS) { +ocre_container_status_t CS_get_container_status(ocre_cs_ctx *ctx, int container_id) { + if (container_id < 0 || container_id >= CONFIG_MAX_CONTAINERS) { LOG_ERR("Invalid container ID: %d", container_id); return CONTAINER_STATUS_ERROR; } - ocre_timer_cleanup_container(ctx->containers[container_id].ocre_runtime_arguments.module_inst); + return ctx->containers[container_id].container_runtime_status; +} - if (ctx->containers[container_id].container_runtime_status == CONTAINER_STATUS_UNKNOWN) { - LOG_ERR("Cannot destroy container %d: It was never created.", container_id); - return CONTAINER_STATUS_ERROR; +ocre_container_status_t CS_stop_container(ocre_container_t *container, ocre_container_runtime_cb callback) { + uint32_t curr_container_ID = container->container_ID; + ocre_runtime_arguments_t *curr_container_arguments = &container->ocre_runtime_arguments; + if (container->container_runtime_status == CONTAINER_STATUS_STOPPED) { + LOG_WRN("Container status is already in STOP state"); + return CONTAINER_STATUS_STOPPED; } + core_mutex_lock(&container->lock); + { + LOG_INF("Stopping container %d from state %d", curr_container_ID, container->container_runtime_status); - if (ctx->containers[container_id].container_runtime_status != CONTAINER_STATUS_STOPPED) { - LOG_WRN("Container %d is not stopped yet. Stopping it first.", container_id); - CS_stop_container(ctx, container_id, NULL); - } + for (int i = 0; i < CONTAINER_THREAD_POOL_SIZE; i++) { + if (container_thread_active[i] && container_threads[i].user_options == curr_container_ID) { + core_thread_destroy(&container_threads[i]); + container_thread_active[i] = false; + } + } +#ifdef CONFIG_OCRE_TIMER + ocre_timer_cleanup_container(curr_container_arguments->module_inst); +#endif +#ifdef CONFIG_OCRE_GPIO + ocre_gpio_cleanup_container(curr_container_arguments->module_inst); +#endif +#if defined(CONFIG_OCRE_TIMER) || defined(CONFIG_OCRE_GPIO) || defined(CONFIG_OCRE_SENSORS) + ocre_unregister_module(curr_container_arguments->module_inst); +#endif + if (curr_container_arguments->module_inst) { + wasm_runtime_deinstantiate(curr_container_arguments->module_inst); + curr_container_arguments->module_inst = NULL; + } - if (container_thread_active[container_id] && container_thread_ids[container_id] != NULL) { - k_thread_abort(container_thread_ids[container_id]); - container_thread_active[container_id] = false; - container_thread_ids[container_id] = NULL; + container->container_runtime_status = CONTAINER_STATUS_STOPPED; } + core_mutex_unlock(&container->lock); - if (ctx->containers[container_id].ocre_runtime_arguments.exec_env) { - wasm_runtime_destroy_exec_env(ctx->containers[container_id].ocre_runtime_arguments.exec_env); - ctx->containers[container_id].ocre_runtime_arguments.exec_env = NULL; - LOG_INF("Destroyed exec environment for container %d", container_id); + if (callback) { + callback(); } + return CONTAINER_STATUS_STOPPED; +} - if (ctx->containers[container_id].ocre_runtime_arguments.module_inst) { - wasm_runtime_deinstantiate(ctx->containers[container_id].ocre_runtime_arguments.module_inst); - ctx->containers[container_id].ocre_runtime_arguments.module_inst = NULL; - LOG_INF("Deinstantiated WASM module for container %d", container_id); - } +ocre_container_status_t CS_destroy_container(ocre_container_t *container, ocre_container_runtime_cb callback) { + core_mutex_lock(&container->lock); + { + LOG_INF("Destroying container %d", container->container_ID); - if (ctx->containers[container_id].ocre_runtime_arguments.module) { - wasm_runtime_unload(ctx->containers[container_id].ocre_runtime_arguments.module); - ctx->containers[container_id].ocre_runtime_arguments.module = NULL; - } - if (ctx->containers[container_id].ocre_runtime_arguments.buffer) { - free(ctx->containers[container_id].ocre_runtime_arguments.buffer); - LOG_INF("Freed buffer for container %d", container_id); - ctx->containers[container_id].ocre_runtime_arguments.buffer = NULL; - } - memset(ctx->containers[container_id].ocre_container_data.name, 0, - sizeof(ctx->containers[container_id].ocre_container_data.name)); - memset(ctx->containers[container_id].ocre_container_data.sha256, 0, - sizeof(ctx->containers[container_id].ocre_container_data.sha256)); - ctx->containers[container_id].ocre_container_data.timers = 0; - ctx->containers[container_id].ocre_container_data.watchdog_interval = 0; + if (container->container_runtime_status != CONTAINER_STATUS_STOPPED) { + CS_stop_container(container, NULL); + } - wasm_runtime_unload(ctx->containers[container_id].ocre_runtime_arguments.module); - free(ctx->containers[container_id].ocre_runtime_arguments.buffer); - ctx->containers[container_id].ocre_runtime_arguments.buffer = NULL; + if (container->ocre_runtime_arguments.module) { + wasm_runtime_unload(container->ocre_runtime_arguments.module); + container->ocre_runtime_arguments.module = NULL; + } + + if (container->ocre_runtime_arguments.buffer) { + free(container->ocre_runtime_arguments.buffer); + container->ocre_runtime_arguments.buffer = NULL; + } - LOG_WRN("Destroyed container: %d", container_id); - ctx->containers[container_id].container_runtime_status = CONTAINER_STATUS_DESTROYED; + memset(&container->ocre_container_data, 0, sizeof(ocre_container_data_t)); + container->container_runtime_status = CONTAINER_STATUS_DESTROYED; + } + core_mutex_unlock(&container->lock); if (callback) { callback(); } - return CONTAINER_STATUS_DESTROYED; } -ocre_container_status_t CS_restart_container(ocre_cs_ctx *ctx, int container_id, ocre_container_runtime_cb callback) { - if (container_id < 0 || container_id >= MAX_CONTAINERS) { - LOG_ERR("Invalid container ID: %d", container_id); - return CONTAINER_STATUS_ERROR; - } - - ocre_container_status_t status = CS_stop_container(ctx, container_id, NULL); +ocre_container_status_t CS_restart_container(ocre_container_t *container, ocre_container_runtime_cb callback) { + ocre_container_status_t status = CS_stop_container(container, NULL); if (status != CONTAINER_STATUS_STOPPED) { - LOG_ERR("Failed to stop container: %d", container_id); + LOG_ERR("Failed to stop container: %d", container->container_ID); return CONTAINER_STATUS_ERROR; } - // Start container with a new thread - status = CS_run_container(ctx, container_id); + status = CS_run_container(container); if (status != CONTAINER_STATUS_RUNNING) { - LOG_ERR("Failed to start container: %d", container_id); + LOG_ERR("Failed to start container: %d", container->container_ID); return CONTAINER_STATUS_ERROR; } - - if (ctx->containers[container_id].ocre_container_data.watchdog_interval > 0) { - ocre_healthcheck_reinit(&ctx->containers[container_id].ocre_container_data.WDT); - } - if (callback) { callback(); } return CONTAINER_STATUS_RUNNING; -} \ No newline at end of file +} diff --git a/src/ocre/components/container_supervisor/cs_sm_impl.h b/src/ocre/components/container_supervisor/cs_sm_impl.h index 79044146..f3cae177 100644 --- a/src/ocre/components/container_supervisor/cs_sm_impl.h +++ b/src/ocre/components/container_supervisor/cs_sm_impl.h @@ -8,11 +8,8 @@ #ifndef OCRE_CS_SM_IMPL_H #define OCRE_CS_SM_IMPL_H -#include -#include -#include -#include -#include "../../api/ocre_api.h" +#include "ocre_core_external.h" +#include "api/ocre_api.h" #include // ----------------------------------------------------------------------------- @@ -24,11 +21,11 @@ /** * @brief Initialize the container runtime environment. * - * This function sets up the runtime environment for OCRE containers. + * This function sets up the runtime environment for OCRE containers, preparing necessary resources and state. * - * @param ctx Pointer to the container runtime context. + * @param ctx Pointer to the container runtime context. * @param args Pointer to initialization arguments. - * @return ocre_container_runtime_status_t Status of the initialization. + * @return ocre_container_runtime_status_t Status of the initialization (e.g., success or error code). */ ocre_container_runtime_status_t CS_runtime_init(ocre_cs_ctx *ctx, ocre_container_init_arguments_t *args); @@ -52,7 +49,7 @@ ocre_container_runtime_status_t CS_runtime_destroy(void); * @param container_id The ID of the container to be created. * @return ocre_container_status_t Status of the container creation. */ -ocre_container_status_t CS_create_container(ocre_cs_ctx *ctx, int container_id); +ocre_container_status_t CS_create_container(ocre_container_t *container); /** * @brief Run a container. @@ -63,7 +60,7 @@ ocre_container_status_t CS_create_container(ocre_cs_ctx *ctx, int container_id); * @param container_id The ID of the container to be run. * @return ocre_container_status_t Status of the container execution. */ -ocre_container_status_t CS_run_container(ocre_cs_ctx *ctx, int *container_id); +ocre_container_status_t CS_run_container(ocre_container_t *ctx); /** * @brief Get the status of a container. @@ -81,36 +78,33 @@ ocre_container_status_t CS_get_container_status(ocre_cs_ctx *ctx, int container_ * * This function stops the execution of the container and invokes a callback function when completed. * - * @param ctx Pointer to the container runtime context. * @param container_id The ID of the container to be stopped. * @param callback Callback function to be invoked after stopping the container. * @return ocre_container_status_t Status of the stop operation. */ -ocre_container_status_t CS_stop_container(ocre_cs_ctx *ctx, int container_id, ocre_container_runtime_cb callback); +ocre_container_status_t CS_stop_container(ocre_container_t *container, ocre_container_runtime_cb callback); /** * @brief Destroy a container. * * This function removes and destroys the specified container from the runtime. * - * @param ctx Pointer to the container runtime context. * @param container_id The ID of the container to be destroyed. * @param callback Callback function to be invoked after destroying the container. * @return ocre_container_status_t Status of the container destruction. */ -ocre_container_status_t CS_destroy_container(ocre_cs_ctx *ctx, int container_id, ocre_container_runtime_cb callback); +ocre_container_status_t CS_destroy_container(ocre_container_t *container, ocre_container_runtime_cb callback); /** * @brief Restart a container. * * This function restarts a container by stopping and then running it again. * - * @param ctx Pointer to the container runtime context. * @param container_id The ID of the container to be restarted. * @param callback Callback function to be invoked after restarting the container. * @return ocre_container_status_t Status of the container restart. */ -ocre_container_status_t CS_restart_container(ocre_cs_ctx *ctx, int container_id, ocre_container_runtime_cb callback); +ocre_container_status_t CS_restart_container(ocre_container_t *container, ocre_container_runtime_cb callback); /** * @brief context initialization diff --git a/src/ocre/components/container_supervisor/message_types.h b/src/ocre/components/container_supervisor/message_types.h index fa5b4a9f..8a7f1e75 100644 --- a/src/ocre/components/container_supervisor/message_types.h +++ b/src/ocre/components/container_supervisor/message_types.h @@ -8,24 +8,26 @@ #ifndef OCRE_CS_MESSAGING_TYPES_H #define OCRE_CS_MESSAGING_TYPES_H +#include "ocre_core_external.h" + struct install { - char name[16]; // timer -int ocre_healthcheck_expiry(struct k_timer *timer, ocre_healthcheck *WDT) { - if (WDT == NULL) { - return -1; // Error: Null pointer - } - if (WDT->enabled && (WDT->is_alive_cnt <= WDT->is_alive_cnt_last)) { - // TODOA:-- containers[current_container_id].container_runtime_status = CONTAINER_STATUS_UNRESPONSIVE; - WDT->is_alive_cnt = 0; - WDT->is_alive_cnt_last = WDT->is_alive_cnt; - return 1; // Health check failed, container is unresponsive - } - - WDT->is_alive_cnt_last = WDT->is_alive_cnt; - - return 0; // Health check passed -} - -int ocre_healthcheck_init(ocre_healthcheck *WDT, int timeout) { - if (WDT == NULL) { - return -1; // Error: Null pointer - } - - WDT->timeout = timeout; - WDT->enabled = 1; - WDT->is_alive_cnt = 0; - WDT->is_alive_cnt_last = WDT->is_alive_cnt; - k_timer_init(&WDT->timer, (k_timer_expiry_t)ocre_healthcheck_expiry, NULL); - - return 0; -} - -int ocre_healthcheck_reinit(ocre_healthcheck *WDT) { - if (WDT == NULL) { - return -1; // Error: Null pointer - } - - WDT->enabled = 1; - WDT->is_alive_cnt = 0; - WDT->is_alive_cnt_last = WDT->is_alive_cnt; - k_timer_init(&WDT->timer, (k_timer_expiry_t)ocre_healthcheck_expiry, NULL); - - return 0; -} -int ocre_healthcheck_restart(ocre_healthcheck *WDT) { - WDT->is_alive_cnt = 0; - WDT->is_alive_cnt_last = WDT->is_alive_cnt; - k_timer_start(&WDT->timer, K_MSEC(WDT->timeout), K_NO_WAIT); - return 0; -} - -int ocre_healthcheck_start(ocre_healthcheck *WDT) { - k_timer_start(&WDT->timer, K_MSEC(WDT->timeout), K_NO_WAIT); - return 0; -} - -int ocre_healthcheck_stop(ocre_healthcheck *WDT) { - k_timer_stop(&WDT->timer); - WDT->enabled = 0; - return 0; -} - -int ocre_get_healthcheck_remaining(ocre_healthcheck *WDT) { - k_timer_remaining_get(&WDT->timer); - return 0; -} - -// health check of container if it does not respond restart the container -int on_ocre_healthcheck(ocre_healthcheck *WDT) { - // TODOA:-- k_timer_start(&WDT->timer, K_MSEC(WDT->timeout), K_NO_WAIT); - // because the start of already runing timer will get it back to initial values - // actually a reset or trigger for watchdog - // and the expiry function is not called - WDT->is_alive_cnt += 1; - - return 1; -} diff --git a/src/ocre/container_healthcheck/ocre_container_healthcheck.h b/src/ocre/container_healthcheck/ocre_container_healthcheck.h deleted file mode 100644 index 66c8d6a7..00000000 --- a/src/ocre/container_healthcheck/ocre_container_healthcheck.h +++ /dev/null @@ -1,89 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef OCRE_WATCHDOG_H -#define OCRE_WATCHDOG_H - -#include - -/** - * Structure to hold the OCRE health check parameters. - */ -typedef struct ocre_healthcheck { - uint32_t interval; - uint32_t timeout; - bool enabled; - uint32_t is_alive_cnt; - uint32_t is_alive_cnt_last; - struct k_timer timer; -} ocre_healthcheck; - -/** - * Initializes the OCRE health check structure. - * - * @param WDT Pointer to the OCRE health check structure. - * @param timeout Timeout value for the watchdog timer in milliseconds. - * - * @return 0 on success, -1 on error (e.g., null pointer). - */ -int ocre_healthcheck_init(ocre_healthcheck *WDT, int timeout); - -/** - * Reset/Trigger the OCRE health check structure. - * - * @param WDT Pointer to the OCRE health check structure. - * - * @return 0 on success, -1 on error (e.g., null pointer). - */ -int ocre_healthcheck_reinit(ocre_healthcheck *WDT); - -/** - * Starts the OCRE health check timer. - * - * @param WDT Pointer to the OCRE health check structure. - * - * @return 0 on success, -1 on error (e.g., null pointer). - */ -int ocre_healthcheck_start(ocre_healthcheck *WDT); - -/** - * Stops the OCRE health check timer. - * - * @param WDT Pointer to the OCRE health check structure. - * - * @return 0 on success, -1 on error (e.g., null pointer). - */ -int ocre_healthcheck_stop(ocre_healthcheck *WDT); - -/** - * Gets the remaining time for the OCRE health check timer. - * - * @param WDT Pointer to the OCRE health check structure. - * - * @return Remaining time in milliseconds, or -1 on error (e.g., null pointer). - */ -int ocre_get_healthcheck_remaining(ocre_healthcheck *WDT); - -/** - * Handles the health check, called cyclically to monitor the container. - * - * @param WDT Pointer to the OCRE health check structure. - * - * @return 1 if health check is alive, 0 otherwise. - */ -int on_ocre_healthcheck(ocre_healthcheck *WDT); - -/** - * Expiry function called by the timer, checks if the container is responsive. - * - * @param WDT Pointer to the OCRE health check structure. - * - * @return 1 if health check failed, 0 otherwise. - */ -int ocre_healthcheck_expiry(struct k_timer *timer, ocre_healthcheck *WDT); - -#endif \ No newline at end of file diff --git a/src/ocre/container_messaging/messaging.c b/src/ocre/container_messaging/messaging.c index cf6d8dc9..58438cd0 100644 --- a/src/ocre/container_messaging/messaging.c +++ b/src/ocre/container_messaging/messaging.c @@ -1,4 +1,11 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + #include #include #include @@ -7,8 +14,10 @@ LOG_MODULE_DECLARE(ocre_container_messaging, OCRE_LOG_LEVEL); +#define MESSAGING_MAX_MODULE_ENVS CONFIG_MAX_CONTAINERS + #define QUEUE_SIZE 100 -#define STACK_SIZE 1024 +#define STACK_SIZE 4096 #define PRIORITY 5 #define WASM_STACK_SIZE (8 * 1024) @@ -20,91 +29,79 @@ typedef struct { wasm_function_inst_t handler; wasm_module_inst_t module_inst; wasm_exec_env_t exec_env; -} ocre_subscription_t; + bool is_active; +} messaging_subscription_t; -static ocre_subscription_t subscriptions[CONFIG_MESSAGING_MAX_SUBSCRIPTIONS]; -static int subscription_count = 0; -static bool msg_system_is_init = false; +typedef struct { + messaging_subscription_t info[CONFIG_MESSAGING_MAX_SUBSCRIPTIONS]; + uint16_t subscriptions_number; +} messaging_subscription_list; -static uint32_t allocate_wasm_memory(wasm_module_inst_t module_inst, const void *src, size_t size) { - void *native_addr = NULL; - uint64_t wasm_ptr = wasm_runtime_module_malloc(module_inst, size, &native_addr); - if (!wasm_ptr || !native_addr) { - LOG_ERR("Failed to allocate memory in WASM"); - return 0; - } - if (src) { - memcpy(native_addr, src, size); - } - return (uint32_t)wasm_ptr; -} +// Structure to hold all module environments +typedef struct { + wasm_module_inst_t module_inst; + wasm_exec_env_t exec_env; + bool is_active; +} messaging_module_env_t; -static void free_wasm_message(wasm_module_inst_t module_inst, uint32_t *ptr_array, uint16_t count) { - for (uint16_t i = 0; i < count; i++) { - if (ptr_array[i]) { - wasm_runtime_module_free(module_inst, ptr_array[i]); - } - } -} +typedef struct { + messaging_module_env_t module_info[MESSAGING_MAX_MODULE_ENVS]; + uint16_t envs_number; +} messaging_module_env_list; + +messaging_subscription_list subscription_list = {0}; +messaging_module_env_list module_env_list = {0}; + +static bool msg_system_is_init = false; void subscriber_thread(void *arg1, void *arg2, void *arg3) { ocre_msg_t msg; + while (1) { if (k_msgq_get(&ocre_msg_queue, &msg, K_FOREVER) == 0) { - for (int i = 0; i < subscription_count; i++) { - if (strcmp(subscriptions[i].topic, msg.topic) == 0) { + for (int i = 0; i < CONFIG_MESSAGING_MAX_SUBSCRIPTIONS; i++) { + if (!subscription_list.info[i].is_active) { + continue; + } + if (strcmp(subscription_list.info[i].topic, msg.topic) == 0) { - wasm_module_inst_t module_inst = subscriptions[i].module_inst; + wasm_module_inst_t module_inst = subscription_list.info[i].module_inst; if (!module_inst) { LOG_ERR("Invalid module instance"); continue; } - uint32_t allocated_pointers[4] = {0}; - // Allocate memory for ocre_msg_t - allocated_pointers[0] = allocate_wasm_memory(module_inst, NULL, sizeof(ocre_msg_t)); - if (allocated_pointers[0] == 0) { - LOG_ERR("Failed to allocate memory for message structure in WASM"); - continue; - } - // Allocate memory for topic - allocated_pointers[1] = allocate_wasm_memory(module_inst, msg.topic, strlen(msg.topic) + 1); - if (allocated_pointers[1] == 0) { + uint32_t topic_offset = + (uint32_t)wasm_runtime_module_dup_data(module_inst, msg.topic, strlen(msg.topic) + 1); + if (topic_offset == 0) { LOG_ERR("Failed to allocate memory for topic in WASM"); - free_wasm_message(module_inst, allocated_pointers, 1); continue; } // Allocate memory for content_type - allocated_pointers[2] = - allocate_wasm_memory(module_inst, msg.content_type, strlen(msg.content_type) + 1); - if (allocated_pointers[2] == 0) { + uint32_t content_offset = (uint32_t)wasm_runtime_module_dup_data(module_inst, msg.content_type, + strlen(msg.content_type) + 1); + if (content_offset == 0) { LOG_ERR("Failed to allocate memory for content_type in WASM"); - free_wasm_message(module_inst, allocated_pointers, 2); + wasm_runtime_module_free(module_inst, topic_offset); continue; } // Allocate memory for payload - allocated_pointers[3] = allocate_wasm_memory(module_inst, msg.payload, msg.payload_len); - if (allocated_pointers[3] == 0) { + uint32_t payload_offset = + (uint32_t)wasm_runtime_module_dup_data(module_inst, msg.payload, msg.payload_len); + if (payload_offset == 0) { LOG_ERR("Failed to allocate memory for payload in WASM"); - free_wasm_message(module_inst, allocated_pointers, 3); + wasm_runtime_module_free(module_inst, topic_offset); + wasm_runtime_module_free(module_inst, content_offset); continue; } - // Populate the WASM message structure - ocre_msg_t *wasm_msg = - (ocre_msg_t *)wasm_runtime_addr_app_to_native(module_inst, allocated_pointers[0]); - wasm_msg->mid = msg.mid; - wasm_msg->topic = (char *)allocated_pointers[1]; - wasm_msg->content_type = (char *)allocated_pointers[2]; - wasm_msg->payload = (void *)allocated_pointers[3]; - wasm_msg->payload_len = msg.payload_len; - // Call the WASM function - uint32_t args[1] = {allocated_pointers[0]}; - if (!wasm_runtime_call_wasm(subscriptions[i].exec_env, subscriptions[i].handler, 1, args)) { + uint32_t args[5] = {msg.mid, topic_offset, content_offset, payload_offset, msg.payload_len}; + if (!wasm_runtime_call_wasm(subscription_list.info[i].exec_env, subscription_list.info[i].handler, + 5, args)) { const char *error = wasm_runtime_get_exception(module_inst); LOG_ERR("Failed to call WASM function: %s", error ? error : "Unknown error"); } else { @@ -112,7 +109,9 @@ void subscriber_thread(void *arg1, void *arg2, void *arg3) { } // Free allocated WASM memory - free_wasm_message(module_inst, allocated_pointers, 4); + wasm_runtime_module_free(module_inst, topic_offset); + wasm_runtime_module_free(module_inst, content_offset); + wasm_runtime_module_free(module_inst, payload_offset); } } } @@ -133,12 +132,16 @@ void ocre_msg_system_init() { k_tid_t tid = k_thread_create(&subscriber_thread_data, subscriber_stack_area, K_THREAD_STACK_SIZEOF(subscriber_stack_area), subscriber_thread, NULL, NULL, NULL, PRIORITY, 0, K_NO_WAIT); - + wasm_runtime_init_thread_env(); if (!tid) { LOG_ERR("Failed to create container messaging thread"); return; } k_thread_name_set(tid, "container_messaging"); + + memset(&module_env_list, 0, sizeof(messaging_module_env_list)); + memset(&subscription_list, 0, sizeof(messaging_subscription_list)); + msg_system_is_init = true; } @@ -166,12 +169,12 @@ int ocre_publish_message(wasm_exec_env_t exec_env, char *topic, char *content_ty return -1; } - static uint64_t message_id = 0; + static uint32_t message_id = 0; ocre_msg_t msg = {.mid = message_id, .topic = topic, .content_type = content_type, .payload = payload, - .payload_len = payload_len}; + .payload_len = (uint32_t)payload_len}; message_id++; @@ -192,37 +195,129 @@ int ocre_subscribe_message(wasm_exec_env_t exec_env, char *topic, char *handler_ return -1; } - if (subscription_count < CONFIG_MESSAGING_MAX_SUBSCRIPTIONS) { + if (subscription_list.subscriptions_number >= CONFIG_MESSAGING_MAX_SUBSCRIPTIONS) { + LOG_ERR("Maximum subscriptions reached"); + return -1; + } + + if (!topic || (topic[0] == '\0')) { + LOG_ERR("topic is NULL or empty, please check function parameters!"); + return -1; + } + + if (!handler_name || (handler_name[0] == '\0')) { + LOG_ERR("handler_name is NULL or empty, please check function parameters!"); + return -1; + } + + wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); + if (!module_inst) { + LOG_ERR("Failed to retrieve module instance from execution environment"); + return -1; + } + + wasm_exec_env_t assigned_exec_env = NULL; + for (int i = 0; i < MESSAGING_MAX_MODULE_ENVS; i++) { + if (module_env_list.module_info[i].module_inst == module_inst) { + assigned_exec_env = module_env_list.module_info[i].exec_env; + break; + } + } + + if (!assigned_exec_env) { + LOG_ERR("Execution environment not found for module instance"); + return -1; + } + + wasm_function_inst_t handler_function = wasm_runtime_lookup_function(module_inst, handler_name); + if (!handler_function) { + LOG_ERR("Failed to find %s in WASM module", handler_name); + return -1; + } - if (!topic || (topic[0] == '\0')) { - LOG_ERR("topic is NULL or empty, please check function parameters!"); - return -1; + // Find a free slot for subscription and set a new subscription + for (int i = 0; i < CONFIG_MESSAGING_MAX_SUBSCRIPTIONS; i++) { + if (!subscription_list.info[i].is_active) { + subscription_list.info[i].topic = topic; + subscription_list.info[i].handler = handler_function; + subscription_list.info[i].exec_env = assigned_exec_env; + subscription_list.info[i].module_inst = module_inst; + subscription_list.info[i].is_active = true; + + subscription_list.subscriptions_number++; + break; } + } + + LOG_INF("WASM messaging callback function set successfully"); + return 0; +} + +void ocre_messaging_register_module(wasm_module_inst_t module_inst) { + if (module_env_list.envs_number >= MESSAGING_MAX_MODULE_ENVS) { + LOG_ERR("Maximum module instances reached"); + return; + } - if (!handler_name || (handler_name[0] == '\0')) { - LOG_ERR("handler_name is NULL or empty, please check function parameters!"); - return -1; + if (!module_inst) { + LOG_ERR("Module instance is NULL"); + return; + } + + // Check if module already exists + for (int i = 0; i < MESSAGING_MAX_MODULE_ENVS; i++) { + if (module_env_list.module_info[i].module_inst == module_inst) { + LOG_WRN("Module instance already set"); + return; } + } - wasm_module_inst_t current_module_inst = wasm_runtime_get_module_inst(exec_env); - wasm_function_inst_t handler_function = wasm_runtime_lookup_function(current_module_inst, handler_name); - if (!handler_function) { - LOG_ERR("Failed to find %s in WASM module", handler_name); - return -1; + // Create new exec env + wasm_exec_env_t exec_env = wasm_runtime_create_exec_env(module_inst, WASM_STACK_SIZE); + if (!exec_env) { + LOG_ERR("Failed to create execution environment"); + return; + } + + // Save module_inst and exec_env + for (int i = 0; i < MESSAGING_MAX_MODULE_ENVS; i++) { + if (!module_env_list.module_info[i].is_active) { + module_env_list.module_info[i].module_inst = module_inst; + module_env_list.module_info[i].exec_env = exec_env; + module_env_list.module_info[i].is_active = true; + module_env_list.envs_number++; + break; } + } - subscriptions[subscription_count].topic = topic; - subscriptions[subscription_count].handler = handler_function; - subscriptions[subscription_count].exec_env = exec_env; - subscriptions[subscription_count].module_inst = current_module_inst; + LOG_INF("Module instance registered successfully"); +} - LOG_INF("WASM messaging callback function set successfully"); +void ocre_messaging_cleanup_container(wasm_module_inst_t module_inst) { + if (!module_inst) { + LOG_ERR("Module instance is NULL"); + return; + } - subscription_count++; - } else { - LOG_ERR("Maximum subscriptions reached, could not subscribe to topic"); - return -1; + /* Stop and clear subscribers related to this module_inst */ + for (int i = 0; i < CONFIG_MESSAGING_MAX_SUBSCRIPTIONS; i++) { + if (subscription_list.info[i].module_inst == module_inst && subscription_list.info[i].is_active) { + LOG_DBG("Cleaning up subscription: %s", subscription_list.info[i].topic); + memset(&subscription_list.info[i], 0, sizeof(messaging_subscription_t)); + subscription_list.subscriptions_number--; + } } + /* Destroy and clear module environments related to this module_inst */ + for (int i = 0; i < MESSAGING_MAX_MODULE_ENVS; i++) { + if (module_env_list.module_info[i].module_inst == module_inst && module_env_list.module_info[i].is_active) { + if (module_env_list.module_info[i].exec_env) { + wasm_runtime_destroy_exec_env(module_env_list.module_info[i].exec_env); + } + memset(&module_env_list.module_info[i], 0, sizeof(messaging_module_env_t)); + module_env_list.envs_number--; + } + } + wasm_runtime_destroy_thread_env(); - return 0; + LOG_INF("Cleaned up messaging for module %p", (void *)module_inst); } diff --git a/src/ocre/container_messaging/messaging.h b/src/ocre/container_messaging/messaging.h index 50dce18f..2ea9971d 100644 --- a/src/ocre/container_messaging/messaging.h +++ b/src/ocre/container_messaging/messaging.h @@ -1,4 +1,12 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef CONTAINER_MESSAGING_H +#define CONTAINER_MESSAGING_H #include "wasm_export.h" /** @@ -9,7 +17,7 @@ */ typedef struct ocre_msg { // message id - increments on each message - uint64_t mid; + uint32_t mid; // url of the request char *topic; @@ -21,7 +29,7 @@ typedef struct ocre_msg { void *payload; // length in bytes of the payload - int payload_len; + uint32_t payload_len; } ocre_msg_t; /** @@ -47,3 +55,17 @@ int ocre_publish_message(wasm_exec_env_t exec_env, char *topic, char *content_ty * @param handler_name name of callback function that will be called when a message is received on this topic */ int ocre_subscribe_message(wasm_exec_env_t exec_env, char *topic, char *handler_name); + +/** + * @brief Register a new WASM module instance + * @param module_inst WASM module instance to register + */ +void ocre_messaging_register_module(wasm_module_inst_t module_inst); + +/** + * @brief Cleans up all subscriptions associated with a WASM module instance + * @param module_inst WASM module instance to clean up + */ +void ocre_messaging_cleanup_container(wasm_module_inst_t module_inst); + +#endif /* CONTAINER_MESSAGING_H */ diff --git a/src/ocre/container_messaging_posix/messaging.c b/src/ocre/container_messaging_posix/messaging.c new file mode 100644 index 00000000..c3d0ba24 --- /dev/null +++ b/src/ocre/container_messaging_posix/messaging.c @@ -0,0 +1,280 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "messaging.h" +#include "ocre/utils/utils.h" + +#define MAX_MSG_SIZE 4096 + +#include +#include +#include +#include +#include +#include +#include +#include + +#define QUEUE_SIZE 10 +#define STACK_SIZE 1024 +#define PRIORITY 5 +#define WASM_STACK_SIZE (8 * 1024) + +static mqd_t ocre_msg_queue; +char mq_name[32]; +static pthread_t subscriber_thread_data; +static struct mq_attr mq_attr = { + .mq_flags = 0, + .mq_maxmsg = QUEUE_SIZE, + .mq_msgsize = MAX_MSG_SIZE, + .mq_curmsgs = 0 +}; + +#define CONFIG_MESSAGING_MAX_SUBSCRIPTIONS 10 + +void cleanup_messaging() { + mq_close(ocre_msg_queue); + mq_unlink(mq_name); + exit(0); // Terminate the process after cleanup +} + + +// Structure to hold the subscription information +typedef struct { + char *topic; + wasm_function_inst_t handler; + wasm_module_inst_t module_inst; +} ocre_subscription_t; + +static ocre_subscription_t subscriptions[CONFIG_MESSAGING_MAX_SUBSCRIPTIONS]; +static int subscription_count = 0; +static bool msg_system_is_init = false; + +static uint32_t allocate_wasm_memory(wasm_module_inst_t module_inst, const void *src, size_t size) { + void *native_addr = NULL; + uint64_t wasm_ptr = wasm_runtime_module_malloc(module_inst, size, &native_addr); + if (!wasm_ptr || !native_addr) { + LOG_ERR("Failed to allocate memory in WASM"); + return 0; + } + if (src) { + memcpy(native_addr, src, size); + } + return (uint32_t)wasm_ptr; +} + +static void free_wasm_message(wasm_module_inst_t module_inst, uint32_t *ptr_array, uint16_t count) { + for (uint16_t i = 0; i < count; i++) { + if (ptr_array[i]) { + wasm_runtime_module_free(module_inst, ptr_array[i]); + } + } +} + +void* subscriber_thread(void *arg) { + wasm_runtime_init_thread_env(); + uint8_t buffer[MAX_MSG_SIZE]; + while (1) { + ssize_t msg_size = mq_receive(ocre_msg_queue, (char *)buffer, sizeof(buffer), NULL); + if (msg_size >= (ssize_t)sizeof(ocre_msg_t)) { + LOG_INF("Got a message from another container"); + ocre_msg_t *msg = (ocre_msg_t *)buffer; + char *topic = (char *)(buffer + msg->topic); + char *content_type = (char *)(buffer + msg->content_type); + void *payload = (void *)(buffer + msg->payload); + + for (int i = 0; i < subscription_count; i++) { + if (strcmp(subscriptions[i].topic, topic) == 0) { + wasm_module_inst_t module_inst = subscriptions[i].module_inst; + if (!module_inst) { + LOG_ERR("Invalid module instance"); + continue; + } + + // Create exec_env for this thread/call + wasm_exec_env_t exec_env = wasm_runtime_create_exec_env(module_inst, WASM_STACK_SIZE); + if (!exec_env) { + LOG_ERR("Failed to create exec_env"); + continue; + } + + uint32_t topic_offset = + (uint32_t)wasm_runtime_module_dup_data(module_inst, topic, strlen(topic) + 1); + if (topic_offset == 0) { + LOG_ERR("Failed to allocate memory for topic in WASM"); + continue; + } + uint32_t content_offset = (uint32_t)wasm_runtime_module_dup_data(module_inst, content_type, strlen(content_type) + 1); + if (content_offset == 0) { + LOG_ERR("Failed to allocate memory for content_type in WASM"); + wasm_runtime_module_free(module_inst, topic_offset); + continue; + } + uint32_t payload_offset = + (uint32_t)wasm_runtime_module_dup_data(module_inst, payload, msg->payload_len); + if (payload_offset == 0) { + LOG_ERR("Failed to allocate memory for payload in WASM"); + wasm_runtime_module_free(module_inst, topic_offset); + wasm_runtime_module_free(module_inst, content_offset); + continue; + } + + uint32_t args[5] = {msg->mid, topic_offset, content_offset, payload_offset, msg->payload_len}; + if (!wasm_runtime_call_wasm(exec_env, subscriptions[i].handler, 5, args)) { + const char *error = wasm_runtime_get_exception(module_inst); + LOG_ERR("Failed to call WASM function: %s", error ? error : "Unknown error"); + } else { + LOG_INF("Function executed successfully"); + } + + // Free memory and exec_env + wasm_runtime_module_free(module_inst, topic_offset); + wasm_runtime_module_free(module_inst, content_offset); + wasm_runtime_module_free(module_inst, payload_offset); + wasm_runtime_destroy_exec_env(exec_env); + } + } + } else { + core_sleep_ms(1000); + } + } + printf("do I get here?\n"); + wasm_runtime_destroy_thread_env(); + return NULL; +} + +void ocre_msg_system_init() { + if (msg_system_is_init) { + LOG_WRN("Messaging System is already initialized"); + return; + } + + // POSIX: Create message queue and thread + snprintf(mq_name, sizeof(mq_name), "/ocre_msgq_%d", getpid()); + ocre_msg_queue = mq_open(mq_name, O_CREAT | O_RDWR, 0644, &mq_attr); + if (ocre_msg_queue == (mqd_t)-1) { + perror("Failed to create message queue"); + return; + } + if (pthread_create(&subscriber_thread_data, NULL, subscriber_thread, NULL) != 0) { + perror("Failed to create subscriber thread"); + mq_close(ocre_msg_queue); + mq_unlink(mq_name); // Clean up if thread creation fails + return; + } + msg_system_is_init = true; +} + +int ocre_publish_message(wasm_exec_env_t exec_env, char *topic, char *content_type, void *payload, int payload_len) { + LOG_INF("---------------_ocre_publish_message: topic: %s [%zu], content_type: %s, payload_len: %u ", topic, + strlen(topic), content_type, payload_len); + + if (!msg_system_is_init) { + LOG_ERR("Messaging system not initialized"); + return -1; + } + + if (!topic || (topic[0] == '\0')) { + LOG_ERR("topic is NULL or empty, please check function parameters!"); + return -1; + } + + if (!content_type || (content_type[0] == '\0')) { + LOG_ERR("content_type is NULL or empty, please check function parameters!"); + return -1; + } + + if (!payload || payload_len == 0) { + LOG_ERR("payload is NULL or payload_len is 0, please check function parameters!"); + return -1; + } + + static uint64_t message_id = 0; + + size_t topic_len = strlen(topic) + 1; + size_t content_type_len = strlen(content_type) + 1; + size_t total_size = sizeof(ocre_msg_t) + topic_len + content_type_len + payload_len; + + if (total_size > MAX_MSG_SIZE) { + LOG_ERR("Message too large for queue: %zu > %d", total_size, MAX_MSG_SIZE); + return -1; + } + + uint8_t *buffer = malloc(total_size); + if (!buffer) { + LOG_ERR("Failed to allocate message buffer"); + return -1; + } + + ocre_msg_t *msg = (ocre_msg_t *)buffer; + msg->mid = message_id++; + msg->topic = sizeof(ocre_msg_t); + msg->content_type = msg->topic + topic_len; + msg->payload = msg->content_type + content_type_len; + msg->payload_len = payload_len; + + memcpy(buffer + msg->topic, topic, topic_len); + memcpy(buffer + msg->content_type, content_type, content_type_len); + memcpy(buffer + msg->payload, payload, payload_len); + + if (mq_send(ocre_msg_queue, (const char *)buffer, total_size, 0) != 0) { + perror("Message queue is full or error occurred"); + free(buffer); + return -1; + } + free(buffer); + + return 0; +} + +int ocre_subscribe_message(wasm_exec_env_t exec_env, char *topic, char *handler_name) { + LOG_INF("---------------_ocre_subscribe_message---------------"); + + if (!msg_system_is_init) { + LOG_ERR("Messaging system not initialized"); + return -1; + } + + signal(SIGINT, cleanup_messaging); + signal(SIGTERM, cleanup_messaging); + + if (subscription_count < CONFIG_MESSAGING_MAX_SUBSCRIPTIONS) { + + if (!topic || (topic[0] == '\0')) { + LOG_ERR("topic is NULL or empty, please check function parameters!"); + return -1; + } + + if (!handler_name || (handler_name[0] == '\0')) { + LOG_ERR("handler_name is NULL or empty, please check function parameters!"); + return -1; + } + + wasm_module_inst_t current_module_inst = wasm_runtime_get_module_inst(exec_env); + wasm_function_inst_t handler_function = wasm_runtime_lookup_function(current_module_inst, handler_name); + if (!handler_function) { + LOG_ERR("Failed to find %s in WASM module", handler_name); + return -1; + } + + subscriptions[subscription_count].topic = topic; + subscriptions[subscription_count].handler = handler_function; + subscriptions[subscription_count].module_inst = current_module_inst; + + LOG_INF("WASM messaging callback function set successfully"); + + subscription_count++; + } else { + LOG_ERR("Maximum subscriptions reached, could not subscribe to topic"); + return -1; + } + + return 0; +} diff --git a/src/ocre/container_messaging_posix/messaging.h b/src/ocre/container_messaging_posix/messaging.h new file mode 100644 index 00000000..2bf77b2c --- /dev/null +++ b/src/ocre/container_messaging_posix/messaging.h @@ -0,0 +1,56 @@ + +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef CONTAINER_MESSAGING_POSIX_H +#define CONTAINER_MESSAGING_POSIX_H +#include "wasm_export.h" + +/** + * @typedef ocre_msg + * + * Structure of ocre messages + * + */ +typedef struct ocre_msg { + // message id - increments on each message + uint64_t mid; + // url of the request + uint32_t topic; // offset from start of buffer + // payload format (MIME type??) + uint32_t content_type; // offset from start of buffer + // payload of the request + uint32_t payload; // offset from start of buffer + // length in bytes of the payload + int32_t payload_len; +} ocre_msg_t; + +/** + * @brief Initialize OCRE Messaging System. + * + */ +void ocre_msg_system_init(); + +/** + * @brief Publish a message to the specified target. + * + * @param topic the name of the topic on which to publish the message + * @param content_type the content type of the message; it is recommended to use a MIME type + * @param payload a buffer containing the message contents + * @param payload_len the length of the payload buffer + */ +int ocre_publish_message(wasm_exec_env_t exec_env, char *topic, char *content_type, void *payload, int payload_len); + +/** + * @brief Subscribe to messages on the specified topic. + * + * @param topic the name of the topic on which to subscribe + * @param handler_name name of callback function that will be called when a message is received on this topic + */ +int ocre_subscribe_message(wasm_exec_env_t exec_env, char *topic, char *handler_name); + +#endif /* CONTAINER_MESSAGING_POSIX_H */ diff --git a/src/ocre/fs/fs.h b/src/ocre/fs/fs.h deleted file mode 100644 index 3bd47f2c..00000000 --- a/src/ocre/fs/fs.h +++ /dev/null @@ -1,26 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef OCRE_FILESYSTEM_H -#define OCRE_FILESYSTEM_H - -void ocre_app_storage_init(); - -// clang-format off -#define FS_MOUNT_POINT "/lfs" -#define OCRE_BASE_PATH FS_MOUNT_POINT "/ocre" - -#define APP_RESOURCE_PATH OCRE_BASE_PATH "/images" -#define PACKAGE_BASE_PATH OCRE_BASE_PATH "/manifests" -#define CONFIG_PATH OCRE_BASE_PATH "/config/" - -#define APP_RESOURCE_PATH_FMT APP_RESOURCE_PATH "/%s.bin" -#define TEMP_RESOURCE_PATH_FMT APP_RESOURCE_PATH "/%s.tmp" -#define PACKAGE_RESOURCE_PATH_FMT PACKAGE_BASE_PATH "/pkg-%d" -#define CONFIG_PATH_FMT CONFIG_PATH "/%s.bin" - -#endif diff --git a/src/ocre/ocre.h b/src/ocre/ocre.h index b48af1bf..dd3edf48 100644 --- a/src/ocre/ocre.h +++ b/src/ocre/ocre.h @@ -8,7 +8,7 @@ #ifndef OCRE_H #define OCRE_H -#include +#include "ocre_core_external.h" #if CONFIG_OCRE_LOG_DEBUG #define OCRE_LOG_LEVEL LOG_LEVEL_DBG @@ -22,4 +22,4 @@ #define OCRE_LOG_LEVEL LOG_LEVEL_NONE #endif -#endif \ No newline at end of file +#endif diff --git a/src/ocre/ocre_container_runtime/ocre_container_runtime.c b/src/ocre/ocre_container_runtime/ocre_container_runtime.c index 9cb0d687..1a17d5ef 100644 --- a/src/ocre/ocre_container_runtime/ocre_container_runtime.c +++ b/src/ocre/ocre_container_runtime/ocre_container_runtime.c @@ -11,9 +11,9 @@ #include "../../application/tests/ocre_container_runtime/stubs/wasm/wasm.h" #include "../../application/tests/ocre_container_runtime/stubs/ocre/fs/fs.h" #else -#include -#include + #include +#include "ocre_core_external.h" #include "../components/container_supervisor/cs_sm.h" #include "../components/container_supervisor/cs_sm_impl.h" // WAMR includes @@ -36,12 +36,10 @@ ocre_container_runtime_status_t ocre_container_runtime_init(ocre_cs_ctx *ctx, oc } CS_ctx_init(ctx); - ctx->current_container_id = 0; ctx->download_count = 0; start_ocre_cs_thread(ctx); - k_sleep(K_MSEC(1000)); // Hack to allow the thread to start prior to continuing - + core_sleep_ms(1000); return RUNTIME_STATUS_INITIALIZED; } @@ -56,10 +54,11 @@ ocre_container_status_t ocre_container_runtime_create_container(ocre_cs_ctx *ctx int i; uint8_t validity_flag = false; // Find available slot for new container - for (i = 0; i < MAX_CONTAINERS; i++) { + for (i = 0; i < CONFIG_MAX_CONTAINERS; i++) { if ((ctx->containers[i].container_runtime_status == CONTAINER_STATUS_UNKNOWN) || (ctx->containers[i].container_runtime_status == CONTAINER_STATUS_DESTROYED)) { *container_id = i; + ctx->containers[i].container_ID = i; validity_flag = true; break; } @@ -69,22 +68,22 @@ ocre_container_status_t ocre_container_runtime_create_container(ocre_cs_ctx *ctx LOG_ERR("No available slots, unable to create container"); return CONTAINER_STATUS_ERROR; } - LOG_INF("Request to create new container in slot:%d", *container_id); + LOG_INF("Request to create new container in slot: %d", *container_id); struct ocre_message event = {.event = EVENT_CREATE_CONTAINER}; ocre_container_data_t Data; event.containerId = *container_id; Data = *container_data; ctx->containers[*container_id].ocre_container_data = Data; + ctx->containers[*container_id].ocre_runtime_arguments.module_inst = NULL; ctx->download_count++; ocre_component_send(&ocre_cs_component, &event); return CONTAINER_STATUS_CREATED; } -ocre_container_status_t ocre_container_runtime_run_container(ocre_cs_ctx *ctx, int container_id, - ocre_container_runtime_cb callback) { - if (container_id < 0 || container_id >= MAX_CONTAINERS) { +ocre_container_status_t ocre_container_runtime_run_container(int container_id, ocre_container_runtime_cb callback) { + if (container_id < 0 || container_id >= CONFIG_MAX_CONTAINERS) { LOG_ERR("Invalid container ID: %d", container_id); return CONTAINER_STATUS_ERROR; } @@ -97,7 +96,7 @@ ocre_container_status_t ocre_container_runtime_run_container(ocre_cs_ctx *ctx, i } ocre_container_status_t ocre_container_runtime_get_container_status(ocre_cs_ctx *ctx, int container_id) { - if (container_id < 0 || container_id >= MAX_CONTAINERS) { + if (container_id < 0 || container_id >= CONFIG_MAX_CONTAINERS) { LOG_ERR("Invalid container ID: %d", container_id); return CONTAINER_STATUS_ERROR; } @@ -105,13 +104,12 @@ ocre_container_status_t ocre_container_runtime_get_container_status(ocre_cs_ctx return ctx->containers[container_id].container_runtime_status; } -ocre_container_status_t ocre_container_runtime_stop_container(ocre_cs_ctx *ctx, int container_id, - ocre_container_runtime_cb callback) { - if (container_id < 0 || container_id >= MAX_CONTAINERS) { +ocre_container_status_t ocre_container_runtime_stop_container(int container_id, ocre_container_runtime_cb callback) { + if (container_id < 0 || container_id >= CONFIG_MAX_CONTAINERS) { LOG_ERR("Invalid container ID: %d", container_id); return CONTAINER_STATUS_ERROR; } - LOG_INF("Request to stop container in slot:%d", container_id); + LOG_INF("Request to stop container in slot: %d", container_id); struct ocre_message event = {.event = EVENT_STOP_CONTAINER}; event.containerId = container_id; ocre_component_send(&ocre_cs_component, &event); @@ -120,11 +118,11 @@ ocre_container_status_t ocre_container_runtime_stop_container(ocre_cs_ctx *ctx, ocre_container_status_t ocre_container_runtime_destroy_container(ocre_cs_ctx *ctx, int container_id, ocre_container_runtime_cb callback) { - if (container_id < 0 || container_id >= MAX_CONTAINERS) { + if (container_id < 0 || container_id >= CONFIG_MAX_CONTAINERS) { LOG_ERR("Invalid container ID: %d", container_id); return CONTAINER_STATUS_ERROR; } - LOG_INF("Request to destroy container in slot:%d", container_id); + LOG_INF("Request to destroy container in slot: %d", container_id); struct ocre_message event = {.event = EVENT_DESTROY_CONTAINER}; event.containerId = container_id; ocre_component_send(&ocre_cs_component, &event); @@ -133,11 +131,11 @@ ocre_container_status_t ocre_container_runtime_destroy_container(ocre_cs_ctx *ct ocre_container_status_t ocre_container_runtime_restart_container(ocre_cs_ctx *ctx, int container_id, ocre_container_runtime_cb callback) { - if (container_id < 0 || container_id >= MAX_CONTAINERS) { + if (container_id < 0 || container_id >= CONFIG_MAX_CONTAINERS) { LOG_ERR("Invalid container ID: %d", container_id); return CONTAINER_STATUS_ERROR; } - LOG_INF("Request to restart container in slot:%d", container_id); + LOG_INF("Request to restart container in slot: %d", container_id); struct ocre_message event = {.event = EVENT_RESTART_CONTAINER}; event.containerId = container_id; ocre_component_send(&ocre_cs_component, &event); diff --git a/src/ocre/ocre_container_runtime/ocre_container_runtime.h b/src/ocre/ocre_container_runtime/ocre_container_runtime.h index 0ed0d960..a1f8e558 100644 --- a/src/ocre/ocre_container_runtime/ocre_container_runtime.h +++ b/src/ocre/ocre_container_runtime/ocre_container_runtime.h @@ -12,11 +12,7 @@ #include "../../application/tests/ocre_container_runtime/stubs/container_healthcheck/ocre_container_healthcheck.h" #include "../../application/tests/ocre_container_runtime/stubs/k_sem/k_sem.h" #else -#include -#include -#include "../container_healthcheck/ocre_container_healthcheck.h" -#include -#include +#include "ocre_core_external.h" #endif #include @@ -24,10 +20,10 @@ #include "wasm_export.h" #define OCRE_CR_DEBUG_ON 0 // Debug flag for container runtime (0: OFF, 1: ON) -#define MAX_CONTAINERS 10 // Maximum number of containers supported by the runtime !!! Can be configurable #define FILE_PATH_MAX 256 // Maximum file path length #define OCRE_CR_INIT_TIMEOUT 500 // Timeout to wait for the container registry to initialize + /** * @brief Structure containing the runtime arguments for a container runtime. */ @@ -38,7 +34,6 @@ typedef struct ocre_runtime_arguments_t { wasm_module_t module; ///< Handle to the loaded WASM module. wasm_module_inst_t module_inst; ///< Handle to the instantiated WASM module. wasm_function_inst_t func; ///< Handle to the function to be executed within the WASM module. - wasm_exec_env_t exec_env; ///< Execution environment for the WASM function. uint32_t stack_size; ///< Stack size for the WASM module. uint32_t heap_size; ///< Heap size for the WASM module. } ocre_runtime_arguments_t; @@ -87,11 +82,10 @@ typedef struct ocre_container_runtime_init_arguments_t { * @brief Structure representing the data associated with a container. */ typedef struct ocre_container_data_t { - char name[16]; // +#include +#include #include #include #include #include -#include "ocre_gpio.h" -#include "wasm_export.h" -#include -#include -#include +#include LOG_MODULE_DECLARE(ocre_cs_component, OCRE_LOG_LEVEL); -#define GPIO_STACK_SIZE 2048 -#define GPIO_THREAD_PRIORITY 5 -#define WASM_STACK_SIZE (8 * 1024) - typedef struct { - bool in_use; - uint32_t id; - const struct device *port; - gpio_pin_t pin_number; - ocre_gpio_direction_t direction; - struct gpio_callback gpio_cb_data; - ocre_gpio_callback_t user_callback; + uint32_t in_use: 1; + uint32_t direction: 1; + uint32_t pin_number: 6; + uint32_t port_idx: 4; + struct gpio_callback *cb; + wasm_module_inst_t owner; } gpio_pin_ocre; -static K_THREAD_STACK_DEFINE(gpio_thread_stack, GPIO_STACK_SIZE); -static struct k_thread gpio_thread; -static k_tid_t gpio_thread_id; - -K_MSGQ_DEFINE(gpio_msgq, sizeof(uint32_t) * 2, 10, 4); - -// GPIO pin management -static gpio_pin_ocre gpio_pins[CONFIG_OCRE_GPIO_MAX_PINS] = {0}; -static wasm_function_inst_t gpio_dispatcher_func = NULL; +static gpio_pin_ocre gpio_pins[CONFIG_OCRE_GPIO_MAX_PINS]; +static const struct device *gpio_ports[CONFIG_OCRE_GPIO_MAX_PORTS]; +static bool port_ready[CONFIG_OCRE_GPIO_MAX_PORTS]; static bool gpio_system_initialized = false; -static wasm_module_inst_t current_module_inst = NULL; -static wasm_exec_env_t shared_exec_env = NULL; +extern struct k_msgq wasm_event_queue; +extern struct k_spinlock wasm_event_queue_lock; -// Define GPIO port devices structure - will be populated at runtime -static const struct device *gpio_ports[CONFIG_OCRE_GPIO_MAX_PORTS] = {NULL}; - -// Board-specific GPIO port device definitions #if defined(CONFIG_BOARD_B_U585I_IOT02A) -// STM32U5 board configuration #define GPIO_PORT_A DT_NODELABEL(gpioa) #define GPIO_PORT_B DT_NODELABEL(gpiob) #define GPIO_PORT_C DT_NODELABEL(gpioc) @@ -59,14 +41,9 @@ static const struct device *gpio_ports[CONFIG_OCRE_GPIO_MAX_PORTS] = {NULL}; #define GPIO_PORT_F DT_NODELABEL(gpiof) #define GPIO_PORT_G DT_NODELABEL(gpiog) #define GPIO_PORT_H DT_NODELABEL(gpioh) -static const char *gpio_port_names[CONFIG_OCRE_GPIO_MAX_PORTS] = {"GPIOA", "GPIOB", "GPIOC", "GPIOD", - "GPIOE", "GPIOF", "GPIOG", "GPIOH"}; #elif defined(CONFIG_BOARD_ESP32C3_DEVKITM) -// ESP32C3 board configuration #define GPIO_PORT_0 DT_NODELABEL(gpio0) -static const char *gpio_port_names[CONFIG_OCRE_GPIO_MAX_PORTS] = {"GPIO0"}; #else -// Generic fallback configuration #if DT_NODE_EXISTS(DT_NODELABEL(gpio0)) #define GPIO_PORT_0 DT_NODELABEL(gpio0) #endif @@ -76,523 +53,405 @@ static const char *gpio_port_names[CONFIG_OCRE_GPIO_MAX_PORTS] = {"GPIO0"}; #if DT_NODE_EXISTS(DT_NODELABEL(gpio)) #define GPIO_PORT_G DT_NODELABEL(gpio) #endif -static const char *gpio_port_names[CONFIG_OCRE_GPIO_MAX_PORTS] = {"GPIO"}; #endif -void ocre_gpio_cleanup_container(wasm_module_inst_t module_inst) { - if (!gpio_system_initialized || !module_inst) { - LOG_ERR("GPIO system not properly initialized"); - return; - } - - if (module_inst != current_module_inst) { - LOG_WRN("Cleanup requested for non-active module instance"); - return; - } - - int cleaned_count = 0; - for (int i = 0; i < CONFIG_OCRE_GPIO_MAX_PINS; i++) { - if (gpio_pins[i].in_use) { - if (gpio_pins[i].direction == OCRE_GPIO_DIR_INPUT && gpio_pins[i].user_callback) { - gpio_pin_interrupt_configure(gpio_pins[i].port, gpio_pins[i].pin_number, GPIO_INT_DISABLE); - gpio_remove_callback(gpio_pins[i].port, &gpio_pins[i].gpio_cb_data); - } - gpio_pins[i].in_use = false; - gpio_pins[i].id = 0; - cleaned_count++; - } - } - - LOG_INF("Cleaned up %d GPIO pins for container", cleaned_count); -} - -static void gpio_thread_fn(void *arg1, void *arg2, void *arg3) { - uint32_t msg_data[2]; // [0] = pin_id, [1] = state - - while (1) { - if (k_msgq_get(&gpio_msgq, &msg_data, K_FOREVER) == 0) { - if (!gpio_dispatcher_func || !current_module_inst || !shared_exec_env) { - LOG_ERR("GPIO system not properly initialized"); - continue; - } - - uint32_t pin_id = msg_data[0]; - uint32_t state = msg_data[1]; - - LOG_DBG("Processing GPIO pin event: %d, state: %d", pin_id, state); - uint32_t args[2] = {pin_id, state}; - - bool call_success = wasm_runtime_call_wasm(shared_exec_env, gpio_dispatcher_func, 2, args); - - if (!call_success) { - const char *error = wasm_runtime_get_exception(current_module_inst); - LOG_ERR("Failed to call WASM function: %s", error ? error : "Unknown error"); - } else { - LOG_INF("Successfully called WASM function for GPIO pin %d", pin_id); - } - } - } -} - -static void gpio_callback_handler(const struct device *port, struct gpio_callback *cb, gpio_port_pins_t pins) { - for (int i = 0; i < CONFIG_OCRE_GPIO_MAX_PINS; i++) { - if (gpio_pins[i].in_use && &gpio_pins[i].gpio_cb_data == cb && gpio_pins[i].port == port) { - if (pins & BIT(gpio_pins[i].pin_number)) { - int state = gpio_pin_get(port, gpio_pins[i].pin_number); - if (state >= 0) { - uint32_t msg_data[2] = {gpio_pins[i].id, (uint32_t)state}; - if (k_msgq_put(&gpio_msgq, &msg_data, K_NO_WAIT) != 0) { - LOG_ERR("Failed to queue GPIO callback for ID: %d", gpio_pins[i].id); - } - } - } - } - } -} - -void ocre_gpio_set_module_inst(wasm_module_inst_t module_inst) { - current_module_inst = module_inst; - if (shared_exec_env) { - wasm_runtime_destroy_exec_env(shared_exec_env); - } - shared_exec_env = wasm_runtime_create_exec_env(module_inst, WASM_STACK_SIZE); -} - -void ocre_gpio_set_dispatcher(wasm_exec_env_t exec_env) { - if (!current_module_inst) { - LOG_ERR("No active WASM module instance"); - return; - } - - wasm_function_inst_t func = wasm_runtime_lookup_function(current_module_inst, "gpio_callback"); - if (!func) { - LOG_ERR("Failed to find 'gpio_callback' in WASM module"); - return; - } - - gpio_dispatcher_func = func; - LOG_INF("WASM GPIO dispatcher function set successfully"); -} - -static gpio_pin_ocre *get_gpio_from_id(int id) { - if (id < 0 || id >= CONFIG_OCRE_GPIO_MAX_PINS) { - return NULL; - } - return &gpio_pins[id]; -} +static void gpio_callback_handler(const struct device *port, struct gpio_callback *cb, gpio_port_pins_t pins); int ocre_gpio_init(void) { if (gpio_system_initialized) { - LOG_INF("GPIO already initialized"); + LOG_INF("GPIO system already initialized"); return 0; } - - // Initialize GPIO ports array based on board + if (!common_initialized && ocre_common_init() != 0) { + LOG_ERR("Failed to initialize common subsystem"); + return -EINVAL; + } int port_count = 0; - + memset(port_ready, 0, sizeof(port_ready)); #if defined(CONFIG_BOARD_B_U585I_IOT02A) - // STM32U5 board initialization if (DT_NODE_EXISTS(GPIO_PORT_A)) { gpio_ports[0] = DEVICE_DT_GET(GPIO_PORT_A); if (!device_is_ready(gpio_ports[0])) { - LOG_ERR("GPIOA not ready"); + LOG_ERR("GPIOA is not ready"); } else { - LOG_INF("GPIOA initialized"); + LOG_INF("GPIOA is initialized"); + port_ready[0] = true; port_count++; } } - if (DT_NODE_EXISTS(GPIO_PORT_B)) { gpio_ports[1] = DEVICE_DT_GET(GPIO_PORT_B); if (!device_is_ready(gpio_ports[1])) { - LOG_ERR("GPIOB not ready"); + LOG_ERR("GPIOB is not ready"); } else { - LOG_INF("GPIOB initialized"); + LOG_INF("GPIOB is initialized"); + port_ready[1] = true; port_count++; } } - if (DT_NODE_EXISTS(GPIO_PORT_C)) { gpio_ports[2] = DEVICE_DT_GET(GPIO_PORT_C); if (!device_is_ready(gpio_ports[2])) { - LOG_ERR("GPIOC not ready"); + LOG_ERR("GPIOC is not ready"); } else { - LOG_INF("GPIOC initialized"); + LOG_INF("GPIOC is initialized"); + port_ready[2] = true; port_count++; } } - if (DT_NODE_EXISTS(GPIO_PORT_D)) { gpio_ports[3] = DEVICE_DT_GET(GPIO_PORT_D); if (!device_is_ready(gpio_ports[3])) { - LOG_ERR("GPIOD not ready"); + LOG_ERR("GPIOD is not ready"); } else { - LOG_INF("GPIOD initialized"); + LOG_INF("GPIOD is initialized"); + port_ready[3] = true; port_count++; } } - if (DT_NODE_EXISTS(GPIO_PORT_E)) { gpio_ports[4] = DEVICE_DT_GET(GPIO_PORT_E); if (!device_is_ready(gpio_ports[4])) { - LOG_ERR("GPIOE not ready"); + LOG_ERR("GPIOE is not ready"); } else { - LOG_INF("GPIOE initialized"); + LOG_INF("GPIOE is initialized"); + port_ready[4] = true; port_count++; } } - if (DT_NODE_EXISTS(GPIO_PORT_F)) { gpio_ports[5] = DEVICE_DT_GET(GPIO_PORT_F); if (!device_is_ready(gpio_ports[5])) { - LOG_ERR("GPIOF not ready"); + LOG_ERR("GPIOF is not ready"); } else { - LOG_INF("GPIOF initialized"); + LOG_INF("GPIOF is initialized"); + port_ready[5] = true; port_count++; } } - if (DT_NODE_EXISTS(GPIO_PORT_G)) { gpio_ports[6] = DEVICE_DT_GET(GPIO_PORT_G); if (!device_is_ready(gpio_ports[6])) { - LOG_ERR("GPIOG not ready"); + LOG_ERR("GPIOG is not ready"); } else { - LOG_INF("GPIOG initialized"); + LOG_INF("GPIOG is initialized"); + port_ready[6] = true; port_count++; } } - if (DT_NODE_EXISTS(GPIO_PORT_H)) { gpio_ports[7] = DEVICE_DT_GET(GPIO_PORT_H); if (!device_is_ready(gpio_ports[7])) { - LOG_ERR("GPIOH not ready"); + LOG_ERR("GPIOH is not ready"); } else { - LOG_INF("GPIOH initialized"); + LOG_INF("GPIOH is initialized"); + port_ready[7] = true; port_count++; } } #elif defined(CONFIG_BOARD_ESP32C3_DEVKITM) - // ESP32C3 board initialization if (DT_NODE_EXISTS(GPIO_PORT_0)) { gpio_ports[0] = DEVICE_DT_GET(GPIO_PORT_0); if (!device_is_ready(gpio_ports[0])) { - LOG_ERR("GPIO0 not ready"); + LOG_ERR("GPIO0 is not ready"); } else { - LOG_INF("GPIO0 initialized"); + LOG_INF("GPIO0 is initialized"); + port_ready[0] = true; port_count++; } } -/* ADD BOARDS HERE */ #else - // Generic fallback initialization - // #if defined(GPIO_PORT_0) - // #if DT_NODE_EXISTS(GPIO_PORT_0) - // gpio_ports[0] = DEVICE_DT_GET(GPIO_PORT_0); - // LOG_INF("No Board Choosen Generic fallback initialization %s\n", gpio_ports[0]); - // port_count = 1; - // #endif - // #endif - -#if defined(GPIO_PORT_A) -#if DT_NODE_EXISTS(GPIO_PORT_A) +#if defined(GPIO_PORT_A) && DT_NODE_EXISTS(GPIO_PORT_A) gpio_ports[0] = DEVICE_DT_GET(GPIO_PORT_A); - LOG_INF("No Board Choosen Generic fallback initialization %s\n", gpio_ports[0]); - port_count = 1; -#endif + if (!device_is_ready(gpio_ports[0])) { + LOG_ERR("GPIOA is not ready"); + } else { + LOG_INF("GPIOA is initialized"); + port_ready[0] = true; + port_count++; + } #endif - -#if defined(GPIO_PORT_G) -#if DT_NODE_EXISTS(GPIO_PORT_G) +#if defined(GPIO_PORT_G) && DT_NODE_EXISTS(GPIO_PORT_G) gpio_ports[0] = DEVICE_DT_GET(GPIO_PORT_G); - LOG_INF("No Board Choosen Generic fallback initialization %s\n", gpio_ports[0]); - port_count = 1; -#endif + if (!device_is_ready(gpio_ports[0])) { + LOG_ERR("GPIO is not ready"); + } else { + LOG_INF("GPIO is initialized"); + port_ready[0] = true; + port_count++; + } #endif #endif - if (port_count == 0) { - LOG_ERR("No GPIO ports were initialized\n"); + LOG_ERR("No GPIO ports were initialized"); return -ENODEV; } - - // Start GPIO thread for callbacks - gpio_thread_id = k_thread_create(&gpio_thread, gpio_thread_stack, K_THREAD_STACK_SIZEOF(gpio_thread_stack), - gpio_thread_fn, NULL, NULL, NULL, GPIO_THREAD_PRIORITY, 0, K_NO_WAIT); - if (!gpio_thread_id) { - LOG_ERR("Failed to create GPIO thread"); - return -ENOMEM; - } - - k_thread_name_set(gpio_thread_id, "gpio_thread"); - LOG_INF("GPIO system initialized with %d ports", port_count); + ocre_register_cleanup_handler(OCRE_RESOURCE_TYPE_GPIO, ocre_gpio_cleanup_container); gpio_system_initialized = true; + LOG_INF("GPIO system initialized, %d ports available", port_count); return 0; } int ocre_gpio_configure(const ocre_gpio_config_t *config) { - if (!gpio_system_initialized) { - LOG_ERR("GPIO not initialized"); - return -ENODEV; - } - - if (config == NULL) { - LOG_ERR("NULL config pointer"); - return -EINVAL; - } - - if (config->pin < 0 || config->pin >= CONFIG_OCRE_GPIO_MAX_PINS) { - LOG_ERR("Invalid GPIO pin number: %d", config->pin); - return -EINVAL; - } - - // Map logical pin to physical port/pin - int port_idx = config->pin / CONFIG_OCRE_GPIO_PINS_PER_PORT; - gpio_pin_t pin_number = config->pin % CONFIG_OCRE_GPIO_PINS_PER_PORT; - - if (port_idx >= CONFIG_OCRE_GPIO_MAX_PORTS || gpio_ports[port_idx] == NULL) { - LOG_ERR("Invalid GPIO port index: %d", port_idx); + if (!gpio_system_initialized || !config || config->pin >= CONFIG_OCRE_GPIO_PINS_PER_PORT || + config->port_idx >= CONFIG_OCRE_GPIO_MAX_PORTS || !port_ready[config->port_idx]) { + LOG_ERR("Invalid GPIO config: port=%d, pin=%d, port_ready=%d", config ? config->port_idx : -1, + config ? config->pin : -1, config ? port_ready[config->port_idx] : false); return -EINVAL; } - - // Configure Zephyr GPIO - gpio_flags_t flags = 0; - + int port_idx = config->port_idx; + gpio_pin_t pin = config->pin; + gpio_flags_t flags = (config->direction == OCRE_GPIO_DIR_INPUT) ? GPIO_INPUT : GPIO_OUTPUT; if (config->direction == OCRE_GPIO_DIR_INPUT) { - flags = GPIO_INPUT; - } else if (config->direction == OCRE_GPIO_DIR_OUTPUT) { - flags = GPIO_OUTPUT; - } else { - LOG_ERR("Invalid GPIO direction"); - return -EINVAL; + flags |= GPIO_PULL_UP; } - - int ret = gpio_pin_configure(gpio_ports[port_idx], pin_number, flags); + int ret = gpio_pin_configure(gpio_ports[port_idx], pin, flags); if (ret != 0) { - LOG_ERR("Failed to configure GPIO pin: %d", ret); + LOG_ERR("Failed to configure GPIO pin %d on port %d: %d", pin, port_idx, ret); return ret; } - - // Update our tracking information - gpio_pins[config->pin].in_use = true; - gpio_pins[config->pin].id = config->pin; - gpio_pins[config->pin].port = gpio_ports[port_idx]; - gpio_pins[config->pin].pin_number = pin_number; - gpio_pins[config->pin].direction = config->direction; - gpio_pins[config->pin].user_callback = NULL; - - LOG_DBG("Configured GPIO pin %d: port=%s, pin=%d, direction=%d", config->pin, gpio_port_names[port_idx], pin_number, - config->direction); - - return 0; -} - -int ocre_gpio_pin_set(int pin, ocre_gpio_pin_state_t state) { - gpio_pin_ocre *gpio = get_gpio_from_id(pin); - - if (!gpio_system_initialized) { - LOG_ERR("GPIO system not initialized when trying to set pin %d", pin); - return -ENODEV; + wasm_module_inst_t module_inst = ocre_get_current_module(); + if (!module_inst) { + LOG_ERR("No current module instance for GPIO configuration"); + return -EINVAL; } - - if (!gpio || !gpio->in_use) { - LOG_ERR("Invalid or unconfigured GPIO pin: %d", pin); + int global_pin = port_idx * CONFIG_OCRE_GPIO_PINS_PER_PORT + pin; + if (global_pin >= CONFIG_OCRE_GPIO_MAX_PINS) { + LOG_ERR("Global pin %d exceeds max %d", global_pin, CONFIG_OCRE_GPIO_MAX_PINS); return -EINVAL; } + gpio_pins[global_pin] = (gpio_pin_ocre){.in_use = 1, + .direction = (config->direction == OCRE_GPIO_DIR_OUTPUT), + .pin_number = pin, + .port_idx = port_idx, + .cb = NULL, + .owner = module_inst}; + ocre_increment_resource_count(module_inst, OCRE_RESOURCE_TYPE_GPIO); + LOG_INF("Configured GPIO pin %d on port %d (global %d) for module %p", pin, port_idx, global_pin, + (void *)module_inst); + return 0; +} - if (gpio->direction != OCRE_GPIO_DIR_OUTPUT) { - LOG_ERR("Cannot set state of input pin: %d", pin); +int ocre_gpio_pin_set(int pin, ocre_gpio_pin_state_t state) { + if (pin >= CONFIG_OCRE_GPIO_MAX_PINS || !gpio_pins[pin].in_use || gpio_pins[pin].direction != 1 || + !port_ready[gpio_pins[pin].port_idx]) { + LOG_ERR("Invalid GPIO pin %d or not configured as output or port not ready", pin); return -EINVAL; } - - LOG_INF("Setting GPIO pin %d (port=%p, pin=%d) to state %d", pin, gpio->port, gpio->pin_number, state); - - int ret = gpio_pin_set(gpio->port, gpio->pin_number, state); + int ret = gpio_pin_set(gpio_ports[gpio_pins[pin].port_idx], gpio_pins[pin].pin_number, state); if (ret != 0) { LOG_ERR("Failed to set GPIO pin %d: %d", pin, ret); - return ret; } - - LOG_INF("Successfully set GPIO pin %d to state %d", pin, state); - return 0; + return ret; } ocre_gpio_pin_state_t ocre_gpio_pin_get(int pin) { - gpio_pin_ocre *gpio = get_gpio_from_id(pin); - - if (!gpio_system_initialized) { - return -ENODEV; - } - - if (!gpio || !gpio->in_use) { - LOG_ERR("Invalid or unconfigured GPIO pin: %d", pin); + if (pin >= CONFIG_OCRE_GPIO_MAX_PINS || !gpio_pins[pin].in_use || !port_ready[gpio_pins[pin].port_idx]) { + LOG_ERR("Invalid or unconfigured GPIO pin %d or port not ready", pin); return -EINVAL; } - - int value = gpio_pin_get(gpio->port, gpio->pin_number); - if (value < 0) { - LOG_ERR("Failed to get GPIO pin %d state: %d", pin, value); - return value; - } - - return value ? OCRE_GPIO_PIN_SET : OCRE_GPIO_PIN_RESET; + int value = gpio_pin_get(gpio_ports[gpio_pins[pin].port_idx], gpio_pins[pin].pin_number); + return (value >= 0) ? (value ? OCRE_GPIO_PIN_SET : OCRE_GPIO_PIN_RESET) : value; } int ocre_gpio_pin_toggle(int pin) { - gpio_pin_ocre *gpio = get_gpio_from_id(pin); - - if (!gpio_system_initialized) { - return -ENODEV; - } - - if (!gpio || !gpio->in_use) { - LOG_ERR("Invalid or unconfigured GPIO pin: %d", pin); + if (pin >= CONFIG_OCRE_GPIO_MAX_PINS || !gpio_pins[pin].in_use || gpio_pins[pin].direction != 1 || + !port_ready[gpio_pins[pin].port_idx]) { + LOG_ERR("Invalid GPIO pin %d or not configured as output or port not ready", pin); return -EINVAL; } - - if (gpio->direction != OCRE_GPIO_DIR_OUTPUT) { - LOG_ERR("Cannot toggle state of input pin: %d", pin); - return -EINVAL; - } - - int ret = gpio_pin_toggle(gpio->port, gpio->pin_number); + int ret = gpio_pin_toggle(gpio_ports[gpio_pins[pin].port_idx], gpio_pins[pin].pin_number); if (ret != 0) { LOG_ERR("Failed to toggle GPIO pin %d: %d", pin, ret); - return ret; } - - return 0; + return ret; } -int ocre_gpio_register_callback(int pin, ocre_gpio_callback_t callback) { - gpio_pin_ocre *gpio = get_gpio_from_id(pin); - - if (!gpio_system_initialized) { - return -ENODEV; - } - - if (!gpio || !gpio->in_use) { - LOG_ERR("Invalid or unconfigured GPIO pin: %d", pin); +int ocre_gpio_register_callback(int pin) { + if (pin >= CONFIG_OCRE_GPIO_MAX_PINS || !gpio_pins[pin].in_use || gpio_pins[pin].direction != 0 || + !port_ready[gpio_pins[pin].port_idx]) { + LOG_ERR("Invalid GPIO pin %d or not configured as input or port not ready", pin); return -EINVAL; } - - if (gpio->direction != OCRE_GPIO_DIR_INPUT) { - LOG_ERR("Cannot register callback on output pin: %d", pin); - return -EINVAL; + gpio_pin_ocre *gpio = &gpio_pins[pin]; + if (!gpio->cb) { + gpio->cb = k_calloc(1, sizeof(struct gpio_callback)); + if (!gpio->cb) { + LOG_ERR("Failed to allocate memory for GPIO callback"); + return -ENOMEM; + } } - - // Configure the interrupt - int ret = gpio_pin_interrupt_configure(gpio->port, gpio->pin_number, GPIO_INT_EDGE_BOTH); - if (ret != 0) { - LOG_ERR("Failed to configure GPIO interrupt: %d", ret); + int ret = gpio_pin_interrupt_configure(gpio_ports[gpio->port_idx], gpio->pin_number, GPIO_INT_EDGE_BOTH); + if (ret) { + LOG_ERR("Failed to configure interrupt for GPIO pin %d: %d", pin, ret); + k_free(gpio->cb); + gpio->cb = NULL; return ret; } - - // Initialize the callback structure - gpio_init_callback(&gpio->gpio_cb_data, gpio_callback_handler, BIT(gpio->pin_number)); - ret = gpio_add_callback(gpio->port, &gpio->gpio_cb_data); - if (ret != 0) { - LOG_ERR("Failed to add GPIO callback: %d", ret); + gpio_init_callback(gpio->cb, gpio_callback_handler, BIT(gpio->pin_number)); + ret = gpio_add_callback(gpio_ports[gpio->port_idx], gpio->cb); + if (ret) { + LOG_ERR("Failed to add callback for GPIO pin %d: %d", pin, ret); + k_free(gpio->cb); + gpio->cb = NULL; return ret; } - - // Store the user callback - gpio->user_callback = callback; - LOG_DBG("Registered callback for GPIO pin %d", pin); - + LOG_INF("Registered callback for GPIO pin %d (port %d, pin %d)", pin, gpio->port_idx, gpio->pin_number); return 0; } int ocre_gpio_unregister_callback(int pin) { - gpio_pin_ocre *gpio = get_gpio_from_id(pin); - - if (!gpio_system_initialized) { - return -ENODEV; - } - - if (!gpio || !gpio->in_use) { - LOG_ERR("Invalid or unconfigured GPIO pin: %d", pin); + if (pin >= CONFIG_OCRE_GPIO_MAX_PINS || !gpio_pins[pin].in_use || !gpio_pins[pin].cb || + !port_ready[gpio_pins[pin].port_idx]) { + LOG_ERR("Invalid GPIO pin %d or no callback registered or port not ready", pin); return -EINVAL; } - - if (!gpio->user_callback) { - LOG_WRN("No callback registered for pin %d", pin); - return 0; - } - - // Disable interrupt and remove callback - int ret = gpio_pin_interrupt_configure(gpio->port, gpio->pin_number, GPIO_INT_DISABLE); - if (ret != 0) { - LOG_ERR("Failed to disable GPIO interrupt: %d", ret); + gpio_pin_ocre *gpio = &gpio_pins[pin]; + int ret = gpio_pin_interrupt_configure(gpio_ports[gpio->port_idx], gpio->pin_number, GPIO_INT_DISABLE); + if (ret) { + LOG_ERR("Failed to disable interrupt for GPIO pin %d: %d", pin, ret); return ret; } - - ret = gpio_remove_callback(gpio->port, &gpio->gpio_cb_data); - if (ret != 0) { - LOG_ERR("Failed to remove GPIO callback: %d", ret); - return ret; + ret = gpio_remove_callback(gpio_ports[gpio->port_idx], gpio->cb); + if (ret) { + LOG_ERR("Failed to remove callback for GPIO pin %d: %d", pin, ret); } + k_free(gpio->cb); + gpio->cb = NULL; + LOG_INF("Unregistered callback for GPIO pin %d", pin); + return ret; +} - gpio->user_callback = NULL; - LOG_DBG("Unregistered callback for GPIO pin %d", pin); - - return 0; +void ocre_gpio_cleanup_container(wasm_module_inst_t module_inst) { + if (!gpio_system_initialized || !module_inst) { + LOG_ERR("GPIO system not initialized or invalid module %p", (void *)module_inst); + return; + } + for (int i = 0; i < CONFIG_OCRE_GPIO_MAX_PINS; i++) { + if (gpio_pins[i].in_use && gpio_pins[i].owner == module_inst) { + if (gpio_pins[i].direction == 0) { + ocre_gpio_unregister_callback(i); + } + gpio_pins[i].in_use = 0; + gpio_pins[i].owner = NULL; + ocre_decrement_resource_count(module_inst, OCRE_RESOURCE_TYPE_GPIO); + LOG_INF("Cleaned up GPIO pin %d", i); + } + } + LOG_INF("Cleaned up GPIO resources for module %p", (void *)module_inst); } -int get_pin_id(int port, int pin) { - // Ensure the pin is valid within the specified port - if (pin < 0 || pin >= CONFIG_OCRE_GPIO_PINS_PER_PORT) { - printf("Invalid pin number\n"); - return -1; // Return -1 for invalid pin number +void ocre_gpio_register_module(wasm_module_inst_t module_inst) { + if (module_inst) { + ocre_register_module(module_inst); + LOG_INF("Registered GPIO module %p", (void *)module_inst); } +} - // Calculate the pin ID - int pin_id = port * CONFIG_OCRE_GPIO_PINS_PER_PORT + pin; - return pin_id; +void ocre_gpio_set_dispatcher(wasm_exec_env_t exec_env) { + wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); + if (module_inst) { + // Dispatcher set in wasm container application + LOG_INF("Set dispatcher for module %p", (void *)module_inst); + } } -// WASM-exposed functions for GPIO control int ocre_gpio_wasm_init(wasm_exec_env_t exec_env) { return ocre_gpio_init(); } -int ocre_gpio_wasm_configure(wasm_exec_env_t exec_env, int port, int P_pin, int direction) { - if (!gpio_system_initialized) { - LOG_ERR("GPIO system not initialized"); - return -ENODEV; +int ocre_gpio_wasm_configure(wasm_exec_env_t exec_env, int port, int pin, int direction) { + wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); + if (!module_inst) { + LOG_ERR("No module instance for GPIO configuration"); + return -EINVAL; } - int pin = get_pin_id(port, P_pin); - ocre_gpio_config_t config; - config.pin = pin; - config.direction = direction; - + if (port >= CONFIG_OCRE_GPIO_MAX_PORTS || pin >= CONFIG_OCRE_GPIO_PINS_PER_PORT || !port_ready[port]) { + LOG_ERR("Invalid port=%d, pin=%d, or port not ready", port, pin); + return -EINVAL; + } + int global_pin = port * CONFIG_OCRE_GPIO_PINS_PER_PORT + pin; + LOG_INF("Configuring GPIO: port=%d, pin=%d, global_pin=%d, direction=%d", port, pin, global_pin, direction); + if (global_pin >= CONFIG_OCRE_GPIO_MAX_PINS) { + LOG_ERR("Global pin %d exceeds max %d", global_pin, CONFIG_OCRE_GPIO_MAX_PINS); + return -EINVAL; + } + ocre_gpio_config_t config = {.pin = pin, .port_idx = port, .direction = direction}; return ocre_gpio_configure(&config); } -int ocre_gpio_wasm_set(wasm_exec_env_t exec_env, int port, int P_pin, int state) { - int pin = get_pin_id(port, P_pin); - return ocre_gpio_pin_set(pin, state ? OCRE_GPIO_PIN_SET : OCRE_GPIO_PIN_RESET); +int ocre_gpio_wasm_set(wasm_exec_env_t exec_env, int port, int pin, int state) { + int global_pin = port * CONFIG_OCRE_GPIO_PINS_PER_PORT + pin; + LOG_INF("Setting GPIO: port=%d, pin=%d, global_pin=%d, state=%d", port, pin, global_pin, state); + if (!port_ready[port]) { + LOG_ERR("Port %d not ready", port); + return -EINVAL; + } + return ocre_gpio_pin_set(global_pin, state ? OCRE_GPIO_PIN_SET : OCRE_GPIO_PIN_RESET); } -int ocre_gpio_wasm_get(wasm_exec_env_t exec_env, int port, int P_pin) { - int pin = get_pin_id(port, P_pin); - return ocre_gpio_pin_get(pin); +int ocre_gpio_wasm_get(wasm_exec_env_t exec_env, int port, int pin) { + int global_pin = port * CONFIG_OCRE_GPIO_PINS_PER_PORT + pin; + LOG_INF("Getting GPIO: port=%d, pin=%d, global_pin=%d", port, pin, global_pin); + if (!port_ready[port]) { + LOG_ERR("Port %d not ready", port); + return -EINVAL; + } + return ocre_gpio_pin_get(global_pin); } -int ocre_gpio_wasm_toggle(wasm_exec_env_t exec_env, int port, int P_pin) { - int pin = get_pin_id(port, P_pin); - return ocre_gpio_pin_toggle(pin); +int ocre_gpio_wasm_toggle(wasm_exec_env_t exec_env, int port, int pin) { + int global_pin = port * CONFIG_OCRE_GPIO_PINS_PER_PORT + pin; + LOG_INF("Toggling GPIO: port=%d, pin=%d, global_pin=%d", port, pin, global_pin); + if (!port_ready[port]) { + LOG_ERR("Port %d not ready", port); + return -EINVAL; + } + return ocre_gpio_pin_toggle(global_pin); } -int ocre_gpio_wasm_register_callback(wasm_exec_env_t exec_env, int port, int P_pin) { - int pin = get_pin_id(port, P_pin); - ocre_gpio_set_dispatcher(exec_env); +int ocre_gpio_wasm_register_callback(wasm_exec_env_t exec_env, int port, int pin) { + int global_pin = port * CONFIG_OCRE_GPIO_PINS_PER_PORT + pin; + LOG_INF("Registering callback: port=%d, pin=%d, global_pin=%d", port, pin, global_pin); + if (global_pin >= CONFIG_OCRE_GPIO_MAX_PINS || !port_ready[port]) { + LOG_ERR("Global pin %d exceeds max %d or port %d not ready", global_pin, CONFIG_OCRE_GPIO_MAX_PINS, port); + return -EINVAL; + } + return ocre_gpio_register_callback(global_pin); +} - // For WASM, we don't need an actual callback function pointer - // as we'll use the dispatcher to call the appropriate WASM function - return ocre_gpio_register_callback(pin, NULL); +int ocre_gpio_wasm_unregister_callback(wasm_exec_env_t exec_env, int port, int pin) { + int global_pin = port * CONFIG_OCRE_GPIO_PINS_PER_PORT + pin; + LOG_INF("Unregistering callback: port=%d, pin=%d, global_pin=%d", port, pin, global_pin); + if (!port_ready[port]) { + LOG_ERR("Port %d not ready", port); + return -EINVAL; + } + return ocre_gpio_unregister_callback(global_pin); } -int ocre_gpio_wasm_unregister_callback(wasm_exec_env_t exec_env, int port, int P_pin) { - int pin = get_pin_id(port, P_pin); - return ocre_gpio_unregister_callback(pin); +static void gpio_callback_handler(const struct device *port, struct gpio_callback *cb, gpio_port_pins_t pins) { + if (!port || !cb) { + LOG_ERR("Null port or callback in GPIO handler"); + return; + } + for (int i = 0; i < CONFIG_OCRE_GPIO_MAX_PINS; i++) { + if (gpio_pins[i].in_use && gpio_pins[i].cb == cb && (pins & BIT(gpio_pins[i].pin_number))) { + int state = gpio_pin_get(port, gpio_pins[i].pin_number); + if (state >= 0) { + wasm_event_t event = {.type = OCRE_RESOURCE_TYPE_GPIO, + .id = i, + .port = gpio_pins[i].port_idx, + .state = (uint32_t)state}; + k_spinlock_key_t key = k_spin_lock(&wasm_event_queue_lock); + if (k_msgq_put(&wasm_event_queue, &event, K_NO_WAIT) != 0) { + LOG_ERR("Failed to queue GPIO event for pin %d", i); + } else { + LOG_INF("Queued GPIO event for pin %d (port=%d, pin=%d), state=%d", i, gpio_pins[i].port_idx, + gpio_pins[i].pin_number, state); + } + k_spin_unlock(&wasm_event_queue_lock, key); + } + } + } } diff --git a/src/ocre/ocre_gpio/ocre_gpio.h b/src/ocre/ocre_gpio/ocre_gpio.h index f722870e..83e660c5 100644 --- a/src/ocre/ocre_gpio/ocre_gpio.h +++ b/src/ocre/ocre_gpio/ocre_gpio.h @@ -45,7 +45,8 @@ typedef enum { * GPIO configuration structure */ typedef struct { - int pin; /**< GPIO pin number (logical) */ + int pin; /**< GPIO pin number (logical) */ + int port_idx; ocre_gpio_direction_t direction; /**< Pin direction */ } ocre_gpio_config_t; @@ -101,7 +102,7 @@ int ocre_gpio_pin_toggle(int pin); * @param callback Callback function * @return 0 on success, negative error code on failure */ -int ocre_gpio_register_callback(int pin, ocre_gpio_callback_t callback); +int ocre_gpio_register_callback(int pin); /** * Unregister GPIO pin callback. @@ -111,6 +112,8 @@ int ocre_gpio_register_callback(int pin, ocre_gpio_callback_t callback); */ int ocre_gpio_unregister_callback(int pin); +void ocre_gpio_register_module(wasm_module_inst_t module_inst); + /** * Clean up GPIO resources for a WASM container. * @@ -118,13 +121,6 @@ int ocre_gpio_unregister_callback(int pin); */ void ocre_gpio_cleanup_container(wasm_module_inst_t module_inst); -/** - * Set the active WASM module instance for GPIO operations. - * - * @param module_inst WASM module instance - */ -void ocre_gpio_set_module_inst(wasm_module_inst_t module_inst); - /** * Set the WASM function that will handle GPIO callbacks. * diff --git a/src/ocre/ocre_input_file.h b/src/ocre/ocre_input_file.h new file mode 100644 index 00000000..74a39052 --- /dev/null +++ b/src/ocre/ocre_input_file.h @@ -0,0 +1,337 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef OCRE_INPUT_FILE_H +#define OCRE_INPUT_FILE_H +// Sample WASM binary data +static unsigned char wasm_binary[] = { + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x35, 0x09, 0x60, + 0x03, 0x7f, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x03, 0x7f, 0x7e, 0x7f, 0x01, + 0x7e, 0x60, 0x01, 0x7f, 0x01, 0x7f, 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, + 0x60, 0x04, 0x7f, 0x7e, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x04, 0x7f, 0x7f, + 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x01, 0x7f, 0x00, 0x60, 0x00, 0x00, 0x60, + 0x00, 0x01, 0x7f, 0x02, 0xb0, 0x01, 0x05, 0x16, 0x77, 0x61, 0x73, 0x69, + 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, + 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x08, 0x66, 0x64, 0x5f, 0x63, 0x6c, + 0x6f, 0x73, 0x65, 0x00, 0x02, 0x16, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, + 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, + 0x69, 0x65, 0x77, 0x31, 0x0d, 0x66, 0x64, 0x5f, 0x66, 0x64, 0x73, 0x74, + 0x61, 0x74, 0x5f, 0x67, 0x65, 0x74, 0x00, 0x03, 0x16, 0x77, 0x61, 0x73, + 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, + 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x07, 0x66, 0x64, 0x5f, 0x73, + 0x65, 0x65, 0x6b, 0x00, 0x04, 0x16, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, + 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, + 0x69, 0x65, 0x77, 0x31, 0x08, 0x66, 0x64, 0x5f, 0x77, 0x72, 0x69, 0x74, + 0x65, 0x00, 0x05, 0x16, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, + 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, + 0x77, 0x31, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x5f, 0x65, 0x78, 0x69, 0x74, + 0x00, 0x06, 0x03, 0x1d, 0x1c, 0x07, 0x07, 0x08, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x07, 0x08, 0x07, 0x02, 0x05, 0x03, 0x03, 0x02, 0x07, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x00, 0x01, 0x01, 0x00, 0x02, 0x04, 0x05, 0x01, + 0x70, 0x01, 0x05, 0x05, 0x05, 0x03, 0x01, 0x00, 0x02, 0x06, 0x0d, 0x02, + 0x7f, 0x01, 0x41, 0xc0, 0x91, 0x04, 0x0b, 0x7f, 0x00, 0x41, 0x00, 0x0b, + 0x07, 0x1a, 0x03, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, + 0x06, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x06, 0x04, 0x6d, 0x61, + 0x69, 0x6e, 0x00, 0x07, 0x09, 0x0a, 0x01, 0x00, 0x41, 0x01, 0x0b, 0x04, + 0x1a, 0x18, 0x1c, 0x1e, 0x0a, 0x93, 0x1a, 0x1c, 0x02, 0x00, 0x0b, 0x51, + 0x01, 0x01, 0x7f, 0x02, 0x40, 0x02, 0x40, 0x23, 0x81, 0x80, 0x80, 0x80, + 0x00, 0x41, 0xa0, 0x89, 0x80, 0x80, 0x00, 0x6a, 0x28, 0x02, 0x00, 0x0d, + 0x00, 0x23, 0x81, 0x80, 0x80, 0x80, 0x00, 0x41, 0xa0, 0x89, 0x80, 0x80, + 0x00, 0x6a, 0x41, 0x01, 0x36, 0x02, 0x00, 0x10, 0x85, 0x80, 0x80, 0x80, + 0x00, 0x10, 0x87, 0x80, 0x80, 0x80, 0x00, 0x21, 0x00, 0x10, 0x8e, 0x80, + 0x80, 0x80, 0x00, 0x20, 0x00, 0x0d, 0x01, 0x0f, 0x0b, 0x00, 0x0b, 0x20, + 0x00, 0x10, 0x8c, 0x80, 0x80, 0x80, 0x00, 0x00, 0x0b, 0x11, 0x00, 0x41, + 0x80, 0x88, 0x80, 0x80, 0x00, 0x10, 0x95, 0x80, 0x80, 0x80, 0x00, 0x1a, + 0x41, 0x00, 0x0b, 0x0f, 0x00, 0x20, 0x00, 0x10, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x41, 0xff, 0xff, 0x03, 0x71, 0x0b, 0x11, 0x00, 0x20, 0x00, 0x20, + 0x01, 0x10, 0x81, 0x80, 0x80, 0x80, 0x00, 0x41, 0xff, 0xff, 0x03, 0x71, + 0x0b, 0x15, 0x00, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x10, + 0x82, 0x80, 0x80, 0x80, 0x00, 0x41, 0xff, 0xff, 0x03, 0x71, 0x0b, 0x15, + 0x00, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x10, 0x83, 0x80, + 0x80, 0x80, 0x00, 0x41, 0xff, 0xff, 0x03, 0x71, 0x0b, 0x0b, 0x00, 0x20, + 0x00, 0x10, 0x84, 0x80, 0x80, 0x80, 0x00, 0x00, 0x0b, 0x02, 0x00, 0x0b, + 0x0e, 0x00, 0x10, 0x8d, 0x80, 0x80, 0x80, 0x00, 0x10, 0x90, 0x80, 0x80, + 0x80, 0x00, 0x0b, 0x08, 0x00, 0x41, 0xa4, 0x89, 0x80, 0x80, 0x00, 0x0b, + 0xa3, 0x03, 0x01, 0x03, 0x7f, 0x02, 0x40, 0x10, 0x8f, 0x80, 0x80, 0x80, + 0x00, 0x28, 0x02, 0x00, 0x22, 0x00, 0x45, 0x0d, 0x00, 0x03, 0x40, 0x02, + 0x40, 0x20, 0x00, 0x28, 0x02, 0x14, 0x20, 0x00, 0x28, 0x02, 0x18, 0x46, + 0x0d, 0x00, 0x20, 0x00, 0x41, 0x00, 0x41, 0x00, 0x20, 0x00, 0x28, 0x02, + 0x20, 0x11, 0x80, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x1a, 0x0b, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x04, 0x22, 0x01, 0x20, + 0x00, 0x28, 0x02, 0x08, 0x22, 0x02, 0x46, 0x0d, 0x00, 0x20, 0x00, 0x20, + 0x01, 0x20, 0x02, 0x6b, 0xac, 0x41, 0x01, 0x20, 0x00, 0x28, 0x02, 0x24, + 0x11, 0x81, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x1a, + 0x0b, 0x20, 0x00, 0x28, 0x02, 0x34, 0x22, 0x00, 0x0d, 0x00, 0x0b, 0x0b, + 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xa8, 0x89, 0x80, 0x80, 0x00, 0x22, + 0x00, 0x45, 0x0d, 0x00, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x14, 0x20, + 0x00, 0x28, 0x02, 0x18, 0x46, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x00, 0x41, + 0x00, 0x20, 0x00, 0x28, 0x02, 0x20, 0x11, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x80, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x00, 0x28, 0x02, 0x04, + 0x22, 0x01, 0x20, 0x00, 0x28, 0x02, 0x08, 0x22, 0x02, 0x46, 0x0d, 0x00, + 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x6b, 0xac, 0x41, 0x01, 0x20, 0x00, + 0x28, 0x02, 0x24, 0x11, 0x81, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, + 0x80, 0x00, 0x1a, 0x0b, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0x90, 0x89, + 0x80, 0x80, 0x00, 0x22, 0x00, 0x45, 0x0d, 0x00, 0x02, 0x40, 0x20, 0x00, + 0x28, 0x02, 0x14, 0x20, 0x00, 0x28, 0x02, 0x18, 0x46, 0x0d, 0x00, 0x20, + 0x00, 0x41, 0x00, 0x41, 0x00, 0x20, 0x00, 0x28, 0x02, 0x20, 0x11, 0x80, + 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, + 0x00, 0x28, 0x02, 0x04, 0x22, 0x01, 0x20, 0x00, 0x28, 0x02, 0x08, 0x22, + 0x02, 0x46, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x6b, 0xac, + 0x41, 0x01, 0x20, 0x00, 0x28, 0x02, 0x24, 0x11, 0x81, 0x80, 0x80, 0x80, + 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x02, 0x40, 0x41, 0x00, + 0x28, 0x02, 0xa8, 0x89, 0x80, 0x80, 0x00, 0x22, 0x00, 0x45, 0x0d, 0x00, + 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x14, 0x20, 0x00, 0x28, 0x02, 0x18, + 0x46, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x00, 0x41, 0x00, 0x20, 0x00, 0x28, + 0x02, 0x20, 0x11, 0x80, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x1a, 0x0b, 0x20, 0x00, 0x28, 0x02, 0x04, 0x22, 0x01, 0x20, 0x00, + 0x28, 0x02, 0x08, 0x22, 0x02, 0x46, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x01, + 0x20, 0x02, 0x6b, 0xac, 0x41, 0x01, 0x20, 0x00, 0x28, 0x02, 0x24, 0x11, + 0x81, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, + 0x0b, 0x5c, 0x01, 0x01, 0x7f, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0x3c, + 0x22, 0x01, 0x41, 0x7f, 0x6a, 0x20, 0x01, 0x72, 0x36, 0x02, 0x3c, 0x02, + 0x40, 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, 0x01, 0x41, 0x08, 0x71, 0x45, + 0x0d, 0x00, 0x20, 0x00, 0x20, 0x01, 0x41, 0x20, 0x72, 0x36, 0x02, 0x00, + 0x41, 0x7f, 0x0f, 0x0b, 0x20, 0x00, 0x42, 0x00, 0x37, 0x02, 0x04, 0x20, + 0x00, 0x20, 0x00, 0x28, 0x02, 0x28, 0x22, 0x01, 0x36, 0x02, 0x18, 0x20, + 0x00, 0x20, 0x01, 0x36, 0x02, 0x14, 0x20, 0x00, 0x20, 0x01, 0x20, 0x00, + 0x28, 0x02, 0x2c, 0x6a, 0x36, 0x02, 0x10, 0x41, 0x00, 0x0b, 0xa8, 0x02, + 0x01, 0x05, 0x7f, 0x20, 0x02, 0x20, 0x01, 0x6c, 0x21, 0x04, 0x02, 0x40, + 0x02, 0x40, 0x20, 0x03, 0x28, 0x02, 0x10, 0x22, 0x05, 0x0d, 0x00, 0x41, + 0x00, 0x21, 0x06, 0x20, 0x03, 0x10, 0x91, 0x80, 0x80, 0x80, 0x00, 0x0d, + 0x01, 0x20, 0x03, 0x28, 0x02, 0x10, 0x21, 0x05, 0x0b, 0x02, 0x40, 0x20, + 0x05, 0x20, 0x03, 0x28, 0x02, 0x14, 0x22, 0x07, 0x6b, 0x20, 0x04, 0x4f, + 0x0d, 0x00, 0x20, 0x03, 0x20, 0x00, 0x20, 0x04, 0x20, 0x03, 0x28, 0x02, + 0x20, 0x11, 0x80, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x21, 0x06, 0x0c, 0x01, 0x0b, 0x41, 0x00, 0x21, 0x08, 0x02, 0x40, 0x02, + 0x40, 0x20, 0x04, 0x0d, 0x00, 0x20, 0x04, 0x21, 0x05, 0x0c, 0x01, 0x0b, + 0x41, 0x00, 0x21, 0x05, 0x02, 0x40, 0x20, 0x03, 0x28, 0x02, 0x40, 0x41, + 0x00, 0x4e, 0x0d, 0x00, 0x20, 0x04, 0x21, 0x05, 0x0c, 0x01, 0x0b, 0x20, + 0x00, 0x20, 0x04, 0x6a, 0x21, 0x06, 0x02, 0x40, 0x03, 0x40, 0x20, 0x06, + 0x20, 0x05, 0x6a, 0x41, 0x7f, 0x6a, 0x2d, 0x00, 0x00, 0x41, 0x0a, 0x46, + 0x0d, 0x01, 0x20, 0x04, 0x20, 0x05, 0x41, 0x7f, 0x6a, 0x22, 0x05, 0x6a, + 0x0d, 0x00, 0x0b, 0x41, 0x00, 0x21, 0x08, 0x20, 0x04, 0x21, 0x05, 0x0c, + 0x01, 0x0b, 0x20, 0x03, 0x20, 0x00, 0x20, 0x04, 0x20, 0x05, 0x6a, 0x22, + 0x08, 0x20, 0x03, 0x28, 0x02, 0x20, 0x11, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x80, 0x80, 0x80, 0x80, 0x00, 0x22, 0x06, 0x20, 0x08, 0x49, 0x0d, 0x01, + 0x20, 0x08, 0x20, 0x00, 0x6a, 0x21, 0x00, 0x41, 0x00, 0x20, 0x05, 0x6b, + 0x21, 0x05, 0x20, 0x03, 0x28, 0x02, 0x14, 0x21, 0x07, 0x0b, 0x20, 0x07, + 0x20, 0x00, 0x20, 0x05, 0x10, 0x9f, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x20, + 0x03, 0x20, 0x03, 0x28, 0x02, 0x14, 0x20, 0x05, 0x6a, 0x36, 0x02, 0x14, + 0x20, 0x08, 0x20, 0x05, 0x6a, 0x21, 0x06, 0x0b, 0x02, 0x40, 0x20, 0x06, + 0x20, 0x04, 0x47, 0x0d, 0x00, 0x20, 0x02, 0x41, 0x00, 0x20, 0x01, 0x1b, + 0x0f, 0x0b, 0x20, 0x06, 0x20, 0x01, 0x6e, 0x0b, 0x24, 0x01, 0x01, 0x7f, + 0x20, 0x00, 0x10, 0xa0, 0x80, 0x80, 0x80, 0x00, 0x21, 0x02, 0x41, 0x7f, + 0x41, 0x00, 0x20, 0x02, 0x20, 0x00, 0x41, 0x01, 0x20, 0x02, 0x20, 0x01, + 0x10, 0x92, 0x80, 0x80, 0x80, 0x00, 0x47, 0x1b, 0x0b, 0xb3, 0x01, 0x01, + 0x03, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x10, 0x6b, 0x22, + 0x02, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x02, 0x20, 0x01, 0x3a, + 0x00, 0x0f, 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x10, 0x22, + 0x03, 0x0d, 0x00, 0x02, 0x40, 0x20, 0x00, 0x10, 0x91, 0x80, 0x80, 0x80, + 0x00, 0x45, 0x0d, 0x00, 0x41, 0x7f, 0x21, 0x03, 0x0c, 0x02, 0x0b, 0x20, + 0x00, 0x28, 0x02, 0x10, 0x21, 0x03, 0x0b, 0x02, 0x40, 0x20, 0x00, 0x28, + 0x02, 0x14, 0x22, 0x04, 0x20, 0x03, 0x46, 0x0d, 0x00, 0x20, 0x00, 0x28, + 0x02, 0x40, 0x20, 0x01, 0x41, 0xff, 0x01, 0x71, 0x22, 0x03, 0x46, 0x0d, + 0x00, 0x20, 0x00, 0x20, 0x04, 0x41, 0x01, 0x6a, 0x36, 0x02, 0x14, 0x20, + 0x04, 0x20, 0x01, 0x3a, 0x00, 0x00, 0x0c, 0x01, 0x0b, 0x02, 0x40, 0x20, + 0x00, 0x20, 0x02, 0x41, 0x0f, 0x6a, 0x41, 0x01, 0x20, 0x00, 0x28, 0x02, + 0x20, 0x11, 0x80, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x41, 0x01, 0x46, 0x0d, 0x00, 0x41, 0x7f, 0x21, 0x03, 0x0c, 0x01, 0x0b, + 0x20, 0x02, 0x2d, 0x00, 0x0f, 0x21, 0x03, 0x0b, 0x20, 0x02, 0x41, 0x10, + 0x6a, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x03, 0x0b, 0x6c, 0x00, + 0x02, 0x40, 0x20, 0x00, 0x41, 0xa0, 0x88, 0x80, 0x80, 0x00, 0x10, 0x93, + 0x80, 0x80, 0x80, 0x00, 0x41, 0x00, 0x4e, 0x0d, 0x00, 0x41, 0x7f, 0x0f, + 0x0b, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xe0, 0x88, 0x80, 0x80, 0x00, + 0x41, 0x0a, 0x46, 0x0d, 0x00, 0x41, 0x00, 0x28, 0x02, 0xb4, 0x88, 0x80, + 0x80, 0x00, 0x22, 0x00, 0x41, 0x00, 0x28, 0x02, 0xb0, 0x88, 0x80, 0x80, + 0x00, 0x46, 0x0d, 0x00, 0x41, 0x00, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x36, + 0x02, 0xb4, 0x88, 0x80, 0x80, 0x00, 0x20, 0x00, 0x41, 0x0a, 0x3a, 0x00, + 0x00, 0x41, 0x00, 0x0f, 0x0b, 0x41, 0xa0, 0x88, 0x80, 0x80, 0x00, 0x41, + 0x0a, 0x10, 0x94, 0x80, 0x80, 0x80, 0x00, 0x41, 0x1f, 0x75, 0x0b, 0x02, + 0x00, 0x0b, 0x27, 0x00, 0x10, 0x96, 0x80, 0x80, 0x80, 0x00, 0x02, 0x40, + 0x20, 0x00, 0x10, 0x88, 0x80, 0x80, 0x80, 0x00, 0x22, 0x00, 0x0d, 0x00, + 0x41, 0x00, 0x0f, 0x0b, 0x41, 0x00, 0x20, 0x00, 0x36, 0x02, 0xac, 0x89, + 0x80, 0x80, 0x00, 0x41, 0x7f, 0x0b, 0x0d, 0x00, 0x20, 0x00, 0x28, 0x02, + 0x38, 0x10, 0x97, 0x80, 0x80, 0x80, 0x00, 0x0b, 0x71, 0x01, 0x02, 0x7f, + 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x03, 0x24, + 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x7f, 0x21, 0x04, 0x02, 0x40, 0x02, + 0x40, 0x20, 0x02, 0x41, 0x7f, 0x4a, 0x0d, 0x00, 0x41, 0x00, 0x41, 0x1c, + 0x36, 0x02, 0xac, 0x89, 0x80, 0x80, 0x00, 0x0c, 0x01, 0x0b, 0x02, 0x40, + 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x41, 0x0c, 0x6a, 0x10, + 0x8b, 0x80, 0x80, 0x80, 0x00, 0x22, 0x02, 0x45, 0x0d, 0x00, 0x41, 0x00, + 0x20, 0x02, 0x36, 0x02, 0xac, 0x89, 0x80, 0x80, 0x00, 0x41, 0x7f, 0x21, + 0x04, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x28, 0x02, 0x0c, 0x21, 0x04, 0x0b, + 0x20, 0x03, 0x41, 0x10, 0x6a, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, + 0x04, 0x0b, 0xbb, 0x02, 0x01, 0x07, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x41, 0x10, 0x6b, 0x22, 0x03, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x20, 0x03, 0x20, 0x02, 0x36, 0x02, 0x0c, 0x20, 0x03, 0x20, 0x01, 0x36, + 0x02, 0x08, 0x20, 0x03, 0x20, 0x00, 0x28, 0x02, 0x18, 0x22, 0x01, 0x36, + 0x02, 0x00, 0x20, 0x03, 0x20, 0x00, 0x28, 0x02, 0x14, 0x20, 0x01, 0x6b, + 0x22, 0x04, 0x36, 0x02, 0x04, 0x41, 0x02, 0x21, 0x05, 0x02, 0x40, 0x02, + 0x40, 0x20, 0x00, 0x28, 0x02, 0x38, 0x20, 0x03, 0x41, 0x02, 0x10, 0x99, + 0x80, 0x80, 0x80, 0x00, 0x22, 0x01, 0x20, 0x04, 0x20, 0x02, 0x6a, 0x22, + 0x06, 0x46, 0x0d, 0x00, 0x20, 0x03, 0x21, 0x04, 0x03, 0x40, 0x02, 0x40, + 0x20, 0x01, 0x41, 0x7f, 0x4a, 0x0d, 0x00, 0x41, 0x00, 0x21, 0x01, 0x20, + 0x00, 0x41, 0x00, 0x36, 0x02, 0x18, 0x20, 0x00, 0x42, 0x00, 0x37, 0x03, + 0x10, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, 0x41, 0x20, 0x72, 0x36, + 0x02, 0x00, 0x20, 0x05, 0x41, 0x02, 0x46, 0x0d, 0x03, 0x20, 0x02, 0x20, + 0x04, 0x28, 0x02, 0x04, 0x6b, 0x21, 0x01, 0x0c, 0x03, 0x0b, 0x20, 0x04, + 0x20, 0x01, 0x20, 0x04, 0x28, 0x02, 0x04, 0x22, 0x07, 0x4b, 0x22, 0x08, + 0x41, 0x03, 0x74, 0x6a, 0x22, 0x09, 0x20, 0x09, 0x28, 0x02, 0x00, 0x20, + 0x01, 0x20, 0x07, 0x41, 0x00, 0x20, 0x08, 0x1b, 0x6b, 0x22, 0x07, 0x6a, + 0x36, 0x02, 0x00, 0x20, 0x04, 0x41, 0x0c, 0x41, 0x04, 0x20, 0x08, 0x1b, + 0x6a, 0x22, 0x04, 0x20, 0x04, 0x28, 0x02, 0x00, 0x20, 0x07, 0x6b, 0x36, + 0x02, 0x00, 0x20, 0x09, 0x21, 0x04, 0x20, 0x06, 0x20, 0x01, 0x6b, 0x22, + 0x06, 0x20, 0x00, 0x28, 0x02, 0x38, 0x20, 0x09, 0x20, 0x05, 0x20, 0x08, + 0x6b, 0x22, 0x05, 0x10, 0x99, 0x80, 0x80, 0x80, 0x00, 0x22, 0x01, 0x47, + 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0x28, 0x22, + 0x01, 0x36, 0x02, 0x18, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x14, 0x20, + 0x00, 0x20, 0x01, 0x20, 0x00, 0x28, 0x02, 0x2c, 0x6a, 0x36, 0x02, 0x10, + 0x20, 0x02, 0x21, 0x01, 0x0b, 0x20, 0x03, 0x41, 0x10, 0x6a, 0x24, 0x80, + 0x80, 0x80, 0x80, 0x00, 0x20, 0x01, 0x0b, 0x66, 0x01, 0x02, 0x7f, 0x23, + 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x20, 0x6b, 0x22, 0x01, 0x24, 0x80, + 0x80, 0x80, 0x80, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x20, 0x01, + 0x41, 0x08, 0x6a, 0x10, 0x89, 0x80, 0x80, 0x80, 0x00, 0x22, 0x00, 0x0d, + 0x00, 0x41, 0x3b, 0x21, 0x00, 0x20, 0x01, 0x2d, 0x00, 0x08, 0x41, 0x02, + 0x47, 0x0d, 0x00, 0x20, 0x01, 0x2d, 0x00, 0x10, 0x41, 0x24, 0x71, 0x0d, + 0x00, 0x41, 0x01, 0x21, 0x02, 0x0c, 0x01, 0x0b, 0x41, 0x00, 0x21, 0x02, + 0x41, 0x00, 0x20, 0x00, 0x36, 0x02, 0xac, 0x89, 0x80, 0x80, 0x00, 0x0b, + 0x20, 0x01, 0x41, 0x20, 0x6a, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, + 0x02, 0x0b, 0x3b, 0x00, 0x20, 0x00, 0x41, 0x81, 0x80, 0x80, 0x80, 0x00, + 0x36, 0x02, 0x20, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0xc0, + 0x00, 0x71, 0x0d, 0x00, 0x20, 0x00, 0x28, 0x02, 0x38, 0x10, 0x9b, 0x80, + 0x80, 0x80, 0x00, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x7f, 0x36, 0x02, 0x40, + 0x0b, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x10, 0x9a, 0x80, 0x80, 0x80, + 0x00, 0x0b, 0x64, 0x01, 0x01, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x41, 0x10, 0x6b, 0x22, 0x03, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x02, + 0x40, 0x02, 0x40, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x41, 0xff, 0x01, + 0x71, 0x20, 0x03, 0x41, 0x08, 0x6a, 0x10, 0x8a, 0x80, 0x80, 0x80, 0x00, + 0x22, 0x02, 0x45, 0x0d, 0x00, 0x41, 0x00, 0x41, 0xc6, 0x00, 0x20, 0x02, + 0x20, 0x02, 0x41, 0xcc, 0x00, 0x46, 0x1b, 0x36, 0x02, 0xac, 0x89, 0x80, + 0x80, 0x00, 0x42, 0x7f, 0x21, 0x01, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x29, + 0x03, 0x08, 0x21, 0x01, 0x0b, 0x20, 0x03, 0x41, 0x10, 0x6a, 0x24, 0x80, + 0x80, 0x80, 0x80, 0x00, 0x20, 0x01, 0x0b, 0x11, 0x00, 0x20, 0x00, 0x28, + 0x02, 0x38, 0x20, 0x01, 0x20, 0x02, 0x10, 0x9d, 0x80, 0x80, 0x80, 0x00, + 0x0b, 0xee, 0x07, 0x01, 0x04, 0x7f, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, + 0x20, 0x02, 0x41, 0x20, 0x4b, 0x0d, 0x00, 0x20, 0x01, 0x41, 0x03, 0x71, + 0x45, 0x0d, 0x01, 0x20, 0x02, 0x45, 0x0d, 0x01, 0x20, 0x00, 0x20, 0x01, + 0x2d, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x20, 0x02, 0x41, 0x7f, 0x6a, 0x21, + 0x03, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x21, 0x04, 0x20, 0x01, 0x41, 0x01, + 0x6a, 0x22, 0x05, 0x41, 0x03, 0x71, 0x45, 0x0d, 0x02, 0x20, 0x03, 0x45, + 0x0d, 0x02, 0x20, 0x00, 0x20, 0x01, 0x2d, 0x00, 0x01, 0x3a, 0x00, 0x01, + 0x20, 0x02, 0x41, 0x7e, 0x6a, 0x21, 0x03, 0x20, 0x00, 0x41, 0x02, 0x6a, + 0x21, 0x04, 0x20, 0x01, 0x41, 0x02, 0x6a, 0x22, 0x05, 0x41, 0x03, 0x71, + 0x45, 0x0d, 0x02, 0x20, 0x03, 0x45, 0x0d, 0x02, 0x20, 0x00, 0x20, 0x01, + 0x2d, 0x00, 0x02, 0x3a, 0x00, 0x02, 0x20, 0x02, 0x41, 0x7d, 0x6a, 0x21, + 0x03, 0x20, 0x00, 0x41, 0x03, 0x6a, 0x21, 0x04, 0x20, 0x01, 0x41, 0x03, + 0x6a, 0x22, 0x05, 0x41, 0x03, 0x71, 0x45, 0x0d, 0x02, 0x20, 0x03, 0x45, + 0x0d, 0x02, 0x20, 0x00, 0x20, 0x01, 0x2d, 0x00, 0x03, 0x3a, 0x00, 0x03, + 0x20, 0x02, 0x41, 0x7c, 0x6a, 0x21, 0x03, 0x20, 0x00, 0x41, 0x04, 0x6a, + 0x21, 0x04, 0x20, 0x01, 0x41, 0x04, 0x6a, 0x21, 0x05, 0x0c, 0x02, 0x0b, + 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0xfc, 0x0a, 0x00, 0x00, 0x20, 0x00, + 0x0f, 0x0b, 0x20, 0x02, 0x21, 0x03, 0x20, 0x00, 0x21, 0x04, 0x20, 0x01, + 0x21, 0x05, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x20, 0x04, 0x41, 0x03, 0x71, + 0x22, 0x02, 0x0d, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x03, 0x41, 0x10, + 0x4f, 0x0d, 0x00, 0x20, 0x03, 0x21, 0x02, 0x0c, 0x01, 0x0b, 0x02, 0x40, + 0x20, 0x03, 0x41, 0x70, 0x6a, 0x22, 0x02, 0x41, 0x10, 0x71, 0x0d, 0x00, + 0x20, 0x04, 0x20, 0x05, 0x29, 0x02, 0x00, 0x37, 0x02, 0x00, 0x20, 0x04, + 0x20, 0x05, 0x29, 0x02, 0x08, 0x37, 0x02, 0x08, 0x20, 0x04, 0x41, 0x10, + 0x6a, 0x21, 0x04, 0x20, 0x05, 0x41, 0x10, 0x6a, 0x21, 0x05, 0x20, 0x02, + 0x21, 0x03, 0x0b, 0x20, 0x02, 0x41, 0x10, 0x49, 0x0d, 0x00, 0x20, 0x03, + 0x21, 0x02, 0x03, 0x40, 0x20, 0x04, 0x20, 0x05, 0x29, 0x02, 0x00, 0x37, + 0x02, 0x00, 0x20, 0x04, 0x20, 0x05, 0x29, 0x02, 0x08, 0x37, 0x02, 0x08, + 0x20, 0x04, 0x20, 0x05, 0x29, 0x02, 0x10, 0x37, 0x02, 0x10, 0x20, 0x04, + 0x20, 0x05, 0x29, 0x02, 0x18, 0x37, 0x02, 0x18, 0x20, 0x04, 0x41, 0x20, + 0x6a, 0x21, 0x04, 0x20, 0x05, 0x41, 0x20, 0x6a, 0x21, 0x05, 0x20, 0x02, + 0x41, 0x60, 0x6a, 0x22, 0x02, 0x41, 0x0f, 0x4b, 0x0d, 0x00, 0x0b, 0x0b, + 0x02, 0x40, 0x20, 0x02, 0x41, 0x08, 0x49, 0x0d, 0x00, 0x20, 0x04, 0x20, + 0x05, 0x29, 0x02, 0x00, 0x37, 0x02, 0x00, 0x20, 0x05, 0x41, 0x08, 0x6a, + 0x21, 0x05, 0x20, 0x04, 0x41, 0x08, 0x6a, 0x21, 0x04, 0x0b, 0x02, 0x40, + 0x20, 0x02, 0x41, 0x04, 0x71, 0x45, 0x0d, 0x00, 0x20, 0x04, 0x20, 0x05, + 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, 0x05, 0x41, 0x04, 0x6a, 0x21, + 0x05, 0x20, 0x04, 0x41, 0x04, 0x6a, 0x21, 0x04, 0x0b, 0x02, 0x40, 0x20, + 0x02, 0x41, 0x02, 0x71, 0x45, 0x0d, 0x00, 0x20, 0x04, 0x20, 0x05, 0x2f, + 0x00, 0x00, 0x3b, 0x00, 0x00, 0x20, 0x04, 0x41, 0x02, 0x6a, 0x21, 0x04, + 0x20, 0x05, 0x41, 0x02, 0x6a, 0x21, 0x05, 0x0b, 0x20, 0x02, 0x41, 0x01, + 0x71, 0x45, 0x0d, 0x01, 0x20, 0x04, 0x20, 0x05, 0x2d, 0x00, 0x00, 0x3a, + 0x00, 0x00, 0x20, 0x00, 0x0f, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, + 0x02, 0x40, 0x02, 0x40, 0x20, 0x03, 0x41, 0x20, 0x49, 0x0d, 0x00, 0x20, + 0x04, 0x20, 0x05, 0x28, 0x02, 0x00, 0x22, 0x03, 0x3a, 0x00, 0x00, 0x02, + 0x40, 0x02, 0x40, 0x20, 0x02, 0x41, 0x7f, 0x6a, 0x0e, 0x03, 0x03, 0x00, + 0x01, 0x03, 0x0b, 0x20, 0x04, 0x20, 0x03, 0x41, 0x08, 0x76, 0x3a, 0x00, + 0x01, 0x20, 0x04, 0x20, 0x05, 0x41, 0x06, 0x6a, 0x29, 0x01, 0x00, 0x37, + 0x02, 0x06, 0x20, 0x04, 0x20, 0x05, 0x28, 0x02, 0x04, 0x41, 0x10, 0x74, + 0x20, 0x03, 0x41, 0x10, 0x76, 0x72, 0x36, 0x02, 0x02, 0x20, 0x04, 0x41, + 0x12, 0x6a, 0x21, 0x02, 0x20, 0x05, 0x41, 0x12, 0x6a, 0x21, 0x01, 0x41, + 0x0e, 0x21, 0x06, 0x20, 0x05, 0x41, 0x0e, 0x6a, 0x28, 0x01, 0x00, 0x21, + 0x05, 0x41, 0x0e, 0x21, 0x03, 0x0c, 0x03, 0x0b, 0x20, 0x04, 0x20, 0x05, + 0x41, 0x05, 0x6a, 0x29, 0x00, 0x00, 0x37, 0x02, 0x05, 0x20, 0x04, 0x20, + 0x05, 0x28, 0x02, 0x04, 0x41, 0x18, 0x74, 0x20, 0x03, 0x41, 0x08, 0x76, + 0x72, 0x36, 0x02, 0x01, 0x20, 0x04, 0x41, 0x11, 0x6a, 0x21, 0x02, 0x20, + 0x05, 0x41, 0x11, 0x6a, 0x21, 0x01, 0x41, 0x0d, 0x21, 0x06, 0x20, 0x05, + 0x41, 0x0d, 0x6a, 0x28, 0x00, 0x00, 0x21, 0x05, 0x41, 0x0f, 0x21, 0x03, + 0x0c, 0x02, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x20, 0x03, 0x41, 0x10, 0x4f, + 0x0d, 0x00, 0x20, 0x04, 0x21, 0x02, 0x20, 0x05, 0x21, 0x01, 0x0c, 0x01, + 0x0b, 0x20, 0x04, 0x20, 0x05, 0x2d, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x20, + 0x04, 0x20, 0x05, 0x28, 0x00, 0x01, 0x36, 0x00, 0x01, 0x20, 0x04, 0x20, + 0x05, 0x29, 0x00, 0x05, 0x37, 0x00, 0x05, 0x20, 0x04, 0x20, 0x05, 0x2f, + 0x00, 0x0d, 0x3b, 0x00, 0x0d, 0x20, 0x04, 0x20, 0x05, 0x2d, 0x00, 0x0f, + 0x3a, 0x00, 0x0f, 0x20, 0x04, 0x41, 0x10, 0x6a, 0x21, 0x02, 0x20, 0x05, + 0x41, 0x10, 0x6a, 0x21, 0x01, 0x0b, 0x20, 0x03, 0x41, 0x08, 0x71, 0x0d, + 0x02, 0x0c, 0x03, 0x0b, 0x20, 0x04, 0x20, 0x03, 0x41, 0x10, 0x76, 0x3a, + 0x00, 0x02, 0x20, 0x04, 0x20, 0x03, 0x41, 0x08, 0x76, 0x3a, 0x00, 0x01, + 0x20, 0x04, 0x20, 0x05, 0x41, 0x07, 0x6a, 0x29, 0x00, 0x00, 0x37, 0x02, + 0x07, 0x20, 0x04, 0x20, 0x05, 0x28, 0x02, 0x04, 0x41, 0x08, 0x74, 0x20, + 0x03, 0x41, 0x18, 0x76, 0x72, 0x36, 0x02, 0x03, 0x20, 0x04, 0x41, 0x13, + 0x6a, 0x21, 0x02, 0x20, 0x05, 0x41, 0x13, 0x6a, 0x21, 0x01, 0x41, 0x0f, + 0x21, 0x06, 0x20, 0x05, 0x41, 0x0f, 0x6a, 0x28, 0x00, 0x00, 0x21, 0x05, + 0x41, 0x0d, 0x21, 0x03, 0x0b, 0x20, 0x04, 0x20, 0x06, 0x6a, 0x20, 0x05, + 0x36, 0x02, 0x00, 0x0b, 0x20, 0x02, 0x20, 0x01, 0x29, 0x00, 0x00, 0x37, + 0x00, 0x00, 0x20, 0x02, 0x41, 0x08, 0x6a, 0x21, 0x02, 0x20, 0x01, 0x41, + 0x08, 0x6a, 0x21, 0x01, 0x0b, 0x02, 0x40, 0x20, 0x03, 0x41, 0x04, 0x71, + 0x45, 0x0d, 0x00, 0x20, 0x02, 0x20, 0x01, 0x28, 0x00, 0x00, 0x36, 0x00, + 0x00, 0x20, 0x02, 0x41, 0x04, 0x6a, 0x21, 0x02, 0x20, 0x01, 0x41, 0x04, + 0x6a, 0x21, 0x01, 0x0b, 0x02, 0x40, 0x20, 0x03, 0x41, 0x02, 0x71, 0x45, + 0x0d, 0x00, 0x20, 0x02, 0x20, 0x01, 0x2f, 0x00, 0x00, 0x3b, 0x00, 0x00, + 0x20, 0x02, 0x41, 0x02, 0x6a, 0x21, 0x02, 0x20, 0x01, 0x41, 0x02, 0x6a, + 0x21, 0x01, 0x0b, 0x20, 0x03, 0x41, 0x01, 0x71, 0x45, 0x0d, 0x00, 0x20, + 0x02, 0x20, 0x01, 0x2d, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x0b, 0x20, 0x00, + 0x0b, 0xcf, 0x01, 0x01, 0x03, 0x7f, 0x20, 0x00, 0x21, 0x01, 0x02, 0x40, + 0x02, 0x40, 0x20, 0x00, 0x41, 0x03, 0x71, 0x45, 0x0d, 0x00, 0x02, 0x40, + 0x20, 0x00, 0x2d, 0x00, 0x00, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x00, 0x6b, + 0x0f, 0x0b, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x22, 0x01, 0x41, 0x03, 0x71, + 0x45, 0x0d, 0x00, 0x20, 0x01, 0x2d, 0x00, 0x00, 0x45, 0x0d, 0x01, 0x20, + 0x00, 0x41, 0x02, 0x6a, 0x22, 0x01, 0x41, 0x03, 0x71, 0x45, 0x0d, 0x00, + 0x20, 0x01, 0x2d, 0x00, 0x00, 0x45, 0x0d, 0x01, 0x20, 0x00, 0x41, 0x03, + 0x6a, 0x22, 0x01, 0x41, 0x03, 0x71, 0x45, 0x0d, 0x00, 0x20, 0x01, 0x2d, + 0x00, 0x00, 0x45, 0x0d, 0x01, 0x20, 0x00, 0x41, 0x04, 0x6a, 0x22, 0x01, + 0x41, 0x03, 0x71, 0x0d, 0x01, 0x0b, 0x20, 0x01, 0x41, 0x7c, 0x6a, 0x21, + 0x02, 0x20, 0x01, 0x41, 0x7b, 0x6a, 0x21, 0x01, 0x03, 0x40, 0x20, 0x01, + 0x41, 0x04, 0x6a, 0x21, 0x01, 0x41, 0x80, 0x82, 0x84, 0x08, 0x20, 0x02, + 0x41, 0x04, 0x6a, 0x22, 0x02, 0x28, 0x02, 0x00, 0x22, 0x03, 0x6b, 0x20, + 0x03, 0x72, 0x41, 0x80, 0x81, 0x82, 0x84, 0x78, 0x71, 0x41, 0x80, 0x81, + 0x82, 0x84, 0x78, 0x46, 0x0d, 0x00, 0x0b, 0x03, 0x40, 0x20, 0x01, 0x41, + 0x01, 0x6a, 0x21, 0x01, 0x20, 0x02, 0x2d, 0x00, 0x00, 0x21, 0x03, 0x20, + 0x02, 0x41, 0x01, 0x6a, 0x21, 0x02, 0x20, 0x03, 0x0d, 0x00, 0x0b, 0x0b, + 0x20, 0x01, 0x20, 0x00, 0x6b, 0x0b, 0x0b, 0x9d, 0x01, 0x02, 0x00, 0x41, + 0x80, 0x08, 0x0b, 0x1c, 0x0a, 0x0a, 0x09, 0x48, 0x65, 0x6c, 0x6c, 0x6f, + 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, + 0x4f, 0x63, 0x72, 0x65, 0x21, 0x0a, 0x0a, 0x00, 0x00, 0x41, 0xa0, 0x08, + 0x0b, 0x74, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xb8, 0x04, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00 + }; + +static unsigned int wasm_binary_len = 3850; + +#endif // OCRE_INPUT_FILE_H diff --git a/src/ocre/ocre_sensors/ocre_sensors.c b/src/ocre/ocre_sensors/ocre_sensors.c index 43549a86..7abf64e0 100644 --- a/src/ocre/ocre_sensors/ocre_sensors.c +++ b/src/ocre/ocre_sensors/ocre_sensors.c @@ -15,6 +15,8 @@ LOG_MODULE_DECLARE(ocre_sensors, OCRE_LOG_LEVEL); #define DEVICE_NODE DT_PATH(devices) #define HAS_DEVICE_NODES DT_NODE_EXISTS(DEVICE_NODE) && DT_PROP_LEN(DEVICE_NODE, device_list) > 1 + +/* Define sensor discovery names from device tree if available */ #if (CONFIG_OCRE_SENSORS) && (HAS_DEVICE_NODES) #define EXTRACT_LABELS(node_id, prop, idx) DT_PROP_BY_PHANDLE_IDX_OR(node_id, prop, idx, label, "undefined") static const char *sensor_discovery_names[] = { @@ -38,7 +40,7 @@ static char sensor_names[CONFIG_MAX_SENSORS][CONFIG_MAX_SENSOR_NAME_LENGTH]; static int set_opened_channels(const struct device *dev, sensor_channel_t *channels) { if (!channels) { LOG_ERR("Channels array is NULL"); - return -1; + return -EINVAL; } int count = 0; @@ -63,28 +65,32 @@ int ocre_sensors_init(wasm_exec_env_t exec_env) { int ocre_sensors_open(wasm_exec_env_t exec_env, ocre_sensor_handle_t handle) { if (handle < 0 || handle >= sensor_count || !sensors[handle].in_use) { LOG_ERR("Invalid sensor handle: %d", handle); - return -1; + return -EINVAL; } if (!device_is_ready(sensors[handle].device)) { LOG_ERR("Device %s is not ready", sensors[handle].info.sensor_name); - return -2; + return -ENODEV; } return 0; } int ocre_sensors_discover(wasm_exec_env_t exec_env) { + memset(sensors, 0, sizeof(sensors)); + memset(sensor_names, 0, sizeof(sensor_names)); + sensor_count = 0; + const struct device *dev = NULL; size_t device_count = z_device_get_all_static(&dev); if (!dev) { LOG_ERR("Device list is NULL. Possible memory corruption!"); - return -1; + return -EINVAL; } if (device_count == 0) { LOG_ERR("No static devices found"); - return -1; + return -ENODEV; } LOG_INF("Total static devices found: %zu", device_count); @@ -97,7 +103,6 @@ int ocre_sensors_discover(wasm_exec_env_t exec_env) { LOG_INF("Checking device: %s", dev[i].name); - // Check if device name is in the sensor discovery list bool sensor_found = false; for (int j = 0; j < sensor_names_count; j++) { if (strcmp(dev[i].name, sensor_discovery_names[j]) == 0) { @@ -110,24 +115,17 @@ int ocre_sensors_discover(wasm_exec_env_t exec_env) { continue; } - // if (!device_is_ready(&dev[i])) { - // LOG_WRN("Device %s is not ready, skipping", dev[i].name); - // continue; - // } - const struct sensor_driver_api *api = (const struct sensor_driver_api *)dev[i].api; if (!api || !api->channel_get) { LOG_WRN("Device %s does not support sensor API or channel_get, skipping", dev[i].name); continue; } - // Ensure we don't exceed sensor limit if (sensor_count >= CONFIG_MAX_SENSORS) { LOG_WRN("Max sensor limit reached, skipping device: %s", dev[i].name); continue; } - // Initialize the sensor ocre_sensor_internal_t *sensor = &sensors[sensor_count]; sensor->device = &dev[i]; sensor->in_use = true; @@ -137,7 +135,6 @@ int ocre_sensors_discover(wasm_exec_env_t exec_env) { sensor_names[sensor_count][CONFIG_MAX_SENSOR_NAME_LENGTH - 1] = '\0'; sensor->info.sensor_name = sensor_names[sensor_count]; - // Get supported channels sensor->info.num_channels = set_opened_channels(&dev[i], sensor->info.channels); if (sensor->info.num_channels <= 0) { LOG_WRN("Device %s does not have opened channels, skipping", dev[i].name); @@ -154,19 +151,19 @@ int ocre_sensors_discover(wasm_exec_env_t exec_env) { int ocre_sensors_get_handle(wasm_exec_env_t exec_env, int sensor_id) { if (sensor_id < 0 || sensor_id >= sensor_count || !sensors[sensor_id].in_use) { - return -1; + return -EINVAL; } return sensors[sensor_id].info.handle; } int ocre_sensors_get_name(wasm_exec_env_t exec_env, int sensor_id, char *buffer, int buffer_size) { if (sensor_id < 0 || sensor_id >= sensor_count || !sensors[sensor_id].in_use || !buffer) { - return -1; + return -EINVAL; } int name_len = strlen(sensors[sensor_id].info.sensor_name); if (name_len >= buffer_size) { - return -2; + return -ENOSPC; } strncpy(buffer, sensors[sensor_id].info.sensor_name, buffer_size - 1); @@ -176,7 +173,7 @@ int ocre_sensors_get_name(wasm_exec_env_t exec_env, int sensor_id, char *buffer, int ocre_sensors_get_channel_count(wasm_exec_env_t exec_env, int sensor_id) { if (sensor_id < 0 || sensor_id >= sensor_count || !sensors[sensor_id].in_use) { - return -1; + return -EINVAL; } return sensors[sensor_id].info.num_channels; } @@ -184,26 +181,103 @@ int ocre_sensors_get_channel_count(wasm_exec_env_t exec_env, int sensor_id) { int ocre_sensors_get_channel_type(wasm_exec_env_t exec_env, int sensor_id, int channel_index) { if (sensor_id < 0 || sensor_id >= sensor_count || !sensors[sensor_id].in_use || channel_index < 0 || channel_index >= sensors[sensor_id].info.num_channels) { - return -1; + return -EINVAL; } return sensors[sensor_id].info.channels[channel_index]; } int ocre_sensors_read(wasm_exec_env_t exec_env, int sensor_id, int channel_type) { if (sensor_id < 0 || sensor_id >= sensor_count || !sensors[sensor_id].in_use) { - return -1; + return -EINVAL; } const struct device *dev = sensors[sensor_id].device; struct sensor_value value = {}; if (sensor_sample_fetch(dev) < 0) { - return -1; + return -EIO; } if (sensor_channel_get(dev, channel_type, &value) < 0) { - return -1; + return -EIO; } return value.val1 * 1000 + value.val2 / 1000; +} + +static int find_sensor_by_name(const char *name) { + if (!name) { + return -EINVAL; + } + + for (int i = 0; i < sensor_count; i++) { + if (sensors[i].in_use && strcmp(sensors[i].info.sensor_name, name) == 0) { + return i; + } + } + + return -ENOENT; +} + +int ocre_sensors_open_by_name(wasm_exec_env_t exec_env, const char *sensor_name) { + int sensor_id = find_sensor_by_name(sensor_name); + if (sensor_id < 0) { + LOG_ERR("Sensor not found: %s", sensor_name); + return -ENOENT; + } + + return ocre_sensors_open(exec_env, sensor_id); +} + +int ocre_sensors_get_handle_by_name(wasm_exec_env_t exec_env, const char *sensor_name) { + int sensor_id = find_sensor_by_name(sensor_name); + if (sensor_id < 0) { + return -ENOENT; + } + + return sensors[sensor_id].info.handle; +} + +int ocre_sensors_get_channel_count_by_name(wasm_exec_env_t exec_env, const char *sensor_name) { + int sensor_id = find_sensor_by_name(sensor_name); + if (sensor_id < 0) { + return -ENOENT; + } + + return sensors[sensor_id].info.num_channels; +} + +int ocre_sensors_get_channel_type_by_name(wasm_exec_env_t exec_env, const char *sensor_name, int channel_index) { + int sensor_id = find_sensor_by_name(sensor_name); + if (sensor_id < 0) { + return -ENOENT; + } + + return ocre_sensors_get_channel_type(exec_env, sensor_id, channel_index); +} + +int ocre_sensors_read_by_name(wasm_exec_env_t exec_env, const char *sensor_name, int channel_type) { + int sensor_id = find_sensor_by_name(sensor_name); + if (sensor_id < 0) { + LOG_ERR("Sensor not found: %s", sensor_name); + return -ENOENT; + } + + return ocre_sensors_read(exec_env, sensor_id, channel_type); +} + +int ocre_sensors_get_list(wasm_exec_env_t exec_env, char **name_list, int max_names) { + if (!name_list || max_names <= 0) { + return -EINVAL; + } + + int count = 0; + for (int i = 0; i < sensor_count && count < max_names; i++) { + if (sensors[i].in_use) { + name_list[count] = (char *)sensors[i].info.sensor_name; + count++; + } + } + + return count; } \ No newline at end of file diff --git a/src/ocre/ocre_sensors/ocre_sensors.h b/src/ocre/ocre_sensors/ocre_sensors.h index 0b3ebca8..02ffd7b5 100644 --- a/src/ocre/ocre_sensors/ocre_sensors.h +++ b/src/ocre/ocre_sensors/ocre_sensors.h @@ -96,4 +96,14 @@ int ocre_sensors_get_channel_type(wasm_exec_env_t exec_env, int sensor_id, int c */ int ocre_sensors_read(wasm_exec_env_t exec_env, int sensor_id, int channel_type); +/* New string-based APIs */ +int ocre_sensors_open_by_name(wasm_exec_env_t exec_env, const char *sensor_name); +int ocre_sensors_get_handle_by_name(wasm_exec_env_t exec_env, const char *sensor_name); +int ocre_sensors_get_channel_count_by_name(wasm_exec_env_t exec_env, const char *sensor_name); +int ocre_sensors_get_channel_type_by_name(wasm_exec_env_t exec_env, const char *sensor_name, int channel_index); +int ocre_sensors_read_by_name(wasm_exec_env_t exec_env, const char *sensor_name, int channel_type); + +/* Utility functions */ +int ocre_sensors_get_list(wasm_exec_env_t exec_env, char **name_list, int max_names); + #endif /* OCRE_SENSORS_H */ diff --git a/src/ocre/ocre_timers/ocre_timer.c b/src/ocre/ocre_timers/ocre_timer.c index b18de5ea..5becc163 100644 --- a/src/ocre/ocre_timers/ocre_timer.c +++ b/src/ocre/ocre_timers/ocre_timer.c @@ -1,251 +1,226 @@ /** * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC + * which has been established as Project Ocre, a Series of LF Projects, LLC * * SPDX-License-Identifier: Apache-2.0 */ #include -#include "ocre_timer.h" -#include "wasm_export.h" -#include +#include +#include + +#include #include +#include +#include + LOG_MODULE_DECLARE(ocre_cs_component, OCRE_LOG_LEVEL); + #include #include #include -#define TIMER_STACK_SIZE 2048 -#define TIMER_THREAD_PRIORITY 5 -#define WASM_STACK_SIZE (8 * 1024) - +// Compact timer structure typedef struct { - struct k_timer timer; - bool in_use; - uint32_t id; -} k_timer_ocre; - -static K_THREAD_STACK_DEFINE(timer_thread_stack, TIMER_STACK_SIZE); -static struct k_thread timer_thread; -static k_tid_t timer_thread_id; - -K_MSGQ_DEFINE(timer_msgq, sizeof(uint32_t), 10, 4); - -// Timer management -static k_timer_ocre timers[CONFIG_MAX_TIMERS] = {0}; -static wasm_function_inst_t timer_dispatcher_func = NULL; + uint32_t in_use: 1; + uint32_t id: 8; // Up to 256 timers + uint32_t interval: 16; // Up to 65s intervals + uint32_t periodic: 1; + struct k_timer *timer; // Pointer to shared timer + wasm_module_inst_t owner; +} ocre_timer; + +#ifndef CONFIG_MAX_TIMER +#define CONFIG_MAX_TIMERS 5 +#endif + +// Static data +static ocre_timer timers[CONFIG_MAX_TIMERS]; +static struct k_timer shared_timer; static bool timer_system_initialized = false; -static wasm_module_inst_t current_module_inst = NULL; -static wasm_exec_env_t shared_exec_env = NULL; +extern struct k_msgq wasm_event_queue; // Defined in ocre_common.c +extern bool wasm_event_queue_initialized; // Defined in ocre_common.c +extern struct k_spinlock wasm_event_queue_lock; // Defined in ocre_common.c +extern char *wasm_event_queue_buffer_ptr; // Defined in ocre_common.c -void ocre_timer_cleanup_container(wasm_module_inst_t module_inst) { - if (!timer_system_initialized || !module_inst) { - LOG_ERR("Timer system not properly initialized"); - return; - } +static void timer_callback_wrapper(struct k_timer *timer); - // Only clean up timers if they belong to the specified module instance - if (module_inst != current_module_inst) { - LOG_WRN("Cleanup requested for non-active module instance"); +void ocre_timer_init(void) { + if (timer_system_initialized) { + LOG_INF("Timer system already initialized"); return; } - int cleaned_count = 0; - for (int i = 0; i < CONFIG_MAX_TIMERS; i++) { - if (timers[i].in_use) { - k_timer_stop(&timers[i].timer); - timers[i].in_use = false; - timers[i].id = 0; - cleaned_count++; - } - } - - LOG_INF("Cleaned up %d timers for container", cleaned_count); -} - -// Thread function to process timer callbacks -static void timer_thread_fn(void *arg1, void *arg2, void *arg3) { - uint32_t timer_id; - - while (1) { - // Wait for a timer message - if (k_msgq_get(&timer_msgq, &timer_id, K_FOREVER) == 0) { - if (!timer_dispatcher_func || !current_module_inst || !shared_exec_env) { - LOG_ERR("Timer system not properly initialized"); - continue; - } - - LOG_DBG("Processing timer ID: %d", timer_id); - uint32_t args[1] = {timer_id}; - - // Execute the WASM callback - bool call_success = wasm_runtime_call_wasm(shared_exec_env, timer_dispatcher_func, 1, args); - - if (!call_success) { - const char *error = wasm_runtime_get_exception(current_module_inst); - LOG_ERR("Failed to call WASM function: %s", error ? error : "Unknown error"); - } else { - LOG_INF("Successfully called WASM function for timer %d", timer_id); - } - } - } -} - -static void wasm_timer_callback(struct k_timer *timer) { - for (int i = 0; i < CONFIG_MAX_TIMERS; i++) { - if (&timers[i].timer == timer && timers[i].in_use) { - // Send timer ID to the processing thread - if (k_msgq_put(&timer_msgq, &timers[i].id, K_NO_WAIT) != 0) { - LOG_ERR("Failed to queue timer callback for ID: %d", timers[i].id); - } - break; - } - } -} - -void ocre_timer_set_module_inst(wasm_module_inst_t module_inst) { - current_module_inst = module_inst; - if (shared_exec_env) { - wasm_runtime_destroy_exec_env(shared_exec_env); - } - shared_exec_env = wasm_runtime_create_exec_env(module_inst, WASM_STACK_SIZE); -} - -static k_timer_ocre *get_timer_from_id(ocre_timer_t id) { - if (id == 0 || id > CONFIG_MAX_TIMERS) { - return NULL; + if (!common_initialized && ocre_common_init() != 0) { + LOG_ERR("Failed to initialize common subsystem"); + return; } - return &timers[id - 1]; -} - -void ocre_timer_init(void) { - if (!timer_system_initialized) { - // Initialize timer array - for (int i = 0; i < CONFIG_MAX_TIMERS; i++) { - timers[i].in_use = false; - timers[i].id = 0; - } - - // Create the timer processing thread - timer_thread_id = k_thread_create(&timer_thread, timer_thread_stack, K_THREAD_STACK_SIZEOF(timer_thread_stack), - timer_thread_fn, NULL, NULL, NULL, TIMER_THREAD_PRIORITY, 0, K_NO_WAIT); - if (timer_thread_id == NULL) { - LOG_ERR("Failed to create timer thread"); - return; - } - - k_thread_name_set(timer_thread_id, "timer_thread"); - timer_system_initialized = true; - LOG_INF("Timer system initialized with dedicated thread"); - } + k_timer_init(&shared_timer, timer_callback_wrapper, NULL); + ocre_register_cleanup_handler(OCRE_RESOURCE_TYPE_TIMER, ocre_timer_cleanup_container); + timer_system_initialized = true; + LOG_INF("Timer system initialized"); } int ocre_timer_create(wasm_exec_env_t exec_env, int id) { - if (!timer_system_initialized || !current_module_inst) { - LOG_ERR("Timer system not properly initialized"); - errno = EINVAL; - return -1; - } - - if (id <= 0 || id > CONFIG_MAX_TIMERS) { - LOG_ERR("Invalid timer ID: %d (Expected between 1-%d)", id, CONFIG_MAX_TIMERS); - errno = EINVAL; - return -1; + wasm_module_inst_t module = wasm_runtime_get_module_inst(exec_env); + if (!module || id <= 0 || id > CONFIG_MAX_TIMERS) { + LOG_ERR("Invalid module %p or timer ID %d (max: %d)", (void *)module, id, CONFIG_MAX_TIMERS); + return -EINVAL; } - k_timer_ocre *timer = get_timer_from_id(id); + ocre_timer *timer = &timers[id - 1]; if (timer->in_use) { - LOG_ERR("Timer ID %d is already in use", id); - errno = EEXIST; - return -1; + LOG_ERR("Timer ID %d already in use", id); + return -EBUSY; } - ocre_timer_set_dispatcher(exec_env); - k_timer_init(&timer->timer, wasm_timer_callback, NULL); - timer->in_use = true; timer->id = id; - - LOG_INF("Timer created successfully: ID %d", id); + timer->owner = module; + timer->in_use = 1; + timer->timer = &shared_timer; + ocre_increment_resource_count(module, OCRE_RESOURCE_TYPE_TIMER); + LOG_INF("Created timer %d for module %p", id, (void *)module); return 0; } int ocre_timer_delete(wasm_exec_env_t exec_env, ocre_timer_t id) { - k_timer_ocre *timer = get_timer_from_id(id); - if (!timer || !timer->in_use) { - LOG_ERR("ERROR: timer %d not found or not in use\n", id); - errno = EINVAL; - return -1; + wasm_module_inst_t module = wasm_runtime_get_module_inst(exec_env); + if (!module || id <= 0 || id > CONFIG_MAX_TIMERS) { + LOG_ERR("Invalid module %p or timer ID %d", (void *)module, id); + return -EINVAL; + } + + ocre_timer *timer = &timers[id - 1]; + if (!timer->in_use || timer->owner != module) { + LOG_ERR("Timer ID %d not in use or not owned by module %p", id, (void *)module); + return -EINVAL; } - k_timer_stop(&timer->timer); - timer->in_use = false; + k_timer_stop(timer->timer); + timer->in_use = 0; + timer->owner = NULL; + ocre_decrement_resource_count(module, OCRE_RESOURCE_TYPE_TIMER); + LOG_INF("Deleted timer %d", id); return 0; } int ocre_timer_start(wasm_exec_env_t exec_env, ocre_timer_t id, int interval, int is_periodic) { - LOG_INF("Timer start called for ID: %d\n", id); - k_timer_ocre *timer = get_timer_from_id(id); - if (!timer || !timer->in_use) { - LOG_ERR("ERROR: timer %d not found or not in use\n", id); - errno = EINVAL; - return -1; + wasm_module_inst_t module = wasm_runtime_get_module_inst(exec_env); + if (!module || id <= 0 || id > CONFIG_MAX_TIMERS) { + LOG_ERR("Invalid module %p or timer ID %d", (void *)module, id); + return -EINVAL; } - if (interval <= 0) { - LOG_ERR("Invalid interval: %d\n", interval); - errno = EINVAL; - return -1; + ocre_timer *timer = &timers[id - 1]; + if (!timer->in_use || timer->owner != module) { + LOG_ERR("Timer ID %d not in use or not owned by module %p", id, (void *)module); + return -EINVAL; } - k_timeout_t start_timeout = K_MSEC(interval); - k_timeout_t repeat_timeout = is_periodic ? K_MSEC(interval) : K_NO_WAIT; + if (interval <= 0 || interval > 65535) { + LOG_ERR("Invalid interval %dms (must be 1-65535ms)", interval); + return -EINVAL; + } - k_timer_start(&timer->timer, start_timeout, repeat_timeout); + timer->interval = interval; + timer->periodic = is_periodic; + k_timeout_t duration = K_MSEC(interval); + k_timeout_t period = is_periodic ? duration : K_NO_WAIT; + k_timer_start(timer->timer, duration, period); + LOG_INF("Started timer %d with interval %dms, periodic=%d", id, interval, is_periodic); return 0; } int ocre_timer_stop(wasm_exec_env_t exec_env, ocre_timer_t id) { - k_timer_ocre *timer = get_timer_from_id(id); - if (!timer || !timer->in_use) { - LOG_ERR("ERROR: timer %d not found or not in use\n", id); - errno = EINVAL; - return -1; + wasm_module_inst_t module = wasm_runtime_get_module_inst(exec_env); + if (!module || id <= 0 || id > CONFIG_MAX_TIMERS) { + LOG_ERR("Invalid module %p or timer ID %d", (void *)module, id); + return -EINVAL; + } + + ocre_timer *timer = &timers[id - 1]; + if (!timer->in_use || timer->owner != module) { + LOG_ERR("Timer ID %d not in use or not owned by module %p", id, (void *)module); + return -EINVAL; } - k_timer_stop(&timer->timer); + k_timer_stop(timer->timer); + LOG_INF("Stopped timer %d", id); return 0; } int ocre_timer_get_remaining(wasm_exec_env_t exec_env, ocre_timer_t id) { - k_timer_ocre *timer = get_timer_from_id(id); - if (!timer) { - LOG_ERR("ERROR: Timer ID %d not found", id); - errno = EINVAL; - return -1; + wasm_module_inst_t module = wasm_runtime_get_module_inst(exec_env); + if (!module || id <= 0 || id > CONFIG_MAX_TIMERS) { + LOG_ERR("Invalid module %p or timer ID %d", (void *)module, id); + return -EINVAL; } - if (!timer->in_use) { - LOG_ERR("ERROR: Timer ID %d is not in use", id); - errno = EINVAL; - return -1; + + ocre_timer *timer = &timers[id - 1]; + if (!timer->in_use || timer->owner != module) { + LOG_ERR("Timer ID %d not in use or not owned by module %p", id, (void *)module); + return -EINVAL; } - k_ticks_t remaining_ticks = k_timer_remaining_ticks(&timer->timer); - uint32_t remaining_ms = k_ticks_to_ms_floor64(remaining_ticks); - return (int)remaining_ms; + int remaining = k_ticks_to_ms_floor32(k_timer_remaining_ticks(timer->timer)); + LOG_INF("Timer %d remaining time: %dms", id, remaining); + return remaining; } -void ocre_timer_set_dispatcher(wasm_exec_env_t exec_env) { - if (!current_module_inst) { - LOG_ERR("No active WASM module instance"); + +void ocre_timer_cleanup_container(wasm_module_inst_t module_inst) { + if (!module_inst) { + LOG_ERR("Invalid module instance for cleanup"); return; } - wasm_function_inst_t func = wasm_runtime_lookup_function(current_module_inst, "timer_callback"); - if (!func) { - LOG_ERR("Failed to find 'timer_callback' in WASM module"); - return; + for (int i = 0; i < CONFIG_MAX_TIMERS; i++) { + if (timers[i].in_use && timers[i].owner == module_inst) { + k_timer_stop(timers[i].timer); + timers[i].in_use = 0; + timers[i].owner = NULL; + ocre_decrement_resource_count(module_inst, OCRE_RESOURCE_TYPE_TIMER); + LOG_INF("Cleaned up timer %d for module %p", i + 1, (void *)module_inst); + } + } + LOG_INF("Cleaned up timer resources for module %p", (void *)module_inst); +} + +void ocre_timer_register_module(wasm_module_inst_t module_inst) { + if (module_inst) { + ocre_register_module(module_inst); + LOG_INF("Registered timer module %p", (void *)module_inst); } +} - timer_dispatcher_func = func; - LOG_INF("WASM timer dispatcher function set successfully"); -} \ No newline at end of file +static void timer_callback_wrapper(struct k_timer *timer) { + if (!timer_system_initialized || !common_initialized || !wasm_event_queue_initialized) { + LOG_ERR("Timer, common, or event queue not initialized, skipping callback"); + return; + } + if (!timer) { + LOG_ERR("Null timer pointer in callback"); + return; + } + if ((uintptr_t)wasm_event_queue_buffer_ptr % 4 != 0) { + LOG_ERR("wasm_event_queue_buffer misaligned: %p", (void *)wasm_event_queue_buffer_ptr); + return; + } + LOG_DBG("Timer callback for timer %p, shared_timer=%p", (void *)timer, (void *)&shared_timer); + LOG_DBG("wasm_event_queue at %p, buffer at %p", (void *)&wasm_event_queue, (void *)wasm_event_queue_buffer_ptr); + for (int i = 0; i < CONFIG_MAX_TIMERS; i++) { + if (timers[i].in_use && timers[i].timer == timer && timers[i].owner) { + wasm_event_t event = {.type = OCRE_RESOURCE_TYPE_TIMER, .id = timers[i].id, .port = 0, .state = 0}; + LOG_DBG("Creating timer event: type=%d, id=%d, port=%d, state=%d for owner %p", event.type, event.id, + event.port, event.state, (void *)timers[i].owner); + LOG_DBG("Event address: %p, Queue buffer: %p", (void *)&event, (void *)wasm_event_queue_buffer_ptr); + k_spinlock_key_t key = k_spin_lock(&wasm_event_queue_lock); + if (k_msgq_put(&wasm_event_queue, &event, K_NO_WAIT) != 0) { + LOG_ERR("Failed to queue timer event for timer %d", timers[i].id); + } else { + LOG_INF("Queued timer event for timer %d", timers[i].id); + } + k_spin_unlock(&wasm_event_queue_lock, key); + } + } +} diff --git a/src/ocre/ocre_timers/ocre_timer.h b/src/ocre/ocre_timers/ocre_timer.h index 5e49b91e..384024d0 100644 --- a/src/ocre/ocre_timers/ocre_timer.h +++ b/src/ocre/ocre_timers/ocre_timer.h @@ -8,64 +8,59 @@ #ifndef OCRE_TIMER_H #define OCRE_TIMER_H -#include -#include "wasm_export.h" +#include +#include "ocre_core_external.h" -/** - * @typedef ocre_timer_t - * @brief Timer identifier type - */ typedef int ocre_timer_t; -void ocre_timer_init(void); - -void ocre_timer_cleanup_container(wasm_module_inst_t module_inst); - -void ocre_timer_set_module_inst(wasm_module_inst_t module_inst); +/** + * @brief Timer callback function type + * @param timer_id ID of the expired timer + */ +typedef void (*timer_dispatcher_t)(uint32_t timer_id); /** - * @brief Creates a timer with the specified ID - * - * @param id Timer identifier (must be between 1 and MAX_TIMERS) + * @brief Creates a new timer instance + * @param exec_env WASM execution environment + * @param id Unique timer identifier (1-CONFIG_MAX_TIMERS) * @return 0 on success, -1 on error with errno set - * @retval EINVAL Invalid timer ID + * @retval EINVAL Invalid ID or timer system not initialized * @retval EEXIST Timer ID already in use */ int ocre_timer_create(wasm_exec_env_t exec_env, int id); /** - * @brief Deletes a timer - * - * - * @param id Timer identifier + * @brief Deletes a timer instance + * @param exec_env WASM execution environment + * @param id Timer identifier to delete * @return 0 on success, -1 on error with errno set - * @retval EINVAL Invalid timer ID or timer not found + * @retval EINVAL Timer not found or not in use */ int ocre_timer_delete(wasm_exec_env_t exec_env, ocre_timer_t id); /** - * @brief Starts a timer - * - * @param id Timer identifier - * @param interval Timer interval in milliseconds - * @param is_periodic True for periodic timer, false for one-shot + * @brief Starts a timer with specified parameters + * @param exec_env WASM execution environment + * @param id Timer identifier to start + * @param interval Initial expiration time in milliseconds + * @param is_periodic 1 for periodic timer, 0 for one-shot * @return 0 on success, -1 on error with errno set - * @retval EINVAL Invalid timer ID or timer not found + * @retval EINVAL Invalid timer ID or interval <= 0 */ int ocre_timer_start(wasm_exec_env_t exec_env, ocre_timer_t id, int interval, int is_periodic); /** - * @brief Stops a timer - * - * @param id Timer identifier + * @brief Stops a running timer + * @param exec_env WASM execution environment + * @param id Timer identifier to stop * @return 0 on success, -1 on error with errno set - * @retval EINVAL Invalid timer ID or timer not found + * @retval EINVAL Timer not found or not in use */ int ocre_timer_stop(wasm_exec_env_t exec_env, ocre_timer_t id); /** * @brief Gets the remaining time for a timer - * + * @param exec_env WASM execution environment * @param id Timer identifier * @return Remaining time in milliseconds, or -1 on error with errno set * @retval EINVAL Invalid timer ID or timer not found @@ -73,10 +68,17 @@ int ocre_timer_stop(wasm_exec_env_t exec_env, ocre_timer_t id); int ocre_timer_get_remaining(wasm_exec_env_t exec_env, ocre_timer_t id); /** - * @brief Sets the WASM dispatcher function for timer callbacks - * - * @param func WASM function instance to be called when timer expires + * @brief Cleans up all timers associated with a WASM module instance + * @param module_inst WASM module instance to clean up + */ +void ocre_timer_cleanup_container(wasm_module_inst_t module_inst); + +/** + * @brief Initializes the timer subsystem + * @note Must be called once before any other timer operations */ -void ocre_timer_set_dispatcher(wasm_exec_env_t exec_env); +void ocre_timer_init(void); +void ocre_timer_register_module(wasm_module_inst_t module_inst); +int ocre_timer_set_callback(wasm_exec_env_t exec_env, const char *callback_name); #endif /* OCRE_TIMER_H */ diff --git a/src/ocre/shell/ocre_shell.c b/src/ocre/shell/ocre_shell.c new file mode 100644 index 00000000..801b2193 --- /dev/null +++ b/src/ocre/shell/ocre_shell.c @@ -0,0 +1,152 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "ocre_shell.h" + +static ocre_cs_ctx *ctx_internal; + +int cmd_ocre_run(const struct shell *shell, size_t argc, char **argv) { + if (!ctx_internal) { + shell_error(shell, "Internal context not initialized."); + return -1; + } + + if (argc != 2) { + shell_error(shell, "Usage: ocre run "); + return -EINVAL; + } + + char *endptr; + int container_id = (int)strtol(argv[1], &endptr, 10); + + if (endptr == argv[1]) { + shell_error(shell, "No digits were found in argument .\n"); + return -EINVAL; + } else if (*endptr != '\0') { + shell_error(shell, "Invalid character: %c\n", *endptr); + return -EINVAL; + } + + int ret = -1; + for (int i = 0; i < CONFIG_MAX_CONTAINERS; i++) { + if (container_id == ctx_internal->containers[i].container_ID) { + ret = ocre_container_runtime_run_container(container_id, NULL); + break; + } + } + + if (ret == CONTAINER_STATUS_RUNNING) { + shell_info(shell, "Container started. Name: %s, ID: %d", + ctx_internal->containers[container_id].ocre_container_data.name, container_id); + return 0; + } else { + shell_error(shell, "Failed to run container: %d", container_id); + return -EIO; + } +} + +int cmd_ocre_stop(const struct shell *shell, size_t argc, char **argv) { + if (!ctx_internal) { + shell_error(shell, "Internal context not initialized."); + return -1; + } + + if (argc != 2) { + shell_error(shell, "Usage: ocre stop "); + return -EINVAL; + } + + const char *name = argv[1]; + shell_info(shell, "OCRE Shell Request to stop container with name: %s", name); + + int ret = -1; + int container_id = -1; + for (int i = 0; i < CONFIG_MAX_CONTAINERS; i++) { + if (strcmp(ctx_internal->containers[i].ocre_container_data.name, name) == 0) { + container_id = ctx_internal->containers[i].container_ID; + ret = ocre_container_runtime_stop_container(container_id, NULL); + } + } + + if (ret == CONTAINER_STATUS_STOPPED) { + shell_info(shell, "Container stopped. Name: %s, ID: %d", name, container_id); + } else if (ret == -1) { + shell_error(shell, "Failed to found container: %s", name); + } else { + shell_error(shell, "Failed to stop container: %s", name); + } + + return ret; +} + +int cmd_ocre_restart(const struct shell *shell, size_t argc, char **argv) { + if (!ctx_internal) { + shell_error(shell, "Internal context not initialized."); + return -1; + } + + if (argc != 2) { + shell_error(shell, "Usage: ocre restart "); + return -EINVAL; + } + + const char *name = argv[1]; + shell_info(shell, "OCRE Shell Request to restart container with name: %s", name); + + int ret = -1; + int container_id = -1; + for (int i = 0; i < CONFIG_MAX_CONTAINERS; i++) { + if (strcmp(ctx_internal->containers[i].ocre_container_data.name, name) == 0) { + container_id = ctx_internal->containers[i].container_ID; + ret = ocre_container_runtime_restart_container(ctx_internal, container_id, NULL); + } + } + + if (ret == CONTAINER_STATUS_RUNNING) { + shell_info(shell, "Container restarted. Name: %s, ID: %d", name, container_id); + } else if (ret == -1) { + shell_error(shell, "Failed to found container: %s", name); + } else { + shell_error(shell, "Failed to restart container: %s, status: %d", name, ret); + } + + return ret; +} + +int cmd_ocre_ls(const struct shell *shell, size_t argc, char **argv) { + ARG_UNUSED(argc); + ARG_UNUSED(argv); + + if (!ctx_internal) { + shell_error(shell, "Internal context not initialized."); + return -1; + } + + for (int i = 0; i < CONFIG_MAX_CONTAINERS; i++) { + shell_info(shell, "Container ID: %d, name: %s, status: %d", ctx_internal->containers[i].container_ID, + ctx_internal->containers[i].ocre_container_data.name, + ctx_internal->containers[i].container_runtime_status); + } + + return 0; +} + +void register_ocre_shell(ocre_cs_ctx *ctx) { + ctx_internal = ctx; + + SHELL_STATIC_SUBCMD_SET_CREATE( + ocre_subcmds, SHELL_CMD(run, NULL, "Start a new container: ocre run ", cmd_ocre_run), + SHELL_CMD(stop, NULL, "Stop a container: ocre stop ", cmd_ocre_stop), + SHELL_CMD(restart, NULL, "Restart a container: ocre restart ", cmd_ocre_restart), + SHELL_CMD(ls, NULL, "List running containers and their status", cmd_ocre_ls), SHELL_SUBCMD_SET_END); + +#if defined(CONFIG_OCRE_SHELL) + SHELL_CMD_REGISTER(ocre, &ocre_subcmds, "OCRE agent commands", NULL); +#endif +} diff --git a/src/ocre/shell/ocre_shell.h b/src/ocre/shell/ocre_shell.h new file mode 100644 index 00000000..3bb0bb81 --- /dev/null +++ b/src/ocre/shell/ocre_shell.h @@ -0,0 +1,22 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef OCRE_SHELL_H +#define OCRE_SHELL_H + +#include +#include "../src/ocre/ocre_container_runtime/ocre_container_runtime.h" + +// Function declarations for `config` subcommand handlers +int cmd_ocre_run(const struct shell *shell, size_t argc, char **argv); +int cmd_ocre_stop(const struct shell *shell, size_t argc, char **argv); +int cmd_ocre_ps(const struct shell *shell, size_t argc, char **argv); + +// Command registration function +void register_ocre_shell(ocre_cs_ctx *ctx); + +#endif /* OCRE_SHELL_H */ diff --git a/src/ocre/sm/sm.c b/src/ocre/sm/sm.c index 19228995..19a09082 100644 --- a/src/ocre/sm/sm.c +++ b/src/ocre/sm/sm.c @@ -6,13 +6,16 @@ */ #include -#include -LOG_MODULE_REGISTER(state_machine, OCRE_LOG_LEVEL); +#include "ocre_core_external.h" -#include "sm.h" +#include +#include #include -void sm_init(state_machine_t *sm, struct k_msgq *msgq, void *msg, void *custom_ctx, const struct smf_state *hsm) { +#include "sm.h" +LOG_MODULE_REGISTER(state_machine, OCRE_LOG_LEVEL); + +void sm_init(state_machine_t *sm, core_mq_t *msgq, void *msg, void *custom_ctx, const struct smf_state *hsm) { sm->hsm = hsm; sm->msgq = msgq; sm->ctx.event.msg = msg; @@ -25,7 +28,8 @@ int sm_run(state_machine_t *sm, int initial_state) { smf_set_initial(SMF_CTX(&sm->ctx), &sm->hsm[initial_state]); while (true) { - k_msgq_get(sm->msgq, sm->ctx.event.msg, K_FOREVER); + // Wait for a message from the queue + core_mq_recv(sm->msgq, sm->ctx.event.msg); sm->ctx.event.handled = false; ret = smf_run_state(SMF_CTX(&sm->ctx)); @@ -40,7 +44,7 @@ int sm_run(state_machine_t *sm, int initial_state) { } // Yield the current thread to allow the queue events to be processed - k_yield(); + core_yield(); } return ret; @@ -62,18 +66,18 @@ int sm_init_event_timer(state_machine_t *sm, int timer_id, void *timer_cb) { LOG_ERR("Invalid timer id: %d", timer_id); return -EINVAL; } - - k_timer_init(&sm->timers[timer_id], timer_cb, NULL); + + core_timer_init(&sm->timers[timer_id], timer_cb, NULL); return 0; } -int sm_set_event_timer(state_machine_t *sm, int timer_id, k_timeout_t duration, k_timeout_t period) { +int sm_set_event_timer(state_machine_t *sm, int timer_id, int duration, int period) { if (timer_id < 0 || timer_id >= MAX_TIMERS) { LOG_ERR("Invalid timer id: %d", timer_id); return -EINVAL; } - k_timer_start(&sm->timers[timer_id], duration, period); + core_timer_start(&sm->timers[timer_id], duration, period); return 0; } @@ -83,6 +87,6 @@ int sm_clear_event_timer(state_machine_t *sm, int timer_id) { return -EINVAL; } - k_timer_stop(&sm->timers[timer_id]); + core_timer_stop(&sm->timers[timer_id]); return 0; } diff --git a/src/ocre/sm/sm.h b/src/ocre/sm/sm.h index 40a24e57..2c677921 100644 --- a/src/ocre/sm/sm.h +++ b/src/ocre/sm/sm.h @@ -8,8 +8,7 @@ #ifndef SM_H #define SM_H -#include -#include +#include "ocre_core_external.h" #define MAX_TIMERS 3 @@ -32,24 +31,24 @@ struct sm_ctx { }; typedef struct state_machine { - struct k_msgq *msgq; - struct k_timer timers[MAX_TIMERS]; - struct sm_ctx ctx; - const struct smf_state *hsm; + core_mq_t *msgq; + core_timer_t timers[MAX_TIMERS]; + struct sm_ctx ctx; /*!< State machine context */ + const struct smf_state *hsm; /*!< State machine states */ } state_machine_t; int sm_transition(state_machine_t *sm, int target_state); +int sm_run(state_machine_t *sm, int initial_state); + int sm_init_event_timer(state_machine_t *sm, int timer_id, void *timer_cb); -int sm_set_event_timer(state_machine_t *sm, int timer_id, k_timeout_t duration, k_timeout_t period); +int sm_set_event_timer(state_machine_t *sm, int timer_id, int duration, int period); int sm_clear_event_timer(state_machine_t *sm, int timer_id); int sm_dispatch_event(state_machine_t *sm, void *msg); -int sm_run(state_machine_t *sm, int initial_state); - -void sm_init(state_machine_t *sm, struct k_msgq *msgq, void *msg, void *custom_ctx, const struct smf_state *hsm); +void sm_init(state_machine_t *sm, core_mq_t *msgq, void *msg, void *custom_ctx, const struct smf_state *hsm); -#endif +#endif // SM_H diff --git a/src/ocre/utils.h b/src/ocre/utils.h deleted file mode 100644 index be1a3024..00000000 --- a/src/ocre/utils.h +++ /dev/null @@ -1,70 +0,0 @@ -/** - * @copyright Copyright © contributors to Project Ocre, - * which has been established as Project Ocre a Series of LF Projects, LLC - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef OCRE_UTILS_H_ -#define OCRE_UTILS_H - -#include - -/** - * Converts a length-64 array of characters representing a sha256 into - * a length-32 binary representation byte array . - */ -int sha256str_to_bytes(uint8_t *bytes, const uint8_t *str) { - uint8_t tmp; - for (int i = 0; i < 64; i++) { - if (str[i] >= '0' && str[i] <= '9') { - tmp = str[i] - '0'; - } else if (str[i] >= 'a' && str[i] <= 'z') { - tmp = str[i] - 'a' + 10; - } else if (str[i] >= 'A' && str[i] <= 'Z') { - tmp = str[i] - 'A' + 10; - } else { - return -1; - } - - if (i % 2 == 0) { - bytes[i / 2] = tmp << 4; - } else { - bytes[i / 2] |= tmp; - } - } - - return 0; -} - -/** - * Converts a length-32 byte array of a sha256 binary representaton - * into a 64-byte length (plus null-terminator) string. - */ -int sha256bytes_to_str(const uint8_t *bytes, uint8_t *str) { - uint8_t tmp[2]; - for (int i = 0; i < 32; i++) { - tmp[0] = (bytes[i] & 0xF0) >> 4; - tmp[1] = (bytes[i] & 0x0F); - - for (int j = 0; j < 2; j++) { - if (tmp[j] >= 0x0 && tmp[j] <= 0x9) { - tmp[j] += '0'; - } else if (tmp[j] >= 0xA && tmp[j] <= 0xF) { - tmp[j] += ('a' - 0xA); - } else { - return -1; - } - } - - str[2 * i] = tmp[0]; - str[2 * i + 1] = tmp[1]; - } - - // Null terminator - str[64] = '\0'; - - return 0; -} - -#endif \ No newline at end of file diff --git a/src/ocre/utils/c-smf/CMakeLists.txt b/src/ocre/utils/c-smf/CMakeLists.txt new file mode 100644 index 00000000..da453ef4 --- /dev/null +++ b/src/ocre/utils/c-smf/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.20) +project(smf LANGUAGES C) + +set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS OFF) + +option(BUILD_TESTS "Build test program" OFF) + +add_subdirectory(smf) + +if(BUILD_TESTS) + add_subdirectory(test) + target_link_libraries(test smf) +endif(BUILD_TESTS) diff --git a/src/ocre/utils/c-smf/LICENSE b/src/ocre/utils/c-smf/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/src/ocre/utils/c-smf/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 + + http://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. diff --git a/src/ocre/utils/c-smf/README.md b/src/ocre/utils/c-smf/README.md new file mode 100644 index 00000000..66db56ed --- /dev/null +++ b/src/ocre/utils/c-smf/README.md @@ -0,0 +1,61 @@ +# c-smf + +## Overview +Platform-independent port of the Zephyr Real-Time Operating System (RTOS) State Machine Framework. + +## Features +Easy integration with CMake. + +Utilizes the powerful Zephyr State Machine Framework for efficient state management. + +Apache 2.0 licensed for open-source use. + +## Getting Started + +1. Clone the repository +``` +git clone https://github.com/kr-t/c-smf.git +``` + +2. Add the new directory to your CMake project and link smf library to your executable +``` +add_subdirectory(c-smf) +target_link_libraries(... smf) +``` + +3. Include the State Machine Framework in your code +``` +#include "smf/smf.h" +``` + +## Example + +Simple stand-alone example is available in /test folder. To build it, use BUILD_TESTS option. +``` +mkdir build && cd build +cmake -DBUILD_TESTS=ON .. +make +./test/test +``` + +Output: +``` +Entering State 1 +Starting State Machine +Running State 1 +Transitioning to State 2 +Exiting State 1 +Entering State 2 +Running State 2 +State Machine Test Complete +``` + +For more information, you can refer to the official Zephyr RTOS documentation for the State Machine Framework: [Zephyr SMF Documentation](https://docs.zephyrproject.org/latest/services/smf/index.html) + +## License +This project is licensed under the Apache License 2.0 - see the LICENSE file for details. + +## Acknowledments +This project is ported from Zephyr Real-Time Operating System. + +Thanks to the open-source community for their contributions. diff --git a/src/ocre/utils/c-smf/smf/CMakeLists.txt b/src/ocre/utils/c-smf/smf/CMakeLists.txt new file mode 100644 index 00000000..3024a766 --- /dev/null +++ b/src/ocre/utils/c-smf/smf/CMakeLists.txt @@ -0,0 +1,16 @@ +add_library(smf SHARED + smf.c +) + +set_target_properties(smf PROPERTIES PUBLIC_HEADER smf.h) +target_include_directories(smf PUBLIC ..) + +target_compile_options(smf PRIVATE + -fdiagnostics-color=always + -pedantic-errors + -Wall + -Wextra + -Wconversion + -Wsign-conversion + -Werror +) diff --git a/src/ocre/utils/c-smf/smf/smf.c b/src/ocre/utils/c-smf/smf/smf.c new file mode 100644 index 00000000..8009367b --- /dev/null +++ b/src/ocre/utils/c-smf/smf/smf.c @@ -0,0 +1,407 @@ +/* + * Copyright 2021 The Chromium OS Authors + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "smf.h" + +/** + * @brief Private structure (to this file) used to track state machine context. + * The structure is not used directly, but instead to cast the "internal" + * member of the smf_ctx structure. + */ +struct internal_ctx { + bool new_state: 1; + bool terminate: 1; + bool is_exit: 1; + bool handled: 1; +}; + +#ifdef CONFIG_SMF_ANCESTOR_SUPPORT +static bool share_paren(const struct smf_state *test_state, const struct smf_state *target_state) +{ + for (const struct smf_state *state = test_state; state != NULL; state = state->parent) { + if (target_state == state) { + return true; + } + } + + return false; +} + +static const struct smf_state *get_child_of(const struct smf_state *states, + const struct smf_state *parent) +{ + const struct smf_state *tmp = states; + + while (true) { + if (tmp->parent == parent) { + return tmp; + } + + if (tmp->parent == NULL) { + return NULL; + } + + tmp = tmp->parent; + } +} + +static const struct smf_state *get_last_of(const struct smf_state *states) +{ + return get_child_of(states, NULL); +} + +/** + * @brief Find the Least Common Ancestor (LCA) of two states + * + * @param source transition source + * @param dest transition destination + * @return LCA state, or NULL if states have no LCA. + */ +static const struct smf_state *get_lca_of(const struct smf_state *source, + const struct smf_state *dest) +{ + for (const struct smf_state *ancestor = source->parent; ancestor != NULL; + ancestor = ancestor->parent) { + if (ancestor == dest) { + return ancestor->parent; + } else if (share_paren(dest, ancestor)) { + return ancestor; + } + } + + return NULL; +} + +/** + * @brief Executes all entry actions from the direct child of topmost to the new state + * + * @param ctx State machine context + * @param new_state State we are transitioning to + * @param topmost State we are entering from. Its entry action is not executed + * @return true if the state machine should terminate, else false + */ +static bool smf_execute_all_entry_actions(struct smf_ctx *const ctx, + const struct smf_state *new_state, + const struct smf_state *topmost) +{ + struct internal_ctx *const internal = (void *)&ctx->internal; + + if (new_state == topmost) { + /* There are no child states, so do nothing */ + return false; + } + + for (const struct smf_state *to_execute = get_child_of(new_state, topmost); + to_execute != NULL && to_execute != new_state; + to_execute = get_child_of(new_state, to_execute)) { + /* Keep track of the executing entry action in case it calls + * smf_set_state() + */ + ctx->executing = to_execute; + /* Execute every entry action EXCEPT that of the topmost state */ + if (to_execute->entry) { + to_execute->entry(ctx); + + /* No need to continue if terminate was set */ + if (internal->terminate) { + return true; + } + } + } + + /* and execute the new state entry action */ + ctx->executing = new_state; + if (new_state->entry) { + new_state->entry(ctx); + + /* No need to continue if terminate was set */ + if (internal->terminate) { + return true; + } + } + + return false; +} + +/** + * @brief Execute all ancestor run actions + * + * @param ctx State machine context + * @param target The run actions of this target's ancestors are executed + * @return true if the state machine should terminate, else false + */ +static bool smf_execute_ancestor_run_actions(struct smf_ctx *ctx) +{ + struct internal_ctx *const internal = (void *)&ctx->internal; + /* Execute all run actions in reverse order */ + + /* Return if the current state terminated */ + if (internal->terminate) { + return true; + } + + /* The child state either transitioned or handled it. Either way, stop propagating. */ + if (internal->new_state || internal->handled) { + internal->new_state = false; + internal->handled = false; + return false; + } + + /* Try to run parent run actions */ + for (const struct smf_state *tmp_state = ctx->current->parent; tmp_state != NULL; + tmp_state = tmp_state->parent) { + /* Keep track of where we are in case an ancestor calls smf_set_state() */ + ctx->executing = tmp_state; + /* Execute parent run action */ + if (tmp_state->run) { + tmp_state->run(ctx); + /* No need to continue if terminate was set */ + if (internal->terminate) { + return true; + } + + /* This state dealt with it. Stop propagating. */ + if (internal->new_state || internal->handled) { + break; + } + } + } + + internal->new_state = false; + internal->handled = false; + + /* All done executing the run actions */ + + return false; +} + +/** + * @brief Executes all exit actions from ctx->current to the direct child of topmost + * + * @param ctx State machine context + * @param topmost State we are exiting to. Its exit action is not executed + * @return true if the state machine should terminate, else false + */ +static bool smf_execute_all_exit_actions(struct smf_ctx *const ctx, const struct smf_state *topmost) +{ + struct internal_ctx *const internal = (void *)&ctx->internal; + + for (const struct smf_state *to_execute = ctx->current; + to_execute != NULL && to_execute != topmost; + to_execute = to_execute->parent) { + if (to_execute->exit) { + to_execute->exit(ctx); + + /* No need to continue if terminate was set in the exit action */ + if (internal->terminate) { + return true; + } + } + } + + return false; +} +#endif /* CONFIG_SMF_ANCESTOR_SUPPORT */ + +void smf_set_initial(struct smf_ctx *ctx, const struct smf_state *init_state) +{ + struct internal_ctx *const internal = (void *)&ctx->internal; + +#ifdef CONFIG_SMF_INITIAL_TRANSITION + /* + * The final target will be the deepest leaf state that + * the target contains. Set that as the real target. + */ + while (init_state->initial) { + init_state = init_state->initial; + } +#endif + + internal->is_exit = false; + internal->terminate = false; + internal->handled = false; + internal->new_state = false; + ctx->current = init_state; + ctx->previous = NULL; + ctx->terminate_val = 0; + +#ifdef CONFIG_SMF_ANCESTOR_SUPPORT + ctx->executing = init_state; + const struct smf_state *topmost = get_last_of(init_state); + + /* Execute topmost state entry action, since smf_execute_all_entry_actions() + * doesn't + */ + if (topmost->entry) { + topmost->entry(ctx); + if (internal->terminate) { + /* No need to continue if terminate was set */ + return; + } + } + + if (smf_execute_all_entry_actions(ctx, init_state, topmost)) { + /* No need to continue if terminate was set */ + return; + } +#else + /* execute entry action if it exists */ + if (init_state->entry) { + init_state->entry(ctx); + } +#endif +} + +void smf_set_state(struct smf_ctx *const ctx, const struct smf_state *new_state) +{ + struct internal_ctx *const internal = (void *)&ctx->internal; + + if (new_state == NULL) { + printf("new_state cannot be NULL"); + return; + } + + /* + * It does not make sense to call smf_set_state in an exit phase of a state + * since we are already in a transition; we would always ignore the + * intended state to transition into. + */ + if (internal->is_exit) { + printf("Calling %s from exit action", __func__); + return; + } + +#ifdef CONFIG_SMF_ANCESTOR_SUPPORT + const struct smf_state *topmost; + + if (share_paren(ctx->executing, new_state)) { + /* new state is a parent of where we are now*/ + topmost = new_state; + } else if (share_paren(new_state, ctx->executing)) { + /* we are a parent of the new state */ + topmost = ctx->executing; + } else { + /* not directly related, find LCA */ + topmost = get_lca_of(ctx->executing, new_state); + } + + internal->is_exit = true; + internal->new_state = true; + + /* call all exit actions up to (but not including) the topmost */ + if (smf_execute_all_exit_actions(ctx, topmost)) { + /* No need to continue if terminate was set in the exit action */ + return; + } + + /* if self-transition, call the exit action */ + if ((ctx->executing == new_state) && (new_state->exit)) { + new_state->exit(ctx); + + /* No need to continue if terminate was set in the exit action */ + if (internal->terminate) { + return; + } + } + + internal->is_exit = false; + + /* if self transition, call the entry action */ + if ((ctx->executing == new_state) && (new_state->entry)) { + new_state->entry(ctx); + + /* No need to continue if terminate was set in the entry action */ + if (internal->terminate) { + return; + } + } +#ifdef CONFIG_SMF_INITIAL_TRANSITION + /* + * The final target will be the deepest leaf state that + * the target contains. Set that as the real target. + */ + while (new_state->initial) { + new_state = new_state->initial; + } +#endif + + /* update the state variables */ + ctx->previous = ctx->current; + ctx->current = new_state; + + /* call all entry actions (except those of topmost) */ + if (smf_execute_all_entry_actions(ctx, new_state, topmost)) { + /* No need to continue if terminate was set in the entry action */ + return; + } +#else + /* Flat state machines have a very simple transition: */ + if (ctx->current->exit) { + internal->is_exit = true; + ctx->current->exit(ctx); + /* No need to continue if terminate was set in the exit action */ + if (internal->terminate) { + return; + } + internal->is_exit = false; + } + /* update the state variables */ + ctx->previous = ctx->current; + ctx->current = new_state; + + if (ctx->current->entry) { + ctx->current->entry(ctx); + /* No need to continue if terminate was set in the entry action */ + if (internal->terminate) { + return; + } + } +#endif +} + +void smf_set_terminate(struct smf_ctx *ctx, int32_t val) +{ + struct internal_ctx *const internal = (void *)&ctx->internal; + + internal->terminate = true; + ctx->terminate_val = val; +} + +void smf_set_handled(struct smf_ctx *ctx) +{ + struct internal_ctx *const internal = (void *)&ctx->internal; + + internal->handled = true; +} + +int32_t smf_run_state(struct smf_ctx *const ctx) +{ + struct internal_ctx *const internal = (void *)&ctx->internal; + + /* No need to continue if terminate was set */ + if (internal->terminate) { + return ctx->terminate_val; + } + +#ifdef CONFIG_SMF_ANCESTOR_SUPPORT + ctx->executing = ctx->current; +#endif + + if (ctx->current->run) { + ctx->current->run(ctx); + } + +#ifdef CONFIG_SMF_ANCESTOR_SUPPORT + if (smf_execute_ancestor_run_actions(ctx)) { + return ctx->terminate_val; + } +#endif + return 0; +} diff --git a/src/ocre/utils/c-smf/smf/smf.h b/src/ocre/utils/c-smf/smf/smf.h new file mode 100644 index 00000000..f8cba140 --- /dev/null +++ b/src/ocre/utils/c-smf/smf/smf.h @@ -0,0 +1,179 @@ +/* + * Copyright 2021 The Chromium OS Authors + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * + * @brief State Machine Framework header file + */ + +#ifndef ZEPHYR_INCLUDE_SMF_H_ +#define ZEPHYR_INCLUDE_SMF_H_ + +#include + +#define CONFIG_SMF_ANCESTOR_SUPPORT +#define CONFIG_SMF_INITIAL_TRANSITION + +/** + * @brief State Machine Framework API + * @defgroup smf State Machine Framework API + * @version 0.1.0 + * @ingroup os_services + * @{ + */ + +/** + * @brief Macro to create a hierarchical state with initial transitions. + * + * @param _entry State entry function or NULL + * @param _run State run function or NULL + * @param _exit State exit function or NULL + * @param _parent State parent object or NULL + * @param _initial State initial transition object or NULL + */ +#define SMF_CREATE_STATE(_entry, _run, _exit, _parent, _initial) \ +{ \ + .entry = _entry, \ + .run = _run, \ + .exit = _exit, \ + .parent = _parent, \ + .initial = _initial \ +} + +/** + * @brief Macro to cast user defined object to state machine + * context. + * + * @param o A pointer to the user defined object + */ +#define SMF_CTX(o) ((struct smf_ctx *)o) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Function pointer that implements a portion of a state + * + * @param obj pointer user defined object + */ +typedef void (*state_execution)(void *obj); + +/** General state that can be used in multiple state machines. */ +struct smf_state { + /** Optional method that will be run when this state is entered */ + const state_execution entry; + /** + * Optional method that will be run repeatedly during state machine + * loop. + */ + const state_execution run; + /** Optional method that will be run when this state exists */ + const state_execution exit; +#ifdef CONFIG_SMF_ANCESTOR_SUPPORT + /** + * Optional parent state that contains common entry/run/exit + * implementation among various child states. + * entry: Parent function executes BEFORE child function. + * run: Parent function executes AFTER child function. + * exit: Parent function executes AFTER child function. + * + * Note: When transitioning between two child states with a shared + * parent, that parent's exit and entry functions do not execute. + */ + const struct smf_state *parent; + +#ifdef CONFIG_SMF_INITIAL_TRANSITION + /** + * Optional initial transition state. NULL for leaf states. + */ + const struct smf_state *initial; +#endif /* CONFIG_SMF_INITIAL_TRANSITION */ +#endif /* CONFIG_SMF_ANCESTOR_SUPPORT */ +}; + +/** Defines the current context of the state machine. */ +struct smf_ctx { + /** Current state the state machine is executing. */ + const struct smf_state *current; + /** Previous state the state machine executed */ + const struct smf_state *previous; + +#ifdef CONFIG_SMF_ANCESTOR_SUPPORT + /** Currently executing state (which may be a parent) */ + const struct smf_state *executing; +#endif /* CONFIG_SMF_ANCESTOR_SUPPORT */ + /** + * This value is set by the set_terminate function and + * should terminate the state machine when its set to a + * value other than zero when it's returned by the + * run_state function. + */ + int32_t terminate_val; + /** + * The state machine casts this to a "struct internal_ctx" and it's + * used to track state machine context + */ + uint32_t internal; +}; + +/** + * @brief Initializes the state machine and sets its initial state. + * + * @param ctx State machine context + * @param init_state Initial state the state machine starts in. + */ +void smf_set_initial(struct smf_ctx *ctx, const struct smf_state *init_state); + +/** + * @brief Changes a state machines state. This handles exiting the previous + * state and entering the target state. For HSMs the entry and exit + * actions of the Least Common Ancestor will not be run. + * + * @param ctx State machine context + * @param new_state State to transition to (NULL is valid and exits all states) + */ +void smf_set_state(struct smf_ctx *ctx, const struct smf_state *new_state); + +/** + * @brief Terminate a state machine + * + * @param ctx State machine context + * @param val Non-Zero termination value that's returned by the smf_run_state + * function. + */ +void smf_set_terminate(struct smf_ctx *ctx, int32_t val); + +/** + * @brief Tell the SMF to stop propagating the event to ancestors. This allows + * HSMs to implement 'programming by difference' where substates can + * handle events on their own or propagate up to a common handler. + * + * @param ctx State machine context + */ +void smf_set_handled(struct smf_ctx *ctx); + +/** + * @brief Runs one iteration of a state machine (including any parent states) + * + * @param ctx State machine context + * @return A non-zero value should terminate the state machine. This + * non-zero value could represent a terminal state being reached + * or the detection of an error that should result in the + * termination of the state machine. + */ +int32_t smf_run_state(struct smf_ctx *ctx); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_SMF_H_ */ diff --git a/src/ocre/utils/c-smf/test/CMakeLists.txt b/src/ocre/utils/c-smf/test/CMakeLists.txt new file mode 100644 index 00000000..6c2278f0 --- /dev/null +++ b/src/ocre/utils/c-smf/test/CMakeLists.txt @@ -0,0 +1,13 @@ +add_executable(test + test.c +) + +target_compile_options(test PRIVATE + -fdiagnostics-color=always + -pedantic-errors + -Wall + -Wextra + -Wconversion + -Wsign-conversion + -Werror +) diff --git a/src/ocre/utils/c-smf/test/test.c b/src/ocre/utils/c-smf/test/test.c new file mode 100644 index 00000000..ebf7ea64 --- /dev/null +++ b/src/ocre/utils/c-smf/test/test.c @@ -0,0 +1,73 @@ +#include +#include "../smf/smf.h" + +// Define state entry, exit, and run actions +void state1_entry(void *ctx) { + (void)ctx; /* -Wunused-parameter */ + printf("Entering State 1\n"); +} + +void state1_exit(void *ctx) { + (void)ctx; /* -Wunused-parameter */ + printf("Exiting State 1\n"); +} + +void state1_run(void *ctx) { + (void)ctx; /* -Wunused-parameter */ + printf("Running State 1\n"); +} + +void state2_entry(void *ctx) { + (void)ctx; /* -Wunused-parameter */ + printf("Entering State 2\n"); +} + +void state2_exit(void *ctx) { + (void)ctx; /* -Wunused-parameter */ + printf("Exiting State 2\n"); +} + +void state2_run(void *ctx) { + (void)ctx; /* -Wunused-parameter */ + printf("Running State 2\n"); +} + +// Define states +struct smf_state state1 = { + .entry = state1_entry, + .run = state1_run, + .exit = state1_exit, +#ifdef CONFIG_SMF_ANCESTOR_SUPPORT + .parent = NULL, +#endif /* CONFIG_SMF_ANCESTOR_SUPPORT */ +}; + +struct smf_state state2 = { + .entry = state2_entry, + .run = state2_run, + .exit = state2_exit, +#ifdef CONFIG_SMF_ANCESTOR_SUPPORT + .parent = NULL, +#endif /* CONFIG_SMF_ANCESTOR_SUPPORT */ +}; + +int main() { + struct smf_ctx ctx; + + // Initialize the state machine with the initial state + smf_set_initial(&ctx, &state1); + + // Run the initial state + printf("Starting State Machine\n"); + smf_run_state(&ctx); + + // Transition to a new state + printf("Transitioning to State 2\n"); + smf_set_state(&ctx, &state2); + + // Run the new state + smf_run_state(&ctx); + + printf("State Machine Test Complete\n"); + return 0; +} \ No newline at end of file diff --git a/src/ocre/utils/strlcat.c b/src/ocre/utils/strlcat.c new file mode 100644 index 00000000..f3597793 --- /dev/null +++ b/src/ocre/utils/strlcat.c @@ -0,0 +1,57 @@ +/* $OpenBSD: strlcat.c,v 1.19 2019/01/25 00:19:25 millert Exp $ */ + +/* + * Copyright (c) 1998, 2015 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +/* + * Appends src to string dst of size dsize (unlike strncat, dsize is the + * full size of dst, not space left). At most dsize-1 characters + * will be copied. Always NUL terminates (unless dsize <= strlen(dst)). + * Returns strlen(src) + MIN(dsize, strlen(initial dst)). + * If retval >= dsize, truncation occurred. + */ +size_t +strlcat (char *dst, + const char *src, + size_t dsize) +{ + const char *odst = dst; + const char *osrc = src; + size_t n = dsize; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end. */ + while (n-- != 0 && *dst != '\0') + dst++; + dlen = dst - odst; + n = dsize - dlen; + + if (n-- == 0) + return(dlen + strlen(src)); + while (*src != '\0') { + if (n != 0) { + *dst++ = *src; + n--; + } + src++; + } + *dst = '\0'; + + return(dlen + (src - osrc)); /* count does not include NUL */ +} diff --git a/src/ocre/utils/utils.h b/src/ocre/utils/utils.h new file mode 100644 index 00000000..3c10d898 --- /dev/null +++ b/src/ocre/utils/utils.h @@ -0,0 +1,22 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef OCRE_UTILS_H_ +#define OCRE_UTILS_H + +#include + +/* + * Appends src to string dst of size dsize (unlike strncat, dsize is the + * full size of dst, not space left). At most dsize-1 characters + * will be copied. Always NUL terminates (unless dsize <= strlen(dst)). + * Returns strlen(src) + MIN(dsize, strlen(initial dst)). + * If retval >= dsize, truncation occurred. + */ +size_t strlcat(char *dst, const char *src, size_t dsize); + +#endif diff --git a/src/samples-mini/posix/main.c b/src/samples-mini/posix/main.c new file mode 100644 index 00000000..b8a7b96b --- /dev/null +++ b/src/samples-mini/posix/main.c @@ -0,0 +1,115 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +int g_argc = 0; + +#include "ocre_core_external.h" +#include +#include + +#ifdef HAS_GENERATED_INPUT +#include +#else +#include +#endif + +void create_sample_container(); + +int main(int argc, char *argv[]) { + ocre_cs_ctx ctx; + ocre_container_init_arguments_t args; + char *container_filename = "hello"; + + ocre_app_storage_init(); + + // Step 1: Initialize the Ocre runtime + ocre_container_runtime_status_t ret = ocre_container_runtime_init(&ctx, &args); + + if (ret == RUNTIME_STATUS_INITIALIZED) { + printf("\n\nOcre runtime started\n"); + + if (argc > 1) { + g_argc = argc; + container_filename = argv[1]; // Use the filename as the container name/sha256 + } + else { + create_sample_container(container_filename); + } + + // Step 2: Create the container, this allocates and loads the container binary + ocre_container_data_t ocre_container_data[CONFIG_MAX_CONTAINERS]; + int container_ID[CONFIG_MAX_CONTAINERS]; + ocre_container_runtime_cb callback[CONFIG_MAX_CONTAINERS]; + + int i = 0; + do { + ocre_container_data[i].heap_size = 0; + snprintf(ocre_container_data[i].name, sizeof(ocre_container_data[i].name), "Container%d", i); + snprintf(ocre_container_data[i].sha256, sizeof(ocre_container_data[i].sha256), "%s", container_filename); + ocre_container_data[i].timers = 0; + ocre_container_data[i].watchdog_interval = 0; + ocre_container_runtime_create_container(&ctx, &ocre_container_data[i], &container_ID[i], callback[i]); + + // Step 3: Execute the container + ocre_container_runtime_run_container(container_ID[i], callback[i]); + core_sleep_ms(1000); + + i++; + container_filename = argv[i+1]; + } while (i < argc - 1); + + // Loop forever, without this the application will exit and stop all execution + while (true) { + core_sleep_ms(1000); + } + + } else { + printf("\n\nOcre runtime failed to start.\n"); + } +} + +/** + * Creates a container image file using the sample "hello-word" WASM module + * This is for demostration purposes only and does not perform any error checking. + * + * @param file_name a string containing the name of the file to create + */ + +void create_sample_container(char *file_name) { + static char file_path[64]; + snprintf(file_path, sizeof(file_path), "./ocre/images/%s.bin", file_name); + + // Create directories if they don't exist + mkdir("./ocre", 0755); + mkdir("./ocre/images", 0755); + + // Open the file for writing + int fd = open(file_path, O_CREAT | O_RDWR, 0644); + if (fd == -1) { + perror("Error opening file"); + return; + } + // Write the binary data to the file + ssize_t bytes_written = write(fd, wasm_binary, wasm_binary_len); + if (bytes_written == -1) { + perror("Error writing to file"); + } else { + printf("Wrote %zd bytes to %s\n", bytes_written, file_path); + } + + // Close the file + close(fd); +} diff --git a/src/samples-mini/zephyr/main.c b/src/samples-mini/zephyr/main.c new file mode 100644 index 00000000..f4d8dbad --- /dev/null +++ b/src/samples-mini/zephyr/main.c @@ -0,0 +1,78 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "ocre_core_external.h" +#include +#include + +#ifdef HAS_GENERATED_INPUT +#include +#else +#include +#endif + +void create_sample_container(); + +int main(int argc, char *argv[]) { + ocre_cs_ctx ctx; + ocre_container_init_arguments_t args; + char *container_filename = "hello"; + + ocre_app_storage_init(); + + // Step 1: Initialize the Ocre runtime + ocre_container_runtime_status_t ret = ocre_container_runtime_init(&ctx, &args); + + if (ret == RUNTIME_STATUS_INITIALIZED) { + printf("\n\nOcre runtime started\n"); + + create_sample_container(container_filename); + + // Step 2: Create the container, this allocates and loads the container binary + ocre_container_data_t ocre_container_data; + int container_ID; + ocre_container_runtime_cb callback; + + ocre_container_data.heap_size = 0; + snprintf(ocre_container_data.name, sizeof(ocre_container_data.name), "Hello World"); + snprintf(ocre_container_data.sha256, sizeof(ocre_container_data.sha256), "%s", container_filename); + ocre_container_data.timers = 0; + ocre_container_runtime_create_container(&ctx, &ocre_container_data, &container_ID, callback); + + // Step 3: Execute the container + ocre_container_runtime_run_container(container_ID, callback); + // Loop forever, without this the application will exit and stop all execution + while (true) { + core_sleep_ms(1000); + } + + } else { + printf("\n\nOcre runtime failed to start.\n"); + } +} + +/** + * Creates a container image file using the sample "hello-word" WASM module + * This is for demostration purposes only and does not perform any error checking. + * + * @param file_name a string containing the name of the file to create + */ + +void create_sample_container(char *file_name) { + static char file_path[64]; + struct fs_file_t f; + snprintf((char *)&file_path, 64, "/lfs/ocre/images/%s.bin", file_name); + int res; + + fs_file_t_init(&f); + res = fs_open(&f, file_path, FS_O_CREATE | FS_O_RDWR); + + fs_write(&f, &wasm_binary, wasm_binary_len); + fs_close(&f); +} diff --git a/src/shared/platform/ocre_core_external.h b/src/shared/platform/ocre_core_external.h new file mode 100644 index 00000000..c38b8c27 --- /dev/null +++ b/src/shared/platform/ocre_core_external.h @@ -0,0 +1,226 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef OCRE_CORE_EXTERNAL_H +#define OCRE_CORE_EXTERNAL_H + +#include +#include +#include + +#ifdef __ZEPHYR__ +#include "zephyr/core_internal.h" +#else +#include "posix/core_internal.h" +#endif + +#define OCRE_MODULE_NAME_LEN 16 + +/** + * @brief Initialize application storage subsystem. + */ +void ocre_app_storage_init(); + +/** + * @brief Thread function type definition. + * + * @param arg Pointer to user data passed to the thread. + */ +typedef void (*core_thread_func_t)(void *arg); + +typedef struct core_thread core_thread_t; + +/** + * @brief Create a new thread. + * + * @param thread Pointer to thread structure to initialize. + * @param func Thread entry function. + * @param arg Argument to pass to the thread function. + * @param name Name of the thread. + * @param stack_size Stack size in bytes. + * @param priority Thread priority. + * @return 0 on success, negative value on error. + */ +int core_thread_create(core_thread_t *thread, core_thread_func_t func, void *arg, const char *name, size_t stack_size, int priority); + +/** + * @brief Destroy a thread and release its resources. + * + * @param thread Pointer to the thread structure. + */ +void core_thread_destroy(core_thread_t *thread); + +typedef struct core_mutex core_mutex_t; + +/** + * @brief Initialize a mutex. + * + * @param mutex Pointer to the mutex structure. + * @return 0 on success, negative value on error. + */ +int core_mutex_init(core_mutex_t *mutex); + +/** + * @brief Destroy a mutex and release its resources. + * + * @param mutex Pointer to the mutex structure. + * @return 0 on success, negative value on error. + */ +int core_mutex_destroy(core_mutex_t *mutex); + +/** + * @brief Lock a mutex. + * + * @param mutex Pointer to the mutex structure. + * @return 0 on success, negative value on error. + */ +int core_mutex_lock(core_mutex_t *mutex); + +/** + * @brief Unlock a mutex. + * + * @param mutex Pointer to the mutex structure. + * @return 0 on success, negative value on error. + */ +int core_mutex_unlock(core_mutex_t *mutex); + +typedef struct core_mq core_mq_t; + +/** + * @brief Initialize a message queue. + * + * @param mq Pointer to the message queue structure. + * @param name Name of the message queue. + * @param msg_size Size of each message in bytes. + * @param max_msgs Maximum number of messages in the queue. + * @return 0 on success, negative value on error. + */ +int core_mq_init(core_mq_t *mq, const char *name, size_t msg_size, uint32_t max_msgs); + +/** + * @brief Send a message to the queue. + * + * @param mq Pointer to the message queue. + * @param data Pointer to the message data. + * @param msg_len Length of the message in bytes. + * @return 0 on success, negative value on error. + */ +int core_mq_send(core_mq_t *mq, const void *data, size_t msg_len); + +/** + * @brief Receive a message from the queue. + * + * @param mq Pointer to the message queue. + * @param data Pointer to the buffer to store the received message. + * @return 0 on success, negative value on error. + */ +int core_mq_recv(core_mq_t *mq, void *data); + +/** + * @brief Sleep for a specified number of milliseconds. + * + * @param milliseconds Number of milliseconds to sleep. + */ +void core_sleep_ms(int milliseconds); + +/** + * @brief Allocate memory. + * + * @param size Number of bytes to allocate. + * @return Pointer to allocated memory, or NULL on failure. + */ +void *core_malloc(size_t size); + +/** + * @brief Free previously allocated memory. + * + * @param ptr Pointer to memory to free. + */ +void core_free(void *ptr); + +/** + * @brief Yield the current thread's execution. + */ +void core_yield(void); + +typedef struct core_timer core_timer_t; + +/** + * @brief Initialize a timer. + * + * @param timer Pointer to the timer structure. + * @param cb Callback function to call when the timer expires. + * @param user_data Pointer to user data for the callback. + * @return 0 on success, negative value on error. + */ +int core_timer_init(core_timer_t *timer, core_timer_callback_t cb, void *user_data); + +/** + * @brief Start a timer. + * + * @param timer Pointer to the timer structure. + * @param timeout_ms Timeout in milliseconds before the timer expires. + * @param period_ms Period in milliseconds for periodic timers (0 for one-shot). + * @return 0 on success, negative value on error. + */ +int core_timer_start(core_timer_t *timer, int timeout_ms, int period_ms); + +/** + * @brief Stop a timer. + * + * @param timer Pointer to the timer structure. + * @return 0 on success, negative value on error. + */ +int core_timer_stop(core_timer_t *timer); + +/** + * @brief Get file status (size). + * + * @param path Path to the file. + * @param size Pointer to store the file size. + * @return 0 on success, negative value on error. + */ +int core_filestat(const char *path, size_t *size); + +/** + * @brief Open a file for reading. + * + * @param path Path to the file. + * @param handle Pointer to store the file handle. + * @return 0 on success, negative value on error. + */ +int core_fileopen(const char *path, void **handle); + +/** + * @brief Read data from a file. + * + * @param handle File handle. + * @param buffer Buffer to store the read data. + * @param size Number of bytes to read. + * @return 0 on success, negative value on error. + */ +int core_fileread(void *handle, void *buffer, size_t size); + +/** + * @brief Close a file. + * + * @param handle File handle. + * @return 0 on success, negative value on error. + */ +int core_fileclose(void *handle); + +/** + * @brief Construct a file path for a container image. + * + * @param path Buffer to store the constructed path. + * @param len Length of the buffer. + * @param name Name or identifier for the file. + * @return 0 on success, negative value on error. + */ +int core_construct_filepath(char *path, size_t len, char *name); + +#endif /* OCRE_CORE_EXTERNAL_H */ diff --git a/src/shared/platform/posix/core_fs.c b/src/shared/platform/posix/core_fs.c new file mode 100644 index 00000000..8174b0a5 --- /dev/null +++ b/src/shared/platform/posix/core_fs.c @@ -0,0 +1,125 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ocre_core_external.h" + +#define MAX_PATH_LEN 256 + +int core_construct_filepath(char *path, size_t len, char *name) { + char cwd[PATH_MAX]; + if (getcwd(cwd, sizeof(cwd)) != NULL) + printf("Current working dir: %s\n", cwd); + if (g_argc) { + strcpy(path, name); + return 0; + } else { + return snprintf(path, len, "%s/ocre/images/%s.bin", cwd, name); + } +} + + +int core_filestat(const char *path, size_t *size) { + struct stat st; + if (stat(path, &st) == 0) { + if (size) { + *size = st.st_size; + } + return 0; // success + } else { + return errno; // return the specific error code + } + +} + +int core_fileopen(const char *path, void **handle) { + int *fd = core_malloc(sizeof(int)); + if (!fd) return -ENOMEM; + *fd = open(path, O_RDONLY); + if (*fd < 0) { + core_free(fd); + return -errno; + } + *handle = fd; + return 0; +} + +int core_fileread(void *handle, void *buffer, size_t size) { + int fd = *(int *)handle; + ssize_t bytes_read = read(fd, buffer, size); + if (bytes_read < 0) return -errno; + if ((size_t)bytes_read != size) return -EIO; + return 0; +} + +int core_fileclose(void *handle) { + int fd = *(int *)handle; + int ret = close(fd); + core_free(handle); + return ret; +} + +static int lsdir(const char *path) { + DIR *dirp; + struct dirent *entry; + + dirp = opendir(path); + if (!dirp) { + printf("Error opening dir %s [%d]\n", path, errno); + return -errno; + } + + while ((entry = readdir(dirp)) != NULL) { + printf("Found: %s", entry->d_name); + } + + if (closedir(dirp) < 0) { + printf("Error closing dir [%d]\n", errno); + return -errno; + } + + return 0; +} + +void ocre_app_storage_init() { + struct stat st; + + if (stat(OCRE_BASE_PATH, &st) == -1) { + if (mkdir(OCRE_BASE_PATH, 0755) < 0) { + printf("Failed to create directory %s [%d]\n", OCRE_BASE_PATH, errno); + } + } + + if (stat(APP_RESOURCE_PATH, &st) == -1) { + if (mkdir(APP_RESOURCE_PATH, 0755) < 0) { + printf("Failed to create directory %s [%d]\n", APP_RESOURCE_PATH, errno); + } + } + + if (stat(PACKAGE_BASE_PATH, &st) == -1) { + if (mkdir(PACKAGE_BASE_PATH, 0755) < 0) { + printf("Failed to create directory %s [%d]\n", PACKAGE_BASE_PATH, errno); + } + } + + if (stat(CONFIG_PATH, &st) == -1) { + if (mkdir(CONFIG_PATH, 0755) < 0) { + printf("Failed to create directory %s [%d]\n", CONFIG_PATH, errno); + } + } + + lsdir(OCRE_BASE_PATH); +} diff --git a/src/shared/platform/posix/core_internal.h b/src/shared/platform/posix/core_internal.h new file mode 100644 index 00000000..857134e7 --- /dev/null +++ b/src/shared/platform/posix/core_internal.h @@ -0,0 +1,139 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef OCRE_CORE_INTERNAL_H +#define OCRE_CORE_INTERNAL_H + +#include +#include +#include +#include +#include + +#include + +// Config macros +//#define CONFIG_OCRE_CONTAINER_MESSAGING /*!< Enable container messaging support */ +#define CONFIG_OCRE_NETWORKING /*!< Enable networking support */ + +// Base paths for the application +#define OCRE_BASE_PATH "./ocre" /*!< Base directory for Ocre resources */ + +#define APP_RESOURCE_PATH OCRE_BASE_PATH "/images" /*!< Path to container images */ +#define PACKAGE_BASE_PATH OCRE_BASE_PATH "/manifests" /*!< Path to package manifests */ +#define CONFIG_PATH OCRE_BASE_PATH "/config" /*!< Path to configuration files */ + +/** + * @brief Ignore Zephyr's log module registration on POSIX. + */ +#define LOG_MODULE_REGISTER(name, level) + +/** + * @brief Log an error message. + */ +#define LOG_ERR(fmt, ...) printf("[ERROR] " fmt "\n", ##__VA_ARGS__) + +/** + * @brief Log a warning message. + */ +#define LOG_WRN(fmt, ...) printf("[WARNING] " fmt "\n", ##__VA_ARGS__) + +/** + * @brief Log an informational message. + */ +#define LOG_INF(fmt, ...) printf("[INFO] " fmt "\n", ##__VA_ARGS__) + +// Constants + +/** + * @brief Global argument count, used for command-line argument handling. + */ +extern int g_argc; + +/** + * @brief Maximum length for SHA256 string representations. + */ +#define OCRE_SHA256_LEN 128 + +/** + * @brief Maximum number of containers supported. + */ +#define CONFIG_MAX_CONTAINERS 10 + +/** + * @brief Default stack size for container supervisor threads (in bytes). + */ +#define OCRE_CS_THREAD_STACK_SIZE (1024 * 1024) + +/** + * @brief Application version string. + */ +#define APP_VERSION_STRING "0.0.0-dev" + +/** + * @brief Default heap buffer size for WAMR (in bytes). + */ +#define CONFIG_OCRE_WAMR_HEAP_BUFFER_SIZE 512000 + +/** + * @brief Default heap size for a container (in bytes). + */ +#define CONFIG_OCRE_CONTAINER_DEFAULT_HEAP_SIZE 4096 + +/** + * @brief Default stack size for a container (in bytes). + */ +#define CONFIG_OCRE_CONTAINER_DEFAULT_STACK_SIZE 4096 * 16 + +/** + * @brief Default stack size for container threads (in bytes). + */ +#define CONTAINER_THREAD_STACK_SIZE 1024 * 1024 + +/** + * @brief Structure representing a thread in the Ocre runtime. + */ +struct core_thread { + pthread_t tid; /*!< POSIX thread identifier */ + void *stack; /*!< Pointer to thread stack memory */ + size_t stack_size; /*!< Size of the thread stack */ + uint32_t user_options; /*!< User-defined options for the thread */ +}; + +/** + * @brief Structure representing a mutex in the Ocre runtime. + */ +struct core_mutex { + pthread_mutex_t native_mutex; /*!< POSIX mutex */ +}; + +#define MQ_DEFAULT_PRIO 0 /*!< Default message queue priority */ + +/** + * @brief Structure representing a message queue in the Ocre runtime. + */ +struct core_mq { + mqd_t msgq; /*!< POSIX message queue descriptor */ +}; + +/** + * @brief Timer callback function type. + * + * @param user_data Pointer to user data passed to the callback. + */ +typedef void (*core_timer_callback_t)(void *user_data); + +/** + * @brief Structure representing a timer in the Ocre runtime. + */ +struct core_timer { + timer_t timerid; /*!< POSIX timer identifier */ + core_timer_callback_t cb; /*!< Timer callback function */ + void *user_data; /*!< User data for the callback */ +}; + +#endif /* OCRE_CORE_INTERNAL_H */ diff --git a/src/shared/platform/posix/core_memory.c b/src/shared/platform/posix/core_memory.c new file mode 100644 index 00000000..0e13a8c0 --- /dev/null +++ b/src/shared/platform/posix/core_memory.c @@ -0,0 +1,18 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "ocre_core_external.h" + +#include + +void *core_malloc(size_t size) { + return malloc(size); +} + +void core_free(void *ptr) { + free(ptr); +} diff --git a/src/shared/platform/posix/core_misc.c b/src/shared/platform/posix/core_misc.c new file mode 100644 index 00000000..e1b0acde --- /dev/null +++ b/src/shared/platform/posix/core_misc.c @@ -0,0 +1,37 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "ocre_core_external.h" + +#include +#include +#include + +void core_sleep_ms(int milliseconds) +{ + struct timespec ts; + int res; + + if (milliseconds < 0) + { + errno = EINVAL; + return -1; + } + + ts.tv_sec = milliseconds / 1000; + ts.tv_nsec = (milliseconds % 1000) * 1000000; + + do { + res = nanosleep(&ts, &ts); + } while (res && errno == EINTR); + + return res; +} + +void core_yield(void) { + sched_yield(); +} diff --git a/src/shared/platform/posix/core_mq.c b/src/shared/platform/posix/core_mq.c new file mode 100644 index 00000000..e9afa8c8 --- /dev/null +++ b/src/shared/platform/posix/core_mq.c @@ -0,0 +1,65 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "ocre_core_external.h" + +#include +#include + +int core_mq_init(core_mq_t *mq, const char *name, size_t msg_size, uint32_t max_msgs) +{ + struct mq_attr attr; + + // Configure the message queue attributes + attr.mq_flags = 0; // Blocking mode + attr.mq_maxmsg = max_msgs; // Maximum number of messages in the queue + attr.mq_msgsize = msg_size; // Size of each message + attr.mq_curmsgs = 0; // Number of messages currently in the queue + + // Try to unlink the message queue, but ignore error if it doesn't exist + if (mq_unlink(name) == -1 && errno != ENOENT) + { + perror("Main: mq_unlink"); + return -errno; + } + + // Create the message queue + mq->msgq = mq_open(name, O_CREAT | O_RDWR, 0644, &attr); + if (mq->msgq == (mqd_t)-1) { + perror("Failed to create message queue"); + return -errno; + } + return 0; +} + +int core_mq_send(core_mq_t *mq, const void *data, size_t msg_len) +{ + int ret = mq_send(mq->msgq, (const char *)data, msg_len, MQ_DEFAULT_PRIO); + + if (ret == -1) { + perror("Failed to send message"); + } + return ret; +} + +int core_mq_recv(core_mq_t *mq, void *data) +{ + struct mq_attr attr; + unsigned int priority; + + // Get message queue attributes + if (mq_getattr(mq->msgq, &attr) == -1) { + perror("Failed to get message queue attributes"); + return -errno; + } + ssize_t bytes_received = mq_receive(mq->msgq, (char *)data, attr.mq_msgsize, &priority); + if (bytes_received == -1) { + perror("Failed to receive message"); + return -errno; + } + return bytes_received; +} diff --git a/src/shared/platform/posix/core_mutex.c b/src/shared/platform/posix/core_mutex.c new file mode 100644 index 00000000..a13753da --- /dev/null +++ b/src/shared/platform/posix/core_mutex.c @@ -0,0 +1,27 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "ocre_core_external.h" +#include +#include + +int core_mutex_init(core_mutex_t *mutex) { + if (!mutex) return -1; + return pthread_mutex_init(&mutex->native_mutex, NULL); +} + +int core_mutex_destroy(core_mutex_t *mutex) { + return pthread_mutex_destroy(&mutex->native_mutex); +} + +int core_mutex_lock(core_mutex_t *mutex) { + return pthread_mutex_lock(&mutex->native_mutex); +} + +int core_mutex_unlock(core_mutex_t *mutex) { + return pthread_mutex_unlock(&mutex->native_mutex); +} diff --git a/src/shared/platform/posix/core_thread.c b/src/shared/platform/posix/core_thread.c new file mode 100644 index 00000000..f6763e5b --- /dev/null +++ b/src/shared/platform/posix/core_thread.c @@ -0,0 +1,77 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "ocre_core_external.h" +#include +#include +#include +#include +#include +#include + + +static void *thread_entry(void *arg) { + struct { + core_thread_func_t func; + void *user_arg; + char name[16]; + } *entry = arg; + + if (entry->name[0]) + pthread_setname_np(pthread_self(), entry->name); + + entry->func(entry->user_arg); + free(entry); + return NULL; +} + +int core_thread_create(core_thread_t *thread, core_thread_func_t func, void *arg, const char *name, size_t stack_size, int priority) { + if (!thread || !func) return -1; + pthread_attr_t attr; + int ret; + + if (stack_size < PTHREAD_STACK_MIN) { + fprintf(stderr, "STACK_SIZE must be at least PTHREAD_STACK_MIN (%zu)\n", (size_t)PTHREAD_STACK_MIN); + return -1; + } + + thread->stack_size = stack_size; + if (posix_memalign(&thread->stack, sysconf(_SC_PAGESIZE), stack_size) != 0) { + perror("posix_memalign"); + return -1; + } + + pthread_attr_init(&attr); + pthread_attr_setstack(&attr, thread->stack, stack_size); + + // Prepare entry struct for name passing + struct { + core_thread_func_t func; + void *user_arg; + char name[16]; + } *entry = malloc(sizeof(*entry)); + entry->func = func; + entry->user_arg = arg; + strncpy(entry->name, name ? name : "", sizeof(entry->name) - 1); + entry->name[sizeof(entry->name) - 1] = '\0'; + + ret = pthread_create(&thread->tid, &attr, thread_entry, entry); + pthread_attr_destroy(&attr); + if (ret != 0) { + free(thread->stack); + free(entry); + return -1; + } + return 0; +} + +void core_thread_destroy(core_thread_t *thread) { + if (!thread) return; + pthread_cancel(thread->tid); + pthread_join(thread->tid, NULL); + free(thread->stack); +} diff --git a/src/shared/platform/posix/core_timer.c b/src/shared/platform/posix/core_timer.c new file mode 100644 index 00000000..0692de34 --- /dev/null +++ b/src/shared/platform/posix/core_timer.c @@ -0,0 +1,52 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "ocre_core_external.h" + +#include +#include +#include +#include + +static void posix_timer_cb(union sigval sv) { + core_timer_t *timer = (core_timer_t *)sv.sival_ptr; + if (timer->cb) { + timer->cb(timer->user_data); + } +} + +int core_timer_init(core_timer_t *timer, core_timer_callback_t cb, void *user_data) { + if (!timer) return -1; + struct sigevent sev = {0}; + timer->cb = cb; + timer->user_data = user_data; + sev.sigev_notify = SIGEV_THREAD; + sev.sigev_notify_function = posix_timer_cb; + sev.sigev_value.sival_ptr = timer; + return timer_create(CLOCK_REALTIME, &sev, &timer->timerid); +} + +int core_timer_start(core_timer_t *timer, int timeout_ms, int period_ms) { + if (!timer) return -1; + struct itimerspec its; + if (timeout_ms == 0 && period_ms > 0) { + its.it_value.tv_sec = period_ms / 1000; + its.it_value.tv_nsec = (period_ms % 1000) * 1000000; + } else { + its.it_value.tv_sec = timeout_ms / 1000; + its.it_value.tv_nsec = (timeout_ms % 1000) * 1000000; + } + its.it_interval.tv_sec = period_ms / 1000; + its.it_interval.tv_nsec = (period_ms % 1000) * 1000000; + return timer_settime(timer->timerid, 0, &its, NULL); +} + +int core_timer_stop(core_timer_t *timer) { + if (!timer) return -1; + struct itimerspec its = {0}; + return timer_settime(timer->timerid, 0, &its, NULL); +} diff --git a/src/shared/platform/posix/ocre_internal.cmake b/src/shared/platform/posix/ocre_internal.cmake new file mode 100644 index 00000000..bc2315c9 --- /dev/null +++ b/src/shared/platform/posix/ocre_internal.cmake @@ -0,0 +1,112 @@ +include(CheckPIESupported) +option(BUILD_SHARED_LIBS "Build using shared libraries" OFF) +set (CMAKE_VERBOSE_MAKEFILE OFF) + +# Build third-party libs +add_subdirectory(${OCRE_ROOT_DIR}/src/ocre/utils/c-smf) + +# Reset default linker flags +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +# Set WAMR_BUILD_TARGET, currently values supported: +# "X86_64", "AMD_64", "X86_32", "AARCH64[sub]", "ARM[sub]", "THUMB[sub]", +# "MIPS", "XTENSA", "RISCV64[sub]", "RISCV32[sub]" +if (NOT DEFINED WAMR_BUILD_TARGET) + if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)") + set (WAMR_BUILD_TARGET "AARCH64") + if (NOT DEFINED WAMR_BUILD_SIMD) + set (WAMR_BUILD_SIMD 1) + endif () + elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64") + set (WAMR_BUILD_TARGET "RISCV64") + elseif (CMAKE_SIZEOF_VOID_P EQUAL 8) + set (WAMR_BUILD_TARGET "X86_64") + if (NOT DEFINED WAMR_BUILD_SIMD) + set (WAMR_BUILD_SIMD 1) + endif () + elseif (CMAKE_SIZEOF_VOID_P EQUAL 4) + set (WAMR_BUILD_TARGET "X86_32") + else () + message(SEND_ERROR "Unsupported build target platform!") + endif () +endif () + +# WAMR Options +set (WAMR_BUILD_PLATFORM "linux") +set (WAMR_BUILD_INTERP 1) +set (WAMR_BUILD_FAST_INTERP 0) +set (WAMR_BUILD_AOT 0) +set (WAMR_BUILD_JIT 0) +set (WAMR_BUILD_LIBC_BUILTIN 0) +set (WAMR_BUILD_LIBC_WASI 1) +set (WAMR_BUILD_LIB_PTHREAD 1) +set (WAMR_BUILD_REF_TYPES 1) +set (WASM_ENABLE_LOG 1) + +if (NOT DEFINED WAMR_BUILD_GLOBAL_HEAP_POOL) + set (WAMR_BUILD_GLOBAL_HEAP_POOL 1) +endif () +if (NOT DEFINED WAMR_BUILD_GLOBAL_HEAP_SIZE) + set (WAMR_BUILD_GLOBAL_HEAP_SIZE 32767) +endif () + +set (WAMR_ROOT_DIR "wasm-micro-runtime") +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +check_pie_supported() + +set(component_sources + # Component support + ${OCRE_ROOT_DIR}/src/ocre/component/component.c + + # Components + ${OCRE_ROOT_DIR}/src/ocre/ocre_container_runtime/ocre_container_runtime.c + ${OCRE_ROOT_DIR}/src/ocre/components/container_supervisor/cs_main.c + ${OCRE_ROOT_DIR}/src/ocre/components/container_supervisor/cs_sm.c + ${OCRE_ROOT_DIR}/src/ocre/components/container_supervisor/cs_sm_impl.c +) + +set(lib_sources + ${OCRE_ROOT_DIR}/src/ocre/sm/sm.c + # Platform + ${OCRE_ROOT_DIR}/src/shared/platform/posix/core_fs.c + ${OCRE_ROOT_DIR}/src/shared/platform/posix/core_thread.c + ${OCRE_ROOT_DIR}/src/shared/platform/posix/core_mutex.c + ${OCRE_ROOT_DIR}/src/shared/platform/posix/core_mq.c + ${OCRE_ROOT_DIR}/src/shared/platform/posix/core_misc.c + ${OCRE_ROOT_DIR}/src/shared/platform/posix/core_memory.c + ${OCRE_ROOT_DIR}/src/shared/platform/posix/core_timer.c + # APIs + ${OCRE_ROOT_DIR}/src/ocre/api/ocre_api.c + # Utils + ${OCRE_ROOT_DIR}/src/ocre/utils/strlcat.c + ${OCRE_ROOT_DIR}/src/ocre/container_messaging_posix/messaging.c +) + +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +set(app_sources + ${OCRE_ROOT_DIR}/src/samples-mini/posix/main.c + ${UNCOMMON_SHARED_SOURCE} + ${WAMR_RUNTIME_LIB_SOURCE} + ${lib_sources} + ${component_sources} +) + +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) +target_link_libraries(vmlib ${LLVM_AVAILABLE_LIBS} ${UV_A_LIBS} -lm -ldl -lpthread) + +add_executable(app ${app_sources}) +target_include_directories(app PUBLIC + ${OCRE_ROOT_DIR}/src + ${OCRE_ROOT_DIR}/src/ocre + ${OCRE_ROOT_DIR}/src/shared/platform +) + +set_target_properties(app PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_link_libraries(app smf vmlib -lm -lpthread) + +add_compile_options(-O0 -Wno-unknown-attributes -Wall -Wextra) + +add_dependencies(app generate_messages) diff --git a/src/ocre/fs/fs.c b/src/shared/platform/zephyr/core_fs.c similarity index 85% rename from src/ocre/fs/fs.c rename to src/shared/platform/zephyr/core_fs.c index ccdce694..310f4a5b 100644 --- a/src/ocre/fs/fs.c +++ b/src/shared/platform/zephyr/core_fs.c @@ -10,13 +10,14 @@ LOG_MODULE_REGISTER(filesystem, OCRE_LOG_LEVEL); #include +#include #include #include #include #include #include #include -#include "fs.h" +#include "ocre_core_external.h" #define MAX_PATH_LEN LFS_NAME_MAX @@ -32,6 +33,44 @@ LOG_MODULE_REGISTER(filesystem, OCRE_LOG_LEVEL); } while (false) #endif +int core_construct_filepath(char *path, size_t len, char *name) { + return snprintf(path, len, "/lfs/ocre/images/%s.bin", name); +} + +int core_filestat(const char *path, size_t *size) { + struct fs_dirent entry; + int ret = fs_stat(path, &entry); + if (ret == 0 && size) { + *size = entry.size; + } + return ret; +} + +int core_fileopen(const char *path, void **handle) { + struct fs_file_t *file = core_malloc(sizeof(struct fs_file_t)); + if (!file) return -ENOMEM; + fs_file_t_init(file); + int ret = fs_open(file, path, FS_O_READ); + if (ret < 0) { + core_free(file); + return ret; + } + *handle = file; + return 0; +} + +int core_fileread(void *handle, void *buffer, size_t size) { + struct fs_file_t *file = (struct fs_file_t *)handle; + return fs_read(file, buffer, size); +} + +int core_fileclose(void *handle) { + struct fs_file_t *file = (struct fs_file_t *)handle; + int ret = fs_close(file); + core_free(file); + return ret; +} + static int lsdir(const char *path) { int res; struct fs_dir_t dirp; diff --git a/src/shared/platform/zephyr/core_internal.h b/src/shared/platform/zephyr/core_internal.h new file mode 100644 index 00000000..571a77ba --- /dev/null +++ b/src/shared/platform/zephyr/core_internal.h @@ -0,0 +1,118 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef OCRE_CORE_INTERNAL_H +#define OCRE_CORE_INTERNAL_H + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +// clang-format off + +/** + * @brief Filesystem mount point for Ocre resources. + */ +#define FS_MOUNT_POINT "/lfs" + +/** + * @brief Base directory for Ocre resources. + */ +#define OCRE_BASE_PATH FS_MOUNT_POINT "/ocre" + +/** + * @brief Path to container images. + */ +#define APP_RESOURCE_PATH OCRE_BASE_PATH "/images" + +/** + * @brief Path to package manifests. + */ +#define PACKAGE_BASE_PATH OCRE_BASE_PATH "/manifests" + +/** + * @brief Path to configuration files. + */ +#define CONFIG_PATH OCRE_BASE_PATH "/config/" + +// Constants + +/** + * @brief Default stack size for container supervisor threads (in bytes). + */ +#define OCRE_CS_THREAD_STACK_SIZE 4096 + +/** + * @brief Default stack size for container threads (in bytes). + */ +#define CONTAINER_THREAD_STACK_SIZE 8192 + +/** + * @brief Default stack size for event threads (in bytes). + */ +#define EVENT_THREAD_STACK_SIZE 2048 + +/** + * @brief Maximum length for SHA256 string representations. + */ +#define OCRE_SHA256_LEN 128 + +/** + * @brief Structure representing a thread in the Ocre runtime (Zephyr). + */ +struct core_thread { + struct k_thread thread; /*!< Zephyr thread structure */ + k_tid_t tid; /*!< Zephyr thread identifier */ + k_thread_stack_t *stack; /*!< Pointer to thread stack memory */ + size_t stack_size; /*!< Size of the thread stack */ + uint32_t user_options; /*!< User-defined options for the thread */ +}; + +/** + * @brief Structure representing a mutex in the Ocre runtime (Zephyr). + */ +struct core_mutex { + struct k_mutex native_mutex; /*!< Zephyr native mutex */ +}; + +/** + * @brief Default timeout value for message queue operations. + */ +#define MQ_DEFAULT_TIMEOUT K_NO_WAIT + +/** + * @brief Structure representing a message queue in the Ocre runtime (Zephyr). + */ +struct core_mq { + struct k_msgq msgq; /*!< Zephyr message queue */ + char __aligned(4) *msgq_buffer; /*!< Message queue buffer (aligned) */ +}; + +/** + * @brief Timer callback function type. + * + * @param user_data Pointer to user data passed to the callback. + */ +typedef void (*core_timer_callback_t)(void *user_data); + +/** + * @brief Structure representing a timer in the Ocre runtime (Zephyr). + */ +struct core_timer { + struct k_timer timer; /*!< Zephyr timer structure */ + core_timer_callback_t cb; /*!< Timer callback function */ + void *user_data; /*!< User data for the callback */ +}; + +#endif diff --git a/src/shared/platform/zephyr/core_memory.c b/src/shared/platform/zephyr/core_memory.c new file mode 100644 index 00000000..3655ed4b --- /dev/null +++ b/src/shared/platform/zephyr/core_memory.c @@ -0,0 +1,18 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "ocre_core_external.h" + +#include + +void *core_malloc(size_t size) { + return k_malloc(size); +} + +void core_free(void *ptr) { + k_free(ptr); +} diff --git a/src/shared/platform/zephyr/core_misc.c b/src/shared/platform/zephyr/core_misc.c new file mode 100644 index 00000000..c689ee77 --- /dev/null +++ b/src/shared/platform/zephyr/core_misc.c @@ -0,0 +1,17 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "ocre_core_external.h" +#include + +void core_sleep_ms(int milliseconds) { + k_msleep(milliseconds); +} + +void core_yield(void) { + k_yield(); +} diff --git a/src/shared/platform/zephyr/core_mq.c b/src/shared/platform/zephyr/core_mq.c new file mode 100644 index 00000000..8378cc43 --- /dev/null +++ b/src/shared/platform/zephyr/core_mq.c @@ -0,0 +1,31 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "ocre_core_external.h" +#include +LOG_MODULE_DECLARE(platform_mq_component, OCRE_LOG_LEVEL); + +int core_mq_init(core_mq_t *mq, const char *name, size_t msg_size, uint32_t max_msgs) +{ + mq->msgq_buffer = k_malloc(msg_size * max_msgs); + k_msgq_init(&mq->msgq, mq->msgq_buffer, msg_size, max_msgs); +} + +int core_mq_send(core_mq_t *mq, const void *data, size_t msg_len) +{ + int ret = k_msgq_put(&mq->msgq, data, MQ_DEFAULT_TIMEOUT); + + if (ret != 0) { + LOG_HEXDUMP_DBG(data, msg_len, "message"); + } + return ret; +} + +int core_mq_recv(core_mq_t *mq, void *data) +{ + return k_msgq_get(&mq->msgq, data, K_FOREVER); +} diff --git a/src/shared/platform/zephyr/core_mutex.c b/src/shared/platform/zephyr/core_mutex.c new file mode 100644 index 00000000..bd8f8034 --- /dev/null +++ b/src/shared/platform/zephyr/core_mutex.c @@ -0,0 +1,28 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "ocre_core_external.h" +#include + +int core_mutex_init(core_mutex_t *mutex) { + if (!mutex) return -1; + k_mutex_init(&mutex->native_mutex); + return 0; +} + +int core_mutex_destroy(core_mutex_t *mutex) { + return 0; +} + +int core_mutex_lock(core_mutex_t *mutex) { + return k_mutex_lock(&mutex->native_mutex, K_FOREVER); +} + +int core_mutex_unlock(core_mutex_t *mutex) { + return k_mutex_unlock(&mutex->native_mutex); +} + diff --git a/src/shared/platform/zephyr/core_thread.c b/src/shared/platform/zephyr/core_thread.c new file mode 100644 index 00000000..1852c892 --- /dev/null +++ b/src/shared/platform/zephyr/core_thread.c @@ -0,0 +1,35 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "ocre_core_external.h" +#include +#include + + +static void thread_entry(void *arg1, void *arg2, void *arg3) { + core_thread_func_t func = (core_thread_func_t)arg2; + void *user_arg = arg3; + func(user_arg); +} + +int core_thread_create(core_thread_t *thread, core_thread_func_t func, void *arg, const char *name, size_t stack_size, int priority) { + if (!thread || !func) return -1; + thread->stack = k_malloc(K_THREAD_STACK_LEN(stack_size)); + if (!thread->stack) return -1; + thread->tid = k_thread_create(&thread->thread, thread->stack, stack_size, + thread_entry, NULL, (void *)func, arg, + priority, 0, K_NO_WAIT); + if (name) + k_thread_name_set(thread->tid, name); + return thread->tid ? 0 : -1; +} + +void core_thread_destroy(core_thread_t *thread) { + if (!thread) return; + k_thread_abort(thread->tid); + k_free(thread->stack); +} diff --git a/src/shared/platform/zephyr/core_timer.c b/src/shared/platform/zephyr/core_timer.c new file mode 100644 index 00000000..10da2d32 --- /dev/null +++ b/src/shared/platform/zephyr/core_timer.c @@ -0,0 +1,37 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "ocre_core_external.h" +#include + + +static void zephyr_timer_cb(struct k_timer *ktimer) { + core_timer_t *otimer = CONTAINER_OF(ktimer, core_timer_t, timer); + if (otimer->cb) { + otimer->cb(otimer->user_data); + } +} + +int core_timer_init(core_timer_t *timer, core_timer_callback_t cb, void *user_data) { + if (!timer) return -1; + timer->cb = cb; + timer->user_data = user_data; + k_timer_init(&timer->timer, zephyr_timer_cb, NULL); + return 0; +} + +int core_timer_start(core_timer_t *timer, int timeout_ms, int period_ms) { + if (!timer) return -1; + k_timer_start(&timer->timer, K_MSEC(timeout_ms), K_MSEC(period_ms)); + return 0; +} + +int core_timer_stop(core_timer_t *timer) { + if (!timer) return -1; + k_timer_stop(&timer->timer); + return 0; +} diff --git a/src/shared/platform/zephyr/ocre_internal.cmake b/src/shared/platform/zephyr/ocre_internal.cmake new file mode 100644 index 00000000..188737f7 --- /dev/null +++ b/src/shared/platform/zephyr/ocre_internal.cmake @@ -0,0 +1,123 @@ +# Zephyr-specific version defines +message("VERSION NUMBER: ${VERSION_NUMBER}") +zephyr_compile_options(-DVERSION_INFO="${VERSION_NUMBER}") +message("BUILD DATE: ${BUILD_DATE}") +zephyr_compile_options(-DVERSION_BUILD_DATE="${BUILD_DATE}") +message("BUILD MACHINE: ${BUILD_MACHINE}") +zephyr_compile_options(-DVERSION_BUILD_MACHINE="${BUILD_MACHINE}") +message("BUILD_INFO: ${BUILD_INFO}") +zephyr_compile_options(-DVERSION_BUILD_INFO="${BUILD_INFO}") + +# Determine the ISA of the target and set appropriately +if (DEFINED CONFIG_ISA_THUMB2) + set(TARGET_ISA THUMB) +elseif (DEFINED CONFIG_ISA_ARM) + set(TARGET_ISA ARM) +elseif (DEFINED CONFIG_X86) + set(TARGET_ISA X86_32) +elseif (DEFINED CONFIG_XTENSA) + set(TARGET_ISA XTENSA) +elseif (DEFINED CONFIG_RISCV) + set(TARGET_ISA RISCV32) +elseif (DEFINED CONFIG_ARCH_POSIX) + set(TARGET_ISA X86_32) +else () + message(FATAL_ERROR "Unsupported ISA: ${CONFIG_ARCH}") +endif () +message("TARGET ISA: ${TARGET_ISA}") + +add_compile_options(-O0 -Wno-unknown-attributes) + +# WAMR Options +set(WAMR_BUILD_PLATFORM "zephyr") +set(WAMR_BUILD_TARGET ${TARGET_ISA}) +set(WAMR_BUILD_INTERP 1) +set(WAMR_BUILD_FAST_INTERP 0) +set(WAMR_BUILD_AOT 0) +set(WAMR_BUILD_JIT 0) +set(WAMR_BUILD_LIBC_BUILTIN 0) +set(WAMR_BUILD_LIBC_WASI 1) +set(WAMR_BUILD_LIB_PTHREAD 1) +set(WAMR_BUILD_REF_TYPES 1) +set(WASM_ENABLE_LOG 1) + +if(NOT DEFINED WAMR_BUILD_GLOBAL_HEAP_POOL) + set(WAMR_BUILD_GLOBAL_HEAP_POOL 1) +endif() +if(NOT DEFINED WAMR_BUILD_GLOBAL_HEAP_SIZE) + set(WAMR_BUILD_GLOBAL_HEAP_SIZE 32767) +endif() + +set(WAMR_ROOT_DIR ${ZEPHYR_WASM_MICRO_RUNTIME_MODULE_DIR}) +include(${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +zephyr_include_directories( + ${OCRE_ROOT_DIR}/src/ + ${OCRE_ROOT_DIR}/src/ocre + ${OCRE_ROOT_DIR}/src/shared/platform +) + +set(component_sources + # Component support + ${OCRE_ROOT_DIR}/src/ocre/component/component.c + + # Components + ${OCRE_ROOT_DIR}/src/ocre/ocre_container_runtime/ocre_container_runtime.c + ${OCRE_ROOT_DIR}/src/ocre/components/container_supervisor/cs_main.c + ${OCRE_ROOT_DIR}/src/ocre/components/container_supervisor/cs_sm.c + ${OCRE_ROOT_DIR}/src/ocre/components/container_supervisor/cs_sm_impl.c +) + +# Collect all sources in one list +set(lib_sources + # Libraries + ${OCRE_ROOT_DIR}/src/ocre/sm/sm.c + ${OCRE_ROOT_DIR}/src/ocre/shell/ocre_shell.c + # Platform + ${OCRE_ROOT_DIR}/src/shared/platform/zephyr/core_fs.c + ${OCRE_ROOT_DIR}/src/shared/platform/zephyr/core_thread.c + ${OCRE_ROOT_DIR}/src/shared/platform/zephyr/core_mutex.c + ${OCRE_ROOT_DIR}/src/shared/platform/zephyr/core_mq.c + ${OCRE_ROOT_DIR}/src/shared/platform/zephyr/core_misc.c + ${OCRE_ROOT_DIR}/src/shared/platform/zephyr/core_memory.c + ${OCRE_ROOT_DIR}/src/shared/platform/zephyr/core_timer.c + + # Ocre APIs + ${OCRE_ROOT_DIR}/src/ocre/api/ocre_api.c + ${OCRE_ROOT_DIR}/src/ocre/api/ocre_common.c +) + +# Conditionally add sources +if(CONFIG_OCRE_TIMER) + list(APPEND lib_sources ${OCRE_ROOT_DIR}/src/ocre/ocre_timers/ocre_timer.c) +endif() + +if(CONFIG_OCRE_SENSORS) + list(APPEND lib_sources ${OCRE_ROOT_DIR}/src/ocre/ocre_sensors/ocre_sensors.c) +endif() + +if(CONFIG_RNG_SENSOR) + list(APPEND lib_sources ${OCRE_ROOT_DIR}/src/ocre/ocre_sensors/rng_sensor.c) +endif() + +if(DEFINED CONFIG_OCRE_GPIO) + list(APPEND lib_sources ${OCRE_ROOT_DIR}/src/ocre/ocre_gpio/ocre_gpio.c) +endif() + +if(CONFIG_OCRE_CONTAINER_MESSAGING) + list(APPEND lib_sources ${OCRE_ROOT_DIR}/src/ocre/container_messaging/messaging.c) +endif() + +# Add all sources to the app target at once +target_sources(app PRIVATE + ${WAMR_RUNTIME_LIB_SOURCE} + ${lib_sources} + ${component_sources} + ${OCRE_ROOT_DIR}/src/samples-mini/zephyr/main.c +) + +add_dependencies(app generate_messages) + +if(NOT "${OCRE_INPUT_FILE}" STREQUAL "") + add_dependencies(app generate_ocre_file) +endif() diff --git a/wasm-micro-runtime b/wasm-micro-runtime new file mode 160000 index 00000000..e3b02441 --- /dev/null +++ b/wasm-micro-runtime @@ -0,0 +1 @@ +Subproject commit e3b024413d61823ec135c813ca4a0966399fe51b