ilabs-flir 1.0.5 → 1.0.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.
- package/android/Flir/src/main/AndroidManifest.xml +14 -0
- package/android/Flir/src/main/java/flir/android/FlirManager.kt +312 -313
- package/android/Flir/src/main/java/flir/android/FlirModule.kt +151 -0
- package/android/Flir/src/main/java/flir/android/FlirSdkManager.java +431 -608
- package/package.json +1 -1
- package/src/index.d.ts +39 -0
|
@@ -4,39 +4,45 @@ import android.content.Context
|
|
|
4
4
|
import android.graphics.Bitmap
|
|
5
5
|
import android.util.Log
|
|
6
6
|
import com.facebook.react.bridge.Arguments
|
|
7
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
8
|
+
import com.facebook.react.bridge.ReactContext
|
|
7
9
|
import com.facebook.react.bridge.WritableArray
|
|
8
10
|
import com.facebook.react.bridge.WritableMap
|
|
9
11
|
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
10
12
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
13
|
+
import com.flir.thermalsdk.live.Identity
|
|
11
14
|
import java.io.File
|
|
12
15
|
import java.io.FileOutputStream
|
|
13
16
|
import java.util.concurrent.atomic.AtomicLong
|
|
14
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Simplified FlirManager - bridge between React Native and FlirSdkManager
|
|
20
|
+
* No filtering - returns ALL discovered devices (USB, Network, Emulator)
|
|
21
|
+
* Let React Native handle any filtering logic
|
|
22
|
+
*/
|
|
15
23
|
object FlirManager {
|
|
16
|
-
private val TAG = "FlirManager"
|
|
17
|
-
|
|
24
|
+
private const val TAG = "FlirManager"
|
|
25
|
+
|
|
18
26
|
private var sdkManager: FlirSdkManager? = null
|
|
19
|
-
private
|
|
20
|
-
private val minEmitIntervalMs = 333L // ~3 fps
|
|
21
|
-
private var discoveryStarted = false
|
|
22
|
-
private var reactContext: ThemedReactContext? = null
|
|
27
|
+
private var reactContext: ReactContext? = null
|
|
23
28
|
private var appContext: Context? = null
|
|
24
|
-
private var frameCount = 0 // For tracking first frame
|
|
25
29
|
|
|
26
|
-
//
|
|
27
|
-
private
|
|
28
|
-
private
|
|
29
|
-
private var connectedDeviceName: String? = null
|
|
30
|
-
private var connectedDeviceId: String? = null
|
|
31
|
-
private var isStreaming = false
|
|
30
|
+
// Frame rate limiting
|
|
31
|
+
private val lastEmitMs = AtomicLong(0)
|
|
32
|
+
private val minEmitIntervalMs = 100L // ~10 fps max for RN events
|
|
32
33
|
|
|
33
|
-
//
|
|
34
|
-
private var
|
|
34
|
+
// State
|
|
35
|
+
private var isInitialized = false
|
|
36
|
+
private var isScanning = false
|
|
37
|
+
private var isConnected = false
|
|
38
|
+
private var isStreaming = false
|
|
39
|
+
private var connectedDeviceId: String? = null
|
|
40
|
+
private var connectedDeviceName: String? = null
|
|
35
41
|
|
|
36
|
-
//
|
|
37
|
-
private var
|
|
42
|
+
// Latest bitmap for texture updates
|
|
43
|
+
private var latestBitmap: Bitmap? = null
|
|
38
44
|
|
|
39
|
-
//
|
|
45
|
+
// Callbacks
|
|
40
46
|
interface TextureUpdateCallback {
|
|
41
47
|
fun onTextureUpdate(bitmap: Bitmap, textureUnit: Int)
|
|
42
48
|
}
|
|
@@ -47,7 +53,6 @@ object FlirManager {
|
|
|
47
53
|
|
|
48
54
|
private var textureCallback: TextureUpdateCallback? = null
|
|
49
55
|
private var temperatureCallback: TemperatureCallback? = null
|
|
50
|
-
private var latestBitmap: Bitmap? = null
|
|
51
56
|
|
|
52
57
|
fun setTextureCallback(callback: TextureUpdateCallback?) {
|
|
53
58
|
textureCallback = callback
|
|
@@ -59,243 +64,203 @@ object FlirManager {
|
|
|
59
64
|
|
|
60
65
|
fun getLatestBitmap(): Bitmap? = latestBitmap
|
|
61
66
|
|
|
62
|
-
fun getTemperatureAtPoint(x: Int, y: Int): Double? {
|
|
63
|
-
return try {
|
|
64
|
-
sdkManager?.getTemperatureAtPoint(x, y)?.takeIf { !it.isNaN() }
|
|
65
|
-
} catch (t: Throwable) {
|
|
66
|
-
null
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
67
|
/**
|
|
71
|
-
*
|
|
68
|
+
* Initialize the FLIR SDK
|
|
72
69
|
*/
|
|
73
|
-
fun
|
|
70
|
+
fun init(context: Context) {
|
|
71
|
+
// Store react context for event emission if it's a React context
|
|
72
|
+
// Always update if we get a valid ReactContext (in case previous was stale)
|
|
73
|
+
if (context is ReactContext) {
|
|
74
|
+
Log.d(TAG, "Storing ReactContext for event emission: ${context.javaClass.simpleName}")
|
|
75
|
+
reactContext = context
|
|
76
|
+
} else {
|
|
77
|
+
Log.d(TAG, "Context is not ReactContext: ${context.javaClass.simpleName}")
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (isInitialized) {
|
|
81
|
+
Log.d(TAG, "Already initialized")
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
appContext = context.applicationContext
|
|
86
|
+
|
|
87
|
+
sdkManager = FlirSdkManager.getInstance(context)
|
|
88
|
+
sdkManager?.setListener(sdkListener)
|
|
89
|
+
sdkManager?.initialize()
|
|
90
|
+
|
|
91
|
+
isInitialized = true
|
|
92
|
+
Log.i(TAG, "FlirManager initialized")
|
|
93
|
+
}
|
|
74
94
|
|
|
75
95
|
/**
|
|
76
|
-
*
|
|
96
|
+
* Start scanning for devices (USB, Network, Emulator - ALL types)
|
|
77
97
|
*/
|
|
78
|
-
fun
|
|
98
|
+
fun startDiscovery(retry: Boolean = false) {
|
|
99
|
+
Log.i(TAG, "startDiscovery(retry=$retry)")
|
|
100
|
+
|
|
101
|
+
if (!isInitialized && appContext != null) {
|
|
102
|
+
init(appContext!!)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (isScanning && !retry) {
|
|
106
|
+
Log.d(TAG, "Already scanning")
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
isScanning = true
|
|
111
|
+
emitDeviceState("discovering")
|
|
112
|
+
sdkManager?.scan()
|
|
113
|
+
}
|
|
79
114
|
|
|
80
115
|
/**
|
|
81
|
-
*
|
|
116
|
+
* Start discovery with React context
|
|
82
117
|
*/
|
|
83
|
-
fun
|
|
118
|
+
fun startDiscoveryAndConnect(context: ThemedReactContext, isEmuMode: Boolean = false) {
|
|
119
|
+
reactContext = context
|
|
120
|
+
startDiscovery(retry = false)
|
|
121
|
+
}
|
|
84
122
|
|
|
85
123
|
/**
|
|
86
|
-
*
|
|
124
|
+
* Stop scanning
|
|
87
125
|
*/
|
|
88
|
-
fun
|
|
126
|
+
fun stopDiscovery() {
|
|
127
|
+
Log.i(TAG, "stopDiscovery")
|
|
128
|
+
sdkManager?.stop()
|
|
129
|
+
isScanning = false
|
|
130
|
+
}
|
|
89
131
|
|
|
90
132
|
/**
|
|
91
|
-
*
|
|
133
|
+
* Connect to a device by ID
|
|
92
134
|
*/
|
|
93
|
-
fun
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
135
|
+
fun connectToDevice(deviceId: String) {
|
|
136
|
+
Log.i(TAG, "connectToDevice: $deviceId")
|
|
137
|
+
|
|
138
|
+
val devices = sdkManager?.discoveredDevices ?: emptyList()
|
|
139
|
+
val identity = devices.find { it.deviceId == deviceId }
|
|
140
|
+
|
|
141
|
+
if (identity != null) {
|
|
142
|
+
sdkManager?.connect(identity)
|
|
143
|
+
} else {
|
|
144
|
+
Log.e(TAG, "Device not found: $deviceId")
|
|
145
|
+
emitError("Device not found: $deviceId")
|
|
98
146
|
}
|
|
99
147
|
}
|
|
100
148
|
|
|
101
149
|
/**
|
|
102
|
-
*
|
|
150
|
+
* Switch to a different device
|
|
103
151
|
*/
|
|
104
|
-
fun
|
|
105
|
-
|
|
106
|
-
"
|
|
107
|
-
else -> FlirSdkManager.EmulatorType.FLIR_ONE_EDGE
|
|
108
|
-
}
|
|
109
|
-
Log.d(TAG, "Preferred emulator type set to: $preferredEmulatorType")
|
|
110
|
-
sdkManager?.setEmulatorType(preferredEmulatorType)
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
fun init(context: Context) {
|
|
114
|
-
// Avoid re-initialization if already done
|
|
115
|
-
if (sdkManager != null) {
|
|
116
|
-
Log.i(FLOW_TAG, "[FlirManager] init() called but already initialized, skipping")
|
|
152
|
+
fun switchToDevice(deviceId: String) {
|
|
153
|
+
if (deviceId == connectedDeviceId) {
|
|
154
|
+
Log.d(TAG, "Already connected to: $deviceId")
|
|
117
155
|
return
|
|
118
156
|
}
|
|
119
157
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
// Initialize SDK manager with listener matching FlirSdkManager.Listener interface
|
|
124
|
-
sdkManager = FlirSdkManager(object : FlirSdkManager.Listener {
|
|
125
|
-
override fun onFrame(bitmap: Bitmap) {
|
|
126
|
-
latestBitmap = bitmap
|
|
127
|
-
if (frameCount == 0) {
|
|
128
|
-
Log.i(FLOW_TAG, "[FlirManager] FIRST FRAME received: ${bitmap.width}x${bitmap.height}")
|
|
129
|
-
}
|
|
130
|
-
frameCount++
|
|
131
|
-
textureCallback?.onTextureUpdate(bitmap, 0)
|
|
132
|
-
emitFrameToReactNative(bitmap)
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
override fun onTemperature(temp: Double, x: Int, y: Int) {
|
|
136
|
-
temperatureCallback?.onTemperatureData(temp, x, y)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
override fun onDeviceFound(deviceId: String, deviceName: String, isEmulator: Boolean) {
|
|
140
|
-
Log.i(FLOW_TAG, "[FlirManager] Device found: $deviceName (id=$deviceId, emulator=$isEmulator)")
|
|
141
|
-
Log.d(TAG, "Device found: $deviceName (id=$deviceId, emulator=$isEmulator)")
|
|
142
|
-
discoveryCallback?.onDeviceFound(deviceName)
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
override fun onDeviceListUpdated(devices: MutableList<FlirSdkManager.DeviceInfo>) {
|
|
146
|
-
discoveredDevices = devices.toList()
|
|
147
|
-
Log.i(FLOW_TAG, "[FlirManager] Device list updated: ${devices.size} devices")
|
|
148
|
-
Log.i(TAG, "Device list updated: ${devices.size} devices")
|
|
149
|
-
devices.forEach {
|
|
150
|
-
Log.d(TAG, " - ${it.deviceName} (${it.commInterface}, emu=${it.isEmulator})")
|
|
151
|
-
}
|
|
152
|
-
emitDevicesFound(devices)
|
|
153
|
-
|
|
154
|
-
// Auto-connect to first device if available
|
|
155
|
-
if (devices.isNotEmpty()) {
|
|
156
|
-
val first = devices[0]
|
|
157
|
-
connectedDeviceName = first.deviceName
|
|
158
|
-
connectedDeviceId = first.deviceId
|
|
159
|
-
isEmulatorMode = first.isEmulator
|
|
160
|
-
isPhysicalDeviceConnected = !first.isEmulator
|
|
161
|
-
|
|
162
|
-
Log.i(FLOW_TAG, "[FlirManager] Auto-connecting to first device: ${first.deviceName}")
|
|
163
|
-
// Connect to the device
|
|
164
|
-
sdkManager?.connectToDevice(first.deviceId)
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
override fun onDeviceConnected(deviceId: String, deviceName: String, isEmulator: Boolean) {
|
|
169
|
-
connectedDeviceId = deviceId
|
|
170
|
-
connectedDeviceName = deviceName
|
|
171
|
-
this@FlirManager.isEmulatorMode = isEmulator
|
|
172
|
-
isPhysicalDeviceConnected = !isEmulator
|
|
173
|
-
|
|
174
|
-
Log.i(TAG, "Device connected: $deviceName (id=$deviceId, emulator=$isEmulator)")
|
|
175
|
-
emitDeviceState("connected", !isEmulator)
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
override fun onDeviceDisconnected() {
|
|
179
|
-
Log.i(TAG, "Device disconnected")
|
|
180
|
-
isPhysicalDeviceConnected = false
|
|
181
|
-
isEmulatorMode = false
|
|
182
|
-
isStreaming = false
|
|
183
|
-
connectedDeviceId = null
|
|
184
|
-
connectedDeviceName = null
|
|
185
|
-
emitDeviceState("disconnected", false)
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
override fun onDiscoveryStarted() {
|
|
189
|
-
Log.i(TAG, "Discovery started")
|
|
190
|
-
emitDeviceState("discovering", false)
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
override fun onDiscoveryTimeout() {
|
|
194
|
-
Log.w(TAG, "Discovery timeout - no devices found")
|
|
195
|
-
discoveryCallback?.onDiscoveryTimeout()
|
|
196
|
-
emitDeviceState("discovery_timeout", false)
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
override fun onStreamStarted(streamType: String) {
|
|
200
|
-
isStreaming = true
|
|
201
|
-
Log.i(TAG, "Streaming started: $streamType")
|
|
202
|
-
emitDeviceState("streaming", isPhysicalDeviceConnected)
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
override fun onError(error: String) {
|
|
206
|
-
Log.e(TAG, "SDK Error: $error")
|
|
207
|
-
emitError(error)
|
|
208
|
-
}
|
|
209
|
-
}, context)
|
|
210
|
-
|
|
211
|
-
// Set emulator type preference
|
|
212
|
-
sdkManager?.setEmulatorType(preferredEmulatorType)
|
|
213
|
-
|
|
214
|
-
Log.i(TAG, "FlirManager initialized with comprehensive SDK manager")
|
|
215
|
-
} catch (t: Throwable) {
|
|
216
|
-
Log.e(TAG, "FlirManager init failed", t)
|
|
158
|
+
// Disconnect current and connect new
|
|
159
|
+
if (isConnected) {
|
|
160
|
+
sdkManager?.disconnect()
|
|
217
161
|
}
|
|
162
|
+
connectToDevice(deviceId)
|
|
218
163
|
}
|
|
219
|
-
|
|
164
|
+
|
|
220
165
|
/**
|
|
221
|
-
* Start
|
|
222
|
-
* @param isEmuMode If true, immediately start emulator without physical device discovery
|
|
166
|
+
* Start streaming from connected device
|
|
223
167
|
*/
|
|
224
|
-
fun
|
|
225
|
-
Log.i(
|
|
226
|
-
|
|
227
|
-
if (discoveryStarted && !isEmuMode) {
|
|
228
|
-
Log.i(FLOW_TAG, "[FlirManager] Discovery already started, skipping")
|
|
229
|
-
return
|
|
230
|
-
}
|
|
231
|
-
discoveryStarted = true
|
|
232
|
-
reactContext = context
|
|
233
|
-
frameCount = 0 // Reset frame counter
|
|
234
|
-
|
|
235
|
-
emitDeviceState("discovering", false)
|
|
236
|
-
|
|
237
|
-
try {
|
|
238
|
-
// Set emulator type preference before discovery
|
|
239
|
-
Log.i(FLOW_TAG, "[FlirManager] Setting emulator type: $preferredEmulatorType")
|
|
240
|
-
sdkManager?.setEmulatorType(preferredEmulatorType)
|
|
241
|
-
|
|
242
|
-
// Start discovery (forceEmulator=isEmuMode)
|
|
243
|
-
Log.i(FLOW_TAG, "[FlirManager] Calling sdkManager.startDiscovery(forceEmulator=$isEmuMode)")
|
|
244
|
-
Log.i(TAG, "Starting discovery (forceEmulator=$isEmuMode)")
|
|
245
|
-
sdkManager?.startDiscovery(isEmuMode)
|
|
246
|
-
} catch (t: Throwable) {
|
|
247
|
-
Log.e(FLOW_TAG, "[FlirManager] startDiscoveryAndConnect failed: ${t.message}")
|
|
248
|
-
Log.e(TAG, "startDiscoveryAndConnect failed", t)
|
|
249
|
-
emitDeviceState("error", false)
|
|
250
|
-
}
|
|
168
|
+
fun startStream() {
|
|
169
|
+
Log.i(TAG, "startStream")
|
|
170
|
+
sdkManager?.startStream()
|
|
251
171
|
}
|
|
252
172
|
|
|
253
173
|
/**
|
|
254
|
-
*
|
|
174
|
+
* Stop streaming
|
|
255
175
|
*/
|
|
256
|
-
fun
|
|
257
|
-
|
|
176
|
+
fun stopStream() {
|
|
177
|
+
Log.i(TAG, "stopStream")
|
|
178
|
+
sdkManager?.stopStream()
|
|
179
|
+
isStreaming = false
|
|
258
180
|
}
|
|
259
|
-
|
|
181
|
+
|
|
260
182
|
/**
|
|
261
|
-
*
|
|
183
|
+
* Disconnect from current device
|
|
262
184
|
*/
|
|
263
|
-
fun
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
sdkManager?.connectToDevice(deviceId)
|
|
185
|
+
fun disconnect() {
|
|
186
|
+
Log.i(TAG, "disconnect")
|
|
187
|
+
sdkManager?.disconnect()
|
|
188
|
+
isConnected = false
|
|
189
|
+
isStreaming = false
|
|
190
|
+
connectedDeviceId = null
|
|
191
|
+
connectedDeviceName = null
|
|
271
192
|
}
|
|
272
193
|
|
|
273
194
|
/**
|
|
274
|
-
*
|
|
195
|
+
* Stop everything
|
|
275
196
|
*/
|
|
276
|
-
fun startEmulator(type: FlirSdkManager.EmulatorType = preferredEmulatorType) {
|
|
277
|
-
Log.i(TAG, "Starting emulator: $type")
|
|
278
|
-
sdkManager?.setEmulatorType(type)
|
|
279
|
-
sdkManager?.startDiscovery(true) // forceEmulator = true
|
|
280
|
-
}
|
|
281
|
-
|
|
282
197
|
fun stop() {
|
|
283
|
-
Log.i(TAG, "
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
// Reset state
|
|
289
|
-
discoveryStarted = false
|
|
290
|
-
isPhysicalDeviceConnected = false
|
|
291
|
-
isEmulatorMode = false
|
|
292
|
-
isStreaming = false
|
|
293
|
-
connectedDeviceName = null
|
|
294
|
-
connectedDeviceId = null
|
|
198
|
+
Log.i(TAG, "stop")
|
|
199
|
+
stopStream()
|
|
200
|
+
disconnect()
|
|
201
|
+
stopDiscovery()
|
|
295
202
|
latestBitmap = null
|
|
296
|
-
discoveredDevices = emptyList()
|
|
297
203
|
}
|
|
298
|
-
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Get temperature at point in image coordinates
|
|
207
|
+
*/
|
|
208
|
+
fun getTemperatureAt(x: Int, y: Int): Double? {
|
|
209
|
+
return sdkManager?.getTemperatureAt(x, y)?.takeIf { !it.isNaN() }
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Get temperature at normalized coordinates (0.0 to 1.0)
|
|
214
|
+
*/
|
|
215
|
+
fun getTemperatureAtNormalized(normalizedX: Double, normalizedY: Double): Double? {
|
|
216
|
+
return sdkManager?.getTemperatureAtNormalized(normalizedX, normalizedY)?.takeIf { !it.isNaN() }
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Alias for getTemperatureAt
|
|
221
|
+
*/
|
|
222
|
+
fun getTemperatureAtPoint(x: Int, y: Int): Double? = getTemperatureAt(x, y)
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Set palette
|
|
226
|
+
*/
|
|
227
|
+
fun setPalette(name: String) {
|
|
228
|
+
Log.d(TAG, "setPalette: $name")
|
|
229
|
+
sdkManager?.setPalette(name)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Get available palettes
|
|
234
|
+
*/
|
|
235
|
+
fun getAvailablePalettes(): List<String> {
|
|
236
|
+
return sdkManager?.availablePalettes ?: emptyList()
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Get list of discovered devices
|
|
241
|
+
*/
|
|
242
|
+
fun getDiscoveredDevices(): List<Identity> {
|
|
243
|
+
return sdkManager?.discoveredDevices ?: emptyList()
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Check states
|
|
248
|
+
*/
|
|
249
|
+
fun isConnected(): Boolean = isConnected
|
|
250
|
+
fun isStreaming(): Boolean = isStreaming
|
|
251
|
+
fun isEmulator(): Boolean = connectedDeviceName?.contains("EMULAT", ignoreCase = true) == true
|
|
252
|
+
fun isDeviceConnected(): Boolean = isConnected
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Get connected device info
|
|
256
|
+
*/
|
|
257
|
+
fun getConnectedDeviceInfo(): String {
|
|
258
|
+
return connectedDeviceName ?: "Not connected"
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Get latest frame as file path (for RN)
|
|
263
|
+
*/
|
|
299
264
|
fun getLatestFramePath(): String? {
|
|
300
265
|
val bitmap = latestBitmap ?: return null
|
|
301
266
|
return try {
|
|
@@ -305,50 +270,109 @@ object FlirManager {
|
|
|
305
270
|
}
|
|
306
271
|
file.absolutePath
|
|
307
272
|
} catch (t: Throwable) {
|
|
273
|
+
Log.e(TAG, "Failed to save frame", t)
|
|
308
274
|
null
|
|
309
275
|
}
|
|
310
276
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
277
|
+
|
|
278
|
+
// SDK Listener
|
|
279
|
+
private val sdkListener = object : FlirSdkManager.Listener {
|
|
280
|
+
override fun onDeviceFound(identity: Identity) {
|
|
281
|
+
Log.i(TAG, "Device found: ${identity.deviceId}")
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
override fun onDeviceListUpdated(devices: List<Identity>) {
|
|
285
|
+
Log.i(TAG, "Devices updated: ${devices.size} found")
|
|
286
|
+
devices.forEach {
|
|
287
|
+
Log.d(TAG, " - ${it.deviceId} (${it.communicationInterface})")
|
|
288
|
+
}
|
|
289
|
+
emitDevicesFound(devices)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
override fun onConnected(identity: Identity?) {
|
|
293
|
+
Log.i(TAG, "Connected to: ${identity?.deviceId}")
|
|
294
|
+
isConnected = true
|
|
295
|
+
connectedDeviceId = identity?.deviceId
|
|
296
|
+
connectedDeviceName = identity?.deviceId
|
|
297
|
+
emitDeviceState("connected")
|
|
298
|
+
|
|
299
|
+
// Auto-start streaming when connected
|
|
300
|
+
startStream()
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
override fun onDisconnected() {
|
|
304
|
+
Log.i(TAG, "Disconnected")
|
|
305
|
+
isConnected = false
|
|
306
|
+
isStreaming = false
|
|
307
|
+
connectedDeviceId = null
|
|
308
|
+
connectedDeviceName = null
|
|
309
|
+
emitDeviceState("disconnected")
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
override fun onFrame(bitmap: Bitmap) {
|
|
313
|
+
if (bitmap.isRecycled || bitmap.width <= 0 || bitmap.height <= 0) {
|
|
314
|
+
return
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
latestBitmap = bitmap
|
|
318
|
+
isStreaming = true
|
|
319
|
+
|
|
320
|
+
// Notify texture callback (for GL rendering)
|
|
321
|
+
if (textureCallback != null) {
|
|
322
|
+
textureCallback?.onTextureUpdate(bitmap, 0)
|
|
323
|
+
} else {
|
|
324
|
+
// Log only occasionally to avoid spam
|
|
325
|
+
if (System.currentTimeMillis() % 5000 < 100) {
|
|
326
|
+
Log.w(TAG, "⚠️ Frame received but textureCallback is null - texture won't update!")
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Rate-limited RN event
|
|
331
|
+
emitFrameToReactNative(bitmap)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
override fun onError(message: String) {
|
|
335
|
+
Log.e(TAG, "Error: $message")
|
|
336
|
+
emitError(message)
|
|
337
|
+
}
|
|
314
338
|
}
|
|
315
|
-
|
|
339
|
+
|
|
340
|
+
// React Native event emitters
|
|
316
341
|
private fun emitFrameToReactNative(bitmap: Bitmap) {
|
|
317
342
|
val now = System.currentTimeMillis()
|
|
318
343
|
if (now - lastEmitMs.get() < minEmitIntervalMs) return
|
|
319
344
|
lastEmitMs.set(now)
|
|
320
|
-
|
|
345
|
+
|
|
321
346
|
val ctx = reactContext ?: return
|
|
322
347
|
try {
|
|
323
|
-
val params = Arguments.createMap()
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
348
|
+
val params = Arguments.createMap().apply {
|
|
349
|
+
putInt("width", bitmap.width)
|
|
350
|
+
putInt("height", bitmap.height)
|
|
351
|
+
putDouble("timestamp", now.toDouble())
|
|
352
|
+
}
|
|
328
353
|
ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
329
354
|
.emit("FlirFrameReceived", params)
|
|
330
355
|
} catch (e: Exception) {
|
|
331
|
-
//
|
|
356
|
+
// Ignore
|
|
332
357
|
}
|
|
333
358
|
}
|
|
334
|
-
|
|
335
|
-
private fun emitDeviceState(state: String
|
|
336
|
-
val ctx = reactContext
|
|
359
|
+
|
|
360
|
+
private fun emitDeviceState(state: String) {
|
|
361
|
+
val ctx = reactContext
|
|
362
|
+
if (ctx == null) {
|
|
363
|
+
Log.e(TAG, "Cannot emit FlirDeviceConnected($state) - reactContext is null!")
|
|
364
|
+
return
|
|
365
|
+
}
|
|
366
|
+
Log.d(TAG, "Emitting FlirDeviceConnected: $state")
|
|
337
367
|
try {
|
|
338
|
-
val params = Arguments.createMap()
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
connectedDeviceName?.let {
|
|
346
|
-
params.putString("deviceName", it)
|
|
347
|
-
}
|
|
348
|
-
connectedDeviceId?.let {
|
|
349
|
-
params.putString("deviceId", it)
|
|
368
|
+
val params = Arguments.createMap().apply {
|
|
369
|
+
putString("state", state)
|
|
370
|
+
putBoolean("isConnected", isConnected)
|
|
371
|
+
putBoolean("isStreaming", isStreaming)
|
|
372
|
+
putBoolean("isEmulator", isEmulator())
|
|
373
|
+
connectedDeviceName?.let { putString("deviceName", it) }
|
|
374
|
+
connectedDeviceId?.let { putString("deviceId", it) }
|
|
350
375
|
}
|
|
351
|
-
|
|
352
376
|
ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
353
377
|
.emit("FlirDeviceConnected", params)
|
|
354
378
|
} catch (e: Exception) {
|
|
@@ -356,26 +380,33 @@ object FlirManager {
|
|
|
356
380
|
}
|
|
357
381
|
}
|
|
358
382
|
|
|
359
|
-
private fun emitDevicesFound(devices: List<
|
|
360
|
-
val ctx = reactContext
|
|
383
|
+
private fun emitDevicesFound(devices: List<Identity>) {
|
|
384
|
+
val ctx = reactContext
|
|
385
|
+
if (ctx == null) {
|
|
386
|
+
Log.e(TAG, "Cannot emit FlirDevicesFound - reactContext is null!")
|
|
387
|
+
return
|
|
388
|
+
}
|
|
389
|
+
Log.d(TAG, "Emitting FlirDevicesFound with ${devices.size} devices")
|
|
361
390
|
try {
|
|
362
391
|
val params = Arguments.createMap()
|
|
363
392
|
val devicesArray: WritableArray = Arguments.createArray()
|
|
364
393
|
|
|
365
|
-
devices.forEach {
|
|
366
|
-
val deviceMap: WritableMap = Arguments.createMap()
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
394
|
+
devices.forEach { identity ->
|
|
395
|
+
val deviceMap: WritableMap = Arguments.createMap().apply {
|
|
396
|
+
putString("id", identity.deviceId)
|
|
397
|
+
putString("name", identity.deviceId)
|
|
398
|
+
putString("communicationType", identity.communicationInterface.name)
|
|
399
|
+
putBoolean("isEmulator", identity.communicationInterface.name == "EMULATOR")
|
|
400
|
+
}
|
|
371
401
|
devicesArray.pushMap(deviceMap)
|
|
372
402
|
}
|
|
373
403
|
|
|
374
404
|
params.putArray("devices", devicesArray)
|
|
375
405
|
params.putInt("count", devices.size)
|
|
376
|
-
|
|
406
|
+
|
|
377
407
|
ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
378
408
|
.emit("FlirDevicesFound", params)
|
|
409
|
+
Log.d(TAG, "Successfully emitted FlirDevicesFound")
|
|
379
410
|
} catch (e: Exception) {
|
|
380
411
|
Log.e(TAG, "Failed to emit devices found", e)
|
|
381
412
|
}
|
|
@@ -384,94 +415,62 @@ object FlirManager {
|
|
|
384
415
|
private fun emitError(message: String) {
|
|
385
416
|
val ctx = reactContext ?: return
|
|
386
417
|
try {
|
|
387
|
-
val params = Arguments.createMap()
|
|
388
|
-
|
|
389
|
-
|
|
418
|
+
val params = Arguments.createMap().apply {
|
|
419
|
+
putString("error", message)
|
|
420
|
+
}
|
|
390
421
|
ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
391
422
|
.emit("FlirError", params)
|
|
392
423
|
} catch (e: Exception) {
|
|
393
424
|
Log.e(TAG, "Failed to emit error", e)
|
|
394
425
|
}
|
|
395
426
|
}
|
|
396
|
-
|
|
397
|
-
//
|
|
427
|
+
|
|
428
|
+
// Legacy compatibility
|
|
398
429
|
@JvmStatic
|
|
399
430
|
fun getInstance(): FlirManager = this
|
|
400
|
-
|
|
431
|
+
|
|
401
432
|
interface DiscoveryCallback {
|
|
402
433
|
fun onDeviceFound(deviceName: String)
|
|
403
434
|
fun onDiscoveryTimeout()
|
|
404
435
|
fun onEmulatorEnabled()
|
|
405
436
|
}
|
|
406
|
-
|
|
437
|
+
|
|
407
438
|
private var discoveryCallback: DiscoveryCallback? = null
|
|
408
|
-
|
|
439
|
+
|
|
409
440
|
fun setDiscoveryCallback(callback: DiscoveryCallback?) {
|
|
410
441
|
discoveryCallback = callback
|
|
411
442
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
sdkManager?.setPalette(name)
|
|
415
|
-
}
|
|
416
|
-
|
|
443
|
+
|
|
444
|
+
// Legacy methods - no-ops or simple forwards
|
|
417
445
|
fun setEmulatorMode(enabled: Boolean) {
|
|
446
|
+
Log.d(TAG, "setEmulatorMode($enabled) - legacy, use startDiscovery() instead")
|
|
418
447
|
if (enabled) {
|
|
419
|
-
|
|
420
|
-
discoveryCallback?.onEmulatorEnabled()
|
|
421
|
-
startEmulator(preferredEmulatorType)
|
|
448
|
+
startDiscovery(retry = true)
|
|
422
449
|
}
|
|
423
450
|
}
|
|
424
|
-
|
|
425
|
-
fun
|
|
426
|
-
|
|
451
|
+
|
|
452
|
+
fun enableEmulatorMode() = setEmulatorMode(true)
|
|
453
|
+
|
|
454
|
+
fun forceEmulatorMode(type: String = "FLIR_ONE_EDGE") {
|
|
455
|
+
Log.d(TAG, "forceEmulatorMode($type) - legacy, use startDiscovery() instead")
|
|
456
|
+
startDiscovery(retry = true)
|
|
427
457
|
}
|
|
428
|
-
|
|
429
|
-
fun
|
|
430
|
-
Log.
|
|
431
|
-
|
|
432
|
-
// Auto-init if not initialized
|
|
433
|
-
if (sdkManager == null && appContext != null) {
|
|
434
|
-
Log.i(FLOW_TAG, "[FlirManager] Auto-initializing from startDiscovery()")
|
|
435
|
-
init(appContext!!)
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
val ctx = reactContext
|
|
439
|
-
if (ctx != null) {
|
|
440
|
-
// Reset discovery state if retrying
|
|
441
|
-
if (retry) {
|
|
442
|
-
discoveryStarted = false
|
|
443
|
-
}
|
|
444
|
-
startDiscoveryAndConnect(ctx, isEmuMode = false)
|
|
445
|
-
} else if (appContext != null) {
|
|
446
|
-
// Fallback: try to start discovery without React context
|
|
447
|
-
try {
|
|
448
|
-
Log.w(TAG, "Starting discovery without React context")
|
|
449
|
-
Log.i(FLOW_TAG, "[FlirManager] Calling sdkManager.startDiscovery(false) without React context")
|
|
450
|
-
sdkManager?.startDiscovery(false)
|
|
451
|
-
} catch (t: Throwable) {
|
|
452
|
-
Log.e(TAG, "startDiscovery failed", t)
|
|
453
|
-
Log.e(FLOW_TAG, "[FlirManager] startDiscovery failed: ${t.message}")
|
|
454
|
-
}
|
|
455
|
-
} else {
|
|
456
|
-
Log.e(FLOW_TAG, "[FlirManager] Cannot startDiscovery - no context available! Call init(context) first.")
|
|
457
|
-
}
|
|
458
|
+
|
|
459
|
+
fun setPreferredEmulatorType(type: String) {
|
|
460
|
+
Log.d(TAG, "setPreferredEmulatorType($type) - legacy, no longer used")
|
|
458
461
|
}
|
|
459
462
|
|
|
460
|
-
fun
|
|
461
|
-
|
|
463
|
+
fun updateAcol(value: Float) {
|
|
464
|
+
// No-op - not used in simplified version
|
|
462
465
|
}
|
|
463
466
|
|
|
464
467
|
/**
|
|
465
|
-
*
|
|
468
|
+
* Cleanup
|
|
466
469
|
*/
|
|
467
|
-
fun
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
startDiscoveryAndConnect(ctx, isEmuMode = true)
|
|
473
|
-
} else {
|
|
474
|
-
startEmulator(preferredEmulatorType)
|
|
475
|
-
}
|
|
470
|
+
fun destroy() {
|
|
471
|
+
stop()
|
|
472
|
+
sdkManager?.destroy()
|
|
473
|
+
sdkManager = null
|
|
474
|
+
isInitialized = false
|
|
476
475
|
}
|
|
477
476
|
}
|