initial commit

This commit is contained in:
Jon Vanvik 2026-05-31 14:11:29 +02:00
commit 8de962eeb8
44 changed files with 4843 additions and 0 deletions

142
OLTDetailView.swift Normal file
View file

@ -0,0 +1,142 @@
import SwiftUI
struct OLTDetailView: View {
let connection: MCMSConnection
@State private var vm: OLTDetailViewModel
@State private var showResetConfirm = false
private let title: String
init(connection: MCMSConnection, oltId: String, title: String? = nil) {
self.connection = connection
self.title = title ?? oltId
_vm = State(initialValue: OLTDetailViewModel(connection: connection, oltId: oltId))
}
var body: some View {
Group {
switch vm.phase {
case .idle:
LoadingView()
case .loading where vm.olt == nil:
LoadingView()
case .failed(let msg):
ErrorStateView(message: msg) { Task { await vm.load() } }
default:
content
}
}
.navigationTitle(title)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Menu {
Button(role: .destructive) { showResetConfirm = true } label: {
Label("Reset OLT", systemImage: "arrow.clockwise")
}
} label: {
Image(systemName: "ellipsis.circle")
}
.disabled(vm.isPerformingAction)
}
}
.sheet(isPresented: $showResetConfirm) {
ResetConfirmSheet(deviceKind: "OLT", deviceName: title) {
Task { await vm.reset() }
}
}
.alert("Operation", isPresented: Binding(
get: { vm.actionMessage != nil },
set: { if !$0 { vm.actionMessage = nil } }
)) {
Button("OK") { vm.actionMessage = nil }
} message: {
Text(vm.actionMessage ?? "")
}
.task { if vm.phase == .idle { await vm.load() } }
.refreshable { await vm.load() }
}
@ViewBuilder
private var content: some View {
if let olt = vm.olt {
List {
Section("Status") {
LabeledContent("ONUs", value: onuSummary(olt))
if let model = olt.model { LabeledContent("Model", value: model) }
if let mode = olt.ponMode { LabeledContent("PON mode", value: mode) }
if let fw = olt.firmwareVersion { LabeledContent("Firmware", value: fw) }
if let online = olt.onlineTime {
if let up = MongoTimestamp.duration(from: online, to: olt.lastUpdate) {
LabeledContent("Uptime", value: up)
}
LabeledContent("Online since", value: MongoTimestamp.displayString(online) ?? online)
}
if let updated = MongoTimestamp.displayString(olt.lastUpdate) {
LabeledContent("Last update", value: updated)
}
}
if olt.rxRateBps != nil || olt.txRateBps != nil || olt.txUtilPercent != nil {
Section("PON traffic") {
LabeledContent("Downstream",
value: "\(Format.bps(olt.txRateBps)) · \(Format.percent(olt.txUtilPercent)) util")
LabeledContent("Upstream",
value: "\(Format.bps(olt.rxRateBps)) · \(Format.percent(olt.rxUtilPercent)) util")
}
}
if olt.asicTempC != nil || olt.voltage != nil {
Section("Environment") {
if let temp = olt.asicTempC { LabeledContent("ASIC temp", value: "\(temp) °C") }
if let volts = olt.voltage {
LabeledContent("Voltage", value: "\(volts.formatted(.number.precision(.fractionLength(2)))) V")
}
}
}
if let switchName = olt.switchName {
Section("Uplink") {
LabeledContent("Switch", value: switchName)
if let port = olt.switchPort { LabeledContent("Port", value: port) }
if let addr = olt.switchAddress { LabeledContent("Address", value: addr) }
}
}
if olt.populatedBuckets.isEmpty {
Section { Text("No ONUs registered on this OLT.").foregroundStyle(.secondary) }
} else {
ForEach(olt.populatedBuckets, id: \.state) { bucket in
Section {
ForEach(bucket.ids, id: \.self) { onuId in
NavigationLink {
ONUDetailView(connection: connection, onuId: onuId)
} label: {
Text(onuId)
}
}
} header: {
HStack {
StateBadge(text: bucket.state.label, color: bucket.state.color)
Spacer()
Text("\(bucket.ids.count)")
.font(.caption.monospacedDigit())
.foregroundStyle(.secondary)
}
}
}
}
Section("Raw document") {
JSONTreeView(value: olt.raw)
}
}
}
}
private func onuSummary(_ olt: OLTStateDoc) -> String {
let down = olt.totalONUs - olt.onlineONUs
var summary = "\(olt.totalONUs) · \(olt.onlineONUs) online"
if down > 0 { summary += " · \(down) down" }
return summary
}
}