172 lines
5.7 KiB
JavaScript
172 lines
5.7 KiB
JavaScript
const express = require('express');
|
||
const cors = require('cors');
|
||
const bodyParser = require('body-parser');
|
||
const path = require('path');
|
||
const mqtt = require('mqtt');
|
||
require('dotenv').config();
|
||
|
||
const db = require('./db/database');
|
||
const apiRoutes = require('./routes/api');
|
||
|
||
const PORT = process.env.PORT || 3000;
|
||
const app = express();
|
||
|
||
app.use(cors());
|
||
app.use(bodyParser.json());
|
||
app.use(bodyParser.urlencoded({ extended: true }));
|
||
app.use(express.static(path.join(__dirname, '..')));
|
||
app.use('/api', apiRoutes);
|
||
|
||
app.get('/', (_req, res) => res.sendFile(path.join(__dirname, '..', 'index.html')));
|
||
app.get('/dashboard', (_req, res) => res.sendFile(path.join(__dirname, '..', 'pages', 'dashboard.html')));
|
||
|
||
|
||
const MQTT_HOST = process.env.MQTT_HOST || 'localhost';
|
||
const MQTT_PORT = process.env.MQTT_PORT || 1883;
|
||
const MQTT_TOPIC = 'smartparking/sensor/#';
|
||
|
||
let mqttClient = null;
|
||
|
||
function connectMQTT() {
|
||
const brokerUrl = `mqtt://${MQTT_HOST}:${MQTT_PORT}`;
|
||
console.log(`🔌 Connexion au broker MQTT : ${brokerUrl}`);
|
||
|
||
mqttClient = mqtt.connect(brokerUrl, {
|
||
clientId: 'smartparking-server-' + Math.random().toString(16).slice(3),
|
||
keepalive: 60,
|
||
reconnectPeriod: 5000,
|
||
connectTimeout: 10000
|
||
});
|
||
|
||
mqttClient.on('connect', () => {
|
||
console.log('✅ MQTT connecté au broker Mosquitto');
|
||
mqttClient.subscribe(MQTT_TOPIC, (err) => {
|
||
if (err) {
|
||
console.error('❌ Erreur abonnement MQTT :', err.message);
|
||
} else {
|
||
console.log(`📡 Abonné au topic : ${MQTT_TOPIC}`);
|
||
}
|
||
});
|
||
});
|
||
|
||
|
||
mqttClient.on('message', async (topic, messageBuffer) => {
|
||
const message = messageBuffer.toString().trim();
|
||
const topicParts = topic.split('/');
|
||
const spotNumber = parseInt(topicParts[2]);
|
||
|
||
if (isNaN(spotNumber)) {
|
||
console.warn(`⚠️ Topic MQTT invalide : ${topic}`);
|
||
return;
|
||
}
|
||
|
||
console.log(`📩 MQTT reçu → topic: ${topic} | valeur: ${message}`);
|
||
|
||
|
||
const newStatus = message === '1' ? 'occupied' : 'free';
|
||
|
||
try {
|
||
|
||
const spots = await db.getAllSpots();
|
||
const spot = spots.find(s => s.number === spotNumber);
|
||
|
||
if (!spot) {
|
||
console.warn(`⚠️ Place numéro ${spotNumber} introuvable en base`);
|
||
return;
|
||
}
|
||
|
||
|
||
if (spot.status === 'reserved' && newStatus === 'occupied') {
|
||
console.log(`ℹ️ Place ${spotNumber} déjà réservée — capteur ignoré`);
|
||
await db.recordMqttEvent(topic, message);
|
||
return;
|
||
}
|
||
|
||
|
||
await db.updateSpotStatus(spot.id, newStatus);
|
||
await db.recordMqttEvent(topic, message);
|
||
|
||
console.log(`✅ Place ${spotNumber} mise à jour → ${newStatus}`);
|
||
} catch (err) {
|
||
console.error('❌ Erreur traitement message MQTT :', err.message);
|
||
}
|
||
});
|
||
|
||
mqttClient.on('error', (err) => {
|
||
console.error('❌ Erreur MQTT :', err.message);
|
||
});
|
||
|
||
mqttClient.on('reconnect', () => {
|
||
console.log('🔄 Reconnexion MQTT en cours...');
|
||
});
|
||
|
||
mqttClient.on('offline', () => {
|
||
console.warn('⚠️ Client MQTT hors-ligne');
|
||
});
|
||
}
|
||
|
||
|
||
|
||
async function startServer() {
|
||
try {
|
||
// 1. Initialiser la base de données
|
||
await db.initDatabase();
|
||
|
||
// 2. Démarrer le serveur HTTP
|
||
app.listen(PORT, () => {
|
||
console.log(`
|
||
╔══════════════════════════════════════════════════════╗
|
||
║ 🅿️ SMART PARKING SERVER v2.0 - PRÊT ║
|
||
╠══════════════════════════════════════════════════════╣
|
||
║ 🌐 Port HTTP : ${PORT}
|
||
║ 🗄️ Base : MariaDB (${process.env.DB_HOST || 'localhost'})
|
||
║ 📡 MQTT : ${MQTT_HOST}:${MQTT_PORT}
|
||
║ 🔐 JWT sécurisé
|
||
╚══════════════════════════════════════════════════════╝
|
||
`);
|
||
});
|
||
|
||
|
||
connectMQTT();
|
||
|
||
|
||
setInterval(async () => {
|
||
try {
|
||
const spots = await db.getAllSpots();
|
||
const total = spots.length;
|
||
const free = spots.filter(s => s.status === 'free').length;
|
||
const occupied = spots.filter(s => s.status === 'occupied').length;
|
||
const reserved = spots.filter(s => s.status === 'reserved').length;
|
||
await db.recordStats(total, free, occupied, reserved);
|
||
} catch (err) {
|
||
console.error('❌ Erreur stats :', err.message);
|
||
}
|
||
}, 5 * 60 * 1000);
|
||
|
||
|
||
setInterval(async () => {
|
||
try {
|
||
const count = await db.expireReservations();
|
||
if (count > 0) {
|
||
console.log(`⏰ ${count} réservation(s) expirée(s) — places libérées`);
|
||
}
|
||
} catch (err) {
|
||
console.error('❌ Erreur expiration réservations :', err.message);
|
||
}
|
||
}, 60 * 1000);
|
||
|
||
} catch (err) {
|
||
console.error('❌ Erreur au démarrage :', err);
|
||
process.exit(1);
|
||
}
|
||
}
|
||
|
||
|
||
process.on('SIGINT', async () => {
|
||
console.log('\n🛑 Arrêt du serveur...');
|
||
if (mqttClient) mqttClient.end();
|
||
await db.closeDatabase();
|
||
process.exit(0);
|
||
});
|
||
|
||
startServer(); |