Skapa en avancerad enhetsmodell

Den här guiden beskriver JSON- och JavaScript-filer som definierar en anpassad enhetsmodell. Artikeln innehåller några exempel på definitionsfiler för enhetsmodeller och visar hur du laddar upp dem till din enhetssimuleringsinstans. Du kan skapa avancerade enhetsmodeller för att simulera mer realistiska enhetsbeteenden för testningen.

Om du inte har någon Azure-prenumeration kan du skapa ett kostnadsfritt konto innan du börjar.

Förutsättningar

Om du vill följa stegen i den här guiden behöver du en distribuerad instans av enhetssimulering i din Azure-prenumeration.

Om du inte har distribuerat enhetssimulering ännu kan du läsa Distribution av enhetssimulering på GitHub.

Öppna enhetssimulering

Om du inte har distribuerat enhetssimulering ännu kan du läsa Distribution av enhetssimulering på GitHub.

Enhetsmodeller

Varje simulerad enhet tillhör en specifik enhetsmodell som definierar simuleringsbeteendet. Det här beteendet omfattar hur ofta telemetri ska skickas, vilken typ av meddelanden som ska skickas och vilka metoder som stöds.

Du definierar en enhetsmodell med hjälp av en JSON-enhetsdefinitionsfil och en uppsättning JavaScript-filer. Dessa JavaScript-filer definierar simuleringsbeteendet, till exempel slumpmässig telemetri och metodlogik.

En typisk enhetsmodell har:

  • En JSON-fil för varje enhetsmodell (till exempel hiss.json).
  • En JavaScript-beteendeskriptfil för varje enhetsmodell (till exempel elevator-state.js)
  • En JavaScript-metodskriptfil för varje enhetsmetod (till exempel elevator-go-down.js)

Anteckning

Alla enhetsmodeller definierar inte metoder. Därför kanske en enhetsmodell har metodskript eller inte. Alla enhetsmodeller måste dock ha ett beteendeskript.

Enhetsdefinitionsfil

Varje enhetsdefinitionsfil innehåller information om en simulerad enhetsmodell, inklusive följande information:

  • Enhetsmodellnamn: sträng.
  • Protokoll: AMQP | MQTT-| HTTP.
  • Det ursprungliga enhetstillståndet.
  • Hur ofta enhetens tillstånd ska uppdateras.
  • Vilken JavaScript-fil som ska användas för att uppdatera enhetstillståndet.
  • En lista över telemetrimeddelanden som ska skickas, var och en med en specifik frekvens.
  • Schemat för telemetrimeddelanden som används av serverdelsprogrammet för att parsa den mottagna telemetrin.
  • En lista över metoder som stöds och JavaScript-filen som ska användas för att simulera varje metod.

Filschema

Schemaversionen är alltid "1.0.0" och är specifik för formatet på den här filen:

"SchemaVersion": "1.0.0"

Beskrivning av enhetsmodell

Följande egenskaper beskriver enhetsmodellen. Varje typ har en unik identifierare, en semantisk version, ett namn och en beskrivning:

"Id": "chiller-01",
"Version": "0.0.1",
"Name": "Chiller",
"Description": "Chiller with external temperature and humidity sensors."

IoT-protokoll

IoT-enheter kan ansluta med olika protokoll. Med simuleringen kan du använda antingen AMQP, MQTT eller HTTP:

"Protocol": "AMQP"

Simulerat enhetstillstånd

Varje simulerad enhet har ett internt tillstånd som måste definieras. Tillståndet definierar också de egenskaper som kan rapporteras i telemetri. En kylaggregat kan till exempel ha ett initialt tillstånd, till exempel:

"InitialState": {
    "temperature": 50,
    "humidity": 40
},

En flyttenhet med flera sensorer kan ha fler egenskaper, till exempel:

"InitialState": {
    "latitude": 47.445301,
    "longitude": -122.296307,
    "speed": 30.0,
    "speed_unit": "mph",
    "cargotemperature": 38.0,
    "cargotemperature_unit": "F"
}

Enhetstillståndet sparas i minnet av simuleringstjänsten och tillhandahålls som indata till JavaScript-funktionen. JavaScript-funktionen kan bestämma:

  • För att ignorera tillståndet och generera vissa slumpmässiga data.
  • Uppdatera enhetstillståndet på något realistiskt sätt för ett visst scenario.

Funktionen som genererar tillståndet tar också emot som indata:

  • Enhets-ID:t.
  • Enhetsmodellen.
  • Aktuell tid. Det här värdet gör det möjligt att generera olika data per enhet och efter tid.

Generera telemetrimeddelanden

Simuleringstjänsten kan skicka flera telemetrityper för varje enhet. Telemetri innehåller vanligtvis data från enhetens tillstånd. Ett simulerat rum kan till exempel skicka information om temperatur och luftfuktighet var tionde sekund. Observera platshållarna i följande kodfragment, som automatiskt ersätts med värden från enhetens tillstånd:

"Telemetry": [
    {
        "Interval": "00:00:10",
        "MessageTemplate":
            "{\"temperature\":${temperature},\"temperature_unit\":\"${temperature_unit}\",\"humidity\":\"${humidity}\"}",
        "MessageSchema": {
            "Name": "RoomComfort;v1",
            "Format": "JSON",
            "Fields": {
                "temperature": "double",
                "temperature_unit": "text",
                "humidity": "integer"
            }
        }
    }
],

Platshållarna använder en särskild syntax ${NAME} där NAME är en nyckel från enhetstillståndsobjektet som returneras av Huvudfunktionen JavaScript. Strängar bör citeras, medan tal inte ska göra det.

Meddelandeschema

Varje meddelandetyp måste ha ett väldefinierat schema. Meddelandeschemat publiceras också till IoT Hub, så att serverdelsprogram kan återanvända informationen för att tolka inkommande telemetri.

Schemat stöder JSON-format, vilket möjliggör enkel parsning, omvandling och analys i flera system och tjänster.

Fälten som anges i schemat kan vara av följande typer:

  • Objekt – serialiserat med JSON
  • Binär – serialiserad med base64
  • Text
  • Boolesk
  • Integer
  • Double
  • DateTime

Metoder som stöds

Simulerade enheter kan också reagera på metodanrop, i vilket fall de kör viss logik och ger ett visst svar. På samma sätt som simuleringen lagras metodlogik i en JavaScript-fil och kan interagera med enhetstillståndet. Ett exempel:

"CloudToDeviceMethods": {
    "Start": {
        "Type": "JavaScript",
        "Path": "release-pressure.js"
    }
}

Skapa en enhetsdefinitionsfil

I den här guiden får du se hur du skapar en enhetsmodell för en drönare. Drönaren flyger slumpmässigt runt en första uppsättning koordinater som ändrar plats och höjd.

Kopiera följande JSON till en textredigerare och spara den som drone.json.

JSON-exempel för enhetsdefinition

{
  "SchemaVersion": "1.0.0",
  "Id": "drone",
  "Version": "0.0.1",
  "Name": "Drone",
  "Description": "Simple drone.",
  "Protocol": "AMQP",
  "Simulation": {
    "InitialState": {
      "velocity": 0.0,
      "velocity_unit": "mm/sec",
      "acceleration": 0.0,
      "acceleration_unit": "mm/sec^2",
      "latitude": 47.476075,
      "longitude": -122.192026,
      "altitude": 0.0
    },
    "Interval": "00:00:05",
    "Scripts": [{
      "Type": "JavaScript",
      "Path": "drone-state.js"
    }]
  },
  "Properties": {
    "Type": "Drone",
    "Firmware": "1.0",
    "Model": "P-96"
  },
  "Tags": {
    "Owner": "Contoso"
  },
  "Telemetry": [{
      "Interval": "00:00:05",
      "MessageTemplate": "{\"velocity\":\"${velocity}\",\"acceleration\":\"${acceleration}\",\"position\":\"${latitude}|${longitude}|${altitude}\"}",
      "MessageSchema": {
        "Name": "drone-event-sensor;v1",
        "Format": "JSON",
        "Fields": {
          "velocity": "double",
          "velocity_unit": "text",
          "acceleration": "double",
          "acceleration_unit": "text",
          "latitude": "double",
          "longitude": "double",
          "altitude": "double"
        }
      }
    }
  ],
    "CloudToDeviceMethods": {
        "RecallDrone": {
            "Type": "JavaScript",
            "Path": "droneRecall-method.js"
        }
    }
}

Beteendeskriptfiler

Koden i beteendeskriptfilen flyttar drönaren. Skriptet ändrar drönarens höjning och plats genom att ändra enhetens minnestillstånd.

JavaScript-filerna måste ha en huvudfunktion som accepterar två parametrar:

  • Ett kontextobjekt som innehåller tre egenskaper:
    • currentTime som en sträng med formatet yyyy-MM-dd'T'HH:mm:sszzz.
    • deviceId. Till exempel Simulated.Elevator.123.
    • deviceModel. Till exempel Hiss.
  • Ett tillståndsobjekt som är det värde som returnerades av funktionen i föregående anrop. Det här enhetstillståndet underhålls av simuleringstjänsten och används för att generera telemetrimeddelanden.

Huvudfunktionen returnerar det nya enhetstillståndet. Ett exempel:

function main(context, state) {

    // Use context if the simulation depends on
    // time or device details.
    // Execute some logic, updating 'state'

    return state;
}

Skapa en beteendeskriptfil

Kopiera följande JavaScript till en textredigerare och spara det som drone-state.js.

Exempel på JavaScript-simulering för enhetsmodell

"use strict";

// Position control
const DefaultLatitude = 47.476075;
const DefaultLongitude = -122.192026;
const DistanceVariation = 10;

// Altitude control
const DefaultAltitude = 0.0;
const AverageAltitude = 499.99;
const AltitudeVariation = 5;

// Velocity control
const AverageVelocity = 60.00;
const MinVelocity = 20.00;
const MaxVelocity = 120.00;
const VelocityVariation = 5;

// Acceleration control
const AverageAcceleration = 2.50;
const MinAcceleration = 0.01;
const MaxAcceleration = 9.99;
const AccelerationVariation = 1;

// Display control for position and other attributes
const GeoSpatialPrecision = 6;
const DecimalPrecision = 2;

// Default state
var state = {
    velocity: 0.0,
    velocity_unit: "mm/sec",
    acceleration: 0.0,
    acceleration_unit: "mm/sec^2",
    latitude: DefaultLatitude,
    longitude: DefaultLongitude,
    altitude: DefaultAltitude
};

// Default device properties
var properties = {};

/**
 * Restore the global state using data from the previous iteration.
 *
 * @param previousState device state from the previous iteration
 * @param previousProperties device properties from the previous iteration
 */
function restoreSimulation(previousState, previousProperties) {
    // If the previous state is null, force a default state
    if (previousState) {
        state = previousState;
    } else {
        log("Using default state");
    }

    if (previousProperties) {
        properties = previousProperties;
    } else {
        log("Using default properties");
    }
}

/**
 * Simple formula generating a random value around the average
 * in between min and max
 */
function vary(avg, percentage, min, max) {
    var value = avg * (1 + ((percentage / 100) * (2 * Math.random() - 1)));
    value = Math.max(value, min);
    value = Math.min(value, max);
    return value;
}

/**
 * Entry point function called by the simulation engine.
 * Returns updated simulation state.
 * Device property updates must call updateProperties() to persist.
 *
 * @param context             The context contains current time, device model and id
 * @param previousState       The device state since the last iteration
 * @param previousProperties  The device properties since the last iteration
 */
/*jslint unparam: true*/
function main(context, previousState, previousProperties) {

    // Restore the global state before generating the new telemetry, so that
    // the telemetry can apply changes using the previous function state.
    restoreSimulation(previousState, previousProperties);

    state.acceleration = vary(AverageAcceleration, AccelerationVariation, MinAcceleration, MaxAcceleration).toFixed(DecimalPrecision);
    state.velocity = vary(AverageVelocity, VelocityVariation, MinVelocity, MaxVelocity).toFixed(DecimalPrecision);

    // Use the last coordinates to calculate the next set with a given variation
    var coords = varylocation(Number(state.latitude), Number(state.longitude), DistanceVariation);
    state.latitude = Number(coords.latitude).toFixed(GeoSpatialPrecision);
    state.longitude = Number(coords.longitude).toFixed(GeoSpatialPrecision);

    // Fluctuate altitude between given variation constant by more or less
    state.altitude = vary(AverageAltitude, AltitudeVariation, AverageAltitude - AltitudeVariation, AverageAltitude + AltitudeVariation).toFixed(DecimalPrecision);

    return state;
}

/**
 * Generate a random geolocation at some distance (in miles)
 * from a given location
 */
function varylocation(latitude, longitude, distance) {
    // Convert to meters, use Earth radius, convert to radians
    var radians = (distance * 1609.344 / 6378137) * (180 / Math.PI);
    return {
        latitude: latitude + radians,
        longitude: longitude + radians / Math.cos(latitude * Math.PI / 180)
    };
}

Skapa en metodskriptfil

Metodskript liknar beteendeskript. De definierar enhetsbeteendet när en specifik moln-till-enhet-metod anropas.

Drönaråterkallelseskriptet ställer in drönarens koordinater till en fast punkt för att simulera drönaren som återvänder hem.

Kopiera följande JavaScript till en textredigerare och spara det som droneRecall-method.js.

Exempel på JavaScript-simulering för enhetsmodell

"use strict";

// Default state
var state = {
    velocity: 0.0,
    velocity_unit: "mm/sec",
    acceleration: 0.0,
    acceleration_unit: "mm/sec^2",
    latitude: 0.0,
    longitude: 0.0,
    altitude: 0.0
};

// Default device properties
var properties = {};

/**
 * Restore the global state using data from the previous iteration.
 *
 * @param previousState device state from the previous iteration
 * @param previousProperties device properties from the previous iteration
 */
function restoreSimulation(previousState, previousProperties) {
    // If the previous state is null, force a default state
    if (previousState) {
        state = previousState;
    } else {
        log("Using default state");
    }

    if (previousProperties) {
        properties = previousProperties;
    } else {
        log("Using default properties");
    }
}

/**
 * Entry point function called by the simulation engine.
 *
 * @param context        The context contains current time, device model and id, not used
 * @param previousState  The device state since the last iteration, not used
 * @param previousProperties  The device properties since the last iteration
 */
/*jslint unparam: true*/
function main(context, previousState, previousProperties) {

    // Restore the global device properties and the global state before
    // generating the new telemetry, so that the telemetry can apply changes
    // using the previous function state.
    restoreSimulation(previousState, previousProperties);

    //simulate the behavior of a drone when recalled
  state.latitude = 47.476075;
  state.longitude = -122.192026;
  return state;
}

Felsöka skriptfiler

Även om du inte kan koppla en felsökare till en beteendefil som körs kan du skriva information till tjänstloggen med hjälp av loggfunktionen . För syntaxfel misslyckas tolken och skriver information om undantaget till loggen.

Loggningsexempel:

function main(context, state) {

    log("This message will appear in the service logs.");

    log(context.deviceId);

    if (typeof(state) !== "undefined" && state !== null) {
        log("Previous value: " + state.temperature);
    }

    // ...

    return updateState;
}

Distribuera en avancerad enhetsmodell

Om du vill distribuera din avancerade enhetsmodell laddar du upp filerna din enhetssimuleringsinstans:

Välj Enhetsmodeller på menyraden. På sidan Enhetsmodeller visas de enhetsmodeller som är tillgängliga i den här instansen av enhetssimulering:

Enhetsmodeller

Klicka på + Lägg till enhetsmodeller i det övre högra hörnet på sidan:

Lägga till enhetsmodell

Klicka på Avancerat för att öppna fliken avancerad enhetsmodell:

Fliken Avancerat

Klicka på Bläddra och välj de JSON- och JavaScript-filer som du skapade. Se till att markera alla tre filerna. Om någon fil saknas misslyckas verifieringen:

Bläddra bland filer

Om filerna godkänns klickar du på Spara och enhetsmodellen är redo att användas i en simulering. Annars kan du åtgärda eventuella fel och läsa in filerna igen:

Spara

Nästa steg

I den här guiden har du lärt dig om enhetsmodellfilerna som används i enhetssimulering och hur du skapar en avancerad enhetsmodell. Nu kanske du vill utforska hur du använder Time Series Insights för att visualisera telemetri som skickas från lösningsacceleratorn för enhetssimulering.