@robotical/webapp-types 3.25.4 → 3.25.6
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/dist-types/package.json +1 -1
- package/dist-types/src/application/RAFTs/Marty/PublishedDataAnalyser.d.ts +2 -2
- package/dist-types/src/application/RAFTs/Marty/PublishedDataAnalyser.js +7 -23
- package/dist-types/src/components/oneoffs/calibration-screens/ColourSensorCalibration/messages.d.ts +16 -3
- package/dist-types/src/components/oneoffs/calibration-screens/ColourSensorCalibration/messages.js +16 -3
- package/dist-types/src/screens/configuration/marty/Diagnostics/ColourSensorDiagnostics/messages.d.ts +2 -0
- package/dist-types/src/screens/configuration/marty/Diagnostics/ColourSensorDiagnostics/messages.js +3 -1
- package/dist-types/src/utils/colour-calibration-reconnect.d.ts +19 -0
- package/dist-types/src/utils/colour-calibration-reconnect.js +261 -0
- package/dist-types/src/utils/colour-calibration-records.d.ts +21 -0
- package/dist-types/src/utils/colour-calibration-records.js +253 -0
- package/dist-types/src/utils/colour-classifier.d.ts +32 -0
- package/dist-types/src/utils/colour-classifier.js +37 -0
- package/dist-types/src/utils/colour-sensor-diagnostics.d.ts +162 -0
- package/dist-types/src/utils/colour-sensor-diagnostics.js +364 -0
- package/dist-types/src/utils/colour-sensor-operation.d.ts +2 -0
- package/dist-types/src/utils/colour-sensor-operation.js +62 -0
- package/dist-types/src/utils/colour-sensor-profile.d.ts +32 -0
- package/dist-types/src/utils/colour-sensor-profile.js +229 -0
- package/dist-types/src/utils/colour-sensor-registers.d.ts +53 -0
- package/dist-types/src/utils/colour-sensor-registers.js +310 -0
- package/dist-types/src/utils/marty-colour-policy.json +5 -3
- package/package.json +1 -1
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
var __extends = (this && this.__extends) || (function () {
|
|
2
|
+
var extendStatics = function (d, b) {
|
|
3
|
+
extendStatics = Object.setPrototypeOf ||
|
|
4
|
+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
5
|
+
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
6
|
+
return extendStatics(d, b);
|
|
7
|
+
};
|
|
8
|
+
return function (d, b) {
|
|
9
|
+
if (typeof b !== "function" && b !== null)
|
|
10
|
+
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
11
|
+
extendStatics(d, b);
|
|
12
|
+
function __() { this.constructor = d; }
|
|
13
|
+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
14
|
+
};
|
|
15
|
+
})();
|
|
16
|
+
var __assign = (this && this.__assign) || function () {
|
|
17
|
+
__assign = Object.assign || function(t) {
|
|
18
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
19
|
+
s = arguments[i];
|
|
20
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
21
|
+
t[p] = s[p];
|
|
22
|
+
}
|
|
23
|
+
return t;
|
|
24
|
+
};
|
|
25
|
+
return __assign.apply(this, arguments);
|
|
26
|
+
};
|
|
27
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
28
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
29
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
30
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
31
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
32
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
33
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
37
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
38
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
39
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
40
|
+
function step(op) {
|
|
41
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
42
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
43
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
44
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
45
|
+
switch (op[0]) {
|
|
46
|
+
case 0: case 1: t = op; break;
|
|
47
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
48
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
49
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
50
|
+
default:
|
|
51
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
52
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
53
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
54
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
55
|
+
if (t[2]) _.ops.pop();
|
|
56
|
+
_.trys.pop(); continue;
|
|
57
|
+
}
|
|
58
|
+
op = body.call(thisArg, _);
|
|
59
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
60
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
64
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
65
|
+
if (ar || !(i in from)) {
|
|
66
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
67
|
+
ar[i] = from[i];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
71
|
+
};
|
|
72
|
+
import packageInfo from "../../package.json";
|
|
73
|
+
import colourPolicy from "./marty-colour-policy.json";
|
|
74
|
+
import { classifyColour, colourChannelsFromValues } from "./colour-classifier";
|
|
75
|
+
import { getActiveColourSensorProfile } from "./colour-sensor-profile";
|
|
76
|
+
import { lockColourSensorOperation, waitForColourProfileRead } from "./colour-sensor-operation";
|
|
77
|
+
import { getPendingColourCalibration } from "./colour-calibration-reconnect";
|
|
78
|
+
import { collectColourSensorSetup, readRawColourSensor } from "./colour-sensor-registers";
|
|
79
|
+
export var CARD_COLOURS = ["green", "purple", "yellow", "blue", "red"];
|
|
80
|
+
export var CAPTURE_TARGETS = __spreadArray(__spreadArray([], CARD_COLOURS, true), ["air"], false);
|
|
81
|
+
export var COLOUR_DATABASE_URL = "https://platforms-error-logging-default-rtdb.europe-west1.firebasedatabase.app";
|
|
82
|
+
var STORAGE_KEY = "marty-colour-diagnostics-v1";
|
|
83
|
+
var CAPTURE_INTERVAL_MS = 200;
|
|
84
|
+
var CAPTURE_DURATION_MS = 2000;
|
|
85
|
+
var MAX_READING_AGE_MS = 1500;
|
|
86
|
+
// The asset preparation script applies this same policy to the bundled Blocks reporter.
|
|
87
|
+
export var BLOCKS_COLOUR_BOUNDARIES = colourPolicy.boundaries;
|
|
88
|
+
var ColourDiagnosticError = /** @class */ (function (_super) {
|
|
89
|
+
__extends(ColourDiagnosticError, _super);
|
|
90
|
+
function ColourDiagnosticError(code) {
|
|
91
|
+
var _this = _super.call(this, code) || this;
|
|
92
|
+
_this.code = code;
|
|
93
|
+
return _this;
|
|
94
|
+
}
|
|
95
|
+
return ColourDiagnosticError;
|
|
96
|
+
}(Error));
|
|
97
|
+
export { ColourDiagnosticError };
|
|
98
|
+
var createId = function () {
|
|
99
|
+
var bytes = new Uint8Array(16);
|
|
100
|
+
window.crypto.getRandomValues(bytes);
|
|
101
|
+
return Array.from(bytes, function (value) { return value.toString(16).padStart(2, "0"); }).join("");
|
|
102
|
+
};
|
|
103
|
+
export var newColourSession = function () { return ({
|
|
104
|
+
schemaVersion: 1, sessionId: createId(), startedAtMs: Date.now(), captures: [],
|
|
105
|
+
}); };
|
|
106
|
+
export var getColourSensors = function (raftId) {
|
|
107
|
+
var _a, _b;
|
|
108
|
+
var raft = window.applicationManager.connectedRafts[raftId];
|
|
109
|
+
if (!raft || raft.type !== "Marty" || !raft.isVerified)
|
|
110
|
+
return [];
|
|
111
|
+
return (((_b = (_a = raft.raftStateInfo) === null || _a === void 0 ? void 0 : _a.addOnInfo) === null || _b === void 0 ? void 0 : _b.addons) || [])
|
|
112
|
+
.filter(function (addon) { return addon.whoAmI === "coloursensor"; })
|
|
113
|
+
.map(function (addon) { return ({ key: JSON.stringify([addon.id, addon.name]), raftId: raftId, raft: raft, addon: addon }); });
|
|
114
|
+
};
|
|
115
|
+
export var assertColourSensorActive = function (sensor, signal) {
|
|
116
|
+
if (signal.aborted)
|
|
117
|
+
throw new ColourDiagnosticError("cancelled");
|
|
118
|
+
if (document.hidden)
|
|
119
|
+
throw new ColourDiagnosticError("backgrounded");
|
|
120
|
+
var current = getColourSensors(sensor.raftId).find(function (candidate) { return candidate.key === sensor.key; });
|
|
121
|
+
if (!current || current.raft !== sensor.raft)
|
|
122
|
+
throw new ColourDiagnosticError("sensorDisconnected");
|
|
123
|
+
};
|
|
124
|
+
export var interpretColour = function (vals, profile) {
|
|
125
|
+
var _a = colourChannelsFromValues(vals), red = _a.red, green = _a.green, blue = _a.blue, clear = _a.clear, air = _a.air;
|
|
126
|
+
if (![red, green, blue, clear].every(function (value) { return typeof value === "number" && Number.isFinite(value); }) ||
|
|
127
|
+
![true, false, 0, 1].includes(air))
|
|
128
|
+
throw new ColourDiagnosticError("invalidReading");
|
|
129
|
+
var r = red, g = green, b = blue, c = clear;
|
|
130
|
+
return __assign(__assign({}, classifyColour({ red: r, green: g, blue: b, clear: c, air: air }, profile)), { red: r, green: g, blue: b, clear: c, isOnAir: Boolean(air),
|
|
131
|
+
// Calibration can produce values outside 0–255. Clamp only the display colour.
|
|
132
|
+
hex: "#".concat([r, g, b].map(function (value) { return Math.max(0, Math.min(255, Math.round(value))).toString(16).padStart(2, "0"); }).join("")) });
|
|
133
|
+
};
|
|
134
|
+
export var readColourSensor = function (sensor) {
|
|
135
|
+
var _a, _b, _c;
|
|
136
|
+
var current = getColourSensors(sensor.raftId).find(function (candidate) { return candidate.key === sensor.key; });
|
|
137
|
+
if (!current || current.raft !== sensor.raft)
|
|
138
|
+
throw new ColourDiagnosticError("sensorDisconnected");
|
|
139
|
+
var observedAtMs = Date.now();
|
|
140
|
+
var receivedAtMs = current.raft.raftStateInfo.addOnInfoValidMs;
|
|
141
|
+
var ageMs = observedAtMs - receivedAtMs;
|
|
142
|
+
if (!Number.isFinite(receivedAtMs) || receivedAtMs <= 0 || ageMs < 0 || ageMs > MAX_READING_AGE_MS) {
|
|
143
|
+
throw new ColourDiagnosticError("staleReading");
|
|
144
|
+
}
|
|
145
|
+
var powerAt = current.raft.raftStateInfo.powerValidMs;
|
|
146
|
+
if (powerAt && observedAtMs - powerAt >= 0 && observedAtMs - powerAt < 3000 &&
|
|
147
|
+
((_a = current.raft.raftStateInfo.power) === null || _a === void 0 ? void 0 : _a.powerStatus.power5VIsOn) === false)
|
|
148
|
+
throw new ColourDiagnosticError("sensorAsleep");
|
|
149
|
+
// The high bit of Marty add-on telemetry means communications are healthy. A new packet can contain cached offline values.
|
|
150
|
+
if (current.addon.status !== undefined && !(current.addon.status & 0x80))
|
|
151
|
+
throw new ColourDiagnosticError("sensorOffline");
|
|
152
|
+
return __assign(__assign({}, interpretColour(current.addon.vals, (_b = getActiveColourSensorProfile(sensor)) === null || _b === void 0 ? void 0 : _b.signal)), { observedAtMs: observedAtMs, receivedAtMs: receivedAtMs, ageMs: ageMs, publishedValues: __assign({}, current.addon.vals), sensorStatus: (_c = current.addon.status) !== null && _c !== void 0 ? _c : null, irFootSensors: (current.raft.raftStateInfo.addOnInfo.addons || [])
|
|
153
|
+
.filter(function (addon) { return addon.whoAmI === "IRFoot"; })
|
|
154
|
+
.map(function (addon) { return (__assign(__assign({}, addon), { vals: __assign({}, addon.vals) })); }) });
|
|
155
|
+
};
|
|
156
|
+
var delay = function (ms, signal) { return new Promise(function (resolve, reject) {
|
|
157
|
+
var abort = function () {
|
|
158
|
+
clearTimeout(timer);
|
|
159
|
+
signal.removeEventListener("abort", abort);
|
|
160
|
+
reject(new ColourDiagnosticError("cancelled"));
|
|
161
|
+
};
|
|
162
|
+
var timer = setTimeout(function () { signal.removeEventListener("abort", abort); resolve(); }, ms);
|
|
163
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
164
|
+
if (signal.aborted)
|
|
165
|
+
abort();
|
|
166
|
+
}); };
|
|
167
|
+
var captureUnlocked = function (sensor, expectedColour, session, signal) { return __awaiter(void 0, void 0, void 0, function () {
|
|
168
|
+
var startedAtMs, checkActive, sensorSetup, sampleWindowStartedAtMs, samples, rawSamples, rawController, abortRaw, rawFailure, collectRaw, rawCapture, index, reading;
|
|
169
|
+
var _a, _b, _c, _d, _e, _f;
|
|
170
|
+
return __generator(this, function (_g) {
|
|
171
|
+
switch (_g.label) {
|
|
172
|
+
case 0:
|
|
173
|
+
startedAtMs = Date.now();
|
|
174
|
+
checkActive = function () {
|
|
175
|
+
assertColourSensorActive(sensor, signal);
|
|
176
|
+
if (getPendingColourCalibration(sensor))
|
|
177
|
+
throw new ColourDiagnosticError("calibrationReconnectRequired");
|
|
178
|
+
};
|
|
179
|
+
checkActive();
|
|
180
|
+
return [4 /*yield*/, collectColourSensorSetup(sensor, signal, checkActive)];
|
|
181
|
+
case 1:
|
|
182
|
+
sensorSetup = _g.sent();
|
|
183
|
+
checkActive();
|
|
184
|
+
if (sensorSetup.readiness.status === "ok" && !sensorSetup.readiness.value)
|
|
185
|
+
throw new ColourDiagnosticError("sensorInitialising");
|
|
186
|
+
if (((_a = sensorSetup.power) === null || _a === void 0 ? void 0 : _a.status) === "unavailable")
|
|
187
|
+
throw new ColourDiagnosticError("sensorPowerFailed");
|
|
188
|
+
if (sensorSetup.hardware.status === "ok" && sensorSetup.hardware.value.commsOk === 0)
|
|
189
|
+
throw new ColourDiagnosticError("sensorOffline");
|
|
190
|
+
readColourSensor(sensor);
|
|
191
|
+
sampleWindowStartedAtMs = Date.now();
|
|
192
|
+
samples = [];
|
|
193
|
+
rawSamples = [];
|
|
194
|
+
rawController = new AbortController();
|
|
195
|
+
abortRaw = function () { return rawController.abort(); };
|
|
196
|
+
signal.addEventListener("abort", abortRaw, { once: true });
|
|
197
|
+
collectRaw = function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
198
|
+
var result, failure_1;
|
|
199
|
+
return __generator(this, function (_a) {
|
|
200
|
+
switch (_a.label) {
|
|
201
|
+
case 0:
|
|
202
|
+
_a.trys.push([0, 5, , 6]);
|
|
203
|
+
if (sensorSetup.readiness.status === "unavailable") {
|
|
204
|
+
rawSamples.push(sensorSetup.readiness);
|
|
205
|
+
return [2 /*return*/];
|
|
206
|
+
}
|
|
207
|
+
_a.label = 1;
|
|
208
|
+
case 1:
|
|
209
|
+
if (!(!rawController.signal.aborted && Date.now() - sampleWindowStartedAtMs < CAPTURE_DURATION_MS)) return [3 /*break*/, 4];
|
|
210
|
+
return [4 /*yield*/, readRawColourSensor(sensor, rawController.signal, function () { return assertColourSensorActive(sensor, rawController.signal); })];
|
|
211
|
+
case 2:
|
|
212
|
+
result = _a.sent();
|
|
213
|
+
rawSamples.push(result);
|
|
214
|
+
if (result.status === "unavailable")
|
|
215
|
+
return [3 /*break*/, 4];
|
|
216
|
+
return [4 /*yield*/, delay(CAPTURE_INTERVAL_MS, rawController.signal)];
|
|
217
|
+
case 3:
|
|
218
|
+
_a.sent();
|
|
219
|
+
return [3 /*break*/, 1];
|
|
220
|
+
case 4: return [3 /*break*/, 6];
|
|
221
|
+
case 5:
|
|
222
|
+
failure_1 = _a.sent();
|
|
223
|
+
if (!(failure_1 instanceof ColourDiagnosticError && failure_1.code === "cancelled" && !signal.aborted))
|
|
224
|
+
rawFailure = failure_1;
|
|
225
|
+
return [3 /*break*/, 6];
|
|
226
|
+
case 6: return [2 /*return*/];
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
}); };
|
|
230
|
+
rawCapture = collectRaw();
|
|
231
|
+
_g.label = 2;
|
|
232
|
+
case 2:
|
|
233
|
+
_g.trys.push([2, , 7, 9]);
|
|
234
|
+
index = 0;
|
|
235
|
+
_g.label = 3;
|
|
236
|
+
case 3:
|
|
237
|
+
if (!(index <= CAPTURE_DURATION_MS / CAPTURE_INTERVAL_MS)) return [3 /*break*/, 6];
|
|
238
|
+
checkActive();
|
|
239
|
+
if (rawFailure)
|
|
240
|
+
throw rawFailure;
|
|
241
|
+
reading = readColourSensor(sensor);
|
|
242
|
+
if (!samples.length || samples[samples.length - 1].receivedAtMs !== reading.receivedAtMs)
|
|
243
|
+
samples.push(reading);
|
|
244
|
+
if (!(index < CAPTURE_DURATION_MS / CAPTURE_INTERVAL_MS)) return [3 /*break*/, 5];
|
|
245
|
+
return [4 /*yield*/, delay(CAPTURE_INTERVAL_MS, signal)];
|
|
246
|
+
case 4:
|
|
247
|
+
_g.sent();
|
|
248
|
+
_g.label = 5;
|
|
249
|
+
case 5:
|
|
250
|
+
index++;
|
|
251
|
+
return [3 /*break*/, 3];
|
|
252
|
+
case 6: return [3 /*break*/, 9];
|
|
253
|
+
case 7:
|
|
254
|
+
rawController.abort();
|
|
255
|
+
signal.removeEventListener("abort", abortRaw);
|
|
256
|
+
return [4 /*yield*/, rawCapture];
|
|
257
|
+
case 8:
|
|
258
|
+
_g.sent();
|
|
259
|
+
return [7 /*endfinally*/];
|
|
260
|
+
case 9:
|
|
261
|
+
if (rawFailure)
|
|
262
|
+
throw rawFailure;
|
|
263
|
+
if (samples.length < 2)
|
|
264
|
+
throw new ColourDiagnosticError("staleReading");
|
|
265
|
+
return [2 /*return*/, __assign(__assign({ schemaVersion: 2, sessionId: session.sessionId, sessionStartedAtMs: session.startedAtMs, captureId: createId(), expectedColour: expectedColour, startedAtMs: startedAtMs, finishedAtMs: Date.now(), appVersion: packageInfo.version, classifier: { source: colourPolicy.source, airFlagTakesPrecedence: false, boundaries: BLOCKS_COLOUR_BOUNDARIES,
|
|
266
|
+
signal: colourPolicy.signal, profile: getActiveColourSensorProfile(sensor), clearBoundsUsedForClassification: false }, device: {
|
|
267
|
+
id: sensor.raftId, type: sensor.raft.type, name: ((_c = (_b = sensor.raft).getFriendlyName) === null || _c === void 0 ? void 0 : _c.call(_b)) || sensor.raftId,
|
|
268
|
+
firmwareVersion: ((_d = sensor.raft.systemInfo) === null || _d === void 0 ? void 0 : _d.SystemVersion) || null, connectionMethod: sensor.raft.connectionMethod || null,
|
|
269
|
+
}, sensor: { id: sensor.addon.id, name: sensor.addon.name, whoAmI: sensor.addon.whoAmI } }, (((_f = (_e = sensor.raft.raftStateInfo) === null || _e === void 0 ? void 0 : _e.power) === null || _f === void 0 ? void 0 : _f.powerStatus) && { powerStatus: {
|
|
270
|
+
receivedAtMs: sensor.raft.raftStateInfo.powerValidMs || 0, values: __assign({}, sensor.raft.raftStateInfo.power.powerStatus),
|
|
271
|
+
} })), { sampleIntervalMs: CAPTURE_INTERVAL_MS, sampleCount: samples.length, samples: samples, sampleWindowStartedAtMs: sampleWindowStartedAtMs, sensorSetup: sensorSetup, rawSamples: rawSamples })];
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
}); };
|
|
275
|
+
export var captureColourSnapshot = function (sensor, expectedColour, session, signal) { return __awaiter(void 0, void 0, void 0, function () {
|
|
276
|
+
var release;
|
|
277
|
+
return __generator(this, function (_a) {
|
|
278
|
+
switch (_a.label) {
|
|
279
|
+
case 0: return [4 /*yield*/, waitForColourProfileRead(sensor.raft, function () { return assertColourSensorActive(sensor, signal); })];
|
|
280
|
+
case 1:
|
|
281
|
+
_a.sent();
|
|
282
|
+
release = lockColourSensorOperation(sensor.raft);
|
|
283
|
+
if (!release)
|
|
284
|
+
throw new ColourDiagnosticError("sensorBusy");
|
|
285
|
+
_a.label = 2;
|
|
286
|
+
case 2:
|
|
287
|
+
_a.trys.push([2, , 4, 5]);
|
|
288
|
+
return [4 /*yield*/, captureUnlocked(sensor, expectedColour, session, signal)];
|
|
289
|
+
case 3: return [2 /*return*/, _a.sent()];
|
|
290
|
+
case 4:
|
|
291
|
+
release();
|
|
292
|
+
return [7 /*endfinally*/];
|
|
293
|
+
case 5: return [2 /*return*/];
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
}); };
|
|
297
|
+
export var uploadColourSnapshot = function (snapshot, signal) { return __awaiter(void 0, void 0, void 0, function () {
|
|
298
|
+
var controller, abort, timer, response, error_1;
|
|
299
|
+
return __generator(this, function (_a) {
|
|
300
|
+
switch (_a.label) {
|
|
301
|
+
case 0:
|
|
302
|
+
controller = new AbortController();
|
|
303
|
+
abort = function () { return controller.abort(); };
|
|
304
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
305
|
+
if (signal.aborted)
|
|
306
|
+
abort();
|
|
307
|
+
timer = setTimeout(abort, 15000);
|
|
308
|
+
_a.label = 1;
|
|
309
|
+
case 1:
|
|
310
|
+
_a.trys.push([1, 3, 4, 5]);
|
|
311
|
+
return [4 /*yield*/, fetch("".concat(COLOUR_DATABASE_URL, "/colours/").concat(snapshot.sessionId, "/captures/").concat(snapshot.captureId, ".json"), {
|
|
312
|
+
method: "PUT", headers: { "Content-Type": "application/json" },
|
|
313
|
+
body: JSON.stringify(__assign(__assign({}, snapshot), { uploadedAt: { ".sv": "timestamp" } })), signal: controller.signal,
|
|
314
|
+
})];
|
|
315
|
+
case 2:
|
|
316
|
+
response = _a.sent();
|
|
317
|
+
if (!response.ok)
|
|
318
|
+
throw new ColourDiagnosticError(response.status === 401 || response.status === 403 ? "uploadDenied" : "uploadFailed");
|
|
319
|
+
return [3 /*break*/, 5];
|
|
320
|
+
case 3:
|
|
321
|
+
error_1 = _a.sent();
|
|
322
|
+
if (signal.aborted)
|
|
323
|
+
throw new ColourDiagnosticError("cancelled");
|
|
324
|
+
throw error_1 instanceof ColourDiagnosticError ? error_1 : new ColourDiagnosticError("uploadFailed");
|
|
325
|
+
case 4:
|
|
326
|
+
clearTimeout(timer);
|
|
327
|
+
signal.removeEventListener("abort", abort);
|
|
328
|
+
return [7 /*endfinally*/];
|
|
329
|
+
case 5: return [2 /*return*/];
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
}); };
|
|
333
|
+
export var loadColourSession = function () {
|
|
334
|
+
try {
|
|
335
|
+
var saved_1 = JSON.parse(localStorage.getItem(STORAGE_KEY) || "null");
|
|
336
|
+
if ((saved_1 === null || saved_1 === void 0 ? void 0 : saved_1.schemaVersion) === 1 && /^[a-f0-9]{32}$/.test(saved_1.sessionId) && Number.isFinite(saved_1.startedAtMs) &&
|
|
337
|
+
Array.isArray(saved_1.captures) && saved_1.captures.every(function (entry) {
|
|
338
|
+
var _a;
|
|
339
|
+
return ((_a = entry.snapshot) === null || _a === void 0 ? void 0 : _a.sessionId) === saved_1.sessionId &&
|
|
340
|
+
/^[a-f0-9]{32}$/.test(entry.snapshot.captureId) && CAPTURE_TARGETS.includes(entry.snapshot.expectedColour) &&
|
|
341
|
+
Array.isArray(entry.snapshot.samples);
|
|
342
|
+
}))
|
|
343
|
+
return saved_1;
|
|
344
|
+
}
|
|
345
|
+
catch ( /* A browser without local storage can still capture and download in-memory data. */_a) { /* A browser without local storage can still capture and download in-memory data. */ }
|
|
346
|
+
return newColourSession();
|
|
347
|
+
};
|
|
348
|
+
export var persistColourSession = function (session) {
|
|
349
|
+
try {
|
|
350
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(session));
|
|
351
|
+
return true;
|
|
352
|
+
}
|
|
353
|
+
catch (_a) {
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
export var downloadColourSession = function (session) {
|
|
358
|
+
var url = URL.createObjectURL(new Blob([JSON.stringify(session, null, 2)], { type: "application/json" }));
|
|
359
|
+
var anchor = document.createElement("a");
|
|
360
|
+
anchor.href = url;
|
|
361
|
+
anchor.download = "marty-colours-".concat(session.sessionId, ".json");
|
|
362
|
+
anchor.click();
|
|
363
|
+
setTimeout(function () { return URL.revokeObjectURL(url); }, 1000);
|
|
364
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
11
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
12
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
13
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
14
|
+
function step(op) {
|
|
15
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
16
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
17
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
18
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
19
|
+
switch (op[0]) {
|
|
20
|
+
case 0: case 1: t = op; break;
|
|
21
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
22
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
23
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
24
|
+
default:
|
|
25
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
26
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
27
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
28
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
29
|
+
if (t[2]) _.ops.pop();
|
|
30
|
+
_.trys.pop(); continue;
|
|
31
|
+
}
|
|
32
|
+
op = body.call(thisArg, _);
|
|
33
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
34
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
// Raw add-on commands have one response slot. Share ownership across all app workflows.
|
|
38
|
+
var owners = new WeakMap();
|
|
39
|
+
export var lockColourSensorOperation = function (raft, background) {
|
|
40
|
+
if (background === void 0) { background = false; }
|
|
41
|
+
if (owners.has(raft))
|
|
42
|
+
return null;
|
|
43
|
+
owners.set(raft, { background: background });
|
|
44
|
+
return function () { owners.delete(raft); };
|
|
45
|
+
};
|
|
46
|
+
export var waitForColourProfileRead = function (raft, check) { return __awaiter(void 0, void 0, void 0, function () {
|
|
47
|
+
var _a;
|
|
48
|
+
return __generator(this, function (_b) {
|
|
49
|
+
switch (_b.label) {
|
|
50
|
+
case 0:
|
|
51
|
+
if (!((_a = owners.get(raft)) === null || _a === void 0 ? void 0 : _a.background)) return [3 /*break*/, 2];
|
|
52
|
+
check();
|
|
53
|
+
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 100); })];
|
|
54
|
+
case 1:
|
|
55
|
+
_b.sent();
|
|
56
|
+
return [3 /*break*/, 0];
|
|
57
|
+
case 2:
|
|
58
|
+
check();
|
|
59
|
+
return [2 /*return*/];
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}); };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ColourSensor } from "./colour-sensor-diagnostics";
|
|
2
|
+
import { ColourSignalProfile } from "./colour-classifier";
|
|
3
|
+
export type ColourSensorProfile = {
|
|
4
|
+
schemaVersion: 1;
|
|
5
|
+
serialNumber: string;
|
|
6
|
+
typeCode: string;
|
|
7
|
+
firmwareVersion: string;
|
|
8
|
+
offsetsHex: string;
|
|
9
|
+
whiteCalibrationHex: string;
|
|
10
|
+
capturedAtMs: number;
|
|
11
|
+
signal: ColourSignalProfile;
|
|
12
|
+
purpleIncluded: boolean;
|
|
13
|
+
groundDetection: "clearThreshold" | "rgbFallback" | "purpleSkipped";
|
|
14
|
+
};
|
|
15
|
+
export declare const validColourSensorProfile: (value: unknown) => value is ColourSensorProfile;
|
|
16
|
+
export declare const cacheColourSensorProfile: (profile: ColourSensorProfile) => void;
|
|
17
|
+
export declare const colourSensorProfilePath: (profile: ColourSensorProfile) => string;
|
|
18
|
+
export declare const bindColourSensorProfile: (sensor: ColourSensor, profile: ColourSensorProfile) => void;
|
|
19
|
+
export declare const getActiveColourSensorProfile: (sensor: Pick<ColourSensor, "raft" | "key">) => ColourSensorProfile | undefined;
|
|
20
|
+
export declare const getAddonColourSensorProfile: (raft: object, addon: {
|
|
21
|
+
id: number;
|
|
22
|
+
name: string;
|
|
23
|
+
}) => ColourSensorProfile | undefined;
|
|
24
|
+
export declare const clearActiveColourSensorProfile: (sensor: Pick<ColourSensor, "raft" | "key">) => boolean | undefined;
|
|
25
|
+
export declare const classifyColourAddon: (addon: {
|
|
26
|
+
id: number;
|
|
27
|
+
name: string;
|
|
28
|
+
vals: Record<string, number | boolean>;
|
|
29
|
+
}) => import("./colour-classifier").ColourName;
|
|
30
|
+
export declare const loadColourSensorProfile: (sensor: ColourSensor, signal: AbortSignal) => Promise<void>;
|
|
31
|
+
/** Read-only browser/WebView hydration. Never wakes sleeping sensors or modifies their registers. */
|
|
32
|
+
export declare const startColourSensorProfileSync: () => () => void;
|