@leonardojc/capacitor-ioboard 1.2.8 → 2.0.0
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/README.md +257 -209
- package/android/build.gradle +0 -1
- package/android/src/main/java/com/leonardojc/capacitor/ioboard/CapacitorIoboardPlugin.java +926 -262
- package/android/src/main/java/com/leonardojc/capacitor/ioboard/CapacitorIoboardPlugin.java.backup +777 -0
- package/android/src/main/java/com/leonardojc/capacitor/ioboard/SerialConnectionManager.java +446 -0
- package/dist/esm/definitions-v2.d.ts +228 -0
- package/dist/esm/definitions-v2.js +11 -0
- package/dist/esm/definitions.d.ts +28 -0
- package/dist/esm/index-v2.d.ts +266 -0
- package/dist/esm/index-v2.js +295 -0
- package/dist/esm/web.d.ts +13 -16
- package/dist/esm/web.js +51 -135
- package/dist/plugin.cjs.js +50 -134
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +50 -134
- package/dist/plugin.js.map +1 -1
- package/package.json +3 -4
package/android/src/main/java/com/leonardojc/capacitor/ioboard/CapacitorIoboardPlugin.java.backup
ADDED
|
@@ -0,0 +1,777 @@
|
|
|
1
|
+
package com.leonardojc.capacitor.ioboard;
|
|
2
|
+
|
|
3
|
+
import com.getcapacitor.JSArray;
|
|
4
|
+
import com.getcapacitor.JSObject;
|
|
5
|
+
import com.getcapacitor.Plugin;
|
|
6
|
+
import com.getcapacitor.PluginCall;
|
|
7
|
+
import com.getcapacitor.PluginMethod;
|
|
8
|
+
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
9
|
+
import android.util.Log;
|
|
10
|
+
import java.util.Arrays;
|
|
11
|
+
import java.util.List;
|
|
12
|
+
import java.util.concurrent.CompletableFuture;
|
|
13
|
+
import java.util.concurrent.TimeUnit;
|
|
14
|
+
import org.json.JSONObject;
|
|
15
|
+
|
|
16
|
+
@CapacitorPlugin(name = "CapacitorIoboard")
|
|
17
|
+
public class CapacitorIoboardPlugin extends Plugin implements SerialConnectionManager.SerialDataListener {
|
|
18
|
+
|
|
19
|
+
private static final String TAG = "CapacitorIoboard";
|
|
20
|
+
private SerialConnectionManager serialManager;
|
|
21
|
+
private IOBoardManager ioboardManager;
|
|
22
|
+
private CompletableFuture<JSObject> currentCommandFuture;
|
|
23
|
+
|
|
24
|
+
@Override
|
|
25
|
+
public void load() {
|
|
26
|
+
super.load();
|
|
27
|
+
Log.d(TAG, "IOBoard Plugin v2.0 loaded - Integrated Serial + Protocol");
|
|
28
|
+
|
|
29
|
+
// Initialize managers
|
|
30
|
+
serialManager = new SerialConnectionManager();
|
|
31
|
+
serialManager.setDataListener(this);
|
|
32
|
+
ioboardManager = new IOBoardManager();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ================== CONNECTION METHODS ==================
|
|
36
|
+
|
|
37
|
+
@PluginMethod
|
|
38
|
+
public void connect(PluginCall call) {
|
|
39
|
+
String portPath = call.getString("portPath", "/dev/ttyS2");
|
|
40
|
+
Integer baudRate = call.getInt("baudRate", 115200);
|
|
41
|
+
Integer dataBits = call.getInt("dataBits", 8);
|
|
42
|
+
Integer stopBits = call.getInt("stopBits", 1);
|
|
43
|
+
String parity = call.getString("parity", "none");
|
|
44
|
+
|
|
45
|
+
Log.d(TAG, "Connecting to " + portPath + " at " + baudRate + " baud");
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
boolean success = serialManager.openPort(portPath, baudRate, dataBits, stopBits, parity);
|
|
49
|
+
|
|
50
|
+
if (success) {
|
|
51
|
+
JSObject result = new JSObject();
|
|
52
|
+
result.put("success", true);
|
|
53
|
+
result.put("portPath", portPath);
|
|
54
|
+
result.put("baudRate", baudRate);
|
|
55
|
+
result.put("message", "Connected successfully");
|
|
56
|
+
call.resolve(result);
|
|
57
|
+
|
|
58
|
+
// Notify connection state
|
|
59
|
+
notifyListeners("connectionStateChanged", result);
|
|
60
|
+
} else {
|
|
61
|
+
call.reject("Failed to connect to serial port");
|
|
62
|
+
}
|
|
63
|
+
} catch (Exception e) {
|
|
64
|
+
Log.e(TAG, "Connection error: " + e.getMessage(), e);
|
|
65
|
+
call.reject("Connection error: " + e.getMessage());
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@PluginMethod
|
|
70
|
+
public void disconnect(PluginCall call) {
|
|
71
|
+
Log.d(TAG, "Disconnecting from serial port");
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
serialManager.closePort();
|
|
75
|
+
|
|
76
|
+
JSObject result = new JSObject();
|
|
77
|
+
result.put("success", true);
|
|
78
|
+
result.put("message", "Disconnected successfully");
|
|
79
|
+
call.resolve(result);
|
|
80
|
+
|
|
81
|
+
// Notify disconnection
|
|
82
|
+
notifyListeners("connectionStateChanged", result);
|
|
83
|
+
} catch (Exception e) {
|
|
84
|
+
Log.e(TAG, "Disconnection error: " + e.getMessage(), e);
|
|
85
|
+
call.reject("Disconnection error: " + e.getMessage());
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@PluginMethod
|
|
90
|
+
public void isConnected(PluginCall call) {
|
|
91
|
+
JSObject result = new JSObject();
|
|
92
|
+
result.put("connected", serialManager.isConnected());
|
|
93
|
+
result.put("portPath", serialManager.getCurrentPortPath());
|
|
94
|
+
call.resolve(result);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@PluginMethod
|
|
98
|
+
public void listPorts(PluginCall call) {
|
|
99
|
+
try {
|
|
100
|
+
List<String> ports = serialManager.listAvailablePorts();
|
|
101
|
+
|
|
102
|
+
JSArray portsArray = new JSArray();
|
|
103
|
+
for (String port : ports) {
|
|
104
|
+
portsArray.put(port);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
JSObject result = new JSObject();
|
|
108
|
+
result.put("ports", portsArray);
|
|
109
|
+
result.put("count", ports.size());
|
|
110
|
+
call.resolve(result);
|
|
111
|
+
} catch (Exception e) {
|
|
112
|
+
Log.e(TAG, "Error listing ports: " + e.getMessage(), e);
|
|
113
|
+
call.reject("Error listing ports: " + e.getMessage());
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ================== IOBoard PROTOCOL METHODS ==================
|
|
118
|
+
|
|
119
|
+
@PluginMethod
|
|
120
|
+
public void getStatus(PluginCall call) {
|
|
121
|
+
Integer address = call.getInt("address", 1);
|
|
122
|
+
|
|
123
|
+
if (!serialManager.isConnected()) {
|
|
124
|
+
call.reject("Not connected to serial port");
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
Log.d(TAG, "Getting status for device address: " + address);
|
|
130
|
+
|
|
131
|
+
// Build GET_STATUS command
|
|
132
|
+
byte[] command = ioboardManager.buildCommand("GET_STATUS", address, null);
|
|
133
|
+
|
|
134
|
+
// Send command and wait for response
|
|
135
|
+
sendCommandAndWaitForResponse(command, call, "GET_STATUS");
|
|
136
|
+
|
|
137
|
+
} catch (Exception e) {
|
|
138
|
+
Log.e(TAG, "Error getting status: " + e.getMessage(), e);
|
|
139
|
+
call.reject("Error getting status: " + e.getMessage());
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
@PluginMethod
|
|
144
|
+
public void unlockPallet(PluginCall call) {
|
|
145
|
+
Integer address = call.getInt("address", 1);
|
|
146
|
+
Integer palletNumber = call.getInt("palletNumber", 0);
|
|
147
|
+
Integer red = call.getInt("red", 0);
|
|
148
|
+
Integer green = call.getInt("green", 255);
|
|
149
|
+
Integer blue = call.getInt("blue", 0);
|
|
150
|
+
Integer intensity = call.getInt("intensity", 100);
|
|
151
|
+
Integer blinkTimes = call.getInt("blinkTimes", 0);
|
|
152
|
+
Integer blinkSpeed = call.getInt("blinkSpeed", 1);
|
|
153
|
+
|
|
154
|
+
if (!serialManager.isConnected()) {
|
|
155
|
+
call.reject("Not connected to serial port");
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
Log.d(TAG, "Unlocking pallet " + palletNumber + " for device " + address);
|
|
161
|
+
|
|
162
|
+
// Prepare RGB parameters
|
|
163
|
+
JSObject params = new JSObject();
|
|
164
|
+
params.put("palletNumber", palletNumber);
|
|
165
|
+
params.put("red", red);
|
|
166
|
+
params.put("green", green);
|
|
167
|
+
params.put("blue", blue);
|
|
168
|
+
params.put("intensity", intensity);
|
|
169
|
+
params.put("blinkTimes", blinkTimes);
|
|
170
|
+
params.put("blinkSpeed", blinkSpeed);
|
|
171
|
+
|
|
172
|
+
// Build UNLOCK_PALLET command
|
|
173
|
+
byte[] command = ioboardManager.buildCommand("UNLOCK_PALLET", address, params);
|
|
174
|
+
|
|
175
|
+
// Send command and wait for response
|
|
176
|
+
sendCommandAndWaitForResponse(command, call, "UNLOCK_PALLET");
|
|
177
|
+
|
|
178
|
+
} catch (Exception e) {
|
|
179
|
+
Log.e(TAG, "Error unlocking pallet: " + e.getMessage(), e);
|
|
180
|
+
call.reject("Error unlocking pallet: " + e.getMessage());
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
@PluginMethod
|
|
185
|
+
public void controlMultiple(PluginCall call) {
|
|
186
|
+
Integer address = call.getInt("address", 1);
|
|
187
|
+
JSArray leds = call.getArray("leds");
|
|
188
|
+
|
|
189
|
+
if (!serialManager.isConnected()) {
|
|
190
|
+
call.reject("Not connected to serial port");
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
Log.d(TAG, "Controlling multiple LEDs for device " + address);
|
|
196
|
+
|
|
197
|
+
// Prepare parameters
|
|
198
|
+
JSObject params = new JSObject();
|
|
199
|
+
params.put("leds", leds);
|
|
200
|
+
|
|
201
|
+
// Build CONTROL_MULTIPLE command
|
|
202
|
+
byte[] command = ioboardManager.buildCommand("CONTROL_MULTIPLE", address, params);
|
|
203
|
+
|
|
204
|
+
// Send command and wait for response
|
|
205
|
+
sendCommandAndWaitForResponse(command, call, "CONTROL_MULTIPLE");
|
|
206
|
+
|
|
207
|
+
} catch (Exception e) {
|
|
208
|
+
Log.e(TAG, "Error controlling multiple LEDs: " + e.getMessage(), e);
|
|
209
|
+
call.reject("Error controlling multiple LEDs: " + e.getMessage());
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
@PluginMethod
|
|
214
|
+
public void startOTAUpdate(PluginCall call) {
|
|
215
|
+
Integer address = call.getInt("address", 1);
|
|
216
|
+
String fileName = call.getString("fileName", "firmware.bin");
|
|
217
|
+
Integer fileSize = call.getInt("fileSize", 0);
|
|
218
|
+
Integer totalPackets = call.getInt("totalPackets", 0);
|
|
219
|
+
|
|
220
|
+
if (!serialManager.isConnected()) {
|
|
221
|
+
call.reject("Not connected to serial port");
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
try {
|
|
226
|
+
Log.d(TAG, "Starting OTA update for device " + address + " - File: " + fileName);
|
|
227
|
+
|
|
228
|
+
// Prepare OTA parameters
|
|
229
|
+
JSObject params = new JSObject();
|
|
230
|
+
params.put("fileName", fileName);
|
|
231
|
+
params.put("fileSize", fileSize);
|
|
232
|
+
params.put("totalPackets", totalPackets);
|
|
233
|
+
|
|
234
|
+
// Build OTA_NOTIFICATION command
|
|
235
|
+
byte[] command = ioboardManager.buildCommand("OTA_NOTIFICATION", address, params);
|
|
236
|
+
|
|
237
|
+
// Send command and wait for response
|
|
238
|
+
sendCommandAndWaitForResponse(command, call, "OTA_NOTIFICATION");
|
|
239
|
+
|
|
240
|
+
} catch (Exception e) {
|
|
241
|
+
Log.e(TAG, "Error starting OTA update: " + e.getMessage(), e);
|
|
242
|
+
call.reject("Error starting OTA update: " + e.getMessage());
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
@PluginMethod
|
|
247
|
+
public void sendOTAData(PluginCall call) {
|
|
248
|
+
Integer address = call.getInt("address", 1);
|
|
249
|
+
Integer packetNumber = call.getInt("packetNumber", 0);
|
|
250
|
+
JSArray dataArray = call.getArray("data");
|
|
251
|
+
|
|
252
|
+
if (!serialManager.isConnected()) {
|
|
253
|
+
call.reject("Not connected to serial port");
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
Log.d(TAG, "Sending OTA data packet " + packetNumber + " to device " + address);
|
|
259
|
+
|
|
260
|
+
// Prepare OTA data parameters
|
|
261
|
+
JSObject params = new JSObject();
|
|
262
|
+
params.put("packetNumber", packetNumber);
|
|
263
|
+
params.put("data", dataArray);
|
|
264
|
+
|
|
265
|
+
// Build OTA_DATA command
|
|
266
|
+
byte[] command = ioboardManager.buildCommand("OTA_DATA", address, params);
|
|
267
|
+
|
|
268
|
+
// Send command and wait for response
|
|
269
|
+
sendCommandAndWaitForResponse(command, call, "OTA_DATA");
|
|
270
|
+
|
|
271
|
+
} catch (Exception e) {
|
|
272
|
+
Log.e(TAG, "Error sending OTA data: " + e.getMessage(), e);
|
|
273
|
+
call.reject("Error sending OTA data: " + e.getMessage());
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
String command = call.getString("command");
|
|
277
|
+
Integer address = call.getInt("address");
|
|
278
|
+
|
|
279
|
+
if (command == null || address == null) {
|
|
280
|
+
call.reject("Command and address are required");
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
try {
|
|
285
|
+
byte[] frame = null;
|
|
286
|
+
|
|
287
|
+
switch (command.toUpperCase()) {
|
|
288
|
+
case "GET_STATUS":
|
|
289
|
+
frame = IOBoardProtocolUtils.createStatusQuery((byte) address.intValue());
|
|
290
|
+
break;
|
|
291
|
+
|
|
292
|
+
case "UNLOCK_PALLET":
|
|
293
|
+
Integer palletNumber = call.getInt("palletNumber");
|
|
294
|
+
Boolean doorLock = call.getBoolean("doorLock", true); // Default unlock
|
|
295
|
+
|
|
296
|
+
// LED RGB parameters with defaults
|
|
297
|
+
Integer red = call.getInt("red", 0); // Default red (0-255)
|
|
298
|
+
Integer green = call.getInt("green", 255); // Default green (0-255)
|
|
299
|
+
Integer blue = call.getInt("blue", 0); // Default blue (0-255)
|
|
300
|
+
Integer intensity = call.getInt("intensity", 100); // Default max intensity (0-100)
|
|
301
|
+
Integer blinkTimes = call.getInt("blinkTimes", 0); // Default no blink (0=no flash, 255=forever)
|
|
302
|
+
Integer blinkSpeed = call.getInt("blinkSpeed", 1); // Default 1 second interval (1-30)
|
|
303
|
+
|
|
304
|
+
if (palletNumber == null) {
|
|
305
|
+
call.reject("palletNumber is required for UNLOCK_PALLET command");
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Create complete single pallet control frame according to protocol
|
|
310
|
+
frame = IOBoardProtocolUtils.createSinglePalletControl(
|
|
311
|
+
(byte) address.intValue(),
|
|
312
|
+
palletNumber.intValue(),
|
|
313
|
+
doorLock.booleanValue(),
|
|
314
|
+
red.intValue(),
|
|
315
|
+
green.intValue(),
|
|
316
|
+
blue.intValue(),
|
|
317
|
+
intensity.intValue(),
|
|
318
|
+
blinkTimes.intValue(),
|
|
319
|
+
blinkSpeed.intValue()
|
|
320
|
+
);
|
|
321
|
+
break;
|
|
322
|
+
|
|
323
|
+
case "CONTROL_MULTIPLE":
|
|
324
|
+
Integer doorLockMask = call.getInt("doorLockMask", 0); // 8-bit mask for door locks
|
|
325
|
+
Integer extendedControl = call.getInt("extendedControl", 0); // 8-bit extended control
|
|
326
|
+
JSArray ledsArray = call.getArray("leds"); // Array of 8 LED configurations
|
|
327
|
+
|
|
328
|
+
// Parse LED configurations for all 8 pallets
|
|
329
|
+
int[][] ledControls = new int[8][6]; // 8 LEDs × 6 parameters each
|
|
330
|
+
|
|
331
|
+
for (int i = 0; i < 8; i++) {
|
|
332
|
+
// Default LED: off (all zeros except blink speed = 1)
|
|
333
|
+
ledControls[i] = new int[]{0, 0, 0, 0, 0, 1};
|
|
334
|
+
|
|
335
|
+
if (ledsArray != null && i < ledsArray.length()) {
|
|
336
|
+
try {
|
|
337
|
+
JSObject ledConfig = ledsArray.getJSObject(i);
|
|
338
|
+
if (ledConfig != null) {
|
|
339
|
+
ledControls[i][0] = ledConfig.getInteger("red", 0); // RGB Red (0-255)
|
|
340
|
+
ledControls[i][1] = ledConfig.getInteger("green", 255); // RGB Green (0-255)
|
|
341
|
+
ledControls[i][2] = ledConfig.getInteger("blue", 0); // RGB Blue (0-255)
|
|
342
|
+
ledControls[i][3] = ledConfig.getInteger("intensity", 100); // Intensity (0-100)
|
|
343
|
+
ledControls[i][4] = ledConfig.getInteger("blinkTimes", 0); // Blink times (0-255)
|
|
344
|
+
ledControls[i][5] = ledConfig.getInteger("blinkSpeed", 1); // Blink speed (1-30)
|
|
345
|
+
}
|
|
346
|
+
} catch (Exception e) {
|
|
347
|
+
Log.w(TAG, "Invalid LED config for pallet " + i + ", using defaults");
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Create complete full pallet control frame according to protocol
|
|
353
|
+
frame = IOBoardProtocolUtils.createFullPalletControl(
|
|
354
|
+
(byte) address.intValue(),
|
|
355
|
+
doorLockMask.intValue(),
|
|
356
|
+
extendedControl.intValue(),
|
|
357
|
+
ledControls
|
|
358
|
+
);
|
|
359
|
+
break;
|
|
360
|
+
|
|
361
|
+
case "OTA_NOTIFICATION":
|
|
362
|
+
Integer majorVersion = call.getInt("majorVersion");
|
|
363
|
+
Integer minorVersion = call.getInt("minorVersion");
|
|
364
|
+
Integer patchVersion = call.getInt("patchVersion");
|
|
365
|
+
Integer firmwareSize = call.getInt("firmwareSize");
|
|
366
|
+
|
|
367
|
+
if (majorVersion == null || minorVersion == null || patchVersion == null || firmwareSize == null) {
|
|
368
|
+
call.reject("All version parameters and firmware size are required for OTA_NOTIFICATION");
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Create OTA notification data payload according to protocol
|
|
373
|
+
// Format: Header('?') + Major + Minor + Patch + Size(4 bytes)
|
|
374
|
+
byte[] otaData = new byte[8];
|
|
375
|
+
otaData[0] = (byte) '?'; // Notification header
|
|
376
|
+
otaData[1] = (byte) majorVersion.intValue(); // Major version (0-99)
|
|
377
|
+
otaData[2] = (byte) minorVersion.intValue(); // Minor version (0-99)
|
|
378
|
+
otaData[3] = (byte) patchVersion.intValue(); // Patch version (0-99)
|
|
379
|
+
// Firmware size as 4 bytes (little endian)
|
|
380
|
+
otaData[4] = (byte) (firmwareSize & 0xFF);
|
|
381
|
+
otaData[5] = (byte) ((firmwareSize >> 8) & 0xFF);
|
|
382
|
+
otaData[6] = (byte) ((firmwareSize >> 16) & 0xFF);
|
|
383
|
+
otaData[7] = (byte) ((firmwareSize >> 24) & 0xFF);
|
|
384
|
+
|
|
385
|
+
frame = IOBoardProtocolUtils.createFrame((byte) address.intValue(), IOBoardProtocolUtils.FRAME_TYPE_OTA_REQUEST, otaData);
|
|
386
|
+
break;
|
|
387
|
+
|
|
388
|
+
case "OTA_DATA":
|
|
389
|
+
Integer packetNumber = call.getInt("packetNumber");
|
|
390
|
+
JSArray dataArray = call.getArray("data");
|
|
391
|
+
|
|
392
|
+
if (packetNumber == null || dataArray == null) {
|
|
393
|
+
call.reject("packetNumber and data array are required for OTA_DATA");
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Create OTA data payload according to protocol
|
|
398
|
+
// Format: PacketNumber(4 bytes) + Data(128 bytes)
|
|
399
|
+
byte[] otaDataFrame = new byte[132]; // 4 + 128
|
|
400
|
+
|
|
401
|
+
// Packet number as 4 bytes (little endian)
|
|
402
|
+
otaDataFrame[0] = (byte) (packetNumber & 0xFF);
|
|
403
|
+
otaDataFrame[1] = (byte) ((packetNumber >> 8) & 0xFF);
|
|
404
|
+
otaDataFrame[2] = (byte) ((packetNumber >> 16) & 0xFF);
|
|
405
|
+
otaDataFrame[3] = (byte) ((packetNumber >> 24) & 0xFF);
|
|
406
|
+
|
|
407
|
+
// Copy data (up to 128 bytes, pad with zeros if less)
|
|
408
|
+
for (int i = 0; i < 128; i++) {
|
|
409
|
+
if (i < dataArray.length()) {
|
|
410
|
+
try {
|
|
411
|
+
otaDataFrame[4 + i] = (byte) dataArray.getInt(i);
|
|
412
|
+
} catch (Exception e) {
|
|
413
|
+
otaDataFrame[4 + i] = 0; // Pad with zero on error
|
|
414
|
+
}
|
|
415
|
+
} else {
|
|
416
|
+
otaDataFrame[4 + i] = 0; // Pad remaining with zeros
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
frame = IOBoardProtocolUtils.createFrame((byte) address.intValue(), IOBoardProtocolUtils.FRAME_TYPE_OTA_DATA, otaDataFrame);
|
|
421
|
+
break;
|
|
422
|
+
|
|
423
|
+
default:
|
|
424
|
+
call.reject("Unknown command: " + command);
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
JSObject result = new JSObject();
|
|
429
|
+
result.put("frame", IOBoardProtocolUtils.frameToHex(frame));
|
|
430
|
+
result.put("command", command);
|
|
431
|
+
result.put("address", address);
|
|
432
|
+
|
|
433
|
+
call.resolve(result);
|
|
434
|
+
|
|
435
|
+
} catch (Exception e) {
|
|
436
|
+
Log.e(TAG, "Error building command", e);
|
|
437
|
+
call.reject("Failed to build command: " + e.getMessage());
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
@PluginMethod
|
|
442
|
+
public void parseResponse(PluginCall call) {
|
|
443
|
+
String hexData = call.getString("data");
|
|
444
|
+
|
|
445
|
+
if (hexData == null || hexData.trim().isEmpty()) {
|
|
446
|
+
call.reject("Data is required");
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
try {
|
|
451
|
+
Log.d(TAG, "Parsing response: " + hexData);
|
|
452
|
+
|
|
453
|
+
IOBoardProtocolUtils.ParsedResponse parsedResponse = IOBoardProtocolUtils.parseResponse(hexData);
|
|
454
|
+
|
|
455
|
+
JSObject result = new JSObject();
|
|
456
|
+
result.put("valid", parsedResponse.success);
|
|
457
|
+
result.put("rawData", hexData);
|
|
458
|
+
result.put("message", parsedResponse.message);
|
|
459
|
+
|
|
460
|
+
if (parsedResponse.success) {
|
|
461
|
+
result.put("address", parsedResponse.address);
|
|
462
|
+
result.put("frameType", parsedResponse.frameType);
|
|
463
|
+
|
|
464
|
+
if (parsedResponse.data != null && parsedResponse.data.length > 0) {
|
|
465
|
+
result.put("payload", IOBoardProtocolUtils.frameToHex(parsedResponse.data));
|
|
466
|
+
|
|
467
|
+
// Parse response data based on frame type
|
|
468
|
+
switch (parsedResponse.frameType) {
|
|
469
|
+
case IOBoardProtocolUtils.FRAME_TYPE_STATUS_QUERY:
|
|
470
|
+
// Status response: LOCK(1) + Version(3)
|
|
471
|
+
if (parsedResponse.data.length >= 4) {
|
|
472
|
+
result.put("doorLockStatus", parsedResponse.data[0] & 0xFF);
|
|
473
|
+
|
|
474
|
+
JSObject version = new JSObject();
|
|
475
|
+
version.put("major", parsedResponse.data[1] & 0xFF);
|
|
476
|
+
version.put("minor", parsedResponse.data[2] & 0xFF);
|
|
477
|
+
version.put("patch", parsedResponse.data[3] & 0xFF);
|
|
478
|
+
result.put("softwareVersion", version);
|
|
479
|
+
}
|
|
480
|
+
break;
|
|
481
|
+
|
|
482
|
+
case IOBoardProtocolUtils.FRAME_TYPE_OTA_RESPONSE:
|
|
483
|
+
// OTA Response: Header(1) + Code(1) + Data(4)
|
|
484
|
+
if (parsedResponse.data.length >= 2) {
|
|
485
|
+
char responseHeader = (char) (parsedResponse.data[0] & 0xFF);
|
|
486
|
+
int responseCode = parsedResponse.data[1] & 0xFF;
|
|
487
|
+
|
|
488
|
+
result.put("otaResponseHeader", String.valueOf(responseHeader));
|
|
489
|
+
result.put("otaResponseCode", responseCode);
|
|
490
|
+
|
|
491
|
+
// Parse OTA response types
|
|
492
|
+
JSObject otaResponse = new JSObject();
|
|
493
|
+
switch (responseHeader) {
|
|
494
|
+
case '&': // Area request
|
|
495
|
+
otaResponse.put("type", "area_request");
|
|
496
|
+
if (responseCode == 'A') {
|
|
497
|
+
otaResponse.put("area", "A");
|
|
498
|
+
otaResponse.put("description", "Request A zone firmware");
|
|
499
|
+
} else if (responseCode == 'B') {
|
|
500
|
+
otaResponse.put("area", "B");
|
|
501
|
+
otaResponse.put("description", "Request B zone firmware");
|
|
502
|
+
}
|
|
503
|
+
// Parse size confirmation (4 bytes)
|
|
504
|
+
if (parsedResponse.data.length >= 6) {
|
|
505
|
+
int size = (parsedResponse.data[2] & 0xFF) |
|
|
506
|
+
((parsedResponse.data[3] & 0xFF) << 8) |
|
|
507
|
+
((parsedResponse.data[4] & 0xFF) << 16) |
|
|
508
|
+
((parsedResponse.data[5] & 0xFF) << 24);
|
|
509
|
+
otaResponse.put("sizeConfirmation", size);
|
|
510
|
+
}
|
|
511
|
+
break;
|
|
512
|
+
|
|
513
|
+
case '#': // Data request
|
|
514
|
+
otaResponse.put("type", "data_request");
|
|
515
|
+
// Parse packet number (4 bytes)
|
|
516
|
+
if (parsedResponse.data.length >= 6) {
|
|
517
|
+
int packetNum = (parsedResponse.data[2] & 0xFF) |
|
|
518
|
+
((parsedResponse.data[3] & 0xFF) << 8) |
|
|
519
|
+
((parsedResponse.data[4] & 0xFF) << 16) |
|
|
520
|
+
((parsedResponse.data[5] & 0xFF) << 24);
|
|
521
|
+
otaResponse.put("requestedPacket", packetNum);
|
|
522
|
+
otaResponse.put("description", "Request packet " + packetNum);
|
|
523
|
+
}
|
|
524
|
+
break;
|
|
525
|
+
|
|
526
|
+
case 'O': // Completion notification
|
|
527
|
+
otaResponse.put("type", "completion");
|
|
528
|
+
otaResponse.put("description", "OTA upgrade completed successfully");
|
|
529
|
+
break;
|
|
530
|
+
|
|
531
|
+
case 'E': // Failure notification
|
|
532
|
+
otaResponse.put("type", "error");
|
|
533
|
+
String errorDesc = "Unknown error";
|
|
534
|
+
switch (responseCode) {
|
|
535
|
+
case 9: errorDesc = "Illegal version/firmware size"; break;
|
|
536
|
+
case 8: errorDesc = "Non-OTA status"; break;
|
|
537
|
+
case 7: errorDesc = "Write failed"; break;
|
|
538
|
+
case 6: errorDesc = "Verification failed"; break;
|
|
539
|
+
case 5: errorDesc = "Transfer failed"; break;
|
|
540
|
+
}
|
|
541
|
+
otaResponse.put("errorCode", responseCode);
|
|
542
|
+
otaResponse.put("description", errorDesc);
|
|
543
|
+
break;
|
|
544
|
+
}
|
|
545
|
+
result.put("otaResponse", otaResponse);
|
|
546
|
+
}
|
|
547
|
+
break;
|
|
548
|
+
|
|
549
|
+
default:
|
|
550
|
+
// For other frame types, just include raw payload
|
|
551
|
+
result.put("frameTypeName", getFrameTypeName(parsedResponse.frameType));
|
|
552
|
+
break;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
call.resolve(result);
|
|
558
|
+
|
|
559
|
+
} catch (Exception e) {
|
|
560
|
+
Log.e(TAG, "Error parsing response", e);
|
|
561
|
+
call.reject("Failed to parse response: " + e.getMessage());
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
@PluginMethod
|
|
566
|
+
public void validateFrame(PluginCall call) {
|
|
567
|
+
String hexData = call.getString("data");
|
|
568
|
+
|
|
569
|
+
if (hexData == null || hexData.trim().isEmpty()) {
|
|
570
|
+
call.reject("Data is required");
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
try {
|
|
575
|
+
// Use parseResponse to validate the frame
|
|
576
|
+
IOBoardProtocolUtils.ParsedResponse parsed = IOBoardProtocolUtils.parseResponse(hexData);
|
|
577
|
+
|
|
578
|
+
JSObject result = new JSObject();
|
|
579
|
+
result.put("valid", parsed.success);
|
|
580
|
+
result.put("data", hexData);
|
|
581
|
+
result.put("message", parsed.message);
|
|
582
|
+
|
|
583
|
+
if (parsed.success) {
|
|
584
|
+
byte[] frameData = IOBoardProtocolUtils.hexStringToBytes(hexData);
|
|
585
|
+
result.put("length", frameData.length);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
call.resolve(result);
|
|
589
|
+
|
|
590
|
+
} catch (Exception e) {
|
|
591
|
+
Log.e(TAG, "Error validating frame", e);
|
|
592
|
+
call.reject("Failed to validate frame: " + e.getMessage());
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
@PluginMethod
|
|
597
|
+
public void hexToBytes(PluginCall call) {
|
|
598
|
+
String hexData = call.getString("data");
|
|
599
|
+
|
|
600
|
+
if (hexData == null || hexData.trim().isEmpty()) {
|
|
601
|
+
call.reject("Data is required");
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
try {
|
|
606
|
+
byte[] bytes = IOBoardProtocolUtils.hexStringToBytes(hexData);
|
|
607
|
+
|
|
608
|
+
// Convert byte array to JSArray of integers
|
|
609
|
+
JSArray bytesArray = new JSArray();
|
|
610
|
+
for (byte b : bytes) {
|
|
611
|
+
bytesArray.put((int) b & 0xFF); // Convert to unsigned int
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
JSObject result = new JSObject();
|
|
615
|
+
result.put("bytes", bytesArray);
|
|
616
|
+
result.put("length", bytes.length);
|
|
617
|
+
result.put("hex", hexData);
|
|
618
|
+
|
|
619
|
+
call.resolve(result);
|
|
620
|
+
|
|
621
|
+
} catch (Exception e) {
|
|
622
|
+
Log.e(TAG, "Error converting hex to bytes", e);
|
|
623
|
+
call.reject("Failed to convert hex to bytes: " + e.getMessage());
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
@PluginMethod
|
|
628
|
+
public void bytesToHex(PluginCall call) {
|
|
629
|
+
JSArray bytesArray = call.getArray("bytes");
|
|
630
|
+
|
|
631
|
+
if (bytesArray == null) {
|
|
632
|
+
call.reject("Bytes array is required");
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
try {
|
|
637
|
+
byte[] bytes = new byte[bytesArray.length()];
|
|
638
|
+
for (int i = 0; i < bytesArray.length(); i++) {
|
|
639
|
+
bytes[i] = (byte) bytesArray.getInt(i);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
String hexString = IOBoardProtocolUtils.frameToHex(bytes);
|
|
643
|
+
|
|
644
|
+
// Convert byte array back to JSArray for consistency
|
|
645
|
+
JSArray resultBytesArray = new JSArray();
|
|
646
|
+
for (byte b : bytes) {
|
|
647
|
+
resultBytesArray.put((int) b & 0xFF);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
JSObject result = new JSObject();
|
|
651
|
+
result.put("hex", hexString);
|
|
652
|
+
result.put("length", bytes.length);
|
|
653
|
+
result.put("bytes", resultBytesArray);
|
|
654
|
+
|
|
655
|
+
call.resolve(result);
|
|
656
|
+
|
|
657
|
+
} catch (Exception e) {
|
|
658
|
+
Log.e(TAG, "Error converting bytes to hex", e);
|
|
659
|
+
call.reject("Failed to convert bytes to hex: " + e.getMessage());
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// ================== HELPER METHODS ==================
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Sends a command and waits for response
|
|
667
|
+
*/
|
|
668
|
+
private void sendCommandAndWaitForResponse(byte[] command, PluginCall call, String commandType) {
|
|
669
|
+
try {
|
|
670
|
+
// Set up future for response
|
|
671
|
+
currentCommandFuture = new CompletableFuture<>();
|
|
672
|
+
|
|
673
|
+
// Send command
|
|
674
|
+
boolean sent = serialManager.writeData(command);
|
|
675
|
+
if (!sent) {
|
|
676
|
+
call.reject("Failed to send command");
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// Wait for response with timeout
|
|
681
|
+
JSObject response = currentCommandFuture.get(5, TimeUnit.SECONDS);
|
|
682
|
+
call.resolve(response);
|
|
683
|
+
|
|
684
|
+
} catch (Exception e) {
|
|
685
|
+
Log.e(TAG, "Error in sendCommandAndWaitForResponse: " + e.getMessage(), e);
|
|
686
|
+
call.reject("Command timeout or error: " + e.getMessage());
|
|
687
|
+
} finally {
|
|
688
|
+
currentCommandFuture = null;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// ================== SERIAL DATA LISTENER IMPLEMENTATION ==================
|
|
693
|
+
|
|
694
|
+
@Override
|
|
695
|
+
public void onDataReceived(byte[] data) {
|
|
696
|
+
try {
|
|
697
|
+
Log.d(TAG, "Received " + data.length + " bytes from serial port");
|
|
698
|
+
|
|
699
|
+
// Parse the response using IOBoardManager
|
|
700
|
+
JSObject response = ioboardManager.parseResponse(data);
|
|
701
|
+
|
|
702
|
+
// Complete the current command future if waiting
|
|
703
|
+
if (currentCommandFuture != null && !currentCommandFuture.isDone()) {
|
|
704
|
+
currentCommandFuture.complete(response);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// Notify listeners
|
|
708
|
+
JSObject eventData = new JSObject();
|
|
709
|
+
eventData.put("data", response);
|
|
710
|
+
eventData.put("rawBytes", Arrays.toString(data));
|
|
711
|
+
notifyListeners("dataReceived", eventData);
|
|
712
|
+
|
|
713
|
+
} catch (Exception e) {
|
|
714
|
+
Log.e(TAG, "Error processing received data: " + e.getMessage(), e);
|
|
715
|
+
|
|
716
|
+
// Complete future with error if waiting
|
|
717
|
+
if (currentCommandFuture != null && !currentCommandFuture.isDone()) {
|
|
718
|
+
JSObject errorResponse = new JSObject();
|
|
719
|
+
errorResponse.put("success", false);
|
|
720
|
+
errorResponse.put("error", "Parse error: " + e.getMessage());
|
|
721
|
+
currentCommandFuture.complete(errorResponse);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
@Override
|
|
727
|
+
public void onConnectionStateChanged(boolean connected) {
|
|
728
|
+
Log.d(TAG, "Connection state changed: " + (connected ? "Connected" : "Disconnected"));
|
|
729
|
+
|
|
730
|
+
JSObject eventData = new JSObject();
|
|
731
|
+
eventData.put("connected", connected);
|
|
732
|
+
eventData.put("portPath", serialManager.getCurrentPortPath());
|
|
733
|
+
notifyListeners("connectionStateChanged", eventData);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
@Override
|
|
737
|
+
public void onError(String error) {
|
|
738
|
+
Log.e(TAG, "Serial connection error: " + error);
|
|
739
|
+
|
|
740
|
+
JSObject eventData = new JSObject();
|
|
741
|
+
eventData.put("error", error);
|
|
742
|
+
notifyListeners("serialError", eventData);
|
|
743
|
+
|
|
744
|
+
// Complete future with error if waiting
|
|
745
|
+
if (currentCommandFuture != null && !currentCommandFuture.isDone()) {
|
|
746
|
+
JSObject errorResponse = new JSObject();
|
|
747
|
+
errorResponse.put("success", false);
|
|
748
|
+
errorResponse.put("error", error);
|
|
749
|
+
currentCommandFuture.complete(errorResponse);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
@Override
|
|
754
|
+
public void onDestroy() {
|
|
755
|
+
super.onDestroy();
|
|
756
|
+
Log.d(TAG, "Plugin destroying - cleaning up resources");
|
|
757
|
+
|
|
758
|
+
if (serialManager != null) {
|
|
759
|
+
serialManager.cleanup();
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
/**
|
|
764
|
+
* Helper method to get frame type name
|
|
765
|
+
*/
|
|
766
|
+
private String getFrameTypeName(byte frameType) {
|
|
767
|
+
switch (frameType) {
|
|
768
|
+
case IOBoardProtocolUtils.FRAME_TYPE_STATUS_QUERY: return "STATUS_QUERY";
|
|
769
|
+
case IOBoardProtocolUtils.FRAME_TYPE_SINGLE_PALLET: return "SINGLE_PALLET";
|
|
770
|
+
case IOBoardProtocolUtils.FRAME_TYPE_FULL_PALLET: return "FULL_PALLET";
|
|
771
|
+
case IOBoardProtocolUtils.FRAME_TYPE_OTA_REQUEST: return "OTA_REQUEST";
|
|
772
|
+
case IOBoardProtocolUtils.FRAME_TYPE_OTA_DATA: return "OTA_DATA";
|
|
773
|
+
case IOBoardProtocolUtils.FRAME_TYPE_OTA_RESPONSE: return "OTA_RESPONSE";
|
|
774
|
+
default: return "UNKNOWN_0x" + String.format("%02X", frameType & 0xFF);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|