|
| 1 | +// Import ECB library |
| 2 | +const ECB=require('../../lib/js') |
| 3 | +const fsP = require('fs/promises') |
| 4 | + |
| 5 | +// Queued blocks parsed from the s19 file |
| 6 | +const pendingBlocks = []; |
| 7 | +// Global variables to track current block and max chunk size |
| 8 | +var currentBlock = void 0; |
| 9 | +var maxChunkSize; |
| 10 | +var testerName; |
| 11 | +var dataFormatIdentifierCache; |
| 12 | +var addressAndLengthFormatIdentifierCache; |
| 13 | +var jobInstance; |
| 14 | + |
| 15 | +// Initialize utility functions |
| 16 | +Util.Init(() => { |
| 17 | + testerName = Util.getTesterName(); |
| 18 | + |
| 19 | + // Build RequestDownload (0x34) for the current block |
| 20 | + const buildRequestDownload = () => { |
| 21 | + if (!currentBlock) { |
| 22 | + throw new Error("currentBlock is undefined"); |
| 23 | + } |
| 24 | + |
| 25 | + const r34 = new ECB.DiagRequest(testerName, { |
| 26 | + id: "", |
| 27 | + name: "", |
| 28 | + serviceId: "0x34", |
| 29 | + params: [], |
| 30 | + respParams: [] |
| 31 | + }); |
| 32 | + |
| 33 | + const prefixBuffer = Buffer.from([0x34, dataFormatIdentifierCache & 0xff, addressAndLengthFormatIdentifierCache & 0xff]); |
| 34 | + |
| 35 | + // Create buffer for memory address based on format identifier |
| 36 | + const memoryAddressBuffer = Buffer.alloc(addressAndLengthFormatIdentifierCache & 0x0f); |
| 37 | + for (let i = 0; i < memoryAddressBuffer.length; i++) { |
| 38 | + memoryAddressBuffer[i] = currentBlock.addr >> (8 * (memoryAddressBuffer.length - 1 - i)) & 0xff; |
| 39 | + } |
| 40 | + |
| 41 | + // Create buffer for memory size based on format identifier |
| 42 | + const memorySizeBuffer = Buffer.alloc((addressAndLengthFormatIdentifierCache & 0xf0) >> 4); |
| 43 | + for (let i = 0; i < memorySizeBuffer.length; i++) { |
| 44 | + memorySizeBuffer[i] = currentBlock.data.length >> (8 * (memorySizeBuffer.length - 1 - i)) & 0xff; |
| 45 | + } |
| 46 | + |
| 47 | + // Set raw request data by concatenating buffers |
| 48 | + r34.diagSetRaw(Buffer.concat([prefixBuffer, memoryAddressBuffer, memorySizeBuffer])); |
| 49 | + |
| 50 | + // Handle response from ECU |
| 51 | + r34.On("recv", (resp) => { |
| 52 | + const data = resp.diagGetRaw(); |
| 53 | + const lengthFormatIdentifier = (data[1] & 0xf0) >> 4; |
| 54 | + |
| 55 | + let maxNumberOfBlockLength = 0; |
| 56 | + // Calculate max block length from response |
| 57 | + for (let i = 0; i < lengthFormatIdentifier; i++) { |
| 58 | + maxNumberOfBlockLength += data[2 + i] * Math.pow(256, lengthFormatIdentifier - i - 1); |
| 59 | + } |
| 60 | + maxChunkSize = maxNumberOfBlockLength; |
| 61 | + }); |
| 62 | + |
| 63 | + return r34; |
| 64 | + }; |
| 65 | + |
| 66 | + // Register main RequestDownloadS19 function |
| 67 | + Util.Register(`${testerName}.RequestDownloadS19`, async function(dataFormatIdentifier, addressAndLengthFormatIdentifier, s19FilePath) { |
| 68 | + // reset state for each call |
| 69 | + pendingBlocks.length = 0; |
| 70 | + currentBlock = void 0; |
| 71 | + maxChunkSize = void 0; |
| 72 | + dataFormatIdentifierCache = dataFormatIdentifier; |
| 73 | + addressAndLengthFormatIdentifierCache = addressAndLengthFormatIdentifier; |
| 74 | + |
| 75 | + // parse s19 file into discrete memory blocks |
| 76 | + const s19Text = await fsP.readFile(s19FilePath, 'utf8'); |
| 77 | + const memMap = ECB.S19MemoryMap.fromS19(s19Text); |
| 78 | + for (const [addr, data] of memMap) { |
| 79 | + pendingBlocks.push({ addr, data }); |
| 80 | + } |
| 81 | + |
| 82 | + if (pendingBlocks.length === 0) { |
| 83 | + throw new Error("s19 file contains no data"); |
| 84 | + } |
| 85 | + |
| 86 | + // prepare first block |
| 87 | + currentBlock = pendingBlocks.shift(); |
| 88 | + |
| 89 | + const r34 = buildRequestDownload(); |
| 90 | + |
| 91 | + // Create temporary job for handling data transfer |
| 92 | + const job = new ECB.DiagJob(testerName, { |
| 93 | + id: "RequestDownloadS19_tmpJob", |
| 94 | + name: "RequestDownloadS19_tmpJob", |
| 95 | + serviceId: "Job", |
| 96 | + params: [], |
| 97 | + respParams: [] |
| 98 | + }); |
| 99 | + jobInstance = job; |
| 100 | + |
| 101 | + return [r34, job]; |
| 102 | + }); |
| 103 | + |
| 104 | + // Register temporary job handler for data transfer |
| 105 | + Util.Register(`${testerName}.RequestDownloadS19_tmpJob`, () => { |
| 106 | + // Validate chunk size |
| 107 | + if (maxChunkSize == void 0 || maxChunkSize <= 2) { |
| 108 | + throw new Error("maxNumberOfBlockLength is undefined or too small"); |
| 109 | + } |
| 110 | + |
| 111 | + if (currentBlock) { |
| 112 | + // Adjust chunk size for overhead |
| 113 | + let chunkSize = maxChunkSize - 2; |
| 114 | + if (chunkSize & 7) { |
| 115 | + chunkSize -= chunkSize & 7; |
| 116 | + } |
| 117 | + |
| 118 | + // Split data into chunks |
| 119 | + const numChunks = Math.ceil(currentBlock.data.length / chunkSize); |
| 120 | + const list = []; |
| 121 | + |
| 122 | + // Create transfer data requests (0x36) for each chunk |
| 123 | + for (let i = 0; i < numChunks; i++) { |
| 124 | + const start = i * chunkSize; |
| 125 | + const end = Math.min(start + chunkSize, currentBlock.data.length); |
| 126 | + const chunk = currentBlock.data.subarray(start, end); |
| 127 | + |
| 128 | + const r36 = new ECB.DiagRequest(testerName, { |
| 129 | + id: "", |
| 130 | + name: "", |
| 131 | + serviceId: "0x36", |
| 132 | + params: [], |
| 133 | + respParams: [] |
| 134 | + }); |
| 135 | + |
| 136 | + // Add block sequence counter |
| 137 | + const blockSequenceCounter = Buffer.alloc(1); |
| 138 | + blockSequenceCounter.writeUInt8((i + 1) & 0xff); |
| 139 | + r36.diagSetRaw(Buffer.concat([Buffer.from([0x36]), blockSequenceCounter, chunk])); |
| 140 | + list.push(r36); |
| 141 | + } |
| 142 | + |
| 143 | + // Create transfer exit request (0x37) |
| 144 | + const r37 = new ECB.DiagRequest(testerName, { |
| 145 | + id: "", |
| 146 | + name: "", |
| 147 | + serviceId: "0x37", |
| 148 | + params: [], |
| 149 | + respParams: [] |
| 150 | + }); |
| 151 | + r37.diagSetRaw(Buffer.from([0x37])); |
| 152 | + list.push(r37); |
| 153 | + |
| 154 | + // prepare next block if any |
| 155 | + currentBlock = void 0; |
| 156 | + maxChunkSize = void 0; |
| 157 | + if (pendingBlocks.length > 0) { |
| 158 | + currentBlock = pendingBlocks.shift(); |
| 159 | + list.push(buildRequestDownload(), jobInstance); |
| 160 | + } |
| 161 | + |
| 162 | + return list; |
| 163 | + } else { |
| 164 | + return []; |
| 165 | + } |
| 166 | + }); |
| 167 | +}); |
| 168 | + |
| 169 | +/*! For license information please see uds.js.LICENSE.txt */ |
| 170 | +//# sourceMappingURL=tester.js.map |
| 171 | + |
0 commit comments