did stuff

This commit is contained in:
legop3
2025-11-13 13:54:14 -05:00
parent 4d5a918921
commit d601e9aa82
22 changed files with 565 additions and 13 deletions
+12
View File
@@ -12,11 +12,21 @@
.row { margin-bottom: 8px; }
#alerts { margin-top: 12px; max-width: 600px; }
.alert { background: #fff1d2; border: 1px solid #f0b651; padding: 6px 8px; margin-bottom: 6px; font-size: 0.9rem; }
#authPanel { margin: 12px 0; display: flex; gap: 8px; align-items: center; }
#authPanel input { padding: 4px; }
#activeDriver { margin-bottom: 8px; font-weight: bold; }
</style>
</head>
<body>
<h1>Multi Roomba Rover</h1>
<div id="status">Connecting…</div>
<div id="authPanel">
<input id="username" placeholder="Username" />
<input id="password" type="password" placeholder="Password" />
<button id="loginBtn">Admin Login</button>
<button id="spectatorBtn">Spectate</button>
<span id="roleStatus">Role: user</span>
</div>
<div id="layout">
<div id="roverList">
<label for="roverSelect">Rovers</label>
@@ -41,6 +51,7 @@
</div>
<div style="flex: 1">
<h3>Sensor Frames</h3>
<div id="activeDriver">Active driver: --</div>
<pre id="sensorOutput"></pre>
<p>Use WASD keys to drive the selected rover. Shift increases speed.</p>
<div id="alerts"></div>
@@ -52,6 +63,7 @@
<script src="src/helpers/formatters.js"></script>
<script src="src/services/state.js"></script>
<script src="src/services/alertFeed.js"></script>
<script src="src/services/authControls.js"></script>
<script src="src/services/roverUI.js"></script>
<script src="src/services/driveControls.js"></script>
<script src="src/main.js"></script>
+1
View File
@@ -2,5 +2,6 @@ requireModule('globals/socket');
requireModule('helpers/formatters');
requireModule('services/state');
requireModule('services/alertFeed');
requireModule('services/authControls');
requireModule('services/roverUI');
requireModule('services/driveControls');
@@ -0,0 +1,41 @@
registerModule('services/authControls', (require, exports) => {
const { socket } = require('globals/socket');
const state = require('services/state');
const usernameInput = document.getElementById('username');
const passwordInput = document.getElementById('password');
const loginBtn = document.getElementById('loginBtn');
const roleStatus = document.getElementById('roleStatus');
const spectatorBtn = document.getElementById('spectatorBtn');
function updateRole(role) {
if (roleStatus) {
roleStatus.textContent = `Role: ${role}`;
}
state.setRole(role);
}
loginBtn?.addEventListener('click', () => {
const username = usernameInput.value.trim();
const password = passwordInput.value;
socket.emit('auth:login', { username, password }, (resp = {}) => {
if (!resp.success) {
alert(resp.error || 'Login failed');
} else {
updateRole(resp.role);
}
passwordInput.value = '';
});
});
socket.on('auth:role', ({ role }) => {
updateRole(role);
});
// initialize from default role
updateRole(state.getRole());
spectatorBtn?.addEventListener('click', () => {
socket.emit('role:set', { role: 'spectator' });
updateRole('spectator');
});
});
+14
View File
@@ -8,9 +8,16 @@ registerModule('services/roverUI', (require, exports) => {
const sensorOutput = document.getElementById('sensorOutput');
const requestBtn = document.getElementById('requestControl');
const lockBtn = document.getElementById('lockToggle');
const activeDriverEl = document.getElementById('activeDriver');
let roster = [];
state.onRoleChange((role) => {
const isAdmin = role === 'admin' || role === 'lockdown';
if (requestBtn) requestBtn.disabled = !isAdmin;
if (lockBtn) lockBtn.disabled = !isAdmin;
});
socket.on('rovers', (list) => {
roster = list;
roverSelect.innerHTML = '';
@@ -48,6 +55,13 @@ registerModule('services/roverUI', (require, exports) => {
sensorOutput.textContent = lines.join('\n');
});
socket.on('activeDriver', ({ roverId, socketId }) => {
if (roverId !== state.getSelected()) return;
if (activeDriverEl) {
activeDriverEl.textContent = `Active driver: ${socketId || 'none'}`;
}
});
requestBtn?.addEventListener('click', () => {
socket.emit('requestControl', { roverId: state.getSelected() });
});
+20
View File
@@ -1,5 +1,7 @@
registerModule('services/state', (require, exports) => {
let selectedRover = null;
let role = 'user';
const roleListeners = new Set();
function setSelected(roverId) {
selectedRover = roverId;
@@ -9,6 +11,24 @@ registerModule('services/state', (require, exports) => {
return selectedRover;
}
function setRole(nextRole) {
role = nextRole;
roleListeners.forEach((cb) => cb(role));
}
function getRole() {
return role;
}
function onRoleChange(cb) {
roleListeners.add(cb);
cb(role);
return () => roleListeners.delete(cb);
}
exports.setSelected = setSelected;
exports.getSelected = getSelected;
exports.setRole = setRole;
exports.getRole = getRole;
exports.onRoleChange = onRoleChange;
});