From 265d1c5f18cdfbcc686cbd976e09698b1f026495 Mon Sep 17 00:00:00 2001 From: Kerboul Date: Thu, 3 Apr 2025 14:42:51 +0200 Subject: [PATCH] =?UTF-8?q?Ajout=20de=20la=20gestion=20des=20routes=20pour?= =?UTF-8?q?=20le=20syst=C3=A8me=20de=20capture,=20mise=20=C3=A0=20jour=20d?= =?UTF-8?q?es=20chemins=20de=20fichiers=20et=20am=C3=A9lioration=20de=20la?= =?UTF-8?q?=20logique=20de=20gestion=20des=20mesures=20et=20vid=C3=A9os.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api.js | 2 - routes/cameraRoutes.js | 359 ------------------------------- routes/capture_system.js | 8 + routes/uploadRoutes.js | 4 +- src/data/filewatcher.js | 2 +- src/data/storage_manager.js | 20 +- src/database/database_manager.js | 10 +- stuff.md | 47 ++-- 8 files changed, 52 insertions(+), 400 deletions(-) delete mode 100644 routes/cameraRoutes.js create mode 100644 routes/capture_system.js diff --git a/api.js b/api.js index 1a82e09..84514b1 100644 --- a/api.js +++ b/api.js @@ -5,7 +5,6 @@ const projectRoutes = require('./routes/projectRoutes'); const measurementRoutes = require('./routes/measurementRoutes'); const videoRoutes = require('./routes/videoRoutes'); const imageRoutes = require('./routes/imageRoutes'); -const cameraRoutes = require('./routes/cameraRoutes'); const uploadRoutes = require('./routes/uploadRoutes'); const FileWatcher = require('./src/data/filewatcher'); const database_manager = require('./src/database/database_manager'); @@ -22,6 +21,5 @@ router.use('/', measurementRoutes); router.use('/', videoRoutes); router.use('/', imageRoutes); router.use('/', uploadRoutes); -router.use('/', cameraRoutes); module.exports = router; diff --git a/routes/cameraRoutes.js b/routes/cameraRoutes.js deleted file mode 100644 index c5d488d..0000000 --- a/routes/cameraRoutes.js +++ /dev/null @@ -1,359 +0,0 @@ -const express = require('express'); -const router = express.Router(); -const db = require('../db'); -const serverError = require('../utils/serverError'); -const { start } = require('repl'); - -//const minInterval = 3; // Minutes -//const maxInterval = 60; // Minutes - -var defaultCaptureInterval = 5; // minutes -var defaultMaintenance = 0; -var defaultActive = 0; // 0 = pas de capture, 1 = capture en cours - -async function initCamera() { - const query = 'SELECT * FROM public.camera WHERE id = $1'; - const values = [1]; - - db.query(query, values, (err, result) => { - if (err) { - console.error('Erreur lors de la vérification de l\'entrée caméra:', err); - return; - } - - if (result.rows.length === 0) { - const insertQuery = ` - INSERT INTO public.camera (id, interval, maintenance, active) - VALUES ($1, $2, $3, $4) - `; - const insertValues = [1, defaultCaptureInterval, defaultMaintenance, defaultActive]; - - db.query(insertQuery, insertValues, (err) => { - if (err) { - console.error('Erreur lors de l\'initialisation de la caméra:', err); - } else { - console.log('Caméra initialisée avec les valeurs par défaut.'); - } - }); - } else { - console.log('L\'entrée caméra avec l\'ID 1 existe déjà. Aucune initialisation nécessaire.'); - } - }); -} - -async function getCamera() { - // retourner l'état de la caméra - const query = 'SELECT * FROM public.camera WHERE id = $1'; - const values = [1]; - - try { - const result = await db.query(query, values); - if (result.rows.length === 0) { - console.log('Aucune entrée caméra trouvée.'); - return null; - } else { - const camera = result.rows[0]; - console.log('État de la caméra récupéré avec succès:', camera); - return { - captureInterval: camera.interval, - captureProjectID: camera.active, - captureStatus: camera.active, - maintenance: camera.maintenance - }; - } - } catch (err) { - console.error('Erreur lors de la récupération de l\'état de la caméra:', err); - throw err; - } -} - -async function printCameraStatus() { - let camera = await getCamera(); - console.log('Statut de la caméra:'); - console.log('Intervalle de capture:', camera.captureInterval, 'minutes'); - console.log('Maintenance:', camera.maintenance === 1 ? 'En cours' : 'Aucune'); - console.log('Statut de la capture:', camera.active === 1 ? 'En cours' : 'Arrêté'); - console.log('-----------------------------------'); -} - -async function isCameraOccupied() { - try { - const query = 'SELECT id FROM public.projects WHERE status = $1 LIMIT 1'; - const values = [1]; - - return new Promise((resolve, reject) => { - db.query(query, values, (err, result) => { - if (err) { - console.error('Erreur lors de la vérification de l\'occupation de la caméra:', err); - reject(err); - } else { - const isOccupied = result.rows.length > 0 ? result.rows[0].id : null; - resolve(isOccupied); - } - }); - }); - - } catch (err) { - console.error('Erreur inattendue lors de la vérification de l\'occupation de la caméra:', err); - } -} - -async function getCurrentProject() { - try { - const query = 'SELECT * FROM public.projects WHERE status = $1 LIMIT 1'; - const values = [1]; - - return new Promise((resolve, reject) => { - db.query(query, values, (err, result) => { - if (err) { - console.error('Erreur lors de la récupération du projet en cours:', err); - reject(err); - } else if (result.rows.length === 0) { - console.log('Aucun projet en cours trouvé.'); - resolve(null); - } else { - const currentProject = result.rows[0]; - console.log('Projet en cours récupéré avec succès:', currentProject); - resolve(currentProject); - } - }); - }); - } catch (err) { - console.error('Erreur inattendue lors de la récupération du projet en cours:', err); - } -} - -async function resetProjectStatus() { - const query = 'UPDATE public.projects SET status = $1 WHERE status = $2'; - const values = [2, 1]; - - db.query(query, values, (err) => { - if (err) { - console.error('Erreur lors de la réinitialisation du statut des projets:', err); - } else { - console.log('Statut des projets réinitialisé avec succès.'); - } - }); -} - -async function activateCamera() { - const query = 'UPDATE public.camera SET active = $1 WHERE id = $2'; - const values = [1, 1]; - - db.query(query, values, (err) => { - if (err) { - console.error('Erreur lors de l\'activation de la caméra:', err); - } else { - console.log('Caméra activée avec succès.'); - } - }); -} - -async function deactivateCamera() { - const query = 'UPDATE public.camera SET active = $1 WHERE id = $2'; - const values = [0, 1]; - - db.query(query, values, (err) => { - if (err) { - console.error('Erreur lors de la désactivation de la caméra:', err); - } else { - console.log('Caméra désactivée avec succès.'); - } - }); -} - -async function changeProjectStatus(projectId, status) { - try { - const query = 'UPDATE public.projects SET status = $1 WHERE id = $2'; - const values = [status, projectId]; - - await db.query(query, values); - console.log(`Statut du projet ID ${projectId} modifié avec succès à ${status}.`); - } catch (err) { - console.error('Une erreur inattendue s\'est produite lors de la modification du statut du projet:', err); - } -} - -async function startup() { - await initCamera(); - await printCameraStatus(); -} - -startup() - .catch(err => { - console.error('Erreur lors de l\'initialisation de la caméra:', err); - }); - -/** - * @swagger - * /camera/status: - * get: - * summary: Get the current status of the camera - * tags: - * - Camera - * responses: - * 200: - * description: Successfully retrieved the camera status - * content: - * application/json: - * schema: - * type: object - * properties: - * captureInterval: - * type: integer - * description: Capture interval in minutes - * captureProjectID: - * type: integer - * description: ID of the project currently being captured - * captureStatus: - * type: integer - * description: Capture status (0 = stopped, 1 = ongoing) - * maintenance: - * type: integer - * description: Maintenance status (0 = none, 1 = ongoing) - * 500: - * description: Internal server error - */ -router.get('/camera/status', async (req, res) => { - try { - const cameraStatus = await getCamera(); - res.status(200).json(cameraStatus); - } catch (err) { - serverError.sendError('Erreur lors de la récupération de l\'état de la caméra:', res, err, 500); - } -}); - -async function setCameraSettings(interval, maintenance) { - try { - const query = ` - UPDATE public.camera - SET interval = $1, maintenance = $2 - WHERE id = $3 - `; - const values = [interval, maintenance, 1]; - - db.query(query, values, (err) => { - if (err) { - console.error('Erreur lors de la mise à jour des paramètres de la caméra:', err); - } else { - console.log('Paramètres de la caméra mis à jour avec succès.'); - //captureInterval = interval; - //maintenance = maintenance; - } - }); - } catch (err) { - console.error('Une erreur inattendue s\'est produite lors de la mise à jour des paramètres de la caméra:', err); - } -} - -async function startProcedure(projectId, interval, maintenance) { - if (isNaN(projectId) || isNaN(interval) || isNaN(maintenance)) { - return { error: 'Invalid parameters' }; - } - const cameraOccupied = await isCameraOccupied(); - if (cameraOccupied) { - return { error: 'Camera is occupied by another project' }; - } else { - await activateCamera(); - await setCameraSettings(interval, maintenance); - await changeProjectStatus(projectId, 1); // changer le statut du projet en cours à 1 (en cours) - console.log('Procédure de capture démarrée avec succès.'); - return { message: 'Capture procedure started successfully' }; - } -} - -async function stopProcedure() { - var project = await getCurrentProject(); - console.log(project); - if (project) { - await resetProjectStatus(); // réinitialiser le statut du projet en cours - await deactivateCamera(); // désactiver la caméra - await changeProjectStatus(project.id, 2); // changer le statut du projet en cours à 2 (terminé) - console.log('Procédure de capture arrêtée avec succès.'); - return { message: 'Capture procedure stopped successfully' }; - } else { - return { error: 'No project is currently being captured' }; - } -} - -/** - * @swagger - * /procedure/start/: - * post: - * summary: Start the capture procedure - * tags: - * - Procedure - * requestBody: - * required: true - * content: - * application/json: - * schema: - * type: object - * properties: - * projectId: - * type: integer - * description: ID of the project to start capturing - * interval: - * type: integer - * description: Capture interval in minutes - * maintenance: - * type: integer - * description: Maintenance status (0 = none, 1 = ongoing) - * responses: - * 200: - * description: Successfully started the capture procedure - * content: - * application/json: - * schema: - * type: object - * properties: - * message: - * type: string - * description: Success message - * error: - * type: string - * description: Error message, if any - * 500: - * description: Internal server error - * /procedure/stop/: - * post: - * summary: Stop the capture procedure - * tags: - * - Procedure - * responses: - * 200: - * description: Successfully stopped the capture procedure - * content: - * application/json: - * schema: - * type: object - * properties: - * message: - * type: string - * description: Success message - * error: - * type: string - * description: Error message, if any - * 500: - * description: Internal server error - */ -router.post('/procedure/start/', async (req, res) => { - const { projectId, interval, maintenance } = req.body; - try { - const result = await startProcedure(projectId, interval, maintenance); - res.status(200).json(result); - } catch (err) { - serverError.sendError('Erreur lors du démarrage de la procédure de capture:', res, err, 500); - } -}); - -router.post('/procedure/stop/', async (req, res) => { - try { - const result = await stopProcedure(); - res.status(200).json(result); - } catch (err) { - serverError.sendError('Erreur lors de l\'arrêt de la procédure de capture:', res, err, 500); - } -}); - -module.exports = router; diff --git a/routes/capture_system.js b/routes/capture_system.js new file mode 100644 index 0000000..2d457e2 --- /dev/null +++ b/routes/capture_system.js @@ -0,0 +1,8 @@ +const express = require('express'); +const router = express.Router(); +const sharp = require('sharp'); +const fs = require('fs'); +const dbTester = require('../test/tester'); +const db = require('../db'); +const serverError = require('../utils/serverError'); + diff --git a/routes/uploadRoutes.js b/routes/uploadRoutes.js index f39e3db..f503566 100644 --- a/routes/uploadRoutes.js +++ b/routes/uploadRoutes.js @@ -34,9 +34,9 @@ router.post('/uploadmeasurement', upload.single('image'), async (req, res) => { if (!imagePath) { return res.status(500).json({ error: 'Failed to upload image' }); } - const measurement = await database_manager.measurement.add_measurement(projectId, timestamp, imagePath, temperature, humidity, nextOrderId); + const measurement = await database_manager.measurement.create_measurement(projectId, timestamp, imagePath, temperature, humidity, nextOrderId); if (!measurement) { - return res.status(500).json({ error: 'Failed to add measurement' }); + return res.status(500).json({ error: 'Failed to create measurement' }); } res.json({ message: 'Measurement uploaded successfully', path: imagePath, id: measurement.id }); } catch (error) { diff --git a/src/data/filewatcher.js b/src/data/filewatcher.js index 97ec123..3ba2fd4 100644 --- a/src/data/filewatcher.js +++ b/src/data/filewatcher.js @@ -38,7 +38,7 @@ async function checkAndRemoveInvalidEntries() { // Run the check periodically console.log('[INFO] Activation du FileWatcher pour surveiller les fichiers invalides...') -setInterval(checkAndRemoveInvalidEntries, 10000); // Every 10 seconds +setInterval(checkAndRemoveInvalidEntries, 1000); // Every 10 seconds // Initial run checkAndRemoveInvalidEntries(); \ No newline at end of file diff --git a/src/data/storage_manager.js b/src/data/storage_manager.js index a6afc11..097fe45 100644 --- a/src/data/storage_manager.js +++ b/src/data/storage_manager.js @@ -1,5 +1,6 @@ const fs = require('fs').promises; const path = require('path'); +const { Buffer } = require('buffer'); const PROJECTS_DIR = path.join('.'); const database_manager = require('../database/database_manager.js'); @@ -62,7 +63,8 @@ async function scanAllImages(dir = 'storage') { } async function saveFile(filePath, content) { - let Buffer=Buffer.from(content, 'base64'); + const dir = path.dirname(filePath); + await fs.mkdir(dir, { recursive: true }); if (Buffer.isBuffer(content)) { await fs.writeFile(filePath, content); } else { @@ -101,7 +103,7 @@ async function handleFileOperation(operation, ...args) { const project = { createProjectDirectory: async function (projectId) { - const projectPath = `${projectId}`; + const projectPath = path.join(PROJECTS_DIR, 'storage', `${projectId}`); await handleFileOperation(createFolder, projectPath); await handleFileOperation(createFolder, `${projectPath}/images`); await handleFileOperation(createFolder, `${projectPath}/videos`); @@ -109,7 +111,7 @@ const project = { }, deleteProjectDirectory: async function (projectId) { - const projectPath = `${projectId}`; + const projectPath = path.join(PROJECTS_DIR, 'storage', `${projectId}`); await handleFileOperation(deleteFolder, projectPath); console.log("[FILE] deleteProjectDirectory : " + projectPath); } @@ -117,15 +119,15 @@ const project = { const measurement = { get_measurement_image: async function (projectId, orderId) { - const projectPath = `${projectId}`; - const imagePath = `${projectPath}/images/${orderId}.jpg`; + const projectPath = path.join(PROJECTS_DIR, 'storage', `${projectId}`); + const imagePath = path.join(projectPath, 'images', `${orderId}.jpg`); console.log("[FILE] get_measurement_image : " + imagePath); return await handleFileOperation(getFile, imagePath); }, upload_measurement_image: async function (image, projectId, orderId) { - const projectPath = `${projectId}`; - const imagePath = `${projectPath}/images/${orderId}.jpg`; + const projectPath = path.join(PROJECTS_DIR, 'storage', `${projectId}`); + const imagePath = path.join(projectPath, 'images', `${orderId}.jpg`); console.log("[FILE] upload_measurement_image : " + imagePath); await handleFileOperation(saveFile, imagePath, image.buffer); return imagePath; @@ -158,8 +160,8 @@ const measurement = { const video = { get_video: async function (projectId, orderId) { - const projectPath = `${projectId}`; - const videoPath = `${projectPath}/videos/${orderId}.mp4`; + const projectPath = path.join(PROJECTS_DIR, `${projectId}`); + const videoPath = `/storage/${projectPath}/videos/${orderId}.mp4`; console.log("[FILE] get_video : " + videoPath); return await handleFileOperation(getFile, videoPath); }, diff --git a/src/database/database_manager.js b/src/database/database_manager.js index 2e5cf26..8bf9528 100644 --- a/src/database/database_manager.js +++ b/src/database/database_manager.js @@ -259,8 +259,8 @@ const video = { const camera = { get_camera: handleDatabaseOperation(async () => { - const query = `SELECT * FROM camera;`; - return (await db.query(query)).rows; + const query = `SELECT * FROM camera WHERE id = 1;`; + return (await db.query(query)).rows[0]; }), edit_camera: handleDatabaseOperation(async (id, updates) => { @@ -276,6 +276,12 @@ const camera = { }) }; +// zone de test +async function test_zone(){ +} + +test_zone(); + // Export des modules module.exports = { project, diff --git a/stuff.md b/stuff.md index 8832e0a..39becb1 100644 --- a/stuff.md +++ b/stuff.md @@ -1,30 +1,27 @@ -Routes : +Workflow Caméra -- /projects = liste des projets -- /projects/:id = détail d'un projet -- /projects/:id/edit = édition d'un projet -- /projects/:id/delete = suppression d'un projet -- /projects/new = création d'un projet -- /projects/:id/measurements = liste des mesures d'un projet -- /projects/:id/measurements/:id = détail d'une mesure -- /projects/:id/measurements/:id/edit = édition d'une mesure -- /projects/:id/measurements/:id/delete = suppression d'une mesure -- /projects/:id/videos = liste des vidéos d'un projet -- /projects/:id/videos/:id = détail d'une vidéo +Côté Caméra +/camera/status // récupérer le statut de la caméra (GET) -- /measurements = liste des mesures -- /measurements/:id = détail d'une mesure -- /measurements/:id/edit = édition d'une mesure -- /measurements/:id/delete = suppression d'une mesure -- /measurements/new = création d'une mesure +si stop : +/camera/stop // arrêter la caméra (POST) -- /cameras = liste des caméras -- /cameras/:id = détail d'une caméra -- /cameras/:id/edit = édition d'une caméra -- /cameras/:id/delete = suppression d'une caméra -- /cameras/new = création d'une caméra +si upload : +/camera/upload // uploader la vidéo (POST) -- /data/image/:id = image depuis le pool de stockage -- /data/video/:id = vidéo depuis le pool de stockage -- \ No newline at end of file +Côté Backend + +/procedure/start // démarrer une procédure (POST) +/procedure/status // récupérer le statut de la caméra/procédure courante (GET) +/procedure/stop // arrêter la procédure courante (POST) (doit attendre la confirmation de /camera/stop) +/procedure/delete // supprimer la procédure courante (POST) (doit attendre la confirmation de /camera/delete) + +Modèle de données : + +table camera (paramètres de la caméra et procédure courante) +id (int, PK) - Toujours 1 +interval(int) - Intervalle de la caméra (en minutes), peut être null +image +maintenance(int) - 1 ou 0, 1 = maintenance, 0 = pas de maintenance (si maintenance, la caméra ne doit pas se redémarrer) +status(int) - 1 ou 0, 1 = caméra en cours d'utilisation, 0 = caméra arrêtée (si la caméra est arrêtée, la procédure doit être arrêtée) \ No newline at end of file