From 4ec0eee2501e47dddca2533ead861ec6de4a1cf3 Mon Sep 17 00:00:00 2001 From: Bhaskar Date: Mon, 3 Mar 2025 17:30:42 +0530 Subject: [PATCH] forced manual notification and survey login --- src/controllers/tanksController.js | 779 +++++++++++++++++++++++------ src/index.js | 72 ++- src/models/store.js | 77 ++- 3 files changed, 772 insertions(+), 156 deletions(-) diff --git a/src/controllers/tanksController.js b/src/controllers/tanksController.js index 880ebae1..a98412ac 100644 --- a/src/controllers/tanksController.js +++ b/src/controllers/tanksController.js @@ -2203,64 +2203,71 @@ eventEmitter.on('sendThresholdTimeNotification', async (fcmTokens, message) => { // } // ); - // πŸš€ Motor Start Notification -eventEmitter.on( - "sendMotorStartNotification", - async (hw_Id, customerId, fcmTokens, waterLevel, blockName, tankName, motorOnType, stopCriteria, typeOfWater, manualThresholdTime) => { - try { - const formattedTime = new Date().toLocaleTimeString("en-IN", { timeZone: "Asia/Kolkata" }); +eventEmitter.on("sendMotorStartNotification", async (hw_Id, customerId, fcmTokens, waterLevel, blockName, tankName, motorOnType, stopCriteria, typeOfWater, manualThresholdTime) => { + try { + const formattedTime = new Date().toLocaleTimeString("en-IN", { timeZone: "Asia/Kolkata" }); - if (motorOnType !== "forced_manual") { - console.log(`⚠️ Skipping notification: Motor was started in **${motorOnType}** mode.`); - return; - } + const normalizedMotorOnType = motorOnType.toLowerCase(); + if (normalizedMotorOnType !== "forced_manual") { + console.log(`⚠️ Skipping notification: Motor started in **${motorOnType}** mode.`); + return; + } - const stopConditionMessage = stopCriteria === "manual" - ? `⚠️ Pump will stop **manually**.` - : `🚨 Pump will stop when the water level reaches **${manualThresholdTime}%**.`; + if (!Array.isArray(fcmTokens) || fcmTokens.length === 0) { + console.warn(`⚠️ No valid FCM tokens found for Customer ID: ${customerId}`); + return; + } - const message = `🚰 **Motor Started** πŸš€\n` + - `πŸ‘€ **Customer ID:** ${customerId}\n` + - `πŸ”Ή **Motor Name:** ${tankName} - ${blockName} - ${typeOfWater}\n` + - `🏒 **Block Name:** ${blockName}\n` + - `πŸ’§ **Water Level:** ${waterLevel}%\n` + - `πŸ“± **Mode:** **Physically Started**\n` + - `πŸ•’ **Pump started at:** ${formattedTime}\n` + - stopConditionMessage; + const stopConditionMessage = stopCriteria === "level" + ? `🚨 Pump will stop when the water level reaches **${manualThresholdTime}%**.` + : `⚠️ Pump will stop **manually**.`; - await sendNotification(hw_Id, customerId, fcmTokens, "Motor Started πŸš€", message); - console.log(`βœ… Motor start notification sent for Customer ID: ${customerId}`); + const message = `🚰 **Motor Started** πŸš€\n` + + `πŸ‘€ **Customer ID:** ${customerId}\n` + + `πŸ”Ή **Motor Name:** ${tankName} - ${blockName} - ${typeOfWater}\n` + + `πŸ’§ **Water Level:** ${waterLevel}%\n` + + `πŸ“± **Mode:** **Forced Manual**\n` + + `πŸ•’ **Pump started at:** ${formattedTime}\n` + - } catch (error) { - console.error(`❌ Error in sendMotorStartNotification for Customer ID: ${customerId}`, error); - } + await sendNotification(hw_Id, customerId, fcmTokens, "Motor Started πŸš€", message); + console.log(`βœ… Motor start notification sent for Customer ID: ${customerId}`); + } catch (error) { + console.error(`❌ Error in sendMotorStartNotification for Customer ID: ${customerId}`, error); } -); +}); // πŸ›‘ Motor Stop Notification -eventEmitter.on( - "sendMotorStopNotification", - async (hw_Id, customerId, fcmTokens, waterLevel, blockName, tankName, motorOnType, typeOfWater) => { - try { - const formattedTime = new Date().toLocaleTimeString("en-IN", { timeZone: "Asia/Kolkata" }); - - const message = `πŸ›‘ **Motor Stopped** ❌\n` + - `πŸ‘€ **Customer ID:** ${customerId}\n` + - `πŸ”Ή **Motor Name:** ${tankName} - ${blockName} - ${typeOfWater}\n` + - `🏒 **Block Name:** ${blockName}\n` + - `πŸ’§ **Water Level:** ${waterLevel}%\n` + - `πŸ“± **Mode:** **Physically Stopped**\n` + - `πŸ•’ **Pump stopped at:** ${formattedTime}`; +eventEmitter.on("sendMotorStopNotification", async (hw_Id, customerId, fcmTokens, waterLevel, blockName, tankName, motorOnType, typeOfWater) => { + try { + const formattedTime = new Date().toLocaleTimeString("en-IN", { timeZone: "Asia/Kolkata" }); - await sendNotification(hw_Id, customerId, fcmTokens, "Motor Stopped ❌", message); - console.log(`βœ… Motor stop notification sent for Customer ID: ${customerId}`); + const normalizedMotorOnType = motorOnType.toLowerCase(); + if (normalizedMotorOnType !== "forced_manual") { + console.log(`⚠️ Skipping notification: Motor stopped in **${motorOnType}** mode.`); + return; + } - } catch (error) { - console.error(`❌ Error in sendMotorStopNotification for Customer ID: ${customerId}`, error); + if (!Array.isArray(fcmTokens) || fcmTokens.length === 0) { + console.warn(`⚠️ No valid FCM tokens found for Customer ID: ${customerId}`); + return; } + + const message = `πŸ›‘ **Motor Stopped** ❌\n` + + `πŸ‘€ **Customer ID:** ${customerId}\n` + + `πŸ”Ή **Motor Name:** ${tankName} - ${blockName} - ${typeOfWater}\n` + + `πŸ’§ **Water Level:** ${waterLevel}%\n` + + `πŸ“± **Mode:** **Forced Manual**\n` + + `πŸ•’ **Pump stopped at:** ${formattedTime}`; + + await sendNotification(hw_Id, customerId, fcmTokens, "Motor Stopped ❌", message); + console.log(`βœ… Motor stop notification sent for Customer ID: ${customerId}`); + } catch (error) { + console.error(`❌ Error in sendMotorStopNotification for Customer ID: ${customerId}`, error); } -); +}); + // eventEmitter.on('sendLowWaterNotification', (fcmTokens, message) => { // const notificationMessage = `Warning: Water level is low in the tank. @@ -2566,96 +2573,79 @@ const emitWithTimestamp = (eventName, fcmTokens, motorId, waterLevel) => { -const sendNotification = async (customerId, fcmIds, title, body) => { +const sendNotification = async (hw_Id, customerId, fcmIds, title, body) => { try { - if (!customerId) { - throw new Error("Customer ID is required."); - } - + if (!customerId) throw new Error("Customer ID is required."); if (!Array.isArray(fcmIds) || fcmIds.length === 0) { throw new Error("No FCM tokens provided or invalid format."); } const flatTokens = fcmIds.flat ? fcmIds.flat() : fcmIds; - if (flatTokens.length === 0) { - throw new Error("Flattened FCM token list is empty."); - } + if (flatTokens.length === 0) throw new Error("Flattened FCM token list is empty."); - // Fetch users based on customerId + console.log(`πŸ“¨ Preparing to send notification to Customer ID: ${customerId}`); + + // Fetch users with FCM tokens & preferences const users = await User.find({ customerId }).select("fcmIds notificationPreference lastNotificationSent"); - + const promises = users.map(async (user) => { const { fcmIds: userFcmIds, notificationPreference, lastNotificationSent } = user; - - if (!Array.isArray(userFcmIds)) { - console.log(`Invalid fcmIds for customer ID: ${customerId}`); - return; - } + if (!Array.isArray(userFcmIds)) return console.log(`⚠️ Invalid fcmIds for customer ID: ${customerId}`); const validTokens = flatTokens.filter(token => userFcmIds.includes(token)); - - if (validTokens.length === 0) { - console.log(`No matching FCM tokens for customer ID: ${customerId}`); - return; - } + if (validTokens.length === 0) return console.log(`⚠️ No matching FCM tokens for customer ID: ${customerId}`); // Handle notification preferences - if (notificationPreference === "never") { - console.log(`Notifications disabled for customer ID: ${customerId}`); - return; - } + if (notificationPreference === "never") return console.log(`πŸ”• Notifications disabled for Customer ID: ${customerId}`); const now = new Date(); const lastSent = new Date(lastNotificationSent || 0); - let minInterval = 0; + switch (notificationPreference) { case "6_hours": - minInterval = 6 * 60 * 60 * 1000; // 6 hours + minInterval = 6 * 60 * 60 * 1000; break; case "8_hours": - minInterval = 8 * 60 * 60 * 1000; // 8 hours + minInterval = 8 * 60 * 60 * 1000; break; case "1_month": - minInterval = 30 * 24 * 60 * 60 * 1000; // 1 month + minInterval = 30 * 24 * 60 * 60 * 1000; break; } - // Check if enough time has passed for non-"always" preferences if (notificationPreference !== "always" && now - lastSent < minInterval) { - console.log(`Skipping notification for customer ID: ${customerId} due to preference (${notificationPreference}).`); - return; + return console.log(`⏳ Skipping notification for Customer ID: ${customerId} due to preference (${notificationPreference}).`); } - // Send notifications + console.log(`πŸš€ Sending notification to Customer ID: ${customerId}, Tokens: ${validTokens.length}`); + const notificationPromises = validTokens.map(async (token) => { try { const response = await admin.messaging().send({ - notification: { title, body }, token, - data: { target: "/tank_levels" }, + notification: { title, body }, + data: { hw_Id, target: "/tank_levels" }, }); - console.log(`Notification sent successfully to token: ${token}`); - console.log("FCM Response:", response); - console.log("Titel:", title); - console.log("Body:", body); - console.log("Data:", data); + console.log(`βœ… Notification sent successfully to token: ${token}`); + console.log("πŸ“¬ FCM Response:", response); + console.log(`πŸ“‘ Sending notification to Customer ID: ${customerId}`); + console.log(`πŸ” FCM Tokens:`, fcmTokens); } catch (error) { - console.error(`Failed to send notification to token: ${token}`, error); + console.error(`❌ Failed to send notification to token: ${token}`, error); if (error.code === "messaging/registration-token-not-registered") { await User.updateOne({ customerId }, { $pull: { fcmIds: token } }); - console.log(`Removed invalid token: ${token}`); + console.log(`πŸ—‘οΈ Removed invalid token: ${token}`); } } }); await Promise.all(notificationPromises); - // Update lastNotificationSent timestamp if preference is not "always" if (notificationPreference !== "always") { await User.updateOne({ customerId }, { $set: { lastNotificationSent: now } }); } @@ -2664,10 +2654,15 @@ const sendNotification = async (customerId, fcmIds, title, body) => { await Promise.all(promises); } catch (error) { - console.error("Error sending notifications:", error); + console.error("❌ Error sending notifications:", error); } }; + + + + + const sendDailyConsumptionNotification = async () => { try { const now = new Date(); @@ -2712,19 +2707,30 @@ const sendDailyConsumptionNotification = async () => { continue; } + // Standardized mapping for water types + const typeMapping = { + "bore water": "Bore Water", + "bore": "Bore Water", + "drinking": "Drinking Water", + "drink": "Drinking Water", + "DRINK": "Drinking Water" + }; + // Calculate consumption per water type let totalConsumption = 0; let consumptionByType = {}; for (const record of consumptions) { - const { typeofwater, consumption } = record; - const consumptionValue = parseInt(consumption, 10) || 0; + let { typeofwater, consumption } = record; + typeofwater = (typeofwater || "").trim().toLowerCase(); // Normalize case + const standardType = typeMapping[typeofwater] || typeofwater; // Use mapped name or original - if (!consumptionByType[typeofwater]) { - consumptionByType[typeofwater] = 0; + const consumptionValue = parseInt(consumption, 10) || 0; + if (!consumptionByType[standardType]) { + consumptionByType[standardType] = 0; } - consumptionByType[typeofwater] += consumptionValue; + consumptionByType[standardType] += consumptionValue; totalConsumption += consumptionValue; } @@ -2766,6 +2772,7 @@ const sendDailyConsumptionNotification = async () => { } }; + // Schedule the function to run every minute to check for user notification times cron.schedule("* * * * *", async () => { console.log("πŸ”„ Running daily consumption notification check..."); @@ -6336,105 +6343,571 @@ client.on('connect', () => { }); }); +// client.on("message", async (topic, message) => { +// console.log(`πŸ“© Message received on topic ${topic}:`, message.toString()); + +// if (topic === "water/iot-data") { +// try { +// const data = JSON.parse(message.toString()); +// const { hw_Id, Motor_status, tanks } = data.objects; + +// const currentDate = new Date(); +// const time = currentDate.toLocaleTimeString("en-IN", { hour12: false, timeZone: "Asia/Kolkata" }); + +// for (const tank of tanks) { +// const { Id: tankhardwareId, level: tankHeight } = tank; +// const existingTank = await Tank.findOne({ hardwareId: hw_Id, tankhardwareId }); + +// if (!existingTank) continue; + +// const customerId = existingTank.customerId; +// const tankName = existingTank.tankName; + +// console.log(`πŸ” Checking FCM tokens for Customer ID: ${customerId}`); + +// const users = await User.find({ customerId }).select("fcmIds"); +// let fcmTokens = users.flatMap(user => user.fcmIds).filter(token => token); + +// console.log(`πŸ“‘ Found ${fcmTokens.length} FCM tokens for Customer ID: ${customerId}`); + +// if (!Array.isArray(fcmTokens) || fcmTokens.length === 0) { +// console.warn(`⚠️ No valid FCM tokens found for Customer ID: ${customerId}`); +// continue; +// } + +// const motorTank = await Tank.findOne({ "connections.inputConnections.motor_id": hw_Id }); + +// if (!motorTank) { +// console.warn(`⚠️ Motor not found for motor_id: ${hw_Id}`); +// return; +// } + +// const inputConnection = motorTank.connections.inputConnections.find(conn => conn.motor_id === hw_Id); +// console.log(`πŸ“’ Emitting 'sendMotorStartNotification' for hw_Id: ${hw_Id}`); + +// if (inputConnection) { +// const blockName = motorTank.blockName || "N/A"; +// console.log(`πŸš€ blockName`, blockName); + +// console.log("πŸ” Debugging Condition Check:"); +// console.log("πŸ› οΈ inputConnection.motor_stop_status:", inputConnection.motor_stop_status); +// console.log("πŸ› οΈ Motor_status:", Motor_status); +// console.log("πŸ› οΈ inputConnection.motor_on_type:", inputConnection.motor_on_type); + +// if (inputConnection.motor_stop_status == 1 && Motor_status == 2) { // Removed forced_manual check +// inputConnection.motor_stop_status = "2"; +// inputConnection.motor_on_type = "forced_manual"; +// inputConnection.startTime = new Date().toISOString(); + +// console.log("πŸš€ Motor started in FORCED_MANUAL mode."); +// console.log("πŸ“’ Emitting sendMotorStartNotification event..."); + +// eventEmitter.emit( +// "sendMotorStartNotification", +// hw_Id, +// customerId, +// fcmTokens, +// inputConnection.water_level || 0, +// blockName, +// tankName, +// "forced_manual", +// inputConnection.stop_criteria, +// inputConnection.typeOfWater, +// inputConnection.manual_threshold_time +// ); +// await motorTank.markModified("connections.inputConnections"); +// await motorTank.save(); // Ensure the change is saved +// console.log("πŸ’Ύ motorTank saved successfully."); +// } + +// console.log("πŸ› οΈ Debugging Motor Stop Condition:"); +// console.log("πŸ› οΈ inputConnection.motor_stop_status:", inputConnection.motor_stop_status); +// console.log("πŸ› οΈ Motor_status:", Motor_status); + +// if (inputConnection.motor_stop_status == 2 && Motor_status == 1) { +// console.log("πŸ›‘ Motor stopping... Updating motor_stop_status."); + +// inputConnection.motor_stop_status = 1; +// inputConnection.stopTime = new Date().toISOString(); + +// console.log("πŸ“’ Emitting sendMotorStopNotification event..."); + +// eventEmitter.emit( +// "sendMotorStopNotification", +// hw_Id, +// customerId, +// fcmTokens, +// inputConnection.water_level || 0, +// blockName, +// tankName, +// "forced_manual", +// inputConnection.typeOfWater +// ); + +// await motorTank.markModified("connections.inputConnections"); +// await motorTank.save(); +// console.log("πŸ’Ύ motorTank saved successfully."); +// } else { +// console.log("❌ Stop condition not met. No notification sent."); +// } +// } +// } + +// console.log(`βœ… Data processed successfully for hardwareId: ${hw_Id}`); +// } catch (err) { +// console.error(`❌ Error processing message: ${err.message}`); +// } +// } +// }); + +// client.on("message", async (topic, message) => { +// console.log(`πŸ“© Message received on topic ${topic}:`, message.toString()); + +// if (topic === "water/iot-data") { +// try { +// const data = JSON.parse(message.toString()); +// const { hw_Id, Motor_status, tanks } = data.objects; + +// for (const tank of tanks) { +// const { Id: tankhardwareId } = tank; +// const existingTank = await Tank.findOne({ hardwareId: hw_Id, tankhardwareId }); + +// if (!existingTank) continue; + +// const customerId = existingTank.customerId; +// const tankName = existingTank.tankName; + +// console.log(`πŸ” Checking FCM tokens for Customer ID: ${customerId}`); + +// const users = await User.find({ customerId }).select("fcmIds"); +// let fcmTokens = users.flatMap(user => user.fcmIds).filter(token => token); + +// console.log(`πŸ“‘ Found ${fcmTokens.length} FCM tokens for Customer ID: ${customerId}`); + +// if (!Array.isArray(fcmTokens) || fcmTokens.length === 0) { +// console.warn(`⚠️ No valid FCM tokens found for Customer ID: ${customerId}`); +// continue; +// } + +// const motorTank = await Tank.findOne({ "connections.inputConnections.motor_id": hw_Id }); + +// if (!motorTank) { +// console.warn(`⚠️ Motor not found for motor_id: ${hw_Id}`); +// return; +// } + +// const inputConnection = motorTank.connections.inputConnections.find(conn => conn.motor_id === hw_Id); + +// if (!inputConnection) { +// console.warn(`⚠️ No inputConnection found for motor_id: ${hw_Id}`); +// return; +// } + +// console.log(`πŸ“’ Processing motor logic for hw_Id: ${hw_Id}`); +// const blockName = motorTank.blockName || "N/A"; + +// console.log("πŸ” Debugging Condition Check:"); +// console.log("πŸ› οΈ inputConnection.motor_stop_status:", inputConnection.motor_stop_status); +// console.log("πŸ› οΈ Motor_status:", Motor_status); +// console.log("πŸ› οΈ inputConnection.motor_on_type:", inputConnection.motor_on_type); + +// /** πŸ”₯ MOTOR START LOGIC */ +// if (Motor_status === 2 && inputConnection.motor_stop_status === 1) { +// // Only update if motor_stop_status is still 1 (motor was previously OFF) +// inputConnection.motor_stop_status = 2; +// inputConnection.motor_on_type = "forced_manual"; +// inputConnection.startTime = new Date().toISOString(); + +// console.log("πŸš€ Motor started. Updating motor_stop_status to 2."); +// console.log("πŸ“’ Emitting sendMotorStartNotification event..."); + +// eventEmitter.emit( +// "sendMotorStartNotification", +// hw_Id, +// customerId, +// fcmTokens, +// inputConnection.water_level || 0, +// blockName, +// tankName, +// "forced_manual", +// inputConnection.stop_criteria, +// inputConnection.typeOfWater, +// inputConnection.manual_threshold_time +// ); + +// await motorTank.markModified("connections.inputConnections"); +// await motorTank.save(); +// console.log("πŸ’Ύ motorTank saved successfully after start."); +// } + +// /** πŸ”₯ MOTOR STOP LOGIC */ +// console.log("πŸ› οΈ Debugging Motor Stop Condition:"); +// console.log("πŸ› οΈ inputConnection.motor_stop_status:", inputConnection.motor_stop_status); +// console.log("πŸ› οΈ Motor_status:", Motor_status); + +// if (Motor_status === 1 && inputConnection.motor_stop_status === 2) { +// // Only update if motor_stop_status was previously 2 (motor was ON) +// console.log("πŸ›‘ Motor stopping... Updating motor_stop_status to 1."); + +// inputConnection.motor_stop_status = 1; +// inputConnection.stopTime = new Date().toISOString(); + +// console.log("πŸ“’ Emitting sendMotorStopNotification event..."); + +// eventEmitter.emit( +// "sendMotorStopNotification", +// hw_Id, +// customerId, +// fcmTokens, +// inputConnection.water_level || 0, +// blockName, +// tankName, +// "forced_manual", +// inputConnection.typeOfWater +// ); + +// await motorTank.markModified("connections.inputConnections"); +// await motorTank.save(); +// console.log("πŸ’Ύ motorTank saved successfully after stop."); +// } else { +// console.log("⚠️ Stop condition not met. No notification sent."); +// } +// } + +// console.log(`βœ… Data processed successfully for hardwareId: ${hw_Id}`); +// } catch (err) { +// console.error(`❌ Error processing message: ${err.message}`); +// } +// } +// }); + + // Handling incoming MQTT messages client.on('message', async (topic, message) => { - console.log(`πŸ“© Message received on topic ${topic}:`, message.toString()); + console.log(`Message received on topic ${topic}:`, message.toString()); if (topic === 'water/iot-data') { try { const data = JSON.parse(message.toString()); - const { hw_Id, Motor_status, tanks } = data.objects; + const { hw_Id, Motor_status, tanks } = data.objects; // Updated variable names according to new format + // Get the current date and time in the required format const currentDate = new Date(); - const date = currentDate.toISOString(); - const time = currentDate.toLocaleTimeString('en-IN', { hour12: false, timeZone: 'Asia/Kolkata' }); + const date = currentDate.toISOString(); // ISO string for date + const time = currentDate.toLocaleTimeString('en-IN', { hour12: false, timeZone: 'Asia/Kolkata' }); // Time in 'HH:MM:SS' - for (const tank of tanks) { - const { Id: tankhardwareId, level: tankHeight } = tank; - const existingTank = await Tank.findOne({ hardwareId: hw_Id, tankhardwareId }); + // Create array of tank documents with current date and time + const tankDocuments = tanks.map(tank => ({ + tankhardwareId: tank.Id, // Updated to match the new format + tankHeight: tank.level, // Updated to match the new format + date, + time + })); - if (!existingTank) continue; + - const customerId = existingTank.customerId; - const tankName = existingTank.tankName; + // Save IoT data for the received tanks + const iotTankData = new IotData({ + hardwareId: hw_Id, // Updated variable name + Motor_status, + tanks: tankDocuments, + date, + time + }); + await iotTankData.save(); - console.log(`πŸ” Checking FCM tokens for Customer ID: ${customerId}`); + // Delete excess records (keep only the latest three records) + const recordsToKeep = 3; + const recordsToDelete = await IotData.find({ hardwareId: hw_Id }) // Updated variable name + .sort({ date: -1, time: -1 }) + .skip(recordsToKeep); - const users = await User.find({ customerId }).select("fcmIds"); - const fcmTokens = users.flatMap(user => user.fcmIds).filter(token => token); + for (const record of recordsToDelete) { + await record.remove(); + } - console.log(`πŸ“‘ Found ${fcmTokens.length} FCM tokens for Customer ID: ${customerId}`); + // Process each tank to update water level and connections + for (const tank of tanks) { + const { Id: tankhardwareId, level: tankHeight } = tank; // Updated to match the new format + // Find the corresponding tank in the Tank schema using hardwareId and tankhardwareId + const existingTank = await Tank.findOne({ hardwareId: hw_Id, tankhardwareId }); // Updated variable name + if (!existingTank) continue; - if (!fcmTokens.length) { - console.warn(`⚠️ No valid FCM tokens found for Customer ID: ${customerId}`); + const customerId = existingTank.customerId; + const tank_name = existingTank.tankName; + + // Calculate water level using tank height and capacity + const tankHeightInCm = (parseInt(existingTank.height.replace(/,/g, ''), 10)) * 30.48; // Convert height to cm + const tank_height = parseInt(tankHeightInCm.toFixed(0), 10); + const waterLevelHeight = tank_height - tankHeight; + const waterCapacityPerCm = parseInt(existingTank.waterCapacityPerCm.replace(/,/g, ''), 10); + + const waterLevel = parseInt(waterLevelHeight * waterCapacityPerCm, 10); // Calculated water level + + // Update water level in the existing tank + console.log(tankHeight,"this is located in tank controllers at iot-data mqtt sub ") + if (tankHeight>0 && waterLevel >= 0) { + existingTank.waterlevel = waterLevel; + await existingTank.save(); + + // Update linked tanks (input/output connections) + for (const outputConnection of existingTank.connections.outputConnections) { + const linkedTank = await Tank.findOne({ customerId, tankName: outputConnection.outputConnections, tankLocation: outputConnection.output_type }); + if (linkedTank) { + for (const inputConnection of linkedTank.connections.inputConnections) { + if (inputConnection.inputConnections === tank_name) { + inputConnection.water_level = waterLevel; // Update water level for linked tank + await linkedTank.save(); // Save updated linked tank + } + } + } + } } + } - const motorTank = await Tank.findOne({ "connections.inputConnections.motor_id": hw_Id }); + // Update motor status + const status = Motor_status; + const motorTank = await Tank.findOne({ "connections.inputConnections.motor_id": hw_Id }); // Updated variable name + + if (!motorTank) { + console.log('Motor not found for the specified motor_id'); + return; + } + + // Find the inputConnection for the motor and update motor status + const inputConnection = motorTank.connections.inputConnections.find(conn => conn.motor_id === hw_Id); // Updated variable name + const user = await User.findOne({ customerId: motorTank.customerId }); // Fetch user by customerId + const allowNotifications = user?.manualStartAndStopNotify ?? true; // Default to true if not set if (!motorTank) { - console.warn(`⚠️ Motor not found for motor_id: ${hw_Id}`); - return; - } + console.warn(`⚠️ Motor not found for motor_id: ${hw_Id}`); + return; + } + + if (!inputConnection) { + console.warn(`⚠️ No inputConnection found for motor_id: ${hw_Id}`); + return; + } - const inputConnection = motorTank.connections.inputConnections.find(conn => conn.motor_id === hw_Id); + if (inputConnection) { + inputConnection.motor_status = status; // Update motor status + const tankName = motorTank.tankName; + const customerId = motorTank.customerId; + console.log("tankName",tankName) + console.log("customerId", customerId) + // Ensure fcmTokens are fetched before emitting + // const user = await User.findOne({ customerId: motorTank.customerId }).select("fcmIds manualStartAndStopNotify"); + // const fcmTokens = user?.fcmIds || []; // Ensure fcmTokens is an array + // console.log("fcmTokens", fcmTokens) + + const users = await User.findOne({ customerId : motorTank.customerId}).select("fcmIds"); + // console.log("users", users) + // let fcmTokens = users.flatMap(user => user.fcmIds).filter(token => token); + let fcmTokens = users.fcmIds.filter(token => token); + + console.log(`πŸ“‘ Found ${fcmTokens.length} FCM tokens for Customer ID: ${customerId}`); + + if (!Array.isArray(fcmTokens) || fcmTokens.length === 0) { + console.warn(`⚠️ No valid FCM tokens found for Customer ID: ${customerId}`); + } + + // const motorTank = await Tank.findOne({ "connections.inputConnections.motor_id": hw_Id }); + + + + // const inputConnection = motorTank.connections.inputConnections.find(conn => conn.motor_id === hw_Id); + + + console.log(`πŸ“’ Processing motor logic for hw_Id: ${hw_Id}`); + const blockName = motorTank.blockName || "N/A"; + + console.log("πŸ” Debugging Condition Check:"); + console.log("πŸ› οΈ inputConnection.motor_stop_status:", inputConnection.motor_stop_status); + console.log("πŸ› οΈ Motor_status:", status); + console.log("πŸ› οΈinputConnection.motor_status:", inputConnection.motor_status); + + console.log("πŸ› οΈ inputConnection.motor_on_type:", inputConnection.motor_on_type); + + + if (status === 2 && inputConnection.motor_stop_status === "1") { + // Only update if motor_stop_status is still 1 (motor was previously OFF) + inputConnection.motor_stop_status = 2; + inputConnection.motor_on_type = "forced_manual"; + inputConnection.startTime = new Date().toISOString(); + // status = 1; + // inputConnection.motor_status = 1; + + + console.log("πŸ› οΈ IN inputConnection.motor_stop_status:", inputConnection.motor_stop_status); + console.log("πŸ› οΈ Motor_status:", status); + console.log("πŸ› οΈinputConnection.motor_status:", inputConnection.motor_status); + + console.log("πŸš€ Motor started. Updating motor_stop_status to 2."); + console.log("πŸ“’ Emitting sendMotorStartNotification event..."); + + await motorTank.markModified("connections.inputConnections"); + await motorTank.save(); + console.log("πŸ’Ύ motorTank saved successfully after start."); + + eventEmitter.emit( + "sendMotorStartNotification", + hw_Id, + customerId, + fcmTokens, + inputConnection.water_level || 0, + blockName, + tankName, + "forced_manual", + inputConnection.stop_criteria, + inputConnection.typeOfWater, + inputConnection.manual_threshold_time + ); + + + } + + // if (allowNotifications && inputConnection.motor_stop_status === "1" && status === 2 && inputConnection.motor_on_type !== "forced_manual") { + // const currentTime = moment().tz('Asia/Kolkata').format('DD-MMM-YYYY - HH:mm'); + // inputConnection.motor_stop_status = "2"; + // inputConnection.motor_on_type = "forced_manual"; + // inputConnection.startTime = currentTime; + + // console.log("πŸ“’ Emitting sendMotorStartNotification event..."); + + // eventEmitter.emit( + // "sendMotorStartNotification", + // hw_Id, + // customerId, + // fcmTokens, + // inputConnection.water_level || 0, + // blockName, + // tankName, + // "forced_manual", + // inputConnection.stop_criteria, + // inputConnection.typeOfWater, + // inputConnection.manual_threshold_time + // ); + + // await motorTank.markModified("connections.inputConnections"); + // await motorTank.save(); + // console.log("πŸ’Ύ motorTank saved successfully."); + // } - if (inputConnection) { - const blockName = motorTank.blockName || "N/A"; - const allowNotifications = (await User.findOne({ customerId }))?.manualStartAndStopNotify ?? true; + // if (allowNotifications && inputConnection.motor_stop_status === "1" && status === 2 && inputConnection.motor_on_type !== "forced_manual") { + // const currentTime = moment().tz('Asia/Kolkata').format('DD-MMM-YYYY - HH:mm'); + // inputConnection.motor_stop_status = "2"; + // inputConnection.motor_on_type = "forced_manual"; + // inputConnection.startTime = currentTime; - if (allowNotifications && inputConnection.motor_stop_status === "1" && Motor_status === 2 && inputConnection.motor_on_type !== "forced_manual") { - inputConnection.motor_stop_status = "2"; - inputConnection.motor_on_type = "forced_manual"; - inputConnection.startTime = new Date().toISOString(); + // console.log("πŸš€ Motor started in FORCED_MANUAL mode."); - console.log(`πŸš€ Motor started in FORCED_MANUAL mode.`); + // console.log("πŸ“’ Emitting sendMotorStartNotification event..."); - eventEmitter.emit( - "sendMotorStartNotification", - hw_Id, - customerId, - fcmTokens, - inputConnection.water_level || 0, - blockName, - tankName, - "forced_manual", - inputConnection.typeOfWater, - inputConnection.manual_threshold_time - ); - } + - if (allowNotifications && inputConnection.motor_stop_status === "2" && Motor_status === 1) { - inputConnection.motor_stop_status = "1"; - inputConnection.stopTime = new Date().toISOString(); + // eventEmitter.emit( + // "sendMotorStartNotification", + // hw_Id, + // customerId, + // fcmTokens, + // inputConnection.water_level || 0, + // blockName, + // tankName, + // "forced_manual", + // inputConnection.stop_criteria, + // inputConnection.typeOfWater, + // inputConnection.manual_threshold_time + // ); + // await motorTank.markModified("connections.inputConnections"); + // await motorTank.save(); // Ensure the change is saved + // console.log("πŸ’Ύ motorTank saved successfully."); + // } + + // if (allowNotifications && inputConnection.motor_stop_status === "2" && status === 1) { + // const currentTime = moment().tz('Asia/Kolkata').format('DD-MMM-YYYY - HH:mm'); + // inputConnection.motor_stop_status = "1"; + // inputConnection.stopTime = currentTime; + + // console.log("πŸ“’ Emitting sendMotorStopNotification event..."); - console.log(`πŸ›‘ Motor stopped manually.`); + + // eventEmitter.emit( + // "sendMotorStopNotification", + // hw_Id, + // customerId, + // fcmTokens, + // inputConnection.water_level || 0, + // blockName, + // tankName, + // "forced_manual", + // inputConnection.typeOfWater + // ); + // } + + // if (allowNotifications && inputConnection.motor_stop_status === "2" && status === 1) { + // const currentTime = moment().tz('Asia/Kolkata').format('DD-MMM-YYYY - HH:mm'); + // inputConnection.motor_stop_status = "1"; + // inputConnection.stopTime = currentTime; + + // console.log("πŸ“’ Emitting sendMotorStopNotification event..."); + + // eventEmitter.emit( + // "sendMotorStopNotification", + // hw_Id, + // customerId, + // fcmTokens, + // inputConnection.water_level || 0, + // blockName, + // tankName, + // "forced_manual", + // inputConnection.typeOfWater + // ); + + // await motorTank.markModified("connections.inputConnections"); + // await motorTank.save(); + // } + console.log(`πŸ” Checking stop condition: status=${status}, motor_stop_status=${inputConnection.motor_stop_status}`); + if (status === 2 && inputConnection.motor_stop_status === "2") { + console.log("πŸ›‘ Motor stopping... Updating motor_stop_status to 1."); + + inputConnection.motor_stop_status = "1"; + inputConnection.stopTime = new Date().toISOString(); + + await motorTank.markModified("connections.inputConnections"); + await motorTank.save(); // Ensure data is saved before emitting + + console.log("πŸ“’ Emitting sendMotorStopNotification event..."); eventEmitter.emit( - "sendMotorStopNotification", - hw_Id, - customerId, - fcmTokens, - inputConnection.water_level || 0, - blockName, - tankName, - "forced_manual", - inputConnection.typeOfWater + "sendMotorStopNotification", + hw_Id, + customerId, + fcmTokens, + inputConnection.water_level || 0, + blockName, + tankName, + "forced_manual", + inputConnection.typeOfWater ); - } - - await motorTank.save(); } + + await motorTank.save(); // Save the updated tank } - console.log(`βœ… Data processed successfully for hardwareId: ${hw_Id}`); + console.log('Data processed successfully for hardwareId:', hw_Id); // Updated variable name + } catch (err) { - console.error(`❌ Error processing message: ${err.message}`); + console.error('Error processing message:', err.message); } } }); + + + + exports.getPendingAndCompletedsurveyOfparticularInstaller = async (request, reply) => { try { const { installationId } = request.params; diff --git a/src/index.js b/src/index.js index f90f4a78..e6347459 100644 --- a/src/index.js +++ b/src/index.js @@ -7,7 +7,7 @@ const createConnectionController = require("./controllers/createConnectionContro const storeController = require("./controllers/storeController.js") const boom = require("boom"); const bcrypt = require('bcrypt'); -const { ProfilePictureStore,generateinstallationId,Store} = require("./models/store"); +const { ProfilePictureStore,generateinstallationId,Store, Survey} = require("./models/store"); const cors = require('fastify-cors'); @@ -1114,6 +1114,7 @@ fastify.post("/api/installLogin", { message: "Login successful", access_token: token, phone: teamMember.phone, + firstName: teamMember.firstName, teamMemberId: teamMember.teamMemberId, alternativePhone: teamMember.alternativePhone || null, email: teamMember.email || null, @@ -1133,6 +1134,75 @@ fastify.post("/api/installLogin", { } }, }); + + fastify.post("/api/surveyLogin", { + schema: { + description: "This is for Login Survey", + tags: ["Survey"], + summary: "This is for Login Survey", + body: { + type: "object", + required: [ "phone", "password"], + properties: { + phone: { type: "string", description: "Registered phone number" }, + password: { type: "string", description: "Password for authentication" }, + }, + }, + }, + async handler(req, reply) { + try { + const { phone, password } = req.body; + + // Check if user exists in the Department Schema + const user = await Deparments.findOne({ phone }); + + if (!user) { + return reply.code(400).send({ message: "User not found" }); + } + + // Verify Password + const isMatch = await bcrypt.compare(password, user.services.password.bcrypt); + + if (!isMatch) { + return reply.code(400).send({ message: "Invalid credentials" }); + } + + let survey = await Survey.findOne({ phone }); + + if (!survey) { + survey = new Survey({ + phone: user.phone, + surveyId: user.surveyId, + firstName: user.firstName, + lastName: user.lastName, + email: user.email, + alternativeNumber: user.alternativeContactNumber, + departmentName: user.departmentName, + designation: user.desginationName, + reportingManager: user.reportingManager, + city: user.city, + zone: user.zone, + address1: user.address1, + address2: user.address2, + profile: { + state: user.state, + country: user.country, + //role: type, // Store type in profile.role + }, + }); + + await survey.save(); + } + + + return reply.send(survey); + } catch (error) { + console.error("Login Error:", error); + return reply.code(500).send({ message: "Internal server error" }); + } + }, + }); + // Run the server! diff --git a/src/models/store.js b/src/models/store.js index 96dc9077..afa37d7e 100644 --- a/src/models/store.js +++ b/src/models/store.js @@ -60,7 +60,7 @@ const installationschema = new mongoose.Schema({ team_member: [ { teamMemberId: { type: String }, - name: { type: String }, + firstName: { type: String }, phone: { type: String }, installationTeamMemId: { type: String }, password: { type: String, default: null }, @@ -95,6 +95,77 @@ const installationschema = new mongoose.Schema({ }); + + const surveyschema = new mongoose.Schema({ + // name: { type: String }, + phone: { type: String, unique: true, trim: true }, + address: String, + surveyId: { type: String }, + phoneVerified: { type: Boolean, default: false }, + phoneVerificationCode: { type: Number, default: 11111 }, + passwordResetCode: { type: Number}, + oneTimePasswordSetFlag: { type: Boolean, default: false }, + emails: [{ email: String, verified: { type: Boolean, default: false } }], + services: { password: { bcrypt: String } }, + alternativeNumber: { type: String, default: null }, + firstName: { type: String, default: null }, + lastName: { type: String, default: null }, + address1: { type: String, default: null }, + address2: { type: String, default: null }, + city: { type: String, default: null }, + designation: { type: String, default: null }, + reportingManager: { type: String, default: null }, + departmentName: { type: String, default: null }, + zone: { type: String, default: null }, + type: { type: String }, + + profile: { + + state: { type: String, default: null }, + country: { type: String, default: null }, + }, + team : { type: String, default: null}, + manager : { type: String, default: null}, + team_member: { + + team_member: [ + { + survey_teamMemberId: { type: String }, + name: { type: String }, + phone: { type: String }, + installationTeamMemId: { type: String }, + password: { type: String, default: null }, + status: { type: String, default: "active" }, + email: { type: String }, + alternativePhone: { type: String }, + + } + ], + + + }, + + longitude: { type : Number,default: 0.0}, + latitude: {type: Number,default: 0.0}, + + fcmId: { type: String, default: null }, + createdAt: { + type: Date, + default: function () { + return Date.now(); + }, + }, + createdBy: ObjectId, + updatedAt: { + type: Date, + default: function () { + return Date.now(); + }, + }, + updatedBy: ObjectId, + + }); + const profilePictureInstallSchema = new Schema({ installationId: { type: String, @@ -447,10 +518,12 @@ const Iotprice = mongoose.model('Iotprice', iotpriceSchema); const SensorQuotation = mongoose.model('SensorQuotationSchema', sensorquotationSchema); const Install = mongoose.model("Install", installationschema); + const Survey = mongoose.model("Survey", surveyschema); + const HardwareCart = mongoose.model("HardwareCart", hardwareCartSchema); const ServiceCart = mongoose.model("ServiceCart", serviceCartSchema); const Sales = mongoose.model("Sales", salesSchema); - module.exports = {Order,Iotprice,Sales, Install, ProfilePictureInstall, SensorQuotation,generateinstallationId,Store,ProfilePictureStore,WaterLeverSensor,MotorSwitchSensor,Insensors,generatequatationId, HardwareCart, ServiceCart}; + module.exports = {Order,Iotprice,Sales, Install,Survey, ProfilePictureInstall, SensorQuotation,generateinstallationId,Store,ProfilePictureStore,WaterLeverSensor,MotorSwitchSensor,Insensors,generatequatationId, HardwareCart, ServiceCart};