@@ -135,17 +135,36 @@ const SYSTEM_ADJUSTMENT_BASES: Record<HydroponicSystemType, Omit<SystemNutrientA
135135 * @param systemType - The type of hydroponic system in use
136136 * @param growthStage - Current growth stage of the crop
137137 * @param cropType - Identifier for the crop species or variety (e.g., 'tomato', 'lettuce')
138+ * @param customRange - Optional user-defined EC range (min, max, and optional optimal) overriding system defaults
138139 * @returns Structured EC target range with min, max, optimal values and environmental modifiers
139- * @throws {RangeError } If system type or growth stage is not supported
140+ * @throws {RangeError } If system type or growth stage is not supported or invalid custom range
140141 * @example
141142 * const range = calculateEcTargetRange(HydroponicSystemType.NFT, GrowthStage.Vegetative, 'butter_lettuce');
142143 * console.log(range.optimalEc); // 1.47 (interpolated)
143144 */
144145export function calculateEcTargetRange (
145146 systemType : HydroponicSystemType ,
146147 growthStage : GrowthStage ,
147- cropType : string
148+ cropType : string ,
149+ customRange ?: { min : number ; max : number ; optimal ?: number }
148150) : ECTargetRange {
151+ if ( customRange ) {
152+ if ( customRange . min >= customRange . max ) {
153+ throw new RangeError ( 'Custom EC range min must be less than max' ) ;
154+ }
155+
156+ const optimalValue = customRange . optimal ?? customRange . min + ( customRange . max - customRange . min ) * 0.45 ;
157+
158+ return {
159+ cropType,
160+ growthStage,
161+ minEc : asElectricalConductivity ( customRange . min ) ,
162+ maxEc : asElectricalConductivity ( customRange . max ) ,
163+ optimalEc : asElectricalConductivity ( Number ( optimalValue . toFixed ( 2 ) ) ) ,
164+ environmentalModifiers : { }
165+ } satisfies ECTargetRange ;
166+ }
167+
149168 const systemRanges = EC_BASE_RANGES [ systemType ] ;
150169 if ( ! systemRanges ) {
151170 throw new RangeError ( `Unsupported hydroponic system type: ${ String ( systemType ) } ` ) ;
@@ -197,51 +216,3 @@ export function classifyEcReading(
197216 // Check if within optimal band (±10% of optimal value)
198217 const optimalTolerance = targetRange . optimalEc * 0.1 ;
199218 if ( Math . abs ( reading - targetRange . optimalEc ) <= optimalTolerance ) return 'optimal' ;
200-
201- // Within min-max range but outside optimal band
202- return reading < targetRange . optimalEc ? 'suboptimal' : 'elevated' ;
203- }
204-
205- /**
206- * Retrieve system-specific nutrient adjustment profile modified for the given growth stage.
207- * Adjusts EC multipliers and scheduling intervals based on plant nutritional demands.
208- * @param systemType - The hydroponic system type
209- * @param growthStage - The plant growth stage affecting nutrient uptake rates
210- * @returns System nutrient adjustment parameters including EC multipliers and maintenance intervals
211- * @throws {RangeError } If system type or growth stage is invalid
212- * @example
213- * const profile = getSystemNutrientAdjustment(HydroponicSystemType.DWC, GrowthStage.Fruiting);
214- * // Returns profile with elevated EC multiplier for fruiting stage
215- */
216- export function getSystemNutrientAdjustment (
217- systemType : HydroponicSystemType ,
218- growthStage : GrowthStage
219- ) : SystemNutrientAdjustment {
220- const baseProfile = SYSTEM_ADJUSTMENT_BASES [ systemType ] ;
221- if ( ! baseProfile ) {
222- throw new RangeError ( `Invalid system type: ${ String ( systemType ) } ` ) ;
223- }
224-
225- // Stage-specific nutritional demand factors
226- const stageDemandFactors : Record < GrowthStage , number > = {
227- [ GrowthStage . Seedling ] : 0.7 ,
228- [ GrowthStage . Vegetative ] : 1.0 ,
229- [ GrowthStage . Flowering ] : 1.1 ,
230- [ GrowthStage . Fruiting ] : 1.2 ,
231- [ GrowthStage . Ripening ] : 0.9
232- } ;
233-
234- const demandFactor = stageDemandFactors [ growthStage ] ;
235- if ( demandFactor === undefined ) {
236- throw new RangeError ( `Invalid growth stage: ${ String ( growthStage ) } ` ) ;
237- }
238-
239- return {
240- systemType,
241- ecMultiplier : Number ( ( baseProfile . ecMultiplier * demandFactor ) . toFixed ( 3 ) ) ,
242- phOffset : baseProfile . phOffset ,
243- oxygenDemandFactor : baseProfile . oxygenDemandFactor ,
244- solutionChangeIntervalHours : baseProfile . solutionChangeIntervalHours ,
245- topUpFrequencyHours : baseProfile . topUpFrequencyHours
246- } satisfies SystemNutrientAdjustment ;
247- }
0 commit comments