Question

I'm kinda new to Java, so haven't yet fully grasped the concept of multithreading.I would like to create a PIDController class that allows me to do this:

ControllerMethods methods = new ControllerMethods()
                            {
                                public long getError(long setpoint)
                                {
                                    //get an input
                                }
                                public void setOutput(long value)
                                {
                                    //do something
                                }
                                public void isComplete(long setpoint)
                                {
                                    return getError() == 0;
                                }
                            };

PIDController motorPID = new PIDController(setpoint, kp, ki, kd, methods);

motorPID.run();
//runs the PID controller to completion (methods.isComplete() == true)

motorPID.run(false);
//starts the PID controller in a separate thread, allowing
//continual monitoring in the current thread

while(motorPID.isRunning())
{
    //do something else
    if(condition1)
        motorPID.pause();
        //pause the PID controller, preventing the integral from increasing
    else if(condition2)
        motorPID.stop();
}

I've worked out how to calculate the standard PID argorithms, but I can't work out how to provide the asynchronous functionality.

Can anybody tell me how I can achieve a similar API?

Was it helpful?

Solution

You already implemented a run() method for PIDController so you should also implement the Runnable interface:

class PIDController implements Runnable {
    ....
}

Now you can start your PIDController asynchonous, by calling:

pidControllerThread = new Thread( pidController );
pidControllerThread.start();

For synchronization (if needed) you should have a look at the sun concurrency guide.

OTHER TIPS

By far, the best mechanism for attaching threads to anything is to separate the object which does the work from the object which is the thread. The Runnable interface can be attractive, because it allows people to pass the object to a Thread constructor or Executor, and run it. However, if you have lifecycle management requirements for your object which are out side of "running to completion", such as pausing, then you will find it more appropriate in most cases, to manage the Thread within your object so that you know which thread is running (yes you can set an instance object to Thread.currentThread() on entry to run, but...).

So, I think what you have is a good start. You need to add the use of some locking to help yourself manage pause() and other thread control.

    public class PIDController {
        private final Object myLock = new Object();
        private final ControllerMethods ctrl;
        private volatile Thread thread;
        private volatile Runner runner;
        private int pidInterval = 700;
        private final double setPoint, kp, ki, kd;

        public PIDController( double setPoint, double kp, double ki, double kd, ControllerMethods inst ) {
            this.ctrl = inst;
            this.setPoint = setPoint;
            this.kp = kp;
            this.ki = ki;
            this.kd = kd;
        }

        public void pause() {
            synchronized( myLock ) {
                if( runner.paused ) {
                    throw new IllegalOperationException(this+": already paused");
                }
                runner.paused = true;
            }
        }

        public void resume() {
            synchronized( myLock ) {
                if( !runner.paused ) {
                    throw new IllegalOperationException(this+": already resumed");
                }
                runner.paused = false;
            }
        }

        public bool isRunning() {
            return running;
        }

        public void start() {
            if( thread != null ) {
                throw new IllegalOperationException( this+": already running");
            }
            myThread = new Thread( runner = new Runner() );
            myThread.start();
        }

        public void stop() {
            if( runner == null ) {
                throw new IllegalOperationException( this+": PID is not running");
            }
            runner.running = false;
            if( runner.paused )
                resume();
            runner = null;
        }


        // It is important, anytime that you implement a stoppable Runnable, that
        // you include the "running" flag as a member of an innner instance class like
        // this so that when you ask this instance to stop, you can immediately restart
        // another instance and not have the two threads observing the same "running" flag
        private class Runner implements Runnable {
            volatile bool running = false, bool paused;
            public void run() {
                running = true;
                while( running ) {
                    // do this at the top of the loop so that a wake from
                    // pause will check running, before recomputing.
                    reComputePID();

                    // Use the double check idiom to 
                    if( paused ) {
                        synchronized( myLock ) {
                            while( paused ) {
                                myLock.wait();
                            }
                        }
                    }
                    Thread.sleep( pidInterval );
                }
            }
        }

        public void reComputePID() {
            ...
        }
    }

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top