Cenário: Normalizando Unidades de Medida usando um Adaptador Digital Twin
Este cenário explica como usar um modelo de gêmeo digital, um adaptador de gêmeo digital com mapeamentos de envelope e rota para normalizar a telemetria específica em um esquema comum e como validar o fluxo publicando payloads de dispositivos de amostra.
Esse cenário demonstra como postar dados de telemetria automotiva com diferentes unidades de medida de milhas por hora e quilômetros por hora usando diferentes pontos finais, duas instâncias de gêmeos digitais e duas chaves externas diferentes. Para obter mais informações sobre os padrões JQ referenciados nesse cenário, consulte Referência: Expressões JQ para Adaptadores Gêmeos Digitais
Um adaptador de gêmeo digital roteia solicitações por ponto final e payloads de mapas para um único modelo. Por exemplo, o modelo de métrica envia velocidade em quilômetros por hora (velocity_kph), enquanto outra instância digital twin padrão envia milhas por hora (speed).
Tarefas
Antes de começar
Verifique se você tem as permissões necessárias e se a CLI do OCI está configurada. Para obter mais informações, consulte Pré-requisitos, Detalhes da Política da Plataforma Internet of Things (IoT) e Usando um Arquivo JSON para Entrada Complexa.
Entenda os arquivos neste cenário:
Trechos de código que você pode usar em seu modelo de gêmeo digital referenciado nas etapas abaixo:
model.json— Modelo de gêmeo digital baseado em especificações DTDL v3 com uma propriedade de telemetriaspeedem milhas por hora que usa uma extensão de validação que aplica limites na faixa de valores de 0 a 100.envelope.json— Configuração de envelope que declara um ponto final de referência e uma forma de payload de exemplo.routes.json— Condições de rota e mapeamentos de payload que convertem quilômetros por hora em milhas por hora.script.sh— Neste exemplo, você pode salvar todos os comandos da CLI do OCI listados abaixo para criar um modelo de gêmeo digital, adaptador e instâncias, além dos comandoscurlpara POSTar a telemetria de amostra e, em seguida, executar como um script shellscript.sh.Para concluir as etapas neste cenário, você pode criar e salvar a CLI do OCI e os comandos
curllistados nas etapas abaixo e executar esse cenário como um script de shell:script.sh
Este exemplo de trecho de código model.json do modelo de gêmeo digital usa uma extensão personalizada dtmi:com:oracle:dtdl:extension:validation;1 que aplica regras de validação ao esquema JSON para os elementos "Telemetry", "Historized", "Validated", "Velocity". Se os dados não corresponderem aos valores esperados definidos nesta validação, os dados serão rejeitados.
Para obter uma lista completa das regras de propriedade de validação suportadas, consulte Referência de Extensão de Validação DTMI.
model.json
{
"@context":[
"dtmi:dtdl:context;3",
"dtmi:dtdl:extension:historization;1",
"dtmi:com:oracle:dtdl:extension:validation;1",
"dtmi:dtdl:extension:quantitativeTypes;1"
],
"@id":"dtmi:com:oracle:iot:poc:testmodel;1",
"@type":"Interface",
"contents":[
{
"@type":[ "Telemetry", "Historized", "Validated", "Velocity" ],
"displayName":"Speed",
"name":"speed",
"schema":"integer",
"unit":"milePerHour",
"minimum":0,
"maximum":100
}
]
}envelope.json
{
"referenceEndpoint": "telemetry/automotive/usa-standard-units",
"referencePayload": {
"dataFormat": "JSON",
"data": {
"speed": 65
}
}
}
routes.json listado abaixo contém 3 expressões que transformam os quilômetros e normalizam a carga útil de dados em uma unidade de medida, milhas por hora:- Uma expressão de condição que avalia os dados do ponto final:
"condition" : "${endpoint(3) == \"metric-units\"}"A sintaxe
${ ... }indica uma expressão que avalia o valor do terceiro parâmetro de ponto final ou elemento de caminho em uma chamada de APIendpoint(3). A condição compara o valor retornado commetric-units. Se for verdade, então aplica essas regras. - Expressão de mapeamento de carga útil:
"payloadMapping" : { "$.speed": "${(.velocity_kph / 1.609) | floor}"}A sintaxe
${ ... }indica uma expressão, essa expressão avalia e executa um cálculo aritmético para converter a velocidade ou a velocidade de quilômetros por hora em milhas por hora(.velocity_kph / 1.609); essa expressão divide o campovelocity_kphpor1.609e, em seguida, aplica a função floor, arredondando para baixo para o inteiro mais próximo. Este valor vem da conversão de quilômetros em milhas, que é quilômetros = milhas × 1.60934, "payloadMapping": {"$.speed": "$.speed"}Esta é uma expressão de mapeamento de valor direto, que passa pelo valor da velocidade como está.
[
{
"description" : "Automotive data using metric units (kilometers) that's converted to miles, with a different external key in the digital twin instance",
"condition" : "${endpoint(3) == \"metric-units\"}",
"payloadMapping" : {
"$.speed": "${(.velocity_kph / 1.609) | floor}"
},
"referencePayload" : {
"dataFormat" : "JSON",
"data" : { "velocity_kph": 104 }
}
},
{
"description" : "Auto 1 and Auto 2 use USA standard units, shows speed as is.",
"condition" : "*",
"payloadMapping" : { "$.speed": "$.speed" }
}
]
Etapa 1: Criar um Modelo Digital Twin
Use este comando da CLI oci iot digital-twin-model create para criar um modelo gêmeo digital usando o arquivo model.json. Este modelo de gêmeo digital padroniza speed em milhas por hora.
Este comando registra o modelo de gêmeo digital com este URI DTMI dtmi:com:oracle:iot:poc:testmodel;1 conforme definido na menção model.json acima.
oci iot digital-twin-model create \
--iot-domain-id <iot-domain-ocid> \
--display-name "Test Digital Twin Model" \
--description "Model for testing automotive telemetry routing and unit normalization" \
--spec file://~/model.json
Etapa 2: Criar um Adaptador Digital Twin com um Envelope e Rotas
Crie um adaptador que faça referência à especificação de modelo de gêmeo digital DTMI e que use o envelope de entrada e as rotas para normalizar a telemetria de entrada.
oci iot digital-twin-adapter create \
--iot-domain-id <iot-domain-ocid> \
--display-name "automotive-speed-adapter" \
--description "Routes by units" \
--digital-twin-model-spec-uri "dtmi:com:oracle:iot:poc:testmodel;1" \
--inbound-envelope file://~/envelope.json \
--inbound-routes file://~/routes.json
O referenceEndpoint no envelope.json é telemetry/automotive/usa-standard-units. O arquivo routes.json:
- Encaminha solicitações para o terceiro segmento de caminho que é igual a
metric-units, por exemplo,/telemetry/automotive/metric-units, e depois convertevelocity_kphemspeedem mph, e dispara o resultado. - Usa uma condição catch-all padrão (
*) para passar porspeedinalterado para automóveis que usam milhas por hora.
Etapa 3: Criar Duas Instâncias Gêmeas Digitais
- Ponto final para mph:
https://device-host/telemetry/automotive/usa-standard-units - Ponto final para kph:
https://device-host/telemetry/automotive/metric-units
Ao criar uma instância de gêmeo digital com autenticação, você pode usar um segredo de vault ou um certificado mTLS para autenticação. Para segurança, é uma prática recomendada criar um segredo de vault ou certificado mTLS exclusivo para cada instância de gêmeo digital. Todos os recursos devem estar na mesma região e tenancy que qualquer outro recurso relacionado do IoT.
Se você usar um certificado mTLS para autenticação, use o nome comum do certificado como a chave externa: --external-key <common-name-from-certificate-details>. Consulte Cenário: Criar uma Instância Digital Twin que use um Certificado mTLS.
Um administrador deve adicionar a política para criar segredos ou certificados. Consulte a Etapa 3 em Pré-requisitos.
Instância de gêmeo digital para unidades padrão dos EUA, milhas por hora (mph), observe a chave externa:
american-auto-standard-unitsoci iot digital-twin-instance create \
--iot-domain-id <IoT-domain-ocid> \
--display-name "auto using miles per hour" \
--external-key american-auto-standard-units \
--digital-twin-adapter-id <same-digital-twin-adapter-ocid> \
--auth-id <secret-ocid-or-certificate-ocid>Instância de gêmeo digital para unidades métricas europeias, quilômetros por hora (kph), observe a chave externa: european-auto-metric-units
oci iot digital-twin-instance create \
--iot-domain-id <IoT-domain-ocid> \
--display-name "auto using kilometers per hour" \
--external-key european-auto-metric-units \
--digital-twin-adapter-id <same-digital-twin-adapter-ocid> \
--auth-id <secret-ocid-or-certificate-ocid>
Etapa 4: Enviar telemetria de amostra para validar o roteamento e o mapeamento
Para enviar a telemetria, você precisa da chave externa da resposta da instância do gêmeo digital da Etapa 3, da senha do dispositivo e do ponto final do host do dispositivo.
Se a instância de gêmeo digital usar o segredo do vault para autenticação, você deverá usar como o valor de segredo codificado de base 64 como a senha do dispositivo.
- Chave externa: Substitua a
external-keypelaexternal-keyda instância de gêmeo digital com a qual você deseja trabalhar. Para evitar problemas de cotação, é uma prática recomendada não usar cotações no valor da chave externa. - Senha do dispositivo: Substitua a senha do dispositivo pelo conteúdo de segredo de texto simples ou pelo certificado mTLS.
- Host do dispositivo: Substitua
device-hostpelo host do dispositivo do domínio IoT. Para obter o URL do ponto final do host do dispositivo para o domínio IoT, consulte Obtendo Detalhes de um Domínio IoT.
-u "external-key:device-password-secret-contents-or-certificate"
Dependendo do seu sistema operacional ou do seu aplicativo, alguns aplicativos ou editores de código podem adicionar aspas indesejadas aos seus valores, isso pode causar um erro. Consulte Diagnosticando e Solucionando Problemas para obter exemplos de cotação.
curl -i -X POST \
-u "european-auto-metric-units:device-password-secret-or-certificate" \
-H "Content-Type: application/json" \
"https://device-host/telemetry/automotive/metric-units" \
-d '{ "velocity_kph": 0 }'curl -i -X POST \
-u "european-auto-metric-units:device-password-secret-or-certificate" \
-H "Content-Type: application/json" \
"https://device-host/telemetry/automotive/metric-units" \
-d '{ "velocity_kph": 110 }'
curl -i -X POST \
-u "american-auto-standard-units:device-password-secret-or-certificate" \
-H "Content-Type: application/json" \
"https://device-host/telemetry/automotive/usa-standard-units" \
-d '{ "speed": 0 }'
curl -i -X POST \
-u "american-auto-standard-units:device-password-vault-secret-base-64-or-certificate-OCID" \
-H "Content-Type: application/json" \
"https://device-host/telemetry/automotive/usa-standard-units" \
-d '{ "speed": 60 }'
Etapa 5: Verificar o comportamento da normalização
A condição de roteamento ${endpoint(3) == "metric-units"} avalia os dados e aplica o seguinte mapeamento de payload ao ponto final de dados das unidades de métrica:
"$.speed": "${(.velocity_kph / 1.609) | floor}"
Resultado esperado:
- O mapeamento do adaptador converte quilômetros por hora (kph) em milhas por hora (mph) e, em seguida, aplica o andar para atender a um esquema inteiro:
speed_mph = floor(velocity_kph / 1.609) - Neste exemplo,
velocity_kph = 0: speed_mph = floor(0 / 1.609) = floor(0) = 0 mphDepois do andar indica o passo de arredondamento que força o resultado a um número inteiro, arredondando para baixo em direção ao infinito negativo. Isso é necessário quando seu modelo DTDL declara a telemetria de velocidade como
schema: "integer"para que o valor seja um inteiro, não um flutuante ou uma string. velocity_kph = 110→speed = floor(110 / 1.609) = 68mph- Postagens de dados padrão com
speedpassam inalteradas, por exemplo, os valores deste exemplo:0,60
Se a validação do modelo estiver ativada minimum: 0, maximum: 100, os valores fora do intervalo serão rejeitados de acordo com as regras de validação.
Usa conversão de tipo flexível:
- Pass-through (mph, schema "integer"):
{"speed": "60"}e{"speed": 60.0}são armazenados como60.{"speed": "60.2"}é rejeitado, a menos que o mapeamento gere para um inteiro (por exemplo, comfloor). - Rota de métrica (kph → mph):
{"velocity_kph": 110}→68;{"velocity_kph": "110"}→68porque o mapeamentoflooremite um inteiro. Mantenha entradas aritméticas numéricas para evitar erros de expressão; sempre que possível, prefira110em vez de"110". - Arredondamento permanece explícito: A conversão suave não arredonda automaticamente
68.35para68. Usefloorpara esquema de número inteiro ou alterne o modelo paraschema: "double"para preservar frações.
Práticas Recomendadas
- Arquivos JSON de referência para adaptadores e especificações de modelo de gêmeo digital: Quando você faz upload de um adaptador usando a CLI, pode usar arquivos JSON para especificar o mapeamento de dados. Nos comandos da CLI, você pode fazer referência a arquivos como
file://~/name.jsonou fornecer um caminho absoluto ou relativo, dependendo do seu ambiente de shell. Dependendo do seu sistema operacional, você pode ter uma sintaxe ligeiramente diferente com aspas, barras ou localização do arquivo por padrão. Consulte Gerenciando Entrada e Saída da CLI e Usando um Arquivo JSON para Entrada Complexa. - Os arquivos de configuração JSON (envelope, rotas) usam nomes de campo de API em
camelCase(por exemplo,referenceEndpoint). A CLI do OCI passa esses arquivos por meio de argumentosfile://inalterados; portanto, o uso de JSON camelCase com CLI é esperado e correto. - O
referenceEndpointemenvelope.jsondeve refletir um ponto final típico do seu adaptador. - A condição de rota curinga (
*) é avaliada após condições específicas; ordene suas definições de rota de acordo. - Escopo de conversão suave: Strings numéricas e duplas de números inteiros são aceitas quando correspondem ao tipo de modelo (por exemplo, inteiro). Os auxiliares de conversão como
number()etoIntegerpermanecem sem suporte em expressões de rota; confie em aritmética efloorou adoteschema: "double"para preservar frações.
Variação: Usando schema="double" em vez de floor
Esta variação mostra como a definição do esquema de propriedade do modelo como double afeta o mapeamento do adaptador e os valores registrados. Com double, o validador aceita qualquer valor numérico integral ou fracionário que atenda às restrições de intervalo, sem arredondamento automático. Você pode optar por preservar a precisão fracionária (bruta) ou coagir a números inteiros usando floor. Ambos passam na validação desde que os valores permaneçam dentro do intervalo definido.
O que a validação schema=double faz
- Aceitação de tipo: Aceita números JSON com ou sem partes fracionais, por exemplo: 60, 68.35. Strings como
"68"permanecem inválidas. - Intervalo: Mínimo e máximo, por exemplo, neste exemplo,
0–100são impostos. - Sem arredondamento automático: A plataforma IoT não arredonda valores; você controla o arredondamento no mapeamento do adaptador de gêmeo digital ou downstream usando APEX ou SQL, dependendo dos sistemas configurados para exibir seus dados.
Arquivos usados nesta variação:
model_double.json— Modelo DTDL comschema: "double".{ "@context": [ "dtmi:dtdl:context;3", "dtmi:dtdl:extension:historization;1", "dtmi:com:oracle:dtdl:extension:validation;1", "dtmi:dtdl:extension:quantitativeTypes;1" ], "@id": "dtmi:com:oracle:iot:poc:testmodeldouble;1", "@type": "Interface", "contents": [ { "@type": [ "Telemetry", "Historized", "Validated", "Velocity" ], "displayName": "Speed", "name": "speed", "schema": "double", "unit": "milePerHour", "minimum": 0, "maximum": 100 } ] }routes_double_raw.json— O mapeamento preserva a precisão fracionária:"$.speed": "${.velocity_kph / 1.609}".[ { "description": "Double model: European metric units to miles per hour (mph); preserving fractional precision (no floor).", "condition": "${endpoint(3) == \"metric-units\"}", "payloadMapping": { "$.speed": "${.velocity_kph / 1.609}" }, "referencePayload": { "dataFormat": "JSON", "data": { "velocity_kph": 110 } } }, { "description": "Double model: USA standard units passthrough.", "condition": "*", "payloadMapping": { "$.speed": "$.speed" } } ]routes_double_floor.json— O mapeamento se coerce para o número inteiro mph:"$.speed": "${(.velocity_kph / 1.609) | floor}"armazenado como um duplo.[ { "description": "Double model: European metric units to miles per hour (mph); floor to whole number (stored as double).", "condition": "${endpoint(3) == \"metric-units\"}", "payloadMapping": { "$.speed": "${(.velocity_kph / 1.609) | floor}" }, "referencePayload": { "dataFormat": "JSON", "data": { "velocity_kph": 110 } } }, { "description": "Double model: USA standard units passthrough.", "condition": "*", "payloadMapping": { "$.speed": "$.speed" } } ]
Etapa A: Criar o modelo de gêmeo digital usando o double
oci iot digital-twin-model create \
--iot-domain-id iot-domain-ocid \
--display-name "TestModelSpeedDouble" \
--spec file://model_double.json
Etapa B: Criar dois adaptadores associados ao modelo duplo
Use valores brutos para preservar a precisão fracionária:
oci iot digital-twin-adapter create \
--iot-domain-id iot-domain-ocid \
--display-name "auto-adapter-double-raw" \
--digital-twin-model-id double-model-ocid \
--inbound-envelope file://envelope.json \
--inbound-routes file://routes_double_raw.json
Floor usa um número inteiro como o mph, que é um dobro:
oci iot digital-twin-adapter create \
--iot-domain-id iot-domain-ocid \
--display-name "auto-adapter-double-floor" \
--digital-twin-model-id double-model-ocid \
--inbound-envelope file://envelope.json \
--inbound-routes file://routes_double_floor.json
Etapa C: Criar instâncias gêmeas digitais para cada adaptador
oci iot digital-twin-instance create \
--iot-domain-id iot-domain-ocid \
--display-name "american-auto-raw" \
--external-key american-auto-raw \
--digital-twin-adapter-id adapter-double-raw-ocid \
--auth-id vault-secret-ocid
oci iot digital-twin-instance create \
--iot-domain-id iot-domain-ocid \
--display-name "european-auto-raw" \
--external-key european-auto-raw \
--digital-twin-adapter-id adapter-double-raw-ocid \
--auth-id vault-secret-ocid
oci iot digital-twin-instance create \
--iot-domain-id iot-domain-ocid \
--display-name "american-auto-dfloor" \
--external-key american-auto-dfloor \
--digital-twin-adapter-id adapter-double-floor-ocid \
--auth-id vault-secret-ocid
oci iot digital-twin-instance create \
--iot-domain-id iot-domain-ocid \
--display-name "european-auto-dfloor" \
--external-key european-auto-dfloor \
--digital-twin-adapter-id adapter-double-floor-ocid \
--auth-id vault-secret-ocid
Etapa D: Poste a telemetria da amostra e compare os resultados
-u "external-key:device-password" \- Se o gêmeo digital usar o segredo do vault para autenticação, use o
base64-secretcomo a senha do dispositivo. - Se a instância de gêmeo digital usar uma certificação mLTS, use
certificate-ocidcomo a senha do dispositivo.
Valores brutos (double, no floor):
curl -i -X POST \
-u "american-auto-raw:device-password" \
-H "Content-Type: application/json" \
"https://device-host/telemetry/automotive/usa-standard-units" \
-d '{ "speed": 60 }'
curl -i -X POST \
-u "european-auto-raw:device-password" \
-H "Content-Type: application/json" \
"https://device-host/telemetry/automotive/metric-units" \
-d '{ "velocity_kph": 110 }'
Resultado Esperado: A segunda publicação produz aproximadamente 68.35… mph (fracionário) e é aceita porque schema=double aceita números fracionários dentro do intervalo.
Piso (duplo, com piso):
curl -i -X POST \
-u "american-auto-dfloor:device-password" \
-H "Content-Type: application/json" \
"https://device-host/telemetry/automotive/usa-standard-units" \
-d '{ "speed": 60 }'
curl -i -X POST \
-u "european-auto-dfloor:device-password" \
-H "Content-Type: application/json" \
"https://device-host/telemetry/automotive/metric-units" \
-d '{ "velocity_kph": 110 }'
Resultado Esperado: A segunda publicação produz um número inteiro para 68 e é aceita. O valor é armazenado como um dobro, por exemplo, 68.0, mesmo que seja um número inteiro.
Observações sobre cotações de nome de usuário e impacto downstream
- Chave externa é igual ao nome de usuário de autenticação: Se uma instância de gêmeo digital for criada usando aspas no valor da chave externa, por exemplo,
"\"american-auto-standard-units\"", o nome de usuário de autenticação básica na sua solicitaçãocurldeverá incluir aspas ou ocorrer uma incompatibilidade e resultar em um erro401 Unauthorized. Para evitar problemas de cotação, é uma prática recomendada não usar aspas no seu valor de chave externa, como nos exemplos neste cenário. - Downstream no APEX ou usando SQL: Com
schema=double, os valores de mph fracionais são preservados. Se você precisar de números inteiros em relatórios, apliqueFLOOReROUNDno SQL, por exemplo,SELECT FLOOR(speed) FROM …. Comschema=integer, certifique-se de que o mapeamento emite valores integrais, por exemplo, usandofloorpara satisfazer a digitação de inteiros. - Suporte a expressões: O
inbound-routeaceita aritmética e ofloor. Funções comotoIntegerounumberforam rejeitadas e não são suportadas; usefloorou adoteschema: "double"para aceitação fracionária.