Device Status (UI)
This is a continuation of the Custom Devices tutorial.
Visual Studio Project:
Many devices display a small section of colored text at the top of the corresponding device icon. This text is useful as it can show the device status without having to navigate (or load) the device user interface. It’s easy to glance at the channel and see that a device is operating as expected (or has established a network connection, for example).
The device developer can set the text and background based on the device state. For this tutorial, let’s create some trivial states and see how they are displayed on our custom device.
This is the updated Device class which includes code to show the device status:
using Connexion.Core;
using Connexion.Core.HL7;
using System;
using System.Linq;
using System.Net;
using System.Runtime.Remoting.Contexts;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Media;
namespace HL7FilerTutorial
{
[DevicePlugin("HL7FilerTutorial", "Writes some HL7v2 fields to a database", DeviceDefinitionFlags.None, typeof(HL7Message), typeof(object), typeof(HL7FilerTutorialFactory))]
public class HL7FilerTutorial : BaseDevice<HL7FilerTutorialConfiguration>, IDatabaseInformation
{
private int m_ProcessedMessageCount = 0;
public HL7FilerTutorial(Guid deviceKey, IMessageChannelDevice messageChannelDevice)
: base(deviceKey, messageChannelDevice)
{
}
public override void Start()
{
// when the device starts, we'll show some green text
RealtimeDeviceDisplayStatus.SetSummaryText("Running", Colors.Green);
}
public override void Stop()
{
// when the device stops, we'll clear the text
RealtimeDeviceDisplayStatus.ClearSummaryText();
}
public override bool DeviceChangesMessage => false;
public override async Task ProcessMessageAsync(IMessageContext context, CancellationToken token)
{
var hl7 = context.GetAsHL7Message(); // get an HL7 message from the queue
// get the PID segment, or throw if there isn't one
var pidSegment = hl7.Segments.OfType<PID>().FirstOrDefault() ?? throw new Exception("PID segment not found in the HL7 message.");
// pull some fields from the PID segment
var firstName = pidSegment.PatientName_05.First.GivenName_02.Value;
var lastName = pidSegment.PatientName_05.First.FamilyName_01.Surname_01.Value;
var address = pidSegment.PatientAddress_11.First.StreetAddress_01.ToString();
var city = pidSegment.PatientAddress_11.First.City_03.Value;
var state = pidSegment.PatientAddress_11.First.StateOrProvince_04.Value;
var zip = pidSegment.PatientAddress_11.First.ZipOrPostalCode_05.Value;
var builder = Configuration.DatabaseConnection.GetProviderConnectionStringBuilder();
using (var connection = new Microsoft.Data.SqlClient.SqlConnection(builder.ConnectionString))
{
await connection.OpenAsync(token);
using (var command = connection.CreateCommand())
{
command.CommandText = "INSERT INTO Patient (FirstName, LastName, Address, City, State, Zip) OUTPUT Inserted.ID VALUES (@FirstName, @LastName, @Address, @City, @State, @Zip)";
command.Parameters.AddWithValue("@FirstName", firstName);
command.Parameters.AddWithValue("@LastName", lastName);
command.Parameters.AddWithValue("@Address", address);
command.Parameters.AddWithValue("@City", city);
command.Parameters.AddWithValue("@State", state);
command.Parameters.AddWithValue("@Zip", zip);
var patientId = (long)await command.ExecuteScalarAsync(token);
// write to the Processing History
context.WriteEvent(EventSeverity.Info, $"Patient {firstName} {lastName} with ID {patientId} has been successfully filed to the database.");
}
}
// show number of messages processed
m_ProcessedMessageCount++;
RealtimeDeviceDisplayStatus.SetSummaryText($"{m_ProcessedMessageCount} message(s) processed", Colors.Green);
}
public override void OnError(IMessageContext context, ErrorEventArgs args)
{
if (args.Exception is Microsoft.Data.SqlClient.SqlException sqlException)
{
// status to show error
RealtimeDeviceDisplayStatus.SetSummaryText(sqlException.Message, Colors.Orange);
args.ShouldRetry = true;
if (args.TotalRetries % 5 == 0)
Logger.Write(EventSeverity.Error, $"Filing failed: {sqlException.Message}");
args.SleepTime = TimeSpan.FromSeconds(args.TotalRetries > 2 ? 30 : 5);
}
}
public async Task<int> GetPatientCountAsync()
{
var builder = Configuration.DatabaseConnection.GetProviderConnectionStringBuilder();
using (var connection = new Microsoft.Data.SqlClient.SqlConnection(builder.ConnectionString))
{
await connection.OpenAsync();
using (var command = connection.CreateCommand())
{
command.CommandText = "Select Count(*) From Patient";
return (int)await command.ExecuteScalarAsync();
}
}
}
}
}On line 26, in the Start method, you can see we’re setting the text to read ‘Running' with a green background. In the Stop method, we’re clearing the text (which hides this overlay altogether).
On line 72, we update the text each time a message is processed. We don’t recommend updating the state this frequently - this is just an example of updating the state based on the processing logic.
Finally, on line 80, we’ll show the error text with an orange background (if a sql error occurs).
We leave it up to you to ensure that you handle all the different states of your device. Typically this means setting some state in the Start and/or ProcessMessage methods, clearing state in the Stop method, and surfacing error information in the OnError method. Remember to clear any error state once the error condition is no longer present.
Here is what the updated device (with status) looks like:
Since Connexion supports both light and dark modes, we recommend the following colors:
Light Green (Success)
Orange (Error)
White (Information)