08 - Neilsoft Integration
Client-specific integration guide for Neilsoft's plant engineering services
Table of Contents
- Neilsoft Overview
- Service Alignment
- Integration Points
- BIM Integration
- SCADA Integration
- Deployment Options
1. Neilsoft Overview
Company Profile
- Website: https://neilsoft.com
- Headquarters: Pune, India
- Key Services:
- Plant & Factory Engineering
- Building Information Modeling (BIM)
- Industrial Automation & IoT
- Process Engineering
- Electrical, Instrumentation & Control (EI&C)
Target Segments
| Segment | Relevance to Our Platform |
|---|---|
| Plant & Factories | STP/ETP monitoring, process optimization |
| Pharma/Healthcare | Water quality compliance, GMP requirements |
| Food & Beverages | Wastewater treatment, quality control |
| Manufacturing | Industrial effluent monitoring |
2. Service Alignment
How Our Platform Complements Neilsoft Services
┌─────────────────────────────────────────────────────────────────────┐
│ NEILSOFT + Edubotx SYNERGY │
│ │
│ NEILSOFT SERVICES OUR PLATFORM │
│ ──────────── ──── ──────────── │
│ │
│ Plant Engineering ◀────▶ Real-time water quality monitoring │
│ ML-based predictions │
│ │
│ Process Engineering ◀────▶ Treatment recommendations │
│ Chemical dosing optimization │
│ │
│ BIM Services ◀────▶ Digital twin visualization │
│ 3D sensor overlay │
│ │
│ Industrial IoT ◀────▶ Sensor integration │
│ Edge computing │
│ │
│ EI&C Engineering ◀────▶ PLC/SCADA integration │
│ OPC-UA connectivity │
│ │
└─────────────────────────────────────────────────────────────────────┘
Value Proposition for Neilsoft Clients
- Predictive Maintenance: ML models predict equipment issues before failure
- Compliance Automation: CPCB compliance checking in real-time
- Cost Optimization: Optimized chemical dosing reduces operational costs
- Digital Twin: Live sensor data integrated with BIM models
- Audit Trail: Blockchain-verified reports for regulatory compliance
3. Integration Points
3.1 Kepware KEPServerEX Integration
Neilsoft commonly uses Kepware for industrial connectivity. Here's how to integrate:
<!-- KEPServerEX Configuration for Water Treatment -->
<Project>
<Channel Name="WaterTreatmentPlant">
<Driver>Siemens TCP/IP Ethernet</Driver>
<Device Name="STP_PLC">
<IPAddress>192.168.1.10</IPAddress>
<Port>102</Port>
<!-- Water Quality Tags -->
<Tag Name="pH_Inlet" Address="DB1.DBD0" DataType="Real"/>
<Tag Name="pH_Outlet" Address="DB1.DBD4" DataType="Real"/>
<Tag Name="BOD_Inlet" Address="DB1.DBD8" DataType="Real"/>
<Tag Name="COD_Inlet" Address="DB1.DBD12" DataType="Real"/>
<Tag Name="TSS_Inlet" Address="DB1.DBD16" DataType="Real"/>
<Tag Name="TDS_Inlet" Address="DB1.DBD20" DataType="Real"/>
<Tag Name="FlowRate" Address="DB1.DBD24" DataType="Real"/>
<Tag Name="Temperature" Address="DB1.DBD28" DataType="Real"/>
<!-- Control Tags -->
<Tag Name="AerationRate" Address="DB2.DBD0" DataType="Real"/>
<Tag Name="ChemicalDose" Address="DB2.DBD4" DataType="Real"/>
<Tag Name="SludgeRecycle" Address="DB2.DBD8" DataType="Real"/>
</Device>
</Channel>
<!-- REST/MQTT Gateway -->
<IoTGateway>
<MQTT>
<BrokerURL>mqtt://your-broker:1883</BrokerURL>
<Topic>neilsoft/plant/{DeviceName}/{TagName}</Topic>
<PublishRate>1000</PublishRate>
</MQTT>
<REST>
<URL>https://api.Edubotx.com/api/iot/ingest</URL>
<Method>POST</Method>
<Headers>
<Header Name="x-api-key">${API_KEY}</Header>
</Headers>
</REST>
</IoTGateway>
</Project>
3.2 Siemens PLC Integration
// src/lib/integrations/siemens.ts
import { S7Client } from "node-snap7";
export class SiemensIntegration {
private client: S7Client;
private connected = false;
constructor() {
this.client = new S7Client();
}
async connect(ip: string, rack = 0, slot = 1) {
return new Promise<void>((resolve, reject) => {
this.client.ConnectTo(ip, rack, slot, (err) => {
if (err) reject(err);
else {
this.connected = true;
resolve();
}
});
});
}
async readWaterQualityData(): Promise<WaterQualityData> {
// Read DB1 (Water Quality Data Block)
const buffer = await this.readDB(1, 0, 32);
return {
pH: buffer.readFloatBE(0),
pH_outlet: buffer.readFloatBE(4),
BOD: buffer.readFloatBE(8),
COD: buffer.readFloatBE(12),
TSS: buffer.readFloatBE(16),
TDS: buffer.readFloatBE(20),
flowRate: buffer.readFloatBE(24),
temperature: buffer.readFloatBE(28),
};
}
async writeControlSetpoints(setpoints: ControlSetpoints) {
const buffer = Buffer.alloc(12);
buffer.writeFloatBE(setpoints.aerationRate, 0);
buffer.writeFloatBE(setpoints.chemicalDose, 4);
buffer.writeFloatBE(setpoints.sludgeRecycle, 8);
await this.writeDB(2, 0, buffer);
}
private readDB(dbNumber: number, start: number, size: number): Promise<Buffer> {
return new Promise((resolve, reject) => {
this.client.DBRead(dbNumber, start, size, (err, data) => {
if (err) reject(err);
else resolve(data);
});
});
}
private writeDB(dbNumber: number, start: number, buffer: Buffer): Promise<void> {
return new Promise((resolve, reject) => {
this.client.DBWrite(dbNumber, start, buffer.length, buffer, (err) => {
if (err) reject(err);
else resolve();
});
});
}
}
3.3 Allen-Bradley Integration
// src/lib/integrations/allen-bradley.ts
import { Controller, Tag } from "ethernet-ip";
export class AllenBradleyIntegration {
private plc: Controller;
private tags: Map<string, Tag> = new Map();
constructor(ipAddress: string) {
this.plc = new Controller();
this.plc.connect(ipAddress);
}
async setupTags() {
const tagNames = ["WQ_pH", "WQ_BOD", "WQ_COD", "WQ_TSS", "WQ_TDS", "WQ_FlowRate", "WQ_Temperature", "WQ_DO"];
for (const name of tagNames) {
const tag = new Tag(name);
this.plc.subscribe(tag);
this.tags.set(name, tag);
}
}
async readAll(): Promise<Record<string, number>> {
await this.plc.readTag(Array.from(this.tags.values()));
const data: Record<string, number> = {};
this.tags.forEach((tag, name) => {
data[name] = tag.value;
});
return data;
}
onTagChange(callback: (name: string, value: number) => void) {
this.tags.forEach((tag, name) => {
tag.on("Changed", (t) => callback(name, t.value));
});
}
}
4. BIM Integration
4.1 Autodesk Forge Integration
Neilsoft uses Autodesk tools extensively. Integrate with Forge for digital twin:
// src/lib/integrations/forge.ts
import { ForgeClient, AuthClientTwoLegged } from "forge-apis";
export class ForgeIntegration {
private authClient: AuthClientTwoLegged;
private credentials: any;
constructor() {
this.authClient = new AuthClientTwoLegged(
process.env.FORGE_CLIENT_ID!,
process.env.FORGE_CLIENT_SECRET!,
["data:read", "data:write", "bucket:read"],
true
);
}
async authenticate() {
this.credentials = await this.authClient.authenticate();
}
async getSensorOverlayData(modelUrn: string, sensorData: SensorReading[]) {
// Map sensor data to BIM element IDs
const overlayData = sensorData.map((sensor) => ({
dbId: this.mapSensorToElement(sensor.sensorId),
color: this.getColorForValue(sensor.parameter, sensor.value),
tooltip: `${sensor.parameter}: ${sensor.value} ${sensor.unit}`,
}));
return overlayData;
}
private mapSensorToElement(sensorId: string): number {
// Map sensor IDs to Revit element IDs
const mapping: Record<string, number> = {
pH_inlet: 12345,
pH_outlet: 12346,
BOD_sensor: 12347,
// ... more mappings
};
return mapping[sensorId] || 0;
}
private getColorForValue(parameter: string, value: number): string {
const thresholds: Record<string, { good: number; warning: number }> = {
pH: { good: 7.5, warning: 8.5 },
BOD: { good: 20, warning: 30 },
COD: { good: 150, warning: 250 },
};
const t = thresholds[parameter];
if (!t) return "#808080"; // Gray for unknown
if (value <= t.good) return "#00FF00"; // Green
if (value <= t.warning) return "#FFFF00"; // Yellow
return "#FF0000"; // Red
}
}
4.2 Forge Viewer Extension
// public/js/forge-sensor-extension.js
class SensorOverlayExtension extends Autodesk.Viewing.Extension {
constructor(viewer, options) {
super(viewer, options);
this.sensorData = new Map();
this.ws = null;
}
load() {
console.log("SensorOverlayExtension loaded");
this.connectWebSocket();
return true;
}
unload() {
if (this.ws) this.ws.close();
return true;
}
connectWebSocket() {
this.ws = new WebSocket(`${window.location.origin}/api/iot/ws`);
this.ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === "sensor:reading") {
this.updateSensorOverlay(data.payload);
}
};
}
updateSensorOverlay(reading) {
const dbId = this.mapSensorToDbId(reading.sensorId);
if (!dbId) return;
// Update color based on value
const color = this.getColor(reading.parameter, reading.value);
this.viewer.setThemingColor(dbId, new THREE.Vector4(color.r, color.g, color.b, 0.8));
// Update tooltip
this.sensorData.set(dbId, reading);
}
getColor(parameter, value) {
// Color logic based on thresholds
const thresholds = {
pH: { good: 7.5, warning: 8.5 },
BOD: { good: 20, warning: 30 },
};
const t = thresholds[parameter] || { good: 50, warning: 80 };
if (value <= t.good) return { r: 0, g: 1, b: 0 };
if (value <= t.warning) return { r: 1, g: 1, b: 0 };
return { r: 1, g: 0, b: 0 };
}
mapSensorToDbId(sensorId) {
// Mapping from sensor IDs to Forge dbIds
const mapping = {
pH_inlet: 12345,
pH_outlet: 12346,
};
return mapping[sensorId];
}
}
Autodesk.Viewing.theExtensionManager.registerExtension("SensorOverlayExtension", SensorOverlayExtension);
5. SCADA Integration
5.1 Ignition Integration
# ignition_gateway_script.py
# Run in Ignition Gateway scripting
import system
import json
from java.net import URL, HttpURLConnection
API_URL = "https://api.Edubotx.com/api/iot/ingest"
API_KEY = "your-api-key"
def publishToCloud(tagPath, value, quality):
"""Publish tag value to Edubotx cloud"""
# Parse tag path
parts = tagPath.split('/')
sensorId = parts[-1]
parameter = mapTagToParameter(sensorId)
payload = {
"sensorId": sensorId,
"parameter": parameter,
"value": float(value),
"unit": getUnit(parameter),
"timestamp": system.date.format(system.date.now(), "yyyy-MM-dd'T'HH:mm:ss'Z'"),
"quality": "good" if quality.isGood() else "bad",
"metadata": {
"plantId": "neilsoft-plant-01",
"source": "ignition"
}
}
# Send HTTP POST
try:
url = URL(API_URL)
conn = url.openConnection()
conn.setRequestMethod("POST")
conn.setRequestProperty("Content-Type", "application/json")
conn.setRequestProperty("x-api-key", API_KEY)
conn.setDoOutput(True)
outputStream = conn.getOutputStream()
outputStream.write(json.dumps(payload).encode('utf-8'))
outputStream.close()
responseCode = conn.getResponseCode()
if responseCode != 201:
system.util.getLogger("CloudPublisher").warn(
"Failed to publish: " + str(responseCode)
)
except Exception as e:
system.util.getLogger("CloudPublisher").error(str(e))
def mapTagToParameter(tagName):
mapping = {
"pH_Sensor": "pH",
"BOD_Analyzer": "BOD",
"COD_Analyzer": "COD",
"TSS_Sensor": "TSS",
"TDS_Sensor": "TDS",
"Flow_Meter": "flow_rate",
"Temp_Sensor": "temperature"
}
return mapping.get(tagName, tagName)
def getUnit(parameter):
units = {
"pH": "pH",
"BOD": "mg/L",
"COD": "mg/L",
"TSS": "mg/L",
"TDS": "mg/L",
"flow_rate": "m³/hr",
"temperature": "°C"
}
return units.get(parameter, "")
5.2 Ignition Tag Change Script
# Tag Change Event Script
# Attach to water quality tags
def valueChanged(tag, tagPath, previousValue, currentValue, initialChange, missedEvents):
if initialChange:
return
# Publish to cloud
publishToCloud(tagPath, currentValue.value, currentValue.quality)
# Check local alerts
checkLocalAlerts(tagPath, currentValue.value)
def checkLocalAlerts(tagPath, value):
thresholds = {
"pH": {"min": 6.5, "max": 8.5},
"BOD": {"max": 30},
"COD": {"max": 250}
}
parameter = tagPath.split('/')[-1]
t = thresholds.get(parameter)
if t:
if t.get("min") and value < t["min"]:
triggerAlarm(parameter, value, "LOW")
if t.get("max") and value > t["max"]:
triggerAlarm(parameter, value, "HIGH")
def triggerAlarm(parameter, value, level):
system.alarm.acknowledge(
["[default]Alarms/" + parameter + "_" + level],
"Auto-acknowledged"
)
6. Deployment Options
6.1 On-Premise (Air-Gapped)
For sensitive installations without internet:
# docker-compose.onprem.yml
version: "3.8"
services:
app:
image: Edubotx/water-quality:latest
ports:
- "443:443"
environment:
- DATABASE_URL=postgresql://db:5432/Edubotx
- ML_API_URL=http://ml-api:8000
volumes:
- ./certs:/app/certs
ml-api:
image: Edubotx/ml-api:latest
ports:
- "8000:8000"
db:
image: postgres:15
volumes:
- pgdata:/var/lib/postgresql/data
influxdb:
image: influxdb:2.7
volumes:
- influxdata:/var/lib/influxdb2
mqtt:
image: eclipse-mosquitto:2
ports:
- "1883:1883"
volumes:
pgdata:
influxdata:
6.2 Hybrid Cloud
┌─────────────────────────────────────────────────────────────────┐
│ HYBRID DEPLOYMENT │
│ │
│ ON-PREMISE (Neilsoft Client Site) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ • Edge Gateway • Local MQTT │ │
│ │ • Kepware/Ignition • Local Database │ │
│ │ • Basic Alerts • Data Buffering │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ │ VPN / Secure Tunnel │
│ ▼ │
│ CLOUD (AWS/Azure) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ • Full ML Models • Long-term Storage │ │
│ │ • Dashboard • Blockchain Verification │ │
│ │ • Analytics • Report Generation │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
6.3 Licensing Model for Neilsoft
| Tier | Features | Price (Annual) |
|---|---|---|
| Basic | 1 plant, 10 sensors, basic predictions | $5,000 |
| Professional | 5 plants, 50 sensors, full ML suite | $15,000 |
| Enterprise | Unlimited, custom integrations, SLA | Custom |
Next Steps
- Proceed to 09-IMPLEMENTATION-ROADMAP.md for timeline
- Review 10-TESTING-SIMULATION.md for testing approach
Last Updated: December 2024