public void PrintInterfaceContent(DTInterfaceInfo dtif, IReadOnlyDictionary <Dtmi, DTEntityInfo> dtdlOM, int indent = 0)
        {
            var sb = new StringBuilder();

            for (int i = 0; i < indent; i++)
            {
                sb.Append("  ");
            }
            Console.WriteLine($"{sb}Interface: {dtif.Id} | {dtif.DisplayName}");
            Dictionary <string, DTContentInfo> contents = dtif.Contents;

            foreach (DTContentInfo item in contents.Values)
            {
                switch (item.EntityKind)
                {
                case DTEntityKind.Property:
                    DTPropertyInfo pi = item as DTPropertyInfo;
                    Console.WriteLine($"{sb}--Property: {pi.Name} with schema {pi.Schema}");
                    break;

                case DTEntityKind.Relationship:
                    DTRelationshipInfo ri = item as DTRelationshipInfo;
                    Console.WriteLine($"{sb}--Relationship: {ri.Name} with target {ri.Target}");
                    break;

                case DTEntityKind.Telemetry:
                    DTTelemetryInfo ti = item as DTTelemetryInfo;
                    Console.WriteLine($"{sb}--Telemetry: {ti.Name} with schema {ti.Schema}");
                    break;

                case DTEntityKind.Component:
                    DTComponentInfo ci = item as DTComponentInfo;
                    Console.WriteLine($"{sb}--Component: {ci.Id} | {ci.Name}");
                    DTInterfaceInfo component = ci.Schema;
                    PrintInterfaceContent(component, dtdlOM, indent + 1);
                    break;
                }
            }
        }
Beispiel #2
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest request,
            ILogger log)
        {
            string      requestBody       = await new StreamReader(request.Body).ReadToEndAsync();
            string      errorMessage      = string.Empty;
            DpsResponse response          = new DpsResponse();
            bool        isGroupEnrollment = false;
            string      registrationId;
            DateTime    localDate = DateTime.Now;

            _logger = log;

            dynamic requestData = JsonConvert.DeserializeObject(requestBody);

            if (requestData.ContainsKey("enrollmentGroup"))
            {
                log.LogInformation("Group Enrollment");
                registrationId    = requestData?.enrollmentGroup?.enrollmentGroupId;
                isGroupEnrollment = true;
            }
            else
            {
                log.LogInformation("Individual Enrollment");
                registrationId = requestData?.deviceRuntimeContext?.registrationId;
            }

            string[] iothubs = requestData?.linkedHubs.ToObject <string[]>();

            log.LogInformation($"dps_processor : Request.Body: {JsonConvert.SerializeObject(requestData, Formatting.Indented)}");

            #region payload_sample

            /* Payload Example
             * {
             * "enrollmentGroup": {
             *  "enrollmentGroupId": "PVDemo-Group-Enrollment-Custom-Allocation",
             *  "attestation": {
             *    "type": "symmetricKey"
             *  },
             *  "capabilities": {
             *    "iotEdge": false
             *  },
             *  "etag": "\"1a055216-0000-0800-0000-60a637160000\"",
             *  "provisioningStatus": "enabled",
             *  "reprovisionPolicy": {
             *    "updateHubAssignment": true,
             *    "migrateDeviceData": true
             *  },
             *  "createdDateTimeUtc": "2021-05-20T10:15:51.2294536Z",
             *  "lastUpdatedDateTimeUtc": "2021-05-20T10:16:54.6543548Z",
             *  "allocationPolicy": "custom",
             *  "iotHubs": [
             *    "PVDemo-IoTHub.azure-devices.net"
             *  ],
             *  "customAllocationDefinition": {
             *    "webhookUrl": "https://pvdemo-functions.azurewebsites.net/api/dps_processor?****",
             *    "apiVersion": "2019-03-31"
             *  }
             * },
             * "deviceRuntimeContext": {
             *  "registrationId": "WioTerminal",
             *  "symmetricKey": {},
             *  "payload": {
             *    "modelId": "dtmi:seeedkk:wioterminal:wioterminal_aziot_example_gps;5"
             *  }
             * },
             * "linkedHubs": [
             *  "PVDemo-IoTHub.azure-devices.net"
             * ]
             * }
             */
            #endregion

            try
            {
                if (registrationId == null)
                {
                    log.LogError($"Missing Registration ID");
                }
                else if (iothubs == null)
                {
                    errorMessage = "No linked hubs for this enrollment.";
                    log.LogError("linked IoT Hub");
                }
                else
                {
                    // Example of specific tasks based on Enrollment
                    if (registrationId.Contains("Group"))
                    {
                        // do specifics based on registration id
                    }

                    if (isGroupEnrollment)
                    {
                        // do specifics for Group Enrollment
                    }
                    else
                    {
                        // do specifics for Individual Enrollment
                    }

                    foreach (var iothub in iothubs)
                    {
                        // do specifics for linked hubs
                        // e.g. pick up right IoT Hub based on device id
                        response.iotHubHostName = iothub;
                    }


                    // build tags for the device
                    // tags are for solution only, devices do not see tags
                    // https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-device-twins#device-twins
                    TwinCollection twinTags = new TwinCollection();
                    twinTags["TagFromDpsWebHook"] = "CustomAllocationSample";

                    // build initial twin (Desired Properties) for the device
                    // these values will be passed to the device during Initial Get
                    TwinCollection desiredProperties = new TwinCollection();
                    desiredProperties["FromDpsWebHook1"] = "InitialTwinByCustomAllocation";
                    desiredProperties["FromDpsWebHook2"] = registrationId;

                    // Get DTDL Model Id
                    string componentName = string.Empty;
                    IReadOnlyDictionary <Dtmi, DTEntityInfo> parsedModel = null;
                    string modelId = requestData?.deviceRuntimeContext?.payload?.modelId;

                    if (!string.IsNullOrEmpty(modelId))
                    {
                        // If DTMI is given to DPS payload, parse it.
                        parsedModel = await DeviceModelResolveAndParse(modelId);
                    }

                    if (parsedModel != null)
                    {
                        string propertyName = "Hostname";
                        // Example : Setting Writable Property using Device Model
                        // We are interested in properties
                        DTPropertyInfo property = parsedModel.Where(r => r.Value.EntityKind == DTEntityKind.Property).Select(x => x.Value as DTPropertyInfo).Where(x => x.Writable == true).Where(x => x.Name == propertyName).FirstOrDefault();

                        if (property != null)
                        {
                            var dateString = $"{localDate.Year}{localDate.Month}{localDate.Day}-{localDate.Hour}{localDate.Minute}{localDate.Second}";
                            log.LogInformation($"Found Writable Property '{propertyName}'");

                            // If no match, this interface must be from Component
                            if (!modelId.Equals(property.DefinedIn.AbsoluteUri))
                            {
                                var component = parsedModel.Where(r => r.Value.EntityKind == DTEntityKind.Component).Select(x => x.Value as DTComponentInfo).Where(x => x.Schema.Id.ToString() == property.ChildOf.AbsoluteUri).FirstOrDefault();
                                if (component != null)
                                {
                                    TwinCollection componentTwin         = new TwinCollection();
                                    TwinCollection hostnameComponentTwin = new TwinCollection();
                                    // Hostname takes a parameter as JSON Object
                                    // JSON looks like this
                                    // "desired" : {
                                    //   "R700": {
                                    //     "__t": "c",
                                    //     "Hostname" : {
                                    //       "hostname" : "<New Name>"
                                    //     }
                                    //   }
                                    // }
                                    if (property.Schema.EntityKind == DTEntityKind.Object)
                                    {
                                        DTObjectInfo parameterObj = property.Schema as DTObjectInfo;
                                        hostnameComponentTwin[parameterObj.Fields[0].Name] = $"impinj-{dateString}";
                                        componentTwin[property.Name]      = hostnameComponentTwin;
                                        componentTwin["__t"]              = "c";
                                        desiredProperties[component.Name] = componentTwin;
                                    }
                                }
                            }
                            else
                            {
                                desiredProperties[property.Name] = $"impinj-{dateString}";
                            }
                        }
                    }

                    TwinState twinState = new TwinState(twinTags, desiredProperties);

                    response.initialTwin = twinState;
                }
            }
            catch (Exception ex)
            {
                log.LogError($"Exception {ex}");
                errorMessage = ex.Message;
            }

            if (!string.IsNullOrEmpty(errorMessage))
            {
                log.LogError($"Error : {errorMessage}");
                return(new BadRequestObjectResult(errorMessage));
            }

            log.LogInformation($"Response to DPS \r\n {JsonConvert.SerializeObject(response)}");
            return((ActionResult) new OkObjectResult(response));
        }
Beispiel #3
0
        private static void CreateCustomEntity(CdmCorpusDefinition cdmCorpus, CdmManifestDefinition manifestAbstract,
                                               CdmFolderDefinition localRoot, DTInterfaceInfo info)
        {
            string EntityName          = info.Id.ToString();
            string convertedEntityName = EntityName.Replace(':', '_');

            convertedEntityName = convertedEntityName.Replace(';', '-');

            // Create an entity - CustomAccount which has a relationship with the entity CustomPerson
            // Create the entity definition instance
            var entity = cdmCorpus.MakeObject <CdmEntityDefinition>(CdmObjectType.EntityDef, convertedEntityName, false);
            // Add type attributes to the entity instance
            var entityAttributeId = CreateEntityAttributeWithPurposeAndDataType(cdmCorpus, $"$dtId", "identifiedBy", "entityId");

            entity.Attributes.Add(entityAttributeId);
            //var entityAttributeName = CreateEntityAttributeWithPurposeAndDataType(cdmCorpus, $"${convertedEntityName}Name", "hasA", "name");
            //entity.Attributes.Add(entityAttributeName);

            var timestamp = CreateEntityAttributeWithPurposeAndDataType(cdmCorpus, "$timestamp", "hasA", "dateTime");

            entity.Attributes.Add(timestamp);

            // Add properties to the entity instance
            entity.DisplayName = info.DisplayName.FirstOrDefault().Value;
            entity.Version     = "0.0.1";
            entity.Description = info.Description.FirstOrDefault().Value;
            // Create the document which contains the entity
            var entityDoc = cdmCorpus.MakeObject <CdmDocumentDefinition>(CdmObjectType.DocumentDef, $"{convertedEntityName}.cdm.json", false);

            // Add an import to the foundations doc so the traits about partitons will resolve nicely
            entityDoc.Imports.Add(FoundationJsonPath);
            entityDoc.Definitions.Add(entity);

            foreach (KeyValuePair <string, DTContentInfo> kvp in info.Contents)
            {
                if (kvp.Value.EntityKind == DTEntityKind.Property)
                {
                    DTPropertyInfo pi  = kvp.Value as DTPropertyInfo;
                    Dtmi           def = DefiningModel(pi.Name, info);
                    if (def == info.Id)
                    {
                        Log.Out($"{info.Id}: Adding locally defined property {pi.Name}");

                        string type = "";
                        if (pi.Schema != null)
                        {
                            switch (pi.Schema.EntityKind)
                            {
                            case DTEntityKind.String: type = "string"; break;

                            case DTEntityKind.Float: type = "float"; break;

                            case DTEntityKind.Double: type = "double"; break;

                            case DTEntityKind.Boolean: type = "boolean"; break;

                            case DTEntityKind.Integer: type = "integer"; break;

                            case DTEntityKind.DateTime: type = "dateTime"; break;

                            default: break;
                            }
                        }
                        if (type != "")
                        {
                            var prop = CreateEntityAttributeWithPurposeAndDataType(cdmCorpus, pi.Name, "hasA", type);
                            entity.Attributes.Add(prop);
                        }
                    }
                    else
                    {
                        Log.Alert($"{info.Id}: Ignored property {pi.Name} because it is defined in \n{def}");
                    }
                }
            }



            // Handle inheritance
            if (info.Extends.Count > 0)
            {
                foreach (DTInterfaceInfo parent in info.Extends)
                {
                    string pEntityName          = parent.Id.ToString();
                    string pConvertedEntityName = pEntityName.Replace(':', '_');
                    pConvertedEntityName = pConvertedEntityName.Replace(';', '-');
                    entity.ExtendsEntity = cdmCorpus.MakeObject <CdmEntityReference>(CdmObjectType.EntityRef, pConvertedEntityName, true);
                    entityDoc.Imports.Add($"{pConvertedEntityName}.cdm.json");
                }
            }

            // Handle references
            foreach (KeyValuePair <string, DTContentInfo> kvp in info.Contents)
            {
                if (kvp.Value.EntityKind == DTEntityKind.Relationship)
                {
                    DTRelationshipInfo ri = kvp.Value as DTRelationshipInfo;
                    Dtmi def = DefiningModel(ri.Name, info);
                    if (def == info.Id)
                    {
                        string pEntityName          = string.Format("{0}_{1}", def.AbsoluteUri.Substring(0, def.AbsoluteUri.IndexOf(";")), ri.Name.ToString());
                        string pConvertedEntityName = pEntityName.Replace(':', '_');
                        pConvertedEntityName = pConvertedEntityName.Replace(';', '-');
                        Log.Out($"{info.Id}: Adding locally defined relationship {ri.Name}");
                        var attributeExplanation = $"{ri.Name}: {ri.Description.Values.FirstOrDefault()}";
                        var t = kvp.Value;
                        CreateRelatedCustomEntity(cdmCorpus, manifestAbstract, localRoot, ri.Properties, pConvertedEntityName, ri.Name);
                        // You can all CreateSimpleAttributeForRelationshipBetweenTwoEntities() instead, but CreateAttributeForRelationshipBetweenTwoEntities() can show
                        // more details of how to use resolution guidance to customize your data
                        var refAttribute = CreateAttributeForRelationshipBetweenTwoEntities(cdmCorpus, convertedEntityName, pConvertedEntityName, attributeExplanation);
                        entity.Attributes.Add(refAttribute);
                        // Add an import to the foundations doc so the traits about partitons will resolve nicely
                        entityDoc.Imports.Add(FoundationJsonPath);
                        // the CustomAccount entity has a relationship with the CustomPerson entity, this relationship is defined from its attribute with traits,
                        // the import to the entity reference CustomPerson's doc is required
                        entityDoc.Imports.Add($"{pConvertedEntityName}.cdm.json");
                    }
                    else
                    {
                        Log.Alert($"{info.Id}: Ignored property {ri.Name} because it is defined in \n{def}");
                    }
                }
            }

            // Add the document to the root of the local documents in the corpus
            localRoot.Documents.Add(entityDoc, entityDoc.Name);
            // Add the entity to the manifest
            manifestAbstract.Entities.Add(entity);
        }