srivatsavdamaraju's picture
Update templates/index.html
4c3f957 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Robot Controller</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
min-height: 100vh;
background-color: #111827;
color: white;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
display: flex;
justify-content: center;
align-items: center;
padding: 1.5rem;
}
.container {
max-width: 28rem;
width: 100%;
display: flex;
flex-direction: column;
gap: 2rem;
}
.header {
text-align: center;
}
.title {
font-size: 2rem;
font-weight: bold;
margin-bottom: 0.5rem;
}
.status {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
border-radius: 9999px;
background-color: rgba(239, 68, 68, 0.2);
color: rgb(248, 113, 113);
font-size: 0.875rem;
transition: all 0.3s ease;
}
.status.connected {
background-color: rgba(34, 197, 94, 0.2);
color: rgb(74, 222, 128);
}
.status::before {
content: '';
width: 0.5rem;
height: 0.5rem;
background-color: currentColor;
border-radius: 50%;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.4; }
100% { opacity: 1; }
}
.manual-status {
margin-top: 1rem;
font-size: 1rem;
font-weight: bold;
text-align: center;
padding: 0.5rem;
background-color: rgba(34, 197, 94, 0.2);
color: rgb(74, 222, 128);
border-radius: 9999px;
transition: all 0.3s ease;
}
.manual-status.deactivated {
background-color: rgba(239, 68, 68, 0.2);
color: rgb(248, 113, 113);
}
.control-pad {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
max-width: 240px;
margin: 0 auto;
place-items: center;
}
.control-button {
width: 4rem;
height: 4rem;
border-radius: 50%;
border: none;
background-color: #4f46e5;
color: white;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
.control-button:hover {
background-color: #4338ca;
transform: translateY(-2px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
.control-button:active {
background-color: #3730a3;
transform: translateY(0);
box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
}
.emergency-stop {
grid-column: 2;
grid-row: 2;
background-color: #dc2626 !important;
width: 4.5rem !important;
height: 4.5rem !important;
border: 3px solid #ef4444;
}
.emergency-stop:hover {
background-color: #b91c1c !important;
}
.emergency-stop:active {
background-color: #991b1b !important;
}
.control-button svg {
width: 1.5rem;
height: 1.5rem;
fill: none;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
}
.command-log {
background-color: #1f2937;
border-radius: 0.5rem;
padding: 1rem;
}
.command-log h2 {
font-size: 1.125rem;
font-weight: 600;
margin-bottom: 0.5rem;
}
.log-container {
height: 12rem;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.log-entry {
padding: 0.25rem 0.5rem;
background-color: rgba(55, 65, 81, 0.5);
border-radius: 0.25rem;
font-size: 0.875rem;
}
/* Grid positioning */
.btn-up { grid-column: 2; grid-row: 1; }
.btn-left { grid-column: 1; grid-row: 2; }
.btn-right { grid-column: 3; grid-row: 2; }
.btn-down { grid-column: 2; grid-row: 3; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1 class="title">Robot Controller</h1>
<div class="status">Disconnected</div>
<div id="manualModeStatus" class="manual-status deactivated">Manual Mode Deactivated</div>
</div>
<div class="control-pad">
<button class="control-button btn-up" data-direction="forward">
<svg viewBox="0 0 24 24">
<path d="M12 19V5M5 12l7-7 7 7"/>
</svg>
</button>
<button class="control-button btn-left" data-direction="left">
<svg viewBox="0 0 24 24">
<path d="M19 12H5m7-7-7 7 7 7"/>
</svg>
</button>
<button class="control-button emergency-stop" data-direction="emergency_stop">
<svg viewBox="0 0 24 24">
<rect x="6" y="6" width="12" height="12" fill="white"/>
</svg>
</button>
<button class="control-button btn-right" data-direction="right">
<svg viewBox="0 0 24 24">
<path d="M5 12h14m-7-7 7 7-7 7"/>
</svg>
</button>
<button class="control-button btn-down" data-direction="backward">
<svg viewBox="0 0 24 24">
<path d="M12 5v14m7-7-7 7-7-7"/>
</svg>
</button>
</div>
<div class="command-log">
<h2>Command Log</h2>
<div class="log-container" id="logContainer"></div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
<script>
const socket = io();
const buttons = document.querySelectorAll('.control-button');
const status = document.querySelector('.status');
const logContainer = document.getElementById('logContainer');
const manualModeStatus = document.getElementById('manualModeStatus');
let commandInterval = null;
let activeCommand = null;
let isButtonPressed = false;
socket.on('connect', () => {
status.textContent = 'Connected';
status.classList.add('connected');
});
socket.on('disconnect', () => {
status.textContent = 'Disconnected';
status.classList.remove('connected');
});
// Listen for manual mode status change
socket.on('manual_mode_status', (data) => {
if (data.status === 'activated') {
manualModeStatus.textContent = 'Manual Mode Activated';
manualModeStatus.classList.remove('deactivated');
} else {
manualModeStatus.textContent = 'Manual Mode Deactivated';
manualModeStatus.classList.add('deactivated');
}
});
function logCommand(command) {
const entry = document.createElement('div');
entry.className = 'log-entry';
entry.textContent = command;
logContainer.insertBefore(entry, logContainer.firstChild);
if (logContainer.children.length > 50) {
logContainer.removeChild(logContainer.lastChild);
}
}
function sendCommand(command) {
socket.emit('control_command', command);
logCommand(command);
}
function startCommand(command) {
if (command === 'emergency_stop') {
stopCommand();
sendCommand('emergency_stop');
return;
}
if (commandInterval) {
clearInterval(commandInterval);
}
activeCommand = command;
isButtonPressed = true;
sendCommand(command);
commandInterval = setInterval(() => {
if (isButtonPressed) {
sendCommand(command);
}
}, 100);
}
function stopCommand() {
isButtonPressed = false;
if (commandInterval) {
clearInterval(commandInterval);
commandInterval = null;
}
if (activeCommand && activeCommand !== 'emergency_stop') {
sendCommand('stop');
}
activeCommand = null;
}
buttons.forEach(button => {
const direction = button.dataset.direction;
button.addEventListener('mousedown', () => {
startCommand(direction);
if (direction !== 'emergency_stop') {
button.style.backgroundColor = '#3730a3';
}
});
button.addEventListener('mouseup', () => {
if (direction !== 'emergency_stop') {
stopCommand();
button.style.backgroundColor = '';
}
});
button.addEventListener('mouseleave', () => {
if (isButtonPressed && direction !== 'emergency_stop') {
stopCommand();
button.style.backgroundColor = '';
}
});
button.addEventListener('touchstart', (e) => {
e.preventDefault();
startCommand(direction);
if (direction !== 'emergency_stop') {
button.style.backgroundColor = '#3730a3';
}
});
button.addEventListener('touchend', (e) => {
e.preventDefault();
if (direction !== 'emergency_stop') {
stopCommand();
button.style.backgroundColor = '';
}
});
});
</script>
</body>
</html>