@7365admin1/core 3.82.0 → 3.84.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.
- package/CHANGELOG.md +53 -0
- package/dist/index.d.ts +462 -1
- package/dist/index.js +476 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +451 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/test/building-map.test.mjs +305 -0
- package/test/robot-adapter.test.mjs +179 -0
package/dist/index.js
CHANGED
|
@@ -77,6 +77,7 @@ __export(src_exports, {
|
|
|
77
77
|
ConsoleAuditAction: () => ConsoleAuditAction,
|
|
78
78
|
ConsoleAuditTarget: () => ConsoleAuditTarget,
|
|
79
79
|
DEFAULT_RING_SECONDS: () => DEFAULT_RING_SECONDS,
|
|
80
|
+
DEFAULT_SCALE_M_PER_PX: () => DEFAULT_SCALE_M_PER_PX,
|
|
80
81
|
DEFAULT_SITE_TIMEZONE: () => DEFAULT_SITE_TIMEZONE,
|
|
81
82
|
DEVICE_STATUS: () => DEVICE_STATUS,
|
|
82
83
|
DOBStatus: () => DOBStatus,
|
|
@@ -215,6 +216,8 @@ __export(src_exports, {
|
|
|
215
216
|
ORG_OWNER_REPAIR_CLAIM_TAKEN_REASON: () => ORG_OWNER_REPAIR_CLAIM_TAKEN_REASON,
|
|
216
217
|
ORG_OWNER_REPAIR_DEFAULT: () => ORG_OWNER_REPAIR_DEFAULT,
|
|
217
218
|
ORG_OWNER_REPAIR_OFF_REASON: () => ORG_OWNER_REPAIR_OFF_REASON,
|
|
219
|
+
OUNITECH_BRAND: () => OUNITECH_BRAND,
|
|
220
|
+
OUNITECH_CAPABILITIES: () => OUNITECH_CAPABILITIES,
|
|
218
221
|
OrgNature: () => OrgNature,
|
|
219
222
|
OvernightParkingRequestSort: () => OvernightParkingRequestSort,
|
|
220
223
|
OvernightParkingRequestStatus: () => OvernightParkingRequestStatus,
|
|
@@ -242,6 +245,7 @@ __export(src_exports, {
|
|
|
242
245
|
REALTIME_MAX_FANOUT: () => REALTIME_MAX_FANOUT,
|
|
243
246
|
REGISTERED_VEHICLE_CATEGORIES: () => REGISTERED_VEHICLE_CATEGORIES,
|
|
244
247
|
ROBOT_APP_SCOPES: () => ROBOT_APP_SCOPES,
|
|
248
|
+
ROBOT_CAPABILITIES: () => ROBOT_CAPABILITIES,
|
|
245
249
|
ROBOT_CLOUD_DOC_CONFIRMATIONS: () => ROBOT_CLOUD_DOC_CONFIRMATIONS,
|
|
246
250
|
ROBOT_CLOUD_PATHS: () => ROBOT_CLOUD_PATHS,
|
|
247
251
|
ROBOT_MODULE: () => ROBOT_MODULE,
|
|
@@ -293,6 +297,8 @@ __export(src_exports, {
|
|
|
293
297
|
VerificationType: () => VerificationType,
|
|
294
298
|
VisitorSort: () => VisitorSort,
|
|
295
299
|
VisitorStatus: () => VisitorStatus,
|
|
300
|
+
WALL_HEIGHT_M: () => WALL_HEIGHT_M,
|
|
301
|
+
WALL_THICKNESS_M: () => WALL_THICKNESS_M,
|
|
296
302
|
addressSchema: () => addressSchema,
|
|
297
303
|
allowedFieldsSite: () => allowedFieldsSite,
|
|
298
304
|
allowedNatures: () => allowedNatures,
|
|
@@ -304,6 +310,8 @@ __export(src_exports, {
|
|
|
304
310
|
attendanceSchema: () => attendanceSchema,
|
|
305
311
|
attendanceSettingsSchema: () => attendanceSettingsSchema,
|
|
306
312
|
buildSelfServiceEmailContext: () => buildSelfServiceEmailContext,
|
|
313
|
+
buildingMapFromRobotMaps: () => buildingMapFromRobotMaps,
|
|
314
|
+
buildingSolids: () => buildingSolids,
|
|
307
315
|
building_level_namespace_collection: () => building_level_namespace_collection,
|
|
308
316
|
building_units_namespace_collection: () => building_units_namespace_collection,
|
|
309
317
|
buildings_namespace_collection: () => buildings_namespace_collection,
|
|
@@ -330,13 +338,16 @@ __export(src_exports, {
|
|
|
330
338
|
chatSchema: () => chatSchema,
|
|
331
339
|
claimReadsAsStale: () => claimReadsAsStale,
|
|
332
340
|
clampPtzSpeed: () => clampPtzSpeed,
|
|
341
|
+
clearRobotAdapters: () => clearRobotAdapters,
|
|
333
342
|
clockDriftSeconds: () => clockDriftSeconds,
|
|
343
|
+
commandSupported: () => commandSupported,
|
|
334
344
|
consoleRoleAllows: () => consoleRoleAllows,
|
|
335
345
|
consoleRoleAllowsAll: () => consoleRoleAllowsAll,
|
|
336
346
|
consoleSpellings: () => consoleSpellings,
|
|
337
347
|
console_audit_namespace_collection: () => console_audit_namespace_collection,
|
|
338
348
|
createLiftSimulator: () => createLiftSimulator,
|
|
339
349
|
createManpowerRemarksDaily: () => createManpowerRemarksDaily,
|
|
350
|
+
createOunitechAdapter: () => createOunitechAdapter,
|
|
340
351
|
createSseParser: () => createSseParser,
|
|
341
352
|
customerSchema: () => customerSchema,
|
|
342
353
|
customerSitePropertyFields: () => customerSitePropertyFields,
|
|
@@ -407,6 +418,9 @@ __export(src_exports, {
|
|
|
407
418
|
isTermsCurrent: () => isTermsCurrent,
|
|
408
419
|
isTrackingStale: () => isTrackingStale,
|
|
409
420
|
isValidTimezone: () => isValidTimezone,
|
|
421
|
+
levelPixelToMetres: () => levelPixelToMetres,
|
|
422
|
+
levelScale: () => levelScale,
|
|
423
|
+
levelSolids: () => levelSolids,
|
|
410
424
|
listStaffUserIds: () => listStaffUserIds,
|
|
411
425
|
liveStatusFor: () => liveStatusFor,
|
|
412
426
|
manpowerDesignationsSchema: () => manpowerDesignationsSchema,
|
|
@@ -418,6 +432,7 @@ __export(src_exports, {
|
|
|
418
432
|
maskNric: () => maskNric,
|
|
419
433
|
memberModulesHandlers: () => memberModulesHandlers,
|
|
420
434
|
metresToPixel: () => metresToPixel,
|
|
435
|
+
missingForTier: () => missingForTier,
|
|
421
436
|
moduleDeniedForCaller: () => moduleDeniedForCaller,
|
|
422
437
|
moduleGateEnforceKeys: () => moduleGateEnforceKeys,
|
|
423
438
|
moduleGateMode: () => moduleGateMode,
|
|
@@ -472,6 +487,9 @@ __export(src_exports, {
|
|
|
472
487
|
recordConsoleAction: () => recordConsoleAction,
|
|
473
488
|
refuseServiceProviderInviteAction: () => refuseServiceProviderInviteAction,
|
|
474
489
|
registerCameraTransport: () => registerCameraTransport,
|
|
490
|
+
registerOunitechBrand: () => registerOunitechBrand,
|
|
491
|
+
registerRobotAdapter: () => registerRobotAdapter,
|
|
492
|
+
registeredRobotBrands: () => registeredRobotBrands,
|
|
475
493
|
relayForRecorder: () => relayForRecorder,
|
|
476
494
|
remarksSchema: () => remarksSchema,
|
|
477
495
|
renderPagePdf: () => renderPagePdf,
|
|
@@ -489,6 +507,7 @@ __export(src_exports, {
|
|
|
489
507
|
resolveSiteTimezone: () => resolveSiteTimezone,
|
|
490
508
|
resolveStaffRole: () => resolveStaffRole,
|
|
491
509
|
robotActionsFor: () => robotActionsFor,
|
|
510
|
+
robotAdapterFor: () => robotAdapterFor,
|
|
492
511
|
robotCloudBody: () => robotCloudBody,
|
|
493
512
|
robotCloudSign: () => robotCloudSign,
|
|
494
513
|
robotCloudSigningString: () => robotCloudSigningString,
|
|
@@ -497,8 +516,10 @@ __export(src_exports, {
|
|
|
497
516
|
robotModeFromList: () => robotModeFromList,
|
|
498
517
|
robotSchema: () => robotSchema,
|
|
499
518
|
robotScopeForApp: () => robotScopeForApp,
|
|
519
|
+
robotTier: () => robotTier,
|
|
500
520
|
robotTypesVisibleTo: () => robotTypesVisibleTo,
|
|
501
521
|
rtspUrl: () => rtspUrl,
|
|
522
|
+
scaleFromMeasurement: () => scaleFromMeasurement,
|
|
502
523
|
schema: () => schema,
|
|
503
524
|
schemaAppSlugNotification: () => schemaAppSlugNotification,
|
|
504
525
|
schemaApprovedBy: () => schemaApprovedBy,
|
|
@@ -670,7 +691,9 @@ __export(src_exports, {
|
|
|
670
691
|
stripFacialImageMetadata: () => stripFacialImageMetadata,
|
|
671
692
|
subscriptionPlanSchema: () => subscriptionPlanSchema,
|
|
672
693
|
summariseBulkCameraPlan: () => summariseBulkCameraPlan,
|
|
694
|
+
supports: () => supports,
|
|
673
695
|
trackingBackoffMs: () => trackingBackoffMs,
|
|
696
|
+
uncontrollableFleetWarning: () => uncontrollableFleetWarning,
|
|
674
697
|
updateRemarksStatusEod: () => updateRemarksStatusEod,
|
|
675
698
|
updateRemarksisAcknowledged: () => updateRemarksisAcknowledged,
|
|
676
699
|
updateSiteSchema: () => updateSiteSchema,
|
|
@@ -914,8 +937,10 @@ __export(src_exports, {
|
|
|
914
937
|
vehicleSchema: () => vehicleSchema,
|
|
915
938
|
vehicleViewOf: () => vehicleViewOf,
|
|
916
939
|
vehicles_namespace_collection: () => vehicles_namespace_collection,
|
|
940
|
+
vertexDistancePx: () => vertexDistancePx,
|
|
917
941
|
visitors_namespace_collection: () => visitors_namespace_collection,
|
|
918
942
|
wallConfig: () => wallConfig,
|
|
943
|
+
wallsFromOccupancyGrid: () => wallsFromOccupancyGrid,
|
|
919
944
|
withLiveLink: () => withLiveLink,
|
|
920
945
|
workOrderSchema: () => workOrderSchema,
|
|
921
946
|
work_orders2_namespace_collection: () => work_orders2_namespace_collection,
|
|
@@ -59003,6 +59028,7 @@ var robotSchema = import_joi74.default.object({
|
|
|
59003
59028
|
// Optional, so every caller written before this field existed still validates.
|
|
59004
59029
|
type: import_joi74.default.string().valid(...ROBOT_TYPES).optional().allow("", null),
|
|
59005
59030
|
robotSn: import_joi74.default.string().optional().allow("", null),
|
|
59031
|
+
brand: import_joi74.default.string().optional().allow("", null),
|
|
59006
59032
|
appKey: import_joi74.default.string().required(),
|
|
59007
59033
|
appToken: import_joi74.default.string().required(),
|
|
59008
59034
|
createdBy: import_joi74.default.string().hex().required(),
|
|
@@ -59064,6 +59090,7 @@ function MRobot(value) {
|
|
|
59064
59090
|
*/
|
|
59065
59091
|
type: value.type || "cleaning",
|
|
59066
59092
|
robotSn: value.robotSn ?? "",
|
|
59093
|
+
brand: value.brand || "ounitech",
|
|
59067
59094
|
appKey: value.appKey ?? "",
|
|
59068
59095
|
appToken: value.appToken ?? "",
|
|
59069
59096
|
createdBy: value.createdBy ?? "",
|
|
@@ -59218,6 +59245,9 @@ function useRobotRepo() {
|
|
|
59218
59245
|
// Not a secret - it is the vendor's device serial, and the apps
|
|
59219
59246
|
// need it to address a robot. The CREDENTIALS stay out.
|
|
59220
59247
|
robotSn: 1,
|
|
59248
|
+
// Not a secret - it names which protocol the robot speaks, and a
|
|
59249
|
+
// screen needs it to know which controls to draw.
|
|
59250
|
+
brand: 1,
|
|
59221
59251
|
createdByName: 1,
|
|
59222
59252
|
createdAt: 1,
|
|
59223
59253
|
status: 1
|
|
@@ -59611,30 +59641,30 @@ function parseRobotCommand(body) {
|
|
|
59611
59641
|
if (kind !== "full" && kind !== "area" && kind !== "spot" && kind !== "return") {
|
|
59612
59642
|
return { ok: false, message: 'kind must be one of "full", "area", "spot", "return".' };
|
|
59613
59643
|
}
|
|
59614
|
-
const
|
|
59644
|
+
const has2 = (k) => Object.prototype.hasOwnProperty.call(b, k);
|
|
59615
59645
|
const optionKeys = ["cleanTimes", "modeFlag", "subModeFlag", "levelFlag"];
|
|
59616
59646
|
if (kind === "return") {
|
|
59617
59647
|
for (const k of [...optionKeys, "areas", "points"]) {
|
|
59618
|
-
if (
|
|
59648
|
+
if (has2(k))
|
|
59619
59649
|
return { ok: false, message: `${k} is not valid for a return command.` };
|
|
59620
59650
|
}
|
|
59621
59651
|
return { ok: true, command: { kind: "return" } };
|
|
59622
59652
|
}
|
|
59623
|
-
if (kind !== "area" &&
|
|
59653
|
+
if (kind !== "area" && has2("areas")) {
|
|
59624
59654
|
return { ok: false, message: "areas is only valid for an area command." };
|
|
59625
59655
|
}
|
|
59626
|
-
if (kind !== "spot" &&
|
|
59656
|
+
if (kind !== "spot" && has2("points")) {
|
|
59627
59657
|
return { ok: false, message: "points is only valid for a spot command." };
|
|
59628
59658
|
}
|
|
59629
59659
|
const int = (k, allowed, fallback) => {
|
|
59630
|
-
if (!
|
|
59660
|
+
if (!has2(k))
|
|
59631
59661
|
return fallback;
|
|
59632
59662
|
const v = b[k];
|
|
59633
59663
|
if (typeof v !== "number" || !Number.isInteger(v) || !allowed.includes(v))
|
|
59634
59664
|
return null;
|
|
59635
59665
|
return v;
|
|
59636
59666
|
};
|
|
59637
|
-
const cleanTimes =
|
|
59667
|
+
const cleanTimes = has2("cleanTimes") ? typeof b.cleanTimes === "number" && Number.isInteger(b.cleanTimes) && b.cleanTimes >= 0 && b.cleanTimes <= 99 ? b.cleanTimes : null : DEFAULTS.cleanTimes;
|
|
59638
59668
|
if (cleanTimes === null)
|
|
59639
59669
|
return { ok: false, message: "cleanTimes must be an integer 0-99." };
|
|
59640
59670
|
const modeFlag = int("modeFlag", [1, 2, 3], DEFAULTS.modeFlag);
|
|
@@ -59850,6 +59880,125 @@ function robotActionsFor(actor) {
|
|
|
59850
59880
|
);
|
|
59851
59881
|
}
|
|
59852
59882
|
|
|
59883
|
+
// src/utils/robot-adapter.util.ts
|
|
59884
|
+
var ROBOT_CAPABILITIES = [
|
|
59885
|
+
/** Read the current location of the robot [x, y, yaw]. */
|
|
59886
|
+
"readLocation",
|
|
59887
|
+
/** Request the robot move to an [x, y, yaw] coordinate. */
|
|
59888
|
+
"navigateToPoint",
|
|
59889
|
+
/** Pause a robot while it is navigating, and resume it. */
|
|
59890
|
+
"pauseResume",
|
|
59891
|
+
/** Read the route/path taken, or the ETA to the destination. */
|
|
59892
|
+
"pathAndEta",
|
|
59893
|
+
/** Read the battery level. */
|
|
59894
|
+
"battery",
|
|
59895
|
+
/** Send the robot to its docking/charging station. */
|
|
59896
|
+
"sendToDock",
|
|
59897
|
+
/** Switch the on-board map and re-localise the robot. */
|
|
59898
|
+
"relocalize",
|
|
59899
|
+
/** Start a process — "clean zone A". */
|
|
59900
|
+
"startProcess",
|
|
59901
|
+
/** Pause, resume or stop a running process. */
|
|
59902
|
+
"pauseProcess",
|
|
59903
|
+
/** Know, rather than infer, when a process has finished. */
|
|
59904
|
+
"processCompletion",
|
|
59905
|
+
/** Report which floor the robot is on. */
|
|
59906
|
+
"floorReporting",
|
|
59907
|
+
/** Ride a lift between floors under our coordination. */
|
|
59908
|
+
"multiFloor"
|
|
59909
|
+
];
|
|
59910
|
+
var TIER_REQUIREMENTS = {
|
|
59911
|
+
"full-control": [
|
|
59912
|
+
"readLocation",
|
|
59913
|
+
"navigateToPoint",
|
|
59914
|
+
"pauseResume",
|
|
59915
|
+
"pathAndEta",
|
|
59916
|
+
"battery",
|
|
59917
|
+
"sendToDock",
|
|
59918
|
+
"relocalize",
|
|
59919
|
+
"startProcess",
|
|
59920
|
+
"pauseProcess",
|
|
59921
|
+
"processCompletion"
|
|
59922
|
+
],
|
|
59923
|
+
"traffic-light": [
|
|
59924
|
+
"readLocation",
|
|
59925
|
+
"pauseResume",
|
|
59926
|
+
"battery",
|
|
59927
|
+
"sendToDock",
|
|
59928
|
+
"startProcess",
|
|
59929
|
+
"pauseProcess",
|
|
59930
|
+
"processCompletion"
|
|
59931
|
+
],
|
|
59932
|
+
"read-only": ["readLocation", "pathAndEta", "battery", "processCompletion"]
|
|
59933
|
+
};
|
|
59934
|
+
var has = (caps, c) => Object.prototype.hasOwnProperty.call(caps, c) && caps[c] === true;
|
|
59935
|
+
function robotTier(capabilities) {
|
|
59936
|
+
const caps = capabilities ?? {};
|
|
59937
|
+
for (const tier of ["full-control", "traffic-light", "read-only"]) {
|
|
59938
|
+
if (TIER_REQUIREMENTS[tier].every((c) => has(caps, c)))
|
|
59939
|
+
return tier;
|
|
59940
|
+
}
|
|
59941
|
+
return "no-interface";
|
|
59942
|
+
}
|
|
59943
|
+
function missingForTier(capabilities, tier) {
|
|
59944
|
+
const caps = capabilities ?? {};
|
|
59945
|
+
return TIER_REQUIREMENTS[tier].filter((c) => !has(caps, c));
|
|
59946
|
+
}
|
|
59947
|
+
function supports(capabilities, capability) {
|
|
59948
|
+
return has(capabilities ?? {}, capability);
|
|
59949
|
+
}
|
|
59950
|
+
function uncontrollableFleetWarning(tiers) {
|
|
59951
|
+
const count = tiers.filter(
|
|
59952
|
+
(t) => t === "read-only" || t === "no-interface"
|
|
59953
|
+
).length;
|
|
59954
|
+
if (count <= 1) {
|
|
59955
|
+
return { ok: true, count, message: "" };
|
|
59956
|
+
}
|
|
59957
|
+
return {
|
|
59958
|
+
ok: false,
|
|
59959
|
+
count,
|
|
59960
|
+
message: `This site has ${count} robot fleets that cannot be controlled (read-only or no interface). A shared space supports at most one: beyond that, robots cannot reliably be kept out of each other's way.`
|
|
59961
|
+
};
|
|
59962
|
+
}
|
|
59963
|
+
var COMMAND_NEEDS = {
|
|
59964
|
+
full: "startProcess",
|
|
59965
|
+
area: "startProcess",
|
|
59966
|
+
spot: "startProcess",
|
|
59967
|
+
return: "sendToDock",
|
|
59968
|
+
pause: "pauseProcess",
|
|
59969
|
+
resume: "pauseProcess",
|
|
59970
|
+
cancel: "pauseProcess",
|
|
59971
|
+
navigate: "navigateToPoint",
|
|
59972
|
+
relocalize: "relocalize"
|
|
59973
|
+
};
|
|
59974
|
+
function commandSupported(capabilities, kind) {
|
|
59975
|
+
const needed = Object.prototype.hasOwnProperty.call(COMMAND_NEEDS, kind) ? COMMAND_NEEDS[kind] : void 0;
|
|
59976
|
+
if (!needed)
|
|
59977
|
+
return false;
|
|
59978
|
+
return supports(capabilities, needed);
|
|
59979
|
+
}
|
|
59980
|
+
var adapters = /* @__PURE__ */ new Map();
|
|
59981
|
+
function registerRobotAdapter(adapter) {
|
|
59982
|
+
if (!adapter?.brand)
|
|
59983
|
+
throw new Error("A robot adapter must name its brand.");
|
|
59984
|
+
adapters.set(adapter.brand, adapter);
|
|
59985
|
+
}
|
|
59986
|
+
function robotAdapterFor(brand) {
|
|
59987
|
+
if (typeof brand !== "string" || !brand)
|
|
59988
|
+
return null;
|
|
59989
|
+
return adapters.get(brand) ?? null;
|
|
59990
|
+
}
|
|
59991
|
+
function registeredRobotBrands() {
|
|
59992
|
+
return [...adapters.values()].map((a) => ({
|
|
59993
|
+
brand: a.brand,
|
|
59994
|
+
displayName: a.displayName,
|
|
59995
|
+
tier: robotTier(a.capabilities)
|
|
59996
|
+
}));
|
|
59997
|
+
}
|
|
59998
|
+
function clearRobotAdapters() {
|
|
59999
|
+
adapters.clear();
|
|
60000
|
+
}
|
|
60001
|
+
|
|
59853
60002
|
// src/utils/lift-contract.util.ts
|
|
59854
60003
|
var LIFT_DOOR = {
|
|
59855
60004
|
CLOSED: 0,
|
|
@@ -59989,6 +60138,179 @@ function createLiftSimulator(options) {
|
|
|
59989
60138
|
};
|
|
59990
60139
|
}
|
|
59991
60140
|
|
|
60141
|
+
// src/utils/building-map.util.ts
|
|
60142
|
+
var WALL_HEIGHT_M = 2.5;
|
|
60143
|
+
var WALL_THICKNESS_M = 0.1;
|
|
60144
|
+
var DEFAULT_SCALE_M_PER_PX = 0.05;
|
|
60145
|
+
function scaleFromMeasurement(pixelDistance, realDistanceM) {
|
|
60146
|
+
if (!Number.isFinite(pixelDistance) || pixelDistance <= 0) {
|
|
60147
|
+
throw new Error("A measurement needs a positive pixel distance.");
|
|
60148
|
+
}
|
|
60149
|
+
if (!Number.isFinite(realDistanceM) || realDistanceM <= 0) {
|
|
60150
|
+
throw new Error("A measurement needs a positive real distance in metres.");
|
|
60151
|
+
}
|
|
60152
|
+
return realDistanceM / pixelDistance;
|
|
60153
|
+
}
|
|
60154
|
+
function vertexDistancePx(a, b) {
|
|
60155
|
+
return Math.hypot(b.x - a.x, b.y - a.y);
|
|
60156
|
+
}
|
|
60157
|
+
function levelScale(level) {
|
|
60158
|
+
const measurement = level.measurements?.find((m) => {
|
|
60159
|
+
const a = level.vertices[m.start];
|
|
60160
|
+
const b = level.vertices[m.end];
|
|
60161
|
+
return a && b && m.distanceM > 0 && vertexDistancePx(a, b) > 0;
|
|
60162
|
+
});
|
|
60163
|
+
if (measurement) {
|
|
60164
|
+
return scaleFromMeasurement(
|
|
60165
|
+
vertexDistancePx(level.vertices[measurement.start], level.vertices[measurement.end]),
|
|
60166
|
+
measurement.distanceM
|
|
60167
|
+
);
|
|
60168
|
+
}
|
|
60169
|
+
return level.scaleMPerPx > 0 ? level.scaleMPerPx : DEFAULT_SCALE_M_PER_PX;
|
|
60170
|
+
}
|
|
60171
|
+
function levelPixelToMetres(level, px, py, imageHeightPx) {
|
|
60172
|
+
const scale = levelScale(level);
|
|
60173
|
+
return {
|
|
60174
|
+
x: level.originXM + px * scale,
|
|
60175
|
+
y: level.originYM + (imageHeightPx - py) * scale
|
|
60176
|
+
};
|
|
60177
|
+
}
|
|
60178
|
+
function levelSolids(level, imageHeightPx) {
|
|
60179
|
+
const toM = (v) => levelPixelToMetres(level, v.x, v.y, imageHeightPx);
|
|
60180
|
+
const walls = [];
|
|
60181
|
+
for (const wall of level.walls ?? []) {
|
|
60182
|
+
const a = level.vertices[wall.start];
|
|
60183
|
+
const b = level.vertices[wall.end];
|
|
60184
|
+
if (!a || !b)
|
|
60185
|
+
continue;
|
|
60186
|
+
const pa = toM(a);
|
|
60187
|
+
const pb = toM(b);
|
|
60188
|
+
const lengthM = Math.hypot(pb.x - pa.x, pb.y - pa.y);
|
|
60189
|
+
if (lengthM <= 0)
|
|
60190
|
+
continue;
|
|
60191
|
+
const heightM = wall.heightM ?? WALL_HEIGHT_M;
|
|
60192
|
+
walls.push({
|
|
60193
|
+
centre: {
|
|
60194
|
+
x: (pa.x + pb.x) / 2,
|
|
60195
|
+
y: (pa.y + pb.y) / 2,
|
|
60196
|
+
// Boxes are centred, so a wall standing ON the floor sits at half its
|
|
60197
|
+
// own height above the level's elevation.
|
|
60198
|
+
z: level.elevationM + heightM / 2
|
|
60199
|
+
},
|
|
60200
|
+
lengthM,
|
|
60201
|
+
thicknessM: wall.thicknessM ?? WALL_THICKNESS_M,
|
|
60202
|
+
heightM,
|
|
60203
|
+
yaw: Math.atan2(pb.y - pa.y, pb.x - pa.x)
|
|
60204
|
+
});
|
|
60205
|
+
}
|
|
60206
|
+
const floors = [];
|
|
60207
|
+
for (const floor of level.floors ?? []) {
|
|
60208
|
+
const points = (floor.vertices ?? []).map((i) => level.vertices[i]).filter(Boolean).map(toM);
|
|
60209
|
+
if (points.length < 3)
|
|
60210
|
+
continue;
|
|
60211
|
+
floors.push({ points, elevationM: level.elevationM, hole: floor.hole === true });
|
|
60212
|
+
}
|
|
60213
|
+
return { level: level.name, elevationM: level.elevationM, walls, floors };
|
|
60214
|
+
}
|
|
60215
|
+
function buildingSolids(map, imageHeightFor) {
|
|
60216
|
+
return (map.levels ?? []).map((level) => levelSolids(level, imageHeightFor(level)));
|
|
60217
|
+
}
|
|
60218
|
+
function wallsFromOccupancyGrid(grid, options) {
|
|
60219
|
+
const threshold = options?.threshold ?? 100;
|
|
60220
|
+
const minRun = Math.max(2, options?.minRunPx ?? 6);
|
|
60221
|
+
const { data, width, height } = grid;
|
|
60222
|
+
if (!width || !height || !data || data.length < width * height) {
|
|
60223
|
+
return { vertices: [], walls: [] };
|
|
60224
|
+
}
|
|
60225
|
+
const occupied = (x, y) => Number(data[y * width + x]) <= threshold;
|
|
60226
|
+
const consumed = new Uint8Array(width * height);
|
|
60227
|
+
const vertices = [];
|
|
60228
|
+
const walls = [];
|
|
60229
|
+
const index = /* @__PURE__ */ new Map();
|
|
60230
|
+
const vertexAt = (x, y) => {
|
|
60231
|
+
const key = `${x}:${y}`;
|
|
60232
|
+
const found = index.get(key);
|
|
60233
|
+
if (found !== void 0)
|
|
60234
|
+
return found;
|
|
60235
|
+
vertices.push({ x, y, z: 0 });
|
|
60236
|
+
index.set(key, vertices.length - 1);
|
|
60237
|
+
return vertices.length - 1;
|
|
60238
|
+
};
|
|
60239
|
+
const addRun = (x1, y1, x2, y2) => {
|
|
60240
|
+
walls.push({ start: vertexAt(x1, y1), end: vertexAt(x2, y2) });
|
|
60241
|
+
};
|
|
60242
|
+
for (let y = 0; y < height; y++) {
|
|
60243
|
+
let run = 0;
|
|
60244
|
+
for (let x = 0; x <= width; x++) {
|
|
60245
|
+
const on = x < width && occupied(x, y);
|
|
60246
|
+
if (on) {
|
|
60247
|
+
run++;
|
|
60248
|
+
continue;
|
|
60249
|
+
}
|
|
60250
|
+
if (run >= minRun) {
|
|
60251
|
+
const startX = x - run;
|
|
60252
|
+
addRun(startX, y, x - 1, y);
|
|
60253
|
+
for (let i = startX; i < x; i++)
|
|
60254
|
+
consumed[y * width + i] = 1;
|
|
60255
|
+
}
|
|
60256
|
+
run = 0;
|
|
60257
|
+
}
|
|
60258
|
+
}
|
|
60259
|
+
for (let x = 0; x < width; x++) {
|
|
60260
|
+
let run = 0;
|
|
60261
|
+
for (let y = 0; y <= height; y++) {
|
|
60262
|
+
const on = y < height && occupied(x, y) && !consumed[y * width + x];
|
|
60263
|
+
if (on) {
|
|
60264
|
+
run++;
|
|
60265
|
+
continue;
|
|
60266
|
+
}
|
|
60267
|
+
if (run >= minRun) {
|
|
60268
|
+
addRun(x, y - run, x, y - 1);
|
|
60269
|
+
}
|
|
60270
|
+
run = 0;
|
|
60271
|
+
}
|
|
60272
|
+
}
|
|
60273
|
+
return { vertices, walls };
|
|
60274
|
+
}
|
|
60275
|
+
function buildingMapFromRobotMaps(name, maps, floorHeightM = 3) {
|
|
60276
|
+
const levels = [...maps ?? []].sort((a, b) => a.floorNo - b.floorNo).map((map) => {
|
|
60277
|
+
const vertices = [];
|
|
60278
|
+
if (map.baseX || map.baseY) {
|
|
60279
|
+
vertices.push({
|
|
60280
|
+
x: map.baseX ?? 0,
|
|
60281
|
+
y: map.baseY ?? 0,
|
|
60282
|
+
z: 0,
|
|
60283
|
+
name: `L${map.floorNo}_charger`,
|
|
60284
|
+
isCharger: true,
|
|
60285
|
+
theta: map.baseTheta ?? 0
|
|
60286
|
+
});
|
|
60287
|
+
}
|
|
60288
|
+
for (const area of map.areaList ?? []) {
|
|
60289
|
+
vertices.push({
|
|
60290
|
+
x: area.x,
|
|
60291
|
+
y: area.y,
|
|
60292
|
+
z: 0,
|
|
60293
|
+
name: area.areaName,
|
|
60294
|
+
isCleaningZone: true
|
|
60295
|
+
});
|
|
60296
|
+
}
|
|
60297
|
+
return {
|
|
60298
|
+
name: map.mapName?.trim() || `L${map.floorNo}`,
|
|
60299
|
+
elevationM: (map.floorNo - 1) * floorHeightM,
|
|
60300
|
+
drawing: { fileKey: map.backgroundFileKey },
|
|
60301
|
+
scaleMPerPx: map.resolution && map.resolution > 0 ? map.resolution : DEFAULT_SCALE_M_PER_PX,
|
|
60302
|
+
originXM: map.originX ?? 0,
|
|
60303
|
+
originYM: map.originY ?? 0,
|
|
60304
|
+
vertices,
|
|
60305
|
+
walls: [],
|
|
60306
|
+
floors: [],
|
|
60307
|
+
doors: [],
|
|
60308
|
+
measurements: []
|
|
60309
|
+
};
|
|
60310
|
+
});
|
|
60311
|
+
return { name, levels, lifts: [] };
|
|
60312
|
+
}
|
|
60313
|
+
|
|
59992
60314
|
// src/services/robot-fleet.service.ts
|
|
59993
60315
|
var import_node_server_utils157 = require("@7365admin1/node-server-utils");
|
|
59994
60316
|
|
|
@@ -60246,6 +60568,129 @@ function useRobotFleetService(deps) {
|
|
|
60246
60568
|
};
|
|
60247
60569
|
}
|
|
60248
60570
|
|
|
60571
|
+
// src/services/ounitech.adapter.ts
|
|
60572
|
+
var OUNITECH_BRAND = "ounitech";
|
|
60573
|
+
var OUNITECH_CAPABILITIES = {
|
|
60574
|
+
// The tracking stream carries x, y, theta.
|
|
60575
|
+
readLocation: true,
|
|
60576
|
+
// `/portal/robots/return-to-base`.
|
|
60577
|
+
sendToDock: true,
|
|
60578
|
+
// `start-full-cleaning`, `start-area-cleaning`, `start-spot-cleaning` — RMF's
|
|
60579
|
+
// "start a process (such as clean Zone_A)", and their areas are RMF's zones.
|
|
60580
|
+
startProcess: true,
|
|
60581
|
+
// Everything below is FALSE on purpose, and each one is a question already put
|
|
60582
|
+
// to the vendor. Their app can do most of it; the API cannot.
|
|
60583
|
+
navigateToPoint: false,
|
|
60584
|
+
// app: "Move to Target Point in standby mode"
|
|
60585
|
+
relocalize: false,
|
|
60586
|
+
// app: "Set Origin"
|
|
60587
|
+
pauseResume: false,
|
|
60588
|
+
// handlebar + app: Pause/Resume, long-press cancel
|
|
60589
|
+
pauseProcess: false,
|
|
60590
|
+
// app: "End work"
|
|
60591
|
+
battery: false,
|
|
60592
|
+
// nothing in the API carries it
|
|
60593
|
+
pathAndEta: false,
|
|
60594
|
+
// nothing in the API carries it
|
|
60595
|
+
processCompletion: false,
|
|
60596
|
+
// no task id is returned, so completion is inferred
|
|
60597
|
+
floorReporting: false,
|
|
60598
|
+
// the tracking stream has no floorNo
|
|
60599
|
+
multiFloor: false
|
|
60600
|
+
// their app does cross-floor; the API does not expose it
|
|
60601
|
+
};
|
|
60602
|
+
function createOunitechAdapter(credentials, options) {
|
|
60603
|
+
const cloud = useRobotCloudService(credentials, options);
|
|
60604
|
+
function toAdapterRobot(raw) {
|
|
60605
|
+
return {
|
|
60606
|
+
robotSn: raw.robotSn,
|
|
60607
|
+
name: raw.robotName,
|
|
60608
|
+
online: raw.onlineFlag !== false,
|
|
60609
|
+
mode: robotModeFromList(raw),
|
|
60610
|
+
statusName: raw.statusName,
|
|
60611
|
+
// Null, not 0 or a guess. A screen must be able to tell "we do not know"
|
|
60612
|
+
// from "empty", and `floorKnown: false` is what stops a robot being drawn
|
|
60613
|
+
// on a storey nobody confirmed.
|
|
60614
|
+
batteryPercent: null,
|
|
60615
|
+
floorNo: null,
|
|
60616
|
+
floorKnown: false,
|
|
60617
|
+
position: null
|
|
60618
|
+
};
|
|
60619
|
+
}
|
|
60620
|
+
return {
|
|
60621
|
+
brand: OUNITECH_BRAND,
|
|
60622
|
+
displayName: "Ounitech (Q3-G / Q3-W)",
|
|
60623
|
+
capabilities: OUNITECH_CAPABILITIES,
|
|
60624
|
+
async listRobots() {
|
|
60625
|
+
const fleet = await cloud.getRobots();
|
|
60626
|
+
return fleet.map(toAdapterRobot);
|
|
60627
|
+
},
|
|
60628
|
+
async getRobot(robotSn) {
|
|
60629
|
+
const fleet = await cloud.getRobots();
|
|
60630
|
+
const match = fleet.find((r) => r.robotSn === robotSn);
|
|
60631
|
+
if (!match) {
|
|
60632
|
+
return {
|
|
60633
|
+
robotSn,
|
|
60634
|
+
online: false,
|
|
60635
|
+
mode: "UNAVAILABLE",
|
|
60636
|
+
statusName: "Not found in the robot cloud",
|
|
60637
|
+
batteryPercent: null,
|
|
60638
|
+
floorNo: null,
|
|
60639
|
+
floorKnown: false,
|
|
60640
|
+
position: null
|
|
60641
|
+
};
|
|
60642
|
+
}
|
|
60643
|
+
return toAdapterRobot(match);
|
|
60644
|
+
},
|
|
60645
|
+
async sendCommand(robotSn, command) {
|
|
60646
|
+
const options2 = {
|
|
60647
|
+
cleanTimes: 1,
|
|
60648
|
+
modeFlag: 1,
|
|
60649
|
+
subModeFlag: 0,
|
|
60650
|
+
levelFlag: 1
|
|
60651
|
+
};
|
|
60652
|
+
switch (command.kind) {
|
|
60653
|
+
case "full":
|
|
60654
|
+
await cloud.startFullCleaning(robotSn, options2);
|
|
60655
|
+
return;
|
|
60656
|
+
case "area":
|
|
60657
|
+
await cloud.startAreaCleaning(robotSn, command.areas, options2);
|
|
60658
|
+
return;
|
|
60659
|
+
case "spot":
|
|
60660
|
+
await cloud.startSpotCleaning(robotSn, command.points, options2);
|
|
60661
|
+
return;
|
|
60662
|
+
case "return":
|
|
60663
|
+
await cloud.returnToBase(robotSn);
|
|
60664
|
+
return;
|
|
60665
|
+
default:
|
|
60666
|
+
throw new Error(
|
|
60667
|
+
`Ounitech robots do not support "${command.kind}" through the API yet.`
|
|
60668
|
+
);
|
|
60669
|
+
}
|
|
60670
|
+
},
|
|
60671
|
+
async getMaps(robotSn) {
|
|
60672
|
+
return cloud.getMaps(robotSn);
|
|
60673
|
+
},
|
|
60674
|
+
async getPlans(robotSn) {
|
|
60675
|
+
return cloud.getPlans(robotSn);
|
|
60676
|
+
}
|
|
60677
|
+
};
|
|
60678
|
+
}
|
|
60679
|
+
function registerOunitechBrand() {
|
|
60680
|
+
const unconfigured = () => {
|
|
60681
|
+
throw new Error(
|
|
60682
|
+
"No Ounitech credentials for this robot. Add the app key and token on the robot's record."
|
|
60683
|
+
);
|
|
60684
|
+
};
|
|
60685
|
+
registerRobotAdapter({
|
|
60686
|
+
brand: OUNITECH_BRAND,
|
|
60687
|
+
displayName: "Ounitech (Q3-G / Q3-W)",
|
|
60688
|
+
capabilities: OUNITECH_CAPABILITIES,
|
|
60689
|
+
listRobots: async () => unconfigured(),
|
|
60690
|
+
getRobot: async () => unconfigured()
|
|
60691
|
+
});
|
|
60692
|
+
}
|
|
60693
|
+
|
|
60249
60694
|
// src/controllers/robot-fleet.controller.ts
|
|
60250
60695
|
var import_joi76 = __toESM(require("joi"));
|
|
60251
60696
|
var import_node_server_utils158 = require("@7365admin1/node-server-utils");
|
|
@@ -93683,6 +94128,7 @@ function usePersonalEmergencyChainController() {
|
|
|
93683
94128
|
ConsoleAuditAction,
|
|
93684
94129
|
ConsoleAuditTarget,
|
|
93685
94130
|
DEFAULT_RING_SECONDS,
|
|
94131
|
+
DEFAULT_SCALE_M_PER_PX,
|
|
93686
94132
|
DEFAULT_SITE_TIMEZONE,
|
|
93687
94133
|
DEVICE_STATUS,
|
|
93688
94134
|
DOBStatus,
|
|
@@ -93821,6 +94267,8 @@ function usePersonalEmergencyChainController() {
|
|
|
93821
94267
|
ORG_OWNER_REPAIR_CLAIM_TAKEN_REASON,
|
|
93822
94268
|
ORG_OWNER_REPAIR_DEFAULT,
|
|
93823
94269
|
ORG_OWNER_REPAIR_OFF_REASON,
|
|
94270
|
+
OUNITECH_BRAND,
|
|
94271
|
+
OUNITECH_CAPABILITIES,
|
|
93824
94272
|
OrgNature,
|
|
93825
94273
|
OvernightParkingRequestSort,
|
|
93826
94274
|
OvernightParkingRequestStatus,
|
|
@@ -93848,6 +94296,7 @@ function usePersonalEmergencyChainController() {
|
|
|
93848
94296
|
REALTIME_MAX_FANOUT,
|
|
93849
94297
|
REGISTERED_VEHICLE_CATEGORIES,
|
|
93850
94298
|
ROBOT_APP_SCOPES,
|
|
94299
|
+
ROBOT_CAPABILITIES,
|
|
93851
94300
|
ROBOT_CLOUD_DOC_CONFIRMATIONS,
|
|
93852
94301
|
ROBOT_CLOUD_PATHS,
|
|
93853
94302
|
ROBOT_MODULE,
|
|
@@ -93899,6 +94348,8 @@ function usePersonalEmergencyChainController() {
|
|
|
93899
94348
|
VerificationType,
|
|
93900
94349
|
VisitorSort,
|
|
93901
94350
|
VisitorStatus,
|
|
94351
|
+
WALL_HEIGHT_M,
|
|
94352
|
+
WALL_THICKNESS_M,
|
|
93902
94353
|
addressSchema,
|
|
93903
94354
|
allowedFieldsSite,
|
|
93904
94355
|
allowedNatures,
|
|
@@ -93910,6 +94361,8 @@ function usePersonalEmergencyChainController() {
|
|
|
93910
94361
|
attendanceSchema,
|
|
93911
94362
|
attendanceSettingsSchema,
|
|
93912
94363
|
buildSelfServiceEmailContext,
|
|
94364
|
+
buildingMapFromRobotMaps,
|
|
94365
|
+
buildingSolids,
|
|
93913
94366
|
building_level_namespace_collection,
|
|
93914
94367
|
building_units_namespace_collection,
|
|
93915
94368
|
buildings_namespace_collection,
|
|
@@ -93936,13 +94389,16 @@ function usePersonalEmergencyChainController() {
|
|
|
93936
94389
|
chatSchema,
|
|
93937
94390
|
claimReadsAsStale,
|
|
93938
94391
|
clampPtzSpeed,
|
|
94392
|
+
clearRobotAdapters,
|
|
93939
94393
|
clockDriftSeconds,
|
|
94394
|
+
commandSupported,
|
|
93940
94395
|
consoleRoleAllows,
|
|
93941
94396
|
consoleRoleAllowsAll,
|
|
93942
94397
|
consoleSpellings,
|
|
93943
94398
|
console_audit_namespace_collection,
|
|
93944
94399
|
createLiftSimulator,
|
|
93945
94400
|
createManpowerRemarksDaily,
|
|
94401
|
+
createOunitechAdapter,
|
|
93946
94402
|
createSseParser,
|
|
93947
94403
|
customerSchema,
|
|
93948
94404
|
customerSitePropertyFields,
|
|
@@ -94013,6 +94469,9 @@ function usePersonalEmergencyChainController() {
|
|
|
94013
94469
|
isTermsCurrent,
|
|
94014
94470
|
isTrackingStale,
|
|
94015
94471
|
isValidTimezone,
|
|
94472
|
+
levelPixelToMetres,
|
|
94473
|
+
levelScale,
|
|
94474
|
+
levelSolids,
|
|
94016
94475
|
listStaffUserIds,
|
|
94017
94476
|
liveStatusFor,
|
|
94018
94477
|
manpowerDesignationsSchema,
|
|
@@ -94024,6 +94483,7 @@ function usePersonalEmergencyChainController() {
|
|
|
94024
94483
|
maskNric,
|
|
94025
94484
|
memberModulesHandlers,
|
|
94026
94485
|
metresToPixel,
|
|
94486
|
+
missingForTier,
|
|
94027
94487
|
moduleDeniedForCaller,
|
|
94028
94488
|
moduleGateEnforceKeys,
|
|
94029
94489
|
moduleGateMode,
|
|
@@ -94078,6 +94538,9 @@ function usePersonalEmergencyChainController() {
|
|
|
94078
94538
|
recordConsoleAction,
|
|
94079
94539
|
refuseServiceProviderInviteAction,
|
|
94080
94540
|
registerCameraTransport,
|
|
94541
|
+
registerOunitechBrand,
|
|
94542
|
+
registerRobotAdapter,
|
|
94543
|
+
registeredRobotBrands,
|
|
94081
94544
|
relayForRecorder,
|
|
94082
94545
|
remarksSchema,
|
|
94083
94546
|
renderPagePdf,
|
|
@@ -94095,6 +94558,7 @@ function usePersonalEmergencyChainController() {
|
|
|
94095
94558
|
resolveSiteTimezone,
|
|
94096
94559
|
resolveStaffRole,
|
|
94097
94560
|
robotActionsFor,
|
|
94561
|
+
robotAdapterFor,
|
|
94098
94562
|
robotCloudBody,
|
|
94099
94563
|
robotCloudSign,
|
|
94100
94564
|
robotCloudSigningString,
|
|
@@ -94103,8 +94567,10 @@ function usePersonalEmergencyChainController() {
|
|
|
94103
94567
|
robotModeFromList,
|
|
94104
94568
|
robotSchema,
|
|
94105
94569
|
robotScopeForApp,
|
|
94570
|
+
robotTier,
|
|
94106
94571
|
robotTypesVisibleTo,
|
|
94107
94572
|
rtspUrl,
|
|
94573
|
+
scaleFromMeasurement,
|
|
94108
94574
|
schema,
|
|
94109
94575
|
schemaAppSlugNotification,
|
|
94110
94576
|
schemaApprovedBy,
|
|
@@ -94276,7 +94742,9 @@ function usePersonalEmergencyChainController() {
|
|
|
94276
94742
|
stripFacialImageMetadata,
|
|
94277
94743
|
subscriptionPlanSchema,
|
|
94278
94744
|
summariseBulkCameraPlan,
|
|
94745
|
+
supports,
|
|
94279
94746
|
trackingBackoffMs,
|
|
94747
|
+
uncontrollableFleetWarning,
|
|
94280
94748
|
updateRemarksStatusEod,
|
|
94281
94749
|
updateRemarksisAcknowledged,
|
|
94282
94750
|
updateSiteSchema,
|
|
@@ -94520,8 +94988,10 @@ function usePersonalEmergencyChainController() {
|
|
|
94520
94988
|
vehicleSchema,
|
|
94521
94989
|
vehicleViewOf,
|
|
94522
94990
|
vehicles_namespace_collection,
|
|
94991
|
+
vertexDistancePx,
|
|
94523
94992
|
visitors_namespace_collection,
|
|
94524
94993
|
wallConfig,
|
|
94994
|
+
wallsFromOccupancyGrid,
|
|
94525
94995
|
withLiveLink,
|
|
94526
94996
|
workOrderSchema,
|
|
94527
94997
|
work_orders2_namespace_collection,
|