Add comprehensive debug logging to SerialManager

- Debug output for connect(), disconnect(), writeData()
- Debug output for formatVUServer() with hex bytes
- Debug output for timer start/stop
- Rate-limited debug output (every 30 frames) to reduce console spam
- Shows connection state changes on main thread
This commit is contained in:
Claude
2025-12-14 21:21:54 +00:00
parent f4b55dbf62
commit e09983223c
+68 -7
View File
@@ -679,7 +679,13 @@ class SerialManager: ObservableObject {
/// Connect to selected serial port /// Connect to selected serial port
func connect() { func connect() {
print("[DEBUG] connect() called")
print("[DEBUG] selectedPortPath: \(selectedPortPath)")
print("[DEBUG] selectedProtocol: \(selectedProtocol)")
print("[DEBUG] baudRate: \(baudRate)")
guard !selectedPortPath.isEmpty else { guard !selectedPortPath.isEmpty else {
print("[DEBUG] ERROR: No port selected")
DispatchQueue.main.async { DispatchQueue.main.async {
self.lastError = "No port selected" self.lastError = "No port selected"
} }
@@ -688,20 +694,25 @@ class SerialManager: ObservableObject {
// Ensure we disconnect first if already connected // Ensure we disconnect first if already connected
if fileDescriptor != -1 { if fileDescriptor != -1 {
print("[DEBUG] Already connected, disconnecting first...")
disconnect() disconnect()
} }
// Open serial port // Open serial port
print("[DEBUG] Opening port: \(selectedPortPath)")
let fd = open(selectedPortPath, O_RDWR | O_NOCTTY | O_NONBLOCK) let fd = open(selectedPortPath, O_RDWR | O_NOCTTY | O_NONBLOCK)
guard fd != -1 else { guard fd != -1 else {
let errorMsg = String(cString: strerror(errno))
print("[DEBUG] ERROR: Failed to open port: \(errorMsg)")
DispatchQueue.main.async { DispatchQueue.main.async {
self.lastError = "Failed to open port: \(String(cString: strerror(errno)))" self.lastError = "Failed to open port: \(errorMsg)"
self.isConnected = false self.isConnected = false
} }
return return
} }
print("[DEBUG] Port opened successfully, fd=\(fd)")
fileDescriptor = fd fileDescriptor = fd
// Configure serial port // Configure serial port
@@ -734,25 +745,32 @@ class SerialManager: ObservableObject {
// Clear any pending data // Clear any pending data
tcflush(fileDescriptor, TCIOFLUSH) tcflush(fileDescriptor, TCIOFLUSH)
print("[DEBUG] Serial port configured, setting isConnected=true")
// Update UI on main thread // Update UI on main thread
DispatchQueue.main.async { DispatchQueue.main.async {
print("[DEBUG] Main thread: setting isConnected=true")
self.isConnected = true self.isConnected = true
self.lastError = nil self.lastError = nil
print("[DEBUG] Main thread: isConnected is now \(self.isConnected)")
} }
print("Connected to \(selectedPortPath) at \(baudRate) baud") print("[DEBUG] Connected to \(selectedPortPath) at \(baudRate) baud")
// Start update timer (must be on main thread) // Start update timer (must be on main thread)
DispatchQueue.main.async { DispatchQueue.main.async {
print("[DEBUG] Starting update timer")
self.startUpdateTimer() self.startUpdateTimer()
} }
// For VU-Server: start response reader and query device info // For VU-Server: start response reader and query device info
if selectedProtocol == .vuServer { if selectedProtocol == .vuServer {
print("[DEBUG] VU-Server protocol: starting response reader")
startResponseReader() startResponseReader()
// Query device info after short delay // Query device info after short delay
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
print("[DEBUG] Requesting device info")
self?.requestDeviceInfo() self?.requestDeviceInfo()
} }
} }
@@ -809,21 +827,26 @@ class SerialManager: ObservableObject {
/// Disconnect from serial port /// Disconnect from serial port
func disconnect() { func disconnect() {
print("[DEBUG] disconnect() called, fd=\(fileDescriptor)")
// Stop timer on main thread // Stop timer on main thread
DispatchQueue.main.async { DispatchQueue.main.async {
print("[DEBUG] Stopping update timer")
self.stopUpdateTimer() self.stopUpdateTimer()
} }
if fileDescriptor != -1 { if fileDescriptor != -1 {
print("[DEBUG] Closing file descriptor \(fileDescriptor)")
close(fileDescriptor) close(fileDescriptor)
fileDescriptor = -1 fileDescriptor = -1
} }
// Update UI on main thread // Update UI on main thread
DispatchQueue.main.async { DispatchQueue.main.async {
print("[DEBUG] Main thread: setting isConnected=false")
self.isConnected = false self.isConnected = false
} }
print("Disconnected from serial port") print("[DEBUG] Disconnected from serial port")
} }
/// Toggle connection state /// Toggle connection state
@@ -883,7 +906,10 @@ class SerialManager: ObservableObject {
/// Send current values to hardware /// Send current values to hardware
func sendValues() { func sendValues() {
guard isConnected, fileDescriptor != -1 else { return } guard isConnected, fileDescriptor != -1 else {
// Only print occasionally to avoid spam
return
}
writeQueue.async { [weak self] in writeQueue.async { [weak self] in
guard let self = self else { return } guard let self = self else { return }
@@ -933,6 +959,9 @@ class SerialManager: ObservableObject {
return Data() return Data()
} }
// Debug counter to limit output spam
private static var debugCounter = 0
/// Format for VU-Server hardware using binary protocol /// Format for VU-Server hardware using binary protocol
/// Sends percentage values (0-100) for each dial /// Sends percentage values (0-100) for each dial
private func formatVUServer() -> Data { private func formatVUServer() -> Data {
@@ -940,7 +969,17 @@ class SerialManager: ObservableObject {
let percentValues = dialValues.map { UInt8((($0) * 100) / 255) } let percentValues = dialValues.map { UInt8((($0) * 100) / 255) }
// Use the optimized "set all dials" command // Use the optimized "set all dials" command
return VUServerProtocol.setAllDialsPercent(values: percentValues) let data = VUServerProtocol.setAllDialsPercent(values: percentValues)
// Debug: print hex bytes (only every 30 frames = ~1 second)
SerialManager.debugCounter += 1
if SerialManager.debugCounter % 30 == 0 {
let hexString = data.map { String(format: "%02X", $0) }.joined(separator: " ")
print("[DEBUG] formatVUServer: dialValues=\(dialValues) -> percent=\(percentValues)")
print("[DEBUG] formatVUServer: sending \(data.count) bytes: \(hexString)")
}
return data
} }
/// Send individual dial value using VU-Server binary protocol /// Send individual dial value using VU-Server binary protocol
@@ -1055,21 +1094,40 @@ class SerialManager: ObservableObject {
/// Write data to serial port /// Write data to serial port
private func writeData(_ data: Data) { private func writeData(_ data: Data) {
guard !data.isEmpty else { return } guard !data.isEmpty else {
print("[DEBUG] writeData: empty data, skipping")
return
}
// Only print every 30th write to reduce spam
let shouldDebug = SerialManager.debugCounter % 30 == 0
if shouldDebug {
print("[DEBUG] writeData: writing \(data.count) bytes to fd=\(fileDescriptor)")
}
data.withUnsafeBytes { buffer in data.withUnsafeBytes { buffer in
guard let baseAddress = buffer.baseAddress else { return } guard let baseAddress = buffer.baseAddress else {
print("[DEBUG] writeData: ERROR - no base address")
return
}
let written = write(fileDescriptor, baseAddress, data.count) let written = write(fileDescriptor, baseAddress, data.count)
if written > 0 { if written > 0 {
if shouldDebug {
print("[DEBUG] writeData: wrote \(written) bytes successfully")
}
DispatchQueue.main.async { DispatchQueue.main.async {
self.bytesSent += UInt64(written) self.bytesSent += UInt64(written)
} }
} else if written < 0 { } else if written < 0 {
let error = String(cString: strerror(errno)) let error = String(cString: strerror(errno))
print("[DEBUG] writeData: ERROR - \(error)")
DispatchQueue.main.async { DispatchQueue.main.async {
self.lastError = "Write error: \(error)" self.lastError = "Write error: \(error)"
} }
} else {
print("[DEBUG] writeData: wrote 0 bytes")
} }
} }
} }
@@ -1077,13 +1135,16 @@ class SerialManager: ObservableObject {
// MARK: - Timer Management // MARK: - Timer Management
private func startUpdateTimer() { private func startUpdateTimer() {
print("[DEBUG] startUpdateTimer called, interval=\(updateInterval)")
stopUpdateTimer() stopUpdateTimer()
updateTimer = Timer.scheduledTimer(withTimeInterval: updateInterval, repeats: true) { [weak self] _ in updateTimer = Timer.scheduledTimer(withTimeInterval: updateInterval, repeats: true) { [weak self] _ in
self?.sendValues() self?.sendValues()
} }
print("[DEBUG] Update timer started")
} }
private func stopUpdateTimer() { private func stopUpdateTimer() {
print("[DEBUG] stopUpdateTimer called")
updateTimer?.invalidate() updateTimer?.invalidate()
updateTimer = nil updateTimer = nil
} }