PONGo_ios/DashboardViewModel.swift
2026-05-31 14:11:29 +02:00

79 lines
3.3 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Foundation
import Observation
@MainActor
@Observable
final class DashboardViewModel {
var phase: LoadPhase = .idle
var onuCounts: [(state: ONULifecycleState, count: Int)] = []
var onuTotal = 0
var oltCount = 0
var controllerCount = 0
var severityCounts: [(severity: AlarmSeverity, count: Int)] = []
// Aggregates derived from the state docs already fetched for the counts above
// no extra API calls.
var totalDownstreamBps = 0.0
var totalUpstreamBps = 0.0
var abnormalRxCount = 0 // ONUs with Rx optical < 28 or > 10 dBm
var lasersOffCount = 0 // OLTs with the laser shut down
var opticalAvailable = false // whether the bulk ONU-STATE response carried Rx levels
// The actual offenders, kept for drill-down (no re-fetch).
var abnormalRxONUs: [ONUStateDoc] = []
var lasersOffOLTs: [OLTStateDoc] = []
private let connection: MCMSConnection
init(connection: MCMSConnection) { self.connection = connection }
/// Light by default: the ONU count, state breakdown, OLT traffic and
/// laser-off count all come from the (small) OLT-STATE docs. The heavy
/// per-ONU optical scan runs only when `extraStats` is on.
func load(extraStats: Bool = false) async {
phase = .loading
do {
// OLT-STATE drives the OLT count, ONU registration (dev guide
// Procedure 2), total traffic, and laser state all from a handful
// of small docs.
let olts = try await connection.olt.allStates()
oltCount = olts.count
let registration = OLTRepository.registrationMap(from: olts)
onuTotal = registration.count
onuCounts = Dictionary(grouping: registration.values, by: { $0 })
.map { (state: $0.key, count: $0.value.count) }
.sorted { $0.count > $1.count }
totalDownstreamBps = olts.compactMap(\.txRateBps).reduce(0, +)
totalUpstreamBps = olts.compactMap(\.rxRateBps).reduce(0, +)
lasersOffOLTs = olts.filter(\.isLaserOff)
lasersOffCount = lasersOffOLTs.count
controllerCount = (try? await connection.controller.allConfigs().count) ?? 0
if let alarms = try? await connection.onu.alarmHistories() {
severityCounts = Dictionary(grouping: alarms, by: { $0.severity })
.map { (severity: $0.key, count: $0.value.count) }
.sorted { $0.severity.rank < $1.severity.rank }
}
// Opt-in heavy scan: pull every ONU-STATE for the Rx optical check.
if extraStats {
let onus = try await connection.onu.allStates()
opticalAvailable = onus.contains { $0.rxOpticalDBm != nil }
abnormalRxONUs = onus.filter { onu in
guard let rx = onu.rxOpticalDBm else { return false }
return rx < -28 || rx > -10
}
abnormalRxCount = abnormalRxONUs.count
} else {
opticalAvailable = false
abnormalRxONUs = []
abnormalRxCount = 0
}
phase = .loaded
} catch {
phase = .failed((error as? APIError)?.localizedDescription ?? error.localizedDescription)
}
}
}