update directory structure
This commit is contained in:
@ -0,0 +1 @@
|
||||
PRINTER_IPS=192.168.0.10,192.168.0.11,192.168.0.12
|
106
packages/@backend-archiv/NETWORK-api-backend_blueprint/README.md
Normal file
106
packages/@backend-archiv/NETWORK-api-backend_blueprint/README.md
Normal file
@ -0,0 +1,106 @@
|
||||
# 🖨️ 3D-Drucker Status API 📊
|
||||
|
||||
Willkommen beim Blueprint der 3D-Drucker Status API! Diese API ermöglicht es Ihnen, den Status mehrerer über LAN verbundener 3D-Drucker zu überwachen und Druckaufträge an sie zu senden.
|
||||
|
||||
## 🌟 Funktionen
|
||||
|
||||
- 🔍 Abrufen des Status von 3D-Druckern, einschließlich ihres aktuellen Status, Fortschrittes und Temperatur.
|
||||
- 📥 Senden von Druckaufträgen an verfügbare 3D-Drucker.
|
||||
- 💾 Speichern und Aktualisieren des Status jedes Druckers in einer SQLite-Datenbank.
|
||||
|
||||
## 🛠️Verwendete Technologien
|
||||
|
||||
- 🐍 Python
|
||||
- 🌶️ Flask
|
||||
- 🗄️ SQLite
|
||||
- 🌐 HTTP-Anfragen
|
||||
|
||||
## 📋 Verordnungen
|
||||
|
||||
Bevor Sie die API starten, stellen Sie sicher, dass Sie folgendes haben:
|
||||
|
||||
- Python 3.x installiert
|
||||
- Flask und python-dotenv-Bibliotheken installiert (`pip install flask python-dotenv`)
|
||||
- Eine Liste von IP-Adressen der 3D-Drucker, die Sie überwachen möchten
|
||||
|
||||
## 🚀 Erste Schritte
|
||||
|
||||
1. Klonen Sie das Repository:
|
||||
```
|
||||
git clone https://git.i.mercedes-benz.com/TBA-Berlin-FI/MYP
|
||||
```
|
||||
|
||||
2. Installieren Sie die erforderlichen Abhängigkeiten:
|
||||
```
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
3. Erstellen Sie eine `.env`-Datei im Projektverzeichnis und geben Sie die IP-Adressen Ihrer 3D-Drucker an:
|
||||
```
|
||||
PRINTER_IPS=192.168.0.10,192.168.0.11,192.168.0.12
|
||||
```
|
||||
|
||||
4. Starten Sie das Skript, um die SQLite-Datenbank zu erstellen:
|
||||
```
|
||||
python create_db.py
|
||||
```
|
||||
|
||||
5. Starten Sie den API-Server:
|
||||
```
|
||||
python app.py
|
||||
```
|
||||
|
||||
6. Die API ist unter `http://localhost:5000` erreichbar.
|
||||
|
||||
## 📡 API-Endpunkte
|
||||
|
||||
- `GET /printer_status`: Rufen Sie den Status aller 3D-Drucker ab.
|
||||
- `POST /print_job`: Senden Sie einen Druckauftrag an einen bestimmten 3D-Drucker.
|
||||
|
||||
## 📝 API-Nutzung
|
||||
|
||||
### Druckerstatus abrufen
|
||||
|
||||
Senden Sie eine `GET`-Anfrage an `/printer_status`, um den Status aller 3D-Drucker abzurufen.
|
||||
|
||||
Antwort:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"ip": "192.168.0.10",
|
||||
"status": "frei",
|
||||
"progress": 0,
|
||||
"temperature": 25
|
||||
},
|
||||
{
|
||||
"ip": "192.168.0.11",
|
||||
"status": "besetzt",
|
||||
"progress": 50,
|
||||
"temperature": 180
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
### Druckauftrag senden
|
||||
|
||||
Senden Sie eine `POST`-Anfrage an `/print_job` mit der folgenden JSON-Last, um einen Druckauftrag an einen bestimmten 3D-Drucker zu senden:
|
||||
|
||||
```json
|
||||
{
|
||||
"printer_ip": "192.168.0.10",
|
||||
"file_url": "http://example.com/print_file.gcode"
|
||||
}
|
||||
```
|
||||
|
||||
Antwort:
|
||||
```json
|
||||
{
|
||||
"message": "Druckauftrag gestartet"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 📄 Lizenz
|
||||
|
||||
- --> Noch nicht verfügbar
|
@ -0,0 +1,25 @@
|
||||
import sqlite3
|
||||
from dotenv import load_dotenv
|
||||
import os
|
||||
|
||||
load_dotenv()
|
||||
printers = os.getenv('PRINTER_IPS').split(',')
|
||||
|
||||
def create_db():
|
||||
conn = sqlite3.connect('printers.db')
|
||||
c = conn.cursor()
|
||||
|
||||
# Tabelle 'printers' erstellen, falls sie nicht existiert
|
||||
c.execute('''CREATE TABLE IF NOT EXISTS printers
|
||||
(ip TEXT PRIMARY KEY, status TEXT)''')
|
||||
|
||||
# Drucker-IPs in die Tabelle einfügen, falls sie noch nicht vorhanden sind
|
||||
for printer_ip in printers:
|
||||
c.execute("INSERT OR IGNORE INTO printers (ip, status) VALUES (?, ?)", (printer_ip, "frei"))
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
print("Datenbank 'printers.db' erfolgreich erstellt.")
|
||||
|
||||
if __name__ == '__main__':
|
||||
create_db()
|
@ -0,0 +1,3 @@
|
||||
flask==2.1.0
|
||||
requests==2.25.1
|
||||
python-dotenv==0.20.0
|
@ -0,0 +1,94 @@
|
||||
from flask import Flask, jsonify, request
|
||||
import requests
|
||||
import sqlite3
|
||||
from dotenv import load_dotenv
|
||||
import os
|
||||
|
||||
load_dotenv()
|
||||
printers = os.getenv('PRINTER_IPS').split(',')
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# SQLite-Datenbank initialisieren
|
||||
def init_db():
|
||||
conn = sqlite3.connect('printers.db')
|
||||
c = conn.cursor()
|
||||
c.execute('''CREATE TABLE IF NOT EXISTS printers
|
||||
(ip TEXT PRIMARY KEY, status TEXT)''')
|
||||
for printer_ip in printers:
|
||||
c.execute("INSERT OR IGNORE INTO printers (ip, status) VALUES (?, ?)", (printer_ip, "frei"))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
@app.route('/printer_status', methods=['GET'])
|
||||
def get_printer_status():
|
||||
printer_status = []
|
||||
conn = sqlite3.connect('printers.db')
|
||||
c = conn.cursor()
|
||||
|
||||
for printer_ip in printers:
|
||||
c.execute("SELECT status FROM printers WHERE ip = ?", (printer_ip,))
|
||||
status = c.fetchone()[0]
|
||||
|
||||
try:
|
||||
response = requests.get(f"http://{printer_ip}/api/printer/status")
|
||||
|
||||
if response.status_code == 200:
|
||||
status_data = response.json()
|
||||
printer_status.append({
|
||||
"ip": printer_ip,
|
||||
"status": status,
|
||||
"progress": status_data["progress"],
|
||||
"temperature": status_data["temperature"]
|
||||
})
|
||||
else:
|
||||
printer_status.append({
|
||||
"ip": printer_ip,
|
||||
"status": "Fehler bei der Abfrage",
|
||||
"progress": None,
|
||||
"temperature": None
|
||||
})
|
||||
except:
|
||||
printer_status.append({
|
||||
"ip": printer_ip,
|
||||
"status": "Drucker nicht erreichbar",
|
||||
"progress": None,
|
||||
"temperature": None
|
||||
})
|
||||
|
||||
conn.close()
|
||||
return jsonify(printer_status)
|
||||
|
||||
@app.route('/print_job', methods=['POST'])
|
||||
def submit_print_job():
|
||||
print_job = request.json
|
||||
printer_ip = print_job["printer_ip"]
|
||||
file_url = print_job["file_url"]
|
||||
|
||||
conn = sqlite3.connect('printers.db')
|
||||
c = conn.cursor()
|
||||
c.execute("SELECT status FROM printers WHERE ip = ?", (printer_ip,))
|
||||
status = c.fetchone()[0]
|
||||
|
||||
if status == "frei":
|
||||
try:
|
||||
response = requests.post(f"http://{printer_ip}/api/print_job", json={"file_url": file_url})
|
||||
|
||||
if response.status_code == 200:
|
||||
c.execute("UPDATE printers SET status = 'besetzt' WHERE ip = ?", (printer_ip,))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return jsonify({"message": "Druckauftrag gestartet"}), 200
|
||||
else:
|
||||
conn.close()
|
||||
return jsonify({"message": "Fehler beim Starten des Druckauftrags"}), 500
|
||||
except:
|
||||
conn.close()
|
||||
return jsonify({"message": "Drucker nicht erreichbar"}), 500
|
||||
else:
|
||||
conn.close()
|
||||
return jsonify({"message": "Drucker ist nicht frei"}), 400
|
||||
|
||||
if __name__ == '__main__':
|
||||
init_db()
|
||||
app.run(host='0.0.0.0', port=5000)
|
@ -0,0 +1,38 @@
|
||||
# entwendet aus:
|
||||
# https://github.com/ut-hnl-lab/ultimakerpy
|
||||
|
||||
# auch zum lesen:
|
||||
# https://github.com/MartinBienz/SDPremote?tab=readme-ov-file
|
||||
|
||||
import time
|
||||
from ultimakerpy import UMS3, JobState
|
||||
|
||||
def print_started(state):
|
||||
if state == JobState.PRINTING:
|
||||
time.sleep(6.0)
|
||||
return True
|
||||
return False
|
||||
|
||||
def layer_reached(pos, n):
|
||||
if round(pos / 0.2) >= n: # set layer pitch: 0.2 mm
|
||||
return True
|
||||
return False
|
||||
|
||||
printer = UMS3(name='MyPrinterName')
|
||||
targets = {
|
||||
'job_state': printer.job_state,
|
||||
'bed_pos': printer.bed.position,
|
||||
}
|
||||
|
||||
printer.print_from_dialog() # select file to print
|
||||
printer.peripherals.camera_streaming()
|
||||
with printer.data_logger('output2.csv', targets) as dl:
|
||||
timer = dl.get_timer()
|
||||
|
||||
# sleep until active leveling finishes
|
||||
timer.wait_for_datalog('job_state', print_started)
|
||||
|
||||
for n in range(1, 101):
|
||||
# sleep until the printing of specified layer to start
|
||||
timer.wait_for_datalog('bed_pos', lambda v: layer_reached(v, n))
|
||||
print('printing layer:', n)
|
148
packages/backend/myp_backend.py
Normal file
148
packages/backend/myp_backend.py
Normal file
@ -0,0 +1,148 @@
|
||||
from flask import Flask, render_template, request, redirect, url_for, jsonify, session
|
||||
import sqlite3
|
||||
import bcrypt
|
||||
|
||||
app = Flask(__name__)
|
||||
app.secret_key = 'supersecretkey'
|
||||
|
||||
# Database setup
|
||||
def init_db():
|
||||
conn = sqlite3.connect('database.db')
|
||||
c = conn.cursor()
|
||||
c.execute('''CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, username TEXT, password TEXT)''')
|
||||
c.execute('''CREATE TABLE IF NOT EXISTS printers (id INTEGER PRIMARY KEY, name TEXT, status TEXT)''')
|
||||
c.execute('''CREATE TABLE IF NOT EXISTS jobs (id INTEGER PRIMARY KEY, printer_id INTEGER, user TEXT, date TEXT, status TEXT)''')
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
init_db()
|
||||
|
||||
# User registration (Admin setup)
|
||||
def add_admin():
|
||||
conn = sqlite3.connect('database.db')
|
||||
c = conn.cursor()
|
||||
hashed_pw = bcrypt.hashpw('adminpassword'.encode('utf-8'), bcrypt.gensalt())
|
||||
c.execute("INSERT INTO users (username, password) VALUES (?, ?)", ('admin', hashed_pw))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
# Comment the next line after the first run
|
||||
# add_admin()
|
||||
|
||||
# API Endpoints
|
||||
@app.route('/api/printers/status', methods=['GET'])
|
||||
def get_printer_status():
|
||||
conn = sqlite3.connect('database.db')
|
||||
c = conn.cursor()
|
||||
c.execute("SELECT * FROM printers")
|
||||
printers = c.fetchall()
|
||||
conn.close()
|
||||
return jsonify(printers)
|
||||
|
||||
@app.route('/api/printers/job', methods=['POST'])
|
||||
def create_job():
|
||||
if not session.get('logged_in'):
|
||||
return jsonify({'error': 'Unauthorized'}), 403
|
||||
|
||||
data = request.json
|
||||
user = session['username']
|
||||
printer_id = data['printer_id']
|
||||
conn = sqlite3.connect('database.db')
|
||||
c = conn.cursor()
|
||||
|
||||
c.execute("SELECT status FROM printers WHERE id=?", (printer_id,))
|
||||
status = c.fetchone()[0]
|
||||
|
||||
if status == 'frei':
|
||||
c.execute("INSERT INTO jobs (printer_id, user, date, status) VALUES (?, ?, datetime('now'), 'in progress')",
|
||||
(printer_id, user))
|
||||
c.execute("UPDATE printers SET status='belegt' WHERE id=?", (printer_id,))
|
||||
conn.commit()
|
||||
elif status == 'belegt':
|
||||
return jsonify({'error': 'Printer already in use'}), 409
|
||||
else:
|
||||
return jsonify({'error': 'Invalid printer status'}), 400
|
||||
|
||||
conn.close()
|
||||
return jsonify({'message': 'Job created and printer turned on'}), 200
|
||||
|
||||
@app.route('/api/printers/reserve', methods=['POST'])
|
||||
def reserve_printer():
|
||||
if not session.get('logged_in'):
|
||||
return jsonify({'error': 'Unauthorized'}), 403
|
||||
|
||||
data = request.json
|
||||
printer_id = data['printer_id']
|
||||
conn = sqlite3.connect('database.db')
|
||||
c = conn.cursor()
|
||||
|
||||
c.execute("SELECT status FROM printers WHERE id=?", (printer_id,))
|
||||
status = c.fetchone()[0]
|
||||
|
||||
if status == 'frei':
|
||||
c.execute("UPDATE printers SET status='reserviert' WHERE id=?", (printer_id,))
|
||||
conn.commit()
|
||||
message = 'Printer reserved'
|
||||
else:
|
||||
message = 'Printer cannot be reserved'
|
||||
|
||||
conn.close()
|
||||
return jsonify({'message': message}), 200
|
||||
|
||||
@app.route('/api/printers/release', methods=['POST'])
|
||||
def release_printer():
|
||||
if not session.get('logged_in'):
|
||||
return jsonify({'error': 'Unauthorized'}), 403
|
||||
|
||||
data = request.json
|
||||
printer_id = data['printer_id']
|
||||
conn = sqlite3.connect('database.db')
|
||||
c = conn.cursor()
|
||||
|
||||
c.execute("UPDATE printers SET status='frei' WHERE id=?", (printer_id,))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return jsonify({'message': 'Printer released'}), 200
|
||||
|
||||
# Authentication routes
|
||||
@app.route('/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
if request.method == 'POST':
|
||||
username = request.form['username']
|
||||
password = request.form['password'].encode('utf-8')
|
||||
|
||||
conn = sqlite3.connect('database.db')
|
||||
c = conn.cursor()
|
||||
c.execute("SELECT * FROM users WHERE username=?", (username,))
|
||||
user = c.fetchone()
|
||||
conn.close()
|
||||
|
||||
if user and bcrypt.checkpw(password, user[2].encode('utf-8')):
|
||||
session['logged_in'] = True
|
||||
session['username'] = username
|
||||
return redirect(url_for('dashboard'))
|
||||
else:
|
||||
return render_template('login.html', error='Invalid Credentials')
|
||||
|
||||
return render_template('login.html')
|
||||
|
||||
@app.route('/dashboard')
|
||||
def dashboard():
|
||||
if not session.get('logged_in'):
|
||||
return redirect(url_for('login'))
|
||||
|
||||
conn = sqlite3.connect('database.db')
|
||||
c = conn.cursor()
|
||||
c.execute("SELECT * FROM printers")
|
||||
printers = c.fetchall()
|
||||
conn.close()
|
||||
|
||||
return render_template('dashboard.html', printers=printers)
|
||||
|
||||
@app.route('/logout')
|
||||
def logout():
|
||||
session.clear()
|
||||
return redirect(url_for('login'))
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True)
|
20
packages/backend/templates/base.html
Normal file
20
packages/backend/templates/base.html
Normal file
@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>3D Printer Management</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/daisyui@1.14.0/dist/full.css" rel="stylesheet">
|
||||
</head>
|
||||
<body class="bg-black text-white">
|
||||
<nav class="bg-gray-800 p-4">
|
||||
<div class="container mx-auto">
|
||||
<h1 class="text-xl">3D Printer Management Dashboard</h1>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container mx-auto mt-5">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
29
packages/backend/templates/dashboard.html
Normal file
29
packages/backend/templates/dashboard.html
Normal file
@ -0,0 +1,29 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h2 class="text-2xl mb-4">Printer Status</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{% for printer in printers %}
|
||||
<div class="card bg-gray-900 shadow-xl">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">{{ printer[1] }}</h2>
|
||||
<p>Status: {{ printer[2] }}</p>
|
||||
{% if printer[2] == 'frei' %}
|
||||
<form method="POST" action="/api/printers/job">
|
||||
<input type="hidden" name="printer_id" value="{{ printer[0] }}">
|
||||
<button class="btn btn-success mt-4 w-full">Start Job</button>
|
||||
</form>
|
||||
{% elif printer[2] == 'belegt' %}
|
||||
<button class="btn btn-warning mt-4 w-full" disabled>In Use</button>
|
||||
{% elif printer[2] == 'reserviert' %}
|
||||
<form method="POST" action="/api/printers/release">
|
||||
<input type="hidden" name="printer_id" value="{{ printer[0] }}">
|
||||
<button class="btn btn-info mt-4 w-full">Release</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<a href="/logout" class="btn btn-secondary mt-4">Logout</a>
|
||||
{% endblock %}
|
33
packages/backend/templates/login.html
Normal file
33
packages/backend/templates/login.html
Normal file
@ -0,0 +1,33 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="flex justify-center items-center h-screen">
|
||||
<div class="card w-96 bg-gray-900 shadow-xl">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">Login</h2>
|
||||
<form method="POST">
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">Username</span>
|
||||
</label>
|
||||
<input type="text" name="username" class="input input-bordered w-full" required>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">Password</span>
|
||||
</label>
|
||||
<input type="password" name="password" class="input input-bordered w-full" required>
|
||||
</div>
|
||||
<div class="form-control mt-6">
|
||||
<button class="btn btn-primary w-full">Login</button>
|
||||
</div>
|
||||
</form>
|
||||
{% if error %}
|
||||
<div class="mt-4 text-red-500">
|
||||
{{ error }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
Reference in New Issue
Block a user