Added comments

This commit is contained in:
Ralf Ramsauer 2015-05-21 13:35:30 +02:00
parent b498e58ea7
commit f44bf630aa
9 changed files with 148 additions and 55 deletions

View File

@ -15,7 +15,6 @@
#define DEFAULT_LDAP_SERVER "ldaps://ldap.binary.kitchen"
#define DEFAULT_BINDDN "cn=%s,ou=Users,dc=binary-kitchen,dc=de"
#define DEFAULT_LOG_FILE "/var/log/doorlockd.log"
#define DEFAULT_ALLOWED_IP_PREFIX "172.23.3."
#define DEFAULT_PID_FILE "/var/run/doorlockd.pid"
#endif

View File

@ -3,6 +3,11 @@
#include <string>
// Daemonizes the process if daemonize is true.
// If daemonize is true, it will write the new PID to the file "pidFile"
//
// This function will also redirect stdin, out and err to the
// specified files
void daemonize(const bool daemonize,
const std::string &dir,
const std::string &stdinfile,

View File

@ -13,8 +13,8 @@ Door::Door() :
{
_l(LogLevel::info, "Initializing Raspberry Pi GPIOs");
wiringPiSetup();
pinMode(_LOCKPIN, OUTPUT);
pinMode(_SCHNAPPER, OUTPUT);
pinMode(_HEARTBEATPIN, OUTPUT);
pinMode(_SCHNAPPERPIN, OUTPUT);
lock();
}
@ -31,49 +31,73 @@ Door &Door::get()
void Door::lock()
{
digitalWrite(_SCHNAPPER, HIGH);
digitalWrite(_SCHNAPPERPIN, HIGH);
_l(LogLevel::info, "Door closed");
if (_open == true)
if (_state == State::Unlocked)
{
_open = false;
// Stop the Heartbeat Thread
_state = State::Locked;
_heartbeat.join();
}
// Turn off all lights
system("wget -O /dev/null --timeout 3 \"http://homer.binary.kitchen:8080/set?color=000000\" > /dev/null 2>&1");
}
void Door::unlock()
{
// In any case, klacker the schnapper
_schnapper = true;
if (_open == true)
// If heartbeat is already running, return
if (_state == State::Unlocked)
{
return;
}
_open = true;
// If not, first set state to unlocked
_state = State::Unlocked;
// Start the Heartbeat Thread
_heartbeat = std::thread([this] () {
// One "beat" is one complete cycle of the heartbeat clock
auto beat = [] () {
digitalWrite(_LOCKPIN, HIGH);
digitalWrite(_HEARTBEATPIN, HIGH);
usleep(10000);
digitalWrite(_LOCKPIN, LOW);
digitalWrite(_HEARTBEATPIN, LOW);
usleep(10000);
};
digitalWrite(_SCHNAPPER, HIGH);
while (_open) {
// The default of the Schnapperpin: always high
digitalWrite(_SCHNAPPERPIN, HIGH);
// Heartbeat while the state is unlocked
while (_state == State::Unlocked) {
// In case of schnapper, send 0x55 resp. 0xaa to the schnapperpin
if (_schnapper == true)
{
for (int i = 0; i < 32 ; i++)
{
digitalWrite(_SCHNAPPER, LOW);
// Set '0'
digitalWrite(_SCHNAPPERPIN, LOW);
// cycle and send
beat();
digitalWrite(_SCHNAPPER, HIGH);
// Set '1'
digitalWrite(_SCHNAPPERPIN, HIGH);
// cycle and send
beat();
}
digitalWrite(_SCHNAPPER, HIGH);
// Reset schnapperpin
digitalWrite(_SCHNAPPERPIN, HIGH);
// and deactivate schnapper for the next round
_schnapper = false;
}
// Heartbeat
beat();
}
});

View File

@ -6,29 +6,52 @@
#include "logger.h"
/*
* The Door class.
*
* This class exists as a singleton as only one door and hence one object may exist.
* Available via the get() method.
*
* This class is responsible for opening and closing the door by sending
* the heartbeat to the AVR board via Raspberry Pi GPIOs
*
* Whenever the unlock() is called, this class also sends a 0x55 resp. 0xaa
* to the AVR in order to klacker the schnapper.
*/
class Door {
public:
static Door &get();
// Returns the singleton
static Door &get();
~Door();
enum class State {Locked, Unlocked};
// Lock the door
void lock();
// Unlock the door
void unlock();
private:
Door();
// used for logging
const Logger &_l;
bool _open = { false };
// Indicates the internal state: Door is open or locked
State _state = { Door::State::Locked };
// A Heartbeat thread is started when the door is unlocked
std::thread _heartbeat = { };
// Read by the Heartbeat thread if it should klacker the schnapper or not
bool _schnapper = { false };
static constexpr int _LOCKPIN = 10;
static constexpr int _SCHNAPPER = 7;
// WiringPi GPIO Pins
static constexpr int _HEARTBEATPIN = 10;
static constexpr int _SCHNAPPERPIN = 7;
};
#endif

View File

@ -16,7 +16,8 @@ using namespace std;
Epaper::Epaper() :
_logger(Logger::get())
{
memset(_prevImg, 0xFF, ARRAY_SIZE);
memset(_prevImg, 0xFF, _ARRAY_SIZE);
// Initialize Epaper library
bsp_init();
}
@ -32,12 +33,12 @@ Epaper &Epaper::get()
void Epaper::draw(const string &uri)
{
unsigned char buffer[ARRAY_SIZE];
snprintf((char*)buffer, ARRAY_SIZE, "qrencode -l M -d 100 -s 5 \"%s\" -o /tmp/qr.png", uri.c_str());
unsigned char buffer[_ARRAY_SIZE];
snprintf((char*)buffer, _ARRAY_SIZE, "qrencode -l M -d 100 -s 5 \"%s\" -o /tmp/qr.png", uri.c_str());
system((char*)buffer);
FILE* f = popen("composite -geometry +90+0 /tmp/qr.png /usr/local/share/doorlockd/template.png -colorspace gray -depth 1 gray:-", "r");
int i = fread(buffer, ARRAY_SIZE, 1, f);
int i = fread(buffer, _ARRAY_SIZE, 1, f);
if (i != 1)
{
_logger(LogLevel::error, "Image format error");
@ -47,5 +48,5 @@ void Epaper::draw(const string &uri)
pclose(f);
epd_DisplayImg(EPDType_270, buffer, _prevImg);
memcpy(_prevImg, buffer, ARRAY_SIZE);
memcpy(_prevImg, buffer, _ARRAY_SIZE);
}

View File

@ -6,24 +6,35 @@
#include "logger.h"
/*
* The "Epaper" class.
*
* Wrapper for the epaper third Party library.
*
* Exists as singleton, as only one display may exist.
*/
class Epaper {
public:
constexpr static int HEIGHT = 176; // In Pixel
constexpr static int WIDTH = 33; // In Byte
constexpr static int ARRAY_SIZE = HEIGHT * WIDTH;
static Epaper &get();
~Epaper();
// This function will draw template.png to the display and
// convert the uri to a QR-Code and paste it on top of template.png
// using imagemagick
void draw(const std::string &uri);
private:
constexpr static int _HEIGHT = 176; // In Pixel
constexpr static int _WIDTH = 33; // In Byte
constexpr static int _ARRAY_SIZE = _HEIGHT * _WIDTH;
Epaper();
uint8_t _prevImg[ARRAY_SIZE];
// The old image is needed when updating the Epaper display
// It informs the display which pixels have to be flipped
uint8_t _prevImg[_ARRAY_SIZE];
const Logger &_logger;
};

View File

@ -16,16 +16,14 @@ using namespace std;
Logic::Logic(const chrono::seconds tokenTimeout,
const string &ldapServer,
const string &bindDN,
const string &webPrefix,
const string &allowedIpPrefix) :
const string &webPrefix) :
_logger(Logger::get()),
_door(Door::get()),
_epaper(Epaper::get()),
_tokenTimeout(tokenTimeout),
_ldapServer(ldapServer),
_bindDN(bindDN),
_webPrefix(webPrefix),
_allowedIpPrefix(allowedIpPrefix)
_webPrefix(webPrefix)
{
srand(time(NULL));
_createNewToken(false);
@ -34,7 +32,7 @@ Logic::Logic(const chrono::seconds tokenTimeout,
while (_run)
{
unique_lock<mutex> l(_mutex);
_c.wait_for(l, _tokenTimeout);
_tokenCondition.wait_for(l, _tokenTimeout);
if (_run == false)
{
break;
@ -48,7 +46,7 @@ Logic::Logic(const chrono::seconds tokenTimeout,
Logic::~Logic()
{
_run = false;
_c.notify_one();
_tokenCondition.notify_one();
_tokenUpdater.join();
}
@ -119,14 +117,14 @@ out:
Logic::Response Logic::_lock()
{
if (_state == LOCKED)
if (_state == Door::State::Locked)
{
_logger(LogLevel::warning, "Unable to lock: already closed");
return AlreadyLocked;
}
_door.lock();
_state = LOCKED;
_state = Door::State::Locked;
_createNewToken(false);
return Success;
@ -137,12 +135,12 @@ Logic::Response Logic::_unlock()
_door.unlock();
_createNewToken(false);
if (_state == UNLOCKED)
if (_state == Door::State::Unlocked)
{
_logger(LogLevel::warning, "Unable to unlock: already unlocked");
return AlreadyUnlocked;
} else {
_state = UNLOCKED;
_state = Door::State::Unlocked;
}
return Success;

View File

@ -12,6 +12,12 @@
#include "door.h"
#include "logger.h"
/* The "Logic" class
*
* This class is initilized by all settings.
*
* It parses incoming JSON-Requests and returns the Response Code.
*/
class Logic
{
public:
@ -19,8 +25,7 @@ public:
Logic(const std::chrono::seconds tokenTimeout,
const std::string &ldapServer,
const std::string &bindDN,
const std::string &webPrefix,
const std::string &allowedIpPrefix);
const std::string &webPrefix);
~Logic();
enum Response {
@ -37,40 +42,63 @@ public:
LDAPInit, // Ldap initialization failed
};
// Parse incoming JSON Requests
Response parseRequest(const std::string &str);
private:
// Internal lock wrapper
Response _lock();
// Internal unlock wrapper
Response _unlock();
// Checks if the incoming token is valid
bool _checkToken(const std::string &token);
// Checks if incoming credentials against LDAP
Response _checkLDAP(const std::string &user,
const std::string &password);
// Creates a new random token and draws it on the epaper.
// stillValid indicates whether the old (previous) token is still valid
void _createNewToken(const bool stillValid);
const Logger &_logger;
// Door reference
Door &_door;
// Epaper reference
Epaper &_epaper;
// Tokens are 64-bit hexadecimal values
using Token = uint64_t;
// The current token
Token _curToken = { 0x0000000000000000 };
bool _prevValid = { false };
// The previous token
Token _prevToken = { 0x0000000000000000 };
// Indicates whether the previous token is valid
bool _prevValid = { false };
// Tokens are refreshed all tokenTimout seconds
const std::chrono::seconds _tokenTimeout;
const std::string _ldapServer;
const std::string _bindDN;
const std::string _webPrefix;
const std::string _allowedIpPrefix;
// Thread for asynchronosly updating tokens
std::thread _tokenUpdater = {};
std::condition_variable _c = {};
std::mutex _mutex = {};
// Thread can be force-triggered for updates using the condition variable
std::condition_variable _tokenCondition = {};
// stop indicator for the thread
bool _run = true;
// Token mutex
std::mutex _mutex = {};
enum {LOCKED, UNLOCKED} _state = { LOCKED };
// The URI of the ldap server
const std::string _ldapServer;
// LDAP bindDN
const std::string _bindDN;
// Prefix of the website
const std::string _webPrefix;
Door::State _state = { Door::State::Locked };
};
#endif

View File

@ -30,6 +30,9 @@ void signal_handler(int signum)
logic.reset();
}
/*
* Session class handles asynchronosly handles one incoming TCP session
*/
class session
: public std::enable_shared_from_this<session>
{
@ -63,6 +66,9 @@ private:
char _data[_maxLen];
};
/*
* The TCP server
*/
class server
{
@ -105,13 +111,13 @@ int main(int argc, char** argv)
string ldapServer;
string bindDN;
string lockPagePrefix;
string allowedIpPrefix;
string logfile;
string pidFile;
bool foreground = false;
l(LogLevel::notice, "Starting doorlockd");
// Load SPI and I2C modules
system("/usr/bin/gpio load spi");
system("/usr/bin/gpio load i2c");
@ -125,7 +131,6 @@ int main(int argc, char** argv)
("ldap,s", po::value<string>(&ldapServer)->default_value(DEFAULT_LDAP_SERVER), "Ldap Server")
("bidndn,b", po::value<string>(&bindDN)->default_value(DEFAULT_BINDDN), "Bind DN, %s means username")
("web,w", po::value<string>(&lockPagePrefix)->default_value(DEFAULT_WEB_PREFIX), "Prefix of the webpage")
("ip,i", po::value<string>(&allowedIpPrefix)->default_value(DEFAULT_ALLOWED_IP_PREFIX), "Default allowed IP Prefix")
("foreground,f", po::bool_switch(&foreground)->default_value(false), "Run in foreground")
("logfile,l", po::value<string>(&logfile)->default_value(DEFAULT_LOG_FILE), "Log file")
("pid,z", po::value<string>(&pidFile)->default_value(DEFAULT_PID_FILE), "PID file");
@ -166,8 +171,7 @@ int main(int argc, char** argv)
logic = unique_ptr<Logic>(new Logic(tokenTimeout,
ldapServer,
bindDN,
lockPagePrefix,
allowedIpPrefix));
lockPagePrefix));
try {
server s(io_service, port);