本文主要介绍在 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}。
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 的私钥
将准备好的 HEADER、PAYLOAD、Client 私钥填入到 JWT.IO 网站对应位置,然后左侧获取生成的 JWT。
打开 MQTTX 软件,新建一个连接
新建连接后会进入配置界面。需要配置的内容如下:
其余选项根据需要进行配置。配置完成过后点击右上角“连接”按钮。
点击连接过后会自动回到上个页面。成功连接后可看到如下提示:
模型定义用于构建 MC MQTT 的 Asset 结构。其中模型包含 Aspect Type、Asset Type、Asset 实例引用,以及变量映射。模型定义过后,后期可以根据在实例化期间具体定义的值来进行初始化模型实例。
参考下例模型定义。其中:
<>
括起来的值需要替换为实际值。${}
括起来的值保持不变,这里作为一个变量供后续实例化时使用。{ "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
发送创建模型消息。至此,模型创建完成。
在实例化模型前可以先订阅“获取实例化状态”的 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
发送消息实例化上一步创建的模型{
"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)环境步骤有所差异)。