cduss commited on
Commit
b8c72c0
·
1 Parent(s): 47d0240
Files changed (9) hide show
  1. Dockerfile +9 -0
  2. README.md +2 -3
  3. app.py +40 -0
  4. assets/client.js +47 -0
  5. dummy_server.py +21 -0
  6. requirements.txt +1 -0
  7. server.py +12 -0
  8. static/index.html +16 -0
  9. static/main.js +25 -0
Dockerfile ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ WORKDIR /app
4
+ COPY requirements.txt .
5
+ RUN pip install --no-cache-dir -r requirements.txt
6
+
7
+ COPY . .
8
+
9
+ CMD ["python", "server.py"]
README.md CHANGED
@@ -3,9 +3,8 @@ title: Mini
3
  emoji: 🌖
4
  colorFrom: blue
5
  colorTo: yellow
6
- sdk: gradio
7
- sdk_version: 5.29.0
8
- app_file: app.py
9
  pinned: false
10
  ---
11
 
 
3
  emoji: 🌖
4
  colorFrom: blue
5
  colorTo: yellow
6
+ sdk: docker
7
+
 
8
  pinned: false
9
  ---
10
 
app.py ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from fastapi import FastAPI
3
+ from fastapi.staticfiles import StaticFiles
4
+
5
+
6
+ # 1. Build Gradio interface
7
+ def build_ui():
8
+ with gr.Blocks() as demo:
9
+ gr.Markdown("## 🕹️ Motor Control – External JS")
10
+
11
+ for i in range(6):
12
+ gr.Slider(
13
+ minimum=0,
14
+ maximum=1,
15
+ step=0.01,
16
+ value=0,
17
+ label=f"motor_{i}",
18
+ elem_id=f"motor_{i}",
19
+ )
20
+
21
+ # Load external JS file from /static/client.js
22
+ gr.HTML('<script src="/static/client.js"></script>')
23
+
24
+ return demo
25
+
26
+
27
+ demo = build_ui()
28
+
29
+ # 2. Mount Gradio app into FastAPI
30
+ app = FastAPI()
31
+ app = gr.mount_gradio_app(app, demo, path="/")
32
+
33
+ # 3. Serve static assets manually from "assets/" directory
34
+ app.mount("/static", StaticFiles(directory="assets"), name="static")
35
+
36
+ # 4. Launch manually via uvicorn
37
+ if __name__ == "__main__":
38
+ import uvicorn
39
+
40
+ uvicorn.run(app, host="0.0.0.0", port=7860)
assets/client.js ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const LOCAL_SERVER_URL = "http://127.0.0.1:5001/motor_control";
2
+
3
+ function sendMotorCommand(motor, value) {
4
+ console.log(`Sending to ${motor}: ${value}`);
5
+ fetch(LOCAL_SERVER_URL, {
6
+ method: "POST",
7
+ headers: {
8
+ "Content-Type": "application/json"
9
+ },
10
+ body: JSON.stringify({ motor, value })
11
+ }).then(res => res.text()).then(txt => {
12
+ console.log("✅ Server response:", txt);
13
+ }).catch(err => {
14
+ console.error("❌ Fetch error:", err);
15
+ });
16
+ }
17
+
18
+ function bindSliders() {
19
+ for (let i = 0; i < 6; i++) {
20
+ const wrapper = document.getElementById("motor_" + i);
21
+ if (!wrapper) {
22
+ console.warn(`motor_${i} not found`);
23
+ continue;
24
+ }
25
+ const slider = wrapper.querySelector("input[type=range]");
26
+ if (!slider) {
27
+ console.warn(`No slider in motor_${i}`);
28
+ continue;
29
+ }
30
+
31
+ slider.addEventListener("input", () => {
32
+ sendMotorCommand("motor_" + i, parseFloat(slider.value));
33
+ });
34
+
35
+ console.log(`✅ Bound slider for motor_${i}`);
36
+ }
37
+ }
38
+
39
+ window.addEventListener("load", () => {
40
+ console.log("🚀 External JS loaded");
41
+ const retry = setInterval(() => {
42
+ if (document.querySelectorAll("input[type=range]").length >= 6) {
43
+ bindSliders();
44
+ clearInterval(retry);
45
+ }
46
+ }, 500);
47
+ });
dummy_server.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, request
2
+ from flask_cors import CORS
3
+
4
+ app = Flask(__name__)
5
+ CORS(app) # Allow cross-origin from browser
6
+
7
+ motor_states = {f"motor_{i}": 0.0 for i in range(6)}
8
+
9
+
10
+ @app.route("/motor_control", methods=["POST"])
11
+ def control_motor():
12
+ data = request.json
13
+ motor = data.get("motor")
14
+ value = data.get("value")
15
+ motor_states[motor] = value
16
+ print(f"{motor} set to {value}")
17
+ return {"status": "ok", "motor_states": motor_states}
18
+
19
+
20
+ if __name__ == "__main__":
21
+ app.run(host="0.0.0.0", port=5001)
requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ flask
server.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, send_from_directory
2
+
3
+ app = Flask(__name__, static_url_path="", static_folder="static")
4
+
5
+
6
+ @app.route("/")
7
+ def root():
8
+ return send_from_directory("static", "index.html")
9
+
10
+
11
+ if __name__ == "__main__":
12
+ app.run(host="0.0.0.0", port=7860)
static/index.html ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+
4
+ <head>
5
+ <meta charset="UTF-8" />
6
+ <title>Motor Control</title>
7
+ </head>
8
+
9
+ <body>
10
+ <h1>Motor Control</h1>
11
+ <div id="sliders"></div>
12
+
13
+ <script src="main.js"></script>
14
+ </body>
15
+
16
+ </html>
static/main.js ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const robotIp = "http://127.0.0.1:5001";
2
+
3
+ window.onload = () => {
4
+ const container = document.getElementById("sliders");
5
+ for (let i = 0; i < 6; i++) {
6
+ const label = document.createElement("label");
7
+ label.innerText = `Motor ${i}`;
8
+ const input = document.createElement("input");
9
+ input.type = "range";
10
+ input.min = 0;
11
+ input.max = 100;
12
+ input.value = 0;
13
+ input.oninput = () => {
14
+ fetch(`${robotIp}/motor_control`, {
15
+ method: "POST",
16
+ headers: { "Content-Type": "application/json" },
17
+ body: JSON.stringify({ motor: `motor_${i}`, value: parseFloat(input.value) }),
18
+ }).catch(err => console.error("Send error:", err));
19
+ };
20
+ container.appendChild(label);
21
+ container.appendChild(document.createElement("br"));
22
+ container.appendChild(input);
23
+ container.appendChild(document.createElement("br"));
24
+ }
25
+ };