import Foundation /// The required functions to implement a reader that provides derived variables protocol GenericReaderDerived: GenericReaderProtocol { associatedtype Derived: RawRepresentableString associatedtype ReaderNext: GenericReaderProtocol var reader: ReaderNext { get } func get(derived: Derived, time: TimerangeDtAndSettings) throws -> DataAndUnit func prefetchData(derived: Derived, time: TimerangeDtAndSettings) throws func get(raw: ReaderNext.MixingVar, time: TimerangeDtAndSettings) throws -> DataAndUnit func prefetchData(raw: ReaderNext.MixingVar, time: TimerangeDtAndSettings) throws } /// Parameters for tilted radiation calculation struct GenericReaderOptions { /// Tilt of a solar panel for GTI calculation. 0° horizontal, 90° vertical. private var tilt: Float /// Azimuth of a solar panel for GTI calculation. 0° south, -90° east, 90° west private var azimuth: Float public init(tilt: Float? = nil, azimuth: Float? = nil) { self.tilt = tilt ?? 0 self.azimuth = azimuth ?? 0 } /// Tilt of a solar panel for GTI calculation. 0° horizontal, 90° vertical. Throws out of bounds error. func getTilt() throws -> Float { guard tilt.isNaN || (tilt >= 0 && tilt <= 90) else { throw ForecastapiError.generic(message: "Parameter `&tilt=` must be within 0° and 90°") } return tilt } /// Azimuth of a solar panel for GTI calculation. 0° south, -90° east, 90° west. Throws out of bounds error. func getAzimuth() throws -> Float { guard azimuth.isNaN || (azimuth >= -180 && azimuth <= 180) else { throw ForecastapiError.generic(message: "Parameter `&azimuth=` must be within -180° and 180°") } return azimuth } } extension GenericReaderDerived { var modelLat: Float { reader.modelLat } /*var domain: ReaderNext.Domain { return reader.domain }*/ var modelLon: Float { reader.modelLon } var modelElevation: ElevationOrSea { reader.modelElevation } var modelDtSeconds: Int { reader.modelDtSeconds } var targetElevation: Float { reader.targetElevation } func prefetchData(variable: VariableOrDerived, time: TimerangeDtAndSettings) throws { switch variable { case .raw(let raw): return try prefetchData(raw: raw, time: time) case .derived(let derived): return try prefetchData(derived: derived, time: time) } } func get(variable: VariableOrDerived, time: TimerangeDtAndSettings) throws -> DataAndUnit { switch variable { case .raw(let raw): return try get(raw: raw, time: time) case .derived(let derived): return try get(derived: derived, time: time) } } func getStatic(type: ReaderStaticVariable) throws -> Float? { return try reader.getStatic(type: type) } func prefetchData(variables: [VariableOrDerived], time: TimerangeDtAndSettings) throws { try variables.forEach { variable in try prefetchData(variable: variable, time: time) } } } /// A reader that does not modify reader. E.g. pass all reads directly to reader protocol GenericReaderDerivedSimple: GenericReaderDerived { } extension GenericReaderDerivedSimple { func get(raw: ReaderNext.MixingVar, time: TimerangeDtAndSettings) throws -> DataAndUnit { try reader.get(variable: raw, time: time) } func prefetchData(raw: ReaderNext.MixingVar, time: TimerangeDtAndSettings) throws { try reader.prefetchData(variable: raw, time: time) } }