Skip to content

n7space/n7s-obcp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

91 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

n7s-obcp

OBCP engine based on Micropython, developed as a part of "Model-Based Execution Platform for Space Applications" project (contract 4000146882/24/NL/KK) financed by the European Space Agency.

The On-Board Control Procedure Engine is based on Micropython (https://micropython.org/), and derived from the "embed" port to maximize compatibility with target platforms, such as x86-64 (for testing/demonstration) and ARM Cortex-M7 (e.g., Microchip SAMV71/SAMRH71). It is intended to be integrated within larger C-based Application Software. Interface requirements, for emitting events, as well as interacting with DataPool, Requests and Reports are driven by MBEP-N7S-EP-SRS document, while its high-level design is described in MBEP-N7S-EP-SDD.

The engine exposes a simple C interface (see src/obcp_engine/obcpengine.h) used to integrate the runtime with host application code (datapool, events, IO, time and packet functions are provided by the embedding application via the engine context).

The engine can work in a concurrent, and non-concurrent mode:

  • in the non-concurrent mode, only a single OBCP can be executed at a time,
  • in the concurrent mode, multiple OBCPs can be executed in parallel, each within its own task.

The concurrent mode modifies the Micropython internals by replacing mp_state_ctx global variable with a thread-local instance. This approach is ported from ESA's Micropython evolution for Leon processors, executed within the scope of ESA Contract No. 4000137198/22/NL/MGu/kk. A custom, non-OS dependent implementation of mp_state_ptr is provided instead of the one used in the original ESA contract. Thread-local handling is done by user-provided functions to maximize compatibility with various environments.

The changes are provided as stand-alone files that are patched into the mainline repository during the build process. This approach was taken to avoid forking the Micropython repository, and facilitating backporting into new Micropython versions (via generating patches and reapplying them to a new version). Fork is avoided to reduce future maintenance effort. Submission of the patches to the main repository is deemed infeasible, as:

  • by replacing a single global variable with thread-local values accessed via function calls, performance may be impacted, which is undesired for many Micropython uses.
  • the approach is derived from a previous ESA project in which the original Micropython maintainer was involved, and the modifications were not backported back then.

Usage

Execute 'make embed' to generate the custom embed port.

Execute 'make test' to execute automated tests.

Execute 'make patches' to generate the diffs between the mainline Micropython and the patched code, for ease of review of the changes and possible backporting.

OBCP Engine Interface

The primary entry points are:

  • obcpengine_init(context) — initialize the engine with callback implementations provided in obcp_engine_context_t.
  • obcpengine_execute_py(script, heap, heap_size) — execute a null-terminated Python source string using the supplied heap.
  • obcpengine_execute_mpy(script, script_length, heap, heap_size) — execute precompiled MicroPython bytecode (.mpy) from a buffer.
  • obcpengine_provide_buffer(buffer, length) — provide an I/O buffer that is required if OBCPs use obcppacket module.

Short usage examples (C):

Initialize and run a Python string:

#include "obcp_engine/obcpengine.h"

/* Provide implementations for required callbacks... */
static obcp_engine_context_t ctx = { /* fill callbacks */ };

int main(void) {
		char heap[8 * 1024]; /* example heap */
        static const char *script =
            "import obcpio\n"
            "obcpio.write('Hello, World!')\n";

		if (!obcpengine_init(&ctx)) return -1;
		if (!obcpengine_execute_py(script, heap, sizeof(heap))) return -1;
		return 0;
}

Run a precompiled .mpy buffer:

const uint8_t mpy_blob[] = { /* compiled .mpy bytes */ };
char heap[8 * 1024];

if (!obcpengine_execute_mpy(mpy_blob, sizeof(mpy_blob), heap, sizeof(heap))) {
		/* handle error */
}

Provide a shared buffer for I/O:

uint8_t shared_buf[1024];
obcpengine_provide_buffer(shared_buf, sizeof(shared_buf));

For full callback types and required behaviour see src/obcp_engine/obcpengine.h.

OBCP_ENABLE_CONCURRENT_OBCPS define shall be provided is the engine is to be used in the concurrent mode.

For authoritative example please refer to test/arm-freertos.

OBCP Interface

The below modules and functions are available to Micropython OBCPs. If any is used, the relevant user-side handling function shall be provided as a callback registered in context submited to 'obcpengine_init'. Please refer to 'src/obcp_interface/' for signatures, and 'test/mocks/.c' for examples.

obcpio

  • write(text): Write a text string to the runtime output. Raises RuntimeError if the embedding runtime did not provide obcp_write or if the write fails.

obcpdatapool

  • readintparameter(id) -> int: Read an integer parameter identified by id. Intended to be used also for enumerations. Raises RuntimeError if datapool callbacks are not provided or the access fails.
  • writeintparameter(id, value): Write an integer parameter.
  • readfloatparameter(id) -> float: Read a floating-point parameter.
  • writefloatparameter(id, value): Write a floating-point parameter.
  • readboolparameter(id) -> bool: Read a boolean parameter.
  • writeboolparameter(id, value): Write a boolean parameter.

obcpevents

  • sendevent(event_id): Emit an event with the given integer ID.

obcpcontrol

  • beginstep(id): Begin a control step with the given ID. Returns normally or raises RuntimeError on failure.
  • endstep(id, success): End a control step and indicate success as a boolean.

obcptime

  • wait(milliseconds): Block (via the embedding runtime) for the specified number of milliseconds.
  • waituntil(seconds, milliseconds): Block until an absolute time given by seconds and milliseconds.
  • gettime() -> (seconds, milliseconds): Return current on-board time as a two-element tuple of unsigned integers.

obcppackets

  • ispacketavailable(channel) -> bool: Check if a packet is available on the given channel.
  • getchannelwithpacketavailable() -> int|None: Return the first channel with an available packet, or None if none is available.
  • cansendpacket(channel) -> bool: Check whether a packet can be sent on the given channel.
  • sendpacket(channel, data): Send a packet on channel. data is a bytes-like object; raises RuntimeError if sending fails or callback is not provided.
  • receivepacket(channel, timeout_ms) -> bytes|None: Receive a packet into the engine-provided buffer and return it as bytes, or None if no packet was received before timeout_ms elapsed. A buffer must be provided to the engine using obcpengine_provide_buffer prior to calling this function when using packet I/O.

Notes

  • All functions raise RuntimeError inside MicroPython if the corresponding runtime callback was not supplied via the obcp_engine_context_t or if the runtime reports failure.
  • See src/obcpmods/*/mod*.c for implementation details and src/obcp_engine/obcpengine.h for the engine context and callback signatures.

About

OBCP engine based on Micropython, developed as a part of "Model-Based Execution Platform for Space Applications" project (contract 4000146882/24/NL/KK) financed by the European Space Agency.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors