亚马逊AWS官方博客

基于Amazon IoT SiteWise 与企业微信平台的数字化工厂虚拟安灯解决方案

工业安灯系统又称“Andon”系统,主要分布在工厂车间各处,是一种通过灯光和声音报警来监控生产线上相关设备和产品质量的信息管理工具。安灯系统最早起源于日本丰田公司,用来实现车间现场的目视管理,如今却已经广泛用于各种工业场景。通过安灯系统,现场作业人员能够及时发送故障报警信号,维修人员也能及时赶到现场处理,帮助企业建立现场故障报警目视管理,从而提高现场管理水平,降低管理成本。

亚马逊云科技为广大工业客户提供了基于亚马逊云的虚拟安灯平台。针对于传统物理安灯系统架构复杂,开发和运维成本较高并不易规模化扩大问题,通过基于云平台的虚拟安灯能够帮助企业客户加速安灯上线时间,节省部署成本并且实现灵活的大规模扩展。除此之外,虚拟安灯能够更好地和各种 Web 以及移动应用集成,帮助客户实现灵活多变的安灯模式。

企业微信平台,是腾讯微信团队为企业打造的专业办公管理工具,同时作为一款“善于连接”的数字化工具,企业微信也在制造业中有着广泛应用。目前国内很多企业利用企业微信作为人,设备,系统之间的连接工具,并成为连接生产,分配,流通,消费等各个重要环节的高速公路。

在这篇博文中,我们将向您介绍如何通过 Amazon IoT SiteWise 将设备产生的运行数据进行实时监控,并且在 Amazon 云中设计不同的事件模型,并最终集成到客户企业微信平台。从而让您可以在工厂内任何时间,任何地点都可以及时收到产线上设备产生的事件通知。

  • 通过 Amazon IoT SiteWise 将需要监控的设备数据导入到 Amazon IoT Events 事件模型
  • 通过 Amazon IoT Events 事件模型来设计不同的触发条件并推送事件到企业微信平台

关于Amazon IoT SiteWise/Amazon IoT Events

Amazon IoT SiteWise 是一项全面托管的亚马逊云科技 IoT 服务,可用于收集,组织和分析大规模工业设备的数据。 Amazon IoT SiteWise 使您能够从传感器,设备或本地网关收集工厂车间的数据,并使用网关软件(Amazon IoT SiteWise连接器)上传到 Amazon 云。Amazon IoT SiteWise Gateway在运行Amazon IoT Greengrass 的通用工业网关设备上运行,并通过OPC UA 协议直接从服务器中读取数据。然后,您可以使用丰富的资产建模框架来构建和组织数据,以创建设备和流程的数字虚拟映射。使用IoT SiteWise Monitor,您可以创建操作仪表盘并与工厂操作员共享,以便实时监控和可视化设备运行状况。

Amazon IoT Events 是一项完全托管的服务,可以帮助您轻松地检测来自 IoT 传感器和应用程序的事件并做出响应。事件是识别比预期更复杂的情况的数据模式,例如当皮带卡住时设备的变化或运动检测器使用移动信号来激活灯和监控摄像机。在 IoT Events 出现之前,您必须构建成本高昂的自定义应用程序来收集数据,通过应用决策逻辑来检测事件,然后触发另一个应用程序对事件做出响应。利用 IoT Events,即可在发送不同的遥测数据的数千个物联网传感器之间轻松检测事件。您只需选择要获取的相关数据源,使用简单的“if-then-else”语句为每个事件定义逻辑,并选择在事件发生时触发的警报或自定义操作。IoT Events 自动触发警报和操作,根据您定义的逻辑响应事件,以快速地解决问题,降低维护成本,并提高运营效率。

关于微信企业平台

企业微信平台,是腾讯微信团队为企业打造的专业办公管理工具,同时作为一款“善于连接”的数字化工具,企业微信也在制造业中有着广泛应用。目前国内很多企业利用企业微信作为人,设备,系统之间的连接工具,并成为连接生产,分配,流通,消费等各个重要环节的高速公路。企业微信提供了通讯录管理、客户联系、身份验证、应用管理、消息推送、素材管理等API,企业可以使用这些API,为企业接入更多个性化的办公应用。

解决方案架构综述

此解决方案利用Amazon IoT Greengrass的边缘网关服务,通过SiteWise Connector将来自不同PLC的数据通过标准OPC UA协议连接到Amazon云平台。到了云端之后,客户可以将需要监控的数据点位进行Notification的配置,这样相应点位数据就会实时推送到Amazon IoT Core MQTT 代理平台。然后我们便可以通过消息代理平台将数据推送到Amazon云的任何服务。在这个方案中,我们将监控数据注入到Amazon IoT Events服务,并设计不同的事件模型来自定义响应条件,最后通过lambda来调用企业微信接口将数据推送给工厂内工人,工程师或者相应管理人员。

先决条件

  • 您已经在Amazon 云端搭建完成基于Amazon IoT SiteWise 的智能工厂 demo,有相应的 Measurements 或 Metrics 可以进行在线实时监控。
  • 如果没有达到以上条件,可以通过以下方式在 Amazon EC2中建立OPC UA 服务器,并将数据导入到 Amazon IoT SiteWise云端。

https://github.com/node-opcua/node-opcua

https://docs.aws.amazon.com/iot-sitewise/latest/userguide/what-is-sitewise.html

演练操作步骤

本演练操作中有四个部分:

  • 配置 Amazon IoT SiteWise 将数据点位发布到 Amazon IoT Core 并建立相关规则引擎
  • 在规则引擎中创建 lambda action 对数据进行抽取,转换并加载到 Amazon IoT Events inputs
  • Amazon IoT Events 中创建事件触发器并配置相应规则
  • 配置企业微信平台推送 Lambda 无服务应用,将事件推送到企业微信端

配置Amazon IoT SiteWise 将数据点位发布到 Amazon IoT Core并建立相关规则引擎

基于先决条件,我们已经搭建了OPC UA 服务器,并且把数据打到Amazon IoT SiteWise,SiteWise 作为工业设备和数据的连接平台可以将数据推送到 Amazon IoT Core,并由Amazon IoT Core推送到 Amazon 云的其他服务中。

打开 Amazon IoT SiteWise console,定位到你已建立的Asset,并且点击Measurements栏。此时,你可以看到已经建立的OPC UA 资产。如下图所示,作者建立了名为 “OPCUA_Server_Asset”的资产,并生成了5个 Measurements 监控指标。请确保每个指标的 Notification Topic 已经enable,这意味着该检测点位的数据会实时推送到 Amazon IoT Core对应的Topic。

如果 Notification Topic 还没有enable,请点击右上角Edit,进入Edit页面,并 enable对应点位的 Notification Topic,如下图所示:

这样数据就可以打到 Amazon IoT Core MQTT Topic,复制 Notification Topic 并在 Amazon IoT Core 中进行订阅,确保数据正常显示如下:

在规则引擎中创建lambda action对数据进行抽取,转换并加载到Amazon IoT Events inputs

目前为止,需要监控的Measurements已经打到Amazon IoT Core,我们看到Topic中的数据格式如下所示:

一个json数据组中包含了10个value,对应10个不同时间的设备数据。我们需要将每个数据展开,形成只包含一个value的json数组,并把每个数组作为一个数据发送给Amazon IoT Events inputs作为事件输入。

IoT Events inputs输入格式为Json数组,示例如下:

因此我们建立一个IoT Rule,将来自’$aws/sitewise/asset-models/+/assets/+/properties/+’的数据导入到 lambda,在lambda中,我们将json数组展平,并调用IoT Events API将数据写入IoT Events服务。

我们可以通过Amazon CloudFormation 来建立所需的 IoT Rule,lambda 以及 IAM role 资源。请事先将所需的lambda上传到S3的指定bucket位置。这里,我们已经将lambda “blog_demo_iot_json_flatten”上传到S3 bucket “blog-test-cloudformation”中。

CloudFormation 模板如下:

IoT Rule 部分:

    "AWSTemplateFormatVersion": "2010-09-09",
	 "Parameters" : {
        "blogdemoiotrulename" : {
        "Type" : "String",
        "Description" : "input name of iot rule"
        },
		"AWSLambdaBasicExecutionRole": {
            "Type": "String",
            "Description": "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
        }
    },
    "Resources": {
		"MyTopicRule": {
			"Type": "AWS::IoT::TopicRule",
			"Properties": {
				"RuleName": {
					"Ref": "blogdemoiotrulename"
				},
				"TopicRulePayload": {
					"RuleDisabled": "false",
					"Sql": "SELECT * FROM '$aws/sitewise/asset-models/+/assets/+/properties/+'",
					"Actions": [{
						"Lambda" : {
							"FunctionArn": {
                                  "Fn::GetAtt": [
                                     "Blogdemoflattenlambda",
                                     "Arn"
							]}
                        }
					}]
				}
			}
		},

Lambda部分:

"Blogdemoflattenlambda": {
            "Type" : "AWS::Lambda::Function",
            "Properties" : {
               "Code" : {
                    "S3Bucket": "blog-test-cloudformation",
                    "S3Key": "blog_demo_iot_json_flatten.zip"
                  },
            "Handler" : "blog_demo_iot_json_flatten/lambda_function.lambda_handler",
            "Role": {
                "Fn::GetAtt": [
                   "BlogdemoiotRuleActionRole",
                   "Arn"
               ]
            },
            "Runtime" : "python3.7",
            "Timeout" : 25,
            "TracingConfig" : {
            "Mode": "Active"
            }
          }
        },

IAM Role部分:

"BlogdemoiotRuleActionRole": {
            "Type": "AWS::IAM::Role",
            "Properties": {
                "AssumeRolePolicyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Principal": {
                                "Service": [                                   
									"iotevents.amazonaws.com",
									"lambda.amazonaws.com"
                                ]
                            },
                            "Action": [
                                "sts:AssumeRole"
                            ]
                        }
                    ]
                },
                "Policies": [
                    {
                        "PolicyName": "Blog_demo_policy1",
                        "PolicyDocument": {
                            "Version": "2012-10-17",
                            "Statement": [
                                {
                                    "Effect": "Allow",
                                    "Action": "iotevents:BatchPutMessage",
                                    "Resource": "arn:aws-cn:iotevents:cn-north-1:Account ID:input/*"
                                }
                            ]
                        }
                    }
                ],
				 "ManagedPolicyArns": [
                    {
						"Ref": "AWSLambdaBasicExecutionRole"
                    }
                ],
				"RoleName" : "Blog_demo_iotRuleActionRole"
            }
        },

通过以上,我们建立了IoT Rule,对应的lambda以及IAM role,并将 role 赋予给了lambda。请注意,由于lambda需要调用 IoT Events inputs API 接口,因此需要赋予 iotevents:BatchPutMessage policy。

另外需要调用接口将 json 数组打入IoT Events 服务,对应的python SDK 文档如下:

https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iotevents-data.html#IoTEventsData.Client.batch_put_message

如果CloudFormation成功部署资源,可在IoT console中看到建立起来的IoT Rule资源。

另外请记得在 lambda console中打开已经建立的 lambda 函数,并在 lambda trigger 中添加 IoT Rule 作为触发源,这样数据就可以从 IoT Core 打入 lambda。

Amazon IoT Events中创建事件触发器并配置相应规则

到这里,数据已经从SiteWise中打入了IoT Core,并且我们已经通过rule执行了lambda函数,将json数组展平并且批量地导入Amazon IoT Events。接下来,我们需要设计IoT Events inputs来作为json数据入口,并设计detector来定义事件逻辑以及对应的触发动作。

在这个demo里面,我们将检测FanSpeed和Pressure这两个值,我们将建立两个inputs和detector来触发对应事件。其中检测和触发逻辑如下定义:

  • FanSpeed (>1030 进入abnormal状态并产生报警,推送IoT topic并推送微信企业号通知)
  • Pressure(> 0.9 进入abnormal状态并产生报警,推送IoT topic并推送微信企业号通知)

同样,我们利用CloudFormation建立相关资源,模板如下:

IoT Events inputs 模板:

"BlogdemoioteventsInput2": {
      "Type": "AWS::IoTEvents::Input",
      "Properties": {
        "InputName": "Blog_demo_PressureInput",
        "InputDescription": "Input Template for CloudFormation for blog demo",
        "InputDefinition": {
          "Attributes": [
            {
              "JsonPath": "type"
            },
            {
              "JsonPath": "asset_id"
            },
            {
              "JsonPath": "asset_property_id"
            },
            {
              "JsonPath": "time_in_seconds"
            },
            {
              "JsonPath": "offset_in_nanos"
            },
            {
              "JsonPath": "asset_property_quality"
            },
            {
              "JsonPath": "asset_property_value"
            },
            {
              "JsonPath": "asset_property_data_type"
            },
            {
              "JsonPath": "event_id"
            }
          ]
        }
      }
    },

IoT Events Detector 模板:

"MyDetectorModel2": {
      "Type": "AWS::IoTEvents::DetectorModel",
      "Properties": {
        "DetectorModelName": "Blog_demo_PressureDetectorModel",
        "DetectorModelDescription": "My Detector Model created by CloudFormation",
        "Key": "asset_id",
        "RoleArn": {
                "Fn::GetAtt": [
                   "BlogdemoioteventsActionRole",
                   "Arn"
               ]
            },
        "DetectorModelDefinition": {
          "InitialStateName": "Pressure_Normal_State",
          "States": [
            {
              "StateName": "Pressure_Normal_State",
              "OnEnter": {
                "Events": [
                  {
                    "EventName": "Pressure_Normal",
					"Condition": "true",
                    "Actions": [
                      {
                                        "IotTopicPublish": {
                                            "MqttTopic": "myIoTTopic3"
                                        }
                                    },
                                    {
                                        "Lambda": {
                                            "FunctionArn": {
                                  "Fn::GetAtt": [
                                     "Blogdemoalarmtowechat",
                                     "Arn"
							]}
                                        }
                                    }
                    ]
                  }
                ]
              },
              "OnInput": {
                "Events": [],
                "TransitionEvents": [
				    {
                                "EventName": "Pressure_Into_Abnormal",
								"Condition": { "Fn::Join" : [ ".", ["$input", {"Ref": "BlogdemoioteventsInput2"}, "asset_property_value > 0.9"] ] },
                                "Actions": [],
                                "NextState": "Pressure_Abnormal_State"
                            }
                ]
              },
              "OnExit": {
                "Events": []
              }
            },
            {
              "StateName": "Pressure_Abnormal_State",
              "OnEnter": {
                "Events": [
                            {
                                "EventName": "Pressure_Abnormal",
                                "Condition": "true",
                                "Actions": [
                                    {
                                        "IotTopicPublish": {
                                            "MqttTopic": "myIoTTopic3"
                                        }
                                    },
                                    {
                                        "Lambda": {
                                            "FunctionArn": {
                                  "Fn::GetAtt": [
                                     "Blogdemoalarmtowechat",
                                     "Arn"
							]}
                                        }
                                    }
                                ]
                            }
                ]
              },
			    "OnInput": {
                "Events": [],
                "TransitionEvents": [
				    {
                                "EventName": "Pressure_Back_to_Normal",
								"Condition": { "Fn::Join" : [ ".", ["$input", {"Ref": "BlogdemoioteventsInput2"}, "asset_property_value < 0.9"] ] },
                                "Actions": [],
                                "NextState": "Pressure_Normal_State"
                            }
                ]
              },
              "OnExit": {
                "Events": []
              }
            }
          ]
        }
      }
},

对应的IAM role模板:

"BlogdemoioteventsActionRole": {
            "Type": "AWS::IAM::Role",
            "Properties": {
                "AssumeRolePolicyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Principal": {
                                "Service": [
									"iotevents.amazonaws.com",
									"lambda.amazonaws.com"
                                ]
                            },
                            "Action": [
                                "sts:AssumeRole"
                            ]
                        }
                    ]
                },
				 "Policies": [
                    {
                        "PolicyName": "Blog_demo_policy2",
                        "PolicyDocument": {
                             "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "iot:Publish",
            "Resource": "arn:aws-cn:iot:cn-north-1:Account ID:topic/*"
        },
        {
            "Effect": "Allow",
            "Action": "lambda:InvokeFunction",
            "Resource": "arn:aws-cn:lambda:cn-north-1:Account ID:function:*"
        }
    ]
                        }
                    }
                ],
             "ManagedPolicyArns": [
                    {
						"Ref": "AWSLambdaBasicExecutionRole"
                    }
                ],
				"RoleName" : "Blog_demo_ioteventsActionRole"
            }
        },

其中 lambda 函数 Blogdemoalarmtowechat 为企业微信推送函数,用于将报警事件按照一定的格式推送到企业微信。该 lambda 函数的详细设计将在后面内容中说明。

由此 IoT Events 服务相关资源都建立完毕,打开 IoT Events console 并点击对应 detector 资源,如下图所示:

对应 iot topic 和企业微信推送事件也建立完毕,如下图所示:

配置企业微信平台推送Lambda无服务应用,将事件推送到企业微信端

到现在,数据已经到达 IoT Events 服务,接下来我们需要配置 lambda 函数,让 detector 调用这个函数来推送事件到企业微信。

企业微信提供了API 接口,你可以根据自己的需要,调用对应的企业微信服务。这里,我们应用发送应用消息接口服务。用户可以发送文本,图片,视频,语音或者 Markdown 消息到企业微信客户端。相应的企业微信订阅人员既可收到相应消息推送。

企业微信 API 接口说明网站地址如下:

https://work.weixin.qq.com/api/doc/90000/90135/90664

这里,我们设计 Markdown 消息类型,示例效果:

由此,我们需要获取 Corpid 和 Secret(请在自己的企业微信号中找到),并调用接口来获取 access_token。

获取 access_token 说明网站地址如下:

https://work.weixin.qq.com/api/doc/90000/90135/91039

另外请准备好 Agentid 和Userid,这样你就可以用刚才获取到的access_token来调用接口进行Markdwon消息推送。

应用消息推送接口说明网站地址如下:

https://work.weixin.qq.com/api/doc/90000/90135/90236

另外,对于客户敏感信息,比如 Corpid, Secret, Agentid 和 Userid,我们用 Amazon Secret Manager 服务进行密码管理,这样您就可以避免将敏感信息写入代码,由此提高数据安全。

关于 Amazon Secret Manager 的介绍如下:

https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html

这里,我们用CloudFormation创建了Amazon Secret Manager服务,模板如下:

"MySecretB": {
    "Type": "AWS::SecretsManager::Secret",
    "Properties": {
      "Name": "MySecretForAppB",
      "Description": "This secret has a hardcoded password in SecretString (use GenerateSecretString instead)",
      "SecretString": "{\"Corpid\":\"abc123456\",\"Secret\":\"def123456\",\"User\":\"demo_user_1\",\"Agentid\":\"ghi123456\"}"
    }
  }

打开 Amazon Secret Manager Console 我们可以看到相关资源创建成功。

另外Amazon云提供相关sample code帮助您在自己的程序中获取密码,我们提供各种语言支持的sdk。在同一Console中,可以看到示例代码如下所示:

这样,企业微信推送相关的 lambda 函数就已经实现了,我们再启用 CloudFormation 进行部署。

由此我们完成了从Amazon IoT SiteWise推送设备运营性能到企业微信通知,并进行事件监控的整体流程和部署。打开Amazon IoT Core,订阅对应的IoT topic,我们看到如下事件数据:

另外打开你的企业微信,会看到如下事件通知:

总结

在本文中,您学习了如何对Amazon IoT SiteWise 中的Measurements 点位进行实时监控,并设计事件模型进行报警触发,最后打通了企业微信接口进行消息通知。在实际大量工业客户使用场景里,客户一般会通过 Amazon IoT SiteWise 服务对工厂内的 PLC 或者各种机器设备进行连接,将产线上物理资产先映射到 Amazon IoT SiteWise 资产模型,这样机器的运行数据就会先到达 Amazon 云端。接下来,客户便可以用本文提到的方法建立虚拟安灯方案,将事件消息发送到企业微信端进行数据实时监控。

本篇作者

杨阳

亚马逊云科技专业服务团队物联网应用架构师。负责基于亚马逊云科技 IoT的解决方案咨询,架构与交付,深度参与过智慧工厂、智能家居等行业的IoT解决方案的咨询与交付。进入亚马逊云科技之前在消费电子产品和制造业拥有丰富的物联网项目开发和管理经验。

罗圣杰

亚马逊云科技专业服务团队大数据架构师。负责基于亚马逊云科技 的数据仓库和数据湖的解决方案咨询,架构与交付,擅长无服务计算,数据迁移,多云数据集成,数仓规划和数据服务的架构设计和落地。

高业坤

亚马逊云科技专业服务团队IoT架构师。负责基于亚马逊云科技 IoT 的解决方案咨询,架构与交付,深度参与过智慧工厂、智慧供热等行业的IoT 解决方案的咨询与交付。在智能家居、智慧安防、智能硬件等 IoT 行业拥有十年以上软件研发管理经验。