feat: Starts manual-d client and adds friction rate calculation and tests.
This commit is contained in:
69
Package.resolved
Normal file
69
Package.resolved
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"originHash" : "426df0aee89a834f20c1c804ecbfbed0bc19ef629c2a1fd2e6260702b97b6f31",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "combine-schedulers",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/pointfreeco/combine-schedulers",
|
||||
"state" : {
|
||||
"revision" : "fd16d76fd8b9a976d88bfb6cacc05ca8d19c91b6",
|
||||
"version" : "1.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "opencombine",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/OpenCombine/OpenCombine.git",
|
||||
"state" : {
|
||||
"revision" : "8576f0d579b27020beccbccc3ea6844f3ddfc2c2",
|
||||
"version" : "0.14.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-clocks",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/pointfreeco/swift-clocks",
|
||||
"state" : {
|
||||
"revision" : "cc46202b53476d64e824e0b6612da09d84ffde8e",
|
||||
"version" : "1.0.6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-concurrency-extras",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/pointfreeco/swift-concurrency-extras",
|
||||
"state" : {
|
||||
"revision" : "5a3825302b1a0d744183200915a47b508c828e6f",
|
||||
"version" : "1.3.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-dependencies",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/pointfreeco/swift-dependencies",
|
||||
"state" : {
|
||||
"revision" : "a10f9feeb214bc72b5337b6ef6d5a029360db4cc",
|
||||
"version" : "1.10.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-syntax",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/swiftlang/swift-syntax",
|
||||
"state" : {
|
||||
"revision" : "4799286537280063c85a32f09884cfbca301b1a1",
|
||||
"version" : "602.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "xctest-dynamic-overlay",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/pointfreeco/xctest-dynamic-overlay",
|
||||
"state" : {
|
||||
"revision" : "31073495cae9caf243c440eac94b3ab067e3d7bc",
|
||||
"version" : "1.8.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 3
|
||||
}
|
||||
@@ -7,6 +7,10 @@ let package = Package(
|
||||
products: [
|
||||
.library(name: "swift-manual-d", targets: ["swift-manual-d"]),
|
||||
.library(name: "ManualDCore", targets: ["ManualDCore"]),
|
||||
.library(name: "ManualDClient", targets: ["ManualDClient"]),
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.0.0")
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
@@ -15,6 +19,18 @@ let package = Package(
|
||||
.target(
|
||||
name: "ManualDCore"
|
||||
),
|
||||
.target(
|
||||
name: "ManualDClient",
|
||||
dependencies: [
|
||||
"ManualDCore",
|
||||
.product(name: "Dependencies", package: "swift-dependencies"),
|
||||
.product(name: "DependenciesMacros", package: "swift-dependencies"),
|
||||
]
|
||||
),
|
||||
.testTarget(
|
||||
name: "ManualDClientTests",
|
||||
dependencies: ["ManualDClient"]
|
||||
),
|
||||
.testTarget(
|
||||
name: "swift-manual-dTests",
|
||||
dependencies: ["swift-manual-d"]
|
||||
|
||||
6
Sources/ManualDClient/Helpers.swift
Normal file
6
Sources/ManualDClient/Helpers.swift
Normal file
@@ -0,0 +1,6 @@
|
||||
import Foundation
|
||||
import ManualDCore
|
||||
|
||||
extension ComponentPressureLosses {
|
||||
var totalLosses: Double { values.reduce(0) { $0 + $1 } }
|
||||
}
|
||||
18
Sources/ManualDClient/Live.swift
Normal file
18
Sources/ManualDClient/Live.swift
Normal file
@@ -0,0 +1,18 @@
|
||||
import Dependencies
|
||||
import ManualDCore
|
||||
|
||||
extension ManualDClient: DependencyKey {
|
||||
public static let liveValue: Self = .init(
|
||||
frictionRate: { request in
|
||||
// Ensure the total effective length is greater than 0.
|
||||
guard request.totalEffectiveLength > 0 else {
|
||||
throw ManualDError(message: "Total Effective Length should be greater than 0.")
|
||||
}
|
||||
|
||||
let totalComponentLosses = request.componentPressureLosses.totalLosses
|
||||
let availableStaticPressure = request.externalStaticPressure - totalComponentLosses
|
||||
let frictionRate = availableStaticPressure * 100.0 / Double(request.totalEffectiveLength)
|
||||
return .init(availableStaticPressure: availableStaticPressure, frictionRate: frictionRate)
|
||||
}
|
||||
)
|
||||
}
|
||||
50
Sources/ManualDClient/ManualDClient.swift
Normal file
50
Sources/ManualDClient/ManualDClient.swift
Normal file
@@ -0,0 +1,50 @@
|
||||
import Dependencies
|
||||
import DependenciesMacros
|
||||
import ManualDCore
|
||||
|
||||
@DependencyClient
|
||||
public struct ManualDClient: Sendable {
|
||||
public var frictionRate: @Sendable (FrictionRateRequest) async throws -> FrictionRateResponse
|
||||
}
|
||||
|
||||
extension ManualDClient: TestDependencyKey {
|
||||
public static let testValue = Self()
|
||||
}
|
||||
|
||||
extension DependencyValues {
|
||||
public var manualD: ManualDClient {
|
||||
get { self[ManualDClient.self] }
|
||||
set { self[ManualDClient.self] = newValue }
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Friction Rate
|
||||
extension ManualDClient {
|
||||
public struct FrictionRateRequest: Codable, Equatable, Sendable {
|
||||
|
||||
public let externalStaticPressure: Double
|
||||
public let componentPressureLosses: ComponentPressureLosses
|
||||
public let totalEffectiveLength: Int
|
||||
|
||||
public init(
|
||||
externalStaticPressure: Double,
|
||||
componentPressureLosses: ComponentPressureLosses,
|
||||
totalEffectiveLength: Int
|
||||
) {
|
||||
self.externalStaticPressure = externalStaticPressure
|
||||
self.componentPressureLosses = componentPressureLosses
|
||||
self.totalEffectiveLength = totalEffectiveLength
|
||||
}
|
||||
}
|
||||
|
||||
public struct FrictionRateResponse: Codable, Equatable, Sendable {
|
||||
|
||||
public let availableStaticPressure: Double
|
||||
public let frictionRate: Double
|
||||
|
||||
public init(availableStaticPressure: Double, frictionRate: Double) {
|
||||
self.availableStaticPressure = availableStaticPressure
|
||||
self.frictionRate = frictionRate
|
||||
}
|
||||
}
|
||||
}
|
||||
5
Sources/ManualDClient/ManualDError.swift
Normal file
5
Sources/ManualDClient/ManualDError.swift
Normal file
@@ -0,0 +1,5 @@
|
||||
import Foundation
|
||||
|
||||
public struct ManualDError: Error {
|
||||
public let message: String
|
||||
}
|
||||
@@ -2,6 +2,16 @@ import Foundation
|
||||
|
||||
public typealias ComponentPressureLosses = [String: Double]
|
||||
|
||||
#if DEBUG
|
||||
extension ComponentPressureLosses {
|
||||
public var totalLosses: Double { values.reduce(0) { $0 + $1 } }
|
||||
public static var mock: Self {
|
||||
[
|
||||
"evaporator-coil": 0.2,
|
||||
"filter": 0.1,
|
||||
"supply-outlet": 0.03,
|
||||
"return-grille": 0.03,
|
||||
"balancing-damper": 0.03,
|
||||
]
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Foundation
|
||||
|
||||
public struct CoolingLoad: Codable, Equatable {
|
||||
public struct CoolingLoad: Codable, Equatable, Sendable {
|
||||
public let total: Double
|
||||
public let sensible: Double
|
||||
public var latent: Double { total - sensible }
|
||||
|
||||
@@ -2,7 +2,7 @@ import Foundation
|
||||
|
||||
// TODO: Add other description / label for items that have same group & letter, but
|
||||
// different effective length.
|
||||
public struct EffectiveLengthGroup: Codable, Equatable {
|
||||
public struct EffectiveLengthGroup: Codable, Equatable, Sendable {
|
||||
public let group: Int
|
||||
public let letter: String
|
||||
public let effectiveLength: Int
|
||||
@@ -24,7 +24,7 @@ public struct EffectiveLengthGroup: Codable, Equatable {
|
||||
|
||||
extension EffectiveLengthGroup {
|
||||
|
||||
public enum Category: String, Codable, Equatable {
|
||||
public enum Category: String, Codable, Equatable, Sendable {
|
||||
case any
|
||||
case supply
|
||||
case `return`
|
||||
@@ -32,7 +32,7 @@ extension EffectiveLengthGroup {
|
||||
|
||||
}
|
||||
|
||||
public let effectiveLengthsLookup: [String: EffectiveLengthGroup] {
|
||||
public let effectiveLengthsLookup: [String: EffectiveLengthGroup] = {
|
||||
[
|
||||
"1a": .init(group: 1, letter: "a", effectiveLength: 35, category: .supply),
|
||||
"1b": .init(group: 1, letter: "b", effectiveLength: 10, category: .supply),
|
||||
@@ -625,4 +625,4 @@ public let effectiveLengthsLookup: [String: EffectiveLengthGroup] {
|
||||
"12u": .init(group: 12, letter: "u", effectiveLength: 25, category: .any),
|
||||
"12v": .init(group: 12, letter: "v", effectiveLength: 30, category: .any),
|
||||
]
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Foundation
|
||||
|
||||
public struct Room: Codable, Equatable {
|
||||
public struct Room: Codable, Equatable, Sendable {
|
||||
public let name: String
|
||||
public let heatingLoad: Double
|
||||
public let coolingLoad: CoolingLoad
|
||||
|
||||
45
Tests/ManualDClientTests/ManualDClientTests.swift
Normal file
45
Tests/ManualDClientTests/ManualDClientTests.swift
Normal file
@@ -0,0 +1,45 @@
|
||||
import Dependencies
|
||||
import Foundation
|
||||
import ManualDClient
|
||||
import ManualDCore
|
||||
import Testing
|
||||
|
||||
@Suite("ManualDClient Tests")
|
||||
struct ManualDClientTests {
|
||||
|
||||
var numberFormatter: NumberFormatter {
|
||||
let formatter = NumberFormatter()
|
||||
formatter.minimumFractionDigits = 2
|
||||
formatter.maximumFractionDigits = 2
|
||||
formatter.roundingMode = .halfUp
|
||||
return formatter
|
||||
}
|
||||
|
||||
@Test
|
||||
func frictionRate() async throws {
|
||||
let manualD = ManualDClient.liveValue
|
||||
let response = try await manualD.frictionRate(
|
||||
.init(
|
||||
externalStaticPressure: 0.5,
|
||||
componentPressureLosses: .mock,
|
||||
totalEffectiveLength: 185
|
||||
)
|
||||
)
|
||||
#expect(numberFormatter.string(for: response.availableStaticPressure) == "0.11")
|
||||
#expect(numberFormatter.string(for: response.frictionRate) == "0.06")
|
||||
}
|
||||
|
||||
@Test
|
||||
func frictionRateFails() async throws {
|
||||
await #expect(throws: ManualDError.self) {
|
||||
let manualD = ManualDClient.liveValue
|
||||
_ = try await manualD.frictionRate(
|
||||
.init(
|
||||
externalStaticPressure: 0.5,
|
||||
componentPressureLosses: .mock,
|
||||
totalEffectiveLength: 0
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user