mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
assignment adjustments and ui tweakings
This commit is contained in:
+2
-2
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -12,8 +12,8 @@
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<!-- site-metadata:inject -->
|
||||
<!-- analytics:inject -->
|
||||
<script type="module" crossorigin src="/assets/index-2kYHs9Qn.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-CX--Qo38.css">
|
||||
<script type="module" crossorigin src="/assets/index-CJaywZ5_.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-D2qTDWX0.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -244,7 +244,8 @@ function pickRover(socket, options = {}) {
|
||||
}
|
||||
/*
|
||||
Eligibility is resolved above, while this shared comparator owns only the
|
||||
requested placement order: undocked-and-empty, battery, then driver count.
|
||||
requested placement order: empty, undocked when empty, driver count, then
|
||||
battery percentage.
|
||||
Keeping those concerns separate prevents a ranking change from weakening
|
||||
lock, private-rover, role, or mode access checks.
|
||||
*/
|
||||
|
||||
@@ -36,13 +36,36 @@ function batteryPercentage(rover) {
|
||||
|
||||
function compareRoversForAssignment(left, right) {
|
||||
/*
|
||||
An undocked rover with nobody assigned is the most useful placement because
|
||||
it starts a fresh driving session without adding another user to a queue.
|
||||
Both conditions must be true to receive this first-priority rank.
|
||||
Spread drivers across the fleet before adding another person to an existing
|
||||
rover queue. This comparison is deliberately independent of battery: a
|
||||
small battery-percentage difference should never concentrate users on one
|
||||
rover while another eligible rover has nobody assigned.
|
||||
*/
|
||||
const leftReadyAndEmpty = readDockedState(left) === false && driverCount(left) === 0;
|
||||
const rightReadyAndEmpty = readDockedState(right) === false && driverCount(right) === 0;
|
||||
if (leftReadyAndEmpty !== rightReadyAndEmpty) return leftReadyAndEmpty ? -1 : 1;
|
||||
const leftDrivers = driverCount(left);
|
||||
const rightDrivers = driverCount(right);
|
||||
const leftEmpty = leftDrivers === 0;
|
||||
const rightEmpty = rightDrivers === 0;
|
||||
if (leftEmpty !== rightEmpty) return leftEmpty ? -1 : 1;
|
||||
|
||||
/*
|
||||
When both choices are empty, prefer the rover that is already away from its
|
||||
dock. Docking state does not separate occupied rovers because queue balance
|
||||
is more useful there, and an existing driver may already be handling the
|
||||
rover's physical state. Unknown docking telemetry receives no undocked
|
||||
preference rather than being guessed as ready.
|
||||
*/
|
||||
if (leftEmpty && rightEmpty) {
|
||||
const leftUndocked = readDockedState(left) === false;
|
||||
const rightUndocked = readDockedState(right) === false;
|
||||
if (leftUndocked !== rightUndocked) return leftUndocked ? -1 : 1;
|
||||
}
|
||||
|
||||
/*
|
||||
For occupied rovers, queue length is the primary balancing signal. This is
|
||||
intentionally evaluated before battery so a one-percent battery advantage
|
||||
cannot cause every later user to pile onto the same rover.
|
||||
*/
|
||||
if (leftDrivers !== rightDrivers) return leftDrivers - rightDrivers;
|
||||
|
||||
const leftBattery = batteryPercentage(left);
|
||||
const rightBattery = batteryPercentage(right);
|
||||
@@ -52,12 +75,11 @@ function compareRoversForAssignment(left, right) {
|
||||
if (leftHasBattery && leftBattery !== rightBattery) return rightBattery - leftBattery;
|
||||
|
||||
/*
|
||||
Battery-equivalent rovers are balanced by current assignment load. Returning
|
||||
zero after this comparison is intentional: assignmentService randomly picks
|
||||
within that exact best tier so stable Map insertion order does not create a
|
||||
permanent favorite rover.
|
||||
Returning zero is intentional. assignmentService randomly selects from the
|
||||
complete best tier so stable Map insertion order cannot permanently favor a
|
||||
rover whose emptiness, docking state, load, and battery are all equivalent.
|
||||
*/
|
||||
return driverCount(left) - driverCount(right);
|
||||
return 0;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -22,21 +22,38 @@ function rankedIds(entries) {
|
||||
return entries.sort(compareRoversForAssignment).map((entry) => entry.id);
|
||||
}
|
||||
|
||||
test('an undocked empty rover outranks every rover that is docked or occupied', () => {
|
||||
test('an empty rover outranks an occupied rover regardless of battery or docking state', () => {
|
||||
const result = rankedIds([
|
||||
rover({ id: 'occupied-high', docked: false, battery: 100, drivers: 1 }),
|
||||
rover({ id: 'docked-high', docked: true, battery: 100 }),
|
||||
rover({ id: 'ready-empty', docked: false, battery: 20 }),
|
||||
rover({ id: 'docked-empty', docked: true, battery: 20 }),
|
||||
]);
|
||||
|
||||
assert.equal(result[0], 'ready-empty');
|
||||
assert.deepEqual(result, ['docked-empty', 'occupied-high']);
|
||||
});
|
||||
|
||||
test('battery percentage ranks rovers after undocked-and-empty readiness', () => {
|
||||
test('an undocked rover is preferred when both rovers are empty', () => {
|
||||
const result = rankedIds([
|
||||
rover({ id: 'low', docked: false, battery: 35 }),
|
||||
rover({ id: 'high', docked: false, battery: 90 }),
|
||||
rover({ id: 'middle', docked: false, battery: 60 }),
|
||||
rover({ id: 'docked-high', docked: true, battery: 100 }),
|
||||
rover({ id: 'undocked-low', docked: false, battery: 20 }),
|
||||
]);
|
||||
|
||||
assert.deepEqual(result, ['undocked-low', 'docked-high']);
|
||||
});
|
||||
|
||||
test('lowest driver count ranks occupied rovers before battery percentage', () => {
|
||||
const result = rankedIds([
|
||||
rover({ id: 'busy-high', docked: false, battery: 100, drivers: 4 }),
|
||||
rover({ id: 'quieter-low', docked: false, battery: 20, drivers: 1 }),
|
||||
]);
|
||||
|
||||
assert.deepEqual(result, ['quieter-low', 'busy-high']);
|
||||
});
|
||||
|
||||
test('battery percentage ranks rovers after availability and load are equal', () => {
|
||||
const result = rankedIds([
|
||||
rover({ id: 'low', docked: false, battery: 35, drivers: 1 }),
|
||||
rover({ id: 'high', docked: false, battery: 90, drivers: 1 }),
|
||||
rover({ id: 'middle', docked: false, battery: 60, drivers: 1 }),
|
||||
]);
|
||||
|
||||
assert.deepEqual(result, ['high', 'middle', 'low']);
|
||||
@@ -51,15 +68,6 @@ test('known battery percentage outranks missing battery telemetry', () => {
|
||||
assert.deepEqual(result, ['known', 'unknown']);
|
||||
});
|
||||
|
||||
test('driver count breaks a battery-percentage tie', () => {
|
||||
const result = rankedIds([
|
||||
rover({ id: 'busy', docked: false, battery: 70, drivers: 3 }),
|
||||
rover({ id: 'less-busy', docked: false, battery: 70, drivers: 1 }),
|
||||
]);
|
||||
|
||||
assert.deepEqual(result, ['less-busy', 'busy']);
|
||||
});
|
||||
|
||||
test('exactly equivalent rovers remain tied for random selection by assignmentService', () => {
|
||||
const left = rover({ id: 'left', docked: false, battery: 80, drivers: 1 });
|
||||
const right = rover({ id: 'right', docked: false, battery: 80, drivers: 1 });
|
||||
|
||||
Reference in New Issue
Block a user