const boom = require("boom"); const bcrypt = require('bcrypt'); const jwt = require('jsonwebtoken'); const customJwtAuth = require("../customAuthJwt"); const { Deparments } = require("../models/Department"); const { Install, SensorStock, SensorQuotation, Order, Insensors, MasterSlaveData, ElectrictyWorkPictures, PlumbingWorkPictures, MaterialRecievedPictures } = require("../models/store"); const { Counter, User } = require("../models/User"); const { IotData, Tank } = require("../models/tanks"); const moment = require('moment-timezone'); const fastify = require("fastify")({ logger: true, //disableRequestLogging: true, genReqId(req) { // you get access to the req here if you need it - must be a synchronous function return uuidv4(); }, }); const generateTeamMemberId = async () => { var result = await Counter.findOneAndUpdate( { _id: 'teamMemberId_id' }, { $inc: { seq: 1 } }, { upsert: true, new: true } ); return result.seq; }; exports.createTeamMember = async (request, reply) => { try { const { departmentId, firstName, phone, password,email,alternativePhone ,status} = request.body; // Check if installation exists const installation = await Deparments.findOne({ departmentId }); if (!installation) { return reply.status(404).send({ simplydata: { error: true, message: "Installation not found", }, }); } // Check if phone number already exists in the team const existingMember = installation.team_member.team_member.find( (member) => member.phone === phone ); if (existingMember) { return reply.status(400).send({ simplydata: { error: true, message: "Phone number already exists in the team", }, }); } // Hash password const hashedPassword = await bcrypt.hash(password, 10); const c_id = await generateTeamMemberId(); const teamMemberId = `AWTM${c_id}`; // Create new team member const newTeamMember = { teamMemberId, firstName, phone, email, alternativePhone, installationTeamMemId: departmentId, password: hashedPassword, status, }; // Add to team members array installation.team_member.team_member.push(newTeamMember); await installation.save(); return reply.send({ simplydata: { error: false, message: "Team member created successfully", teamMemberId: newTeamMember.teamMemberId, }, }); } catch (err) { console.error("Error creating team member:", err); reply.status(500).send({ simplydata: { error: true, message: "Internal server error", }, }); } }; exports.getTeamMembers = async (request, reply) => { try { const { departmentId } = request.params; // ✅ Get departmentId from request params // ✅ Find the department using departmentId const department = await Deparments.findOne({ departmentId }); if (!department) { return reply.status(404).send({ simplydata: { error: true, message: "Department not found", }, }); } // ✅ Extract team members from department schema const teamMembers = department.team_member.team_member; return reply.send({ simplydata: { error: false, message: "Team members retrieved successfully", teamMembers, // ✅ Return the list of team members }, }); } catch (err) { console.error("Error fetching team members:", err); reply.status(500).send({ simplydata: { error: true, message: "Internal server error", }, }); } }; // exports.assignTeamMemberToQuotation = async (request, reply) => { // try { // const { installationId } = request.params; // Get installationId from URL params // const { teamMemberId } = request.body; // Get teamMemberId from request body // if (!teamMemberId) { // return reply.status(400).send({ // simplydata: { // error: true, // message: "teamMemberId is required", // }, // }); // } // // Find installation by installationId // const installation = await Install.findOne({ installationId }); // if (!installation) { // return reply.status(404).send({ // simplydata: { // error: true, // message: "Installation not found", // }, // }); // } // // Extract team members list // const teamMembers = installation.team_member.team_member; // // Check if provided teamMemberId exists in the installation's team // const assignedTeamMember = teamMembers.find(member => member.teamMemberId === teamMemberId); // if (!assignedTeamMember) { // return reply.status(404).send({ // simplydata: { // error: true, // message: "Team member not found in this installation", // }, // }); // } // // Here, you would save the assigned team member to the quotation (modify as needed) // const quotation = { // installationId, // assignedTeamMember // }; // return reply.send({ // simplydata: { // error: false, // message: "Team member assigned to quotation successfully", // quotation // }, // }); // } catch (err) { // console.error("Error assigning team member to quotation:", err); // reply.status(500).send({ // simplydata: { // error: true, // message: "Internal server error", // }, // }); // } // }; exports.assignTeamMemberToQuotation = async (request, reply) => { try { const { installationId } = request.params; const { teamMemberId, quotationId } = request.body; if (!teamMemberId || !quotationId) { return reply.status(400).send({ simplydata: { error: true, message: "Both teamMemberId and quotationId are required", }, }); } // 🔹 Find installation by installationId const installation = await Install.findOne({ installationId }); if (!installation) { return reply.status(404).send({ simplydata: { error: true, message: "Installation not found", }, }); } // 🔹 Extract team members list const teamMembers = installation.team_member?.team_member || []; // 🔹 Check if the provided teamMemberId exists in the installation's team const assignedTeamMember = teamMembers.find( (member) => member.teamMemberId === teamMemberId ); if (!assignedTeamMember) { return reply.status(404).send({ simplydata: { error: true, message: "Team member not found in this installation", }, }); } // 🔹 Find or create the quotation for the given installationId let quotation = await Order.findOne({ installationId, quatationId: quotationId }); if (!quotation) { quotation = new Order({ installationId, quatationId: quotationId, assignedTeamMembers: [], // Ensure assignedTeamMembers array is initialized quatation_status: "Pending", // Default status when created }); } // 🔹 Assign the team member to the quotation if (!quotation.assignedTeamMembers) { quotation.assignedTeamMembers = []; } if (!quotation.assignedTeamMembers.includes(teamMemberId)) { quotation.assignedTeamMembers.push(teamMemberId); } // 🔹 Update order status when a team member is assigned quotation.quatation_status = "Assigned"; // Update status // 🔹 Save the updated quotation in the Order schema await quotation.save(); // 🔹 Update Installation schema with quotationId installation.quatationId = quotationId; await installation.save(); return reply.send({ simplydata: { error: false, message: "Team member assigned to quotation successfully", quotation, }, }); } catch (err) { console.error("Error assigning team member to quotation:", err); reply.status(500).send({ simplydata: { error: true, message: "Internal server error", }, }); } }; exports.getAllInstallers = async (request, reply) => { try { const { departmentName } = request.params; // Get installationId from request params // Check if installation exists const installationList = await Deparments.find({ departmentName }); if (!installationList) { return reply.status(404).send({ simplydata: { error: true, message: "Installation not found", }, }); } return reply.send({ simplydata: { error: false, installationList, // Return the list of team members }, }); } catch (err) { console.error("Error fetching team members:", err); reply.status(500).send({ simplydata: { error: true, message: "Internal server error", }, }); } }; exports.editTeamMember = async (request, reply) => { try { const { installationId, teamMemberId } = request.params; const updateData = request.body; // Find the installation const installation = await Install.findOne({ installationId }); if (!installation) { return reply.status(404).send({ simplydata: { error: true, message: "Installation not found", }, }); } // Find the team member let teamMember = installation.team_member.team_member.find( (member) => member.teamMemberId === teamMemberId ); if (!teamMember) { return reply.status(404).send({ simplydata: { error: true, message: "Team member not found", }, }); } // Update fields Object.assign(teamMember, updateData); // Save changes await installation.markModified("team_member.team_member"); await installation.save(); return reply.send({ simplydata: { error: false, message: "Team member updated successfully", }, }); } catch (err) { console.error("Error updating team member:", err); reply.status(500).send({ simplydata: { error: true, message: "Internal server error", }, }); } }; exports.deleteTeamMember = async (request, reply) => { try { const { installationId, teamMemberId } = request.params; // Find the installation const installation = await Install.findOne({ installationId }); if (!installation) { return reply.status(404).send({ simplydata: { error: true, message: "Installation not found", }, }); } // Find index of the team member const memberIndex = installation.team_member.team_member.findIndex( (member) => member.teamMemberId === teamMemberId ); if (memberIndex === -1) { return reply.status(404).send({ simplydata: { error: true, message: "Team member not found", }, }); } // Remove the team member from the array installation.team_member.team_member.splice(memberIndex, 1); // Save changes await installation.markModified("team_member.team_member"); await installation.save(); return reply.send({ simplydata: { error: false, message: "Team member deleted successfully", }, }); } catch (err) { console.error("Error deleting team member:", err); reply.status(500).send({ simplydata: { error: true, message: "Internal server error", }, }); } }; exports.getQuotationsByInstallationId = async (request, reply) => { try { const { installationId } = request.params; if (!installationId) { return reply.status(400).send({ simplydata: { error: true, message: "Installation ID is required", }, }); } // 🔹 Fetch quotations based on installationId const quotations = await Order.find({ installationId }); if (!quotations || quotations.length === 0) { return reply.status(404).send({ simplydata: { error: true, message: "No quotations found for this installation ID", }, }); } return reply.send({ simplydata: { error: false, message: "Quotations fetched successfully", quotations, }, }); } catch (err) { console.error("Error fetching quotations:", err); reply.status(500).send({ simplydata: { error: true, message: "Internal server error", }, }); } }; exports.getQuotationsByInstallationAndTeamMember = async (request, reply) => { try { const { installationId, teamMemberId } = request.params; if (!installationId || !teamMemberId) { return reply.status(400).send({ simplydata: { error: true, message: "Both installationId and teamMemberId are required", }, }); } // 🔹 Fetch quotations where installationId matches and teamMemberId is assigned const quotations = await Order.find({ installationId, assignedTeamMembers: teamMemberId, }); if (!quotations || quotations.length === 0) { return reply.status(404).send({ simplydata: { error: true, message: "No quotations found for this installation and team member", }, }); } return reply.send({ simplydata: { error: false, message: "Quotations fetched successfully", quotations, }, }); } catch (err) { console.error("Error fetching quotations:", err); reply.status(500).send({ simplydata: { error: true, message: "Internal server error", }, }); } }; exports.getDepartmentByFirstName = async (req, reply) => { try { let { firstName } = req.params; if (!firstName) { return reply.status(400).send({ simplydata: { error: true, message: "firstName is required" }, }); } // Trim and convert to lowercase firstName = firstName.trim().toLowerCase(); console.log("Searching for firstName:", firstName); // Debugging log // Search for the department with case-insensitive and space-tolerant regex const department = await Deparments.findOne({ firstName: { $regex: `^\\s*${firstName}\\s*$`, $options: "i" } }).lean(); console.log("Department found:", department); // Debugging log if (!department) { return reply.status(404).send({ simplydata: { error: true, message: "Department not found" }, }); } return reply.send({ simplydata: { error: false, message: "Department details fetched successfully", firstName: department.firstName, phone: department.phone, }, }); } catch (err) { console.error("Error fetching department details:", err); return reply.status(500).send({ simplydata: { error: true, message: "Internal server error" }, }); } }; // const moment = require('moment-timezone'); exports.getByHardwareId = async (req, reply) => { try { const { hardwareId } = req.params; if (!hardwareId) { return reply.status(400).send({ error: "hardwareId is required" }); } console.log("Fetching details for hardwareId:", hardwareId); const iotData = await IotData.find({ hardwareId }) .sort({ date: -1 }) .limit(3) .lean(); if (!iotData || iotData.length === 0) { return reply.send({ status_code: 404, message: "IoT Data not found", data: null }); } const latestRecord = iotData[0]; const indiaTime = moment.tz(latestRecord.date, "Asia/Kolkata"); const connected_gsm_date = indiaTime.format("YYYY-MM-DD"); const connected_gsm_time = indiaTime.format("HH:mm:ss"); const now = moment.tz("Asia/Kolkata"); const diffInMinutes = now.diff(indiaTime, "minutes"); const isGSMConnected = diffInMinutes <= 1; const gsmStatus = isGSMConnected ? "connected" : "disconnected"; // ✅ Step 1: Update Insensors with GSM date/time/status await Insensors.findOneAndUpdate( { connected_to: hardwareId }, { $set: { connected_gsm_date, connected_gsm_time, connected_status: gsmStatus } }, { new: true } ); // ✅ Step 2: Annotate tanks with LoRa connection status based on tank date const tanksWithConnectionStatus = latestRecord.tanks.map(tank => { const tankMoment = moment.tz(tank.date, "Asia/Kolkata"); const tankDiff = now.diff(tankMoment, "minutes"); return { ...tank, connected_status: tankDiff <= 1 ? "connected" : "disconnected" }; }); // ✅ Step 3: Response return reply.send({ status_code: 200, message: "Success", data: { hardwareId, gsm_connected_status: gsmStatus, gsmStatus: isGSMConnected ? "GSM Connected" : "GSM Not Connected", connected_gsm_date, connected_gsm_time, tanks: tanksWithConnectionStatus, date: latestRecord.date, time: latestRecord.time, } }); } catch (err) { console.error("Error in getByHardwareId:", err); return reply.status(500).send({ error: "Internal Server Error" }); } }; exports.getByHardwareAndTankId = async (req, reply) => { try { const { hardwareId, tankhardwareId } = req.params; if (!hardwareId || !tankhardwareId) { return reply.status(400).send({ error: "Both hardwareId and tankhardwareId are required" }); } console.log("Fetching tank data for:", { hardwareId, tankhardwareId }); const latestData = await IotData.findOne({ hardwareId }) .sort({ date: -1 }) .lean(); if (!latestData || !Array.isArray(latestData.tanks)) { return reply.code(404).send({ message: "No data found for given hardwareId and tankhardwareId" }); } // Step 1: Check GSM Connection - if data is fresh within 1 minute const now = new Date(); const dataDate = new Date(latestData.date); const diffInMs = now - dataDate; const isGSMConnected = diffInMs <= 60000; // 60 seconds // Step 2: Find the matching tank const matchedTank = latestData.tanks.find(tank => tank.tankhardwareId === tankhardwareId); if (!matchedTank) { return reply.code(404).send({ message: "Tank not found in latest record" }); } // Step 3: Determine LoRa connection status const tankHeight = parseFloat(matchedTank.tankHeight || "0"); const isLoraConnected = isGSMConnected && tankHeight > 0; const matchedTankDateObj = new Date(matchedTank.date); const day = String(matchedTankDateObj.getDate()).padStart(2, '0'); const month = String(matchedTankDateObj.getMonth() + 1).padStart(2, '0'); const year = matchedTankDateObj.getFullYear(); const formattedDate = `${day}-${month}-${year}`; // Add formatted date to matchedTank matchedTank.date = formattedDate; const updateFields = { connected_status: isLoraConnected ? "connected" : "disconnected", }; let connected_lora_date = null; let connected_lora_time = null; if (isLoraConnected) { connected_lora_date = formattedDate; connected_lora_time = matchedTank.time || matchedTankDateObj.toTimeString().split(" ")[0]; updateFields.connected_lora_date = connected_lora_date; updateFields.connected_lora_time = connected_lora_time; } // Step 4: Update Insensors collection await Insensors.findOneAndUpdate( { connected_to: hardwareId, hardwareId: tankhardwareId }, { $set: updateFields }, { new: true } ); // Step 5: Prepare response const displayMessage = isLoraConnected ? "LoRa connected" : "LoRa not connected"; return reply.send({ status_code: 200, message: displayMessage, data: matchedTank, lora_connected_status: updateFields.connected_status, connected_lora_date, connected_lora_time }); } catch (err) { console.error("Error in getByHardwareAndTankId:", err); return reply.status(500).send({ error: "Internal Server Error" }); } }; exports.getAllocatedSensorsByTank = async (req, reply) => { try { let { customerId, tankName } = req.params; if (!customerId || !tankName) { return reply.status(400).send({ error: "customerId and tankName are required" }); } tankName = tankName.trim(); // Trim spaces console.log("Querying MongoDB with:", { customerId, tankName, status: "blocked" }); const allocatedSensors = await Insensors.find({ customerId, tankName: { $regex: `^${tankName}$`, $options: "i" }, // Case-insensitive search status: "blocked", }).lean(); if (!allocatedSensors.length) { return reply.send({ status_code: 200, message: "No allocated sensors found for this tank", allocatedSensors: [], }); } return reply.send({ status_code: 200, message: "Allocated sensors fetched successfully", allocatedSensors, }); } catch (err) { console.error("Error fetching allocated sensors:", err); return reply.status(500).send({ error: "Internal server error" }); } }; exports.createMasterSlaveData = async (req, reply) => { try { const { installationId } = req.params; const { type, customerId, hardwareId, batchno, masterId, tankName, tankLocation, materialRecived, electricityWork, plumbingWork, loraCheck } = req.body; if (!installationId || !hardwareId || !masterId) { return reply.status(400).send({ message: "installationId, hardwareId, and masterId are required." }); } // 🔹 Fetch stored electricity work pictures const electricityWorkData = await ElectrictyWorkPictures.findOne({ installationId, customerId }); const electricityWorkPictures = electricityWorkData ? electricityWorkData.pictureUrl.map(pic => ({ url: pic.url, uploadedAt: new Date() })) : []; // 🔹 Fetch stored plumbing work pictures const plumbingWorkData = await PlumbingWorkPictures.findOne({ installationId, customerId }); const plumbingWorkPictures = plumbingWorkData ? plumbingWorkData.pictureUrl.map(pic => ({ url: pic.url, uploadedAt: new Date() })) : []; const materialRecievedData = await MaterialRecievedPictures.findOne({ installationId, customerId }); const materialRecievedPictures = materialRecievedData ? materialRecievedData.pictureUrl.map(pic => ({ url: pic.url, uploadedAt: new Date() })) : []; // 🔹 Save all data to MasterSlaveData const newData = new MasterSlaveData({ installationId, type, customerId, hardwareId, batchno, masterId, tankName, tankLocation, materialRecived, electricityWork, plumbingWork, loraCheck, electricityWorkPictures, plumbingWorkPictures, materialRecievedPictures }); await newData.save(); reply.status(201).send({ message: "Master-Slave data created successfully", data: newData }); } catch (err) { reply.status(500).send({ message: err.message }); } }; exports.masterConnectedSlaveList = async (req, reply) => { try { const { connectedTo } = req.params; // Step 1: Find all slave tanks where connected_to matches const slaveTanks = await Insensors.find({ connected_to: connectedTo }); console.log(slaveTanks,"slaveTanks") if (!slaveTanks || slaveTanks.length === 0) { return reply.status(404).send({ success: false, message: "No tanks found for the given connected_to value" }); } // Step 2: Fetch `tankLocation` and `typeOfWater` from the Tank schema const tankDetails = await Tank.findOne({ hardwareId: connectedTo }, { tankLocation: 1, typeOfWater: 1 }); if (!tankDetails) { return reply.status(404).send({ success: false, message: "Tank details not found for the given connectedTo" }); } // Step 3: Count the number of connected slaves const slaveCount = slaveTanks.length; return reply.send({ success: true, tankLocation: tankDetails.tankLocation, typeOfWater: tankDetails.typeOfWater, connectedSlaveCount: slaveCount, data: slaveTanks, }); } catch (error) { console.error("Error fetching master connected slave data:", error); return reply.status(500).send({ success: false, message: "Internal Server Error" }); } }; exports.mastrerList = async (req, reply) => { try { const { customerId, installationId } = req.params; // Step 1: Get User and extract buildingName const user = await User.findOne({ customerId , installationId }); if (!user) { return reply.status(404).send({ success: false, message: "User not found" }); } const { buildingName } = user; // Step 2: Get Tanks with matching customerId const tanks = await Tank.find({ customerId }); if (!tanks.length) { return reply.status(404).send({ success: false, message: "No tanks found for this customer" }); } const {tankLocation,typeOfWater} = tanks //console.log(tanks) // Step 3: Extract hardwareId from tanks const hardwareIds = tanks.map(tank => tank.hardwareId); // Step 4: Find master tanks in InSensors with both customerId and installationId const masterTanks = await Insensors.find({ customerId, connected_to: { $in: hardwareIds }, type: "master" }); if (!masterTanks.length) { return reply.status(404).send({ success: false, message: "No master tanks found" }); } return reply.send({ success: true,tankLocation,typeOfWater ,buildingName, data: masterTanks, user }); } catch (error) { console.error("Error fetching master tanks:", error); return reply.status(500).send({ success: false, message: "Internal Server Error" }); } }; //const Insensor = mongoose.model('insensors', insensorsSchema); // if not already defined // exports.getMasterSlaveSummary = async (req, reply) => { // const { customerId } = req.params; // try { // const masters = await Insensors.aggregate([ // { // $match: { // customerId, // type: "master" // } // }, // { // $lookup: { // from: 'Insensors', // collection name should match MongoDB collection // let: { masterId: '$hardwareId' }, // pipeline: [ // { // $match: { // $expr: { // $and: [ // { $eq: ['$type', 'slave'] }, // { $eq: ['$customerId', customerId] }, // { $eq: ['$connected_to', '$$masterId'] } // ] // } // } // }, // { // $project: { // _id: 1, // hardwareId: 1, // tankName: 1, // tankLocation: 1, // model: 1, // status: 1, // indate: 1 // } // } // ], // as: 'connected_slaves' // } // }, // { // $addFields: { // connected_slave_count: { $size: '$connected_slaves' } // } // }, // { // $project: { // _id: 1, // hardwareId: 1, // tankName: 1, // tankLocation: 1, // model: 1, // status: 1, // indate: 1, // connected_slave_count: 1, // connected_slaves: 1 // } // } // ]); // const totalMasters = await Insensors.countDocuments({ customerId, type: 'master' }); // const totalSlaves = await Insensors.countDocuments({ customerId, type: 'slave' }); // reply.code(200).send({ // totalMasters, // totalSlaves, // masters // }); // } catch (err) { // console.error(err); // reply.code(500).send({ error: 'Server error', details: err.message }); // } // } //const moment = require('moment'); // For time calculations exports.getMasterSlaveSummary = async (req, reply) => { try { const { customerId } = req.params; if (!customerId) { return reply.status(400).send({ message: "Missing customerId" }); } const allDevices = await Insensors.find({ customerId }); const masters = allDevices.filter(device => device.type === 'master'); const slaves = allDevices.filter(device => device.type === 'slave'); const enrichDeviceWithTimestamp = async (device) => { const hardwareId = device.hardwareId?.trim(); if (!hardwareId) return device.toObject(); // Fetch latest IoT data const latestData = await IotData.findOne({ hardwareId }) .sort({ date: -1 }) .lean(); const enriched = device.toObject(); // Add GSM Connection info (date/time/status) for master if (latestData?.date) { const indiaTime = moment.tz(latestData.date, "Asia/Kolkata"); const date = indiaTime.format("DD-MM-YYYY"); const time = indiaTime.format("HH:mm:ss"); const now = moment.tz("Asia/Kolkata"); const diffInMinutes = now.diff(indiaTime, "minutes"); const isGSMConnected = diffInMinutes <= 1; const gsmStatus = isGSMConnected ? "connected" : "disconnected"; if (device.type === 'master') { enriched.connected_gsm_date = date; enriched.connected_gsm_time = time; enriched.gsm_connected_status = gsmStatus; } } // Add LoRa Connection info for slaves (per-tank) if (latestData?.tanks && Array.isArray(latestData.tanks)) { const enrichedTanks = latestData.tanks.map(tank => { const tankMoment = moment.tz(tank.date, "Asia/Kolkata"); const tankDiff = moment.tz("Asia/Kolkata").diff(tankMoment, "minutes"); const loraStatus = tankDiff <= 1 ? "connected" : "disconnected"; return { ...tank, connected_status: loraStatus }; }); enriched.tanks = enrichedTanks; // Update LoRa status for the device itself (if slave) if (device.type === 'slave') { const connectedTank = enrichedTanks.find(tank => tank.tankhardwareId === device.hardwareId); enriched.lora_connected_status = connectedTank ? connectedTank.connected_status : "disconnected"; enriched.connected_lora_date = connectedTank ? connectedTank.date : null; enriched.connected_lora_time = connectedTank ? connectedTank.time : null; } } // Remove GSM fields for slave devices and LoRa fields for master devices if (device.type === 'slave') { delete enriched.connected_gsm_date; delete enriched.connected_gsm_time; delete enriched.gsm_connected_status; } if (device.type === 'master') { delete enriched.connected_lora_date; delete enriched.connected_lora_time; delete enriched.lora_connected_status; } return enriched; }; // Enrich masters and slaves with timestamps and connection statuses const enrichedMasters = await Promise.all(masters.map(enrichDeviceWithTimestamp)); const enrichedSlaves = await Promise.all(slaves.map(enrichDeviceWithTimestamp)); // Match slaves to their masters const masterSummary = enrichedMasters.map(master => { const connectedSlaves = enrichedSlaves.filter(slave => slave.connected_to === master.connected_to); return { ...master, connected_slave_count: connectedSlaves.length, connected_slaves: connectedSlaves }; }); return reply.send({ status_code: 200, message: "Success", master_count: enrichedMasters.length, slave_count: enrichedSlaves.length, data: masterSummary }); } catch (err) { console.error("Error in getMasterSlaveSummary:", err); return reply.status(500).send({ status_code: 500, message: "Internal Server Error" }); } }; // exports.getIotDataByCustomer = async (req, reply) => { // try { // const { customerId } = req.params; // if (!customerId) { // return reply.code(400).send({ error: "customerId is required" }); // } // // 1. Fetch all tanks by customerId // const tanks = await Tank.find({ customerId }); // console.log("Tanks found:", tanks.length); // if (!tanks || tanks.length === 0) { // return reply.code(404).send({ message: "No tanks found for this customer." }); // } // // 2. Collect all motor_ids from input & output connections // const motorIdSet = new Set(); // for (const tank of tanks) { // const { inputConnections = [], outputConnections = [] } = tank.connections || {}; // inputConnections.forEach(conn => { // if (conn.motor_id) motorIdSet.add(conn.motor_id.trim()); // }); // outputConnections.forEach(conn => { // if (conn.motor_id) motorIdSet.add(conn.motor_id.trim()); // }); // } // const motorIds = Array.from(motorIdSet); // console.log("Unique motorIds collected:", motorIds); // if (motorIds.length === 0) { // return reply.send({ status_code: 200, message: "No motors connected", data: [] }); // } // // 3. Fetch the latest IotData for each unique hardwareId // const latestIotDataPromises = motorIds.map(hardwareId => // IotData.findOne({ hardwareId }).sort({ date: -1 }).lean() // ); // const iotDataResults = await Promise.all(latestIotDataPromises); // const filteredData = iotDataResults.filter(doc => doc); // remove nulls // console.log("Latest IotData entries found:", filteredData.length); // return reply.send({ // status_code: 200, // message: "Success", // data: filteredData // }); // } catch (err) { // console.error("Error fetching IoT data by customerId:", err); // return reply.code(500).send({ error: "Internal Server Error" }); // } // }; // exports.getIotDataByCustomer = async (req, reply) => { // try { // const { customerId } = req.params; // if (!customerId) { // return reply.code(400).send({ error: "customerId is required" }); // } // // Fetch all sensors of the customer // const sensors = await Insensors.find({ customerId }); // if (!sensors || sensors.length === 0) { // return reply.code(404).send({ message: "No sensors found for this customer." }); // } // // Extract unique hardwareIds // const hardwareIds = [ // ...new Set( // sensors.map(sensor => sensor.connected_to?.trim()).filter(Boolean) // ) // ]; // if (hardwareIds.length === 0) { // return reply.send({ status_code: 200, message: "No connected hardwareIds found", data: [] }); // } // // Fetch latest IoT data and enrich it // const latestDataPromises = hardwareIds.map(async hardwareId => { // const latestRecord = await IotData.findOne({ hardwareId }).sort({ date: -1 }).lean(); // const relatedSensors = sensors.filter(sensor => sensor.connected_to?.trim() === hardwareId); // let message = "GSM is not connected"; // if (latestRecord?.tanks?.some(tank => parseFloat(tank.tankHeight || 0) > 0)) { // message = "GSM is connected"; // } // // Prepare individual tank connection status // const tanksStatus = relatedSensors.map(sensor => { // const tankhardwareId = sensor.hardwareId?.trim(); // const matchedTank = latestRecord?.tanks?.find( // tank => tank.tankhardwareId === tankhardwareId // ); // const loraMessage = // matchedTank && parseFloat(matchedTank.tankHeight || 0) > 0 // ? "LORA is connected" // : "LORA is not connected"; // return { // tankhardwareId, // tankName: sensor.tankName ?? null, // tankLocation: sensor.tankLocation ?? null, // masterName: sensor.masterName ?? null, // location: sensor.location ?? null, // loraMessage, // latestTankData: matchedTank || null // }; // }); // return { // hardwareId, // message, // masterName: relatedSensors[0]?.masterName ?? null, // location: relatedSensors[0]?.location ?? null, // tanks: tanksStatus.slice(1) // 👈 Skip the first tank // }; // }); // const iotDataGrouped = await Promise.all(latestDataPromises); // return reply.send({ // status_code: 200, // message: "Success", // data: iotDataGrouped // }); // } catch (err) { // console.error("Error fetching IoT data by customerId:", err); // return reply.code(500).send({ error: "Internal Server Error" }); // } // }; exports.getIotDataByCustomer = async (req, reply) => { try { const { customerId } = req.params; if (!customerId) { return reply.code(400).send({ error: "customerId is required" }); } // Fetch all sensors for the customer const sensors = await Insensors.find({ customerId }); if (!sensors || sensors.length === 0) { return reply.code(404).send({ message: "No sensors found for this customer." }); } // Extract unique hardwareIds that are connected to sensors const hardwareIds = [ ...new Set( sensors.map(sensor => sensor.connected_to?.trim()).filter(Boolean) ) ]; if (hardwareIds.length === 0) { return reply.send({ status_code: 200, message: "No connected hardwareIds found", data: [] }); } // Fetch the latest IoT data and enrich it with sensor details const latestDataPromises = hardwareIds.map(async hardwareId => { // Fetch the latest IoT data for this hardwareId const latestRecord = await IotData.findOne({ hardwareId }) .sort({ date: -1 }) // Sorting to get the latest record based on the date .lean(); // If no IoT data is found for this hardwareId, return a placeholder if (!latestRecord) { return { hardwareId, message: "No IoT data found", tanks: [] }; } // Find sensors related to this hardwareId const relatedSensors = sensors.filter(sensor => sensor.connected_to?.trim() === hardwareId); // Check if this hardwareId is "disconnected" in Insensors (based on your logic, e.g., isConnected=false) const isDisconnected = relatedSensors.some(sensor => sensor.isConnected === false); // You can adjust this check based on your schema. // Default message for GSM connection status let message = isDisconnected ? "GSM is disconnected" : "GSM is not connected"; // If any tank data has height > 0, assume GSM is connected, overriding the disconnected state if (latestRecord?.tanks?.some(tank => parseFloat(tank.tankHeight || 0) > 0)) { message = "GSM is connected"; } // Enrich each tank with the IoT data const tanksStatus = relatedSensors.map(sensor => { const tankhardwareId = sensor.hardwareId?.trim(); const matchedTank = latestRecord?.tanks?.find( tank => tank.tankhardwareId === tankhardwareId ); // Check if LoRa is connected based on the tankHeight const loraMessage = matchedTank && parseFloat(matchedTank.tankHeight || 0) > 0 ? "LORA is connected" : "LORA is not connected"; return { tankhardwareId, tankName: sensor.tankName ?? null, tankLocation: sensor.tankLocation ?? null, masterName: sensor.masterName ?? null, location: sensor.location ?? null, loraMessage, latestTankData: matchedTank || null }; }); return { hardwareId, message, masterName: relatedSensors[0]?.masterName ?? null, location: relatedSensors[0]?.location ?? null, tanks: tanksStatus }; }); // Wait for all promises to resolve to gather all the enriched data const iotDataGrouped = await Promise.all(latestDataPromises); // Return the final response return reply.send({ status_code: 200, message: "Success", data: iotDataGrouped }); } catch (err) { console.error("Error fetching IoT data by customerId:", err); return reply.code(500).send({ error: "Internal Server Error" }); } }; //important // exports.getIotDataByCustomer = async (req, reply) => { // try { // const { customerId } = req.params; // if (!customerId) { // return reply.status(400).send({ error: "customerId is required" }); // } // console.log("Fetching data for customerId:", customerId); // // Step 1: Fetch all sensors for the customer // const sensors = await Insensors.find({ customerId }); // if (!sensors || sensors.length === 0) { // return reply.send({ status_code: 404, message: "No sensors found for this customer", data: [] }); // } // // Step 2: Fetch latest IoT data for each sensor's hardwareId // const enrichedSensors = await Promise.all(sensors.map(async (sensor) => { // const { connected_to: hardwareId, masterName, location } = sensor; // // Fetch the latest IoT data for this hardwareId // const iotData = await IotData.findOne({ hardwareId }) // .sort({ date: -1 }) // Sort to get the latest data // .lean(); // if (!iotData) { // return { // hardwareId, // message: "No IoT data found", // masterName, // location, // tanks: [] // }; // } // // Step 3: Get GSM status based on the latest IoT data // const latestRecord = iotData; // const indiaTime = moment.tz(latestRecord.date, "Asia/Kolkata"); // const connected_gsm_date = indiaTime.format("YYYY-MM-DD"); // const connected_gsm_time = indiaTime.format("HH:mm:ss"); // const now = moment.tz("Asia/Kolkata"); // const diffInMinutes = now.diff(indiaTime, "minutes"); // const isGSMConnected = diffInMinutes <= 1; // const gsmStatus = isGSMConnected ? "GSM Connected" : "GSM Not Connected"; // // Step 4: Annotate each tank with LoRa connection status // const tanksWithConnectionStatus = latestRecord.tanks.map(tank => { // const tankMoment = moment.tz(tank.date, "Asia/Kolkata"); // const tankDiff = now.diff(tankMoment, "minutes"); // const loraStatus = tankDiff <= 1 ? "LORA is connected" : "LORA is not connected"; // return { // tankhardwareId: tank.tankhardwareId, // tankName: sensor.tankName ?? null, // tankLocation: sensor.tankLocation ?? null, // masterName: sensor.masterName ?? null, // location: sensor.location ?? null, // loraMessage: loraStatus, // latestTankData: tank // }; // }); // // Step 5: Return enriched sensor data in the desired format // return { // hardwareId, // message: gsmStatus, // masterName, // location, // tanks: tanksWithConnectionStatus // }; // })); // // Step 6: Return the enriched data for the customer // return reply.send({ // status_code: 200, // message: "Success", // data: enrichedSensors // }); // } catch (err) { // console.error("Error in getAllDataForCustomer:", err); // return reply.status(500).send({ error: "Internal Server Error" }); // } // }; // exports.getIotDataByCustomer = async (req, reply) => { // try { // const { customerId } = req.params; // if (!customerId) { // return reply.status(400).send({ error: "customerId is required" }); // } // console.log("Fetching data for customerId:", customerId); // // Step 1: Fetch all sensors for the customer // const sensors = await Insensors.find({ customerId }); // if (!sensors || sensors.length === 0) { // return reply.send({ status_code: 404, message: "No sensors found for this customer", data: [] }); // } // // Step 2: Fetch latest IoT data for each sensor's hardwareId // const enrichedSensors = await Promise.all(sensors.map(async (sensor) => { // const { connected_to: hardwareId, masterName, location } = sensor; // // Fetch the latest IoT data for this hardwareId // const iotData = await IotData.findOne({ hardwareId }) // .sort({ date: -1 }) // Sort to get the latest data // .lean(); // if (!iotData) { // return { // hardwareId, // message: "No IoT data found", // masterName, // location, // tanks: [] // }; // } // // Step 3: Get GSM status based on the latest IoT data // const latestRecord = iotData; // const indiaTime = moment.tz(latestRecord.date, "Asia/Kolkata"); // const connected_gsm_date = indiaTime.format("YYYY-MM-DD"); // const connected_gsm_time = indiaTime.format("HH:mm:ss"); // const now = moment.tz("Asia/Kolkata"); // const diffInMinutes = now.diff(indiaTime, "minutes"); // const isGSMConnected = diffInMinutes <= 1; // const gsmStatus = isGSMConnected ? "GSM Connected" : "GSM Not Connected"; // // Step 4: Annotate each tank with LoRa connection status // const tanksWithConnectionStatus = latestRecord.tanks.slice(0, 2).map(tank => { // Limit to 2 tanks // const tankMoment = moment.tz(tank.date, "Asia/Kolkata"); // const tankDiff = now.diff(tankMoment, "minutes"); // const loraStatus = tankDiff <= 1 ? "LORA is connected" : "LORA is not connected"; // return { // tankhardwareId: tank.tankhardwareId, // tankName: sensor.tankName ?? null, // tankLocation: sensor.tankLocation ?? null, // masterName: sensor.masterName ?? null, // location: sensor.location ?? null, // loraMessage: loraStatus, // latestTankData: tank // }; // }); // // Step 5: Return enriched sensor data in the desired format // return { // hardwareId, // message: gsmStatus, // masterName, // location, // tanks: tanksWithConnectionStatus // }; // })); // // Step 6: Return the enriched data for the customer // return reply.send({ // status_code: 200, // message: "Success", // data: enrichedSensors // }); // } catch (err) { // console.error("Error in getAllDataForCustomer:", err); // return reply.status(500).send({ error: "Internal Server Error" }); // } // };