ilabs-flir 2.1.37 → 2.1.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/Flir/build.gradle.kts +1 -1
- package/android/Flir/src/main/java/flir/android/FlirManager.kt +16 -15
- package/android/Flir/src/main/java/flir/android/FlirSdkManager.java +19 -15
- package/ios/Flir/src/FLIRManagerShim.swift +64 -8
- package/ios/Flir/src/FlirLogger.swift +118 -0
- package/ios/Flir/src/FlirManager.swift +154 -41
- package/package.json +1 -1
|
@@ -38,7 +38,7 @@ android {
|
|
|
38
38
|
|
|
39
39
|
dependencies {
|
|
40
40
|
// React Native
|
|
41
|
-
implementation("com.facebook.react:react-native:+")
|
|
41
|
+
implementation("com.facebook.react:react-native:+")
|
|
42
42
|
|
|
43
43
|
// Kotlin coroutines
|
|
44
44
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
|
|
@@ -88,14 +88,14 @@ object FlirManager {
|
|
|
88
88
|
// Store react context for event emission if it's a React context
|
|
89
89
|
// Always update if we get a valid ReactContext (in case previous was stale)
|
|
90
90
|
if (context is ReactContext) {
|
|
91
|
-
Log.d(TAG, "Storing ReactContext for event emission: ${context.javaClass.simpleName}")
|
|
91
|
+
Log.d(TAG, "[Flir-BRIDGE-LOAD] Storing ReactContext for event emission: ${context.javaClass.simpleName}")
|
|
92
92
|
reactContext = context
|
|
93
93
|
} else {
|
|
94
|
-
Log.d(TAG, "Context is not ReactContext: ${context.javaClass.simpleName}")
|
|
94
|
+
Log.d(TAG, "[Flir-BRIDGE-LOAD] Context is not ReactContext: ${context.javaClass.simpleName}")
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
if (isInitialized) {
|
|
98
|
-
Log.d(TAG, "Already initialized")
|
|
98
|
+
Log.d(TAG, "[Flir-BRIDGE-LOAD] Already initialized")
|
|
99
99
|
return
|
|
100
100
|
}
|
|
101
101
|
|
|
@@ -106,14 +106,14 @@ object FlirManager {
|
|
|
106
106
|
sdkManager?.initialize()
|
|
107
107
|
|
|
108
108
|
isInitialized = true
|
|
109
|
-
Log.i(TAG, "FlirManager initialized")
|
|
109
|
+
Log.i(TAG, "[Flir-BRIDGE-LOAD] FlirManager initialized")
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
/**
|
|
113
113
|
* Start scanning for devices (USB, Network, Emulator - ALL types)
|
|
114
114
|
*/
|
|
115
115
|
fun startDiscovery(retry: Boolean = false) {
|
|
116
|
-
Log.i(TAG, "startDiscovery(retry=$retry)")
|
|
116
|
+
Log.i(TAG, "[Flir-BRIDGE-DISCOVERY] startDiscovery(retry=$retry)")
|
|
117
117
|
|
|
118
118
|
if (!isInitialized && appContext != null) {
|
|
119
119
|
init(appContext!!)
|
|
@@ -141,7 +141,7 @@ object FlirManager {
|
|
|
141
141
|
* Stop scanning
|
|
142
142
|
*/
|
|
143
143
|
fun stopDiscovery() {
|
|
144
|
-
Log.i(TAG, "stopDiscovery")
|
|
144
|
+
Log.i(TAG, "[Flir-BRIDGE-DISCOVERY] stopDiscovery")
|
|
145
145
|
sdkManager?.stop()
|
|
146
146
|
isScanning = false
|
|
147
147
|
}
|
|
@@ -156,9 +156,10 @@ object FlirManager {
|
|
|
156
156
|
val identity = devices.find { it.deviceId == deviceId }
|
|
157
157
|
|
|
158
158
|
if (identity != null) {
|
|
159
|
+
Log.i(TAG, "[Flir-BRIDGE-CONNECTION] Connecting to found device: $deviceId")
|
|
159
160
|
sdkManager?.connect(identity)
|
|
160
161
|
} else {
|
|
161
|
-
Log.e(TAG, "Device not found: $deviceId")
|
|
162
|
+
Log.e(TAG, "[Flir-BRIDGE-ERROR] Device not found: $deviceId")
|
|
162
163
|
emitError("Device not found: $deviceId")
|
|
163
164
|
}
|
|
164
165
|
}
|
|
@@ -191,7 +192,7 @@ object FlirManager {
|
|
|
191
192
|
* Stop streaming
|
|
192
193
|
*/
|
|
193
194
|
fun stopStream() {
|
|
194
|
-
Log.i(TAG, "stopStream")
|
|
195
|
+
Log.i(TAG, "[Flir-BRIDGE-STREAMING] stopStream")
|
|
195
196
|
sdkManager?.stopStream()
|
|
196
197
|
isStreaming = false
|
|
197
198
|
}
|
|
@@ -200,7 +201,7 @@ object FlirManager {
|
|
|
200
201
|
* Disconnect from current device
|
|
201
202
|
*/
|
|
202
203
|
fun disconnect() {
|
|
203
|
-
Log.i(TAG, "disconnect")
|
|
204
|
+
Log.i(TAG, "[Flir-BRIDGE-DISCONNECT] disconnect")
|
|
204
205
|
sdkManager?.disconnect()
|
|
205
206
|
isConnected = false
|
|
206
207
|
isStreaming = false
|
|
@@ -318,7 +319,7 @@ object FlirManager {
|
|
|
318
319
|
}
|
|
319
320
|
|
|
320
321
|
override fun onDisconnected() {
|
|
321
|
-
Log.i(TAG, "Disconnected")
|
|
322
|
+
Log.i(TAG, "[Flir-BRIDGE-DISCONNECT] Disconnected callback")
|
|
322
323
|
isConnected = false
|
|
323
324
|
isStreaming = false
|
|
324
325
|
connectedDeviceId = null
|
|
@@ -354,7 +355,7 @@ object FlirManager {
|
|
|
354
355
|
}
|
|
355
356
|
|
|
356
357
|
override fun onBatteryUpdated(level: Int, isCharging: Boolean) {
|
|
357
|
-
Log.d(TAG, "onBatteryUpdated: level=$level charging=$isCharging")
|
|
358
|
+
Log.d(TAG, "[Flir-BRIDGE-BATTERY] onBatteryUpdated: level=$level charging=$isCharging")
|
|
358
359
|
emitBatteryState(level, isCharging)
|
|
359
360
|
}
|
|
360
361
|
}
|
|
@@ -382,10 +383,10 @@ object FlirManager {
|
|
|
382
383
|
private fun emitDeviceState(state: String) {
|
|
383
384
|
val ctx = reactContext
|
|
384
385
|
if (ctx == null) {
|
|
385
|
-
Log.e(TAG, "Cannot emit FlirDeviceConnected($state) - reactContext is null!")
|
|
386
|
+
Log.e(TAG, "[Flir-BRIDGE-ERROR] Cannot emit FlirDeviceConnected($state) - reactContext is null!")
|
|
386
387
|
return
|
|
387
388
|
}
|
|
388
|
-
Log.d(TAG, "Emitting FlirDeviceConnected: $state")
|
|
389
|
+
Log.d(TAG, "[Flir-BRIDGE-CONNECTION] Emitting FlirDeviceConnected: $state")
|
|
389
390
|
try {
|
|
390
391
|
val params = Arguments.createMap().apply {
|
|
391
392
|
putString("state", state)
|
|
@@ -428,9 +429,9 @@ object FlirManager {
|
|
|
428
429
|
|
|
429
430
|
ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
430
431
|
.emit("FlirDevicesFound", params)
|
|
431
|
-
Log.d(TAG, "Successfully emitted FlirDevicesFound")
|
|
432
|
+
Log.d(TAG, "[Flir-BRIDGE-DISCOVERY] Successfully emitted FlirDevicesFound (${devices.size} devices)")
|
|
432
433
|
} catch (e: Exception) {
|
|
433
|
-
Log.e(TAG, "Failed to emit devices found", e)
|
|
434
|
+
Log.e(TAG, "[Flir-BRIDGE-ERROR] Failed to emit devices found", e)
|
|
434
435
|
}
|
|
435
436
|
}
|
|
436
437
|
|
|
@@ -175,9 +175,9 @@ public class FlirSdkManager {
|
|
|
175
175
|
|
|
176
176
|
ThermalSdkAndroid.init(context);
|
|
177
177
|
isInitialized = true;
|
|
178
|
-
Log.d(TAG, "SDK initialized successfully");
|
|
178
|
+
Log.d(TAG, "[Flir-LOAD] SDK initialized successfully");
|
|
179
179
|
} catch (Exception e) {
|
|
180
|
-
Log.e(TAG, "Failed to initialize SDK", e);
|
|
180
|
+
Log.e(TAG, "[Flir-ERROR] Failed to initialize SDK", e);
|
|
181
181
|
notifyError("SDK initialization failed: " + e.getMessage());
|
|
182
182
|
}
|
|
183
183
|
}
|
|
@@ -208,7 +208,7 @@ public class FlirSdkManager {
|
|
|
208
208
|
isScanning = true;
|
|
209
209
|
discoveredDevices.clear();
|
|
210
210
|
|
|
211
|
-
Log.d(TAG, "Starting discovery for EMULATOR, NETWORK, USB...");
|
|
211
|
+
Log.d(TAG, "[Flir-DISCOVERY] Starting discovery for EMULATOR, NETWORK, USB...");
|
|
212
212
|
|
|
213
213
|
try {
|
|
214
214
|
DiscoveryFactory.getInstance().scan(
|
|
@@ -241,7 +241,7 @@ public class FlirSdkManager {
|
|
|
241
241
|
}
|
|
242
242
|
|
|
243
243
|
isScanning = false;
|
|
244
|
-
Log.d(TAG, "Discovery stopped");
|
|
244
|
+
Log.d(TAG, "[Flir-DISCOVERY] Discovery stopped");
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
/**
|
|
@@ -273,7 +273,7 @@ public class FlirSdkManager {
|
|
|
273
273
|
camera = new Camera();
|
|
274
274
|
camera.connect(identity, connectionStatusListener, new ConnectParameters());
|
|
275
275
|
|
|
276
|
-
Log.d(TAG, "Connected to camera");
|
|
276
|
+
Log.d(TAG, "[Flir-CONNECTION] Connected to camera: " + identity.deviceId);
|
|
277
277
|
|
|
278
278
|
if (listener != null) {
|
|
279
279
|
listener.onConnected(identity);
|
|
@@ -309,7 +309,7 @@ public class FlirSdkManager {
|
|
|
309
309
|
listener.onDisconnected();
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
-
Log.d(TAG, "Disconnected");
|
|
312
|
+
Log.d(TAG, "[Flir-DISCONNECT] Disconnected");
|
|
313
313
|
}
|
|
314
314
|
|
|
315
315
|
/**
|
|
@@ -397,10 +397,10 @@ public class FlirSdkManager {
|
|
|
397
397
|
notifyError("Stream error: " + error);
|
|
398
398
|
});
|
|
399
399
|
|
|
400
|
-
Log.d(TAG, "Streaming started");
|
|
400
|
+
Log.d(TAG, "[Flir-STREAMING] Streaming started");
|
|
401
401
|
|
|
402
402
|
} catch (Exception e) {
|
|
403
|
-
Log.e(TAG, "Failed to start stream", e);
|
|
403
|
+
Log.e(TAG, "[Flir-ERROR] Failed to start stream", e);
|
|
404
404
|
notifyError("Stream failed: " + e.getMessage());
|
|
405
405
|
}
|
|
406
406
|
});
|
|
@@ -425,7 +425,7 @@ public class FlirSdkManager {
|
|
|
425
425
|
isProcessingFrame = false;
|
|
426
426
|
lastFrameProcessedMs = 0;
|
|
427
427
|
|
|
428
|
-
Log.d(TAG, "Streaming stopped");
|
|
428
|
+
Log.d(TAG, "[Flir-STREAMING] Streaming stopped");
|
|
429
429
|
}
|
|
430
430
|
|
|
431
431
|
/**
|
|
@@ -522,12 +522,13 @@ public class FlirSdkManager {
|
|
|
522
522
|
streamer.withThermalImage(thermalImage -> {
|
|
523
523
|
thermalImage.setPalette(palette);
|
|
524
524
|
});
|
|
525
|
-
Log.d(TAG, "Palette set to: " + paletteName);
|
|
525
|
+
Log.d(TAG, "[Flir-STREAMING] Palette set to: " + paletteName);
|
|
526
526
|
}
|
|
527
527
|
} catch (Exception e) {
|
|
528
528
|
Log.e(TAG, "Error setting palette", e);
|
|
529
529
|
}
|
|
530
530
|
});
|
|
531
|
+
|
|
531
532
|
}
|
|
532
533
|
|
|
533
534
|
/**
|
|
@@ -644,8 +645,8 @@ public class FlirSdkManager {
|
|
|
644
645
|
@Override
|
|
645
646
|
public void onCameraFound(DiscoveredCamera discoveredCamera) {
|
|
646
647
|
Identity identity = discoveredCamera.getIdentity();
|
|
647
|
-
Log.d(TAG,
|
|
648
|
-
" type=" + identity.communicationInterface);
|
|
648
|
+
Log.d(TAG,
|
|
649
|
+
"[Flir-DISCOVERY] Device found: " + identity.deviceId + " type=" + identity.communicationInterface);
|
|
649
650
|
|
|
650
651
|
// Add to list if not already present
|
|
651
652
|
synchronized (discoveredDevices) {
|
|
@@ -669,7 +670,7 @@ public class FlirSdkManager {
|
|
|
669
670
|
|
|
670
671
|
@Override
|
|
671
672
|
public void onCameraLost(Identity identity) {
|
|
672
|
-
Log.d(TAG, "Device lost: " + identity.deviceId);
|
|
673
|
+
Log.d(TAG, "[Flir-DISCOVERY] Device lost: " + identity.deviceId);
|
|
673
674
|
|
|
674
675
|
synchronized (discoveredDevices) {
|
|
675
676
|
discoveredDevices.removeIf(d -> d.deviceId.equals(identity.deviceId));
|
|
@@ -682,13 +683,13 @@ public class FlirSdkManager {
|
|
|
682
683
|
|
|
683
684
|
@Override
|
|
684
685
|
public void onDiscoveryError(CommunicationInterface iface, ErrorCode error) {
|
|
685
|
-
Log.e(TAG, "Discovery error: " + iface + " - " + error);
|
|
686
|
+
Log.e(TAG, "[Flir-ERROR] Discovery error: " + iface + " - " + error);
|
|
686
687
|
notifyError("Discovery error: " + error);
|
|
687
688
|
}
|
|
688
689
|
|
|
689
690
|
@Override
|
|
690
691
|
public void onDiscoveryFinished(CommunicationInterface iface) {
|
|
691
|
-
Log.d(TAG, "Discovery finished for: " + iface);
|
|
692
|
+
Log.d(TAG, "[Flir-DISCOVERY] Discovery finished for: " + iface);
|
|
692
693
|
}
|
|
693
694
|
};
|
|
694
695
|
|
|
@@ -739,6 +740,9 @@ public class FlirSdkManager {
|
|
|
739
740
|
if (level != lastPolledBatteryLevel || charging != lastPolledCharging) {
|
|
740
741
|
lastPolledBatteryLevel = level;
|
|
741
742
|
lastPolledCharging = charging;
|
|
743
|
+
|
|
744
|
+
Log.d(TAG, String.format("[Flir-BATTERY] Level: %d%%, Charging: %b", level, charging));
|
|
745
|
+
|
|
742
746
|
if (listener != null) {
|
|
743
747
|
try {
|
|
744
748
|
listener.onBatteryUpdated(level, charging);
|
|
@@ -1,43 +1,67 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
import UIKit
|
|
3
3
|
|
|
4
|
+
/// ObjC-compatible shim that forwards to FlirManager
|
|
5
|
+
/// This provides a clean API for native consumers (FilterDataProvider, etc.)
|
|
4
6
|
@objc public class FLIRManager: NSObject {
|
|
5
7
|
@objc public static let shared = FLIRManager()
|
|
8
|
+
|
|
9
|
+
private override init() {
|
|
10
|
+
super.init()
|
|
11
|
+
}
|
|
6
12
|
|
|
13
|
+
// MARK: - SDK Availability
|
|
14
|
+
|
|
7
15
|
@objc public func isAvailable() -> Bool {
|
|
8
16
|
return FlirManager.isSDKAvailable
|
|
9
17
|
}
|
|
18
|
+
|
|
19
|
+
@objc public static var isSDKAvailable: Bool {
|
|
20
|
+
return FlirManager.isSDKAvailable
|
|
21
|
+
}
|
|
10
22
|
|
|
23
|
+
// MARK: - Temperature APIs
|
|
24
|
+
|
|
11
25
|
@objc public func getTemperatureAtPoint(x: Int, y: Int) -> Double {
|
|
12
|
-
|
|
13
|
-
// Return NaN for now (consumers should handle NaN) until full parity is implemented.
|
|
14
|
-
return Double.nan
|
|
26
|
+
return FlirManager.shared.getTemperatureAtPoint(x, y: y)
|
|
15
27
|
}
|
|
16
28
|
|
|
17
29
|
@objc public func getTemperatureAtNormalized(_ nx: Double, y: Double) -> Double {
|
|
18
|
-
return
|
|
30
|
+
return FlirManager.shared.getTemperatureAtNormalized(nx, y: y)
|
|
19
31
|
}
|
|
20
32
|
|
|
33
|
+
// MARK: - Battery APIs
|
|
34
|
+
|
|
21
35
|
@objc public func getBatteryLevel() -> Int {
|
|
22
|
-
return
|
|
36
|
+
return FlirManager.shared.getBatteryLevel()
|
|
23
37
|
}
|
|
24
38
|
|
|
25
39
|
@objc public func isBatteryCharging() -> Bool {
|
|
26
|
-
return
|
|
40
|
+
return FlirManager.shared.isBatteryCharging()
|
|
27
41
|
}
|
|
28
42
|
|
|
43
|
+
// MARK: - Rotation Preference
|
|
44
|
+
|
|
29
45
|
@objc public func setPreferSdkRotation(_ prefer: Bool) {
|
|
30
|
-
|
|
46
|
+
FlirManager.shared.setPreferSdkRotation(prefer)
|
|
31
47
|
}
|
|
32
48
|
|
|
33
49
|
@objc public func isPreferSdkRotation() -> Bool {
|
|
34
|
-
return
|
|
50
|
+
return FlirManager.shared.isPreferSdkRotation()
|
|
35
51
|
}
|
|
36
52
|
|
|
53
|
+
// MARK: - Frame Access
|
|
54
|
+
|
|
37
55
|
@objc public func latestFrameImage() -> UIImage? {
|
|
38
56
|
return FlirManager.shared.latestImage
|
|
39
57
|
}
|
|
58
|
+
|
|
59
|
+
@objc public var latestImage: UIImage? {
|
|
60
|
+
return FlirManager.shared.latestImage
|
|
61
|
+
}
|
|
40
62
|
|
|
63
|
+
// MARK: - Discovery & Connection
|
|
64
|
+
|
|
41
65
|
@objc public func startDiscovery() {
|
|
42
66
|
FlirManager.shared.startDiscovery()
|
|
43
67
|
}
|
|
@@ -45,4 +69,36 @@ import UIKit
|
|
|
45
69
|
@objc public func stopDiscovery() {
|
|
46
70
|
FlirManager.shared.stopDiscovery()
|
|
47
71
|
}
|
|
72
|
+
|
|
73
|
+
@objc public var isConnected: Bool {
|
|
74
|
+
return FlirManager.shared.isConnected
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@objc public var isStreaming: Bool {
|
|
78
|
+
return FlirManager.shared.isStreaming
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@objc public var isEmulator: Bool {
|
|
82
|
+
return FlirManager.shared.isEmulator
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// MARK: - Palette Control
|
|
86
|
+
|
|
87
|
+
@objc public func setPalette(_ name: String) {
|
|
88
|
+
FlirManager.shared.setPalette(name)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@objc public func setPaletteFromAcol(_ acol: Float) {
|
|
92
|
+
FlirManager.shared.setPaletteFromAcol(acol)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// MARK: - Client Lifecycle
|
|
96
|
+
|
|
97
|
+
@objc public func retainClient(_ clientId: String) {
|
|
98
|
+
FlirManager.shared.retainClient(clientId)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@objc public func releaseClient(_ clientId: String) {
|
|
102
|
+
FlirManager.shared.releaseClient(clientId)
|
|
103
|
+
}
|
|
48
104
|
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
//
|
|
2
|
+
// FlirLogger.swift
|
|
3
|
+
// Flir
|
|
4
|
+
//
|
|
5
|
+
// Comprehensive lifecycle logging for FLIR camera operations
|
|
6
|
+
// Helps debug: load → discovery → connection → streaming → frame → battery
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
import Foundation
|
|
10
|
+
|
|
11
|
+
/// Lifecycle stages for FLIR operations
|
|
12
|
+
@objc public enum FlirLifecycleStage: Int {
|
|
13
|
+
case load // SDK initialization
|
|
14
|
+
case discovery // Device scanning
|
|
15
|
+
case connection // Device pairing/connecting
|
|
16
|
+
case streaming // Stream start/stop
|
|
17
|
+
case frame // Frame receiving
|
|
18
|
+
case battery // Battery polling
|
|
19
|
+
case error // Error states
|
|
20
|
+
case disconnect // Disconnection events
|
|
21
|
+
|
|
22
|
+
var tag: String {
|
|
23
|
+
switch self {
|
|
24
|
+
case .load: return "LOAD"
|
|
25
|
+
case .discovery: return "DISCOVERY"
|
|
26
|
+
case .connection: return "CONNECTION"
|
|
27
|
+
case .streaming: return "STREAMING"
|
|
28
|
+
case .frame: return "FRAME"
|
|
29
|
+
case .battery: return "BATTERY"
|
|
30
|
+
case .error: return "ERROR"
|
|
31
|
+
case .disconnect: return "DISCONNECT"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// Centralized logger for FLIR operations with consistent formatting
|
|
37
|
+
@objc public class FlirLogger: NSObject {
|
|
38
|
+
|
|
39
|
+
/// Frame counter for periodic frame logging
|
|
40
|
+
private static var frameCount: Int = 0
|
|
41
|
+
private static let frameLogInterval: Int = 100 // Log every 100 frames
|
|
42
|
+
|
|
43
|
+
/// Log a message at a specific lifecycle stage
|
|
44
|
+
@objc public static func log(_ stage: FlirLifecycleStage, _ message: String) {
|
|
45
|
+
let timestamp = Date()
|
|
46
|
+
let formatter = DateFormatter()
|
|
47
|
+
formatter.dateFormat = "HH:mm:ss.SSS"
|
|
48
|
+
let timeStr = formatter.string(from: timestamp)
|
|
49
|
+
NSLog("[Flir-\(stage.tag)] [\(timeStr)] \(message)")
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/// Log an error with optional Error object
|
|
53
|
+
@objc public static func logError(_ stage: FlirLifecycleStage, _ message: String, error: Error? = nil) {
|
|
54
|
+
let errorDesc = error?.localizedDescription ?? "no error details"
|
|
55
|
+
log(stage, "❌ \(message) - \(errorDesc)")
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/// Log frame received (rate-limited to avoid log spam)
|
|
59
|
+
@objc public static func logFrame(width: Int, height: Int, temperature: Double? = nil) {
|
|
60
|
+
frameCount += 1
|
|
61
|
+
if frameCount % frameLogInterval == 0 {
|
|
62
|
+
var msg = "Frame #\(frameCount) received (\(width)x\(height))"
|
|
63
|
+
if let temp = temperature, !temp.isNaN {
|
|
64
|
+
msg += " temp=\(String(format: "%.1f", temp))°C"
|
|
65
|
+
}
|
|
66
|
+
log(.frame, msg)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/// Reset frame counter (call on disconnect)
|
|
71
|
+
@objc public static func resetFrameCounter() {
|
|
72
|
+
frameCount = 0
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/// Log discovery interface status (important for debugging network vs lightning)
|
|
76
|
+
@objc public static func logDiscoveryInterfaces(lightning: Bool, network: Bool, wireless: Bool, emulator: Bool) {
|
|
77
|
+
var interfaces: [String] = []
|
|
78
|
+
if lightning { interfaces.append("Lightning") }
|
|
79
|
+
if network { interfaces.append("Network") }
|
|
80
|
+
if wireless { interfaces.append("FlirOneWireless") }
|
|
81
|
+
if emulator { interfaces.append("Emulator") }
|
|
82
|
+
|
|
83
|
+
log(.discovery, "Starting discovery on interfaces: \(interfaces.joined(separator: ", "))")
|
|
84
|
+
|
|
85
|
+
if !network {
|
|
86
|
+
log(.discovery, "⚠️ Network discovery DISABLED - requires paid iOS Developer License with NSLocalNetworkUsageDescription")
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/// Log device found with details
|
|
91
|
+
@objc public static func logDeviceFound(deviceId: String, name: String, type: String, isEmulator: Bool) {
|
|
92
|
+
var msg = "Device found: '\(name)' (id=\(deviceId), type=\(type))"
|
|
93
|
+
if isEmulator {
|
|
94
|
+
msg += " [EMULATOR]"
|
|
95
|
+
}
|
|
96
|
+
log(.discovery, msg)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/// Log connection attempt
|
|
100
|
+
@objc public static func logConnectionAttempt(deviceId: String) {
|
|
101
|
+
log(.connection, "Attempting connection to: \(deviceId)")
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/// Log connection success with stream info
|
|
105
|
+
@objc public static func logConnectionSuccess(deviceId: String, streamCount: Int, hasThermal: Bool) {
|
|
106
|
+
log(.connection, "✅ Connected to: \(deviceId) - \(streamCount) stream(s), thermal=\(hasThermal)")
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/// Log battery state
|
|
110
|
+
@objc public static func logBattery(level: Int, isCharging: Bool) {
|
|
111
|
+
if level >= 0 {
|
|
112
|
+
let chargingStr = isCharging ? "charging" : "not charging"
|
|
113
|
+
log(.battery, "Level: \(level)%, \(chargingStr)")
|
|
114
|
+
} else {
|
|
115
|
+
log(.battery, "Battery info unavailable")
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -75,6 +75,11 @@ import ThermalSDK
|
|
|
75
75
|
private var activeClients: Set<String> = []
|
|
76
76
|
private var shutdownWorkItem: DispatchWorkItem? = nil
|
|
77
77
|
|
|
78
|
+
// Battery polling timer (like Android)
|
|
79
|
+
private var batteryPollingTimer: Timer?
|
|
80
|
+
private var lastPolledBatteryLevel: Int = -1
|
|
81
|
+
private var lastPolledCharging: Bool = false
|
|
82
|
+
|
|
78
83
|
#if FLIR_ENABLED
|
|
79
84
|
private var discovery: FLIRDiscovery?
|
|
80
85
|
private var camera: FLIRCamera?
|
|
@@ -85,7 +90,12 @@ import ThermalSDK
|
|
|
85
90
|
|
|
86
91
|
private override init() {
|
|
87
92
|
super.init()
|
|
88
|
-
|
|
93
|
+
FlirLogger.log(.load, "FlirManager singleton initialized")
|
|
94
|
+
#if FLIR_ENABLED
|
|
95
|
+
FlirLogger.log(.load, "✅ FLIR SDK is ENABLED (ThermalSDK available)")
|
|
96
|
+
#else
|
|
97
|
+
FlirLogger.log(.load, "⚠️ FLIR SDK is DISABLED (FLIR_ENABLED not defined)")
|
|
98
|
+
#endif
|
|
89
99
|
}
|
|
90
100
|
|
|
91
101
|
// MARK: - Public State Accessors
|
|
@@ -97,6 +107,17 @@ import ThermalSDK
|
|
|
97
107
|
connectedDeviceName?.lowercased().contains("emulat") == true
|
|
98
108
|
}
|
|
99
109
|
|
|
110
|
+
// Preference: ask SDK to deliver oriented/rotated frames (if SDK supports it)
|
|
111
|
+
private var _preferSdkRotation: Bool = false
|
|
112
|
+
|
|
113
|
+
@objc public func setPreferSdkRotation(_ prefer: Bool) {
|
|
114
|
+
_preferSdkRotation = prefer
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@objc public func isPreferSdkRotation() -> Bool {
|
|
118
|
+
return _preferSdkRotation
|
|
119
|
+
}
|
|
120
|
+
|
|
100
121
|
@objc public func getConnectedDeviceInfo() -> String {
|
|
101
122
|
return connectedDeviceName ?? "Not connected"
|
|
102
123
|
}
|
|
@@ -324,11 +345,11 @@ import ThermalSDK
|
|
|
324
345
|
// MARK: - Discovery
|
|
325
346
|
|
|
326
347
|
@objc public func startDiscovery() {
|
|
327
|
-
|
|
348
|
+
FlirLogger.log(.discovery, "Starting discovery...")
|
|
328
349
|
|
|
329
350
|
#if FLIR_ENABLED
|
|
330
351
|
if isScanning {
|
|
331
|
-
|
|
352
|
+
FlirLogger.log(.discovery, "Already scanning - skipping")
|
|
332
353
|
return
|
|
333
354
|
}
|
|
334
355
|
|
|
@@ -338,6 +359,7 @@ import ThermalSDK
|
|
|
338
359
|
if discovery == nil {
|
|
339
360
|
discovery = FLIRDiscovery()
|
|
340
361
|
discovery?.delegate = self
|
|
362
|
+
FlirLogger.log(.discovery, "Created FLIRDiscovery instance")
|
|
341
363
|
}
|
|
342
364
|
|
|
343
365
|
// Build interfaces based on available permissions
|
|
@@ -351,19 +373,24 @@ import ThermalSDK
|
|
|
351
373
|
// Only add network discovery if NSLocalNetworkUsageDescription is present
|
|
352
374
|
// This prevents crashes/errors when user doesn't have iOS developer registration
|
|
353
375
|
// or hasn't declared network permission
|
|
354
|
-
|
|
376
|
+
let networkEnabled = shouldEnableNetworkDiscovery()
|
|
377
|
+
if networkEnabled {
|
|
355
378
|
interfaces.insert(.network)
|
|
356
|
-
NSLog("[FlirManager] Network discovery enabled (NSLocalNetworkUsageDescription present)")
|
|
357
|
-
} else {
|
|
358
|
-
NSLog("[FlirManager] Network discovery disabled (no NSLocalNetworkUsageDescription)")
|
|
359
379
|
}
|
|
360
380
|
|
|
381
|
+
// Log which interfaces are enabled (important for debugging)
|
|
382
|
+
FlirLogger.logDiscoveryInterfaces(
|
|
383
|
+
lightning: true,
|
|
384
|
+
network: networkEnabled,
|
|
385
|
+
wireless: true,
|
|
386
|
+
emulator: true
|
|
387
|
+
)
|
|
388
|
+
|
|
361
389
|
discovery?.start(interfaces)
|
|
362
390
|
|
|
363
391
|
emitStateChange("discovering")
|
|
364
|
-
NSLog("[FlirManager] Discovery started on interfaces: Lightning, \(interfaces.contains(.network) ? "Network, " : "")FlirOneWireless, Emulator")
|
|
365
392
|
#else
|
|
366
|
-
|
|
393
|
+
FlirLogger.logError(.discovery, "FLIR SDK not available - discovery disabled")
|
|
367
394
|
delegate?.onError("FLIR SDK not available")
|
|
368
395
|
#endif
|
|
369
396
|
}
|
|
@@ -388,28 +415,28 @@ import ThermalSDK
|
|
|
388
415
|
/// Allow explicit override of network discovery (called from React Native)
|
|
389
416
|
@objc public func setNetworkDiscoveryEnabled(_ enabled: Bool) {
|
|
390
417
|
UserDefaults.standard.set(enabled, forKey: "ilabsFlir.networkDiscoveryEnabled")
|
|
391
|
-
|
|
418
|
+
FlirLogger.log(.discovery, "Network discovery override set to: \(enabled)")
|
|
392
419
|
}
|
|
393
420
|
|
|
394
421
|
@objc public func stopDiscovery() {
|
|
395
|
-
|
|
422
|
+
FlirLogger.log(.discovery, "Stopping discovery...")
|
|
396
423
|
|
|
397
424
|
#if FLIR_ENABLED
|
|
398
425
|
discovery?.stop()
|
|
399
426
|
isScanning = false
|
|
400
|
-
|
|
427
|
+
FlirLogger.log(.discovery, "Discovery stopped")
|
|
401
428
|
#endif
|
|
402
429
|
}
|
|
403
430
|
|
|
404
431
|
// MARK: - Connection
|
|
405
432
|
|
|
406
433
|
@objc public func connectToDevice(_ deviceId: String) {
|
|
407
|
-
|
|
434
|
+
FlirLogger.logConnectionAttempt(deviceId: deviceId)
|
|
408
435
|
|
|
409
436
|
#if FLIR_ENABLED
|
|
410
437
|
// Find the identity for this device
|
|
411
438
|
guard let identity = findIdentity(for: deviceId) else {
|
|
412
|
-
|
|
439
|
+
FlirLogger.logError(.connection, "Device not found in identity map: \(deviceId)")
|
|
413
440
|
delegate?.onError("Device not found: \(deviceId)")
|
|
414
441
|
return
|
|
415
442
|
}
|
|
@@ -418,6 +445,7 @@ import ThermalSDK
|
|
|
418
445
|
self?.performConnection(identity: identity)
|
|
419
446
|
}
|
|
420
447
|
#else
|
|
448
|
+
FlirLogger.logError(.connection, "FLIR SDK not available")
|
|
421
449
|
delegate?.onError("FLIR SDK not available")
|
|
422
450
|
#endif
|
|
423
451
|
}
|
|
@@ -432,14 +460,16 @@ import ThermalSDK
|
|
|
432
460
|
private func performConnection(identity: FLIRIdentity) {
|
|
433
461
|
// Use the proven connection pattern from FLIR SDK samples:
|
|
434
462
|
// FLIROneCameraSwift uses: pair(identity, code:) then connect()
|
|
463
|
+
FlirLogger.log(.connection, "performConnection starting for: \(identity.deviceId())")
|
|
435
464
|
|
|
436
465
|
if camera == nil {
|
|
437
466
|
camera = FLIRCamera()
|
|
438
467
|
camera?.delegate = self
|
|
468
|
+
FlirLogger.log(.connection, "Created new FLIRCamera instance")
|
|
439
469
|
}
|
|
440
470
|
|
|
441
471
|
guard let cam = camera else {
|
|
442
|
-
|
|
472
|
+
FlirLogger.logError(.connection, "Failed to create FLIRCamera instance")
|
|
443
473
|
DispatchQueue.main.async { [weak self] in
|
|
444
474
|
self?.delegate?.onError("Failed to create camera instance")
|
|
445
475
|
}
|
|
@@ -448,27 +478,30 @@ import ThermalSDK
|
|
|
448
478
|
|
|
449
479
|
// Handle authentication for generic cameras (network cameras)
|
|
450
480
|
if identity.cameraType() == .generic {
|
|
481
|
+
FlirLogger.log(.connection, "Generic/network camera - starting authentication...")
|
|
451
482
|
let certName = getCertificateName()
|
|
452
483
|
var status = FLIRAuthenticationStatus.pending
|
|
453
484
|
while status == .pending {
|
|
454
485
|
status = cam.authenticate(identity, trustedConnectionName: certName)
|
|
455
486
|
if status == .pending {
|
|
456
|
-
|
|
487
|
+
FlirLogger.log(.connection, "Waiting for camera authentication approval...")
|
|
457
488
|
Thread.sleep(forTimeInterval: 1.0)
|
|
458
489
|
}
|
|
459
490
|
}
|
|
460
|
-
|
|
491
|
+
FlirLogger.log(.connection, "Authentication status: \(status.rawValue)")
|
|
461
492
|
}
|
|
462
493
|
|
|
463
494
|
do {
|
|
464
495
|
// Step 1: Pair with identity (required for FLIR One devices)
|
|
465
496
|
// The code parameter is for BLE pairing, 0 for direct connection
|
|
497
|
+
FlirLogger.log(.connection, "Step 1: Pairing with device...")
|
|
466
498
|
try cam.pair(identity, code: 0)
|
|
467
|
-
|
|
499
|
+
FlirLogger.log(.connection, "✅ Paired successfully with: \(identity.deviceId())")
|
|
468
500
|
|
|
469
501
|
// Step 2: Connect (no identity parameter - uses paired identity)
|
|
502
|
+
FlirLogger.log(.connection, "Step 2: Connecting to device...")
|
|
470
503
|
try cam.connect()
|
|
471
|
-
|
|
504
|
+
FlirLogger.log(.connection, "✅ Connected successfully to: \(identity.deviceId())")
|
|
472
505
|
|
|
473
506
|
// Update state
|
|
474
507
|
connectedIdentity = identity
|
|
@@ -479,23 +512,27 @@ import ThermalSDK
|
|
|
479
512
|
// Get camera info if available
|
|
480
513
|
if let remoteControl = cam.getRemoteControl(),
|
|
481
514
|
let cameraInfo = try? remoteControl.getCameraInformation() {
|
|
482
|
-
|
|
515
|
+
FlirLogger.log(.connection, "Camera info: \(cameraInfo)")
|
|
483
516
|
}
|
|
484
517
|
|
|
485
518
|
// Get streams
|
|
486
519
|
let streams = cam.getStreams()
|
|
520
|
+
let thermalCount = streams.filter { $0.isThermal }.count
|
|
521
|
+
FlirLogger.logConnectionSuccess(deviceId: identity.deviceId(), streamCount: streams.count, hasThermal: thermalCount > 0)
|
|
522
|
+
|
|
487
523
|
if !streams.isEmpty {
|
|
488
|
-
NSLog("[FlirManager] Found \(streams.count) streams")
|
|
489
|
-
|
|
490
524
|
// Find and start the first thermal stream (preferred) or any stream
|
|
491
525
|
let thermalStream = streams.first { $0.isThermal } ?? streams.first
|
|
492
526
|
if let streamToStart = thermalStream {
|
|
493
527
|
startStreamInternal(streamToStart)
|
|
494
528
|
}
|
|
495
529
|
} else {
|
|
496
|
-
|
|
530
|
+
FlirLogger.log(.streaming, "⚠️ No streams available on camera")
|
|
497
531
|
}
|
|
498
532
|
|
|
533
|
+
// Start battery polling (like Android does)
|
|
534
|
+
startBatteryPolling()
|
|
535
|
+
|
|
499
536
|
// Notify delegate on main thread
|
|
500
537
|
DispatchQueue.main.async { [weak self] in
|
|
501
538
|
guard let self = self else { return }
|
|
@@ -510,7 +547,7 @@ import ThermalSDK
|
|
|
510
547
|
}
|
|
511
548
|
|
|
512
549
|
} catch {
|
|
513
|
-
|
|
550
|
+
FlirLogger.logError(.connection, "Connection failed", error: error)
|
|
514
551
|
_isConnected = false
|
|
515
552
|
camera = nil
|
|
516
553
|
DispatchQueue.main.async { [weak self] in
|
|
@@ -547,7 +584,7 @@ import ThermalSDK
|
|
|
547
584
|
@objc public func startStream() {
|
|
548
585
|
#if FLIR_ENABLED
|
|
549
586
|
guard let streams = camera?.getStreams(), !streams.isEmpty else {
|
|
550
|
-
|
|
587
|
+
FlirLogger.log(.streaming, "⚠️ No streams available")
|
|
551
588
|
return
|
|
552
589
|
}
|
|
553
590
|
startStreamInternal(streams[0])
|
|
@@ -555,7 +592,7 @@ import ThermalSDK
|
|
|
555
592
|
}
|
|
556
593
|
|
|
557
594
|
@objc public func stopStream() {
|
|
558
|
-
|
|
595
|
+
FlirLogger.log(.streaming, "Stopping stream...")
|
|
559
596
|
|
|
560
597
|
#if FLIR_ENABLED
|
|
561
598
|
stream?.stop()
|
|
@@ -563,18 +600,20 @@ import ThermalSDK
|
|
|
563
600
|
streamer = nil
|
|
564
601
|
_isStreaming = false
|
|
565
602
|
emitStateChange("connected")
|
|
603
|
+
FlirLogger.log(.streaming, "Stream stopped")
|
|
566
604
|
#endif
|
|
567
605
|
}
|
|
568
606
|
|
|
569
607
|
#if FLIR_ENABLED
|
|
570
608
|
private func startStreamInternal(_ newStream: FLIRStream) {
|
|
571
|
-
|
|
609
|
+
FlirLogger.log(.streaming, "Starting stream (thermal=\(newStream.isThermal))...")
|
|
572
610
|
|
|
573
611
|
stream?.stop()
|
|
574
612
|
stream = newStream
|
|
575
613
|
|
|
576
614
|
if newStream.isThermal {
|
|
577
615
|
streamer = FLIRThermalStreamer(stream: newStream)
|
|
616
|
+
FlirLogger.log(.streaming, "Created FLIRThermalStreamer for thermal stream")
|
|
578
617
|
}
|
|
579
618
|
|
|
580
619
|
newStream.delegate = self
|
|
@@ -583,9 +622,9 @@ import ThermalSDK
|
|
|
583
622
|
try newStream.start()
|
|
584
623
|
_isStreaming = true
|
|
585
624
|
emitStateChange("streaming")
|
|
586
|
-
|
|
625
|
+
FlirLogger.log(.streaming, "✅ Stream started successfully (thermal=\(newStream.isThermal))")
|
|
587
626
|
} catch {
|
|
588
|
-
|
|
627
|
+
FlirLogger.logError(.streaming, "Stream start failed", error: error)
|
|
589
628
|
stream = nil
|
|
590
629
|
streamer = nil
|
|
591
630
|
delegate?.onError("Stream start failed: \(error.localizedDescription)")
|
|
@@ -596,9 +635,12 @@ import ThermalSDK
|
|
|
596
635
|
// MARK: - Disconnect
|
|
597
636
|
|
|
598
637
|
@objc public func disconnect() {
|
|
599
|
-
|
|
638
|
+
FlirLogger.log(.disconnect, "Disconnecting...")
|
|
600
639
|
|
|
601
640
|
#if FLIR_ENABLED
|
|
641
|
+
// Stop battery polling
|
|
642
|
+
stopBatteryPolling()
|
|
643
|
+
|
|
602
644
|
stopStream()
|
|
603
645
|
camera?.disconnect()
|
|
604
646
|
camera = nil
|
|
@@ -609,14 +651,19 @@ import ThermalSDK
|
|
|
609
651
|
_isStreaming = false
|
|
610
652
|
_latestImage = nil
|
|
611
653
|
|
|
654
|
+
// Reset frame counter for next connection
|
|
655
|
+
FlirLogger.resetFrameCounter()
|
|
656
|
+
|
|
612
657
|
DispatchQueue.main.async { [weak self] in
|
|
613
658
|
self?.delegate?.onDeviceDisconnected()
|
|
614
659
|
self?.emitStateChange("disconnected")
|
|
615
660
|
}
|
|
661
|
+
FlirLogger.log(.disconnect, "Disconnected successfully")
|
|
616
662
|
#endif
|
|
617
663
|
}
|
|
618
664
|
|
|
619
665
|
@objc public func stop() {
|
|
666
|
+
FlirLogger.log(.disconnect, "stop() called - full shutdown")
|
|
620
667
|
stopStream()
|
|
621
668
|
disconnect()
|
|
622
669
|
stopDiscovery()
|
|
@@ -647,10 +694,56 @@ import ThermalSDK
|
|
|
647
694
|
return lastTemperature
|
|
648
695
|
}
|
|
649
696
|
|
|
697
|
+
// MARK: - Battery Polling (like Android)
|
|
698
|
+
|
|
699
|
+
private func startBatteryPolling() {
|
|
700
|
+
FlirLogger.log(.battery, "Starting battery polling timer (5s interval)")
|
|
701
|
+
|
|
702
|
+
// Cancel any existing timer
|
|
703
|
+
stopBatteryPolling()
|
|
704
|
+
|
|
705
|
+
// Schedule timer on main run loop
|
|
706
|
+
DispatchQueue.main.async { [weak self] in
|
|
707
|
+
self?.batteryPollingTimer = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) { [weak self] _ in
|
|
708
|
+
self?.pollBatteryState()
|
|
709
|
+
}
|
|
710
|
+
// Run immediately once
|
|
711
|
+
self?.pollBatteryState()
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
private func stopBatteryPolling() {
|
|
716
|
+
if batteryPollingTimer != nil {
|
|
717
|
+
FlirLogger.log(.battery, "Stopping battery polling timer")
|
|
718
|
+
}
|
|
719
|
+
batteryPollingTimer?.invalidate()
|
|
720
|
+
batteryPollingTimer = nil
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
private func pollBatteryState() {
|
|
724
|
+
let level = getBatteryLevel()
|
|
725
|
+
let charging = isBatteryCharging()
|
|
726
|
+
|
|
727
|
+
// Only log and emit if values changed
|
|
728
|
+
if level != lastPolledBatteryLevel || charging != lastPolledCharging {
|
|
729
|
+
lastPolledBatteryLevel = level
|
|
730
|
+
lastPolledCharging = charging
|
|
731
|
+
|
|
732
|
+
FlirLogger.logBattery(level: level, isCharging: charging)
|
|
733
|
+
|
|
734
|
+
// Emit to delegate/RN via notification
|
|
735
|
+
NotificationCenter.default.post(
|
|
736
|
+
name: Notification.Name("FlirBatteryUpdated"),
|
|
737
|
+
object: nil,
|
|
738
|
+
userInfo: ["level": level, "isCharging": charging]
|
|
739
|
+
)
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
650
743
|
// MARK: - Emulator
|
|
651
744
|
|
|
652
745
|
@objc public func startEmulator(type: String) {
|
|
653
|
-
|
|
746
|
+
FlirLogger.log(.connection, "Starting emulator with type: \(type)")
|
|
654
747
|
|
|
655
748
|
#if FLIR_ENABLED
|
|
656
749
|
// Create emulator identity
|
|
@@ -660,6 +753,7 @@ import ThermalSDK
|
|
|
660
753
|
} else if type.lowercased().contains("pro") {
|
|
661
754
|
cameraType = .flirOneEdgePro
|
|
662
755
|
}
|
|
756
|
+
FlirLogger.log(.connection, "Emulator camera type: \(cameraType)")
|
|
663
757
|
|
|
664
758
|
if let emulatorIdentity = FLIRIdentity(emulatorType: cameraType) {
|
|
665
759
|
discoveredDevices.append(FlirDeviceInfo(
|
|
@@ -669,11 +763,15 @@ import ThermalSDK
|
|
|
669
763
|
isEmulator: true
|
|
670
764
|
))
|
|
671
765
|
identityMap[emulatorIdentity.deviceId()] = emulatorIdentity
|
|
766
|
+
FlirLogger.log(.connection, "Emulator identity created: \(emulatorIdentity.deviceId())")
|
|
672
767
|
|
|
673
768
|
// Auto-connect to emulator
|
|
674
769
|
performConnection(identity: emulatorIdentity)
|
|
770
|
+
} else {
|
|
771
|
+
FlirLogger.logError(.connection, "Failed to create emulator identity for type: \(type)")
|
|
675
772
|
}
|
|
676
773
|
#else
|
|
774
|
+
FlirLogger.logError(.connection, "FLIR SDK not available - emulator disabled")
|
|
677
775
|
delegate?.onError("FLIR SDK not available - emulator disabled")
|
|
678
776
|
#endif
|
|
679
777
|
}
|
|
@@ -753,8 +851,12 @@ extension FlirManager: FLIRDiscoveryEventDelegate {
|
|
|
753
851
|
public func cameraDiscovered(_ discoveredCamera: FLIRDiscoveredCamera) {
|
|
754
852
|
let identity = discoveredCamera.identity
|
|
755
853
|
let deviceId = identity.deviceId()
|
|
854
|
+
let displayName = discoveredCamera.displayName ?? deviceId
|
|
855
|
+
let commType = communicationInterfaceName(identity.communicationInterface())
|
|
856
|
+
let isEmulator = identity.communicationInterface() == .emulator
|
|
756
857
|
|
|
757
|
-
|
|
858
|
+
// Use FlirLogger for consistent logging
|
|
859
|
+
FlirLogger.logDeviceFound(deviceId: deviceId, name: displayName, type: commType, isEmulator: isEmulator)
|
|
758
860
|
|
|
759
861
|
// Store identity for later connection
|
|
760
862
|
identityMap[deviceId] = identity
|
|
@@ -762,14 +864,15 @@ extension FlirManager: FLIRDiscoveryEventDelegate {
|
|
|
762
864
|
// Create device info
|
|
763
865
|
let deviceInfo = FlirDeviceInfo(
|
|
764
866
|
deviceId: deviceId,
|
|
765
|
-
name:
|
|
766
|
-
communicationType:
|
|
767
|
-
isEmulator:
|
|
867
|
+
name: displayName,
|
|
868
|
+
communicationType: commType,
|
|
869
|
+
isEmulator: isEmulator
|
|
768
870
|
)
|
|
769
871
|
|
|
770
872
|
// Add to discovered list if not already present
|
|
771
873
|
if !discoveredDevices.contains(where: { $0.deviceId == deviceId }) {
|
|
772
874
|
discoveredDevices.append(deviceInfo)
|
|
875
|
+
FlirLogger.log(.discovery, "Total discovered devices: \(discoveredDevices.count)")
|
|
773
876
|
}
|
|
774
877
|
|
|
775
878
|
// Notify delegate
|
|
@@ -781,13 +884,14 @@ extension FlirManager: FLIRDiscoveryEventDelegate {
|
|
|
781
884
|
|
|
782
885
|
public func cameraLost(_ cameraIdentity: FLIRIdentity) {
|
|
783
886
|
let deviceId = cameraIdentity.deviceId()
|
|
784
|
-
|
|
887
|
+
FlirLogger.log(.discovery, "Camera lost: \(deviceId)")
|
|
785
888
|
|
|
786
889
|
identityMap.removeValue(forKey: deviceId)
|
|
787
890
|
discoveredDevices.removeAll { $0.deviceId == deviceId }
|
|
788
891
|
|
|
789
892
|
// If this was our connected device, handle disconnect
|
|
790
893
|
if connectedDeviceId == deviceId {
|
|
894
|
+
FlirLogger.log(.disconnect, "Lost connected device - triggering disconnect")
|
|
791
895
|
disconnect()
|
|
792
896
|
}
|
|
793
897
|
|
|
@@ -798,7 +902,7 @@ extension FlirManager: FLIRDiscoveryEventDelegate {
|
|
|
798
902
|
}
|
|
799
903
|
|
|
800
904
|
public func discoveryError(_ error: String, netServiceError nsnetserviceserror: Int32, on iface: FLIRCommunicationInterface) {
|
|
801
|
-
|
|
905
|
+
FlirLogger.logError(.discovery, "Discovery error: \(error) (code=\(nsnetserviceserror)) on interface: \(iface)")
|
|
802
906
|
|
|
803
907
|
DispatchQueue.main.async { [weak self] in
|
|
804
908
|
self?.delegate?.onError("Discovery error: \(error)")
|
|
@@ -806,7 +910,7 @@ extension FlirManager: FLIRDiscoveryEventDelegate {
|
|
|
806
910
|
}
|
|
807
911
|
|
|
808
912
|
public func discoveryFinished(_ iface: FLIRCommunicationInterface) {
|
|
809
|
-
|
|
913
|
+
FlirLogger.log(.discovery, "Discovery finished on interface: \(iface)")
|
|
810
914
|
isScanning = false
|
|
811
915
|
}
|
|
812
916
|
}
|
|
@@ -815,13 +919,19 @@ extension FlirManager: FLIRDiscoveryEventDelegate {
|
|
|
815
919
|
|
|
816
920
|
extension FlirManager: FLIRDataReceivedDelegate {
|
|
817
921
|
public func onDisconnected(_ camera: FLIRCamera, withError error: Error?) {
|
|
818
|
-
|
|
922
|
+
FlirLogger.logError(.disconnect, "Camera disconnected callback", error: error)
|
|
923
|
+
|
|
924
|
+
// Stop battery polling
|
|
925
|
+
stopBatteryPolling()
|
|
819
926
|
|
|
820
927
|
_isConnected = false
|
|
821
928
|
_isStreaming = false
|
|
822
929
|
connectedDeviceId = nil
|
|
823
930
|
connectedDeviceName = nil
|
|
824
931
|
|
|
932
|
+
// Reset frame counter
|
|
933
|
+
FlirLogger.resetFrameCounter()
|
|
934
|
+
|
|
825
935
|
DispatchQueue.main.async { [weak self] in
|
|
826
936
|
self?.delegate?.onDeviceDisconnected()
|
|
827
937
|
self?.emitStateChange("disconnected")
|
|
@@ -833,7 +943,7 @@ extension FlirManager: FLIRDataReceivedDelegate {
|
|
|
833
943
|
|
|
834
944
|
extension FlirManager: FLIRStreamDelegate {
|
|
835
945
|
public func onError(_ error: Error) {
|
|
836
|
-
|
|
946
|
+
FlirLogger.logError(.streaming, "Stream error", error: error)
|
|
837
947
|
|
|
838
948
|
DispatchQueue.main.async { [weak self] in
|
|
839
949
|
self?.delegate?.onError("Stream error: \(error.localizedDescription)")
|
|
@@ -873,6 +983,9 @@ extension FlirManager: FLIRStreamDelegate {
|
|
|
873
983
|
}
|
|
874
984
|
}
|
|
875
985
|
|
|
986
|
+
// Rate-limited frame logging
|
|
987
|
+
FlirLogger.logFrame(width: Int(image.size.width), height: Int(image.size.height), temperature: lastTemperature)
|
|
988
|
+
|
|
876
989
|
DispatchQueue.main.async { [weak self] in
|
|
877
990
|
guard let self = self else { return }
|
|
878
991
|
self.delegate?.onFrameReceived(
|
|
@@ -900,7 +1013,7 @@ extension FlirManager: FLIRStreamDelegate {
|
|
|
900
1013
|
}
|
|
901
1014
|
}
|
|
902
1015
|
} catch {
|
|
903
|
-
|
|
1016
|
+
FlirLogger.logError(.frame, "Streamer update error", error: error)
|
|
904
1017
|
}
|
|
905
1018
|
}
|
|
906
1019
|
}
|