Sedona

Apps

Overview

The standard design for deploying applications in the Sedona Framework is to utilize the component model to separate the code from the application. In this architecture you have a clean boundary between the code packaged up as kits and the application packaged up as a tree of components. This model allows you to build applications simply by assembling components together, configuring their properties, and linking slots together to define control flow. This style of programming is especially amenable to graphical programming.

So when talking about a Sedona Framework application, we are really talking about a tree of components assembled together. An application is purely declarative, all the code is encapsulated in the kits. The application itself is stored as a file using one of two file formats:

You can convert between the two file formats using sedonac.

Boot Strap

Applications are boot strapped using the following phases:

  1. Loaded: all components are loaded from SAB file into memory and have their Component.loaded callback invoked.
  2. Start: all components have their Component.start callback invoked.
  3. Running: the application enters in main loop for execution (described next).

Execution

The Sedona Framework's execution model is based a single threaded main loop with a fixed scan rate:

  1. Recursively execute components (children first). For each component:
    1. Propagate incoming links
    2. Call the Component.execute virtual method
  2. Give any remaining time in the scan cycle to services via Service.work
  3. If no services have remaining work, then relinquish CPU (via yield or sleep) until next cycle

Yield vs Sleep

Depending on the underlying execution environment, the App can either return control to the OS (exit the VM) or sleep until it's time to run again.

Preemptive multithreaded OS

These include Windows, Linux or QNX. When a thread calls the OS sleep primitive, other threads are given a chance to run. The VM may be swapped in and out several times during an execute cycle.

Main Loop

In this environment, the Sedona VM executes as the main loop and all other work is done @ ISR level. Once the App completes an execute cycle, it can delay by entering a busy-wait loop.

Cooperative tasking OS

In this environment, a task must return control to the scheduler to allow other tasks a chance to run. The sleep and busy-wait approaches won't work here since the VM will never exit, so the VM must support a clean exit and re-entry. This is accomplished by the yield mechanism described below.

Yield

The Sedona Framework supports yielding to provide a graceful exit (and subsequent reentry) of the VM, allowing the CPU to perform other operations.

Systems that require yield functionality must override the following functions on Platform

yieldRequired() - return true if the VM should exit after each App execute cycle

yield(long yieldTime) - indicates the VM will be exiting and requests to be resumed in yieldTime nanoseconds.

If a Platform returns true to yieldRequired(), then the VM will exit with the error code ERR_YIELD after each pass execution loop. Before exiting, yield(yieldTime) will be called indicating how long the VM wishes to delay before re-entry.

Native code can resume the VM via vmResume(SedonaVM*)

If vmResume is not called before yieldTime expires, App overruns will occur.

Hibernation

When entering hibernation, the App exits and returns control to the bootstrap code. It is similar to yield but it is expected that the hibernation time will be much longer than a typical yield time. Hibernation is driven by application logic and most likely will not occur each App execute cycle, whereas yield must occur each cycle.

To enter the hibernation state, an application calls App.hibernate(). This will set a flag on App and when the current execution cycle is complete, it will cause the App to exit with error code ERR_HIBERNATE. This will gracefully unwind the call stack, returning control to the boot code. The device's boot code should then put the device to sleep. It is a device dependent issue to decide how it will wake up from hibernation. When the device does wake, it should restart the VM with a call to vmResume(SedonaVM*). If your device doesn't support hibernation, then you will need to simulate it using code such as the following:

result = vmRun(&vm);
while (result == ERR_HIBERNATE)
{
  printf("-- Simulated hibernate --\n");
  result = vmResume(&vm);
}

If you are developing Sedona Framework components, applications, or drivers you should keep hibernation in mind. Any software that might run on a battery powered device needs to support hibernation cleanly. This means that function blocks should assume the scan rate might have hibernation pauses. If you have services that need to do something special when hibernating or waking, then you will need to override the functions Service.onHibernate() and Service.onUnhibernate(). Service.onHibernate() will be called prior to VM exit. Service.onUnhibernate() will be called after the VM is resumed and prior to the App execution loop starting back up.

Steady State

Most apps will be fully operational by the end of the first cycle. When hardware I/O is involved, however, an app may need to allow additional time for the hardware to warm up, or for complex logic results to propagate fully to all components. For this purpose, the Sedona Framework provides a "steady state" timing feature that should be used to protect the hardware from reading or writing transient values while the app is starting up.

The steady state feature consists of two pieces:

Note: This feature is not used internally within the App. It only affects behavior of components that use the isSteadyState() method. It is the responsibility of each kit developer to call this method as needed to protect the hardware.

By default, App.timeToSteadyState applies only to the first time the VM is started. Once steady time has elapsed, hibernate/yield will not affect it. If App.hibernationResetsSteadyState is set to true, then the steady state flag will be reset each time device exits hibernation.

Links

Links are the mechanism used to define control flow in an app. Links are said to be from a given component's slot to another component's slot.

Currently only prop-to-prop links are supported. During link propagation, the from-property value is copied into the to-property. This mechanism is often used to link sensor inputs through control logic to actuator outputs.

Services

Services are special components that subclass from sys::Service. Services have three primary characteristics that set them apart from other types of components:

Services are often used to provide functionality to other components. For example the UserService is used to lookup and authenticate user accounts. Many services such as protocol drivers also perform background work to service network messages.

SAX Files

The SAX format is structured as follows:

<sedonaApp>
<schema>
  <kit name='sys'/>
  ...
</schema>
<app>
  <comp name="play" id="1" type="sys::Folder">
    <comp name="rampA" id="7" type="control::Ramp">
      <prop name="min" val="20.00000"/>
      <prop name="max" val="80.00000"/>
    </comp>
    ...
  </comp>
  ...
</app>
<links>
  <link from="/play/rampA.out" to="/play/something.else"/>
  ...
</links>
</sedonaApp>

<sedonaApp> root element that contains:

<kit> defines the kits used by the application:

<comp> defines each component in the application:

<prop> defines the property value of a component within a <comp> element. Supported attributes:

<link> element defines a link between two slots using the format of "/path/comp.slot":

SAB Files

While XML is a nice representation for tools to work with app files, XML is too big and difficult to work with in an embedded device. So we use the SAB format when we need a compact binary representation. Sedona Framework devices typically store their application as an SAB "file" in FLASH (although often it might just be location or sector of FLASH versus a real file system).

Although SAB is a very compact format for applications it must be used with a matching schema. For example, in order for a Sedona Framework device to load a given SAB file, its scode must have the exact same kits and checksums installed.

APIs

You can work with both SAX and SAB files in Java using the sedona.offline APIs: