Skip to content

Controller API Reference ​

Overview ​

ErgoPlanner provides comprehensive process control interfaces supporting PID controllers and advanced control strategies for wastewater treatment processes. Controllers manage dissolved oxygen, solids retention time, return activated sludge, waste activated sludge, flow, pH, and advanced control schemes.

Base Controller Types ​

ControllerType ​

Union of all supported controller types.

Base Controllers ​

TypeDescription
'DO'Dissolved oxygen controller
'SRT'Solids retention time controller
'RAS'Return activated sludge controller
'WAS'Waste activated sludge controller
'Flow'Flow rate controller
'pH'pH controller

Extended Controllers ​

TypeDescription
'Cascade'Master-slave cascade PID
'Deadband'On/off with hysteresis
'Switch'Smooth transition switch with S-curve
'TimeOnOff'Programmable time-based cycling
'Ratio'Flow ratio controller with PID trim
'ContinuousP'P-only for integrating processes
'Feedforward'Disturbance-based feedforward

Core Interfaces ​

ControllerConfig ​

Base controller configuration interface.

Properties ​

PropertyTypeRequiredDescription
idstringYesUnique controller ID
typeControllerTypeYesController type
enabledbooleanYesWhether controller is enabled
setpointnumberYesController setpoint value
kpnumberYesProportional gain
kinumberYesIntegral gain (1/d)
kdnumberNoDerivative gain (d)
targetNodeIdstringYesTarget process unit node ID
manipulatedVariablestringYesVariable being manipulated

Example ​

typescript
const doController: ControllerConfig = {
  id: 'do-ctrl-1',
  type: 'DO',
  enabled: true,
  setpoint: 2.0,      // 2.0 mg/L DO
  kp: 0.5,            // Proportional gain
  ki: 0.1,            // Integral gain (1/d)
  kd: 0.05,           // Derivative gain (d)
  targetNodeId: 'aeration-1',
  manipulatedVariable: 'airFlow'
};

PIDConfiguration ​

Complete PID configuration with anti-windup and filtering.

Properties ​

PropertyTypeDescription
kpnumberProportional gain
kinumberIntegral gain (1/s or 1/d)
kdnumberDerivative gain (s or d)
limitsControllerLimitsOutput limits
antiWindupAntiWindupConfigAnti-windup configuration
derivativeFilterDerivativeFilterConfigDerivative filter configuration
setpointnumberSetpoint value
enabledbooleanWhether controller is enabled

Example ​

typescript
const pidConfig: PIDConfiguration = {
  kp: 0.5,
  ki: 0.1,
  kd: 0.05,
  limits: {
    outputMin: 0,
    outputMax: 100
  },
  antiWindup: {
    enabled: true,
    maxIntegral: 50,
    minIntegral: -50,
    backCalculationCoeff: 0.5
  },
  derivativeFilter: {
    enabled: true,
    coefficient: 0.1
  },
  setpoint: 2.0,
  enabled: true
};

ControllerState ​

Extended controller state with runtime data.

Properties ​

PropertyTypeDescription
integralAccumulatornumberAccumulated integral term
previousErrornumberPrevious error for derivative calculation
lastOutputnumberLast calculated output
lastTimestampnumberTimestamp of last calculation
isActivebooleanWhether controller is actively controlling

Extends ControllerConfig with all base configuration properties.

Example ​

typescript
const controllerState: ControllerState = {
  id: 'do-ctrl-1',
  type: 'DO',
  enabled: true,
  setpoint: 2.0,
  kp: 0.5,
  ki: 0.1,
  kd: 0.05,
  targetNodeId: 'aeration-1',
  manipulatedVariable: 'airFlow',
  integralAccumulator: 5.2,
  previousError: 0.3,
  lastOutput: 45.5,
  lastTimestamp: 1234567890,
  isActive: true
};

ControllerOutput ​

Controller output result from a calculation step.

Properties ​

PropertyTypeDescription
controllerIdstringController ID
typeControllerTypeController type
setpointnumberCurrent setpoint value
processVariablenumberCurrent process variable (measured value)
errornumberControl error (setpoint - processVariable)
outputnumberController output value
saturatedbooleanWhether output is saturated (at limits)
pTermnumberProportional term contribution
iTermnumberIntegral term contribution
dTermnumberDerivative term contribution
outputUnitstringUnit of the output
targetNodeIdstringNode ID being controlled

Example ​

typescript
const output: ControllerOutput = {
  controllerId: 'do-ctrl-1',
  type: 'DO',
  setpoint: 2.0,
  processVariable: 1.8,
  error: 0.2,
  output: 48.5,
  saturated: false,
  pTerm: 0.1,      // 0.5 × 0.2
  iTerm: 48.0,     // accumulated integral
  dTerm: 0.4,      // derivative component
  outputUnit: 'm³/h',
  targetNodeId: 'aeration-1'
};

console.log(`DO Error: ${output.error} mg/L`);
console.log(`Air Flow Output: ${output.output} ${output.outputUnit}`);
console.log(`P: ${output.pTerm}, I: ${output.iTerm}, D: ${output.dTerm}`);

ControllerLimits ​

Output limits for controller.

Properties ​

PropertyTypeDescription
outputMinnumberMinimum output value
outputMaxnumberMaximum output value

AntiWindupConfig ​

Anti-windup configuration for integral term.

Properties ​

PropertyTypeDescription
enabledbooleanEnable anti-windup
maxIntegralnumberMaximum integral value
minIntegralnumberMinimum integral value
backCalculationCoeffnumberBack-calculation coefficient (0-1)

DerivativeFilterConfig ​

Derivative filter configuration to reduce noise sensitivity.

Properties ​

PropertyTypeDescription
enabledbooleanEnable derivative filtering
coefficientnumberFilter coefficient (0-1, lower = more filtering)

Extended Controller Types ​

CascadeControllerConfig ​

Master-slave PID structure where master output becomes slave setpoint.

Properties ​

PropertyTypeDescription
idstringController ID
type'Cascade'Controller type
enabledbooleanWhether controller is enabled
masterMasterPIDConfigMaster PID configuration
slaveSlavePIDConfigSlave PID configuration
modeExtendedControllerModeMode: 'auto', 'manual', 'tracking'
bumplessTransferbooleanBumpless transfer enabled

Master Configuration ​

PropertyTypeDescription
setpointnumberMaster setpoint
kpnumberProportional gain
kinumberIntegral gain (1/d)
kdnumberDerivative gain (d)
outputMinnumberOutput minimum
outputMaxnumberOutput maximum
pvNodeIdstringProcess variable source node
pvVariablestringProcess variable name

Slave Configuration ​

Includes master properties plus:

PropertyTypeDescription
manipulatedVariablestringManipulated variable name
targetNodeIdstringTarget node for output

Example ​

typescript
const cascadeController: CascadeControllerConfig = {
  id: 'cascade-do-air',
  type: 'Cascade',
  enabled: true,
  master: {
    setpoint: 2.0,           // 2.0 mg/L DO
    kp: 0.8,
    ki: 0.2,
    kd: 0.1,
    outputMin: 0,
    outputMax: 100,          // Maximum air flow setpoint
    pvNodeId: 'aeration-1',
    pvVariable: 'DO'
  },
  slave: {
    kp: 0.5,
    ki: 0.3,
    kd: 0.05,
    outputMin: 0,
    outputMax: 100,          // Maximum blower speed %
    pvNodeId: 'blower-1',
    pvVariable: 'airFlow',
    manipulatedVariable: 'blowerSpeed',
    targetNodeId: 'blower-1'
  },
  mode: 'auto',
  bumplessTransfer: true
};

RatioControllerConfig ​

Maintains ratio between wild and controlled flows with PID trim.

Properties ​

PropertyTypeDescription
idstringController ID
type'Ratio'Controller type
enabledbooleanWhether controller is enabled
wildNodeIdstringWild variable source node (leader)
wildVariablestringWild variable name
controlledNodeIdstringControlled variable source node (follower)
controlledVariablestringControlled variable name
targetNodeIdstringTarget node for manipulated output
manipulatedVariablestringManipulated variable
rationumberDesired ratio (controlled/wild)
pidTrimPIDTrimConfigPID trim configuration
outputMinnumberOutput minimum
outputMaxnumberOutput maximum
wildLagnumberLag compensation for wild variable (days)
modeExtendedControllerModeMode of operation

PID Trim Configuration ​

PropertyTypeDescription
enabledbooleanEnable PID trim
kpnumberProportional gain
kinumberIntegral gain
kdnumberDerivative gain
maxTrimnumberMax trim adjustment (fraction, e.g., 0.1 = ±10%)

Example ​

typescript
const rasRatioController: RatioControllerConfig = {
  id: 'ras-ratio-1',
  type: 'Ratio',
  enabled: true,
  wildNodeId: 'influent-1',
  wildVariable: 'flow',
  controlledNodeId: 'ras-pump-1',
  controlledVariable: 'flow',
  targetNodeId: 'ras-pump-1',
  manipulatedVariable: 'pumpSpeed',
  ratio: 1.0,              // 1:1 RAS ratio
  pidTrim: {
    enabled: true,
    kp: 0.2,
    ki: 0.05,
    kd: 0.01,
    maxTrim: 0.1           // ±10% trim
  },
  outputMin: 0,
  outputMax: 100,
  wildLag: 0.042,          // ~1 hour lag
  mode: 'auto'
};

DeadbandControllerConfig ​

On/off control with hysteresis to prevent hunting.

Properties ​

PropertyTypeDescription
idstringController ID
type'Deadband'Controller type
enabledbooleanWhether controller is enabled
targetNodeIdstringTarget node ID
pvNodeIdstringProcess variable source node
pvVariablestringProcess variable name
manipulatedVariablestringManipulated variable (on/off output)
setpointnumberSetpoint (center of deadband)
deadbandnumberDeadband width (total band = 2 × deadband)
highLimitnumberHigh limit for turn on
lowLimitnumberLow limit for turn off
onValuenumberOutput when ON
offValuenumberOutput when OFF
actionControllerAction'direct' or 'reverse'
minOnTimenumberMinimum on time (days)
minOffTimenumberMinimum off time (days)
modeExtendedControllerModeMode of operation

Example ​

typescript
const mixerController: DeadbandControllerConfig = {
  id: 'mixer-deadband-1',
  type: 'Deadband',
  enabled: true,
  targetNodeId: 'anoxic-1',
  pvNodeId: 'anoxic-1',
  pvVariable: 'DO',
  manipulatedVariable: 'mixerSpeed',
  setpoint: 0.3,           // 0.3 mg/L DO target
  deadband: 0.1,           // ±0.1 mg/L deadband
  highLimit: 0.4,          // Turn off mixer at 0.4 mg/L
  lowLimit: 0.2,           // Turn on mixer at 0.2 mg/L
  onValue: 100,            // 100% mixer speed
  offValue: 0,             // 0% mixer speed
  action: 'reverse',       // High DO = turn off mixer
  minOnTime: 0.021,        // 30 minutes minimum on
  minOffTime: 0.021,       // 30 minutes minimum off
  mode: 'auto'
};

FeedforwardControllerConfig ​

Disturbance-based feedforward with optional lead-lag compensation.

Properties ​

PropertyTypeDescription
idstringController ID
type'Feedforward'Controller type
enabledbooleanWhether controller is enabled
targetNodeIdstringTarget node ID
disturbanceNodeIdstringDisturbance variable source node
disturbanceVariablestringDisturbance variable name
manipulatedVariablestringManipulated variable
staticGainnumberStatic gain (output/disturbance relationship)
leadLagLeadLagConfigLead-lag compensation
outputMinnumberOutput minimum
outputMaxnumberOutput maximum
biasnumberBias/offset for output
feedbackPIDFeedbackPIDConfigundefined
modeExtendedControllerModeMode of operation

Lead-Lag Configuration ​

PropertyTypeDescription
leadTimenumberLead time constant (days)
lagTimenumberLag time constant (days)
gainnumberGain

Example ​

typescript
const carbonDoseController: FeedforwardControllerConfig = {
  id: 'carbon-ff-1',
  type: 'Feedforward',
  enabled: true,
  targetNodeId: 'carbon-dose-1',
  disturbanceNodeId: 'influent-1',
  disturbanceVariable: 'flow',
  manipulatedVariable: 'doseRate',
  staticGain: 0.05,        // 50 mg/L per m³/d influent flow
  leadLag: {
    leadTime: 0.083,       // 2 hours lead
    lagTime: 0.042,        // 1 hour lag
    gain: 1.0
  },
  outputMin: 0,
  outputMax: 200,
  bias: 10,                // 10 mg/L base dose
  mode: 'auto'
};

Controller Execution ​

ControllerExecutionContext ​

Context for controller execution during a simulation step.

Properties ​

PropertyTypeDescription
currentTimenumberCurrent simulation time (days)
dtnumberTime step (days)
measurementsMap<string, ProcessMeasurement>Current process measurements
configsControllerConfig[]Controller configurations

ControllerExecutionResult ​

Result of controller execution for a simulation step.

Properties ​

PropertyTypeDescription
outputsMap<string, ControllerOutput>All controller outputs
parameterUpdatesMap<string, Record<string, number>>Modified parameters to apply
warningsstring[]Any warnings during execution
errorsstring[]Any errors during execution

Example ​

typescript
const executionResult: ControllerExecutionResult = {
  outputs: new Map([
    ['do-ctrl-1', doControllerOutput],
    ['ras-ratio-1', rasControllerOutput]
  ]),
  parameterUpdates: new Map([
    ['aeration-1', { airFlow: 48.5 }],
    ['ras-pump-1', { pumpSpeed: 85.2 }]
  ]),
  warnings: [],
  errors: []
};

// Apply parameter updates to nodes
for (const [nodeId, params] of executionResult.parameterUpdates) {
  const node = graph.nodes.get(nodeId);
  if (node) {
    Object.assign(node.parameters, params);
  }
}

Tuning Guidelines ​

Typical PID Gains by Controller Type ​

DO Controller ​

ParameterTypical RangeNotes
Kp0.3 - 0.8Higher for faster response
Ki0.05 - 0.2Lower to prevent overshoot
Kd0.01 - 0.1Small derivative for stability

SRT Controller ​

ParameterTypical RangeNotes
Kp0.1 - 0.5Slow process, conservative tuning
Ki0.01 - 0.05Very slow integral action
Kd0 - 0.05Often zero or very small

RAS Flow Controller ​

ParameterTypical RangeNotes
Kp0.5 - 1.5Fast flow control
Ki0.1 - 0.5Moderate integral action
Kd0.05 - 0.2Derivative for smooth control

Implementation Notes ​

Anti-Windup ​

Anti-windup prevents integral term buildup during output saturation:

typescript
// Clamping method
if (output > limits.outputMax) {
  integralAccumulator = Math.min(integralAccumulator, maxIntegral);
} else if (output < limits.outputMin) {
  integralAccumulator = Math.max(integralAccumulator, minIntegral);
}

// Back-calculation method
const saturatedError = output - clampedOutput;
integralAccumulator -= backCalculationCoeff * saturatedError;

Derivative Filtering ​

First-order filter reduces noise sensitivity:

typescript
const rawDerivative = (error - previousError) / dt;
const filteredDerivative = coefficient * rawDerivative +
                          (1 - coefficient) * previousFilteredDerivative;
dTerm = kd * filteredDerivative;

Bumpless Transfer ​

For mode changes (auto/manual/tracking):

typescript
// When switching to auto, initialize integral to maintain current output
integralAccumulator = (currentOutput - pTerm - dTerm) / ki;