diff --git a/software/examples/alohamini/dashboard/app.py b/software/examples/alohamini/dashboard/app.py deleted file mode 100644 index 8aa2175..0000000 --- a/software/examples/alohamini/dashboard/app.py +++ /dev/null @@ -1,150 +0,0 @@ -import json -import threading -import time -import base64 -import zmq -import cv2 -import numpy as np -from flask import Flask, render_template, Response, jsonify, request - -app = Flask(__name__) - -# Global state -latest_observation = {} -lock = threading.Lock() -connected = False -recording = False -cmd_socket = None - -def zmq_worker(ip='127.0.0.1', port=5556, cmd_port=5555): - global latest_observation, connected, cmd_socket - context = zmq.Context() - - # Sub Socket - socket = context.socket(zmq.SUB) - socket.setsockopt(zmq.SUBSCRIBE, b"") - socket.connect(f"tcp://{ip}:{port}") - socket.setsockopt(zmq.CONFLATE, 1) - - # Cmd Socket (Push to Sim) - cmd_socket = context.socket(zmq.PUSH) - cmd_socket.setsockopt(zmq.CONFLATE, 1) - cmd_socket.connect(f"tcp://{ip}:{cmd_port}") - - print(f"Connecting to ZMQ Stream at {ip}:{port}...") - - while True: - try: - msg = socket.recv_string() - data = json.loads(msg) - - with lock: - latest_observation = data - connected = True - except Exception as e: - print(f"Error in ZMQ worker: {e}") - connected = False - time.sleep(1) - -def generate_frames(camera_name): - while True: - frame_bytes = None - detections = [] - - with lock: - if camera_name in latest_observation: - b64_str = latest_observation[camera_name] - if b64_str: - try: - frame_bytes = base64.b64decode(b64_str) - except Exception: - pass - - # Get detections - raw_dets = latest_observation.get("detections", {}) - if isinstance(raw_dets, dict): - detections = raw_dets.get(camera_name, []) - - if frame_bytes: - # Decode to image to draw on it - nparr = np.frombuffer(frame_bytes, np.uint8) - img = cv2.imdecode(nparr, cv2.IMREAD_COLOR) - - if img is not None: - # Draw detections - for det in detections: - box = det.get("box", []) - label = det.get("label", "obj") - if len(box) == 4: - x1, y1, x2, y2 = map(int, box) - cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) - cv2.putText(img, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2) - - # Re-encode - ret, buffer = cv2.imencode('.jpg', img) - if ret: - frame_bytes = buffer.tobytes() - - yield (b'--frame\r\n' - b'Content-Type: image/jpeg\r\n\r\n' + frame_bytes + b'\r\n') - else: - # Return a blank or placeholder image if no data - pass - - time.sleep(0.05) # Limit FPS for browser - -@app.route('/') -def index(): - return render_template('index.html') - -@app.route('/video_feed/') -def video_feed(camera_name): - return Response(generate_frames(camera_name), - mimetype='multipart/x-mixed-replace; boundary=frame') - -@app.route('/api/command', methods=['POST']) -def send_command(): - global cmd_socket - if not request.json or 'command' not in request.json: - return jsonify({'error': 'No command provided'}), 400 - - cmd = request.json['command'] - print(f"Received command: {cmd}") - - # Example handling - if cmd == 'reset_sim': - # Send reset command (Isaac Sim needs to handle this logic) - # For now, we can just zero out velocities or send a special flag - if cmd_socket: - cmd_socket.send_string(json.dumps({"reset": True})) - - elif cmd == 'start_recording': - # Trigger recording logic - if cmd_socket: - cmd_socket.send_string(json.dumps({"start_recording": True})) - - elif cmd == 'stop_recording': - if cmd_socket: - cmd_socket.send_string(json.dumps({"stop_recording": True})) - - return jsonify({'status': 'ok'}) - -@app.route('/api/status') -def get_status(): - with lock: - # Filter out large image data for status endpoint, but keep the key - status = {} - for k, v in latest_observation.items(): - if isinstance(v, str) and len(v) > 1000: - status[k] = "__IMAGE_DATA__" - else: - status[k] = v - status['connected'] = connected - return jsonify(status) - -if __name__ == '__main__': - # Start ZMQ thread - t = threading.Thread(target=zmq_worker, daemon=True) - t.start() - - app.run(host='0.0.0.0', port=5001, debug=False) diff --git a/software/examples/alohamini/dashboard/templates/index.html b/software/examples/alohamini/dashboard/templates/index.html deleted file mode 100644 index 49d8ed2..0000000 --- a/software/examples/alohamini/dashboard/templates/index.html +++ /dev/null @@ -1,159 +0,0 @@ - - - - - AlohaMini Dashboard - - - -
-

AlohaMini Dashboard

-
- Disconnected -
-
- -
-
- -
- -
-

Robot State

- - -
-
-
- -
-

Simulation Controls

-

Control the remote simulation directly from here.

- - - -
- - - - diff --git a/software/examples/alohamini/visualize_stream.py b/software/examples/alohamini/visualize_stream.py deleted file mode 100644 index 3b225d0..0000000 --- a/software/examples/alohamini/visualize_stream.py +++ /dev/null @@ -1,68 +0,0 @@ -import argparse -import zmq -import cv2 -import json -import base64 -import numpy as np -import time - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument("--ip", default="127.0.0.1", help="IP of the simulation/robot") - parser.add_argument("--port", type=int, default=5556, help="Observation ZMQ port") - args = parser.parse_args() - - context = zmq.Context() - socket = context.socket(zmq.SUB) - socket.setsockopt(zmq.SUBSCRIBE, b"") - socket.connect(f"tcp://{args.ip}:{args.port}") - socket.setsockopt(zmq.CONFLATE, 1) - - print(f"Listening for video on {args.ip}:{args.port}...") - print("Press 'q' to quit.") - - try: - while True: - # Non-blocking check - if socket.poll(100): - msg = socket.recv_string() - data = json.loads(msg) - - # Look for images - images = [] - for k, v in data.items(): - # Heuristic: if key contains 'cam' or 'head' and value is string (base64) - if isinstance(v, str) and len(v) > 1000: - try: - # Decode - jpg_original = base64.b64decode(v) - jpg_as_np = np.frombuffer(jpg_original, dtype=np.uint8) - img = cv2.imdecode(jpg_as_np, flags=1) - if img is not None: - # Add label - cv2.putText(img, k, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) - images.append(img) - except: - pass - - if images: - # Stack images horizontally - # Resize to same height if needed, but for now assume same size - h_min = min(img.shape[0] for img in images) - images_resized = [cv2.resize(img, (int(img.shape[1] * h_min / img.shape[0]), h_min)) for img in images] - - combined = np.hstack(images_resized) - cv2.imshow("Remote Stream", combined) - - if cv2.waitKey(1) & 0xFF == ord('q'): - break - - except KeyboardInterrupt: - pass - finally: - socket.close() - context.term() - cv2.destroyAllWindows() - -if __name__ == "__main__": - main()