ilabs-flir 2.2.13 → 2.2.14
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.
|
@@ -35,6 +35,10 @@ object FlirManager {
|
|
|
35
35
|
private var isStreaming = false
|
|
36
36
|
private var connectedDeviceId: String? = null
|
|
37
37
|
private var connectedDeviceName: String? = null
|
|
38
|
+
|
|
39
|
+
// Concurrency control
|
|
40
|
+
private val shouldProcessFrames = java.util.concurrent.atomic.AtomicBoolean(false)
|
|
41
|
+
private val isUpdatingTexture = java.util.concurrent.atomic.AtomicBoolean(false)
|
|
38
42
|
|
|
39
43
|
// Latest bitmap
|
|
40
44
|
private var latestBitmap: Bitmap? = null
|
|
@@ -119,6 +123,7 @@ object FlirManager {
|
|
|
119
123
|
val identity = devices.find { it.deviceId == deviceId }
|
|
120
124
|
|
|
121
125
|
if (identity != null) {
|
|
126
|
+
shouldProcessFrames.set(true)
|
|
122
127
|
sdkManager?.connect(identity)
|
|
123
128
|
} else {
|
|
124
129
|
Log.e(TAG, "Device not found: $deviceId")
|
|
@@ -130,6 +135,7 @@ object FlirManager {
|
|
|
130
135
|
* Disconnect
|
|
131
136
|
*/
|
|
132
137
|
fun disconnect() {
|
|
138
|
+
shouldProcessFrames.set(false)
|
|
133
139
|
sdkManager?.disconnect()
|
|
134
140
|
isConnected = false
|
|
135
141
|
isStreaming = false
|
|
@@ -141,6 +147,7 @@ object FlirManager {
|
|
|
141
147
|
* Stop everything
|
|
142
148
|
*/
|
|
143
149
|
fun stop() {
|
|
150
|
+
shouldProcessFrames.set(false)
|
|
144
151
|
disconnect()
|
|
145
152
|
stopDiscovery()
|
|
146
153
|
latestBitmap = null
|
|
@@ -229,14 +236,34 @@ object FlirManager {
|
|
|
229
236
|
}
|
|
230
237
|
|
|
231
238
|
override fun onFrame(bitmap: Bitmap) {
|
|
239
|
+
// IMMEDIATE STOP CHECK
|
|
240
|
+
if (!shouldProcessFrames.get()) {
|
|
241
|
+
return
|
|
242
|
+
}
|
|
243
|
+
|
|
232
244
|
latestBitmap = bitmap
|
|
233
|
-
isStreaming = true
|
|
234
245
|
|
|
235
|
-
//
|
|
236
|
-
|
|
246
|
+
// If this is the first frame, notify JS that we are now streaming
|
|
247
|
+
if (!isStreaming) {
|
|
248
|
+
isStreaming = true
|
|
249
|
+
emitDeviceState("streaming")
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// NON-BLOCKING TEXTURE UPDATE (Drop frames if busy)
|
|
253
|
+
if (textureCallback != null) {
|
|
254
|
+
if (isUpdatingTexture.compareAndSet(false, true)) {
|
|
255
|
+
try {
|
|
256
|
+
textureCallback?.onTextureUpdate(bitmap, 0)
|
|
257
|
+
} finally {
|
|
258
|
+
isUpdatingTexture.set(false)
|
|
259
|
+
}
|
|
260
|
+
} else {
|
|
261
|
+
// Log.v(TAG, "Dropping frame - texture update busy")
|
|
262
|
+
}
|
|
263
|
+
}
|
|
237
264
|
|
|
238
|
-
// Notify RN
|
|
239
|
-
emitFrameToReactNative(bitmap)
|
|
265
|
+
// Notify RN - disabled
|
|
266
|
+
// emitFrameToReactNative(bitmap)
|
|
240
267
|
}
|
|
241
268
|
|
|
242
269
|
override fun onError(message: String) {
|
|
@@ -247,6 +274,8 @@ object FlirManager {
|
|
|
247
274
|
// React Native Emitters
|
|
248
275
|
|
|
249
276
|
private fun emitFrameToReactNative(bitmap: Bitmap) {
|
|
277
|
+
// PERF: Disabled to reduce bridge traffic
|
|
278
|
+
/*
|
|
250
279
|
val now = System.currentTimeMillis()
|
|
251
280
|
if (now - lastEmitMs.get() < minEmitIntervalMs) return
|
|
252
281
|
lastEmitMs.set(now)
|
|
@@ -261,6 +290,7 @@ object FlirManager {
|
|
|
261
290
|
ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
262
291
|
.emit("FlirFrameReceived", params)
|
|
263
292
|
} catch (e: Exception) { }
|
|
293
|
+
*/
|
|
264
294
|
}
|
|
265
295
|
|
|
266
296
|
private fun emitDeviceState(state: String) {
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
#import <React/RCTLog.h>
|
|
14
14
|
#import <objc/message.h>
|
|
15
15
|
#import <objc/runtime.h>
|
|
16
|
+
#import <stdatomic.h>
|
|
16
17
|
|
|
17
18
|
// Import Swift-generated header for FlirManagerDelegate protocol
|
|
18
19
|
#if __has_include("Flir-Swift.h")
|
|
@@ -36,61 +37,7 @@ static id flir_manager_shared(void) {
|
|
|
36
37
|
return msgSend0((id)cls, sel);
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
//
|
|
40
|
-
static double flir_getTemperatureAtPoint(int x, int y) {
|
|
41
|
-
id inst = flir_manager_shared();
|
|
42
|
-
if (!inst)
|
|
43
|
-
return NAN;
|
|
44
|
-
SEL sel = sel_registerName("getTemperatureAtPoint:y:");
|
|
45
|
-
if (![inst respondsToSelector:sel])
|
|
46
|
-
return NAN;
|
|
47
|
-
double (*msgSend2)(id, SEL, int, int) =
|
|
48
|
-
(double (*)(id, SEL, int, int))objc_msgSend;
|
|
49
|
-
return msgSend2(inst, sel, x, y);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
static int flir_getBatteryLevel(void) {
|
|
53
|
-
id inst = flir_manager_shared();
|
|
54
|
-
if (!inst)
|
|
55
|
-
return -1;
|
|
56
|
-
SEL sel = sel_registerName("getBatteryLevel");
|
|
57
|
-
if (![inst respondsToSelector:sel])
|
|
58
|
-
return -1;
|
|
59
|
-
int (*msgSend0)(id, SEL) = (int (*)(id, SEL))objc_msgSend;
|
|
60
|
-
return msgSend0(inst, sel);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
static BOOL flir_isBatteryCharging(void) {
|
|
64
|
-
id inst = flir_manager_shared();
|
|
65
|
-
if (!inst)
|
|
66
|
-
return NO;
|
|
67
|
-
SEL sel = sel_registerName("isBatteryCharging");
|
|
68
|
-
if (![inst respondsToSelector:sel])
|
|
69
|
-
return NO;
|
|
70
|
-
BOOL (*msgSend0)(id, SEL) = (BOOL (*)(id, SEL))objc_msgSend;
|
|
71
|
-
return msgSend0(inst, sel);
|
|
72
|
-
}
|
|
73
|
-
static void flir_setPreferSdkRotation(BOOL prefer) {
|
|
74
|
-
id inst = flir_manager_shared();
|
|
75
|
-
if (!inst)
|
|
76
|
-
return;
|
|
77
|
-
SEL sel = sel_registerName("setPreferSdkRotation:");
|
|
78
|
-
if (![inst respondsToSelector:sel])
|
|
79
|
-
return;
|
|
80
|
-
void (*msgSend1)(id, SEL, BOOL) = (void (*)(id, SEL, BOOL))objc_msgSend;
|
|
81
|
-
msgSend1(inst, sel, prefer);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
static BOOL flir_isPreferSdkRotation(void) {
|
|
85
|
-
id inst = flir_manager_shared();
|
|
86
|
-
if (!inst)
|
|
87
|
-
return NO;
|
|
88
|
-
SEL sel = sel_registerName("isPreferSdkRotation");
|
|
89
|
-
if (![inst respondsToSelector:sel])
|
|
90
|
-
return NO;
|
|
91
|
-
BOOL (*msgSend0)(id, SEL) = (BOOL (*)(id, SEL))objc_msgSend;
|
|
92
|
-
return msgSend0(inst, sel);
|
|
93
|
-
}
|
|
40
|
+
// ... helper primitives skipped in replace ...
|
|
94
41
|
|
|
95
42
|
@interface FlirModule () <FlirManagerDelegate>
|
|
96
43
|
@property(nonatomic, copy) RCTPromiseResolveBlock connectResolve;
|
|
@@ -99,6 +46,7 @@ static BOOL flir_isPreferSdkRotation(void) {
|
|
|
99
46
|
|
|
100
47
|
@implementation FlirModule {
|
|
101
48
|
NSInteger _listenerCount;
|
|
49
|
+
atomic_bool _isCapturing;
|
|
102
50
|
}
|
|
103
51
|
|
|
104
52
|
RCT_EXPORT_MODULE(FlirModule);
|
|
@@ -110,6 +58,7 @@ RCT_EXPORT_MODULE(FlirModule);
|
|
|
110
58
|
- (instancetype)init {
|
|
111
59
|
if (self = [super init]) {
|
|
112
60
|
_listenerCount = 0;
|
|
61
|
+
atomic_store(&_isCapturing, false);
|
|
113
62
|
// Wire up delegate
|
|
114
63
|
id manager = flir_manager_shared();
|
|
115
64
|
if (manager) {
|
|
@@ -124,8 +73,8 @@ RCT_EXPORT_MODULE(FlirModule);
|
|
|
124
73
|
- (NSArray<NSString *> *)supportedEvents {
|
|
125
74
|
return @[
|
|
126
75
|
@"FlirDeviceConnected", @"FlirDeviceDisconnected", @"FlirDevicesFound",
|
|
127
|
-
@"FlirFrameReceived", @"FlirFrameBitmapAvailable", @"FlirError",
|
|
128
|
-
@"FlirBatteryUpdated"
|
|
76
|
+
@"FlirFrameReceived", @"FlirFrameBitmapAvailable", @"FlirError",
|
|
77
|
+
@"FlirStateChanged", @"FlirBatteryUpdated"
|
|
129
78
|
];
|
|
130
79
|
}
|
|
131
80
|
|
|
@@ -140,23 +89,27 @@ RCT_EXPORT_MODULE(FlirModule);
|
|
|
140
89
|
|
|
141
90
|
RCT_EXPORT_METHOD(addListener : (NSString *)eventName) {
|
|
142
91
|
_listenerCount++;
|
|
143
|
-
NSLog(@"[FlirModule] addListener: %@ (count: %ld)", eventName,
|
|
144
|
-
|
|
92
|
+
NSLog(@"[FlirModule] addListener: %@ (count: %ld)", eventName,
|
|
93
|
+
(long)_listenerCount);
|
|
94
|
+
|
|
145
95
|
// CRITICAL: Call parent to register with RCTEventEmitter's internal tracking
|
|
146
96
|
// Without this, sendEventWithName will show "no listeners registered" warning
|
|
147
97
|
// and may not deliver events properly
|
|
148
98
|
[super addListener:eventName];
|
|
149
|
-
|
|
150
|
-
// When FlirDevicesFound listener is added, immediately emit current device
|
|
151
|
-
// This handles the case where discovery happened before React Native
|
|
99
|
+
|
|
100
|
+
// When FlirDevicesFound listener is added, immediately emit current device
|
|
101
|
+
// list This handles the case where discovery happened before React Native
|
|
102
|
+
// mounted
|
|
152
103
|
if ([eventName isEqualToString:@"FlirDevicesFound"]) {
|
|
153
104
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
154
105
|
id manager = flir_manager_shared();
|
|
155
106
|
if (manager) {
|
|
156
|
-
NSArray *devices = ((NSArray * (*)(id, SEL))
|
|
157
|
-
|
|
107
|
+
NSArray *devices = ((NSArray * (*)(id, SEL)) objc_msgSend)(
|
|
108
|
+
manager, sel_registerName("getDiscoveredDevices"));
|
|
158
109
|
if (devices && devices.count > 0) {
|
|
159
|
-
NSLog(
|
|
110
|
+
NSLog(
|
|
111
|
+
@"[FlirModule] addListener - re-emitting %lu discovered devices",
|
|
112
|
+
(unsigned long)devices.count);
|
|
160
113
|
[self onDevicesFound:devices];
|
|
161
114
|
}
|
|
162
115
|
}
|
|
@@ -166,21 +119,26 @@ RCT_EXPORT_METHOD(addListener : (NSString *)eventName) {
|
|
|
166
119
|
|
|
167
120
|
RCT_EXPORT_METHOD(removeListeners : (NSInteger)count) {
|
|
168
121
|
_listenerCount -= count;
|
|
169
|
-
if (_listenerCount < 0)
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
122
|
+
if (_listenerCount < 0)
|
|
123
|
+
_listenerCount = 0;
|
|
124
|
+
NSLog(@"[FlirModule] removeListeners: %ld (remaining: %ld)", (long)count,
|
|
125
|
+
(long)_listenerCount);
|
|
126
|
+
|
|
127
|
+
// CRITICAL: Call parent to unregister with RCTEventEmitter's internal
|
|
128
|
+
// tracking
|
|
173
129
|
[super removeListeners:count];
|
|
174
130
|
}
|
|
175
131
|
|
|
176
132
|
+ (void)emitBatteryUpdateWithLevel:(NSInteger)level charging:(BOOL)charging {
|
|
177
133
|
NSDictionary *payload = @{@"level" : @(level), @"isCharging" : @(charging)};
|
|
178
|
-
NSLog(@"[FlirModule] Emitting battery update - level: %ld, charging: %d",
|
|
179
|
-
|
|
134
|
+
NSLog(@"[FlirModule] Emitting battery update - level: %ld, charging: %d",
|
|
135
|
+
(long)level, charging);
|
|
136
|
+
|
|
180
137
|
// Note: This is a class method, so we need to get the module instance
|
|
181
|
-
// For now, we'll just log - in production you'd need to get the module
|
|
182
|
-
// or convert this to an instance method
|
|
183
|
-
// [[FlirModule sharedInstance] sendEventWithName:@"FlirBatteryUpdated"
|
|
138
|
+
// For now, we'll just log - in production you'd need to get the module
|
|
139
|
+
// instance or convert this to an instance method
|
|
140
|
+
// [[FlirModule sharedInstance] sendEventWithName:@"FlirBatteryUpdated"
|
|
141
|
+
// body:payload];
|
|
184
142
|
}
|
|
185
143
|
|
|
186
144
|
#pragma mark - Methods
|
|
@@ -209,10 +167,12 @@ RCT_EXPORT_METHOD(startDiscovery : (RCTPromiseResolveBlock)
|
|
|
209
167
|
id manager = flir_manager_shared();
|
|
210
168
|
if (manager &&
|
|
211
169
|
[manager respondsToSelector:sel_registerName("startDiscovery")]) {
|
|
212
|
-
NSLog(@"[FlirModule] [%@] ⏱ Calling FlirManager.startDiscovery",
|
|
170
|
+
NSLog(@"[FlirModule] [%@] ⏱ Calling FlirManager.startDiscovery",
|
|
171
|
+
[NSDate date]);
|
|
213
172
|
((void (*)(id, SEL))objc_msgSend)(manager,
|
|
214
173
|
sel_registerName("startDiscovery"));
|
|
215
|
-
NSLog(@"[FlirModule] [%@] ⏱ FlirManager.startDiscovery returned",
|
|
174
|
+
NSLog(@"[FlirModule] [%@] ⏱ FlirManager.startDiscovery returned",
|
|
175
|
+
[NSDate date]);
|
|
216
176
|
}
|
|
217
177
|
resolve(@(YES));
|
|
218
178
|
});
|
|
@@ -253,23 +213,30 @@ RCT_EXPORT_METHOD(getDiscoveredDevices : (RCTPromiseResolveBlock)
|
|
|
253
213
|
|
|
254
214
|
RCT_EXPORT_METHOD(connectToDevice : (NSString *)deviceId resolver : (
|
|
255
215
|
RCTPromiseResolveBlock)resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
256
|
-
NSLog(@"[FlirModule] [%@] ⏱ RN->connectToDevice called for: %@",
|
|
216
|
+
NSLog(@"[FlirModule] [%@] ⏱ RN->connectToDevice called for: %@",
|
|
217
|
+
[NSDate date], deviceId);
|
|
257
218
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
258
219
|
id manager = flir_manager_shared();
|
|
259
220
|
if (manager &&
|
|
260
221
|
[manager respondsToSelector:sel_registerName("connectToDevice:")]) {
|
|
261
|
-
NSLog(@"[FlirModule] [%@] ⏱ Calling FlirManager.connectToDevice",
|
|
262
|
-
|
|
222
|
+
NSLog(@"[FlirModule] [%@] ⏱ Calling FlirManager.connectToDevice",
|
|
223
|
+
[NSDate date]);
|
|
224
|
+
|
|
263
225
|
// Store callbacks for event-driven updates (but don't block on them)
|
|
264
|
-
self.connectResolve = nil;
|
|
226
|
+
self.connectResolve = nil; // Don't use promise for blocking
|
|
265
227
|
self.connectReject = nil;
|
|
266
|
-
|
|
228
|
+
|
|
229
|
+
// Enable capturing
|
|
230
|
+
atomic_store(&_isCapturing, true);
|
|
231
|
+
|
|
267
232
|
// Initiate connection asynchronously
|
|
268
233
|
((void (*)(id, SEL, id))objc_msgSend)(
|
|
269
234
|
manager, sel_registerName("connectToDevice:"), deviceId);
|
|
270
|
-
|
|
271
|
-
NSLog(@"[FlirModule] [%@] ⏱ FlirManager.connectToDevice returned (async
|
|
272
|
-
|
|
235
|
+
|
|
236
|
+
NSLog(@"[FlirModule] [%@] ⏱ FlirManager.connectToDevice returned (async "
|
|
237
|
+
@"started)",
|
|
238
|
+
[NSDate date]);
|
|
239
|
+
|
|
273
240
|
// Resolve immediately - connection status will come via events
|
|
274
241
|
resolve(@(YES));
|
|
275
242
|
} else {
|
|
@@ -285,6 +252,8 @@ RCT_EXPORT_METHOD(disconnect : (RCTPromiseResolveBlock)
|
|
|
285
252
|
id manager = flir_manager_shared();
|
|
286
253
|
if (manager &&
|
|
287
254
|
[manager respondsToSelector:sel_registerName("disconnect")]) {
|
|
255
|
+
atomic_store(&_isCapturing, false);
|
|
256
|
+
[[FlirState shared] reset];
|
|
288
257
|
((void (*)(id, SEL))objc_msgSend)(manager,
|
|
289
258
|
sel_registerName("disconnect"));
|
|
290
259
|
}
|
|
@@ -297,6 +266,8 @@ RCT_EXPORT_METHOD(stopFlir : (RCTPromiseResolveBlock)
|
|
|
297
266
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
298
267
|
id manager = flir_manager_shared();
|
|
299
268
|
if (manager && [manager respondsToSelector:sel_registerName("stop")]) {
|
|
269
|
+
atomic_store(&_isCapturing, false);
|
|
270
|
+
[[FlirState shared] reset];
|
|
300
271
|
((void (*)(id, SEL))objc_msgSend)(manager, sel_registerName("stop"));
|
|
301
272
|
}
|
|
302
273
|
resolve(@(YES));
|
|
@@ -307,18 +278,21 @@ RCT_EXPORT_METHOD(startEmulator : (NSString *)emulatorType resolver : (
|
|
|
307
278
|
RCTPromiseResolveBlock)resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
308
279
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
309
280
|
NSLog(@"[FlirModule] startEmulator called for type: %@", emulatorType);
|
|
310
|
-
|
|
281
|
+
|
|
311
282
|
id manager = flir_manager_shared();
|
|
312
283
|
if (manager && [manager respondsToSelector:sel_registerName(
|
|
313
284
|
"startEmulatorWithType:")]) {
|
|
314
285
|
// Store callbacks for event-driven updates (but don't block on them)
|
|
315
286
|
self.connectResolve = nil;
|
|
316
287
|
self.connectReject = nil;
|
|
317
|
-
|
|
288
|
+
|
|
289
|
+
// Enable capturing
|
|
290
|
+
atomic_store(&_isCapturing, true);
|
|
291
|
+
|
|
318
292
|
// Initiate emulator start asynchronously
|
|
319
293
|
((void (*)(id, SEL, id))objc_msgSend)(
|
|
320
294
|
manager, sel_registerName("startEmulatorWithType:"), emulatorType);
|
|
321
|
-
|
|
295
|
+
|
|
322
296
|
// Resolve immediately - connection status will come via events
|
|
323
297
|
resolve(@(YES));
|
|
324
298
|
} else {
|
|
@@ -368,14 +342,18 @@ RCT_EXPORT_METHOD(isEmulator : (RCTPromiseResolveBlock)
|
|
|
368
342
|
});
|
|
369
343
|
}
|
|
370
344
|
|
|
371
|
-
RCT_EXPORT_METHOD(getLatestFrameBitmap : (RCTPromiseResolveBlock)
|
|
345
|
+
RCT_EXPORT_METHOD(getLatestFrameBitmap : (RCTPromiseResolveBlock)
|
|
346
|
+
resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
372
347
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
373
348
|
id manager = flir_manager_shared();
|
|
374
|
-
if (!manager ||
|
|
349
|
+
if (!manager ||
|
|
350
|
+
![manager
|
|
351
|
+
respondsToSelector:sel_registerName("latestFrameBitmapBase64")]) {
|
|
375
352
|
resolve([NSNull null]);
|
|
376
353
|
return;
|
|
377
354
|
}
|
|
378
|
-
NSDictionary *dict = ((NSDictionary * (*)(id, SEL)) objc_msgSend)(
|
|
355
|
+
NSDictionary *dict = ((NSDictionary * (*)(id, SEL)) objc_msgSend)(
|
|
356
|
+
manager, sel_registerName("latestFrameBitmapBase64"));
|
|
379
357
|
if (!dict) {
|
|
380
358
|
resolve([NSNull null]);
|
|
381
359
|
} else {
|
|
@@ -465,15 +443,17 @@ RCT_EXPORT_METHOD(isPreferSdkRotation : (RCTPromiseResolveBlock)
|
|
|
465
443
|
objc_msgSend)(d, sel_registerName("toDictionary"))];
|
|
466
444
|
}
|
|
467
445
|
}
|
|
468
|
-
|
|
469
|
-
NSLog(@"[FlirModule] onDevicesFound - %lu devices, listenerCount: %ld",
|
|
470
|
-
|
|
446
|
+
|
|
447
|
+
NSLog(@"[FlirModule] onDevicesFound - %lu devices, listenerCount: %ld",
|
|
448
|
+
(unsigned long)arr.count, (long)_listenerCount);
|
|
449
|
+
|
|
471
450
|
if (_listenerCount > 0) {
|
|
472
451
|
NSLog(@"[FlirModule] emitting FlirDevicesFound event");
|
|
473
|
-
[self sendEventWithName:@"FlirDevicesFound"
|
|
452
|
+
[self sendEventWithName:@"FlirDevicesFound"
|
|
474
453
|
body:@{@"devices" : arr, @"count" : @(arr.count)}];
|
|
475
454
|
} else {
|
|
476
|
-
NSLog(@"[FlirModule] ⚠️ No listeners registered yet - devices will be
|
|
455
|
+
NSLog(@"[FlirModule] ⚠️ No listeners registered yet - devices will be "
|
|
456
|
+
@"re-emitted when listener is added");
|
|
477
457
|
}
|
|
478
458
|
}
|
|
479
459
|
|
|
@@ -485,30 +465,39 @@ RCT_EXPORT_METHOD(isPreferSdkRotation : (RCTPromiseResolveBlock)
|
|
|
485
465
|
addEntriesFromDictionary:((NSDictionary * (*)(id, SEL)) objc_msgSend)(
|
|
486
466
|
device, sel_registerName("toDictionary"))];
|
|
487
467
|
}
|
|
488
|
-
|
|
468
|
+
|
|
489
469
|
// Add state info to match Android's FlirDeviceConnected event format
|
|
490
470
|
[body setObject:@"connected" forKey:@"state"];
|
|
491
471
|
[body setObject:@(YES) forKey:@"isConnected"];
|
|
492
|
-
[body setObject:@(NO)
|
|
472
|
+
[body setObject:@(NO)
|
|
473
|
+
forKey:@"isStreaming"]; // streaming starts after connection
|
|
493
474
|
// isEmulator info should be in device dictionary already from toDictionary
|
|
494
475
|
|
|
495
|
-
NSLog(@"[FlirModule] onDeviceConnected - emitting FlirDeviceConnected event
|
|
476
|
+
NSLog(@"[FlirModule] onDeviceConnected - emitting FlirDeviceConnected event "
|
|
477
|
+
@"with state info");
|
|
496
478
|
[self sendEventWithName:@"FlirDeviceConnected" body:body];
|
|
497
479
|
}
|
|
498
480
|
|
|
499
481
|
- (void)onDeviceDisconnected {
|
|
500
|
-
NSLog(@"[FlirModule] onDeviceDisconnected - emitting FlirDeviceDisconnected
|
|
482
|
+
NSLog(@"[FlirModule] onDeviceDisconnected - emitting FlirDeviceDisconnected "
|
|
483
|
+
@"event");
|
|
501
484
|
[self sendEventWithName:@"FlirDeviceDisconnected" body:@{}];
|
|
502
485
|
}
|
|
503
486
|
|
|
504
487
|
- (void)onFrameReceived:(UIImage *)image
|
|
505
488
|
width:(NSInteger)width
|
|
506
489
|
height:(NSInteger)height {
|
|
507
|
-
|
|
508
|
-
|
|
490
|
+
|
|
491
|
+
if (!atomic_load(&_isCapturing)) {
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// CRITICAL: Update shared state so native preview (FlirPreviewView) receives
|
|
496
|
+
// the texture
|
|
509
497
|
[[FlirState shared] updateFrame:image];
|
|
510
498
|
|
|
511
|
-
//
|
|
499
|
+
// PERF: Commented out to reduce bridge traffic (JS dev not using it)
|
|
500
|
+
/*
|
|
512
501
|
[self sendEventWithName:@"FlirFrameReceived"
|
|
513
502
|
body:@{
|
|
514
503
|
@"width" : @(width),
|
|
@@ -516,16 +505,23 @@ RCT_EXPORT_METHOD(isPreferSdkRotation : (RCTPromiseResolveBlock)
|
|
|
516
505
|
@"timestamp" :
|
|
517
506
|
@([[NSDate date] timeIntervalSince1970] * 1000)
|
|
518
507
|
}];
|
|
508
|
+
*/
|
|
519
509
|
}
|
|
520
510
|
|
|
521
|
-
- (void)onFrameReceivedRaw:(NSData *)data
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
511
|
+
- (void)onFrameReceivedRaw:(NSData *)data
|
|
512
|
+
width:(NSInteger)width
|
|
513
|
+
height:(NSInteger)height
|
|
514
|
+
bytesPerRow:(NSInteger)bytesPerRow
|
|
515
|
+
timestamp:(double)timestamp {
|
|
516
|
+
// Emit a lightweight event to notify JS that a raw bitmap is available; raw
|
|
517
|
+
// bytes are available via getLatestFrameBitmap()
|
|
518
|
+
[self sendEventWithName:@"FlirFrameBitmapAvailable"
|
|
519
|
+
body:@{
|
|
520
|
+
@"width" : @(width),
|
|
521
|
+
@"height" : @(height),
|
|
522
|
+
@"bytesPerRow" : @(bytesPerRow),
|
|
523
|
+
@"timestamp" : @(timestamp)
|
|
524
|
+
}];
|
|
529
525
|
}
|
|
530
526
|
|
|
531
527
|
- (void)onError:(NSString *)message {
|
|
@@ -544,7 +540,9 @@ RCT_EXPORT_METHOD(isPreferSdkRotation : (RCTPromiseResolveBlock)
|
|
|
544
540
|
@"isStreaming" : @(isStreaming),
|
|
545
541
|
@"isEmulator" : @(isEmulator)
|
|
546
542
|
};
|
|
547
|
-
NSLog(
|
|
543
|
+
NSLog(
|
|
544
|
+
@"[FlirModule] onStateChanged - state: %@, connected: %d, streaming: %d",
|
|
545
|
+
state, isConnected, isStreaming);
|
|
548
546
|
[self sendEventWithName:@"FlirStateChanged" body:body];
|
|
549
547
|
}
|
|
550
548
|
|
package/ios/Flir/src/FlirState.m
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
#import "FlirState.h"
|
|
9
9
|
|
|
10
|
+
#import <stdatomic.h>
|
|
11
|
+
|
|
10
12
|
static FlirState *_sharedState = nil;
|
|
11
13
|
|
|
12
14
|
@implementation FlirState {
|
|
@@ -14,6 +16,7 @@ static FlirState *_sharedState = nil;
|
|
|
14
16
|
int _imageWidth;
|
|
15
17
|
int _imageHeight;
|
|
16
18
|
dispatch_queue_t _accessQueue;
|
|
19
|
+
atomic_bool _isTextureBusy;
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
+ (instancetype)shared {
|
|
@@ -33,6 +36,7 @@ static FlirState *_sharedState = nil;
|
|
|
33
36
|
_imageHeight = 0;
|
|
34
37
|
_accessQueue =
|
|
35
38
|
dispatch_queue_create("com.flir.state.access", DISPATCH_QUEUE_SERIAL);
|
|
39
|
+
atomic_store(&_isTextureBusy, false);
|
|
36
40
|
}
|
|
37
41
|
return self;
|
|
38
42
|
}
|
|
@@ -101,11 +105,18 @@ static FlirState *_sharedState = nil;
|
|
|
101
105
|
|
|
102
106
|
// Invoke texture callback on main thread (for Metal filters, texture unit 7)
|
|
103
107
|
if (self.onTextureUpdate) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
108
|
+
bool expected = false;
|
|
109
|
+
if (atomic_compare_exchange_strong(&_isTextureBusy, &expected, true)) {
|
|
110
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
111
|
+
@try {
|
|
112
|
+
if (self.onTextureUpdate) {
|
|
113
|
+
self.onTextureUpdate(image, 7);
|
|
114
|
+
}
|
|
115
|
+
} @finally {
|
|
116
|
+
atomic_store(&_isTextureBusy, false);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
109
120
|
}
|
|
110
121
|
|
|
111
122
|
// Sample temperature at center point and invoke callback
|