ilabs-flir 2.4.5 → 2.4.7
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.
|
@@ -86,16 +86,6 @@ object FlirManager {
|
|
|
86
86
|
|
|
87
87
|
fun setPalette(name: String) {
|
|
88
88
|
sdkManager?.setPalette(name)
|
|
89
|
-
// Also try to update the app's global Var.cool if possible
|
|
90
|
-
try {
|
|
91
|
-
val palettes = getAvailablePalettes()
|
|
92
|
-
val idx = palettes.indexOfFirst { it.equals(name, ignoreCase = true) }
|
|
93
|
-
if (idx != -1) {
|
|
94
|
-
updateAcol(idx.toFloat())
|
|
95
|
-
}
|
|
96
|
-
} catch (e: Exception) {
|
|
97
|
-
Log.e(TAG, "setPalette: Failed to update Var.cool", e)
|
|
98
|
-
}
|
|
99
89
|
}
|
|
100
90
|
|
|
101
91
|
fun getAvailablePalettes(): List<String> {
|
|
@@ -254,20 +244,19 @@ object FlirManager {
|
|
|
254
244
|
val bitmap = latestBitmap ?: return null
|
|
255
245
|
|
|
256
246
|
// Map UI normalized (0..1) to Raw sensor normalized (0..1) based on display rotation
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
else -> Pair(1.0 - ny, nx) // Default to -90 for backward compatibility
|
|
263
|
-
}
|
|
247
|
+
// Using generic trigonometric rotation formula for total precision
|
|
248
|
+
val angle = -rotation.toDouble() // Inverse the display rotation
|
|
249
|
+
val rad = Math.toRadians(angle)
|
|
250
|
+
val cosA = Math.cos(rad)
|
|
251
|
+
val sinA = Math.sin(rad)
|
|
264
252
|
|
|
265
|
-
|
|
266
|
-
val
|
|
253
|
+
// Rotate around center (0.5, 0.5)
|
|
254
|
+
val dx = nx - 0.5
|
|
255
|
+
val dy = ny - 0.5
|
|
256
|
+
val rawX = dx * cosA - dy * sinA + 0.5
|
|
257
|
+
val rawY = dx * sinA + dy * cosA + 0.5
|
|
267
258
|
|
|
268
|
-
|
|
269
|
-
val py = (rawY * bitmap.height).toInt().coerceIn(0, bitmap.height - 1)
|
|
270
|
-
return getTemperatureAt(px, py)
|
|
259
|
+
return FlirSdkManager.getInstance(reactContext).getTemperatureAtNormalized(rawX, rawY)
|
|
271
260
|
}
|
|
272
261
|
|
|
273
262
|
fun getTemperatureAtPoint(x: Int, y: Int): Double? = getTemperatureAt(x, y)
|
|
@@ -464,15 +453,6 @@ object FlirManager {
|
|
|
464
453
|
}
|
|
465
454
|
|
|
466
455
|
coolField?.set(null, shaderIdx)
|
|
467
|
-
|
|
468
|
-
// Standard FLIR palette list
|
|
469
|
-
val paletteNames = getAvailablePalettes()
|
|
470
|
-
val maxEff = paletteNames.size
|
|
471
|
-
val paletteIdx = rawIdx % maxEff
|
|
472
|
-
val safeIdx = if (paletteIdx < 0) paletteIdx + maxEff else paletteIdx
|
|
473
|
-
|
|
474
|
-
val targetPaletteName = paletteNames[safeIdx]
|
|
475
|
-
sdkManager?.setPalette(targetPaletteName)
|
|
476
456
|
|
|
477
457
|
} catch (e: Throwable) {
|
|
478
458
|
Log.w(TAG, "updateAcol reflection failed: ${e.message}")
|
|
@@ -34,7 +34,7 @@ class FlirModule(private val reactContext: ReactApplicationContext) : ReactConte
|
|
|
34
34
|
// Simple placeholder conversion: converts an ARGB color to a pseudo-temperature value.
|
|
35
35
|
// Replace with SDK call when integrating thermalsdk APIs.
|
|
36
36
|
@ReactMethod
|
|
37
|
-
fun getTemperatureFromColor(color: Int, promise:
|
|
37
|
+
fun getTemperatureFromColor(color: Int, promise: Promise?) {
|
|
38
38
|
try {
|
|
39
39
|
val r = (color shr 16) and 0xFF
|
|
40
40
|
val g = (color shr 8) and 0xFF
|
|
@@ -305,17 +305,18 @@ class FlirModule(private val reactContext: ReactApplicationContext) : ReactConte
|
|
|
305
305
|
|
|
306
306
|
@ReactMethod
|
|
307
307
|
fun stopFlir(promise: Promise?) {
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
308
|
+
if (isDebounced()) {
|
|
309
|
+
promise?.resolve(false)
|
|
310
|
+
return
|
|
311
|
+
}
|
|
312
|
+
try {
|
|
313
|
+
FlirManager.stop()
|
|
314
|
+
promise?.resolve(true)
|
|
315
|
+
} catch (e: Exception) {
|
|
316
|
+
promise?.reject("ERR_FLIR_STOP", e)
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
319
320
|
|
|
320
321
|
@ReactMethod
|
|
321
322
|
fun simulateFlirContextLoss(promise: Promise?) {
|
|
@@ -440,7 +441,7 @@ class FlirModule(private val reactContext: ReactApplicationContext) : ReactConte
|
|
|
440
441
|
}
|
|
441
442
|
|
|
442
443
|
@ReactMethod
|
|
443
|
-
fun getDebugInfo(promise:
|
|
444
|
+
fun getDebugInfo(promise: Promise?) {
|
|
444
445
|
try {
|
|
445
446
|
val result = com.facebook.react.bridge.Arguments.createMap()
|
|
446
447
|
|
|
@@ -134,12 +134,17 @@ public class FlirSdkManager {
|
|
|
134
134
|
notifyError("SDK not initialized");
|
|
135
135
|
return;
|
|
136
136
|
}
|
|
137
|
-
if (isScanning)
|
|
138
|
-
|
|
137
|
+
if (isScanning) {
|
|
138
|
+
Log.d(TAG, "Discovery already running, ensuring clean state...");
|
|
139
|
+
try {
|
|
140
|
+
DiscoveryFactory.getInstance().stop();
|
|
141
|
+
} catch (Throwable ignored) {}
|
|
142
|
+
isScanning = false;
|
|
143
|
+
}
|
|
139
144
|
|
|
140
145
|
isScanning = true;
|
|
141
146
|
discoveredDevices.clear();
|
|
142
|
-
Log.d(TAG, "Starting discovery...");
|
|
147
|
+
Log.d(TAG, "Starting discovery for all interfaces...");
|
|
143
148
|
|
|
144
149
|
try {
|
|
145
150
|
DiscoveryFactory.getInstance().scan(
|
|
@@ -157,19 +162,23 @@ public class FlirSdkManager {
|
|
|
157
162
|
}
|
|
158
163
|
|
|
159
164
|
public void stopScan() {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
165
|
+
executor.execute(() -> {
|
|
166
|
+
if (!isScanning) return;
|
|
167
|
+
isScanning = false;
|
|
168
|
+
stopScanInternal();
|
|
169
|
+
});
|
|
163
170
|
}
|
|
164
171
|
|
|
165
172
|
private void stopScanInternal() {
|
|
166
173
|
try {
|
|
167
|
-
Log.d(TAG, "Stopping discovery...");
|
|
174
|
+
Log.d(TAG, "Stopping all discovery scanners...");
|
|
168
175
|
// Use zero-arg stop() as seen in official samples to stop all scanners
|
|
169
176
|
DiscoveryFactory.getInstance().stop();
|
|
170
177
|
Log.d(TAG, "Discovery stopped successfully");
|
|
171
|
-
} catch (
|
|
172
|
-
|
|
178
|
+
} catch (Throwable t) {
|
|
179
|
+
// This catches the notorious 'Receiver not registered' IllegalArgumentException
|
|
180
|
+
// and any other JNI-bubbled exceptions during teardown.
|
|
181
|
+
Log.w(TAG, "Stop scan suppressed (SDK internal race): " + t.getMessage());
|
|
173
182
|
}
|
|
174
183
|
}
|
|
175
184
|
|
|
@@ -297,6 +306,10 @@ public class FlirSdkManager {
|
|
|
297
306
|
|
|
298
307
|
public void disconnect() {
|
|
299
308
|
executor.execute(() -> {
|
|
309
|
+
if (isScanning) {
|
|
310
|
+
isScanning = false;
|
|
311
|
+
stopScanInternal();
|
|
312
|
+
}
|
|
300
313
|
stopStreamInternal();
|
|
301
314
|
|
|
302
315
|
if (camera != null) {
|
|
@@ -579,6 +592,37 @@ public class FlirSdkManager {
|
|
|
579
592
|
return result[0];
|
|
580
593
|
}
|
|
581
594
|
|
|
595
|
+
/**
|
|
596
|
+
* Samples temperature using normalized coordinates (0.0 to 1.0)
|
|
597
|
+
* This avoids clamping bugs when UI dimensions differ from sensor dimensions.
|
|
598
|
+
*/
|
|
599
|
+
public double getTemperatureAtNormalized(double nx, double ny) {
|
|
600
|
+
final double[] result = { Double.NaN };
|
|
601
|
+
synchronized (this) {
|
|
602
|
+
if (streamer == null) return Double.NaN;
|
|
603
|
+
try {
|
|
604
|
+
streamer.withThermalImage(thermalImage -> {
|
|
605
|
+
try {
|
|
606
|
+
int w = thermalImage.getWidth();
|
|
607
|
+
int h = thermalImage.getHeight();
|
|
608
|
+
// Map normalized 0..1 to sensor pixels 0..w-1
|
|
609
|
+
int cx = (int) Math.max(0, Math.min(w - 1, nx * w));
|
|
610
|
+
int cy = (int) Math.max(0, Math.min(h - 1, ny * h));
|
|
611
|
+
ThermalValue value = thermalImage.getValueAt(new Point(cx, cy));
|
|
612
|
+
if (value != null) {
|
|
613
|
+
result[0] = value.asCelsius().value;
|
|
614
|
+
}
|
|
615
|
+
} catch (Exception e) {
|
|
616
|
+
Log.w(TAG, "Normalized temp query error", e);
|
|
617
|
+
}
|
|
618
|
+
});
|
|
619
|
+
} catch (Exception e) {
|
|
620
|
+
Log.w(TAG, "Normalized temp query failed", e);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
return result[0];
|
|
624
|
+
}
|
|
625
|
+
|
|
582
626
|
// ==================== LISTENERS ====================
|
|
583
627
|
|
|
584
628
|
public void setPalette(String paletteName) {
|
|
@@ -478,22 +478,17 @@ import ThermalSDK
|
|
|
478
478
|
let h = Double(thermalImage.getHeight())
|
|
479
479
|
|
|
480
480
|
// Map UI normalized (0..1) to Raw sensor normalized (0..1) based on display rotation
|
|
481
|
-
|
|
482
|
-
|
|
481
|
+
// Using generic trigonometric rotation formula for total precision
|
|
482
|
+
let angle = -Double(rotation) // Inverse the display rotation
|
|
483
|
+
let rad = angle * .pi / 180.0
|
|
484
|
+
let cosA = cos(rad)
|
|
485
|
+
let sinA = sin(rad)
|
|
483
486
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
rawX = 1.0 - nx
|
|
490
|
-
rawY = 1.0 - y
|
|
491
|
-
case 270, -90:
|
|
492
|
-
rawX = 1.0 - y
|
|
493
|
-
rawY = nx
|
|
494
|
-
default:
|
|
495
|
-
break // 0 deg or default
|
|
496
|
-
}
|
|
487
|
+
// Rotate around center (0.5, 0.5)
|
|
488
|
+
let dx = nx - 0.5
|
|
489
|
+
let dy = y - 0.5
|
|
490
|
+
let rawX = dx * cosA - dy * sinA + 0.5
|
|
491
|
+
let rawY = dx * sinA + dy * cosA + 0.5
|
|
497
492
|
|
|
498
493
|
// Map normalized (0.0 - 1.0) to actual sensor pixels
|
|
499
494
|
let cx = max(0, min(Int(w) - 1, Int(rawX * w)))
|