CMSDK 2.0.1
Cross-platform C++ base library and SDK for the Psyclone AIOS platform
Loading...
Searching...
No Matches
Whiteboards & Catalogs

Pub/sub delivers messages to whoever is listening right now. Whiteboards and catalogs cover the other half of the problem: keeping data so it can be found later — past messages, files, persistent key/value data, recorded sessions.

Whiteboards

A whiteboard holds messages in memory at run-time for any module to retrieve. Messages are indexed by time, and optionally by user-defined keys.

<whiteboard name="WB1" key="count" keytype="integer" />
<whiteboard name="WB2" key="count" keytype="integer">
<trigger name="Ball" type="ball.4" />
</whiteboard>

Messages arrive at a whiteboard in two ways:

  • Addressed posts — the poster names the whiteboard: <post name="Ball" to="WB1" type="ball.4" ttl="100000" guaranteed="yes" />
  • Whiteboard subscriptions — the whiteboard itself carries <trigger> entries and receives matching messages automatically; posters don't need to know it exists (WB2 above).

Any message entering a whiteboard needs a TTL (time to live) greater than 0 and is removed automatically when it expires; the ttl= attribute on the post sets it in milliseconds, and a whiteboard-side KeepMS <parameter> can supply a default TTL for messages that arrive without one. Expiry is what bounds a whiteboard's size — there is no per-whiteboard message-count cap; the maxcount= attribute belongs to <retrieve> entries (below), where it limits how many messages one retrieve returns.

Retrieving

A module declares named retrieves in the PsySpec:

<retrieve name="r1" source="WB1" maxcount="100" />
<retrieve name="r2" source="WB1" key="count" keytype="integer" />

r1 is a plain time-indexed retrieve; r2 uses the custom integer index over the count user entry that WB1 declared with key=/keytype=. Crank code executes them by name via cmlabs::PsyAPI:

std::list<DataMessage*> msgs;
uint8 status = api->retrieve(msgs, "r1"); // time index
// or by key range: entries with 2 <= count <= 8, max 4 messages
status = api->retrieveIntegerParam(msgs, "r2", 2, 8, 4);
if (status == QUERY_SUCCESS)
api->logPrint(1, "Retrieved %u messages...", msgs.size());
else if (status == QUERY_TIMEOUT)
api->logPrint(1, "Retrieve timed out...");
for (DataMessage* m : msgs)
delete m; // retrieved messages are yours to delete
#define QUERY_SUCCESS
The query succeeded.
Definition PsyAPI.h:60
#define QUERY_TIMEOUT
No reply within the timeout.
Definition PsyAPI.h:56

Status codes are the QUERY_SUCCESS / QUERY_TIMEOUT / QUERY_FAILED family. Note the ownership difference from triggers: messages returned by a retrieve are copies owned by you — delete them when done.

The classic use case combines whiteboards with contexts: when the system context changes, data that was irrelevant a moment ago may suddenly matter, and a freshly-in-context module can pull the recent history it missed from a whiteboard instead of having subscribed all along. Runnable demo: Examples/whiteboard.xml with the Examples::RetrieveTest crank in Examples/src/Examples.cpp.

Catalogs

A catalog fronts larger or longer-lived data with a query mechanism. Modules access catalogs through named <query> entries and one call, cmlabs::PsyAPI::queryCatalog() — regardless of which machine either side runs on. Three catalog types ship with the system (in the PsySystem library — System/src/Catalog.cpp — which you can copy and modify to build your own):

FileCatalog — central file access

<catalog name="MyFiles" type="FileCatalog" root="./">
<parameter name="ReadOnly" type="String" value="no" />
</catalog>

In the module:

<query name="MyFiles" source="MyFiles" subdir="test" ext="txt" binary="yes" />

In the crank — read ./test/test.txt from wherever the catalog lives:

uint32 datasize = 0;
char* result = NULL;
uint8 status = api->queryCatalog(&result, datasize, "MyFiles", "test", "read");
if (status == QUERY_SUCCESS)
api->logPrint(1, "Read %u bytes...", datasize);
delete[] result;

Writing works the same way with a "write" action and a data buffer:

char* data = utils::StringFormat(datasize, "Hello World");
status = api->queryCatalog(&result, datasize, "MyFiles", "test", "write", data, datasize);
delete[] data; delete[] result;

The ReadOnly parameter (default no) blocks writes when set to yes.

DataCatalog — persistent key/blob store

<catalog name="MyData" type="DataCatalog" interval="2000" root="./mydata.dat" />

A central datastore, flushed from memory to root= every interval= milliseconds, surviving system restarts. Same query pattern as the FileCatalog — read/write a named entry:

status = api->queryCatalog(&result, datasize, "MyData", "test", "read");
// QUERY_FAILED on first run with a fresh store is normal — nothing stored yet

ReplayCatalog — record and play back message traffic

Record chosen message types in one system, replay them later in another — e.g. capture two minutes of a robot's video/sensor messages, then develop against the recording with no robot present.

Recording (Examples/messagerecord.xml): the catalog subscribes like any component and writes matching messages to disk, bounded by maxsize= bytes and/or maxcount= messages:

<catalog name="Replay1" type="ReplayCatalog" root="./replay1" maxsize="1024000" maxcount="20">
<trigger name="Ball" type="ball" />
</catalog>

Playback (Examples/messageplayback.xml): the same catalog with <post> entries re-posts the recorded messages with their original timing. Trigger and post names must match; the posted types may differ from the recorded ones:

<catalog name="Replay1" type="ReplayCatalog" root="./replay1" interval="0" rotate="yes">
<post name="Ball" type="ball" />
</catalog>
  • interval= overrides the recorded timing with a fixed posting interval in ms (0 = as recorded).
  • rotate="yes" loops back to the beginning after the last message.

Choosing between them

Need Use
Recent message history, queryable by time or key Whiteboard
Central file read/write across machines FileCatalog
Small persistent state surviving restarts DataCatalog
Deterministic replay of recorded traffic ReplayCatalog
Custom storage/query semantics Your own catalog (start from PsySystem)

Everything on this page runs in Examples/single.xml (the Test.Catalogs phase) and Examples/whiteboard.xml; the crank side is Examples::RetrieveTest in Examples/src/Examples.cpp.