Skip to main content

Services Documentation

Service Overview

ServiceLocationPurpose
Firebaselib/firebase.tsAuth & database initialization
ML API Clientlib/ml-api/client.tsML model predictions
Blockchain Servicelib/blockchain/service.tsOn-chain storage
Reports Servicelib/reports/service.tsFirestore CRUD
PDF Generatorslib/pdf/generators/Report PDF export

Firebase Service

Initialization

Location: src/lib/firebase.ts

import { initializeApp, getApps } from "firebase/app";
import { getAuth } from "firebase/auth";
import { getFirestore } from "firebase/firestore";

const firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
};

// Singleton initialization
const app = getApps().length === 0 ? initializeApp(firebaseConfig) : getApps()[0];
const auth = getAuth(app);
const db = getFirestore(app);

export { app, auth, db };

Usage

import { auth, db } from "@/lib/firebase";
import { signInWithEmailAndPassword } from "firebase/auth";
import { collection, addDoc } from "firebase/firestore";

// Auth
await signInWithEmailAndPassword(auth, email, password);

// Firestore
await addDoc(collection(db, "reports"), data);

ML API Client

MLAPIClient Class

Location: src/lib/ml-api/client.ts

Type-safe client for ML API communication.

class MLAPIClient {
private baseUrl: string;
private timeout: number;
private headers: Record<string, string>;

constructor(config?: MLClientConfig);

// Health & Status
async health(): Promise<HealthResponse>;
async ping(): Promise<boolean>;
async getModels(): Promise<ModelsResponse>;
async getSchema(modelId: "reusability" | "treatment"): Promise<SchemaResponse>;

// Reference Data
async getCPCBLimits(): Promise<CPCBLimitsResponse>;
async getClassInfo(): Promise<ClassInfoResponse>;

// Predictions
async predictReusability(input: WastewaterInput): Promise<ReusabilityResponse>;
async recommendTreatment(input: TreatmentInput, useCase?: TreatmentUseCase): Promise<TreatmentResponse>;
async batchPredict(request: BatchPredictionRequest): Promise<BatchResponse>;

// Reports
async generateReport(input: TreatmentInput): Promise<Blob>;
async downloadReport(input: TreatmentInput, filename?: string): Promise<void>;
}

Default Instance

// Singleton instance with default config
export const mlApi = new MLAPIClient();

// Convenience functions
export const checkHealth = () => mlApi.health();
export const predictReusability = (input) => mlApi.predictReusability(input);
export const recommendTreatment = (input, useCase?) => mlApi.recommendTreatment(input, useCase);

Custom Configuration

const customClient = new MLAPIClient({
baseUrl: "https://custom-api.example.com",
timeout: 60000,
headers: { "X-Custom-Header": "value" },
});

Error Handling

// Custom error classes
class MLAPIError extends Error {
constructor(message: string, public statusCode: number, public response?: APIErrorResponse);
}

class MLValidationError extends MLAPIError {
constructor(message: string, public details: ValidationError[]);
}

// Usage
try {
const result = await mlApi.predictReusability(input);
} catch (error) {
if (error instanceof MLValidationError) {
console.log("Validation errors:", error.details);
} else if (error instanceof MLAPIError) {
console.log("API error:", error.statusCode, error.message);
}
}

API Endpoints

EndpointMethodDescription
/healthGETAPI health status
/api/modelsGETAvailable models list
/api/schema/{model}GETModel input schema
/api/cpcb-limitsGETCPCB regulatory limits
/api/class-infoGETReusability class info
/api/predict/reusabilityPOSTReusability prediction
/api/predict/treatmentPOSTTreatment recommendation
/api/predict/batchPOSTBatch predictions
/api/report/generatePOSTGenerate PDF report
/ws/reusabilityWSReal-time reusability
/ws/treatmentWSReal-time treatment

WebSocket Service

Overview

The ML API provides WebSocket endpoints for real-time streaming predictions, ideal for SCADA system integration and continuous monitoring.

Frontend Page: src/app/(core)/websocket-monitor/page.tsx

WebSocket Endpoints

EndpointDescription
/ws/reusabilityReal-time reusability predictions
/ws/treatmentReal-time treatment recommendations

Connection

// Get WebSocket URL from environment
const wsBaseUrl = process.env.NEXT_PUBLIC_ML_API_URL?.replace("http", "ws") || "ws://localhost:8000";

// Connect to reusability endpoint
const reusabilityWs = new WebSocket(`${wsBaseUrl}/ws/reusability`);

// Connect to treatment endpoint
const treatmentWs = new WebSocket(`${wsBaseUrl}/ws/treatment`);

Message Protocol

Welcome Message (Server → Client)

Upon connection, the server sends a welcome message:

{
"type": "welcome",
"message": "Connected to reusability prediction WebSocket",
"timestamp": "2024-12-08T12:00:00Z"
}

Prediction Request (Client → Server)

// Reusability prediction request
const reusabilityMessage = {
type: "predict",
request_id: "scada-reuse-001", // Unique identifier for tracking
payload: {
flow_rate: 150,
influent_BOD: 45,
influent_COD: 120,
influent_TSS: 80,
influent_pH: 7.2,
influent_TDS: 800,
aeration_rate: 35,
chemical_dose: 12,
sludge_recycle_rate: 25,
retention_time: 8,
temperature: 28,
effluent_BOD: 15,
effluent_COD: 60,
effluent_TSS: 25,
effluent_TDS: 500,
effluent_pH: 7.0,
},
};

// Treatment prediction request
const treatmentMessage = {
type: "predict",
request_id: "scada-treat-001",
use_case: "Industrial Use", // Optional
payload: {
pH: 7.5,
TSS: 120,
Turbidity: 45,
BOD: 180,
COD: 350,
NH4_N: 25,
Total_Nitrogen: 40,
Phosphate: 8,
Fecal_Coliform: 5000,
Oil_Grease: 15,
TDS: 600,
Heavy_Metals: 0.5,
},
};

ws.send(JSON.stringify(message));

Prediction Response (Server → Client)

{
"ok": true,
"request_id": "scada-reuse-001",
"data": {
"prediction": "Class_A",
"prediction_display": "Class A",
"confidence_percent": 87.5,
"cpcb_compliance": {
"status": "PASS",
"violations": []
}
}
}

Error Response

{
"ok": false,
"request_id": "scada-reuse-001",
"error": "Invalid payload: missing required field 'pH'"
}

React Hook Example

function useWebSocket(model: "reusability" | "treatment") {
const [status, setStatus] = useState<"disconnected" | "connecting" | "connected" | "error">("disconnected");
const [lastMessage, setLastMessage] = useState<any>(null);
const wsRef = useRef<WebSocket | null>(null);

const connect = useCallback(() => {
const wsUrl = process.env.NEXT_PUBLIC_ML_API_URL?.replace("http", "ws") || "ws://localhost:8000";
const url = `${wsUrl}/ws/${model}`;

setStatus("connecting");
const ws = new WebSocket(url);

ws.onopen = () => setStatus("connected");
ws.onmessage = (event) => setLastMessage(JSON.parse(event.data));
ws.onerror = () => setStatus("error");
ws.onclose = () => setStatus("disconnected");

wsRef.current = ws;
}, [model]);

const send = useCallback((payload: any, requestId?: string) => {
if (wsRef.current?.readyState === WebSocket.OPEN) {
wsRef.current.send(
JSON.stringify({
type: "predict",
request_id: requestId || `req-${Date.now()}`,
payload,
})
);
}
}, []);

const disconnect = useCallback(() => {
wsRef.current?.close();
}, []);

return { status, lastMessage, connect, send, disconnect };
}

WebSocket Monitor Page

The frontend includes a dedicated WebSocket monitoring page at /websocket-monitor with:

  • Connection Management: Connect/disconnect to both endpoints
  • Stream Controls: Start/stop continuous data streaming
  • Live Log Viewer: Real-time message log with expandable JSON
  • Configurable Settings: Adjust stream interval, select models
  • SCADA Simulation: Auto-generates realistic sensor data

SCADA Simulator (Python)

Location: ml/ws_scada_simulator.py

A Python script that simulates SCADA system integration:

# Install dependencies
pip install websockets

# Run simulator
python ws_scada_simulator.py

Features:

  • Connects to both WebSocket endpoints
  • Generates realistic sensor data
  • Supports sequential or parallel streaming
  • Configurable frame count and interval

Blockchain Service

BlockchainService Class

Location: src/lib/blockchain/service.ts

Server-side service for Polygon blockchain operations.

class BlockchainService {
private provider: JsonRpcProvider;
private wallet: Wallet | null;
private contract: Contract | null;

constructor();

// Status
isReady(): boolean;
getWalletAddress(): string;
async getBalance(): Promise<string>;
async getTotalReports(): Promise<number>;
async getStatus(): Promise<BlockchainStatus>;

// Storage
async storeReport(report: ReportData): Promise<StoreResult>;
async storeReportsBatch(reports: ReportData[]): Promise<StoreResult>;

// Verification
async verifyHash(reportHash: string): Promise<boolean>;
async verifyReport(report: ReportData): Promise<VerifyResult>;

// Helpers
getExplorerUrl(transactionHash: string): string;
getContractExplorerUrl(): string;
}

// Singleton instance
export const blockchainService = new BlockchainService();

Hash Generation

Location: src/lib/blockchain/hash.ts

// Generate deterministic hash for report
function generateReportHash(report: {
reportType: string;
userId: string;
input: Record<string, any>;
output: Record<string, any>;
createdAt: Date;
}): string;

// Hash user ID for privacy
function hashUserId(userId: string): string;

Configuration

Location: src/lib/blockchain/config.ts

export const BLOCKCHAIN_CONFIG = {
rpcUrl: process.env.NEXT_PUBLIC_POLYGON_RPC_URL || "https://rpc-amoy.polygon.technology",
chainId: 80002,
chainName: "Polygon Amoy Testnet",
contractAddress: process.env.NEXT_PUBLIC_CONTRACT_ADDRESS,
explorerUrl: "https://amoy.polygonscan.com",
};

export const CONTRACT_ABI = [
"function storeReport(string reportId, bytes32 reportHash, string reportType, bytes32 userIdHash)",
"function storeReportsBatch(string[] reportIds, bytes32[] reportHashes, string[] reportTypes, bytes32[] userIdHashes)",
"function verifyHash(bytes32 reportHash) view returns (bool)",
"function totalReports() view returns (uint256)",
];

API Routes

Location: src/app/api/blockchain/

RouteMethodDescription
/api/blockchain/statusGETGet blockchain status
/api/blockchain/storePOSTStore report hash
/api/blockchain/verifyPOSTVerify report hash

Reports Service

CRUD Operations

Location: src/lib/reports/service.ts

// Save a new report
async function saveReport(report: Omit<Report, "id">): Promise<string>;

// Get single report by ID
async function getReport(reportId: string, reportType?: ReportType): Promise<Report | null>;

// Get all reports for a user with filters
async function getReports(userId: string, filters?: ReportFilters): Promise<Report[]>;

// Get report summaries for list view
async function getReportSummaries(userId: string, filters?: ReportFilters): Promise<ReportSummary[]>;

// Delete a report
async function deleteReport(reportId: string, reportType?: ReportType): Promise<void>;

Report Type Mapping

// Each report type has its own Firestore collection
const REPORT_COLLECTIONS = {
prediction: "prediction_reports",
treatment: "treatment_reports",
"twin-engine": "twin_engine_reports",
"adaptive-optimizer": "adaptive_optimizer_reports",
};

function getCollectionName(reportType: ReportType): string {
return REPORT_COLLECTIONS[reportType];
}

Filters

interface ReportFilters {
reportType?: ReportType; // Filter by type
startDate?: Date; // Filter by date range
endDate?: Date;
limit?: number; // Limit results
}

// Usage
const reports = await getReports(userId, {
reportType: "prediction",
limit: 10,
});

Document Conversion

// Firestore document → Report object
function documentToReport(doc: DocumentData, id: string): Report {
return {
...doc,
id,
createdAt: doc.createdAt?.toDate() || new Date(),
};
}

// Report object → Firestore document
function reportToDocument(report: Omit<Report, "id">): DocumentData {
return {
...removeUndefinedValues(report),
createdAt: Timestamp.fromDate(report.createdAt),
};
}

PDF Generators

Prediction PDF

Location: src/lib/pdf/generators/prediction-pdf.ts

async function generatePredictionPDF(result: ReusabilityResult): Promise<void> {
// Generates and downloads PDF with:
// - Prediction summary
// - CPCB compliance table
// - Smart verdict
// - Treatment recommendations
// - Estimated values
}

Treatment PDF

Location: src/lib/pdf/generators/treatment-pdf.ts

async function generateTreatmentPDF(result: TreatmentResult): Promise<void> {
// Generates and downloads PDF with:
// - Treatment stage recommendation
// - Quality score
// - Treatment steps with descriptions
// - Parameter status
// - Chemical dosing suggestions
// - Risk assessment
}

PDF Templates

Location: src/lib/pdf/templates.ts

// Shared styles and utilities
const PDF_STYLES = {
headerColor: [40, 40, 40],
accentColor: [100, 100, 255],
tableHead: { fillColor: [40, 40, 40], textColor: [255, 255, 255] },
alternateRow: { fillColor: [245, 245, 245] },
};

function addHeader(doc: jsPDF, title: string): void;
function addTimestamp(doc: jsPDF): void;
function addTable(doc: jsPDF, data: any[], headers: string[]): void;

Usage

import { generatePredictionPDF } from "@/lib/pdf";

// After prediction completes
const result = await predictReusability(input);
await generatePredictionPDF(result);
// Downloads: prediction_report_TIMESTAMP.pdf

Service Patterns

Singleton Pattern

// Services are instantiated once
export const blockchainService = new BlockchainService();
export const mlApi = new MLAPIClient();

Error Handling

// Wrap operations in try-catch
async function saveReport(report) {
try {
const docRef = await addDoc(collection(db, collectionName), document);
return docRef.id;
} catch (error) {
console.error("Error saving report:", error);
throw new Error("Failed to save report");
}
}

Type Safety

// Strong typing for all service inputs/outputs
interface ReportData {
id: string;
reportType: ReportType;
userId: string;
input: Record<string, any>;
output: Record<string, any>;
createdAt: Date;
}

Last Updated: December 2024