User manual - PerttuP/EventTimer GitHub Wiki

The user manual

This page describes the EventTimer component in the user's perspective. This page contains all neccessary information for compiling, deploying and using the component.

General purpose

EventTimer is a generalized version of the SignalGenerator application used in Auxilo2 smart home project. The original application for this component is to schedule smart home events, such as 'Switch coffee machine off after 30 minutes'. SignalGenerator kept track on events and notified the system when events occur. After being generalized, this component can be used for similiar purposes in other context.

Events are stored into persistent database. This allows storing events permanently, and make them survive program start and exit. Since database operations are inherently slow, using this component is feasible only if timing accuracy does not need to be high (response time < 1 second).

Building the EventTimer

EventTimer is a Qt-based cross platform component. Compiling and using the EventTimer requires QtCore and QtSql libraries, coming with Qt-libraries. The software was developed with Qt version 5.4.2. Compatibility with older Qt versions is not guaranteed.

Compiling EventTimer requires a compiler supporting C++11 standard. C++11 features have been avoided in component's public interfaces, and therefore one should be able to use precompiled EventTimer in projects using an older compiler.

EventTimer source code comes with a Qt-project file (EventTimer/EventTimer.pro). Makefile can be generated using qmake. Compile the project. Both debug and release builds are available. Be sure to use the release build in your final product. After compiling, move the output binary file (.dll/.so depending on your system) to location of your choice. Component can be interacted with header files in the EventTimer/inc directory. Copy this directory to location of your choice.

To use the component in your project, link the binary file as a shared library. Add the inc-directory into the project's includepath.

Using the EventTimer component in your code

This section describes broadly how EventTimer can be used in your code. More detailed reference to classes and interfaces is created using Doxygen. Class reference can be generated running Doxygen in the EventTimer directory. Predefined doxyfile is provided there. Class reference gives detailded description for all available classes, methods, types and constants. This documentation is based on doxygen comments embedded into the header files in the EventTimer/inc directory.

Implementing interfaces

Using the EventTimer requires user-provided implementation for the EventHandler interface. This interface implementation is responsible for reacting to occuring events. Interface consists of one method, which will be called from the EventTimer every time events occur. Below is an example implementation, which only prints the name of the occured event.

class MyEventHandler : public EventTimerNS::EventHandler
{
public:
   virtual void notify(const EventTimerNS::Event& e)
   {
      std::cout << e.name().toStdString() << std::endl;
   }
};

User can also provide implementation for the Logger interface. This interface consists of one method that will be called by the EventTimer in certain situations. Implementing this interface is not mandatory. If implementation is not provided, no log messages are generated. Below is an example implementation that prints the log messages.

class MyLogger : public EventTimerNS::Logger
{
public:
   virtual void logMsg(const QString& msg)
   {
      std::cout << msg.toStdString() << std::endl;
   }
};

Instantiating EventTimer

The EventTimerBuilder class is responsible for configuration and instantiation of the EventTimer component. The instantiation is done by calling the static create-method in the EventTimerNS::EventTimerBuilder class. This method creates a new instance of EventTimer. The ownership of new instance is passed to the caller, and the caller is responsible for deleting the instance at some point.

The EventTimerNS::EventTimerBuilder::create method takes a configuration object as its parameter. Members of this object define the configuration parameters for the EventTimer instance. The table below describes these configuration parameters.

Parameter Type Description Notes
dbType QString Type of underlaying database. Refer to QSqlDriver documentation for available values. Using SQLite as underlaying database type is recommended (requires installed sqlite3). Value: 'QSQLITE'
dbName QString Name of the underlaying database. dbName must be a valid database name. Example: 'MyDB.db'
tableName QString Name of used table in the underlaying database. tableName must be a valid SQL identifier. Example: 'events'.
dbHostName QString Database host name (remote databases only). Leave this parameter empty, when using local database (recommended).
userName QString Database user name. Leave this parameter empty, if authentication is not required.
password QString Database user password. Leave this parameter empty if authentication is not required.
refreshRateMsec integer Defines the rate of event polling. EventTimer checks for occured events periodically, and the length of period is defined by this parameter. Value 0 is a special case, where events are not polled, but EventTimer calculates the next occurence time and wakes up for that. Negative values are not allowed. Using value 0 may save CPU time but leads to less predictable response times. Recommended refresh rate is 1000 or greater.

If configuration parameters are invalid or instantiation fails for some other reason, EventTimer is still instantiated, but is not in a valid state. Validity can be checked using the isValid-method of the EventTimer instance. Error message is available calling instance's errorString-method. Discard an invalid EventTimer instance.

When using SQLite as underlaying database type, no other initialization tasks are required. The EventTimer creates the database the table if those do not already exist. Be sure that only one EventTimer instance is using the same table in the same database at the same time.

After instantiation, the user must set at liest the event handler for the EventTimer before starting it. User is required to provide implementation for the EventHandler interface. User can also provide implementation for the Logger interface to receive log messages from the EventTimer. Below is an example code for instantiating EventTimer. Code instantiates EventTimer using 'event_database.db' named SQLite database, and table named 'events' in it. Database is a local database and authentication is not required. Timer checks for occured events every 1000 milliseconds.

// Set configuration and instantiate timer
EventTimerNS::EventTimerBuilderConfiguration conf;
conf.dbType = "QSQLITE";
conf.dbName = "event_database.db";
conf.tableName = "events";
conf.refreshRateMsec = 1000;
EventTimerNS::EventTimer* timer = EventTimerNS::EventTimerBuilder::create(conf);

// Check that timer was instantiated successfully.
if (!timer->isValid()){
   std::cerr << timer->errorString().toStdString() << std::endl;
   // Error handling code...
} 
else {
   // Start timer
   MyEventHandler* handler = new MyEventHandler();   // User implementation for the EventHandler interface.
   MyLogger* logger = new MyLogger();                // User implementation for the Logger interface.
   timer->setHandler(handler);
   timer->setLogger(logger);
   timer->start();
   // etc...
}

Using the EventTimer

After instantiation, EventTimer instace can be accessed through the EventTimer interface. This interface allows following operations:

  1. Scheduling new events. Events can be added using the addEvent method. See doxygen-documentation or header file for reference. Example:
// Set light on after 10 seconds.
QDateTime now = QDateTime::currentDateTime();
EventTimerNS::Event lightOn("lightOn", now.addSecs(10).toString(EventTimerNS::Event::TIME_FORMAT));
timer->addEvent(&lightOn);
if (lightOn.id() == EventTimerNS::Event::UNASSIGNED_ID) {
   std::cerr << timer->errorString().toStdString() << std::endl;
   // Error handling...
]
  1. Removing scheduled events. Events can be removed using the removeEvent-method. See doxygen-documentation or header file for reference. Example:
// Cancel switching light on
if ( !timer->removeEvent(lightOn.id()) ) {
   std::cerr << timer->errorString().toStdString() << std::endl;
   // Error handling...
}
  1. Checking event status. Event can be returned from the database using the getEvent-method. See doxygen-documentation or header file for reference. Example:
// Check how many repeats event has left.
EventTimerNS::Event e = timer->getEvet(repetedEvent.id());
if (e.id() == EventTimerNS::Event::UNASSIGNED_ID){
   if (timer->errorString().isEmpty()) {
      std::cout << "Event has 0 repeats left and has been removed." << std::endl;
   }
   else {
      std::cerr << "Error: " << timer->errorString().toStdString() << std::endl;
      // Error handling...
   }
}
else {
   std::cout << "Repeats left: " << e.repeats() << std::endl;
}
  1. Checking next occuring events. This can be done using the nextEvents-method. See doxygen-documentation or header file for reference. Example:
// Check next occuring event
using namespace EventTimerNS;
std::vector<Event> next = timer->nextEvents(QDateTime::currentDateTime().toString(Event::TIME_FORMAT), 1);
if (next.empty()) {
   if (timer->errorString.isEmpty()) {
      std::cout << "No scheduled events." << std::endl;
   }
   else {
      std::cerr << "Error: " << timer->errorString().toStdString() << std::endl;
      // Error handling...
   }
}
else {
   std::cout << "Next event: " << next[0].name().toStdString() << std::endl;
}
  1. Clearing dynamic events. Events having dynamic type can be removed with cleadDynamic-method. This will be done automatically when start-method is called. See doxygen-documentation or header file for reference.

  2. Clearing all events. The clearAll-method removes all scheduled events from the database, including static and infinitely repeated events. See doxygen-documentation or header file for reference.

  3. Setting new EventHandler. EventHandler can be changed at any time calling the setEventHandler method. See doxygen-documentation or header file for reference.

  4. Setting new Logger. Logger can be changed at any time calling the setLogger method. Log messages can be disabled setting null-pointer as logger. See doxygen-documentation or header file for reference.

⚠️ **GitHub.com Fallback** ⚠️