05 - Implementation Guide
Technical implementation details for CAD integration
Prerequisites
Dependencies
# 3D rendering
npm install three @react-three/fiber @react-three/drei
# Already installed (verify)
# - React 18+
# - TypeScript
# - Tailwind CSS
# - shadcn/ui components
Existing Code to Reuse
// ML API hooks (already exist)
import { useReusabilityPrediction, useTreatmentRecommendation } from "@/lib/ml-api/hooks";
// Types (already exist)
import { TreatmentInput, WastewaterInput, TreatmentResult, ReusabilityResult } from "@/lib/ml-api/types";
// UI components (already exist)
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
File Structure
src/
├── app/(core)/cad-integration/
│ ├── page.tsx # Main page component
│ ├── layout.tsx # Optional layout
│ └── components/
│ ├── WTPModel3D.tsx # 3D water treatment plant
│ ├── ComponentInfoPanel.tsx # Selected component info
│ ├── ParameterForm.tsx # Input form
│ ├── ModelSelector.tsx # Model tabs
│ ├── ResultsPanel.tsx # Results display
│ └── ComponentTooltip.tsx # Hover tooltip
│
├── lib/cad-integration/
│ ├── types.ts # Type definitions
│ ├── components-data.ts # Component metadata store
│ ├── metadata-mapper.ts # Metadata to input mapping
│ └── constants.ts # Constants and configs
│
└── hooks/
└── useCADIntegration.ts # State management hook
Step 1: Type Definitions
lib/cad-integration/types.ts
import { TreatmentInput, WastewaterInput } from "@/lib/ml-api/types";
// Component types in the WTP
export type WTPComponentType =
| "inlet"
| "screening"
| "grit_chamber"
| "primary_clarifier"
| "aeration_tank"
| "secondary_clarifier"
| "filtration"
| "disinfection"
| "outlet"
| "sludge_thickener"
| "sludge_digester";
// Water quality parameters at a specific stage
export interface WaterQualityMetadata {
pH: number;
TSS: number;
Turbidity: number;
BOD: number;
COD: number;
NH4_N: number;
Total_Nitrogen: number;
Phosphate: number;
Fecal_Coliform: number;
Oil_Grease: number;
TDS: number;
Heavy_Metals: number;
}
// Operational parameters
export interface OperationalMetadata {
flow_rate: number;
aeration_rate?: number;
chemical_dose?: number;
sludge_recycle_rate?: number;
retention_time?: number;
temperature?: number;
}
// Complete component metadata
export interface ComponentMetadata {
id: string;
name: string;
type: WTPComponentType;
position: [number, number, number];
rotation: [number, number, number];
scale: [number, number, number];
color: string;
highlightColor: string;
description: string;
processDescription: string;
waterQuality: WaterQualityMetadata;
operational: OperationalMetadata;
}
// Model selection
export type ModelType = "prediction" | "treatment" | "twin-engine";
// CAD integration state
export interface CADIntegrationState {
selectedComponent: string | null;
hoveredComponent: string | null;
selectedModel: ModelType;
treatmentInput: TreatmentInput;
reusabilityInput: WastewaterInput;
isLoading: boolean;
error: string | null;
}
// Pipe connection
export interface PipeConnection {
from: string;
to: string;
type: "main" | "sludge" | "return";
color: string;
}
Step 2: Component Metadata Store
lib/cad-integration/components-data.ts
import { ComponentMetadata, PipeConnection } from "./types";
export const componentMetadataStore: Record<string, ComponentMetadata> = {
inlet: {
id: "inlet",
name: "Raw Water Inlet",
type: "inlet",
position: [-15, 0, 0],
rotation: [0, 0, Math.PI / 2],
scale: [1, 1, 1],
color: "#8B4513",
highlightColor: "#D2691E",
description: "Raw wastewater entry point",
processDescription: "Untreated wastewater enters the treatment plant here",
waterQuality: {
pH: 6.8,
TSS: 250,
Turbidity: 150,
BOD: 300,
COD: 600,
NH4_N: 35,
Total_Nitrogen: 50,
Phosphate: 12,
Fecal_Coliform: 100000,
Oil_Grease: 50,
TDS: 800,
Heavy_Metals: 1.5,
},
operational: {
flow_rate: 1000,
temperature: 25,
},
},
screening: {
id: "screening",
name: "Screening Unit",
type: "screening",
position: [-11, 0, 0],
rotation: [0, 0, 0],
scale: [1, 1, 1],
color: "#696969",
highlightColor: "#A9A9A9",
description: "Coarse solids removal",
processDescription: "Removes large debris, rags, and floating materials",
waterQuality: {
pH: 6.8,
TSS: 230,
Turbidity: 140,
BOD: 290,
COD: 580,
NH4_N: 35,
Total_Nitrogen: 50,
Phosphate: 12,
Fecal_Coliform: 95000,
Oil_Grease: 45,
TDS: 790,
Heavy_Metals: 1.4,
},
operational: {
flow_rate: 1000,
temperature: 25,
},
},
grit_chamber: {
id: "grit_chamber",
name: "Grit Chamber",
type: "grit_chamber",
position: [-6, 0, 0],
rotation: [0, 0, 0],
scale: [1.5, 1, 1],
color: "#808080",
highlightColor: "#C0C0C0",
description: "Sand and grit removal",
processDescription: "Removes sand, gravel, and heavy inorganic particles",
waterQuality: {
pH: 6.9,
TSS: 200,
Turbidity: 120,
BOD: 280,
COD: 560,
NH4_N: 34,
Total_Nitrogen: 48,
Phosphate: 11,
Fecal_Coliform: 90000,
Oil_Grease: 40,
TDS: 780,
Heavy_Metals: 1.2,
},
operational: {
flow_rate: 1000,
retention_time: 0.5,
temperature: 25,
},
},
primary_clarifier: {
id: "primary_clarifier",
name: "Primary Clarifier",
type: "primary_clarifier",
position: [0, 0, 0],
rotation: [0, 0, 0],
scale: [2, 1.5, 2],
color: "#4682B4",
highlightColor: "#87CEEB",
description: "Primary sedimentation tank",
processDescription: "Settles suspended solids through gravity separation",
waterQuality: {
pH: 7.0,
TSS: 120,
Turbidity: 80,
BOD: 200,
COD: 400,
NH4_N: 32,
Total_Nitrogen: 45,
Phosphate: 10,
Fecal_Coliform: 70000,
Oil_Grease: 25,
TDS: 750,
Heavy_Metals: 1.0,
},
operational: {
flow_rate: 1000,
retention_time: 2,
sludge_recycle_rate: 0,
temperature: 24,
},
},
aeration_tank: {
id: "aeration_tank",
name: "Aeration Tank",
type: "aeration_tank",
position: [7, 0, 0],
rotation: [0, 0, 0],
scale: [3, 1.5, 2],
color: "#20B2AA",
highlightColor: "#48D1CC",
description: "Biological treatment zone",
processDescription: "Aerobic bacteria break down organic matter",
waterQuality: {
pH: 7.2,
TSS: 150,
Turbidity: 60,
BOD: 80,
COD: 180,
NH4_N: 15,
Total_Nitrogen: 30,
Phosphate: 6,
Fecal_Coliform: 40000,
Oil_Grease: 10,
TDS: 700,
Heavy_Metals: 0.8,
},
operational: {
flow_rate: 1000,
aeration_rate: 50,
retention_time: 6,
sludge_recycle_rate: 25,
temperature: 22,
},
},
secondary_clarifier: {
id: "secondary_clarifier",
name: "Secondary Clarifier",
type: "secondary_clarifier",
position: [14, 0, 0],
rotation: [0, 0, 0],
scale: [2, 1.5, 2],
color: "#5F9EA0",
highlightColor: "#7FFFD4",
description: "Secondary sedimentation",
processDescription: "Settles biological floc from aeration tank",
waterQuality: {
pH: 7.3,
TSS: 40,
Turbidity: 25,
BOD: 30,
COD: 80,
NH4_N: 10,
Total_Nitrogen: 20,
Phosphate: 4,
Fecal_Coliform: 20000,
Oil_Grease: 5,
TDS: 650,
Heavy_Metals: 0.5,
},
operational: {
flow_rate: 1000,
retention_time: 3,
sludge_recycle_rate: 30,
temperature: 22,
},
},
filtration: {
id: "filtration",
name: "Filtration Unit",
type: "filtration",
position: [14, 0, -6],
rotation: [0, 0, 0],
scale: [1.5, 1, 1.5],
color: "#DEB887",
highlightColor: "#F5DEB3",
description: "Tertiary filtration",
processDescription: "Sand/media filtration removes remaining particles",
waterQuality: {
pH: 7.4,
TSS: 10,
Turbidity: 5,
BOD: 15,
COD: 40,
NH4_N: 5,
Total_Nitrogen: 12,
Phosphate: 2,
Fecal_Coliform: 5000,
Oil_Grease: 2,
TDS: 600,
Heavy_Metals: 0.3,
},
operational: {
flow_rate: 1000,
retention_time: 0.5,
temperature: 21,
},
},
disinfection: {
id: "disinfection",
name: "Disinfection Unit",
type: "disinfection",
position: [7, 0, -6],
rotation: [0, 0, 0],
scale: [1, 1, 1],
color: "#FFD700",
highlightColor: "#FFFF00",
description: "Chlorination/UV treatment",
processDescription: "Kills pathogens using chlorine or UV light",
waterQuality: {
pH: 7.5,
TSS: 8,
Turbidity: 3,
BOD: 10,
COD: 30,
NH4_N: 3,
Total_Nitrogen: 10,
Phosphate: 1.5,
Fecal_Coliform: 100,
Oil_Grease: 1,
TDS: 580,
Heavy_Metals: 0.2,
},
operational: {
flow_rate: 1000,
chemical_dose: 5,
retention_time: 0.5,
temperature: 21,
},
},
outlet: {
id: "outlet",
name: "Treated Water Outlet",
type: "outlet",
position: [0, 0, -6],
rotation: [0, 0, Math.PI / 2],
scale: [1, 1, 1],
color: "#00CED1",
highlightColor: "#00FFFF",
description: "Final effluent discharge",
processDescription: "Treated water ready for discharge or reuse",
waterQuality: {
pH: 7.5,
TSS: 5,
Turbidity: 2,
BOD: 8,
COD: 25,
NH4_N: 2,
Total_Nitrogen: 8,
Phosphate: 1,
Fecal_Coliform: 50,
Oil_Grease: 0.5,
TDS: 550,
Heavy_Metals: 0.1,
},
operational: {
flow_rate: 950,
temperature: 20,
},
},
};
export const pipeConnections: PipeConnection[] = [
{ from: "inlet", to: "screening", type: "main", color: "#8B4513" },
{ from: "screening", to: "grit_chamber", type: "main", color: "#8B4513" },
{ from: "grit_chamber", to: "primary_clarifier", type: "main", color: "#4682B4" },
{ from: "primary_clarifier", to: "aeration_tank", type: "main", color: "#4682B4" },
{ from: "aeration_tank", to: "secondary_clarifier", type: "main", color: "#20B2AA" },
{ from: "secondary_clarifier", to: "filtration", type: "main", color: "#5F9EA0" },
{ from: "filtration", to: "disinfection", type: "main", color: "#DEB887" },
{ from: "disinfection", to: "outlet", type: "main", color: "#00CED1" },
];
export const componentList = Object.values(componentMetadataStore);
Step 3: Metadata Mapper
lib/cad-integration/metadata-mapper.ts
import { TreatmentInput, WastewaterInput } from "@/lib/ml-api/types";
import { ComponentMetadata } from "./types";
/**
* Maps component metadata to TreatmentInput
*/
export function mapToTreatmentInput(metadata: ComponentMetadata): TreatmentInput {
const { waterQuality, operational } = metadata;
return {
pH: waterQuality.pH,
TSS: waterQuality.TSS,
Turbidity: waterQuality.Turbidity,
BOD: waterQuality.BOD,
COD: waterQuality.COD,
NH4_N: waterQuality.NH4_N,
Total_Nitrogen: waterQuality.Total_Nitrogen,
Phosphate: waterQuality.Phosphate,
Fecal_Coliform: waterQuality.Fecal_Coliform,
Oil_Grease: waterQuality.Oil_Grease,
TDS: waterQuality.TDS,
Heavy_Metals: waterQuality.Heavy_Metals,
flow_rate: operational.flow_rate,
};
}
/**
* Maps component metadata to WastewaterInput
*/
export function mapToWastewaterInput(metadata: ComponentMetadata): WastewaterInput {
const { waterQuality, operational } = metadata;
return {
flow_rate: operational.flow_rate,
influent_BOD: waterQuality.BOD,
influent_COD: waterQuality.COD,
influent_TSS: waterQuality.TSS,
influent_pH: waterQuality.pH,
influent_TDS: waterQuality.TDS,
aeration_rate: operational.aeration_rate,
chemical_dose: operational.chemical_dose,
sludge_recycle_rate: operational.sludge_recycle_rate,
retention_time: operational.retention_time,
temperature: operational.temperature,
};
}
/**
* Default empty TreatmentInput
*/
export const defaultTreatmentInput: TreatmentInput = {
pH: 7,
TSS: 0,
Turbidity: 0,
BOD: 0,
COD: 0,
NH4_N: 0,
Total_Nitrogen: 0,
Phosphate: 0,
Fecal_Coliform: 0,
Oil_Grease: 0,
TDS: 0,
Heavy_Metals: 0,
};
/**
* Default empty WastewaterInput
*/
export const defaultWastewaterInput: WastewaterInput = {
flow_rate: 0,
influent_BOD: 0,
influent_COD: 0,
influent_TSS: 0,
influent_pH: 7,
influent_TDS: 0,
};
Step 4: State Management Hook
hooks/useCADIntegration.ts
"use client";
import { useState, useCallback } from "react";
import { TreatmentInput, WastewaterInput, TreatmentResult, ReusabilityResult } from "@/lib/ml-api/types";
import { useReusabilityPrediction, useTreatmentRecommendation } from "@/lib/ml-api/hooks";
import { componentMetadataStore } from "@/lib/cad-integration/components-data";
import { mapToTreatmentInput, mapToWastewaterInput, defaultTreatmentInput, defaultWastewaterInput } from "@/lib/cad-integration/metadata-mapper";
import { ModelType } from "@/lib/cad-integration/types";
export function useCADIntegration() {
// Selection state
const [selectedComponent, setSelectedComponent] = useState<string | null>(null);
const [hoveredComponent, setHoveredComponent] = useState<string | null>(null);
// Model selection
const [selectedModel, setSelectedModel] = useState<ModelType>("treatment");
// Input parameters
const [treatmentInput, setTreatmentInput] = useState<TreatmentInput>(defaultTreatmentInput);
const [reusabilityInput, setReusabilityInput] = useState<WastewaterInput>(defaultWastewaterInput);
// Results
const [predictionResult, setPredictionResult] = useState<ReusabilityResult | null>(null);
const [treatmentResult, setTreatmentResult] = useState<TreatmentResult | null>(null);
// ML hooks
const { predict: predictReusability, isLoading: reusabilityLoading, error: reusabilityError, reset: resetReusability } = useReusabilityPrediction();
const { recommend: recommendTreatment, isLoading: treatmentLoading, error: treatmentError, reset: resetTreatment } = useTreatmentRecommendation();
const isLoading = reusabilityLoading || treatmentLoading;
const error = reusabilityError || treatmentError;
// Get selected component metadata
const selectedMetadata = selectedComponent ? componentMetadataStore[selectedComponent] : null;
// Handle component selection
const handleComponentSelect = useCallback((componentId: string) => {
setSelectedComponent(componentId);
}, []);
// Handle component hover
const handleComponentHover = useCallback((componentId: string | null) => {
setHoveredComponent(componentId);
}, []);
// Fetch parameters from selected component
const fetchParameters = useCallback(() => {
if (!selectedMetadata) return;
setTreatmentInput(mapToTreatmentInput(selectedMetadata));
setReusabilityInput(mapToWastewaterInput(selectedMetadata));
}, [selectedMetadata]);
// Run selected model
const runModel = useCallback(async () => {
try {
switch (selectedModel) {
case "prediction":
const reusability = await predictReusability(reusabilityInput);
setPredictionResult(reusability);
break;
case "treatment":
const treatment = await recommendTreatment(treatmentInput);
setTreatmentResult(treatment);
break;
case "twin-engine":
const [reusabilityRes, treatmentRes] = await Promise.all([predictReusability(reusabilityInput), recommendTreatment(treatmentInput)]);
setPredictionResult(reusabilityRes);
setTreatmentResult(treatmentRes);
break;
}
} catch (err) {
console.error("Model run failed:", err);
}
}, [selectedModel, treatmentInput, reusabilityInput, predictReusability, recommendTreatment]);
// Reset all state
const reset = useCallback(() => {
setSelectedComponent(null);
setTreatmentInput(defaultTreatmentInput);
setReusabilityInput(defaultWastewaterInput);
setPredictionResult(null);
setTreatmentResult(null);
resetReusability();
resetTreatment();
}, [resetReusability, resetTreatment]);
return {
// Selection
selectedComponent,
hoveredComponent,
selectedMetadata,
handleComponentSelect,
handleComponentHover,
// Model
selectedModel,
setSelectedModel,
// Input
treatmentInput,
setTreatmentInput,
reusabilityInput,
setReusabilityInput,
fetchParameters,
// Results
predictionResult,
treatmentResult,
// Actions
runModel,
reset,
// State
isLoading,
error,
};
}
Step 5: Main Page Component
app/(core)/cad-integration/page.tsx
"use client";
import { Suspense } from "react";
import dynamic from "next/dynamic";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Loader2, Download, Play, RotateCcw } from "lucide-react";
import { useCADIntegration } from "@/hooks/useCADIntegration";
import { ComponentInfoPanel } from "./components/ComponentInfoPanel";
import { ParameterForm } from "./components/ParameterForm";
import { ResultsPanel } from "./components/ResultsPanel";
import { ModelType } from "@/lib/cad-integration/types";
// Dynamic import for 3D component (no SSR)
const WTPModel3D = dynamic(() => import("./components/WTPModel3D"), {
ssr: false,
loading: () => (
<div className="flex items-center justify-center h-[400px] bg-muted rounded-lg">
<Loader2 className="w-8 h-8 animate-spin" />
</div>
),
});
export default function CADIntegrationPage() {
const {
selectedComponent,
hoveredComponent,
selectedMetadata,
handleComponentSelect,
handleComponentHover,
selectedModel,
setSelectedModel,
treatmentInput,
setTreatmentInput,
reusabilityInput,
setReusabilityInput,
fetchParameters,
predictionResult,
treatmentResult,
runModel,
reset,
isLoading,
error,
} = useCADIntegration();
const hasResults = predictionResult || treatmentResult;
return (
<div className="container mx-auto py-6 space-y-6">
{/* Header */}
<div>
<h1 className="text-3xl font-bold">CAD Integration</h1>
<p className="text-muted-foreground">Interactive Water Treatment Plant Model with ML Predictions</p>
</div>
{/* Main Content */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* 3D Viewport */}
<div className="lg:col-span-2">
<Card>
<CardHeader>
<CardTitle>Water Treatment Plant</CardTitle>
<CardDescription>Click on a component to select it and view its parameters</CardDescription>
</CardHeader>
<CardContent>
<div className="h-[400px] rounded-lg overflow-hidden">
<Suspense fallback={<div className="h-full bg-muted animate-pulse" />}>
<WTPModel3D
selectedComponent={selectedComponent}
hoveredComponent={hoveredComponent}
onComponentSelect={handleComponentSelect}
onComponentHover={handleComponentHover}
/>
</Suspense>
</div>
</CardContent>
</Card>
</div>
{/* Component Info Panel */}
<div>
<ComponentInfoPanel metadata={selectedMetadata} onFetch={fetchParameters} />
</div>
</div>
{/* Model Selection */}
<Card>
<CardHeader>
<CardTitle>Select Model</CardTitle>
<CardDescription>Choose which ML model to run on the fetched parameters</CardDescription>
</CardHeader>
<CardContent>
<Tabs value={selectedModel} onValueChange={(v) => setSelectedModel(v as ModelType)}>
<TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="prediction">Prediction Model</TabsTrigger>
<TabsTrigger value="treatment">Treatment Model</TabsTrigger>
<TabsTrigger value="twin-engine">Twin Engine</TabsTrigger>
</TabsList>
<TabsContent value="prediction" className="mt-4">
<p className="text-sm text-muted-foreground">
Predicts the reusability class of water based on quality parameters. Output includes confidence score and CPCB compliance status.
</p>
</TabsContent>
<TabsContent value="treatment" className="mt-4">
<p className="text-sm text-muted-foreground">
Recommends treatment stage (Primary, Secondary, Tertiary, Advanced) with detailed steps and chemical suggestions.
</p>
</TabsContent>
<TabsContent value="twin-engine" className="mt-4">
<p className="text-sm text-muted-foreground">
Runs both models for comprehensive analysis. Provides reusability prediction and treatment recommendations together.
</p>
</TabsContent>
</Tabs>
</CardContent>
</Card>
{/* Input Form & Results */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Parameter Form */}
<ParameterForm
selectedModel={selectedModel}
treatmentInput={treatmentInput}
reusabilityInput={reusabilityInput}
onTreatmentChange={setTreatmentInput}
onReusabilityChange={setReusabilityInput}
/>
{/* Results Panel */}
<ResultsPanel
selectedModel={selectedModel}
predictionResult={predictionResult}
treatmentResult={treatmentResult}
isLoading={isLoading}
error={error}
/>
</div>
{/* Action Buttons */}
<div className="flex gap-4">
<Button onClick={runModel} disabled={isLoading || !selectedComponent} className="flex-1">
{isLoading ? (
<>
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
Running...
</>
) : (
<>
<Play className="w-4 h-4 mr-2" />
Run {selectedModel === "twin-engine" ? "Twin Engine" : selectedModel === "prediction" ? "Prediction" : "Treatment"} Model
</>
)}
</Button>
<Button variant="outline" onClick={reset}>
<RotateCcw className="w-4 h-4 mr-2" />
Reset
</Button>
</div>
{/* Error Display */}
{error && (
<Card className="border-destructive">
<CardContent className="pt-6">
<p className="text-destructive">{error}</p>
</CardContent>
</Card>
)}
</div>
);
}
Next Steps After Documentation
- Review this documentation with the team
- Create the file structure as outlined
- Implement components one by one
- Test with sample component selections
- Integrate with existing ML API
Estimated Implementation Time
| Task | Time |
|---|---|
| Type definitions | 30 min |
| Component metadata | 1 hour |
| 3D WTP model | 2 hours |
| State management hook | 1 hour |
| UI components | 2 hours |
| Integration & testing | 2 hours |
| Total | 8-9 hours |
Document Version: 1.0
Last Updated: December 2024