大家好,我是Edison。

全球电子制造主要集中在中国,面向未来工业4.0、中国制造2025的战略转型升级,互联互通是基础、数据是核心,如何从用户角度来定义设备加工数据的内容完整性、有效性、可扩展性将是工厂通讯连接交换的工作重点。

IPC-CFX是什么?

首先,解释下这两个缩写的意思:IPC是国际电子工业联接协会,CFX是互联工厂数据交换。

IPC-CFX是一个开放式的国际标准,它简化了机器到机器之间的通信。IPC-CFX基于IPC-2591,是全球互联工厂数据交换标准统一定义数据语言格式,我们也可以将其理解为是一个协议,它为制造商、设备、硬件及软件供应商节约沟通和再开发成本,CFX标准的应用将简化并标准化机器设备之间的通信。

总结一下,IPC-CFX就是机器设备之间通信的“统一语言”,是大家都懂的“普通话”而不是“方言”。

话外音:对于IT开发者在追求的DDD(领域驱动设计),其核心思想也是“统一语言”,统一业务和技术之间的语言,提高沟通效率,进而提高软件质量。

IPC-CFX的适用范围

目前IPC-CFX主要在电子制造行业得到应用,特别是在 SMT(表面组装技术)行业,越来越多的SMT设备厂商开始加入CFX联盟,比如SMT检测设备知名厂商Koh Young就是其中一员。

IPC-CFX标准不仅适用于 SMT(表面组装技术)的相关生产,也支持机械装配、定制化、包装和运输等上游环节,甚至电气、机械子部件等上游环节。我们使用基于PC-CFX标准的传输信息来开发一些常见的应用例如设备利用率、生产线效率 和 整体设备效率(OEE)指标等。

IPC-CFX下设备如何通信?

在IPC-CFX的标准下,设备的数据被定义为制造主题(Topic)消息结构(Message)。设备不太需要关注数据发送到哪里,数据来源于哪里,只需要知道在什么时机下发送什么数据,收到什么数据执行什么操作即可。

CFX都定义了哪些标准的Topic呢,如下图所示:

以一个Topic "WorkOrdersScheduled"为例,顾名思义:工单已排程。这个Topic代表会发出一个已经排好执行计划的工单,该工单即将在稍后某个特定时间某条生产线如SMT Line 1开始执行生产,其定义的消息数据体如下所示,还是比较完善的:

{
"ScheduledWorkOrders": [
{
"WorkOrderIdentifier": {
"WorkOrderId": "WO1122334455",
"Batch": null
},
"Scheduler": {
"OperatorIdentifier": "BADGE4486",
"ActorType": "Human",
"LastName": "Doe",
"FirstName": "John",
"LoginName": "john.doe@domain1.com"
},
"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": "joseph.smith@abcdrepairs.com"
}
],
"ReservedMaterials": [
{
"UniqueIdentifier": "UID23849854385",
"InternalPartNumber": "PN4452",
"Quantity": 0.0
},
{
"UniqueIdentifier": "UID23849854386",
"InternalPartNumber": "PN4452",
"Quantity": 0.0
},
{
"UniqueIdentifier": "UID23849854446",
"InternalPartNumber": "PN3358",
"Quantity": 0.0
}
]
}
]
}

又如 "WorkOrdersUnschedule" 这个Topic,代表SMT Line 1的某个工单即将被取消,其数据格式就简单很多:

{
"ScheduledWorkOrderIdentifiers": [
{
"WorkOrderIdentifier": {
"WorkOrderId": "WO1122334455",
"Batch": null
},
"WorkArea": "SMT Line 1"
}
]
}

除此之外,CFX还用一个统一的消息信封来包裹这个消息体,我们可以理解为定义了一个如下所示的统一消息格式:

public class CFXEnvelope
{
public string MessageName {get;set;} // eg. CFX.Produciton.Application.MaterialsApplied
public string Version {get;set;} // eg. 1.7
......
public T MessageBody {get;set;} // 消息体内容:泛型
}

Anyway,这个对于我们IT工程师是比较好理解的,这就跟我们的系统和系统之间通过消息队列(如Kafka)进行发布订阅模式的异步通信一模一样。不过这里呢,是机器与机器,机器与企业之间的通信。

但是,IPC-CFX标准下是基于AMQP协议做消息传输的,每台设备都可以看作是一个AMQP端点,通过发布和订阅实现数据的交互。此外,IPC-CFX还支持点对点(Point-to-Point)的消息模式(请求/响应模式)。

我们都知道,Kafka是不支持AMQP协议的,因此,要使用IPC-CFX就不能直接使用Kafka作为Message Broker,而IPC-CFX官方的案例也都是使用RabbitMQ来写的,虽然我觉得在设备数据交换场景Kafka的性能会更好。

如何开发机台程序?

如何让一台台的设备变成符合IPC-CFX标准的AMQP节点呢?常规做法就是在机台侧开发一个程序,这里IPC-CFX组织为我们提供了一个SDK,其实是一个.NET开发包(Nuget安装即可),所以对咱们.NET开发者是十分友好的。

  • 对于.NET 4.6可以使用 CFX.CFXSDK

  • 对于.NET Core及以上可以使用 CFX.CFXSDK.NetStandard

这个SDK提供了以下功能:

  • 将所有CFX消息的用Class/Object表示。

  • 能够将CFX消息对象序列化和反序列化为JSON格式。

  • 能够通过AMQP传输协议将CFX消息发布到一个或多个目的地。

  • 能够通过AMQP传输协议从一个或多个订阅源接收CFX消息。

  • 完全自动化的网络连接故障管理(即使在网络宕机或其他不可靠的情况下保持AMQP连接)。

  • CFX消息“假脱机”。维护由于网络条件错误而无法传输的CFX消息的持久队列。一旦网络服务恢复,消息将自动按原来的顺序传输。

  • 点对点CFX端点命令(请求/响应)支持。

  • 支持AMQP 1.0 SASL认证和TLS加密。

官方SDK文档传送门:SDK文档

不过,通过学习发现,这个SDK主要还是提供了统一的Topic和Message的数据结构,至于和RabbitMQ的连接,个人感觉不太方便使用,我们完全可以使用其他成熟的RabbitMQ API组件来完成发布和订阅。

接下来,我们来快速实践一下CFX的两种通信方式:发布订阅 和 点对点。

快速开始:搭建一个RabbitMQ

既然IPC-CFX是基于AMQP协议来的,那我们就搭一个RabbitMQ吧。这里我们快速用docker-compose安装一个RabbitMQ。

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

然后,通过下面的命令把RabbitMQ Run起来:需要注意的点就是需要手动开启AMQP1.0协议!

docker-compose up -d

#进入RabbitMQ容器
docker exec -it rabbit-mq-service /bin/bash
#开启AMQP1.0协议
rabbitmq-plugins enable rabbitmq_amqp1_0

成功运行起来后,能够成功打开RabbitMQ管理界面:

快速开始:实现基于CFX标准的发布订阅通信

发布者

这里我们通过Visual Studio创建一个控制台应用程序,基于.NET Framework 4.8来实现。

首先,安装Nuget包:

  • CFX.CFXSDK
  • EasyNetQ

其次,完成联接Broker 和 发布Message 的代码:

namespace AMQP.MachineA
{
/// <summary>
/// MachineA: SEWC.SMT.001
/// </summary>
public class Program
{
private const string _machineName = "SDC.SMT.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)
{
Console.WriteLine($"Current Machine: {_machineName}");
Console.Write($"Current Role: Publisher {Environment.NewLine}"); var connStr = $"host={_amqpBroker};username={_amqpUsername};password={_amqpPassword}";
using (var amqpBus = RabbitHutch.CreateBus(connStr))
{
while (true)
{
Console.WriteLine($"[Info] Starting to send a message to AMQP broker.");
// Build a CFX Message of MaterialsApplied
var message = new CFXEnvelope(new MaterialsApplied()
{
TransactionId = Guid.NewGuid(),
AppliedMaterials = new List<InstalledMaterial>
{
new InstalledMaterial()
{
QuantityInstalled = 1,
QuantityNonInstalled = 2
}
}
}); amqpBus.PubSub.Publish(message);
Console.WriteLine($"[Info] Finished to send a message to AMQP broker.");
Console.WriteLine("-------------------------------------------------------------------"); Thread.Sleep(1000 * 3);
}
}
}
}
}

Note:这里只是为了快速演示,实际中账号密码以及Broker地址建议写到配置文件中,并使用AMQPS协议联接,否则你的账号密码会被明文在网络中传输。

订阅者

参考发布者,仍然创建一个控制台应用程序,安装两个NuGet包。

然后,实现消费者逻辑:

namespace AMQP.MachineB
{
/// <summary>
/// MachineB: SEWC.SMT.002
/// </summary>
public class Program
{
private const string _machineName = "SDC.SMT.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)
{
Console.WriteLine($"Current Machine: {_machineName}");
Console.WriteLine($"Current Role: Subscriber {Environment.NewLine}"); var connStr = $"host={_amqpBroker};username={_amqpUsername};password={_amqpPassword}";
using (var amqpBus = RabbitHutch.CreateBus(connStr))
{
amqpBus.PubSub.Subscribe<CFXEnvelope>(_machineName, message =>
{
if (message.MessageBody is MaterialsApplied)
{
Console.WriteLine($"[Info] Got a message with topic {message.MessageName} :{Environment.NewLine}{message.ToJson(true)}");
Console.WriteLine("-------------------------------------------------------");
}
}); Console.WriteLine("Press any key to exit.");
Console.ReadLine();
}
}
}
}

最终的Demo效果如下图所示:

两个控制台应用程序模拟两个机台程序,实现了基于AMQP协议和CFX标准格式的异步通信。但是整体来讲,实现异步通信并不是重点,而是两个机台采用了所谓的“统一语言”。

快速开始:实现基于CFX标准的点对点通信

基于上面的了解,我们知道基于CFX我们还可以让设备之间实现点对点的通信,也可以不通过Broker转发,而且它仍然是基于AMQP协议的。

在点对点模式下,基于CFX SDK,会自动帮你创建一个基于Socket的通信进程,机台程序之间可以互相应答。

(1)机台A

namespace P2P.MachineA
{
/// <summary>
/// MachineA: SEWC.SMT.001
/// </summary>
public class Program
{
private const string _sendCfxHandle = "SDC.SMT.001"; // Sender
private const string _receiveCfxHandle = "SDC.SMT.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)
{
Console.WriteLine($"Current Machine: {_sendCfxHandle}");
Console.WriteLine($"Current Uri: {_sendRequestUri}");
OpenRequest();
Console.WriteLine("Press Enter Key to start the CFX Sender");
Console.ReadKey(); while (true)
{
SendRequest();
Thread.Sleep(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();
Console.WriteLine($"[Debug] SendCFXEndpoint.IsOpen : {_sendRequestEndpoint.IsOpen.ToString()}");
_sendRequestEndpoint.Open(_sendCfxHandle, new Uri(_sendRequestUri));
Console.WriteLine($"[Debug] SendCFXEndpoint.IsOpen : {_sendRequestEndpoint.IsOpen.ToString()}"); AmqpCFXEndpoint.RequestTimeout = TimeSpan.FromSeconds(10 * 2);
} private static void SendRequest()
{
var message = CFXEnvelope.FromCFXMessage(new MaterialsApplied()
{
TransactionId = Guid.NewGuid(),
AppliedMaterials = new List<InstalledMaterial>
{
new InstalledMaterial()
{
QuantityInstalled = 1,
QuantityNonInstalled = 2
}
}
});
message.Source = _sendCfxHandle;
message.Target = _receiveCfxHandle;
message.TimeStamp = DateTime.Now; try
{
Console.WriteLine($"[Info] Starting to send a message to Target Machine {_receiveCfxHandle}.");
var response = _sendRequestEndpoint.ExecuteRequest(_receiveRequestUri, message);
Console.WriteLine($"[Info] Target Machine {_receiveCfxHandle} returns : {Environment.NewLine}{response.ToJson(true)}");
}
catch (Exception ex)
{
Console.WriteLine($"[Error] Exception message: {ex.Message}");
}
finally
{
Console.WriteLine("-------------------------------------------------------");
}
}
#endregion
}
}

Note:既然是点对点,那发送者就必须要知道接收者的位置。

(2)机台B

namespace P2P.MachineB
{
/// <summary>
/// MachineB: SEWC.SMT.002
/// </summary>
public class Program
{
private const string _receiveCfxHandle = "SDC.SMT.002";
private const string _receiveRequestUri = "amqp://127.0.0.1:8235"; public static void Main(string[] args)
{
Console.WriteLine($"Current Machine: {_receiveCfxHandle}");
Console.WriteLine($"Current Uri: {_receiveRequestUri}");
OpenListener();
Console.WriteLine("Press Entery Key to end the CFX Listener"); Console.ReadKey();
} #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; Console.WriteLine($"[Debug] SendCFXEndpoint.IsOpen: {_receiveRequestEndpoint.IsOpen.ToString()}");
_receiveRequestEndpoint.Open(_receiveCfxHandle, new Uri(_receiveRequestUri));
Console.WriteLine($"[Debug] SendCFXEndpoint.IsOpen: {_receiveRequestEndpoint.IsOpen.ToString()}"); AmqpCFXEndpoint.RequestTimeout = TimeSpan.FromSeconds(10 * 2);
} private static CFXEnvelope CFXMessageOnRequestReceived(CFXEnvelope message)
{
Console.WriteLine($"[Info] Got a message from Source Machine {message.Source} :{Environment.NewLine}{message.ToJson(true)}");
Console.WriteLine("-------------------------------------------------------"); var result = (CFXEnvelope)null;
if (message.MessageBody is WhoIsThereRequest)
{
result = CFXEnvelope.FromCFXMessage(new WhoIsThereResponse()
{
CFXHandle = _receiveCfxHandle,
RequestNetworkUri = _receiveRequestUri,
RequestTargetAddress = "..."
});
}
else if (message.MessageBody is MaterialsApplied)
{
result = CFXEnvelope.FromCFXMessage(new WhoIsThereResponse()
{
CFXHandle = _receiveCfxHandle,
RequestNetworkUri = _receiveRequestUri,
RequestTargetAddress = "..."
});
}
else
{
return null;
} result.Source = _receiveCfxHandle;
result.Target = result.Source;
result.TimeStamp = DateTime.Now;
return result;
}
#endregion
}
}

点对点Demo效果:

小结

本文我们了解了IPC-CFX标准产生的背景 和 用途,它是机器设备之间通信的“统一语言”,是大家都懂的“普通话”而不是“方言”。

首先,IPC-CFX使用AMQP v1.0传输协议实现安全的连接,使用JSON进行数据编码,提供了明确的消息结构和数据内容,确保即插即用。

其次,我们通过两个Demo快速了解了如何实现一个基于CFX标准的机台端应用程序,来实现“统一语言”的设备间通信。

最后,就目前互联网上的资料来看,国内社区对于CFX的应用来看整体都还是不多的,我们也还处于学习阶段,希望未来或许有新的更新分享。

参考资料

IPC CFX 官方文档:Getting Started with SDK

齐开得科技:IPC-CFX在SMT领域的应用

MQTT vs AMQP:物联网通信协议对比

作者:周旭龙

出处:https://edisonchou.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

互联工厂数据交换标准:IPC-CFX的更多相关文章

  1. Atitit.常见软件 数据 交换格式 标准

    Atitit.常见软件 数据 交换格式 标准 1. 常见的数据格式txt ,doc ,pic,music ,vodio1 2. 通用格式json yaml phpstr1 3. 专用格式1 4. 用户 ...

  2. Java 常见数据交换格式——xml、json、yaml

    目录 数据交换格式介绍 XML 使用DOM方式解析 使用SAX方式解析 使用DOM4J方式解析 使用JDOM方式解析 JSON 使用JSONObject方式将数据转换为JSON格式 利用JSONObj ...

  3. 两种常用的数据交换格式:XML和JSON

    不同编程语言之间的数据传输,需要一种通用的数据交换格式,它需要简洁.易于数据储存.快速读取,且独立于各种编程语言.我们往往传输的是文本文件,比如我们都知道的csv(comma seperated va ...

  4. 常用两种数据交换格式之XML和JSON的比较

    目前,在web开发领域,主要的数据交换格式有XML和JSON,对于XML相信每一个web developer都不会感到陌生: 相比之下,JSON可能对于一些新步入开发领域的新手会感到有些陌生,也可能你 ...

  5. XML和JSON两种数据交换格式的比较

    在web开发领域,主要的数据交换格式有XML和JSON,对于在 Ajax开发中,是选择XML还是JSON,一直存在着争议,个人还是比较倾向于JSON的.一般都输出Json不输出xml,原因就是因为 x ...

  6. Disruptor——一种可替代有界队列完成并发线程间数据交换的高性能解决方案

    本文翻译自LMAX关于Disruptor的论文,同时加上一些自己的理解和标注.Disruptor是一个高效的线程间交换数据的基础组件,它使用栅栏(barrier)+序号(Sequencing)机制协调 ...

  7. JSON的基本结构和数据交换原理

    0.补充的写在前面的话 2017.03.29 补充内容 最近看到这篇博客的阅读量,想来应该是有部分网友来过想要了解JSON的基本概念,这篇博文写得可能不是那么好,所以现在再补充贴一位老师的文章,希望能 ...

  8. servlet与ajax数据交换(json格式)

    JSON数据格式: JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式. 易于人阅读和编写.同时也易于机器解析和生成. 它基于的一个子集. JSON采用完全独 ...

  9. (77)Wangdao.com第十五天_JavaScript 用于数据交换的文本格式 JSON 对象

    JSON 对象 JSON (JavaScript Object Notation 的缩写) 也是一种数据,是 JavaScript 的原生对象,用来处理 JSON 格式数据.它有两个静态方法:JSON ...

  10. DataStage 九、数据交换到MySQL以及乱码问题

    DataStage序列文章 DataStage 一.安装 DataStage 二.InfoSphere Information Server进程的启动和停止 DataStage 三.配置ODBC Da ...

随机推荐

  1. Android Verified Boot 2.0 AVB详解(基于Android P)

    原文地址:https://android.googlesource.com/platform/external/avb/+/master/ 译文地址:https://blog.csdn.net/sha ...

  2. Node.js - fs.path模块

    首先我有话说,是谁说的学完ajax就可以去vue了,太天真了我,学会js钻出来个ajax,学完ajax钻出来个node.js这一步步的,当然node不会学到太深入把表面的认识一下就可以了,这之后可能更 ...

  3. 如何将自己的网站从 HTTP 的转换为 HTTPS 的

    1. 获取 SSL/TLS 证书 首先,你需要获得一个 SSL/TLS 证书.你可以从以下来源之一获取证书: 免费证书: Let's Encrypt:一个免费的.自动化的证书颁发机构(CA),广泛使用 ...

  4. Spring声明事务和分布式事务处理技术

    Spring声明事务的两种方式 方式一.传统的编程式事务管理: 需要手动编写代码在业务层注入事务管理模板(一般不用) 方式二.基于 AOP 技术实现的声明式事务管理: Spring 声明式事务管理在底 ...

  5. koa web框架入门

    1.在hello-koa这个目录下创建一个package.json,这个文件描述了我们的hello-koa工程会用到哪些包.完整的文件内容如下: { "name": "h ...

  6. Solo 开发者周刊 (第5期):打破常规,探索技术新边界

    这里会整合 Solo 社区每周推广内容.产品模块或活动投稿,每周五发布.在这期周刊中,我们将深入探讨开源软件产品的开发旅程,分享来自一线独立开发者的经验和见解.本杂志开源,欢迎投稿. 产品推荐 1. ...

  7. SeaweedFS + TiKV 部署保姆级教程

    在使用 JuiceFS 时,我们选择了 SeaweedFS 作为对象存储,以及 TiKV 作为元数据存储,目前在 SeaweedFS 上已经存储了近1.5PB 的数据.关于 SeaweedFS 和 T ...

  8. C#:只支持GET和POST方法的浏览器,如何发送PUT/DELETE请求?RESTful WebAPI如何响应?

    理想的RESTful WebAPI采用面向资源的架构,并使用请求的HTTP方法表示针对目标资源的操作类型.但是理想和现实是有距离的,虽然HTTP协议提供了一系列原生的HTTP方法,但是在具体的网络环境 ...

  9. golang 实现的零依赖、高性能、并发 mysqldump 工具。

    mysqldump golang 中实现的零依赖.高性能.并发 mysqldump 工具. 项目地址: https://github.com/dengjiawen8955/mysqldump/blob ...

  10. [rCore学习笔记 019]在main中测试本章实现

    写在前面 本随笔是非常菜的菜鸡写的.如有问题请及时提出. 可以联系:1160712160@qq.com GitHhub:https://github.com/WindDevil (目前啥也没有 批处理 ...