You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3611 lines
116 KiB
3611 lines
116 KiB
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, Support } = 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("DD-MM-YYYY");
|
|
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";
|
|
|
|
const gsmLastCheckTime = now.format("DD-MM-YYYY HH:mm:ss"); // formatted current time
|
|
|
|
// ✅ Step 1: Update Insensors with GSM date/time/status and last check time
|
|
await Insensors.findOneAndUpdate(
|
|
{ connected_to: hardwareId },
|
|
{
|
|
$set: {
|
|
connected_gsm_date,
|
|
connected_gsm_time,
|
|
connected_status: gsmStatus,
|
|
gsm_last_check_time: gsmLastCheckTime
|
|
}
|
|
},
|
|
{ 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,
|
|
gsm_last_check_time: gsmLastCheckTime,
|
|
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.getByHardwareIdSupport = 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("DD-MM-YYYY");
|
|
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";
|
|
|
|
const gsmLastCheckTime = now.format("DD-MM-YYYY HH:mm:ss"); // formatted current time
|
|
|
|
// ✅ Step 1: Update Insensors with GSM date/time/status and last check time
|
|
await Insensors.findOneAndUpdate(
|
|
{ connected_to: hardwareId },
|
|
{
|
|
$set: {
|
|
connected_gsm_date,
|
|
connected_gsm_time,
|
|
connected_status: gsmStatus,
|
|
support_gsm_last_check_time: gsmLastCheckTime
|
|
}
|
|
},
|
|
{ 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,
|
|
support_gsm_last_check_time: gsmLastCheckTime,
|
|
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 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" });
|
|
}
|
|
|
|
const now = new Date();
|
|
const dataDate = new Date(latestData.date);
|
|
const isGSMConnected = now - dataDate <= 60000;
|
|
|
|
const matchedTank = latestData.tanks.find(tank => tank.tankhardwareId === tankhardwareId);
|
|
|
|
if (!matchedTank) {
|
|
return reply.code(404).send({ message: "Tank not found in latest record" });
|
|
}
|
|
|
|
const tankHeight = parseFloat(matchedTank.tankHeight || "0");
|
|
const isLoraConnected = isGSMConnected && tankHeight > 0;
|
|
|
|
// Format date and time from matched tank
|
|
const matchedTankDateObj = new Date(matchedTank.date);
|
|
const formattedDate = moment(matchedTankDateObj).tz("Asia/Kolkata").format("DD-MM-YYYY");
|
|
const formattedTime = matchedTank.time || moment(matchedTankDateObj).tz("Asia/Kolkata").format("HH:mm:ss");
|
|
|
|
matchedTank.date = formattedDate;
|
|
matchedTank.time = formattedTime;
|
|
|
|
const updateFields = {
|
|
connected_status: isLoraConnected ? "connected" : "disconnected",
|
|
connected_lora_date: formattedDate,
|
|
connected_lora_time: formattedTime,
|
|
lora_last_check_time: moment().tz("Asia/Kolkata").format("DD-MM-YYYY HH:mm:ss")
|
|
};
|
|
|
|
// Check if Insensors document exists before updating
|
|
const sensorDoc = await Insensors.findOne({ connected_to: hardwareId, hardwareId: tankhardwareId });
|
|
|
|
if (!sensorDoc) {
|
|
console.warn("⚠️ No Insensors doc found with:", { connected_to: hardwareId, hardwareId: tankhardwareId });
|
|
} else {
|
|
const updated = await Insensors.findByIdAndUpdate(sensorDoc._id, { $set: updateFields }, { new: true });
|
|
console.log("✅ Insensors updated:", updated._id);
|
|
}
|
|
|
|
return reply.send({
|
|
status_code: 200,
|
|
message: isLoraConnected ? "LoRa connected" : "LoRa not connected",
|
|
data: matchedTank,
|
|
lora_connected_status: updateFields.connected_status,
|
|
connected_lora_date: updateFields.connected_lora_date,
|
|
connected_lora_time: updateFields.connected_lora_time,
|
|
lora_last_check_time: updateFields.lora_last_check_time
|
|
});
|
|
|
|
} catch (err) {
|
|
console.error("❌ Error in getByHardwareAndTankId:", err);
|
|
return reply.status(500).send({ error: "Internal Server Error" });
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
exports.getByHardwareAndTankIdSupport = 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" });
|
|
}
|
|
|
|
const now = new Date();
|
|
const dataDate = new Date(latestData.date);
|
|
const diffInMs = now - dataDate;
|
|
const isGSMConnected = diffInMs <= 60000;
|
|
|
|
const matchedTank = latestData.tanks.find(tank => tank.tankhardwareId === tankhardwareId);
|
|
|
|
if (!matchedTank) {
|
|
return reply.code(404).send({ message: "Tank not found in latest record" });
|
|
}
|
|
|
|
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}`;
|
|
|
|
matchedTank.date = formattedDate;
|
|
|
|
const updateFields = {
|
|
connected_status: isLoraConnected ? "connected" : "disconnected"
|
|
};
|
|
|
|
let connected_lora_date = null;
|
|
let connected_lora_time = null;
|
|
let support_lora_last_check_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;
|
|
}
|
|
|
|
// ✅ Format LoRa last check time in "YYYY-MM-DD HH:mm:ss"
|
|
support_lora_last_check_time = moment.tz("Asia/Kolkata").format("DD-MM-YYYY HH:mm:ss");
|
|
updateFields.support_lora_last_check_time = support_lora_last_check_time;
|
|
|
|
await Insensors.findOneAndUpdate(
|
|
{ connected_to: hardwareId, hardwareId: tankhardwareId },
|
|
{ $set: updateFields },
|
|
{ new: true }
|
|
);
|
|
|
|
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,
|
|
support_lora_last_check_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 }).lean();
|
|
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 (for master device display)
|
|
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;
|
|
|
|
// Step 4: Fetch latest IotData for the master to extract tankHeight for each slave
|
|
const latestIotData = await IotData.findOne({ hardwareId: connectedTo }).sort({ date: -1 }).lean();
|
|
|
|
// Step 5: Prepare processed response for each tank
|
|
const processedSlaves = slaveTanks.map(slave => {
|
|
if (slave.type === 'slave') {
|
|
// Find matching tank data for this slave in IotData
|
|
const matchingTankData = latestIotData?.tanks?.find(tank => tank.tankhardwareId === slave.hardwareId);
|
|
|
|
return {
|
|
...slave,
|
|
tankHeight: matchingTankData?.tankHeight ?? null // Add tankHeight if found
|
|
};
|
|
} else {
|
|
// Remove unnecessary fields for master type
|
|
const {
|
|
tankName,
|
|
tankLocation,
|
|
typeOfWater,
|
|
...rest
|
|
} = slave;
|
|
return rest;
|
|
}
|
|
});
|
|
|
|
return reply.send({
|
|
success: true,
|
|
tankLocation: tankDetails.tankLocation,
|
|
typeOfWater: tankDetails.typeOfWater,
|
|
connectedSlaveCount: slaveCount,
|
|
data: processedSlaves
|
|
});
|
|
|
|
} 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 });
|
|
// }
|
|
// }
|
|
|
|
|
|
|
|
|
|
// 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();
|
|
|
|
// const latestData = await IotData.findOne({ hardwareId }).sort({ date: -1 }).lean();
|
|
// const enriched = device.toObject();
|
|
|
|
// 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;
|
|
// enriched.gsm_last_check_time = indiaTime.format("YYYY-MM-DD HH:mm:ss");
|
|
// }
|
|
// }
|
|
|
|
// 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;
|
|
|
|
// 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 ? moment(connectedTank.date).tz("Asia/Kolkata").format("DD-MM-YYYY") : null;
|
|
// enriched.connected_lora_time = connectedTank ? connectedTank.time : null;
|
|
// enriched.lora_last_check_time = connectedTank ? moment(connectedTank.date).tz("Asia/Kolkata").format("YYYY-MM-DD HH:mm:ss") : null;
|
|
// }
|
|
// }
|
|
|
|
// if (device.type === 'slave') {
|
|
// delete enriched.connected_gsm_date;
|
|
// delete enriched.connected_gsm_time;
|
|
// delete enriched.gsm_connected_status;
|
|
// delete enriched.gsm_last_check_time;
|
|
// }
|
|
|
|
// if (device.type === 'master') {
|
|
// delete enriched.connected_lora_date;
|
|
// delete enriched.connected_lora_time;
|
|
// delete enriched.lora_connected_status;
|
|
// delete enriched.lora_last_check_time;
|
|
// delete enriched.tanks; // ✅ Remove tanks for master
|
|
// }
|
|
|
|
// return enriched;
|
|
// };
|
|
|
|
// const enrichedMasters = await Promise.all(masters.map(enrichDeviceWithTimestamp));
|
|
// const enrichedSlaves = await Promise.all(slaves.map(enrichDeviceWithTimestamp));
|
|
|
|
// 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.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(d => d.type === 'master');
|
|
// const slaves = allDevices.filter(d => d.type === 'slave');
|
|
|
|
// const enrichDevice = async (device) => {
|
|
// const enriched = device.toObject();
|
|
// const hardwareId = device.hardwareId?.trim();
|
|
|
|
// if (!hardwareId) return enriched;
|
|
|
|
// // Fetch latest IotData blindly for this device
|
|
// const latestData = await IotData.findOne({ hardwareId }).sort({ date: -1 }).lean();
|
|
|
|
// if (latestData?.date) {
|
|
// const indiaTime = moment.tz(latestData.date, "Asia/Kolkata");
|
|
// const now = moment.tz("Asia/Kolkata");
|
|
// const diffMins = now.diff(indiaTime, "minutes");
|
|
// const isConnected = diffMins <= 1;
|
|
|
|
// enriched.connected_gsm_date = indiaTime.format("DD-MM-YYYY");
|
|
// enriched.connected_gsm_time = indiaTime.format("HH:mm:ss");
|
|
// enriched.gsm_connected_status = isConnected ? "connected" : "disconnected";
|
|
// enriched.gsm_last_check_time = indiaTime.format("YYYY-MM-DD HH:mm:ss");
|
|
// }
|
|
|
|
// // Only apply LoRa logic for slaves
|
|
// if (device.type === 'slave' && latestData?.tanks?.length) {
|
|
// const matchingTank = latestData.tanks.find(
|
|
// tank => tank.tankhardwareId?.trim() === hardwareId
|
|
// );
|
|
|
|
// if (matchingTank) {
|
|
// const loraTime = moment.tz(matchingTank.date, "Asia/Kolkata");
|
|
// const loraDiff = moment.tz("Asia/Kolkata").diff(loraTime, "minutes");
|
|
// const isLoraConnected = loraDiff <= 1;
|
|
|
|
// enriched.connected_lora_date = loraTime.format("DD-MM-YYYY");
|
|
// enriched.connected_lora_time = matchingTank.time;
|
|
// enriched.lora_connected_status = isLoraConnected ? "connected" : "disconnected";
|
|
// enriched.lora_last_check_time = loraTime.format("YYYY-MM-DD HH:mm:ss");
|
|
// } else {
|
|
// enriched.connected_lora_date = null;
|
|
// enriched.connected_lora_time = null;
|
|
// enriched.lora_connected_status = "disconnected";
|
|
// enriched.lora_last_check_time = null;
|
|
// }
|
|
// }
|
|
|
|
// // Remove irrelevant fields
|
|
// if (device.type === 'slave') {
|
|
// delete enriched.tanks;
|
|
// }
|
|
// if (device.type === 'master') {
|
|
// delete enriched.connected_lora_date;
|
|
// delete enriched.connected_lora_time;
|
|
// delete enriched.lora_connected_status;
|
|
// delete enriched.lora_last_check_time;
|
|
// delete enriched.tanks;
|
|
// }
|
|
|
|
// return enriched;
|
|
// };
|
|
|
|
// const enrichedMasters = await Promise.all(masters.map(enrichDevice));
|
|
// const enrichedSlaves = await Promise.all(slaves.map(enrichDevice));
|
|
|
|
// 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.getMasterSlaveSummary = async (req, reply) => {
|
|
// try {
|
|
// const { customerId } = req.params;
|
|
|
|
// if (!customerId) {
|
|
// return reply.status(400).send({ error: 'customerId is required' });
|
|
// }
|
|
|
|
// // Fetch all devices for the customer
|
|
// const allDevices = await Insensors.find({ customerId }).lean();
|
|
|
|
// // Group devices
|
|
// const masters = allDevices.filter(dev => dev.type === 'master');
|
|
// const slaves = allDevices.filter(dev => dev.type === 'slave');
|
|
|
|
// // Create a map of slaves by connected_to hardwareId
|
|
// const slaveMap = {};
|
|
// for (const slave of slaves) {
|
|
// const masterId = slave.connected_to;
|
|
// if (!slaveMap[masterId]) {
|
|
// slaveMap[masterId] = [];
|
|
// }
|
|
|
|
// const loraTime = slave.connected_lora_time || null;
|
|
// const loraDate = slave.connected_lora_date || null;
|
|
|
|
// slaveMap[masterId].push({
|
|
// hardwareId: slave.hardwareId,
|
|
// tankName: slave.tankName || null,
|
|
// location: slave.tankLocation || null,
|
|
// connected_status: slave.connected_status || 'disconnected',
|
|
// connected_lora_time: loraTime,
|
|
// connected_lora_date: loraDate
|
|
// });
|
|
// }
|
|
|
|
// const response = [];
|
|
|
|
// for (const master of masters) {
|
|
// // Fetch latest IoT data for this master
|
|
// const latestData = await IotData.findOne({ hardwareId: master.hardwareId })
|
|
// .sort({ date: -1 })
|
|
// .lean();
|
|
|
|
// const enriched = {
|
|
// ...master,
|
|
// connected_status: master.connected_status || 'disconnected',
|
|
// connected_slave_count: slaveMap[master.hardwareId]?.length || 0,
|
|
// connected_slaves: slaveMap[master.hardwareId] || []
|
|
// };
|
|
|
|
// // Use saved GSM fields from Insensors
|
|
// if (master.gsm_last_check_time) {
|
|
// enriched.gsm_last_check_time = master.gsm_last_check_time;
|
|
|
|
// const indiaTime = moment.tz(master.gsm_last_check_time, 'Asia/Kolkata');
|
|
// enriched.connected_gsm_date = indiaTime.format('DD-MM-YYYY');
|
|
// enriched.connected_gsm_time = indiaTime.format('HH:mm:ss');
|
|
// }
|
|
|
|
// // If LoRa timestamps are available from master (some masters act like slaves too)
|
|
// if (master.connected_lora_date && master.connected_lora_time) {
|
|
// enriched.connected_lora_date = master.connected_lora_date;
|
|
// enriched.connected_lora_time = master.connected_lora_time;
|
|
// }
|
|
|
|
// response.push(enriched);
|
|
// }
|
|
|
|
// return reply.send({
|
|
// status_code: 200,
|
|
// message: 'Master-slave summary retrieved successfully',
|
|
// data: response
|
|
// });
|
|
// } catch (err) {
|
|
// console.error('Error in getMasterSlaveSummary:', err);
|
|
// return reply.status(500).send({ error: 'Internal Server Error' });
|
|
// }
|
|
// };
|
|
|
|
|
|
// exports.getMasterSlaveSummary = async (req, reply) => {
|
|
// try {
|
|
// const { customerId } = req.params;
|
|
|
|
// if (!customerId) {
|
|
// return reply.status(400).send({ error: 'customerId is required' });
|
|
// }
|
|
|
|
// // Fetch all devices for the customer
|
|
// const allDevices = await Insensors.find({ customerId }).lean();
|
|
|
|
// // Group devices
|
|
// const masters = allDevices.filter(dev => dev.type === 'master');
|
|
// const slaves = allDevices.filter(dev => dev.type === 'slave');
|
|
|
|
// // Create a map of slaves by connected_to hardwareId
|
|
// const slaveMap = {};
|
|
// for (const slave of slaves) {
|
|
// const masterId = slave.connected_to;
|
|
// if (!slaveMap[masterId]) {
|
|
// slaveMap[masterId] = [];
|
|
// }
|
|
|
|
// const loraTime = slave.connected_lora_time || null;
|
|
// const loraDate = slave.connected_lora_date || null;
|
|
|
|
// // Fetch masterName using the connected_to masterId
|
|
// const master = masters.find(m => m.hardwareId === masterId);
|
|
// const masterName = master ? master.masterName : 'Unknown';
|
|
|
|
// slaveMap[masterId].push({
|
|
// hardwareId: slave.hardwareId,
|
|
// tankName: slave.tankName || null,
|
|
// location: slave.tankLocation || null,
|
|
// connected_status: slave.connected_status || 'disconnected',
|
|
// connected_lora_time: loraTime,
|
|
// connected_lora_date: loraDate,
|
|
// type: slave.type || 'N/A', // Add 'type' field
|
|
// typeOfWater: slave.typeOfWater || 'N/A', // Add 'typeOfWater' field
|
|
// lora_last_check_time: slave.lora_last_check_time && slave.lora_last_check_time
|
|
// ? `${slave.lora_last_check_time}`
|
|
// : null, // Add 'lora_last_check_time' field
|
|
// connected_to: slave.connected_to, // Add 'connected_to' field
|
|
// masterName: masterName // Add 'masterName' field
|
|
// });
|
|
// }
|
|
|
|
// const response = [];
|
|
|
|
// for (const master of masters) {
|
|
// // Fetch latest IoT data for this master
|
|
// const latestData = await IotData.findOne({ hardwareId: master.hardwareId })
|
|
// .sort({ date: -1 })
|
|
// .lean();
|
|
|
|
// const enriched = {
|
|
// ...master,
|
|
// connected_status: master.connected_status || 'disconnected',
|
|
// connected_slave_count: slaveMap[master.hardwareId]?.length || 0,
|
|
// connected_slaves: slaveMap[master.hardwareId] || []
|
|
// };
|
|
|
|
// // Use saved GSM fields from Insensors
|
|
// if (master.gsm_last_check_time) {
|
|
// enriched.gsm_last_check_time = master.gsm_last_check_time;
|
|
|
|
// const indiaTime = moment.tz(master.gsm_last_check_time, 'Asia/Kolkata');
|
|
// enriched.connected_gsm_date = indiaTime.format('DD-MM-YYYY');
|
|
// enriched.connected_gsm_time = indiaTime.format('HH:mm:ss');
|
|
// }
|
|
|
|
// // If LoRa timestamps are available from master (some masters act like slaves too)
|
|
// if (master.connected_lora_date && master.connected_lora_time) {
|
|
// enriched.connected_lora_date = master.connected_lora_date;
|
|
// enriched.connected_lora_time = master.connected_lora_time;
|
|
// }
|
|
|
|
// response.push(enriched);
|
|
// }
|
|
|
|
// return reply.send({
|
|
// status_code: 200,
|
|
// message: 'Master-slave summary retrieved successfully',
|
|
// data: response
|
|
// });
|
|
// } catch (err) {
|
|
// console.error('Error in getMasterSlaveSummary:', err);
|
|
// return reply.status(500).send({ error: 'Internal Server Error' });
|
|
// }
|
|
// };
|
|
// exports.getMasterSlaveSummary = async (req, reply) => {
|
|
// try {
|
|
// const { customerId } = req.params;
|
|
|
|
// if (!customerId) {
|
|
// return reply.status(400).send({ error: 'customerId is required' });
|
|
// }
|
|
|
|
// // Fetch all masters for the customer from the Insensors schema
|
|
// const masters = await Insensors.find({ customerId, type: 'master' }).lean();
|
|
|
|
// // Fetch orders for the customer to map master connections
|
|
// const orders = await Order.find({ customerId }).lean();
|
|
|
|
// const orderMap = {};
|
|
// // Mapping master hardwareId from the orders schema to masterName and location
|
|
// orders.forEach(order => {
|
|
// order.master_connections.forEach(connection => {
|
|
// orderMap[connection.hardwareId] = {
|
|
// masterName: connection.master_name || 'Unknown',
|
|
// location: connection.location || 'Unknown'
|
|
// };
|
|
// });
|
|
// });
|
|
|
|
// const result = [];
|
|
|
|
// // Loop through each master and enrich it with data
|
|
// for (const master of masters) {
|
|
// const orderInfo = orderMap[master.hardwareId] || {}; // Get order info based on hardwareId
|
|
// const masterName = orderInfo.masterName || 'Unknown';
|
|
// const location = orderInfo.location || 'Unknown';
|
|
|
|
// const latestGsmData = await IotData.findOne({ hardwareId: master.hardwareId })
|
|
// .sort({ date: -1, time: -1 })
|
|
// .lean();
|
|
|
|
// let connectedGsmDate = null;
|
|
// let connectedGsmTime = null;
|
|
// if (latestGsmData?.date && latestGsmData?.time) {
|
|
// const indiaTime = moment.tz(latestGsmData.date, 'Asia/Kolkata');
|
|
// connectedGsmDate = indiaTime.format('DD-MM-YYYY');
|
|
// connectedGsmTime = latestGsmData.time;
|
|
// }
|
|
|
|
// let gsmLastDisconnect = master.gsm_last_disconnect_time;
|
|
// if (master.connected_status === 'disconnected' && connectedGsmDate && connectedGsmTime) {
|
|
// const disconnectTime = `${connectedGsmDate} ${connectedGsmTime}`;
|
|
// await Insensors.updateOne({ hardwareId: master.hardwareId }, { $set: { gsm_last_disconnect_time: disconnectTime } });
|
|
// gsmLastDisconnect = disconnectTime;
|
|
// }
|
|
|
|
// const connectedSlaves = [];
|
|
// const slaves = await Insensors.find({ connected_to: master.hardwareId, type: 'slave' }).lean();
|
|
|
|
// for (const slave of slaves) {
|
|
// const slaveIot = await IotData.findOne({ hardwareId: slave.hardwareId }).sort({ date: -1, time: -1 }).lean();
|
|
|
|
// const loraDate = slave.connected_lora_date || (slaveIot?.date ? moment.tz(slaveIot.date, 'Asia/Kolkata').format('DD-MM-YYYY') : null);
|
|
// const loraTime = slave.connected_lora_time || slaveIot?.time || null;
|
|
|
|
// let loraLastDisconnect = slave.lora_last_disconnect_time;
|
|
// if (slave.connected_status === 'disconnected' && loraDate && loraTime) {
|
|
// const disconnectTime = `${loraDate} ${loraTime}`;
|
|
// await Insensors.updateOne({ hardwareId: slave.hardwareId }, { $set: { lora_last_disconnect_time: disconnectTime } });
|
|
// loraLastDisconnect = disconnectTime;
|
|
// }
|
|
|
|
// connectedSlaves.push({
|
|
// hardwareId: slave.hardwareId,
|
|
// tankName: slave.tankName,
|
|
// location: slave.tankLocation,
|
|
// connected_status: slave.connected_status,
|
|
// connected_lora_date: loraDate,
|
|
// connected_lora_time: loraTime,
|
|
// lora_last_disconnect_time: loraLastDisconnect,
|
|
// type: slave.type || 'slave'
|
|
// });
|
|
// }
|
|
|
|
// result.push({
|
|
// hardwareId: master.hardwareId,
|
|
// masterName,
|
|
// location,
|
|
// type: master.type || 'master',
|
|
// connected_status: master.connected_status,
|
|
// connected_slave_count: connectedSlaves.length,
|
|
// connected_slaves: connectedSlaves,
|
|
// connected_gsm_date: connectedGsmDate,
|
|
// connected_gsm_time: connectedGsmTime,
|
|
// gsm_last_check_time: master.gsm_last_check_time || null,
|
|
// gsm_last_disconnect_time: gsmLastDisconnect,
|
|
// connected_lora_date: master.connected_lora_date || null,
|
|
// connected_lora_time: master.connected_lora_time || null
|
|
// });
|
|
// }
|
|
|
|
// return reply.send({
|
|
// status_code: 200,
|
|
// message: 'Master-slave summary retrieved successfully',
|
|
// data: result
|
|
// });
|
|
|
|
// } catch (error) {
|
|
// console.error('Error in getMasterSlaveSummary:', error);
|
|
// return reply.status(500).send({ error: 'Internal Server Error' });
|
|
// }
|
|
// };
|
|
|
|
|
|
|
|
exports.getMasterSlaveSummary = async (req, reply) => {
|
|
try {
|
|
const { customerId } = req.params;
|
|
|
|
if (!customerId) {
|
|
return reply.status(400).send({ error: "customerId is required" });
|
|
}
|
|
|
|
// Fetch all master devices for the customer
|
|
const masters = await Insensors.find({ customerId, type: "master" }).lean();
|
|
|
|
// Fetch orders to map hardwareId to masterName/location
|
|
const orders = await Order.find({ customerId }).lean();
|
|
const orderMap = {};
|
|
orders.forEach(order => {
|
|
order.master_connections.forEach(connection => {
|
|
orderMap[connection.hardwareId] = {
|
|
masterName: connection.master_name || null,
|
|
location: connection.location || null
|
|
};
|
|
});
|
|
});
|
|
|
|
const result = [];
|
|
|
|
for (const master of masters) {
|
|
const orderInfo = orderMap[master.hardwareId] || {};
|
|
|
|
// Use Insensors values first, fallback to orders
|
|
const masterName = master.masterName || orderInfo.masterName || null;
|
|
const location = master.location || orderInfo.location || null;
|
|
|
|
// ➤ Fetch the latest GSM data for the master device
|
|
const latestGsmData = await IotData.findOne({ hardwareId: master.hardwareId })
|
|
.sort({ date: -1, time: -1 })
|
|
.lean();
|
|
|
|
let connectedGsmDate = null;
|
|
let connectedGsmTime = null;
|
|
let gsmStatus = "unknown";
|
|
let gsmLastCheckTime = null;
|
|
let gsmLastDisconnect = master.gsm_last_disconnect_time || null;
|
|
|
|
if (latestGsmData?.date && latestGsmData?.time) {
|
|
const indiaTime = moment.tz(latestGsmData.date, "Asia/Kolkata");
|
|
connectedGsmDate = indiaTime.format("DD-MM-YYYY");
|
|
connectedGsmTime = latestGsmData.time;
|
|
|
|
const now = moment.tz("Asia/Kolkata");
|
|
const diffInMinutes = now.diff(indiaTime, "minutes");
|
|
|
|
gsmStatus = diffInMinutes <= 1 ? "connected" : "disconnected";
|
|
gsmLastCheckTime = now.format("DD-MM-YYYY HH:mm:ss");
|
|
|
|
// If disconnected, update the disconnect time based on the latest GSM data
|
|
if (gsmStatus === "disconnected" && !gsmLastDisconnect) {
|
|
gsmLastDisconnect = `${connectedGsmDate} ${connectedGsmTime}`;
|
|
}
|
|
|
|
// ✅ Update master record with latest GSM info
|
|
await Insensors.updateOne(
|
|
{ hardwareId: master.hardwareId },
|
|
{
|
|
$set: {
|
|
connected_status: gsmStatus,
|
|
connected_gsm_date: connectedGsmDate,
|
|
connected_gsm_time: connectedGsmTime,
|
|
gsm_last_check_time: gsmLastCheckTime,
|
|
gsm_last_disconnect_time: gsmLastDisconnect // Update the disconnect time
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
// ➤ Save GSM disconnect time if status is disconnected and gsmLastDisconnect is set
|
|
if (gsmStatus === "disconnected" && gsmLastDisconnect) {
|
|
await Insensors.updateOne(
|
|
{ hardwareId: master.hardwareId },
|
|
{ $set: { gsm_last_disconnect_time: gsmLastDisconnect } }
|
|
);
|
|
}
|
|
|
|
// ➤ Now handle slaves
|
|
const connectedSlaves = [];
|
|
const slaves = await Insensors.find({ connected_to: master.hardwareId, type: "slave" }).lean();
|
|
|
|
for (const slave of slaves) {
|
|
const slaveIot = await IotData.findOne({ hardwareId: slave.hardwareId })
|
|
.sort({ date: -1, time: -1 })
|
|
.lean();
|
|
|
|
let connectedLoraDate = null;
|
|
let connectedLoraTime = null;
|
|
let loraStatus = "unknown";
|
|
let loraLastCheckTime = null;
|
|
let loraLastDisconnect = slave.lora_last_disconnect_time || null;
|
|
|
|
if (slaveIot?.date && slaveIot?.time) {
|
|
const indiaTime = moment.tz(`${slaveIot.date} ${slaveIot.time}`, "Asia/Kolkata");
|
|
connectedLoraDate = indiaTime.format("DD-MM-YYYY");
|
|
connectedLoraTime = slaveIot.time;
|
|
|
|
const now = moment.tz("Asia/Kolkata");
|
|
const diff = now.diff(indiaTime, "minutes");
|
|
|
|
loraStatus = diff <= 1 ? "connected" : "disconnected";
|
|
loraLastCheckTime = now.format("DD-MM-YYYY HH:mm:ss");
|
|
|
|
// Update slave record with new values
|
|
await Insensors.updateOne(
|
|
{ hardwareId: slave.hardwareId },
|
|
{
|
|
$set: {
|
|
connected_status: loraStatus,
|
|
connected_lora_date: connectedLoraDate,
|
|
connected_lora_time: connectedLoraTime,
|
|
lora_last_check_time: loraLastCheckTime
|
|
}
|
|
}
|
|
);
|
|
|
|
// Save disconnect time if applicable
|
|
if (loraStatus === "disconnected") {
|
|
const disconnectTime = `${connectedLoraDate} ${connectedLoraTime}`;
|
|
await Insensors.updateOne(
|
|
{ hardwareId: slave.hardwareId },
|
|
{ $set: { lora_last_disconnect_time: disconnectTime } }
|
|
);
|
|
loraLastDisconnect = disconnectTime;
|
|
}
|
|
} else {
|
|
// If no IoT data is found, mark explicitly as disconnected
|
|
loraStatus = "disconnected";
|
|
loraLastDisconnect = loraLastDisconnect || null;
|
|
await Insensors.updateOne(
|
|
{ hardwareId: slave.hardwareId },
|
|
{
|
|
$set: {
|
|
connected_status: loraStatus,
|
|
lora_last_disconnect_time: loraLastDisconnect
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
connectedSlaves.push({
|
|
hardwareId: slave.hardwareId,
|
|
tankName: slave.tankName || null,
|
|
location: slave.tankLocation || null,
|
|
connected_status: loraStatus,
|
|
connected_lora_date: connectedLoraDate,
|
|
connected_lora_time: connectedLoraTime,
|
|
lora_last_disconnect_time: loraLastDisconnect,
|
|
type: slave.type || "slave"
|
|
});
|
|
}
|
|
|
|
result.push({
|
|
hardwareId: master.hardwareId,
|
|
masterName,
|
|
location,
|
|
type: master.type || "master",
|
|
connected_status: gsmStatus,
|
|
connected_slave_count: connectedSlaves.length,
|
|
connected_slaves: connectedSlaves,
|
|
connected_gsm_date: connectedGsmDate,
|
|
connected_gsm_time: connectedGsmTime,
|
|
gsm_last_check_time: gsmLastCheckTime,
|
|
gsm_last_disconnect_time: gsmLastDisconnect,
|
|
connected_lora_date: master.connected_lora_date || null,
|
|
connected_lora_time: master.connected_lora_time || null
|
|
});
|
|
}
|
|
|
|
return reply.send({
|
|
status_code: 200,
|
|
message: "Master-slave summary retrieved successfully",
|
|
data: result
|
|
});
|
|
} catch (error) {
|
|
console.error("Error in getMasterSlaveSummary:", error);
|
|
return reply.status(500).send({ error: "Internal Server Error" });
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
// 🔍 Helper to get tankHeight from latest IotData record
|
|
async function getTankHeight(hardwareId) {
|
|
const iotData = await IotData.findOne({ 'tanks.tankhardwareId': hardwareId })
|
|
.sort({ date: -1 })
|
|
.lean();
|
|
|
|
if (!iotData) return null;
|
|
|
|
const matchedTank = iotData.tanks.find(t => t.tankhardwareId === hardwareId);
|
|
return matchedTank?.tankHeight || null;
|
|
}
|
|
|
|
|
|
// 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" });
|
|
// }
|
|
// };
|
|
|
|
|
|
// exports.getIotDataByCustomer = async (req, reply) => {
|
|
// try {
|
|
// const { customerId } = req.params;
|
|
|
|
// if (!customerId) {
|
|
// return reply.code(400).send({ error: "customerId is required" });
|
|
// }
|
|
|
|
// // Step 1: Get all devices for this customer
|
|
// const sensors = await Insensors.find({ customerId });
|
|
|
|
// if (!sensors.length) {
|
|
// return reply.code(404).send({ message: "No sensors found for this customer." });
|
|
// }
|
|
|
|
// // Step 2: Get all unique master hardwareIds from connected_to
|
|
// const masterHardwareIds = [
|
|
// ...new Set(
|
|
// sensors.map(s => s.connected_to?.trim()).filter(Boolean)
|
|
// )
|
|
// ];
|
|
|
|
// // Step 3: Loop over each master (connected_to) and build enriched data
|
|
// const enrichedMasters = await Promise.all(masterHardwareIds.map(async (hardwareId) => {
|
|
// const latestRecord = await IotData.findOne({ hardwareId }).sort({ date: -1 }).lean();
|
|
|
|
// if (!latestRecord) {
|
|
// return {
|
|
// hardwareId,
|
|
// message: "No IoT data found",
|
|
// tanks: []
|
|
// };
|
|
// }
|
|
|
|
// // Get all slaves connected to this master
|
|
// const connectedSlaves = sensors.filter(sensor => sensor.connected_to?.trim() === hardwareId);
|
|
|
|
// const message = latestRecord.tanks.some(t => parseFloat(t.tankHeight || 0) > 0)
|
|
// ? "GSM is connected"
|
|
// : "GSM is not connected";
|
|
|
|
// // Map connected slaves to tank details
|
|
// let tanks = connectedSlaves.map(slave => {
|
|
// const slaveId = slave.hardwareId?.trim();
|
|
// const matchedTank = latestRecord.tanks.find(tank => tank.tankhardwareId === slaveId);
|
|
|
|
// const loraMessage = matchedTank && parseFloat(matchedTank.tankHeight || 0) > 0
|
|
// ? "LORA is connected"
|
|
// : "LORA is not connected";
|
|
|
|
// return {
|
|
// tankhardwareId: slaveId,
|
|
// tankName: slave.tankName ?? null,
|
|
// tankLocation: slave.tankLocation ?? null,
|
|
// masterName: slave.masterName ?? null,
|
|
// location: slave.location ?? null,
|
|
// loraMessage,
|
|
// latestTankData: matchedTank ?? null
|
|
// };
|
|
// });
|
|
|
|
// // ❗ Remove the first tank entry
|
|
// tanks = tanks.slice(1);
|
|
|
|
// return {
|
|
// hardwareId,
|
|
// message,
|
|
// masterName: connectedSlaves[0]?.masterName ?? null,
|
|
// location: connectedSlaves[0]?.location ?? null,
|
|
// tanks
|
|
// };
|
|
// }));
|
|
|
|
// return reply.send({
|
|
// status_code: 200,
|
|
// message: "Success",
|
|
// data: enrichedMasters
|
|
// });
|
|
|
|
// } 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" });
|
|
}
|
|
|
|
// ✅ Get all sensors for the customer
|
|
const sensors = await Insensors.find({ customerId });
|
|
|
|
if (!sensors.length) {
|
|
return reply.code(404).send({ message: "No sensors found for this customer." });
|
|
}
|
|
|
|
// ✅ Get only master hardware IDs from Insensors directly
|
|
const masterSensors = sensors.filter(s => s.type === 'master');
|
|
const masterHardwareIds = masterSensors.map(m => m.hardwareId?.trim());
|
|
|
|
// ✅ Map for masterName/location from Order
|
|
const orders = await Order.find({ customerId }).lean();
|
|
const orderMap = {};
|
|
orders.forEach(order => {
|
|
order.master_connections.forEach(connection => {
|
|
orderMap[connection.hardwareId] = {
|
|
masterName: connection.master_name || null,
|
|
location: connection.location || null
|
|
};
|
|
});
|
|
});
|
|
|
|
// ✅ Prepare final enriched master data
|
|
const enrichedMasters = await Promise.all(masterHardwareIds.map(async (hardwareId) => {
|
|
const latestRecord = await IotData.findOne({ hardwareId }).sort({ date: -1 }).lean();
|
|
|
|
const orderInfo = orderMap[hardwareId] || {};
|
|
|
|
if (!latestRecord) {
|
|
return {
|
|
hardwareId,
|
|
message: "No IoT data found",
|
|
masterName: orderInfo.masterName ?? null,
|
|
location: orderInfo.location ?? null,
|
|
tanks: []
|
|
};
|
|
}
|
|
|
|
// ✅ Check GSM status
|
|
const indiaTime = moment.tz(latestRecord.date, "Asia/Kolkata");
|
|
const now = moment.tz("Asia/Kolkata");
|
|
const diffInMinutes = now.diff(indiaTime, "minutes");
|
|
const gsmConnected = diffInMinutes <= 1;
|
|
const message = gsmConnected ? "GSM is connected" : "GSM is not connected";
|
|
|
|
// ✅ Get slaves connected to this master
|
|
const connectedSlaves = sensors.filter(sensor => sensor.connected_to?.trim() === hardwareId);
|
|
|
|
// ✅ Prepare tank info
|
|
const tanks = connectedSlaves.map(slave => {
|
|
const slaveId = slave.hardwareId?.trim();
|
|
const matchedTank = latestRecord.tanks?.find(t => t.tankhardwareId === slaveId);
|
|
|
|
let loraMessage = "LORA is not connected";
|
|
|
|
if (matchedTank?.date && matchedTank.tankHeight !== "0") {
|
|
const tankTime = moment.tz(matchedTank.date, "Asia/Kolkata");
|
|
const loraDiff = now.diff(tankTime, "minutes");
|
|
loraMessage = loraDiff <= 1 ? "LORA is connected" : "LORA is not connected";
|
|
}
|
|
|
|
return {
|
|
tankhardwareId: slaveId,
|
|
tankName: slave.tankName ?? null,
|
|
tankLocation: slave.tankLocation ?? null,
|
|
masterName: orderInfo.masterName ?? null,
|
|
location: orderInfo.location ?? null,
|
|
loraMessage,
|
|
latestTankData: matchedTank ?? null
|
|
};
|
|
});
|
|
|
|
return {
|
|
hardwareId,
|
|
message,
|
|
masterName: orderInfo.masterName ?? null,
|
|
location: orderInfo.location ?? null,
|
|
tanks
|
|
};
|
|
}));
|
|
|
|
return reply.send({
|
|
status_code: 200,
|
|
message: "Success",
|
|
data: enrichedMasters
|
|
});
|
|
|
|
} catch (err) {
|
|
console.error("Error fetching IoT data by customerId:", err);
|
|
return reply.code(500).send({ error: "Internal Server Error" });
|
|
}
|
|
};
|
|
|
|
|
|
exports.getIotDataByCustomerAndHardwareId = async (req, reply) => {
|
|
try {
|
|
const { customerId, hardwareId } = req.params;
|
|
|
|
if (!customerId || !hardwareId) {
|
|
return reply.code(400).send({ error: "Both customerId and hardwareId are required" });
|
|
}
|
|
|
|
// Get all sensors for the customer
|
|
const sensors = await Insensors.find({ customerId });
|
|
|
|
if (!sensors.length) {
|
|
return reply.code(404).send({ message: "No sensors found for this customer." });
|
|
}
|
|
|
|
// Get IoT data for that master hardwareId
|
|
const latestRecord = await IotData.findOne({ hardwareId }).sort({ date: -1 }).lean();
|
|
|
|
if (!latestRecord) {
|
|
return reply.code(404).send({
|
|
hardwareId,
|
|
message: "No IoT data found",
|
|
tanks: []
|
|
});
|
|
}
|
|
|
|
const indiaTime = moment.tz(latestRecord.date, "Asia/Kolkata");
|
|
const now = moment.tz("Asia/Kolkata");
|
|
const diffInMinutes = now.diff(indiaTime, "minutes");
|
|
const gsmConnected = diffInMinutes <= 1;
|
|
const message = gsmConnected ? "GSM is connected" : "GSM is not connected";
|
|
|
|
// Get all slaves connected to this master
|
|
const connectedSlaves = sensors.filter(sensor => sensor.connected_to?.trim() === hardwareId);
|
|
|
|
// Enrich slave tanks with tank data and LoRa status
|
|
const tanks = connectedSlaves.map(slave => {
|
|
const slaveId = slave.hardwareId?.trim();
|
|
const matchedTank = latestRecord.tanks?.find(tank => tank.tankhardwareId === slaveId);
|
|
|
|
let loraConnected = false;
|
|
let loraMessage = "LORA is not connected";
|
|
|
|
if (matchedTank?.date && matchedTank.tankHeight !== "0") {
|
|
const tankTime = moment.tz(matchedTank.date, "Asia/Kolkata");
|
|
const loraDiff = now.diff(tankTime, "minutes");
|
|
loraConnected = loraDiff <= 1;
|
|
loraMessage = loraConnected ? "LORA is connected" : "LORA is not connected";
|
|
}
|
|
|
|
return {
|
|
tankhardwareId: slaveId,
|
|
tankName: slave.tankName ?? null,
|
|
tankLocation: slave.tankLocation ?? null,
|
|
masterName: slave.masterName ?? null,
|
|
location: slave.location ?? null,
|
|
message: loraMessage,
|
|
latestTankData: matchedTank ?? null
|
|
};
|
|
});
|
|
|
|
return reply.send({
|
|
status_code: 200,
|
|
message: "Success",
|
|
data: {
|
|
hardwareId,
|
|
message,
|
|
masterName: connectedSlaves[0]?.masterName ?? null,
|
|
location: connectedSlaves[0]?.location ?? null,
|
|
tanks
|
|
}
|
|
});
|
|
|
|
} catch (err) {
|
|
console.error("Error fetching IoT data by customerId and hardwareId:", err);
|
|
return reply.code(500).send({ error: "Internal Server Error" });
|
|
}
|
|
};
|
|
|
|
|
|
|
|
//const moment = require("moment-timezone");
|
|
|
|
exports.raiseATicket = async (req, reply) => {
|
|
try {
|
|
const { customerId, connected_to } = req.params;
|
|
|
|
if (!customerId || !connected_to) {
|
|
return reply.code(400).send({ error: "customerId and connected_to are required" });
|
|
}
|
|
|
|
const sensors = await Insensors.find({ customerId }).lean();
|
|
const orders = await Order.find({ customerId }).lean();
|
|
|
|
if (!sensors.length) {
|
|
return reply.code(404).send({ message: "No sensors found for this customer." });
|
|
}
|
|
|
|
const masterSensor = sensors.find(s => s.hardwareId?.trim() === connected_to.trim() && s.type === "master");
|
|
if (!masterSensor) {
|
|
return reply.code(404).send({ message: "Master hardwareId not found." });
|
|
}
|
|
|
|
const orderMap = {};
|
|
orders.forEach(order => {
|
|
order.master_connections.forEach(conn => {
|
|
orderMap[conn.hardwareId] = {
|
|
masterName: conn.master_name || null,
|
|
location: conn.location || null
|
|
};
|
|
});
|
|
});
|
|
|
|
const latestMasterRecord = await IotData.findOne({ hardwareId: connected_to }).sort({ date: -1 }).lean();
|
|
const now = moment.tz("Asia/Kolkata");
|
|
|
|
let masterConnectedStatus = "disconnected";
|
|
let lastDataTime = "No data";
|
|
let diffInMinutes = null;
|
|
|
|
if (latestMasterRecord?.date) {
|
|
const indiaTime = moment.tz(latestMasterRecord.date, "Asia/Kolkata");
|
|
diffInMinutes = now.diff(indiaTime, "minutes");
|
|
lastDataTime = indiaTime.format("DD-MM-YYYY HH:mm:ss");
|
|
masterConnectedStatus = diffInMinutes <= 1 ? "connected" : "disconnected";
|
|
}
|
|
|
|
const connectedSlaves = sensors.filter(s =>
|
|
s.connected_to?.trim() === connected_to.trim() && s.type === "slave"
|
|
);
|
|
|
|
const formattedSlaves = [];
|
|
const disconnectedSlaves = [];
|
|
|
|
for (const slave of connectedSlaves) {
|
|
const slaveData = latestMasterRecord?.tanks.find(t => t.tankhardwareId === slave.hardwareId);
|
|
let slaveStatus = "disconnected";
|
|
let lastSlaveDataTime = "No data";
|
|
let slaveDiff = null;
|
|
|
|
if (slaveData?.date) {
|
|
const slaveTime = moment.tz(slaveData.date, "Asia/Kolkata");
|
|
slaveDiff = now.diff(slaveTime, "minutes");
|
|
lastSlaveDataTime = slaveTime.format("DD-MM-YYYY HH:mm:ss");
|
|
slaveStatus = slaveDiff <= 1 ? "connected" : "disconnected";
|
|
}
|
|
|
|
if (slaveStatus === "disconnected") {
|
|
disconnectedSlaves.push({
|
|
slaveHardwareId: slave.hardwareId,
|
|
slaveName: slave.tankName || "Unknown Slave"
|
|
});
|
|
}
|
|
|
|
formattedSlaves.push({
|
|
hardwareId: slave.hardwareId,
|
|
slaveName: slave.tankName || null,
|
|
location: slave.tankLocation || null,
|
|
type: "slave",
|
|
connected_status: slaveStatus,
|
|
last_data_time: lastSlaveDataTime,
|
|
diff_in_minutes: slaveDiff
|
|
});
|
|
}
|
|
|
|
// Raise tickets if needed
|
|
const issuesToAdd = [];
|
|
|
|
if (masterConnectedStatus === "disconnected") {
|
|
const existing = await Support.findOne({
|
|
"issues.hardwareId": connected_to,
|
|
"issues.type": "GSM Disconnected"
|
|
});
|
|
if (!existing) {
|
|
issuesToAdd.push({
|
|
type: "GSM Disconnected",
|
|
hardwareId: connected_to,
|
|
message: `Master GSM disconnected - ${connected_to}`
|
|
});
|
|
}
|
|
}
|
|
|
|
const newHardwareIds = [];
|
|
const newSlaveNames = [];
|
|
|
|
for (const s of disconnectedSlaves) {
|
|
const existing = await Support.findOne({
|
|
"issues.hardwareIds": s.slaveHardwareId,
|
|
"issues.masterHardwareId": connected_to,
|
|
"issues.type": "LoRa Disconnected"
|
|
});
|
|
if (!existing) {
|
|
newHardwareIds.push(s.slaveHardwareId);
|
|
newSlaveNames.push(s.slaveName);
|
|
}
|
|
}
|
|
|
|
if (newHardwareIds.length > 0) {
|
|
issuesToAdd.push({
|
|
type: "LoRa Disconnected",
|
|
masterHardwareId: connected_to,
|
|
hardwareIds: newHardwareIds,
|
|
slaveNames: newSlaveNames,
|
|
message: `Slaves LoRa disconnected under master ${connected_to}`
|
|
});
|
|
}
|
|
|
|
if (issuesToAdd.length > 0) {
|
|
const supportRecord = await Support.findOne({ supportId: "AWHYSU64" });
|
|
if (supportRecord) {
|
|
await Support.findOneAndUpdate(
|
|
{ _id: supportRecord._id },
|
|
{
|
|
$push: {
|
|
issues: { $each: issuesToAdd }
|
|
},
|
|
updatedAt: new Date()
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
const masterDetails = {
|
|
hardwareId: connected_to,
|
|
masterName: masterSensor.masterName || orderMap[connected_to]?.masterName || null,
|
|
location: masterSensor.location || orderMap[connected_to]?.location || null,
|
|
type: "master",
|
|
connected_status: masterConnectedStatus,
|
|
last_data_time: lastDataTime,
|
|
diff_in_minutes: diffInMinutes
|
|
};
|
|
|
|
return reply.send({
|
|
status_code: 200,
|
|
message: "Checked connection and updated support if needed.",
|
|
master: masterDetails,
|
|
connected_slaves: formattedSlaves
|
|
});
|
|
} catch (error) {
|
|
console.error("Error raising ticket:", error);
|
|
return reply.code(500).send({ error: "Internal server error" });
|
|
}
|
|
};
|
|
|
|
|
|
// exports.raiseATicket = async (req, reply) => {
|
|
// try {
|
|
// const { customerId, connected_to } = req.params;
|
|
|
|
// if (!customerId || !connected_to) {
|
|
// return reply.code(400).send({ error: "customerId and connected_to are required" });
|
|
// }
|
|
|
|
// const sensors = await Insensors.find({ customerId });
|
|
|
|
// if (!sensors.length) {
|
|
// return reply.code(404).send({ message: "No sensors found for this customer." });
|
|
// }
|
|
|
|
// const masterSensor = sensors.find(s => s.hardwareId?.trim() === connected_to.trim() && s.type === "master");
|
|
|
|
// if (!masterSensor) {
|
|
// return reply.code(404).send({ message: "Master hardwareId not found." });
|
|
// }
|
|
|
|
// const latestMasterRecord = await IotData.findOne({ hardwareId: connected_to }).sort({ date: -1 }).lean();
|
|
|
|
// if (!latestMasterRecord) {
|
|
// return reply.code(404).send({ message: "No IoT data found for this hardwareId." });
|
|
// }
|
|
|
|
// const now = moment.tz("Asia/Kolkata");
|
|
// const indiaTime = moment.tz(latestMasterRecord.date, "Asia/Kolkata");
|
|
// const diffInMinutesMaster = now.diff(indiaTime, "minutes");
|
|
|
|
// const masterDisconnected = diffInMinutesMaster > 1 ? [{
|
|
// hardwareId: connected_to,
|
|
// masterName: masterSensor.tankName || "Unknown Master",
|
|
// connected_status: "disconnected",
|
|
// last_seen_minutes_ago: diffInMinutesMaster
|
|
// }] : [];
|
|
|
|
// const connectedSlaves = sensors.filter(sensor =>
|
|
// sensor.connected_to?.trim() === connected_to.trim() &&
|
|
// sensor.type === "slave"
|
|
// );
|
|
|
|
// const disconnectedSlaves = [];
|
|
// const slaveStatusList = [];
|
|
|
|
// for (const slave of connectedSlaves) {
|
|
// const slaveId = slave.hardwareId?.trim();
|
|
// const matchedTank = latestMasterRecord.tanks.find(tank => tank.tankhardwareId === slaveId);
|
|
|
|
// let isDisconnected = true;
|
|
// let loraDiffInMinutes = null;
|
|
|
|
// if (matchedTank && matchedTank.date) {
|
|
// const tankTime = moment.tz(matchedTank.date, "Asia/Kolkata");
|
|
// loraDiffInMinutes = now.diff(tankTime, "minutes");
|
|
|
|
// if (loraDiffInMinutes <= 1) {
|
|
// isDisconnected = false;
|
|
// }
|
|
// }
|
|
|
|
// const slaveInfo = {
|
|
// hardwareId: connected_to,
|
|
// slaveHardwareId: slaveId,
|
|
// slaveName: slave.tankName || "Unknown Slave",
|
|
// connected_status: isDisconnected ? "disconnected" : "connected",
|
|
// last_seen_minutes_ago: loraDiffInMinutes
|
|
// };
|
|
|
|
// slaveStatusList.push(slaveInfo);
|
|
|
|
// if (isDisconnected) {
|
|
// disconnectedSlaves.push(slaveInfo);
|
|
// }
|
|
// }
|
|
|
|
// const issuesToAdd = [];
|
|
|
|
// if (masterDisconnected.length > 0) {
|
|
// const existingGsmIssue = await Support.findOne({
|
|
// "issues.hardwareId": connected_to,
|
|
// "issues.type": "GSM Disconnected"
|
|
// });
|
|
|
|
// if (!existingGsmIssue) {
|
|
// issuesToAdd.push({
|
|
// type: "GSM Disconnected",
|
|
// hardwareId: connected_to,
|
|
// message: `Master GSM disconnected - ${connected_to}`
|
|
// });
|
|
// }
|
|
// }
|
|
|
|
// const newHardwareIds = [];
|
|
// const newSlaveNames = [];
|
|
|
|
// for (const slave of disconnectedSlaves) {
|
|
// const existingSlaveIssue = await Support.findOne({
|
|
// "issues.hardwareIds": slave.slaveHardwareId,
|
|
// "issues.masterHardwareId": connected_to,
|
|
// "issues.type": "LoRa Disconnected"
|
|
// });
|
|
|
|
// if (!existingSlaveIssue) {
|
|
// newHardwareIds.push(slave.slaveHardwareId);
|
|
// newSlaveNames.push(slave.slaveName);
|
|
// }
|
|
// }
|
|
|
|
// if (newHardwareIds.length > 0) {
|
|
// issuesToAdd.push({
|
|
// type: "LoRa Disconnected",
|
|
// masterHardwareId: connected_to,
|
|
// hardwareIds: newHardwareIds,
|
|
// slaveNames: newSlaveNames,
|
|
// message: `Slaves LoRa disconnected under master ${connected_to}`
|
|
// });
|
|
// }
|
|
|
|
// if (issuesToAdd.length > 0) {
|
|
// const supportRecord = await Support.findOne({ supportId: "AWHYSU64" });
|
|
|
|
// if (supportRecord) {
|
|
// await Support.findOneAndUpdate(
|
|
// { _id: supportRecord._id },
|
|
// {
|
|
// $push: {
|
|
// issues: { $each: issuesToAdd }
|
|
// },
|
|
// updatedAt: new Date()
|
|
// },
|
|
// { new: true }
|
|
// );
|
|
// } else {
|
|
// console.error("Support record not found for supportId AWHYSU64");
|
|
// }
|
|
// }
|
|
|
|
// return reply.send({
|
|
// status_code: 200,
|
|
// message: "Checked connection and updated support if needed.",
|
|
// masterDisconnected,
|
|
// disconnectedSlaves,
|
|
// connectedSlaves: slaveStatusList
|
|
// });
|
|
|
|
// } catch (error) {
|
|
// console.error("Error raising ticket:", error);
|
|
// return reply.code(500).send({ error: "Internal server error" });
|
|
// }
|
|
// };
|
|
|
|
|
|
|
|
exports.raiseATicketBuildingDetails = async (req, reply) => {
|
|
try {
|
|
const { customerId, connected_to, installationId } = req.params;
|
|
|
|
if (!customerId || !connected_to || !installationId) {
|
|
return reply.code(400).send({ error: "customerId, connected_to, and installationId are required" });
|
|
}
|
|
|
|
const customer = await User.findOne({ customerId, installationId }).lean();
|
|
if (!customer) {
|
|
return reply.code(404).send({ message: "Customer not found." });
|
|
}
|
|
|
|
const sensors = await Insensors.find({ customerId });
|
|
if (!sensors.length) {
|
|
return reply.code(404).send({ message: "No sensors found for this customer." });
|
|
}
|
|
|
|
const masterSensor = sensors.find(s => (s.hardwareId?.trim() === connected_to.trim()));
|
|
if (!masterSensor) {
|
|
return reply.code(404).send({ message: "Master hardwareId not found." });
|
|
}
|
|
|
|
const latestMasterRecord = await IotData.findOne({ hardwareId: connected_to }).sort({ date: -1 }).lean();
|
|
if (!latestMasterRecord) {
|
|
return reply.code(404).send({ message: "No IoT data found for this hardwareId." });
|
|
}
|
|
|
|
const indiaTime = moment.tz(latestMasterRecord.date, "Asia/Kolkata");
|
|
const now = moment.tz("Asia/Kolkata");
|
|
const formattedNow = now.format("YYYY-MM-DD HH:mm:ss");
|
|
|
|
const diffInMinutesMaster = now.diff(indiaTime, "minutes");
|
|
|
|
if (diffInMinutesMaster > 1) {
|
|
await Insensors.updateOne(
|
|
{ hardwareId: connected_to },
|
|
{ $set: { lastTicketRaisedAt: formattedNow } }
|
|
);
|
|
}
|
|
|
|
const connectedSlaves = sensors.filter(sensor => sensor.connected_to?.trim() === connected_to.trim());
|
|
|
|
// ✅ Check if any slave is disconnected
|
|
const disconnectedSlave = connectedSlaves.find(slave => slave.connected_status === "disconnected");
|
|
if (disconnectedSlave) {
|
|
return reply.code(400).send({
|
|
error: `Slave device ${disconnectedSlave.hardwareId} is disconnected. Cannot raise ticket.`
|
|
});
|
|
}
|
|
|
|
for (const slave of connectedSlaves) {
|
|
const slaveId = slave.hardwareId?.trim();
|
|
const matchedTank = latestMasterRecord.tanks.find(tank => tank.tankhardwareId === slaveId);
|
|
|
|
if (matchedTank && matchedTank.date) {
|
|
const tankTime = moment.tz(matchedTank.date, "Asia/Kolkata");
|
|
const loraDiffInMinutes = now.diff(tankTime, "minutes");
|
|
|
|
if (loraDiffInMinutes > 1) {
|
|
await Insensors.updateOne(
|
|
{ hardwareId: slaveId },
|
|
{ $set: { lastTicketRaisedAt: formattedNow } }
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
await Support.updateOne(
|
|
{ supportId: "AWHYSU64" },
|
|
{ $set: { lastTicketRaisedAt: formattedNow } }
|
|
);
|
|
|
|
// Fetch updated values
|
|
const updatedMasterSensor = await Insensors.findOne({ hardwareId: connected_to }).lean();
|
|
const updatedSupport = await Support.findOne({ supportId: "AWHYSU64" }).lean();
|
|
|
|
return reply.send({
|
|
status_code: 200,
|
|
customer,
|
|
lastTicketRaisedAt: {
|
|
masterSensor: updatedMasterSensor?.lastTicketRaisedAt || null,
|
|
support: updatedSupport?.lastTicketRaisedAt || null
|
|
}
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error("Error raising ticket:", error);
|
|
return reply.code(500).send({ error: "Internal server error" });
|
|
}
|
|
};
|
|
|
|
|
|
|
|
exports.raiseATicketSlave = async (req, reply) => {
|
|
try {
|
|
const { customerId, connected_to, tankHardwareId } = req.params;
|
|
|
|
if (!customerId || !connected_to) {
|
|
return reply.code(400).send({ error: "customerId and connected_to are required" });
|
|
}
|
|
|
|
const sensors = await Insensors.find({ customerId });
|
|
|
|
if (!sensors.length) {
|
|
return reply.code(404).send({ message: "No sensors found for this customer." });
|
|
}
|
|
|
|
const masterSensor = sensors.find(s => s.hardwareId?.trim() === connected_to.trim());
|
|
|
|
if (!masterSensor) {
|
|
return reply.code(404).send({ message: "Master hardwareId not found." });
|
|
}
|
|
|
|
const latestMasterRecord = await IotData.findOne({ hardwareId: connected_to }).sort({ date: -1 }).lean();
|
|
|
|
if (!latestMasterRecord) {
|
|
return reply.code(404).send({ message: "No IoT data found for this hardwareId." });
|
|
}
|
|
|
|
const now = moment.tz("Asia/Kolkata");
|
|
const masterTime = moment.tz(latestMasterRecord.date, "Asia/Kolkata");
|
|
const masterDiff = now.diff(masterTime, "minutes");
|
|
|
|
const masterDisconnected = masterDiff > 1 ? [{
|
|
hardwareId: connected_to,
|
|
masterName: masterSensor.tankName || "Unknown Master",
|
|
}] : [];
|
|
|
|
let connectedSlaves = sensors.filter(sensor =>
|
|
sensor.connected_to?.trim() === connected_to.trim() &&
|
|
sensor.type === "slave"
|
|
);
|
|
|
|
if (tankHardwareId) {
|
|
connectedSlaves = connectedSlaves.filter(s => s.hardwareId?.trim() === tankHardwareId.trim());
|
|
}
|
|
|
|
const disconnectedSlaves = [];
|
|
|
|
for (const slave of connectedSlaves) {
|
|
const slaveId = slave.hardwareId?.trim();
|
|
const matchedTank = latestMasterRecord.tanks.find(tank => tank.tankhardwareId === slaveId);
|
|
|
|
if (matchedTank?.date) {
|
|
const tankTime = moment.tz(matchedTank.date, "Asia/Kolkata");
|
|
const loraDiff = now.diff(tankTime, "minutes");
|
|
|
|
if (loraDiff > 1) {
|
|
disconnectedSlaves.push({
|
|
hardwareId: connected_to,
|
|
slaveHardwareId: slaveId,
|
|
slaveName: slave.tankName || "Unknown Slave"
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
const issuesToAdd = [];
|
|
|
|
// Check for existing GSM issue
|
|
if (masterDisconnected.length > 0) {
|
|
const existingGsmIssue = await Support.findOne({
|
|
"issues.hardwareId": connected_to,
|
|
"issues.type": "GSM Disconnected"
|
|
});
|
|
|
|
if (!existingGsmIssue) {
|
|
issuesToAdd.push({
|
|
type: "GSM Disconnected",
|
|
hardwareId: connected_to,
|
|
message: `Master GSM disconnected - ${connected_to}`
|
|
});
|
|
}
|
|
}
|
|
|
|
// Check for new LoRa issues
|
|
const newSlaveHardwareIds = [];
|
|
const newSlaveNames = [];
|
|
|
|
for (const slave of disconnectedSlaves) {
|
|
const existingSlaveIssue = await Support.findOne({
|
|
"issues.hardwareIds": slave.slaveHardwareId,
|
|
"issues.masterHardwareId": connected_to,
|
|
"issues.type": "LoRa Disconnected"
|
|
});
|
|
|
|
if (!existingSlaveIssue) {
|
|
newSlaveHardwareIds.push(slave.slaveHardwareId);
|
|
newSlaveNames.push(slave.slaveName);
|
|
}
|
|
}
|
|
|
|
if (newSlaveHardwareIds.length > 0) {
|
|
issuesToAdd.push({
|
|
type: "LoRa Disconnected",
|
|
masterHardwareId: connected_to,
|
|
hardwareIds: newSlaveHardwareIds,
|
|
slaveNames: newSlaveNames,
|
|
message: `Slaves LoRa disconnected under master ${connected_to}`
|
|
});
|
|
}
|
|
|
|
if (issuesToAdd.length > 0) {
|
|
const supportRecord = await Support.findOne({ supportId: "AWHYSU64" });
|
|
|
|
if (supportRecord) {
|
|
await Support.findOneAndUpdate(
|
|
{ _id: supportRecord._id },
|
|
{
|
|
$push: {
|
|
issues: { $each: issuesToAdd }
|
|
},
|
|
updatedAt: new Date()
|
|
},
|
|
{ new: true }
|
|
);
|
|
} else {
|
|
console.error("Support record not found for supportId AWHYSU64");
|
|
}
|
|
}
|
|
|
|
return reply.send({
|
|
status_code: 200,
|
|
message: "Checked connection and updated support if needed.",
|
|
masterDisconnected,
|
|
disconnectedSlaves
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error("Error raising ticket (slave):", error);
|
|
return reply.code(500).send({ error: "Internal server error" });
|
|
}
|
|
};
|
|
|
|
|
|
exports.getDisconnectedIssuesBySupportId = async (req, reply) => {
|
|
try {
|
|
const { supportId } = req.params;
|
|
|
|
if (!supportId) {
|
|
return reply.code(400).send({ error: "supportId is required" });
|
|
}
|
|
|
|
const supportRecord = await Support.findOne({ supportId }).lean();
|
|
if (!supportRecord) {
|
|
return reply.code(404).send({ message: "No support record found for this supportId" });
|
|
}
|
|
|
|
const allIssues = supportRecord.issues || [];
|
|
const hardwareSet = new Set();
|
|
|
|
// Collect all hardware IDs from issues
|
|
for (const issue of allIssues) {
|
|
if (issue.hardwareId) hardwareSet.add(issue.hardwareId); // GSM
|
|
if (Array.isArray(issue.hardwareIds)) {
|
|
issue.hardwareIds.forEach(id => hardwareSet.add(id)); // LoRa slaves
|
|
}
|
|
if (issue.masterHardwareId) hardwareSet.add(issue.masterHardwareId); // LoRa master
|
|
}
|
|
|
|
const hardwareIds = [...hardwareSet];
|
|
const sensors = await Insensors.find({ hardwareId: { $in: hardwareIds } }).lean();
|
|
|
|
// Map sensors by hardwareId
|
|
const sensorMap = {};
|
|
for (const sensor of sensors) {
|
|
sensorMap[sensor.hardwareId] = sensor;
|
|
}
|
|
|
|
const masterMap = {};
|
|
|
|
for (const issue of allIssues) {
|
|
// GSM Disconnected
|
|
if (issue.type === "GSM Disconnected" && issue.hardwareId) {
|
|
const sensor = sensorMap[issue.hardwareId];
|
|
if (sensor && sensor.type === "master") {
|
|
masterMap[sensor.hardwareId] = {
|
|
hardwareId: sensor.hardwareId,
|
|
masterName: sensor.masterName || null,
|
|
location: sensor.location || "",
|
|
type: "master",
|
|
connected_status: sensor.connected_status,
|
|
gsm_last_check_time: sensor.gsm_last_check_time,
|
|
gsm_last_disconnect_time: sensor.gsm_last_disconnect_time,
|
|
connected_gsm_date: sensor.connected_gsm_date,
|
|
connected_gsm_time: sensor.connected_gsm_time,
|
|
connected_lora_date: sensor.connected_lora_date,
|
|
connected_lora_time: sensor.connected_lora_time,
|
|
support_gsm_last_check_time: sensor.support_gsm_last_check_time,
|
|
connected_slave_count: 0,
|
|
connected_slaves: []
|
|
};
|
|
}
|
|
}
|
|
|
|
// LoRa Disconnected
|
|
if (issue.type === "LoRa Disconnected" && issue.masterHardwareId) {
|
|
const masterSensor = sensorMap[issue.masterHardwareId];
|
|
if (!masterSensor || masterSensor.type !== "master") continue;
|
|
|
|
if (!masterMap[masterSensor.hardwareId]) {
|
|
masterMap[masterSensor.hardwareId] = {
|
|
hardwareId: masterSensor.hardwareId,
|
|
masterName: masterSensor.masterName || null,
|
|
location: masterSensor.location || "",
|
|
type: "master",
|
|
connected_status: masterSensor.connected_status,
|
|
gsm_last_check_time: masterSensor.gsm_last_check_time,
|
|
gsm_last_disconnect_time: masterSensor.gsm_last_disconnect_time,
|
|
connected_gsm_date: masterSensor.connected_gsm_date,
|
|
connected_gsm_time: masterSensor.connected_gsm_time,
|
|
connected_lora_date: masterSensor.connected_lora_date,
|
|
connected_lora_time: masterSensor.connected_lora_time,
|
|
support_lora_last_check_time: masterSensor.support_lora_last_check_time,
|
|
connected_slave_count: 0,
|
|
connected_slaves: []
|
|
};
|
|
}
|
|
|
|
const master = masterMap[masterSensor.hardwareId];
|
|
|
|
// First try from issue.hardwareIds
|
|
let slaveIds = Array.isArray(issue.hardwareIds) ? issue.hardwareIds : [];
|
|
|
|
// If empty, fallback to slaves connected to this master
|
|
if (slaveIds.length === 0) {
|
|
const fallbackSlaves = await Insensors.find({ connected_to: masterSensor.hardwareId, type: "slave" }).lean();
|
|
for (const slaveSensor of fallbackSlaves) {
|
|
master.connected_slaves.push({
|
|
hardwareId: slaveSensor.hardwareId,
|
|
tankName: slaveSensor.tankName || "",
|
|
location: slaveSensor.location || "",
|
|
connected_status: slaveSensor.connected_status,
|
|
connected_lora_time: slaveSensor.connected_lora_time,
|
|
connected_lora_date: slaveSensor.connected_lora_date,
|
|
lora_last_check_time: slaveSensor.lora_last_check_time,
|
|
lora_last_disconnect_time: slaveSensor.lora_last_disconnect_time,
|
|
connected_to: slaveSensor.connected_to,
|
|
masterName: master.masterName,
|
|
type: "slave",
|
|
typeOfWater: slaveSensor.typeOfWater,
|
|
tankHeight: slaveSensor.tankHeight,
|
|
support_lora_last_check_time: slaveSensor.support_lora_last_check_time,
|
|
});
|
|
master.connected_slave_count++;
|
|
}
|
|
} else {
|
|
// Populate slaves based on provided hardwareIds
|
|
for (const slaveId of slaveIds) {
|
|
const slaveSensor = sensorMap[slaveId];
|
|
if (slaveSensor && slaveSensor.type === "slave") {
|
|
master.connected_slaves.push({
|
|
hardwareId: slaveSensor.hardwareId,
|
|
tankName: slaveSensor.tankName || "",
|
|
location: slaveSensor.location || "",
|
|
connected_status: slaveSensor.connected_status,
|
|
connected_lora_time: slaveSensor.connected_lora_time,
|
|
connected_lora_date: slaveSensor.connected_lora_date,
|
|
lora_last_check_time: slaveSensor.lora_last_check_time,
|
|
lora_last_disconnect_time: slaveSensor.lora_last_disconnect_time,
|
|
connected_to: slaveSensor.connected_to,
|
|
masterName: master.masterName,
|
|
type: "slave",
|
|
typeOfWater: slaveSensor.typeOfWater,
|
|
tankHeight: slaveSensor.tankHeight,
|
|
support_lora_last_check_time: slaveSensor.support_lora_last_check_time,
|
|
});
|
|
master.connected_slave_count++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const finalResponse = Object.values(masterMap);
|
|
|
|
return reply.send({
|
|
status_code: 200,
|
|
supportId,
|
|
totalMasters: finalResponse.length,
|
|
disconnectedIssues: finalResponse
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error("Error fetching disconnected issues:", error);
|
|
return reply.code(500).send({ error: "Internal server error" });
|
|
}
|
|
};
|
|
|
|
|
|
exports.getDisconnectedCustomerDetails = async (req, reply) => {
|
|
try {
|
|
const { supportId } = req.params;
|
|
|
|
if (!supportId) {
|
|
return reply.code(400).send({ error: "supportId is required" });
|
|
}
|
|
|
|
// 1. Get the support record by supportId
|
|
const supportRecord = await Support.findOne({ supportId }).lean();
|
|
if (!supportRecord) {
|
|
return reply.code(404).send({ message: "No support record found for this supportId" });
|
|
}
|
|
|
|
// 2. Extract all hardwareIds from issues
|
|
const hardwareIds = [];
|
|
for (const issue of supportRecord.issues) {
|
|
if (issue.hardwareId) hardwareIds.push(issue.hardwareId);
|
|
if (Array.isArray(issue.hardwareIds)) hardwareIds.push(...issue.hardwareIds);
|
|
}
|
|
|
|
if (hardwareIds.length === 0) {
|
|
return reply.code(404).send({ message: "No hardware IDs found in issues" });
|
|
}
|
|
|
|
// 3. Find disconnected Insensors
|
|
const disconnectedSensors = await Insensors.find({
|
|
$or: [
|
|
// { hardwareId: { $in: hardwareIds } },
|
|
{ connected_to: { $in: hardwareIds } }
|
|
],
|
|
connected_status: "disconnected"
|
|
}).lean();
|
|
|
|
if (disconnectedSensors.length === 0) {
|
|
return reply.code(404).send({ message: "No disconnected issues found" });
|
|
}
|
|
|
|
// 4. Get all unique customerIds
|
|
const customerIds = [...new Set(disconnectedSensors.map(s => s.customerId))];
|
|
const customers = await User.find({ customerId: { $in: customerIds } }).lean();
|
|
|
|
// 5. Map unique customers
|
|
const uniqueCustomerMap = {};
|
|
for (const user of customers) {
|
|
if (!uniqueCustomerMap[user.customerId]) {
|
|
uniqueCustomerMap[user.customerId] = {
|
|
customer: {
|
|
customerId: user.customerId,
|
|
username: user.username || "",
|
|
firstName: user.profile?.firstName || "",
|
|
lastName: user.profile?.lastName || "",
|
|
phone: user.phone || user.profile?.contactNumber || "",
|
|
email: user.emails?.[0]?.email || "",
|
|
phoneVerified: user.phoneVerified || false,
|
|
address1: user.profile?.address1 || "",
|
|
address2: user.profile?.address2 || "",
|
|
city: user.profile?.city || "",
|
|
state: user.profile?.state || "",
|
|
country: user.profile?.country || "",
|
|
zip: user.profile?.zip || "",
|
|
notes: user.profile?.notes || "",
|
|
latitude: user.latitude,
|
|
longitude: user.longitude,
|
|
fcmIds: (user.fcmIds || []).filter(fcm => typeof fcm === "string" && fcm.startsWith("d")),
|
|
installationId: user.installationId || "",
|
|
notificationPreferences: {
|
|
allowNotifications: user.allowNotifications || false,
|
|
automaticStartAndStopNotify: user.automaticStartAndStopNotify || false,
|
|
manualStartAndStopNotify: user.manualStartAndStopNotify || false,
|
|
criticalLowWaterAlert: user.criticalLowWaterAlert || false,
|
|
lowWaterAlert: user.lowWaterAlert || false,
|
|
notificationPreference: user.notificationPreference || "never"
|
|
},
|
|
surveyStatus: user.survey_status || "pending",
|
|
buildingName: user.buildingName,
|
|
stripePaymentStatus: user.stripePaymentStatus || false,
|
|
stripeSubscriptionStatus: user.stripeSubscriptionStatus || false,
|
|
createdAt: user.createdAt,
|
|
updatedAt: user.updatedAt
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
const response = Object.values(uniqueCustomerMap);
|
|
|
|
return reply.send({
|
|
status_code: 200,
|
|
data: response
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error("Error fetching disconnected customer details:", error);
|
|
return reply.code(500).send({ error: "Internal server error" });
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// const bcrypt = require("bcrypt");
|
|
|
|
exports.createTeamMemberSupport = async (req, reply) => {
|
|
try {
|
|
const { supportId } = req.params;
|
|
|
|
const c_id = await generateTeamMemberId();
|
|
const support_teamMemberId = `AWTMSU${c_id}`;
|
|
|
|
const {
|
|
name,
|
|
phone,
|
|
installationTeamMemId,
|
|
password,
|
|
email,
|
|
alternativePhone,
|
|
status
|
|
} = req.body;
|
|
|
|
if (!supportId || !support_teamMemberId || !name || !phone || !password) {
|
|
return reply.code(400).send({ error: "Missing required fields" });
|
|
}
|
|
|
|
const supportRecord = await Support.findOne({ supportId });
|
|
|
|
if (!supportRecord) {
|
|
return reply.code(404).send({ error: "Support record not found" });
|
|
}
|
|
|
|
const existingMember = supportRecord.team_member?.team_member.find(
|
|
member => member.phone === phone || member.support_teamMemberId === support_teamMemberId
|
|
);
|
|
|
|
if (existingMember) {
|
|
return reply.code(400).send({ error: "Team member with this phone or ID already exists" });
|
|
}
|
|
|
|
const hashedPassword = await bcrypt.hash(password, 10);
|
|
|
|
const newTeamMember = {
|
|
support_teamMemberId,
|
|
name,
|
|
phone,
|
|
installationTeamMemId,
|
|
password: hashedPassword,
|
|
status: status || "active",
|
|
email: email || null,
|
|
alternativePhone: alternativePhone || null,
|
|
};
|
|
|
|
await Support.findOneAndUpdate(
|
|
{ supportId },
|
|
{
|
|
$push: {
|
|
"team_member.team_member": newTeamMember
|
|
},
|
|
$set: {
|
|
updatedAt: new Date()
|
|
}
|
|
},
|
|
{ new: true }
|
|
);
|
|
|
|
return reply.send({
|
|
status_code: 200,
|
|
message: "Team member added successfully",
|
|
teamMember: newTeamMember
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error("Error adding team member:", error);
|
|
return reply.code(500).send({ error: "Internal server error" });
|
|
}
|
|
};
|
|
|
|
|
|
exports.getAllTeamMembersListSupport = async (req, reply) => {
|
|
try {
|
|
const { supportId } = req.params;
|
|
|
|
const support = await Support.findOne({ supportId });
|
|
|
|
if (!support) {
|
|
return reply.code(404).send({ error: "Support record not found" });
|
|
}
|
|
|
|
const teamMembers = support.team_member?.team_member || [];
|
|
|
|
return reply.send({
|
|
status_code: 200,
|
|
message: "Team members fetched successfully",
|
|
count: teamMembers.length,
|
|
teamMembers
|
|
});
|
|
} catch (error) {
|
|
console.error("Error fetching team members:", error);
|
|
return reply.code(500).send({ error: "Internal server error" });
|
|
}
|
|
}
|
|
exports.updateTeamMemberSupport = async (req, reply) => {
|
|
try {
|
|
const { supportId, teamMemberId } = req.params;
|
|
const updateData = req.body;
|
|
|
|
const support = await Support.findOne({ supportId });
|
|
|
|
if (!support) {
|
|
return reply.code(404).send({ error: "Support record not found" });
|
|
}
|
|
|
|
const teamMembers = support.team_member?.team_member || [];
|
|
const memberIndex = teamMembers.findIndex(m => m.support_teamMemberId === teamMemberId);
|
|
|
|
if (memberIndex === -1) {
|
|
return reply.code(404).send({ error: "Team member not found" });
|
|
}
|
|
|
|
Object.assign(teamMembers[memberIndex], updateData);
|
|
|
|
await Support.updateOne(
|
|
{ supportId },
|
|
{
|
|
$set: {
|
|
"team_member.team_member": teamMembers,
|
|
updatedAt: new Date()
|
|
}
|
|
}
|
|
);
|
|
|
|
return reply.send({
|
|
status_code: 200,
|
|
message: "Team member updated successfully",
|
|
teamMember: teamMembers[memberIndex]
|
|
});
|
|
} catch (error) {
|
|
console.error("Error updating team member:", error);
|
|
return reply.code(500).send({ error: "Internal server error" });
|
|
}
|
|
};
|
|
|
|
|
|
exports.deleteTeamMemberSupport = async (req, reply)=> {
|
|
try {
|
|
const { supportId, teamMemberId } = req.params;
|
|
|
|
const support = await Support.findOne({ supportId });
|
|
|
|
if (!support) {
|
|
return reply.code(404).send({ error: "Support record not found" });
|
|
}
|
|
|
|
const originalLength = support.team_member?.team_member.length || 0;
|
|
|
|
const updatedTeam = support.team_member?.team_member.filter(
|
|
m => m.support_teamMemberId !== teamMemberId
|
|
);
|
|
|
|
if (originalLength === updatedTeam.length) {
|
|
return reply.code(404).send({ error: "Team member not found" });
|
|
}
|
|
|
|
await Support.updateOne(
|
|
{ supportId },
|
|
{
|
|
$set: {
|
|
"team_member.team_member": updatedTeam,
|
|
updatedAt: new Date()
|
|
}
|
|
}
|
|
);
|
|
|
|
return reply.send({
|
|
status_code: 200,
|
|
message: "Team member deleted successfully"
|
|
});
|
|
} catch (error) {
|
|
console.error("Error deleting team member:", error);
|
|
return reply.code(500).send({ error: "Internal server error" });
|
|
}
|
|
}
|
|
|