/// <summary> /// Handle method call to get configured endpoints /// </summary> private ServiceResult OnGetConfiguredEndpointsCall(ISystemContext context, MethodState method, IList <object> inputArguments, IList <object> outputArguments) { string logPrefix = "GetConfiguredEndpointsCall:"; try { var methodResult = HubCommunication.HandleGetConfiguredEndpointsMethodAsync(new Microsoft.Azure.Devices.Client.MethodRequest("GetConfiguredEndpointsMethod", Encoding.UTF8.GetBytes(inputArguments[0] as string)), null).Result; outputArguments[0] = methodResult.ResultAsJson; } catch (Exception ex) { Logger.Error($"{logPrefix} The request is invalid!"); return(ServiceResult.Create(ex, null, StatusCodes.Bad)); } return(ServiceResult.Good); }
/// <summary> /// The notification that the data for a monitored item has changed on an OPC UA server. /// </summary> public void MonitoredItemNotificationEventHandler(MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e) { try { if (e == null || e.NotificationValue == null || monitoredItem == null || monitoredItem.Subscription == null || monitoredItem.Subscription.Session == null) { return; } if (!(e.NotificationValue is MonitoredItemNotification notification)) { return; } if (!(notification.Value is DataValue value)) { return; } MessageData messageData = new MessageData(); if (IotCentralMode) { // for IoTCentral we use the DisplayName as the key in the telemetry and the Value as the value. if (monitoredItem.DisplayName != null) { // use the DisplayName as reported in the MonitoredItem messageData.DisplayName = monitoredItem.DisplayName; } if (value.Value != null) { // use the Value as reported in the notification event argument encoded with the OPC UA JSON endcoder JsonEncoder encoder = new JsonEncoder(monitoredItem.Subscription.Session.MessageContext, false); value.ServerTimestamp = DateTime.MinValue; value.SourceTimestamp = DateTime.MinValue; value.StatusCode = StatusCodes.Good; encoder.WriteDataValue("Value", value); string valueString = encoder.CloseAndReturnText(); // we only want the value string, search for everything till the real value starts // and get it string marker = "{\"Value\":{\"Value\":"; int markerStart = valueString.IndexOf(marker, StringComparison.InvariantCulture); messageData.PreserveValueQuotes = true; if (markerStart >= 0) { // we either have a value in quotes or just a value int valueLength; int valueStart = marker.Length; if (valueString.IndexOf("\"", valueStart, StringComparison.InvariantCulture) >= 0) { // value is in quotes and two closing curly brackets at the end valueStart++; valueLength = valueString.Length - valueStart - 3; } else { // value is without quotes with two curly brackets at the end valueLength = valueString.Length - marker.Length - 2; messageData.PreserveValueQuotes = false; } messageData.Value = valueString.Substring(valueStart, valueLength); } Logger.Debug($" IoTCentral key: {messageData.DisplayName}"); Logger.Debug($" IoTCentral values: {messageData.Value}"); } } else { // update the required message data to pass only the required data to HubCommunication EndpointTelemetryConfiguration telemetryConfiguration = GetEndpointTelemetryConfiguration(EndpointUrl); // the endpoint URL is required to allow HubCommunication lookup the telemetry configuration messageData.EndpointUrl = EndpointUrl; if (telemetryConfiguration.NodeId.Publish == true) { messageData.NodeId = OriginalId; } if (telemetryConfiguration.MonitoredItem.ApplicationUri.Publish == true) { messageData.ApplicationUri = (monitoredItem.Subscription.Session.Endpoint.Server.ApplicationUri + (string.IsNullOrEmpty(OpcSession.PublisherSite) ? "" : $":{OpcSession.PublisherSite}")); } if (telemetryConfiguration.MonitoredItem.DisplayName.Publish == true && monitoredItem.DisplayName != null) { // use the DisplayName as reported in the MonitoredItem messageData.DisplayName = monitoredItem.DisplayName; } if (telemetryConfiguration.Value.SourceTimestamp.Publish == true && value.SourceTimestamp != null) { // use the SourceTimestamp as reported in the notification event argument in ISO8601 format messageData.SourceTimestamp = value.SourceTimestamp.ToString("o", CultureInfo.InvariantCulture); } if (telemetryConfiguration.Value.StatusCode.Publish == true && value.StatusCode != null) { // use the StatusCode as reported in the notification event argument messageData.StatusCode = value.StatusCode.Code; } if (telemetryConfiguration.Value.Status.Publish == true && value.StatusCode != null) { // use the StatusCode as reported in the notification event argument to lookup the symbolic name messageData.Status = StatusCode.LookupSymbolicId(value.StatusCode.Code); } if (telemetryConfiguration.Value.Value.Publish == true && value.Value != null) { // use the Value as reported in the notification event argument encoded with the OPC UA JSON endcoder JsonEncoder encoder = new JsonEncoder(monitoredItem.Subscription.Session.MessageContext, false); value.ServerTimestamp = DateTime.MinValue; value.SourceTimestamp = DateTime.MinValue; value.StatusCode = StatusCodes.Good; encoder.WriteDataValue("Value", value); string valueString = encoder.CloseAndReturnText(); // we only want the value string, search for everything till the real value starts // and get it string marker = "{\"Value\":{\"Value\":"; int markerStart = valueString.IndexOf(marker, StringComparison.InvariantCulture); messageData.PreserveValueQuotes = true; if (markerStart >= 0) { // we either have a value in quotes or just a value int valueLength; int valueStart = marker.Length; if (valueString.IndexOf("\"", valueStart, StringComparison.InvariantCulture) >= 0) { // value is in quotes and two closing curly brackets at the end valueStart++; valueLength = valueString.Length - valueStart - 3; } else { // value is without quotes with two curly brackets at the end valueLength = valueString.Length - marker.Length - 2; messageData.PreserveValueQuotes = false; } messageData.Value = valueString.Substring(valueStart, valueLength); } } // currently the pattern processing is done here, which adds runtime to the notification processing. // In case of perf issues it can be also done in CreateJsonMessageAsync of IoTHubMessaging.cs. // apply patterns messageData.ApplyPatterns(telemetryConfiguration); Logger.Debug($" ApplicationUri: {messageData.ApplicationUri}"); Logger.Debug($" EndpointUrl: {messageData.EndpointUrl}"); Logger.Debug($" DisplayName: {messageData.DisplayName}"); Logger.Debug($" Value: {messageData.Value}"); } // add message to fifo send queue if (monitoredItem.Subscription == null) { Logger.Debug($"Subscription already removed. No more details available."); } else { Logger.Debug($"Enqueue a new message from subscription {(monitoredItem.Subscription == null ? "removed" : monitoredItem.Subscription.Id.ToString(CultureInfo.InvariantCulture))}"); Logger.Debug($" with publishing interval: {monitoredItem.Subscription.PublishingInterval} and sampling interval: {monitoredItem.SamplingInterval}):"); } HubCommunication.Enqueue(messageData); } catch (Exception ex) { Logger.Error(ex, "Error processing monitored item notification"); } }