Overview
Because the Sedona Framework is designed to work in very constrained embedded environments, we have to make design tradeoffs. One of the biggest tradeoffs is using a low level binary format for Sedona Framework application files and the Sox protocol. This compact form requires out-of-band information for encoding and decoding.
For example the binary format does not contain kit, type, or slot definitions, only their numeric ids. So in order to reconstruct the original app from its binary SAB file, we must have some external way to map those ids to the right definitions. Correct versioning is also important, since kits may evolve over time to include new types and new slots which could change their ids. The schema tells us how to create the right mapping for the app, by specifying the kit versions and checksums that were used to create the app.
Kit Parts
Every kit contains zero or more named types. Each type declares zero or more slots. For a given version of a kit, there is a fixed list of types and their declared slots. Each slot has a fixed name, flags, and type. When a kit is compiled from source into a kit zip file, the compiler generates a checksum for this fixed list of types and slots. The combination of a kit name and checksum is called a kit part.
The kit meta-data, checksum, and list of types and slots is included in the kit file as an XML file called "manifest.xml". Here is an example manifest file:
<?xml version='1.0'?> <kitManifest name="sysTest" checksum="da52f78f" version="1.0" vendor="Tridium" description="Test suite for core language and sys APIs" buildHost="BLAZE" buildTime="2007-05-17T16:21:08.030-04:00" > <type id="0" name="AbstractTestComp" base="sys::Component"> <slot id="0" name="az" type="bool"/> <slot id="1" name="ai" type="int"/> </type> <type id="1" name="TestComp" base="sysTest::AbstractTestComp"> <slot id="0" name="z1" type="bool"/> <slot id="1" name="b1" type="byte"/> <slot id="2" name="addF1" type="float" flags="a"/> </type> <type id="2" name="SubTestComp" base="sysTest::TestComp"> <slot id="0" name="sb" type="byte"/> <slot id="1" name="si" type="int"/> </type> </kitManifest>
The checksum is based only on the kit's component types (those that
subclass sys::Component
) and slots.
The checksum is not based on variable meta-data such as the kit's version number,
or on non-component types which could never appear in an app definition.
This means that multiple versions of the same kit might share the same checksum
if no component types or slots have been modified between versions.
Don't confuse version with checksum. Version represents a revision of the whole kit including its code, algorithms, and when it was built; the version only changes when the developer chooses to specify a different version number. Checksum represents a revision of the declared types and slots, and it changes automatically whenever the type or slot definitions change.
Kit Database
Within a Sedona Framework installation we store all the local copies of
kits with the file pattern:
{Sedona home}/kits/{kit}/{kit}-{checksum}-{version}.kit
For example:
{Sedona home}/ kits/ control/ control-cdf5f0f0-1.0.28.kit control-cdf5f0f0-1.0.29.kit control-1239c0de-1.0.29.kit sys/ sys-ef94b11d-1.0.28.kit . . .
We call this directory the kit database. It can store
multiple versions of each kit with different checksums.
The sedona.kit.*
Java APIs can be used to work with the kit database.
Manifest Database
Kit files contain all the information we need when working with schema.
However, to interface with a device that is using a kit we don't need the full
kit file, only the XML manifest file that represents it.
So in addition to the kit database, we also create a manifest database with
the file pattern: {Sedona home}/manifests/{kit}/{kit}-{checksum}.xml
For example:
{Sedona home}/ manifests/ control/ control-cdf5f0f0.xml control-1239c0de.xml sys/ sys-ef94b11d.xml . . .
By storing the manifests in a separate database, we don't need to use the kit files themselves to work with the kits' types. This is typically more efficient, and in addition it allows a vendor to publish just the manifests for their kits as opposed to entire kit files.
Note, however, that manifests cannot be used to create an SCode image. Building an SCode image requires access to the actual kit files.
The sedona.manifest.*
APIs are used to work with manifests
and the manifest database via the Java toolkit. The KitManifest
class represents the information stored a kit manifest file and provides
methods for encoding and decoding from XML. The ManifestDb
class is used to load and save KitManifest
s to the file
system.
Schemas
A Sedona Framework application is composed of multiple kits. A specific list of kit parts (kits at a specific checksum revision) is called a schema. Matching schemas guarantee compatibility between the app running on the device and the app's representation elsewhere, such as in a PC-based tool.
Only when a kit manifest is accessed for a specific schema can we correctly interpret binary information such as kit id and slot id. For example the kit id for the "control" kit might be 3 in one schema, but 5 in another schema that has a different list of kit parts. Slot ids for a given kit part can also change across schemas, if there are changes to slots inherited from base types.
The Java API for working with schemas is in sedona.*
.
Schemas are built with the sedona.Schema
class from a list of KitParts
which are resolved against
the manifest database. Assuming all the kit parts can be resolved to
kit manifests, we can build a complete representation of the schema
including the full list of kits, types, and slots along with
their respective ids. The Schema
is then used
to work with binary formats such as ".sab" files and Sox messages.
Kits versus Manifests
So when do you need a kit and when do you only need a manifest? This table helps summarize the differences:
Encapsulates | Versioned By | Uses | |
---|---|---|---|
Manifest | type and slot schema | checksum | sax, sab, and sox |
Kit | code | version number | compiling dependencies and scode images |
For example when working with an application file or Sox, only the type schemas are needed. No knowledge of the internal code is required. However when compiling, you need the full kit that contains the actual code. So a manifest is somewhat analogous to a C header file, which declares function prototypes but does not contain any code for the functions.
Manifests are versioned with a checksum each time a type or slot definition is modified. Kits are versioned with a version number typically whenever code is modified.
Resolving Manifests
To fully connect to a remote Sedona device, there must be local copies of the kit manifests corresponding to the device's current schema. The recommended rules for locating each kit manifest are:
- Check the local manifest database first; if the manifest is found then use it
- Check the local kit database for the given kit, if found then extract the manifest file and store it in the local manifest database
- Download the manifest from sedonadev.org to the local manifest database
These steps are automatically implemented by the Sedona ManifestDb
API.
If any manifests required by the schema are not found a
MissingKitManifestException
is thrown, which contains information about
the specific manifests that are missing.
This information can be used to compose a friendly error message to the user
about why the connection failed, or to request the missing file(s) from the
device itself (see next section).
Kit Manifest Server
Beginning with Sedona 1.2, the Sedona Sox Client supports retrieving missing
manifest files from the remote Sedona device itself.
If it detects the MissingKitManifestException
when resolving the
device's schema locally, it requests the missing manifests from the device.
If the device is set up to serve the requested manifests it will send them.
This feature can make it easier to deploy new devices,
as they can be shipped with the necessary manifests installed on the device
rather than requiring them to be installed separately in advance on
the Sox Client host.
Serving kit manifests is accomplished very simply, using a special filename prefix called a scheme to indicate to the device where to look for the file in its local file system. This allows the manifest retrieval to be accomplished using the regular Sox file transfer protocol. When the Sox Client detects that a manifest is not found by the usual rules described above, it sends a Sox "get file" request to the device using the filename of the missing manifest adding the appropriate scheme prefix. When the device receives the request, it recognizes by the prefix that this is a manifest file, and looks in its local database for the one requested. If the file is found, it sends it via Sox back to the requesting client.
Implementation Details
To make this feature work on a given Sedona platform, the following things must be true:
- The required manifest file(s) must be stored on the device
- The "m:" scheme must be implemented on the remote Sedona device (see below)
- The
sedonadev.autodownload
property in sedona.properties must be set to false, or omitted entirely
A scheme is simply a prefix to the filename, such as "m:", which is translated on the remote device into a local path where the file should be located. There are two ways to implement a scheme:
- It can be implemented entirely at the native level, encapsulated in the
appropriate
sys::FileStore
natives - It can be implemented at the Sedona level using a platform-specific FileStore subclass.
If it is implemented at the native level, then at the Sedona level the device
treats the prefix as part of the actual filename. The native implementation
of FileStore.doOpen()
must then
strip off the prefix and substitute the path to the local manifest database.
The resulting full path to the file is then passed to the local file system
and handled just like any other file transfer request.
If it is implemented using a FileStore subclass, then the FileStore subclass
should override the accept()
method such that it returns true if
the filename begins with the desired prefix.
Then the open()
method can be overridden to strip off the scheme
prefix and substitute the desired path, or the FileStore subclass may have an
additional native method that provides the appropriate path for all files in
that scheme.
The Sedona 1.2 open source includes a class in the 'win32' platform kit,
called Win32ProvDbFileStore
. This class demonstrates the
second (Sedona-level) implementation strategy described above.
N.B. An ambitious Sedona developer could use this same strategy to provide kit files and/or platform manifests as well as kit manifests.
Review
To summarize the schema pipeline:
- Checksum: Generated by the compiler when compiling a kit file from source, the checksum is based on the declared types and slots in the kit.
- Kit Part: The combination of kit name and kit checksum that uniquely identifies a kit for a specific schema revision.
- Kit Manifest: A file containing a kit's checksum and type definitions, stored as a zip archive entry named "manifest.xml".
- Kit Database: A local database of kit versions, created and maintained by the Sedona Framework Java toolkit.
- Manifest Database: A local database of all the kit manifests that have been accumulated, created, and maintained by the Sedona Framework Java toolkit. These manifests are XML files keyed by kit name and checksum.
- Schema: A list of kit parts aggregated by the Sedona Framework Java toolkit.
A schema defines the full meta-data required to work with binary format
entities with matching schemas.
Each Sedona Framework runtime and application file has a single, fixed schema.
The Java toolkit models schemas via the
sedona.Schema
API.