demeter
Autonomous Hydroponic Intelligencegit clone https://github.com/maydayv7/demeter
backend/node_server/controllers/cropController.js (4176B)
1const CropStateSchema = require("../schema/cropSchema");
2
3// HELPERS
4
5/** Generates a fake sensor hardware ID */
6function fakeSensorId(prefix) {
7 const chars = "ABCDEF0123456789";
8 const rand4 = () =>
9 Array.from(
10 { length: 4 },
11 () => chars[Math.floor(Math.random() * chars.length)],
12 ).join("");
13 return `${prefix}-SEN-${rand4()}`;
14}
15
16/** Auto cycle_duration_hours per crop type */
17const CYCLE_HOURS = {
18 lettuce: 1,
19 basil: 1,
20 tomato: 2,
21 strawberry: 2,
22};
23
24function cycleDurationForCrop(cropName) {
25 if (!cropName) return 1;
26 return CYCLE_HOURS[cropName.toLowerCase()] || 1;
27}
28
29// CONTROLLERS
30
31const createCrop = async (req, res) => {
32 try {
33 const { crop_id, crop, stage, location, notes, image_url, ...rest } =
34 req.body;
35
36 if (!crop_id) {
37 return res.status(400).json({ error: "crop_id is required" });
38 }
39
40 const existingCrop = await CropStateSchema.findOne({ crop_id });
41 if (existingCrop) {
42 return res
43 .status(409)
44 .json({ error: "Crop with this ID already exists" });
45 }
46
47 const newCrop = new CropStateSchema({
48 crop_id,
49 crop,
50 stage: stage || "seedling",
51 sequence_number: 0,
52 total_crop_lifetime_days: 0,
53 planted_at: new Date(),
54 last_updated: new Date(),
55
56 // Auto-set initial sensor arrays
57 sensors: {
58 pH: [6.0],
59 EC: [1.5],
60 temp: [24.0],
61 humidity: [60.0],
62 },
63
64 // Fallback to auto-generated fake sensor IDs
65 sensor_ids: {
66 ph_sensor: req.body.sensor_ids?.ph_sensor || fakeSensorId("PH"),
67 ec_sensor: req.body.sensor_ids?.ec_sensor || fakeSensorId("EC"),
68 temp_sensor: req.body.sensor_ids?.temp_sensor || fakeSensorId("TMP"),
69 humidity_sensor:
70 req.body.sensor_ids?.humidity_sensor || fakeSensorId("HUM"),
71 },
72
73 // Auto-set cycle_duration_hours based on crop type
74 cycle_duration_hours: cycleDurationForCrop(crop),
75
76 location: location || "",
77 notes: notes || "",
78 image_url: image_url || "",
79
80 ...rest,
81 });
82
83 const savedCrop = await newCrop.save();
84
85 res.status(201).json({
86 message: "Crop created successfully",
87 data: savedCrop,
88 });
89 } catch (error) {
90 res.status(500).json({ error: error.message });
91 }
92};
93
94const getAllCrops = async (req, res) => {
95 console.log("getAllCrops endpoint called");
96 try {
97 const crops = await CropStateSchema.find();
98 console.log(`Retrieved ${crops.length} crops from the database.`);
99 res.status(200).json({
100 message: "Crops retrieved successfully",
101 data: crops,
102 });
103 } catch (error) {
104 res.status(500).json({ error: error.message });
105 } finally {
106 console.log("getAllCrops endpoint was called");
107 }
108};
109
110const getCropById = async (req, res) => {
111 try {
112 const { cropId } = req.params;
113 const crop = await CropStateSchema.findOne({ crop_id: cropId });
114 if (!crop) {
115 return res.status(404).json({ error: "Crop not found" });
116 }
117 res.status(200).json({ data: crop });
118 } catch (error) {
119 res.status(500).json({ error: error.message });
120 }
121};
122
123const updateCrop = async (req, res) => {
124 try {
125 const { cropId } = req.params;
126 const updates = { ...req.body, last_updated: new Date() };
127
128 const updated = await CropStateSchema.findOneAndUpdate(
129 { crop_id: cropId },
130 { $set: updates },
131 { new: true, runValidators: true },
132 );
133
134 if (!updated) {
135 return res.status(404).json({ error: "Crop not found" });
136 }
137 res.status(200).json({ message: "Crop updated", data: updated });
138 } catch (error) {
139 res.status(500).json({ error: error.message });
140 }
141};
142
143const deleteCrop = async (req, res) => {
144 try {
145 const { cropId } = req.params;
146 const deleted = await CropStateSchema.findOneAndDelete({ crop_id: cropId });
147 if (!deleted) {
148 return res.status(404).json({ error: "Crop not found" });
149 }
150 res.status(200).json({ message: "Crop deleted", data: deleted });
151 } catch (error) {
152 res.status(500).json({ error: error.message });
153 }
154};
155
156module.exports = {
157 createCrop,
158 getAllCrops,
159 getCropById,
160 updateCrop,
161 deleteCrop,
162};