Very simple cooperative round-robin task scheduler. See examples.

Dependents:   Garage_Control

SimpleScheduler.h

Committer:
AjK
Date:
2011-03-04
Revision:
4:49652acb6806
Parent:
3:95ec5c83c2fe

File content as of revision 4:49652acb6806:

/*
    Copyright (c) 2011 Andy Kirkham
 
    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:
 
    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.
 
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.
    
    @file          SimpleScheduler.h 
    @purpose       Simple round-robin cooperative scheduler
    @version       see ChangeLog.c
    @date          Mar 2011
    @author        Andy Kirkham
*/

#ifndef AJK_SIMPLESCHEDULER_H
#define AJK_SIMPLESCHEDULER_H

#include "mbed.h"
#include <list>
#include "STcallback.h"

namespace AjK {

/** SimpleTask
 *
 * A defined function task requires a "holder" for SimpleScheduler
 * to manage it. This is the holder class.
 *
 * @see SimpleScheduler
 * @see example1.h
 * @see example2.h
 * @see example3.h
 * @see example4.h
 * @see http://mbed.org/cookbook/SimpleScheduler
 *
 * @code
 *     SimpleTask *task = new SimpleTask(100);
 *     task->attach(func);
 * @endcode
 * @code
 *     SimpleTask *task = new SimpleTask(100, func); // Every 100ms
 *     SimpleTask *task = new SimpleTask(1.0, func); // Every 1second
 * @endcode
 * 
 * When creating new SimpleTasks you pass the time as a frequency
 * of how often to call the task function. If the time is an integer
 * then milliseconds is assumed. If the number you pass is a read (double)
 * number then the time assumed is seconds.
 */
class SimpleTask {
public:

    friend class SimpleScheduler;
    
    void attach(void (*fptr)(SimpleTask *pST)) {        
        _callback->attach(fptr);
    }
    
    template<typename T>
    void attach(T* tptr, void (T::*mptr)(SimpleTask *pST)) {  
        if((mptr != NULL) && (tptr != NULL)) { 
            _callback->attach(tptr, mptr);
        }        
    }
    
    bool suspend(bool b) {
        bool prev = _suspended;
        _suspended = b;
        return prev;
    }
    
    // Constructor with frequency in miliseconds
    SimpleTask(int f) { init(f); }
    
    // Constructor with frequency in miliseconds
    SimpleTask(double f) { init(1000.0 * f); }
    
    // Constructor with frequency in miliseconds and function
    SimpleTask(int f, void (*fptr)(SimpleTask *pST)) { 
        init(f); 
        _callback->attach(fptr);
    }
    
    // Constructor with frequency in miliseconds and obj/method
    template<typename T>
    SimpleTask(int f, T* tptr, void (T::*mptr)(SimpleTask *pST)) {  
        init(f);
        if((mptr != NULL) && (tptr != NULL)) { 
            _callback->attach(tptr, mptr);
        }        
    }
    
    // Constructor with frequency in miliseconds and function to call
    SimpleTask(double f, void (*fptr)(SimpleTask *pST)) { 
        init(f * 1000.0); 
        _callback->attach(fptr);
    }
    
    // Constructor with frequency in miliseconds and obj/method to call
    template<typename T>
    SimpleTask(double f, T* tptr, void (T::*mptr)(SimpleTask *pST)) {  
        init(f * 1000.0);
        if((mptr != NULL) && (tptr != NULL)) { 
            _callback->attach(tptr, mptr);
        }        
    }
    
    ~SimpleTask() { 
        delete(_callback);
    }
    
protected:
    STcallback      *_callback;
    uint32_t        _counter;
    uint32_t        _reload;
    bool            _suspended;
    
    void init(int f = 0) {
        _counter = f;
        _reload = f;
        _suspended = false;
        _callback = new STcallback;
    }
};

/** SimpleScheduler
 *
 * Manages a set of function or class.methods as tasks to
 * be called at a scheduled frquency. Simple round-robin
 * task caller. Note, it's cooperative. That means your
 * task functions must return BEFORE another task can be
 * called.
 *
 * @see http://mbed.org/cookbook/SimpleScheduler
 * @see example1.h
 * @see example2.h
 *
 * @code
 * #include "mbed.h"
 * #include "SimpleScheduler.h"
 * 
 * DigitalOut led1(LED1);
 * DigitalOut led2(LED2);
 * DigitalOut led3(LED3);
 * 
 * void f1(SimpleTask *task) { led1 = !led1; }
 * void f2(SimpleTask *task) { led2 = !led2; }
 * void f3(SimpleTask *task) { led3 = !led3; }
 * 
 * SimpleScheduler *scheduler;
 * 
 * int main() {
 * 
 *     scheduler = new SimpleScheduler;
 *     
 *     scheduler
 *         ->addTask( new SimpleTask(0,   f1) ) // As often as possible
 *         ->addTask( new SimpleTask(200, f2) ) // Every 200milliseconds
 *         ->addTask( new SimpleTask(1.0, f3) ) // Once a second
 *     ;
 *     
 *     scheduler->run();    
 * }
 * @endcode
 */
class SimpleScheduler {
protected:
    Ticker _tick;
    list<SimpleTask *> _tasks;
    list<SimpleTask *>::iterator _itor;
    
    friend class Ticker;
    
    void ticktock(void) {
        for (list<SimpleTask *>::iterator itor = _tasks.begin(); itor != _tasks.end(); ++itor) {
            if ((*itor)->_counter && !(*itor)->_suspended) {
                (*itor)->_counter--;
            }                
        }
    }
     
public:
    
    SimpleScheduler * addTask(SimpleTask *t) {
        _tasks.push_back(t);
        return this;
    }
    
    void run(void) {        
        _tick.attach_us(this, &SimpleScheduler::ticktock, 1000);
        while(1) {
            for (list<SimpleTask *>::iterator itor = _tasks.begin(); itor != _tasks.end(); ++itor) {
                if((*itor)->_counter == 0) {
                    (*itor)->_counter = (*itor)->_reload;
                    if (!(*itor)->_suspended) {
                        (*itor)->_callback->call((*itor));  
                    }                  
                }
            }
        }
    }
};

}; // namespace AjK ends.

using namespace AjK;

#endif