فهرست منبع

TaskScheduler v1.8.2 Local Task Storage and bug fixes

 * implemented Local Task Storage Pointer (allow use of same callback code for different tasks)
 * bug fix: currentTask() method returns incorrect Task reference if called within OnEnable and OnDisable methods
 * protection against infinite loop in OnEnable (if enable() methods are called within OnEnable)
 * new currentLts() method in the scheduler class returns current task's LTS pointer in one call
Anatoli Arkhipenko 10 سال پیش
والد
کامیت
ef2329bdea
7فایلهای تغییر یافته به همراه680 افزوده شده و 239 حذف شده
  1. 8 2
      README
  2. 142 0
      examples/Scheduler_example8_LTS/Scheduler_example8_LTS.ino
  3. BIN
      extras/TaskScheduler.doc
  4. 438 187
      extras/TaskScheduler.html
  5. 3 0
      keywords.txt
  6. 3 3
      library.properties
  7. 86 47
      src/TaskScheduler.h

+ 8 - 2
README

@@ -1,5 +1,5 @@
 Task Scheduler – cooperative multitasking for Arduino microcontrollers
-Version 1.8.1: 2015-10-22
+Version 1.8.2: 2015-10-29
  
  
 OVERVIEW:
@@ -11,7 +11,7 @@ A lightweight implementation of cooperative multitasking (task scheduling) suppo
 5. Power saving via entering IDLE sleep mode between tasks are scheduled to run
 6. Support for task invocation via Status Request object
 7. Support for task IDs and Control Points for error handling and watchdog timer
-
+8. Support for Local Task Storage pointer (allowing use of same callback code for multiple tasks)
 
 Changelog:
 v1.0.0:
@@ -56,3 +56,9 @@ v1.8.0:
 v1.8.1:
     2015-10-22 - implement Task id and control points to support identification of failure points for watchdog timer logging
 	
+v1.8.2:
+    2015-10-27 - implement Local Task Storage Pointer (allow use of same callback code for different tasks)
+    2015-10-27 - bug: currentTask() method returns incorrect Task reference if called within OnEnable and OnDisable methods
+    2015-10-27 - protection against infinite loop in OnEnable (if enable() methods are called within OnEnable)
+    2015-10-29 - new currentLts() method in the scheduler class returns current task's LTS pointer in one call
+	

+ 142 - 0
examples/Scheduler_example8_LTS/Scheduler_example8_LTS.ino

@@ -0,0 +1,142 @@
+/** 
+ *  TaskScheduler Test sketch - use of task's Local Task Storage pointer
+ *  Test case: 
+ *    Overall test runs for 5 seconds
+ *    A number of calculator tasks run every one second, and update their respective variables using Local Task Storage pointer
+ *    All calculator tasks use the same callback code, which obtains reference to appropriate variables via LTS pointer
+ *    Calculaotr tasks perform simple calculation (as an example):
+ *      adding task id number to itself
+ *      multiplying task id number by 10
+ *      
+ *  Upon completion of the overall test, all results are printed out.
+ *  Test could be repeated with various number of calculator tasks. 
+ *  All that needs to change is data definitions - code is completely agnostic of number of tasks
+ */
+ 
+#define _TASK_SLEEP_ON_IDLE_RUN  // Compile with support for entering IDLE SLEEP state for 1 ms if not tasks are scheduled to run
+#define _TASK_WDT_IDS           // Compile with support for Task IDs and Watchdog timer
+#define _TASK_LTS_POINTER       // Compile with support for Local Task Storage pointer
+#include <TaskScheduler.h>
+
+// Overall number of calculator tasks:
+#define NO_TASKS  3
+
+Scheduler ts;
+
+// Calculator tasks.
+// Note that all three tasks use the same callback methods
+// They will be updating specific variables based on the
+// Locat Task Storage pointers 
+Task t1(1000, -1, &Calculate, &ts, false, &CalcOn); 
+Task t2(1000, -1, &Calculate, &ts, false, &CalcOn); 
+Task t3(1000, -1, &Calculate, &ts, false, &CalcOn);
+// add more calc tasks here if necessary
+
+Task tWrapper(5000, 1, NULL, &ts, false, &WrapperOn, &WrapperOff); 
+
+// The below structure is an object referenced by LTS pointer
+typedef struct {
+  unsigned int id;
+  long         sum;
+  long         product;
+} task_var;
+
+// These are actual structures which hold tasks specific values
+task_var v1;
+task_var v2; 
+task_var v3; 
+
+// Arrays below allow indexed access to specific tasks and tasks variables
+Task     *tasks[] = { &t1, &t2, &t3 };
+task_var *vars[]  = { &v1, &v2, &v3 };
+
+
+/**
+ * This method is called when a wrapper task is enabled
+ * The purpose is to supply LTS pointers to all the tasks
+ */
+bool WrapperOn() {
+
+  for (int i=0; i < NO_TASKS; i++) {
+    Task& T = *tasks[i];
+    T.setLtsPointer( vars[i] );
+    T.enableDelayed();
+  }
+  
+  return true;  // Signal that Task could be enabled
+}
+
+/**
+ * This method is called when Wrapper task is disabled (after first and only iteration is executed)
+ * For each of the calculor tasks the results are printed out. 
+ */
+void WrapperOff() {
+  Serial.println("Finished processing");
+  
+  ts.disableAll();
+  
+  for (int i=0; i < NO_TASKS; i++) {
+    Serial.print("ID: "); Serial.println(vars[i]->id);
+    Serial.print("Sum: "); Serial.println(vars[i]->sum);
+    Serial.print("Product: "); Serial.println(vars[i]->product);
+    Serial.println();
+  }
+}
+
+
+/**
+ * This method is executed when each calculator task is enabled
+ * The purpose is to initiate all local variables
+ */
+bool CalcOn() {
+  Task& T = ts.currentTask();
+  task_var& var = *((task_var*) T.getLtsPointer());
+  
+// Initialize local variables
+  var.id = T.getId();
+  var.sum = 0;
+  var.product = var.id;  
+  
+  return true;
+}
+
+
+/**
+ * This method performs simple calculations on task's local variables
+ */
+void Calculate() {
+  Task& T = ts.currentTask();
+// Another way to get to LTS pointer:  
+  task_var& var = *((task_var*) ts.currentLts());
+
+
+  Serial.print("Calculating for task: ");
+  Serial.print(T.getId());
+  Serial.print("; Task id per LTS is: ");
+  Serial.println( var.id );
+  
+  var.sum += T.getId();
+  var.product = var.product * 10;
+  
+}
+
+
+/**
+ * Standard Arduino setup and loo methods
+ */
+void setup() {
+  Serial.begin(115200);
+
+  randomSeed(analogRead(0)+analogRead(5));
+  
+  pinMode(13, OUTPUT);
+  digitalWrite(13, LOW); 
+ 
+  Serial.println("Local Task Storage pointer test");
+
+  tWrapper.enableDelayed();
+}
+
+void loop() {
+  ts.execute();
+}

BIN
extras/TaskScheduler.doc


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 438 - 187
extras/TaskScheduler.html


+ 3 - 0
keywords.txt

@@ -20,6 +20,7 @@ deleteTask	KEYWORD2
 disableAll	KEYWORD2
 enableAll	KEYWORD2
 currentTask	KEYWORD2
+currentLts	KEYWORD2
 execute	KEYWORD2
 allowSleep	KEYWORD2
 enable	KEYWORD2
@@ -56,6 +57,8 @@ setId	KEYWORD2
 getId	KEYWORD2
 setControlPoint	KEYWORD2
 getControlPoint	KEYWORD2
+setLtsPointer	KEYWORD2
+getLtsPointer	KEYWORD2
 
 #######################################
 # Constants (LITERAL1)

+ 3 - 3
library.properties

@@ -1,9 +1,9 @@
 name=TaskScheduler
-version=1.8.1
+version=1.8.2
 author=Anatoli Arkhipenko <arkhipenko@hotmail.com>
 maintainer=Anatoli Arkhipenko <arkhipenko@hotmail.com>
 sentence=A light-weight cooperative multitasking library for arduino microcontrollers.
-paragraph=Enables developers to achieve pseudo multi-tasking. The library is NOT pre-emptive, and as such requires cooperative programming to be used; does NOT use any of the timers therefore does not affect PWM pins. Includes support for status request objects, allowing tasks to wait on and signal event completion between each other. 
-category=Schedulers
+paragraph=Supports: periodic task execution (with dynamic execution period in milliseconds – frequency of execution), number of iterations (limited or infinite number of iterations), execution of tasks in predefined sequence, dynamic change of task execution parameters (frequency, number of iterations, callback methods), power saving via entering IDLE sleep mode when tasks are not scheduled to run, support for event-driven task invocation via Status Request object, support for task IDs and Control Points for error handling and watchdog timer, support for Local Task Storage pointer (allowing use of same callback code for multiple tasks)
+category=Timing
 url=https://github.com/arkhipenko/TaskScheduler.git
 architectures=*

+ 86 - 47
src/TaskScheduler.h

@@ -1,23 +1,23 @@
-// Cooperative multitasking library for Arduino version 1.8.1
+// Cooperative multitasking library for Arduino version 1.8.2
 // Copyright (c) 2015 Anatoli Arkhipenko
 //
 // Changelog:
 // v1.0.0:
 //     2015-02-24 - Initial release 
-//     2015-02-28 - added delay() and disableOnLastIteration() functions
-//     2015-03-25 - changed scheduler execute() function for a more precise delay calculation:
+//     2015-02-28 - added delay() and disableOnLastIteration() methods
+//     2015-03-25 - changed scheduler execute() method for a more precise delay calculation:
 //                  1. Do not delay if any of the tasks ran (making request for immediate execution redundant)
 //                  2. Delay is invoked only if none of the tasks ran 
-//                  3. Delay is based on the min anticipated wait until next task _AND_ the runtime of execute function itself.
-//     2015-05-11 - added  restart() and restartDelayed() functions to restart tasks which are on hold after running all iterations
+//                  3. Delay is based on the min anticipated wait until next task _AND_ the runtime of execute method itself.
+//     2015-05-11 - added  restart() and restartDelayed() methods to restart tasks which are on hold after running all iterations
 //     2015-05-19 - completely removed  delay from the scheduler since there are no power saving there. using 1 ms sleep instead
 //
 // v1.4.1:
-//     2015-09-15 - more careful placement of AVR-specific includes for sleep functions (compatibility with DUE)
+//     2015-09-15 - more careful placement of AVR-specific includes for sleep method (compatibility with DUE)
 //                          sleep on idle run is no longer a default and should be explicitly compiled with _TASK_SLEEP_ON_IDLE_RUN defined
 //
 // v1.5.0:
-//	   2015-09-20 - access to currently executing task (for callback functions)
+//	   2015-09-20 - access to currently executing task (for callback methods)
 //	   2015-09-20 - pass scheduler as a parameter to the task constructor to append the task to the end of the chain
 //     2015-09-20 - option to create a task already enabled
 //
@@ -32,10 +32,10 @@
 //	   2015-10-01 - made version numbers semver compliant (documentation only)
 //
 // v1.7.0:
-//	  2015-10-08 - introduced callback run counter - callback functions can branch on the iteration number. 
+//	  2015-10-08 - introduced callback run counter - callback methods can branch on the iteration number. 
 //	  2015-10-11 - enableIfNot() - enable a task only if it is not already enabled. Returns true if was already enabled, false if was disabled. 
 //	  2015-10-11 - disable() returns previous enable state (true if was enabled, false if was already disabled)
-//	  2015-10-11 - introduced callback functions "on enable" and "on disable". On enable runs every time enable is called, on disable runs only if task was enabled
+//	  2015-10-11 - introduced callback methods "on enable" and "on disable". On enable runs every time enable is called, on disable runs only if task was enabled
 //	  2015-10-12 - new Task method: forceNextIteration() - makes next iteration happen immediately during the next pass regardless how much time is left
 //
 // v1.8.0:
@@ -44,6 +44,13 @@
 //
 // v1.8.1:
 //	  2015-10-22 - implement Task id and control points to support identification of failure points for watchdog timer logging
+//
+// v1.8.2:
+//    2015-10-27 - implement Local Task Storage Pointer (allow use of same callback code for different tasks)
+//    2015-10-27 - bug: currentTask() method returns incorrect Task reference if called within OnEnable and OnDisable methods
+//    2015-10-27 - protection against infinite loop in OnEnable (if enable() methods are called within OnEnable)
+//    2015-10-29 - new currentLts() method in the scheduler class returns current task's LTS pointer in one call
+
 
 /* ============================================
 Cooperative multitasking library code is placed under the MIT license
@@ -80,10 +87,11 @@ THE SOFTWARE.
  * The following "defines" control library functionality at compile time,
  * and should be used in the main sketch depending on the functionality required
  * 
- *	#define _TASK_TIMECRITICAL		// Enable monitoring scheduling overruns
- *	#define _TASK_SLEEP_ON_IDLE_RUN	// Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback functions were invoked during the pass 
- *	#define _TASK_STATUS_REQUEST	// Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
- *	#define _TASK_WDT_IDS		// Compile with support of wdt control points and task ids
+ *	#define _TASK_TIMECRITICAL      // Enable monitoring scheduling overruns
+ *	#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass 
+ *	#define _TASK_STATUS_REQUEST    // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
+ *	#define _TASK_WDT_IDS           // Compile with support for wdt control points and task ids
+ *  #define _TASK_LTS_POINTER       // Compile with support for local task storage pointer
  */
 
 #ifdef _TASK_SLEEP_ON_IDLE_RUN
@@ -104,34 +112,13 @@ class StatusRequest {
 		inline int getStatus() { return iStatus; }
 		
 	private:
-		unsigned int	iCount;
+		unsigned int	iCount;  // waiting for more that 65000 events seems unreasonable: unsigned int should be sufficient
 		int			iStatus;  // negative = error;  zero = OK; >positive = OK with a specific status
 };
 #endif
 
 
-class Task; 
-
-class Scheduler {
-	public:
-		Scheduler();
-		inline void init() { iFirst = NULL; iLast = NULL; iCurrent = NULL; }
-		void addTask(Task& aTask);
-		void deleteTask(Task& aTask);
-		void disableAll();
-		void enableAll();
-		void execute();
-		inline Task& currentTask() {return *iCurrent; }
-#ifdef _TASK_SLEEP_ON_IDLE_RUN
-		void allowSleep(bool aState = true) { iAllowSleep = aState; }
-#endif
-
-	private:
-		Task			*iFirst, *iLast, *iCurrent;
-#ifdef _TASK_SLEEP_ON_IDLE_RUN
-		bool	iAllowSleep;
-#endif
-};
+class Scheduler; 
 
 class Task {
     friend class Scheduler;
@@ -174,12 +161,16 @@ class Task {
 	inline void setControlPoint(unsigned int aPoint) { iControlPoint = aPoint; }
 	inline unsigned int getControlPoint() { return iControlPoint; }
 #endif
-	
+#ifdef _TASK_LTS_POINTER
+	inline void	setLtsPointer(void *aPtr) { iLTS = aPtr; }
+	inline void* getLtsPointer() { return iLTS; }
+#endif
 	
     private:
 	void reset();
 
     volatile bool			iEnabled;
+	bool					iInOnEnable;
     volatile unsigned long	iInterval;
 	volatile unsigned long	iPreviousMillis;
 #ifdef _TASK_TIMECRITICAL
@@ -200,6 +191,34 @@ class Task {
 	unsigned int			iTaskID;
 	unsigned int			iControlPoint;
 #endif
+#ifdef _TASK_LTS_POINTER
+	void					*iLTS;
+#endif
+};
+
+class Scheduler {
+	friend class Task;
+	public:
+		Scheduler();
+		inline void init() { iFirst = NULL; iLast = NULL; iCurrent = NULL; }
+		void addTask(Task& aTask);
+		void deleteTask(Task& aTask);
+		void disableAll();
+		void enableAll();
+		void execute();
+		inline Task& currentTask() {return *iCurrent; }
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+		void allowSleep(bool aState = true) { iAllowSleep = aState; }
+#endif
+#ifdef _TASK_LTS_POINTER
+		inline void* currentLts() {return iCurrent->iLTS; }
+#endif
+
+	private:
+		Task	*iFirst, *iLast, *iCurrent;
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+		bool	iAllowSleep;
+#endif
 };
 
 
@@ -214,13 +233,13 @@ Task::Task( unsigned long aInterval, long aIterations, void (*aCallback)(), Sche
 	reset();
 	set(aInterval, aIterations, aCallback, aOnEnable, aOnDisable);
 	if (aScheduler) aScheduler->addTask(*this);
-	if (aEnable) enable();
 #ifdef _TASK_STATUS_REQUEST
 	iStatusRequest = NULL;
 #endif
 #ifdef _TASK_WDT_IDS
 	iTaskID = ++__task_id_counter;
 #endif
+	if (aEnable) enable();
 }
 
 
@@ -278,7 +297,7 @@ void Task::waitFor(StatusRequest* aStatusRequest) {
  * out of the execution chain as a result
  */
 void Task::reset() {
-	iEnabled = false;
+	iEnabled = iInOnEnable = false;
 	iPreviousMillis = 0;
 	iPrev = NULL;
 	iNext = NULL;
@@ -290,14 +309,17 @@ void Task::reset() {
 #ifdef _TASK_WDT_IDS
 	iControlPoint = 0;
 #endif
+#ifdef _TASK_LTS_POINTER
+	iLTS = NULL;
+#endif
 }
 
 /** Explicitly set Task execution parameters
  * @param aInterval - execution interval in ms
  * @param aIterations - number of iterations, use -1 for no limit
- * @param aCallback - pointer to the callback function which executes the task actions
- * @param aOnEnable - pointer to the callback function which is called on enable()
- * @param aOnDisable - pointer to the callback function which is called on disable()
+ * @param aCallback - pointer to the callback method which executes the task actions
+ * @param aOnEnable - pointer to the callback method which is called on enable()
+ * @param aOnDisable - pointer to the callback method which is called on disable()
  */
 void Task::set(unsigned long aInterval, long aIterations, void (*aCallback)(),bool (*aOnEnable)(), void (*aOnDisable)()) {
 	iInterval = aInterval;
@@ -320,9 +342,21 @@ void Task::setIterations(long aIterations) {
  *  and resets the RunCounter back to zero
  */
 void Task::enable() {
-	iRunCounter = 0;
-	iPreviousMillis = millis() - iInterval;
-	iEnabled = iOnEnable ? (*iOnEnable)() : true;
+	if (iScheduler) { // activation without active scheduler does not make sense
+		iRunCounter = 0;
+		iPreviousMillis = millis() - iInterval;
+		if (iOnEnable && !iInOnEnable) {
+			Task *current = iScheduler->iCurrent;
+			iScheduler->iCurrent = this;
+			iInOnEnable = true;			// Protection against potential infinite loop
+			iEnabled = (*iOnEnable)();
+			iInOnEnable = false;		// Protection against potential infinite loop
+			iScheduler->iCurrent = current;
+		}
+		else {
+			iEnabled = true;
+		}
+	}
 }
 
 /** Enables the task only if it was not enabled already
@@ -375,8 +409,13 @@ void Task::setInterval (unsigned long aInterval) {
  */
 bool Task::disable() {
 	bool previousEnabled = iEnabled;
-	iEnabled = false;
-	if (previousEnabled && iOnDisable) (*iOnDisable)();
+	iEnabled = iInOnEnable = false;
+	if (previousEnabled && iOnDisable) {
+		Task *current = iScheduler->iCurrent;
+		iScheduler->iCurrent = this;
+		(*iOnDisable)();
+		iScheduler->iCurrent = current;
+	}
 	return (previousEnabled);
 }
 

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است