content: package mods.eln.sim;
  
  import mods.eln.item.regulator.IRegulatorDescriptor.RegulatorType;
  import mods.eln.misc.INBTTReady;
  import net.minecraft.nbt.NBTTagCompound;
  
  public abstract class RegulatorProcess implements IProcess, INBTTReady {
  
      RegulatorType type = RegulatorType.None;
      double target;
      double OnOffHysteresisDiv2;
      double P, I, D;
      double hitLast = 0, errorIntegrated = 0;
      boolean boot = true;
      String name;
  
      public void setManual() {
          type = RegulatorType.Manual;
      }
  
      public void setNone() {
          type = RegulatorType.None;
      }
  
      public void setOnOff(double OnOffHysteresisFactor, double workingPoint) {
          type = RegulatorType.OnOff;
          this.OnOffHysteresisDiv2 = OnOffHysteresisFactor * workingPoint / 2;
          boot = false;
          setCmd(0.0);
      }
  
      public void setAnalog(double P, double I, double D, double workingPoint) {
          P /= workingPoint;
          I /= workingPoint;
          D /= workingPoint;
  
          if (!boot && (this.P != P || this.I != I || this.D != D || type != RegulatorType.Analog)) {
              errorIntegrated = 0;
              hitLast = getHit();
          }
  
          this.P = P;
          this.I = I;
          this.D = D;
  
          type = RegulatorType.Analog;
          boot = false;
      }
  
      public void setTarget(double target) {
          this.target = target;
      }
  
      public double getTarget() {
          return target;
      }
  
      protected abstract double getHit();
  
      protected abstract void setCmd(double cmd);
  
      public RegulatorProcess(String name) {
          this.name = name;
      }
  
      @Override
      public void process(double time) {
          double hit = getHit();
  
          switch (type) {
              case Manual:
                  break;
              case None:
                  setCmd(1.0);
                  break;
              case Analog:
                  double error = target - hit;
                  double fP = error * P;
                  double cmd = fP - (hit - hitLast) * D * time;
  
                  errorIntegrated += error * time * I;
  
                  if (errorIntegrated > 1.0 - fP) {
                      errorIntegrated = 1.0 - fP;
                      if (errorIntegrated < 0.0) errorIntegrated = 0.0;
                  } else if (errorIntegrated < (-1.0 + fP)) {
                      errorIntegrated = (-1.0 + fP);
                      if (errorIntegrated > 0.0) errorIntegrated = 0.0;
                  }
  
                  cmd += errorIntegrated;
  
                  if (cmd > 1.0) setCmd(1.0);
                  else if (cmd < -1.0) setCmd(-1.0);
                  else setCmd(cmd);
  
                  hitLast = hit;
                  break;
  
              case OnOff:
                  if (hit > target + OnOffHysteresisDiv2) setCmd(0.0);
                  if (hit < target - OnOffHysteresisDiv2) setCmd(1.0);
                  break;
  
              default:
                  break;
          }
      }
  
      @Override
      public void readFromNBT(NBTTagCompound nbt, String str) {
          errorIntegrated = nbt.getDouble(str + name + "errorIntegrated");
          if (Double.isNaN(errorIntegrated)) errorIntegrated = 0;
          setTarget(nbt.getDouble(str + name + "target"));
      }
  
      @Override
      public void writeToNBT(NBTTagCompound nbt, String str) {
          nbt.setDouble(str + name + "errorIntegrated", errorIntegrated);
          nbt.setDouble(str + name + "target", target);
      }
  }
  