Dispatched Event — Message Sequence
This page traces a single Dispatched Event end to end: the NATS message the Schedule Dispatch UI sends when an operator clicks Send Dispatches, how a Dispatch Service picks it up, digests it to schedule its DER equipment, and responds that the event was scheduled (an opt-in).
The flow below uses the Mock DER Dispatch Service (mock-der-dispatch-service) as the consuming Dispatch Service. The mock implements the same OpenFMB topics and message contracts a real Dispatch Service must implement, so the sequence is representative of production.
The step-by-step walkthrough below cites the specific functions and enums in mock-der-dispatch-service that implement each step, and a Source Reference table at the end collects them in one place.
Actors
| Actor | Role |
|---|---|
| Operator | Builds a schedule in the calendar and clicks Send Dispatches. |
| Schedule Dispatch UI | Converts the scheduled event into an OpenFMB LoadControlProfile and publishes it; waits for the opt-in/opt-out response. |
| NATS | The message bus carrying OpenFMB protobuf messages between the UI and the Dispatch Service. |
| Dispatch Service | The DER controller (here, the Mock DER Dispatch Service). Subscribes to load-control requests, schedules its equipment, and replies. |
The Two Topics
A dispatched event is a request/response exchange over two related subjects, both keyed by the Dispatch Node's MRID ({nodeMrid}):
| Direction | Subject | Payload |
|---|---|---|
| Request (UI → Service) | openfmb.loadmodule.LoadControlProfile.{nodeMrid} | LoadControlProfile (binary protobuf) |
| Response (Service → UI) | openfmb.loadmodule.LoadPlannedControlProfile.{nodeMrid} | LoadControlProfile (binary protobuf) carrying the opt-in/opt-out status |
The UI publishes the request, then listens on the response subject and matches the reply back to the originating event by its ControlEventID (the event MRID). The service replies on the response subject after it has scheduled (or rejected) the event.
Sequence Diagram
Step-by-Step
1. Operator confirms the send
In the Send Distributed Report dialog, the operator reviews the pending changes, checks "I have verified the data above," and confirms. The UI gathers every event in a pending state (Unscheduled, Updated, OptOut, or Cancelling) and sends each as its own request.
2. UI builds the LoadControlProfile
For the new event, the UI serializes the DispatchEvent into an OpenFMB LoadControlProfile. The fields the Dispatch Service depends on are:
| Field | Source in the profile | Purpose |
|---|---|---|
| ControlEventID | ControlMessageInfo → MessageInfo → IdentifiedObject.mRID | Unique event ID; used to match the response back to this event. |
| Creator name | ControlMessageInfo → MessageInfo → IdentifiedObject.name | The event dispatcher (the entity creating the event). |
| EventType | LoadControl → ControlValue → IdentifiedObject.description | The CRUD operation: LoadControl_CreateEvent, LoadControl_UpdateEvent, or LoadControl_CancelEvent. |
| Node name | LoadControl → ControlValue → IdentifiedObject.name | The target Dispatch Node's name. |
| Schedule points | LoadControl → LoadControlFSCC → ControlFSCC → ControlScheduleFSCH → ValACSG → SchPts[] | The hourly setpoints, each a start time + wattage. The final point is value 0 to mark the end of the schedule. |
3. UI publishes the request
The UI publishes the serialized profile on openfmb.loadmodule.LoadControlProfile.{nodeMrid} and simultaneously subscribes to openfmb.loadmodule.LoadPlannedControlProfile.{nodeMrid}, awaiting a reply whose ControlEventID matches the event it just sent.
4. Dispatch Service receives and digests
The service is subscribed to the wildcard openfmb.loadmodule.LoadControlProfile.> (src/main.ts:239–243). The subscription handler (src/main.ts:259–265) calls handleLoadControlRequest() (src/NodeController.ts:78–141), which:
- Extracts the node MRID from the 4th segment of the topic.
- Deserializes the
LoadControlProfile. - Reads the EventType, ControlEventID, and creator name. (Missing any of these is an error.)
- Dispatches on the EventType (the values come from the
LoadControlEventNamesenum,src/NodeController.ts:20–24):LoadControl_CreateEvent→handleCreateEvent()(src/NodeController.ts:264–318) validates and stores a new schedule.LoadControl_UpdateEvent→handleUpdateEvent()(src/NodeController.ts:320–362) replaces the schedule points of the existing dispatch with the same ControlEventID.LoadControl_CancelEvent→handleCancelEvent()(src/NodeController.ts:364–371) deletes the stored dispatch with that ControlEventID.
5. Service schedules its DER equipment (Create)
For a create event (handleCreateEvent(), src/NodeController.ts:264–318) the service validates the request before scheduling:
- The Dispatch Node MRID must exist in the service's known nodes.
- The schedule must contain at least 2 points.
- The last point must have value
0, marking the end of the dispatch.
If validation passes, the service converts the protobuf schedule points into its internal ScheduleDispatch (start time + wattage per point) and stores it against the node — this is the act of "scheduling the DER equipment."
6. Service responds with opt-in
At the end of handleLoadControlRequest() (src/NodeController.ts:111–135) the service builds a response LoadControlProfile whose ControlMessageInfo → MessageInfo → IdentifiedObject carries:
- the original ControlEventID (mRID),
- the creator name, and
- a Description of
LoadControl_optIn— the opt-in status confirming the resource accepted the dispatch. TheLoadControl_optIn/LoadControl_optOutvalues come from theLoadControlResponseenum (src/NodeController.ts:15–18).
After a short simulated delay (~500–1500 ms), it publishes this on openfmb.loadmodule.LoadPlannedControlProfile.{nodeMrid}.
The mock service always opts in for a successfully validated event. A real Dispatch Service may instead set the Description to LoadControl_optOut if the resource cannot honor the request. The UI handles both: an opt-in marks the event Scheduled; an opt-out marks it OptOut.
7. UI confirms the event is scheduled
The UI receives the response on the planned-control subject, matches it to the originating event by ControlEventID, and reads the Description. Because it is LoadControl_optIn, the UI sets that event's status to Scheduled — the operator sees the event move from a pending color to scheduled in the calendar.
8. Periodic state re-publishing
Independent of any request, the Dispatch Service re-publishes all of its currently stored dispatches every 10 seconds on openfmb.loadmodule.LoadPlannedControlProfile.{nodeMrid} (one message per scheduled dispatch). This is driven by startScheduledDispatchTimer() (src/main.ts:304–337), which calls getScheduledDispatches() (src/NodeController.ts:375–483) to build the per-dispatch LoadControlProfile messages. This is OpenFMB's periodic state-publication pattern: it keeps the UI — and any other listener — synchronized with the resource's current planned schedule, and it is how the UI rediscovers existing schedules when it (re)loads. Scheduled events the UI stops hearing about are eventually treated as stale and removed from the display.
Source Reference
This table references the mock-der-dispatch-service src code files, which should be a helpful aid if you are implementing your own dispatch service. The steps in parentheses refer to the Step-by-Step sections above. Line numbers reflect the service at the time of writing and may drift as the code evolves.
| What it does | Step | File | Lines |
|---|---|---|---|
Subscribe to the request topic LoadControlProfile.> | 4 | src/main.ts | 239–243 |
Hand the request to the controller and publish the reply on LoadPlannedControlProfile.{nodeId} | 4, 6 | src/main.ts | 259–265 |
handleLoadControlRequest() — digest request, branch on event type, build opt-in response | 4, 6 | src/NodeController.ts | 78–141 |
LoadControlEventNames enum — LoadControl_CreateEvent / _UpdateEvent / _CancelEvent | 4 | src/NodeController.ts | 20–24 |
handleCreateEvent() — validate node + schedule points, store the dispatch | 5 | src/NodeController.ts | 264–318 |
handleUpdateEvent() — replace schedule points by ControlEventID | 4 | src/NodeController.ts | 320–362 |
handleCancelEvent() — delete dispatch by ControlEventID | 4 | src/NodeController.ts | 364–371 |
LoadControlResponse enum — LoadControl_optIn / LoadControl_optOut | 6 | src/NodeController.ts | 15–18 |
startScheduledDispatchTimer() — 10-second periodic re-publish | 8 | src/main.ts | 304–337 |
getScheduledDispatches() — build the periodic LoadControlProfile messages | 8 | src/NodeController.ts | 375–483 |
Related
- Schedule Dispatch UI — User Guide — the operator workflow that produces these events.
- Deployment: Adding Services — deploying backend services such as
mock-der-dispatch-service.