mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 09:31:20 -04:00
assignment adjustments and ui tweakings
This commit is contained in:
@@ -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