Skip to main content

08 - Neilsoft Integration

Client-specific integration guide for Neilsoft's plant engineering services


Table of Contents​

  1. Neilsoft Overview
  2. Service Alignment
  3. Integration Points
  4. BIM Integration
  5. SCADA Integration
  6. 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​

SegmentRelevance to Our Platform
Plant & FactoriesSTP/ETP monitoring, process optimization
Pharma/HealthcareWater quality compliance, GMP requirements
Food & BeveragesWastewater treatment, quality control
ManufacturingIndustrial 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​

  1. Predictive Maintenance: ML models predict equipment issues before failure
  2. Compliance Automation: CPCB compliance checking in real-time
  3. Cost Optimization: Optimized chemical dosing reduces operational costs
  4. Digital Twin: Live sensor data integrated with BIM models
  5. 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​

TierFeaturesPrice (Annual)
Basic1 plant, 10 sensors, basic predictions$5,000
Professional5 plants, 50 sensors, full ML suite$15,000
EnterpriseUnlimited, custom integrations, SLACustom

Next Steps​


Last Updated: December 2024