Files
Camper-Monitor/VanControl/Bluetooth/BitReader.swift
T
fototeddyandClaude Sonnet 5 303a9735d0 App-Namen im iOS-Projekt auf VanControl vereinheitlichen
CamperMonitor (Haupt-Repo) und VanAligneiOS (aus dem gemergten
solar-integration-Branch) liefen unter zwei verschiedenen internen
Namen, obwohl die App nach aussen längst einheitlich "VanControl Pro"
heisst. Jetzt durchgängig VanControl:

- Ordner: CamperMonitor/, CamperMonitorWatch/, CamperMonitorComplication/,
  VanAligneiOSWidget/ → VanControl/, VanControlWatch/,
  VanControlComplication/, VanControlWidget/
- Xcode-Projekt: CamperMonitor.xcodeproj → VanControl.xcodeproj, alle
  Targets/Schemes/Produktnamen entsprechend umbenannt
- Bundle-Identifier auf Wunsch mitgeändert: de.s0.fototeddy.VanControl*
  (App noch nicht veröffentlicht); dabei auch die
  WKCompanionAppBundleIdentifier-Werte korrigiert, die noch das alte
  de.fritob-Präfix statt des tatsächlichen de.s0.fototeddy-Präfixes
  trugen
- Swift-Dateien/Typen: CamperMonitorApp → VanControlApp,
  VanAligneiOSWidget* → VanControlWidget*
- Config/*-Info.plist umbenannt, README.md/Tools/README.md/run-tests.sh
  auf die neuen Pfade angepasst

Bewusst unverändert: firmware/vanalign und alle Bezüge auf "VanAlign"
als Namen der Neigungsmesser-Hardware (eigenständiges Produkt, kein
App-Name) sowie der komplette Android/-Ordner.

Build (App, Watch, Debug) und Protokoll-Testlauf grün.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 21:25:28 +02:00

62 lines
2.3 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 }
}
}