forked from fritob/Camper-Monitor
init
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
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 }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user