Overview
Sox is the standard protocol used to communicate with Sedona Framework-enabled devices. Sox provides the following services:
- Version Info: ability to query current version of software.
- Polling: ability to poll for current values.
- COV: ability to subscribe to change of value events.
- RPC: ability to invoke component actions remotely.
- Programming: ability to add/remove/rename/link/unlink components.
- File Transfer: ability to get or put arbitrary streams of bytes.
- Provisioning: ability to backup/upgrade the SVM, kits, or app via file transfers.
Dasp
Sox is designed to be run over UDP via the Dasp protocol. Dasp handles a lot of complexity for networking:
- Dasp manages authenticated sessions, so that we can associate incoming Sox messages against a user account.
- Dasp manages reliability. When Sox sends a message, it doesn't have to worry about retries - Dasp ensures the message gets delivered.
- Dasp manages flow control. Communication such as a file transfer that creates bursts of packets is automatically throttled to the network's capacity by Dasp.
- Dasp is full duplex - once a session is established, either side may initiate a message send.
Dasp also defines some restrictions that Sox must take into consideration:
- Dasp is packet based, so Sox must design messages to fit within the maximum packet size (typically around 500 bytes).
- Dasp is reliable, but unordered, so Sox cannot assume that messages are processed in order.
Design
Sox has a very basic design. All sox messages are formatted with a standard two byte header:
u1 cmd u1 replyNum u1[] payload
Every request is assigned a command identifier, which is a lower
case ASCII letter. For example 'r'
indicates a read
property request. Responses to a request use a capitalized
ASCII letter, for example a read property response has a cmd
identifier of 'R'
.
Every request is tagged with a one byte replyNum. The replyNum is used to associate requests with their responses. Because the replyNum is only byte, there can only 255 outstanding requests (although it is quite likely Dasp will restrict flow control to a much smaller number of requests).
Each specific command has a predefined payload structure.
Sedona Framework APIs
There is a server side Sox implementation written in 100% Sedona code and
deployed in the sox
kit. The
sox
kit includes both the Sox and Dasp protocol stack and depends
on inet
kit for the IP stack. You must have a port of the
inet
kit available for your platform in order to run Sox.
Most often you will put an instance of the SoxService
in your
application. The SoxService opens a configured UDP port (default is 1876). The
SoxService manages authenticating incoming sessions, handling all the sox
commands, and ensuring that sessions are timed out and cleaned up properly.
Java APIs
The open source distribution also includes a full Java implementation of the Dasp/Sox protocol stacks. These stacks include both a client and service side implementation (although typically you will only use the client side). The Java APIs are designed to "Sedona Framework enable" your tools and supervisor applications.
The sedona.dasp
package implements the lower level Dasp
protocol stack. This code is cleanly separated from Sox and may be
used stand alone if desired.
The sedona.sox
package implements the Sox protocol stack
and provides a light weight Java model for Sedona Framework components. The
SoxClient
class provides a Sox aware wrapper around
DaspSession
for sending/receiving the set of sox
commands. Many of the commands use SoxComponent
as
the Java side representation of Sedona Framework components.
The SoxClient
class is also used to initiate get/put
file transfers. You will use these APIs if implementing your own
provisioning strategy.
Messages
The following Sox message commands are defined:
Id | Command | Description |
---|---|---|
a | add | Add a new component to the application |
b | fileRename | Rename or move a file |
c | readComp | Read a component in the application |
d | delete | Delete an component and its children from the application |
e | event | COV event for a subscribed component |
f | fileOpen | Begin a get or put file transfer operation |
i | invoke | Invoke a component action |
k | fileChunk | Receive or send a chunk during a file transfer |
l | link | Add or delete a link |
n | rename | Rename a component |
o | reorder | Reorder a component's children |
q | query | Query installed services |
r | readProp | Read a single property from a component |
s | subscribe | Subscribe to a component for COV events |
u | unsubscribe | Unsubscribe from a component for COV events |
v | version | Query for the kits installed |
w | write | Write the value of a single component property |
y | versionMore | Query for additional version meta-data |
z | fileClose | Close a file transfer operation |
! | error | Response id for a command that could not be processed |
add
The add command is used to add a new component to the application. The configProps must match the schema definition for the type.
req { u1 'a' u1 replyNum u2 parentId u1 kitId u1 typeId str name val[] configProps } res { u1 'A' u1 replyNum u2 compId }
fileRename
Rename a file from one path to another path. Also see the
sys::File
API.
req { u1 'b' u1 replyNum str from str to } res { u1 'B' u1 replyNum }
readComp
Read a given component's state. Component state is broken up into four sections:
- tree: the component's id, name, parent, and children
- config: all the persistent properties (marked with
@config
facet) - runtime: all the transient properties (not marked with
@config
facet) - links: all the links to and from the component
req { u1 'c' u1 replyNum u2 componentId u1 what: 't', 'c', 'r', 'l' (tree, config, runtime, links) } res { u1 'C' u1 replyNum u2 componentId compTree | compProps | compLinks } compTree { u1 't' u1 kitId u1 typeId str name u2 parent u1 permissions u1 numKids u2[numKids] kidIds } compProps { u1 'c' or 'r' for config or runtime, 'C'/'R' if operator only val[] propValues } compLinks { u1 'l' Link[] links u2 0xffff end marker }
delete
Delete a component from the application. This command automatically deletes any child components as well as any links to or from the deleted components.
req { u1 'd' u1 replyNum u2 compId } res { u1 'D' u1 replyNum }
event
The event command packages up a COV event and is pushed asynchronously from the server to client periodically for subscribed components. It uses the same component state model as readComp. Events don't have responses, but rather rely on Dasp's acknowledgements to guarantee delivery.
req { u1 'e' u1 replyNum u2 componentId compTree | compProps | compLinks }
fileOpen
The fileOpen command is used to initiate a get or put file transfer. The chunk size is negotiated such that chunk messages fit within the underlying transport's packet size (for example if running over 6LoWPAN we want to fit each chunk into a single 802.15.4 packet).
On a put, the fileSize is the number of bytes that will be written. On a get, the fileSize is the maximum number of bytes to read. The actual number of bytes read might be less if there are not fileSize bytes available (taking into account the size of the actual file, and the potential offset).
req { u1 'f' u1 replyNum str method ("g" for get, "p" for put) str uri u4 fileSize u2 suggestedChunkSize (suggested by client) headers[] { str name str value } u1 end of headers '\0' } res { u1 'F' u1 replyNum u4 fileSize u2 actualChunkSize headers[] { str name str value } u1 end of headers '\0' }
Name | Value | Method | Default | Description |
---|---|---|---|---|
offset | int |
get, put | 0 | Byte offset to use when reading/writing the uri |
mode | w, m | put | w | File mode to use on a put. 'w' opens the file and truncates it if the file already exists. 'm' opens the file for random access. |
invoke
Invoke an action on a component. This command can be used as an RPC mechanism. Note that the action is executed synchronously; the Sox server blocks until the action completes and returns.
req { u1 'i' u1 replyNum u2 componentId u1 slotId val argument } res { u1 'I' u1 replyNum }
fileChunk
The fileChunk command transfers a single chunk within a file transfer operation. The same message structure is used for both get and put operations. Because Sox is run over Dasp, it is quite possible for chunks to be received out of order.
req { u1 'k' u1 replyNum (ignored) u2 chunkNum u2 chunkSize u1[chunkSize] chunk }
link
The link command is used to add or delete a link between two Component slots in the application.
req { u1 'l' u1 replyNum u1 'a' | 'd' (add/delete) u2 fromCompId u1 fromSlotId u2 toCompId u1 toSlotId } res { u1 'L' u1 replyNum }
rename
Rename is used to change a Component's name.
req { u1 'n' u1 replyNum u2 compId str newName } res { u1 'N' u1 replyNum }
reorder
Reorder a component's child components.
req { u1 'o' u1 replyNum u2 compId u1 numChildren u2[] childrenIds } res { u1 'O' u1 replyNum }
readProp
Read a single property value from a component.
req { u1 'r' u1 replyNum u2 compId u1 propId } res { u1 'R' u1 replyNum u1 any code var propValue }
subscribe
The subscribe command allows you to subscribe to multiple components by registering for a set of change events. The subscribe command will return the number of components that were actually subscribed. This number may be less than the number of components requested if the user does not have permissions on a component, or if the component does not exist anymore
You can also subscribe to all tree events (instead of subscribing to individual component tree events). In this case, the reponse contains no payload.
req { u1 's' u1 replyNum u1 whatMask: tree=0x1 config=0x2 rt=0x4 links=0x8 0xff all tree u1 num: u2[] compIds: The ids of all the components to subscribe to } res { u1 'S' u1 replyNum u1 numSubscribed: The actual number of components that were subscribed }
unsubscribe
Unregister a subscription. The client will stop receiving change events for the set of components specified.
req { u1 'u' u1 replyNum u1 whatMask: tree=0x1 config=0x2 rt=0x4 links=0x8 0xff all tree u1 num u2[] compIds } res { u1 'U' u1 replyNum }
version
Get the list of installed kits and their checksums, which defines the schema.
versionReq { u1 'v' u1 replyNum } versionRes { u1 'V' u1 replyNum u1 kitCount kits[kitCount] { str name i4 checksum } }
write
Set a component property value to a specific value.
req { u1 'w' u1 replyNum u2 componentId u1 slotId val value } res { u1 'W' u1 replyNum }
versionMore
Get extra version information, which includes the version of the installed kits plus platform specific name/value pairs.
Name | Value | Notes |
---|---|---|
soxVer | Sox protocol version | Introduced in build 1.0.45 (see discussion below) |
All builds of the Sedona Framework prior to 1.0.45 will return null
for the
sox protocol version. Starting in build 1.0.45 the sox protocol was changed
to allow batch subscription, to components. If the
sox protocol supports batch subscription, then it will return a
soxVer of 1.1
(or higher).
req { u1 'y' u1 replyNum } res { u1 'Y' u1 replyNum str platformId u1 scodeFlags kitVersions[kitCount] { str version } u1 pairs pairs { str key str val } }
query
Perform a query for a set of components. Currently you can only query for a service type using queryType of 's'. But the message is designed to be enhanced thru additional queryTypes.
req { u1 'q' u1 replyNum u1 queryType u1[] queryReq } queryType 's': query service req-service { u1 kitId u1 typeId } res { u1 'Q' u1 replyNum u1[] queryRes } queryType 's': query service res-service { u2[num] compIds u2 0xffff end marker }
fileClose
Terminates a file transfer operation.
req { u1 'z' u1 replyNum } res { u1 'Z' u1 replyNum }
error
The error response may sent back as a response to any request. It indicates to the client that the request could not be processed. The cause of failure is returned as a short string in the message.
res { u1 '!' u1 replyNum str cause }