feat: Implement initialization exceptions handling and add API endpoint for retrieval

This commit is contained in:
Neil Dorin 2026-04-13 20:57:15 -06:00
parent 554916a74a
commit ac3b73cf32
4 changed files with 99 additions and 6 deletions

View file

@ -0,0 +1,16 @@
using System.Collections.Generic;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces;
/// <summary>
/// Defines the contract for devices that can report initialization exceptions.
/// </summary>
public interface IInitializationExceptions
{
/// <summary>
/// Gets a list of exceptions that occurred during the initialization of the program.
/// </summary>
List<System.Exception> InitializationExceptions { get; }
}

View file

@ -184,6 +184,11 @@ public class EssentialsWebApi : EssentialsDevice
Name = "Get Routing Devices and TieLines",
RouteHandler = new GetRoutingDevicesAndTieLinesHandler()
},
new HttpCwsRoute("initializationExceptions")
{
Name = "Get Initialization Exceptions",
RouteHandler = new GetInitializationExceptionsRequestHandler()
}
};
AddRoute(routes);
@ -296,4 +301,5 @@ public class EssentialsWebApi : EssentialsDevice
Debug.LogMessage(LogEventLevel.Information, this, new string('-', 50));
}
}
}

View file

@ -0,0 +1,50 @@
using Crestron.SimplSharp.WebScripting;
using System.Collections.Generic;
using System.Text;
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
using PepperDash.Core.Web.RequestHandlers;
using System.Linq;
namespace PepperDash.Essentials.Core.Web.RequestHandlers;
/// <summary>
/// Request handler for retrieving initialization exceptions from the control system.
/// </summary>
public class GetInitializationExceptionsRequestHandler : WebApiBaseRequestHandler
{
/// <summary>
/// Initializes a new instance of the <see cref="GetInitializationExceptionsRequestHandler"/> class.
/// </summary>
public GetInitializationExceptionsRequestHandler() : base(true) { }
/// <inheritdoc />
protected override void HandleGet(HttpCwsContext context)
{
var exceptions = (Global.ControlSystem as IInitializationExceptions)?.InitializationExceptions ?? new List<System.Exception>();
var response = Newtonsoft.Json.JsonConvert.SerializeObject(new
{
Exceptions = exceptions.Select(e => new
{
Message = e.Message,
StackTrace = e.StackTrace,
Type = e.GetType().FullName,
InnerException = e.InnerException != null ? new
{
Message = e.InnerException.Message,
StackTrace = e.InnerException.StackTrace,
Type = e.InnerException.GetType().FullName
} : null
}).ToList()
});
context.Response.StatusCode = 200;
context.Response.StatusDescription = "OK";
context.Response.ContentType = "application/json";
context.Response.ContentEncoding = Encoding.UTF8;
context.Response.Write(response, false);
context.Response.End();
}
}

View file

@ -18,6 +18,8 @@ using Timeout = Crestron.SimplSharp.Timeout;
using Serilog.Events;
using System.Threading.Tasks;
using PepperDash.Essentials.Core.Web;
using System.Collections.Generic;
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
namespace PepperDash.Essentials;
@ -28,12 +30,19 @@ namespace PepperDash.Essentials;
/// <remarks>This class extends <see cref="CrestronControlSystem"/> and serves as the entry point for the control
/// system. It manages the initialization of devices, rooms, tie lines, and other system components. Additionally, it
/// provides methods for platform determination, configuration loading, and system teardown.</remarks>
public class ControlSystem : CrestronControlSystem, ILoadConfig
public class ControlSystem : CrestronControlSystem, ILoadConfig, IInitializationExceptions
{
private Timer startTimer;
private ManualResetEventSlim initializeEvent;
private const long StartupTime = 500;
/// <summary>
/// List of exceptions that occurred during initialization.
/// This can be used to report issues with loading devices or tie lines without crashing the entire system,
/// which allows for partial functionality in cases where some components are misconfigured or have issues.
/// </summary>
public List<Exception> InitializationExceptions { get; private set; } = new List<Exception>();
/// <summary>
/// Initializes a new instance of the <see cref="ControlSystem"/> class, setting up the system's global state and
/// dependencies.
@ -457,6 +466,7 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig
}
catch (Exception e)
{
InitializationExceptions.Add(e);
Debug.LogMessage(e, "ERROR: Creating device {deviceKey:l}. Skipping device.", args: new[] { devConf.Key });
}
}
@ -482,12 +492,22 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig
return;
}
foreach (var tieLineConfig in ConfigReader.ConfigObject.TieLines)
try
{
var newTL = tieLineConfig.GetTieLine();
if (newTL != null)
tlc.Add(newTL);
foreach (var tieLineConfig in ConfigReader.ConfigObject.TieLines)
{
var newTL = tieLineConfig.GetTieLine();
if (newTL != null)
tlc.Add(newTL);
}
}
catch (Exception e)
{
InitializationExceptions.Add(e);
Debug.LogMessage(e, "ERROR: Creating tie line. Skipping tie line.");
}
Debug.LogMessage(LogEventLevel.Information, "All Tie Lines Loaded.");
@ -796,6 +816,7 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig
}
catch (Exception ex)
{
InitializationExceptions.Add(ex);
Debug.LogMessage(ex, "Exception loading room {roomKey}:{roomType}", null, roomConfig.Key, roomConfig.Type);
continue;
}