CMLabs · Psyclone AIOS

# 7. Whiteboards

Whiteboards are the system's shared short-term memory: catalogs specialised for storing messages, with a retrieval query language, keyed indexes, and visual browsing in PsyProbe.

## What is a whiteboard?

A whiteboard is a special catalog that stores messages only. Where an ordinary module reacts to a message and forgets it, a whiteboard keeps it, indexed for later retrieval by other components. Every stored message is automatically indexed on its creation time; you can add further keyed indexes over the message contents. PsyProbe shows every whiteboard's stored messages with live filtering.

## Declaring whiteboards and storing messages

The minimal whiteboard only receives messages addressed directly to it via a post's `to=` attribute (direct posts are also still routed to normal subscribers):

    <whiteboard name="WB1" />
    <!-- elsewhere: -->
    <post name="Save" type="Data.Web" to="WB1" />

Usually a whiteboard subscribes by type, just like a module — wildcards included:

    <whiteboard name="WB2">
      <trigger name="Ball"   type="ball.1" />
      <trigger name="Dialog" type="dialog.*" />
    </whiteboard>

## Retrieving messages

Retrieval is declared in the spec and executed from code. A `<retrieve>` names the source whiteboard and the constraints:

    <retrieve name="r1" source="WB1" maxcount="10" />

    std::list<DataMessage*> msgs;
    uint8 status = api->retrieve(msgs, "r1");   // last 10 messages, any type

    msgs = api.retrieve("r1")   # last 10 messages, any type (returns a list)

Retrieve attributes: `type` (PsyType filter), `from`/`to` (original sender/addressee), `maxcount`, `maxage` (stored ×1000; see the unit caveat below), `tag`, and the index selection `key`/`keytype`/`start`/`end` described below.

### Status codes

Every retrieve/query returns one of the `QUERY_*` codes:

Code | Value | Meaning
--- | --- | ---
`QUERY_FAILED` | 1 | General failure
`QUERY_TIMEOUT` | 2 | No reply within the timeout
`QUERY_NAME_UNKNOWN` | 3 | No retrieve/query with that name
`QUERY_COMPONENT_UNKNOWN` | 4 | The source component does not exist
`QUERY_QUERYFAILED` | 5 | The component rejected the query
`QUERY_SUCCESS` | 6 | Results delivered
`QUERY_NOT_AVAILABLE` | 7 | Component not currently available
`QUERY_NOT_REACHABLE` | 8 | Component could not be reached

## Keyed indexes

Besides the automatic time index, a whiteboard can index messages on a content entry. Declare the key on the whiteboard, then select it in the retrieve:

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

    <retrieve name="r2" source="WB2" key="count" keytype="integer" />

    std::list<DataMessage*> msgs;
    // signature: retrieveIntegerParam(result, name, startInt, endInt, maxcount, maxage, timeout)
    // messages whose "count" entry is between 2 and 8, at most 4 of them:
    uint8 status = api->retrieveIntegerParam(msgs, "r2", 2, 8, 4);
    // 5th arg is maxcount; 6th arg is maxage in MICROSECONDS (here 100 ms):
    status = api->retrieveIntegerParam(msgs, "r2", 2, 8, 4, 100000);

    # retrieveIntegerParam(name, startInt, endInt, maxcount, maxage_us, timeout)
    # messages whose "count" entry is between 2 and 8, at most 4 of them:
    msgs = api.retrieveIntegerParam("r2", 2, 8, 4)
    # 4th arg is maxcount; 5th arg is maxage in MICROSECONDS (here 100 ms):
    msgs = api.retrieveIntegerParam("r2", 2, 8, 4, 100000)

Valid `keytype` values are `string`, `integer`, `float`/`double` and `time` (the default). Range bounds go in the retrieve's `start`/`end` attributes or the ranged retrieve calls, interpreted per keytype. Additional keys can be declared with repeated `key=`/`keytype=` pairs or `<key name type>` children.

There are four ranged retrieve calls, one per key type, all with the same shape `(result, name, start, end, maxcount, maxage, timeout)` (Python drops the first arg and returns the list): `retrieveIntegerParam`, `retrieveFloatParam`, `retrieveStringParam` and `retrieveTimeParam`. `maxcount` caps how many messages come back (0 = all); `maxage` is a microsecond window against each message's creation time (0 = no age limit); `timeout` is milliseconds.

Unit caveat. The ranged API calls take `maxage` in microseconds. The XML `<retrieve maxage=>`/`<query maxage=>` attributes are parsed as a value ×1000 and the exact wall-clock unit of that attribute is being confirmed against the engine build — if you need a precise age window, prefer the API call with an explicit microsecond value.
Note. The whiteboard's `root=` attribute (on-disk persistence) is currently parsed but not acted on Stub — treat whiteboard contents as in-memory. Use `maxcount`/`maxsize` to bound memory, and a Replay Catalog (chapter 8) for durable recording.

### The whiteboard in the dataflow

Messages reach the whiteboard by subscription or direct `to=` addressing; consumers pull them back with declared retrieves; PsyProbe browses the store live.

## Free-form queries

Whiteboards (like all catalogs) also accept free-form `<query>` declarations for cases retrieves don't cover — a query names a `source` and optional constraints (`maxcount`, `maxage`, `key`, `value`, `operation`…) and is executed with `api->queryCatalog(…)`. The full query mechanism, including writing your own catalog that answers queries, is covered in Catalogs.

## Whiteboards in PsyProbe

Every whiteboard gets a Stored Messages tab in PsyProbe showing its contents live, with a filter line for free text, message count, size and age — ideal for verifying that the right messages are being captured. Hovering a message shows its full content; clicking opens it in a static window. The usual component tabs (Performance, Activity, Subscriptions) are there too. See PsyProbe.

Tip. A wildcard whiteboard like `<whiteboard name="MessagesOfInterest"><trigger name="d" type="dialog.*" />…</whiteboard>` is a cheap, non-intrusive debugging tap: it records interesting traffic without touching the modules that produce it.
PreviousContexts NextCatalogs