Compare commits
3 Commits
0.1.0
...
dbec7fb920
| Author | SHA1 | Date | |
|---|---|---|---|
|
dbec7fb920
|
|||
|
6b8cb73434
|
|||
|
c6a29313aa
|
@@ -8,6 +8,7 @@ let package = Package(
|
|||||||
.executable(name: "App", targets: ["App"]),
|
.executable(name: "App", targets: ["App"]),
|
||||||
.library(name: "ApiController", targets: ["ApiController"]),
|
.library(name: "ApiController", targets: ["ApiController"]),
|
||||||
.library(name: "DatabaseClient", targets: ["DatabaseClient"]),
|
.library(name: "DatabaseClient", targets: ["DatabaseClient"]),
|
||||||
|
.library(name: "ProjectClient", targets: ["ProjectClient"]),
|
||||||
.library(name: "ManualDCore", targets: ["ManualDCore"]),
|
.library(name: "ManualDCore", targets: ["ManualDCore"]),
|
||||||
.library(name: "ManualDClient", targets: ["ManualDClient"]),
|
.library(name: "ManualDClient", targets: ["ManualDClient"]),
|
||||||
.library(name: "Styleguide", targets: ["Styleguide"]),
|
.library(name: "Styleguide", targets: ["Styleguide"]),
|
||||||
@@ -63,6 +64,13 @@ let package = Package(
|
|||||||
.product(name: "Vapor", package: "vapor"),
|
.product(name: "Vapor", package: "vapor"),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
.target(
|
||||||
|
name: "ProjectClient",
|
||||||
|
dependencies: [
|
||||||
|
.target(name: "DatabaseClient"),
|
||||||
|
.target(name: "ManualDClient"),
|
||||||
|
]
|
||||||
|
),
|
||||||
.target(
|
.target(
|
||||||
name: "ManualDCore",
|
name: "ManualDCore",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
@@ -105,6 +113,7 @@ let package = Package(
|
|||||||
name: "ViewController",
|
name: "ViewController",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.target(name: "DatabaseClient"),
|
.target(name: "DatabaseClient"),
|
||||||
|
.target(name: "ProjectClient"),
|
||||||
.target(name: "ManualDClient"),
|
.target(name: "ManualDClient"),
|
||||||
.target(name: "ManualDCore"),
|
.target(name: "ManualDCore"),
|
||||||
.target(name: "Styleguide"),
|
.target(name: "Styleguide"),
|
||||||
|
|||||||
@@ -4224,9 +4224,6 @@
|
|||||||
.top-2 {
|
.top-2 {
|
||||||
top: calc(var(--spacing) * 2);
|
top: calc(var(--spacing) * 2);
|
||||||
}
|
}
|
||||||
.top-\[100vh\] {
|
|
||||||
top: 100vh;
|
|
||||||
}
|
|
||||||
.right-2 {
|
.right-2 {
|
||||||
right: calc(var(--spacing) * 2);
|
right: calc(var(--spacing) * 2);
|
||||||
}
|
}
|
||||||
@@ -4807,18 +4804,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.col-span-1 {
|
|
||||||
grid-column: span 1 / span 1;
|
|
||||||
}
|
|
||||||
.col-span-2 {
|
.col-span-2 {
|
||||||
grid-column: span 2 / span 2;
|
grid-column: span 2 / span 2;
|
||||||
}
|
}
|
||||||
.col-span-3 {
|
.col-span-3 {
|
||||||
grid-column: span 3 / span 3;
|
grid-column: span 3 / span 3;
|
||||||
}
|
}
|
||||||
.col-span-5 {
|
|
||||||
grid-column: span 5 / span 5;
|
|
||||||
}
|
|
||||||
.timeline-end {
|
.timeline-end {
|
||||||
@layer daisyui.l1.l2.l3 {
|
@layer daisyui.l1.l2.l3 {
|
||||||
grid-column-start: 1;
|
grid-column-start: 1;
|
||||||
@@ -5288,12 +5279,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.mx-2 {
|
|
||||||
margin-inline: calc(var(--spacing) * 2);
|
|
||||||
}
|
|
||||||
.mx-4 {
|
|
||||||
margin-inline: calc(var(--spacing) * 4);
|
|
||||||
}
|
|
||||||
.mx-auto {
|
.mx-auto {
|
||||||
margin-inline: auto;
|
margin-inline: auto;
|
||||||
}
|
}
|
||||||
@@ -5383,15 +5368,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.my-1 {
|
|
||||||
margin-block: calc(var(--spacing) * 1);
|
|
||||||
}
|
|
||||||
.my-1\.5 {
|
.my-1\.5 {
|
||||||
margin-block: calc(var(--spacing) * 1.5);
|
margin-block: calc(var(--spacing) * 1.5);
|
||||||
}
|
}
|
||||||
.my-4 {
|
|
||||||
margin-block: calc(var(--spacing) * 4);
|
|
||||||
}
|
|
||||||
.my-6 {
|
.my-6 {
|
||||||
margin-block: calc(var(--spacing) * 6);
|
margin-block: calc(var(--spacing) * 6);
|
||||||
}
|
}
|
||||||
@@ -5661,9 +5640,6 @@
|
|||||||
.mt-6 {
|
.mt-6 {
|
||||||
margin-top: calc(var(--spacing) * 6);
|
margin-top: calc(var(--spacing) * 6);
|
||||||
}
|
}
|
||||||
.mt-auto {
|
|
||||||
margin-top: auto;
|
|
||||||
}
|
|
||||||
.breadcrumbs {
|
.breadcrumbs {
|
||||||
@layer daisyui.l1.l2.l3 {
|
@layer daisyui.l1.l2.l3 {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
@@ -5740,9 +5716,6 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.mb-2 {
|
|
||||||
margin-bottom: calc(var(--spacing) * 2);
|
|
||||||
}
|
|
||||||
.mb-4 {
|
.mb-4 {
|
||||||
margin-bottom: calc(var(--spacing) * 4);
|
margin-bottom: calc(var(--spacing) * 4);
|
||||||
}
|
}
|
||||||
@@ -6473,12 +6446,6 @@
|
|||||||
.h-full {
|
.h-full {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
.h-screen {
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
.min-h-full {
|
|
||||||
min-height: 100%;
|
|
||||||
}
|
|
||||||
.min-h-screen {
|
.min-h-screen {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
@@ -6610,12 +6577,6 @@
|
|||||||
width: calc(var(--size-selector, 0.25rem) * 4);
|
width: calc(var(--size-selector, 0.25rem) * 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.w-\[300px\] {
|
|
||||||
width: 300px;
|
|
||||||
}
|
|
||||||
.w-\[310px\] {
|
|
||||||
width: 310px;
|
|
||||||
}
|
|
||||||
.w-\[330px\] {
|
.w-\[330px\] {
|
||||||
width: 330px;
|
width: 330px;
|
||||||
}
|
}
|
||||||
@@ -6625,18 +6586,12 @@
|
|||||||
.w-full {
|
.w-full {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.min-w-\[100px\] {
|
|
||||||
min-width: 100px;
|
|
||||||
}
|
|
||||||
.min-w-\[200px\] {
|
.min-w-\[200px\] {
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
}
|
}
|
||||||
.min-w-\[220px\] {
|
.min-w-\[220px\] {
|
||||||
min-width: 220px;
|
min-width: 220px;
|
||||||
}
|
}
|
||||||
.min-w-\[300px\] {
|
|
||||||
min-width: 300px;
|
|
||||||
}
|
|
||||||
.min-w-full {
|
.min-w-full {
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
}
|
}
|
||||||
@@ -6839,15 +6794,9 @@
|
|||||||
.grid-cols-3 {
|
.grid-cols-3 {
|
||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
.grid-cols-5 {
|
|
||||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
.flex-col {
|
.flex-col {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
.flex-row {
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
.flex-wrap {
|
.flex-wrap {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
@@ -6872,9 +6821,6 @@
|
|||||||
.justify-start {
|
.justify-start {
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
.justify-items-end {
|
|
||||||
justify-items: end;
|
|
||||||
}
|
|
||||||
.gap-1 {
|
.gap-1 {
|
||||||
gap: calc(var(--spacing) * 1);
|
gap: calc(var(--spacing) * 1);
|
||||||
}
|
}
|
||||||
@@ -6884,9 +6830,6 @@
|
|||||||
.gap-4 {
|
.gap-4 {
|
||||||
gap: calc(var(--spacing) * 4);
|
gap: calc(var(--spacing) * 4);
|
||||||
}
|
}
|
||||||
.gap-6 {
|
|
||||||
gap: calc(var(--spacing) * 6);
|
|
||||||
}
|
|
||||||
.space-y-1 {
|
.space-y-1 {
|
||||||
:where(& > :not(:last-child)) {
|
:where(& > :not(:last-child)) {
|
||||||
--tw-space-y-reverse: 0;
|
--tw-space-y-reverse: 0;
|
||||||
@@ -6936,9 +6879,6 @@
|
|||||||
margin-inline-end: calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-x-reverse)));
|
margin-inline-end: calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-x-reverse)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.gap-y-2 {
|
|
||||||
row-gap: calc(var(--spacing) * 2);
|
|
||||||
}
|
|
||||||
.gap-y-4 {
|
.gap-y-4 {
|
||||||
row-gap: calc(var(--spacing) * 4);
|
row-gap: calc(var(--spacing) * 4);
|
||||||
}
|
}
|
||||||
@@ -7230,10 +7170,6 @@
|
|||||||
border-style: var(--tw-border-style);
|
border-style: var(--tw-border-style);
|
||||||
border-width: 2px;
|
border-width: 2px;
|
||||||
}
|
}
|
||||||
.border-b {
|
|
||||||
border-bottom-style: var(--tw-border-style);
|
|
||||||
border-bottom-width: 1px;
|
|
||||||
}
|
|
||||||
.border-b-1 {
|
.border-b-1 {
|
||||||
border-bottom-style: var(--tw-border-style);
|
border-bottom-style: var(--tw-border-style);
|
||||||
border-bottom-width: 1px;
|
border-bottom-width: 1px;
|
||||||
@@ -7507,9 +7443,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.bg-base-100 {
|
|
||||||
background-color: var(--color-base-100);
|
|
||||||
}
|
|
||||||
.bg-base-300 {
|
.bg-base-300 {
|
||||||
background-color: var(--color-base-300);
|
background-color: var(--color-base-300);
|
||||||
}
|
}
|
||||||
@@ -7939,9 +7872,6 @@
|
|||||||
.px-4 {
|
.px-4 {
|
||||||
padding-inline: calc(var(--spacing) * 4);
|
padding-inline: calc(var(--spacing) * 4);
|
||||||
}
|
}
|
||||||
.py-1 {
|
|
||||||
padding-block: calc(var(--spacing) * 1);
|
|
||||||
}
|
|
||||||
.py-1\.5 {
|
.py-1\.5 {
|
||||||
padding-block: calc(var(--spacing) * 1.5);
|
padding-block: calc(var(--spacing) * 1.5);
|
||||||
}
|
}
|
||||||
@@ -7968,9 +7898,6 @@
|
|||||||
.pt-2 {
|
.pt-2 {
|
||||||
padding-top: calc(var(--spacing) * 2);
|
padding-top: calc(var(--spacing) * 2);
|
||||||
}
|
}
|
||||||
.pt-6 {
|
|
||||||
padding-top: calc(var(--spacing) * 6);
|
|
||||||
}
|
|
||||||
.pb-6 {
|
.pb-6 {
|
||||||
padding-bottom: calc(var(--spacing) * 6);
|
padding-bottom: calc(var(--spacing) * 6);
|
||||||
}
|
}
|
||||||
@@ -8603,9 +8530,6 @@
|
|||||||
.text-info {
|
.text-info {
|
||||||
color: var(--color-info);
|
color: var(--color-info);
|
||||||
}
|
}
|
||||||
.text-primary {
|
|
||||||
color: var(--color-primary);
|
|
||||||
}
|
|
||||||
.text-slate-900 {
|
.text-slate-900 {
|
||||||
color: var(--color-slate-900);
|
color: var(--color-slate-900);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,10 +73,16 @@ extension DatabaseClient.Projects: TestDependencyKey {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
getSensibleHeatRatio: { id in
|
getSensibleHeatRatio: { id in
|
||||||
guard let model = try await ProjectModel.find(id, on: database) else {
|
guard
|
||||||
|
let shr = try await ProjectModel.query(on: database)
|
||||||
|
.field(\.$id)
|
||||||
|
.field(\.$sensibleHeatRatio)
|
||||||
|
.filter(\.$id == id)
|
||||||
|
.first()
|
||||||
|
else {
|
||||||
throw NotFoundError()
|
throw NotFoundError()
|
||||||
}
|
}
|
||||||
return model.sensibleHeatRatio
|
return shr.sensibleHeatRatio
|
||||||
},
|
},
|
||||||
fetch: { userID, request in
|
fetch: { userID, request in
|
||||||
try await ProjectModel.query(on: database)
|
try await ProjectModel.query(on: database)
|
||||||
|
|||||||
@@ -3,6 +3,13 @@ import DependenciesMacros
|
|||||||
import Logging
|
import Logging
|
||||||
import ManualDCore
|
import ManualDCore
|
||||||
|
|
||||||
|
extension DependencyValues {
|
||||||
|
public var manualD: ManualDClient {
|
||||||
|
get { self[ManualDClient.self] }
|
||||||
|
set { self[ManualDClient.self] = newValue }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@DependencyClient
|
@DependencyClient
|
||||||
public struct ManualDClient: Sendable {
|
public struct ManualDClient: Sendable {
|
||||||
public var ductSize: @Sendable (DuctSizeRequest) async throws -> DuctSizeResponse
|
public var ductSize: @Sendable (DuctSizeRequest) async throws -> DuctSizeResponse
|
||||||
@@ -11,144 +18,155 @@ public struct ManualDClient: Sendable {
|
|||||||
public var equivalentRectangularDuct:
|
public var equivalentRectangularDuct:
|
||||||
@Sendable (EquivalentRectangularDuctRequest) async throws -> EquivalentRectangularDuctResponse
|
@Sendable (EquivalentRectangularDuctRequest) async throws -> EquivalentRectangularDuctResponse
|
||||||
|
|
||||||
public func calculateSizes(
|
// TODO: add (Project.ID) async throws -> ProjectResponse
|
||||||
rooms: [Room],
|
|
||||||
trunks: [DuctSizing.TrunkSize],
|
|
||||||
equipmentInfo: EquipmentInfo,
|
|
||||||
maxSupplyLength: EffectiveLength,
|
|
||||||
maxReturnLength: EffectiveLength,
|
|
||||||
designFrictionRate: Double,
|
|
||||||
projectSHR: Double,
|
|
||||||
logger: Logger? = nil
|
|
||||||
) async throws -> (rooms: [DuctSizing.RoomContainer], trunks: [DuctSizing.TrunkContainer]) {
|
|
||||||
try await (
|
|
||||||
calculateSizes(
|
|
||||||
rooms: rooms, equipmentInfo: equipmentInfo,
|
|
||||||
maxSupplyLength: maxSupplyLength, maxReturnLength: maxReturnLength,
|
|
||||||
designFrictionRate: designFrictionRate, projectSHR: projectSHR
|
|
||||||
),
|
|
||||||
calculateSizes(
|
|
||||||
rooms: rooms, trunks: trunks, equipmentInfo: equipmentInfo,
|
|
||||||
maxSupplyLength: maxSupplyLength, maxReturnLength: maxReturnLength,
|
|
||||||
designFrictionRate: designFrictionRate, projectSHR: projectSHR)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func calculateSizes(
|
// TODO: Move to helpers.
|
||||||
rooms: [Room],
|
// public func calculateSizes(
|
||||||
equipmentInfo: EquipmentInfo,
|
// rooms: [Room],
|
||||||
maxSupplyLength: EffectiveLength,
|
// trunks: [DuctSizing.TrunkSize],
|
||||||
maxReturnLength: EffectiveLength,
|
// equipmentInfo: EquipmentInfo,
|
||||||
designFrictionRate: Double,
|
// maxSupplyLength: EffectiveLength,
|
||||||
projectSHR: Double,
|
// maxReturnLength: EffectiveLength,
|
||||||
logger: Logger? = nil
|
// designFrictionRate: Double,
|
||||||
) async throws -> [DuctSizing.RoomContainer] {
|
// projectSHR: Double,
|
||||||
|
// logger: Logger? = nil
|
||||||
var retval: [DuctSizing.RoomContainer] = []
|
// ) async throws -> ProjectResponse {
|
||||||
let totalHeatingLoad = rooms.totalHeatingLoad
|
// try await .init(
|
||||||
let totalCoolingSensible = rooms.totalCoolingSensible(shr: projectSHR)
|
// rooms: calculateRoomSizes(
|
||||||
|
// rooms: rooms,
|
||||||
for room in rooms {
|
// equipmentInfo: equipmentInfo,
|
||||||
let heatingLoad = room.heatingLoadPerRegister
|
// maxSupplyLength: maxSupplyLength,
|
||||||
let coolingLoad = room.coolingSensiblePerRegister(projectSHR: projectSHR)
|
// maxReturnLength: maxReturnLength,
|
||||||
let heatingPercent = heatingLoad / totalHeatingLoad
|
// designFrictionRate: designFrictionRate,
|
||||||
let coolingPercent = coolingLoad / totalCoolingSensible
|
// projectSHR: projectSHR
|
||||||
let heatingCFM = heatingPercent * Double(equipmentInfo.heatingCFM)
|
// ),
|
||||||
let coolingCFM = coolingPercent * Double(equipmentInfo.coolingCFM)
|
// trunks: calculateTrunkSizes(
|
||||||
let designCFM = DuctSizing.DesignCFM(heating: heatingCFM, cooling: coolingCFM)
|
// rooms: rooms,
|
||||||
let sizes = try await self.ductSize(
|
// trunks: trunks,
|
||||||
.init(designCFM: Int(designCFM.value), frictionRate: designFrictionRate)
|
// equipmentInfo: equipmentInfo,
|
||||||
)
|
// maxSupplyLength: maxSupplyLength,
|
||||||
|
// maxReturnLength: maxReturnLength,
|
||||||
for n in 1...room.registerCount {
|
// designFrictionRate: designFrictionRate,
|
||||||
|
// projectSHR: projectSHR
|
||||||
var rectangularWidth: Int? = nil
|
// )
|
||||||
let rectangularSize = room.rectangularSizes?
|
// )
|
||||||
.first(where: { $0.register == nil || $0.register == n })
|
// }
|
||||||
|
//
|
||||||
if let rectangularSize {
|
// func calculateRoomSizes(
|
||||||
let response = try await self.equivalentRectangularDuct(
|
// rooms: [Room],
|
||||||
.init(round: sizes.finalSize, height: rectangularSize.height)
|
// equipmentInfo: EquipmentInfo,
|
||||||
)
|
// maxSupplyLength: EffectiveLength,
|
||||||
rectangularWidth = response.width
|
// maxReturnLength: EffectiveLength,
|
||||||
}
|
// designFrictionRate: Double,
|
||||||
|
// projectSHR: Double,
|
||||||
retval.append(
|
// logger: Logger? = nil
|
||||||
.init(
|
// ) async throws -> [DuctSizing.RoomContainer] {
|
||||||
roomID: room.id,
|
//
|
||||||
roomName: "\(room.name)-\(n)",
|
// var retval: [DuctSizing.RoomContainer] = []
|
||||||
roomRegister: n,
|
// let totalHeatingLoad = rooms.totalHeatingLoad
|
||||||
heatingLoad: heatingLoad,
|
// let totalCoolingSensible = rooms.totalCoolingSensible(shr: projectSHR)
|
||||||
coolingLoad: coolingLoad,
|
//
|
||||||
heatingCFM: heatingCFM,
|
// for room in rooms {
|
||||||
coolingCFM: coolingCFM,
|
// let heatingLoad = room.heatingLoadPerRegister
|
||||||
designCFM: designCFM,
|
// let coolingLoad = room.coolingSensiblePerRegister(projectSHR: projectSHR)
|
||||||
roundSize: sizes.ductulatorSize,
|
// let heatingPercent = heatingLoad / totalHeatingLoad
|
||||||
finalSize: sizes.finalSize,
|
// let coolingPercent = coolingLoad / totalCoolingSensible
|
||||||
velocity: sizes.velocity,
|
// let heatingCFM = heatingPercent * Double(equipmentInfo.heatingCFM)
|
||||||
flexSize: sizes.flexSize,
|
// let coolingCFM = coolingPercent * Double(equipmentInfo.coolingCFM)
|
||||||
rectangularSize: rectangularSize,
|
// let designCFM = DuctSizing.DesignCFM(heating: heatingCFM, cooling: coolingCFM)
|
||||||
rectangularWidth: rectangularWidth
|
// let sizes = try await self.ductSize(
|
||||||
)
|
// .init(designCFM: Int(designCFM.value), frictionRate: designFrictionRate)
|
||||||
)
|
// )
|
||||||
}
|
//
|
||||||
}
|
// for n in 1...room.registerCount {
|
||||||
|
//
|
||||||
return retval
|
// var rectangularWidth: Int? = nil
|
||||||
}
|
// let rectangularSize = room.rectangularSizes?
|
||||||
|
// .first(where: { $0.register == nil || $0.register == n })
|
||||||
func calculateSizes(
|
//
|
||||||
rooms: [Room],
|
// if let rectangularSize {
|
||||||
trunks: [DuctSizing.TrunkSize],
|
// let response = try await self.equivalentRectangularDuct(
|
||||||
equipmentInfo: EquipmentInfo,
|
// .init(round: sizes.finalSize, height: rectangularSize.height)
|
||||||
maxSupplyLength: EffectiveLength,
|
// )
|
||||||
maxReturnLength: EffectiveLength,
|
// rectangularWidth = response.width
|
||||||
designFrictionRate: Double,
|
// }
|
||||||
projectSHR: Double,
|
//
|
||||||
logger: Logger? = nil
|
// retval.append(
|
||||||
) async throws -> [DuctSizing.TrunkContainer] {
|
// .init(
|
||||||
|
// roomID: room.id,
|
||||||
var retval = [DuctSizing.TrunkContainer]()
|
// roomName: "\(room.name)-\(n)",
|
||||||
let totalHeatingLoad = rooms.totalHeatingLoad
|
// roomRegister: n,
|
||||||
let totalCoolingSensible = rooms.totalCoolingSensible(shr: projectSHR)
|
// heatingLoad: heatingLoad,
|
||||||
|
// coolingLoad: coolingLoad,
|
||||||
for trunk in trunks {
|
// heatingCFM: heatingCFM,
|
||||||
let heatingLoad = trunk.totalHeatingLoad
|
// coolingCFM: coolingCFM,
|
||||||
let coolingLoad = trunk.totalCoolingSensible(projectSHR: projectSHR)
|
// designCFM: designCFM,
|
||||||
let heatingPercent = heatingLoad / totalHeatingLoad
|
// roundSize: sizes.ductulatorSize,
|
||||||
let coolingPercent = coolingLoad / totalCoolingSensible
|
// finalSize: sizes.finalSize,
|
||||||
let heatingCFM = heatingPercent * Double(equipmentInfo.heatingCFM)
|
// velocity: sizes.velocity,
|
||||||
let coolingCFM = coolingPercent * Double(equipmentInfo.coolingCFM)
|
// flexSize: sizes.flexSize,
|
||||||
let designCFM = DuctSizing.DesignCFM(heating: heatingCFM, cooling: coolingCFM)
|
// rectangularSize: rectangularSize,
|
||||||
let sizes = try await self.ductSize(
|
// rectangularWidth: rectangularWidth
|
||||||
.init(designCFM: Int(designCFM.value), frictionRate: designFrictionRate)
|
// )
|
||||||
)
|
// )
|
||||||
var width: Int? = nil
|
// }
|
||||||
if let height = trunk.height {
|
// }
|
||||||
let rectangularSize = try await self.equivalentRectangularDuct(
|
//
|
||||||
.init(round: sizes.finalSize, height: height)
|
// return retval
|
||||||
)
|
// }
|
||||||
width = rectangularSize.width
|
//
|
||||||
}
|
// func calculateTrunkSizes(
|
||||||
|
// rooms: [Room],
|
||||||
retval.append(
|
// trunks: [DuctSizing.TrunkSize],
|
||||||
.init(
|
// equipmentInfo: EquipmentInfo,
|
||||||
trunk: trunk,
|
// maxSupplyLength: EffectiveLength,
|
||||||
ductSize: .init(
|
// maxReturnLength: EffectiveLength,
|
||||||
designCFM: designCFM,
|
// designFrictionRate: Double,
|
||||||
roundSize: sizes.ductulatorSize,
|
// projectSHR: Double,
|
||||||
finalSize: sizes.finalSize,
|
// logger: Logger? = nil
|
||||||
velocity: sizes.velocity,
|
// ) async throws -> [DuctSizing.TrunkContainer] {
|
||||||
flexSize: sizes.flexSize,
|
//
|
||||||
height: trunk.height,
|
// var retval = [DuctSizing.TrunkContainer]()
|
||||||
width: width
|
// let totalHeatingLoad = rooms.totalHeatingLoad
|
||||||
)
|
// let totalCoolingSensible = rooms.totalCoolingSensible(shr: projectSHR)
|
||||||
)
|
//
|
||||||
)
|
// for trunk in trunks {
|
||||||
}
|
// let heatingLoad = trunk.totalHeatingLoad
|
||||||
|
// let coolingLoad = trunk.totalCoolingSensible(projectSHR: projectSHR)
|
||||||
return retval
|
// let heatingPercent = heatingLoad / totalHeatingLoad
|
||||||
}
|
// let coolingPercent = coolingLoad / totalCoolingSensible
|
||||||
|
// let heatingCFM = heatingPercent * Double(equipmentInfo.heatingCFM)
|
||||||
|
// let coolingCFM = coolingPercent * Double(equipmentInfo.coolingCFM)
|
||||||
|
// let designCFM = DuctSizing.DesignCFM(heating: heatingCFM, cooling: coolingCFM)
|
||||||
|
// let sizes = try await self.ductSize(
|
||||||
|
// .init(designCFM: Int(designCFM.value), frictionRate: designFrictionRate)
|
||||||
|
// )
|
||||||
|
// var width: Int? = nil
|
||||||
|
// if let height = trunk.height {
|
||||||
|
// let rectangularSize = try await self.equivalentRectangularDuct(
|
||||||
|
// .init(round: sizes.finalSize, height: height)
|
||||||
|
// )
|
||||||
|
// width = rectangularSize.width
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// retval.append(
|
||||||
|
// .init(
|
||||||
|
// trunk: trunk,
|
||||||
|
// ductSize: .init(
|
||||||
|
// designCFM: designCFM,
|
||||||
|
// roundSize: sizes.ductulatorSize,
|
||||||
|
// finalSize: sizes.finalSize,
|
||||||
|
// velocity: sizes.velocity,
|
||||||
|
// flexSize: sizes.flexSize,
|
||||||
|
// height: trunk.height,
|
||||||
|
// width: width
|
||||||
|
// )
|
||||||
|
// )
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return retval
|
||||||
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,15 +174,27 @@ extension ManualDClient: TestDependencyKey {
|
|||||||
public static let testValue = Self()
|
public static let testValue = Self()
|
||||||
}
|
}
|
||||||
|
|
||||||
extension DependencyValues {
|
// MARK: Project Response
|
||||||
public var manualD: ManualDClient {
|
// extension ManualDClient {
|
||||||
get { self[ManualDClient.self] }
|
//
|
||||||
set { self[ManualDClient.self] = newValue }
|
// public struct ProjectResponse: Codable, Equatable, Sendable {
|
||||||
}
|
// public let rooms: [DuctSizing.RoomContainer]
|
||||||
}
|
// public let trunks: [DuctSizing.TrunkContainer]
|
||||||
|
//
|
||||||
|
// public init(
|
||||||
|
// rooms: [DuctSizing.RoomContainer],
|
||||||
|
// trunks: [DuctSizing.TrunkContainer]
|
||||||
|
// ) {
|
||||||
|
// self.rooms = rooms
|
||||||
|
// self.trunks = trunks
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
// MARK: Duct Size
|
// MARK: Duct Size
|
||||||
extension ManualDClient {
|
extension ManualDClient {
|
||||||
|
|
||||||
public struct DuctSizeRequest: Codable, Equatable, Sendable {
|
public struct DuctSizeRequest: Codable, Equatable, Sendable {
|
||||||
public let designCFM: Int
|
public let designCFM: Int
|
||||||
public let frictionRate: Double
|
public let frictionRate: Double
|
||||||
|
|||||||
9
Sources/ProjectClient/DuctCalcClientError.swift
Normal file
9
Sources/ProjectClient/DuctCalcClientError.swift
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public struct DuctCalcClientError: Error {
|
||||||
|
public let reason: String
|
||||||
|
|
||||||
|
public init(_ reason: String) {
|
||||||
|
self.reason = reason
|
||||||
|
}
|
||||||
|
}
|
||||||
35
Sources/ProjectClient/Interface.swift
Normal file
35
Sources/ProjectClient/Interface.swift
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import Dependencies
|
||||||
|
import DependenciesMacros
|
||||||
|
import ManualDCore
|
||||||
|
|
||||||
|
extension DependencyValues {
|
||||||
|
public var projectClient: ProjectClient {
|
||||||
|
get { self[ProjectClient.self] }
|
||||||
|
set { self[ProjectClient.self] = newValue }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@DependencyClient
|
||||||
|
public struct ProjectClient: Sendable {
|
||||||
|
public var calculateDuctSizes: @Sendable (Project.ID) async throws -> ProjectResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ProjectClient: TestDependencyKey {
|
||||||
|
public static let testValue = Self()
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ProjectClient {
|
||||||
|
|
||||||
|
public struct ProjectResponse: Codable, Equatable, Sendable {
|
||||||
|
public let rooms: [DuctSizing.RoomContainer]
|
||||||
|
public let trunks: [DuctSizing.TrunkContainer]
|
||||||
|
|
||||||
|
public init(
|
||||||
|
rooms: [DuctSizing.RoomContainer],
|
||||||
|
trunks: [DuctSizing.TrunkContainer]
|
||||||
|
) {
|
||||||
|
self.rooms = rooms
|
||||||
|
self.trunks = trunks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
import DatabaseClient
|
||||||
|
import Dependencies
|
||||||
|
import ManualDClient
|
||||||
|
import ManualDCore
|
||||||
|
|
||||||
|
extension DatabaseClient {
|
||||||
|
|
||||||
|
func calculateDuctSizes(
|
||||||
|
projectID: Project.ID
|
||||||
|
) async throws -> ProjectClient.ProjectResponse {
|
||||||
|
@Dependency(\.manualD) var manualD
|
||||||
|
|
||||||
|
guard let dfrResponse = try await designFrictionRate(projectID: projectID) else {
|
||||||
|
throw DuctCalcClientError("Project not complete.")
|
||||||
|
}
|
||||||
|
|
||||||
|
let ensuredTEL = try dfrResponse.ensureMaxContainer()
|
||||||
|
|
||||||
|
return try await manualD.calculateDuctSizes(
|
||||||
|
rooms: rooms.fetch(projectID),
|
||||||
|
trunks: trunkSizes.fetch(projectID),
|
||||||
|
equipmentInfo: dfrResponse.equipmentInfo,
|
||||||
|
maxSupplyLength: ensuredTEL.supply,
|
||||||
|
maxReturnLength: ensuredTEL.return,
|
||||||
|
designFrictionRate: dfrResponse.designFrictionRate,
|
||||||
|
projectSHR: ensuredSHR(projectID)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetches the project sensible heat ratio or throws an error if it's nil.
|
||||||
|
func ensuredSHR(_ projectID: Project.ID) async throws -> Double {
|
||||||
|
guard let projectSHR = try await projects.getSensibleHeatRatio(projectID) else {
|
||||||
|
throw DuctCalcClientError("Project sensible heat ratio not set.")
|
||||||
|
}
|
||||||
|
return projectSHR
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal container.
|
||||||
|
struct DesignFrictionRateResponse: Equatable, Sendable {
|
||||||
|
|
||||||
|
typealias EnsuredTEL = (supply: EffectiveLength, return: EffectiveLength)
|
||||||
|
|
||||||
|
let designFrictionRate: Double
|
||||||
|
let equipmentInfo: EquipmentInfo
|
||||||
|
let telMaxContainer: EffectiveLength.MaxContainer
|
||||||
|
|
||||||
|
func ensureMaxContainer() throws -> EnsuredTEL {
|
||||||
|
|
||||||
|
guard let maxSupplyLength = telMaxContainer.supply else {
|
||||||
|
throw DuctCalcClientError("Max supply TEL not found")
|
||||||
|
}
|
||||||
|
guard let maxReturnLength = telMaxContainer.return else {
|
||||||
|
throw DuctCalcClientError("Max supply TEL not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return (maxSupplyLength, maxReturnLength)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func designFrictionRate(
|
||||||
|
projectID: Project.ID
|
||||||
|
) async throws -> DesignFrictionRateResponse? {
|
||||||
|
guard let equipmentInfo = try await equipment.fetch(projectID) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let equivalentLengths = try await effectiveLength.fetchMax(projectID)
|
||||||
|
guard let tel = equivalentLengths.total else { return nil }
|
||||||
|
|
||||||
|
let componentLosses = try await componentLoss.fetch(projectID)
|
||||||
|
guard componentLosses.count > 0 else { return nil }
|
||||||
|
|
||||||
|
let availableStaticPressure =
|
||||||
|
equipmentInfo.staticPressure - componentLosses.total
|
||||||
|
|
||||||
|
return .init(
|
||||||
|
designFrictionRate: (availableStaticPressure * 100) / tel,
|
||||||
|
equipmentInfo: equipmentInfo,
|
||||||
|
telMaxContainer: equivalentLengths
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
import Logging
|
||||||
|
import ManualDClient
|
||||||
|
import ManualDCore
|
||||||
|
|
||||||
|
// TODO: Remove Logger and use depedency logger.
|
||||||
|
|
||||||
|
extension ManualDClient {
|
||||||
|
|
||||||
|
func calculateDuctSizes(
|
||||||
|
rooms: [Room],
|
||||||
|
trunks: [DuctSizing.TrunkSize],
|
||||||
|
equipmentInfo: EquipmentInfo,
|
||||||
|
maxSupplyLength: EffectiveLength,
|
||||||
|
maxReturnLength: EffectiveLength,
|
||||||
|
designFrictionRate: Double,
|
||||||
|
projectSHR: Double,
|
||||||
|
logger: Logger? = nil
|
||||||
|
) async throws -> ProjectClient.ProjectResponse {
|
||||||
|
try await .init(
|
||||||
|
rooms: calculateRoomSizes(
|
||||||
|
rooms: rooms,
|
||||||
|
equipmentInfo: equipmentInfo,
|
||||||
|
maxSupplyLength: maxSupplyLength,
|
||||||
|
maxReturnLength: maxReturnLength,
|
||||||
|
designFrictionRate: designFrictionRate,
|
||||||
|
projectSHR: projectSHR
|
||||||
|
),
|
||||||
|
trunks: calculateTrunkSizes(
|
||||||
|
rooms: rooms,
|
||||||
|
trunks: trunks,
|
||||||
|
equipmentInfo: equipmentInfo,
|
||||||
|
maxSupplyLength: maxSupplyLength,
|
||||||
|
maxReturnLength: maxReturnLength,
|
||||||
|
designFrictionRate: designFrictionRate,
|
||||||
|
projectSHR: projectSHR
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculateRoomSizes(
|
||||||
|
rooms: [Room],
|
||||||
|
equipmentInfo: EquipmentInfo,
|
||||||
|
maxSupplyLength: EffectiveLength,
|
||||||
|
maxReturnLength: EffectiveLength,
|
||||||
|
designFrictionRate: Double,
|
||||||
|
projectSHR: Double,
|
||||||
|
logger: Logger? = nil
|
||||||
|
) async throws -> [DuctSizing.RoomContainer] {
|
||||||
|
|
||||||
|
var retval: [DuctSizing.RoomContainer] = []
|
||||||
|
let totalHeatingLoad = rooms.totalHeatingLoad
|
||||||
|
let totalCoolingSensible = rooms.totalCoolingSensible(shr: projectSHR)
|
||||||
|
|
||||||
|
for room in rooms {
|
||||||
|
let heatingLoad = room.heatingLoadPerRegister
|
||||||
|
let coolingLoad = room.coolingSensiblePerRegister(projectSHR: projectSHR)
|
||||||
|
let heatingPercent = heatingLoad / totalHeatingLoad
|
||||||
|
let coolingPercent = coolingLoad / totalCoolingSensible
|
||||||
|
let heatingCFM = heatingPercent * Double(equipmentInfo.heatingCFM)
|
||||||
|
let coolingCFM = coolingPercent * Double(equipmentInfo.coolingCFM)
|
||||||
|
let designCFM = DuctSizing.DesignCFM(heating: heatingCFM, cooling: coolingCFM)
|
||||||
|
let sizes = try await self.ductSize(
|
||||||
|
.init(designCFM: Int(designCFM.value), frictionRate: designFrictionRate)
|
||||||
|
)
|
||||||
|
|
||||||
|
for n in 1...room.registerCount {
|
||||||
|
|
||||||
|
var rectangularWidth: Int? = nil
|
||||||
|
let rectangularSize = room.rectangularSizes?
|
||||||
|
.first(where: { $0.register == nil || $0.register == n })
|
||||||
|
|
||||||
|
if let rectangularSize {
|
||||||
|
let response = try await self.equivalentRectangularDuct(
|
||||||
|
.init(round: sizes.finalSize, height: rectangularSize.height)
|
||||||
|
)
|
||||||
|
rectangularWidth = response.width
|
||||||
|
}
|
||||||
|
|
||||||
|
retval.append(
|
||||||
|
.init(
|
||||||
|
roomID: room.id,
|
||||||
|
roomName: "\(room.name)-\(n)",
|
||||||
|
roomRegister: n,
|
||||||
|
heatingLoad: heatingLoad,
|
||||||
|
coolingLoad: coolingLoad,
|
||||||
|
heatingCFM: heatingCFM,
|
||||||
|
coolingCFM: coolingCFM,
|
||||||
|
designCFM: designCFM,
|
||||||
|
roundSize: sizes.ductulatorSize,
|
||||||
|
finalSize: sizes.finalSize,
|
||||||
|
velocity: sizes.velocity,
|
||||||
|
flexSize: sizes.flexSize,
|
||||||
|
rectangularSize: rectangularSize,
|
||||||
|
rectangularWidth: rectangularWidth
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculateTrunkSizes(
|
||||||
|
rooms: [Room],
|
||||||
|
trunks: [DuctSizing.TrunkSize],
|
||||||
|
equipmentInfo: EquipmentInfo,
|
||||||
|
maxSupplyLength: EffectiveLength,
|
||||||
|
maxReturnLength: EffectiveLength,
|
||||||
|
designFrictionRate: Double,
|
||||||
|
projectSHR: Double,
|
||||||
|
logger: Logger? = nil
|
||||||
|
) async throws -> [DuctSizing.TrunkContainer] {
|
||||||
|
|
||||||
|
var retval = [DuctSizing.TrunkContainer]()
|
||||||
|
let totalHeatingLoad = rooms.totalHeatingLoad
|
||||||
|
let totalCoolingSensible = rooms.totalCoolingSensible(shr: projectSHR)
|
||||||
|
|
||||||
|
for trunk in trunks {
|
||||||
|
let heatingLoad = trunk.totalHeatingLoad
|
||||||
|
let coolingLoad = trunk.totalCoolingSensible(projectSHR: projectSHR)
|
||||||
|
let heatingPercent = heatingLoad / totalHeatingLoad
|
||||||
|
let coolingPercent = coolingLoad / totalCoolingSensible
|
||||||
|
let heatingCFM = heatingPercent * Double(equipmentInfo.heatingCFM)
|
||||||
|
let coolingCFM = coolingPercent * Double(equipmentInfo.coolingCFM)
|
||||||
|
let designCFM = DuctSizing.DesignCFM(heating: heatingCFM, cooling: coolingCFM)
|
||||||
|
let sizes = try await self.ductSize(
|
||||||
|
.init(designCFM: Int(designCFM.value), frictionRate: designFrictionRate)
|
||||||
|
)
|
||||||
|
var width: Int? = nil
|
||||||
|
if let height = trunk.height {
|
||||||
|
let rectangularSize = try await self.equivalentRectangularDuct(
|
||||||
|
.init(round: sizes.finalSize, height: height)
|
||||||
|
)
|
||||||
|
width = rectangularSize.width
|
||||||
|
}
|
||||||
|
|
||||||
|
retval.append(
|
||||||
|
.init(
|
||||||
|
trunk: trunk,
|
||||||
|
ductSize: .init(
|
||||||
|
designCFM: designCFM,
|
||||||
|
roundSize: sizes.ductulatorSize,
|
||||||
|
finalSize: sizes.finalSize,
|
||||||
|
velocity: sizes.velocity,
|
||||||
|
flexSize: sizes.flexSize,
|
||||||
|
height: trunk.height,
|
||||||
|
width: width
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Room {
|
||||||
|
|
||||||
|
var heatingLoadPerRegister: Double {
|
||||||
|
|
||||||
|
heatingLoad / Double(registerCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func coolingSensiblePerRegister(projectSHR: Double) -> Double {
|
||||||
|
let sensible = coolingSensible ?? (coolingTotal * projectSHR)
|
||||||
|
return sensible / Double(registerCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DuctSizing.TrunkSize.RoomProxy {
|
||||||
|
|
||||||
|
// We need to make sure if registers got removed after a trunk
|
||||||
|
// was already made / saved that we do not include registers that
|
||||||
|
// no longer exist.
|
||||||
|
private var actualRegisterCount: Int {
|
||||||
|
guard registers.count <= room.registerCount else {
|
||||||
|
return room.registerCount
|
||||||
|
}
|
||||||
|
return registers.count
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalHeatingLoad: Double {
|
||||||
|
room.heatingLoadPerRegister * Double(actualRegisterCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func totalCoolingSensible(projectSHR: Double) -> Double {
|
||||||
|
room.coolingSensiblePerRegister(projectSHR: projectSHR) * Double(actualRegisterCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DuctSizing.TrunkSize {
|
||||||
|
|
||||||
|
var totalHeatingLoad: Double {
|
||||||
|
rooms.reduce(into: 0) { $0 += $1.totalHeatingLoad }
|
||||||
|
}
|
||||||
|
|
||||||
|
func totalCoolingSensible(projectSHR: Double) -> Double {
|
||||||
|
rooms.reduce(into: 0) { $0 += $1.totalCoolingSensible(projectSHR: projectSHR) }
|
||||||
|
}
|
||||||
|
}
|
||||||
19
Sources/ProjectClient/Live.swift
Normal file
19
Sources/ProjectClient/Live.swift
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import DatabaseClient
|
||||||
|
import Dependencies
|
||||||
|
import Logging
|
||||||
|
import ManualDClient
|
||||||
|
import ManualDCore
|
||||||
|
|
||||||
|
extension ProjectClient: DependencyKey {
|
||||||
|
|
||||||
|
public static var liveValue: Self {
|
||||||
|
@Dependency(\.database) var database
|
||||||
|
|
||||||
|
return .init(
|
||||||
|
calculateDuctSizes: { projectID in
|
||||||
|
try await database.calculateDuctSizes(projectID: projectID)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -24,42 +24,42 @@ extension DatabaseClient.Projects {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension DatabaseClient {
|
// extension DatabaseClient {
|
||||||
|
//
|
||||||
func calculateDuctSizes(
|
// func calculateDuctSizes(
|
||||||
projectID: Project.ID
|
// projectID: Project.ID
|
||||||
) async throws -> (rooms: [DuctSizing.RoomContainer], trunks: [DuctSizing.TrunkContainer]) {
|
// ) async throws -> (rooms: [DuctSizing.RoomContainer], trunks: [DuctSizing.TrunkContainer]) {
|
||||||
@Dependency(\.manualD) var manualD
|
// @Dependency(\.manualD) var manualD
|
||||||
|
//
|
||||||
return try await manualD.calculate(
|
// return try await manualD.calculate(
|
||||||
rooms: rooms.fetch(projectID),
|
// rooms: rooms.fetch(projectID),
|
||||||
trunks: trunkSizes.fetch(projectID),
|
// trunks: trunkSizes.fetch(projectID),
|
||||||
designFrictionRateResult: designFrictionRate(projectID: projectID),
|
// designFrictionRateResult: designFrictionRate(projectID: projectID),
|
||||||
projectSHR: projects.getSensibleHeatRatio(projectID)
|
// projectSHR: projects.getSensibleHeatRatio(projectID)
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
func designFrictionRate(
|
// func designFrictionRate(
|
||||||
projectID: Project.ID
|
// projectID: Project.ID
|
||||||
) async throws -> (EquipmentInfo, EffectiveLength.MaxContainer, Double)? {
|
// ) async throws -> (EquipmentInfo, EffectiveLength.MaxContainer, Double)? {
|
||||||
guard let equipmentInfo = try await equipment.fetch(projectID) else {
|
// guard let equipmentInfo = try await equipment.fetch(projectID) else {
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
let equivalentLengths = try await effectiveLength.fetchMax(projectID)
|
// let equivalentLengths = try await effectiveLength.fetchMax(projectID)
|
||||||
guard let tel = equivalentLengths.total else { return nil }
|
// guard let tel = equivalentLengths.total else { return nil }
|
||||||
|
//
|
||||||
let componentLosses = try await componentLoss.fetch(projectID)
|
// let componentLosses = try await componentLoss.fetch(projectID)
|
||||||
guard componentLosses.count > 0 else { return nil }
|
// guard componentLosses.count > 0 else { return nil }
|
||||||
|
//
|
||||||
let availableStaticPressure =
|
// let availableStaticPressure =
|
||||||
equipmentInfo.staticPressure - componentLosses.total
|
// equipmentInfo.staticPressure - componentLosses.total
|
||||||
|
//
|
||||||
let designFrictionRate = (availableStaticPressure * 100) / tel
|
// let designFrictionRate = (availableStaticPressure * 100) / tel
|
||||||
|
//
|
||||||
return (equipmentInfo, equivalentLengths, designFrictionRate)
|
// return (equipmentInfo, equivalentLengths, designFrictionRate)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
extension DatabaseClient.ComponentLoss {
|
extension DatabaseClient.ComponentLoss {
|
||||||
|
|
||||||
|
|||||||
@@ -1,38 +1,38 @@
|
|||||||
import Logging
|
// import Logging
|
||||||
import ManualDClient
|
// import ManualDClient
|
||||||
import ManualDCore
|
// import ManualDCore
|
||||||
|
//
|
||||||
extension ManualDClient {
|
// extension ManualDClient {
|
||||||
|
//
|
||||||
func calculate(
|
// func calculate(
|
||||||
rooms: [Room],
|
// rooms: [Room],
|
||||||
trunks: [DuctSizing.TrunkSize],
|
// trunks: [DuctSizing.TrunkSize],
|
||||||
designFrictionRateResult: (EquipmentInfo, EffectiveLength.MaxContainer, Double)?,
|
// designFrictionRateResult: (EquipmentInfo, EffectiveLength.MaxContainer, Double)?,
|
||||||
projectSHR: Double?,
|
// projectSHR: Double?,
|
||||||
logger: Logger? = nil
|
// logger: Logger? = nil
|
||||||
) async throws -> (rooms: [DuctSizing.RoomContainer], trunks: [DuctSizing.TrunkContainer]) {
|
// ) async throws -> (rooms: [DuctSizing.RoomContainer], trunks: [DuctSizing.TrunkContainer]) {
|
||||||
guard let designFrictionRateResult else { return ([], []) }
|
// guard let designFrictionRateResult else { return ([], []) }
|
||||||
let equipmentInfo = designFrictionRateResult.0
|
// let equipmentInfo = designFrictionRateResult.0
|
||||||
let effectiveLengths = designFrictionRateResult.1
|
// let effectiveLengths = designFrictionRateResult.1
|
||||||
let designFrictionRate = designFrictionRateResult.2
|
// let designFrictionRate = designFrictionRateResult.2
|
||||||
|
//
|
||||||
guard let maxSupply = effectiveLengths.supply else { return ([], []) }
|
// guard let maxSupply = effectiveLengths.supply else { return ([], []) }
|
||||||
guard let maxReturn = effectiveLengths.return else { return ([], []) }
|
// guard let maxReturn = effectiveLengths.return else { return ([], []) }
|
||||||
|
//
|
||||||
let ductRooms = try await self.calculateSizes(
|
// let ductRooms = try await self.calculateSizes(
|
||||||
rooms: rooms,
|
// rooms: rooms,
|
||||||
trunks: trunks,
|
// trunks: trunks,
|
||||||
equipmentInfo: equipmentInfo,
|
// equipmentInfo: equipmentInfo,
|
||||||
maxSupplyLength: maxSupply,
|
// maxSupplyLength: maxSupply,
|
||||||
maxReturnLength: maxReturn,
|
// maxReturnLength: maxReturn,
|
||||||
designFrictionRate: designFrictionRate,
|
// designFrictionRate: designFrictionRate,
|
||||||
projectSHR: projectSHR ?? 1.0,
|
// projectSHR: projectSHR ?? 1.0,
|
||||||
logger: logger
|
// logger: logger
|
||||||
)
|
// )
|
||||||
|
//
|
||||||
// logger?.debug("Rooms: \(ductRooms)")
|
// // logger?.debug("Rooms: \(ductRooms)")
|
||||||
|
//
|
||||||
return ductRooms
|
// return ductRooms
|
||||||
|
//
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import Dependencies
|
|||||||
import Elementary
|
import Elementary
|
||||||
import Foundation
|
import Foundation
|
||||||
import ManualDCore
|
import ManualDCore
|
||||||
|
import ProjectClient
|
||||||
import Styleguide
|
import Styleguide
|
||||||
|
|
||||||
extension ViewController.Request {
|
extension ViewController.Request {
|
||||||
@@ -10,6 +11,7 @@ extension ViewController.Request {
|
|||||||
func render() async -> AnySendableHTML {
|
func render() async -> AnySendableHTML {
|
||||||
|
|
||||||
@Dependency(\.database) var database
|
@Dependency(\.database) var database
|
||||||
|
@Dependency(\.projectClient) var projectClient
|
||||||
|
|
||||||
switch route {
|
switch route {
|
||||||
case .test:
|
case .test:
|
||||||
@@ -18,7 +20,7 @@ extension ViewController.Request {
|
|||||||
await ResultView {
|
await ResultView {
|
||||||
return (
|
return (
|
||||||
try await database.projects.getCompletedSteps(projectID),
|
try await database.projects.getCompletedSteps(projectID),
|
||||||
try await database.calculateDuctSizes(projectID: projectID)
|
try await projectClient.calculateDuctSizes(projectID)
|
||||||
)
|
)
|
||||||
} onSuccess: { (_, result) in
|
} onSuccess: { (_, result) in
|
||||||
TestPage(trunks: result.trunks, rooms: result.rooms)
|
TestPage(trunks: result.trunks, rooms: result.rooms)
|
||||||
@@ -65,13 +67,16 @@ extension ViewController.Request {
|
|||||||
// let user = try currentUser()
|
// let user = try currentUser()
|
||||||
return (
|
return (
|
||||||
userID,
|
userID,
|
||||||
try await database.projects.fetch(userID, .init(page: 1, per: 25))
|
try await database.projects.fetch(userID, .init(page: 1, per: 25)),
|
||||||
|
profile.theme
|
||||||
)
|
)
|
||||||
} onSuccess: { (userID, projects) in
|
} onSuccess: { (userID, projects, theme) in
|
||||||
|
MainPage(displayFooter: true, theme: theme) {
|
||||||
ProjectsTable(userID: userID, projects: projects)
|
ProjectsTable(userID: userID, projects: projects)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
case .project(let route):
|
case .project(let route):
|
||||||
return await route.renderView(on: self)
|
return await route.renderView(on: self)
|
||||||
|
|
||||||
@@ -552,6 +557,7 @@ extension SiteRoute.View.ProjectRoute.DuctSizingRoute {
|
|||||||
) async -> AnySendableHTML {
|
) async -> AnySendableHTML {
|
||||||
@Dependency(\.database) var database
|
@Dependency(\.database) var database
|
||||||
@Dependency(\.manualD) var manualD
|
@Dependency(\.manualD) var manualD
|
||||||
|
@Dependency(\.projectClient) var projectClient
|
||||||
|
|
||||||
switch self {
|
switch self {
|
||||||
case .index:
|
case .index:
|
||||||
@@ -560,7 +566,7 @@ extension SiteRoute.View.ProjectRoute.DuctSizingRoute {
|
|||||||
case .deleteRectangularSize(let roomID, let request):
|
case .deleteRectangularSize(let roomID, let request):
|
||||||
return await ResultView {
|
return await ResultView {
|
||||||
let room = try await database.rooms.deleteRectangularSize(roomID, request.rectangularSizeID)
|
let room = try await database.rooms.deleteRectangularSize(roomID, request.rectangularSizeID)
|
||||||
return try await database.calculateDuctSizes(projectID: projectID)
|
return try await projectClient.calculateDuctSizes(projectID)
|
||||||
.rooms
|
.rooms
|
||||||
.filter({ $0.roomID == room.id && $0.roomRegister == request.register })
|
.filter({ $0.roomID == room.id && $0.roomRegister == request.register })
|
||||||
.first!
|
.first!
|
||||||
@@ -574,7 +580,7 @@ extension SiteRoute.View.ProjectRoute.DuctSizingRoute {
|
|||||||
roomID,
|
roomID,
|
||||||
.init(id: form.id ?? .init(), register: form.register, height: form.height)
|
.init(id: form.id ?? .init(), register: form.register, height: form.height)
|
||||||
)
|
)
|
||||||
return try await database.calculateDuctSizes(projectID: projectID)
|
return try await projectClient.calculateDuctSizes(projectID)
|
||||||
.rooms
|
.rooms
|
||||||
.filter({ $0.roomID == room.id && $0.roomRegister == form.register })
|
.filter({ $0.roomID == room.id && $0.roomRegister == form.register })
|
||||||
.first!
|
.first!
|
||||||
@@ -609,13 +615,14 @@ extension SiteRoute.View.ProjectRoute.DuctSizingRoute {
|
|||||||
catching: @escaping @Sendable () async throws -> Void = {}
|
catching: @escaping @Sendable () async throws -> Void = {}
|
||||||
) async -> AnySendableHTML {
|
) async -> AnySendableHTML {
|
||||||
@Dependency(\.database) var database
|
@Dependency(\.database) var database
|
||||||
|
@Dependency(\.projectClient) var project
|
||||||
|
|
||||||
return await request.view {
|
return await request.view {
|
||||||
await ResultView {
|
await ResultView {
|
||||||
try await catching()
|
try await catching()
|
||||||
return (
|
return (
|
||||||
try await database.projects.getCompletedSteps(projectID),
|
try await database.projects.getCompletedSteps(projectID),
|
||||||
try await database.calculateDuctSizes(projectID: projectID)
|
try await project.calculateDuctSizes(projectID)
|
||||||
)
|
)
|
||||||
} onSuccess: { (steps, ducts) in
|
} onSuccess: { (steps, ducts) in
|
||||||
ProjectView(projectID: projectID, activeTab: .ductSizing, completedSteps: steps) {
|
ProjectView(projectID: projectID, activeTab: .ductSizing, completedSteps: steps) {
|
||||||
|
|||||||
9
TODO.md
Normal file
9
TODO.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# TODO's
|
||||||
|
|
||||||
|
- [x] Fix theme not working when selected upon signup.
|
||||||
|
- [ ] Pdf generation
|
||||||
|
- [ ] Add postgres / mysql support
|
||||||
|
- [ ] Opensource / license ??
|
||||||
|
- [ ] Figure out domain to host (currently thinking ductcalc.pro)
|
||||||
|
- [ ] Logo / navbar name may have to change if it's not duct-calc.
|
||||||
|
- [ ] MainPage meta items will have to change also
|
||||||
Reference in New Issue
Block a user