ilabs-flir 2.1.32 → 2.1.33
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.
|
@@ -3,107 +3,98 @@
|
|
|
3
3
|
// Flir
|
|
4
4
|
//
|
|
5
5
|
// React Native bridge module for FLIR thermal camera SDK
|
|
6
|
-
//
|
|
6
|
+
// Delegate to FlirManager (Swift) for all functionality.
|
|
7
7
|
//
|
|
8
8
|
|
|
9
9
|
#import "FlirModule.h"
|
|
10
10
|
#import "FlirEventEmitter.h"
|
|
11
11
|
#import "FlirState.h"
|
|
12
|
-
#import <React/RCTLog.h>
|
|
13
12
|
#import <React/RCTBridge.h>
|
|
13
|
+
#import <React/RCTLog.h>
|
|
14
14
|
#import <objc/message.h>
|
|
15
15
|
#import <objc/runtime.h>
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
#
|
|
19
|
-
#import
|
|
20
|
-
#
|
|
21
|
-
#
|
|
17
|
+
// Import Swift-generated header for FlirManagerDelegate protocol
|
|
18
|
+
#if __has_include("Flir-Swift.h")
|
|
19
|
+
#import "Flir-Swift.h"
|
|
20
|
+
#elif __has_include(<Flir/Flir-Swift.h>)
|
|
21
|
+
#import <Flir/Flir-Swift.h>
|
|
22
22
|
#endif
|
|
23
23
|
|
|
24
|
-
//
|
|
25
|
-
// This prevents duplicate-definition and missing-symbol build failures when
|
|
26
|
-
// the Swift `FLIRManager` may or may not be available at build/link time.
|
|
24
|
+
// Helper to access FlirManager singleton
|
|
27
25
|
static id flir_manager_shared(void) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return
|
|
34
|
-
|
|
35
|
-
|
|
26
|
+
Class cls = NSClassFromString(@"Flir.FlirManager");
|
|
27
|
+
if (!cls) {
|
|
28
|
+
cls = NSClassFromString(@"FlirManager");
|
|
29
|
+
}
|
|
30
|
+
if (!cls)
|
|
31
|
+
return nil;
|
|
32
|
+
SEL sel = sel_registerName("shared");
|
|
33
|
+
if (![cls respondsToSelector:sel])
|
|
34
|
+
return nil;
|
|
35
|
+
id (*msgSend0)(id, SEL) = (id (*)(id, SEL))objc_msgSend;
|
|
36
|
+
return msgSend0((id)cls, sel);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Helper for primitives
|
|
36
40
|
static double flir_getTemperatureAtPoint(int x, int y) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return
|
|
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);
|
|
43
50
|
}
|
|
44
51
|
|
|
45
52
|
static int flir_getBatteryLevel(void) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return
|
|
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);
|
|
52
61
|
}
|
|
53
62
|
|
|
54
63
|
static BOOL flir_isBatteryCharging(void) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return
|
|
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);
|
|
61
72
|
}
|
|
62
|
-
|
|
63
73
|
static void flir_setPreferSdkRotation(BOOL prefer) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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);
|
|
70
82
|
}
|
|
71
83
|
|
|
72
84
|
static BOOL flir_isPreferSdkRotation(void) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return
|
|
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);
|
|
79
93
|
}
|
|
80
94
|
|
|
81
|
-
@interface FlirModule()
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
#endif
|
|
85
|
-
|
|
86
|
-
#if FLIR_SDK_AVAILABLE
|
|
87
|
-
@property (nonatomic, strong) FLIRDiscovery *discovery;
|
|
88
|
-
@property (nonatomic, strong) FLIRCamera *camera;
|
|
89
|
-
@property (nonatomic, strong) FLIRStream *stream;
|
|
90
|
-
@property (nonatomic, strong) FLIRThermalStreamer *streamer;
|
|
91
|
-
@property (nonatomic, strong) FLIRIdentity *connectedIdentity;
|
|
92
|
-
@property (nonatomic, strong) NSMutableDictionary<NSString *, FLIRIdentity *> *identityMap;
|
|
93
|
-
#endif
|
|
94
|
-
|
|
95
|
-
@property (nonatomic, strong) NSMutableArray<NSDictionary *> *discoveredDevices;
|
|
96
|
-
@property (nonatomic, assign) BOOL isScanning;
|
|
97
|
-
@property (nonatomic, assign) BOOL isConnected;
|
|
98
|
-
@property (nonatomic, assign) BOOL isStreaming;
|
|
99
|
-
@property (nonatomic, copy) NSString *connectedDeviceId;
|
|
100
|
-
@property (nonatomic, copy) NSString *connectedDeviceName;
|
|
101
|
-
@property (nonatomic, assign) double lastTemperature;
|
|
102
|
-
|
|
103
|
-
- (void)emitStateChange:(NSString *)state;
|
|
104
|
-
- (void)emitDeviceConnected;
|
|
105
|
-
- (void)stopStreamInternal;
|
|
106
|
-
|
|
95
|
+
@interface FlirModule () <FlirManagerDelegate>
|
|
96
|
+
@property(nonatomic, copy) RCTPromiseResolveBlock connectResolve;
|
|
97
|
+
@property(nonatomic, copy) RCTPromiseRejectBlock connectReject;
|
|
107
98
|
@end
|
|
108
99
|
|
|
109
100
|
@implementation FlirModule
|
|
@@ -111,907 +102,357 @@ static BOOL flir_isPreferSdkRotation(void) {
|
|
|
111
102
|
RCT_EXPORT_MODULE(FlirModule);
|
|
112
103
|
|
|
113
104
|
+ (BOOL)requiresMainQueueSetup {
|
|
114
|
-
|
|
105
|
+
return YES;
|
|
115
106
|
}
|
|
116
107
|
|
|
117
108
|
- (instancetype)init {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
_isScanning = NO;
|
|
124
|
-
_isConnected = NO;
|
|
125
|
-
_isStreaming = NO;
|
|
126
|
-
_lastTemperature = NAN;
|
|
109
|
+
if (self = [super init]) {
|
|
110
|
+
// Wire up delegate
|
|
111
|
+
id manager = flir_manager_shared();
|
|
112
|
+
if (manager) {
|
|
113
|
+
[manager setValue:self forKey:@"delegate"];
|
|
127
114
|
}
|
|
128
|
-
|
|
115
|
+
}
|
|
116
|
+
return self;
|
|
129
117
|
}
|
|
130
118
|
|
|
131
119
|
#pragma mark - Event Emitter Support
|
|
132
120
|
|
|
133
121
|
- (NSArray<NSString *> *)supportedEvents {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
@"FlirError",
|
|
140
|
-
@"FlirStateChanged"
|
|
141
|
-
, @"FlirBatteryUpdated"
|
|
142
|
-
];
|
|
122
|
+
return @[
|
|
123
|
+
@"FlirDeviceConnected", @"FlirDeviceDisconnected", @"FlirDevicesFound",
|
|
124
|
+
@"FlirFrameReceived", @"FlirError", @"FlirStateChanged",
|
|
125
|
+
@"FlirBatteryUpdated"
|
|
126
|
+
];
|
|
143
127
|
}
|
|
144
128
|
|
|
145
|
-
RCT_EXPORT_METHOD(addListener:(NSString *)eventName) {
|
|
146
|
-
|
|
129
|
+
RCT_EXPORT_METHOD(addListener : (NSString *)eventName) {
|
|
130
|
+
// Required for RCTEventEmitter
|
|
147
131
|
}
|
|
148
132
|
|
|
149
|
-
RCT_EXPORT_METHOD(removeListeners:(NSInteger)count) {
|
|
150
|
-
|
|
133
|
+
RCT_EXPORT_METHOD(removeListeners : (NSInteger)count) {
|
|
134
|
+
// Required for RCTEventEmitter
|
|
151
135
|
}
|
|
152
136
|
|
|
153
|
-
// Provide a class helper so other native modules can post a battery update
|
|
154
137
|
+ (void)emitBatteryUpdateWithLevel:(NSInteger)level charging:(BOOL)charging {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
//
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
138
|
+
NSDictionary *payload = @{@"level" : @(level), @"isCharging" : @(charging)};
|
|
139
|
+
[[FlirEventEmitter shared] sendDeviceEvent:@"FlirBatteryUpdated"
|
|
140
|
+
body:payload];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
#pragma mark - Methods
|
|
144
|
+
|
|
145
|
+
RCT_EXPORT_METHOD(setNetworkDiscoveryEnabled : (BOOL)enabled resolver : (
|
|
146
|
+
RCTPromiseResolveBlock)resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
147
|
+
// FlirManager uses UserDefaults directly for this too
|
|
148
|
+
id manager = flir_manager_shared();
|
|
149
|
+
if (manager &&
|
|
150
|
+
[manager
|
|
151
|
+
respondsToSelector:sel_registerName("setNetworkDiscoveryEnabled:")]) {
|
|
152
|
+
((void (*)(id, SEL, BOOL))objc_msgSend)(
|
|
153
|
+
manager, sel_registerName("setNetworkDiscoveryEnabled:"), enabled);
|
|
154
|
+
} else {
|
|
155
|
+
[[NSUserDefaults standardUserDefaults]
|
|
156
|
+
setBool:enabled
|
|
157
|
+
forKey:@"ilabsFlir.networkDiscoveryEnabled"];
|
|
158
|
+
}
|
|
159
|
+
resolve(@(YES));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
RCT_EXPORT_METHOD(startDiscovery : (RCTPromiseResolveBlock)
|
|
163
|
+
resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
164
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
165
|
+
id manager = flir_manager_shared();
|
|
166
|
+
if (manager &&
|
|
167
|
+
[manager respondsToSelector:sel_registerName("startDiscovery")]) {
|
|
168
|
+
((void (*)(id, SEL))objc_msgSend)(manager,
|
|
169
|
+
sel_registerName("startDiscovery"));
|
|
182
170
|
}
|
|
183
|
-
return NO;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
RCT_EXPORT_METHOD(setNetworkDiscoveryEnabled:(BOOL)enabled
|
|
187
|
-
resolver:(RCTPromiseResolveBlock)resolve
|
|
188
|
-
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
189
|
-
#if FLIR_SDK_AVAILABLE
|
|
190
|
-
[[NSUserDefaults standardUserDefaults] setBool:enabled forKey:@"ilabsFlir.networkDiscoveryEnabled"];
|
|
191
|
-
resolve(@(YES));
|
|
192
|
-
#else
|
|
193
|
-
resolve(@(YES));
|
|
194
|
-
#endif
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
RCT_EXPORT_METHOD(startDiscovery:(RCTPromiseResolveBlock)resolve
|
|
198
|
-
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
199
|
-
#if FLIR_SDK_AVAILABLE
|
|
200
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
201
|
-
if (self.isScanning) {
|
|
202
|
-
RCTLogInfo(@"[FlirModule] Already scanning");
|
|
203
|
-
resolve(@(YES));
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
self.isScanning = YES;
|
|
208
|
-
[self.discoveredDevices removeAllObjects];
|
|
209
|
-
[self.identityMap removeAllObjects];
|
|
210
|
-
|
|
211
|
-
// Always expose emulator options to JS/UI so the user can connect even when
|
|
212
|
-
// physical devices are not present.
|
|
213
|
-
NSDictionary *emuOne = @{
|
|
214
|
-
@"id": @"emu:FLIR_ONE",
|
|
215
|
-
@"name": @"FLIR One Emulator",
|
|
216
|
-
@"communicationType": @"EMULATOR",
|
|
217
|
-
@"isEmulator": @(YES)
|
|
218
|
-
};
|
|
219
|
-
NSDictionary *emuEdge = @{
|
|
220
|
-
@"id": @"emu:FLIR_ONE_EDGE",
|
|
221
|
-
@"name": @"FLIR One Edge Emulator",
|
|
222
|
-
@"communicationType": @"EMULATOR",
|
|
223
|
-
@"isEmulator": @(YES)
|
|
224
|
-
};
|
|
225
|
-
[self.discoveredDevices addObjectsFromArray:@[ emuOne, emuEdge ]];
|
|
226
|
-
|
|
227
|
-
NSDictionary *initialDevicesBody = @{
|
|
228
|
-
@"devices": self.discoveredDevices,
|
|
229
|
-
@"count": @(self.discoveredDevices.count)
|
|
230
|
-
};
|
|
231
|
-
[[FlirEventEmitter shared] sendDeviceEvent:@"FlirDevicesFound" body:initialDevicesBody];
|
|
232
|
-
|
|
233
|
-
if (!self.discovery) {
|
|
234
|
-
self.discovery = [[FLIRDiscovery alloc] init];
|
|
235
|
-
self.discovery.delegate = self;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Start discovery on allowed interfaces.
|
|
239
|
-
// Always include wired/BLE/emulator. Only include network when the app has
|
|
240
|
-
// Local Network usage description (or the app explicitly enabled it).
|
|
241
|
-
FLIRCommunicationInterface interfaces = FLIRCommunicationInterfaceLightning |
|
|
242
|
-
FLIRCommunicationInterfaceFlirOneWireless |
|
|
243
|
-
FLIRCommunicationInterfaceEmulator |
|
|
244
|
-
FLIRCommunicationInterfaceUSB;
|
|
245
|
-
if ([self shouldEnableNetworkDiscovery]) {
|
|
246
|
-
interfaces |= FLIRCommunicationInterfaceNetwork;
|
|
247
|
-
} else {
|
|
248
|
-
RCTLogInfo(@"[FlirModule] Network discovery disabled (missing NSLocalNetworkUsageDescription or overridden)");
|
|
249
|
-
}
|
|
250
|
-
[self.discovery start:interfaces];
|
|
251
|
-
|
|
252
|
-
[self emitStateChange:@"discovering"];
|
|
253
|
-
RCTLogInfo(@"[FlirModule] Discovery started");
|
|
254
|
-
|
|
255
|
-
__weak typeof(self) weakSelf = self;
|
|
256
|
-
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
257
|
-
__strong typeof(self) strongSelf = weakSelf;
|
|
258
|
-
if (!strongSelf) return;
|
|
259
|
-
if (!strongSelf.isScanning || strongSelf.isConnected) return;
|
|
260
|
-
|
|
261
|
-
BOOL hasRealDevice = NO;
|
|
262
|
-
for (NSDictionary *dev in strongSelf.discoveredDevices) {
|
|
263
|
-
NSString *did = dev[@"id"];
|
|
264
|
-
if (did.length > 0 && ![did hasPrefix:@"emu:"]) {
|
|
265
|
-
hasRealDevice = YES;
|
|
266
|
-
break;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
if (!hasRealDevice) {
|
|
270
|
-
[strongSelf emitStateChange:@"no_device_found"];
|
|
271
|
-
}
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
resolve(@(YES));
|
|
275
|
-
});
|
|
276
|
-
#else
|
|
277
|
-
reject(@"ERR_FLIR_NOT_AVAILABLE", @"FLIR SDK not available", nil);
|
|
278
|
-
#endif
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
RCT_EXPORT_METHOD(stopDiscovery:(RCTPromiseResolveBlock)resolve
|
|
282
|
-
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
283
|
-
#if FLIR_SDK_AVAILABLE
|
|
284
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
285
|
-
[self.discovery stop];
|
|
286
|
-
self.isScanning = NO;
|
|
287
|
-
RCTLogInfo(@"[FlirModule] Discovery stopped");
|
|
288
|
-
resolve(@(YES));
|
|
289
|
-
});
|
|
290
|
-
#else
|
|
291
171
|
resolve(@(YES));
|
|
292
|
-
|
|
172
|
+
});
|
|
293
173
|
}
|
|
294
174
|
|
|
295
|
-
RCT_EXPORT_METHOD(
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
RCT_EXPORT_METHOD(connectToDevice:(NSString *)deviceId
|
|
305
|
-
resolver:(RCTPromiseResolveBlock)resolve
|
|
306
|
-
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
307
|
-
#if FLIR_SDK_AVAILABLE
|
|
308
|
-
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
309
|
-
// Synthetic emulator ids exposed during discovery.
|
|
310
|
-
if ([deviceId hasPrefix:@"emu:"]) {
|
|
311
|
-
NSString *typePart = [deviceId substringFromIndex:4];
|
|
312
|
-
FLIRCameraType cameraType = FLIRCameraType_flirOne;
|
|
313
|
-
if ([typePart.lowercaseString containsString:@"edge"]) {
|
|
314
|
-
cameraType = FLIRCameraType_flirOneEdge;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
FLIRIdentity *emulatorIdentity = [[FLIRIdentity alloc] initWithEmulatorType:cameraType];
|
|
318
|
-
if (!emulatorIdentity) {
|
|
319
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
320
|
-
reject(@"ERR_EMULATOR_INIT", @"Failed to create emulator identity", nil);
|
|
321
|
-
});
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
self.identityMap[[emulatorIdentity deviceId]] = emulatorIdentity;
|
|
326
|
-
|
|
327
|
-
[self performConnectionWithIdentity:emulatorIdentity completion:^(BOOL success, NSError *error) {
|
|
328
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
329
|
-
if (success) {
|
|
330
|
-
resolve(@(YES));
|
|
331
|
-
} else {
|
|
332
|
-
reject(@"ERR_CONNECTION_FAILED", error.localizedDescription ?: @"Connection failed", error);
|
|
333
|
-
}
|
|
334
|
-
});
|
|
335
|
-
}];
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
FLIRIdentity *identity = self.identityMap[deviceId];
|
|
340
|
-
if (!identity) {
|
|
341
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
342
|
-
reject(@"ERR_DEVICE_NOT_FOUND", [NSString stringWithFormat:@"Device not found: %@", deviceId], nil);
|
|
343
|
-
});
|
|
344
|
-
return;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
[self performConnectionWithIdentity:identity completion:^(BOOL success, NSError *error) {
|
|
348
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
349
|
-
if (success) {
|
|
350
|
-
resolve(@(YES));
|
|
351
|
-
} else {
|
|
352
|
-
reject(@"ERR_CONNECTION_FAILED", error.localizedDescription ?: @"Connection failed", error);
|
|
353
|
-
}
|
|
354
|
-
});
|
|
355
|
-
}];
|
|
356
|
-
});
|
|
357
|
-
#else
|
|
358
|
-
reject(@"ERR_FLIR_NOT_AVAILABLE", @"FLIR SDK not available", nil);
|
|
359
|
-
#endif
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
#if FLIR_SDK_AVAILABLE
|
|
363
|
-
- (void)performConnectionWithIdentity:(FLIRIdentity *)identity completion:(void(^)(BOOL success, NSError *error))completion {
|
|
364
|
-
if (!self.camera) {
|
|
365
|
-
self.camera = [[FLIRCamera alloc] init];
|
|
366
|
-
self.camera.delegate = self;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
NSError *error = nil;
|
|
370
|
-
|
|
371
|
-
// Handle authentication for generic cameras (network cameras)
|
|
372
|
-
if ([identity cameraType] == FLIRCameraType_generic) {
|
|
373
|
-
NSString *certName = [self getCertificateName];
|
|
374
|
-
FLIRAuthenticationStatus status = pending;
|
|
375
|
-
while (status == pending) {
|
|
376
|
-
status = [self.camera authenticate:identity trustedConnectionName:certName];
|
|
377
|
-
if (status == pending) {
|
|
378
|
-
RCTLogInfo(@"[FlirModule] Waiting for camera authentication...");
|
|
379
|
-
[NSThread sleepForTimeInterval:1.0];
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
RCTLogInfo(@"[FlirModule] Authentication status: %d", (int)status);
|
|
175
|
+
RCT_EXPORT_METHOD(stopDiscovery : (RCTPromiseResolveBlock)
|
|
176
|
+
resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
177
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
178
|
+
id manager = flir_manager_shared();
|
|
179
|
+
if (manager &&
|
|
180
|
+
[manager respondsToSelector:sel_registerName("stopDiscovery")]) {
|
|
181
|
+
((void (*)(id, SEL))objc_msgSend)(manager,
|
|
182
|
+
sel_registerName("stopDiscovery"));
|
|
383
183
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
BOOL connected = NO;
|
|
402
|
-
@try {
|
|
403
|
-
if (![self.camera connect:&error]) {
|
|
404
|
-
RCTLogError(@"[FlirModule] Connect failed: %@", error.localizedDescription);
|
|
405
|
-
if (completion) completion(NO, error);
|
|
406
|
-
return;
|
|
407
|
-
}
|
|
408
|
-
connected = YES;
|
|
409
|
-
RCTLogInfo(@"[FlirModule] Connected to: %@", [identity deviceId]);
|
|
410
|
-
} @catch (NSException *exception) {
|
|
411
|
-
RCTLogError(@"[FlirModule] Connect exception: %@", exception.reason);
|
|
412
|
-
error = [NSError errorWithDomain:@"FlirModule" code:1002 userInfo:@{NSLocalizedDescriptionKey: exception.reason ?: @"Connect failed"}];
|
|
413
|
-
connected = NO;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
if (connected) {
|
|
417
|
-
self.connectedIdentity = identity;
|
|
418
|
-
self.connectedDeviceId = [identity deviceId];
|
|
419
|
-
NSString *displayName = [identity deviceId];
|
|
420
|
-
if ([identity communicationInterface] == FLIRCommunicationInterfaceEmulator) {
|
|
421
|
-
if ([identity cameraType] == FLIRCameraType_flirOneEdge || [identity cameraType] == FLIRCameraType_flirOneEdgePro) {
|
|
422
|
-
displayName = @"FLIR One Edge Emulator";
|
|
423
|
-
} else {
|
|
424
|
-
displayName = @"FLIR One Emulator";
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
self.connectedDeviceName = displayName;
|
|
428
|
-
self.isConnected = YES;
|
|
429
|
-
|
|
430
|
-
RCTLogInfo(@"[FlirModule] Successfully connected to: %@", displayName);
|
|
431
|
-
|
|
432
|
-
// Get available streams and prefer thermal stream
|
|
433
|
-
NSArray<FLIRStream *> *streams = [self.camera getStreams];
|
|
434
|
-
if (streams.count > 0) {
|
|
435
|
-
RCTLogInfo(@"[FlirModule] Found %lu stream(s)", (unsigned long)streams.count);
|
|
436
|
-
|
|
437
|
-
// Find thermal stream (preferred) or use first stream
|
|
438
|
-
FLIRStream *streamToStart = nil;
|
|
439
|
-
for (FLIRStream *stream in streams) {
|
|
440
|
-
if (stream.isThermal) {
|
|
441
|
-
streamToStart = stream;
|
|
442
|
-
break;
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
if (!streamToStart) {
|
|
446
|
-
streamToStart = streams[0];
|
|
447
|
-
}
|
|
448
|
-
[self startStreamInternal:streamToStart];
|
|
184
|
+
resolve(@(YES));
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
RCT_EXPORT_METHOD(getDiscoveredDevices : (RCTPromiseResolveBlock)
|
|
189
|
+
resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
190
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
191
|
+
id manager = flir_manager_shared();
|
|
192
|
+
NSMutableArray *arr = [NSMutableArray new];
|
|
193
|
+
if (manager &&
|
|
194
|
+
[manager respondsToSelector:sel_registerName("getDiscoveredDevices")]) {
|
|
195
|
+
NSArray *devs = ((NSArray * (*)(id, SEL)) objc_msgSend)(
|
|
196
|
+
manager, sel_registerName("getDiscoveredDevices"));
|
|
197
|
+
for (id d in devs) {
|
|
198
|
+
if ([d respondsToSelector:sel_registerName("toDictionary")]) {
|
|
199
|
+
[arr addObject:((NSDictionary * (*)(id, SEL)) objc_msgSend)(
|
|
200
|
+
d, sel_registerName("toDictionary"))];
|
|
449
201
|
}
|
|
450
|
-
|
|
451
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
452
|
-
[self emitDeviceConnected];
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
if (completion) completion(YES, nil);
|
|
456
|
-
} else {
|
|
457
|
-
RCTLogError(@"[FlirModule] Connection failed: %@", error.localizedDescription);
|
|
458
|
-
self.camera = nil;
|
|
459
|
-
if (completion) completion(NO, error);
|
|
202
|
+
}
|
|
460
203
|
}
|
|
204
|
+
resolve(arr);
|
|
205
|
+
});
|
|
461
206
|
}
|
|
462
207
|
|
|
208
|
+
RCT_EXPORT_METHOD(connectToDevice : (NSString *)deviceId resolver : (
|
|
209
|
+
RCTPromiseResolveBlock)resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
210
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
211
|
+
self.connectResolve = resolve;
|
|
212
|
+
self.connectReject = reject;
|
|
463
213
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
214
|
+
id manager = flir_manager_shared();
|
|
215
|
+
if (manager &&
|
|
216
|
+
[manager respondsToSelector:sel_registerName("connectToDevice:")]) {
|
|
217
|
+
((void (*)(id, SEL, id))objc_msgSend)(
|
|
218
|
+
manager, sel_registerName("connectToDevice:"), deviceId);
|
|
219
|
+
} else {
|
|
220
|
+
reject(@"ERR_NO_MANAGER", @"FlirManager not found", nil);
|
|
221
|
+
self.connectResolve = nil;
|
|
222
|
+
self.connectReject = nil;
|
|
471
223
|
}
|
|
472
|
-
|
|
473
|
-
NSString *newName = [[NSUUID UUID] UUIDString];
|
|
474
|
-
[[NSUserDefaults standardUserDefaults] setObject:newName forKey:key];
|
|
475
|
-
return newName;
|
|
224
|
+
});
|
|
476
225
|
}
|
|
477
|
-
#endif
|
|
478
226
|
|
|
479
|
-
RCT_EXPORT_METHOD(disconnect:(RCTPromiseResolveBlock)
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
[
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
self.connectedDeviceName = nil;
|
|
489
|
-
self.isConnected = NO;
|
|
490
|
-
self.isStreaming = NO;
|
|
491
|
-
|
|
492
|
-
[[FlirEventEmitter shared] sendDeviceEvent:@"FlirDeviceDisconnected" body:@{}];
|
|
493
|
-
[self emitStateChange:@"disconnected"];
|
|
494
|
-
|
|
495
|
-
RCTLogInfo(@"[FlirModule] Disconnected");
|
|
496
|
-
resolve(@(YES));
|
|
497
|
-
});
|
|
498
|
-
#else
|
|
227
|
+
RCT_EXPORT_METHOD(disconnect : (RCTPromiseResolveBlock)
|
|
228
|
+
resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
229
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
230
|
+
id manager = flir_manager_shared();
|
|
231
|
+
if (manager &&
|
|
232
|
+
[manager respondsToSelector:sel_registerName("disconnect")]) {
|
|
233
|
+
((void (*)(id, SEL))objc_msgSend)(manager,
|
|
234
|
+
sel_registerName("disconnect"));
|
|
235
|
+
}
|
|
499
236
|
resolve(@(YES));
|
|
500
|
-
|
|
237
|
+
});
|
|
501
238
|
}
|
|
502
239
|
|
|
503
|
-
RCT_EXPORT_METHOD(stopFlir:(RCTPromiseResolveBlock)
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
self.camera = nil;
|
|
512
|
-
self.connectedIdentity = nil;
|
|
513
|
-
self.connectedDeviceId = nil;
|
|
514
|
-
self.connectedDeviceName = nil;
|
|
515
|
-
self.isConnected = NO;
|
|
516
|
-
self.isStreaming = NO;
|
|
517
|
-
self.isScanning = NO;
|
|
518
|
-
|
|
519
|
-
[self emitStateChange:@"stopped"];
|
|
520
|
-
RCTLogInfo(@"[FlirModule] Stopped");
|
|
521
|
-
resolve(@(YES));
|
|
522
|
-
});
|
|
523
|
-
#else
|
|
240
|
+
RCT_EXPORT_METHOD(stopFlir : (RCTPromiseResolveBlock)
|
|
241
|
+
resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
242
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
243
|
+
id manager = flir_manager_shared();
|
|
244
|
+
if (manager && [manager respondsToSelector:sel_registerName("stop")]) {
|
|
245
|
+
((void (*)(id, SEL))objc_msgSend)(manager, sel_registerName("stop"));
|
|
246
|
+
}
|
|
524
247
|
resolve(@(YES));
|
|
525
|
-
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
RCT_EXPORT_METHOD(startEmulator : (NSString *)emulatorType resolver : (
|
|
252
|
+
RCTPromiseResolveBlock)resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
253
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
254
|
+
self.connectResolve = resolve;
|
|
255
|
+
self.connectReject = reject;
|
|
256
|
+
id manager = flir_manager_shared();
|
|
257
|
+
if (manager && [manager respondsToSelector:sel_registerName(
|
|
258
|
+
"startEmulatorWithType:")]) {
|
|
259
|
+
// Swift: startEmulator(type: String) -> exposed as startEmulatorWithType:
|
|
260
|
+
// ? Or startEmulatorWith? Swift default naming: startEmulator(type:) ->
|
|
261
|
+
// startEmulatorWithType:
|
|
262
|
+
((void (*)(id, SEL, id))objc_msgSend)(
|
|
263
|
+
manager, sel_registerName("startEmulatorWithType:"), emulatorType);
|
|
264
|
+
} else {
|
|
265
|
+
// Fallback if selector assumption wrong/mismatch
|
|
266
|
+
reject(@"ERR_NOT_IMPL",
|
|
267
|
+
@"startEmulator not implemented or signature mismatch", nil);
|
|
268
|
+
self.connectResolve = nil;
|
|
269
|
+
self.connectReject = nil;
|
|
270
|
+
}
|
|
271
|
+
});
|
|
526
272
|
}
|
|
527
273
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
[
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
self.streamer = [[FLIRThermalStreamer alloc] initWithStream:newStream];
|
|
274
|
+
RCT_EXPORT_METHOD(getTemperatureAt : (nonnull NSNumber *)x y : (
|
|
275
|
+
nonnull NSNumber *)y resolver : (RCTPromiseResolveBlock)
|
|
276
|
+
resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
277
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
278
|
+
double temp = flir_getTemperatureAtPoint([x intValue], [y intValue]);
|
|
279
|
+
if (isnan(temp)) {
|
|
280
|
+
resolve([NSNull null]);
|
|
281
|
+
} else {
|
|
282
|
+
resolve(@(temp));
|
|
538
283
|
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
RCT_EXPORT_METHOD(getTemperatureFromColor : (NSInteger)color resolver : (
|
|
288
|
+
RCTPromiseResolveBlock)resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
289
|
+
int r = (color >> 16) & 0xFF;
|
|
290
|
+
int g = (color >> 8) & 0xFF;
|
|
291
|
+
int b = color & 0xFF;
|
|
292
|
+
double lum = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
293
|
+
double temp = (lum / 255.0) * 400.0;
|
|
294
|
+
resolve(@(temp));
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
RCT_EXPORT_METHOD(isEmulator : (RCTPromiseResolveBlock)
|
|
298
|
+
resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
299
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
300
|
+
id manager = flir_manager_shared();
|
|
301
|
+
BOOL isEm = NO;
|
|
302
|
+
if (manager &&
|
|
303
|
+
[manager respondsToSelector:sel_registerName("isEmulator")]) {
|
|
304
|
+
isEm = ((BOOL (*)(id, SEL))objc_msgSend)(manager,
|
|
305
|
+
sel_registerName("isEmulator"));
|
|
558
306
|
}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
307
|
+
resolve(@(isEm));
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
RCT_EXPORT_METHOD(isDeviceConnected : (RCTPromiseResolveBlock)
|
|
312
|
+
resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
313
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
314
|
+
id manager = flir_manager_shared();
|
|
315
|
+
BOOL isC = NO;
|
|
316
|
+
if (manager &&
|
|
317
|
+
[manager respondsToSelector:sel_registerName("isConnected")]) {
|
|
318
|
+
isC = ((BOOL (*)(id, SEL))objc_msgSend)(manager,
|
|
319
|
+
sel_registerName("isConnected"));
|
|
320
|
+
}
|
|
321
|
+
resolve(@(isC));
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
RCT_EXPORT_METHOD(getConnectedDeviceInfo : (RCTPromiseResolveBlock)
|
|
326
|
+
resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
327
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
328
|
+
id manager = flir_manager_shared();
|
|
329
|
+
NSString *info = @"Not connected";
|
|
330
|
+
if (manager && [manager respondsToSelector:sel_registerName(
|
|
331
|
+
"getConnectedDeviceInfo")]) {
|
|
332
|
+
info = ((NSString * (*)(id, SEL)) objc_msgSend)(
|
|
333
|
+
manager, sel_registerName("getConnectedDeviceInfo"));
|
|
334
|
+
}
|
|
335
|
+
resolve(info);
|
|
336
|
+
});
|
|
588
337
|
}
|
|
589
338
|
|
|
590
|
-
RCT_EXPORT_METHOD(
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
int r = (color >> 16) & 0xFF;
|
|
595
|
-
int g = (color >> 8) & 0xFF;
|
|
596
|
-
int b = color & 0xFF;
|
|
597
|
-
double lum = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
598
|
-
double temp = (lum / 255.0) * 400.0;
|
|
599
|
-
resolve(@(temp));
|
|
339
|
+
RCT_EXPORT_METHOD(isSDKDownloaded : (RCTPromiseResolveBlock)
|
|
340
|
+
resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
341
|
+
// Assuming integrated SDK
|
|
342
|
+
resolve(@(YES));
|
|
600
343
|
}
|
|
601
344
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
606
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
607
|
-
BOOL isEmu = [self.connectedDeviceName.lowercaseString containsString:@"emulator"] ||
|
|
608
|
-
[self.connectedDeviceName.lowercaseString containsString:@"emulat"];
|
|
609
|
-
resolve(@(isEmu));
|
|
610
|
-
});
|
|
345
|
+
RCT_EXPORT_METHOD(getSDKStatus : (RCTPromiseResolveBlock)
|
|
346
|
+
resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
347
|
+
resolve(@{@"available" : @(YES), @"arch" : @"arm64", @"platform" : @"iOS"});
|
|
611
348
|
}
|
|
612
349
|
|
|
613
|
-
RCT_EXPORT_METHOD(
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
350
|
+
RCT_EXPORT_METHOD(getBatteryLevel : (RCTPromiseResolveBlock)
|
|
351
|
+
resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
352
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
353
|
+
int level = flir_getBatteryLevel();
|
|
354
|
+
resolve(@(level));
|
|
355
|
+
});
|
|
618
356
|
}
|
|
619
357
|
|
|
620
|
-
RCT_EXPORT_METHOD(
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
358
|
+
RCT_EXPORT_METHOD(isBatteryCharging : (RCTPromiseResolveBlock)
|
|
359
|
+
resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
360
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
361
|
+
BOOL ch = flir_isBatteryCharging();
|
|
362
|
+
resolve(@(ch));
|
|
363
|
+
});
|
|
625
364
|
}
|
|
626
365
|
|
|
627
|
-
RCT_EXPORT_METHOD(
|
|
628
|
-
|
|
629
|
-
|
|
366
|
+
RCT_EXPORT_METHOD(setPreferSdkRotation : (BOOL)prefer resolver : (
|
|
367
|
+
RCTPromiseResolveBlock)resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
368
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
369
|
+
flir_setPreferSdkRotation(prefer);
|
|
630
370
|
resolve(@(YES));
|
|
631
|
-
|
|
632
|
-
resolve(@(NO));
|
|
633
|
-
#endif
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
RCT_EXPORT_METHOD(getSDKStatus:(RCTPromiseResolveBlock)resolve
|
|
637
|
-
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
638
|
-
NSDictionary *status = @{
|
|
639
|
-
#if FLIR_SDK_AVAILABLE
|
|
640
|
-
@"available": @(YES),
|
|
641
|
-
#else
|
|
642
|
-
@"available": @(NO),
|
|
643
|
-
#endif
|
|
644
|
-
@"arch": @"arm64",
|
|
645
|
-
@"platform": @"iOS"
|
|
646
|
-
};
|
|
647
|
-
resolve(status);
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
#pragma mark - Emulator
|
|
651
|
-
|
|
652
|
-
RCT_EXPORT_METHOD(startEmulator:(NSString *)emulatorType
|
|
653
|
-
resolver:(RCTPromiseResolveBlock)resolve
|
|
654
|
-
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
655
|
-
#if FLIR_SDK_AVAILABLE
|
|
656
|
-
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
657
|
-
FLIRCameraType cameraType = FLIRCameraType_flirOne;
|
|
658
|
-
if ([emulatorType.lowercaseString containsString:@"edge"]) {
|
|
659
|
-
cameraType = FLIRCameraType_flirOneEdge;
|
|
660
|
-
} else if ([emulatorType.lowercaseString containsString:@"pro"]) {
|
|
661
|
-
cameraType = FLIRCameraType_flirOneEdgePro;
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
FLIRIdentity *emulatorIdentity = [[FLIRIdentity alloc] initWithEmulatorType:cameraType];
|
|
665
|
-
if (emulatorIdentity) {
|
|
666
|
-
self.identityMap[[emulatorIdentity deviceId]] = emulatorIdentity;
|
|
667
|
-
|
|
668
|
-
[self performConnectionWithIdentity:emulatorIdentity completion:^(BOOL success, NSError *error) {
|
|
669
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
670
|
-
if (success) {
|
|
671
|
-
resolve(@(YES));
|
|
672
|
-
} else {
|
|
673
|
-
reject(@"ERR_EMULATOR_FAILED", error.localizedDescription ?: @"Emulator start failed", error);
|
|
674
|
-
}
|
|
675
|
-
});
|
|
676
|
-
}];
|
|
677
|
-
} else {
|
|
678
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
679
|
-
reject(@"ERR_EMULATOR_INIT", @"Failed to create emulator identity", nil);
|
|
680
|
-
});
|
|
681
|
-
}
|
|
682
|
-
});
|
|
683
|
-
#else
|
|
684
|
-
reject(@"ERR_FLIR_NOT_AVAILABLE", @"FLIR SDK not available", nil);
|
|
685
|
-
#endif
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
#pragma mark - Debug
|
|
689
|
-
|
|
690
|
-
RCT_EXPORT_METHOD(initializeSDK:(RCTPromiseResolveBlock)resolve
|
|
691
|
-
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
692
|
-
NSDictionary *result = @{
|
|
693
|
-
#if FLIR_SDK_AVAILABLE
|
|
694
|
-
@"initialized": @(YES),
|
|
695
|
-
@"message": @"SDK initialized successfully"
|
|
696
|
-
#else
|
|
697
|
-
@"initialized": @(NO),
|
|
698
|
-
@"message": @"SDK not available - built without FLIR"
|
|
699
|
-
#endif
|
|
700
|
-
};
|
|
701
|
-
resolve(result);
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
RCT_EXPORT_METHOD(getDebugInfo:(RCTPromiseResolveBlock)resolve
|
|
705
|
-
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
706
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
707
|
-
NSDictionary *info = @{
|
|
708
|
-
#if FLIR_SDK_AVAILABLE
|
|
709
|
-
@"sdkAvailable": @(YES),
|
|
710
|
-
#else
|
|
711
|
-
@"sdkAvailable": @(NO),
|
|
712
|
-
#endif
|
|
713
|
-
@"arch": @"arm64",
|
|
714
|
-
@"discoveredDeviceCount": @(self.discoveredDevices.count),
|
|
715
|
-
@"isConnected": @(self.isConnected),
|
|
716
|
-
@"isStreaming": @(self.isStreaming),
|
|
717
|
-
@"connectedDevice": self.connectedDeviceName ?: @"None"
|
|
718
|
-
};
|
|
719
|
-
resolve(info);
|
|
720
|
-
});
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
RCT_EXPORT_METHOD(getLatestFramePath:(RCTPromiseResolveBlock)resolve
|
|
724
|
-
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
725
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
726
|
-
UIImage *image = [FlirState shared].latestImage;
|
|
727
|
-
if (!image) {
|
|
728
|
-
resolve([NSNull null]);
|
|
729
|
-
return;
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
NSData *jpegData = UIImageJPEGRepresentation(image, 0.9);
|
|
733
|
-
if (!jpegData) {
|
|
734
|
-
resolve([NSNull null]);
|
|
735
|
-
return;
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
NSString *tempPath = [NSTemporaryDirectory() stringByAppendingPathComponent:
|
|
739
|
-
[NSString stringWithFormat:@"flir_frame_%lld.jpg", (long long)[[NSDate date] timeIntervalSince1970] * 1000]];
|
|
740
|
-
[jpegData writeToFile:tempPath atomically:YES];
|
|
741
|
-
resolve(tempPath);
|
|
742
|
-
});
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
RCT_EXPORT_METHOD(getBatteryLevel:(RCTPromiseResolveBlock)resolve
|
|
746
|
-
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
747
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
748
|
-
#if FLIR_SDK_AVAILABLE
|
|
749
|
-
int level = flir_getBatteryLevel();
|
|
750
|
-
resolve(@(level));
|
|
751
|
-
#else
|
|
752
|
-
resolve(@(-1));
|
|
753
|
-
#endif
|
|
754
|
-
});
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
RCT_EXPORT_METHOD(isBatteryCharging:(RCTPromiseResolveBlock)resolve
|
|
758
|
-
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
759
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
760
|
-
#if FLIR_SDK_AVAILABLE
|
|
761
|
-
BOOL ch = flir_isBatteryCharging();
|
|
762
|
-
resolve(@(ch));
|
|
763
|
-
#else
|
|
764
|
-
resolve(@(NO));
|
|
765
|
-
#endif
|
|
766
|
-
});
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
RCT_EXPORT_METHOD(setPreferSdkRotation:(BOOL)prefer
|
|
770
|
-
resolver:(RCTPromiseResolveBlock)resolve
|
|
771
|
-
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
772
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
773
|
-
@try {
|
|
774
|
-
flir_setPreferSdkRotation(prefer);
|
|
775
|
-
resolve(@(YES));
|
|
776
|
-
} @catch (NSException *ex) {
|
|
777
|
-
reject(@"ERR_FLIR_SET_ROTATION_PREF", ex.reason, nil);
|
|
778
|
-
}
|
|
779
|
-
});
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
RCT_EXPORT_METHOD(isPreferSdkRotation:(RCTPromiseResolveBlock)resolve
|
|
783
|
-
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
784
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
785
|
-
BOOL v = flir_isPreferSdkRotation();
|
|
786
|
-
resolve(@(v));
|
|
787
|
-
});
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
#pragma mark - Helper Methods
|
|
791
|
-
|
|
792
|
-
- (void)emitDeviceConnected {
|
|
793
|
-
[self emitStateChange:@"connected"];
|
|
371
|
+
});
|
|
794
372
|
}
|
|
795
373
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
isEmu = [self.connectedDeviceName.lowercaseString containsString:@"emulator"];
|
|
803
|
-
}
|
|
804
|
-
#else
|
|
805
|
-
isEmu = [self.connectedDeviceName.lowercaseString containsString:@"emulator"];
|
|
806
|
-
#endif
|
|
807
|
-
|
|
808
|
-
NSDictionary *body = @{
|
|
809
|
-
@"state": state,
|
|
810
|
-
@"isConnected": @(self.isConnected),
|
|
811
|
-
@"isStreaming": @(self.isStreaming),
|
|
812
|
-
@"isEmulator": @(isEmu),
|
|
813
|
-
@"deviceName": self.connectedDeviceName ?: @"",
|
|
814
|
-
@"deviceId": self.connectedDeviceId ?: @"",
|
|
815
|
-
@"identity": @{
|
|
816
|
-
@"deviceId": self.connectedDeviceId ?: @"",
|
|
817
|
-
@"isEmulator": @(isEmu)
|
|
818
|
-
}
|
|
819
|
-
};
|
|
820
|
-
|
|
821
|
-
// App JS listens for FlirDeviceConnected state transitions.
|
|
822
|
-
[[FlirEventEmitter shared] sendDeviceEvent:@"FlirDeviceConnected" body:body];
|
|
823
|
-
|
|
824
|
-
// Keep legacy event for backwards compatibility.
|
|
825
|
-
[[FlirEventEmitter shared] sendDeviceEvent:@"FlirStateChanged" body:body];
|
|
374
|
+
RCT_EXPORT_METHOD(isPreferSdkRotation : (RCTPromiseResolveBlock)
|
|
375
|
+
resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
376
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
377
|
+
BOOL v = flir_isPreferSdkRotation();
|
|
378
|
+
resolve(@(v));
|
|
379
|
+
});
|
|
826
380
|
}
|
|
827
381
|
|
|
828
|
-
#
|
|
829
|
-
- (NSString *)communicationInterfaceName:(FLIRCommunicationInterface)iface {
|
|
830
|
-
if (iface & FLIRCommunicationInterfaceLightning) return @"LIGHTNING";
|
|
831
|
-
if (iface & FLIRCommunicationInterfaceNetwork) return @"NETWORK";
|
|
832
|
-
if (iface & FLIRCommunicationInterfaceFlirOneWireless) return @"WIRELESS";
|
|
833
|
-
if (iface & FLIRCommunicationInterfaceEmulator) return @"EMULATOR";
|
|
834
|
-
if (iface & FLIRCommunicationInterfaceUSB) return @"USB";
|
|
835
|
-
return @"UNKNOWN";
|
|
836
|
-
}
|
|
837
|
-
#endif
|
|
382
|
+
#pragma mark - FlirManagerDelegate
|
|
838
383
|
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
RCTLogInfo(@"[FlirModule] Camera discovered: %@", deviceId);
|
|
847
|
-
|
|
848
|
-
// Store identity
|
|
849
|
-
self.identityMap[deviceId] = identity;
|
|
850
|
-
|
|
851
|
-
// Create device info
|
|
852
|
-
NSDictionary *deviceInfo = @{
|
|
853
|
-
@"id": deviceId,
|
|
854
|
-
@"name": discoveredCamera.displayName ?: deviceId,
|
|
855
|
-
@"communicationType": [self communicationInterfaceName:[identity communicationInterface]],
|
|
856
|
-
@"isEmulator": @([identity communicationInterface] == FLIRCommunicationInterfaceEmulator)
|
|
857
|
-
};
|
|
858
|
-
|
|
859
|
-
// Add if not already present
|
|
860
|
-
BOOL found = NO;
|
|
861
|
-
for (NSDictionary *existing in self.discoveredDevices) {
|
|
862
|
-
if ([existing[@"id"] isEqualToString:deviceId]) {
|
|
863
|
-
found = YES;
|
|
864
|
-
break;
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
if (!found) {
|
|
869
|
-
[self.discoveredDevices addObject:deviceInfo];
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
// Emit devices found event
|
|
873
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
874
|
-
NSDictionary *body = @{
|
|
875
|
-
@"devices": self.discoveredDevices,
|
|
876
|
-
@"count": @(self.discoveredDevices.count)
|
|
877
|
-
};
|
|
878
|
-
[[FlirEventEmitter shared] sendDeviceEvent:@"FlirDevicesFound" body:body];
|
|
879
|
-
});
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
- (void)cameraLost:(FLIRIdentity *)cameraIdentity {
|
|
883
|
-
NSString *deviceId = [cameraIdentity deviceId];
|
|
884
|
-
RCTLogInfo(@"[FlirModule] Camera lost: %@", deviceId);
|
|
885
|
-
|
|
886
|
-
[self.identityMap removeObjectForKey:deviceId];
|
|
887
|
-
|
|
888
|
-
NSMutableArray *toRemove = [NSMutableArray new];
|
|
889
|
-
for (NSDictionary *device in self.discoveredDevices) {
|
|
890
|
-
if ([device[@"id"] isEqualToString:deviceId]) {
|
|
891
|
-
[toRemove addObject:device];
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
[self.discoveredDevices removeObjectsInArray:toRemove];
|
|
895
|
-
|
|
896
|
-
// If this was our connected device, handle disconnect
|
|
897
|
-
if ([self.connectedDeviceId isEqualToString:deviceId]) {
|
|
898
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
899
|
-
[self stopStreamInternal];
|
|
900
|
-
self.camera = nil;
|
|
901
|
-
self.connectedIdentity = nil;
|
|
902
|
-
self.connectedDeviceId = nil;
|
|
903
|
-
self.connectedDeviceName = nil;
|
|
904
|
-
self.isConnected = NO;
|
|
905
|
-
self.isStreaming = NO;
|
|
906
|
-
|
|
907
|
-
[[FlirEventEmitter shared] sendDeviceEvent:@"FlirDeviceDisconnected" body:@{}];
|
|
908
|
-
[self emitStateChange:@"disconnected"];
|
|
909
|
-
});
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
913
|
-
NSDictionary *body = @{
|
|
914
|
-
@"devices": self.discoveredDevices,
|
|
915
|
-
@"count": @(self.discoveredDevices.count)
|
|
916
|
-
};
|
|
917
|
-
[[FlirEventEmitter shared] sendDeviceEvent:@"FlirDevicesFound" body:body];
|
|
918
|
-
});
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
- (void)discoveryError:(NSString *)error netServiceError:(int)nsnetserviceserror on:(FLIRCommunicationInterface)iface {
|
|
922
|
-
// Network discovery failures are expected when Local Network permission is missing/denied.
|
|
923
|
-
// Do not surface those as fatal errors; keep USB/BLE discovery running.
|
|
924
|
-
if ((iface & FLIRCommunicationInterfaceNetwork) == FLIRCommunicationInterfaceNetwork) {
|
|
925
|
-
RCTLogInfo(@"[FlirModule] Network discovery error (suppressed): %@ (%d)", error, nsnetserviceserror);
|
|
926
|
-
return;
|
|
384
|
+
- (void)onDevicesFound:(NSArray *)devices {
|
|
385
|
+
NSMutableArray *arr = [NSMutableArray new];
|
|
386
|
+
for (id d in devices) {
|
|
387
|
+
if ([d respondsToSelector:sel_registerName("toDictionary")]) {
|
|
388
|
+
[arr addObject:((NSDictionary * (*)(id, SEL))
|
|
389
|
+
objc_msgSend)(d, sel_registerName("toDictionary"))];
|
|
927
390
|
}
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
391
|
+
}
|
|
392
|
+
[[FlirEventEmitter shared]
|
|
393
|
+
sendDeviceEvent:@"FlirDevicesFound"
|
|
394
|
+
body:@{@"devices" : arr, @"count" : @(arr.count)}];
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
- (void)onDeviceConnected:(id)device {
|
|
398
|
+
if (self.connectResolve) {
|
|
399
|
+
self.connectResolve(@(YES));
|
|
400
|
+
self.connectResolve = nil;
|
|
401
|
+
self.connectReject = nil;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// device is FlirDeviceInfo
|
|
405
|
+
NSMutableDictionary *body = [NSMutableDictionary new];
|
|
406
|
+
if ([device respondsToSelector:sel_registerName("toDictionary")]) {
|
|
407
|
+
[body
|
|
408
|
+
addEntriesFromDictionary:((NSDictionary * (*)(id, SEL)) objc_msgSend)(
|
|
409
|
+
device, sel_registerName("toDictionary"))];
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
[[FlirEventEmitter shared] sendDeviceEvent:@"FlirDeviceConnected" body:body];
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
- (void)onDeviceDisconnected {
|
|
416
|
+
[[FlirEventEmitter shared] sendDeviceEvent:@"FlirDeviceDisconnected"
|
|
417
|
+
body:@{}];
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
- (void)onFrameReceived:(UIImage *)image
|
|
421
|
+
width:(NSInteger)width
|
|
422
|
+
height:(NSInteger)height {
|
|
423
|
+
// Also emit event for JS consumers (though slow, some might use it)
|
|
424
|
+
[[FlirEventEmitter shared]
|
|
425
|
+
sendDeviceEvent:@"FlirFrameReceived"
|
|
426
|
+
body:@{
|
|
427
|
+
@"width" : @(width),
|
|
428
|
+
@"height" : @(height),
|
|
429
|
+
@"timestamp" :
|
|
430
|
+
@([[NSDate date] timeIntervalSince1970] * 1000)
|
|
431
|
+
}];
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
- (void)onError:(NSString *)message {
|
|
435
|
+
if (self.connectReject) {
|
|
436
|
+
self.connectReject(@"ERR_FLIR", message, nil);
|
|
437
|
+
self.connectResolve = nil;
|
|
438
|
+
self.connectReject = nil;
|
|
439
|
+
}
|
|
440
|
+
[[FlirEventEmitter shared]
|
|
441
|
+
sendDeviceEvent:@"FlirError"
|
|
442
|
+
body:@{@"error" : message ?: @"Unknown error"}];
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
- (void)onStateChanged:(NSString *)state
|
|
446
|
+
isConnected:(BOOL)isConnected
|
|
447
|
+
isStreaming:(BOOL)isStreaming
|
|
448
|
+
isEmulator:(BOOL)isEmulator {
|
|
449
|
+
NSDictionary *body = @{
|
|
450
|
+
@"state" : state,
|
|
451
|
+
@"isConnected" : @(isConnected),
|
|
452
|
+
@"isStreaming" : @(isStreaming),
|
|
453
|
+
@"isEmulator" : @(isEmulator)
|
|
454
|
+
};
|
|
455
|
+
[[FlirEventEmitter shared] sendDeviceEvent:@"FlirStateChanged" body:body];
|
|
960
456
|
}
|
|
961
|
-
#endif
|
|
962
|
-
|
|
963
|
-
#pragma mark - FLIRStreamDelegate
|
|
964
|
-
|
|
965
|
-
#if FLIR_SDK_AVAILABLE
|
|
966
|
-
- (void)onError:(NSError *)error {
|
|
967
|
-
RCTLogError(@"[FlirModule] Stream error: %@", error.localizedDescription);
|
|
968
|
-
|
|
969
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
970
|
-
[[FlirEventEmitter shared] sendDeviceEvent:@"FlirError" body:@{
|
|
971
|
-
@"error": error.localizedDescription ?: @"Stream error",
|
|
972
|
-
@"type": @"stream"
|
|
973
|
-
}];
|
|
974
|
-
});
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
- (void)onImageReceived {
|
|
978
|
-
if (!self.streamer) return;
|
|
979
|
-
|
|
980
|
-
NSError *error = nil;
|
|
981
|
-
if ([self.streamer update:&error]) {
|
|
982
|
-
UIImage *image = [self.streamer getImage];
|
|
983
|
-
if (image) {
|
|
984
|
-
// Update shared state
|
|
985
|
-
[[FlirState shared] updateFrame:image];
|
|
986
|
-
|
|
987
|
-
// Get temperature from thermal image if available
|
|
988
|
-
[self.streamer withThermalImage:^(FLIRThermalImage *thermalImage) {
|
|
989
|
-
// Some SDK versions call getImageStatistics(), try both selectors
|
|
990
|
-
FLIRImageStatistics *stats = nil;
|
|
991
|
-
if ([thermalImage respondsToSelector:sel_registerName("getImageStatistics")]) {
|
|
992
|
-
stats = ((id (*)(id, SEL))objc_msgSend)((id)thermalImage, sel_registerName("getImageStatistics"));
|
|
993
|
-
} else if ([thermalImage respondsToSelector:sel_registerName("getStatistics")]) {
|
|
994
|
-
stats = ((id (*)(id, SEL))objc_msgSend)((id)thermalImage, sel_registerName("getStatistics"));
|
|
995
|
-
}
|
|
996
|
-
if (stats) {
|
|
997
|
-
self.lastTemperature = [[stats getMax] value];
|
|
998
|
-
[FlirState shared].lastTemperature = self.lastTemperature;
|
|
999
|
-
}
|
|
1000
|
-
}];
|
|
1001
|
-
|
|
1002
|
-
// Emit frame received event (rate-limited by RN event queue)
|
|
1003
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
1004
|
-
[[FlirEventEmitter shared] sendDeviceEvent:@"FlirFrameReceived" body:@{
|
|
1005
|
-
@"width": @(image.size.width),
|
|
1006
|
-
@"height": @(image.size.height),
|
|
1007
|
-
@"timestamp": @([[NSDate date] timeIntervalSince1970] * 1000)
|
|
1008
|
-
}];
|
|
1009
|
-
});
|
|
1010
|
-
}
|
|
1011
|
-
} else {
|
|
1012
|
-
RCTLogError(@"[FlirModule] Streamer update error: %@", error.localizedDescription);
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
#endif
|
|
1016
457
|
|
|
1017
458
|
@end
|