首页 > 其他分享 >互联工厂数据交换标准:IPC-CFX

互联工厂数据交换标准:IPC-CFX

时间:2024-08-26 08:58:09浏览次数:8  
标签:IPC Console CFX WriteLine message 数据交换 string

大家好,我是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": "[email protected]"
      },
      "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": "[email protected]"
        }
      ],
      "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,Console,CFX,WriteLine,message,数据交换,string
From: https://www.cnblogs.com/edisonchou/p/18377876/quick-start-on-ipc-cfx

相关文章

  • Android开发 - Binder 类进程间通信(IPC)的机制解析
    什么是BinderBinder是一种用于进程间通信(IPC)的机制,允许不同的进程(或者不同的组件)相互交互,提供了跨进程通信(IPC)的基础。它允许一个进程中的对象(如服务)被另一个进程中的代码(如应用组件)调用。Binder是一种特殊的对象,它能够在不同进程之间传递数据和调用方法Binder的作用进......
  • Android开发 - IBinder 类实现跨进程通信(IPC)解析
    什么是IBinderIBinder类是一个重要的接口,常用于实现跨进程通信(IPC);IBinder允许不同的进程或组件之间相互传递数据和调用方法。主要用于实现进程间通信。它是Android中的一个底层机制,允许不同的应用或组件(即使它们在不同的进程中)通过IBinder对象进行数据交换和方法调用IB......
  • 重头开始嵌入式第二十六天(Linux系统编程 进程间通信 IPC)
    目录IPC进程间通信1.管道通信管道的特性使用流程无名管道1.创建并打开管道:2.无名管道的读写:3.关闭管道: close();4.使用例子:有名管道1、创建:mkfifo2、打开有名管道 open3、管道的读写: 文件IO4、关闭管道:5、卸载管道:remove();IPC进程间通信进程间通信(In......
  • IPC对象通信方式---共享内存 | 网络通信 -编程
    共享内存共享内存机制其允许两个或多个进程共享一个给定的存储区,这一段存储区可以被两个或两个以上的进程映射至自身的地址空间中,一个进程写入共享内存的信息,可以被其他使用这个共享内存的进程,通过一个简单的内存读取错做读出,从而实现了进程间的通信。是内核预留的内存空间,最......
  • VisualStudio 产生的.sdf和.ipch文件删除、不生成
    前言全局说明VisualStudio产生的.sdf和.ipch文件删除、不生成一、说明环境:Windows7旗舰版VisualStudio2013二、原因某天,打算给vs2013的一个工程,打包备份,打包后,发现压缩包有90MB,看到数字确实很惊讶。因为这个工程就是画了几个按钮的小功能,怎么会这么大。......
  • 进程间通信IPC
    前言        进程是操作系统中独立运行的程序单元,每个进程拥有自己的内存空间。由于进程之间的内存空间是隔离的,不能直接访问彼此的内存,因此需要借助IPC来实现进程间的数据交换一.管道进程间通信的本质是让不同的进程看到同一份资源, 我们先来看管道是如何做到的......
  • 【IO】IPC通信机制函数(消息队列,共享内存,信号量集函数整理汇总)
            整理了一下IPC通信的函数,包括消息队列,共享内存,信号量集;信号量集的使用是在共享内存的基础上使用,函数太多啦,慢慢学吧cc,争取全部记住        其中在使用有关信号量集的函数的时候,进行简单的封装函数功能之后,再进行使用,会更加方便,在文章最后对信号量集的......
  • IPC-6012F-CN-中文版\英文版,2024 刚性印制板的鉴定及性能规范
    IPC-6012F-CN-中文版,2024刚性印制板的鉴定及性能规范链接:https://pan.baidu.com/s/1z1x5JPmcRHzeIQgMsMQRxg提取码:1234https://share.weiyun.com/s7XNX9gE 2023年10月,IPC-6012发布了最新版F版。与以往版本不同的是,F版中国成立了分技术组,收集,讨论和提交了大量制修订的意......
  • 广域网(WAN)、局域网(LAN)的区别与联系、WLAN与WiFi的关系,ipconfig和ping
    1.广域网和局域网广域网(WideAreaNetwork),简称WAN,是一种地域范围覆盖广的计算机网络的集合,通常所覆盖的范围从几十公里到几千公里,它能连接多个地区、城市和国家。由于其超长的覆盖范围,发送介质主要是政府或者大型企业部署的电话线或光纤,因此又被大家亲切的称为:外网、公网。......
  • 如何使用 JSON 进行数据交换 ?
    JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。JSON的设计灵感来自于JavaScript的对象表示法,但它与编程语言无关,几乎所有现代编程语言都提供了对JSON的支持。JSON已成为Web应用程序中数据交换的事实标准。JSON......