fix: surface masked device/room construction failures

GetDevice returns null for both a genuinely unknown type and a registered type whose factory throws/returns null, so LoadDevices/LoadRooms logged a misleading 'unknown type' message (at Information level for devices) even when the type was known but failed to construct - e.g. a duplicate-feedback ArgumentException from a plugin constructor.

Add DeviceFactory.HasFactoryForType so the callers can distinguish the two: a registered-but-failed type now logs an Error pointing at the exception already logged by GetDevice; a genuinely unknown type keeps the 'unknown type' message (now Warning). No change to exception flow.
This commit is contained in:
Erik Meyer 2026-08-31 11:02:39 -04:00
parent fec59d2149
commit 325ac4d04f
2 changed files with 20 additions and 2 deletions

View file

@ -267,4 +267,17 @@ public class DeviceFactory
? FactoryMethods
: FactoryMethods.Where(k => k.Key.Contains(filter)).ToDictionary(k => k.Key, k => k.Value);
}
/// <summary>
/// Indicates whether a factory is registered for the specified device type name.
/// </summary>
/// <remarks>Lets callers of <see cref="GetDevice"/> distinguish a genuinely unknown device type from a
/// known type whose factory failed to build the device (both currently return <see langword="null"/> from
/// <see cref="GetDevice"/>), so the two can be logged with accurate, distinct messages.</remarks>
/// <param name="typeName">The device type name to check. Matching is case-insensitive.</param>
/// <returns><see langword="true"/> if a factory is registered for <paramref name="typeName"/>; otherwise <see langword="false"/>.</returns>
public static bool HasFactoryForType(string typeName)
{
return !string.IsNullOrEmpty(typeName) && FactoryMethods.ContainsKey(typeName);
}
}

View file

@ -556,8 +556,10 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig, IInitialization
if (newDev != null)
DeviceManager.AddDevice(newDev);
else if (Core.DeviceFactory.HasFactoryForType(devConf.Type))
Debug.LogMessage(LogEventLevel.Error, "ERROR: Device type '{deviceType:l}' is registered but failed to build device '{deviceKey:l}' (factory returned null; see any exception logged above for the cause).", devConf.Type, devConf.Key);
else
Debug.LogMessage(LogEventLevel.Information, "ERROR: Cannot load unknown device type '{deviceType:l}', key '{deviceKey:l}'.", devConf.Type, devConf.Key);
Debug.LogMessage(LogEventLevel.Warning, "ERROR: Cannot load unknown device type '{deviceType:l}', key '{deviceKey:l}'.", devConf.Type, devConf.Key);
}
catch (Exception e)
{
@ -904,7 +906,10 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig, IInitialization
if (room == null)
{
Debug.LogWarning("ERROR: Cannot load unknown room type '{roomType:l}', key '{roomKey:l}'.", roomConfig.Type, roomConfig.Key);
if (Core.DeviceFactory.HasFactoryForType(roomConfig.Type))
Debug.LogError("ERROR: Room type '{roomType:l}' is registered but failed to build room '{roomKey:l}' (factory returned null; see any exception logged above for the cause).", roomConfig.Type, roomConfig.Key);
else
Debug.LogWarning("ERROR: Cannot load unknown room type '{roomType:l}', key '{roomKey:l}'.", roomConfig.Type, roomConfig.Key);
continue;
}