@hangtime/grip-connect 0.5.7 → 0.5.8
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 +9 -6
- package/dist/models/device/entralpi.model.js +2 -4
- package/dist/models/device/forceboard.model.js +34 -31
- package/dist/models/device/progressor.model.js +22 -13
- package/dist/models/device.model.js +1 -4
- package/package.json +1 -1
- package/src/models/device/entralpi.model.ts +2 -4
- package/src/models/device/forceboard.model.ts +34 -32
- package/src/models/device/progressor.model.ts +22 -13
- package/src/models/device.model.ts +1 -4
- package/dist/helpers/struct.d.ts +0 -9
- package/dist/helpers/struct.js +0 -203
- package/src/helpers/struct.ts +0 -239
package/README.md
CHANGED
|
@@ -63,12 +63,14 @@ motherboard.notify((data) => {
|
|
|
63
63
|
|
|
64
64
|
// Optional: Check if the device is active
|
|
65
65
|
motherboard.active(
|
|
66
|
-
(isActive) => {
|
|
66
|
+
(isActive) => {
|
|
67
|
+
console.log(isActive)
|
|
68
|
+
},
|
|
67
69
|
// Optionally using a weight threshold and duration
|
|
68
70
|
{ threshold: 2.5, duration: 1000 },
|
|
69
71
|
)
|
|
70
72
|
|
|
71
|
-
document.querySelector("#motherboard").addEventListener("click", () => {
|
|
73
|
+
document.querySelector("#motherboard").addEventListener("click", async () => {
|
|
72
74
|
// Connect to device
|
|
73
75
|
await motherboard.connect(
|
|
74
76
|
async () => {
|
|
@@ -80,8 +82,8 @@ document.querySelector("#motherboard").addEventListener("click", () => {
|
|
|
80
82
|
// await motherboard.led("red")
|
|
81
83
|
// await motherboard.led()
|
|
82
84
|
|
|
83
|
-
// Start weight streaming (for
|
|
84
|
-
await motherboard.stream(
|
|
85
|
+
// Start weight streaming (for 30s) remove parameter for a continues stream
|
|
86
|
+
await motherboard.stream(30000)
|
|
85
87
|
|
|
86
88
|
// Manualy tare the device when the stream is running
|
|
87
89
|
// await motherboard.tare(5000)
|
|
@@ -91,14 +93,15 @@ document.querySelector("#motherboard").addEventListener("click", () => {
|
|
|
91
93
|
|
|
92
94
|
// Download data as CSV, JSON, or XML (default: CSV) format => timestamp, frame, battery, samples, masses
|
|
93
95
|
// motherboard.download('json')
|
|
96
|
+
|
|
97
|
+
// Optionally disconnect from device after we are done
|
|
98
|
+
motherboard.disconnect(Motherboard)
|
|
94
99
|
},
|
|
95
100
|
(error) => {
|
|
96
101
|
// Optinal custom error handeling
|
|
97
102
|
console.error(error.message)
|
|
98
103
|
},
|
|
99
104
|
)
|
|
100
|
-
// Disconnect from device after we are done
|
|
101
|
-
motherboard.disconnect(Motherboard)
|
|
102
105
|
})
|
|
103
106
|
```
|
|
104
107
|
|
|
@@ -154,14 +154,12 @@ export class Entralpi extends Device {
|
|
|
154
154
|
const value = characteristic.value;
|
|
155
155
|
if (value) {
|
|
156
156
|
if (value.buffer) {
|
|
157
|
-
const buffer = value.buffer;
|
|
158
|
-
const rawData = new DataView(buffer);
|
|
159
157
|
const receivedTime = Date.now();
|
|
160
|
-
const receivedData = (
|
|
158
|
+
const receivedData = (value.getUint16(0) / 100).toFixed(1);
|
|
161
159
|
const convertedData = Number(receivedData);
|
|
162
160
|
// Adjust weight by using the tare value
|
|
163
161
|
// If tare is 0, use the original weight, otherwise subtract tare and invert.
|
|
164
|
-
// This will display the
|
|
162
|
+
// This will display the removed or 'no-hanging' weight.
|
|
165
163
|
const tare = this.applyTare(convertedData);
|
|
166
164
|
const numericData = tare === 0 ? convertedData : (convertedData - tare) * -1;
|
|
167
165
|
// Add data to downloadable Array
|
|
@@ -183,38 +183,41 @@ export class ForceBoard extends Device {
|
|
|
183
183
|
const value = characteristic.value;
|
|
184
184
|
if (value) {
|
|
185
185
|
if (value.buffer) {
|
|
186
|
-
const buffer = value.buffer;
|
|
187
|
-
const rawData = new DataView(buffer);
|
|
188
186
|
const receivedTime = Date.now();
|
|
189
|
-
const
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
187
|
+
const dataArray = new Uint8Array(value.buffer);
|
|
188
|
+
// Skip the first 2 bytes, which are the command and length
|
|
189
|
+
// The data is sent in groups of 3 bytes
|
|
190
|
+
for (let i = 2; i < dataArray.length; i += 3) {
|
|
191
|
+
const receivedData = (dataArray[i] << 16) | (dataArray[i + 1] << 8) | dataArray[i + 2];
|
|
192
|
+
// Convert from LBS to KG
|
|
193
|
+
const convertedReceivedData = receivedData * 0.453592;
|
|
194
|
+
// Tare correction
|
|
195
|
+
const numericData = convertedReceivedData - this.applyTare(convertedReceivedData);
|
|
196
|
+
// Add data to downloadable Array
|
|
197
|
+
this.downloadPackets.push({
|
|
198
|
+
received: receivedTime,
|
|
199
|
+
sampleNum: this.dataPointCount,
|
|
200
|
+
battRaw: 0,
|
|
201
|
+
samples: [convertedReceivedData],
|
|
202
|
+
masses: [numericData],
|
|
203
|
+
});
|
|
204
|
+
// Update massMax
|
|
205
|
+
this.massMax = Math.max(Number(this.massMax), numericData).toFixed(1);
|
|
206
|
+
// Update running sum and count
|
|
207
|
+
const currentMassTotal = Math.max(-1000, numericData);
|
|
208
|
+
this.massTotalSum += currentMassTotal;
|
|
209
|
+
this.dataPointCount++;
|
|
210
|
+
// Calculate the average dynamically
|
|
211
|
+
this.massAverage = (this.massTotalSum / this.dataPointCount).toFixed(1);
|
|
212
|
+
// Check if device is being used
|
|
213
|
+
this.activityCheck(numericData);
|
|
214
|
+
// Notify with weight data
|
|
215
|
+
this.notifyCallback({
|
|
216
|
+
massMax: this.massMax,
|
|
217
|
+
massAverage: this.massAverage,
|
|
218
|
+
massTotal: Math.max(-1000, numericData).toFixed(1),
|
|
219
|
+
});
|
|
220
|
+
}
|
|
218
221
|
}
|
|
219
222
|
}
|
|
220
223
|
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Device } from "../device.model";
|
|
2
|
-
import struct from "../../helpers/struct";
|
|
3
2
|
/**
|
|
4
3
|
* Represents the possible responses of a Tindeq Progressor device.
|
|
5
4
|
*/
|
|
@@ -119,15 +118,25 @@ export class Progressor extends Device {
|
|
|
119
118
|
const value = characteristic.value;
|
|
120
119
|
if (value) {
|
|
121
120
|
if (value.buffer) {
|
|
122
|
-
const buffer = value.buffer;
|
|
123
|
-
const rawData = new DataView(buffer);
|
|
124
121
|
const receivedTime = Date.now();
|
|
125
|
-
|
|
122
|
+
// Read the first byte of the buffer to determine the kind of message
|
|
123
|
+
const kind = value.getInt8(0);
|
|
124
|
+
// Check if the message is a weight measurement
|
|
126
125
|
if (kind === ProgressorResponses.WEIGHT_MEASURE) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
126
|
+
// Start parsing data from the 3rd byte (index 2)
|
|
127
|
+
let offset = 2;
|
|
128
|
+
// Continue parsing while there's data left in the buffer
|
|
129
|
+
while (offset < value.byteLength) {
|
|
130
|
+
// Read a 32-bit float (4 bytes) for the weight, using little-endian
|
|
131
|
+
const weight = value.getFloat32(offset, true);
|
|
132
|
+
// Move the offset by 4 bytes
|
|
133
|
+
offset += 4;
|
|
134
|
+
// Read a 32-bit integer (4 bytes) for the seconds, using little-endian
|
|
135
|
+
const seconds = value.getInt32(offset, true);
|
|
136
|
+
// Move the offset by 4 bytes
|
|
137
|
+
offset += 4;
|
|
138
|
+
// Check if both weight and seconds are valid numbers
|
|
139
|
+
if (!isNaN(weight) && !isNaN(seconds)) {
|
|
131
140
|
// Tare correction
|
|
132
141
|
const numericData = weight - this.applyTare(weight);
|
|
133
142
|
// Add data to downloadable Array
|
|
@@ -159,17 +168,17 @@ export class Progressor extends Device {
|
|
|
159
168
|
else if (kind === ProgressorResponses.COMMAND_RESPONSE) {
|
|
160
169
|
if (!this.writeLast)
|
|
161
170
|
return;
|
|
162
|
-
let
|
|
171
|
+
let output = "";
|
|
163
172
|
if (this.writeLast === this.commands.GET_BATT_VLTG) {
|
|
164
|
-
|
|
173
|
+
output = new DataView(value.buffer, 2).getUint32(0, true).toString();
|
|
165
174
|
}
|
|
166
175
|
else if (this.writeLast === this.commands.GET_FW_VERSION) {
|
|
167
|
-
|
|
176
|
+
output = new TextDecoder().decode(new Uint8Array(value.buffer).slice(2));
|
|
168
177
|
}
|
|
169
178
|
else if (this.writeLast === this.commands.GET_ERR_INFO) {
|
|
170
|
-
|
|
179
|
+
output = new TextDecoder().decode(new Uint8Array(value.buffer.slice(2)));
|
|
171
180
|
}
|
|
172
|
-
this.writeCallback(
|
|
181
|
+
this.writeCallback(output);
|
|
173
182
|
}
|
|
174
183
|
else if (kind === ProgressorResponses.LOW_BATTERY_WARNING) {
|
|
175
184
|
console.warn("⚠️ Low power detected. Please consider connecting to a power source.");
|
|
@@ -376,10 +376,7 @@ export class Device extends BaseModel {
|
|
|
376
376
|
const value = characteristic.value;
|
|
377
377
|
if (value) {
|
|
378
378
|
if (value.buffer) {
|
|
379
|
-
|
|
380
|
-
console.log(new Uint8Array(buffer));
|
|
381
|
-
const rawData = new DataView(buffer);
|
|
382
|
-
console.log(rawData);
|
|
379
|
+
console.log(value);
|
|
383
380
|
}
|
|
384
381
|
else {
|
|
385
382
|
console.log(value);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hangtime/grip-connect",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.8",
|
|
4
4
|
"description": "Griptonite Motherboard, Tindeq Progressor, PitchSix Force Board, WHC-06, Entralpi, Climbro, mySmartBoard: Web Bluetooth API Force-Sensing strength analysis for climbers",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -161,15 +161,13 @@ export class Entralpi extends Device implements IEntralpi {
|
|
|
161
161
|
|
|
162
162
|
if (value) {
|
|
163
163
|
if (value.buffer) {
|
|
164
|
-
const buffer: ArrayBuffer = value.buffer
|
|
165
|
-
const rawData: DataView = new DataView(buffer)
|
|
166
164
|
const receivedTime: number = Date.now()
|
|
167
|
-
const receivedData: string = (
|
|
165
|
+
const receivedData: string = (value.getUint16(0) / 100).toFixed(1)
|
|
168
166
|
|
|
169
167
|
const convertedData = Number(receivedData)
|
|
170
168
|
// Adjust weight by using the tare value
|
|
171
169
|
// If tare is 0, use the original weight, otherwise subtract tare and invert.
|
|
172
|
-
// This will display the
|
|
170
|
+
// This will display the removed or 'no-hanging' weight.
|
|
173
171
|
const tare = this.applyTare(convertedData)
|
|
174
172
|
const numericData = tare === 0 ? convertedData : (convertedData - tare) * -1
|
|
175
173
|
// Add data to downloadable Array
|
|
@@ -187,44 +187,46 @@ export class ForceBoard extends Device implements IForceBoard {
|
|
|
187
187
|
const value: DataView | undefined = characteristic.value
|
|
188
188
|
if (value) {
|
|
189
189
|
if (value.buffer) {
|
|
190
|
-
const buffer: ArrayBuffer = value.buffer
|
|
191
|
-
const rawData: DataView = new DataView(buffer)
|
|
192
190
|
const receivedTime: number = Date.now()
|
|
191
|
+
const dataArray = new Uint8Array(value.buffer)
|
|
192
|
+
// Skip the first 2 bytes, which are the command and length
|
|
193
|
+
// The data is sent in groups of 3 bytes
|
|
194
|
+
for (let i = 2; i < dataArray.length; i += 3) {
|
|
195
|
+
const receivedData = (dataArray[i] << 16) | (dataArray[i + 1] << 8) | dataArray[i + 2]
|
|
196
|
+
// Convert from LBS to KG
|
|
197
|
+
const convertedReceivedData = receivedData * 0.453592
|
|
198
|
+
// Tare correction
|
|
199
|
+
const numericData = convertedReceivedData - this.applyTare(convertedReceivedData)
|
|
200
|
+
// Add data to downloadable Array
|
|
201
|
+
this.downloadPackets.push({
|
|
202
|
+
received: receivedTime,
|
|
203
|
+
sampleNum: this.dataPointCount,
|
|
204
|
+
battRaw: 0,
|
|
205
|
+
samples: [convertedReceivedData],
|
|
206
|
+
masses: [numericData],
|
|
207
|
+
})
|
|
193
208
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const convertedReceivedData = receivedData * 0.453592
|
|
197
|
-
// Tare correction
|
|
198
|
-
const numericData = convertedReceivedData - this.applyTare(convertedReceivedData)
|
|
199
|
-
// Add data to downloadable Array
|
|
200
|
-
this.downloadPackets.push({
|
|
201
|
-
received: receivedTime,
|
|
202
|
-
sampleNum: this.dataPointCount,
|
|
203
|
-
battRaw: 0,
|
|
204
|
-
samples: [convertedReceivedData],
|
|
205
|
-
masses: [numericData],
|
|
206
|
-
})
|
|
209
|
+
// Update massMax
|
|
210
|
+
this.massMax = Math.max(Number(this.massMax), numericData).toFixed(1)
|
|
207
211
|
|
|
208
|
-
|
|
209
|
-
|
|
212
|
+
// Update running sum and count
|
|
213
|
+
const currentMassTotal = Math.max(-1000, numericData)
|
|
214
|
+
this.massTotalSum += currentMassTotal
|
|
215
|
+
this.dataPointCount++
|
|
210
216
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
this.massTotalSum += currentMassTotal
|
|
214
|
-
this.dataPointCount++
|
|
217
|
+
// Calculate the average dynamically
|
|
218
|
+
this.massAverage = (this.massTotalSum / this.dataPointCount).toFixed(1)
|
|
215
219
|
|
|
216
|
-
|
|
217
|
-
|
|
220
|
+
// Check if device is being used
|
|
221
|
+
this.activityCheck(numericData)
|
|
218
222
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
massTotal: Math.max(-1000, numericData).toFixed(1),
|
|
227
|
-
})
|
|
223
|
+
// Notify with weight data
|
|
224
|
+
this.notifyCallback({
|
|
225
|
+
massMax: this.massMax,
|
|
226
|
+
massAverage: this.massAverage,
|
|
227
|
+
massTotal: Math.max(-1000, numericData).toFixed(1),
|
|
228
|
+
})
|
|
229
|
+
}
|
|
228
230
|
}
|
|
229
231
|
}
|
|
230
232
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Device } from "../device.model"
|
|
2
2
|
import type { IProgressor } from "../../interfaces/device/progressor.interface"
|
|
3
|
-
import struct from "../../helpers/struct"
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* Represents the possible responses of a Tindeq Progressor device.
|
|
@@ -129,15 +128,25 @@ export class Progressor extends Device implements IProgressor {
|
|
|
129
128
|
|
|
130
129
|
if (value) {
|
|
131
130
|
if (value.buffer) {
|
|
132
|
-
const buffer: ArrayBuffer = value.buffer
|
|
133
|
-
const rawData: DataView = new DataView(buffer)
|
|
134
131
|
const receivedTime: number = Date.now()
|
|
135
|
-
|
|
132
|
+
// Read the first byte of the buffer to determine the kind of message
|
|
133
|
+
const kind = value.getInt8(0)
|
|
134
|
+
// Check if the message is a weight measurement
|
|
136
135
|
if (kind === ProgressorResponses.WEIGHT_MEASURE) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
136
|
+
// Start parsing data from the 3rd byte (index 2)
|
|
137
|
+
let offset = 2
|
|
138
|
+
// Continue parsing while there's data left in the buffer
|
|
139
|
+
while (offset < value.byteLength) {
|
|
140
|
+
// Read a 32-bit float (4 bytes) for the weight, using little-endian
|
|
141
|
+
const weight = value.getFloat32(offset, true)
|
|
142
|
+
// Move the offset by 4 bytes
|
|
143
|
+
offset += 4
|
|
144
|
+
// Read a 32-bit integer (4 bytes) for the seconds, using little-endian
|
|
145
|
+
const seconds = value.getInt32(offset, true)
|
|
146
|
+
// Move the offset by 4 bytes
|
|
147
|
+
offset += 4
|
|
148
|
+
// Check if both weight and seconds are valid numbers
|
|
149
|
+
if (!isNaN(weight) && !isNaN(seconds)) {
|
|
141
150
|
// Tare correction
|
|
142
151
|
const numericData = weight - this.applyTare(weight)
|
|
143
152
|
// Add data to downloadable Array
|
|
@@ -171,16 +180,16 @@ export class Progressor extends Device implements IProgressor {
|
|
|
171
180
|
} else if (kind === ProgressorResponses.COMMAND_RESPONSE) {
|
|
172
181
|
if (!this.writeLast) return
|
|
173
182
|
|
|
174
|
-
let
|
|
183
|
+
let output = ""
|
|
175
184
|
|
|
176
185
|
if (this.writeLast === this.commands.GET_BATT_VLTG) {
|
|
177
|
-
|
|
186
|
+
output = new DataView(value.buffer, 2).getUint32(0, true).toString()
|
|
178
187
|
} else if (this.writeLast === this.commands.GET_FW_VERSION) {
|
|
179
|
-
|
|
188
|
+
output = new TextDecoder().decode(new Uint8Array(value.buffer).slice(2))
|
|
180
189
|
} else if (this.writeLast === this.commands.GET_ERR_INFO) {
|
|
181
|
-
|
|
190
|
+
output = new TextDecoder().decode(new Uint8Array(value.buffer.slice(2)))
|
|
182
191
|
}
|
|
183
|
-
this.writeCallback(
|
|
192
|
+
this.writeCallback(output)
|
|
184
193
|
} else if (kind === ProgressorResponses.LOW_BATTERY_WARNING) {
|
|
185
194
|
console.warn("⚠️ Low power detected. Please consider connecting to a power source.")
|
|
186
195
|
} else {
|
|
@@ -437,10 +437,7 @@ export abstract class Device extends BaseModel implements IDevice {
|
|
|
437
437
|
|
|
438
438
|
if (value) {
|
|
439
439
|
if (value.buffer) {
|
|
440
|
-
|
|
441
|
-
console.log(new Uint8Array(buffer))
|
|
442
|
-
const rawData: DataView = new DataView(buffer)
|
|
443
|
-
console.log(rawData)
|
|
440
|
+
console.log(value)
|
|
444
441
|
} else {
|
|
445
442
|
console.log(value)
|
|
446
443
|
}
|
package/dist/helpers/struct.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export default function struct(format: string): Readonly<{
|
|
2
|
-
unpack: (arrb: ArrayBuffer) => unknown[];
|
|
3
|
-
pack: (...values: unknown[]) => ArrayBuffer;
|
|
4
|
-
unpack_from: (arrb: ArrayBuffer, offs: number) => unknown[];
|
|
5
|
-
pack_into: (arrb: ArrayBuffer, offs: number, ...values: unknown[]) => void;
|
|
6
|
-
iter_unpack: (arrb: ArrayBuffer) => IterableIterator<unknown[]>;
|
|
7
|
-
format: string;
|
|
8
|
-
size: number;
|
|
9
|
-
}>;
|
package/dist/helpers/struct.js
DELETED
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
const rechk = /^([<>])?(([1-9]\d*)?([xcbB?hHiIfdsp]))*$/;
|
|
2
|
-
const refmt = /([1-9]\d*)?([xcbB?hHiIfdsp])/g;
|
|
3
|
-
const str = (v, o, c) => String.fromCharCode(...Array.from(new Uint8Array(v.buffer, v.byteOffset + o, c)));
|
|
4
|
-
const rts = (v, o, c, s) => {
|
|
5
|
-
new Uint8Array(v.buffer, v.byteOffset + o, c).set(s.split("").map((str) => str.charCodeAt(0)));
|
|
6
|
-
};
|
|
7
|
-
const pst = (v, o, c) => str(v, o + 1, Math.min(v.getUint8(o), c - 1));
|
|
8
|
-
const tsp = (v, o, c, s) => {
|
|
9
|
-
v.setUint8(o, s.length);
|
|
10
|
-
rts(v, o + 1, c - 1, s);
|
|
11
|
-
};
|
|
12
|
-
const lut = (le) => ({
|
|
13
|
-
x: (c) => [
|
|
14
|
-
1,
|
|
15
|
-
c,
|
|
16
|
-
() => ({
|
|
17
|
-
u: () => undefined,
|
|
18
|
-
p: () => undefined,
|
|
19
|
-
}),
|
|
20
|
-
],
|
|
21
|
-
c: (c) => [
|
|
22
|
-
c,
|
|
23
|
-
1,
|
|
24
|
-
(o) => ({
|
|
25
|
-
u: (v) => str(v, o, 1),
|
|
26
|
-
p: (v, s) => {
|
|
27
|
-
rts(v, o, 1, s);
|
|
28
|
-
},
|
|
29
|
-
}),
|
|
30
|
-
],
|
|
31
|
-
"?": (c) => [
|
|
32
|
-
c,
|
|
33
|
-
1,
|
|
34
|
-
(o) => ({
|
|
35
|
-
u: (v) => Boolean(v.getUint8(o)),
|
|
36
|
-
p: (v, B) => {
|
|
37
|
-
v.setUint8(o, B ? 1 : 0);
|
|
38
|
-
},
|
|
39
|
-
}),
|
|
40
|
-
],
|
|
41
|
-
b: (c) => [
|
|
42
|
-
c,
|
|
43
|
-
1,
|
|
44
|
-
(o) => ({
|
|
45
|
-
u: (v) => v.getInt8(o),
|
|
46
|
-
p: (v, b) => {
|
|
47
|
-
v.setInt8(o, b);
|
|
48
|
-
},
|
|
49
|
-
}),
|
|
50
|
-
],
|
|
51
|
-
B: (c) => [
|
|
52
|
-
c,
|
|
53
|
-
1,
|
|
54
|
-
(o) => ({
|
|
55
|
-
u: (v) => v.getUint8(o),
|
|
56
|
-
p: (v, B) => {
|
|
57
|
-
v.setUint8(o, B);
|
|
58
|
-
},
|
|
59
|
-
}),
|
|
60
|
-
],
|
|
61
|
-
h: (c) => [
|
|
62
|
-
c,
|
|
63
|
-
2,
|
|
64
|
-
(o) => ({
|
|
65
|
-
u: (v) => v.getInt16(o, le),
|
|
66
|
-
p: (v, h) => {
|
|
67
|
-
v.setInt16(o, h, le);
|
|
68
|
-
},
|
|
69
|
-
}),
|
|
70
|
-
],
|
|
71
|
-
H: (c) => [
|
|
72
|
-
c,
|
|
73
|
-
2,
|
|
74
|
-
(o) => ({
|
|
75
|
-
u: (v) => v.getUint16(o, le),
|
|
76
|
-
p: (v, H) => {
|
|
77
|
-
v.setUint16(o, H, le);
|
|
78
|
-
},
|
|
79
|
-
}),
|
|
80
|
-
],
|
|
81
|
-
i: (c) => [
|
|
82
|
-
c,
|
|
83
|
-
4,
|
|
84
|
-
(o) => ({
|
|
85
|
-
u: (v) => v.getInt32(o, le),
|
|
86
|
-
p: (v, i) => {
|
|
87
|
-
v.setInt32(o, i, le);
|
|
88
|
-
},
|
|
89
|
-
}),
|
|
90
|
-
],
|
|
91
|
-
I: (c) => [
|
|
92
|
-
c,
|
|
93
|
-
4,
|
|
94
|
-
(o) => ({
|
|
95
|
-
u: (v) => v.getUint32(o, le),
|
|
96
|
-
p: (v, I) => {
|
|
97
|
-
v.setUint32(o, I, le);
|
|
98
|
-
},
|
|
99
|
-
}),
|
|
100
|
-
],
|
|
101
|
-
f: (c) => [
|
|
102
|
-
c,
|
|
103
|
-
4,
|
|
104
|
-
(o) => ({
|
|
105
|
-
u: (v) => v.getFloat32(o, le),
|
|
106
|
-
p: (v, f) => {
|
|
107
|
-
v.setFloat32(o, f, le);
|
|
108
|
-
},
|
|
109
|
-
}),
|
|
110
|
-
],
|
|
111
|
-
d: (c) => [
|
|
112
|
-
c,
|
|
113
|
-
8,
|
|
114
|
-
(o) => ({
|
|
115
|
-
u: (v) => v.getFloat64(o, le),
|
|
116
|
-
p: (v, d) => {
|
|
117
|
-
v.setFloat64(o, d, le);
|
|
118
|
-
},
|
|
119
|
-
}),
|
|
120
|
-
],
|
|
121
|
-
s: (c) => [
|
|
122
|
-
1,
|
|
123
|
-
c,
|
|
124
|
-
(o) => ({
|
|
125
|
-
u: (v) => str(v, o, c),
|
|
126
|
-
p: (v, s) => {
|
|
127
|
-
rts(v, o, c, s.slice(0, c));
|
|
128
|
-
},
|
|
129
|
-
}),
|
|
130
|
-
],
|
|
131
|
-
p: (c) => [
|
|
132
|
-
1,
|
|
133
|
-
c,
|
|
134
|
-
(o) => ({
|
|
135
|
-
u: (v) => pst(v, o, c),
|
|
136
|
-
p: (v, s) => {
|
|
137
|
-
tsp(v, o, c, s.slice(0, c - 1));
|
|
138
|
-
},
|
|
139
|
-
}),
|
|
140
|
-
],
|
|
141
|
-
});
|
|
142
|
-
const errbuf = new RangeError("Structure larger than remaining buffer");
|
|
143
|
-
const errval = new RangeError("Not enough values for structure");
|
|
144
|
-
export default function struct(format) {
|
|
145
|
-
const fns = [];
|
|
146
|
-
let size = 0;
|
|
147
|
-
let m = rechk.exec(format);
|
|
148
|
-
if (!m) {
|
|
149
|
-
throw new RangeError("Invalid format string");
|
|
150
|
-
}
|
|
151
|
-
const t = lut("<" === m[1]);
|
|
152
|
-
const lu = (n, c) => t[c](n ? parseInt(n, 10) : 1);
|
|
153
|
-
while ((m = refmt.exec(format))) {
|
|
154
|
-
;
|
|
155
|
-
((r, s, f) => {
|
|
156
|
-
for (let i = 0; i < r; ++i, size += s) {
|
|
157
|
-
if (f) {
|
|
158
|
-
fns.push(f(size));
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
})(...lu(...m.slice(1)));
|
|
162
|
-
}
|
|
163
|
-
const unpack_from = (arrb, offs) => {
|
|
164
|
-
if (arrb.byteLength < (offs | 0) + size) {
|
|
165
|
-
throw errbuf;
|
|
166
|
-
}
|
|
167
|
-
const v = new DataView(arrb, offs | 0);
|
|
168
|
-
return fns.map((f) => f.u(v));
|
|
169
|
-
};
|
|
170
|
-
const pack_into = (arrb, offs, ...values) => {
|
|
171
|
-
if (values.length < fns.length) {
|
|
172
|
-
throw errval;
|
|
173
|
-
}
|
|
174
|
-
if (arrb.byteLength < offs + size) {
|
|
175
|
-
throw errbuf;
|
|
176
|
-
}
|
|
177
|
-
const v = new DataView(arrb, offs);
|
|
178
|
-
new Uint8Array(arrb, offs, size).fill(0);
|
|
179
|
-
fns.forEach((f, i) => {
|
|
180
|
-
f.p(v, values[i]);
|
|
181
|
-
});
|
|
182
|
-
};
|
|
183
|
-
const pack = (...values) => {
|
|
184
|
-
const b = new ArrayBuffer(size);
|
|
185
|
-
pack_into(b, 0, ...values);
|
|
186
|
-
return b;
|
|
187
|
-
};
|
|
188
|
-
const unpack = (arrb) => unpack_from(arrb, 0);
|
|
189
|
-
function* iter_unpack(arrb) {
|
|
190
|
-
for (let offs = 0; offs + size <= arrb.byteLength; offs += size) {
|
|
191
|
-
yield unpack_from(arrb, offs);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
return Object.freeze({
|
|
195
|
-
unpack,
|
|
196
|
-
pack,
|
|
197
|
-
unpack_from,
|
|
198
|
-
pack_into,
|
|
199
|
-
iter_unpack,
|
|
200
|
-
format,
|
|
201
|
-
size,
|
|
202
|
-
});
|
|
203
|
-
}
|
package/src/helpers/struct.ts
DELETED
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
const rechk = /^([<>])?(([1-9]\d*)?([xcbB?hHiIfdsp]))*$/
|
|
2
|
-
const refmt = /([1-9]\d*)?([xcbB?hHiIfdsp])/g
|
|
3
|
-
|
|
4
|
-
const str = (v: DataView, o: number, c: number): string =>
|
|
5
|
-
String.fromCharCode(...Array.from(new Uint8Array(v.buffer, v.byteOffset + o, c)))
|
|
6
|
-
|
|
7
|
-
const rts = (v: DataView, o: number, c: number, s: string): void => {
|
|
8
|
-
new Uint8Array(v.buffer, v.byteOffset + o, c).set(s.split("").map((str) => str.charCodeAt(0)))
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const pst = (v: DataView, o: number, c: number): string => str(v, o + 1, Math.min(v.getUint8(o), c - 1))
|
|
12
|
-
|
|
13
|
-
const tsp = (v: DataView, o: number, c: number, s: string): void => {
|
|
14
|
-
v.setUint8(o, s.length)
|
|
15
|
-
rts(v, o + 1, c - 1, s)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface FormatFn {
|
|
19
|
-
u: (v: DataView) => unknown
|
|
20
|
-
p: (v: DataView, value: unknown) => void
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
type LUT = Record<string, (c: number) => [number, number, (o: number) => FormatFn]>
|
|
24
|
-
|
|
25
|
-
const lut = (le: boolean): LUT => ({
|
|
26
|
-
x: (c: number) =>
|
|
27
|
-
[
|
|
28
|
-
1,
|
|
29
|
-
c,
|
|
30
|
-
() => ({
|
|
31
|
-
u: () => undefined,
|
|
32
|
-
p: () => undefined,
|
|
33
|
-
}),
|
|
34
|
-
] as [number, number, (o: number) => FormatFn],
|
|
35
|
-
c: (c: number) =>
|
|
36
|
-
[
|
|
37
|
-
c,
|
|
38
|
-
1,
|
|
39
|
-
(o: number) => ({
|
|
40
|
-
u: (v: DataView) => str(v, o, 1),
|
|
41
|
-
p: (v: DataView, s: string) => {
|
|
42
|
-
rts(v, o, 1, s)
|
|
43
|
-
},
|
|
44
|
-
}),
|
|
45
|
-
] as [number, number, (o: number) => FormatFn],
|
|
46
|
-
"?": (c: number) =>
|
|
47
|
-
[
|
|
48
|
-
c,
|
|
49
|
-
1,
|
|
50
|
-
(o: number) => ({
|
|
51
|
-
u: (v: DataView) => Boolean(v.getUint8(o)),
|
|
52
|
-
p: (v: DataView, B: boolean) => {
|
|
53
|
-
v.setUint8(o, B ? 1 : 0)
|
|
54
|
-
},
|
|
55
|
-
}),
|
|
56
|
-
] as [number, number, (o: number) => FormatFn],
|
|
57
|
-
b: (c: number) =>
|
|
58
|
-
[
|
|
59
|
-
c,
|
|
60
|
-
1,
|
|
61
|
-
(o: number) => ({
|
|
62
|
-
u: (v: DataView) => v.getInt8(o),
|
|
63
|
-
p: (v: DataView, b: number) => {
|
|
64
|
-
v.setInt8(o, b)
|
|
65
|
-
},
|
|
66
|
-
}),
|
|
67
|
-
] as [number, number, (o: number) => FormatFn],
|
|
68
|
-
B: (c: number) =>
|
|
69
|
-
[
|
|
70
|
-
c,
|
|
71
|
-
1,
|
|
72
|
-
(o: number) => ({
|
|
73
|
-
u: (v: DataView) => v.getUint8(o),
|
|
74
|
-
p: (v: DataView, B: number) => {
|
|
75
|
-
v.setUint8(o, B)
|
|
76
|
-
},
|
|
77
|
-
}),
|
|
78
|
-
] as [number, number, (o: number) => FormatFn],
|
|
79
|
-
h: (c: number) =>
|
|
80
|
-
[
|
|
81
|
-
c,
|
|
82
|
-
2,
|
|
83
|
-
(o: number) => ({
|
|
84
|
-
u: (v: DataView) => v.getInt16(o, le),
|
|
85
|
-
p: (v: DataView, h: number) => {
|
|
86
|
-
v.setInt16(o, h, le)
|
|
87
|
-
},
|
|
88
|
-
}),
|
|
89
|
-
] as [number, number, (o: number) => FormatFn],
|
|
90
|
-
H: (c: number) =>
|
|
91
|
-
[
|
|
92
|
-
c,
|
|
93
|
-
2,
|
|
94
|
-
(o: number) => ({
|
|
95
|
-
u: (v: DataView) => v.getUint16(o, le),
|
|
96
|
-
p: (v: DataView, H: number) => {
|
|
97
|
-
v.setUint16(o, H, le)
|
|
98
|
-
},
|
|
99
|
-
}),
|
|
100
|
-
] as [number, number, (o: number) => FormatFn],
|
|
101
|
-
i: (c: number) =>
|
|
102
|
-
[
|
|
103
|
-
c,
|
|
104
|
-
4,
|
|
105
|
-
(o: number) => ({
|
|
106
|
-
u: (v: DataView) => v.getInt32(o, le),
|
|
107
|
-
p: (v: DataView, i: number) => {
|
|
108
|
-
v.setInt32(o, i, le)
|
|
109
|
-
},
|
|
110
|
-
}),
|
|
111
|
-
] as [number, number, (o: number) => FormatFn],
|
|
112
|
-
I: (c: number) =>
|
|
113
|
-
[
|
|
114
|
-
c,
|
|
115
|
-
4,
|
|
116
|
-
(o: number) => ({
|
|
117
|
-
u: (v: DataView) => v.getUint32(o, le),
|
|
118
|
-
p: (v: DataView, I: number) => {
|
|
119
|
-
v.setUint32(o, I, le)
|
|
120
|
-
},
|
|
121
|
-
}),
|
|
122
|
-
] as [number, number, (o: number) => FormatFn],
|
|
123
|
-
f: (c: number) =>
|
|
124
|
-
[
|
|
125
|
-
c,
|
|
126
|
-
4,
|
|
127
|
-
(o: number) => ({
|
|
128
|
-
u: (v: DataView) => v.getFloat32(o, le),
|
|
129
|
-
p: (v: DataView, f: number) => {
|
|
130
|
-
v.setFloat32(o, f, le)
|
|
131
|
-
},
|
|
132
|
-
}),
|
|
133
|
-
] as [number, number, (o: number) => FormatFn],
|
|
134
|
-
d: (c: number) =>
|
|
135
|
-
[
|
|
136
|
-
c,
|
|
137
|
-
8,
|
|
138
|
-
(o: number) => ({
|
|
139
|
-
u: (v: DataView) => v.getFloat64(o, le),
|
|
140
|
-
p: (v: DataView, d: number) => {
|
|
141
|
-
v.setFloat64(o, d, le)
|
|
142
|
-
},
|
|
143
|
-
}),
|
|
144
|
-
] as [number, number, (o: number) => FormatFn],
|
|
145
|
-
s: (c: number) =>
|
|
146
|
-
[
|
|
147
|
-
1,
|
|
148
|
-
c,
|
|
149
|
-
(o: number) => ({
|
|
150
|
-
u: (v: DataView) => str(v, o, c),
|
|
151
|
-
p: (v: DataView, s: string) => {
|
|
152
|
-
rts(v, o, c, s.slice(0, c))
|
|
153
|
-
},
|
|
154
|
-
}),
|
|
155
|
-
] as [number, number, (o: number) => FormatFn],
|
|
156
|
-
p: (c: number) =>
|
|
157
|
-
[
|
|
158
|
-
1,
|
|
159
|
-
c,
|
|
160
|
-
(o: number) => ({
|
|
161
|
-
u: (v: DataView) => pst(v, o, c),
|
|
162
|
-
p: (v: DataView, s: string) => {
|
|
163
|
-
tsp(v, o, c, s.slice(0, c - 1))
|
|
164
|
-
},
|
|
165
|
-
}),
|
|
166
|
-
] as [number, number, (o: number) => FormatFn],
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
const errbuf: RangeError = new RangeError("Structure larger than remaining buffer")
|
|
170
|
-
const errval: RangeError = new RangeError("Not enough values for structure")
|
|
171
|
-
|
|
172
|
-
export default function struct(format: string) {
|
|
173
|
-
const fns: FormatFn[] = []
|
|
174
|
-
let size = 0
|
|
175
|
-
let m: RegExpExecArray | null = rechk.exec(format)
|
|
176
|
-
|
|
177
|
-
if (!m) {
|
|
178
|
-
throw new RangeError("Invalid format string")
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const t: LUT = lut("<" === m[1])
|
|
182
|
-
const lu = (n: string, c: string): [number, number, (o: number) => FormatFn] => t[c](n ? parseInt(n, 10) : 1)
|
|
183
|
-
|
|
184
|
-
while ((m = refmt.exec(format))) {
|
|
185
|
-
;((r: number, s: number, f: (o: number) => FormatFn) => {
|
|
186
|
-
for (let i = 0; i < r; ++i, size += s) {
|
|
187
|
-
if (f) {
|
|
188
|
-
fns.push(f(size))
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
})(...lu(...(m.slice(1) as [string, string])))
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
const unpack_from = (arrb: ArrayBuffer, offs: number): unknown[] => {
|
|
195
|
-
if (arrb.byteLength < (offs | 0) + size) {
|
|
196
|
-
throw errbuf
|
|
197
|
-
}
|
|
198
|
-
const v = new DataView(arrb, offs | 0)
|
|
199
|
-
return fns.map((f) => f.u(v))
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const pack_into = (arrb: ArrayBuffer, offs: number, ...values: unknown[]): void => {
|
|
203
|
-
if (values.length < fns.length) {
|
|
204
|
-
throw errval
|
|
205
|
-
}
|
|
206
|
-
if (arrb.byteLength < offs + size) {
|
|
207
|
-
throw errbuf
|
|
208
|
-
}
|
|
209
|
-
const v = new DataView(arrb, offs)
|
|
210
|
-
new Uint8Array(arrb, offs, size).fill(0)
|
|
211
|
-
fns.forEach((f, i) => {
|
|
212
|
-
f.p(v, values[i])
|
|
213
|
-
})
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const pack = (...values: unknown[]): ArrayBuffer => {
|
|
217
|
-
const b = new ArrayBuffer(size)
|
|
218
|
-
pack_into(b, 0, ...values)
|
|
219
|
-
return b
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const unpack = (arrb: ArrayBuffer): unknown[] => unpack_from(arrb, 0)
|
|
223
|
-
|
|
224
|
-
function* iter_unpack(arrb: ArrayBuffer): IterableIterator<unknown[]> {
|
|
225
|
-
for (let offs = 0; offs + size <= arrb.byteLength; offs += size) {
|
|
226
|
-
yield unpack_from(arrb, offs)
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
return Object.freeze({
|
|
231
|
-
unpack,
|
|
232
|
-
pack,
|
|
233
|
-
unpack_from,
|
|
234
|
-
pack_into,
|
|
235
|
-
iter_unpack,
|
|
236
|
-
format,
|
|
237
|
-
size,
|
|
238
|
-
})
|
|
239
|
-
}
|