3 Commits

13 changed files with 256 additions and 210 deletions

View File

@@ -74,7 +74,7 @@ extension DatabaseClient.Migrations: DependencyKey {
EquipmentInfo.Migrate(), EquipmentInfo.Migrate(),
Room.Migrate(), Room.Migrate(),
EffectiveLength.Migrate(), EffectiveLength.Migrate(),
DuctSizing.TrunkSize.Migrate(), TrunkSize.Migrate(),
] ]
} }
) )

View File

@@ -10,12 +10,11 @@ extension DatabaseClient {
public var create: @Sendable (Room.Create) async throws -> Room public var create: @Sendable (Room.Create) async throws -> Room
public var delete: @Sendable (Room.ID) async throws -> Void public var delete: @Sendable (Room.ID) async throws -> Void
public var deleteRectangularSize: public var deleteRectangularSize:
@Sendable (Room.ID, DuctSizing.RectangularDuct.ID) async throws -> Room @Sendable (Room.ID, Room.RectangularSize.ID) async throws -> Room
public var get: @Sendable (Room.ID) async throws -> Room? public var get: @Sendable (Room.ID) async throws -> Room?
public var fetch: @Sendable (Project.ID) async throws -> [Room] public var fetch: @Sendable (Project.ID) async throws -> [Room]
public var update: @Sendable (Room.ID, Room.Update) async throws -> Room public var update: @Sendable (Room.ID, Room.Update) async throws -> Room
public var updateRectangularSize: public var updateRectangularSize: @Sendable (Room.ID, Room.RectangularSize) async throws -> Room
@Sendable (Room.ID, DuctSizing.RectangularDuct) async throws -> Room
} }
} }
@@ -204,7 +203,7 @@ final class RoomModel: Model, @unchecked Sendable {
var registerCount: Int var registerCount: Int
@Field(key: "rectangularSizes") @Field(key: "rectangularSizes")
var rectangularSizes: [DuctSizing.RectangularDuct]? var rectangularSizes: [Room.RectangularSize]?
@Timestamp(key: "createdAt", on: .create, format: .iso8601) @Timestamp(key: "createdAt", on: .create, format: .iso8601)
var createdAt: Date? var createdAt: Date?
@@ -224,7 +223,7 @@ final class RoomModel: Model, @unchecked Sendable {
coolingTotal: Double, coolingTotal: Double,
coolingSensible: Double? = nil, coolingSensible: Double? = nil,
registerCount: Int, registerCount: Int,
rectangularSizes: [DuctSizing.RectangularDuct]? = nil, rectangularSizes: [Room.RectangularSize]? = nil,
createdAt: Date? = nil, createdAt: Date? = nil,
updatedAt: Date? = nil, updatedAt: Date? = nil,
projectID: Project.ID projectID: Project.ID

View File

@@ -7,13 +7,13 @@ import ManualDCore
extension DatabaseClient { extension DatabaseClient {
@DependencyClient @DependencyClient
public struct TrunkSizes: Sendable { public struct TrunkSizes: Sendable {
public var create: @Sendable (DuctSizing.TrunkSize.Create) async throws -> DuctSizing.TrunkSize public var create: @Sendable (TrunkSize.Create) async throws -> TrunkSize
public var delete: @Sendable (DuctSizing.TrunkSize.ID) async throws -> Void public var delete: @Sendable (TrunkSize.ID) async throws -> Void
public var fetch: @Sendable (Project.ID) async throws -> [DuctSizing.TrunkSize] public var fetch: @Sendable (Project.ID) async throws -> [TrunkSize]
public var get: @Sendable (DuctSizing.TrunkSize.ID) async throws -> DuctSizing.TrunkSize? public var get: @Sendable (TrunkSize.ID) async throws -> TrunkSize?
public var update: public var update:
@Sendable (DuctSizing.TrunkSize.ID, DuctSizing.TrunkSize.Update) async throws -> @Sendable (TrunkSize.ID, TrunkSize.Update) async throws ->
DuctSizing.TrunkSize TrunkSize
} }
} }
@@ -26,7 +26,7 @@ extension DatabaseClient.TrunkSizes: TestDependencyKey {
try request.validate() try request.validate()
let trunk = request.toModel() let trunk = request.toModel()
var roomProxies = [DuctSizing.TrunkSize.RoomProxy]() var roomProxies = [TrunkSize.RoomProxy]()
try await trunk.save(on: database) try await trunk.save(on: database)
@@ -90,7 +90,7 @@ extension DatabaseClient.TrunkSizes: TestDependencyKey {
} }
} }
extension DuctSizing.TrunkSize.Create { extension TrunkSize.Create {
func validate() throws(ValidationError) { func validate() throws(ValidationError) {
guard rooms.count > 0 else { guard rooms.count > 0 else {
@@ -113,7 +113,7 @@ extension DuctSizing.TrunkSize.Create {
} }
} }
extension DuctSizing.TrunkSize.Update { extension TrunkSize.Update {
func validate() throws(ValidationError) { func validate() throws(ValidationError) {
if let rooms { if let rooms {
guard rooms.count > 0 else { guard rooms.count > 0 else {
@@ -128,7 +128,7 @@ extension DuctSizing.TrunkSize.Update {
} }
} }
extension DuctSizing.TrunkSize { extension TrunkSize {
struct Migrate: AsyncMigration { struct Migrate: AsyncMigration {
let name = "CreateTrunkSize" let name = "CreateTrunkSize"
@@ -192,7 +192,7 @@ final class TrunkRoomModel: Model, @unchecked Sendable {
trunkID: TrunkModel.IDValue, trunkID: TrunkModel.IDValue,
roomID: RoomModel.IDValue, roomID: RoomModel.IDValue,
registers: [Int], registers: [Int],
type: DuctSizing.TrunkSize.TrunkType type: TrunkSize.TrunkType
) { ) {
self.id = id self.id = id
$trunk.id = trunkID $trunk.id = trunkID
@@ -201,7 +201,7 @@ final class TrunkRoomModel: Model, @unchecked Sendable {
self.type = type.rawValue self.type = type.rawValue
} }
func toDTO(on database: any Database) async throws -> DuctSizing.TrunkSize.RoomProxy { func toDTO(on database: any Database) async throws -> TrunkSize.RoomProxy {
guard let room = try await RoomModel.find($room.id, on: database) else { guard let room = try await RoomModel.find($room.id, on: database) else {
throw NotFoundError() throw NotFoundError()
} }
@@ -240,7 +240,7 @@ final class TrunkModel: Model, @unchecked Sendable {
init( init(
id: UUID? = nil, id: UUID? = nil,
projectID: Project.ID, projectID: Project.ID,
type: DuctSizing.TrunkSize.TrunkType, type: TrunkSize.TrunkType,
height: Int? = nil, height: Int? = nil,
name: String? = nil name: String? = nil
) { ) {
@@ -251,15 +251,15 @@ final class TrunkModel: Model, @unchecked Sendable {
self.name = name self.name = name
} }
func toDTO(on database: any Database) async throws -> DuctSizing.TrunkSize { func toDTO(on database: any Database) async throws -> TrunkSize {
let rooms = try await withThrowingTaskGroup(of: DuctSizing.TrunkSize.RoomProxy.self) { group in let rooms = try await withThrowingTaskGroup(of: TrunkSize.RoomProxy.self) { group in
for room in self.rooms { for room in self.rooms {
group.addTask { group.addTask {
try await room.toDTO(on: database) try await room.toDTO(on: database)
} }
} }
return try await group.reduce(into: [DuctSizing.TrunkSize.RoomProxy]()) { return try await group.reduce(into: [TrunkSize.RoomProxy]()) {
$0.append($1) $0.append($1)
} }
@@ -277,7 +277,7 @@ final class TrunkModel: Model, @unchecked Sendable {
} }
func applyUpdates( func applyUpdates(
_ updates: DuctSizing.TrunkSize.Update, _ updates: TrunkSize.Update,
on database: any Database on database: any Database
) async throws { ) async throws {
if let type = updates.type, type.rawValue != self.type { if let type = updates.type, type.rawValue != self.type {
@@ -340,15 +340,15 @@ final class TrunkModel: Model, @unchecked Sendable {
extension Array where Element == TrunkModel { extension Array where Element == TrunkModel {
func toDTO(on database: any Database) async throws -> [DuctSizing.TrunkSize] { func toDTO(on database: any Database) async throws -> [TrunkSize] {
try await withThrowingTaskGroup(of: DuctSizing.TrunkSize.self) { group in try await withThrowingTaskGroup(of: TrunkSize.self) { group in
for model in self { for model in self {
group.addTask { group.addTask {
try await model.toDTO(on: database) try await model.toDTO(on: database)
} }
} }
return try await group.reduce(into: [DuctSizing.TrunkSize]()) { return try await group.reduce(into: [TrunkSize]()) {
$0.append($1) $0.append($1)
} }
} }

View File

@@ -14,7 +14,7 @@ extension Room {
} }
} }
extension DuctSizing.TrunkSize.RoomProxy { extension TrunkSize.RoomProxy {
// We need to make sure if registers got removed after a trunk // We need to make sure if registers got removed after a trunk
// was already made / saved that we do not include registers that // was already made / saved that we do not include registers that
@@ -35,7 +35,7 @@ extension DuctSizing.TrunkSize.RoomProxy {
} }
} }
extension DuctSizing.TrunkSize { extension TrunkSize {
var totalHeatingLoad: Double { var totalHeatingLoad: Double {
rooms.reduce(into: 0) { $0 += $1.totalHeatingLoad } rooms.reduce(into: 0) { $0 += $1.totalHeatingLoad }

View File

@@ -3,26 +3,9 @@ import Foundation
public enum DuctSizing { public enum DuctSizing {
public struct RectangularDuct: Codable, Equatable, Identifiable, Sendable {
public let id: UUID
public let register: Int?
public let height: Int
public init(
id: UUID = .init(),
register: Int? = nil,
height: Int,
) {
self.id = id
self.register = register
self.height = height
}
}
public struct SizeContainer: Codable, Equatable, Sendable { public struct SizeContainer: Codable, Equatable, Sendable {
public let rectangularID: Room.RectangularSize.ID?
public let designCFM: DesignCFM public let designCFM: DesignCFM
public let roundSize: Double public let roundSize: Double
public let finalSize: Int public let finalSize: Int
@@ -32,6 +15,7 @@ public enum DuctSizing {
public let width: Int? public let width: Int?
public init( public init(
rectangularID: Room.RectangularSize.ID? = nil,
designCFM: DuctSizing.DesignCFM, designCFM: DuctSizing.DesignCFM,
roundSize: Double, roundSize: Double,
finalSize: Int, finalSize: Int,
@@ -40,6 +24,7 @@ public enum DuctSizing {
height: Int? = nil, height: Int? = nil,
width: Int? = nil width: Int? = nil
) { ) {
self.rectangularID = rectangularID
self.designCFM = designCFM self.designCFM = designCFM
self.roundSize = roundSize self.roundSize = roundSize
self.finalSize = finalSize self.finalSize = finalSize
@@ -52,6 +37,7 @@ public enum DuctSizing {
// TODO: Uses SizeContainer // TODO: Uses SizeContainer
@dynamicMemberLookup
public struct RoomContainer: Codable, Equatable, Sendable { public struct RoomContainer: Codable, Equatable, Sendable {
public let roomID: Room.ID public let roomID: Room.ID
@@ -61,13 +47,7 @@ public enum DuctSizing {
public let coolingLoad: Double public let coolingLoad: Double
public let heatingCFM: Double public let heatingCFM: Double
public let coolingCFM: Double public let coolingCFM: Double
public let designCFM: DesignCFM public let ductSize: SizeContainer
public let roundSize: Double
public let finalSize: Int
public let velocity: Int
public let flexSize: Int
public let rectangularSize: RectangularDuct?
public let rectangularWidth: Int?
public init( public init(
roomID: Room.ID, roomID: Room.ID,
@@ -77,13 +57,7 @@ public enum DuctSizing {
coolingLoad: Double, coolingLoad: Double,
heatingCFM: Double, heatingCFM: Double,
coolingCFM: Double, coolingCFM: Double,
designCFM: DesignCFM, ductSize: SizeContainer
roundSize: Double,
finalSize: Int,
velocity: Int,
flexSize: Int,
rectangularSize: RectangularDuct? = nil,
rectangularWidth: Int? = nil
) { ) {
self.roomID = roomID self.roomID = roomID
self.roomName = roomName self.roomName = roomName
@@ -92,13 +66,11 @@ public enum DuctSizing {
self.coolingLoad = coolingLoad self.coolingLoad = coolingLoad
self.heatingCFM = heatingCFM self.heatingCFM = heatingCFM
self.coolingCFM = coolingCFM self.coolingCFM = coolingCFM
self.designCFM = designCFM self.ductSize = ductSize
self.roundSize = roundSize }
self.finalSize = finalSize
self.velocity = velocity public subscript<T>(dynamicMember keyPath: KeyPath<DuctSizing.SizeContainer, T>) -> T {
self.flexSize = flexSize ductSize[keyPath: keyPath]
self.rectangularSize = rectangularSize
self.rectangularWidth = rectangularWidth
} }
} }
@@ -142,7 +114,7 @@ extension DuctSizing {
self.ductSize = ductSize self.ductSize = ductSize
} }
public subscript<T>(dynamicMember keyPath: KeyPath<DuctSizing.TrunkSize, T>) -> T { public subscript<T>(dynamicMember keyPath: KeyPath<TrunkSize, T>) -> T {
trunk[keyPath: keyPath] trunk[keyPath: keyPath]
} }
@@ -150,99 +122,4 @@ extension DuctSizing {
ductSize[keyPath: keyPath] ductSize[keyPath: keyPath]
} }
} }
// TODO: Add an optional label that the user can set.
// Represents the database model.
public struct TrunkSize: Codable, Equatable, Identifiable, Sendable {
public let id: UUID
public let projectID: Project.ID
public let type: TrunkType
public let rooms: [RoomProxy]
public let height: Int?
public let name: String?
public init(
id: UUID,
projectID: Project.ID,
type: DuctSizing.TrunkSize.TrunkType,
rooms: [DuctSizing.TrunkSize.RoomProxy],
height: Int? = nil,
name: String? = nil
) {
self.id = id
self.projectID = projectID
self.type = type
self.rooms = rooms
self.height = height
self.name = name
}
}
}
extension DuctSizing.TrunkSize {
public struct Create: Codable, Equatable, Sendable {
public let projectID: Project.ID
public let type: TrunkType
public let rooms: [Room.ID: [Int]]
public let height: Int?
public let name: String?
public init(
projectID: Project.ID,
type: DuctSizing.TrunkSize.TrunkType,
rooms: [Room.ID: [Int]],
height: Int? = nil,
name: String? = nil
) {
self.projectID = projectID
self.type = type
self.rooms = rooms
self.height = height
self.name = name
}
}
public struct Update: Codable, Equatable, Sendable {
public let type: TrunkType?
public let rooms: [Room.ID: [Int]]?
public let height: Int?
public let name: String?
public init(
type: DuctSizing.TrunkSize.TrunkType? = nil,
rooms: [Room.ID: [Int]]? = nil,
height: Int? = nil,
name: String? = nil
) {
self.type = type
self.rooms = rooms
self.height = height
self.name = name
}
}
// TODO: Make registers non-optional
public struct RoomProxy: Codable, Equatable, Identifiable, Sendable {
public var id: Room.ID { room.id }
public let room: Room
public let registers: [Int]
public init(room: Room, registers: [Int]) {
self.room = room
self.registers = registers
}
}
public enum TrunkType: String, CaseIterable, Codable, Equatable, Sendable {
case `return`
case supply
public static let allCases = [Self.supply, .return]
}
} }

View File

@@ -9,7 +9,7 @@ public struct Room: Codable, Equatable, Identifiable, Sendable {
public let coolingTotal: Double public let coolingTotal: Double
public let coolingSensible: Double? public let coolingSensible: Double?
public let registerCount: Int public let registerCount: Int
public let rectangularSizes: [DuctSizing.RectangularDuct]? public let rectangularSizes: [RectangularSize]?
public let createdAt: Date public let createdAt: Date
public let updatedAt: Date public let updatedAt: Date
@@ -21,7 +21,7 @@ public struct Room: Codable, Equatable, Identifiable, Sendable {
coolingTotal: Double, coolingTotal: Double,
coolingSensible: Double? = nil, coolingSensible: Double? = nil,
registerCount: Int = 1, registerCount: Int = 1,
rectangularSizes: [DuctSizing.RectangularDuct]? = nil, rectangularSizes: [RectangularSize]? = nil,
createdAt: Date, createdAt: Date,
updatedAt: Date updatedAt: Date
) { ) {
@@ -65,13 +65,30 @@ extension Room {
} }
} }
public struct RectangularSize: Codable, Equatable, Identifiable, Sendable {
public let id: UUID
public let register: Int?
public let height: Int
public init(
id: UUID = .init(),
register: Int? = nil,
height: Int,
) {
self.id = id
self.register = register
self.height = height
}
}
public struct Update: Codable, Equatable, Sendable { public struct Update: Codable, Equatable, Sendable {
public let name: String? public let name: String?
public let heatingLoad: Double? public let heatingLoad: Double?
public let coolingTotal: Double? public let coolingTotal: Double?
public let coolingSensible: Double? public let coolingSensible: Double?
public let registerCount: Int? public let registerCount: Int?
public let rectangularSizes: [DuctSizing.RectangularDuct]? public let rectangularSizes: [RectangularSize]?
public init( public init(
name: String? = nil, name: String? = nil,
@@ -89,7 +106,7 @@ extension Room {
} }
public init( public init(
rectangularSizes: [DuctSizing.RectangularDuct] rectangularSizes: [RectangularSize]
) { ) {
self.name = nil self.name = nil
self.heatingLoad = nil self.heatingLoad = nil

View File

@@ -627,7 +627,7 @@ extension SiteRoute.View.ProjectRoute {
} }
Method.delete Method.delete
Query { Query {
Field("rectangularSize") { DuctSizing.RectangularDuct.ID.parser() } Field("rectangularSize") { Room.RectangularSize.ID.parser() }
Field("register") { Int.parser() } Field("register") { Int.parser() }
} }
.map(.memberwise(DeleteRectangularDuct.init)) .map(.memberwise(DeleteRectangularDuct.init))
@@ -642,7 +642,7 @@ extension SiteRoute.View.ProjectRoute {
Body { Body {
FormData { FormData {
Optionally { Optionally {
Field("id") { DuctSizing.RectangularDuct.ID.parser() } Field("id") { Room.RectangularSize.ID.parser() }
} }
Field("register") { Int.parser() } Field("register") { Int.parser() }
Field("height") { Int.parser() } Field("height") { Int.parser() }
@@ -658,19 +658,19 @@ extension SiteRoute.View.ProjectRoute {
public struct DeleteRectangularDuct: Equatable, Sendable { public struct DeleteRectangularDuct: Equatable, Sendable {
public let rectangularSizeID: DuctSizing.RectangularDuct.ID public let rectangularSizeID: Room.RectangularSize.ID
public let register: Int public let register: Int
public init(rectangularSizeID: DuctSizing.RectangularDuct.ID, register: Int) { public init(rectangularSizeID: Room.RectangularSize.ID, register: Int) {
self.rectangularSizeID = rectangularSizeID self.rectangularSizeID = rectangularSizeID
self.register = register self.register = register
} }
} }
public enum TrunkRoute: Equatable, Sendable { public enum TrunkRoute: Equatable, Sendable {
case delete(DuctSizing.TrunkSize.ID) case delete(TrunkSize.ID)
case submit(TrunkSizeForm) case submit(TrunkSizeForm)
case update(DuctSizing.TrunkSize.ID, TrunkSizeForm) case update(TrunkSize.ID, TrunkSizeForm)
public static let rootPath = "trunk" public static let rootPath = "trunk"
@@ -678,7 +678,7 @@ extension SiteRoute.View.ProjectRoute {
Route(.case(Self.delete)) { Route(.case(Self.delete)) {
Path { Path {
rootPath rootPath
DuctSizing.TrunkSize.ID.parser() TrunkSize.ID.parser()
} }
Method.delete Method.delete
} }
@@ -690,7 +690,7 @@ extension SiteRoute.View.ProjectRoute {
Body { Body {
FormData { FormData {
Field("projectID") { Project.ID.parser() } Field("projectID") { Project.ID.parser() }
Field("type") { DuctSizing.TrunkSize.TrunkType.parser() } Field("type") { TrunkSize.TrunkType.parser() }
Optionally { Optionally {
Field("height") { Int.parser() } Field("height") { Int.parser() }
@@ -708,13 +708,13 @@ extension SiteRoute.View.ProjectRoute {
Route(.case(Self.update)) { Route(.case(Self.update)) {
Path { Path {
rootPath rootPath
DuctSizing.TrunkSize.ID.parser() TrunkSize.ID.parser()
} }
Method.patch Method.patch
Body { Body {
FormData { FormData {
Field("projectID") { Project.ID.parser() } Field("projectID") { Project.ID.parser() }
Field("type") { DuctSizing.TrunkSize.TrunkType.parser() } Field("type") { TrunkSize.TrunkType.parser() }
Optionally { Optionally {
Field("height") { Int.parser() } Field("height") { Int.parser() }
} }
@@ -732,17 +732,43 @@ extension SiteRoute.View.ProjectRoute {
} }
public struct RoomRectangularForm: Equatable, Sendable { public struct RoomRectangularForm: Equatable, Sendable {
public let id: DuctSizing.RectangularDuct.ID?
public let id: Room.RectangularSize.ID?
public let register: Int public let register: Int
public let height: Int public let height: Int
public init(
id: Room.RectangularSize.ID? = nil,
register: Int,
height: Int
) {
self.id = id
self.register = register
self.height = height
}
} }
public struct TrunkSizeForm: Equatable, Sendable { public struct TrunkSizeForm: Equatable, Sendable {
public let projectID: Project.ID public let projectID: Project.ID
public let type: DuctSizing.TrunkSize.TrunkType public let type: TrunkSize.TrunkType
public let height: Int? public let height: Int?
public let name: String? public let name: String?
public let rooms: [String] public let rooms: [String]
public init(
projectID: Project.ID,
type: TrunkSize.TrunkType,
height: Int? = nil,
name: String? = nil,
rooms: [String]
) {
self.projectID = projectID
self.type = type
self.height = height
self.name = name
self.rooms = rooms
}
} }
} }
} }

View File

@@ -0,0 +1,93 @@
import Foundation
// Represents the database model.
public struct TrunkSize: Codable, Equatable, Identifiable, Sendable {
public let id: UUID
public let projectID: Project.ID
public let type: TrunkType
public let rooms: [RoomProxy]
public let height: Int?
public let name: String?
public init(
id: UUID,
projectID: Project.ID,
type: TrunkType,
rooms: [RoomProxy],
height: Int? = nil,
name: String? = nil
) {
self.id = id
self.projectID = projectID
self.type = type
self.rooms = rooms
self.height = height
self.name = name
}
}
extension TrunkSize {
public struct Create: Codable, Equatable, Sendable {
public let projectID: Project.ID
public let type: TrunkType
public let rooms: [Room.ID: [Int]]
public let height: Int?
public let name: String?
public init(
projectID: Project.ID,
type: TrunkType,
rooms: [Room.ID: [Int]],
height: Int? = nil,
name: String? = nil
) {
self.projectID = projectID
self.type = type
self.rooms = rooms
self.height = height
self.name = name
}
}
public struct Update: Codable, Equatable, Sendable {
public let type: TrunkType?
public let rooms: [Room.ID: [Int]]?
public let height: Int?
public let name: String?
public init(
type: TrunkType? = nil,
rooms: [Room.ID: [Int]]? = nil,
height: Int? = nil,
name: String? = nil
) {
self.type = type
self.rooms = rooms
self.height = height
self.name = name
}
}
// TODO: Make registers non-optional
public struct RoomProxy: Codable, Equatable, Identifiable, Sendable {
public var id: Room.ID { room.id }
public let room: Room
public let registers: [Int]
public init(room: Room, registers: [Int]) {
self.room = room
self.registers = registers
}
}
public enum TrunkType: String, CaseIterable, Codable, Equatable, Sendable {
case `return`
case supply
public static let allCases = [Self.supply, .return]
}
}

View File

@@ -16,7 +16,7 @@ extension ManualDClient {
func calculateDuctSizes( func calculateDuctSizes(
rooms: [Room], rooms: [Room],
trunks: [DuctSizing.TrunkSize], trunks: [TrunkSize],
sharedRequest: DuctSizeSharedRequest, sharedRequest: DuctSizeSharedRequest,
logger: Logger? = nil logger: Logger? = nil
) async throws -> ProjectClient.DuctSizeResponse { ) async throws -> ProjectClient.DuctSizeResponse {
@@ -77,13 +77,12 @@ extension ManualDClient {
coolingLoad: coolingLoad, coolingLoad: coolingLoad,
heatingCFM: heatingCFM, heatingCFM: heatingCFM,
coolingCFM: coolingCFM, coolingCFM: coolingCFM,
designCFM: designCFM, ductSize: .init(
roundSize: sizes.ductulatorSize, designCFM: designCFM,
finalSize: sizes.finalSize, sizes: sizes,
velocity: sizes.velocity, rectangularSize: rectangularSize,
flexSize: sizes.flexSize, width: rectangularWidth
rectangularSize: rectangularSize, )
rectangularWidth: rectangularWidth
) )
) )
} }
@@ -94,7 +93,7 @@ extension ManualDClient {
func calculateTrunkSizes( func calculateTrunkSizes(
rooms: [Room], rooms: [Room],
trunks: [DuctSizing.TrunkSize], trunks: [TrunkSize],
sharedRequest: DuctSizeSharedRequest, sharedRequest: DuctSizeSharedRequest,
logger: Logger? = nil logger: Logger? = nil
) async throws -> [DuctSizing.TrunkContainer] { ) async throws -> [DuctSizing.TrunkContainer] {
@@ -127,10 +126,7 @@ extension ManualDClient {
trunk: trunk, trunk: trunk,
ductSize: .init( ductSize: .init(
designCFM: designCFM, designCFM: designCFM,
roundSize: sizes.ductulatorSize, sizes: sizes,
finalSize: sizes.finalSize,
velocity: sizes.velocity,
flexSize: sizes.flexSize,
height: trunk.height, height: trunk.height,
width: width width: width
) )
@@ -143,6 +139,44 @@ extension ManualDClient {
} }
extension DuctSizing.SizeContainer {
init(
designCFM: DuctSizing.DesignCFM,
sizes: ManualDClient.DuctSizeResponse,
height: Int?,
width: Int?
) {
self.init(
rectangularID: nil,
designCFM: designCFM,
roundSize: sizes.ductulatorSize,
finalSize: sizes.finalSize,
velocity: sizes.velocity,
flexSize: sizes.flexSize,
height: height,
width: width
)
}
init(
designCFM: DuctSizing.DesignCFM,
sizes: ManualDClient.DuctSizeResponse,
rectangularSize: Room.RectangularSize?,
width: Int?
) {
self.init(
rectangularID: rectangularSize?.id,
designCFM: designCFM,
roundSize: sizes.ductulatorSize,
finalSize: sizes.finalSize,
velocity: sizes.velocity,
flexSize: sizes.flexSize,
height: rectangularSize?.height,
width: width
)
}
}
extension Room { extension Room {
var heatingLoadPerRegister: Double { var heatingLoadPerRegister: Double {
@@ -156,7 +190,7 @@ extension Room {
} }
} }
extension DuctSizing.TrunkSize.RoomProxy { extension TrunkSize.RoomProxy {
// We need to make sure if registers got removed after a trunk // We need to make sure if registers got removed after a trunk
// was already made / saved that we do not include registers that // was already made / saved that we do not include registers that
@@ -177,7 +211,7 @@ extension DuctSizing.TrunkSize.RoomProxy {
} }
} }
extension DuctSizing.TrunkSize { extension TrunkSize {
var totalHeatingLoad: Double { var totalHeatingLoad: Double {
rooms.reduce(into: 0) { $0 += $1.totalHeatingLoad } rooms.reduce(into: 0) { $0 += $1.totalHeatingLoad }

View File

@@ -4,7 +4,7 @@ import ManualDCore
extension SiteRoute.View.ProjectRoute.DuctSizingRoute.TrunkSizeForm { extension SiteRoute.View.ProjectRoute.DuctSizingRoute.TrunkSizeForm {
func toCreate(logger: Logger? = nil) throws -> DuctSizing.TrunkSize.Create { func toCreate(logger: Logger? = nil) throws -> TrunkSize.Create {
try .init( try .init(
projectID: projectID, projectID: projectID,
type: type, type: type,
@@ -14,7 +14,7 @@ extension SiteRoute.View.ProjectRoute.DuctSizingRoute.TrunkSizeForm {
) )
} }
func toUpdate(logger: Logger? = nil) throws -> DuctSizing.TrunkSize.Update { func toUpdate(logger: Logger? = nil) throws -> TrunkSize.Update {
try .init( try .init(
type: type, type: type,
rooms: makeRooms(logger: logger), rooms: makeRooms(logger: logger),

View File

@@ -40,7 +40,7 @@ struct RectangularSizeForm: HTML, Sendable {
} }
var height: Int? { var height: Int? {
room.rectangularSize?.height room.ductSize.height
} }
var body: some HTML<HTMLTag.dialog> { var body: some HTML<HTMLTag.dialog> {
@@ -54,7 +54,7 @@ struct RectangularSizeForm: HTML, Sendable {
.hx.swap(.outerHTML) .hx.swap(.outerHTML)
) { ) {
input(.class("hidden"), .name("register"), .value(room.roomRegister)) input(.class("hidden"), .name("register"), .value(room.roomRegister))
input(.class("hidden"), .name("id"), .value(room.rectangularSize?.id)) input(.class("hidden"), .name("id"), .value(room.ductSize.rectangularID))
LabeledInput( LabeledInput(
"Height", "Height",

View File

@@ -44,7 +44,7 @@ extension DuctSizingView {
let formID = UUID().idString let formID = UUID().idString
var deleteRoute: String { var deleteRoute: String {
guard let id = room.rectangularSize?.id else { return "" } guard let id = room.rectangularID else { return "" }
return SiteRoute.View.router.path( return SiteRoute.View.router.path(
for: .project( for: .project(
@@ -80,7 +80,7 @@ extension DuctSizingView {
span(.class("label")) { "Design" } span(.class("label")) { "Design" }
div(.class("flex justify-center")) { div(.class("flex justify-center")) {
Badge(number: room.designCFM.value, digits: 0) Badge(number: room.ductSize.designCFM.value, digits: 0)
} }
span(.class("label")) { "Heating" } span(.class("label")) { "Heating" }
@@ -103,28 +103,28 @@ extension DuctSizingView {
div(.class("label")) { "Calculated" } div(.class("label")) { "Calculated" }
div(.class("flex justify-center")) { div(.class("flex justify-center")) {
Badge(number: room.roundSize, digits: 2) Badge(number: room.ductSize.roundSize, digits: 2)
} }
div {} div {}
div(.class("label")) { "Final" } div(.class("label")) { "Final" }
div(.class("flex justify-center")) { div(.class("flex justify-center")) {
Badge(number: room.finalSize) Badge(number: room.ductSize.finalSize)
.attributes(.class("badge-secondary")) .attributes(.class("badge-secondary"))
} }
div {} div {}
div(.class("label")) { "Flex" } div(.class("label")) { "Flex" }
div(.class("flex justify-center")) { div(.class("flex justify-center")) {
Badge(number: room.flexSize) Badge(number: room.ductSize.flexSize)
.attributes(.class("badge-primary")) .attributes(.class("badge-primary"))
} }
div {} div {}
div(.class("label")) { "Rectangular" } div(.class("label")) { "Rectangular" }
div(.class("flex justify-center")) { div(.class("flex justify-center")) {
if let width = room.rectangularWidth, if let width = room.ductSize.width,
let height = room.rectangularSize?.height let height = room.ductSize.height
{ {
Badge { Badge {
span { "\(width) x \(height)" } span { "\(width) x \(height)" }
@@ -134,7 +134,7 @@ extension DuctSizingView {
} }
div(.class("flex justify-end")) { div(.class("flex justify-end")) {
div(.class("join")) { div(.class("join")) {
if room.rectangularSize != nil { if room.ductSize.width != nil {
Tooltip("Delete Size", position: .bottom) { Tooltip("Delete Size", position: .bottom) {
TrashButton() TrashButton()
.attributes(.class("join-item btn-ghost")) .attributes(.class("join-item btn-ghost"))
@@ -142,7 +142,7 @@ extension DuctSizingView {
.hx.delete(deleteRoute), .hx.delete(deleteRoute),
.hx.target("#\(rowID)"), .hx.target("#\(rowID)"),
.hx.swap(.outerHTML), .hx.swap(.outerHTML),
when: room.rectangularSize != nil when: room.ductSize.width != nil
) )
} }
} }

View File

@@ -17,7 +17,7 @@ struct TrunkSizeForm: HTML, Sendable {
let rooms: [DuctSizing.RoomContainer] let rooms: [DuctSizing.RoomContainer]
let dismiss: Bool let dismiss: Bool
var trunk: DuctSizing.TrunkSize? { var trunk: TrunkSize? {
container?.trunk container?.trunk
} }
@@ -56,7 +56,7 @@ struct TrunkSizeForm: HTML, Sendable {
label(.class("select w-full")) { label(.class("select w-full")) {
span(.class("label")) { "Type" } span(.class("label")) { "Type" }
select(.name("type")) { select(.name("type")) {
for type in DuctSizing.TrunkSize.TrunkType.allCases { for type in TrunkSize.TrunkType.allCases {
option(.value(type.rawValue)) { type.rawValue.capitalized } option(.value(type.rawValue)) { type.rawValue.capitalized }
.attributes(.selected, when: trunk?.type == type) .attributes(.selected, when: trunk?.type == type)
} }
@@ -121,7 +121,7 @@ struct TrunkSizeForm: HTML, Sendable {
} }
extension Array where Element == DuctSizing.TrunkSize.RoomProxy { extension Array where Element == TrunkSize.RoomProxy {
func hasRoom(_ room: DuctSizing.RoomContainer) -> Bool { func hasRoom(_ room: DuctSizing.RoomContainer) -> Bool {
first { first {
$0.id == room.roomID $0.id == room.roomID