import ArgumentParser import CliClient import Dependencies import Foundation import Logging import Models import MQTTConnectionService import MQTTManager import MQTTNIO import NIO import PsychrometricClientLive import SensorsService import ServiceLifecycle extension Application { /// Run the controller. /// struct Run: AsyncParsableCommand { static let configuration = CommandConfiguration( commandName: "run", abstract: "Run the controller." ) @OptionGroup var shared: SharedOptions mutating func run() async throws { @Dependency(\.cliClient) var cliClient let eventloopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) var logger = Logger(label: "dewpoint-controller") let mqtt = try await setup(eventLoopGroup: eventloopGroup, logger: &logger) do { try await withDependencies { $0.psychrometricClient = .liveValue $0.mqtt = .live(client: mqtt, logger: logger) } operation: { let mqttConnection = MQTTConnectionService(logger: logger) let sensors = SensorsService(sensors: .live, logger: logger) var serviceGroupConfiguration = ServiceGroupConfiguration( services: [ mqttConnection, sensors ], gracefulShutdownSignals: [.sigterm, .sigint], logger: logger ) serviceGroupConfiguration.maximumCancellationDuration = .seconds(5) serviceGroupConfiguration.maximumGracefulShutdownDuration = .seconds(10) let serviceGroup = ServiceGroup(configuration: serviceGroupConfiguration) logger.info("Starting dewpoint-controller!") try await serviceGroup.run() } try await mqtt.shutdown() try await eventloopGroup.shutdownGracefully() } catch { try await eventloopGroup.shutdownGracefully() } } private func setup( eventLoopGroup: MultiThreadedEventLoopGroup, logger: inout Logger ) async throws -> MQTTClient { @Dependency(\.cliClient) var cliClient let environment = try await cliClient.makeEnvVars(shared.envVarsRequest(logger: logger)) logger.logLevel = cliClient.logLevel(environment) return try cliClient.makeClient(.init( envVars: environment, eventLoopGroup: eventLoopGroup, logger: logger )) } } } // MARK: - Helpers