import Foundation /// Liest Felder beliebiger Bitbreite aus einem Byte-Array. /// /// Victron packt die Felder seiner Werbedaten little-endian und bitweise ohne /// Byte-Ausrichtung: das erste Feld beginnt am niederwertigsten Bit von Byte 0, /// jedes weitere schliesst direkt an. struct BitReader { private let bytes: [UInt8] private var bitOffset = 0 init(_ bytes: [UInt8]) { self.bytes = bytes } var bitsRemaining: Int { bytes.count * 8 - bitOffset } /// Liest `width` Bits als vorzeichenlose Zahl. Gibt nil zurück, wenn die /// Daten zu kurz sind. mutating func read(_ width: Int) -> UInt32? { guard width > 0, width <= 32, bitsRemaining >= width else { return nil } var result: UInt32 = 0 for i in 0..> UInt8(absolute % 8)) & 1 result |= UInt32(bit) << UInt32(i) } bitOffset += width return result } /// Wie `read`, liefert aber nil wenn alle Bits gesetzt sind – so markiert /// Victron "Wert nicht verfügbar". mutating func readOptional(_ width: Int) -> UInt32? { guard let raw = read(width) else { return nil } let notAvailable: UInt32 = width >= 32 ? .max : (1 << UInt32(width)) - 1 return raw == notAvailable ? nil : raw } /// Zweierkomplement-Feld. Der NA-Wert 0x7F..F wird zu nil. mutating func readOptionalSigned(_ width: Int) -> Int32? { guard width > 1, let raw = read(width) else { return nil } let notAvailable: UInt32 = (1 << UInt32(width - 1)) - 1 if raw == notAvailable { return nil } let signBit: UInt32 = 1 << UInt32(width - 1) if raw & signBit != 0 { let mask: UInt32 = width >= 32 ? 0 : ~((1 << UInt32(width)) - 1) return Int32(bitPattern: raw | mask) } return Int32(bitPattern: raw) } /// Skaliertes, optionales Feld ohne Vorzeichen. mutating func scaled(_ width: Int, _ factor: Double) -> Double? { readOptional(width).map { Double($0) * factor } } /// Skaliertes, optionales Feld mit Vorzeichen. mutating func scaledSigned(_ width: Int, _ factor: Double) -> Double? { readOptionalSigned(width).map { Double($0) * factor } } }