using System.Device.Gpio; using System.Device.Pwm; using Microsoft.Extensions.Logging; using GSS2.Core.Abstractions; namespace GSS2.Core.Hardware; public class IlluminatorService : IDisposable { public record IlluminatorServiceConfiguration : ServiceConfigurationBase { public static IlluminatorServiceConfiguration Default => new IlluminatorServiceConfiguration { WhitePwmPin = 13, Uv254PwmPin = 18, Uv365PwmPin = 12, WhiteRelayPin = 16, Uv254RelayPin = 15, Uv365RelayPin = 20, FanPin = 21, PwmFrequency = 200, MaxWhitePwmDuty = 1.0, MaxUv254PwmDuty = 1.0, MaxUv365PwmDuty = 1.0 }; public int WhitePwmPin { get; set; } public int Uv254PwmPin { get; set; } public int Uv365PwmPin { get; set; } public int WhiteRelayPin { get; set; } public int Uv254RelayPin { get; set; } public int Uv365RelayPin { get; set; } public int FanPin { get; set; } public int PwmFrequency { get; set; } public double MaxWhitePwmDuty { get; set; } public double MaxUv254PwmDuty { get; set; } public double MaxUv365PwmDuty { get; set; } } private static readonly Dictionary PinPwmChips = new() { { 12, 0 }, { 13, 0 }, { 14, 0 }, { 15, 0 }, { 18, 0 }, { 19, 0 }, }; private static readonly Dictionary PinPwmChannels = new() { { 12, 0 }, { 13, 1 }, { 14, 2 }, { 15, 3 }, { 18, 2 }, { 19, 3 } }; private bool _disposedValue; private readonly ILogger _logger; private readonly IlluminatorServiceConfiguration _configuration; private readonly GpioController _gpio; private readonly PwmChannel _whitePwm; private readonly PwmChannel _uv254Pwm; private readonly PwmChannel _uv365Pwm; private readonly GpioPin _whiteRelay; private readonly GpioPin _uv254Relay; private readonly GpioPin _uv365Relay; private readonly GpioPin _fanRelay; public IlluminatorService(ILogger logger, IlluminatorServiceConfiguration? configuration = null) { _logger = logger; _logger.LogInformation("Initialization"); try { if (configuration is null) { configuration = IlluminatorServiceConfiguration.Default; _logger.LogWarning("Configuration not set. Using default configuration."); } _configuration = configuration; _logger.LogInformation("Using following configuration: \n{}", _configuration.ToString("\n", 4)); CheckPwmPin(_configuration.WhitePwmPin); CheckPwmPin(_configuration.Uv254PwmPin); CheckPwmPin(_configuration.Uv365PwmPin); _gpio = new GpioController(); _whitePwm = PwmChannel.Create(PinPwmChips[_configuration.WhitePwmPin], PinPwmChannels[_configuration.WhitePwmPin], _configuration.PwmFrequency, CalculateDuty(0, _configuration.MaxWhitePwmDuty)); _uv254Pwm = PwmChannel.Create(PinPwmChips[_configuration.Uv254PwmPin], PinPwmChannels[_configuration.Uv254PwmPin], _configuration.PwmFrequency, CalculateDuty(0, _configuration.MaxUv254PwmDuty)); _uv365Pwm = PwmChannel.Create(PinPwmChips[_configuration.Uv365PwmPin], PinPwmChannels[_configuration.Uv365PwmPin], _configuration.PwmFrequency, CalculateDuty(0, _configuration.MaxUv365PwmDuty)); _whiteRelay = _gpio.OpenPin(_configuration.WhiteRelayPin, PinMode.Output, PinValue.Low); _uv254Relay = _gpio.OpenPin(_configuration.Uv254RelayPin, PinMode.Output, PinValue.Low); _uv365Relay = _gpio.OpenPin(_configuration.Uv365RelayPin, PinMode.Output, PinValue.Low); _fanRelay = _gpio.OpenPin(_configuration.FanPin, PinMode.Output, PinValue.Low); } catch (Exception e) { _logger.LogCritical(e, "An exception occurred while initialization"); throw; } _logger.LogInformation("Initialized"); } public void SetIntensity(double? white = null, double? uv365 = null, double? uv254 = null) { try { if (white is not null) _whitePwm.DutyCycle = CalculateDuty(white.Value, _configuration.MaxWhitePwmDuty); if (uv365 is not null) _uv365Pwm.DutyCycle = CalculateDuty(uv365.Value, _configuration.MaxUv365PwmDuty); if (uv254 is not null) _uv254Pwm.DutyCycle = CalculateDuty(uv254.Value, _configuration.MaxUv254PwmDuty); } catch (Exception e) { _logger.LogError(e, "An exception occurred while setting illuminator intensity"); } } public void TurnOn() { try { _fanRelay.Write(PinValue.High); _whitePwm.Start(); _uv254Pwm.Start(); _uv365Pwm.Start(); _whiteRelay.Write(PinValue.High); _uv254Relay.Write(PinValue.High); _uv365Relay.Write(PinValue.High); } catch (Exception e) { _logger.LogError(e, "An exception occurred while turning on illuminator"); } } public void TurnOff() { try { _whitePwm.Stop(); _uv254Pwm.Stop(); _uv365Pwm.Stop(); _whiteRelay.Write(PinValue.Low); _uv254Relay.Write(PinValue.Low); _uv365Relay.Write(PinValue.Low); _fanRelay.Write(PinValue.Low); } catch (Exception e) { _logger.LogError(e, "An exception occurred while turning off illuminator"); } } protected virtual void Dispose(bool disposing) { if (!_disposedValue) { if (disposing) { try { TurnOff(); } catch { } _whiteRelay?.Dispose(); _uv254Relay?.Dispose(); _uv365Relay?.Dispose(); _fanRelay?.Dispose(); _whitePwm?.Dispose(); _uv254Pwm?.Dispose(); _uv365Pwm?.Dispose(); _gpio?.Dispose(); } _disposedValue = true; } } void IDisposable.Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } private static void CheckPwmPin(int pin) { if (!PinPwmChips.TryGetValue(pin, out var _) || !PinPwmChannels.TryGetValue(pin, out var _)) throw new ArgumentException($"Unsupported PWM pin {pin}"); } private static double CalculateDuty(double intensity, double maxDuty) => Math.Clamp(intensity, 0.0, 1.0) * Math.Clamp(maxDuty, 0.0, 1.0); }