Skip to main content

ControlForge Protocol Analyzer & Store-and-Forward Guide

James M. Belcher Founder, JMB Technical Services LLC April 2026 | ControlForge v1.0.535


1. Protocol Analyzer

The protocol analyzer captures industrial protocol transactions in real time — Modbus, FINS, EtherNet/IP, S7, OPC UA, DNP3, BACnet, and more. Captures are viewable from ST code, queryable via REST API, and exportable to PCAP for Wireshark analysis.

Capture Workflow

(* 1. Initialize with buffer size *)
AN_INIT(1000);

(* 2. Start capture — optionally filter by device/protocol *)
AN_START('10.0.0.50', 'modbus');

(* 3. ... protocol traffic is captured automatically ... *)

(* 4. Check status *)
IF AN_IS_CAPTURING() THEN
count := AN_COUNT();
stats := AN_STATS();
END_IF;

(* 5. Retrieve and decode *)
transactions := AN_GET(50);
decoded := AN_DECODE('modbus', '0001000000060103000A0002');

(* 6. Export and stop *)
AN_EXPORT_PCAP('/data/capture.pcap');
AN_STOP();

ST Functions (12)

FunctionParametersReturnsDescription
AN_INIT(buffer_size)1BOOLInitialize analyzer with transaction buffer
AN_START([device, protocol])0-2BOOLStart capture (optional device/protocol filter)
AN_STOP()0BOOLStop capture
AN_IS_CAPTURING()0BOOLCheck if capture is active
AN_RECORD(device, protocol, direction, hex)4BOOLRecord transaction manually
AN_FILTER(device, protocol, limit)3ARRAYGet filtered transactions
AN_GET(limit)1ARRAYGet recent transactions
AN_DECODE(protocol, hex)2MAPDecode raw packet to fields
AN_COUNT()0INTTransaction count
AN_STATS()0MAPCapture statistics (total, errors, by-protocol)
AN_CLEAR()0BOOLClear transaction buffer
AN_EXPORT_PCAP(path)1BOOLExport to PCAP file

REST API Endpoints

MethodEndpointDescription
GET/api/analyzerCapture state and statistics
POST/api/analyzer/startStart capture
POST/api/analyzer/stopStop capture
GET/api/analyzer/transactionsList transactions (limit/offset)
GET/api/analyzer/transactions/{id}Single transaction
DELETE/api/analyzer/transactionsClear buffer
GET/api/analyzer/statsDetailed statistics
POST/api/analyzer/decodeDecode hex packet
GET/api/analyzer/export/pcapDownload PCAP
GET/api/analyzer/protocolsList supported protocols

Example: Capture and Decode Modbus Traffic

PROGRAM POU_PacketCapture
VAR
state : INT := 0;
capture_timer : DINT := 0;
transactions : STRING;
decoded : STRING;
stats : STRING;
END_VAR

CASE state OF
0: (* Initialize *)
AN_INIT(500);
AN_START('10.0.0.50', 'modbus');
state := 1;

1: (* Capture for 3000 scans (~5 min at 100ms) *)
capture_timer := capture_timer + 1;

IF capture_timer >= 3000 THEN
AN_STOP();
stats := AN_STATS();
transactions := AN_GET(20);
DEBUG_INFO('capture', CONCAT('Captured ', INT_TO_STRING(AN_COUNT()), ' transactions'));
state := 2;
END_IF;

2: (* Done — data available via API *)
END_CASE;
END_PROGRAM

2. Store-and-Forward

The store-and-forward subsystem queues messages locally when the network is unavailable and forwards them when connectivity is restored. Messages persist in a SQLite database — no data loss across restarts.

Workflow

(* 1. Initialize with database path *)
SF_INIT('/data/telemetry_queue.db', 10000, 86400);

(* 2. Store messages (always succeeds — local DB) *)
SF_STORE('plant/telemetry', payload, 1);
SF_STORE_JSON('plant/status', 1, status_map);

(* 3. Check pending *)
pending := SF_COUNT();
is_online := SF_ONLINE(TRUE);

(* 4. Forward when ready *)
forwarded := SF_FORWARD(forward_callback);

(* 5. Stats and cleanup *)
stats := SF_STATS();
SF_CLEAR();
SF_CLOSE();

ST Functions (10)

FunctionParametersReturnsDescription
SF_INIT(db_path, max_msgs, max_age_sec)3BOOLInitialize with SQLite database
SF_STORE(topic, payload, priority)3INTStore text message (returns ID)
SF_STORE_JSON(topic, priority, value)3INTStore structured value as JSON
SF_FORWARD(callback)1INTForward pending messages (returns count)
SF_ONLINE(online)1Set network availability flag
SF_COUNT()0INTPending message count
SF_GET_PENDING(limit)1ARRAYView pending messages
SF_STATS()0MAPQueue statistics (stored, forwarded, dropped)
SF_CLEAR()0BOOLClear all pending messages
SF_CLOSE()0Close database connection

Example: Resilient MQTT Telemetry

PROGRAM POU_ResilientTelemetry
VAR
initialized : BOOL := FALSE;
scan_count : DINT := 0;
pending : INT;
payload : STRING;
END_VAR

IF NOT initialized THEN
SF_INIT('/data/telemetry.db', 50000, 604800); (* 50K msgs, 7-day max age *)
MQTT_CLIENT_CREATE('broker', 'tcp://10.0.0.144:1883', 'goplc-sf');
MQTT_CLIENT_CONNECT('broker');
initialized := TRUE;
END_IF;

scan_count := scan_count + 1;

(* Store telemetry every 10 scans *)
IF (scan_count MOD 10) = 0 THEN
payload := JSON_STRINGIFY(JSON_OBJECT(
'temp', temperature,
'pressure', pressure
));
SF_STORE('plant/telemetry', payload, 1);
END_IF;

(* Track connectivity *)
SF_ONLINE(MQTT_CLIENT_IS_CONNECTED('broker'));

(* Monitor queue depth *)
pending := SF_COUNT();
IF pending > 1000 THEN
DEBUG_WARN('sf', CONCAT('Queue depth: ', INT_TO_STRING(pending)));
END_IF;
END_PROGRAM

ControlForge v1.0.535 | Protocol Analyzer (12) + Store-and-Forward (10) | Packet Capture & Offline Queuing

© 2026 JMB Technical Services LLC. All rights reserved. Back to All Guides