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

56
Firmware.swift Normal file
View file

@ -0,0 +1,56 @@
import Foundation
/// One uploaded ONU firmware image, from `GET /files/onu-firmware/`.
struct FirmwareFile: MongoDocument {
let raw: JSONValue
init(from decoder: Decoder) throws { raw = try JSONValue(from: decoder) }
init(raw: JSONValue) { self.raw = raw }
var id: String { raw["_id"]?.stringValue ?? filename ?? UUID().uuidString }
var filename: String? { raw["filename"]?.stringValue ?? raw["_id"]?.stringValue }
var version: String? { raw["metadata"]?["Version"]?.stringValue }
var manufacturer: String? { raw["metadata"]?["Compatible Manufacturer"]?.stringValue }
var models: [String] { raw["metadata"]?["Compatible Model"]?.arrayValue?.compactMap(\.stringValue) ?? [] }
var sizeBytes: Double? { raw["length"]?.doubleValue }
var uploadDate: String? { raw["uploadDate"]?.stringValue }
/// True when this image targets the given ONU: manufacturer matches the ONU
/// vendor AND the ONU's equipment id is one of the compatible models (exact,
/// case-insensitive substring matching would wrongly pass e.g. an
/// "XGS2110" image for an "FTXGS2110B" ONU).
func isCompatible(vendor: String?, equipmentId: String?) -> Bool {
if let vendor, let manufacturer, !manufacturer.isEmpty,
manufacturer.caseInsensitiveCompare(vendor) != .orderedSame { return false }
guard let equipmentId, !models.isEmpty else { return true }
return models.contains { $0.caseInsensitiveCompare(equipmentId) == .orderedSame }
}
}
/// Procedure 7 (per-ONU firmware upgrade) as a pure transform so it can be
/// unit-tested before it ever touches a live device.
enum FirmwareUpgrade {
/// Mutate a full ONU-CFG document to stage `filename`/`version` in the ONU's
/// **inactive** firmware bank and point at it. Writes to the inactive bank so
/// the running image is preserved (§6.3): `FW Bank Ptr` 01, 10, anything
/// else (incl. 65535 unset) 1. Returns the new document + the slot written,
/// or nil if the document isn't a recognizable ONU-CFG.
static func apply(to config: JSONValue, filename: String, version: String) -> (document: JSONValue, slot: Int)? {
guard var root = config.objectValue, var onu = root["ONU"]?.objectValue else { return nil }
let currentPtr = onu["FW Bank Ptr"]?.intValue ?? 65535
let slot = (currentPtr == 1) ? 0 : 1
var files = onu["FW Bank Files"]?.arrayValue ?? []
var versions = onu["FW Bank Versions"]?.arrayValue ?? []
while files.count <= slot { files.append(.string("")) }
while versions.count <= slot { versions.append(.string("")) }
files[slot] = .string(filename)
versions[slot] = .string(version)
onu["FW Bank Files"] = .array(files)
onu["FW Bank Versions"] = .array(versions)
onu["FW Bank Ptr"] = .int(slot)
root["ONU"] = .object(onu)
return (.object(root), slot)
}
}