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