|
1 | 1 | import ConnectionMgr from "@/managers/connectionmgr"; |
2 | 2 | import logger from "@/utils/logger"; |
3 | 3 | import Joystick from '@/managers/joystickmgr'; |
| 4 | +import TableMgr from '@/managers/tablemgr'; |
4 | 5 |
|
5 | 6 |
|
6 | 7 | /** |
@@ -50,6 +51,11 @@ abstract class Connection { |
50 | 51 |
|
51 | 52 | readonly XRP_SEND_BLOCK_SIZE: number = 250; // wired can handle 255 bytes, but BLE 5.0 is only 250 |
52 | 53 |
|
| 54 | + // XPP Protocol constants for packet detection |
| 55 | + protected readonly XPP_START_SEQ: number[] = [0xAA, 0x55]; |
| 56 | + protected readonly XPP_END_SEQ: number[] = [0x55, 0xAA]; |
| 57 | + protected xppBuffer: Uint8Array = new Uint8Array(0); // Buffer for incomplete XPP packets |
| 58 | + |
53 | 59 | joyStick: Joystick | undefined; |
54 | 60 |
|
55 | 61 | constructor() { |
@@ -107,6 +113,162 @@ abstract class Connection { |
107 | 113 | return result; |
108 | 114 | } |
109 | 115 |
|
| 116 | + /** |
| 117 | + * Extracts complete XPP packets from the buffer and returns them. |
| 118 | + * XPP packet format: [0xAA 0x55] [Type] [Length] [Payload] [0x55 0xAA] |
| 119 | + * @param data - New data to add to the buffer |
| 120 | + * @returns Array of complete XPP packets |
| 121 | + * |
| 122 | + * regularData anything that is not part of a XPP packet |
| 123 | + * packets are the complete XPP packets |
| 124 | + * anything before start sequence is regular data |
| 125 | + * anything after an end with no other start sequence is regular data |
| 126 | + * |
| 127 | + */ |
| 128 | + protected extractCompleteXPPPackets(data: Uint8Array): { packets: Uint8Array[], regularData: Uint8Array } { |
| 129 | + // Add new data to buffer |
| 130 | + this.xppBuffer = this.concatUint8Arrays(this.xppBuffer, data); |
| 131 | + let regularData: Uint8Array = new Uint8Array(0); |
| 132 | + |
| 133 | + const completePackets: Uint8Array[] = []; |
| 134 | + |
| 135 | + // Process buffer to extract packets and regular data |
| 136 | + while (this.xppBuffer.length > 0) { |
| 137 | + // If buffer is too small to be a complete packet, check if we can pass through regular data |
| 138 | + if (this.xppBuffer.length < 4) { |
| 139 | + // Check if buffer could potentially start an XPP packet |
| 140 | + if (this.xppBuffer.length === 1) { |
| 141 | + // Single byte: if it's 0xAA, might be start of XPP, keep it |
| 142 | + // Otherwise, it's definitely regular data |
| 143 | + if (this.xppBuffer[0] !== this.XPP_START_SEQ[0]) { |
| 144 | + regularData = this.concatUint8Arrays(regularData, this.xppBuffer); |
| 145 | + this.xppBuffer = new Uint8Array(0); |
| 146 | + } |
| 147 | + break; // Need more data to determine |
| 148 | + } else if (this.xppBuffer.length === 2) { |
| 149 | + // Two bytes: check if it's [0xAA, 0x55] |
| 150 | + if (this.xppBuffer[0] === this.XPP_START_SEQ[0] && |
| 151 | + this.xppBuffer[1] === this.XPP_START_SEQ[1]) { |
| 152 | + // Could be start of XPP packet, keep it |
| 153 | + break; // Need more data |
| 154 | + } else if (this.xppBuffer[0] === this.XPP_START_SEQ[0]) { |
| 155 | + // First byte is 0xAA but second isn't 0x55, pass first byte as regular data |
| 156 | + regularData = this.concatUint8Arrays(regularData, this.xppBuffer.subarray(0, 1)); |
| 157 | + this.xppBuffer = this.xppBuffer.subarray(1); |
| 158 | + // Continue processing the remaining byte |
| 159 | + } else { |
| 160 | + // First byte is not 0xAA, both bytes are regular data |
| 161 | + regularData = this.concatUint8Arrays(regularData, this.xppBuffer); |
| 162 | + this.xppBuffer = new Uint8Array(0); |
| 163 | + break; |
| 164 | + } |
| 165 | + } else if (this.xppBuffer.length === 3) { |
| 166 | + // Three bytes: check if it starts with [0xAA, 0x55] |
| 167 | + if (this.xppBuffer[0] === this.XPP_START_SEQ[0] && |
| 168 | + this.xppBuffer[1] === this.XPP_START_SEQ[1]) { |
| 169 | + // Could be start of XPP packet, keep it |
| 170 | + break; // Need more data |
| 171 | + } else if (this.xppBuffer[0] === this.XPP_START_SEQ[0]) { |
| 172 | + // First byte is 0xAA but second isn't 0x55, pass first byte as regular data |
| 173 | + regularData = this.concatUint8Arrays(regularData, this.xppBuffer.subarray(0, 1)); |
| 174 | + this.xppBuffer = this.xppBuffer.subarray(1); |
| 175 | + // Continue processing the remaining 2 bytes |
| 176 | + } else { |
| 177 | + // First byte is not 0xAA, check if second byte is 0xAA (could be start of XPP) |
| 178 | + if (this.xppBuffer[1] === this.XPP_START_SEQ[0]) { |
| 179 | + // Second byte might be start, pass first byte as regular data |
| 180 | + regularData = this.concatUint8Arrays(regularData, this.xppBuffer.subarray(0, 1)); |
| 181 | + this.xppBuffer = this.xppBuffer.subarray(1); |
| 182 | + // Continue processing the remaining 2 bytes |
| 183 | + } else { |
| 184 | + // No potential XPP start, pass all as regular data |
| 185 | + regularData = this.concatUint8Arrays(regularData, this.xppBuffer); |
| 186 | + this.xppBuffer = new Uint8Array(0); |
| 187 | + break; |
| 188 | + } |
| 189 | + } |
| 190 | + } |
| 191 | + continue; // Re-check buffer after modifications |
| 192 | + } |
| 193 | + |
| 194 | + // Buffer has at least 4 bytes, look for start sequence [0xAA 0x55] |
| 195 | + const startIndex = this.xppBuffer.findIndex((val, idx) => |
| 196 | + idx < this.xppBuffer.length - 1 && |
| 197 | + val === this.XPP_START_SEQ[0] && |
| 198 | + this.xppBuffer[idx + 1] === this.XPP_START_SEQ[1] |
| 199 | + ); |
| 200 | + |
| 201 | + if (startIndex === -1) { |
| 202 | + // No start sequence found |
| 203 | + // Check if last byte is 0xAA (might be start of next XPP packet) |
| 204 | + const lastByte = this.xppBuffer[this.xppBuffer.length - 1]; |
| 205 | + if (lastByte === this.XPP_START_SEQ[0]) { |
| 206 | + // Keep last byte in buffer, pass rest as regular data |
| 207 | + regularData = this.concatUint8Arrays(regularData, this.xppBuffer.subarray(0, this.xppBuffer.length - 1)); |
| 208 | + this.xppBuffer = this.xppBuffer.subarray(this.xppBuffer.length - 1); |
| 209 | + } else { |
| 210 | + // No potential XPP start, clear buffer |
| 211 | + regularData = this.concatUint8Arrays(regularData, this.xppBuffer); |
| 212 | + this.xppBuffer = new Uint8Array(0); |
| 213 | + } |
| 214 | + break; |
| 215 | + } |
| 216 | + |
| 217 | + // Remove any data before the start sequence |
| 218 | + if (startIndex > 0) { |
| 219 | + regularData = this.concatUint8Arrays(regularData, this.xppBuffer.subarray(0, startIndex)); |
| 220 | + this.xppBuffer = this.xppBuffer.subarray(startIndex); |
| 221 | + } |
| 222 | + |
| 223 | + // Check if we have enough data for type and length (at least 4 bytes: start + type + length) |
| 224 | + if (this.xppBuffer.length < 4) { |
| 225 | + break; // Need more data |
| 226 | + } |
| 227 | + |
| 228 | + // Extract length (byte 3 after start sequence) |
| 229 | + // Type is at byte 2, but we don't need it for packet extraction |
| 230 | + const payloadLength = this.xppBuffer[3]; |
| 231 | + |
| 232 | + // Calculate total packet size: start(2) + type(1) + length(1) + payload + end(2) |
| 233 | + const totalPacketSize = 2 + 1 + 1 + payloadLength + 2; |
| 234 | + |
| 235 | + // Check if we have the complete packet |
| 236 | + if (this.xppBuffer.length < totalPacketSize) { |
| 237 | + break; // Need more data |
| 238 | + } |
| 239 | + |
| 240 | + // Verify end sequence |
| 241 | + const endIndex = totalPacketSize - 2; |
| 242 | + if (this.xppBuffer[endIndex] === this.XPP_END_SEQ[0] && |
| 243 | + this.xppBuffer[endIndex + 1] === this.XPP_END_SEQ[1]) { |
| 244 | + // Complete packet found |
| 245 | + const completePacket = this.xppBuffer.subarray(0, totalPacketSize); |
| 246 | + completePackets.push(completePacket); |
| 247 | + |
| 248 | + // Remove processed packet from buffer |
| 249 | + this.xppBuffer = this.xppBuffer.subarray(totalPacketSize); |
| 250 | + } else { |
| 251 | + // Invalid end sequence, skip this start sequence and continue |
| 252 | + regularData = this.concatUint8Arrays(regularData, this.xppBuffer.subarray(0, 2)); |
| 253 | + this.xppBuffer = this.xppBuffer.subarray(2); |
| 254 | + } |
| 255 | + } |
| 256 | + |
| 257 | + return { packets: completePackets, regularData }; |
| 258 | + } |
| 259 | + |
| 260 | + /** |
| 261 | + * Processes a complete XPP packet by routing it to the appropriate handlers. |
| 262 | + * @param packet - Complete XPP packet |
| 263 | + * @param tableMgr - Optional TableMgr instance for handling table-related XPP messages |
| 264 | + */ |
| 265 | + protected processXPPPacket(packet: Uint8Array, tableMgr?: TableMgr): void { |
| 266 | + this.joyStick?.handleXPPMessage(packet); |
| 267 | + tableMgr?.readFromDevice(packet); |
| 268 | + } |
| 269 | + |
| 270 | + |
| 271 | + |
110 | 272 | /** |
111 | 273 | * readData - read data from the XRP connection |
112 | 274 | * @param values |
|
0 commit comments