This commit is contained in:
Thomas Forgione 2024-07-12 14:42:35 +02:00
parent aa55934bf0
commit 31610f6100
10 changed files with 114 additions and 66 deletions

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
db.sqlite db.sqlite
.device .device
data data
data-keep
node_modules node_modules
static/calibration-visualiser.* static/calibration-visualiser.*

17
app.py
View File

@ -21,6 +21,12 @@ def get_calibration(conn: sqlite3.Connection) -> Optional[db.Calibration]:
return db.Calibration.get_from_id(calibration_id, conn) return db.Calibration.get_from_id(calibration_id, conn)
@app.context_processor
def inject_stage_and_region():
conn = db.get()
return dict(calibration=get_calibration(conn))
@app.route("/") @app.route("/")
def hello_world(): def hello_world():
conn = db.get() conn = db.get()
@ -40,7 +46,7 @@ def create_object():
def object(id: int): def object(id: int):
conn = db.get() conn = db.get()
object = db.Object.get_from_id(id, conn) object = db.Object.get_from_id(id, conn)
return render_template('object.html', object=object, calibration=get_calibration(conn)) return render_template('object.html', object=object)
@app.route('/scan/<id>') @app.route('/scan/<id>')
@ -90,10 +96,17 @@ def scan_calibration(id: int):
@app.route("/api/calibrate/<id>") @app.route("/api/calibrate/<id>")
def run_calibration(id: int): def run_calibration(id: int):
conn = db.get() conn = db.get()
db.Calibration.get_from_id(id, conn) calib = db.Calibration.get_from_id(id, conn)
if calib is None:
return 'oops', 404
calibration_json = calibration.calibrate(join(config.CALIBRATION_DIR, str(id))) calibration_json = calibration.calibrate(join(config.CALIBRATION_DIR, str(id)))
with open(join(config.CALIBRATION_DIR, str(id), 'calibration.json'), 'w') as f: with open(join(config.CALIBRATION_DIR, str(id), 'calibration.json'), 'w') as f:
json.dump(calibration_json, f, indent=4) json.dump(calibration_json, f, indent=4)
with conn:
calib.state = db.CalibrationState.IsComputed
calib.save(conn)
return 'ok' return 'ok'

View File

@ -1,7 +1,9 @@
from os.path import join from os.path import join
MODE = 'debug'
DATA_DIR = 'data' DATA_DIR = 'data'
CALIBRATION_DIR = join(DATA_DIR, 'calibration') CALIBRATION_DIR = join(DATA_DIR, 'calibrations')
OBJECT_DIR = join(DATA_DIR, 'objects')
LEDS_UUIDS = [ LEDS_UUIDS = [
'ac59350e-3787-46d2-88fa-743c1d34fe86', 'ac59350e-3787-46d2-88fa-743c1d34fe86',

67
db.py
View File

@ -3,9 +3,17 @@
from enum import IntEnum from enum import IntEnum
from flask import g from flask import g
import os import os
from os.path import join
import shutil
import sqlite3 import sqlite3
import sys
from typing import Optional from typing import Optional
if __name__ != '__main__':
from . import config
else:
import config
def get() -> sqlite3.Connection: def get() -> sqlite3.Connection:
if 'db' not in g: if 'db' not in g:
@ -40,7 +48,8 @@ def init_app(app):
class CalibrationState(IntEnum): class CalibrationState(IntEnum):
Empty = 0 Empty = 0
HasData = 1 HasData = 1
IsValidated = 2 IsComputed = 2
IsValidated = 3
class Calibration: class Calibration:
@ -59,7 +68,9 @@ class Calibration:
'INSERT INTO calibration(state) VALUES (?) RETURNING ' + Calibration.select_args() + ';', 'INSERT INTO calibration(state) VALUES (?) RETURNING ' + Calibration.select_args() + ';',
[int(CalibrationState.Empty)] [int(CalibrationState.Empty)]
) )
return Calibration.from_row(response.fetchone()) calibration = Calibration.from_row(response.fetchone())
os.makedirs(join(config.CALIBRATION_DIR, str(calibration.id)))
return calibration
@staticmethod @staticmethod
def from_row(row: sqlite3.Row) -> 'Calibration': def from_row(row: sqlite3.Row) -> 'Calibration':
@ -96,7 +107,7 @@ class Acquisition:
cur = db.cursor() cur = db.cursor()
cur.execute( cur.execute(
'UPDATE acquisition SET calibration_id = ?, object_id = ? WHERE id = ?', 'UPDATE acquisition SET calibration_id = ?, object_id = ? WHERE id = ?',
[self.calibration_id, self.objct_id, self.id] [self.calibration_id, self.object_id, self.id]
) )
@staticmethod @staticmethod
@ -146,7 +157,9 @@ class Object:
'INSERT INTO object(name) VALUES (?) RETURNING ' + Object.select_args() + ';', 'INSERT INTO object(name) VALUES (?) RETURNING ' + Object.select_args() + ';',
[name] [name]
) )
return Object.from_row(response.fetchone()) object = Object.from_row(response.fetchone())
os.makedirs(join(config.OBJECT_DIR, str(object.id)))
return object
@staticmethod @staticmethod
def get_from_id(object_id: int, db: sqlite3.Connection) -> Optional['Object']: def get_from_id(object_id: int, db: sqlite3.Connection) -> Optional['Object']:
@ -176,6 +189,10 @@ class Object:
def main(): def main():
if config.MODE != 'debug':
print('Can only reset db in debug mode')
sys.exit(1)
db = sqlite3.connect( db = sqlite3.connect(
os.environ.get('DATABASE_PATH', 'db.sqlite'), os.environ.get('DATABASE_PATH', 'db.sqlite'),
detect_types=sqlite3.PARSE_DECLTYPES, detect_types=sqlite3.PARSE_DECLTYPES,
@ -183,28 +200,32 @@ def main():
db.row_factory = sqlite3.Row db.row_factory = sqlite3.Row
init(db) init(db)
if os.path.isdir(config.DATA_DIR):
shutil.rmtree(config.DATA_DIR)
# Create a new object # Create a new object
with db: with db:
object = Object.create('Mon premier objet', db) Object.create('Mon premier objet', db)
calibration = Calibration.create(db) # calibration = Calibration.create(db)
object.add_acquisition(calibration.id, db) # object.add_acquisition(calibration.id, db)
object.add_acquisition(calibration.id, db) # object.add_acquisition(calibration.id, db)
object.add_acquisition(calibration.id, db) # object.add_acquisition(calibration.id, db)
calibration = Calibration.create(db) # calibration = Calibration.create(db)
object.add_acquisition(calibration.id, db) # object.add_acquisition(calibration.id, db)
object.add_acquisition(calibration.id, db) # object.add_acquisition(calibration.id, db)
object.add_acquisition(calibration.id, db) # object.add_acquisition(calibration.id, db)
object = Object.create('Mon deuxième objet', db) Object.create('Mon deuxième objet', db)
calibration = Calibration.create(db) # calibration = Calibration.create(db)
object.add_acquisition(calibration.id, db) # object.add_acquisition(calibration.id, db)
object.add_acquisition(calibration.id, db) # object.add_acquisition(calibration.id, db)
object.add_acquisition(calibration.id, db) # object.add_acquisition(calibration.id, db)
calibration = Calibration.create(db) # calibration = Calibration.create(db)
object.add_acquisition(calibration.id, db) # object.add_acquisition(calibration.id, db)
object.add_acquisition(calibration.id, db) # object.add_acquisition(calibration.id, db)
object.add_acquisition(calibration.id, db) # object.add_acquisition(calibration.id, db)
if __name__ == '__main__': if __name__ == '__main__':
main() if len(sys.argv) > 1 and sys.argv[1] == 'reset':
main()

View File

@ -35,7 +35,7 @@ def scan(output_dir: str):
# capture(img) # capture(img)
# For debug purposes # For debug purposes
shutil.copyfile(join(config.DATA_DIR, 'small', led + '.jpg'), img) shutil.copyfile(join('data-keep/small', led + '.jpg'), img)
delta = time.time() - start delta = time.time() - start

View File

@ -22,8 +22,28 @@
</div> </div>
<div id="navbarBasicExample" class="navbar-menu"> <div id="navbarBasicExample" class="navbar-menu">
<div class="navbar-end"> <div class="navbar-end">
<a class="navbar-item" href="/"> <a class="navbar-item" href="#">
Page 1 {% if calibration is none or calibration.state == 0 %}
<span class="tags has-addons">
<span class="tag is-dark">étalonnage</span>
<span class="tag is-danger">aucune donnée</span>
</span>
{% elif calibration.state == 1 %}
<span class="tags has-addons">
<span class="tag is-dark">étalonnage</span>
<span class="tag is-warning">non calculé</span>
</span>
{% elif calibration.state == 2 %}
<span class="tags has-addons">
<span class="tag is-dark">étalonnage</span>
<span class="tag is-warning">non validé</span>
</span>
{% elif calibration.state == 3 %}
<a href="/calibration/{{ object.id }}" class="tags has-addons">
<span class="tag is-dark">étalonnage</span>
<span class="tag is-success">fait</span>
</a>
{% endif %}
</a> </a>
</div> </div>
</div> </div>

View File

@ -30,8 +30,8 @@
</div> </div>
</div> </div>
<progress id="progress-bar" class="progress is-link" style="display: none;" value="0" max="1000"></progress> <progress id="progress-bar" class="progress is-link" style="display: none;" value="0" max="1000"></progress>
<div id="calibrate" style="display: none;"> <div id="calibrate" {% if calibration.state == 0 %}style="display: none;" {% endif %}>
<p>Si les données d'étalonnage conviennent, appuyez sur le bouton ci-dessous pour procéder à l'étalonnage du scanner</p> <p>Si les données d'étalonnage conviennent, appuyez sur le bouton ci-dessous pour procéder à l'étalonnage du scanner.</p>
<button id="calibrate-button" class="button is-link">Étalonner le scanner</button> <button id="calibrate-button" class="button is-link">Étalonner le scanner</button>
<p id="calibration-info"></p> <p id="calibration-info"></p>
</div> </div>
@ -74,6 +74,20 @@
buttons.forEach(x => x.removeAttribute('disabled')); buttons.forEach(x => x.removeAttribute('disabled'));
}); });
// If we already have calibration images, we show them right now
if ({% if calibration.state > 0 %}true{% else %}false{% endif %}) {
let cell, img;
{% for led in leds %}
cell = document.createElement('div');
cell.classList.add('cell');
img = document.createElement('img');
img.classList.add('is-loading');
img.src = '/data/calibration/{{ calibration.id }}/{{ led }}.jpg?v=0';
cell.appendChild(img);
grid.appendChild(cell);
{% endfor %}
}
scanButton.addEventListener('click', async () => { scanButton.addEventListener('click', async () => {
scanIndex++; scanIndex++;
progress = 0; progress = 0;
@ -103,7 +117,7 @@
cell.classList.add('cell'); cell.classList.add('cell');
let img = document.createElement('img'); let img = document.createElement('img');
img.classList.add('is-loading'); img.classList.add('is-loading');
img.src = '/data/calibration/{{ calibration.id }}/' + uuid + '.jpg?v=' + scanIndex; img.src = '/data/calibrations/{{ calibration.id }}/' + uuid + '.jpg?v=' + scanIndex;
cell.appendChild(img); cell.appendChild(img);
grid.appendChild(cell); grid.appendChild(cell);
} }

View File

@ -24,7 +24,7 @@
<div class="field"> <div class="field">
<label class="label">Nom de l'objet</label> <label class="label">Nom de l'objet</label>
<div class="control"> <div class="control">
<input class="input" type="text" name="name" placeholder="Nom de l'objet"> <input class="input" type="text" name="name" placeholder="Nom de l'objet" required>
</div> </div>
</div> </div>

View File

@ -4,34 +4,6 @@
<section class="section"> <section class="section">
<div class="container"> <div class="container">
<h1 class="title">{{ object.name }}</h1> <h1 class="title">{{ object.name }}</h1>
{#
<div class="field is-grouped is-grouped-multiline">
<div class="control">
{% if calibration is none or calibration.state == 0 %}
<span class="tags has-addons">
<span class="tag is-dark">étalonnage</span>
<span class="tag is-danger">aucune donnée</span>
</span>
{% elif calibration.state == 1 %}
<span class="tags has-addons">
<span class="tag is-dark">étalonnage</span>
<span class="tag is-warning">pas fait</span>
</span>
{% elif calibration.state == 2 %}
<a href="/calibration/{{ object.id }}" class="tags has-addons">
<span class="tag is-dark">étalonnage</span>
<span class="tag is-success">fait</span>
</a>
{% endif %}
</div>
</div>
{% if calibration is none or calibration.state == 0 %}
<div class="field is-grouped">
<a href="/calibrate/{{ object.id }}" class="button is-link">Étalonner le scanner</a>
<a href="#" title="Non implémenté" disabled="disabled" class="button is-link">Utiliser le dernier étalonnage connu</a>
</div>
{% endif %}
#}
{% if calibration %} {% if calibration %}
<a href="/scan/{{ object.id }}" class="button is-link">Faire un scan</a> <a href="/scan/{{ object.id }}" class="button is-link">Faire un scan</a>
{% else %} {% else %}

View File

@ -44,7 +44,7 @@ function getImageElementById(id: string): HTMLImageElement {
*/ */
export class Engine { export class Engine {
/** The id of the object to scan. */ /** The id of the object to scan. */
objectId: number; calibrationId: number;
/** HTML element on which the renderer will be added. */ /** HTML element on which the renderer will be added. */
domElement: HTMLElement; domElement: HTMLElement;
@ -105,15 +105,15 @@ export class Engine {
/** Initialises the engine. */ /** Initialises the engine. */
static async create(domId: string) { static async create(domId: string) {
let objectId = parseInt(window.location.pathname.split('/')[2], 10); let calibrationId = parseInt(window.location.pathname.split('/')[2], 10);
let domElement = getElementById(domId); let domElement = getElementById(domId);
let engine = new Engine(); let engine = new Engine();
engine.objectId = objectId; engine.calibrationId = calibrationId;
engine.domElement = domElement; engine.domElement = domElement;
engine.initHtml(); engine.initHtml();
let request = await fetch('/data/calibration/' + objectId + '/calibration.json'); let request = await fetch(engine.dataPath('calibration.json'));
let calibration = await request.json(); let calibration = await request.json();
engine.initScene(calibration); engine.initScene(calibration);
engine.initListeners(); engine.initListeners();
@ -130,6 +130,11 @@ export class Engine {
return this.domElement === document.body ? window.innerHeight : this.domElement.offsetHeight; return this.domElement === document.body ? window.innerHeight : this.domElement.offsetHeight;
} }
/** Returns the url of calibration assets. */
dataPath(path: string): string {
return '/data/calibrations/' + this.calibrationId + '/' + path;
}
/** /**
* Initialises the HTML components of the engine. * Initialises the HTML components of the engine.
*/ */
@ -296,7 +301,7 @@ export class Engine {
showImage(led: Led): void { showImage(led: Led): void {
if (led.on) { if (led.on) {
this.selectedObject.innerText = led.name.split('.')[0] + ' (' + (<number> this.leds.currentLedIndex + 1) + '/' + this.leds.leds.length + ')'; this.selectedObject.innerText = led.name.split('.')[0] + ' (' + (<number> this.leds.currentLedIndex + 1) + '/' + this.leds.leds.length + ')';
this.ledView.src = '/data/calibration/' + this.objectId + '/' + led.name; this.ledView.src = this.dataPath(led.name);
this.ledView.style.display = 'block'; this.ledView.style.display = 'block';
} else { } else {
this.selectedObject.innerText = 'aucune'; this.selectedObject.innerText = 'aucune';