little_sophia_brain_ros2/index.html

193 lines
5.4 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ROS 2 Topic Viewer</title>
<script src="https://cdn.jsdelivr.net/npm/roslib/build/roslib.min.js"></script>
<style>
body {
font-family: sans-serif;
padding: 1em;
background: #f5f5f5;
}
select,
button,
input[type=range] {
margin: 0.5em;
padding: 0.5em;
}
#output {
white-space: pre-wrap;
background: #fff;
padding: 1em;
border: 1px solid #ccc;
}
#rateControl {
margin-top: 2em;
}
#output.flash {
background: #d0f0c0;
transition: background 0.5s ease;
}
</style>
</head>
<body>
<h1>ROS 2 Topic Viewer</h1>
<label for="topicSelect">Select a topic:</label>
<select id="topicSelect"></select>
<button onclick="subscribe()">Subscribe</button>
<div id="output">Waiting for messages...</div>
<div id="rateControl">
<h2>Set Publish Rate (Hz)</h2>
<input type="range" id="rateSlider" min="0.5" max="60" step="0.5" value="1" onmousedown="isDraggingSlider = true"
onmouseup="isDraggingSlider = false" oninput="updateRateLabel(this.value)" onchange="setPublishRate(this.value)">
<span id="rateValue">1.0</span> Hz
</div>
<p>Current publish rate: <span id="currentRate">1.0</span> Hz</p>
<h2>Live Camera Feed</h2>
<img id="cameraImage" width="640" height="480" alt="Camera Feed">
<script>
const ros = new ROSLIB.Ros({ url: 'ws://localhost:9090' });
ros.on('connection', () => {
console.log('Connected to rosbridge');
ros.getTopics((topics) => {
const select = document.getElementById('topicSelect');
topics.topics.forEach((topic) => {
const option = document.createElement('option');
option.value = topic;
option.textContent = topic;
select.appendChild(option);
});
});
setInterval(fetchCurrentRate, 1000); // Poll every second
});
ros.on('error', (err) => {
console.error('Error connecting to rosbridge:', err);
});
let listener = null;
function subscribe() {
const topicName = document.getElementById('topicSelect').value;
if (listener) listener.unsubscribe();
ros.getTopicType(topicName, (type) => {
listener = new ROSLIB.Topic({
ros: ros,
name: topicName,
messageType: type
});
listener.subscribe((message) => {
const output = document.getElementById('output');
output.classList.add('flash');
setTimeout(() => output.classList.remove('flash'), 300);
// Handle image display
if (type === 'sensor_msgs/msg/CompressedImage') {
const imgElement = document.getElementById('cameraImage');
imgElement.src = 'data:image/jpeg;base64,' + message.data;
// Show summary instead of full payload
output.textContent = `Topic: ${topicName}\nType: ${type}\n\n[CompressedImage received: ${message.data.length} bytes]`;
return;
}
// Default: show full message
output.textContent =
`Topic: ${topicName}\nType: ${type}\n\n` + JSON.stringify(message, null, 2);
});
});
}
function updateRateLabel(value) {
document.getElementById('rateValue').textContent = value;
}
let isDraggingSlider = false;
let rateUpdateTimeout = null;
function setPublishRate(value) {
clearTimeout(rateUpdateTimeout);
rateUpdateTimeout = setTimeout(() => {
const baseValue = parseFloat(value);
const param = new ROSLIB.Param({
ros: ros,
name: 'camera_module:publish_rate'
});
let attempts = 0;
const maxAttempts = 5;
const bump = 0.0001;
isDraggingSlider = true;
function trySetParam() {
const bumpedValue = baseValue + bump * attempts;
param.set(bumpedValue.toFixed(4), () => { // Convert bumpedValue to string
setTimeout(() => {
param.get((currentValue) => {
const numericValue = typeof currentValue === 'number' ? currentValue : NaN;
if (!isNaN(numericValue) && Math.abs(numericValue - bumpedValue) < 1e-6) {
console.log(`✅ Confirmed: publish_rate set to ${bumpedValue.toFixed(4)} Hz`);
isDraggingSlider = false;
} else if (++attempts < maxAttempts) {
console.warn(`🔁 Retry ${attempts}: still ${numericValue}, retrying with ${bumpedValue.toFixed(4)}...`);
trySetParam();
} else {
console.error(`❌ Failed to set publish_rate after ${maxAttempts} attempts`);
isDraggingSlider = false;
}
});
}, 300);
});
}
trySetParam();
}, 300);
}
function fetchCurrentRate() {
if (isDraggingSlider) return; // ✅ Skip update if user is dragging
const param = new ROSLIB.Param({
ros: ros,
name: 'camera_module:publish_rate'
});
param.get((value) => {
if (value !== null) {
document.getElementById('currentRate').textContent = value.toFixed(1);
document.getElementById('rateSlider').value = value;
document.getElementById('rateValue').textContent = value.toFixed(1);
}
});
}
</script>
</body>
</html>