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; } } }
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)); }
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); }