From aefbc377959baa3b9125bad93f99cc511576b654 Mon Sep 17 00:00:00 2001 From: pulipakaa24 Date: Sun, 28 Dec 2025 14:06:52 -0600 Subject: [PATCH] Now have support for 2-stage calibration sequence --- agenda.js | 18 +++++-- index.js | 153 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 165 insertions(+), 6 deletions(-) diff --git a/agenda.js b/agenda.js index 78779b0..0787f29 100644 --- a/agenda.js +++ b/agenda.js @@ -25,11 +25,23 @@ const initializeAgenda = async (mongoUri, pool, io) => { // Now accepts pgPool if (result.rowCount != 1) throw new Error("No such peripheral in database"); const {rows} = await sharedPgPool.query("select socket from device_tokens where device_id=$1 and connected=TRUE", [result.rows[0].device_id]); - if (rows.length != 1) console.log("No device with that ID connected to Socket."); + if (rows.length != 1) { + console.log("No device with that ID connected to Socket."); + // Notify user that device is not connected + const {rows: userRows} = await sharedPgPool.query("select socket from user_tokens where user_id=$1", [userID]); + if (userRows.length == 1 && userRows[0]) { + socketIoInstance.to(userRows[0].socket).emit("calib_error", { + periphID: result.rows[0].id, + message: "Device not connected" + }); + } + // Reset await_calib since calibration cannot proceed + await sharedPgPool.query("update peripherals set await_calib=FALSE where id=$1", [periphID]); + } else { const socket = rows[0].socket; if (socket) { - socketIoInstance.to(socket).emit("calib", {periphNum: result.rows[0].peripheral_number}); + socketIoInstance.to(socket).emit("calib_start", {port: result.rows[0].peripheral_number}); } } @@ -61,7 +73,7 @@ const initializeAgenda = async (mongoUri, pool, io) => { // Now accepts pgPool else { const socket = rows[0].socket; if (socket) { - socketIoInstance.to(socket).emit("cancel_calib", {periphNum: result.rows[0].peripheral_number}); + socketIoInstance.to(socket).emit("cancel_calib", {port: result.rows[0].peripheral_number}); } } } catch (error) { diff --git a/index.js b/index.js index d6eecea..28c7603 100644 --- a/index.js +++ b/index.js @@ -87,7 +87,7 @@ io.on('connection', async (socket) => { try { // Get peripheral states for this device const {rows: periphRows} = await pool.query( - "select last_pos, peripheral_number, await_calib, calibrated from peripherals where device_id=$1", + "select last_pos, peripheral_number, calibrated from peripherals where device_id=$1", [id] ); @@ -98,12 +98,20 @@ io.on('connection', async (socket) => { deviceState: periphRows.map(row => ({ port: row.peripheral_number, lastPos: row.last_pos, - awaitCalib: row.await_calib, calibrated: row.calibrated })) }; socket.emit("device_init", successResponse); + + // Notify user app that device is now connected + const {rows: deviceUserRows} = await pool.query("select user_id from devices where id=$1", [id]); + if (deviceUserRows.length === 1) { + const {rows: userRows} = await pool.query("select socket from user_tokens where user_id=$1 and connected=TRUE", [deviceUserRows[0].user_id]); + if (userRows.length === 1 && userRows[0]) { + io.to(userRows[0].socket).emit("device_connected", {deviceID: id}); + } + } } catch (err) { console.error("Error fetching device state:", err); socket.emit("device_init", { @@ -134,6 +142,111 @@ io.on('connection', async (socket) => { socket.disconnect(true); } + // Device acknowledges ready for stage 1 (tilt up) + socket.on('calib_stage1_ready', async (data) => { + console.log(`Device ready for stage 1 (tilt up): ${socket.id}`); + console.log(data); + try { + const {rows} = await pool.query("select device_id from device_tokens where socket=$1 and connected=TRUE", [socket.id]); + if (rows.length != 1) throw new Error("No device with that ID connected to Socket."); + + const result = await pool.query( + "select id, user_id from peripherals where device_id=$1 and peripheral_number=$2", + [rows[0].device_id, data.port] + ); + if (result.rowCount != 1) throw new Error("No such peripheral in database"); + + const {rows: userRows} = await pool.query("select socket from user_tokens where user_id=$1", [result.rows[0].user_id]); + if (userRows.length == 1 && userRows[0]) { + const userSocket = userRows[0].socket; + io.to(userSocket).emit("calib_stage1_ready", {periphID: result.rows[0].id}); + } + } catch (error) { + console.error(`Error in calib_stage1_ready:`, error); + } + }); + + // Device acknowledges ready for stage 2 (tilt down) + socket.on('calib_stage2_ready', async (data) => { + console.log(`Device ready for stage 2 (tilt down): ${socket.id}`); + console.log(data); + try { + const {rows} = await pool.query("select device_id from device_tokens where socket=$1 and connected=TRUE", [socket.id]); + if (rows.length != 1) throw new Error("No device with that ID connected to Socket."); + + const result = await pool.query( + "select id, user_id from peripherals where device_id=$1 and peripheral_number=$2", + [rows[0].device_id, data.port] + ); + if (result.rowCount != 1) throw new Error("No such peripheral in database"); + + const {rows: userRows} = await pool.query("select socket from user_tokens where user_id=$1", [result.rows[0].user_id]); + if (userRows.length == 1 && userRows[0]) { + const userSocket = userRows[0].socket; + io.to(userSocket).emit("calib_stage2_ready", {periphID: result.rows[0].id}); + } + } catch (error) { + console.error(`Error in calib_stage2_ready:`, error); + } + }); + + // User confirms stage 1 complete (tilt up done) + socket.on('user_stage1_complete', async (data) => { + console.log(`User confirms stage 1 complete: ${socket.id}`); + console.log(data); + try { + const {rows} = await pool.query("select socket from device_tokens where device_id=$1 and connected=TRUE", [data.deviceID]); + if (rows.length != 1) { + // Device not connected - notify app + socket.emit("calib_error", { + periphID: data.periphID, + message: "Device disconnected during calibration" + }); + // Reset peripheral state + await pool.query("update peripherals set await_calib=FALSE where id=$1", [data.periphID]); + return; + } + + const deviceSocket = rows[0].socket; + io.to(deviceSocket).emit("user_stage1_complete", {port: data.periphNum}); + } catch (error) { + console.error(`Error in user_stage1_complete:`, error); + socket.emit("calib_error", { + periphID: data.periphID, + message: "Error during calibration" + }); + } + }); + + // User confirms stage 2 complete (tilt down done) + socket.on('user_stage2_complete', async (data) => { + console.log(`User confirms stage 2 complete: ${socket.id}`); + console.log(data); + try { + const {rows} = await pool.query("select socket from device_tokens where device_id=$1 and connected=TRUE", [data.deviceID]); + if (rows.length != 1) { + // Device not connected - notify app + socket.emit("calib_error", { + periphID: data.periphID, + message: "Device disconnected during calibration" + }); + // Reset peripheral state + await pool.query("update peripherals set await_calib=FALSE where id=$1", [data.periphID]); + return; + } + + const deviceSocket = rows[0].socket; + io.to(deviceSocket).emit("user_stage2_complete", {port: data.periphNum}); + } catch (error) { + console.error(`Error in user_stage2_complete:`, error); + socket.emit("calib_error", { + periphID: data.periphID, + message: "Error during calibration" + }); + } + }); + + // Device acknowledges calibration complete socket.on('calib_done', async (data) => { console.log(`Received 'calib_done' event from client ${socket.id}:`); console.log(data); @@ -194,7 +307,21 @@ io.on('connection', async (socket) => { } else { console.log("device disconnect"); - await pool.query("update device_tokens set connected=FALSE where socket=$1", [socket.id]); + const {rows} = await pool.query( + "update device_tokens set connected=FALSE where socket=$1 returning device_id", + [socket.id] + ); + + // Notify user app that device disconnected + if (rows.length === 1) { + const {rows: deviceRows} = await pool.query("select user_id from devices where id=$1", [rows[0].device_id]); + if (deviceRows.length === 1) { + const {rows: userRows} = await pool.query("select socket from user_tokens where user_id=$1 and connected=TRUE", [deviceRows[0].user_id]); + if (userRows.length === 1 && userRows[0]) { + io.to(userRows[0].socket).emit("device_disconnected", {deviceID: rows[0].device_id}); + } + } + } } }); }); @@ -520,6 +647,26 @@ app.get('/peripheral_name', authenticateToken, async (req, res) => { } }) +app.get('/device_connection_status', authenticateToken, async (req, res) => { + console.log("device connection status"); + try { + const {deviceId} = req.query; + // Verify device belongs to user + const {rows: deviceRows} = await pool.query("select id from devices where id=$1 and user_id=$2", + [deviceId, req.user] + ); + if (deviceRows.length != 1) return res.sendStatus(404); + + // Check if device has an active socket connection + const {rows} = await pool.query("select connected from device_tokens where device_id=$1 and connected=TRUE", + [deviceId] + ); + res.status(200).json({connected: rows.length > 0}); + } catch { + res.sendStatus(500); + } +}) + app.post('/completed_calib', authenticateToken, async (req, res) => { console.log("calibration complete"); try {