Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update device mapping templates for the sandbox setup to support IoT Central's latest data export schema #109

Merged
merged 3 commits into from
Jun 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion Microsoft.Health.Fhir.Ingest.sln
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Fhir.Inges
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Logging", "src\lib\Microsoft.Health.Logger\Microsoft.Health.Logging.csproj", "{05123BAE-E96E-4C7E-95CB-C616DF940F17}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Health.Events.UnitTest", "test\Microsoft.Health.Events.UnitTest\Microsoft.Health.Events.UnitTest.csproj", "{77E5164F-2DED-4297-A082-54FAFD1C4EC9}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Events.UnitTest", "test\Microsoft.Health.Events.UnitTest\Microsoft.Health.Events.UnitTest.csproj", "{77E5164F-2DED-4297-A082-54FAFD1C4EC9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sandbox", "sandbox", "{4793E9D2-6D10-4AB4-8D4C-04E0F0309AF5}"
ProjectSection(SolutionItems) = preProject
sample\templates\sandbox\devicecontent.json = sample\templates\sandbox\devicecontent.json
sample\templates\sandbox\fhirmapping.json = sample\templates\sandbox\fhirmapping.json
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "legacy", "legacy", "{657FB6EC-2F58-43B6-AD14-5EDE9D73C1E5}"
ProjectSection(SolutionItems) = preProject
sample\templates\sandbox\legacy\devicecontent.json = sample\templates\sandbox\legacy\devicecontent.json
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -208,6 +219,8 @@ Global
{927BC214-ABD9-4A1B-9F7C-75973513D141} = {1EF3584A-C437-4B45-8BF8-1597D5A8DBC7}
{05123BAE-E96E-4C7E-95CB-C616DF940F17} = {513D67B4-80E1-476D-955F-E7E7C79D144A}
{77E5164F-2DED-4297-A082-54FAFD1C4EC9} = {FAF8B402-892E-4EA2-B4CF-69B0C70BA762}
{4793E9D2-6D10-4AB4-8D4C-04E0F0309AF5} = {75D08B93-4CE1-4967-B0C3-DAA792F1D19A}
{657FB6EC-2F58-43B6-AD14-5EDE9D73C1E5} = {75D08B93-4CE1-4967-B0C3-DAA792F1D19A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A358924D-F948-4AE8-8CD0-A0F56225CE0C}
Expand Down
5 changes: 2 additions & 3 deletions docs/ARMInstallation.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,5 @@ After the ARM template is successfully deployed, [configure Azure RBAC for your

Also, the mapping configurations for device content and converting to FHIR need to be added to the template container in the deployed Azure Storage blob. You can use a tool like [Azure Storage Explorer](https://azure.microsoft.com/en-us/features/storage-explorer/) to easily upload and update the configurations. Navigate to the Azure Storage account deployed by the ARM template (it will be service name you selected) and select the template storage to container. From there upload the configurations and you are done.

Default templates:
[Device Content](../sample/templates/basic/devicecontent.json)
[FHIR Mapping](../sample/templates/basic/fhirmapping.json)
More information on mapping templates can be found [here](https://github.com/microsoft/iomt-fhir/blob/7794cbcc463e8d26c3097cd5e2243d770f26fe45/docs/Configuration.md).
Full examples can be found in the repository under [/sample/templates](https://github.com/microsoft/iomt-fhir/tree/7794cbcc463e8d26c3097cd5e2243d770f26fe45/sample/templates)
10 changes: 7 additions & 3 deletions docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This article details how to configure your instance of the IoMT FHIR Connector for Azure.

The IoMT FHIR Connector for Azure requires two JSON configuration files. The first, device content, is responsible for mapping the payloads sent to the Event Hub end point and extracting types, device identifiers, measurement date time, and the measurement value(s). The second template controls the FHIR mapping. The FHIR mapping allows configuration of the length of the observation period, FHIR data type used to store the values, and code(s). The two configuration files should be uploaded to the storage container "template" created under the blob storage account provisioned during the [ARM template deployment](ARMInstallation.md). The device content mapping file should be call `devicecontent.json` and the FHIR mapping file should be called `fhirmapping.json`. Full examples can be found in the repository under [/sample/templates](../sample/templates). Configuration files are loaded from blob per compute execution. Once updated they should take effect immediately.
The IoMT FHIR Connector for Azure requires two JSON configuration files. The first, device content, is responsible for mapping the payloads sent to the Event Hub end point and extracting types, device identifiers, measurement date time, and the measurement value(s). The second template controls the FHIR mapping. The FHIR mapping allows configuration of the length of the observation period, FHIR data type used to store the values, and code(s). The two configuration files should be uploaded to the storage container "template" created under the blob storage account provisioned during the [ARM template deployment](ARMInstallation.md). The device content mapping file **MUST** be named `devicecontent.json` and the FHIR mapping file **MUST** be named `fhirmapping.json`. Full examples can be found in the repository under [/sample/templates](../sample/templates). Configuration files are loaded from blob per compute execution. Once updated they should take effect immediately.

# Device Content Mapping

Expand Down Expand Up @@ -265,7 +265,7 @@ The JsonPathContentTemplate allows matching on and extracting values from an Eve

The IotJsonPathContentTemplate is similar to the JsonPathContentTemplate except the DeviceIdExpression and TimestampExpression are not required.

The assumption when using this template is the messages being evaluated were sent using the [Azure IoT Hub Device SDKs](https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-sdks#azure-iot-hub-device-sdks). When using these SDKs the device identity (assuming the device id from Iot Hub/Central is registered as an identifer for a device resource on the destination FHIR server) is known as well as the timestamp of the message. If you are using Azure IoT Hub Device SDKs but are using custom properties in the message body for the device identity or measurement timestamp you can still use the JsonPathContentTemplate.
The assumption when using this template is the messages being evaluated were sent using the [Azure IoT Hub Device SDKs](https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-sdks#azure-iot-hub-device-sdks) or [Export Data (legacy)](https://docs.microsoft.com/en-us/azure/iot-central/core/howto-export-data-legacy) feature of [Azure IoT Central](https://docs.microsoft.com/en-us/azure/iot-central/core/howto-export-data). When using these SDKs the device identity (assuming the device id from Iot Hub/Central is registered as an identifer for a device resource on the destination FHIR server) is known as well as the timestamp of the message. If you are using Azure IoT Hub Device SDKs but are using custom properties in the message body for the device identity or measurement timestamp you can still use the JsonPathContentTemplate.

*Note: When using the IotJsonPathContentTemplate the TypeMatchExpression should resolve to the entire message as a JToken. Please see the examples below.*

Expand Down Expand Up @@ -347,11 +347,13 @@ The assumption when using this template is the messages being evaluated were sen
}
```

Full example template can be found [here](https://github.com/microsoft/iomt-fhir/tree/7794cbcc463e8d26c3097cd5e2243d770f26fe45/sample/templates/legacy).

### **IotCentralJsonPathContentTemplate**

The IotCentralJsonPathContentTemplate is similar to the JsonPathContentTemplate except the DeviceIdExpression and TimestampExpression are not required.

The assumption when using this template is the messages being evaluated were sent using the `Export Data` feature of [Azure IoT Central](https://docs.microsoft.com/en-us/azure/iot-central/core/howto-export-data). When using this feature the device identity (assuming the device id from Iot Central is registered as an identifer for a device resource on the destination FHIR server) is known as well as the timestamp of the message. If you are using this export feature but are using custom properties in the message body for the device identity or measurement timestamp you can still use the JsonPathContentTemplate.
The assumption when using this template is the messages being evaluated were sent using the [Export Data](https://docs.microsoft.com/en-us/azure/iot-central/core/howto-export-data) feature of [Azure IoT Central](https://docs.microsoft.com/en-us/azure/iot-central/core/howto-export-data). When using this feature the device identity (assuming the device id from Iot Central is registered as an identifer for a device resource on the destination FHIR server) is known as well as the timestamp of the message. If you are using this export feature but are using custom properties in the message body for the device identity or measurement timestamp you can still use the JsonPathContentTemplate.

*Note: When using the IotCentralJsonPathContentTemplate the TypeMatchExpression should resolve to the entire message as a JToken. Please see the examples below.*

Expand Down Expand Up @@ -459,6 +461,8 @@ The assumption when using this template is the messages being evaluated were sen
}
}
```
Full example template can be found [here](https://github.com/microsoft/iomt-fhir/tree/7794cbcc463e8d26c3097cd5e2243d770f26fe45/sample/templates/sandbox).

---

# FHIR Mapping
Expand Down
18 changes: 12 additions & 6 deletions docs/Sandbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,22 @@ Then deploy the scenario with the Open Source IoMT FHIR Connector for Azure:

## Post Deployment

**NOTE** The device conversion mapping template provided in this guide is designed to work with Data export (legacy) within IoT Central.
**NOTE** The device conversion mapping template provided in this guide is designed to work with the [Export Data](https://docs.microsoft.com/en-us/azure/iot-central/core/howto-export-data) feature of [Azure IoT Central](https://docs.microsoft.com/en-us/azure/iot-central/core/howto-export-data).

After successful deployment, your IoT Central application must be connected to the IoMT FHIR Connector for Azure. To do so:

1. Navigate to your IoT Central app at \<ENVIRONMENTNAME\>.azureiotcentral.com
2. On the left panel, navigate to "Data export (legacy)"
3. Click New > Azure Event Hubs
4. Under "Event Hubs namespace" choose your environment name.
5. Under "Event hub" choose "devicedata"
6. We only need to export "Telemetry", so you can turn off "Devices" and "Device Templates".
2. On the left panel, navigate to "Data export".
3. Setup the destination to which the data has to be exported to:
* Under the Destinations tab, click "Add a destination" or "+ New Destination".
* Enter a name for this destination.
* Select "Azure Event Hubs" as the Destination type.
* [Get the connection string to the Event Hubs Namespace](https://docs.microsoft.com/en-us/azure/event-hubs/event-hubs-get-connection-string) resource created in your environment and enter it in the Connection string field.
* Enter "devicedata" for the Event Hub field.
* Click Save.
4. Under the Exports tab, click "Add an export" or "+ New export".
5. Select "Telemetry" for type of data to export.
6. Select the name of the destination created in step 3.
7. Click Save.

## Verification
Expand Down
60 changes: 30 additions & 30 deletions sample/templates/sandbox/devicecontent.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,114 +2,114 @@
"templateType": "CollectionContent",
"template": [
{
"templateType": "IotJsonPathContent",
"templateType": "IotCentralJsonPathContent",
"template": {
"typeName": "heartrate",
"typeMatchExpression": "$..[?(@Body.HeartRate)]",
"patientIdExpression": "$.SystemProperties.iothub-connection-device-id",
"typeMatchExpression": "$..[?(@telemetry.HeartRate)]",
"patientIdExpression": "$.deviceId",
"values": [
{
"required": "true",
"valueExpression": "$.Body.HeartRate",
"valueExpression": "$.telemetry.HeartRate",
"valueName": "hr"
}
]
}
},
{
"templateType": "IotJsonPathContent",
"templateType": "IotCentralJsonPathContent",
"template": {
"typeName": "respiratoryrate",
"typeMatchExpression": "$..[?(@Body.RespiratoryRate)]",
"patientIdExpression": "$.SystemProperties.iothub-connection-device-id",
"typeMatchExpression": "$..[?(@telemetry.RespiratoryRate)]",
"patientIdExpression": "$.deviceId",
"values": [
{
"required": "true",
"valueExpression": "$.Body.RespiratoryRate",
"valueExpression": "$.telemetry.RespiratoryRate",
"valueName": "respiratoryrate"
}
]
}
},
{
"templateType": "IotJsonPathContent",
"templateType": "IotCentralJsonPathContent",
"template": {
"typeName": "hrv",
"typeMatchExpression": "$..[?(@Body.HeartRateVariability)]",
"patientIdExpression": "$.SystemProperties.iothub-connection-device-id",
"typeMatchExpression": "$..[?(@telemetry.HeartRateVariability)]",
"patientIdExpression": "$.deviceId",
"values": [
{
"required": "true",
"valueExpression": "$.Body.HeartRateVariability",
"valueExpression": "$.telemetry.HeartRateVariability",
"valueName": "hrv"
}
]
}
},
{
"templateType": "IotJsonPathContent",
"templateType": "IotCentralJsonPathContent",
"template": {
"typeName": "bodytemperature",
"typeMatchExpression": "$..[?(@Body.BodyTemperature)]",
"patientIdExpression": "$.SystemProperties.iothub-connection-device-id",
"typeMatchExpression": "$..[?(@telemetry.BodyTemperature)]",
"patientIdExpression": "$.deviceId",
"values": [
{
"required": "true",
"valueExpression": "$.Body.BodyTemperature",
"valueExpression": "$.telemetry.BodyTemperature",
"valueName": "temp"
}
]
}
},
{
"templateType": "IotJsonPathContent",
"templateType": "IotCentralJsonPathContent",
"template": {
"typeName": "bp",
"typeMatchExpression": "$..[?(@Body.Systolic && @Body.Diastolic)]",
"patientIdExpression": "$.SystemProperties.iothub-connection-device-id",
"typeMatchExpression": "$..[?(@telemetry.BloodPressure.Systolic && @telemetry.BloodPressure.Diastolic)]",
"patientIdExpression": "$.deviceId",
"values": [
{
"required": "true",
"valueExpression": "$.Body.Systolic",
"valueExpression": "$.telemetry.BloodPressure.Systolic",
"valueName": "systolic"
},
{
"required": "true",
"valueExpression": "$.Body.Diastolic",
"valueExpression": "$.telemetry.BloodPressure.Diastolic",
"valueName": "diastolic"
}
]
}
},
{
"templateType": "IotJsonPathContent",
"templateType": "IotCentralJsonPathContent",
"template": {
"typeName": "rangeofmotion",
"typeMatchExpression": "$..[?(@Body.RangeOfMotion)]",
"patientIdExpression": "$.SystemProperties.iothub-connection-device-id",
"typeMatchExpression": "$..[?(@telemetry.RangeOfMotion)]",
"patientIdExpression": "$.deviceId",
"values": [
{
"required": "true",
"valueExpression": "$.Body.RangeOfMotion",
"valueExpression": "$.telemetry.RangeOfMotion",
"valueName": "rangeofmotion"
}
]
}
},
{
"templateType": "IotJsonPathContent",
"templateType": "IotCentralJsonPathContent",
"template": {
"typeName": "kneebend",
"typeMatchExpression": "$..[?(@Body.KneeBend)]",
"patientIdExpression": "$.SystemProperties.iothub-connection-device-id",
"typeMatchExpression": "$..[?(@telemetry.KneeBend)]",
"patientIdExpression": "$.deviceId",
"values": [
{
"required": "true",
"valueExpression": "$.Body.KneeBend",
"valueExpression": "$.telemetry.KneeBend",
"valueName": "kneebend"
}
]
}
}
]
}
}
115 changes: 115 additions & 0 deletions sample/templates/sandbox/legacy/devicecontent.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
{
"templateType": "CollectionContent",
"template": [
{
"templateType": "IotJsonPathContent",
"template": {
"typeName": "heartrate",
"typeMatchExpression": "$..[?(@Body.HeartRate)]",
"patientIdExpression": "$.SystemProperties.iothub-connection-device-id",
"values": [
{
"required": "true",
"valueExpression": "$.Body.HeartRate",
"valueName": "hr"
}
]
}
},
{
"templateType": "IotJsonPathContent",
"template": {
"typeName": "respiratoryrate",
"typeMatchExpression": "$..[?(@Body.RespiratoryRate)]",
"patientIdExpression": "$.SystemProperties.iothub-connection-device-id",
"values": [
{
"required": "true",
"valueExpression": "$.Body.RespiratoryRate",
"valueName": "respiratoryrate"
}
]
}
},
{
"templateType": "IotJsonPathContent",
"template": {
"typeName": "hrv",
"typeMatchExpression": "$..[?(@Body.HeartRateVariability)]",
"patientIdExpression": "$.SystemProperties.iothub-connection-device-id",
"values": [
{
"required": "true",
"valueExpression": "$.Body.HeartRateVariability",
"valueName": "hrv"
}
]
}
},
{
"templateType": "IotJsonPathContent",
"template": {
"typeName": "bodytemperature",
"typeMatchExpression": "$..[?(@Body.BodyTemperature)]",
"patientIdExpression": "$.SystemProperties.iothub-connection-device-id",
"values": [
{
"required": "true",
"valueExpression": "$.Body.BodyTemperature",
"valueName": "temp"
}
]
}
},
{
"templateType": "IotJsonPathContent",
"template": {
"typeName": "bp",
"typeMatchExpression": "$..[?(@Body.Systolic && @Body.Diastolic)]",
"patientIdExpression": "$.SystemProperties.iothub-connection-device-id",
"values": [
{
"required": "true",
"valueExpression": "$.Body.Systolic",
"valueName": "systolic"
},
{
"required": "true",
"valueExpression": "$.Body.Diastolic",
"valueName": "diastolic"
}
]
}
},
{
"templateType": "IotJsonPathContent",
"template": {
"typeName": "rangeofmotion",
"typeMatchExpression": "$..[?(@Body.RangeOfMotion)]",
"patientIdExpression": "$.SystemProperties.iothub-connection-device-id",
"values": [
{
"required": "true",
"valueExpression": "$.Body.RangeOfMotion",
"valueName": "rangeofmotion"
}
]
}
},
{
"templateType": "IotJsonPathContent",
"template": {
"typeName": "kneebend",
"typeMatchExpression": "$..[?(@Body.KneeBend)]",
"patientIdExpression": "$.SystemProperties.iothub-connection-device-id",
"values": [
{
"required": "true",
"valueExpression": "$.Body.KneeBend",
"valueName": "kneebend"
}
]
}
}
]
}
Loading