I am working on a project that involves scheduling real world events for a pool. Example: turn a pump on at 8AM, turn heater on at 9 AM, set heating temperature to 80 degrees, turn the pump off at 10 AM, etc. Events can be inserted into the queue in order by time and be popped off and processed at a later time. I also wanted the events to be repeatable, aka once placed in the schedule and given a repeat length they would be reinserted to the schedule at a later date. My Schedule class does not process the events, but is rather a queue that stores the necessary information.
Context information:
The project will be running on an Arduino Mega so all std::cout etc. calls will be replaced with Serial.write() when actually implemented. I will be using a timestamp class to use in the project that uses the Time Arduino library. The timestamp can be converted to a time_t value (UNIX time) which is why my class uses unsigned int for time to order the Schedule list.
I would like to follow C++ conventions and make my code more efficient. What works just fine, what could be improved, any errors in logic? Am I missing obvious things a person would want to control in a pool? Performance is not a huge deal since most of the time the program will be sitting idle but I would like to increase readability or make the class easier to use.
Schedule.h
#ifndef _Schedule_h
#define _Schedule_h
#include <iostream>
// Specification file for the Schedule class
class Schedule{
private:
// Declare a structure for the list
struct ListNode {
unsigned int _time; // The value in this node
int _instruction; // ScheduleCommand for scheduling
int _temperature; // Temperature for setting temp command
unsigned int _repeatTime; // Added to previous time for repeated events
struct ListNode* next; // To point to the next node
};
ListNode* headPtr; // List head pointer
public:
Schedule() { headPtr = NULL; } // Constructor
~Schedule(); // Destructor
// Schedule commands
enum ScheduleCommands {PumpOn, PumpOff, HeaterOn, HeaterOff, SetLowTemp, SetHighTemp};
// Displays what's currently in the schedule (for testing only)
void displayList() const;
// Inserts an event to the schedule based on the time
void addEvent(unsigned int time, int command, unsigned int repeatTime, int temperature);
void addEvent(unsigned int time, int command, unsigned int repeatTime);
void addEvent(unsigned int time, int command);
// Removes event from the schedule
bool deleteEvent(unsigned int time, int command);
// For polling whether a new event should be processed
bool timeForNextEvent(unsigned int time) const;
// Gets the next event in the schedule and removes it from the list, returning info
bool popOffNextEvent(int& command, int& temp, unsigned int& repeatTime);
// If a searched for event repeats, it doesn't anymore
bool stopEventRepeat(unsigned int time, int command);
};
#endif /* _Schedule_h */
Schedule.cpp
#include "Schedule.h"
// Displays what's currently in the schedule (for testing only)
void Schedule::displayList() const {
ListNode* nodePtr = headPtr; // To move through the list, starting at head
std::cout << "\n";
// While nodePtr points to a node, traverse the list
while (nodePtr != NULL) {
// Display info about each node
std::cout << "\nTime: " << nodePtr->_time
<< " | Command: " << nodePtr->_instruction
<< " | Repeated: " << nodePtr->_repeatTime;
if (nodePtr->_temperature > 0)
std::cout << "\tTemp: " << nodePtr->_temperature;
nodePtr = nodePtr->next; // Move to the next node
}
std::cout << std::endl;
}
void Schedule::addEvent(unsigned int time, int command){
Schedule::addEvent(time, command, 0, 0);
}
void Schedule::addEvent(unsigned int time, int command, unsigned int repeatTime = 0) {
Schedule::addEvent(time, command, repeatTime, 0);
}
// Inserts a scheduled event into the queue in order by time
void Schedule::addEvent(unsigned int time, int command, unsigned int repeatTime = 0, int temperature = 0) {
ListNode* newNode; // Dynamically allocated ListNode
ListNode* nodePtr; // Goes through list
ListNode* prevNode = NULL; // Previous nodePtr
// Allocate a new node and store info there
newNode = new ListNode;
newNode->_time = time;
newNode->_instruction = command;
newNode->_repeatTime = repeatTime;
newNode->_temperature = temperature;
// If no nodes, newNode is first node
if (headPtr == NULL) {
headPtr = newNode;
newNode->next = NULL;
}
else {
nodePtr = headPtr; // Initialize nodePtr to head of list
prevNode = NULL; // No previous node yet
// Find the node in the list that's higher than passed in value
while ( (nodePtr != NULL) && (nodePtr->_time < time) ) {
prevNode = nodePtr;
nodePtr = nodePtr->next;
}
// Passed in time lower than first in list, so goes first
if (prevNode == NULL) {
headPtr = newNode; // newNode = head node
newNode->next = nodePtr; // newNode is only node, so NULL
}
else { // Insert after previous node
prevNode->next = newNode;
newNode->next = nodePtr;
}
}
}
// Will remove event and repeated events from schedule
bool Schedule::deleteEvent(unsigned int time, int command) {
ListNode* nodePtr; // Current node
ListNode* prevNode; // Previous node
bool nodeDeleted = false;
if (headPtr == NULL) { // If no nodes to delete
//std::cout << "\n No nodes to be deleted!\n\n";
return nodeDeleted;
}
// If first node is to one to deleted
if (headPtr->_time == time && headPtr->_instruction == command) {
nodePtr = headPtr->next;
delete headPtr;
headPtr = nodePtr;
nodeDeleted = true;
}
else {
nodePtr = headPtr; // Start at start of list
// Skip all nodes not equal to passing in value
while ( (nodePtr != NULL) && (nodePtr->_time != time) && (nodePtr->_instruction != command) ) {
prevNode = nodePtr;
nodePtr = nodePtr->next;
}
// If node not at end of list, link prev node to node after
// nodePtr, then delete nodePtr
if (nodePtr != NULL) {
prevNode->next = nodePtr->next;
delete nodePtr;
nodeDeleted = true;
}
else {
//std::cout << "\n Event " << std::endl << time << " is not in the list to delete!";
}
}
return nodeDeleted;
}
// Returns whether it's time for next event processing
bool Schedule::timeForNextEvent(unsigned int time) const {
bool eventNeedsProcessed = false; // Returns whether it's time for next scheduled event
if ( (headPtr != NULL) && (headPtr->_time <= time) ) // Next event's time is passed
eventNeedsProcessed = true;
return eventNeedsProcessed;
}
// Gets the next scheduled events information. It will delete the event but if the event is
// meant to be repeated then it will add a copy of the event at the new location
bool Schedule::popOffNextEvent(int& command, int& temp, unsigned int& repeatTime) {
bool popSuccessful = false; // Returns whether anything was popped
ListNode* nodePtr; // For temp storing
// If there's something in the list, delete it and return values
if (headPtr != NULL) {
command = headPtr->_instruction;
temp = headPtr->_temperature;
repeatTime = headPtr->_repeatTime;
popSuccessful = true;
// If repeat time was specified, re-add the event back into the list at the new time
if (headPtr->_repeatTime > 0) {
addEvent(headPtr->_time + headPtr->_repeatTime, headPtr->_instruction,
headPtr->_repeatTime, headPtr->_temperature);
}
nodePtr = headPtr->next;
delete headPtr;
headPtr = nodePtr;
}
return popSuccessful;
}
// Stop a currently reoccuring scheduled event from repeating again after the next
// time it is popped off (processed)
bool Schedule::stopEventRepeat(unsigned int time, int command) {
bool eventRepeatStopped = false; // Has the event been stopped from repeating?
ListNode* nodePtr;
if (headPtr == NULL) {
// Nothing to stop repeating
}
else {
nodePtr = headPtr; // Initialize nodePtr to head of list
// Find the node in the list that is being searched for
while ( (nodePtr != NULL) &&
(nodePtr->_time != time) && (nodePtr->_instruction != command) && (nodePtr->_repeatTime != 0) )
nodePtr = nodePtr->next;
// If node not at end of list, the event was found. Repeating stopped
if (nodePtr != NULL) {
nodePtr->_repeatTime = 0;
eventRepeatStopped = true;
}
else {
//std::cout << std::endl << "\n Event is not in the list to stop its repeating!";
}
}
return eventRepeatStopped;
}
Schedule::~Schedule() {
ListNode* nodePtr = headPtr; // To traverse the list
ListNode* nextNode; // To point to the next node
while (nodePtr != NULL) { // 'Til end of list
nextNode = nodePtr->next; // Store next node address
delete nodePtr; // Delete current address
nodePtr = nextNode; // Move to next node
}
}
Main.cpp (testing the Schedule class)
#include "Schedule.h"
// Will be removed when moved to Arduino and replaced with Serial.write() calls
#include <iostream>
void timeForNextEventTest(Schedule* scheduleList, unsigned int time);
void popOffNextEventTest(Schedule* scheduleList, int command, int temperature, unsigned int repeatTime);
void deleteEventTest(Schedule* scheduleList, unsigned int time, int command);
void stopEventRepeatTest(Schedule* scheduleList, unsigned int time, int command);
// Tests the Schedule class
void main() {
Schedule scheduleList;
unsigned int time = 0;
int command = 0;
int temperature = 0;
unsigned int repeatTime = 0;
timeForNextEventTest(&scheduleList, 1005);
scheduleList.addEvent(1000, Schedule::PumpOff);
scheduleList.addEvent(2000, Schedule::PumpOn);
scheduleList.addEvent(1500, Schedule::HeaterOn, 0, 80);
scheduleList.addEvent(1750, Schedule::HeaterOff, 150, 75);
scheduleList.addEvent(1600, Schedule::SetLowTemp, 160, 60);
scheduleList.displayList();
timeForNextEventTest(&scheduleList, 995);
timeForNextEventTest(&scheduleList, 1000);
timeForNextEventTest(&scheduleList, 1005);
popOffNextEventTest(&scheduleList, command, temperature, repeatTime);
scheduleList.displayList();
popOffNextEventTest(&scheduleList, command, temperature, repeatTime);
scheduleList.displayList();
popOffNextEventTest(&scheduleList, command, temperature, repeatTime);
scheduleList.displayList();
popOffNextEventTest(&scheduleList, command, temperature, repeatTime);
scheduleList.displayList();
time = 2000;
command = Schedule::PumpOn;
deleteEventTest(&scheduleList, time, command);
deleteEventTest(&scheduleList, time, command);
scheduleList.displayList();
time = 111;
command = 9;
stopEventRepeatTest(&scheduleList, time, command);
time = 1760;
command = 3;
stopEventRepeatTest(&scheduleList, time, command);
scheduleList.displayList();
popOffNextEventTest(&scheduleList, command, temperature, repeatTime);
scheduleList.displayList();
popOffNextEventTest(&scheduleList, command, temperature, repeatTime);
scheduleList.displayList();
std::cout << std::endl;
}
void timeForNextEventTest(Schedule* scheduleList, unsigned int time) {
if (!scheduleList->timeForNextEvent(time))
std::cout << "\n No Events Scheduled By: " << time;
else
std::cout << "\n Next Event Ready By: " << time;
}
void popOffNextEventTest(Schedule* scheduleList, int command, int temperature, unsigned int repeatTime) {
if (scheduleList->popOffNextEvent(command, temperature, repeatTime)) {
std::cout << "\n Event Popped. Command: " << command
<< " RepeatTime: " << repeatTime;
if (temperature > 0)
std::cout << " Temperature: " << temperature;
}
else {
std::cout << "\n No event left in queue.";
}
}
void deleteEventTest(Schedule* scheduleList, unsigned int time, int command) {
if (scheduleList->deleteEvent(time, command))
std::cout << "\n Delete Successful of Time: " << time << " Command: " << command;
else
std::cout << "\n Failure to Delete Time: " << time << " Command: " << command;
}
void stopEventRepeatTest(Schedule* scheduleList, unsigned int time, int command) {
if (scheduleList->stopEventRepeat(time, command))
std::cout << "\n Event Repeat Stopped. Time: " << time << " Command: " << command;
else
std::cout << "\n No Event with specified repeat found. Time: " << time << " Command: " << command;
}