ilabs-flir 2.2.8 → 2.2.10
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.
|
@@ -413,7 +413,7 @@ import ThermalSDK
|
|
|
413
413
|
discoveryTimeoutWorkItem?.cancel()
|
|
414
414
|
let timeoutWork = DispatchWorkItem { [weak self] in
|
|
415
415
|
guard let self = self, self.isScanning else { return }
|
|
416
|
-
FlirLogger.log(.discovery, "⏱
|
|
416
|
+
FlirLogger.log(.discovery, "❌ ⏱ DISCOVERY TIMEOUT triggered (8s) - stopping scan")
|
|
417
417
|
self.discovery?.stop()
|
|
418
418
|
self.isScanning = false
|
|
419
419
|
|
|
@@ -481,16 +481,31 @@ import ThermalSDK
|
|
|
481
481
|
|
|
482
482
|
// MARK: - Connection
|
|
483
483
|
|
|
484
|
+
// Flag to prevent re-entrant connections
|
|
485
|
+
private var isConnecting = false
|
|
486
|
+
|
|
484
487
|
@objc public func connectToDevice(_ deviceId: String) {
|
|
485
488
|
FlirLogger.logConnectionAttempt(deviceId: deviceId)
|
|
486
489
|
|
|
487
490
|
#if FLIR_ENABLED
|
|
488
|
-
// Guard: if already
|
|
489
|
-
|
|
490
|
-
FlirLogger.log(.connection, "⚠️
|
|
491
|
+
// Guard: if already connecting, skip (like demo app's guard pattern)
|
|
492
|
+
guard !isConnecting else {
|
|
493
|
+
FlirLogger.log(.connection, "⚠️ Connection already in progress - skipping")
|
|
494
|
+
return
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Guard: if already connected to this device, skip (matches demo app)
|
|
498
|
+
if _isConnected && connectedDeviceId == deviceId {
|
|
499
|
+
FlirLogger.log(.connection, "⚠️ Already connected to this device - skipping")
|
|
500
|
+
return
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// If connected to a different device, disconnect first but DON'T block
|
|
504
|
+
if _isConnected && connectedDeviceId != deviceId {
|
|
505
|
+
FlirLogger.log(.connection, "⚠️ Connected to different device - disconnecting first...")
|
|
491
506
|
disconnect()
|
|
492
|
-
//
|
|
493
|
-
|
|
507
|
+
// Don't block! The new connection will proceed immediately
|
|
508
|
+
// The disconnect is async and will complete in the background
|
|
494
509
|
}
|
|
495
510
|
|
|
496
511
|
// Find the identity for this device
|
|
@@ -503,32 +518,14 @@ import ThermalSDK
|
|
|
503
518
|
return
|
|
504
519
|
}
|
|
505
520
|
|
|
506
|
-
|
|
507
|
-
let timeoutSeconds: Double = 15.0
|
|
508
|
-
var connectionCompleted = false
|
|
509
|
-
let connectionQueue = DispatchQueue.global(qos: .userInitiated)
|
|
510
|
-
|
|
511
|
-
// Start connection attempt
|
|
512
|
-
connectionQueue.async { [weak self] in
|
|
513
|
-
self?.performConnection(identity: identity)
|
|
514
|
-
connectionCompleted = true
|
|
515
|
-
}
|
|
521
|
+
isConnecting = true
|
|
516
522
|
|
|
517
|
-
//
|
|
518
|
-
DispatchQueue.global(
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
// Force cleanup
|
|
524
|
-
self.camera = nil
|
|
525
|
-
self._isConnected = false
|
|
526
|
-
self.connectedDeviceId = nil
|
|
527
|
-
|
|
528
|
-
DispatchQueue.main.async {
|
|
529
|
-
self.emitStateChange("connection_failed")
|
|
530
|
-
self.delegate?.onError("Connection timeout - device not responding")
|
|
531
|
-
}
|
|
523
|
+
// Run connection on background thread - simple pattern matching demo app
|
|
524
|
+
// Demo app uses: DispatchQueue.global().async { try pair(); try connect(); try stream.start() }
|
|
525
|
+
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
|
|
526
|
+
guard let self = self else { return }
|
|
527
|
+
self.performConnection(identity: identity)
|
|
528
|
+
self.isConnecting = false
|
|
532
529
|
}
|
|
533
530
|
#else
|
|
534
531
|
FlirLogger.logError(.connection, "FLIR SDK not available")
|
|
@@ -537,6 +534,7 @@ import ThermalSDK
|
|
|
537
534
|
}
|
|
538
535
|
#endif
|
|
539
536
|
}
|
|
537
|
+
|
|
540
538
|
|
|
541
539
|
#if FLIR_ENABLED
|
|
542
540
|
private func findIdentity(for deviceId: String) -> FLIRIdentity? {
|
|
@@ -546,6 +544,8 @@ import ThermalSDK
|
|
|
546
544
|
private func performConnection(identity: FLIRIdentity) {
|
|
547
545
|
// Use the proven connection pattern from FLIR SDK samples:
|
|
548
546
|
// FLIROneCameraSwift uses: pair(identity, code:) then connect()
|
|
547
|
+
let startTime = Date()
|
|
548
|
+
FlirLogger.log(.connection, "⏱ performConnection STARTED for: \(identity.deviceId())")
|
|
549
549
|
FlirLogger.log(.connection, "performConnection starting for: \(identity.deviceId())")
|
|
550
550
|
|
|
551
551
|
if camera == nil {
|
|
@@ -563,45 +563,42 @@ import ThermalSDK
|
|
|
563
563
|
}
|
|
564
564
|
|
|
565
565
|
// Handle authentication for generic cameras (network cameras)
|
|
566
|
+
// Note: This uses a polling approach since the SDK doesn't provide async callback
|
|
567
|
+
// Reduced polling frequency to minimize blocking
|
|
566
568
|
if identity.cameraType() == .generic {
|
|
567
|
-
FlirLogger.log(.connection, "Generic/network camera -
|
|
569
|
+
FlirLogger.log(.connection, "Generic/network camera - attempting authentication...")
|
|
568
570
|
let certName = getCertificateName()
|
|
569
|
-
|
|
570
|
-
var attempts = 0
|
|
571
|
-
let maxAttempts = 10 // 10 seconds max
|
|
572
|
-
|
|
573
|
-
while status == .pending && attempts < maxAttempts {
|
|
574
|
-
status = cam.authenticate(identity, trustedConnectionName: certName)
|
|
575
|
-
if status == .pending {
|
|
576
|
-
FlirLogger.log(.connection, "Waiting for camera authentication approval... (\(attempts + 1)/\(maxAttempts))")
|
|
577
|
-
Thread.sleep(forTimeInterval: 1.0)
|
|
578
|
-
attempts += 1
|
|
579
|
-
}
|
|
580
|
-
}
|
|
571
|
+
let status = cam.authenticate(identity, trustedConnectionName: certName)
|
|
581
572
|
|
|
582
573
|
if status == .pending {
|
|
583
|
-
|
|
574
|
+
// For pending auth, just log and continue - user needs to approve on camera
|
|
575
|
+
FlirLogger.log(.connection, "Authentication pending - camera may require approval")
|
|
576
|
+
// Don't block here - proceed with connection attempt
|
|
577
|
+
// If auth is required, the connect() call will fail appropriately
|
|
578
|
+
} else if status == .notUsed || status == .trusted {
|
|
579
|
+
FlirLogger.log(.connection, "✅ Authentication status: \(status.rawValue)")
|
|
580
|
+
} else {
|
|
581
|
+
FlirLogger.logError(.connection, "Authentication failed with status: \(status.rawValue)")
|
|
584
582
|
DispatchQueue.main.async { [weak self] in
|
|
585
|
-
self?.
|
|
583
|
+
self?.emitStateChange("connection_failed")
|
|
584
|
+
self?.delegate?.onError("Camera authentication failed")
|
|
586
585
|
}
|
|
587
586
|
return
|
|
588
587
|
}
|
|
589
|
-
|
|
590
|
-
FlirLogger.log(.connection, "Authentication status: \(status.rawValue)")
|
|
591
588
|
}
|
|
592
589
|
|
|
593
590
|
do {
|
|
594
591
|
// Step 1: Pair with identity (required for FLIR One devices)
|
|
595
592
|
// The code parameter is for BLE pairing, 0 for direct connection
|
|
596
|
-
FlirLogger.log(.connection, "Step 1:
|
|
593
|
+
FlirLogger.log(.connection, "⏱ Step 1: PAIRING starting... [time since start: \(Date().timeIntervalSince(startTime))s]")
|
|
597
594
|
try cam.pair(identity, code: 0)
|
|
598
|
-
FlirLogger.log(.connection, "✅
|
|
595
|
+
FlirLogger.log(.connection, "✅ Step 1: PAIRED successfully [time: \(Date().timeIntervalSince(startTime))s]")
|
|
599
596
|
|
|
600
597
|
// Step 2: Connect (no identity parameter - uses paired identity)
|
|
601
598
|
// Note: This can hang on some devices - ensure we have timeout in place
|
|
602
|
-
FlirLogger.log(.connection, "Step 2:
|
|
599
|
+
FlirLogger.log(.connection, "⏱ Step 2: CONNECTING starting... [time: \(Date().timeIntervalSince(startTime))s]")
|
|
603
600
|
try cam.connect()
|
|
604
|
-
FlirLogger.log(.connection, "✅
|
|
601
|
+
FlirLogger.log(.connection, "✅ Step 2: CONNECTED successfully [time: \(Date().timeIntervalSince(startTime))s]")
|
|
605
602
|
|
|
606
603
|
// Update state
|
|
607
604
|
connectedIdentity = identity
|
|
@@ -702,9 +699,15 @@ import ThermalSDK
|
|
|
702
699
|
}
|
|
703
700
|
|
|
704
701
|
@objc public func stopStream() {
|
|
702
|
+
#if FLIR_ENABLED
|
|
703
|
+
// Guard: if not streaming, skip to avoid redundant operations
|
|
704
|
+
guard _isStreaming || stream != nil else {
|
|
705
|
+
FlirLogger.log(.streaming, "Not streaming - skipping stopStream")
|
|
706
|
+
return
|
|
707
|
+
}
|
|
708
|
+
|
|
705
709
|
FlirLogger.log(.streaming, "Stopping stream...")
|
|
706
710
|
|
|
707
|
-
#if FLIR_ENABLED
|
|
708
711
|
stream?.stop()
|
|
709
712
|
stream = nil
|
|
710
713
|
streamer = nil
|
|
@@ -737,6 +740,9 @@ import ThermalSDK
|
|
|
737
740
|
FlirLogger.logError(.streaming, "Stream start failed", error: error)
|
|
738
741
|
stream = nil
|
|
739
742
|
streamer = nil
|
|
743
|
+
_isStreaming = false
|
|
744
|
+
// Emit state change so RN knows streaming failed
|
|
745
|
+
emitStateChange("connected") // Fall back to connected (not streaming)
|
|
740
746
|
delegate?.onError("Stream start failed: \(error.localizedDescription)")
|
|
741
747
|
}
|
|
742
748
|
}
|
|
@@ -745,9 +751,15 @@ import ThermalSDK
|
|
|
745
751
|
// MARK: - Disconnect
|
|
746
752
|
|
|
747
753
|
@objc public func disconnect() {
|
|
754
|
+
#if FLIR_ENABLED
|
|
755
|
+
// Guard: if already disconnected, do nothing (prevents re-entrant issues)
|
|
756
|
+
guard _isConnected || stream != nil || camera != nil else {
|
|
757
|
+
FlirLogger.log(.disconnect, "Already disconnected - skipping")
|
|
758
|
+
return
|
|
759
|
+
}
|
|
760
|
+
|
|
748
761
|
FlirLogger.log(.disconnect, "Disconnecting...")
|
|
749
762
|
|
|
750
|
-
#if FLIR_ENABLED
|
|
751
763
|
// Stop battery polling
|
|
752
764
|
stopBatteryPolling()
|
|
753
765
|
|
|
@@ -887,6 +899,12 @@ import ThermalSDK
|
|
|
887
899
|
// MARK: - State Emission
|
|
888
900
|
|
|
889
901
|
private func emitStateChange(_ state: String) {
|
|
902
|
+
let timestamp = Date()
|
|
903
|
+
let formatter = DateFormatter()
|
|
904
|
+
formatter.dateFormat = "HH:mm:ss.SSS"
|
|
905
|
+
let timeStr = formatter.string(from: timestamp)
|
|
906
|
+
FlirLogger.log(.connection, "⏱ [\(timeStr)] EMITTING STATE to RN: '\(state)'")
|
|
907
|
+
|
|
890
908
|
DispatchQueue.main.async { [weak self] in
|
|
891
909
|
guard let self = self else { return }
|
|
892
910
|
self.delegate?.onStateChanged(
|
|
@@ -895,6 +913,7 @@ import ThermalSDK
|
|
|
895
913
|
isStreaming: self._isStreaming,
|
|
896
914
|
isEmulator: self.isEmulator
|
|
897
915
|
)
|
|
916
|
+
FlirLogger.log(.connection, "✅ [\(timeStr)] State '\(state)' emitted to RN")
|
|
898
917
|
}
|
|
899
918
|
}
|
|
900
919
|
|
|
@@ -204,12 +204,15 @@ RCT_EXPORT_METHOD(setNetworkDiscoveryEnabled : (BOOL)enabled resolver : (
|
|
|
204
204
|
|
|
205
205
|
RCT_EXPORT_METHOD(startDiscovery : (RCTPromiseResolveBlock)
|
|
206
206
|
resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
207
|
+
NSLog(@"[FlirModule] [%@] ⏱ RN->startDiscovery called", [NSDate date]);
|
|
207
208
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
208
209
|
id manager = flir_manager_shared();
|
|
209
210
|
if (manager &&
|
|
210
211
|
[manager respondsToSelector:sel_registerName("startDiscovery")]) {
|
|
212
|
+
NSLog(@"[FlirModule] [%@] ⏱ Calling FlirManager.startDiscovery", [NSDate date]);
|
|
211
213
|
((void (*)(id, SEL))objc_msgSend)(manager,
|
|
212
214
|
sel_registerName("startDiscovery"));
|
|
215
|
+
NSLog(@"[FlirModule] [%@] ⏱ FlirManager.startDiscovery returned", [NSDate date]);
|
|
213
216
|
}
|
|
214
217
|
resolve(@(YES));
|
|
215
218
|
});
|
|
@@ -250,13 +253,12 @@ RCT_EXPORT_METHOD(getDiscoveredDevices : (RCTPromiseResolveBlock)
|
|
|
250
253
|
|
|
251
254
|
RCT_EXPORT_METHOD(connectToDevice : (NSString *)deviceId resolver : (
|
|
252
255
|
RCTPromiseResolveBlock)resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
256
|
+
NSLog(@"[FlirModule] [%@] ⏱ RN->connectToDevice called for: %@", [NSDate date], deviceId);
|
|
253
257
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
254
|
-
NSLog(@"[FlirModule] connectToDevice called for: %@", deviceId);
|
|
255
|
-
|
|
256
258
|
id manager = flir_manager_shared();
|
|
257
259
|
if (manager &&
|
|
258
260
|
[manager respondsToSelector:sel_registerName("connectToDevice:")]) {
|
|
259
|
-
NSLog(@"[FlirModule] Calling FlirManager.connectToDevice");
|
|
261
|
+
NSLog(@"[FlirModule] [%@] ⏱ Calling FlirManager.connectToDevice", [NSDate date]);
|
|
260
262
|
|
|
261
263
|
// Store callbacks for event-driven updates (but don't block on them)
|
|
262
264
|
self.connectResolve = nil; // Don't use promise for blocking
|
|
@@ -266,10 +268,12 @@ RCT_EXPORT_METHOD(connectToDevice : (NSString *)deviceId resolver : (
|
|
|
266
268
|
((void (*)(id, SEL, id))objc_msgSend)(
|
|
267
269
|
manager, sel_registerName("connectToDevice:"), deviceId);
|
|
268
270
|
|
|
271
|
+
NSLog(@"[FlirModule] [%@] ⏱ FlirManager.connectToDevice returned (async started)", [NSDate date]);
|
|
272
|
+
|
|
269
273
|
// Resolve immediately - connection status will come via events
|
|
270
274
|
resolve(@(YES));
|
|
271
275
|
} else {
|
|
272
|
-
NSLog(@"[FlirModule] FlirManager not found");
|
|
276
|
+
NSLog(@"[FlirModule] [%@] ❌ FlirManager not found", [NSDate date]);
|
|
273
277
|
reject(@"ERR_NO_MANAGER", @"FlirManager not found", nil);
|
|
274
278
|
}
|
|
275
279
|
});
|