@7365admin1/core 3.130.0 → 3.131.1
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 +12 -0
- package/dist/index.d.ts +28 -11
- package/dist/index.js +317 -143
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +315 -143
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/test/e2e/member-direct-grantor.e2e.test.mjs +137 -0
- package/test/e2e/org-members-people.e2e.test.mjs +137 -0
- package/test/e2e/privilege-escalation.e2e.test.mjs +12 -6
- package/test/ounitech-api-2025-10.test.mjs +103 -4
- package/test/role-member-org-scope.test.mjs +4 -3
package/dist/index.js
CHANGED
|
@@ -250,6 +250,7 @@ __export(src_exports, {
|
|
|
250
250
|
OUNITECH_BRAND: () => OUNITECH_BRAND,
|
|
251
251
|
OUNITECH_BRAND_DEFINITION: () => OUNITECH_BRAND_DEFINITION,
|
|
252
252
|
OUNITECH_CAPABILITIES: () => OUNITECH_CAPABILITIES,
|
|
253
|
+
OUNITECH_FRESH_MS: () => OUNITECH_FRESH_MS,
|
|
253
254
|
OUNITECH_PENDING_COMMANDS: () => OUNITECH_PENDING_COMMANDS,
|
|
254
255
|
OUNITECH_REGION_BASE_URLS: () => OUNITECH_REGION_BASE_URLS,
|
|
255
256
|
OUNITECH_TIMESTAMP_FORMAT: () => OUNITECH_TIMESTAMP_FORMAT,
|
|
@@ -617,6 +618,7 @@ __export(src_exports, {
|
|
|
617
618
|
orgRoleSets: () => orgRoleSets,
|
|
618
619
|
orgSchema: () => orgSchema,
|
|
619
620
|
ounitechFault: () => ounitechFault,
|
|
621
|
+
ounitechJobWords: () => ounitechJobWords,
|
|
620
622
|
ounitechRobotFromHomeRow: () => ounitechRobotFromHomeRow,
|
|
621
623
|
overnight_parking_requests_namespace_collection: () => overnight_parking_requests_namespace_collection,
|
|
622
624
|
padbotAccountKey: () => padbotAccountKey,
|
|
@@ -4834,6 +4836,63 @@ function useMemberRepo() {
|
|
|
4834
4836
|
throw error;
|
|
4835
4837
|
}
|
|
4836
4838
|
}
|
|
4839
|
+
async function peoplePage(query2, page2, limit, rowStages) {
|
|
4840
|
+
const noSite = { $in: ["", null] };
|
|
4841
|
+
const orgWide = await collection.distinct("user", {
|
|
4842
|
+
org: query2.org,
|
|
4843
|
+
type: query2.type,
|
|
4844
|
+
siteId: noSite,
|
|
4845
|
+
status: { $ne: "deleted" }
|
|
4846
|
+
});
|
|
4847
|
+
const { type: orgType, siteId: pinned, ...rest } = query2;
|
|
4848
|
+
const limitedRow = {
|
|
4849
|
+
siteId: pinned ?? { $nin: ["", null] },
|
|
4850
|
+
user: { $nin: orgWide }
|
|
4851
|
+
};
|
|
4852
|
+
const head = [
|
|
4853
|
+
// `$text` (search) may only sit in the first stage.
|
|
4854
|
+
{ $match: { ...rest, type: { $nin: ["resident", "admin"] } } },
|
|
4855
|
+
{ $match: pinned ? limitedRow : { $or: [{ type: orgType }, limitedRow] } },
|
|
4856
|
+
{ $sort: { _id: -1 } },
|
|
4857
|
+
{
|
|
4858
|
+
$group: {
|
|
4859
|
+
_id: { $ifNull: ["$user", "$_id"] },
|
|
4860
|
+
doc: { $first: "$$ROOT" },
|
|
4861
|
+
orgWide: {
|
|
4862
|
+
$max: { $and: [{ $eq: ["$type", orgType] }, { $in: [{ $ifNull: ["$siteId", ""] }, ["", null]] }] }
|
|
4863
|
+
},
|
|
4864
|
+
limitedTo: { $push: { siteId: "$siteId", siteName: "$siteName", role: "$role" } }
|
|
4865
|
+
}
|
|
4866
|
+
},
|
|
4867
|
+
{
|
|
4868
|
+
$replaceRoot: {
|
|
4869
|
+
newRoot: { $mergeObjects: ["$doc", { limitedTo: { $cond: ["$orgWide", [], "$limitedTo"] } }] }
|
|
4870
|
+
}
|
|
4871
|
+
}
|
|
4872
|
+
];
|
|
4873
|
+
const items = await collection.aggregate([...head, { $sort: { _id: -1 } }, { $skip: page2 * limit }, { $limit: limit }, ...rowStages]).toArray();
|
|
4874
|
+
const [counted] = await collection.aggregate([...head, { $count: "n" }]).toArray();
|
|
4875
|
+
const ids2 = (key) => [...new Set(items.flatMap((i) => (i.limitedTo ?? []).map((l) => l[key]?.toString?.() ?? "")))].filter((id) => import_mongodb14.ObjectId.isValid(id)).map((id) => new import_mongodb14.ObjectId(id));
|
|
4876
|
+
const [sites, roles] = await Promise.all([
|
|
4877
|
+
db2.collection("sites").find({ _id: { $in: ids2("siteId") } }, { projection: { name: 1 } }).toArray(),
|
|
4878
|
+
db2.collection("roles").find({ _id: { $in: ids2("role") } }, { projection: { name: 1 } }).toArray()
|
|
4879
|
+
]);
|
|
4880
|
+
const nameOf = (list) => new Map(list.map((d) => [d._id.toString(), d.name ?? ""]));
|
|
4881
|
+
const siteName = nameOf(sites);
|
|
4882
|
+
const roleName = nameOf(roles);
|
|
4883
|
+
for (const item of items) {
|
|
4884
|
+
const bySite = /* @__PURE__ */ new Map();
|
|
4885
|
+
for (const l of item.limitedTo ?? []) {
|
|
4886
|
+
const site = l.siteId?.toString?.() ?? "";
|
|
4887
|
+
const role = roleName.get(l.role?.toString?.() ?? "") ?? "";
|
|
4888
|
+
const entry = bySite.get(site) ?? { siteId: site, siteName: siteName.get(site) || l.siteName || "", roleName: "" };
|
|
4889
|
+
entry.roleName = [entry.roleName, role].filter(Boolean).join(", ");
|
|
4890
|
+
bySite.set(site, entry);
|
|
4891
|
+
}
|
|
4892
|
+
item.limitedTo = [...bySite.values()].sort((a, b) => a.siteName.localeCompare(b.siteName));
|
|
4893
|
+
}
|
|
4894
|
+
return { items, length: counted?.n ?? 0 };
|
|
4895
|
+
}
|
|
4837
4896
|
async function getAll({
|
|
4838
4897
|
search = "",
|
|
4839
4898
|
page: page2 = 1,
|
|
@@ -4844,7 +4903,8 @@ function useMemberRepo() {
|
|
|
4844
4903
|
siteIds,
|
|
4845
4904
|
type = "",
|
|
4846
4905
|
status = "",
|
|
4847
|
-
roleState = ""
|
|
4906
|
+
roleState = "",
|
|
4907
|
+
people = false
|
|
4848
4908
|
}) {
|
|
4849
4909
|
limit = limit > 0 ? limit : 10;
|
|
4850
4910
|
search = search || "";
|
|
@@ -4903,6 +4963,9 @@ function useMemberRepo() {
|
|
|
4903
4963
|
query2.$or = await otherSiteRoleClauses2(query2.org);
|
|
4904
4964
|
cacheOptions.roleState = roleState;
|
|
4905
4965
|
}
|
|
4966
|
+
const peopleMode = people && type === "organization" && !!query2.org && !siteId && !roleState;
|
|
4967
|
+
if (peopleMode)
|
|
4968
|
+
cacheOptions.people = 1;
|
|
4906
4969
|
const cacheKey = (0, import_node_server_utils15.makeCacheKey)(namespace_collection3, cacheOptions);
|
|
4907
4970
|
const cachedData = await getCache(cacheKey);
|
|
4908
4971
|
if (cachedData) {
|
|
@@ -4910,15 +4973,7 @@ function useMemberRepo() {
|
|
|
4910
4973
|
return cachedData;
|
|
4911
4974
|
}
|
|
4912
4975
|
try {
|
|
4913
|
-
const
|
|
4914
|
-
{ $match: query2 },
|
|
4915
|
-
{ $sort: { _id: -1 } },
|
|
4916
|
-
// `paginate()` only LABELS the range ("11-20 of 42"); it does not
|
|
4917
|
-
// slice. Without this $skip every page returned page 1's rows under
|
|
4918
|
-
// a later page's heading. `page` is already zero-based above, and
|
|
4919
|
-
// this is the same $skip/$limit pair the two getOrgs* reads use.
|
|
4920
|
-
{ $skip: page2 * limit },
|
|
4921
|
-
{ $limit: limit },
|
|
4976
|
+
const rowStages = [
|
|
4922
4977
|
{
|
|
4923
4978
|
$lookup: {
|
|
4924
4979
|
from: "roles",
|
|
@@ -4992,11 +5047,29 @@ function useMemberRepo() {
|
|
|
4992
5047
|
dateInvited: { $ifNull: ["$dateInvited", "$createdAt"] },
|
|
4993
5048
|
siteId: 1,
|
|
4994
5049
|
siteName: 1,
|
|
4995
|
-
type: 1
|
|
5050
|
+
type: 1,
|
|
5051
|
+
limitedTo: 1
|
|
4996
5052
|
}
|
|
4997
5053
|
}
|
|
4998
|
-
]
|
|
4999
|
-
|
|
5054
|
+
];
|
|
5055
|
+
let items;
|
|
5056
|
+
let length;
|
|
5057
|
+
if (peopleMode) {
|
|
5058
|
+
({ items, length } = await peoplePage(query2, page2, limit, rowStages));
|
|
5059
|
+
} else {
|
|
5060
|
+
items = await collection.aggregate([
|
|
5061
|
+
{ $match: query2 },
|
|
5062
|
+
{ $sort: { _id: -1 } },
|
|
5063
|
+
// `paginate()` only LABELS the range ("11-20 of 42"); it does not
|
|
5064
|
+
// slice. Without this $skip every page returned page 1's rows under
|
|
5065
|
+
// a later page's heading. `page` is already zero-based above, and
|
|
5066
|
+
// this is the same $skip/$limit pair the two getOrgs* reads use.
|
|
5067
|
+
{ $skip: page2 * limit },
|
|
5068
|
+
{ $limit: limit },
|
|
5069
|
+
...rowStages
|
|
5070
|
+
]).toArray();
|
|
5071
|
+
length = await collection.countDocuments(query2);
|
|
5072
|
+
}
|
|
5000
5073
|
const data = { ...(0, import_node_server_utils15.paginate)(items, page2, limit, length), total: length };
|
|
5001
5074
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
5002
5075
|
import_node_server_utils15.logger.info(`Cache set for key: ${cacheKey}`);
|
|
@@ -11183,15 +11256,15 @@ function robotConnectionText(robotName, online, minutes) {
|
|
|
11183
11256
|
}
|
|
11184
11257
|
var OFFLINE_FAULT_CODE = "OFFLINE";
|
|
11185
11258
|
var SAME_OCCURRENCE_MS = 1e3;
|
|
11186
|
-
function planFaultChanges(open,
|
|
11259
|
+
function planFaultChanges(open, reading2) {
|
|
11187
11260
|
const byCode = new Map(open.map((r) => [r.code, r]));
|
|
11188
11261
|
const want = [];
|
|
11189
|
-
const offline = !
|
|
11262
|
+
const offline = !reading2.ok || reading2.online === false;
|
|
11190
11263
|
if (offline) {
|
|
11191
|
-
const reason =
|
|
11264
|
+
const reason = reading2.ok ? "" : String(reading2.error ?? "").slice(0, 200);
|
|
11192
11265
|
want.push({ code: OFFLINE_FAULT_CODE, message: reason ? `Not reporting: ${reason}` : "Not reporting" });
|
|
11193
11266
|
}
|
|
11194
|
-
const fault =
|
|
11267
|
+
const fault = reading2.ok && reading2.fault?.code ? reading2.fault : null;
|
|
11195
11268
|
if (fault)
|
|
11196
11269
|
want.push({ code: fault.code, message: fault.message || fault.code, since: fault.since });
|
|
11197
11270
|
const clear = [];
|
|
@@ -11223,12 +11296,12 @@ function planFaultChanges(open, reading) {
|
|
|
11223
11296
|
return { open: toOpen, clear, refresh };
|
|
11224
11297
|
}
|
|
11225
11298
|
function robotFaultSweepConfig(env2 = process.env) {
|
|
11226
|
-
const
|
|
11299
|
+
const num4 = (v, def, min, max) => {
|
|
11227
11300
|
const n = Math.floor(Number(v));
|
|
11228
11301
|
return Number.isFinite(n) && String(v ?? "").trim() !== "" ? Math.min(max, Math.max(min, n)) : def;
|
|
11229
11302
|
};
|
|
11230
11303
|
const enabled = String(env2.ROBOT_FAULT_SWEEP ?? "on").trim().toLowerCase() !== "off";
|
|
11231
|
-
const asked =
|
|
11304
|
+
const asked = num4(env2.ROBOT_FAULT_SWEEP_SECONDS, 60, 10, 3600);
|
|
11232
11305
|
const pick = (steps, v) => steps.filter((d) => d <= v).pop() ?? steps[0];
|
|
11233
11306
|
const subMinute = asked < 60;
|
|
11234
11307
|
const step = subMinute ? pick([10, 12, 15, 20, 30], asked) : pick([1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60], Math.floor(asked / 60));
|
|
@@ -11238,9 +11311,9 @@ function robotFaultSweepConfig(env2 = process.env) {
|
|
|
11238
11311
|
enabled,
|
|
11239
11312
|
seconds,
|
|
11240
11313
|
cron: cron2,
|
|
11241
|
-
concurrency:
|
|
11242
|
-
timeoutMs:
|
|
11243
|
-
offlineAlertMinutes:
|
|
11314
|
+
concurrency: num4(env2.ROBOT_FAULT_SWEEP_CONCURRENCY, 4, 1, 16),
|
|
11315
|
+
timeoutMs: num4(env2.ROBOT_FAULT_SWEEP_TIMEOUT_MS, 1e4, 1e3, 6e4),
|
|
11316
|
+
offlineAlertMinutes: num4(env2.ROBOT_OFFLINE_ALERT_MINUTES, 10, 1, 1440)
|
|
11244
11317
|
};
|
|
11245
11318
|
}
|
|
11246
11319
|
function withTimeout(promise, ms2, label2 = "Timed out") {
|
|
@@ -24001,6 +24074,29 @@ async function requireNotOwnerUnlessOwner(req, orgId, targetUser) {
|
|
|
24001
24074
|
return;
|
|
24002
24075
|
throw new import_node_server_utils58.UnauthorizedError("Only the organisation's owner can change the owner's access.");
|
|
24003
24076
|
}
|
|
24077
|
+
async function requireMemberGrantor(req, input) {
|
|
24078
|
+
const { orgId, targetUser, roleId } = input;
|
|
24079
|
+
const siteId = input.siteId?.toString() ?? "";
|
|
24080
|
+
const id = await requireSiteAccessEditor(req, orgId);
|
|
24081
|
+
await requireNotOwnerUnlessOwner(req, orgId, targetUser);
|
|
24082
|
+
if (staffMayBypass(await resolveInviteActor(id)))
|
|
24083
|
+
return;
|
|
24084
|
+
if (siteId)
|
|
24085
|
+
await requireSiteServedByOrg(siteId, orgId);
|
|
24086
|
+
if (await hasOrgOwnership(id, orgId))
|
|
24087
|
+
return;
|
|
24088
|
+
const db2 = import_node_server_utils58.useAtlas.getDb();
|
|
24089
|
+
if (!db2 || !import_mongodb54.ObjectId.isValid(roleId))
|
|
24090
|
+
return;
|
|
24091
|
+
const role = await db2.collection("roles").findOne({ _id: new import_mongodb54.ObjectId(roleId) }, { projection: { permissions: 1 } });
|
|
24092
|
+
if (isOwnerRole(role)) {
|
|
24093
|
+
const rows = await db2.collection("members").find({ user: new import_mongodb54.ObjectId(id), org: new import_mongodb54.ObjectId(orgId), status: "active" }, { projection: { role: 1 } }).toArray();
|
|
24094
|
+
const held = rows.map((m) => m.role?.toString?.() ?? "").filter((r) => import_mongodb54.ObjectId.isValid(r)).map((r) => new import_mongodb54.ObjectId(r));
|
|
24095
|
+
const holdsOwner = held.length ? await db2.collection("roles").countDocuments({ _id: { $in: held }, permissions: "*", status: "active" }) : 0;
|
|
24096
|
+
if (!holdsOwner)
|
|
24097
|
+
throw new import_node_server_utils58.UnauthorizedError("Only the organisation's owner can give an owner role.");
|
|
24098
|
+
}
|
|
24099
|
+
}
|
|
24004
24100
|
async function requireSiteReachInOrg(req, siteId, orgId) {
|
|
24005
24101
|
if (await hasOrgOwnership(callerId(req), orgId)) {
|
|
24006
24102
|
await requireSiteServedByOrg(siteId, orgId);
|
|
@@ -27025,7 +27121,7 @@ function usePersonRepo() {
|
|
|
27025
27121
|
}
|
|
27026
27122
|
async function pushVehicleById(id, plate, session) {
|
|
27027
27123
|
const { plateNumber, recNo } = plate;
|
|
27028
|
-
const
|
|
27124
|
+
const updatedAt2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
27029
27125
|
const _id = (0, import_node_server_utils64.toObjectId)(id);
|
|
27030
27126
|
try {
|
|
27031
27127
|
const updateExisting = await collection.updateOne(
|
|
@@ -27036,7 +27132,7 @@ function usePersonRepo() {
|
|
|
27036
27132
|
{
|
|
27037
27133
|
$set: {
|
|
27038
27134
|
"plates.$.recNo": recNo,
|
|
27039
|
-
updatedAt
|
|
27135
|
+
updatedAt: updatedAt2
|
|
27040
27136
|
}
|
|
27041
27137
|
},
|
|
27042
27138
|
{ session }
|
|
@@ -27052,7 +27148,7 @@ function usePersonRepo() {
|
|
|
27052
27148
|
}
|
|
27053
27149
|
},
|
|
27054
27150
|
$set: {
|
|
27055
|
-
updatedAt
|
|
27151
|
+
updatedAt: updatedAt2
|
|
27056
27152
|
}
|
|
27057
27153
|
},
|
|
27058
27154
|
{ session }
|
|
@@ -27063,7 +27159,7 @@ function usePersonRepo() {
|
|
|
27063
27159
|
}
|
|
27064
27160
|
}
|
|
27065
27161
|
async function pullVehicleByRecNo(recNo, scope, session) {
|
|
27066
|
-
const
|
|
27162
|
+
const updatedAt2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
27067
27163
|
const owner = scope?.peopleId ? (0, import_node_server_utils64.toObjectId)(scope.peopleId) : null;
|
|
27068
27164
|
const site = scope?.site ? (0, import_node_server_utils64.toObjectId)(scope.site) : null;
|
|
27069
27165
|
if (!owner && !site) {
|
|
@@ -27102,7 +27198,7 @@ function usePersonRepo() {
|
|
|
27102
27198
|
plates: match
|
|
27103
27199
|
},
|
|
27104
27200
|
$set: {
|
|
27105
|
-
updatedAt
|
|
27201
|
+
updatedAt: updatedAt2
|
|
27106
27202
|
}
|
|
27107
27203
|
},
|
|
27108
27204
|
{ session }
|
|
@@ -29244,7 +29340,9 @@ function useMemberController() {
|
|
|
29244
29340
|
siteId: import_joi29.default.string().hex().optional().allow("", null),
|
|
29245
29341
|
type: import_joi29.default.string().required(),
|
|
29246
29342
|
status: import_joi29.default.string().required(),
|
|
29247
|
-
roleState: import_joi29.default.string().valid("other-site").optional().allow("", null)
|
|
29343
|
+
roleState: import_joi29.default.string().valid("other-site").optional().allow("", null),
|
|
29344
|
+
// Organization app Members: every person, site-limited ones included.
|
|
29345
|
+
people: import_joi29.default.boolean().truthy("1").falsy("0").optional().allow("", null)
|
|
29248
29346
|
});
|
|
29249
29347
|
const query2 = { ...req.query };
|
|
29250
29348
|
const { error } = validation.validate(query2);
|
|
@@ -29299,7 +29397,8 @@ function useMemberController() {
|
|
|
29299
29397
|
siteIds,
|
|
29300
29398
|
type,
|
|
29301
29399
|
status,
|
|
29302
|
-
roleState: req.query.roleState ?? ""
|
|
29400
|
+
roleState: req.query.roleState ?? "",
|
|
29401
|
+
people: ["1", "true"].includes(String(req.query.people ?? ""))
|
|
29303
29402
|
});
|
|
29304
29403
|
res.json(items);
|
|
29305
29404
|
return;
|
|
@@ -29440,7 +29539,7 @@ function useMemberController() {
|
|
|
29440
29539
|
await requireOrgReach(req, orgId);
|
|
29441
29540
|
await requireSelfEnrolRole(req, orgId, roleId);
|
|
29442
29541
|
} else {
|
|
29443
|
-
await
|
|
29542
|
+
await requireMemberGrantor(req, { orgId, targetUser: userId, roleId, siteId });
|
|
29444
29543
|
}
|
|
29445
29544
|
const data = await _createMemberDirect({ userId, orgId, roleId, app, siteId, siteName, onboardingRequired, callerId: callerId(req) });
|
|
29446
29545
|
res.status(201).json(data);
|
|
@@ -48321,7 +48420,7 @@ var MKeyLog = class {
|
|
|
48321
48420
|
organization = "",
|
|
48322
48421
|
serviceProvider = "",
|
|
48323
48422
|
createdAt = /* @__PURE__ */ new Date(),
|
|
48324
|
-
updatedAt = /* @__PURE__ */ new Date(),
|
|
48423
|
+
updatedAt: updatedAt2 = /* @__PURE__ */ new Date(),
|
|
48325
48424
|
template,
|
|
48326
48425
|
status = "Available",
|
|
48327
48426
|
updatedBy = "",
|
|
@@ -48342,7 +48441,7 @@ var MKeyLog = class {
|
|
|
48342
48441
|
this.organization = organization;
|
|
48343
48442
|
this.serviceProvider = serviceProvider;
|
|
48344
48443
|
this.createdAt = createdAt;
|
|
48345
|
-
this.updatedAt =
|
|
48444
|
+
this.updatedAt = updatedAt2;
|
|
48346
48445
|
this.template = template;
|
|
48347
48446
|
this.status = status;
|
|
48348
48447
|
this.updatedBy = updatedBy;
|
|
@@ -56981,7 +57080,7 @@ var MAccessCard = class {
|
|
|
56981
57080
|
isLiftCard,
|
|
56982
57081
|
liftAccessLevel,
|
|
56983
57082
|
createdAt,
|
|
56984
|
-
updatedAt,
|
|
57083
|
+
updatedAt: updatedAt2,
|
|
56985
57084
|
assignedUnit,
|
|
56986
57085
|
userType,
|
|
56987
57086
|
doorName,
|
|
@@ -57008,7 +57107,7 @@ var MAccessCard = class {
|
|
|
57008
57107
|
this.isLiftCard = isLiftCard;
|
|
57009
57108
|
this.liftAccessLevel = liftAccessLevel;
|
|
57010
57109
|
this.createdAt = createdAt;
|
|
57011
|
-
this.updatedAt =
|
|
57110
|
+
this.updatedAt = updatedAt2;
|
|
57012
57111
|
this.assignedUnit = assignedUnit;
|
|
57013
57112
|
this.userType = userType;
|
|
57014
57113
|
this.doorName = doorName;
|
|
@@ -57187,8 +57286,8 @@ function encryptPayload(payloadHex) {
|
|
|
57187
57286
|
);
|
|
57188
57287
|
return encrypted.toString("base64");
|
|
57189
57288
|
}
|
|
57190
|
-
function toHex(
|
|
57191
|
-
return
|
|
57289
|
+
function toHex(num4, length) {
|
|
57290
|
+
return num4.toString(16).padStart(length * 2, "0");
|
|
57192
57291
|
}
|
|
57193
57292
|
function buildPayload(cardNumber, validityMinutes) {
|
|
57194
57293
|
const randomHex = import_crypto5.default.randomBytes(2).toString("hex");
|
|
@@ -57368,8 +57467,8 @@ function UseAccessManagementRepo() {
|
|
|
57368
57467
|
}
|
|
57369
57468
|
}
|
|
57370
57469
|
function generateCardNo() {
|
|
57371
|
-
const
|
|
57372
|
-
return
|
|
57470
|
+
const num4 = Math.floor(Math.random() * 65536);
|
|
57471
|
+
return num4.toString().padStart(10, "0");
|
|
57373
57472
|
}
|
|
57374
57473
|
async function createQrData({
|
|
57375
57474
|
cardNumber,
|
|
@@ -58609,13 +58708,13 @@ function UseAccessManagementRepo() {
|
|
|
58609
58708
|
const prevCard = res && res?.[1]?.cardNo || "-1";
|
|
58610
58709
|
const currentCard = res && res?.[0]?.cardNo || "0";
|
|
58611
58710
|
let availableCardNo = [];
|
|
58612
|
-
let
|
|
58711
|
+
let num4 = parseInt(prevCard, 10) + 1;
|
|
58613
58712
|
while (availableCardNo.length < (unit.length > 0 ? unit.length : quantity)) {
|
|
58614
|
-
if (
|
|
58615
|
-
|
|
58713
|
+
if (num4 === parseInt(currentCard, 10)) {
|
|
58714
|
+
num4++;
|
|
58616
58715
|
}
|
|
58617
|
-
availableCardNo.push(
|
|
58618
|
-
|
|
58716
|
+
availableCardNo.push(num4);
|
|
58717
|
+
num4++;
|
|
58619
58718
|
}
|
|
58620
58719
|
const cardNumbers = availableCardNo.map(
|
|
58621
58720
|
(no2) => no2.toString().padStart(10, "0")
|
|
@@ -64334,13 +64433,13 @@ function parseRobotCommand(body) {
|
|
|
64334
64433
|
if (has2(k))
|
|
64335
64434
|
return { ok: false, message: `${k} is not valid for a ${kind} command.` };
|
|
64336
64435
|
}
|
|
64337
|
-
const
|
|
64338
|
-
if (!
|
|
64436
|
+
const num4 = (k) => typeof b[k] === "number" && Number.isFinite(b[k]);
|
|
64437
|
+
if (!num4("x") || !num4("y"))
|
|
64339
64438
|
return { ok: false, message: `${kind} needs x and y (metres).` };
|
|
64340
64439
|
if (kind === "navigate") {
|
|
64341
64440
|
if (has2("floorNo"))
|
|
64342
64441
|
return { ok: false, message: "floorNo is not valid for a navigate command." };
|
|
64343
|
-
if (has2("theta") && !
|
|
64442
|
+
if (has2("theta") && !num4("theta"))
|
|
64344
64443
|
return { ok: false, message: "theta must be a number (radians)." };
|
|
64345
64444
|
return {
|
|
64346
64445
|
ok: true,
|
|
@@ -64352,7 +64451,7 @@ function parseRobotCommand(body) {
|
|
|
64352
64451
|
}
|
|
64353
64452
|
};
|
|
64354
64453
|
}
|
|
64355
|
-
if (!
|
|
64454
|
+
if (!num4("theta"))
|
|
64356
64455
|
return { ok: false, message: "relocalize needs theta (radians)." };
|
|
64357
64456
|
if (!Number.isInteger(b.floorNo))
|
|
64358
64457
|
return { ok: false, message: "relocalize needs an integer floorNo." };
|
|
@@ -64776,23 +64875,81 @@ var OUNITECH_CAPABILITIES = {
|
|
|
64776
64875
|
var OUNITECH_PENDING_COMMANDS = ["pause", "resume", "cancel"].map(
|
|
64777
64876
|
(kind) => ({ kind, reason: "awaiting Ounitech's robot-control action numbers" })
|
|
64778
64877
|
);
|
|
64779
|
-
function propertyNumber(row, key) {
|
|
64780
|
-
const p = row.robotState?.properties?.find((x) => x?.key === key);
|
|
64781
|
-
const n = p?.value != null && String(p.value).trim() !== "" ? Number(p.value) : NaN;
|
|
64782
|
-
return Number.isFinite(n) ? n : null;
|
|
64783
|
-
}
|
|
64784
64878
|
var finite = (v) => typeof v === "number" && Number.isFinite(v);
|
|
64785
|
-
function
|
|
64786
|
-
|
|
64787
|
-
|
|
64788
|
-
|
|
64879
|
+
function num(v) {
|
|
64880
|
+
if (finite(v))
|
|
64881
|
+
return v;
|
|
64882
|
+
if (typeof v === "string" && v.trim() !== "") {
|
|
64883
|
+
const n = Number(v);
|
|
64884
|
+
return Number.isFinite(n) ? n : void 0;
|
|
64885
|
+
}
|
|
64886
|
+
return void 0;
|
|
64887
|
+
}
|
|
64888
|
+
function reading(st, key) {
|
|
64889
|
+
const v = st.properties?.find((x) => x?.key === key)?.value;
|
|
64890
|
+
return v == null || String(v).trim() === "" ? void 0 : String(v).trim();
|
|
64891
|
+
}
|
|
64892
|
+
var OUNITECH_FRESH_MS = 3 * 6e4;
|
|
64893
|
+
var STATUS_CODE_BY_READING = {
|
|
64894
|
+
\u5F85\u673A: 1,
|
|
64895
|
+
\u7A7A\u95F2: 1,
|
|
64896
|
+
\u6E05\u626B\u4E2D: 2,
|
|
64897
|
+
\u5DE5\u4F5C\u4E2D: 2,
|
|
64898
|
+
\u6682\u505C: 3,
|
|
64899
|
+
\u6682\u505C\u4E2D: 3,
|
|
64900
|
+
\u5EFA\u56FE\u4E2D: 4,
|
|
64901
|
+
\u56DE\u5145\u4E2D: 6,
|
|
64902
|
+
\u8FD4\u56DE\u5145\u7535: 6,
|
|
64903
|
+
\u6545\u969C: 7,
|
|
64904
|
+
\u5F02\u5E38: 7,
|
|
64905
|
+
\u5145\u7535\u4E2D: 8,
|
|
64906
|
+
\u65AD\u70B9\u5145\u7535\u4E2D: 16,
|
|
64907
|
+
\u5DF2\u5145\u6EE1: 9,
|
|
64908
|
+
\u5145\u6EE1: 9,
|
|
64909
|
+
\u6025\u505C: 11,
|
|
64910
|
+
\u5173\u673A: 14,
|
|
64911
|
+
\u79BB\u7EBF: 99
|
|
64912
|
+
};
|
|
64913
|
+
var JOB_WORDS = {
|
|
64914
|
+
\u5DF2\u7ED3\u675F: "job finished",
|
|
64915
|
+
\u5DF2\u5B8C\u6210: "job finished",
|
|
64916
|
+
\u5B8C\u6210: "job finished",
|
|
64917
|
+
\u6E05\u626B\u4E2D: "job in progress",
|
|
64918
|
+
\u8FDB\u884C\u4E2D: "job in progress",
|
|
64919
|
+
\u6682\u505C: "job paused",
|
|
64920
|
+
\u6682\u505C\u4E2D: "job paused",
|
|
64921
|
+
\u672A\u5F00\u59CB: "no job started",
|
|
64922
|
+
\u5DF2\u53D6\u6D88: "job cancelled",
|
|
64923
|
+
\u53D6\u6D88: "job cancelled"
|
|
64924
|
+
};
|
|
64925
|
+
function ounitechJobWords(st) {
|
|
64926
|
+
const v = reading(st, "job");
|
|
64927
|
+
return v ? JOB_WORDS[v] ?? null : null;
|
|
64928
|
+
}
|
|
64929
|
+
function updatedAt(v) {
|
|
64930
|
+
const n = num(v);
|
|
64931
|
+
if (n !== void 0)
|
|
64932
|
+
return n < 1e12 ? n * 1e3 : n;
|
|
64933
|
+
const t = typeof v === "string" ? Date.parse(v) : NaN;
|
|
64934
|
+
return Number.isFinite(t) ? t : null;
|
|
64935
|
+
}
|
|
64936
|
+
function ounitechRobotFromHomeRow(row, live2, now = Date.now()) {
|
|
64937
|
+
const listed = row.robotState ?? {};
|
|
64938
|
+
const st = live2 ? { ...listed, ...live2, properties: live2.properties?.length ? live2.properties : listed.properties } : listed;
|
|
64939
|
+
const statusReading = reading(st, "status");
|
|
64940
|
+
const flag = num(st.statusFlag) ?? (statusReading ? STATUS_CODE_BY_READING[statusReading] : void 0);
|
|
64941
|
+
const at2 = updatedAt(st.updateOn);
|
|
64942
|
+
const fresh = at2 !== null && now - at2 <= OUNITECH_FRESH_MS;
|
|
64943
|
+
const online = flag !== 99 && (flag !== void 0 || fresh);
|
|
64789
64944
|
const mode = online ? robotMode(flag) : "UNAVAILABLE";
|
|
64790
|
-
const pct =
|
|
64945
|
+
const pct = num(reading(st, "battery-percent")) ?? num(st.battery) ?? null;
|
|
64791
64946
|
const task = row.taskState;
|
|
64792
64947
|
const jobPct = task && String(task.statusFlag) === "1" && finite(task.accPercent) ? Math.round(task.accPercent) : null;
|
|
64793
|
-
const words = flag
|
|
64948
|
+
const words = flag !== void 0 ? ROBOT_STATUS_NAMES[flag] ?? `Status ${flag}` : online ? "Online - status not reported" : "Unknown";
|
|
64794
64949
|
const model = String(row.modelCode || row.modelName || "").trim().slice(0, 40);
|
|
64795
|
-
|
|
64950
|
+
const x = num(st.x);
|
|
64951
|
+
const y = num(st.y);
|
|
64952
|
+
const robot = {
|
|
64796
64953
|
robotSn: row.robotSn,
|
|
64797
64954
|
name: row.aliasName || row.robotName,
|
|
64798
64955
|
...model ? { model } : {},
|
|
@@ -64802,8 +64959,16 @@ function ounitechRobotFromHomeRow(row) {
|
|
|
64802
64959
|
batteryPercent: online && pct !== null && pct >= 0 && pct <= 100 ? Math.round(pct) : null,
|
|
64803
64960
|
floorNo: Number.isInteger(row.floorNo) ? row.floorNo : null,
|
|
64804
64961
|
floorKnown: Number.isInteger(row.floorNo),
|
|
64805
|
-
position: online &&
|
|
64962
|
+
position: online && x !== void 0 && y !== void 0 ? { x, y, theta: num(st.theta) ?? 0 } : null
|
|
64806
64963
|
};
|
|
64964
|
+
if (online && (flag === 7 || Array.isArray(st.rtFaultList) && st.rtFaultList.length)) {
|
|
64965
|
+
robot.fault = ounitechFault(st.rtFaultList);
|
|
64966
|
+
}
|
|
64967
|
+
return robot;
|
|
64968
|
+
}
|
|
64969
|
+
function needsLiveState(row) {
|
|
64970
|
+
const f = num(row.robotState?.statusFlag);
|
|
64971
|
+
return finite(row.robotId) && (f === void 0 || f === 99 || f === 7);
|
|
64807
64972
|
}
|
|
64808
64973
|
function ounitechFault(list) {
|
|
64809
64974
|
const f = Array.isArray(list) ? list[0] : null;
|
|
@@ -64820,6 +64985,9 @@ function ounitechFault(list) {
|
|
|
64820
64985
|
var robotTime = (d) => robotCloudTimestamp(d, "yyyy-MM-dd HH:mm:ss", "Asia/Shanghai");
|
|
64821
64986
|
function createOunitechAdapter(credentials, options) {
|
|
64822
64987
|
const cloud = useRobotCloudService(credentials, options);
|
|
64988
|
+
async function liveFor(row) {
|
|
64989
|
+
return needsLiveState(row) ? cloud.getRobotState(row.robotId).catch(() => null) : null;
|
|
64990
|
+
}
|
|
64823
64991
|
async function rowFor(robotSn) {
|
|
64824
64992
|
return (await cloud.getHomeRobots()).find((r) => r.robotSn === robotSn) ?? null;
|
|
64825
64993
|
}
|
|
@@ -64828,7 +64996,7 @@ function createOunitechAdapter(credentials, options) {
|
|
|
64828
64996
|
displayName: "Ounitech (Q3-G / Q3-W)",
|
|
64829
64997
|
capabilities: OUNITECH_CAPABILITIES,
|
|
64830
64998
|
async listRobots() {
|
|
64831
|
-
return (await cloud.getHomeRobots()).map(ounitechRobotFromHomeRow);
|
|
64999
|
+
return Promise.all((await cloud.getHomeRobots()).map(async (row) => ounitechRobotFromHomeRow(row, await liveFor(row))));
|
|
64832
65000
|
},
|
|
64833
65001
|
async getRobot(robotSn) {
|
|
64834
65002
|
const row = await rowFor(robotSn);
|
|
@@ -64844,12 +65012,7 @@ function createOunitechAdapter(credentials, options) {
|
|
|
64844
65012
|
position: null
|
|
64845
65013
|
};
|
|
64846
65014
|
}
|
|
64847
|
-
|
|
64848
|
-
if (row.robotState?.statusFlag === 7 && finite(row.robotId)) {
|
|
64849
|
-
const state = await cloud.getRobotState(row.robotId).catch(() => null);
|
|
64850
|
-
robot.fault = ounitechFault(state?.rtFaultList);
|
|
64851
|
-
}
|
|
64852
|
-
return robot;
|
|
65015
|
+
return ounitechRobotFromHomeRow(row, await liveFor(row));
|
|
64853
65016
|
},
|
|
64854
65017
|
async sendCommand(robotSn, command) {
|
|
64855
65018
|
const options2 = {
|
|
@@ -64964,10 +65127,19 @@ function createOunitechAdapter(credentials, options) {
|
|
|
64964
65127
|
const window = { timeFrom: robotTime(from), timeTo: robotTime(to) };
|
|
64965
65128
|
await run("live state (get-robot-state-v2.0)", ROBOT_CLOUD_PATHS.robotState, { robotId }, async () => {
|
|
64966
65129
|
const s = await cloud.getRobotState(robotId);
|
|
65130
|
+
const r = ounitechRobotFromHomeRow(row, s);
|
|
65131
|
+
const level2 = (...keys) => keys.map((k) => num(reading(s, k))).find((n) => n !== void 0) ?? null;
|
|
65132
|
+
const at2 = updatedAt(s.updateOn);
|
|
64967
65133
|
return {
|
|
65134
|
+
online: r.online,
|
|
65135
|
+
status: r.statusName,
|
|
65136
|
+
job: ounitechJobWords(s),
|
|
65137
|
+
battery: r.batteryPercent,
|
|
65138
|
+
cleanWater: level2("clean-water", "clean-percent"),
|
|
65139
|
+
dirtyWater: level2("dirty-water", "dirty-percent"),
|
|
65140
|
+
fault: r.fault ?? null,
|
|
64968
65141
|
statusFlag: s.statusFlag,
|
|
64969
|
-
|
|
64970
|
-
updateOn: s.updateOn ? new Date(s.updateOn).toISOString() : null,
|
|
65142
|
+
updateOn: at2 !== null ? new Date(at2).toISOString() : null,
|
|
64971
65143
|
x: s.x,
|
|
64972
65144
|
y: s.y,
|
|
64973
65145
|
theta: s.theta,
|
|
@@ -66899,7 +67071,7 @@ function statesByType(entry) {
|
|
|
66899
67071
|
}
|
|
66900
67072
|
var PADBOT_ONLINE_WINDOW_MS = 12e4;
|
|
66901
67073
|
var ms = (t) => typeof t === "string" ? Date.parse(t) : NaN;
|
|
66902
|
-
var
|
|
67074
|
+
var num2 = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
66903
67075
|
function padbotMode(states, now = Date.now()) {
|
|
66904
67076
|
const hb = ms(states["1000"]?.updatedAt);
|
|
66905
67077
|
const online = Number.isFinite(hb) && now - hb <= PADBOT_ONLINE_WINDOW_MS;
|
|
@@ -66907,7 +67079,7 @@ function padbotMode(states, now = Date.now()) {
|
|
|
66907
67079
|
return { online: false, mode: "UNAVAILABLE", statusName: "Not reporting to PadBot" };
|
|
66908
67080
|
const nav = states["1003"] ?? {};
|
|
66909
67081
|
const work = states["1010"] ?? {};
|
|
66910
|
-
const battery =
|
|
67082
|
+
const battery = num2(states["1001"]?.batteryPercent);
|
|
66911
67083
|
const charging = states["1002"]?.chargingState === 1;
|
|
66912
67084
|
const type = Number(nav.actionType) || 0;
|
|
66913
67085
|
const st = Number(nav.actionState) || 0;
|
|
@@ -66944,7 +67116,7 @@ function padbotCommandBlock(states, kind, now = Date.now()) {
|
|
|
66944
67116
|
return "The robot is not reporting to PadBot, so it cannot take a command. Check it is switched on and connected.";
|
|
66945
67117
|
if (states["1006"]?.emergencyStopState === 1)
|
|
66946
67118
|
return "The robot's emergency stop button is pressed. Release the red button on the robot, then try again.";
|
|
66947
|
-
const battery =
|
|
67119
|
+
const battery = num2(states["1001"]?.batteryPercent);
|
|
66948
67120
|
if (kind !== "return" && battery !== null && battery < 10)
|
|
66949
67121
|
return `The battery is at ${battery}%. PadBot robots do not start jobs under 10%; send it to charge first.`;
|
|
66950
67122
|
if (kind === "return" && [1, 2].includes(states["1002"]?.chargingState))
|
|
@@ -66966,7 +67138,7 @@ function padbotReadiness(states, now = Date.now()) {
|
|
|
66966
67138
|
heartbeatAt: st["1000"]?.updatedAt ?? null,
|
|
66967
67139
|
emergencyStopPressed: es.emergencyStopState === 1,
|
|
66968
67140
|
wheelLockState: es.wheelLockState ?? null,
|
|
66969
|
-
batteryPercent:
|
|
67141
|
+
batteryPercent: num2(st["1001"]?.batteryPercent),
|
|
66970
67142
|
chargingState: charging ?? null,
|
|
66971
67143
|
map: { id: st["1007"]?.mapId ?? null, name: st["1007"]?.mapDisplayName ?? null, floor: st["1007"]?.floor ?? null },
|
|
66972
67144
|
navigation: { actionType: nav.actionType ?? null, actionState: nav.actionState ?? null, message: nav.message ?? null, updatedAt: nav.updatedAt ?? null },
|
|
@@ -67023,19 +67195,19 @@ function padbotMotion(m, now) {
|
|
|
67023
67195
|
}
|
|
67024
67196
|
function networkText(net) {
|
|
67025
67197
|
const say = (label2, v) => v < 0 ? ` \xB7 ${label2} ${v} dBm` : ` \xB7 ${label2} signal ${v}`;
|
|
67026
|
-
const wifi =
|
|
67198
|
+
const wifi = num2(net?.wifi?.signalStrength);
|
|
67027
67199
|
if (wifi !== null)
|
|
67028
67200
|
return say("Wi-Fi", wifi);
|
|
67029
|
-
const cell =
|
|
67201
|
+
const cell = num2(net?.cellular?.signalStrength);
|
|
67030
67202
|
return cell !== null ? say("Mobile", cell) : "";
|
|
67031
67203
|
}
|
|
67032
67204
|
function padbotPlanShare(frame2, x, y) {
|
|
67033
|
-
const w =
|
|
67034
|
-
const wx =
|
|
67205
|
+
const w = num2(frame2?.width), h = num2(frame2?.height), r = num2(frame2?.resolution);
|
|
67206
|
+
const wx = num2(x), wy = num2(y);
|
|
67035
67207
|
if (!w || !h || !r || r <= 0 || wx === null || wy === null)
|
|
67036
67208
|
return null;
|
|
67037
|
-
const px = (wx - (
|
|
67038
|
-
const pyUp = (wy - (
|
|
67209
|
+
const px = (wx - (num2(frame2?.originX) ?? 0)) / r;
|
|
67210
|
+
const pyUp = (wy - (num2(frame2?.originY) ?? 0)) / r;
|
|
67039
67211
|
const sx = px / w * 100, sy = 100 - pyUp / h * 100;
|
|
67040
67212
|
if (sx < 0 || sx > 100 || sy < 0 || sy > 100)
|
|
67041
67213
|
return null;
|
|
@@ -67065,9 +67237,9 @@ function padbotCurrentMap(maps, ids2, floor) {
|
|
|
67065
67237
|
}
|
|
67066
67238
|
function padbotChargingPoint(map) {
|
|
67067
67239
|
const piles = (Array.isArray(map?.targetPoints) ? map.targetPoints : []).filter(
|
|
67068
|
-
(t) => Number(t?.targetPointType) === 10 &&
|
|
67240
|
+
(t) => Number(t?.targetPointType) === 10 && num2(t?.worldX) !== null && num2(t?.worldY) !== null
|
|
67069
67241
|
);
|
|
67070
|
-
return piles.length === 1 ? { worldX:
|
|
67242
|
+
return piles.length === 1 ? { worldX: num2(piles[0].worldX), worldY: num2(piles[0].worldY), yaw: piles[0].yaw } : null;
|
|
67071
67243
|
}
|
|
67072
67244
|
var PADBOT_OTHER_MAP_TEXT = "The robot must be on this map first.";
|
|
67073
67245
|
function padbotAlarmFault(alarm) {
|
|
@@ -67162,9 +67334,9 @@ function padbotPatrolRecord(r) {
|
|
|
67162
67334
|
result: PATROL_RESULT[r?.completionState] ?? "unknown",
|
|
67163
67335
|
startedAt: r?.startedAt ?? null,
|
|
67164
67336
|
endedAt: r?.endedAt ?? null,
|
|
67165
|
-
durationS:
|
|
67166
|
-
distanceM:
|
|
67167
|
-
alarmCount:
|
|
67337
|
+
durationS: num2(r?.duration),
|
|
67338
|
+
distanceM: num2(r?.mileage),
|
|
67339
|
+
alarmCount: num2(r?.alarmCount) ?? 0
|
|
67168
67340
|
};
|
|
67169
67341
|
}
|
|
67170
67342
|
function padbotCheckpointVisit(d) {
|
|
@@ -67174,15 +67346,15 @@ function padbotCheckpointVisit(d) {
|
|
|
67174
67346
|
skipped: d?.skipped === true,
|
|
67175
67347
|
imageUrl: d?.visibleLightImageUrl ?? null,
|
|
67176
67348
|
thermalImageUrl: d?.thermalImageUrl ?? null,
|
|
67177
|
-
centerTempC:
|
|
67178
|
-
maxTempC:
|
|
67349
|
+
centerTempC: num2(d?.centerTemperature),
|
|
67350
|
+
maxTempC: num2(d?.maxTemperature)
|
|
67179
67351
|
};
|
|
67180
67352
|
}
|
|
67181
67353
|
var asList = (d) => Array.isArray(d) ? d : Array.isArray(d?.items) ? d.items : Array.isArray(d?.list) ? d.list : Array.isArray(d?.records) ? d.records : [];
|
|
67182
67354
|
var page = (data, map) => ({
|
|
67183
67355
|
items: (Array.isArray(data?.items) ? data.items : []).map(map),
|
|
67184
|
-
total:
|
|
67185
|
-
pages:
|
|
67356
|
+
total: num2(data?.totalItems) ?? 0,
|
|
67357
|
+
pages: num2(data?.totalPages) ?? 0
|
|
67186
67358
|
});
|
|
67187
67359
|
function createPadbotClient(credentials, options = {}) {
|
|
67188
67360
|
const baseUrl = (options.baseUrl ?? padbotBaseUrl()).replace(/\/+$/, "");
|
|
@@ -67404,7 +67576,7 @@ function createPadbotAdapter(credentials, options = {}) {
|
|
|
67404
67576
|
const { online, mode, statusName } = padbotMode(st, now());
|
|
67405
67577
|
const floor = st["1007"]?.floor;
|
|
67406
67578
|
const restBatteryAt = ms(st["1001"]?.updatedAt);
|
|
67407
|
-
const battery = heard?.batteryPercent !== void 0 && (!Number.isFinite(restBatteryAt) || (heard.batteryAt ?? 0) > restBatteryAt) ? heard.batteryPercent :
|
|
67579
|
+
const battery = heard?.batteryPercent !== void 0 && (!Number.isFinite(restBatteryAt) || (heard.batteryAt ?? 0) > restBatteryAt) ? heard.batteryPercent : num2(st["1001"]?.batteryPercent);
|
|
67408
67580
|
const failed = lastFailure.get(`${account}:${robotSn}`);
|
|
67409
67581
|
const showFailure = online && failed && now() - failed.at < PADBOT_FAILURE_SHOW_MS && /^(Standing by|Charging)/.test(statusName);
|
|
67410
67582
|
const robot = {
|
|
@@ -67431,15 +67603,15 @@ function createPadbotAdapter(credentials, options = {}) {
|
|
|
67431
67603
|
return robot;
|
|
67432
67604
|
const [loc, openA] = await Promise.all([locRead, alarmRead]);
|
|
67433
67605
|
const alarm = robot.fault ? null : openA;
|
|
67434
|
-
const lx =
|
|
67435
|
-
const ly =
|
|
67606
|
+
const lx = num2(loc?.worldX ?? loc?.x);
|
|
67607
|
+
const ly = num2(loc?.worldY ?? loc?.y);
|
|
67436
67608
|
if (typeof loc?.mapId === "string" && loc.mapId)
|
|
67437
67609
|
robot.mapId = loc.mapId;
|
|
67438
67610
|
const map = await maps(robotSn).then((all) => padbotCurrentMap(all, [robot.mapId, st["1007"]?.mapId], robot.floorNo)).catch(() => null);
|
|
67439
67611
|
if (map)
|
|
67440
67612
|
robot.mapId = map.mapId;
|
|
67441
67613
|
if (lx !== null && ly !== null) {
|
|
67442
|
-
robot.position = { x: lx, y: ly, theta: (
|
|
67614
|
+
robot.position = { x: lx, y: ly, theta: (num2(loc.yaw ?? loc.r) ?? 0) * Math.PI / 180 };
|
|
67443
67615
|
robot.planPosition = padbotPlanShare(map, lx, ly);
|
|
67444
67616
|
}
|
|
67445
67617
|
const mo = motions.get(`${account}:${robotSn}`);
|
|
@@ -67459,7 +67631,7 @@ function createPadbotAdapter(credentials, options = {}) {
|
|
|
67459
67631
|
const onCharger = [1, 2].includes(st["1002"]?.chargingState);
|
|
67460
67632
|
const pile = onCharger && !robot.position ? padbotChargingPoint(map) : null;
|
|
67461
67633
|
if (pile) {
|
|
67462
|
-
robot.position = { x: pile.worldX, y: pile.worldY, theta: (
|
|
67634
|
+
robot.position = { x: pile.worldX, y: pile.worldY, theta: (num2(pile.yaw) ?? 0) * Math.PI / 180 };
|
|
67463
67635
|
robot.planPosition = padbotPlanShare(map, pile.worldX, pile.worldY);
|
|
67464
67636
|
if (!going || going.done)
|
|
67465
67637
|
robot.statusName = `${robot.statusName} \xB7 At charger`;
|
|
@@ -67559,12 +67731,12 @@ function createPadbotAdapter(credentials, options = {}) {
|
|
|
67559
67731
|
throw new import_node_server_utils164.BadRequestError(block);
|
|
67560
67732
|
lastFailure.delete(`${account}:${robotSn}`);
|
|
67561
67733
|
const heard = padbotLive(account, robotSn)?.pose;
|
|
67562
|
-
const lx =
|
|
67563
|
-
const ly =
|
|
67734
|
+
const lx = num2(where?.loc?.worldX);
|
|
67735
|
+
const ly = num2(where?.loc?.worldY);
|
|
67564
67736
|
let target = null;
|
|
67565
67737
|
if (command.kind === "go-to-point") {
|
|
67566
67738
|
const pt = (where?.all ?? []).flatMap((m) => Array.isArray(m?.targetPoints) ? m.targetPoints : []).find((t) => t?.targetPointId === command.targetPointId);
|
|
67567
|
-
target = { name: String(pt?.name || "the chosen point"), x:
|
|
67739
|
+
target = { name: String(pt?.name || "the chosen point"), x: num2(pt?.worldX), y: num2(pt?.worldY) };
|
|
67568
67740
|
}
|
|
67569
67741
|
motion = {
|
|
67570
67742
|
kind: command.kind,
|
|
@@ -68616,11 +68788,11 @@ function decodeTrackingFrame(data) {
|
|
|
68616
68788
|
if (!parsed || typeof parsed !== "object")
|
|
68617
68789
|
return null;
|
|
68618
68790
|
const frame2 = parsed;
|
|
68619
|
-
const
|
|
68620
|
-
const x =
|
|
68621
|
-
const y =
|
|
68622
|
-
const theta =
|
|
68623
|
-
const statusFlag =
|
|
68791
|
+
const num4 = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
68792
|
+
const x = num4(frame2.x);
|
|
68793
|
+
const y = num4(frame2.y);
|
|
68794
|
+
const theta = num4(frame2.theta);
|
|
68795
|
+
const statusFlag = num4(frame2.status);
|
|
68624
68796
|
if (x === null || y === null || theta === null || statusFlag === null) {
|
|
68625
68797
|
return null;
|
|
68626
68798
|
}
|
|
@@ -69144,22 +69316,22 @@ function useRobotFaultSweep(deps) {
|
|
|
69144
69316
|
if (!robot)
|
|
69145
69317
|
return;
|
|
69146
69318
|
out.robots++;
|
|
69147
|
-
let
|
|
69319
|
+
let reading2;
|
|
69148
69320
|
try {
|
|
69149
69321
|
const state = deps?.readState ? await deps.readState(robot) : await readRobotState(robot, { timeoutMs: cfg.timeoutMs });
|
|
69150
69322
|
if (deps?.readState)
|
|
69151
69323
|
rememberRobotState(robot, state);
|
|
69152
|
-
|
|
69153
|
-
if (
|
|
69154
|
-
Object.assign(
|
|
69324
|
+
reading2 = { ok: true, online: state.online === true, fault: state.fault ?? null };
|
|
69325
|
+
if (reading2.fault)
|
|
69326
|
+
Object.assign(reading2.fault, { floorNo: state.floorNo ?? null });
|
|
69155
69327
|
} catch (error) {
|
|
69156
69328
|
if (deps?.readState)
|
|
69157
69329
|
rememberRobotState(robot, null);
|
|
69158
|
-
|
|
69330
|
+
reading2 = { ok: false, error: error?.message ?? "no answer" };
|
|
69159
69331
|
}
|
|
69160
69332
|
const records = byRobot2.get(String(robot._id)) ?? [];
|
|
69161
69333
|
const offlineRec = records.find((r) => r.code === OFFLINE_FAULT_CODE);
|
|
69162
|
-
const plan = planFaultChanges(records,
|
|
69334
|
+
const plan = planFaultChanges(records, reading2);
|
|
69163
69335
|
for (const id of plan.clear) {
|
|
69164
69336
|
await repo.clearFault(id);
|
|
69165
69337
|
out.cleared++;
|
|
@@ -69172,7 +69344,7 @@ function useRobotFaultSweep(deps) {
|
|
|
69172
69344
|
out.alerts++;
|
|
69173
69345
|
}
|
|
69174
69346
|
for (const f of plan.open) {
|
|
69175
|
-
const detail =
|
|
69347
|
+
const detail = reading2.ok && reading2.fault?.code === f.code ? reading2.fault : null;
|
|
69176
69348
|
const r = await openFaultRecord(robot, detail ? { ...detail, ...f } : f, { notify: true, notifyFn: deps?.notifyFn });
|
|
69177
69349
|
if (r.opened)
|
|
69178
69350
|
out.opened++;
|
|
@@ -70490,7 +70662,7 @@ function measurementScale(vertices, meas) {
|
|
|
70490
70662
|
return est.length ? est.reduce((n, v) => n + v, 0) / est.length : null;
|
|
70491
70663
|
}
|
|
70492
70664
|
var pval = (p) => Array.isArray(p) ? p[1] : p;
|
|
70493
|
-
var
|
|
70665
|
+
var num3 = (v, fallback = 0) => v !== null && v !== "" && Number.isFinite(Number(v)) ? Number(v) : fallback;
|
|
70494
70666
|
var str60 = (v) => String(v ?? "").slice(0, 60);
|
|
70495
70667
|
function decodeParams(raw) {
|
|
70496
70668
|
const out = {};
|
|
@@ -70510,7 +70682,7 @@ function fileVertexParams(raw) {
|
|
|
70510
70682
|
const v = raw[k];
|
|
70511
70683
|
if (v === void 0 || v === null)
|
|
70512
70684
|
continue;
|
|
70513
|
-
out[k] = k.startsWith("is_") ? Boolean(v) : k === "merge_radius" ? clamp3(
|
|
70685
|
+
out[k] = k.startsWith("is_") ? Boolean(v) : k === "merge_radius" ? clamp3(num3(v), 0, 100) : str60(v);
|
|
70514
70686
|
}
|
|
70515
70687
|
return out;
|
|
70516
70688
|
}
|
|
@@ -70583,14 +70755,14 @@ function fromBuildingYaml(doc) {
|
|
|
70583
70755
|
const raw = names.map((name) => {
|
|
70584
70756
|
const y = doc.levels[name] ?? {};
|
|
70585
70757
|
const vertices = (Array.isArray(y.vertices) ? y.vertices : []).map((v) => ({
|
|
70586
|
-
x:
|
|
70587
|
-
y:
|
|
70758
|
+
x: num3(v?.[0]),
|
|
70759
|
+
y: num3(v?.[1]) * flip,
|
|
70588
70760
|
name: str60(v?.[3]),
|
|
70589
70761
|
params: fileVertexParams(decodeParams(v?.[4]))
|
|
70590
70762
|
}));
|
|
70591
|
-
const fiducials = (Array.isArray(y.fiducials) ? y.fiducials : []).map((f) => ({ x:
|
|
70592
|
-
const meas = edgeList(y.measurements).map((e) => [e.a, e.b,
|
|
70593
|
-
return { name: str60(name), y, vertices, fiducials, meas, elevation:
|
|
70763
|
+
const fiducials = (Array.isArray(y.fiducials) ? y.fiducials : []).map((f) => ({ x: num3(f?.[0]), y: num3(f?.[1]) * flip, name: str60(f?.[2]) }));
|
|
70764
|
+
const meas = edgeList(y.measurements).map((e) => [e.a, e.b, num3(e.p.distance)]);
|
|
70765
|
+
return { name: str60(name), y, vertices, fiducials, meas, elevation: num3(y.elevation) };
|
|
70594
70766
|
});
|
|
70595
70767
|
const refName = raw.some((l) => l.name === doc.reference_level_name) ? String(doc.reference_level_name) : raw[0].name;
|
|
70596
70768
|
const ref = raw.find((l) => l.name === refName);
|
|
@@ -70664,20 +70836,20 @@ function fromBuildingYaml(doc) {
|
|
|
70664
70836
|
const extra = {};
|
|
70665
70837
|
if (e.p.texture_name !== void 0 && e.p.texture_name !== "default")
|
|
70666
70838
|
extra.textureName = str60(e.p.texture_name);
|
|
70667
|
-
if (e.p.alpha !== void 0 &&
|
|
70668
|
-
extra.alpha = clamp3(
|
|
70669
|
-
if (e.p.texture_height !== void 0 &&
|
|
70670
|
-
extra.textureHeight = clamp3(
|
|
70671
|
-
if (e.p.texture_width !== void 0 &&
|
|
70672
|
-
extra.textureWidth = clamp3(
|
|
70673
|
-
if (e.p.texture_scale !== void 0 &&
|
|
70674
|
-
extra.textureScale = clamp3(
|
|
70839
|
+
if (e.p.alpha !== void 0 && num3(e.p.alpha, 1) !== 1)
|
|
70840
|
+
extra.alpha = clamp3(num3(e.p.alpha, 1), 0, 1);
|
|
70841
|
+
if (e.p.texture_height !== void 0 && num3(e.p.texture_height, 2.5) !== 2.5)
|
|
70842
|
+
extra.textureHeight = clamp3(num3(e.p.texture_height, 2.5), 0, 100);
|
|
70843
|
+
if (e.p.texture_width !== void 0 && num3(e.p.texture_width, 1) !== 1)
|
|
70844
|
+
extra.textureWidth = clamp3(num3(e.p.texture_width, 1), 0, 100);
|
|
70845
|
+
if (e.p.texture_scale !== void 0 && num3(e.p.texture_scale, 1) !== 1)
|
|
70846
|
+
extra.textureScale = clamp3(num3(e.p.texture_scale, 1), 0, 100);
|
|
70675
70847
|
level2.walls.push({ v1: ids2[e.a], v2: ids2[e.b], ...extra });
|
|
70676
70848
|
}
|
|
70677
70849
|
for (const e of edgeList(lv.y.doors).filter(ok)) {
|
|
70678
70850
|
const type = String(e.p.type ?? "hinged");
|
|
70679
|
-
const degrees = clamp3(
|
|
70680
|
-
const ratio = clamp3(
|
|
70851
|
+
const degrees = clamp3(num3(e.p.motion_degrees, 90), -360, 360);
|
|
70852
|
+
const ratio = clamp3(num3(e.p.right_left_ratio, 1), 0, 10);
|
|
70681
70853
|
const plugin = str60(e.p.plugin ?? "normal");
|
|
70682
70854
|
level2.doors.push({
|
|
70683
70855
|
name: str60(e.p.name ?? "").trim() || `door_${level2.doors.length + 1}`,
|
|
@@ -70686,13 +70858,13 @@ function fromBuildingYaml(doc) {
|
|
|
70686
70858
|
type: ["hinged", "double_hinged", "sliding", "double_sliding"].includes(type) ? type : "hinged",
|
|
70687
70859
|
...e.p.motion_axis === "end" ? { motionAxis: "end" } : {},
|
|
70688
70860
|
...degrees !== 90 ? { motionDegrees: degrees } : {},
|
|
70689
|
-
...
|
|
70861
|
+
...num3(e.p.motion_direction, 1) < 0 ? { motionDirection: -1 } : {},
|
|
70690
70862
|
...ratio !== 1 ? { rightLeftRatio: ratio } : {},
|
|
70691
70863
|
...plugin !== "normal" ? { plugin } : {}
|
|
70692
70864
|
});
|
|
70693
70865
|
}
|
|
70694
70866
|
for (const e of edgeList(lv.y.lanes).filter(ok)) {
|
|
70695
|
-
const gi = Math.round(
|
|
70867
|
+
const gi = Math.round(num3(e.p.graph_idx, 0));
|
|
70696
70868
|
if (gi < 0 || gi > 8) {
|
|
70697
70869
|
warnings.push(`Floor ${lv.name}: a lane on graph ${gi} was skipped (graphs 0-8 only).`);
|
|
70698
70870
|
continue;
|
|
@@ -70705,14 +70877,14 @@ function fromBuildingYaml(doc) {
|
|
|
70705
70877
|
graphIdx: gi,
|
|
70706
70878
|
bidirectional: e.p.bidirectional === true,
|
|
70707
70879
|
...orientation ? { orientation } : {},
|
|
70708
|
-
...
|
|
70880
|
+
...num3(e.p.speed_limit) > 0 ? { speedLimit: clamp3(num3(e.p.speed_limit), 0, 20) } : {},
|
|
70709
70881
|
...e.p.mutex ? { mutex: str60(e.p.mutex) } : {},
|
|
70710
70882
|
...e.p.demo_mock_floor_name ? { demoMockFloorName: str60(e.p.demo_mock_floor_name) } : {},
|
|
70711
70883
|
...e.p.demo_mock_lift_name ? { demoMockLiftName: str60(e.p.demo_mock_lift_name) } : {}
|
|
70712
70884
|
});
|
|
70713
70885
|
}
|
|
70714
70886
|
for (const e of edgeList(lv.y.measurements).filter(ok)) {
|
|
70715
|
-
const d =
|
|
70887
|
+
const d = num3(e.p.distance);
|
|
70716
70888
|
if (d >= 0.01)
|
|
70717
70889
|
level2.measurements.push({ v1: ids2[e.a], v2: ids2[e.b], distanceM: Math.min(d, 1e4) });
|
|
70718
70890
|
}
|
|
@@ -70743,14 +70915,14 @@ function fromBuildingYaml(doc) {
|
|
|
70743
70915
|
if (ly?.reference_floor_name && !levelNames.has(ly.reference_floor_name)) {
|
|
70744
70916
|
warnings.push(`Lift ${str60(liftName)}: its reference floor "${str60(ly.reference_floor_name)}" is not in the file; ${refName} was used.`);
|
|
70745
70917
|
}
|
|
70746
|
-
const c = applyXform(xf(refFloor), { x:
|
|
70918
|
+
const c = applyXform(xf(refFloor), { x: num3(ly?.x), y: num3(ly?.y) * flip });
|
|
70747
70919
|
const doors = Object.entries(ly?.doors ?? {}).slice(0, 8).map(([dn, d]) => ({
|
|
70748
70920
|
name: str60(dn),
|
|
70749
|
-
type: MSG_LIFT_DOOR_TYPE[Math.round(
|
|
70750
|
-
x: r3(clamp3(
|
|
70751
|
-
y: r3(clamp3(
|
|
70752
|
-
widthM: r3(clamp3(
|
|
70753
|
-
axisYawRad: clamp3(
|
|
70921
|
+
type: MSG_LIFT_DOOR_TYPE[Math.round(num3(d?.door_type, 2))] ?? "double_sliding",
|
|
70922
|
+
x: r3(clamp3(num3(d?.x), -20, 20)),
|
|
70923
|
+
y: r3(clamp3(num3(d?.y), -20, 20)),
|
|
70924
|
+
widthM: r3(clamp3(num3(d?.width, 1), 0.3, 10)),
|
|
70925
|
+
axisYawRad: clamp3(num3(d?.motion_axis_orientation), -10, 10)
|
|
70754
70926
|
}));
|
|
70755
70927
|
const doorNames = new Set(doors.map((d) => d.name));
|
|
70756
70928
|
const levelDoors = Object.entries(ly?.level_doors ?? {}).filter(([level2]) => levelNames.has(level2)).map(([level2, list]) => ({ level: level2, doors: (Array.isArray(list) ? list : []).map(str60).filter((d) => doorNames.has(d)) }));
|
|
@@ -70759,9 +70931,9 @@ function fromBuildingYaml(doc) {
|
|
|
70759
70931
|
name: str60(liftName),
|
|
70760
70932
|
x: r3(c.x),
|
|
70761
70933
|
y: r3(c.y),
|
|
70762
|
-
yawRad: clamp3(
|
|
70763
|
-
widthM: r3(clamp3(
|
|
70764
|
-
depthM: r3(clamp3(
|
|
70934
|
+
yawRad: clamp3(num3(ly?.yaw), -10, 10),
|
|
70935
|
+
widthM: r3(clamp3(num3(ly?.width, 2), 0.3, 20)),
|
|
70936
|
+
depthM: r3(clamp3(num3(ly?.depth, 2), 0.3, 20)),
|
|
70765
70937
|
referenceFloor: refFloor,
|
|
70766
70938
|
...floorName(ly?.initial_floor_name) ? { initialFloor: floorName(ly.initial_floor_name) } : {},
|
|
70767
70939
|
...floorName(ly?.lowest_floor) ? { lowestFloor: floorName(ly.lowest_floor) } : {},
|
|
@@ -70815,7 +70987,7 @@ function fromNavGraph(doc, graphIdx) {
|
|
|
70815
70987
|
level2.vertices = verts.map((v, k) => {
|
|
70816
70988
|
const p = v?.[2] && typeof v[2] === "object" ? v[2] : {};
|
|
70817
70989
|
const params = fileVertexParams(p);
|
|
70818
|
-
return { id: `v${k + 1}`, x: r3(
|
|
70990
|
+
return { id: `v${k + 1}`, x: r3(num3(v?.[0])), y: r3(num3(v?.[1])), ...p.name ? { name: str60(p.name) } : {}, ...Object.keys(params).length ? { params } : {} };
|
|
70819
70991
|
});
|
|
70820
70992
|
if (Array.isArray(ly?.lanes) && ly.lanes.length > 1e4)
|
|
70821
70993
|
throw new RmfImportError(`Floor ${str60(name)} has more than 10000 one-way lanes - more than a building map can hold.`);
|
|
@@ -70855,7 +71027,7 @@ function fromNavGraph(doc, graphIdx) {
|
|
|
70855
71027
|
graphIdx,
|
|
70856
71028
|
bidirectional,
|
|
70857
71029
|
...o === "forward" || o === "backward" ? { orientation: o } : {},
|
|
70858
|
-
...
|
|
71030
|
+
...num3(p.speed_limit) > 0 ? { speedLimit: clamp3(num3(p.speed_limit), 0, 20) } : {},
|
|
70859
71031
|
...p.mutex ? { mutex: str60(p.mutex) } : {},
|
|
70860
71032
|
...p.demo_mock_floor_name ? { demoMockFloorName: str60(p.demo_mock_floor_name) } : {},
|
|
70861
71033
|
...p.demo_mock_lift_name ? { demoMockLiftName: str60(p.demo_mock_lift_name) } : {}
|
|
@@ -83074,7 +83246,7 @@ function useAccessManagementController() {
|
|
|
83074
83246
|
liftName,
|
|
83075
83247
|
unit = [],
|
|
83076
83248
|
createdAt,
|
|
83077
|
-
updatedAt,
|
|
83249
|
+
updatedAt: updatedAt2,
|
|
83078
83250
|
startDate,
|
|
83079
83251
|
endDate,
|
|
83080
83252
|
isWinsland
|
|
@@ -83110,7 +83282,7 @@ function useAccessManagementController() {
|
|
|
83110
83282
|
startDate,
|
|
83111
83283
|
endDate,
|
|
83112
83284
|
createdAt,
|
|
83113
|
-
updatedAt,
|
|
83285
|
+
updatedAt: updatedAt2,
|
|
83114
83286
|
isWinsland
|
|
83115
83287
|
});
|
|
83116
83288
|
if (error) {
|
|
@@ -83132,7 +83304,7 @@ function useAccessManagementController() {
|
|
|
83132
83304
|
startDate,
|
|
83133
83305
|
endDate,
|
|
83134
83306
|
createdAt,
|
|
83135
|
-
updatedAt,
|
|
83307
|
+
updatedAt: updatedAt2,
|
|
83136
83308
|
isWinsland
|
|
83137
83309
|
});
|
|
83138
83310
|
return res.status(201).json({
|
|
@@ -106835,6 +107007,7 @@ function usePersonalEmergencyChainController() {
|
|
|
106835
107007
|
OUNITECH_BRAND,
|
|
106836
107008
|
OUNITECH_BRAND_DEFINITION,
|
|
106837
107009
|
OUNITECH_CAPABILITIES,
|
|
107010
|
+
OUNITECH_FRESH_MS,
|
|
106838
107011
|
OUNITECH_PENDING_COMMANDS,
|
|
106839
107012
|
OUNITECH_REGION_BASE_URLS,
|
|
106840
107013
|
OUNITECH_TIMESTAMP_FORMAT,
|
|
@@ -107202,6 +107375,7 @@ function usePersonalEmergencyChainController() {
|
|
|
107202
107375
|
orgRoleSets,
|
|
107203
107376
|
orgSchema,
|
|
107204
107377
|
ounitechFault,
|
|
107378
|
+
ounitechJobWords,
|
|
107205
107379
|
ounitechRobotFromHomeRow,
|
|
107206
107380
|
overnight_parking_requests_namespace_collection,
|
|
107207
107381
|
padbotAccountKey,
|