ilabs-flir 2.2.5 → 2.2.6
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.
|
@@ -49,11 +49,7 @@ import ThermalSDK
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
/// Main FLIR Manager - Singleton that manages all FLIR camera operations
|
|
52
|
-
#if FLIR_ENABLED
|
|
53
|
-
@objc public class FlirManager: NSObject, FLIRDiscoveryEventDelegate, FLIRDataReceivedDelegate, FLIRStreamDelegate {
|
|
54
|
-
#else
|
|
55
52
|
@objc public class FlirManager: NSObject {
|
|
56
|
-
#endif
|
|
57
53
|
@objc public static let shared = FlirManager()
|
|
58
54
|
|
|
59
55
|
// MARK: - Properties
|
|
@@ -78,9 +74,8 @@ import ThermalSDK
|
|
|
78
74
|
// Client lifecycle for discovery/connection ownership
|
|
79
75
|
private var activeClients: Set<String> = []
|
|
80
76
|
private var shutdownWorkItem: DispatchWorkItem? = nil
|
|
81
|
-
// Discovery
|
|
77
|
+
// Discovery timeout to prevent infinite scanning
|
|
82
78
|
private var discoveryTimeoutWorkItem: DispatchWorkItem? = nil
|
|
83
|
-
private var streamWatchdogWorkItem: DispatchWorkItem? = nil
|
|
84
79
|
|
|
85
80
|
// Battery polling timer (like Android)
|
|
86
81
|
private var batteryPollingTimer: Timer?
|
|
@@ -93,6 +88,7 @@ import ThermalSDK
|
|
|
93
88
|
private var stream: FLIRStream?
|
|
94
89
|
private var streamer: FLIRThermalStreamer?
|
|
95
90
|
private var connectedIdentity: FLIRIdentity?
|
|
91
|
+
private var identityMap: [String: FLIRIdentity] = [:]
|
|
96
92
|
#endif
|
|
97
93
|
|
|
98
94
|
private override init() {
|
|
@@ -375,12 +371,7 @@ import ThermalSDK
|
|
|
375
371
|
|
|
376
372
|
if discovery == nil {
|
|
377
373
|
discovery = FLIRDiscovery()
|
|
378
|
-
|
|
379
|
-
if let dd = self as? FLIRDiscoveryEventDelegate {
|
|
380
|
-
discovery?.delegate = dd
|
|
381
|
-
} else {
|
|
382
|
-
FlirLogger.log(.discovery, "Warning: FlirManager does not conform to FLIRDiscoveryEventDelegate at runtime; delegate not assigned")
|
|
383
|
-
}
|
|
374
|
+
discovery?.delegate = self
|
|
384
375
|
FlirLogger.log(.discovery, "Created FLIRDiscovery instance")
|
|
385
376
|
}
|
|
386
377
|
|
|
@@ -411,27 +402,26 @@ import ThermalSDK
|
|
|
411
402
|
discovery?.start(interfaces)
|
|
412
403
|
|
|
413
404
|
emitStateChange("discovering")
|
|
414
|
-
|
|
415
|
-
//
|
|
416
|
-
// the UI doesn't remain stuck when discovery yields no devices.
|
|
405
|
+
|
|
406
|
+
// Set timeout to prevent infinite scanning (matches Android's 8-second timeout)
|
|
417
407
|
discoveryTimeoutWorkItem?.cancel()
|
|
418
|
-
let
|
|
419
|
-
guard let self = self else { return }
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
408
|
+
let timeoutWork = DispatchWorkItem { [weak self] in
|
|
409
|
+
guard let self = self, self.isScanning else { return }
|
|
410
|
+
FlirLogger.log(.discovery, "⏱ Discovery timeout reached - stopping scan")
|
|
411
|
+
self.discovery?.stop()
|
|
412
|
+
self.isScanning = false
|
|
413
|
+
|
|
414
|
+
// Emit final device list and state
|
|
415
|
+
DispatchQueue.main.async {
|
|
416
|
+
self.delegate?.onDevicesFound(self.discoveredDevices)
|
|
417
|
+
if self.discoveredDevices.isEmpty {
|
|
428
418
|
self.emitStateChange("no_device_found")
|
|
429
|
-
self.delegate?.onError("
|
|
419
|
+
self.delegate?.onError("No FLIR devices found")
|
|
430
420
|
}
|
|
431
421
|
}
|
|
432
422
|
}
|
|
433
|
-
discoveryTimeoutWorkItem =
|
|
434
|
-
DispatchQueue.main.asyncAfter(deadline: .now() + 8.0, execute:
|
|
423
|
+
discoveryTimeoutWorkItem = timeoutWork
|
|
424
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 8.0, execute: timeoutWork)
|
|
435
425
|
#else
|
|
436
426
|
FlirLogger.logError(.discovery, "FLIR SDK not available - discovery disabled")
|
|
437
427
|
delegate?.onError("FLIR SDK not available")
|
|
@@ -470,7 +460,13 @@ import ThermalSDK
|
|
|
470
460
|
discovery?.stop()
|
|
471
461
|
isScanning = false
|
|
472
462
|
FlirLogger.log(.discovery, "Discovery stopped")
|
|
473
|
-
|
|
463
|
+
#endif
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// MARK: - Connection
|
|
467
|
+
|
|
468
|
+
@objc public func connectToDevice(_ deviceId: String) {
|
|
469
|
+
FlirLogger.logConnectionAttempt(deviceId: deviceId)
|
|
474
470
|
|
|
475
471
|
#if FLIR_ENABLED
|
|
476
472
|
// Find the identity for this device
|
|
@@ -490,8 +486,6 @@ import ThermalSDK
|
|
|
490
486
|
}
|
|
491
487
|
|
|
492
488
|
#if FLIR_ENABLED
|
|
493
|
-
private var identityMap: [String: FLIRIdentity] = [:]
|
|
494
|
-
|
|
495
489
|
private func findIdentity(for deviceId: String) -> FLIRIdentity? {
|
|
496
490
|
return identityMap[deviceId]
|
|
497
491
|
}
|
|
@@ -515,29 +509,19 @@ import ThermalSDK
|
|
|
515
509
|
return
|
|
516
510
|
}
|
|
517
511
|
|
|
518
|
-
//
|
|
512
|
+
// Handle authentication for generic cameras (network cameras)
|
|
519
513
|
if identity.cameraType() == .generic {
|
|
520
514
|
FlirLogger.log(.connection, "Generic/network camera - starting authentication...")
|
|
521
515
|
let certName = getCertificateName()
|
|
522
|
-
var
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
if authStatus == .pending {
|
|
528
|
-
FlirLogger.log(.connection, "Authentication pending... attempt \(attempts + 1)")
|
|
516
|
+
var status = FLIRAuthenticationStatus.pending
|
|
517
|
+
while status == .pending {
|
|
518
|
+
status = cam.authenticate(identity, trustedConnectionName: certName)
|
|
519
|
+
if status == .pending {
|
|
520
|
+
FlirLogger.log(.connection, "Waiting for camera authentication approval...")
|
|
529
521
|
Thread.sleep(forTimeInterval: 1.0)
|
|
530
|
-
attempts += 1
|
|
531
522
|
}
|
|
532
523
|
}
|
|
533
|
-
FlirLogger.log(.connection, "Authentication status: \(
|
|
534
|
-
if authStatus == .pending {
|
|
535
|
-
FlirLogger.logError(.connection, "Authentication timed out for device: \(identity.deviceId())")
|
|
536
|
-
DispatchQueue.main.async { [weak self] in
|
|
537
|
-
self?.delegate?.onError("Authentication timed out")
|
|
538
|
-
}
|
|
539
|
-
return
|
|
540
|
-
}
|
|
524
|
+
FlirLogger.log(.connection, "Authentication status: \(status.rawValue)")
|
|
541
525
|
}
|
|
542
526
|
|
|
543
527
|
do {
|
|
@@ -551,7 +535,6 @@ import ThermalSDK
|
|
|
551
535
|
FlirLogger.log(.connection, "Step 2: Connecting to device...")
|
|
552
536
|
try cam.connect()
|
|
553
537
|
FlirLogger.log(.connection, "✅ Connected successfully to: \(identity.deviceId())")
|
|
554
|
-
|
|
555
538
|
|
|
556
539
|
// Update state
|
|
557
540
|
connectedIdentity = identity
|
|
@@ -649,9 +632,10 @@ import ThermalSDK
|
|
|
649
632
|
FlirLogger.log(.streaming, "Stopping stream...")
|
|
650
633
|
|
|
651
634
|
#if FLIR_ENABLED
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
635
|
+
stream?.stop()
|
|
636
|
+
stream = nil
|
|
637
|
+
streamer = nil
|
|
638
|
+
_isStreaming = false
|
|
655
639
|
emitStateChange("connected")
|
|
656
640
|
FlirLogger.log(.streaming, "Stream stopped")
|
|
657
641
|
#endif
|
|
@@ -676,28 +660,6 @@ import ThermalSDK
|
|
|
676
660
|
_isStreaming = true
|
|
677
661
|
emitStateChange("streaming")
|
|
678
662
|
FlirLogger.log(.streaming, "✅ Stream started successfully (thermal=\(newStream.isThermal))")
|
|
679
|
-
|
|
680
|
-
// Schedule a short watchdog to detect silent no-frame scenarios.
|
|
681
|
-
streamWatchdogWorkItem?.cancel()
|
|
682
|
-
let watch = DispatchWorkItem { [weak self] in
|
|
683
|
-
guard let self = self else { return }
|
|
684
|
-
if self._latestImage == nil {
|
|
685
|
-
FlirLogger.logError(.streaming, "No frames received within watchdog period after stream start")
|
|
686
|
-
// Attempt a soft restart
|
|
687
|
-
do {
|
|
688
|
-
self.stream?.stop()
|
|
689
|
-
try self.stream?.start()
|
|
690
|
-
FlirLogger.log(.streaming, "Attempted stream restart")
|
|
691
|
-
} catch {
|
|
692
|
-
FlirLogger.logError(.streaming, "Stream restart failed", error: error)
|
|
693
|
-
DispatchQueue.main.async {
|
|
694
|
-
self.delegate?.onError("No frames received after starting stream")
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
streamWatchdogWorkItem = watch
|
|
700
|
-
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0, execute: watch)
|
|
701
663
|
} catch {
|
|
702
664
|
FlirLogger.logError(.streaming, "Stream start failed", error: error)
|
|
703
665
|
stream = nil
|
|
@@ -977,12 +939,15 @@ extension FlirManager: FLIRDiscoveryEventDelegate {
|
|
|
977
939
|
public func discoveryError(_ error: String, netServiceError nsnetserviceserror: Int32, on iface: FLIRCommunicationInterface) {
|
|
978
940
|
FlirLogger.logError(.discovery, "Discovery error: \(error) (code=\(nsnetserviceserror)) on interface: \(iface)")
|
|
979
941
|
|
|
980
|
-
// Stop scanning and
|
|
942
|
+
// Stop scanning and cancel timeout on error
|
|
943
|
+
discoveryTimeoutWorkItem?.cancel()
|
|
944
|
+
discoveryTimeoutWorkItem = nil
|
|
981
945
|
discovery?.stop()
|
|
982
946
|
isScanning = false
|
|
947
|
+
|
|
948
|
+
// Emit current device list (could be empty) so RN/UI can recover
|
|
983
949
|
DispatchQueue.main.async { [weak self] in
|
|
984
950
|
guard let self = self else { return }
|
|
985
|
-
// Re-emit device list (could be empty) so JS/UI can recover
|
|
986
951
|
self.delegate?.onDevicesFound(self.discoveredDevices)
|
|
987
952
|
self.delegate?.onError("Discovery error: \(error)")
|
|
988
953
|
}
|
|
@@ -991,23 +956,27 @@ extension FlirManager: FLIRDiscoveryEventDelegate {
|
|
|
991
956
|
public func discoveryFinished(_ iface: FLIRCommunicationInterface) {
|
|
992
957
|
FlirLogger.log(.discovery, "Discovery finished on interface: \(iface)")
|
|
993
958
|
isScanning = false
|
|
994
|
-
|
|
959
|
+
|
|
960
|
+
// Cancel timeout since discovery finished normally
|
|
961
|
+
discoveryTimeoutWorkItem?.cancel()
|
|
962
|
+
discoveryTimeoutWorkItem = nil
|
|
963
|
+
|
|
964
|
+
// CRITICAL: Emit final device list so RN layer doesn't hang waiting for results
|
|
995
965
|
DispatchQueue.main.async { [weak self] in
|
|
996
966
|
guard let self = self else { return }
|
|
997
967
|
self.delegate?.onDevicesFound(self.discoveredDevices)
|
|
998
|
-
// If no devices were found, emit
|
|
968
|
+
// If no devices were found, emit explicit state so UI can show "no devices"
|
|
999
969
|
if self.discoveredDevices.isEmpty {
|
|
1000
970
|
self.emitStateChange("no_device_found")
|
|
1001
971
|
}
|
|
1002
972
|
}
|
|
1003
|
-
// Cancel the timeout if discovery finished normally
|
|
1004
|
-
discoveryTimeoutWorkItem?.cancel()
|
|
1005
|
-
discoveryTimeoutWorkItem = nil
|
|
1006
973
|
}
|
|
1007
974
|
}
|
|
975
|
+
#endif
|
|
1008
976
|
|
|
1009
977
|
// MARK: - FLIRDataReceivedDelegate
|
|
1010
978
|
|
|
979
|
+
#if FLIR_ENABLED
|
|
1011
980
|
extension FlirManager: FLIRDataReceivedDelegate {
|
|
1012
981
|
public func onDisconnected(_ camera: FLIRCamera, withError error: Error?) {
|
|
1013
982
|
FlirLogger.logError(.disconnect, "Camera disconnected callback", error: error)
|
|
@@ -1029,9 +998,12 @@ extension FlirManager: FLIRDataReceivedDelegate {
|
|
|
1029
998
|
}
|
|
1030
999
|
}
|
|
1031
1000
|
}
|
|
1001
|
+
#endif
|
|
1032
1002
|
|
|
1033
1003
|
// MARK: - FLIRStreamDelegate
|
|
1034
1004
|
|
|
1005
|
+
#if FLIR_ENABLED
|
|
1006
|
+
|
|
1035
1007
|
extension FlirManager: FLIRStreamDelegate {
|
|
1036
1008
|
public func onError(_ error: Error) {
|
|
1037
1009
|
FlirLogger.logError(.streaming, "Stream error", error: error)
|
|
@@ -155,14 +155,9 @@ RCT_EXPORT_METHOD(addListener : (NSString *)eventName) {
|
|
|
155
155
|
if (manager) {
|
|
156
156
|
NSArray *devices = ((NSArray * (*)(id, SEL))
|
|
157
157
|
objc_msgSend)(manager, sel_registerName("getDiscoveredDevices"));
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if (devices) {
|
|
161
|
-
NSLog(@"[FlirModule] addListener - re-emitting discovered devices (count=%lu)", (unsigned long)devices.count);
|
|
158
|
+
if (devices && devices.count > 0) {
|
|
159
|
+
NSLog(@"[FlirModule] addListener - re-emitting %lu discovered devices", (unsigned long)devices.count);
|
|
162
160
|
[self onDevicesFound:devices];
|
|
163
|
-
} else {
|
|
164
|
-
NSLog(@"[FlirModule] addListener - no discovered devices available to re-emit");
|
|
165
|
-
[self onDevicesFound:@[]];
|
|
166
161
|
}
|
|
167
162
|
}
|
|
168
163
|
});
|