How do devices communicate under IPC-CFX?
Under the IPC-CFX standard, the data of a device is defined asManufacturing Theme (Topic)cap (a poem)Message structure (Message)The device does not need to be concerned with where the data is being sent to or from. The device is less concerned with where the data is being sent to and where it originates from, and only needs to know what data is being sent and what action is being performed on what data is being received at what timing.
What standard Topics are defined by CFX, as shown below:
Take a Topic "WorkOrdersScheduled" as an example, as the name suggests: work order has been scheduled. This Topic on behalf of a work order has been scheduled for the execution of the plan will be issued, the work order is about to be a specific time later a production line such as SMT Line 1 to start the implementation of the production, the definition of the message data body is shown below, or more complete:
{ "ScheduledWorkOrders": [ { "WorkOrderIdentifier": { "WorkOrderId": "WO1122334455", "Batch": null }, "Scheduler": { "OperatorIdentifier": "BADGE4486", "ActorType": "Human", "LastName": "Doe", "FirstName": "John", "LoginName": "@" }, "WorkArea": "SMT Line 1", "StartTime": "2018-08-02T11:00:00", "EndTime": "2018-08-02T15:00:00", "ReservedResources": [ "L1PRINTER1", "L1PLACER1", "L1PLACER2", "L1OVEN1" ], "ReservedTools": [ { "UniqueIdentifier": "UID23890430", "Name": "TorqueWrench_123" } ], "ReservedOperators": [ { "OperatorIdentifier": "BADGE489435", "ActorType": "Human", "LastName": "Smith", "FirstName": "Joseph", "LoginName": "@" } ], "ReservedMaterials": [ { "UniqueIdentifier": "UID23849854385", "InternalPartNumber": "PN4452", "Quantity": 0.0 }, { "UniqueIdentifier": "UID23849854386", "InternalPartNumber": "PN4452", "Quantity": 0.0 }, { "UniqueIdentifier": "UID23849854446", "InternalPartNumber": "PN3358", "Quantity": 0.0 } ] } ] }
Another example is the "WorkOrdersUnschedule" Topic, which represents that a work order for SMT Line 1 is about to be canceled, and its data format is much simpler:
{ "ScheduledWorkOrderIdentifiers": [ { "WorkOrderIdentifier": { "WorkOrderId": "WO1122334455", "Batch": null }, "WorkArea": "SMT Line 1" } ] }
In addition to this, CFX wraps this message body in a uniform message envelope, which we can understand as defining a uniform message format as shown below:
public class CFXEnvelope { public string MessageName {get;set;} // eg. public string Version {get;set;} // eg. 1.7 ...... public T MessageBody {get;set;} // Message body content: generalized }
Anyway, this one is better understood by us IT engineers, it's exactly the same as asynchronous communication between our systems and our systems in publish-subscribe mode via message queues (e.g. Kafka). But here, it's machine-to-machine, machine-to-enterprise communication.
But.The IPC-CFX standard is based on the AMQP protocol for messaging, and each device can be considered an AMQP endpoint.that enables data interaction through publish and subscribe. In addition, IPC-CFXPoint-to-Point is also supported.of the message mode (request/response mode).
We all know that Kafka does not support the AMQP protocol, so to use IPC-CFX can not directly use Kafka as Message Broker, and IPC-CFX official cases are written using RabbitMQ, although I think that in the device data exchange scenarios Kafka's performance will be better.
How to develop a machine program?
How to make a machine become an IPC-CFX compliant AMQP node? The conventional practice is to develop a program on the machine side, here IPC-CFX organization provides us with an SDK, which is actually a .NET development kit (Nuget installation can be), so it is very friendly to our .NET developers.
-
For .NET 4.6 you can use the
-
For .NET Core and above you can use the
This SDK provides the following features:
-
Represent all CFX messages as Class/Object.
-
Ability to serialize and deserialize CFX message objects into JSON format.
-
Ability to publish CFX messages to one or more destinations via the AMQP transport protocol.
-
Ability to receive CFX messages from one or more subscription sources via the AMQP transport protocol.
-
Fully automated network connection fault management (maintaining AMQP connectivity even when the network is down or otherwise unreliable).
-
CFX messages "fake offline". Maintains a persistent queue of CFX messages that could not be transmitted due to faulty network conditions. Once network service is restored, the messages are automatically transmitted in the original order.
-
Peer-to-peer CFX endpoint command (request/response) support.
-
Supports AMQP 1.0 SASL authentication and TLS encryption.
Official SDK documentation portal:SDK Documentation
However, through the study found that this SDK mainly provides a unified Topic and Message data structure, as for the connection with RabbitMQ, I personally feel that it is not very convenient to use, we can use other mature RabbitMQ API components to complete the publish and subscribe.
Next, let's take a quick hands-on look at CFX's two communication methods: publish-subscribe and peer-to-peer.
Quick Start: Building a RabbitMQ
Since IPC-CFX comes based on the AMQP protocol, let's build a RabbitMQ. Here we quickly install a RabbitMQ with docker-compose.
version: '3' services: rabbitmq1: image: rabbitmq:3.8-management container_name: rabbit-mq-service hostname: rabbit-mq-server ports: - "5672:5672" - "15672:15672" restart: always environment: - RABBITMQ_DEFAULT_USER=rabbit # user account - RABBITMQ_DEFAULT_PASS=EdisonTalk2024 # password volumes: - rabbitmq_data:/var/lib/rabbitmq volumes: rabbitmq_data: driver: local
Then, run RabbitMQ with the following command: the point to note is that you need to manually enable the AMQP1.0 protocol!
docker-compose up -d
#go intoRabbitMQcontainers
docker exec -it rabbit-mq-service /bin/bash
#opensAMQP1.0protocols
rabbitmq-plugins enable rabbitmq_amqp1_0
After successfully running up, you are able to successfully open the RabbitMQ management interface:
Quick Start: Implementing CFX Standards-based Publish-Subscribe Communication
publisher
Here we create a console application through Visual Studio, based on .
First, install the Nuget package:
- EasyNetQ
Next, complete the code that links the Broker and publishes the Message:
namespace { /// <summary> /// MachineA: .001 /// </summary> public class Program { private const string _machineName = ".001"; private const string _amqpBroker = "rabbit-mq-server"; // RabbitMQ-Host private const string _amqpUsername = "rabbit"; // RabbitMQ-User private const string _amqpPassword = "rabbit-password"; // RabbitMQ-Password public static void Main(string[] args) { ($"Current Machine: {_machineName}"); ($"Current Role: Publisher {}"); var connStr = $"host={_amqpBroker};username={_amqpUsername};password={_amqpPassword}"; using (var amqpBus = (connStr)) { while (true) { ($"[Info] Starting to send a message to AMQP broker."); // Build a CFX Message of MaterialsApplied var message = new CFXEnvelope(new MaterialsApplied() { TransactionId = (), AppliedMaterials = new List<InstalledMaterial> { new InstalledMaterial() { QuantityInstalled = 1, QuantityNonInstalled = 2 } } }); (message); ($"[Info] Finished to send a message to AMQP broker."); ("-------------------------------------------------------------------"); (1000 * 3); } } } } }
Note:Here is just for a quick demonstration, in practice it is recommended that the account password and the Broker address be written to a configuration file and linked using the AMQPS protocol, otherwise your account password will be transmitted over the network in clear text.
subscriber
Referring to the publisher, still create a console application and install the two NuGet packages.
Then, implement the consumer logic:
namespace { /// <summary> /// MachineB: .002 /// </summary> public class Program { private const string _machineName = ".002"; private const string _amqpBroker = "rabbit-mq-server"; // RabbitMQ-Host private const string _amqpUsername = "rabbit"; // RabbitMQ-User private const string _amqpPassword = "rabbit-password"; // RabbitMQ-Password public static void Main(string[] args) { ($"Current Machine: {_machineName}"); ($"Current Role: Subscriber {}"); var connStr = $"host={_amqpBroker};username={_amqpUsername};password={_amqpPassword}"; using (var amqpBus = (connStr)) { <CFXEnvelope>(_machineName, message => { if ( is MaterialsApplied) { ($"[Info] Got a message with topic {} :{}{(true)}"); ("-------------------------------------------------------"); } }); ("Press any key to exit."); (); } } } }
The final demo result is shown below:
Two console applications simulate two machine programs that implement asynchronous communication based on the AMQP protocol and the CFX standard format. But overall, the implementation of asynchronous communication is not the point, but the two machines use the so-called "Unified language”。
Quick Start: Implementing Point-to-Point Communication Based on the CFX Standard
Based on the above, we know that based on CFX we can also enable point-to-point communication between devices, also without forwarding through Broker, and it is still based on AMQP protocol.
In peer-to-peer mode, based on the CFX SDK, it will automatically create a socket-based communication process for you, and the machine programs can answer each other.
(1) Machine A
namespace { /// <summary> /// MachineA: .001 /// </summary> public class Program { private const string _sendCfxHandle = ".001"; // Sender private const string _receiveCfxHandle = ".002"; // Receiver private const string _sendRequestUri = "amqp://127.0.0.1:8234"; // Sender private const string _receiveRequestUri = "amqp://127.0.0.1:8235"; // Receiver public static void Main(string[] args) { ($"Current Machine: {_sendCfxHandle}"); ($"Current Uri: {_sendRequestUri}"); OpenRequest(); ("Press Enter Key to start the CFX Sender"); (); while (true) { SendRequest(); (1000 * 5); // Send message every 5 seconds } } #region AMQP Sender private static AmqpCFXEndpoint _sendRequestEndpoint; private static void OpenRequest() { if (_sendRequestEndpoint != null) { _sendRequestEndpoint.Close(); _sendRequestEndpoint = null; } _sendRequestEndpoint = new AmqpCFXEndpoint(); ($"[Debug] : {_sendRequestEndpoint.()}"); _sendRequestEndpoint.Open(_sendCfxHandle, new Uri(_sendRequestUri)); ($"[Debug] : {_sendRequestEndpoint.()}"); = (10 * 2); } private static void SendRequest() { var message = (new MaterialsApplied() { TransactionId = (), AppliedMaterials = new List<InstalledMaterial> { new InstalledMaterial() { QuantityInstalled = 1, QuantityNonInstalled = 2 } } }); = _sendCfxHandle; = _receiveCfxHandle; = ; try { ($"[Info] Starting to send a message to Target Machine {_receiveCfxHandle}."); var response = _sendRequestEndpoint.ExecuteRequest(_receiveRequestUri, message); ($"[Info] Target Machine {_receiveCfxHandle} returns : {}{(true)}"); } catch (Exception ex) { ($"[Error] Exception message: {}"); } finally { ("-------------------------------------------------------"); } } #endregion } }
Note:Since it's peer-to-peer, it's important for the sender to know the location of the receiver.
(2) Machine B
namespace { /// <summary> /// MachineB: .002 /// </summary> public class Program { private const string _receiveCfxHandle = ".002"; private const string _receiveRequestUri = "amqp://127.0.0.1:8235"; public static void Main(string[] args) { ($"Current Machine: {_receiveCfxHandle}"); ($"Current Uri: {_receiveRequestUri}"); OpenListener(); ("Press Entery Key to end the CFX Listener"); (); } #region AMQP Receiver private static AmqpCFXEndpoint _receiveRequestEndpoint; private static void OpenListener() { if (_receiveRequestEndpoint != null) { _receiveRequestEndpoint.Close(); _receiveRequestEndpoint = null; } _receiveRequestEndpoint = new AmqpCFXEndpoint(); _receiveRequestEndpoint.OnRequestReceived -= CFXMessageOnRequestReceived; _receiveRequestEndpoint.OnRequestReceived += CFXMessageOnRequestReceived; ($"[Debug] : {_receiveRequestEndpoint.()}"); _receiveRequestEndpoint.Open(_receiveCfxHandle, new Uri(_receiveRequestUri)); ($"[Debug] : {_receiveRequestEndpoint.()}"); = (10 * 2); } private static CFXEnvelope CFXMessageOnRequestReceived(CFXEnvelope message) { ($"[Info] Got a message from Source Machine {} :{}{(true)}"); ("-------------------------------------------------------"); var result = (CFXEnvelope)null; if ( is WhoIsThereRequest) { result = (new WhoIsThereResponse() { CFXHandle = _receiveCfxHandle, RequestNetworkUri = _receiveRequestUri, RequestTargetAddress = "..." }); } else if ( is MaterialsApplied) { result = (new WhoIsThereResponse() { CFXHandle = _receiveCfxHandle, RequestNetworkUri = _receiveRequestUri, RequestTargetAddress = "..." }); } else { return null; } = _receiveCfxHandle; = ; = ; return result; } #endregion } }
Peer-to-peer Demo effect:
wrap-up
In this article we have learned about the background and uses of the IPC-CFX standard, which is a standard for communication between machines and devices."Unified language"It is "Putonghua", which everyone understands, and not "dialect".
First, IPC-CFX uses the AMQP v1.0 transport protocol for secure connectivity and JSON for data encoding, providing a clear message structure and data content to ensure plug-and-play.
Secondly, we took a quick look at two demos to see how to implement a CFX standard-based console application for "Unified Language" inter-device communication.
Finally, the current information on the Internet, the domestic community for the application of CFX to see the overall are still not much, we are also still in the learning stage, I hope that in the future there may be new updates to share.
bibliography
IPC CFX Official Documentation: Getting Started with SDK
Qikaider Technology: IPC-CFX in SMT Field
MQTT vs AMQP: A Comparison of IoT Communication Protocols