IPC-CFX message workbench
Look up IPC-CFX (IPC-2591) messages by topic, generate a message envelope from a form, validate messages and logs against definitions derived from the official IPC CFX SDK, and see how CFX travels over AMQP 1.0: exchange, subject, queue, and direct requests. It does not connect to any broker.
Message definitions derived from the IPC CFX SDK 2.1.1 source (Apache-2.0, commit 880255d, CFX 2.1), not from the IPC-2591 document itself. Nothing here connects to a broker or an endpoint.
Topics and messages
An event is published to everyone who subscribes (WorkCompleted, FaultOccurred …); a request goes to one endpoint, which answers with the matching response. The search needs JavaScript; without it the whole catalogue is shown.
Message
Message body
Empty fields are written as null. Objects that may be null start excluded: tick “Include” to fill them in. Where a member can hold a derived type, choose it under “Type ($type)”.
Runs in this browser. Data is not uploaded or stored.
CFX envelope (JSON)
“$type” first and the members in the SDK’s order (its own, then its base classes’), as the SDK’s Newtonsoft.Json writes them; whole numbers of double and float members get “.0”, decimals keep the digits typed. The JSON Schema download is FactoKit’s schema derived from the SDK, not an official IPC schema.
Messages
Drop a .json, .ndjson, .jsonl, .txt or .log file on the field or open one (up to 5 MB); it is read in this browser only.
Runs in this browser. Data is not uploaded or stored.
Validation
An error is something the SDK would refuse or read wrongly; a warning is accepted but probably not meant. Select a line and column to jump to it in the field.
Addresses
Addresses as amqp://host:5672 or amqps://host:5671 (TLS). Exchange and queue in the SDK documentation’s RabbitMQ form (/exchange/name, /queue/name). Topic patterns: “*” one word, “#” zero or more words, words separated by dots. This is an explanation only: nothing is sent.
Runs in this browser. Data is not uploaded or stored.
How the message travels
Publishing (the endpoint)
Routing and subscribing (the broker)
Request and response (point to point)
How the messages are checked and generated
IPC-CFX (IPC-2591, Connected Factory Exchange) messages are JSON envelopes sent over AMQP 1.0. The tool knows 183 messages (98 events, 42 requests, 43 responses) and the structures and enumerations they use, derived from the source of the official IPC CFX SDK. Everything runs in the browser and nothing connects to a broker or an endpoint.
Where the definitions come from
- Source: https://github.com/IPCConnectedFactoryExchange/CFX, commit 880255d2f7380f340f56b243a51d99f2136bdae3 (2026-04-17), package version 2.1.1, CFX version 2.1; Copyright 2018 IPC International, licensed under the Apache License 2.0 (the license text is at the end of this file). A FactoKit script reads the C# message and structure classes and writes the tool’s compact definitions; descriptions are shortened from the SDK’s documentation comments.
- Taken from the SDK as it is (verified against its source): the names of messages, members, structures and enumeration values, the member types, which members may be null, derived types and “$type” names, the version that added a member (where the SDK marks it), the order in which Newtonsoft.Json writes members, and which enumerations are written as names, numbers or codes.
- Not read here: the IPC-2591 document itself. Where the standard says more than the SDK (for example which members a message must carry), the tool does not know it.
The envelope
- Members in this order: MessageName (the full name, such as CFX.Production.WorkCompleted), Version (the CFX version, “2.1” for this SDK), TimeStamp (when the event happened, ISO 8601 with its time zone), UniqueID (a GUID; receivers use it to drop duplicates), Source (the sender’s CFX handle), Target and RequestID (for requests and responses), MessageBody.
- The body starts with “$type”: “<MessageName>, CFX”; then its members in the SDK’s order (those the class declares, then those of each base class; [JsonProperty(Order)] where set). The SDK writes null members too; doubles are written with a decimal point (20.0), a C# float member (ConveyingDistance, Air_Pressure …) with the fewest digits of its single-precision value (0.3, not 0.30000001192092896), a decimal member with its digits and scale (1.50).
- A member typed as a base class may hold a derived type: the object then starts with its own “$type” (a NumericMeasurement in a list of Measurements). Abstract classes always need it.
What the validator reports
An error is something the SDK would refuse or read wrongly; a warning is accepted by the SDK but probably not meant. Every finding has its line, column and JSON pointer.
- Errors: JSON syntax (with the position where reading stopped); a MessageName that is not a CFX message; envelope members missing (MessageName, Version, TimeStamp, UniqueID, MessageBody); a TimeStamp without its offset or Z; a GUID that is not 8-4-4-4-12 hex digits; a date that is not ISO 8601; a duration that is not .NET’s [-][d.]hh:mm:ss[.fffffff]; a value of the wrong JSON type (an object where a number belongs, null in a value type, text the SDK cannot read as the member’s number or boolean: “1.0” for a whole number, “12.5 mm”, “yes”); an enumeration value that does not exist; a whole number written with a decimal point or outside its range; a decimal beyond ±79228162514264337593543950335 (decimal.MaxValue); a double written as text beyond a double’s range; base64 that is not base64; a body “$type” that names another message; a “$type” that is not a CFX type, not derived from the declared type, or abstract; an abstract member without “$type”.
- Warnings: members the definitions do not know (the SDK ignores them); missing value-type members that are not documented as optional (the SDK reads them as their default value: messages written before a member was added lack it too); a body without “$type” (older SDK form, read by MessageName); “$type” not first or not written as “Name, CFX”; an enumeration written as a number where the SDK writes names (or the other way round); a number or boolean written as text that the SDK’s Newtonsoft.Json parses (“1”, “12.5”, “1.5e3”, “NaN”, “true” in any letter case; “12,5” is read as 125, the comma taken as a thousands separator; an empty string is null, accepted for a nullable member only); a decimal with more digits than a .NET decimal keeps (28 after the point, 96 bits in all: the SDK rounds it); a date in a body without its offset; a message or member newer than the envelope’s Version; a request without Target or RequestID, a response without RequestID; a key written twice.
- Input: one envelope, a JSON array of envelopes, or a log with one envelope per line (NDJSON / JSON Lines). A body alone is checked when it has a “$type” naming a message, or as the message chosen under “Check bodies as” (as in the SDK’s own documentation examples).
- FactoKit’s rules, not the SDK’s: which missing members are reported (value types not documented as optional), the strict forms of GUIDs, dates and durations (the .NET readers accept more forms), and that an enumeration name must match in case.
The generator
- The form is built from the definitions: text for strings, GUIDs (with “New GUID”, random version 4), dates (“Now”, local time with its offset) and durations; selects for enumerations and booleans; nested objects with “Include” (otherwise null) and a type choice where derived types exist; lists with “Add item”. A new message starts with values for value types (a new GUID, the current time, 0, false, the first choice) and null for the rest.
- Empty fields are written as null (or left out when “Write empty members as null” is off). Numbers may be typed with a decimal comma. A decimal keeps the digits and the scale typed (0.1234567890123456789, 1.50; an exponent is multiplied out), a long beyond 2^53 its digits. The output is checked by the validator at once, so a missing or malformed value shows up below it.
- “JSON Schema of this message” downloads a draft 2020-12 schema of the envelope and its body, built from the same definitions (FactoKit’s schema, not an official IPC one).
CFX over AMQP 1.0
- From the SDK source (CFX/Transport): an endpoint opens an AMQP 1.0 connection to a broker (amqp://, port 5672; amqps:// with TLS, port 5671) and a sender link to a target address such as /exchange/cfx.line1. Each AMQP message carries one envelope (or a JSON array of several), gzip-compressed by default, marked durable; message-id = UniqueID, subject = “<handle>.<MessageName>” (configurable with ${cfx-handle}, ${cfx-topic}, ${cfx-messagename}), to = Target, reply-to = Source, correlation-id = RequestID, content-type application/json; application properties cfx-topic, cfx-message, cfx-handle, cfx-target. A Heartbeat goes out when a publish channel opens and every minute by default.
- Subscribers open a receiver link on a source address such as /queue/mes.line1.
- Requests are point to point: the requester connects directly to the endpoint’s own listener (its RequestNetworkUri) and sends to a link target equal to the endpoint’s CFX handle; the response comes back on the same connection with Source and Target swapped and the same RequestID (“REQUEST-” and a GUID when the requester left it empty); unhandled requests get NotSupportedResponse; the SDK waits 30 s.
- Broker conventions, not verified here: on RabbitMQ with AMQP 1.0, “/exchange/name” and “/queue/name” address an exchange and a queue, a topic exchange uses the subject as the routing key, and a queue receives what matches its binding pattern (“*” one word, “#” zero or more, split at dots). One exchange per line is a common layout, not a rule of the standard. Other brokers (ActiveMQ Artemis, Azure Service Bus …) address and route differently.
Worked examples (the tool’s examples)
- Generator: an illustrative WorkCompleted for panel PANEL-2026-0001 with two circuits (Units at X 12.5 and 112.5 mm, Status Pass), Result Completed, sent by FactoKit.Example.Line1.AOI1. The check below the output reports no errors and no warnings.
- Validator: an array of two envelopes. The UnitsInspected (an AOI finding insufficient solder on R22, with a NumericMeasurement and its “$type”) passes. The WorkCompleted has deliberate mistakes: 5 errors — TimeStamp without its offset (line 96), a UniqueID one digit short, Result “Complete” (WorkResult has Completed, Failed, Aborted, Skipped), UnitCount written as 2.0, PositionNumber as the text “1.0” (the SDK reads a whole number written as text only as digits) — and 2 warnings: Status written as the number 0 (the SDK writes “Pass”) and an unknown member “Operator”.
- Routing: FactoKit.Example.Line1.AOI1 publishes to /exchange/cfx.line1 with the subject “FactoKit.Example.Line1.AOI1.CFX.Production.WorkCompleted”; the queue /queue/mes.line1 bound with “#.CFX.Production.#” receives it; a request from FactoKit.Example.MES (GetEndpointInformationRequest, for instance) goes directly to amqp://aoi1.example.local:5672, link target FactoKit.Example.Line1.AOI1.
Common mistakes
- A TimeStamp without its time zone: receivers cannot tell local time from UTC; the SDK writes the offset (2026-10-02T08:15:42.250+03:00).
- “$type” missing, not first, or without “, CFX”: Newtonsoft.Json reads it only as the first member and needs the assembly name.
- Enumeration values with another spelling (“Complete” for Completed, “PASS” for Pass) or a number the enumeration does not define.
- Reusing the UniqueID of an earlier message: receivers drop it as a duplicate.
- Sending requests through the exchange: the SDK expects them on the endpoint’s own request address.
Files
- The generated message as .json, its JSON Schema, the validation findings as CSV (one row per finding with line, column and pointer), the routing explanation as CSV, and one settings JSON for every part (the generator’s body as JSON); nothing is stored by this file.
Not verified
- The IPC-2591 standard itself was not available: the definitions are the SDK’s (2.1.1); messages or members the standard defines differently, and anything newer than this SDK, are not known.
- The broker side of the routing explanation (RabbitMQ’s addresses, the subject as routing key, topic patterns, one exchange per line) is recalled, not checked against broker documentation.
- How the SDK writes values was read from its source, not observed by running it (no .NET here): in particular ResourceState as its code in a string (“1100”), very large or small doubles in exponent form (1E+15), floats as .NET Core writes them (.NET Framework may write some with other digits), that “$type” is read only as the first member (Newtonsoft.Json’s default), and which numbers and booleans written as text Newtonsoft.Json 13 parses (invariant culture; NaN and Infinity only in the case it writes them).
Questions
- What is IPC-CFX?
- IPC-CFX (Connected Factory Exchange, IPC-2591) is a standard for the messages machines and software exchange on an electronics assembly line: work started and completed, units inspected and tested, faults, materials, station states. Each message is JSON in an envelope (MessageName, Version, TimeStamp, UniqueID, Source, Target, RequestID, MessageBody) and travels over AMQP 1.0. The IPC publishes an open-source .NET SDK with the definitions of every message; this tool’s definitions are derived from it.
- What is the “$type” in a CFX message?
- The SDK serializes with Newtonsoft.Json and writes the .NET type of the body as its first member, for example "$type": "CFX.Production.WorkCompleted, CFX". It writes "$type" again wherever a derived type stands in for its base class, such as a NumericMeasurement in a list of Measurements. A reader built on the SDK needs it there, and only as the first member of the object; where the base class is abstract it cannot read the object at all without it.
- Does a CFX message need a broker?
- Events (WorkCompleted, UnitsInspected, Heartbeat …) are published to an AMQP 1.0 broker, usually to an exchange, and subscribers read them from queues. Requests and responses do not use the broker: the SDK opens a direct AMQP connection to the request address the endpoint announces. This page explains both with your addresses but connects to nothing.
- Why does a message from my machine fail here but work in our MES?
- The SDK is lenient where a validator is strict: it ignores members it does not know, fills missing ones with their default value (an empty GUID, 0, the first choice of an enumeration) and reads enumerations as names or numbers. Those are warnings here. Errors are what the SDK refuses or reads wrongly: a string where a number belongs, an unknown enumeration value, a GUID or a date in another form, a missing or wrong “$type”, a TimeStamp without its time zone.
- Are these the official CFX schemas?
- No. The message definitions are derived from the source of the official IPC CFX SDK (version 2.1.1, Apache License 2.0); the IPC-2591 document itself was not read. The JSON Schema offered for download is FactoKit’s, built from those definitions, and its rule that value-type members are required is FactoKit’s reading of the SDK. Check against the standard and your partners’ implementations.