diff options
| -rw-r--r-- | simplehttp.py | 11 | ||||
| -rw-r--r-- | ui.html | 444 |
2 files changed, 455 insertions, 0 deletions
diff --git a/simplehttp.py b/simplehttp.py new file mode 100644 index 0000000..18fc054 --- /dev/null +++ b/simplehttp.py @@ -0,0 +1,11 @@ +from http.server import HTTPServer, SimpleHTTPRequestHandler, test +import sys + +class CORSRequestHandler(SimpleHTTPRequestHandler): + def end_headers(self): + self.send_header("Access-Control-Allow-Origin", "*") + self.send_header("Access-Control-Allow-Methods", "*") + SimpleHTTPRequestHandler.end_headers(self) + +if __name__ == "__main__": + test(CORSRequestHandler, HTTPServer, port=8000) @@ -0,0 +1,444 @@ +<!DOCTYPE html> +<html> + <head> + <title>Microscope UI</title> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + + <style> +body { + font-family: 'Segoe UI', Arial, sans-serif; + background-color: #1a1a1a; + color: #e0e0e0; + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + margin: 0; +} + +.main-container { + display: flex; + flex-wrap: wrap; + gap: 20px; + padding: 25px; + background-color: #2b2b2b; + border-radius: 12px; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.5); + justify-content: center; + width: fit-content; + max-width: 90%; +} + +.status-indicators-container { + display: flex; + flex-direction: column; + gap: 15px; + /* No background here, the children have their own */ +} + +.command-controls { + flex: 1 1 300px; + display: flex; + flex-direction: column; + gap: 15px; + padding: 15px; + background-color: #3c3c3c; + border-radius: 8px; +} + +.display-box { + background-color: #3c3c3c; /* This is what makes each box visually separate */ + border-radius: 8px; + padding: 15px 20px; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; +} + +/* The rest of the CSS for labels, values, buttons, etc. remains the same */ + +.label { + text-transform: uppercase; + font-size: 0.7em; + letter-spacing: 1px; + color: #999; + margin-bottom: 5px; +} + +.operation-mode .value { + font-size: 1.5em; + font-weight: 600; + color: #88ff88; +} + +.busy { + background-color: #734b00; +} + +.busy .value { + color: #ffbf00; +} + +.error { + background-color: #712f20; +} + +.error .value { + color: #f93c3c; +} + +.coordinate-display { + flex: 1; + position: relative; +} + +.coord-label { + font-size: 2em; + font-weight: bold; + color: #66bbff; + margin-bottom: 5px; +} + +.current-value { + font-size: 2.5em; + font-weight: 700; + color: #fff; + margin: 5px 0; +} + +.details { + font-size: 0.9em; + color: #aaa; + margin-top: 10px; + line-height: 1.4; +} + +.limit-status { + position: absolute; + top: -10px; + right: -10px; + background-color: #ff3333; + color: #fff; + padding: 4px 8px; + border-radius: 20px; + font-size: 0.7em; + font-weight: bold; + text-transform: uppercase; + visibility: hidden; + opacity: 0; + transition: opacity 0.3s; +} + +.limit-status.active { + visibility: visible; + opacity: 1; +} + +.image-info .image-value { + display: flex; + align-items: baseline; + gap: 5px; + font-weight: bold; +} + +.image-number { + font-size: 2em; + color: #ffd700; +} + +.degree-value { + font-size: 1.2em; + color: #ffd700; +} + +.directional-pad { + display: grid; + grid-template-areas: + ". y-plus ." + "x-minus . x-plus" + ". y-minus ."; +gap: 10px; +max-width: 200px; +margin: 0 auto; +} + +.d-pad-x-minus { grid-area: x-minus; } +.d-pad-x-plus { grid-area: x-plus; } +.d-pad-y-plus { grid-area: y-plus; } +.d-pad-y-minus { grid-area: y-minus; } + +.arrow-btn { + background-color: #555; + color: #fff; + border: none; + padding: 10px 15px; + font-size: 1.2em; + font-weight: bold; + border-radius: 6px; + cursor: pointer; + transition: background-color 0.2s; +} + +.arrow-btn:hover { + background-color: #666; +} + +.options-row { + display: flex; + flex-wrap: wrap; + gap: 10px; + justify-content: center; +} + +.option-btn { + background-color: #555; + color: #fff; + border: none; + padding: 8px 12px; + font-size: 0.9em; + border-radius: 6px; + cursor: pointer; + transition: background-color 0.2s; +} + +.option-btn:hover { + background-color: #666; +} + +.image-options { + display: flex; + gap: 5px; + align-items: center; +} + +.image-select, +.image-pos-input, +.command-input { + background-color: #444; + color: #fff; + border: 1px solid #555; + padding: 6px 10px; + border-radius: 6px; + font-size: 0.9em; +} + +.image-pos-input { + width: 60px; +} + +.command-line-box { + display: flex; + gap: 10px; +} + +.command-input { + flex-grow: 1; +} + +.send-btn { + background-color: #66bbff; + color: #fff; + border: none; + padding: 8px 15px; + border-radius: 6px; + cursor: pointer; + font-weight: bold; + transition: background-color 0.2s; +} + +.send-btn:hover { + background-color: #55aaff; +} + </style> + </head> + + <body> + + <div class="main-container"> + <div class="status-indicators-container"> + <div class="display-box operation-mode"> + <span class="label">Operation Mode <span id="time-remaining"></span></span> + <span class="value" id="operation">MANUAL</span> + </div> + + <div class="display-box coordinate-display"> + <div class="coord-label">X</div> + <span class="current-value" id="xPos">-</span> + <div class="details"> + <span class="home-coord">Home: <span id="xHome">-</span></span> + <span class="range">Range: <span id="xLower">-</span>-<span id="xUpper">-</span></span> + </div> + <div class="limit-status" id="xLimit">Limit Active</div> + </div> + + <div class="display-box coordinate-display"> + <div class="coord-label">Y</div> + <span class="current-value" id="yPos">-</span> + <div class="details"> + <span class="home-coord">Home: <span id="yHome">-</span></span> + <span class="range">Range: <span id="yLower">-</span>-<span id="yUpper">-</span></span> + </div> + <div class="limit-status" id="yLimit">Limit Active</div> + </div> + + <div class="display-box image-info"> + <span class="label">Image</span> + <div class="image-value"> + <span class="image-number">3</span> + <span class="degree-value">180°</span> + </div> + </div> + </div> + + <div class="command-controls"> + <div class="directional-pad"> + <button class="arrow-btn d-pad-y-plus" id="btnIncY">Y+</button> + <button class="arrow-btn d-pad-x-minus" id="btnDecX">X-</button> + <button class="arrow-btn d-pad-x-plus" id="btnIncX">X+</button> + <button class="arrow-btn d-pad-y-minus" id="btnDecY">Y-</button> + </div> + + <div class="options-row"> + <button class="option-btn" id="btnCancel">CANCEL</button> + <button class="option-btn" id="btnHome">Home</button> + <button class="option-btn" id="btnCenter">Center</button> + <button class="option-btn" id="btnCalibrate">Calibrate</button> + <button class="option-btn" id="btnDismantle">Dismantle</button> + <button class="option-btn" id="btnToPos">Go to Position</button> + <div class="image-options"> + <select class="image-select"> + <option value="1">Image 1</option> + <option value="2">Image 2</option> + <option value="3">Image 3</option> + <option value="4">Image 4</option> + <option value="5">Image 5</option> + <option value="6">Image 6</option> + </select> + <button class="option-btn go-image-btn" id="btnSelectImg">Go</button> + <input type="number" class="image-pos-input" placeholder="°" min="0" max="360"> + <button class="option-btn go-pos-btn" id="btnSelectDeg">Go</button> + </div> + </div> + + <div class="command-line-box"> + <input type="text" class="command-input" placeholder="Enter command..."> + <button class="send-btn">Send</button> + </div> + </div> + </div> + + <script type="text/javascript"> + let server_address = "http://10.42.0.1:8000" + + function updateState(stage) { + document.querySelector("#operation").innerHTML = stage.operation; + document.querySelector(".operation-mode").classList.remove("error"); + if (stage.operation != "MANUAL") { + document.querySelector(".operation-mode").classList.add("busy"); + } else { + document.querySelector(".operation-mode").classList.remove("busy"); + } + + if (stage.time_remaining > 0) { + document.querySelector("#time-remaining").innerHTML = `(~${Math.round(stage.time_remaining)}s)`; + } else { + document.querySelector("#time-remaining").innerHTML = ``; + } + + document.querySelector("#xPos").innerHTML = stage.x.pos; + document.querySelector("#xLower").innerHTML = stage.x.lower; + document.querySelector("#xUpper").innerHTML = stage.x.upper; + document.querySelector("#xHome").innerHTML = stage.x.home; + if (stage.x.limit) { + document.querySelector("#xLimit").classList.add("active"); + } else { + document.querySelector("#xLimit").classList.remove("active"); + } + + document.querySelector("#yPos").innerHTML = stage.y.pos; + document.querySelector("#yLower").innerHTML = stage.y.lower; + document.querySelector("#yUpper").innerHTML = stage.y.upper; + document.querySelector("#yHome").innerHTML = stage.y.home; + if (stage.y.limit) { + document.querySelector("#yLimit").classList.add("active"); + } else { + document.querySelector("#yLimit").classList.remove("active"); + } + + } + + const intervalId = setInterval(async function() { + try { + let res = await fetch(`${server_address}/state`); + let data = await res.json(); + updateState(data) + } catch (e) { + document.querySelector("#operation").innerHTML = "CON ERR"; + document.querySelector(".operation-mode").classList.add("error"); + console.error(e); + } + }, 500); + + document.querySelector("#btnCancel").onmousedown = function() { + fetch(`${server_address}/cancel`, { + "method": "PUT", + }); + }; + document.querySelector("#btnHome").onmousedown = function() { + fetch(`${server_address}/home`, { + "method": "PUT", + }); + }; + document.querySelector("#btnCenter").onmousedown = function() { + fetch(`${server_address}/center`, { + "method": "PUT", + }); + }; + + document.querySelector("#btnIncX").onmousedown = function() { + fetch(`${server_address}/set_signal?signal=xInc&value=1`, { + "method": "PUT", + }); + }; + document.querySelector("#btnIncX").onmouseup = function() { + fetch(`${server_address}/set_signal?signal=xInc&value=0`, { + "method": "PUT", + }); + }; + + document.querySelector("#btnDecX").onmousedown = function() { + fetch(`${server_address}/set_signal?signal=xDec&value=1`, { + "method": "PUT", + }); + }; + document.querySelector("#btnDecX").onmouseup = function() { + fetch(`${server_address}/set_signal?signal=xDec&value=0`, { + "method": "PUT", + }); + }; + + document.querySelector("#btnIncY").onmousedown = function() { + fetch(`${server_address}/set_signal?signal=yInc&value=1`, { + "method": "PUT", + }); + }; + document.querySelector("#btnIncY").onmouseup = function() { + fetch(`${server_address}/set_signal?signal=yInc&value=0`, { + "method": "PUT", + }); + }; + + document.querySelector("#btnDecY").onmousedown = function() { + fetch(`${server_address}/set_signal?signal=yDec&value=1`, { + "method": "PUT", + }); + }; + document.querySelector("#btnDecY").onmouseup = function() { + fetch(`${server_address}/set_signal?signal=yDec&value=0`, { + "method": "PUT", + }); + }; + </script> + </body> +</html> |
