@leonardojc/capacitor-ioboard 1.1.0 → 1.2.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.
@@ -3,26 +3,21 @@ package com.leonardojc.capacitor.ioboard;
3
3
  import android.util.Log;
4
4
  import android.util.Base64;
5
5
  import java.util.Arrays;
6
+ import java.util.ArrayList;
7
+ import java.util.List;
6
8
  import java.util.concurrent.TimeUnit;
7
9
 
8
10
  /**
9
11
  * Manager class for IOBOARD communication protocol
10
12
  * This class integrates with the @leonardojc/capacitor-serial-port plugin
13
+ * Uses IOBoardProtocolUtils for MTC3P08L protocol implementation
11
14
  */
12
15
  public class IOBoardManager {
13
16
 
14
17
  private static final String TAG = "IOBoardManager";
15
- private static final int SOI = 0x0D; // Start of frame
16
- private static final int EOI = 0x0A; // End of frame
17
-
18
- // Frame types
19
- private static final int FRAME_TYPE_STATUS_QUERY = 0x00;
20
- private static final int FRAME_TYPE_SINGLE_PALLET = 0x01;
21
- private static final int FRAME_TYPE_FULL_PALLET = 0x02;
22
- private static final int FRAME_TYPE_OTA_REQUEST = 0xF1;
23
- private static final int FRAME_TYPE_OTA_DATA = 0xF2;
24
18
 
25
19
  private boolean isConnected = false;
20
+ private String currentSerialPortId = null;
26
21
 
27
22
  // Response classes
28
23
  public static class IOBoardResponse {
@@ -127,89 +122,18 @@ public class IOBoardManager {
127
122
  }
128
123
 
129
124
  /**
130
- * Calculate CRC-16 CCITT checksum
131
- */
132
- private int calculateCRC16(int[] data) {
133
- int crc = 0xFFFF;
134
-
135
- for (int dataByte : data) {
136
- crc ^= (dataByte << 8);
137
-
138
- for (int bit = 0; bit < 8; bit++) {
139
- if ((crc & 0x8000) != 0) {
140
- crc = (crc << 1) ^ 0x1021;
141
- } else {
142
- crc = crc << 1;
143
- }
144
- crc &= 0xFFFF;
145
- }
146
- }
147
-
148
- return crc;
149
- }
150
-
151
- /**
152
- * Build a command frame for IOBOARD
153
- */
154
- private int[] buildFrame(int address, int frameType, int[] data) {
155
- // Calculate frame size: SOI + Address + FrameType + Data + CRC(2) + EOI
156
- int frameSize = 1 + 1 + 1 + data.length + 2 + 1;
157
- int[] frame = new int[frameSize];
158
- int index = 0;
159
-
160
- // SOI
161
- frame[index++] = SOI;
162
-
163
- // Address
164
- frame[index++] = address;
165
-
166
- // Frame Type
167
- frame[index++] = frameType;
168
-
169
- // Data
170
- for (int dataByte : data) {
171
- frame[index++] = dataByte;
172
- }
173
-
174
- // Calculate CRC for address + frameType + data
175
- int[] crcData = new int[1 + 1 + data.length];
176
- crcData[0] = address;
177
- crcData[1] = frameType;
178
- System.arraycopy(data, 0, crcData, 2, data.length);
179
-
180
- int crc = calculateCRC16(crcData);
181
-
182
- // CRC (high byte first)
183
- frame[index++] = (crc >> 8) & 0xFF;
184
- frame[index++] = crc & 0xFF;
185
-
186
- // EOI
187
- frame[index] = EOI;
188
-
189
- return frame;
190
- }
191
-
192
- /**
193
- * Convert frame to byte array for serial transmission
194
- */
195
- private byte[] frameToBytes(int[] frame) {
196
- byte[] bytes = new byte[frame.length];
197
- for (int i = 0; i < frame.length; i++) {
198
- bytes[i] = (byte) (frame[i] & 0xFF);
199
- }
200
- return bytes;
201
- }
202
-
203
- /**
204
- * Convert frame to hex string for debugging
125
+ * Send command via SerialPort plugin and wait for response
205
126
  */
206
- private String frameToHex(int[] frame) {
207
- StringBuilder sb = new StringBuilder();
208
- for (int i = 0; i < frame.length; i++) {
209
- if (i > 0) sb.append(" ");
210
- sb.append(String.format("%02X", frame[i]));
211
- }
212
- return sb.toString();
127
+ private String sendCommandAndWaitResponse(byte[] command, int timeoutMs) throws Exception {
128
+ // TODO: Integrate with @leonardojc/capacitor-serial-port plugin
129
+ // This method should:
130
+ // 1. Convert command to appropriate format for SerialPort plugin
131
+ // 2. Call SerialPort.writeData(portId, data)
132
+ // 3. Wait for response using SerialPort.readData() or data listeners
133
+ // 4. Return response string
134
+
135
+ // For now, return mock response for testing
136
+ throw new Exception("SerialPort integration not yet implemented");
213
137
  }
214
138
 
215
139
  // New simplified API methods
@@ -221,9 +145,13 @@ public class IOBoardManager {
221
145
  Log.d(TAG, "Connecting to serial port: " + config.portPath + " at " + config.baudRate + " baud");
222
146
 
223
147
  try {
224
- // TODO: Initialize connection with serial port plugin
148
+ // TODO: Initialize connection with SerialPort plugin
149
+ // Call SerialPort.openConnection() with config parameters
150
+ // Store the returned port ID for future operations
151
+
225
152
  // For now, just set the connected flag
226
153
  isConnected = true;
154
+ currentSerialPortId = "mock_port_id";
227
155
 
228
156
  return new IOBoardResponse(true, "Connected to " + config.portPath + " successfully");
229
157
 
@@ -240,8 +168,11 @@ public class IOBoardManager {
240
168
  Log.d(TAG, "Disconnecting from serial port");
241
169
 
242
170
  try {
243
- // TODO: Close connection with serial port plugin
171
+ // TODO: Close connection with SerialPort plugin
172
+ // Call SerialPort.closeConnection(currentSerialPortId)
173
+
244
174
  isConnected = false;
175
+ currentSerialPortId = null;
245
176
 
246
177
  return new IOBoardResponse(true, "Disconnected successfully");
247
178
 
@@ -262,15 +193,33 @@ public class IOBoardManager {
262
193
  }
263
194
 
264
195
  try {
265
- // Build status query frame
266
- int[] frame = buildFrame(address, FRAME_TYPE_STATUS_QUERY, new int[0]);
196
+ // Build status query frame using IOBoardProtocolUtils
197
+ byte[] frame = IOBoardProtocolUtils.createStatusQuery((byte) address);
198
+
199
+ Log.d(TAG, "Sending status query frame: " + IOBoardProtocolUtils.frameToHex(frame));
267
200
 
268
- Log.d(TAG, "Sending status query frame: " + frameToHex(frame));
201
+ // Send frame and wait for response
202
+ String response = sendCommandAndWaitResponse(frame, timeout);
269
203
 
270
- // TODO: Send frame via serial port plugin and receive response
271
- // For now, return a mock response
272
- StatusData mockData = new StatusData(0b11010001, new SoftwareVersion(1, 2, 3));
273
- return new StatusResponse(true, "Status retrieved successfully", mockData);
204
+ // Parse response
205
+ IOBoardProtocolUtils.ParsedResponse parsedResponse = IOBoardProtocolUtils.parseResponse(response);
206
+
207
+ if (!parsedResponse.success) {
208
+ return new StatusResponse(false, "Failed to parse response: " + parsedResponse.message, null);
209
+ }
210
+
211
+ // Extract status data from response
212
+ if (parsedResponse.data.length >= 4) {
213
+ int doorLockStatus = parsedResponse.data[0] & 0xFF;
214
+ int majorVersion = parsedResponse.data[1] & 0xFF;
215
+ int minorVersion = parsedResponse.data[2] & 0xFF;
216
+ int patchVersion = parsedResponse.data[3] & 0xFF;
217
+
218
+ StatusData statusData = new StatusData(doorLockStatus, new SoftwareVersion(majorVersion, minorVersion, patchVersion));
219
+ return new StatusResponse(true, "Status retrieved successfully", statusData);
220
+ } else {
221
+ return new StatusResponse(false, "Invalid response data length", null);
222
+ }
274
223
 
275
224
  } catch (Exception e) {
276
225
  Log.e(TAG, "Error getting status", e);
@@ -292,22 +241,25 @@ public class IOBoardManager {
292
241
  // Convert LED color to RGB values
293
242
  LEDControl ledControl = getLEDControlFromColor(ledColor);
294
243
 
295
- // Build data array for single pallet control
296
- int[] data = new int[8];
297
- data[0] = palletNumber;
298
- data[1] = 1; // doorLock = true (unlock)
299
- data[2] = ledControl.red;
300
- data[3] = ledControl.green;
301
- data[4] = ledControl.blue;
302
- data[5] = ledControl.intensity;
303
- data[6] = ledControl.blinkTimes;
304
- data[7] = ledControl.blinkSpeed;
244
+ // Build single pallet control frame using IOBoardProtocolUtils
245
+ byte[] frame = IOBoardProtocolUtils.createSinglePalletControl(
246
+ (byte) address, palletNumber, true,
247
+ ledControl.red, ledControl.green, ledControl.blue,
248
+ ledControl.intensity, ledControl.blinkTimes, ledControl.blinkSpeed
249
+ );
250
+
251
+ Log.d(TAG, "Sending unlock pallet frame: " + IOBoardProtocolUtils.frameToHex(frame));
305
252
 
306
- int[] frame = buildFrame(address, FRAME_TYPE_SINGLE_PALLET, data);
253
+ // Send frame and wait for response
254
+ String response = sendCommandAndWaitResponse(frame, timeout);
307
255
 
308
- Log.d(TAG, "Sending unlock pallet frame: " + frameToHex(frame));
256
+ // Parse response
257
+ IOBoardProtocolUtils.ParsedResponse parsedResponse = IOBoardProtocolUtils.parseResponse(response);
258
+
259
+ if (!parsedResponse.success) {
260
+ return new IOBoardResponse(false, "Failed to parse response: " + parsedResponse.message);
261
+ }
309
262
 
310
- // TODO: Send frame via serial port plugin and receive response
311
263
  return new IOBoardResponse(true, "Pallet " + palletNumber + " unlocked successfully");
312
264
 
313
265
  } catch (Exception e) {
@@ -330,30 +282,35 @@ public class IOBoardManager {
330
282
  // Convert LED color to RGB values
331
283
  LEDControl ledControl = getLEDControlFromColor(ledColor);
332
284
 
333
- // Build data array for full pallet control
334
- int[] data = new int[2 + (8 * 6)];
335
- int index = 0;
336
-
337
- data[index++] = doorLockMask;
338
- data[index++] = 0; // extendedControl
339
-
340
- // Add LED controls for 8 pallets
285
+ // Build LED controls array for 8 pallets
286
+ int[][] ledControls = new int[8][6];
341
287
  for (int i = 0; i < 8; i++) {
342
288
  // If pallet is affected by mask, use specified LED color, otherwise turn off
343
- LEDControl led = ((doorLockMask & (1 << i)) != 0) ? ledControl : new LEDControl(0, 0, 0, 0, 0, 0);
344
- data[index++] = led.red;
345
- data[index++] = led.green;
346
- data[index++] = led.blue;
347
- data[index++] = led.intensity;
348
- data[index++] = led.blinkTimes;
349
- data[index++] = led.blinkSpeed;
289
+ if ((doorLockMask & (1 << i)) != 0) {
290
+ ledControls[i] = new int[]{ledControl.red, ledControl.green, ledControl.blue,
291
+ ledControl.intensity, ledControl.blinkTimes, ledControl.blinkSpeed};
292
+ } else {
293
+ ledControls[i] = new int[]{0, 0, 0, 0, 0, 1}; // Off
294
+ }
350
295
  }
351
296
 
352
- int[] frame = buildFrame(address, FRAME_TYPE_FULL_PALLET, data);
297
+ // Build full pallet control frame using IOBoardProtocolUtils
298
+ byte[] frame = IOBoardProtocolUtils.createFullPalletControl(
299
+ (byte) address, doorLockMask, 0, ledControls
300
+ );
301
+
302
+ Log.d(TAG, "Sending multiple pallets control frame: " + IOBoardProtocolUtils.frameToHex(frame));
303
+
304
+ // Send frame and wait for response
305
+ String response = sendCommandAndWaitResponse(frame, timeout);
353
306
 
354
- Log.d(TAG, "Sending multiple pallets control frame: " + frameToHex(frame));
307
+ // Parse response
308
+ IOBoardProtocolUtils.ParsedResponse parsedResponse = IOBoardProtocolUtils.parseResponse(response);
309
+
310
+ if (!parsedResponse.success) {
311
+ return new IOBoardResponse(false, "Failed to parse response: " + parsedResponse.message);
312
+ }
355
313
 
356
- // TODO: Send frame via serial port plugin and receive response
357
314
  return new IOBoardResponse(true, "Multiple pallets controlled successfully");
358
315
 
359
316
  } catch (Exception e) {
@@ -374,17 +331,52 @@ public class IOBoardManager {
374
331
 
375
332
  try {
376
333
  int deviceCount = endAddress - startAddress + 1;
377
- DeviceInfo[] devices = new DeviceInfo[deviceCount];
334
+ List<DeviceInfo> deviceList = new ArrayList<>();
378
335
 
379
- // TODO: Actually scan devices by sending status queries
380
- // For now, create mock responses
336
+ // Scan each address by sending status queries
381
337
  for (int i = 0; i < deviceCount; i++) {
382
338
  int address = startAddress + i;
383
- boolean responding = (i % 3 == 0); // Mock: every 3rd device responds
384
- StatusData status = responding ? new StatusData(0x00, new SoftwareVersion(1, 0, 0)) : null;
385
- devices[i] = new DeviceInfo(address, responding, status);
339
+
340
+ try {
341
+ // Build status query frame using IOBoardProtocolUtils
342
+ byte[] frame = IOBoardProtocolUtils.createStatusQuery((byte) address);
343
+
344
+ Log.d(TAG, "Scanning address " + address + ": " + IOBoardProtocolUtils.frameToHex(frame));
345
+
346
+ // Send frame and wait for response (with shorter timeout for scanning)
347
+ String response = sendCommandAndWaitResponse(frame, Math.min(timeout, 1000));
348
+
349
+ // Parse response
350
+ IOBoardProtocolUtils.ParsedResponse parsedResponse = IOBoardProtocolUtils.parseResponse(response);
351
+
352
+ if (parsedResponse.success) {
353
+ // Device responded - extract status data
354
+ StatusData status = null;
355
+ if (parsedResponse.data.length >= 4) {
356
+ int doorLockStatus = parsedResponse.data[0] & 0xFF;
357
+ int majorVersion = parsedResponse.data[1] & 0xFF;
358
+ int minorVersion = parsedResponse.data[2] & 0xFF;
359
+ int patchVersion = parsedResponse.data[3] & 0xFF;
360
+
361
+ status = new StatusData(doorLockStatus, new SoftwareVersion(majorVersion, minorVersion, patchVersion));
362
+ }
363
+
364
+ deviceList.add(new DeviceInfo(address, true, status));
365
+ Log.d(TAG, "Device found at address " + address);
366
+ } else {
367
+ // No response or invalid response
368
+ deviceList.add(new DeviceInfo(address, false, null));
369
+ Log.d(TAG, "No response from address " + address);
370
+ }
371
+
372
+ } catch (Exception e) {
373
+ // Device not responding
374
+ deviceList.add(new DeviceInfo(address, false, null));
375
+ Log.d(TAG, "Error scanning address " + address + ": " + e.getMessage());
376
+ }
386
377
  }
387
378
 
379
+ DeviceInfo[] devices = deviceList.toArray(new DeviceInfo[0]);
388
380
  return new ScanResponse(true, "Scanned addresses " + startAddress + "-" + endAddress, devices);
389
381
 
390
382
  } catch (Exception e) {
@@ -446,15 +438,33 @@ public class IOBoardManager {
446
438
  }
447
439
 
448
440
  try {
449
- // Build status query frame
450
- int[] frame = buildFrame(address, FRAME_TYPE_STATUS_QUERY, new int[0]);
441
+ // Build status query frame using IOBoardProtocolUtils
442
+ byte[] frame = IOBoardProtocolUtils.createStatusQuery((byte) address);
443
+
444
+ Log.d(TAG, "Sending status query frame: " + IOBoardProtocolUtils.frameToHex(frame));
445
+
446
+ // Send frame and wait for response
447
+ String response = sendCommandAndWaitResponse(frame, 5000);
451
448
 
452
- Log.d(TAG, "Sending status query frame: " + frameToHex(frame));
449
+ // Parse response
450
+ IOBoardProtocolUtils.ParsedResponse parsedResponse = IOBoardProtocolUtils.parseResponse(response);
451
+
452
+ if (!parsedResponse.success) {
453
+ return new StatusResponse(false, "Failed to parse response: " + parsedResponse.message, null);
454
+ }
453
455
 
454
- // TODO: Send frame via serial port plugin and receive response
455
- // For now, return a mock response
456
- StatusData mockData = new StatusData(0x00, new SoftwareVersion(1, 0, 0));
457
- return new StatusResponse(true, "Status queried successfully", mockData);
456
+ // Extract status data from response
457
+ if (parsedResponse.data.length >= 4) {
458
+ int doorLockStatus = parsedResponse.data[0] & 0xFF;
459
+ int majorVersion = parsedResponse.data[1] & 0xFF;
460
+ int minorVersion = parsedResponse.data[2] & 0xFF;
461
+ int patchVersion = parsedResponse.data[3] & 0xFF;
462
+
463
+ StatusData statusData = new StatusData(doorLockStatus, new SoftwareVersion(majorVersion, minorVersion, patchVersion));
464
+ return new StatusResponse(true, "Status queried successfully", statusData);
465
+ } else {
466
+ return new StatusResponse(false, "Invalid response data length", null);
467
+ }
458
468
 
459
469
  } catch (Exception e) {
460
470
  Log.e(TAG, "Error querying status", e);
@@ -473,22 +483,25 @@ public class IOBoardManager {
473
483
  }
474
484
 
475
485
  try {
476
- // Build data array
477
- int[] data = new int[8];
478
- data[0] = palletNumber;
479
- data[1] = doorLock ? 1 : 0;
480
- data[2] = ledControl.red;
481
- data[3] = ledControl.green;
482
- data[4] = ledControl.blue;
483
- data[5] = ledControl.intensity;
484
- data[6] = ledControl.blinkTimes;
485
- data[7] = ledControl.blinkSpeed;
486
-
487
- int[] frame = buildFrame(address, FRAME_TYPE_SINGLE_PALLET, data);
488
-
489
- Log.d(TAG, "Sending single pallet control frame: " + frameToHex(frame));
490
-
491
- // TODO: Send frame via serial port plugin and receive response
486
+ // Build single pallet control frame using IOBoardProtocolUtils
487
+ byte[] frame = IOBoardProtocolUtils.createSinglePalletControl(
488
+ (byte) address, palletNumber, doorLock,
489
+ ledControl.red, ledControl.green, ledControl.blue,
490
+ ledControl.intensity, ledControl.blinkTimes, ledControl.blinkSpeed
491
+ );
492
+
493
+ Log.d(TAG, "Sending single pallet control frame: " + IOBoardProtocolUtils.frameToHex(frame));
494
+
495
+ // Send frame and wait for response
496
+ String response = sendCommandAndWaitResponse(frame, 5000);
497
+
498
+ // Parse response
499
+ IOBoardProtocolUtils.ParsedResponse parsedResponse = IOBoardProtocolUtils.parseResponse(response);
500
+
501
+ if (!parsedResponse.success) {
502
+ return new IOBoardResponse(false, "Failed to parse response: " + parsedResponse.message);
503
+ }
504
+
492
505
  return new IOBoardResponse(true, "Single pallet controlled successfully");
493
506
 
494
507
  } catch (Exception e) {
@@ -508,29 +521,33 @@ public class IOBoardManager {
508
521
  }
509
522
 
510
523
  try {
511
- // Build data array: doorLockControl + extendedControl + 8 LED controls (6 bytes each)
512
- int[] data = new int[2 + (8 * 6)];
513
- int index = 0;
514
-
515
- data[index++] = doorLockControl;
516
- data[index++] = extendedControl;
517
-
518
- // Add LED controls for 8 pallets
524
+ // Convert LEDControl array to int[][] for IOBoardProtocolUtils
525
+ int[][] ledControlsArray = new int[8][6];
519
526
  for (int i = 0; i < 8; i++) {
520
527
  LEDControl led = (i < ledControls.length) ? ledControls[i] : new LEDControl(0, 0, 0, 0, 0, 0);
521
- data[index++] = led.red;
522
- data[index++] = led.green;
523
- data[index++] = led.blue;
524
- data[index++] = led.intensity;
525
- data[index++] = led.blinkTimes;
526
- data[index++] = led.blinkSpeed;
528
+ ledControlsArray[i] = new int[]{
529
+ led.red, led.green, led.blue,
530
+ led.intensity, led.blinkTimes, led.blinkSpeed
531
+ };
527
532
  }
528
533
 
529
- int[] frame = buildFrame(address, FRAME_TYPE_FULL_PALLET, data);
534
+ // Build full pallet control frame using IOBoardProtocolUtils
535
+ byte[] frame = IOBoardProtocolUtils.createFullPalletControl(
536
+ (byte) address, doorLockControl, extendedControl, ledControlsArray
537
+ );
538
+
539
+ Log.d(TAG, "Sending full pallet control frame: " + IOBoardProtocolUtils.frameToHex(frame));
530
540
 
531
- Log.d(TAG, "Sending full pallet control frame: " + frameToHex(frame));
541
+ // Send frame and wait for response
542
+ String response = sendCommandAndWaitResponse(frame, 5000);
543
+
544
+ // Parse response
545
+ IOBoardProtocolUtils.ParsedResponse parsedResponse = IOBoardProtocolUtils.parseResponse(response);
546
+
547
+ if (!parsedResponse.success) {
548
+ return new IOBoardResponse(false, "Failed to parse response: " + parsedResponse.message);
549
+ }
532
550
 
533
- // TODO: Send frame via serial port plugin and receive response
534
551
  return new IOBoardResponse(true, "Full pallet controlled successfully");
535
552
 
536
553
  } catch (Exception e) {
@@ -550,21 +567,32 @@ public class IOBoardManager {
550
567
  }
551
568
 
552
569
  try {
553
- // Build OTA notification data: version(3) + firmware size(4)
554
- int[] data = new int[7];
555
- data[0] = majorVersion;
556
- data[1] = minorVersion;
557
- data[2] = patchVersion;
558
- data[3] = firmwareSize & 0xFF;
559
- data[4] = (firmwareSize >> 8) & 0xFF;
560
- data[5] = (firmwareSize >> 16) & 0xFF;
561
- data[6] = (firmwareSize >> 24) & 0xFF;
562
-
563
- int[] frame = buildFrame(address, FRAME_TYPE_OTA_REQUEST, data);
564
-
565
- Log.d(TAG, "Sending OTA notification frame: " + frameToHex(frame));
570
+ // TODO: Add OTA notification method to IOBoardProtocolUtils
571
+ // For now, use manual frame building based on protocol specs
572
+ byte[] data = new byte[8]; // '?' + version(3) + firmware size(4)
573
+ data[0] = 0x3F; // '?' Notification header
574
+ data[1] = (byte) majorVersion;
575
+ data[2] = (byte) minorVersion;
576
+ data[3] = (byte) patchVersion;
577
+ data[4] = (byte) (firmwareSize & 0xFF);
578
+ data[5] = (byte) ((firmwareSize >> 8) & 0xFF);
579
+ data[6] = (byte) ((firmwareSize >> 16) & 0xFF);
580
+ data[7] = (byte) ((firmwareSize >> 24) & 0xFF);
581
+
582
+ byte[] frame = IOBoardProtocolUtils.createFrame((byte) address, IOBoardProtocolUtils.FRAME_TYPE_OTA_REQUEST, data);
583
+
584
+ Log.d(TAG, "Sending OTA notification frame: " + IOBoardProtocolUtils.frameToHex(frame));
585
+
586
+ // Send frame and wait for response
587
+ String response = sendCommandAndWaitResponse(frame, 5000);
588
+
589
+ // Parse response
590
+ IOBoardProtocolUtils.ParsedResponse parsedResponse = IOBoardProtocolUtils.parseResponse(response);
591
+
592
+ if (!parsedResponse.success) {
593
+ return new IOBoardResponse(false, "Failed to parse response: " + parsedResponse.message);
594
+ }
566
595
 
567
- // TODO: Send frame via serial port plugin and receive response
568
596
  return new IOBoardResponse(true, "OTA notification sent successfully");
569
597
 
570
598
  } catch (Exception e) {
@@ -587,22 +615,39 @@ public class IOBoardManager {
587
615
  // Decode base64 data
588
616
  byte[] firmwareData = Base64.decode(dataBase64, Base64.DEFAULT);
589
617
 
590
- // Build OTA data: packet number(4) + data
591
- int[] data = new int[4 + firmwareData.length];
592
- data[0] = packetNumber & 0xFF;
593
- data[1] = (packetNumber >> 8) & 0xFF;
594
- data[2] = (packetNumber >> 16) & 0xFF;
595
- data[3] = (packetNumber >> 24) & 0xFF;
596
-
597
- for (int i = 0; i < firmwareData.length; i++) {
598
- data[4 + i] = firmwareData[i] & 0xFF;
618
+ // TODO: Add OTA data method to IOBoardProtocolUtils
619
+ // For now, use manual frame building based on protocol specs
620
+ byte[] data = new byte[4 + Math.min(firmwareData.length, 128)]; // Packet number(4) + Data content (max 128 bytes)
621
+ data[0] = (byte) (packetNumber & 0xFF);
622
+ data[1] = (byte) ((packetNumber >> 8) & 0xFF);
623
+ data[2] = (byte) ((packetNumber >> 16) & 0xFF);
624
+ data[3] = (byte) ((packetNumber >> 24) & 0xFF);
625
+
626
+ // Copy firmware data (max 128 bytes as per protocol)
627
+ int dataLength = Math.min(firmwareData.length, 128);
628
+ System.arraycopy(firmwareData, 0, data, 4, dataLength);
629
+
630
+ // Pad with zeros if needed
631
+ if (dataLength < 128) {
632
+ for (int i = 4 + dataLength; i < 4 + 128 && i < data.length; i++) {
633
+ data[i] = 0;
634
+ }
599
635
  }
600
636
 
601
- int[] frame = buildFrame(address, FRAME_TYPE_OTA_DATA, data);
637
+ byte[] frame = IOBoardProtocolUtils.createFrame((byte) address, IOBoardProtocolUtils.FRAME_TYPE_OTA_DATA, data);
638
+
639
+ Log.d(TAG, "Sending OTA data frame: " + IOBoardProtocolUtils.frameToHex(frame));
640
+
641
+ // Send frame and wait for response
642
+ String response = sendCommandAndWaitResponse(frame, 5000);
602
643
 
603
- Log.d(TAG, "Sending OTA data frame: " + frameToHex(frame));
644
+ // Parse response
645
+ IOBoardProtocolUtils.ParsedResponse parsedResponse = IOBoardProtocolUtils.parseResponse(response);
646
+
647
+ if (!parsedResponse.success) {
648
+ return new IOBoardResponse(false, "Failed to parse response: " + parsedResponse.message);
649
+ }
604
650
 
605
- // TODO: Send frame via serial port plugin and receive response
606
651
  return new IOBoardResponse(true, "OTA data sent successfully");
607
652
 
608
653
  } catch (Exception e) {
@@ -615,12 +660,15 @@ public class IOBoardManager {
615
660
  * Open serial connection
616
661
  */
617
662
  public IOBoardResponse openConnection(SerialConfig config) {
618
- Log.d(TAG, "Opening serial connection - BaudRate: " + config.baudRate);
663
+ Log.d(TAG, "Opening serial connection - Port: " + config.portPath + ", BaudRate: " + config.baudRate);
619
664
 
620
665
  try {
621
- // TODO: Initialize connection with serial port plugin
622
- // For now, just set the connected flag
666
+ // TODO: Initialize connection with SerialPort plugin
667
+ // Call SerialPort.openConnection() with config parameters
668
+ // Store the returned port ID for future operations
669
+
623
670
  isConnected = true;
671
+ currentSerialPortId = "mock_port_id";
624
672
 
625
673
  return new IOBoardResponse(true, "Serial connection opened successfully");
626
674
 
@@ -637,8 +685,11 @@ public class IOBoardManager {
637
685
  Log.d(TAG, "Closing serial connection");
638
686
 
639
687
  try {
640
- // TODO: Close connection with serial port plugin
688
+ // TODO: Close connection with SerialPort plugin
689
+ // Call SerialPort.closeConnection(currentSerialPortId)
690
+
641
691
  isConnected = false;
692
+ currentSerialPortId = null;
642
693
 
643
694
  return new IOBoardResponse(true, "Serial connection closed successfully");
644
695
 
@@ -0,0 +1,278 @@
1
+ package com.leonardojc.capacitor.ioboard;
2
+
3
+ import java.util.ArrayList;
4
+ import java.util.List;
5
+
6
+ /**
7
+ * IOBOARD Protocol Helper Functions
8
+ * Protocolo actualizado según manual MTC3P08L
9
+ * Frame format: SOI | LEN | ADDR | TYPE | DATA | CRC | EOI
10
+ */
11
+ public class IOBoardProtocolUtils {
12
+
13
+ // Frame constants
14
+ public static final byte SOI = 0x0D; // Start of frame
15
+ public static final byte EOI = 0x0A; // End of frame
16
+
17
+ // Frame types según manual actualizado
18
+ public static final byte FRAME_TYPE_STATUS_QUERY = 0x00; // Status query
19
+ public static final byte FRAME_TYPE_SINGLE_PALLET = 0x01; // Parameter settings (single pallet)
20
+ public static final byte FRAME_TYPE_FULL_PALLET = 0x02; // Parameter settings (full pallet)
21
+ public static final byte FRAME_TYPE_OTA_REQUEST = (byte) 0xF1; // OTA request
22
+ public static final byte FRAME_TYPE_OTA_DATA = (byte) 0xF2; // OTA data
23
+ public static final byte FRAME_TYPE_OTA_RESPONSE = (byte) 0xF3; // OTA response
24
+
25
+ /**
26
+ * CRC-16/Modbus (x16+x15+x2+1) según manual
27
+ */
28
+ public static int calculateCRC16(byte[] data) {
29
+ int crc = 0xFFFF;
30
+ for (byte b : data) {
31
+ crc ^= (b & 0xFF);
32
+ for (int j = 0; j < 8; j++) {
33
+ if ((crc & 0x0001) != 0) {
34
+ crc = (crc >> 1) ^ 0xA001;
35
+ } else {
36
+ crc = crc >> 1;
37
+ }
38
+ }
39
+ }
40
+ return crc & 0xFFFF;
41
+ }
42
+
43
+ /**
44
+ * Formato de frame según manual: SOI | LEN | ADDR | TYPE | DATA | CRC | EOI
45
+ */
46
+ public static byte[] createFrame(byte address, byte frameType, byte[] data) {
47
+ if (data == null) data = new byte[0];
48
+
49
+ int frameLength = 7 + data.length; // SOI(1) + LEN(1) + ADDR(1) + TYPE(1) + DATA(n) + CRC(2) + EOI(1)
50
+ byte[] frame = new byte[frameLength];
51
+ int index = 0;
52
+
53
+ // SOI (Frame Header)
54
+ frame[index++] = SOI;
55
+
56
+ // LEN (Data Length) - longitud completa del frame
57
+ frame[index++] = (byte) frameLength;
58
+
59
+ // ADDR (Target Address)
60
+ frame[index++] = address;
61
+
62
+ // TYPE (Frame Type)
63
+ frame[index++] = frameType;
64
+
65
+ // DATA (Data Content)
66
+ for (byte b : data) {
67
+ frame[index++] = b;
68
+ }
69
+
70
+ // Calculate CRC para todos los datos desde SOI hasta antes del CRC
71
+ byte[] crcData = new byte[index];
72
+ System.arraycopy(frame, 0, crcData, 0, index);
73
+ int crc = calculateCRC16(crcData);
74
+
75
+ // CRC (Check Field) - Low byte first, High byte second según manual
76
+ frame[index++] = (byte) (crc & 0xFF); // Low byte
77
+ frame[index++] = (byte) ((crc >> 8) & 0xFF); // High byte
78
+
79
+ // EOI (Frame End)
80
+ frame[index] = EOI;
81
+
82
+ return frame;
83
+ }
84
+
85
+ /**
86
+ * Convert frame to hexadecimal string for debugging
87
+ */
88
+ public static String frameToHex(byte[] frame) {
89
+ StringBuilder sb = new StringBuilder();
90
+ for (int i = 0; i < frame.length; i++) {
91
+ if (i > 0) sb.append(" ");
92
+ sb.append(String.format("%02X", frame[i] & 0xFF));
93
+ }
94
+ return sb.toString();
95
+ }
96
+
97
+ /**
98
+ * Convert frame to Latin-1 string preserving exact byte values
99
+ */
100
+ public static String frameToLatin1String(byte[] frame) {
101
+ StringBuilder result = new StringBuilder();
102
+ for (byte b : frame) {
103
+ result.append((char) (b & 0xFF));
104
+ }
105
+ return result.toString();
106
+ }
107
+
108
+ /**
109
+ * Create status query frame
110
+ */
111
+ public static byte[] createStatusQuery(byte address) {
112
+ return createFrame(address, FRAME_TYPE_STATUS_QUERY, null);
113
+ }
114
+
115
+ /**
116
+ * Single Pallet Control según manual: NUM | LOCK | LED (8 bytes)
117
+ */
118
+ public static byte[] createSinglePalletControl(byte address, int palletNumber, boolean doorLock,
119
+ int red, int green, int blue, int intensity,
120
+ int blinkTimes, int blinkSpeed) {
121
+ byte[] data = new byte[8];
122
+ data[0] = (byte) palletNumber; // Pallet number (0-7)
123
+ data[1] = (byte) (doorLock ? 1 : 0); // Door lock control (1=unlock, 0=no action)
124
+ data[2] = (byte) (red & 0xFF); // RGB Red (0-255)
125
+ data[3] = (byte) (green & 0xFF); // RGB Green (0-255)
126
+ data[4] = (byte) (blue & 0xFF); // RGB Blue (0-255)
127
+ data[5] = (byte) (intensity & 0xFF); // Intensity (0-100 percentage, >100 = max)
128
+ data[6] = (byte) (blinkTimes & 0xFF); // Blink times (0=no flash, 255=forever, 1-254=times)
129
+ data[7] = (byte) (blinkSpeed & 0xFF); // Blink speed (1-30 seconds interval)
130
+
131
+ return createFrame(address, FRAME_TYPE_SINGLE_PALLET, data);
132
+ }
133
+
134
+ /**
135
+ * Full Pallet Control según manual: LOCK | EXT | LED (8 x 6 bytes = 50 bytes total)
136
+ */
137
+ public static byte[] createFullPalletControl(byte address, int doorLockControl, int extendedControl,
138
+ int[][] ledControls) {
139
+ byte[] data = new byte[50]; // 2 + (8 * 6)
140
+ int index = 0;
141
+
142
+ data[index++] = (byte) (doorLockControl & 0xFF); // Door lock control (8 bits)
143
+ data[index++] = (byte) (extendedControl & 0xFF); // Extended control (8 bits)
144
+
145
+ // Add LED controls (8 LEDs * 6 bytes each)
146
+ for (int i = 0; i < 8; i++) {
147
+ int[] led = (ledControls != null && i < ledControls.length && ledControls[i] != null)
148
+ ? ledControls[i]
149
+ : new int[]{0, 0, 0, 0, 0, 1}; // Default: off
150
+
151
+ data[index++] = (byte) (led[0] & 0xFF); // RGB Red
152
+ data[index++] = (byte) (led[1] & 0xFF); // RGB Green
153
+ data[index++] = (byte) (led[2] & 0xFF); // RGB Blue
154
+ data[index++] = (byte) (led[3] & 0xFF); // Intensity
155
+ data[index++] = (byte) (led[4] & 0xFF); // Blink times
156
+ data[index++] = (byte) (led[5] & 0xFF); // Blink speed
157
+ }
158
+
159
+ return createFrame(address, FRAME_TYPE_FULL_PALLET, data);
160
+ }
161
+
162
+ /**
163
+ * Response parsing result
164
+ */
165
+ public static class ParsedResponse {
166
+ public boolean success;
167
+ public String message;
168
+ public byte address;
169
+ public byte frameType;
170
+ public byte[] data;
171
+
172
+ public ParsedResponse(boolean success, String message) {
173
+ this.success = success;
174
+ this.message = message;
175
+ }
176
+
177
+ public ParsedResponse(boolean success, String message, byte address, byte frameType, byte[] data) {
178
+ this.success = success;
179
+ this.message = message;
180
+ this.address = address;
181
+ this.frameType = frameType;
182
+ this.data = data;
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Parse response frame según formato: SOI | LEN | ADDR | TYPE | DATA | CRC | EOI
188
+ */
189
+ public static ParsedResponse parseResponse(String responseString) {
190
+ if (responseString == null || responseString.length() < 7) {
191
+ return new ParsedResponse(false, "Response too short");
192
+ }
193
+
194
+ byte[] allBytes = responseString.getBytes(java.nio.charset.StandardCharsets.ISO_8859_1);
195
+
196
+ byte soi = allBytes[0];
197
+ if (soi != SOI) {
198
+ return new ParsedResponse(false, "Invalid SOI");
199
+ }
200
+
201
+ int len = allBytes[1] & 0xFF;
202
+ if (len > allBytes.length) {
203
+ return new ParsedResponse(false, "Invalid frame length");
204
+ }
205
+
206
+ // Extraer solo los bytes que corresponden al frame
207
+ byte[] bytes = new byte[len];
208
+ System.arraycopy(allBytes, 0, bytes, 0, len);
209
+
210
+ byte address = bytes[2];
211
+ byte frameType = bytes[3];
212
+ byte eoi = bytes[len - 1];
213
+
214
+ if (eoi != EOI) {
215
+ return new ParsedResponse(false, "Invalid EOI");
216
+ }
217
+
218
+ // Extract data (excluding SOI, LEN, ADDR, TYPE, CRC, CRC, EOI)
219
+ int dataLength = len - 7;
220
+ byte[] data = new byte[dataLength];
221
+ if (dataLength > 0) {
222
+ System.arraycopy(bytes, 4, data, 0, dataLength);
223
+ }
224
+
225
+ // Extract CRC
226
+ int receivedCrcLow = bytes[4 + dataLength] & 0xFF;
227
+ int receivedCrcHigh = bytes[4 + dataLength + 1] & 0xFF;
228
+ int receivedCrc = receivedCrcLow | (receivedCrcHigh << 8);
229
+
230
+ // Verify CRC
231
+ byte[] crcData = new byte[4 + dataLength];
232
+ System.arraycopy(bytes, 0, crcData, 0, 4 + dataLength);
233
+ int calculatedCrc = calculateCRC16(crcData);
234
+
235
+ if (receivedCrc != calculatedCrc) {
236
+ return new ParsedResponse(false, "CRC verification failed");
237
+ }
238
+
239
+ return new ParsedResponse(true, "Response parsed successfully", address, frameType, data);
240
+ }
241
+
242
+ /**
243
+ * Create test frames based on manual examples
244
+ */
245
+ public static byte[] createTestStatusQuery() {
246
+ return createStatusQuery((byte) 1); // Address 01
247
+ }
248
+
249
+ public static byte[] createTestSinglePallet() {
250
+ return createSinglePalletControl((byte) 1, 0, true, 255, 0, 0, 255, 16, 1);
251
+ }
252
+
253
+ /**
254
+ * Validate that our commands match manual examples
255
+ */
256
+ public static String validateExamples() {
257
+ StringBuilder results = new StringBuilder();
258
+
259
+ // Test Status Query
260
+ byte[] statusFrame = createTestStatusQuery();
261
+ String statusHex = frameToHex(statusFrame);
262
+ String expectedStatus = "0D 07 01 00 B2 D9 0A";
263
+ results.append("Status Query - Expected: ").append(expectedStatus)
264
+ .append(", Actual: ").append(statusHex)
265
+ .append(", Match: ").append(statusHex.equals(expectedStatus))
266
+ .append("\n");
267
+
268
+ // Test Single Pallet
269
+ byte[] singleFrame = createTestSinglePallet();
270
+ String singleHex = frameToHex(singleFrame);
271
+ String expectedSingle = "0D 0F 01 01 00 01 FF 00 00 FF 10 01 1D 6F 0A";
272
+ results.append("Single Pallet - Expected: ").append(expectedSingle)
273
+ .append(", Actual: ").append(singleHex)
274
+ .append(", Match: ").append(singleHex.equals(expectedSingle));
275
+
276
+ return results.toString();
277
+ }
278
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@leonardojc/capacitor-ioboard",
3
- "version": "1.1.0",
4
- "description": "A Capacitor plugin for controlling custom IOBOARD devices via RS485 serial communication with simplified native API",
3
+ "version": "1.2.0",
4
+ "description": "A Capacitor plugin for controlling custom IOBOARD devices via RS485 serial communication with full native protocol implementation",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",
7
7
  "types": "dist/esm/index.d.ts",