62 lines
2.3 KiB
Swift
62 lines
2.3 KiB
Swift
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..<width {
|
||
let absolute = bitOffset + i
|
||
let byte = bytes[absolute / 8]
|
||
let bit = (byte >> 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 }
|
||
}
|
||
}
|