本文主要介绍在 Insights Hub 本地私有云(LPC)环境中使用 MindConnect MQTT。根据文章里的操作步骤,用户可以通过 MindConnect MQTT 上传 Time Series 数据、Event 数据,以及文件到 Insights Hub。参考官网:https://documentation.mindsphere.io/MindSphere/howto/howto-managing-ca-certificates.html 获取更多内容。
用户需要在 Insights Hub 注册自己生成的 CA 证书。
set TENANT=suppdev
set COUNTRY_CODE=CN
set CITY=Chengdu
set ORGANIZATION=CTS
set ORGANIZATIONAL_UNIT=CTS
openssl genrsa -out %TENANT%.key 2048
openssl req -x509 -new -nodes -key %TENANT%.key -sha256 -days 3650 -out %TENANT%.pem -subj "/C=%COUNTRY_CODE%/ST=%CITY%/O=%ORGANIZATION%/OU=%ORGANIZATIONAL_UNIT%/CN=%TENANT%"
填写过后,将命令复制到命令行工具里并执行
set TENANT=suppdev
set REGISTRATION_CODE=9041961a00224c0ea093077c3d6ac0f5
openssl genrsa -out verificationCert.key 2048
openssl req -new -key verificationCert.key -out verificationCert.csr -subj /CN=%REGISTRATION_CODE%
openssl x509 -req -in verificationCert.csr -CA %TENANT%.pem -CAkey %TENANT%.key -CAcreateserial -out verificationCert.pem -days 7 -sha256
至此,生成 Tenant CA 证书步骤结束。
下面已填好对应的信息:
set TENANT=suppdev
set DEVICE_NAME=device01
set COUNTRY_CODE=CN
set CITY=Chengdu
set ORGANIZATION=CTS
openssl genrsa -out %DEVICE_NAME%.key 2048
openssl req -new -key %DEVICE_NAME%.key -out %DEVICE_NAME%.csr -subj "/C=%COUNTRY_CODE%/ST=%CITY%/O=%ORGANIZATION%/OU=IT/CN=%DEVICE_NAME%"
openssl x509 -req -in %DEVICE_NAME%.csr -CA "%TENANT%.pem" -CAkey "%TENANT%.key" -CAcreateserial -out %DEVICE_NAME%.pem -days 365 -sha256
type %DEVICE_NAME%.pem "%TENANT%.pem" > "%DEVICE_NAME%"_chain.pem
执行上述命令后,可看到后续生成的文件:
Client 连接 MC MQTT Broker 时需要提供 JWT。此节将介绍生成 JWT 的步骤。
打开 JWT.IO,看到如下界面:
alg
。默认为:RS256
;x5c
。客户端证书与 Tenant CA 证书的组合。需要满足下面要求:
typ
。默认为:JWT
。{
"alg": "RS256",
"x5c": [
"MIIDNjCCAh4CFDvz+vdRGEfz3wGKa0+JdkOnWefHMA0GCSqGSIb3DQ...",
"MIIDezCCAmOgAwIBAgIUMoRljy56YvnC9bJQs9aFS3PVx8kwDQYJKoZIhvcNAQ..."
],
"typ": "JWT"
}
Note: Client ID 的格式为:{Tenant name}_{Device name}
,例如:suppdev_device01
jti
。唯一标识,格式为 UUID,最大长度 36 个字符。iss
。Token 签发信息。值为 "MQTT 的 Client ID"。sub
。Subject 信息。值为 "MQTT 的 Client ID"。aud
。Audience claim。值为 [ "MQTTBroker" ]
。iat
。Token 签发时间。时间戳格式,精度为“秒”。exp
。Token 过期时间。时间戳格式,精度为“秒”。注意:令牌有效期为”一小时“;因此,iat 和 exp 之间的差值必须最多为一小时。schemas
。令牌自定义语义声明。值为:[ "urn:siemens:mindsphere:v1" ]
。ten
。Tenant 名字。格式参考如下:
{
"jti": "d9f24ddd-447e-446c-aeaf-e4a976635e8c",
"iss": "suppdev_device01",
"sub": "suppdev_device01",
"aud": [
"MQTTBroker"
],
"iat": 1692609640,
"exp": 1692613240,
"schemas": [
"urn:siemens:mindsphere:v1"
],
"ten": "suppdev"
}
Note: 上述 iss、sub 两个参数涉及到的格式为:{Tenant name}_{Device name}
,即为 Client ID 的格式。
这里需要填入 Client 私钥(直接打开 {Client file name}.key
文件,复制文件内容粘贴过来即可):
将准备好的 HEADER、PAYLOAD、Client 私钥填入 JWT.IO 网站对应位置,然后左侧获取生成的 JWT:
打开 MQTTX 软件,新建一个连接:
新建连接后会进入配置界面。需要配置的内容如下:
{Tenant name}_{Device name}
。_CertificateBearer
。其余选项根据需要进行配置。配置完成过后点击右上角“连接”按钮:
点击连接过后会自动回到上个页面。成功连接后可看到如下提示:
模型用于构建 MC MQTT 的 Asset 结构。其中模型包含:Aspect Type、Asset Type、Asset 实例引用,以及变量映射。模型定义过后,后续根据在模型实例化期间具体传入的值,来初始化模型实例。
参考下例模型定义。其中:
<>
括起来的值需要替换为实际值。${}
括起来的值保持不变,这里作为一个变量供后续实例化时使用。externalId
需要特别注意,后续实例化模型时需要用到(需确保“值唯一”)。这里使用的是 "SpaceShip"。{
"id": "<requestId>",
"data": {
"externalId": "SpaceShip",
"typeModel": {
"aspectTypes": [
{
"id": "<tenantId>.wing",
"name": "${aspectTypeName}",
"category": "static",
"scope": "private",
"variables": [
{
"name": "temperature",
"dataType": "STRING",
"unit": "C/F",
"searchable": true,
"length": 5,
"qualityCode": true
}
],
"description": "wing aspect type description",
"referenceId": "287adc1a086840e0a6721dfd1170e97c"
}
],
"assetTypes": [
{
"id": "<tenantId>.spaceship",
"name": "wingAssetTypeName",
"parentTypeId": "core.basicasset",
"aspects": [
{
"name": "wingAspect",
"aspectTypeId": "<tenantId>.wing"
}
],
"description": "Hyperspace jump capable space ship",
"instantiable": true,
"scope": "private",
"referenceId": "82a4cc2a69cc42af80c1c6cf5dbefde5"
}
]
},
"instanceModel": {
"assets": [
{
"referenceId": "wingAssetReference",
"parentReferenceId": "root",
"typeId": "<tenantId>.spaceship",
"name": "wingAsset",
"description": "The ship of Han Solo and Chewbacca"
}
]
},
"mappingModel": {
"mappings": [
{
"dataPointId": "dp01",
"assetReferenceId": "wingAssetReference",
"aspectName": "wingAspect",
"variableName": "temperature",
"referenceId": "19e9048e78f540e7a9ba25e1249fea9b"
}
]
}
}
}
这里需要替换 <requestId>
(需确保“值唯一”)、<tenantId>
。修改过后的结构如下:
{
"id": "suppdev-request-20231205001",
"data": {
"externalId": "spaceShip-001",
"typeModel": {
"aspectTypes": [
{
"id": "suppdev.wing",
"name": "${aspectTypeName}",
"category": "static",
"scope": "private",
"variables": [
{
"name": "temperature",
"dataType": "STRING",
"unit": "C/F",
"searchable": true,
"length": 5,
"qualityCode": true
}
],
"description": "wing aspect type description",
"referenceId": "287adc1a086840e0a6721dfd1170e97c"
}
],
"assetTypes": [
{
"id": "suppdev.spaceship",
"name": "wingAssetTypeName",
"parentTypeId": "core.basicasset",
"aspects": [
{
"name": "wingAspect",
"aspectTypeId": "suppdev.wing"
}
],
"description": "Hyperspace jump capable space ship",
"instantiable": true,
"scope": "private",
"referenceId": "82a4cc2a69cc42af80c1c6cf5dbefde5"
}
]
},
"instanceModel": {
"assets": [
{
"referenceId": "wingAssetReference",
"parentReferenceId": "root",
"typeId": "suppdev.spaceship",
"name": "wingAsset",
"description": "The ship of Han Solo and Chewbacca"
}
]
},
"mappingModel": {
"mappings": [
{
"dataPointId": "dp01",
"assetReferenceId": "wingAssetReference",
"aspectName": "wingAspect",
"variableName": "temperature",
"referenceId": "19e9048e78f540e7a9ba25e1249fea9b"
}
]
}
}
}
向 MQTT Broker 发送消息创建模型。在创建模型前可以先订阅 tc/<tenantId>/<clientId>/i/amo_v3/ms
或者 tc/<tenantId>/<clientId>/i/amo_v3/#
,这样在创建过后可以收到创建模型时返回的消息,从而判断模型创建的状态。
tc/<tenantId>/<clientId>/i/amo_v3/ms
tc/<tenantId>/<clientId>/o/amo_v3/m
发送创建模型消息<tenantId>
和 <clientId>
要替换为实际值)至此,模型创建完成。
在实例化模型前可以先订阅“获取实例化状态”的 Topic - tc/<tenantId>/<clientId>/i/amo_v3/ip
或 tc/<tenantId>/<clientId>/i/amo_v3/#
,这样发送实例化消息后可以接收到返回的消息。
tc/<tenantId>/<clientId>/i/amo_v3/ip
tc/<tenantId>/<clientId>/o/amo_v3/i
发送消息实例化上一步创建的模型。externalId
值;${}
括起来的参数。如果之前定义了多个参数,这里需以数组结构填入。{
"id": "<requestId>",
"data": {
"modelExternalId": "SpaceShip",
"parameterization": {
"values": [
{
"name": "aspectTypeName",
"value": "wingAspectTypeName"
}
]
}
}
}
修改过后的消息体如下:
{
"id": "suppdev-request-20231205001",
"data": {
"modelExternalId": "spaceShip-001",
"parameterization": {
"values": [
{
"name": "aspectTypeName",
"value": "wingAspectTypeName"
}
]
}
}
}
将准备好的 "Topic 地址"和“初始化模型消息体”贴入 MQTTX,然后点击右下角发送图标。(注意:Topic 地址中的 <tenantId>
和 <clientId>
要替换为实际值)
发送成功后,可以看到上方返回的消息。实例化模型消息发送后可以收到多个通知,每个通知标识着不同的状态。
Asset Manager 中查看实例化过后的 Asset
当实例化完成后,回到 Asset Manager 中可查看到已经实例化过后的 Agent Asset 以及其信息:
至此,模型实例化完成。
本节将介绍通过 MC MQTT 发送 Time Series 数据、Event、File 到 Insights Hub。(案例基于官网教程,请参考)
模型定义消息体中的参数已在“第五章节”中介绍,下文模型定义的消息体将不再细述参数细节。
{
"id": "request-suppdev-20231208001",
"data": {
"externalId": "ElectricMechanicalDeviceModel",
"typeModel": {
"aspectTypes": [
{
"id": "suppdev.mechanicalAspectType",
"name": "mechanicalAspectType",
"category": "dynamic",
"scope": "private",
"variables": [
{
"name": "readTime",
"dataType": "TIMESTAMP",
"unit": "second",
"searchable": false,
"qualityCode": true
},
{
"name": "displacement",
"dataType": "INT",
"unit": "cm",
"searchable": true,
"qualityCode": true
},
{
"name": "rpm",
"dataType": "LONG",
"searchable": true,
"qualityCode": true
}
],
"description": "Mechanical properties of an device"
},
{
"id": "suppdev.electricalAspectType",
"name": "electricalAspectType",
"category": "dynamic",
"scope": "private",
"variables": [
{
"name": "overloaded",
"dataType": "BOOLEAN",
"searchable": true,
"qualityCode": true
},
{
"name": "current",
"dataType": "DOUBLE",
"unit": "A",
"searchable": true,
"qualityCode": true
},
{
"name": "errorMessage",
"dataType": "STRING",
"length": 255,
"searchable": true,
"qualityCode": true
}
],
"description": "Electrical properties of an device"
}
],
"assetTypes": [
{
"id": "suppdev.electricalAndMechanicalAssetType",
"name": "electricalAndMechanicalAssetType",
"parentTypeId": "core.basicasset",
"aspects": [
{
"name": "electricalAspect",
"aspectTypeId": "suppdev.electricalAspectType"
},
{
"name": "mechanicalAspect",
"aspectTypeId": "suppdev.mechanicalAspectType"
}
],
"description": "Asset Type with electrical and mechanical properties",
"instantiable": true,
"scope": "private"
}
]
},
"instanceModel": {
"assets": [
{
"referenceId": "electricalAndMechanicalAssetReference1",
"parentReferenceId": "root",
"typeId": "suppdev.electricalAndMechanicalAssetType",
"name": "electricalAndMechanicalAsset",
"description": "Asset with electrical and mechanical properties"
}
]
},
"mappingModel": {
"mappings": [
{
"dataPointId": "dataPoint1",
"assetReferenceId": "electricalAndMechanicalAssetReference1",
"aspectName": "electricalAspect",
"variableName": "current"
},
{
"dataPointId": "dataPoint2",
"assetReferenceId": "electricalAndMechanicalAssetReference1",
"aspectName": "mechanicalAspect",
"variableName": "displacement"
},
{
"dataPointId": "dataPoint3",
"assetReferenceId": "electricalAndMechanicalAssetReference1",
"aspectName": "mechanicalAspect",
"variableName": "rpm"
},
{
"dataPointId": "dataPoint4",
"assetReferenceId": "electricalAndMechanicalAssetReference1",
"aspectName": "electricalAspect",
"variableName": "errorMessage"
},
{
"dataPointId": "dataPoint5",
"assetReferenceId": "electricalAndMechanicalAssetReference1",
"aspectName": "electricalAspect",
"variableName": "overloaded"
},
{
"dataPointId": "dataPoint6",
"assetReferenceId": "electricalAndMechanicalAssetReference1",
"aspectName": "mechanicalAspect",
"variableName": "readTime"
}
]
}
}
}
消息体定义完成后,进入 MQTTX 向 Topic - tc/<tenantId>/<clientId>/o/amo_v3/m
发送创建模型消息。
实例化模型消息体参考如下代码:
{
"id": "request-suppdev-20231208002",
"data": {
"modelExternalId": "ElectricMechanicalDeviceModel",
"parameterization": {
"values": []
}
}
}
向 Topic - tc/<tenantId>/<clientId>/o/amo_v3/i
发送消息实例化模型。
初始化完成后,可以在 Asset Manager 中看到新生成的 Asset。
根据如下的 TS 数据,客户端每 10 秒会向前 3 个数据点发送数据(dataPoint1, dataPoint2, dataPoint3);每 20 秒向后三个数据点发送数据(dataPoint4, dataPoint5, dataPoint6)。
{
"timeseries": [
{
"timestamp": "2023-12-12T02:05:00.000Z",
"values": [
{
"dataPointId": "dataPoint1",
"value": 20.1,
"qualityCode": "0"
},
{
"dataPointId": "dataPoint2",
"value": 555,
"qualityCode": "0"
},
{
"dataPointId": "dataPoint3",
"value": 2147483648,
"qualityCode": "0"
},
{
"dataPointId": "dataPoint4",
"value": "error",
"qualityCode": "0"
},
{
"dataPointId": "dataPoint5",
"value": "true",
"qualityCode": "0"
},
{
"dataPointId": "dataPoint6",
"value": "2022-02-10T01:05:20.020Z",
"qualityCode": "0"
}
]
},
{
"timestamp": "2023-12-12T02:05:10.000Z",
"values": [
{
"dataPointId": "dataPoint1",
"value": 34.14,
"qualityCode": "0"
},
{
"dataPointId": "dataPoint2",
"value": 654,
"qualityCode": "0"
},
{
"dataPointId": "dataPoint3",
"value": 4294967296,
"qualityCode": "0"
}
]
},
{
"timestamp": "2023-12-12T02:05:20.000Z",
"values": [
{
"dataPointId": "dataPoint1",
"value": 80.6,
"qualityCode": "0"
},
{
"dataPointId": "dataPoint2",
"value": 565,
"qualityCode": "0"
},
{
"dataPointId": "dataPoint3",
"value": 8589934592,
"qualityCode": "0"
},
{
"dataPointId": "dataPoint4",
"value": "error",
"qualityCode": "0"
},
{
"dataPointId": "dataPoint5",
"value": "true",
"qualityCode": "0"
},
{
"dataPointId": "dataPoint6",
"value": "2022-02-10T01:10:20.020Z",
"qualityCode": "0"
}
]
},
{
"timestamp": "2023-12-12T02:05:30.000Z",
"values": [
{
"dataPointId": "dataPoint1",
"value": 43.13,
"qualityCode": "0"
},
{
"dataPointId": "dataPoint2",
"value": 456,
"qualityCode": "0"
},
{
"dataPointId": "dataPoint3",
"value": 8589934595,
"qualityCode": "0"
}
]
}
]
}
tc/<tenantId>/<clientId>/o/mc_v3/ts
首先通过调用 Event Management API (POST - /api/eventmanagement/v3/eventTypes
) 来创建自定义的 Event 类型。
参考如下格式:
{
"name": "FileUploadCompletedEvent",
"parentId": "core.connectivity.event.type.AgentBaseEvent",
"ttl": 35,
"scope": "LOCAL",
"fields": [
{
"name": "fileNumber",
"filterable": true,
"required": false,
"updatable": true,
"type": "INTEGER"
},
{
"name": "fileSize",
"filterable": true,
"required": false,
"updatable": true,
"type": "DOUBLE"
},
{
"name": "fileName",
"filterable": true,
"required": true,
"updatable": true,
"type": "STRING"
},
{
"name": "isValid",
"filterable": true,
"required": false,
"updatable": true,
"type": "BOOLEAN"
}
]
}
下面用 Postman 调用 API 来创建自定义 Event Type:
Event Type 创建完后,便可以通过 MC MQTT 向 Asset 发送 Event。
发送 Event 消息体如下:
{
"events": [
{
"id": "e6e31c6f-7963-4f62-92a9-46b1b8b0649",
"correlationId": "eventCorrelationId",
"timestamp": "2022-03-17T15:45:02.302Z",
"severity": 20,
"type": "FileUploadCompletedEvent",
"description": "File upload test event",
"details": {
"fileName": "test2",
"fileSize": 11.2,
"isValid": "true",
"fileNumber": 15
}
}
]
}
向 Topic - tc/<tenantId>/<clientId>/o/mc_v3/e
发送创建 Event 消息。
消息发送完成后,回到 Insights Hub Monitor 应用中查看创建的消息:
每个客户端可通过 MQTT 上传最大 75 KB 的文件。文件会被上传到对应的 Asset 中,例如:设备日志文件,或者其它较复杂的传感器结构定义之类的内容。上传后的文件可被对应 Asset 的父 Asset 引用。Insights Hub 不会解析这些文件的内容,用户可以自行获取这些文件并解析。
Note: 文件内容需要经过 Base64 编码后再被上传。
{
"file": {
"name": "samplefile",
"creationDate": "2022-03-17T15:46:02.302Z",
"content": "U2FtcGxlIEZpbGUgQ29udGVudA=="
}
}
tc/<tenantId>/<clientId>/o/mc_v3/f
发送消息至此,关于 MC MQTT 的介绍就结束了。更多的内容、细节描述可参考官方文档来获取更多的介绍(包括本文所述的内容。但需要注意的是:Tenant CA 证书生成步骤需要参考本文中的步骤来生成,因为官方文档给出的步骤是基于公有云环境,与本地私有云(LPC)环境步骤有所差异)。