public DeviceTableEntity(DeviceDocument deviceDoc)
 {
     this.PartitionKey = deviceDoc.GatewayName;
     this.RowKey       = deviceDoc.DeviceName;
     this.Timestamp    = DateTime.UtcNow;
     this.DeviceDoc    = deviceDoc;
     this.Text         = JsonConvert.SerializeObject(deviceDoc);
 }
        public static async Task HandleMessageBatch(
            List <BACNetIoTHubMessage> messageBatch,
            List <DeviceDocument> knownDeviceDocuments,
            IAsyncCollector <DeviceDocument> deviceDocumentsWriteCollector,
            IAsyncCollector <string> eventCollector,
            IAsyncCollector <DeviceTableEntity> unprovisionedDeviceCollector,
            ILogger log,
            CancellationToken cancellationToken)
        {
            List <string> unprovisioned = new List <string>();

            foreach (var bacNetIoTHubMsg in messageBatch)
            {
                if (cancellationToken.IsCancellationRequested)
                {
                    log.LogWarning("Function was cancelled.");
                    break;
                }

                DeviceDocument deviceDoc = GetKnownOrNewDeviceDoc(knownDeviceDocuments, bacNetIoTHubMsg.BACNetMsg.name);

                //update the device doc with inbound telemetry (value, timestamp, status, etc.)
                deviceDoc = ApplyTelemetryToDeviceDoc(bacNetIoTHubMsg, deviceDoc);

                //UpSERT the device document to Cosmos
                await deviceDocumentsWriteCollector.AddAsync(deviceDoc, cancellationToken);

                if (String.Equals(deviceDoc.DeviceStatus, DEVICE_STATUS_UNPROVISIONED, StringComparison.OrdinalIgnoreCase))
                {   //Handle Unprovisioned Devices here
                    unprovisioned.Add(bacNetIoTHubMsg.BACNetMsg.name);
                    await unprovisionedDeviceCollector.AddAsync(new DeviceTableEntity(bacNetIoTHubMsg), cancellationToken);
                }
                else
                {
                    //Provisioned Device only code
                    await eventCollector.AddAsync(JsonConvert.SerializeObject(deviceDoc), cancellationToken);
                }
            }

            if (unprovisioned.Any())
            {
                Interlocked.Increment(ref unprovisionedCount);
                log.LogError($"Unprovisioned DeviceIds encountered: {String.Join(',', unprovisioned)} ");
            }
        }
        private static DeviceDocument GetKnownOrNewDeviceDoc(
            List <DeviceDocument> knownDeviceDocuments,
            string deviceId)
        {
            //Lookup Known Devices in CosmosDB by Id
            var deviceDoc = knownDeviceDocuments.SingleOrDefault(
                document => document.id == deviceId);

            if (deviceDoc == null) //not found, so create a new one and mark it unprovisioned
            {
                deviceDoc = new DeviceDocument()
                {
                    id           = deviceId,
                    DeviceStatus = DEVICE_STATUS_UNPROVISIONED
                };
            }

            return(deviceDoc);
        }
        public static DeviceDocument ApplyTelemetryToDeviceDoc(
            BACNetIoTHubMessage bacNetIoTHubMessage,
            DeviceDocument inputDeviceDocument)
        {
            //Todo figure out how this function becomes modularized/configurable
            var parsedDeviceName = ((string)bacNetIoTHubMessage.BACNetMsg.name).Split('_');

            string unparsedValue = bacNetIoTHubMessage.BACNetMsg.value.ToString();

            (string value, string unit) = ParseValueUnit(unparsedValue, parsedDeviceName[2]);

            inputDeviceDocument.PresentValue    = value;
            inputDeviceDocument.ValueUnits      = unit;
            inputDeviceDocument.DeviceTimestamp = DateTimeOffset.Parse((string)bacNetIoTHubMessage.BACNetMsg.timestamp, styles: DateTimeStyles.RoundtripKind);

            //preserve Unprovisioned status
            if (!String.Equals(inputDeviceDocument.DeviceStatus, DEVICE_STATUS_UNPROVISIONED, StringComparison.OrdinalIgnoreCase))
            {
                inputDeviceDocument.DeviceStatus = bacNetIoTHubMessage.BACNetMsg.status;
            }
            inputDeviceDocument.EventEnqueuedUtcTime = bacNetIoTHubMessage.SystemProperties?.EnqueuedTimeUtc ?? DateTime.UtcNow;

            return(inputDeviceDocument);
        }