@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.mjs
CHANGED
|
@@ -3703,6 +3703,63 @@ function useMemberRepo() {
|
|
|
3703
3703
|
throw error;
|
|
3704
3704
|
}
|
|
3705
3705
|
}
|
|
3706
|
+
async function peoplePage(query2, page2, limit, rowStages) {
|
|
3707
|
+
const noSite = { $in: ["", null] };
|
|
3708
|
+
const orgWide = await collection.distinct("user", {
|
|
3709
|
+
org: query2.org,
|
|
3710
|
+
type: query2.type,
|
|
3711
|
+
siteId: noSite,
|
|
3712
|
+
status: { $ne: "deleted" }
|
|
3713
|
+
});
|
|
3714
|
+
const { type: orgType, siteId: pinned, ...rest } = query2;
|
|
3715
|
+
const limitedRow = {
|
|
3716
|
+
siteId: pinned ?? { $nin: ["", null] },
|
|
3717
|
+
user: { $nin: orgWide }
|
|
3718
|
+
};
|
|
3719
|
+
const head = [
|
|
3720
|
+
// `$text` (search) may only sit in the first stage.
|
|
3721
|
+
{ $match: { ...rest, type: { $nin: ["resident", "admin"] } } },
|
|
3722
|
+
{ $match: pinned ? limitedRow : { $or: [{ type: orgType }, limitedRow] } },
|
|
3723
|
+
{ $sort: { _id: -1 } },
|
|
3724
|
+
{
|
|
3725
|
+
$group: {
|
|
3726
|
+
_id: { $ifNull: ["$user", "$_id"] },
|
|
3727
|
+
doc: { $first: "$$ROOT" },
|
|
3728
|
+
orgWide: {
|
|
3729
|
+
$max: { $and: [{ $eq: ["$type", orgType] }, { $in: [{ $ifNull: ["$siteId", ""] }, ["", null]] }] }
|
|
3730
|
+
},
|
|
3731
|
+
limitedTo: { $push: { siteId: "$siteId", siteName: "$siteName", role: "$role" } }
|
|
3732
|
+
}
|
|
3733
|
+
},
|
|
3734
|
+
{
|
|
3735
|
+
$replaceRoot: {
|
|
3736
|
+
newRoot: { $mergeObjects: ["$doc", { limitedTo: { $cond: ["$orgWide", [], "$limitedTo"] } }] }
|
|
3737
|
+
}
|
|
3738
|
+
}
|
|
3739
|
+
];
|
|
3740
|
+
const items = await collection.aggregate([...head, { $sort: { _id: -1 } }, { $skip: page2 * limit }, { $limit: limit }, ...rowStages]).toArray();
|
|
3741
|
+
const [counted] = await collection.aggregate([...head, { $count: "n" }]).toArray();
|
|
3742
|
+
const ids2 = (key) => [...new Set(items.flatMap((i) => (i.limitedTo ?? []).map((l) => l[key]?.toString?.() ?? "")))].filter((id) => ObjectId14.isValid(id)).map((id) => new ObjectId14(id));
|
|
3743
|
+
const [sites, roles] = await Promise.all([
|
|
3744
|
+
db2.collection("sites").find({ _id: { $in: ids2("siteId") } }, { projection: { name: 1 } }).toArray(),
|
|
3745
|
+
db2.collection("roles").find({ _id: { $in: ids2("role") } }, { projection: { name: 1 } }).toArray()
|
|
3746
|
+
]);
|
|
3747
|
+
const nameOf = (list) => new Map(list.map((d) => [d._id.toString(), d.name ?? ""]));
|
|
3748
|
+
const siteName = nameOf(sites);
|
|
3749
|
+
const roleName = nameOf(roles);
|
|
3750
|
+
for (const item of items) {
|
|
3751
|
+
const bySite = /* @__PURE__ */ new Map();
|
|
3752
|
+
for (const l of item.limitedTo ?? []) {
|
|
3753
|
+
const site = l.siteId?.toString?.() ?? "";
|
|
3754
|
+
const role = roleName.get(l.role?.toString?.() ?? "") ?? "";
|
|
3755
|
+
const entry = bySite.get(site) ?? { siteId: site, siteName: siteName.get(site) || l.siteName || "", roleName: "" };
|
|
3756
|
+
entry.roleName = [entry.roleName, role].filter(Boolean).join(", ");
|
|
3757
|
+
bySite.set(site, entry);
|
|
3758
|
+
}
|
|
3759
|
+
item.limitedTo = [...bySite.values()].sort((a, b) => a.siteName.localeCompare(b.siteName));
|
|
3760
|
+
}
|
|
3761
|
+
return { items, length: counted?.n ?? 0 };
|
|
3762
|
+
}
|
|
3706
3763
|
async function getAll({
|
|
3707
3764
|
search = "",
|
|
3708
3765
|
page: page2 = 1,
|
|
@@ -3713,7 +3770,8 @@ function useMemberRepo() {
|
|
|
3713
3770
|
siteIds,
|
|
3714
3771
|
type = "",
|
|
3715
3772
|
status = "",
|
|
3716
|
-
roleState = ""
|
|
3773
|
+
roleState = "",
|
|
3774
|
+
people = false
|
|
3717
3775
|
}) {
|
|
3718
3776
|
limit = limit > 0 ? limit : 10;
|
|
3719
3777
|
search = search || "";
|
|
@@ -3772,6 +3830,9 @@ function useMemberRepo() {
|
|
|
3772
3830
|
query2.$or = await otherSiteRoleClauses2(query2.org);
|
|
3773
3831
|
cacheOptions.roleState = roleState;
|
|
3774
3832
|
}
|
|
3833
|
+
const peopleMode = people && type === "organization" && !!query2.org && !siteId && !roleState;
|
|
3834
|
+
if (peopleMode)
|
|
3835
|
+
cacheOptions.people = 1;
|
|
3775
3836
|
const cacheKey = makeCacheKey5(namespace_collection3, cacheOptions);
|
|
3776
3837
|
const cachedData = await getCache(cacheKey);
|
|
3777
3838
|
if (cachedData) {
|
|
@@ -3779,15 +3840,7 @@ function useMemberRepo() {
|
|
|
3779
3840
|
return cachedData;
|
|
3780
3841
|
}
|
|
3781
3842
|
try {
|
|
3782
|
-
const
|
|
3783
|
-
{ $match: query2 },
|
|
3784
|
-
{ $sort: { _id: -1 } },
|
|
3785
|
-
// `paginate()` only LABELS the range ("11-20 of 42"); it does not
|
|
3786
|
-
// slice. Without this $skip every page returned page 1's rows under
|
|
3787
|
-
// a later page's heading. `page` is already zero-based above, and
|
|
3788
|
-
// this is the same $skip/$limit pair the two getOrgs* reads use.
|
|
3789
|
-
{ $skip: page2 * limit },
|
|
3790
|
-
{ $limit: limit },
|
|
3843
|
+
const rowStages = [
|
|
3791
3844
|
{
|
|
3792
3845
|
$lookup: {
|
|
3793
3846
|
from: "roles",
|
|
@@ -3861,11 +3914,29 @@ function useMemberRepo() {
|
|
|
3861
3914
|
dateInvited: { $ifNull: ["$dateInvited", "$createdAt"] },
|
|
3862
3915
|
siteId: 1,
|
|
3863
3916
|
siteName: 1,
|
|
3864
|
-
type: 1
|
|
3917
|
+
type: 1,
|
|
3918
|
+
limitedTo: 1
|
|
3865
3919
|
}
|
|
3866
3920
|
}
|
|
3867
|
-
]
|
|
3868
|
-
|
|
3921
|
+
];
|
|
3922
|
+
let items;
|
|
3923
|
+
let length;
|
|
3924
|
+
if (peopleMode) {
|
|
3925
|
+
({ items, length } = await peoplePage(query2, page2, limit, rowStages));
|
|
3926
|
+
} else {
|
|
3927
|
+
items = await collection.aggregate([
|
|
3928
|
+
{ $match: query2 },
|
|
3929
|
+
{ $sort: { _id: -1 } },
|
|
3930
|
+
// `paginate()` only LABELS the range ("11-20 of 42"); it does not
|
|
3931
|
+
// slice. Without this $skip every page returned page 1's rows under
|
|
3932
|
+
// a later page's heading. `page` is already zero-based above, and
|
|
3933
|
+
// this is the same $skip/$limit pair the two getOrgs* reads use.
|
|
3934
|
+
{ $skip: page2 * limit },
|
|
3935
|
+
{ $limit: limit },
|
|
3936
|
+
...rowStages
|
|
3937
|
+
]).toArray();
|
|
3938
|
+
length = await collection.countDocuments(query2);
|
|
3939
|
+
}
|
|
3869
3940
|
const data = { ...paginate5(items, page2, limit, length), total: length };
|
|
3870
3941
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
3871
3942
|
logger8.info(`Cache set for key: ${cacheKey}`);
|
|
@@ -10133,15 +10204,15 @@ function robotConnectionText(robotName, online, minutes) {
|
|
|
10133
10204
|
}
|
|
10134
10205
|
var OFFLINE_FAULT_CODE = "OFFLINE";
|
|
10135
10206
|
var SAME_OCCURRENCE_MS = 1e3;
|
|
10136
|
-
function planFaultChanges(open,
|
|
10207
|
+
function planFaultChanges(open, reading2) {
|
|
10137
10208
|
const byCode = new Map(open.map((r) => [r.code, r]));
|
|
10138
10209
|
const want = [];
|
|
10139
|
-
const offline = !
|
|
10210
|
+
const offline = !reading2.ok || reading2.online === false;
|
|
10140
10211
|
if (offline) {
|
|
10141
|
-
const reason =
|
|
10212
|
+
const reason = reading2.ok ? "" : String(reading2.error ?? "").slice(0, 200);
|
|
10142
10213
|
want.push({ code: OFFLINE_FAULT_CODE, message: reason ? `Not reporting: ${reason}` : "Not reporting" });
|
|
10143
10214
|
}
|
|
10144
|
-
const fault =
|
|
10215
|
+
const fault = reading2.ok && reading2.fault?.code ? reading2.fault : null;
|
|
10145
10216
|
if (fault)
|
|
10146
10217
|
want.push({ code: fault.code, message: fault.message || fault.code, since: fault.since });
|
|
10147
10218
|
const clear = [];
|
|
@@ -10173,12 +10244,12 @@ function planFaultChanges(open, reading) {
|
|
|
10173
10244
|
return { open: toOpen, clear, refresh };
|
|
10174
10245
|
}
|
|
10175
10246
|
function robotFaultSweepConfig(env2 = process.env) {
|
|
10176
|
-
const
|
|
10247
|
+
const num4 = (v, def, min, max) => {
|
|
10177
10248
|
const n = Math.floor(Number(v));
|
|
10178
10249
|
return Number.isFinite(n) && String(v ?? "").trim() !== "" ? Math.min(max, Math.max(min, n)) : def;
|
|
10179
10250
|
};
|
|
10180
10251
|
const enabled = String(env2.ROBOT_FAULT_SWEEP ?? "on").trim().toLowerCase() !== "off";
|
|
10181
|
-
const asked =
|
|
10252
|
+
const asked = num4(env2.ROBOT_FAULT_SWEEP_SECONDS, 60, 10, 3600);
|
|
10182
10253
|
const pick = (steps, v) => steps.filter((d) => d <= v).pop() ?? steps[0];
|
|
10183
10254
|
const subMinute = asked < 60;
|
|
10184
10255
|
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));
|
|
@@ -10188,9 +10259,9 @@ function robotFaultSweepConfig(env2 = process.env) {
|
|
|
10188
10259
|
enabled,
|
|
10189
10260
|
seconds,
|
|
10190
10261
|
cron: cron2,
|
|
10191
|
-
concurrency:
|
|
10192
|
-
timeoutMs:
|
|
10193
|
-
offlineAlertMinutes:
|
|
10262
|
+
concurrency: num4(env2.ROBOT_FAULT_SWEEP_CONCURRENCY, 4, 1, 16),
|
|
10263
|
+
timeoutMs: num4(env2.ROBOT_FAULT_SWEEP_TIMEOUT_MS, 1e4, 1e3, 6e4),
|
|
10264
|
+
offlineAlertMinutes: num4(env2.ROBOT_OFFLINE_ALERT_MINUTES, 10, 1, 1440)
|
|
10194
10265
|
};
|
|
10195
10266
|
}
|
|
10196
10267
|
function withTimeout(promise, ms2, label2 = "Timed out") {
|
|
@@ -23028,6 +23099,29 @@ async function requireNotOwnerUnlessOwner(req, orgId, targetUser) {
|
|
|
23028
23099
|
return;
|
|
23029
23100
|
throw new UnauthorizedError3("Only the organisation's owner can change the owner's access.");
|
|
23030
23101
|
}
|
|
23102
|
+
async function requireMemberGrantor(req, input) {
|
|
23103
|
+
const { orgId, targetUser, roleId } = input;
|
|
23104
|
+
const siteId = input.siteId?.toString() ?? "";
|
|
23105
|
+
const id = await requireSiteAccessEditor(req, orgId);
|
|
23106
|
+
await requireNotOwnerUnlessOwner(req, orgId, targetUser);
|
|
23107
|
+
if (staffMayBypass(await resolveInviteActor(id)))
|
|
23108
|
+
return;
|
|
23109
|
+
if (siteId)
|
|
23110
|
+
await requireSiteServedByOrg(siteId, orgId);
|
|
23111
|
+
if (await hasOrgOwnership(id, orgId))
|
|
23112
|
+
return;
|
|
23113
|
+
const db2 = useAtlas36.getDb();
|
|
23114
|
+
if (!db2 || !ObjectId54.isValid(roleId))
|
|
23115
|
+
return;
|
|
23116
|
+
const role = await db2.collection("roles").findOne({ _id: new ObjectId54(roleId) }, { projection: { permissions: 1 } });
|
|
23117
|
+
if (isOwnerRole(role)) {
|
|
23118
|
+
const rows = await db2.collection("members").find({ user: new ObjectId54(id), org: new ObjectId54(orgId), status: "active" }, { projection: { role: 1 } }).toArray();
|
|
23119
|
+
const held = rows.map((m) => m.role?.toString?.() ?? "").filter((r) => ObjectId54.isValid(r)).map((r) => new ObjectId54(r));
|
|
23120
|
+
const holdsOwner = held.length ? await db2.collection("roles").countDocuments({ _id: { $in: held }, permissions: "*", status: "active" }) : 0;
|
|
23121
|
+
if (!holdsOwner)
|
|
23122
|
+
throw new UnauthorizedError3("Only the organisation's owner can give an owner role.");
|
|
23123
|
+
}
|
|
23124
|
+
}
|
|
23031
23125
|
async function requireSiteReachInOrg(req, siteId, orgId) {
|
|
23032
23126
|
if (await hasOrgOwnership(callerId(req), orgId)) {
|
|
23033
23127
|
await requireSiteServedByOrg(siteId, orgId);
|
|
@@ -26079,7 +26173,7 @@ function usePersonRepo() {
|
|
|
26079
26173
|
}
|
|
26080
26174
|
async function pushVehicleById(id, plate, session) {
|
|
26081
26175
|
const { plateNumber, recNo } = plate;
|
|
26082
|
-
const
|
|
26176
|
+
const updatedAt2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
26083
26177
|
const _id = toObjectId9(id);
|
|
26084
26178
|
try {
|
|
26085
26179
|
const updateExisting = await collection.updateOne(
|
|
@@ -26090,7 +26184,7 @@ function usePersonRepo() {
|
|
|
26090
26184
|
{
|
|
26091
26185
|
$set: {
|
|
26092
26186
|
"plates.$.recNo": recNo,
|
|
26093
|
-
updatedAt
|
|
26187
|
+
updatedAt: updatedAt2
|
|
26094
26188
|
}
|
|
26095
26189
|
},
|
|
26096
26190
|
{ session }
|
|
@@ -26106,7 +26200,7 @@ function usePersonRepo() {
|
|
|
26106
26200
|
}
|
|
26107
26201
|
},
|
|
26108
26202
|
$set: {
|
|
26109
|
-
updatedAt
|
|
26203
|
+
updatedAt: updatedAt2
|
|
26110
26204
|
}
|
|
26111
26205
|
},
|
|
26112
26206
|
{ session }
|
|
@@ -26117,7 +26211,7 @@ function usePersonRepo() {
|
|
|
26117
26211
|
}
|
|
26118
26212
|
}
|
|
26119
26213
|
async function pullVehicleByRecNo(recNo, scope, session) {
|
|
26120
|
-
const
|
|
26214
|
+
const updatedAt2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
26121
26215
|
const owner = scope?.peopleId ? toObjectId9(scope.peopleId) : null;
|
|
26122
26216
|
const site = scope?.site ? toObjectId9(scope.site) : null;
|
|
26123
26217
|
if (!owner && !site) {
|
|
@@ -26156,7 +26250,7 @@ function usePersonRepo() {
|
|
|
26156
26250
|
plates: match
|
|
26157
26251
|
},
|
|
26158
26252
|
$set: {
|
|
26159
|
-
updatedAt
|
|
26253
|
+
updatedAt: updatedAt2
|
|
26160
26254
|
}
|
|
26161
26255
|
},
|
|
26162
26256
|
{ session }
|
|
@@ -28311,7 +28405,9 @@ function useMemberController() {
|
|
|
28311
28405
|
siteId: Joi29.string().hex().optional().allow("", null),
|
|
28312
28406
|
type: Joi29.string().required(),
|
|
28313
28407
|
status: Joi29.string().required(),
|
|
28314
|
-
roleState: Joi29.string().valid("other-site").optional().allow("", null)
|
|
28408
|
+
roleState: Joi29.string().valid("other-site").optional().allow("", null),
|
|
28409
|
+
// Organization app Members: every person, site-limited ones included.
|
|
28410
|
+
people: Joi29.boolean().truthy("1").falsy("0").optional().allow("", null)
|
|
28315
28411
|
});
|
|
28316
28412
|
const query2 = { ...req.query };
|
|
28317
28413
|
const { error } = validation.validate(query2);
|
|
@@ -28366,7 +28462,8 @@ function useMemberController() {
|
|
|
28366
28462
|
siteIds,
|
|
28367
28463
|
type,
|
|
28368
28464
|
status,
|
|
28369
|
-
roleState: req.query.roleState ?? ""
|
|
28465
|
+
roleState: req.query.roleState ?? "",
|
|
28466
|
+
people: ["1", "true"].includes(String(req.query.people ?? ""))
|
|
28370
28467
|
});
|
|
28371
28468
|
res.json(items);
|
|
28372
28469
|
return;
|
|
@@ -28507,7 +28604,7 @@ function useMemberController() {
|
|
|
28507
28604
|
await requireOrgReach(req, orgId);
|
|
28508
28605
|
await requireSelfEnrolRole(req, orgId, roleId);
|
|
28509
28606
|
} else {
|
|
28510
|
-
await
|
|
28607
|
+
await requireMemberGrantor(req, { orgId, targetUser: userId, roleId, siteId });
|
|
28511
28608
|
}
|
|
28512
28609
|
const data = await _createMemberDirect({ userId, orgId, roleId, app, siteId, siteName, onboardingRequired, callerId: callerId(req) });
|
|
28513
28610
|
res.status(201).json(data);
|
|
@@ -47611,7 +47708,7 @@ var MKeyLog = class {
|
|
|
47611
47708
|
organization = "",
|
|
47612
47709
|
serviceProvider = "",
|
|
47613
47710
|
createdAt = /* @__PURE__ */ new Date(),
|
|
47614
|
-
updatedAt = /* @__PURE__ */ new Date(),
|
|
47711
|
+
updatedAt: updatedAt2 = /* @__PURE__ */ new Date(),
|
|
47615
47712
|
template,
|
|
47616
47713
|
status = "Available",
|
|
47617
47714
|
updatedBy = "",
|
|
@@ -47632,7 +47729,7 @@ var MKeyLog = class {
|
|
|
47632
47729
|
this.organization = organization;
|
|
47633
47730
|
this.serviceProvider = serviceProvider;
|
|
47634
47731
|
this.createdAt = createdAt;
|
|
47635
|
-
this.updatedAt =
|
|
47732
|
+
this.updatedAt = updatedAt2;
|
|
47636
47733
|
this.template = template;
|
|
47637
47734
|
this.status = status;
|
|
47638
47735
|
this.updatedBy = updatedBy;
|
|
@@ -56287,7 +56384,7 @@ var MAccessCard = class {
|
|
|
56287
56384
|
isLiftCard,
|
|
56288
56385
|
liftAccessLevel,
|
|
56289
56386
|
createdAt,
|
|
56290
|
-
updatedAt,
|
|
56387
|
+
updatedAt: updatedAt2,
|
|
56291
56388
|
assignedUnit,
|
|
56292
56389
|
userType,
|
|
56293
56390
|
doorName,
|
|
@@ -56314,7 +56411,7 @@ var MAccessCard = class {
|
|
|
56314
56411
|
this.isLiftCard = isLiftCard;
|
|
56315
56412
|
this.liftAccessLevel = liftAccessLevel;
|
|
56316
56413
|
this.createdAt = createdAt;
|
|
56317
|
-
this.updatedAt =
|
|
56414
|
+
this.updatedAt = updatedAt2;
|
|
56318
56415
|
this.assignedUnit = assignedUnit;
|
|
56319
56416
|
this.userType = userType;
|
|
56320
56417
|
this.doorName = doorName;
|
|
@@ -56493,8 +56590,8 @@ function encryptPayload(payloadHex) {
|
|
|
56493
56590
|
);
|
|
56494
56591
|
return encrypted.toString("base64");
|
|
56495
56592
|
}
|
|
56496
|
-
function toHex(
|
|
56497
|
-
return
|
|
56593
|
+
function toHex(num4, length) {
|
|
56594
|
+
return num4.toString(16).padStart(length * 2, "0");
|
|
56498
56595
|
}
|
|
56499
56596
|
function buildPayload(cardNumber, validityMinutes) {
|
|
56500
56597
|
const randomHex = crypto7.randomBytes(2).toString("hex");
|
|
@@ -56674,8 +56771,8 @@ function UseAccessManagementRepo() {
|
|
|
56674
56771
|
}
|
|
56675
56772
|
}
|
|
56676
56773
|
function generateCardNo() {
|
|
56677
|
-
const
|
|
56678
|
-
return
|
|
56774
|
+
const num4 = Math.floor(Math.random() * 65536);
|
|
56775
|
+
return num4.toString().padStart(10, "0");
|
|
56679
56776
|
}
|
|
56680
56777
|
async function createQrData({
|
|
56681
56778
|
cardNumber,
|
|
@@ -57915,13 +58012,13 @@ function UseAccessManagementRepo() {
|
|
|
57915
58012
|
const prevCard = res && res?.[1]?.cardNo || "-1";
|
|
57916
58013
|
const currentCard = res && res?.[0]?.cardNo || "0";
|
|
57917
58014
|
let availableCardNo = [];
|
|
57918
|
-
let
|
|
58015
|
+
let num4 = parseInt(prevCard, 10) + 1;
|
|
57919
58016
|
while (availableCardNo.length < (unit.length > 0 ? unit.length : quantity)) {
|
|
57920
|
-
if (
|
|
57921
|
-
|
|
58017
|
+
if (num4 === parseInt(currentCard, 10)) {
|
|
58018
|
+
num4++;
|
|
57922
58019
|
}
|
|
57923
|
-
availableCardNo.push(
|
|
57924
|
-
|
|
58020
|
+
availableCardNo.push(num4);
|
|
58021
|
+
num4++;
|
|
57925
58022
|
}
|
|
57926
58023
|
const cardNumbers = availableCardNo.map(
|
|
57927
58024
|
(no2) => no2.toString().padStart(10, "0")
|
|
@@ -63657,13 +63754,13 @@ function parseRobotCommand(body) {
|
|
|
63657
63754
|
if (has2(k))
|
|
63658
63755
|
return { ok: false, message: `${k} is not valid for a ${kind} command.` };
|
|
63659
63756
|
}
|
|
63660
|
-
const
|
|
63661
|
-
if (!
|
|
63757
|
+
const num4 = (k) => typeof b[k] === "number" && Number.isFinite(b[k]);
|
|
63758
|
+
if (!num4("x") || !num4("y"))
|
|
63662
63759
|
return { ok: false, message: `${kind} needs x and y (metres).` };
|
|
63663
63760
|
if (kind === "navigate") {
|
|
63664
63761
|
if (has2("floorNo"))
|
|
63665
63762
|
return { ok: false, message: "floorNo is not valid for a navigate command." };
|
|
63666
|
-
if (has2("theta") && !
|
|
63763
|
+
if (has2("theta") && !num4("theta"))
|
|
63667
63764
|
return { ok: false, message: "theta must be a number (radians)." };
|
|
63668
63765
|
return {
|
|
63669
63766
|
ok: true,
|
|
@@ -63675,7 +63772,7 @@ function parseRobotCommand(body) {
|
|
|
63675
63772
|
}
|
|
63676
63773
|
};
|
|
63677
63774
|
}
|
|
63678
|
-
if (!
|
|
63775
|
+
if (!num4("theta"))
|
|
63679
63776
|
return { ok: false, message: "relocalize needs theta (radians)." };
|
|
63680
63777
|
if (!Number.isInteger(b.floorNo))
|
|
63681
63778
|
return { ok: false, message: "relocalize needs an integer floorNo." };
|
|
@@ -64099,23 +64196,81 @@ var OUNITECH_CAPABILITIES = {
|
|
|
64099
64196
|
var OUNITECH_PENDING_COMMANDS = ["pause", "resume", "cancel"].map(
|
|
64100
64197
|
(kind) => ({ kind, reason: "awaiting Ounitech's robot-control action numbers" })
|
|
64101
64198
|
);
|
|
64102
|
-
function propertyNumber(row, key) {
|
|
64103
|
-
const p = row.robotState?.properties?.find((x) => x?.key === key);
|
|
64104
|
-
const n = p?.value != null && String(p.value).trim() !== "" ? Number(p.value) : NaN;
|
|
64105
|
-
return Number.isFinite(n) ? n : null;
|
|
64106
|
-
}
|
|
64107
64199
|
var finite = (v) => typeof v === "number" && Number.isFinite(v);
|
|
64108
|
-
function
|
|
64109
|
-
|
|
64110
|
-
|
|
64111
|
-
|
|
64200
|
+
function num(v) {
|
|
64201
|
+
if (finite(v))
|
|
64202
|
+
return v;
|
|
64203
|
+
if (typeof v === "string" && v.trim() !== "") {
|
|
64204
|
+
const n = Number(v);
|
|
64205
|
+
return Number.isFinite(n) ? n : void 0;
|
|
64206
|
+
}
|
|
64207
|
+
return void 0;
|
|
64208
|
+
}
|
|
64209
|
+
function reading(st, key) {
|
|
64210
|
+
const v = st.properties?.find((x) => x?.key === key)?.value;
|
|
64211
|
+
return v == null || String(v).trim() === "" ? void 0 : String(v).trim();
|
|
64212
|
+
}
|
|
64213
|
+
var OUNITECH_FRESH_MS = 3 * 6e4;
|
|
64214
|
+
var STATUS_CODE_BY_READING = {
|
|
64215
|
+
\u5F85\u673A: 1,
|
|
64216
|
+
\u7A7A\u95F2: 1,
|
|
64217
|
+
\u6E05\u626B\u4E2D: 2,
|
|
64218
|
+
\u5DE5\u4F5C\u4E2D: 2,
|
|
64219
|
+
\u6682\u505C: 3,
|
|
64220
|
+
\u6682\u505C\u4E2D: 3,
|
|
64221
|
+
\u5EFA\u56FE\u4E2D: 4,
|
|
64222
|
+
\u56DE\u5145\u4E2D: 6,
|
|
64223
|
+
\u8FD4\u56DE\u5145\u7535: 6,
|
|
64224
|
+
\u6545\u969C: 7,
|
|
64225
|
+
\u5F02\u5E38: 7,
|
|
64226
|
+
\u5145\u7535\u4E2D: 8,
|
|
64227
|
+
\u65AD\u70B9\u5145\u7535\u4E2D: 16,
|
|
64228
|
+
\u5DF2\u5145\u6EE1: 9,
|
|
64229
|
+
\u5145\u6EE1: 9,
|
|
64230
|
+
\u6025\u505C: 11,
|
|
64231
|
+
\u5173\u673A: 14,
|
|
64232
|
+
\u79BB\u7EBF: 99
|
|
64233
|
+
};
|
|
64234
|
+
var JOB_WORDS = {
|
|
64235
|
+
\u5DF2\u7ED3\u675F: "job finished",
|
|
64236
|
+
\u5DF2\u5B8C\u6210: "job finished",
|
|
64237
|
+
\u5B8C\u6210: "job finished",
|
|
64238
|
+
\u6E05\u626B\u4E2D: "job in progress",
|
|
64239
|
+
\u8FDB\u884C\u4E2D: "job in progress",
|
|
64240
|
+
\u6682\u505C: "job paused",
|
|
64241
|
+
\u6682\u505C\u4E2D: "job paused",
|
|
64242
|
+
\u672A\u5F00\u59CB: "no job started",
|
|
64243
|
+
\u5DF2\u53D6\u6D88: "job cancelled",
|
|
64244
|
+
\u53D6\u6D88: "job cancelled"
|
|
64245
|
+
};
|
|
64246
|
+
function ounitechJobWords(st) {
|
|
64247
|
+
const v = reading(st, "job");
|
|
64248
|
+
return v ? JOB_WORDS[v] ?? null : null;
|
|
64249
|
+
}
|
|
64250
|
+
function updatedAt(v) {
|
|
64251
|
+
const n = num(v);
|
|
64252
|
+
if (n !== void 0)
|
|
64253
|
+
return n < 1e12 ? n * 1e3 : n;
|
|
64254
|
+
const t = typeof v === "string" ? Date.parse(v) : NaN;
|
|
64255
|
+
return Number.isFinite(t) ? t : null;
|
|
64256
|
+
}
|
|
64257
|
+
function ounitechRobotFromHomeRow(row, live2, now = Date.now()) {
|
|
64258
|
+
const listed = row.robotState ?? {};
|
|
64259
|
+
const st = live2 ? { ...listed, ...live2, properties: live2.properties?.length ? live2.properties : listed.properties } : listed;
|
|
64260
|
+
const statusReading = reading(st, "status");
|
|
64261
|
+
const flag = num(st.statusFlag) ?? (statusReading ? STATUS_CODE_BY_READING[statusReading] : void 0);
|
|
64262
|
+
const at2 = updatedAt(st.updateOn);
|
|
64263
|
+
const fresh = at2 !== null && now - at2 <= OUNITECH_FRESH_MS;
|
|
64264
|
+
const online = flag !== 99 && (flag !== void 0 || fresh);
|
|
64112
64265
|
const mode = online ? robotMode(flag) : "UNAVAILABLE";
|
|
64113
|
-
const pct =
|
|
64266
|
+
const pct = num(reading(st, "battery-percent")) ?? num(st.battery) ?? null;
|
|
64114
64267
|
const task = row.taskState;
|
|
64115
64268
|
const jobPct = task && String(task.statusFlag) === "1" && finite(task.accPercent) ? Math.round(task.accPercent) : null;
|
|
64116
|
-
const words = flag
|
|
64269
|
+
const words = flag !== void 0 ? ROBOT_STATUS_NAMES[flag] ?? `Status ${flag}` : online ? "Online - status not reported" : "Unknown";
|
|
64117
64270
|
const model = String(row.modelCode || row.modelName || "").trim().slice(0, 40);
|
|
64118
|
-
|
|
64271
|
+
const x = num(st.x);
|
|
64272
|
+
const y = num(st.y);
|
|
64273
|
+
const robot = {
|
|
64119
64274
|
robotSn: row.robotSn,
|
|
64120
64275
|
name: row.aliasName || row.robotName,
|
|
64121
64276
|
...model ? { model } : {},
|
|
@@ -64125,8 +64280,16 @@ function ounitechRobotFromHomeRow(row) {
|
|
|
64125
64280
|
batteryPercent: online && pct !== null && pct >= 0 && pct <= 100 ? Math.round(pct) : null,
|
|
64126
64281
|
floorNo: Number.isInteger(row.floorNo) ? row.floorNo : null,
|
|
64127
64282
|
floorKnown: Number.isInteger(row.floorNo),
|
|
64128
|
-
position: online &&
|
|
64283
|
+
position: online && x !== void 0 && y !== void 0 ? { x, y, theta: num(st.theta) ?? 0 } : null
|
|
64129
64284
|
};
|
|
64285
|
+
if (online && (flag === 7 || Array.isArray(st.rtFaultList) && st.rtFaultList.length)) {
|
|
64286
|
+
robot.fault = ounitechFault(st.rtFaultList);
|
|
64287
|
+
}
|
|
64288
|
+
return robot;
|
|
64289
|
+
}
|
|
64290
|
+
function needsLiveState(row) {
|
|
64291
|
+
const f = num(row.robotState?.statusFlag);
|
|
64292
|
+
return finite(row.robotId) && (f === void 0 || f === 99 || f === 7);
|
|
64130
64293
|
}
|
|
64131
64294
|
function ounitechFault(list) {
|
|
64132
64295
|
const f = Array.isArray(list) ? list[0] : null;
|
|
@@ -64143,6 +64306,9 @@ function ounitechFault(list) {
|
|
|
64143
64306
|
var robotTime = (d) => robotCloudTimestamp(d, "yyyy-MM-dd HH:mm:ss", "Asia/Shanghai");
|
|
64144
64307
|
function createOunitechAdapter(credentials, options) {
|
|
64145
64308
|
const cloud = useRobotCloudService(credentials, options);
|
|
64309
|
+
async function liveFor(row) {
|
|
64310
|
+
return needsLiveState(row) ? cloud.getRobotState(row.robotId).catch(() => null) : null;
|
|
64311
|
+
}
|
|
64146
64312
|
async function rowFor(robotSn) {
|
|
64147
64313
|
return (await cloud.getHomeRobots()).find((r) => r.robotSn === robotSn) ?? null;
|
|
64148
64314
|
}
|
|
@@ -64151,7 +64317,7 @@ function createOunitechAdapter(credentials, options) {
|
|
|
64151
64317
|
displayName: "Ounitech (Q3-G / Q3-W)",
|
|
64152
64318
|
capabilities: OUNITECH_CAPABILITIES,
|
|
64153
64319
|
async listRobots() {
|
|
64154
|
-
return (await cloud.getHomeRobots()).map(ounitechRobotFromHomeRow);
|
|
64320
|
+
return Promise.all((await cloud.getHomeRobots()).map(async (row) => ounitechRobotFromHomeRow(row, await liveFor(row))));
|
|
64155
64321
|
},
|
|
64156
64322
|
async getRobot(robotSn) {
|
|
64157
64323
|
const row = await rowFor(robotSn);
|
|
@@ -64167,12 +64333,7 @@ function createOunitechAdapter(credentials, options) {
|
|
|
64167
64333
|
position: null
|
|
64168
64334
|
};
|
|
64169
64335
|
}
|
|
64170
|
-
|
|
64171
|
-
if (row.robotState?.statusFlag === 7 && finite(row.robotId)) {
|
|
64172
|
-
const state = await cloud.getRobotState(row.robotId).catch(() => null);
|
|
64173
|
-
robot.fault = ounitechFault(state?.rtFaultList);
|
|
64174
|
-
}
|
|
64175
|
-
return robot;
|
|
64336
|
+
return ounitechRobotFromHomeRow(row, await liveFor(row));
|
|
64176
64337
|
},
|
|
64177
64338
|
async sendCommand(robotSn, command) {
|
|
64178
64339
|
const options2 = {
|
|
@@ -64287,10 +64448,19 @@ function createOunitechAdapter(credentials, options) {
|
|
|
64287
64448
|
const window = { timeFrom: robotTime(from), timeTo: robotTime(to) };
|
|
64288
64449
|
await run("live state (get-robot-state-v2.0)", ROBOT_CLOUD_PATHS.robotState, { robotId }, async () => {
|
|
64289
64450
|
const s = await cloud.getRobotState(robotId);
|
|
64451
|
+
const r = ounitechRobotFromHomeRow(row, s);
|
|
64452
|
+
const level2 = (...keys) => keys.map((k) => num(reading(s, k))).find((n) => n !== void 0) ?? null;
|
|
64453
|
+
const at2 = updatedAt(s.updateOn);
|
|
64290
64454
|
return {
|
|
64455
|
+
online: r.online,
|
|
64456
|
+
status: r.statusName,
|
|
64457
|
+
job: ounitechJobWords(s),
|
|
64458
|
+
battery: r.batteryPercent,
|
|
64459
|
+
cleanWater: level2("clean-water", "clean-percent"),
|
|
64460
|
+
dirtyWater: level2("dirty-water", "dirty-percent"),
|
|
64461
|
+
fault: r.fault ?? null,
|
|
64291
64462
|
statusFlag: s.statusFlag,
|
|
64292
|
-
|
|
64293
|
-
updateOn: s.updateOn ? new Date(s.updateOn).toISOString() : null,
|
|
64463
|
+
updateOn: at2 !== null ? new Date(at2).toISOString() : null,
|
|
64294
64464
|
x: s.x,
|
|
64295
64465
|
y: s.y,
|
|
64296
64466
|
theta: s.theta,
|
|
@@ -66222,7 +66392,7 @@ function statesByType(entry) {
|
|
|
66222
66392
|
}
|
|
66223
66393
|
var PADBOT_ONLINE_WINDOW_MS = 12e4;
|
|
66224
66394
|
var ms = (t) => typeof t === "string" ? Date.parse(t) : NaN;
|
|
66225
|
-
var
|
|
66395
|
+
var num2 = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
66226
66396
|
function padbotMode(states, now = Date.now()) {
|
|
66227
66397
|
const hb = ms(states["1000"]?.updatedAt);
|
|
66228
66398
|
const online = Number.isFinite(hb) && now - hb <= PADBOT_ONLINE_WINDOW_MS;
|
|
@@ -66230,7 +66400,7 @@ function padbotMode(states, now = Date.now()) {
|
|
|
66230
66400
|
return { online: false, mode: "UNAVAILABLE", statusName: "Not reporting to PadBot" };
|
|
66231
66401
|
const nav = states["1003"] ?? {};
|
|
66232
66402
|
const work = states["1010"] ?? {};
|
|
66233
|
-
const battery =
|
|
66403
|
+
const battery = num2(states["1001"]?.batteryPercent);
|
|
66234
66404
|
const charging = states["1002"]?.chargingState === 1;
|
|
66235
66405
|
const type = Number(nav.actionType) || 0;
|
|
66236
66406
|
const st = Number(nav.actionState) || 0;
|
|
@@ -66267,7 +66437,7 @@ function padbotCommandBlock(states, kind, now = Date.now()) {
|
|
|
66267
66437
|
return "The robot is not reporting to PadBot, so it cannot take a command. Check it is switched on and connected.";
|
|
66268
66438
|
if (states["1006"]?.emergencyStopState === 1)
|
|
66269
66439
|
return "The robot's emergency stop button is pressed. Release the red button on the robot, then try again.";
|
|
66270
|
-
const battery =
|
|
66440
|
+
const battery = num2(states["1001"]?.batteryPercent);
|
|
66271
66441
|
if (kind !== "return" && battery !== null && battery < 10)
|
|
66272
66442
|
return `The battery is at ${battery}%. PadBot robots do not start jobs under 10%; send it to charge first.`;
|
|
66273
66443
|
if (kind === "return" && [1, 2].includes(states["1002"]?.chargingState))
|
|
@@ -66289,7 +66459,7 @@ function padbotReadiness(states, now = Date.now()) {
|
|
|
66289
66459
|
heartbeatAt: st["1000"]?.updatedAt ?? null,
|
|
66290
66460
|
emergencyStopPressed: es.emergencyStopState === 1,
|
|
66291
66461
|
wheelLockState: es.wheelLockState ?? null,
|
|
66292
|
-
batteryPercent:
|
|
66462
|
+
batteryPercent: num2(st["1001"]?.batteryPercent),
|
|
66293
66463
|
chargingState: charging ?? null,
|
|
66294
66464
|
map: { id: st["1007"]?.mapId ?? null, name: st["1007"]?.mapDisplayName ?? null, floor: st["1007"]?.floor ?? null },
|
|
66295
66465
|
navigation: { actionType: nav.actionType ?? null, actionState: nav.actionState ?? null, message: nav.message ?? null, updatedAt: nav.updatedAt ?? null },
|
|
@@ -66346,19 +66516,19 @@ function padbotMotion(m, now) {
|
|
|
66346
66516
|
}
|
|
66347
66517
|
function networkText(net) {
|
|
66348
66518
|
const say = (label2, v) => v < 0 ? ` \xB7 ${label2} ${v} dBm` : ` \xB7 ${label2} signal ${v}`;
|
|
66349
|
-
const wifi =
|
|
66519
|
+
const wifi = num2(net?.wifi?.signalStrength);
|
|
66350
66520
|
if (wifi !== null)
|
|
66351
66521
|
return say("Wi-Fi", wifi);
|
|
66352
|
-
const cell =
|
|
66522
|
+
const cell = num2(net?.cellular?.signalStrength);
|
|
66353
66523
|
return cell !== null ? say("Mobile", cell) : "";
|
|
66354
66524
|
}
|
|
66355
66525
|
function padbotPlanShare(frame2, x, y) {
|
|
66356
|
-
const w =
|
|
66357
|
-
const wx =
|
|
66526
|
+
const w = num2(frame2?.width), h = num2(frame2?.height), r = num2(frame2?.resolution);
|
|
66527
|
+
const wx = num2(x), wy = num2(y);
|
|
66358
66528
|
if (!w || !h || !r || r <= 0 || wx === null || wy === null)
|
|
66359
66529
|
return null;
|
|
66360
|
-
const px = (wx - (
|
|
66361
|
-
const pyUp = (wy - (
|
|
66530
|
+
const px = (wx - (num2(frame2?.originX) ?? 0)) / r;
|
|
66531
|
+
const pyUp = (wy - (num2(frame2?.originY) ?? 0)) / r;
|
|
66362
66532
|
const sx = px / w * 100, sy = 100 - pyUp / h * 100;
|
|
66363
66533
|
if (sx < 0 || sx > 100 || sy < 0 || sy > 100)
|
|
66364
66534
|
return null;
|
|
@@ -66388,9 +66558,9 @@ function padbotCurrentMap(maps, ids2, floor) {
|
|
|
66388
66558
|
}
|
|
66389
66559
|
function padbotChargingPoint(map) {
|
|
66390
66560
|
const piles = (Array.isArray(map?.targetPoints) ? map.targetPoints : []).filter(
|
|
66391
|
-
(t) => Number(t?.targetPointType) === 10 &&
|
|
66561
|
+
(t) => Number(t?.targetPointType) === 10 && num2(t?.worldX) !== null && num2(t?.worldY) !== null
|
|
66392
66562
|
);
|
|
66393
|
-
return piles.length === 1 ? { worldX:
|
|
66563
|
+
return piles.length === 1 ? { worldX: num2(piles[0].worldX), worldY: num2(piles[0].worldY), yaw: piles[0].yaw } : null;
|
|
66394
66564
|
}
|
|
66395
66565
|
var PADBOT_OTHER_MAP_TEXT = "The robot must be on this map first.";
|
|
66396
66566
|
function padbotAlarmFault(alarm) {
|
|
@@ -66485,9 +66655,9 @@ function padbotPatrolRecord(r) {
|
|
|
66485
66655
|
result: PATROL_RESULT[r?.completionState] ?? "unknown",
|
|
66486
66656
|
startedAt: r?.startedAt ?? null,
|
|
66487
66657
|
endedAt: r?.endedAt ?? null,
|
|
66488
|
-
durationS:
|
|
66489
|
-
distanceM:
|
|
66490
|
-
alarmCount:
|
|
66658
|
+
durationS: num2(r?.duration),
|
|
66659
|
+
distanceM: num2(r?.mileage),
|
|
66660
|
+
alarmCount: num2(r?.alarmCount) ?? 0
|
|
66491
66661
|
};
|
|
66492
66662
|
}
|
|
66493
66663
|
function padbotCheckpointVisit(d) {
|
|
@@ -66497,15 +66667,15 @@ function padbotCheckpointVisit(d) {
|
|
|
66497
66667
|
skipped: d?.skipped === true,
|
|
66498
66668
|
imageUrl: d?.visibleLightImageUrl ?? null,
|
|
66499
66669
|
thermalImageUrl: d?.thermalImageUrl ?? null,
|
|
66500
|
-
centerTempC:
|
|
66501
|
-
maxTempC:
|
|
66670
|
+
centerTempC: num2(d?.centerTemperature),
|
|
66671
|
+
maxTempC: num2(d?.maxTemperature)
|
|
66502
66672
|
};
|
|
66503
66673
|
}
|
|
66504
66674
|
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 : [];
|
|
66505
66675
|
var page = (data, map) => ({
|
|
66506
66676
|
items: (Array.isArray(data?.items) ? data.items : []).map(map),
|
|
66507
|
-
total:
|
|
66508
|
-
pages:
|
|
66677
|
+
total: num2(data?.totalItems) ?? 0,
|
|
66678
|
+
pages: num2(data?.totalPages) ?? 0
|
|
66509
66679
|
});
|
|
66510
66680
|
function createPadbotClient(credentials, options = {}) {
|
|
66511
66681
|
const baseUrl = (options.baseUrl ?? padbotBaseUrl()).replace(/\/+$/, "");
|
|
@@ -66727,7 +66897,7 @@ function createPadbotAdapter(credentials, options = {}) {
|
|
|
66727
66897
|
const { online, mode, statusName } = padbotMode(st, now());
|
|
66728
66898
|
const floor = st["1007"]?.floor;
|
|
66729
66899
|
const restBatteryAt = ms(st["1001"]?.updatedAt);
|
|
66730
|
-
const battery = heard?.batteryPercent !== void 0 && (!Number.isFinite(restBatteryAt) || (heard.batteryAt ?? 0) > restBatteryAt) ? heard.batteryPercent :
|
|
66900
|
+
const battery = heard?.batteryPercent !== void 0 && (!Number.isFinite(restBatteryAt) || (heard.batteryAt ?? 0) > restBatteryAt) ? heard.batteryPercent : num2(st["1001"]?.batteryPercent);
|
|
66731
66901
|
const failed = lastFailure.get(`${account}:${robotSn}`);
|
|
66732
66902
|
const showFailure = online && failed && now() - failed.at < PADBOT_FAILURE_SHOW_MS && /^(Standing by|Charging)/.test(statusName);
|
|
66733
66903
|
const robot = {
|
|
@@ -66754,15 +66924,15 @@ function createPadbotAdapter(credentials, options = {}) {
|
|
|
66754
66924
|
return robot;
|
|
66755
66925
|
const [loc, openA] = await Promise.all([locRead, alarmRead]);
|
|
66756
66926
|
const alarm = robot.fault ? null : openA;
|
|
66757
|
-
const lx =
|
|
66758
|
-
const ly =
|
|
66927
|
+
const lx = num2(loc?.worldX ?? loc?.x);
|
|
66928
|
+
const ly = num2(loc?.worldY ?? loc?.y);
|
|
66759
66929
|
if (typeof loc?.mapId === "string" && loc.mapId)
|
|
66760
66930
|
robot.mapId = loc.mapId;
|
|
66761
66931
|
const map = await maps(robotSn).then((all) => padbotCurrentMap(all, [robot.mapId, st["1007"]?.mapId], robot.floorNo)).catch(() => null);
|
|
66762
66932
|
if (map)
|
|
66763
66933
|
robot.mapId = map.mapId;
|
|
66764
66934
|
if (lx !== null && ly !== null) {
|
|
66765
|
-
robot.position = { x: lx, y: ly, theta: (
|
|
66935
|
+
robot.position = { x: lx, y: ly, theta: (num2(loc.yaw ?? loc.r) ?? 0) * Math.PI / 180 };
|
|
66766
66936
|
robot.planPosition = padbotPlanShare(map, lx, ly);
|
|
66767
66937
|
}
|
|
66768
66938
|
const mo = motions.get(`${account}:${robotSn}`);
|
|
@@ -66782,7 +66952,7 @@ function createPadbotAdapter(credentials, options = {}) {
|
|
|
66782
66952
|
const onCharger = [1, 2].includes(st["1002"]?.chargingState);
|
|
66783
66953
|
const pile = onCharger && !robot.position ? padbotChargingPoint(map) : null;
|
|
66784
66954
|
if (pile) {
|
|
66785
|
-
robot.position = { x: pile.worldX, y: pile.worldY, theta: (
|
|
66955
|
+
robot.position = { x: pile.worldX, y: pile.worldY, theta: (num2(pile.yaw) ?? 0) * Math.PI / 180 };
|
|
66786
66956
|
robot.planPosition = padbotPlanShare(map, pile.worldX, pile.worldY);
|
|
66787
66957
|
if (!going || going.done)
|
|
66788
66958
|
robot.statusName = `${robot.statusName} \xB7 At charger`;
|
|
@@ -66882,12 +67052,12 @@ function createPadbotAdapter(credentials, options = {}) {
|
|
|
66882
67052
|
throw new BadRequestError137(block);
|
|
66883
67053
|
lastFailure.delete(`${account}:${robotSn}`);
|
|
66884
67054
|
const heard = padbotLive(account, robotSn)?.pose;
|
|
66885
|
-
const lx =
|
|
66886
|
-
const ly =
|
|
67055
|
+
const lx = num2(where?.loc?.worldX);
|
|
67056
|
+
const ly = num2(where?.loc?.worldY);
|
|
66887
67057
|
let target = null;
|
|
66888
67058
|
if (command.kind === "go-to-point") {
|
|
66889
67059
|
const pt = (where?.all ?? []).flatMap((m) => Array.isArray(m?.targetPoints) ? m.targetPoints : []).find((t) => t?.targetPointId === command.targetPointId);
|
|
66890
|
-
target = { name: String(pt?.name || "the chosen point"), x:
|
|
67060
|
+
target = { name: String(pt?.name || "the chosen point"), x: num2(pt?.worldX), y: num2(pt?.worldY) };
|
|
66891
67061
|
}
|
|
66892
67062
|
motion = {
|
|
66893
67063
|
kind: command.kind,
|
|
@@ -67939,11 +68109,11 @@ function decodeTrackingFrame(data) {
|
|
|
67939
68109
|
if (!parsed || typeof parsed !== "object")
|
|
67940
68110
|
return null;
|
|
67941
68111
|
const frame2 = parsed;
|
|
67942
|
-
const
|
|
67943
|
-
const x =
|
|
67944
|
-
const y =
|
|
67945
|
-
const theta =
|
|
67946
|
-
const statusFlag =
|
|
68112
|
+
const num4 = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
68113
|
+
const x = num4(frame2.x);
|
|
68114
|
+
const y = num4(frame2.y);
|
|
68115
|
+
const theta = num4(frame2.theta);
|
|
68116
|
+
const statusFlag = num4(frame2.status);
|
|
67947
68117
|
if (x === null || y === null || theta === null || statusFlag === null) {
|
|
67948
68118
|
return null;
|
|
67949
68119
|
}
|
|
@@ -68467,22 +68637,22 @@ function useRobotFaultSweep(deps) {
|
|
|
68467
68637
|
if (!robot)
|
|
68468
68638
|
return;
|
|
68469
68639
|
out.robots++;
|
|
68470
|
-
let
|
|
68640
|
+
let reading2;
|
|
68471
68641
|
try {
|
|
68472
68642
|
const state = deps?.readState ? await deps.readState(robot) : await readRobotState(robot, { timeoutMs: cfg.timeoutMs });
|
|
68473
68643
|
if (deps?.readState)
|
|
68474
68644
|
rememberRobotState(robot, state);
|
|
68475
|
-
|
|
68476
|
-
if (
|
|
68477
|
-
Object.assign(
|
|
68645
|
+
reading2 = { ok: true, online: state.online === true, fault: state.fault ?? null };
|
|
68646
|
+
if (reading2.fault)
|
|
68647
|
+
Object.assign(reading2.fault, { floorNo: state.floorNo ?? null });
|
|
68478
68648
|
} catch (error) {
|
|
68479
68649
|
if (deps?.readState)
|
|
68480
68650
|
rememberRobotState(robot, null);
|
|
68481
|
-
|
|
68651
|
+
reading2 = { ok: false, error: error?.message ?? "no answer" };
|
|
68482
68652
|
}
|
|
68483
68653
|
const records = byRobot2.get(String(robot._id)) ?? [];
|
|
68484
68654
|
const offlineRec = records.find((r) => r.code === OFFLINE_FAULT_CODE);
|
|
68485
|
-
const plan = planFaultChanges(records,
|
|
68655
|
+
const plan = planFaultChanges(records, reading2);
|
|
68486
68656
|
for (const id of plan.clear) {
|
|
68487
68657
|
await repo.clearFault(id);
|
|
68488
68658
|
out.cleared++;
|
|
@@ -68495,7 +68665,7 @@ function useRobotFaultSweep(deps) {
|
|
|
68495
68665
|
out.alerts++;
|
|
68496
68666
|
}
|
|
68497
68667
|
for (const f of plan.open) {
|
|
68498
|
-
const detail =
|
|
68668
|
+
const detail = reading2.ok && reading2.fault?.code === f.code ? reading2.fault : null;
|
|
68499
68669
|
const r = await openFaultRecord(robot, detail ? { ...detail, ...f } : f, { notify: true, notifyFn: deps?.notifyFn });
|
|
68500
68670
|
if (r.opened)
|
|
68501
68671
|
out.opened++;
|
|
@@ -69813,7 +69983,7 @@ function measurementScale(vertices, meas) {
|
|
|
69813
69983
|
return est.length ? est.reduce((n, v) => n + v, 0) / est.length : null;
|
|
69814
69984
|
}
|
|
69815
69985
|
var pval = (p) => Array.isArray(p) ? p[1] : p;
|
|
69816
|
-
var
|
|
69986
|
+
var num3 = (v, fallback = 0) => v !== null && v !== "" && Number.isFinite(Number(v)) ? Number(v) : fallback;
|
|
69817
69987
|
var str60 = (v) => String(v ?? "").slice(0, 60);
|
|
69818
69988
|
function decodeParams(raw) {
|
|
69819
69989
|
const out = {};
|
|
@@ -69833,7 +70003,7 @@ function fileVertexParams(raw) {
|
|
|
69833
70003
|
const v = raw[k];
|
|
69834
70004
|
if (v === void 0 || v === null)
|
|
69835
70005
|
continue;
|
|
69836
|
-
out[k] = k.startsWith("is_") ? Boolean(v) : k === "merge_radius" ? clamp3(
|
|
70006
|
+
out[k] = k.startsWith("is_") ? Boolean(v) : k === "merge_radius" ? clamp3(num3(v), 0, 100) : str60(v);
|
|
69837
70007
|
}
|
|
69838
70008
|
return out;
|
|
69839
70009
|
}
|
|
@@ -69906,14 +70076,14 @@ function fromBuildingYaml(doc) {
|
|
|
69906
70076
|
const raw = names.map((name) => {
|
|
69907
70077
|
const y = doc.levels[name] ?? {};
|
|
69908
70078
|
const vertices = (Array.isArray(y.vertices) ? y.vertices : []).map((v) => ({
|
|
69909
|
-
x:
|
|
69910
|
-
y:
|
|
70079
|
+
x: num3(v?.[0]),
|
|
70080
|
+
y: num3(v?.[1]) * flip,
|
|
69911
70081
|
name: str60(v?.[3]),
|
|
69912
70082
|
params: fileVertexParams(decodeParams(v?.[4]))
|
|
69913
70083
|
}));
|
|
69914
|
-
const fiducials = (Array.isArray(y.fiducials) ? y.fiducials : []).map((f) => ({ x:
|
|
69915
|
-
const meas = edgeList(y.measurements).map((e) => [e.a, e.b,
|
|
69916
|
-
return { name: str60(name), y, vertices, fiducials, meas, elevation:
|
|
70084
|
+
const fiducials = (Array.isArray(y.fiducials) ? y.fiducials : []).map((f) => ({ x: num3(f?.[0]), y: num3(f?.[1]) * flip, name: str60(f?.[2]) }));
|
|
70085
|
+
const meas = edgeList(y.measurements).map((e) => [e.a, e.b, num3(e.p.distance)]);
|
|
70086
|
+
return { name: str60(name), y, vertices, fiducials, meas, elevation: num3(y.elevation) };
|
|
69917
70087
|
});
|
|
69918
70088
|
const refName = raw.some((l) => l.name === doc.reference_level_name) ? String(doc.reference_level_name) : raw[0].name;
|
|
69919
70089
|
const ref = raw.find((l) => l.name === refName);
|
|
@@ -69987,20 +70157,20 @@ function fromBuildingYaml(doc) {
|
|
|
69987
70157
|
const extra = {};
|
|
69988
70158
|
if (e.p.texture_name !== void 0 && e.p.texture_name !== "default")
|
|
69989
70159
|
extra.textureName = str60(e.p.texture_name);
|
|
69990
|
-
if (e.p.alpha !== void 0 &&
|
|
69991
|
-
extra.alpha = clamp3(
|
|
69992
|
-
if (e.p.texture_height !== void 0 &&
|
|
69993
|
-
extra.textureHeight = clamp3(
|
|
69994
|
-
if (e.p.texture_width !== void 0 &&
|
|
69995
|
-
extra.textureWidth = clamp3(
|
|
69996
|
-
if (e.p.texture_scale !== void 0 &&
|
|
69997
|
-
extra.textureScale = clamp3(
|
|
70160
|
+
if (e.p.alpha !== void 0 && num3(e.p.alpha, 1) !== 1)
|
|
70161
|
+
extra.alpha = clamp3(num3(e.p.alpha, 1), 0, 1);
|
|
70162
|
+
if (e.p.texture_height !== void 0 && num3(e.p.texture_height, 2.5) !== 2.5)
|
|
70163
|
+
extra.textureHeight = clamp3(num3(e.p.texture_height, 2.5), 0, 100);
|
|
70164
|
+
if (e.p.texture_width !== void 0 && num3(e.p.texture_width, 1) !== 1)
|
|
70165
|
+
extra.textureWidth = clamp3(num3(e.p.texture_width, 1), 0, 100);
|
|
70166
|
+
if (e.p.texture_scale !== void 0 && num3(e.p.texture_scale, 1) !== 1)
|
|
70167
|
+
extra.textureScale = clamp3(num3(e.p.texture_scale, 1), 0, 100);
|
|
69998
70168
|
level2.walls.push({ v1: ids2[e.a], v2: ids2[e.b], ...extra });
|
|
69999
70169
|
}
|
|
70000
70170
|
for (const e of edgeList(lv.y.doors).filter(ok)) {
|
|
70001
70171
|
const type = String(e.p.type ?? "hinged");
|
|
70002
|
-
const degrees = clamp3(
|
|
70003
|
-
const ratio = clamp3(
|
|
70172
|
+
const degrees = clamp3(num3(e.p.motion_degrees, 90), -360, 360);
|
|
70173
|
+
const ratio = clamp3(num3(e.p.right_left_ratio, 1), 0, 10);
|
|
70004
70174
|
const plugin = str60(e.p.plugin ?? "normal");
|
|
70005
70175
|
level2.doors.push({
|
|
70006
70176
|
name: str60(e.p.name ?? "").trim() || `door_${level2.doors.length + 1}`,
|
|
@@ -70009,13 +70179,13 @@ function fromBuildingYaml(doc) {
|
|
|
70009
70179
|
type: ["hinged", "double_hinged", "sliding", "double_sliding"].includes(type) ? type : "hinged",
|
|
70010
70180
|
...e.p.motion_axis === "end" ? { motionAxis: "end" } : {},
|
|
70011
70181
|
...degrees !== 90 ? { motionDegrees: degrees } : {},
|
|
70012
|
-
...
|
|
70182
|
+
...num3(e.p.motion_direction, 1) < 0 ? { motionDirection: -1 } : {},
|
|
70013
70183
|
...ratio !== 1 ? { rightLeftRatio: ratio } : {},
|
|
70014
70184
|
...plugin !== "normal" ? { plugin } : {}
|
|
70015
70185
|
});
|
|
70016
70186
|
}
|
|
70017
70187
|
for (const e of edgeList(lv.y.lanes).filter(ok)) {
|
|
70018
|
-
const gi = Math.round(
|
|
70188
|
+
const gi = Math.round(num3(e.p.graph_idx, 0));
|
|
70019
70189
|
if (gi < 0 || gi > 8) {
|
|
70020
70190
|
warnings.push(`Floor ${lv.name}: a lane on graph ${gi} was skipped (graphs 0-8 only).`);
|
|
70021
70191
|
continue;
|
|
@@ -70028,14 +70198,14 @@ function fromBuildingYaml(doc) {
|
|
|
70028
70198
|
graphIdx: gi,
|
|
70029
70199
|
bidirectional: e.p.bidirectional === true,
|
|
70030
70200
|
...orientation ? { orientation } : {},
|
|
70031
|
-
...
|
|
70201
|
+
...num3(e.p.speed_limit) > 0 ? { speedLimit: clamp3(num3(e.p.speed_limit), 0, 20) } : {},
|
|
70032
70202
|
...e.p.mutex ? { mutex: str60(e.p.mutex) } : {},
|
|
70033
70203
|
...e.p.demo_mock_floor_name ? { demoMockFloorName: str60(e.p.demo_mock_floor_name) } : {},
|
|
70034
70204
|
...e.p.demo_mock_lift_name ? { demoMockLiftName: str60(e.p.demo_mock_lift_name) } : {}
|
|
70035
70205
|
});
|
|
70036
70206
|
}
|
|
70037
70207
|
for (const e of edgeList(lv.y.measurements).filter(ok)) {
|
|
70038
|
-
const d =
|
|
70208
|
+
const d = num3(e.p.distance);
|
|
70039
70209
|
if (d >= 0.01)
|
|
70040
70210
|
level2.measurements.push({ v1: ids2[e.a], v2: ids2[e.b], distanceM: Math.min(d, 1e4) });
|
|
70041
70211
|
}
|
|
@@ -70066,14 +70236,14 @@ function fromBuildingYaml(doc) {
|
|
|
70066
70236
|
if (ly?.reference_floor_name && !levelNames.has(ly.reference_floor_name)) {
|
|
70067
70237
|
warnings.push(`Lift ${str60(liftName)}: its reference floor "${str60(ly.reference_floor_name)}" is not in the file; ${refName} was used.`);
|
|
70068
70238
|
}
|
|
70069
|
-
const c = applyXform(xf(refFloor), { x:
|
|
70239
|
+
const c = applyXform(xf(refFloor), { x: num3(ly?.x), y: num3(ly?.y) * flip });
|
|
70070
70240
|
const doors = Object.entries(ly?.doors ?? {}).slice(0, 8).map(([dn, d]) => ({
|
|
70071
70241
|
name: str60(dn),
|
|
70072
|
-
type: MSG_LIFT_DOOR_TYPE[Math.round(
|
|
70073
|
-
x: r3(clamp3(
|
|
70074
|
-
y: r3(clamp3(
|
|
70075
|
-
widthM: r3(clamp3(
|
|
70076
|
-
axisYawRad: clamp3(
|
|
70242
|
+
type: MSG_LIFT_DOOR_TYPE[Math.round(num3(d?.door_type, 2))] ?? "double_sliding",
|
|
70243
|
+
x: r3(clamp3(num3(d?.x), -20, 20)),
|
|
70244
|
+
y: r3(clamp3(num3(d?.y), -20, 20)),
|
|
70245
|
+
widthM: r3(clamp3(num3(d?.width, 1), 0.3, 10)),
|
|
70246
|
+
axisYawRad: clamp3(num3(d?.motion_axis_orientation), -10, 10)
|
|
70077
70247
|
}));
|
|
70078
70248
|
const doorNames = new Set(doors.map((d) => d.name));
|
|
70079
70249
|
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)) }));
|
|
@@ -70082,9 +70252,9 @@ function fromBuildingYaml(doc) {
|
|
|
70082
70252
|
name: str60(liftName),
|
|
70083
70253
|
x: r3(c.x),
|
|
70084
70254
|
y: r3(c.y),
|
|
70085
|
-
yawRad: clamp3(
|
|
70086
|
-
widthM: r3(clamp3(
|
|
70087
|
-
depthM: r3(clamp3(
|
|
70255
|
+
yawRad: clamp3(num3(ly?.yaw), -10, 10),
|
|
70256
|
+
widthM: r3(clamp3(num3(ly?.width, 2), 0.3, 20)),
|
|
70257
|
+
depthM: r3(clamp3(num3(ly?.depth, 2), 0.3, 20)),
|
|
70088
70258
|
referenceFloor: refFloor,
|
|
70089
70259
|
...floorName(ly?.initial_floor_name) ? { initialFloor: floorName(ly.initial_floor_name) } : {},
|
|
70090
70260
|
...floorName(ly?.lowest_floor) ? { lowestFloor: floorName(ly.lowest_floor) } : {},
|
|
@@ -70138,7 +70308,7 @@ function fromNavGraph(doc, graphIdx) {
|
|
|
70138
70308
|
level2.vertices = verts.map((v, k) => {
|
|
70139
70309
|
const p = v?.[2] && typeof v[2] === "object" ? v[2] : {};
|
|
70140
70310
|
const params = fileVertexParams(p);
|
|
70141
|
-
return { id: `v${k + 1}`, x: r3(
|
|
70311
|
+
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 } : {} };
|
|
70142
70312
|
});
|
|
70143
70313
|
if (Array.isArray(ly?.lanes) && ly.lanes.length > 1e4)
|
|
70144
70314
|
throw new RmfImportError(`Floor ${str60(name)} has more than 10000 one-way lanes - more than a building map can hold.`);
|
|
@@ -70178,7 +70348,7 @@ function fromNavGraph(doc, graphIdx) {
|
|
|
70178
70348
|
graphIdx,
|
|
70179
70349
|
bidirectional,
|
|
70180
70350
|
...o === "forward" || o === "backward" ? { orientation: o } : {},
|
|
70181
|
-
...
|
|
70351
|
+
...num3(p.speed_limit) > 0 ? { speedLimit: clamp3(num3(p.speed_limit), 0, 20) } : {},
|
|
70182
70352
|
...p.mutex ? { mutex: str60(p.mutex) } : {},
|
|
70183
70353
|
...p.demo_mock_floor_name ? { demoMockFloorName: str60(p.demo_mock_floor_name) } : {},
|
|
70184
70354
|
...p.demo_mock_lift_name ? { demoMockLiftName: str60(p.demo_mock_lift_name) } : {}
|
|
@@ -82571,7 +82741,7 @@ function useAccessManagementController() {
|
|
|
82571
82741
|
liftName,
|
|
82572
82742
|
unit = [],
|
|
82573
82743
|
createdAt,
|
|
82574
|
-
updatedAt,
|
|
82744
|
+
updatedAt: updatedAt2,
|
|
82575
82745
|
startDate,
|
|
82576
82746
|
endDate,
|
|
82577
82747
|
isWinsland
|
|
@@ -82607,7 +82777,7 @@ function useAccessManagementController() {
|
|
|
82607
82777
|
startDate,
|
|
82608
82778
|
endDate,
|
|
82609
82779
|
createdAt,
|
|
82610
|
-
updatedAt,
|
|
82780
|
+
updatedAt: updatedAt2,
|
|
82611
82781
|
isWinsland
|
|
82612
82782
|
});
|
|
82613
82783
|
if (error) {
|
|
@@ -82629,7 +82799,7 @@ function useAccessManagementController() {
|
|
|
82629
82799
|
startDate,
|
|
82630
82800
|
endDate,
|
|
82631
82801
|
createdAt,
|
|
82632
|
-
updatedAt,
|
|
82802
|
+
updatedAt: updatedAt2,
|
|
82633
82803
|
isWinsland
|
|
82634
82804
|
});
|
|
82635
82805
|
return res.status(201).json({
|
|
@@ -106646,6 +106816,7 @@ export {
|
|
|
106646
106816
|
OUNITECH_BRAND,
|
|
106647
106817
|
OUNITECH_BRAND_DEFINITION,
|
|
106648
106818
|
OUNITECH_CAPABILITIES,
|
|
106819
|
+
OUNITECH_FRESH_MS,
|
|
106649
106820
|
OUNITECH_PENDING_COMMANDS,
|
|
106650
106821
|
OUNITECH_REGION_BASE_URLS,
|
|
106651
106822
|
OUNITECH_TIMESTAMP_FORMAT,
|
|
@@ -107013,6 +107184,7 @@ export {
|
|
|
107013
107184
|
orgRoleSets,
|
|
107014
107185
|
orgSchema,
|
|
107015
107186
|
ounitechFault,
|
|
107187
|
+
ounitechJobWords,
|
|
107016
107188
|
ounitechRobotFromHomeRow,
|
|
107017
107189
|
overnight_parking_requests_namespace_collection,
|
|
107018
107190
|
padbotAccountKey,
|