Event Types¶
Understanding the three types of messages that flow through your adapter.
What Are Message Types?¶
Your adapter receives messages from physical devices. Not all messages are the same:
Physical Device
|
+-- "Card 12345 granted access" (Audit Event)
+-- "Door is now unlocked" (Status Update)
+-- "ACK command received" (Control Message)
|
v
Your Adapter
|
v
Different handling for each type
Three fundamentally different message types require different handling.
The Three Types¶
| Type | What it represents | Must process? | Becomes PqEvent? |
|---|---|---|---|
| Audit Log Event | Something happened | YES - ALL | YES |
| Status Update | Current state | YES | NO |
| Control Message | Protocol communication | NO | NO |
1. Audit Log Events¶
What They Are¶
Events that report something that already happened on the device. These are historical records required for compliance, security audits, and ABAC rule evaluation.
Examples:
- Access granted to John Smith with card 12345
- Door forced open without authorization
- Alarm triggered on zone 3
- User enrolled fingerprint
- Access denied due to invalid time zone
Critical Rule: Process ALL Events¶
YOU MUST PROCESS EVERY AUDIT EVENT THE DEVICE REPORTS. NEVER SILENTLY DROP EVENTS.
Why:
- Compliance - regulatory requirements (GDPR, HIPAA, SOX) demand complete audit trails
- ABAC Rules - PQ system may have rules triggered by ANY event type
- Security - missing events create security blind spots
- Forensics - incomplete logs make incident investigation impossible
// WRONG - silently ignoring events you don't recognize
if (deviceEvent.Type == KnownEventType.AccessGranted)
{
await ProcessAccessEvent(deviceEvent);
}
// Unknown events just disappear - NEVER DO THIS!
// RIGHT - process ALL events
if (deviceEvent.Type == KnownEventType.AccessGranted)
{
await ProcessAccessEvent(deviceEvent);
}
else
{
// publish as PqEvents.Unknown - let PQ decide what to do
await PublishUnknownEvent(deviceEvent);
}
How to Handle¶
Audit events get routed to Thing handlers:
Device Event
|
v
EventRouter.Dispatch()
|
v
Routes match event to Thing
|
v
Thing handler method
|
v
Publishes PqEvent (access.granted, door.forced, etc.)
Example from access control:
// in your EventRouting configuration
router.When(e => e.EventType == 5)
.WithAddress<ReaderDevice, uint>(e => e.ReaderId)
.WithPerson(e => e.CardHolderId.ToString())
.WithCredential(e => e.CardNumber.ToString())
.Handle((reader, personId, credentialId, e) =>
reader.AccessGranted(
DeviceTimestamp.FromUnixSeconds(e.UnixTimestamp),
personId: personId,
credentialId: credentialId));
Thing handler publishes PqEvent:
public partial class ReaderDevice : Thing
{
public Task AccessGranted(DeviceTimestamp timestamp, Guid? personId, Guid? credentialId)
{
return PublishPqEvent(
PqEvents.AccessGranted,
timestamp,
data: new { personId, credentialId });
}
}
Common Audit Events¶
| Category | Examples |
|---|---|
| Access | access.granted, access.denied, access.unknown |
| Door | door.opened, door.closed, door.forced, door.held |
| Alarm | alarm.triggered, alarm.cleared, alarm.tamper |
| Fire | fire.alarm, fire.alarm.general, fire.trouble, fire.sounder.silenced, fire.routing.started |
| User | user.enrolled, user.deleted, credential.added |
| System | device.reboot, configuration.changed, time.synchronized |
2. Status Updates¶
What They Are¶
Messages that report the current state of the device or component. Status describes "what is" right now, not "what happened."
Examples:
- Device connection: online
- Door lock state: unlocked
- Input state: open
- Function status: ready
See Status concept for complete details.
Key Differences from Events¶
EVENT: "Door was opened at 10:15:03" (point in time, historical)
STATUS: "Door is open" (current state, continuous)
EVENT: "Access granted to John" (one-time occurrence)
STATUS: "Reader online" (ongoing state)
How to Handle¶
Status updates publish to OutboundStatusChannel:
public async Task OnDeviceStatusChange(DeviceStatus status)
{
var statusMessage = new StatusMessage(
adapterId: _adapterId,
nodeId: deviceId,
data: JsonSerializer.SerializeToElement(new {
connection = status.IsOnline ? "online" : "offline",
lockState = status.IsLocked ? "locked" : "unlocked"
}));
await _communication.PublishStatusAsync(statusMessage, ct);
}
Status does NOT become a PqEvent. Status is queryable state, events are historical records.
3. Control Messages¶
What They Are¶
Protocol-specific messages used for communication flow control. These are implementation details of the protocol, not business events.
Examples:
- ACK (acknowledgment)
- NAK (negative acknowledgment)
- Keepalive/heartbeat
- Handshake messages
- Protocol version negotiation
- Ping/pong
How to Handle¶
Handle internally in your protocol layer. NEVER publish control messages as PqEvents.
// in your protocol message handler
private async Task OnMessageReceived(ProtocolMessage msg)
{
switch (msg.Type)
{
case MessageType.Keepalive:
// respond to keepalive, don't publish event
await SendAck(msg.SequenceNumber);
break;
case MessageType.AccessEvent:
// this is an audit event - route to EventRouter
await _eventRouter.Dispatch(msg.ToAccessEvent(), ct);
break;
case MessageType.StatusUpdate:
// this is status - publish to status channel
await PublishDeviceStatus(msg.ToStatus());
break;
}
}
Control messages are consumed by the protocol layer and never leave the adapter.
Decision Tree: What Type Is This Message?¶
Device sent a message
|
v
Does it report something
that already happened?
|
+----+----+
| |
YES NO
| |
v v
Is it an Is it current
occurrence state information?
once? |
| +----+----+
| | |
| YES NO
| | |
v v v
AUDIT STATUS CONTROL
EVENT UPDATE MESSAGE
| | |
v v v
Route Publish Handle
to to in
Thing Status Protocol
Channel Layer
| | |
v | v
PqEvent | (consumed)
v
(queryable)
Mixed Message Protocols¶
Some protocols embed multiple message types in a single stream:
Example: Access Control Panel Events¶
Event Type 5: Access granted -> AUDIT EVENT
Event Type 9: Door state -> STATUS UPDATE (but also audit!)
Event Type 7: Input changed -> Could be either
ACK responses -> CONTROL MESSAGE
When a message has dual nature (both event and status), framework handles it automatically when you use function methods:
// door state change is BOTH event and status
router.When(e => e.EventType == EventCode.DoorOpened)
.WithAddress<DoorController, int>(e => e.DoorId)
.Handle((door, evt) =>
// Framework automatically:
// 1. Publishes audit event (PqEvents.DoorOpened)
// 2. Updates function state (Door.State = "open")
// 3. Updates status for PQ UI
door.Door.Opened(
DeviceTimestamp.FromUnixSeconds(evt.Timestamp),
CancellationToken.None));
You don't need to handle status separately - function methods publish both event and status automatically.
Example: Keepalive with Status¶
Some devices embed status in keepalive messages:
private async Task OnKeepalive(KeepaliveMessage msg)
{
// 1. Handle keepalive (control message)
_lastKeepalive = timeProvider.GetUtcNow();
// 2. Extract embedded status
if (msg.HasStatusData)
{
await PublishStatus(new {
cpuLoad = msg.CpuPercent,
memoryUsed = msg.MemoryBytes,
uptime = msg.UptimeSeconds
});
}
// 3. NO PqEvent - keepalive itself is not an audit event
}
Why ALL Audit Events Matter¶
Compliance Requirements¶
Many industries require complete audit trails:
HIPAA (Healthcare):
- Every access to patient records must be logged
- Missing events = compliance violation = fines
GDPR (Privacy):
- Complete record of who accessed personal data
- Right to audit requires full history
SOX (Financial):
- Complete access logs for financial systems
- Missing events compromise audit integrity
ABAC Rule Examples¶
PQ system may have rules triggered by ANY event:
Rule: "Alert security if >5 access denied events in 10 minutes"
-> Requires ALL denied events, even from unknown cards
Rule: "Disable credential after 3 failed attempts"
-> Missing one attempt breaks the rule
Rule: "Log all tamper events regardless of source"
-> Even unknown device types must report tampers
Unknown Events¶
If you receive an event you don't recognize:
// publish as PqEvents.Unknown with raw data
router.Unhandled(async (evt, ct) =>
{
await PublishPqEvent(
PqEvents.Unknown,
DeviceTimestamp.FromEvent(evt),
severity: "warning",
data: new {
rawEventType = evt.Type,
rawEventCode = evt.Code,
rawData = evt.RawBytes,
message = $"Unknown event type {evt.Type} from device"
});
});
PQ system administrators can:
- See unknown events in audit log
- Create ABAC rules for them
- Request adapter enhancement to handle them properly
Summary¶
| Type | Process? | Route to Thing? | Publish to PQ? | Used for |
|---|---|---|---|---|
| Audit Event | YES - ALL | YES | YES (PqEvent) | Compliance, ABAC, forensics |
| Status Update | YES | NO | YES (Status) | Current state, monitoring |
| Control Message | YES | NO | NO | Protocol communication |
Key Takeaways¶
- Never drop audit events - process ALL events from device
- Events become PqEvents - routed through EventRouter to Things
- Status is separate - current state, not historical
- Control stays internal - protocol layer only, never published
- When in doubt - publish as PqEvents.Unknown, let PQ decide
Related:
- Event Mapping Guide - Systematic vendor event extraction and mapping
- Events - Detailed event handling
- Status - Status reporting patterns
- Channels - Message pipelines