Sox¶
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'
}
Supported Headers¶
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 response 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 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
}