Skip to main content

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

ActorRole
OperatorBuilds a schedule in the calendar and clicks Send Dispatches.
Schedule Dispatch UIConverts the scheduled event into an OpenFMB LoadControlProfile and publishes it; waits for the opt-in/opt-out response.
NATSThe message bus carrying OpenFMB protobuf messages between the UI and the Dispatch Service.
Dispatch ServiceThe 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}):

DirectionSubjectPayload
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:

FieldSource in the profilePurpose
ControlEventIDControlMessageInfo → MessageInfo → IdentifiedObject.mRIDUnique event ID; used to match the response back to this event.
Creator nameControlMessageInfo → MessageInfo → IdentifiedObject.nameThe event dispatcher (the entity creating the event).
EventTypeLoadControl → ControlValue → IdentifiedObject.descriptionThe CRUD operation: LoadControl_CreateEvent, LoadControl_UpdateEvent, or LoadControl_CancelEvent.
Node nameLoadControl → ControlValue → IdentifiedObject.nameThe target Dispatch Node's name.
Schedule pointsLoadControl → 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:

  1. Extracts the node MRID from the 4th segment of the topic.
  2. Deserializes the LoadControlProfile.
  3. Reads the EventType, ControlEventID, and creator name. (Missing any of these is an error.)
  4. Dispatches on the EventType (the values come from the LoadControlEventNames enum, src/NodeController.ts:20–24):
    • LoadControl_CreateEventhandleCreateEvent() (src/NodeController.ts:264–318) validates and stores a new schedule.
    • LoadControl_UpdateEventhandleUpdateEvent() (src/NodeController.ts:320–362) replaces the schedule points of the existing dispatch with the same ControlEventID.
    • LoadControl_CancelEventhandleCancelEvent() (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. The LoadControl_optIn / LoadControl_optOut values come from the LoadControlResponse enum (src/NodeController.ts:15–18).

After a short simulated delay (~500–1500 ms), it publishes this on openfmb.loadmodule.LoadPlannedControlProfile.{nodeMrid}.

Opt-in vs. opt-out

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 doesStepFileLines
Subscribe to the request topic LoadControlProfile.>4src/main.ts239–243
Hand the request to the controller and publish the reply on LoadPlannedControlProfile.{nodeId}4, 6src/main.ts259–265
handleLoadControlRequest() — digest request, branch on event type, build opt-in response4, 6src/NodeController.ts78–141
LoadControlEventNames enum — LoadControl_CreateEvent / _UpdateEvent / _CancelEvent4src/NodeController.ts20–24
handleCreateEvent() — validate node + schedule points, store the dispatch5src/NodeController.ts264–318
handleUpdateEvent() — replace schedule points by ControlEventID4src/NodeController.ts320–362
handleCancelEvent() — delete dispatch by ControlEventID4src/NodeController.ts364–371
LoadControlResponse enum — LoadControl_optIn / LoadControl_optOut6src/NodeController.ts15–18
startScheduledDispatchTimer() — 10-second periodic re-publish8src/main.ts304–337
getScheduledDispatches() — build the periodic LoadControlProfile messages8src/NodeController.ts375–483
  • Schedule Dispatch UI — User Guide — the operator workflow that produces these events.
  • Deployment: Adding Services — deploying backend services such as mock-der-dispatch-service.