add: version.h
fix: refactor
This commit is contained in:
parent
532750da74
commit
f5f9be803c
36 changed files with 27 additions and 34 deletions
384
src/argparse.c
Normal file
384
src/argparse.c
Normal file
|
@ -0,0 +1,384 @@
|
|||
/**
|
||||
* Copyright (C) 2012-2015 Yecheng Fu <cofyc.jackson at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style license that can be found
|
||||
* in the LICENSE file.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include "argparse.h"
|
||||
|
||||
#define OPT_UNSET 1
|
||||
#define OPT_LONG (1 << 1)
|
||||
|
||||
static const char *
|
||||
prefix_skip(const char *str, const char *prefix)
|
||||
{
|
||||
size_t len = strlen(prefix);
|
||||
return strncmp(str, prefix, len) ? NULL : str + len;
|
||||
}
|
||||
|
||||
static int
|
||||
prefix_cmp(const char *str, const char *prefix)
|
||||
{
|
||||
for (;; str++, prefix++)
|
||||
if (!*prefix) {
|
||||
return 0;
|
||||
} else if (*str != *prefix) {
|
||||
return (unsigned char)*prefix - (unsigned char)*str;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
argparse_error(struct argparse *self, const struct argparse_option *opt,
|
||||
const char *reason, int flags)
|
||||
{
|
||||
(void)self;
|
||||
if (flags & OPT_LONG) {
|
||||
fprintf(stderr, "error: option `--%s` %s\n", opt->long_name, reason);
|
||||
} else {
|
||||
fprintf(stderr, "error: option `-%c` %s\n", opt->short_name, reason);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int
|
||||
argparse_getvalue(struct argparse *self, const struct argparse_option *opt,
|
||||
int flags)
|
||||
{
|
||||
const char *s = NULL;
|
||||
if (!opt->value)
|
||||
goto skipped;
|
||||
switch (opt->type) {
|
||||
case ARGPARSE_OPT_BOOLEAN:
|
||||
if (flags & OPT_UNSET) {
|
||||
*(int *)opt->value = *(int *)opt->value - 1;
|
||||
} else {
|
||||
*(int *)opt->value = *(int *)opt->value + 1;
|
||||
}
|
||||
if (*(int *)opt->value < 0) {
|
||||
*(int *)opt->value = 0;
|
||||
}
|
||||
break;
|
||||
case ARGPARSE_OPT_BIT:
|
||||
if (flags & OPT_UNSET) {
|
||||
*(int *)opt->value &= ~opt->data;
|
||||
} else {
|
||||
*(int *)opt->value |= opt->data;
|
||||
}
|
||||
break;
|
||||
case ARGPARSE_OPT_STRING:
|
||||
if (self->optvalue) {
|
||||
*(const char **)opt->value = self->optvalue;
|
||||
self->optvalue = NULL;
|
||||
} else if (self->argc > 1) {
|
||||
self->argc--;
|
||||
*(const char **)opt->value = *++self->argv;
|
||||
} else {
|
||||
argparse_error(self, opt, "requires a value", flags);
|
||||
}
|
||||
break;
|
||||
case ARGPARSE_OPT_INTEGER:
|
||||
errno = 0;
|
||||
if (self->optvalue) {
|
||||
*(int *)opt->value = strtol(self->optvalue, (char **)&s, 0);
|
||||
self->optvalue = NULL;
|
||||
} else if (self->argc > 1) {
|
||||
self->argc--;
|
||||
*(int *)opt->value = strtol(*++self->argv, (char **)&s, 0);
|
||||
} else {
|
||||
argparse_error(self, opt, "requires a value", flags);
|
||||
}
|
||||
if (errno)
|
||||
argparse_error(self, opt, strerror(errno), flags);
|
||||
if (s[0] != '\0')
|
||||
argparse_error(self, opt, "expects an integer value", flags);
|
||||
break;
|
||||
case ARGPARSE_OPT_FLOAT:
|
||||
errno = 0;
|
||||
if (self->optvalue) {
|
||||
*(float *)opt->value = strtof(self->optvalue, (char **)&s);
|
||||
self->optvalue = NULL;
|
||||
} else if (self->argc > 1) {
|
||||
self->argc--;
|
||||
*(float *)opt->value = strtof(*++self->argv, (char **)&s);
|
||||
} else {
|
||||
argparse_error(self, opt, "requires a value", flags);
|
||||
}
|
||||
if (errno)
|
||||
argparse_error(self, opt, strerror(errno), flags);
|
||||
if (s[0] != '\0')
|
||||
argparse_error(self, opt, "expects a numerical value", flags);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
skipped:
|
||||
if (opt->callback) {
|
||||
return opt->callback(self, opt);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
argparse_options_check(const struct argparse_option *options)
|
||||
{
|
||||
for (; options->type != ARGPARSE_OPT_END; options++) {
|
||||
switch (options->type) {
|
||||
case ARGPARSE_OPT_END:
|
||||
case ARGPARSE_OPT_BOOLEAN:
|
||||
case ARGPARSE_OPT_BIT:
|
||||
case ARGPARSE_OPT_INTEGER:
|
||||
case ARGPARSE_OPT_FLOAT:
|
||||
case ARGPARSE_OPT_STRING:
|
||||
case ARGPARSE_OPT_GROUP:
|
||||
continue;
|
||||
default:
|
||||
fprintf(stderr, "wrong option type: %d", options->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
argparse_short_opt(struct argparse *self, const struct argparse_option *options)
|
||||
{
|
||||
for (; options->type != ARGPARSE_OPT_END; options++) {
|
||||
if (options->short_name == *self->optvalue) {
|
||||
self->optvalue = self->optvalue[1] ? self->optvalue + 1 : NULL;
|
||||
return argparse_getvalue(self, options, 0);
|
||||
}
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
|
||||
static int
|
||||
argparse_long_opt(struct argparse *self, const struct argparse_option *options)
|
||||
{
|
||||
for (; options->type != ARGPARSE_OPT_END; options++) {
|
||||
const char *rest;
|
||||
int opt_flags = 0;
|
||||
if (!options->long_name)
|
||||
continue;
|
||||
|
||||
rest = prefix_skip(self->argv[0] + 2, options->long_name);
|
||||
if (!rest) {
|
||||
// negation disabled?
|
||||
if (options->flags & OPT_NONEG) {
|
||||
continue;
|
||||
}
|
||||
// only OPT_BOOLEAN/OPT_BIT supports negation
|
||||
if (options->type != ARGPARSE_OPT_BOOLEAN && options->type !=
|
||||
ARGPARSE_OPT_BIT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prefix_cmp(self->argv[0] + 2, "no-")) {
|
||||
continue;
|
||||
}
|
||||
rest = prefix_skip(self->argv[0] + 2 + 3, options->long_name);
|
||||
if (!rest)
|
||||
continue;
|
||||
opt_flags |= OPT_UNSET;
|
||||
}
|
||||
if (*rest) {
|
||||
if (*rest != '=')
|
||||
continue;
|
||||
self->optvalue = rest + 1;
|
||||
}
|
||||
return argparse_getvalue(self, options, opt_flags | OPT_LONG);
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
|
||||
int
|
||||
argparse_init(struct argparse *self, struct argparse_option *options,
|
||||
const char *const *usages, int flags)
|
||||
{
|
||||
memset(self, 0, sizeof(*self));
|
||||
self->options = options;
|
||||
self->usages = usages;
|
||||
self->flags = flags;
|
||||
self->description = NULL;
|
||||
self->epilog = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
argparse_describe(struct argparse *self, const char *description,
|
||||
const char *epilog)
|
||||
{
|
||||
self->description = description;
|
||||
self->epilog = epilog;
|
||||
}
|
||||
|
||||
int
|
||||
argparse_parse(struct argparse *self, int argc, const char **argv)
|
||||
{
|
||||
self->argc = argc - 1;
|
||||
self->argv = argv + 1;
|
||||
self->out = argv;
|
||||
|
||||
argparse_options_check(self->options);
|
||||
|
||||
for (; self->argc; self->argc--, self->argv++) {
|
||||
const char *arg = self->argv[0];
|
||||
if (arg[0] != '-' || !arg[1]) {
|
||||
if (self->flags & ARGPARSE_STOP_AT_NON_OPTION) {
|
||||
goto end;
|
||||
}
|
||||
// if it's not option or is a single char '-', copy verbatim
|
||||
self->out[self->cpidx++] = self->argv[0];
|
||||
continue;
|
||||
}
|
||||
// short option
|
||||
if (arg[1] != '-') {
|
||||
self->optvalue = arg + 1;
|
||||
switch (argparse_short_opt(self, self->options)) {
|
||||
case -1:
|
||||
break;
|
||||
case -2:
|
||||
goto unknown;
|
||||
}
|
||||
while (self->optvalue) {
|
||||
switch (argparse_short_opt(self, self->options)) {
|
||||
case -1:
|
||||
break;
|
||||
case -2:
|
||||
goto unknown;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// if '--' presents
|
||||
if (!arg[2]) {
|
||||
self->argc--;
|
||||
self->argv++;
|
||||
break;
|
||||
}
|
||||
// long option
|
||||
switch (argparse_long_opt(self, self->options)) {
|
||||
case -1:
|
||||
break;
|
||||
case -2:
|
||||
goto unknown;
|
||||
}
|
||||
continue;
|
||||
|
||||
unknown:
|
||||
fprintf(stderr, "error: unknown option `%s`\n", self->argv[0]);
|
||||
argparse_usage(self);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
end:
|
||||
memmove(self->out + self->cpidx, self->argv,
|
||||
self->argc * sizeof(*self->out));
|
||||
self->out[self->cpidx + self->argc] = NULL;
|
||||
|
||||
return self->cpidx + self->argc;
|
||||
}
|
||||
|
||||
void
|
||||
argparse_usage(struct argparse *self)
|
||||
{
|
||||
if (self->usages) {
|
||||
fprintf(stdout, "Usage: %s\n", *self->usages++);
|
||||
while (*self->usages && **self->usages)
|
||||
fprintf(stdout, " or: %s\n", *self->usages++);
|
||||
} else {
|
||||
fprintf(stdout, "Usage:\n");
|
||||
}
|
||||
|
||||
// print description
|
||||
if (self->description)
|
||||
fprintf(stdout, "%s\n", self->description);
|
||||
|
||||
fputc('\n', stdout);
|
||||
|
||||
const struct argparse_option *options;
|
||||
|
||||
// figure out best width
|
||||
size_t usage_opts_width = 0;
|
||||
size_t len;
|
||||
options = self->options;
|
||||
for (; options->type != ARGPARSE_OPT_END; options++) {
|
||||
len = 0;
|
||||
if ((options)->short_name) {
|
||||
len += 2;
|
||||
}
|
||||
if ((options)->short_name && (options)->long_name) {
|
||||
len += 2; // separator ", "
|
||||
}
|
||||
if ((options)->long_name) {
|
||||
len += strlen((options)->long_name) + 2;
|
||||
}
|
||||
if (options->type == ARGPARSE_OPT_INTEGER) {
|
||||
len += strlen("=<int>");
|
||||
}
|
||||
if (options->type == ARGPARSE_OPT_FLOAT) {
|
||||
len += strlen("=<flt>");
|
||||
} else if (options->type == ARGPARSE_OPT_STRING) {
|
||||
len += strlen("=<str>");
|
||||
}
|
||||
len = (len + 3) - ((len + 3) & 3);
|
||||
if (usage_opts_width < len) {
|
||||
usage_opts_width = len;
|
||||
}
|
||||
}
|
||||
usage_opts_width += 4; // 4 spaces prefix
|
||||
|
||||
options = self->options;
|
||||
for (; options->type != ARGPARSE_OPT_END; options++) {
|
||||
size_t pos = 0;
|
||||
int pad = 0;
|
||||
if (options->type == ARGPARSE_OPT_GROUP) {
|
||||
fputc('\n', stdout);
|
||||
fprintf(stdout, "%s", options->help);
|
||||
fputc('\n', stdout);
|
||||
continue;
|
||||
}
|
||||
pos = fprintf(stdout, " ");
|
||||
if (options->short_name) {
|
||||
pos += fprintf(stdout, "-%c", options->short_name);
|
||||
}
|
||||
if (options->long_name && options->short_name) {
|
||||
pos += fprintf(stdout, ", ");
|
||||
}
|
||||
if (options->long_name) {
|
||||
pos += fprintf(stdout, "--%s", options->long_name);
|
||||
}
|
||||
if (options->type == ARGPARSE_OPT_INTEGER) {
|
||||
pos += fprintf(stdout, "=<int>");
|
||||
} else if (options->type == ARGPARSE_OPT_FLOAT) {
|
||||
pos += fprintf(stdout, "=<flt>");
|
||||
} else if (options->type == ARGPARSE_OPT_STRING) {
|
||||
pos += fprintf(stdout, "=<str>");
|
||||
}
|
||||
if (pos <= usage_opts_width) {
|
||||
pad = usage_opts_width - pos;
|
||||
} else {
|
||||
fputc('\n', stdout);
|
||||
pad = usage_opts_width;
|
||||
}
|
||||
fprintf(stdout, "%*s%s\n", pad + 2, "", options->help);
|
||||
}
|
||||
|
||||
// print epilog
|
||||
if (self->epilog)
|
||||
fprintf(stdout, "%s\n", self->epilog);
|
||||
}
|
||||
|
||||
int
|
||||
argparse_help_cb(struct argparse *self, const struct argparse_option *option)
|
||||
{
|
||||
(void)option;
|
||||
argparse_usage(self);
|
||||
exit(0);
|
||||
}
|
5016
src/confini.c
Normal file
5016
src/confini.c
Normal file
File diff suppressed because it is too large
Load diff
31
src/database.c
Normal file
31
src/database.c
Normal file
|
@ -0,0 +1,31 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <lmdb.h>
|
||||
|
||||
#include <config.h>
|
||||
#include <constants.h>
|
||||
|
||||
void
|
||||
database_setup(MDB_env **mdb_env, config_t *config)
|
||||
{
|
||||
int err;
|
||||
|
||||
if(mdb_env_create(mdb_env) != 0)
|
||||
{
|
||||
perror("Can't create mdb handle");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if((err = mdb_env_set_maxdbs(*mdb_env, MDB_MAXDBS)) != 0)
|
||||
{
|
||||
fprintf(stderr, "mdb_env_set_maxdbs error %s\n", mdb_strerror(err));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if(mdb_env_open(*mdb_env, config->database, MDB_NOSUBDIR, 0700) != 0)
|
||||
{
|
||||
perror("Can't open mdb file");
|
||||
exit(1);
|
||||
}
|
||||
}
|
14
src/drivers/gpio.c
Normal file
14
src/drivers/gpio.c
Normal file
|
@ -0,0 +1,14 @@
|
|||
#include <wiringPi.h>
|
||||
#include <piFace.h>
|
||||
#include <wiring_debug.h>
|
||||
|
||||
#include <drivers.h>
|
||||
|
||||
void
|
||||
driver_gpio_set(int pin, int value)
|
||||
{
|
||||
// disable "unused parameter" warning (happens when using wiring_debug)
|
||||
(void)pin;
|
||||
(void)value;
|
||||
digitalWrite(pin, value);
|
||||
}
|
14
src/drivers/piface.c
Normal file
14
src/drivers/piface.c
Normal file
|
@ -0,0 +1,14 @@
|
|||
#include <wiringPi.h>
|
||||
#include <piFace.h>
|
||||
#include <wiring_debug.h>
|
||||
|
||||
#include <drivers.h>
|
||||
|
||||
void
|
||||
driver_piface_set(int pin, int value)
|
||||
{
|
||||
// disable "unused parameter" warning (happens when using wiring_debug)
|
||||
(void)pin;
|
||||
(void)value;
|
||||
digitalWrite(PIFACE_GPIO_BASE + pin, value);
|
||||
}
|
142
src/handlers/command.c
Normal file
142
src/handlers/command.c
Normal file
|
@ -0,0 +1,142 @@
|
|||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <uuid/uuid.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <logger.h>
|
||||
#include <handlers.h>
|
||||
#include <helpers.h>
|
||||
#include <enums.h>
|
||||
#include <mpack.h>
|
||||
#include <models/schedule.h>
|
||||
|
||||
static void
|
||||
handler_command_set_name(mpack_node_t map, controller_t *controller)
|
||||
{
|
||||
char name_buffer[MAX_NAME_LENGTH + 1];
|
||||
mpack_node_copy_cstr(mpack_node_map_uint(map, COMMAND_MAPPING_NAME), name_buffer, MAX_NAME_LENGTH + 1);
|
||||
controller_set_name(controller, name_buffer);
|
||||
}
|
||||
|
||||
static void
|
||||
handler_command_set_schedule(mpack_node_t map, controller_t *controller)
|
||||
{
|
||||
uint8_t relay_num = mpack_node_u8(mpack_node_map_uint(map, COMMAND_MAPPING_RELAY_NUM));
|
||||
|
||||
relay_t *target_relay = controller->relays[relay_num];
|
||||
|
||||
mpack_node_t schedules_array = mpack_node_map_uint(map, COMMAND_MAPPING_SCHEDULES_ARRAY);
|
||||
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
mpack_node_t schedules_map = mpack_node_array_at(schedules_array, i);
|
||||
|
||||
uuid_t schedule_id;
|
||||
memcpy(schedule_id, mpack_node_data(mpack_node_map_uint(schedules_map, COMMAND_MAPPING_SCHEDULE_ID)), sizeof(uuid_t));
|
||||
|
||||
uint16_t periods_count = mpack_node_u16(mpack_node_map_uint(schedules_map, COMMAND_MAPPING_PERIODS_COUNT));
|
||||
uint16_t *periods = (uint16_t*)mpack_node_bin_data(mpack_node_map_uint(schedules_map, COMMAND_MAPPING_PERIODS_BLOB));
|
||||
|
||||
if(target_relay->schedules[i])
|
||||
{
|
||||
schedule_free(target_relay->schedules[i]);
|
||||
}
|
||||
target_relay->schedules[i] = schedule_create(schedule_id, i, periods_count, periods);
|
||||
}
|
||||
|
||||
relay_debug(target_relay);
|
||||
}
|
||||
|
||||
static void
|
||||
handler_command_set_relay_name(mpack_node_t map, controller_t *controller)
|
||||
{
|
||||
uint8_t relay_num = mpack_node_u8(mpack_node_map_uint(map, COMMAND_MAPPING_RELAY_NUM));
|
||||
const char *relay_name = mpack_node_str(mpack_node_map_uint(map, COMMAND_MAPPING_NAME));
|
||||
|
||||
if(relay_num < controller->relay_count)
|
||||
{
|
||||
relay_set_name(controller->relays[relay_num], relay_name);
|
||||
}
|
||||
relay_debug(controller->relays[relay_num]);
|
||||
}
|
||||
|
||||
void
|
||||
handler_command(int fd, controller_t *controller)
|
||||
{
|
||||
struct sockaddr_storage their_addr;
|
||||
socklen_t addr_size;
|
||||
int client_fd;
|
||||
|
||||
addr_size = sizeof(their_addr);
|
||||
|
||||
if((client_fd = accept(fd, (struct sockaddr *) &their_addr, &addr_size)) < 0)
|
||||
{
|
||||
LOG_ERROR("could not accept client: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t payload_length;
|
||||
|
||||
if(recv(client_fd, &payload_length, sizeof(payload_length), 0) <= 0)
|
||||
{
|
||||
LOG_ERROR("unable to receive header: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
void *payload = malloc((payload_length + 1));
|
||||
ssize_t bytes_transferred;
|
||||
|
||||
if((bytes_transferred = recv(client_fd, payload, payload_length, 0)) <= 0)
|
||||
{
|
||||
LOG_ERROR("unable to receive payload: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
mpack_tree_t tree;
|
||||
mpack_tree_init_data(&tree, payload, payload_length);
|
||||
mpack_tree_parse(&tree);
|
||||
mpack_node_t root = mpack_tree_root(&tree);
|
||||
|
||||
uint8_t command_code = mpack_node_u8(mpack_node_map_uint(root, COMMAND_MAPPING_CODE));
|
||||
|
||||
LOG_INFO("received command %d\n", command_code);
|
||||
|
||||
switch(command_code)
|
||||
{
|
||||
case COMMAND_CODE_GET_TIME:
|
||||
break;
|
||||
case COMMAND_CODE_GET_ID:
|
||||
break;
|
||||
case COMMAND_CODE_SET_NAME:
|
||||
handler_command_set_name(root, controller);
|
||||
break;
|
||||
case COMMAND_CODE_GET_NAME:
|
||||
break;
|
||||
case COMMAND_CODE_SET_SCHEDULE:
|
||||
handler_command_set_schedule(root, controller);
|
||||
break;
|
||||
case COMMAND_CODE_GET_SCHEDULE:
|
||||
break;
|
||||
case COMMAND_CODE_SET_RELAY_NAME:
|
||||
handler_command_set_relay_name(root, controller);
|
||||
break;
|
||||
case COMMAND_CODE_GET_RELAY_NAME:
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("received invalid command\n");
|
||||
}
|
||||
|
||||
if(mpack_tree_destroy(&tree) != mpack_ok)
|
||||
{
|
||||
LOG_WARN("error when destroying mpack tree\n");
|
||||
}
|
||||
free(payload);
|
||||
close(client_fd);
|
||||
}
|
89
src/handlers/discovery.c
Normal file
89
src/handlers/discovery.c
Normal file
|
@ -0,0 +1,89 @@
|
|||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <uuid/uuid.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <logger.h>
|
||||
#include <handlers.h>
|
||||
#include <helpers.h>
|
||||
#include <mpack.h>
|
||||
#include <enums.h>
|
||||
|
||||
void
|
||||
handler_discovery(int fd, controller_t *controller)
|
||||
{
|
||||
ssize_t bytes_transferred;
|
||||
uint16_t discovery_answer_port;
|
||||
struct sockaddr_in si_other;
|
||||
socklen_t slen = sizeof(si_other);
|
||||
|
||||
if((bytes_transferred = recvfrom(fd, &discovery_answer_port, sizeof(discovery_answer_port), 0, (struct sockaddr *) &si_other, &slen)) <= 0)
|
||||
{
|
||||
LOG_ERROR("received invalid discovery from %s\n", inet_ntoa(si_other.sin_addr));
|
||||
return;
|
||||
}
|
||||
LOG_INFO("received discovery from %s:%d\n", inet_ntoa(si_other.sin_addr), discovery_answer_port);
|
||||
|
||||
if(discovery_answer_port == 0)
|
||||
{
|
||||
LOG_ERROR("invalid port received\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char* payload;
|
||||
size_t payload_size;
|
||||
mpack_writer_t writer;
|
||||
mpack_writer_init_growable(&writer, &payload, &payload_size);
|
||||
|
||||
mpack_start_map(&writer, 4);
|
||||
mpack_write_uint(&writer, DISCOVERY_MAPPING_ID);
|
||||
mpack_write_bin(&writer, (char*)controller->id, sizeof(uuid_t));
|
||||
mpack_write_uint(&writer, DISCOVERY_MAPPING_COMMAND_PORT);
|
||||
mpack_write_u16(&writer, controller->command_port);
|
||||
mpack_write_uint(&writer, DISCOVERY_MAPPING_RELAY_COUNT);
|
||||
mpack_write_u8(&writer, controller->relay_count);
|
||||
mpack_write_uint(&writer, DISCOVERY_MAPPING_NAME);
|
||||
mpack_write_cstr(&writer, controller->name);
|
||||
mpack_finish_map(&writer);
|
||||
|
||||
// finish writing
|
||||
if(mpack_writer_destroy(&writer) != mpack_ok)
|
||||
{
|
||||
LOG_ERROR("error writing discovery answer payload\n");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
int fd_answer = helper_connect_tcp_server(inet_ntoa(si_other.sin_addr), discovery_answer_port);
|
||||
if(fd_answer == -1)
|
||||
{
|
||||
LOG_ERROR("error during connecting\n");
|
||||
free(payload);
|
||||
return;
|
||||
}
|
||||
|
||||
if((bytes_transferred = send(fd_answer, &payload_size, sizeof(payload_size), 0)) <= 0)
|
||||
{
|
||||
LOG_ERROR("error during sending\n");
|
||||
free(payload);
|
||||
close(fd_answer);
|
||||
return;
|
||||
}
|
||||
if((bytes_transferred = send(fd_answer, payload, payload_size, 0)) <= 0)
|
||||
{
|
||||
LOG_ERROR("error during sending\n");
|
||||
free(payload);
|
||||
close(fd_answer);
|
||||
return;
|
||||
}
|
||||
|
||||
free(payload);
|
||||
close(fd_answer);
|
||||
}
|
46
src/handlers/loop.c
Normal file
46
src/handlers/loop.c
Normal file
|
@ -0,0 +1,46 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <logger.h>
|
||||
#include <models/controller.h>
|
||||
#include <handlers.h>
|
||||
#include <drivers.h>
|
||||
#include <enums.h>
|
||||
#include <helpers.h>
|
||||
#include <wiringPi.h>
|
||||
#include <wiring_debug.h>
|
||||
|
||||
void
|
||||
handler_loop(controller_t *controller)
|
||||
{
|
||||
time_t timestamp = time(NULL);
|
||||
struct tm *time_struct = localtime(×tamp);
|
||||
LOG_DEBUG("===== IDLE LOOP START =====\n");
|
||||
for(uint_fast8_t i = 0; i < controller->relay_count; ++i)
|
||||
{
|
||||
relay_t *relay = controller->relays[i];
|
||||
int is_active = 0;
|
||||
if(relay_is_active(relay, time_struct))
|
||||
{
|
||||
LOG_DEBUG("relay %d is active\n", i);
|
||||
is_active = 1;
|
||||
}
|
||||
if(global_config.relay_configs[i].inverted)
|
||||
{
|
||||
is_active = !is_active;
|
||||
}
|
||||
switch(global_config.relay_configs[i].driver)
|
||||
{
|
||||
case RELAY_DRIVER_GPIO:
|
||||
driver_gpio_set(global_config.relay_configs[i].pin, is_active);
|
||||
break;
|
||||
case RELAY_DRIVER_PIFACE:
|
||||
driver_piface_set(global_config.relay_configs[i].pin, is_active);
|
||||
break;
|
||||
default:
|
||||
LOG_WARN("relay %d is not using a driver\n", i);
|
||||
}
|
||||
}
|
||||
}
|
50
src/handlers/poll.c
Normal file
50
src/handlers/poll.c
Normal file
|
@ -0,0 +1,50 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <lmdb.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <logger.h>
|
||||
#include <models/controller.h>
|
||||
#include <database.h>
|
||||
#include <config.h>
|
||||
#include <constants.h>
|
||||
#include <handlers.h>
|
||||
#include <drivers.h>
|
||||
#include <enums.h>
|
||||
#include <helpers.h>
|
||||
#include <wiringPi.h>
|
||||
#include <piFace.h>
|
||||
#include <wiring_debug.h>
|
||||
|
||||
void
|
||||
handler_poll(struct pollfd *fds, controller_t *controller, MDB_env *mdb_env)
|
||||
{
|
||||
/* An event on one of the fds has occurred. */
|
||||
for(int i = 0; i < POLL_FDS_COUNT; i++) {
|
||||
if(fds[i].revents & POLLIN)
|
||||
{
|
||||
/* data may be read on device number i. */
|
||||
LOG_DEBUG("fd %i may read data\n", fds[i].fd);
|
||||
switch(i)
|
||||
{
|
||||
case POLL_FDS_DISCOVERY:
|
||||
handler_discovery(fds[i].fd, controller);
|
||||
break;
|
||||
case POLL_FDS_COMMAND:
|
||||
handler_command(fds[i].fd, controller);
|
||||
controller_save(controller, mdb_env);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(fds[i].revents & POLLHUP)
|
||||
{
|
||||
/* A hangup has occurred on device number i. */
|
||||
LOG_DEBUG("fd %i got closed\n", fds[i].fd);
|
||||
}
|
||||
}
|
||||
}
|
52
src/helpers/bind_server.c
Normal file
52
src/helpers/bind_server.c
Normal file
|
@ -0,0 +1,52 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <logger.h>
|
||||
#include <helpers.h>
|
||||
|
||||
int
|
||||
helper_bind_tcp_server(char* addr, uint16_t port, int max_client_backlog)
|
||||
{
|
||||
char port_str[6];
|
||||
sprintf(port_str, "%d", port);
|
||||
|
||||
struct addrinfo hints, *res;
|
||||
int fd;
|
||||
int status;
|
||||
|
||||
memset(&hints, 0, sizeof hints);
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
|
||||
if ((status = getaddrinfo(addr, port_str, &hints, &res)) != 0)
|
||||
{
|
||||
LOG_ERROR("getaddrinfo: %s\n", gai_strerror(status));
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
||||
|
||||
if ((status = bind(fd, res->ai_addr, res->ai_addrlen)) == -1)
|
||||
{
|
||||
LOG_ERROR("error binding socket: %s\n", strerror(errno));
|
||||
freeaddrinfo(res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((status = listen(fd, max_client_backlog)) == -1)
|
||||
{
|
||||
LOG_ERROR("error setting up listener: %s\n", strerror(errno));
|
||||
freeaddrinfo(res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
|
||||
return fd;
|
||||
}
|
39
src/helpers/connect_server.c
Normal file
39
src/helpers/connect_server.c
Normal file
|
@ -0,0 +1,39 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <logger.h>
|
||||
#include <helpers.h>
|
||||
|
||||
int
|
||||
helper_connect_tcp_server(char* host, uint16_t port)
|
||||
{
|
||||
char port_str[6];
|
||||
sprintf(port_str, "%d", port);
|
||||
|
||||
int s, status;
|
||||
struct addrinfo hints, *res;
|
||||
memset(&hints, 0, sizeof hints);
|
||||
hints.ai_family = AF_INET; //set IP Protocol flag (IPv4 or IPv6 - we don't care)
|
||||
hints.ai_socktype = SOCK_STREAM; //set socket flag
|
||||
|
||||
if ((status = getaddrinfo(host, port_str, &hints, &res)) != 0) { //getaddrinfo() will evaluate the given address, using the hints-flags and port, and return an IP address and other server infos
|
||||
LOG_ERROR("getaddrinfo: %s\n", gai_strerror(status));
|
||||
return -1;
|
||||
}
|
||||
|
||||
//res got filled out by getaddrinfo() for us
|
||||
s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); //creating Socket
|
||||
|
||||
if ((status = connect(s, res->ai_addr, res->ai_addrlen)) != 0) {
|
||||
LOG_ERROR("connect() failed\n");
|
||||
freeaddrinfo(res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
|
||||
return s;
|
||||
}
|
24
src/helpers/get_port.c
Normal file
24
src/helpers/get_port.c
Normal file
|
@ -0,0 +1,24 @@
|
|||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <helpers.h>
|
||||
#include <logger.h>
|
||||
|
||||
uint16_t
|
||||
helper_get_port(int sock)
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
socklen_t len = sizeof(sin);
|
||||
if (getsockname(sock, (struct sockaddr *)&sin, &len) == -1)
|
||||
{
|
||||
LOG_ERROR("could not get socket name for port: %s\n", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ntohs(sin.sin_port);
|
||||
}
|
||||
}
|
11
src/helpers/get_weekday.c
Normal file
11
src/helpers/get_weekday.c
Normal file
|
@ -0,0 +1,11 @@
|
|||
#include <time.h>
|
||||
|
||||
#include <helpers.h>
|
||||
|
||||
int
|
||||
helper_get_weekday(const struct tm *time_struct)
|
||||
{
|
||||
int wday_sun_sat = time_struct->tm_wday;
|
||||
int wday_mon_sun = (wday_sun_sat + 6) % 7;
|
||||
return wday_mon_sun;
|
||||
}
|
115
src/helpers/load_config.c
Normal file
115
src/helpers/load_config.c
Normal file
|
@ -0,0 +1,115 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <helpers.h>
|
||||
#include <config.h>
|
||||
#include <logger.h>
|
||||
#include <confini.h>
|
||||
|
||||
#define CONFINI_IS_KEY(SECTION, KEY) \
|
||||
(ini_array_match(SECTION, disp->append_to, '.', disp->format) && \
|
||||
ini_string_match_ii(KEY, disp->data, disp->format))
|
||||
|
||||
int
|
||||
helper_load_config(IniDispatch *disp, void *config_void)
|
||||
{
|
||||
config_t *config = (config_t*)config_void;
|
||||
char relay_section_name[10]; // "relay-255\0" is longest name
|
||||
|
||||
if(disp->type == INI_KEY)
|
||||
{
|
||||
if(CONFINI_IS_KEY("controller", "name"))
|
||||
{
|
||||
strncpy(config->name, disp->value, MAX_NAME_LENGTH);
|
||||
config->name[MAX_NAME_LENGTH] = '\0';
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY("controller", "database"))
|
||||
{
|
||||
config->database = malloc(sizeof(char) * (strlen(disp->value) + 1));
|
||||
strcpy(config->database, disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY("controller", "log-level"))
|
||||
{
|
||||
if(strcasecmp(disp->value, "trace") == 0)
|
||||
{
|
||||
config->log_level = LOG_LEVEL_TRACE;
|
||||
return 0;
|
||||
}
|
||||
if(strcasecmp(disp->value, "debug") == 0)
|
||||
{
|
||||
config->log_level = LOG_LEVEL_DEBUG;
|
||||
return 0;
|
||||
}
|
||||
if(strcasecmp(disp->value, "info") == 0)
|
||||
{
|
||||
config->log_level = LOG_LEVEL_INFO;
|
||||
return 0;
|
||||
}
|
||||
if(strcasecmp(disp->value, "warn") == 0)
|
||||
{
|
||||
config->log_level = LOG_LEVEL_WARN;
|
||||
return 0;
|
||||
}
|
||||
if(strcasecmp(disp->value, "error") == 0)
|
||||
{
|
||||
config->log_level = LOG_LEVEL_ERROR;
|
||||
return 0;
|
||||
}
|
||||
if(strcasecmp(disp->value, "fatal") == 0)
|
||||
{
|
||||
config->log_level = LOG_LEVEL_FATAL;
|
||||
return 0;
|
||||
}
|
||||
LOG_WARN("invalid log-level '%s'\n", disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY("controller", "discovery-port"))
|
||||
{
|
||||
config->discovery_port = atoi(disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY("controller", "relay-count"))
|
||||
{
|
||||
config->relay_count = atoi(disp->value);
|
||||
config->relay_configs = malloc(sizeof(config_relay_t) * config->relay_count);
|
||||
for(uint8_t i = 0; i < config->relay_count; ++i)
|
||||
{
|
||||
config->relay_configs[i].driver= RELAY_DRIVER_NONE;
|
||||
}
|
||||
LOG_TRACE("config relay-count set to %u\n", config->relay_count);
|
||||
return 0;
|
||||
}
|
||||
for(uint8_t i = 0; i < config->relay_count; ++i)
|
||||
{
|
||||
sprintf(relay_section_name, "relay-%u", i);
|
||||
if(CONFINI_IS_KEY(relay_section_name, "pin"))
|
||||
{
|
||||
config->relay_configs[i].pin = atoi(disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY(relay_section_name, "inverted"))
|
||||
{
|
||||
config->relay_configs[i].inverted = atoi(disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY(relay_section_name, "driver"))
|
||||
{
|
||||
if(strcasecmp(disp->value, "gpio") == 0)
|
||||
{
|
||||
config->relay_configs[i].driver = RELAY_DRIVER_GPIO;
|
||||
return 0;
|
||||
}
|
||||
if(strcasecmp(disp->value, "piface") == 0)
|
||||
{
|
||||
config->relay_configs[i].driver = RELAY_DRIVER_PIFACE;
|
||||
return 0;
|
||||
}
|
||||
LOG_WARN("invalid driver '%s' in section '%s'\n", disp->value, relay_section_name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
57
src/helpers/open_discovery_socket.c
Normal file
57
src/helpers/open_discovery_socket.c
Normal file
|
@ -0,0 +1,57 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <logger.h>
|
||||
#include <helpers.h>
|
||||
|
||||
int
|
||||
helper_open_discovery_socket(uint16_t discovery_port)
|
||||
{
|
||||
struct addrinfo hints, *res;
|
||||
int fd, status;
|
||||
|
||||
memset(&hints, 0, sizeof hints);
|
||||
hints.ai_family = AF_INET; // use ipv4
|
||||
hints.ai_socktype = SOCK_DGRAM; //set socket flag
|
||||
hints.ai_flags = AI_PASSIVE; // get my IP
|
||||
|
||||
char discovery_port_str[6];
|
||||
sprintf(discovery_port_str, "%u", discovery_port);
|
||||
|
||||
//get connection info for our computer
|
||||
if ((status = getaddrinfo(NULL, discovery_port_str, &hints, &res)) != 0)
|
||||
{
|
||||
LOG_FATAL("getaddrinfo: %s\n", gai_strerror(status));
|
||||
freeaddrinfo(res);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
//creating socket
|
||||
fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
||||
int yes = 1;
|
||||
|
||||
// lose the pesky "Address already in use" error message
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1)
|
||||
{
|
||||
LOG_FATAL("setsockopt: %s\n", strerror(errno));
|
||||
freeaddrinfo(res);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (bind(fd, res->ai_addr, res->ai_addrlen) == -1)
|
||||
{
|
||||
LOG_FATAL("bind: %s\n", strerror(errno));
|
||||
freeaddrinfo(res);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
|
||||
LOG_INFO("opened discovery socket on port %u\n", discovery_port);
|
||||
|
||||
return fd;
|
||||
}
|
70
src/helpers/parse_cli.c
Normal file
70
src/helpers/parse_cli.c
Normal file
|
@ -0,0 +1,70 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <argparse.h>
|
||||
#include <config.h>
|
||||
#include <logger.h>
|
||||
#include <helpers.h>
|
||||
#include <version.h>
|
||||
|
||||
static const char *const usage[] = {
|
||||
"controller [options] [[--] args]",
|
||||
"controller [options]",
|
||||
NULL,
|
||||
};
|
||||
|
||||
#define PERM_READ (1<<0)
|
||||
#define PERM_WRITE (1<<1)
|
||||
#define PERM_EXEC (1<<2)
|
||||
|
||||
void
|
||||
helper_parse_cli(int argc, const char **argv, config_t *config)
|
||||
{
|
||||
int version = 0;
|
||||
struct argparse_option options[] =
|
||||
{
|
||||
OPT_HELP(),
|
||||
OPT_GROUP("Basic options"),
|
||||
OPT_STRING('c', "config", &config->file, "path to config file", NULL, 0, OPT_NONEG),
|
||||
OPT_BOOLEAN('v', "version", &version, "print version", NULL, 0, OPT_NONEG),
|
||||
OPT_END(),
|
||||
};
|
||||
|
||||
struct argparse argparse;
|
||||
argparse_init(&argparse, options, usage, 0);
|
||||
argparse_describe(
|
||||
&argparse,
|
||||
"\nA brief description of what the program does and how it works.",
|
||||
"\nAdditional description of the program after the description of the arguments."
|
||||
);
|
||||
argc = argparse_parse(&argparse, argc, argv);
|
||||
|
||||
if(version)
|
||||
{
|
||||
printf("%s\n", EMGAUWA_CONTROLLER_VERSION);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if(argc == 1)
|
||||
{
|
||||
if(strcmp(argv[0], "start") == 0)
|
||||
{
|
||||
config->run_type = RUN_TYPE_START;
|
||||
return;
|
||||
}
|
||||
if(strcmp(argv[0], "test") == 0)
|
||||
{
|
||||
config->run_type = RUN_TYPE_TEST;
|
||||
return;
|
||||
}
|
||||
LOG_FATAL("bad action '%s' given ('start', 'test')\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_FATAL("no action given ('start', 'test')\n");
|
||||
exit(1);
|
||||
}
|
||||
return;
|
||||
}
|
58
src/logger.c
Normal file
58
src/logger.c
Normal file
|
@ -0,0 +1,58 @@
|
|||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <config.h>
|
||||
#include <logger.h>
|
||||
|
||||
#define COLOR_TRACE COLOR_GREEN
|
||||
#define COLOR_DEBUG COLOR_BLUE
|
||||
#define COLOR_INFO COLOR_CYAN
|
||||
#define COLOR_WARN COLOR_YELLOW
|
||||
#define COLOR_ERROR COLOR_RED
|
||||
#define COLOR_FATAL COLOR_MAGENTA
|
||||
|
||||
void
|
||||
logger_log(FILE *stream, log_level_t level, const char *filename, int line, const char *func, const char *msg, ...)
|
||||
{
|
||||
if(global_config.log_level < level)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch(level)
|
||||
{
|
||||
case LOG_LEVEL_TRACE:
|
||||
fprintf(stream, COLOR_TRACE "[TRACE] ");
|
||||
break;
|
||||
case LOG_LEVEL_DEBUG:
|
||||
fprintf(stream, COLOR_DEBUG "[DEBUG] ");
|
||||
break;
|
||||
case LOG_LEVEL_INFO:
|
||||
fprintf(stream, COLOR_INFO "[INFO ] ");
|
||||
break;
|
||||
case LOG_LEVEL_WARN:
|
||||
fprintf(stream, COLOR_WARN "[WARN ] ");
|
||||
break;
|
||||
case LOG_LEVEL_ERROR:
|
||||
fprintf(stream, COLOR_ERROR "[ERROR] ");
|
||||
break;
|
||||
case LOG_LEVEL_FATAL:
|
||||
fprintf(stream, COLOR_FATAL "[FATAL] ");
|
||||
break;
|
||||
default:
|
||||
fprintf(stream, COLOR_NONE "[LOG ] ");
|
||||
break;
|
||||
}
|
||||
|
||||
char timestamp_str[32];
|
||||
time_t rawtime = time(NULL);
|
||||
strftime(timestamp_str, 32, "%Y-%m-%d %H:%M:%S", localtime(&rawtime));
|
||||
|
||||
fprintf(stream, "%s %s:%d:%s " COLOR_NONE, timestamp_str, filename, line, func);
|
||||
|
||||
va_list args;
|
||||
va_start(args, msg);
|
||||
vfprintf(stream, msg, args);
|
||||
va_end(args);
|
||||
}
|
168
src/main.c
Normal file
168
src/main.c
Normal file
|
@ -0,0 +1,168 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <lmdb.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <logger.h>
|
||||
#include <models/controller.h>
|
||||
#include <database.h>
|
||||
#include <config.h>
|
||||
#include <constants.h>
|
||||
#include <handlers.h>
|
||||
#include <drivers.h>
|
||||
#include <enums.h>
|
||||
#include <runners.h>
|
||||
#include <helpers.h>
|
||||
#include <wiringPi.h>
|
||||
#include <piFace.h>
|
||||
#include <wiring_debug.h>
|
||||
#include <confini.h>
|
||||
|
||||
config_t global_config;
|
||||
|
||||
static MDB_env *mdb_env;
|
||||
static controller_t *this_controller;
|
||||
static struct pollfd poll_fds[POLL_FDS_COUNT];
|
||||
|
||||
static void
|
||||
terminate(int signum)
|
||||
{
|
||||
LOG_INFO("terminating controller (%d)\n", signum);
|
||||
|
||||
for(int i = 0; i < POLL_FDS_COUNT; ++i)
|
||||
{
|
||||
close(poll_fds[i].fd);
|
||||
}
|
||||
|
||||
mdb_env_close(mdb_env);
|
||||
|
||||
controller_free(this_controller);
|
||||
|
||||
free(global_config.database);
|
||||
free(global_config.relay_configs);
|
||||
|
||||
exit(signum);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The main function
|
||||
*
|
||||
* @param argc UNUSED
|
||||
* @param argv UNUSED
|
||||
*
|
||||
* @return Statuscode to indicate success (0) or failure (!0)
|
||||
*/
|
||||
int
|
||||
main(int argc, const char** argv)
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
signal(SIGINT, terminate);
|
||||
signal(SIGABRT, terminate);
|
||||
signal(SIGTERM, terminate);
|
||||
|
||||
/******************** LOAD CONFIG ********************/
|
||||
|
||||
global_config.file = "controller.ini";
|
||||
global_config.log_level = LOG_LEVEL_INFO;
|
||||
|
||||
helper_parse_cli(argc, argv, &global_config);
|
||||
|
||||
FILE * const ini_file = fopen(global_config.file, "rb");
|
||||
if(ini_file == NULL)
|
||||
{
|
||||
LOG_FATAL("config file '%s' was not found\n", global_config.file);
|
||||
exit(1);
|
||||
}
|
||||
if(load_ini_file( ini_file, INI_DEFAULT_FORMAT, NULL, helper_load_config, &global_config))
|
||||
{
|
||||
LOG_FATAL("unable to parse ini file\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fclose(ini_file);
|
||||
|
||||
if(sizeof(time_t) < 8)
|
||||
{
|
||||
LOG_WARN("this system is not using 8-bit time\n");
|
||||
}
|
||||
|
||||
/******************** SETUP DATABASE AND THIS CONTROLLER ********************/
|
||||
|
||||
database_setup(&mdb_env, &global_config);
|
||||
|
||||
this_controller = controller_load(mdb_env);
|
||||
|
||||
int fd_discovery = helper_open_discovery_socket(this_controller->discovery_port);
|
||||
int fd_command = helper_bind_tcp_server("0.0.0.0", this_controller->command_port, 128);
|
||||
|
||||
this_controller->command_port = helper_get_port(fd_command);
|
||||
|
||||
controller_save(this_controller, mdb_env);
|
||||
|
||||
|
||||
/******************** SETUP WIRINGPI ********************/
|
||||
|
||||
wiringPiSetup();
|
||||
piFaceSetup(PIFACE_GPIO_BASE);
|
||||
|
||||
for(uint_fast8_t i = 0; i < this_controller->relay_count; ++i)
|
||||
{
|
||||
if(global_config.relay_configs[i].driver == RELAY_DRIVER_GPIO)
|
||||
{
|
||||
pinMode(global_config.relay_configs[i].pin, OUTPUT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/******************** SETUP SOCKETS ********************/
|
||||
|
||||
int timeout_msecs = ACCEPT_TIMEOUT_MSECONDS;
|
||||
int ret;
|
||||
|
||||
/* Open STREAMS device. */
|
||||
poll_fds[POLL_FDS_DISCOVERY].fd = fd_discovery;
|
||||
poll_fds[POLL_FDS_DISCOVERY].events = POLLIN;
|
||||
LOG_DEBUG("setup fd_discovery as %i on index %i\n", fd_discovery, POLL_FDS_DISCOVERY);
|
||||
poll_fds[POLL_FDS_COMMAND].fd = fd_command;
|
||||
poll_fds[POLL_FDS_COMMAND].events = POLLIN;
|
||||
LOG_DEBUG("setup fd_command as %i on index %i\n", fd_command, POLL_FDS_COMMAND);
|
||||
|
||||
|
||||
/******************** CHECK FOR TESTING RUN ********************/
|
||||
|
||||
if(global_config.run_type == RUN_TYPE_TEST)
|
||||
{
|
||||
runner_test(this_controller);
|
||||
terminate(0);
|
||||
}
|
||||
|
||||
|
||||
/******************** START MAIN LOOP ********************/
|
||||
|
||||
for(;;)
|
||||
{
|
||||
ret = poll(poll_fds, POLL_FDS_COUNT, timeout_msecs);
|
||||
|
||||
if(ret == 0)
|
||||
{
|
||||
handler_loop(this_controller);
|
||||
}
|
||||
if(ret > 0)
|
||||
{
|
||||
handler_poll(poll_fds, this_controller, mdb_env);
|
||||
}
|
||||
}
|
||||
|
||||
close(fd_discovery);
|
||||
|
||||
mdb_env_close(mdb_env);
|
||||
|
||||
return 0;
|
||||
}
|
72
src/models/controller.c
Normal file
72
src/models/controller.c
Normal file
|
@ -0,0 +1,72 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
#include <models/controller.h>
|
||||
#include <macros.h>
|
||||
#include <config.h>
|
||||
#include <constants.h>
|
||||
|
||||
controller_t*
|
||||
controller_create(void)
|
||||
{
|
||||
controller_t *new_controller = malloc(sizeof(*new_controller));
|
||||
uuid_generate(new_controller->id);
|
||||
|
||||
strncpy(new_controller->name, global_config.name, MAX_NAME_LENGTH);
|
||||
new_controller->name[MAX_NAME_LENGTH] = '\0';
|
||||
|
||||
new_controller->command_port = 0;
|
||||
new_controller->discovery_port = global_config.discovery_port;
|
||||
new_controller->relay_count = global_config.relay_count;
|
||||
|
||||
new_controller->relays = malloc(sizeof(relay_t) * new_controller->relay_count);
|
||||
uint8_t i;
|
||||
for(i = 0; i < new_controller->relay_count; ++i)
|
||||
{
|
||||
new_controller->relays[i] = relay_create(i);
|
||||
}
|
||||
|
||||
return new_controller;
|
||||
}
|
||||
|
||||
void
|
||||
controller_set_name(controller_t *controller, const char *name)
|
||||
{
|
||||
strncpy(controller->name, name, MAX_NAME_LENGTH);
|
||||
controller->name[MAX_NAME_LENGTH] = '\0';
|
||||
}
|
||||
|
||||
void
|
||||
controller_free(controller_t *controller)
|
||||
{
|
||||
for(int i = 0; i < controller->relay_count; ++i)
|
||||
{
|
||||
relay_free(controller->relays[i]);
|
||||
}
|
||||
free(controller->relays);
|
||||
free(controller);
|
||||
}
|
||||
|
||||
void
|
||||
controller_debug(controller_t *controller)
|
||||
{
|
||||
if(controller == NULL)
|
||||
{
|
||||
LOG_DEBUG("controller is NULL\n");
|
||||
return;
|
||||
}
|
||||
char uuid_str[37];
|
||||
uuid_unparse(controller->id, uuid_str);
|
||||
LOG_DEBUG("(1/5) %s @ %p\n", uuid_str, (void*)controller);
|
||||
LOG_DEBUG("(2/5) name: %s\n", controller->name);
|
||||
LOG_DEBUG("(3/5) command_port: %5d discovery_port: %5d\n", controller->command_port, controller->discovery_port);
|
||||
LOG_DEBUG("(4/5) relay count: %3d\n", controller->relay_count);
|
||||
LOG_DEBUG("(5/5) relays @ %p:\n", (void*)controller->relays);
|
||||
for(int i = 0; i < controller->relay_count; ++i)
|
||||
{
|
||||
relay_debug(controller->relays[i]);
|
||||
}
|
||||
}
|
85
src/models/controller_load.c
Normal file
85
src/models/controller_load.c
Normal file
|
@ -0,0 +1,85 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
#include <models/controller.h>
|
||||
#include <macros.h>
|
||||
|
||||
static void
|
||||
controller_load_single(MDB_txn *mdb_txn, MDB_dbi mdb_dbi, db_key_controller_e key_controller, MDB_val *value)
|
||||
{
|
||||
int err;
|
||||
MDB_val key;
|
||||
|
||||
key.mv_size = sizeof(db_key_controller_e);
|
||||
key.mv_data = &key_controller;
|
||||
|
||||
if((err = mdb_get(mdb_txn, mdb_dbi, &key, value)) != 0)
|
||||
{
|
||||
LOG_ERROR("mdb_get error %s\n", mdb_strerror(err));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
controller_t*
|
||||
controller_load(MDB_env *mdb_env)
|
||||
{
|
||||
int err;
|
||||
MDB_txn *mdb_txn;
|
||||
MDB_dbi mdb_dbi;
|
||||
|
||||
controller_t *new_controller;
|
||||
|
||||
if((err = mdb_txn_begin(mdb_env, NULL, MDB_RDONLY, &mdb_txn)) != 0)
|
||||
{
|
||||
LOG_ERROR("mdb_txn_begin error %s\n", mdb_strerror(err));
|
||||
return NULL;
|
||||
}
|
||||
if((err = mdb_dbi_open(mdb_txn, "controller", 0, &mdb_dbi)) != 0)
|
||||
{
|
||||
switch(err)
|
||||
{
|
||||
case MDB_NOTFOUND:
|
||||
LOG_INFO("no controller found in db. creating new one\n");
|
||||
mdb_txn_abort(mdb_txn);
|
||||
new_controller = controller_create();
|
||||
controller_save(new_controller, mdb_env);
|
||||
return new_controller;
|
||||
default:
|
||||
LOG_ERROR("mdb_txn_begin error %s\n", mdb_strerror(err));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
new_controller = malloc(sizeof(controller_t));
|
||||
|
||||
MDB_val value;
|
||||
|
||||
controller_load_single(mdb_txn, mdb_dbi, DB_KEY_CONTROLLER_ID, &value);
|
||||
memmove(new_controller->id, (uuid_t*)value.mv_data, sizeof(uuid_t));
|
||||
|
||||
controller_load_single(mdb_txn, mdb_dbi, DB_KEY_CONTROLLER_NAME, &value);
|
||||
strncpy(new_controller->name, (char*)value.mv_data, MAX_NAME_LENGTH);
|
||||
new_controller->name[MAX_NAME_LENGTH] = '\0';
|
||||
|
||||
controller_load_single(mdb_txn, mdb_dbi, DB_KEY_CONTROLLER_COMMAND_PORT, &value);
|
||||
new_controller->command_port = ((uint16_t*)value.mv_data)[0];
|
||||
|
||||
controller_load_single(mdb_txn, mdb_dbi, DB_KEY_CONTROLLER_DISCOVERY_PORT, &value);
|
||||
new_controller->discovery_port = ((uint16_t*)value.mv_data)[0];
|
||||
|
||||
new_controller->relay_count = global_config.relay_count;
|
||||
|
||||
mdb_txn_abort(mdb_txn); // transaction is read only
|
||||
|
||||
new_controller->relays = malloc(sizeof(relay_t*) * new_controller->relay_count);
|
||||
for(uint8_t i = 0; i < new_controller->relay_count; i++)
|
||||
{
|
||||
LOG_TRACE("loading relay %d\n", i);
|
||||
new_controller->relays[i] = relay_load(mdb_env, i);
|
||||
}
|
||||
|
||||
return new_controller;
|
||||
}
|
90
src/models/controller_save.c
Normal file
90
src/models/controller_save.c
Normal file
|
@ -0,0 +1,90 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
#include <models/controller.h>
|
||||
#include <macros.h>
|
||||
|
||||
int
|
||||
controller_save_single(MDB_txn *mdb_txn, MDB_dbi mdb_dbi, db_key_controller_e key_controller, MDB_val value)
|
||||
{
|
||||
int err;
|
||||
|
||||
MDB_val key;
|
||||
key.mv_size = sizeof(db_key_controller_e);
|
||||
key.mv_data = &key_controller;
|
||||
|
||||
if((err = mdb_put(mdb_txn, mdb_dbi, &key, &value, 0)) != 0)
|
||||
{
|
||||
LOG_ERROR("mdb_put error %s\n", mdb_strerror(err));
|
||||
mdb_txn_abort(mdb_txn);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
controller_save(controller_t *controller, MDB_env *mdb_env)
|
||||
{
|
||||
int err;
|
||||
|
||||
MDB_txn *mdb_txn;
|
||||
MDB_dbi mdb_dbi;
|
||||
MDB_val value;
|
||||
|
||||
if((err = mdb_txn_begin(mdb_env, NULL, 0, &mdb_txn)) != 0)
|
||||
{
|
||||
LOG_ERROR("mdb_txn_begin error %s\n", mdb_strerror(err));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if((err = mdb_dbi_open(mdb_txn, "controller", MDB_CREATE, &mdb_dbi)) != 0)
|
||||
{
|
||||
LOG_ERROR("mdb_dbi_open error %s\n", mdb_strerror(err));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
value.mv_size = sizeof(uuid_t);
|
||||
value.mv_data = controller->id;
|
||||
if(controller_save_single(mdb_txn, mdb_dbi, DB_KEY_CONTROLLER_ID, value))
|
||||
{
|
||||
LOG_ERROR("failed to save ID\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
value.mv_size = sizeof(char) * (strlen(controller->name) + 1);
|
||||
value.mv_data = controller->name;
|
||||
if(controller_save_single(mdb_txn, mdb_dbi, DB_KEY_CONTROLLER_NAME, value))
|
||||
{
|
||||
LOG_ERROR("failed to save name\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
value.mv_size = sizeof(controller->command_port);
|
||||
value.mv_data = &controller->command_port;
|
||||
if(controller_save_single(mdb_txn, mdb_dbi, DB_KEY_CONTROLLER_COMMAND_PORT, value))
|
||||
{
|
||||
LOG_ERROR("failed to save command port\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
value.mv_size = sizeof(controller->discovery_port);
|
||||
value.mv_data = &controller->discovery_port;
|
||||
if(controller_save_single(mdb_txn, mdb_dbi, DB_KEY_CONTROLLER_DISCOVERY_PORT, value))
|
||||
{
|
||||
LOG_ERROR("failed to save discovery port\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
mdb_txn_commit(mdb_txn);
|
||||
|
||||
for(uint8_t i = 0; i < controller->relay_count; ++i)
|
||||
{
|
||||
LOG_TRACE("saving relays[%d/%d]\n", i, controller->relay_count);
|
||||
relay_save(controller->relays[i], mdb_env);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
47
src/models/period.c
Normal file
47
src/models/period.c
Normal file
|
@ -0,0 +1,47 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include <logger.h>
|
||||
#include <constants.h>
|
||||
#include <models/period.h>
|
||||
|
||||
period_t*
|
||||
period_create(uint16_t start, uint16_t end)
|
||||
{
|
||||
period_t *new_period = malloc(sizeof(period_t));
|
||||
new_period->start = start;
|
||||
new_period->end = end;
|
||||
|
||||
return new_period;
|
||||
}
|
||||
|
||||
int
|
||||
period_includes_time(period_t *period, struct tm *time_struct)
|
||||
{
|
||||
uint16_t start = period->start;
|
||||
uint16_t end = period->end;
|
||||
|
||||
time_t timestamp = time_struct->tm_hour * 60;
|
||||
timestamp += time_struct->tm_min;
|
||||
|
||||
// "normal" timespan
|
||||
if(start < end)
|
||||
{
|
||||
if(start <= timestamp && end > timestamp)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// timespan goes through 00:00
|
||||
if(end < start)
|
||||
{
|
||||
if(start >= timestamp && end < timestamp)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
80
src/models/relay.c
Normal file
80
src/models/relay.c
Normal file
|
@ -0,0 +1,80 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
#include <logger.h>
|
||||
#include <helpers.h>
|
||||
#include <models/relay.h>
|
||||
|
||||
relay_t*
|
||||
relay_create(uint8_t number)
|
||||
{
|
||||
relay_t *new_relay = malloc(sizeof(relay_t));
|
||||
|
||||
new_relay->number = number;
|
||||
new_relay->name[0] = '\0';
|
||||
|
||||
uuid_t off_id;
|
||||
memset(off_id, 0, sizeof(uuid_t));
|
||||
memcpy(off_id, "off", 3);
|
||||
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
new_relay->schedules[i] = schedule_create(off_id, i, 0, NULL);
|
||||
}
|
||||
|
||||
return new_relay;
|
||||
}
|
||||
|
||||
void
|
||||
relay_set_name(relay_t *relay, const char *name)
|
||||
{
|
||||
strncpy(relay->name, name, MAX_NAME_LENGTH);
|
||||
relay->name[MAX_NAME_LENGTH] = '\0';
|
||||
}
|
||||
|
||||
int
|
||||
relay_is_active(relay_t *relay, struct tm *time_struct)
|
||||
{
|
||||
schedule_t *schedule = relay->schedules[helper_get_weekday(time_struct)];
|
||||
if(schedule->length == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
for(uint16_t i = 0; i < schedule->length; ++i)
|
||||
{
|
||||
if(period_includes_time(schedule->periods[i], time_struct))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
relay_debug(relay_t *relay)
|
||||
{
|
||||
if(relay == NULL)
|
||||
{
|
||||
LOG_DEBUG("relay is NULL\n");
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG("(1/3) %d @ %p\n", relay->number, (void*)relay);
|
||||
LOG_DEBUG("(2/3) name: %s\n", relay->name);
|
||||
LOG_DEBUG("(3/3) schedules @ %p:\n", (void*)relay->schedules);
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
schedule_debug(relay->schedules[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
relay_free(relay_t *relay)
|
||||
{
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
schedule_free(relay->schedules[i]);
|
||||
}
|
||||
free(relay);
|
||||
}
|
88
src/models/relay_load.c
Normal file
88
src/models/relay_load.c
Normal file
|
@ -0,0 +1,88 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
#include <models/relay.h>
|
||||
#include <macros.h>
|
||||
|
||||
static int
|
||||
relay_load_single(MDB_txn *mdb_txn, MDB_dbi mdb_dbi, db_key_relay_e key_relay, uint8_t relay_num, MDB_val *value)
|
||||
{
|
||||
int err;
|
||||
|
||||
size_t key_size = sizeof(db_key_relay_e) + sizeof(uint8_t);
|
||||
uint8_t *key_data = malloc(key_size);
|
||||
uint8_t *key_data_writer = key_data;
|
||||
memmove(key_data_writer, &relay_num, sizeof(uint8_t));
|
||||
key_data_writer += sizeof(uint8_t);
|
||||
memmove(key_data_writer, &key_relay, sizeof(db_key_relay_e));
|
||||
|
||||
MDB_val key;
|
||||
key.mv_size = key_size;
|
||||
key.mv_data = key_data;
|
||||
|
||||
if((err = mdb_get(mdb_txn, mdb_dbi, &key, value)) != 0)
|
||||
{
|
||||
LOG_ERROR("mdb_get error %s\n", mdb_strerror(err));
|
||||
mdb_txn_abort(mdb_txn);
|
||||
free(key_data);
|
||||
return 1;
|
||||
}
|
||||
free(key_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
relay_t*
|
||||
relay_load(MDB_env *mdb_env, uint8_t num)
|
||||
{
|
||||
int err;
|
||||
MDB_txn *mdb_txn;
|
||||
MDB_dbi mdb_dbi;
|
||||
|
||||
relay_t *new_relay;
|
||||
|
||||
if((err = mdb_txn_begin(mdb_env, NULL, MDB_RDONLY, &mdb_txn)) != 0)
|
||||
{
|
||||
LOG_ERROR("mdb_txn_begin error %s\n", mdb_strerror(err));
|
||||
return relay_create(num);
|
||||
}
|
||||
|
||||
if((err = mdb_dbi_open(mdb_txn, "relays", 0, &mdb_dbi)) != 0)
|
||||
{
|
||||
switch(err)
|
||||
{
|
||||
case MDB_NOTFOUND:
|
||||
LOG_INFO("no relay for num %d found in db. returning new one (no relays db)\n", num);
|
||||
mdb_txn_abort(mdb_txn);
|
||||
return relay_create(num);
|
||||
default:
|
||||
LOG_ERROR("mdb_txn_begin error %s\n", mdb_strerror(err));
|
||||
return relay_create(num);
|
||||
}
|
||||
}
|
||||
|
||||
new_relay = malloc(sizeof(relay_t));
|
||||
new_relay->number = num;
|
||||
|
||||
MDB_val value;
|
||||
|
||||
if((err = relay_load_single(mdb_txn, mdb_dbi, DB_KEY_RELAY_NAME, num, &value)) != 0)
|
||||
{
|
||||
LOG_INFO("no relay for num %d found in db. returning new one\n", num);
|
||||
mdb_txn_abort(mdb_txn); // transaction is read only
|
||||
return relay_create(num);
|
||||
}
|
||||
strncpy(new_relay->name, (char*)value.mv_data, MAX_NAME_LENGTH);
|
||||
new_relay->name[MAX_NAME_LENGTH] = '\0';
|
||||
|
||||
mdb_txn_abort(mdb_txn); // transaction is read only
|
||||
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
new_relay->schedules[i] = schedule_load(mdb_env, num, i);
|
||||
}
|
||||
|
||||
return new_relay;
|
||||
}
|
75
src/models/relay_save.c
Normal file
75
src/models/relay_save.c
Normal file
|
@ -0,0 +1,75 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
#include <models/relay.h>
|
||||
#include <macros.h>
|
||||
|
||||
static int
|
||||
relay_save_single(MDB_txn *mdb_txn, MDB_dbi mdb_dbi, db_key_relay_e key_relay, uint8_t relay_num, MDB_val value)
|
||||
{
|
||||
int err;
|
||||
|
||||
size_t key_size = sizeof(db_key_relay_e) + sizeof(uint8_t);
|
||||
uint8_t *key_data = malloc(key_size);
|
||||
uint8_t *key_data_writer = key_data;
|
||||
memmove(key_data_writer, &relay_num, sizeof(uint8_t));
|
||||
key_data_writer += sizeof(uint8_t);
|
||||
memmove(key_data_writer, &key_relay, sizeof(db_key_relay_e));
|
||||
|
||||
MDB_val key;
|
||||
key.mv_size = key_size;
|
||||
key.mv_data = key_data;
|
||||
|
||||
if((err = mdb_put(mdb_txn, mdb_dbi, &key, &value, 0)) != 0)
|
||||
{
|
||||
LOG_ERROR("mdb_put error %s\n", mdb_strerror(err));
|
||||
mdb_txn_abort(mdb_txn);
|
||||
free(key_data);
|
||||
return 1;
|
||||
}
|
||||
free(key_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
relay_save(relay_t *relay, MDB_env *mdb_env)
|
||||
{
|
||||
LOG_TRACE("saving relay %d @ %p\n", relay->number, relay);
|
||||
int err;
|
||||
|
||||
MDB_txn *mdb_txn;
|
||||
MDB_dbi mdb_dbi;
|
||||
MDB_val value;
|
||||
|
||||
if((err = mdb_txn_begin(mdb_env, NULL, 0, &mdb_txn)) != 0)
|
||||
{
|
||||
LOG_ERROR("mdb_txn_begin error %s\n", mdb_strerror(err));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if((err = mdb_dbi_open(mdb_txn, "relays", MDB_CREATE, &mdb_dbi)) != 0)
|
||||
{
|
||||
LOG_ERROR("mdb_dbi_open error %s\n", mdb_strerror(err));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
value.mv_size = sizeof(char) * (strlen(relay->name) + 1);
|
||||
value.mv_data = relay->name;
|
||||
if(relay_save_single(mdb_txn, mdb_dbi, DB_KEY_RELAY_NAME, relay->number, value))
|
||||
{
|
||||
LOG_ERROR("failed to save name\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
mdb_txn_commit(mdb_txn);
|
||||
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
schedule_save(relay->schedules[i], relay->number, mdb_env);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
94
src/models/schedule.c
Normal file
94
src/models/schedule.c
Normal file
|
@ -0,0 +1,94 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <logger.h>
|
||||
#include <models/schedule.h>
|
||||
|
||||
schedule_t*
|
||||
schedule_create(uuid_t id, uint8_t weekday, uint16_t length, uint16_t *periods_blob)
|
||||
{
|
||||
schedule_t *new_schedule = malloc(sizeof(schedule_t));
|
||||
|
||||
memmove(new_schedule->id, id, sizeof(uuid_t));
|
||||
|
||||
new_schedule->weekday = weekday;
|
||||
|
||||
new_schedule->length = length;
|
||||
new_schedule->periods = NULL;
|
||||
|
||||
if(length)
|
||||
{
|
||||
new_schedule->periods = malloc(sizeof(period_t*) * length);
|
||||
|
||||
for(uint16_t i = 0; i < length; ++i)
|
||||
{
|
||||
uint16_t start = periods_blob[0 + (i * 2)];
|
||||
uint16_t end = periods_blob[1 + (i * 2)];
|
||||
new_schedule->periods[i] = period_create(start, end);
|
||||
}
|
||||
}
|
||||
|
||||
return new_schedule;
|
||||
}
|
||||
|
||||
uint16_t*
|
||||
schedule_periods_to_blob(schedule_t *schedule)
|
||||
{
|
||||
uint16_t *periods_blob = malloc(sizeof(uint16_t) * ((2 * schedule->length) + 1));
|
||||
periods_blob[0] = schedule->length;
|
||||
|
||||
for(uint16_t i = 0; i < schedule->length; ++i)
|
||||
{
|
||||
|
||||
periods_blob[1 + (i * 2)] = schedule->periods[i]->start;
|
||||
periods_blob[2 + (i * 2)] = schedule->periods[i]->end;
|
||||
}
|
||||
|
||||
return periods_blob;
|
||||
}
|
||||
|
||||
void
|
||||
schedule_free(schedule_t *schedule)
|
||||
{
|
||||
for(uint16_t i = 0; i < schedule->length; ++i)
|
||||
{
|
||||
free(schedule->periods[i]);
|
||||
}
|
||||
free(schedule->periods);
|
||||
free(schedule);
|
||||
}
|
||||
|
||||
void
|
||||
schedule_debug(schedule_t *schedule)
|
||||
{
|
||||
if(schedule == NULL)
|
||||
{
|
||||
LOG_DEBUG("schedule is NULL\n");
|
||||
return;
|
||||
}
|
||||
char uuid_str[UUID_STR_LEN];
|
||||
uuid_unparse(schedule->id, uuid_str);
|
||||
LOG_DEBUG("(1/3) %s @ %p\n", uuid_str, (void*)schedule);
|
||||
LOG_DEBUG("(2/4) period count: %3d\n", schedule->length);
|
||||
LOG_DEBUG("(3/4) weekday: %3d\n", schedule->weekday);
|
||||
|
||||
// one block: "HH:MM-HH:MM, " --> size: 13 (14 with '\0')
|
||||
char *periods_debug_str = malloc(sizeof(char) * ((schedule->length * 13) + 1));
|
||||
periods_debug_str[0] = '\0';
|
||||
|
||||
for(uint16_t i = 0; i < schedule->length; ++i)
|
||||
{
|
||||
sprintf(
|
||||
periods_debug_str + (13 * i),
|
||||
"%02d:%02d-%02d:%02d, ",
|
||||
schedule->periods[i]->start / 60,
|
||||
schedule->periods[i]->start % 60,
|
||||
schedule->periods[i]->end / 60,
|
||||
schedule->periods[i]->end % 60
|
||||
);
|
||||
}
|
||||
|
||||
LOG_DEBUG("(4/4) periods: %s\n", periods_debug_str);
|
||||
|
||||
free(periods_debug_str);
|
||||
}
|
96
src/models/schedule_load.c
Normal file
96
src/models/schedule_load.c
Normal file
|
@ -0,0 +1,96 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
#include <models/relay.h>
|
||||
#include <macros.h>
|
||||
|
||||
static int
|
||||
schedule_load_single(MDB_txn *mdb_txn, MDB_dbi mdb_dbi, db_key_schedule_e key_schedule, uint8_t relay_num, uint8_t weekday, MDB_val *value)
|
||||
{
|
||||
int err;
|
||||
|
||||
size_t key_size = sizeof(db_key_schedule_e) + (2 * sizeof(uint8_t));
|
||||
uint8_t *key_data = malloc(key_size);
|
||||
uint8_t *key_data_writer = key_data;
|
||||
memmove(key_data_writer, &relay_num, sizeof(uint8_t));
|
||||
key_data_writer += sizeof(uint8_t);
|
||||
memmove(key_data_writer, &weekday, sizeof(uint8_t));
|
||||
key_data_writer += sizeof(uint8_t);
|
||||
memmove(key_data_writer, &key_schedule, sizeof(db_key_schedule_e));
|
||||
|
||||
MDB_val key;
|
||||
key.mv_size = key_size;
|
||||
key.mv_data = key_data;
|
||||
|
||||
if((err = mdb_get(mdb_txn, mdb_dbi, &key, value)) != 0)
|
||||
{
|
||||
LOG_ERROR("mdb_get error %s\n", mdb_strerror(err));
|
||||
mdb_txn_abort(mdb_txn);
|
||||
free(key_data);
|
||||
return 1;
|
||||
}
|
||||
free(key_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
schedule_t*
|
||||
schedule_load(MDB_env *mdb_env, uint8_t relay_num, uint8_t weekday)
|
||||
{
|
||||
int err;
|
||||
MDB_txn *mdb_txn;
|
||||
MDB_dbi mdb_dbi;
|
||||
|
||||
schedule_t *new_schedule;
|
||||
|
||||
uuid_t off_id;
|
||||
memset(off_id, 0, sizeof(uuid_t));
|
||||
memcpy(off_id, "off", 3);
|
||||
|
||||
if((err = mdb_txn_begin(mdb_env, NULL, MDB_RDONLY, &mdb_txn)) != 0)
|
||||
{
|
||||
LOG_ERROR("mdb_txn_begin error %s\n", mdb_strerror(err));
|
||||
return schedule_create(off_id, weekday, 0, NULL);
|
||||
}
|
||||
|
||||
if((err = mdb_dbi_open(mdb_txn, "schedules", 0, &mdb_dbi)) != 0)
|
||||
{
|
||||
switch(err)
|
||||
{
|
||||
case MDB_NOTFOUND:
|
||||
LOG_INFO("no schedule db found in db. returning new one (no schedules db)\n");
|
||||
mdb_txn_abort(mdb_txn);
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("mdb_txn_begin error %s\n", mdb_strerror(err));
|
||||
}
|
||||
return schedule_create(off_id, weekday, 0, NULL);
|
||||
}
|
||||
|
||||
MDB_val value;
|
||||
|
||||
if((err = schedule_load_single(mdb_txn, mdb_dbi, DB_KEY_SCHEDULE_ID, relay_num, weekday, &value)) != 0)
|
||||
{
|
||||
LOG_INFO("no schedule for relay %d and weekday %d found in db. returning new one\n", relay_num, weekday);
|
||||
mdb_txn_abort(mdb_txn); // transaction is read only
|
||||
return schedule_create(off_id, weekday, 0, NULL);
|
||||
}
|
||||
uuid_t *schedule_id = (uuid_t*)value.mv_data;
|
||||
|
||||
if((err = schedule_load_single(mdb_txn, mdb_dbi, DB_KEY_SCHEDULE_PERIODS, relay_num, weekday, &value)) != 0)
|
||||
{
|
||||
LOG_INFO("no schedule for relay %d and weekday %d found in db. returning new one\n", relay_num, weekday);
|
||||
mdb_txn_abort(mdb_txn); // transaction is read only
|
||||
return schedule_create(off_id, weekday, 0, NULL);
|
||||
}
|
||||
uint16_t schedule_periods_length = ((uint16_t*)value.mv_data)[0];
|
||||
uint16_t *schedule_periods = ((uint16_t*)value.mv_data) + 1;
|
||||
|
||||
new_schedule = schedule_create(*schedule_id, weekday, schedule_periods_length, schedule_periods);
|
||||
|
||||
mdb_txn_abort(mdb_txn); // transaction is read only
|
||||
|
||||
return new_schedule;
|
||||
}
|
87
src/models/schedule_save.c
Normal file
87
src/models/schedule_save.c
Normal file
|
@ -0,0 +1,87 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
#include <models/schedule.h>
|
||||
#include <models/relay.h>
|
||||
#include <macros.h>
|
||||
|
||||
static int
|
||||
schedule_save_single(MDB_txn *mdb_txn, MDB_dbi mdb_dbi, db_key_schedule_e key_schedule, uint8_t relay_num, uint8_t weekday, MDB_val value)
|
||||
{
|
||||
int err;
|
||||
|
||||
size_t key_size = sizeof(db_key_schedule_e) + (2 * sizeof(uint8_t));
|
||||
uint8_t *key_data = malloc(key_size);
|
||||
uint8_t *key_data_writer = key_data;
|
||||
memmove(key_data_writer, &relay_num, sizeof(uint8_t));
|
||||
key_data_writer += sizeof(uint8_t);
|
||||
memmove(key_data_writer, &weekday, sizeof(uint8_t));
|
||||
key_data_writer += sizeof(uint8_t);
|
||||
memmove(key_data_writer, &key_schedule, sizeof(db_key_schedule_e));
|
||||
|
||||
MDB_val key;
|
||||
key.mv_size = key_size;
|
||||
key.mv_data = key_data;
|
||||
|
||||
if((err = mdb_put(mdb_txn, mdb_dbi, &key, &value, 0)) != 0)
|
||||
{
|
||||
LOG_ERROR("mdb_put error %s\n", mdb_strerror(err));
|
||||
mdb_txn_abort(mdb_txn);
|
||||
free(key_data);
|
||||
return 1;
|
||||
}
|
||||
free(key_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
schedule_save(schedule_t *schedule, uint8_t relay_num, MDB_env *mdb_env)
|
||||
{
|
||||
char uuid_str[37];
|
||||
uuid_unparse(schedule->id, uuid_str);
|
||||
LOG_TRACE("saving schedule %s @ %p\n", uuid_str, schedule);
|
||||
int err;
|
||||
|
||||
MDB_txn *mdb_txn;
|
||||
MDB_dbi mdb_dbi;
|
||||
MDB_val value;
|
||||
|
||||
if((err = mdb_txn_begin(mdb_env, NULL, 0, &mdb_txn)) != 0)
|
||||
{
|
||||
LOG_ERROR("mdb_txn_begin error %s\n", mdb_strerror(err));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if((err = mdb_dbi_open(mdb_txn, "schedules", MDB_CREATE, &mdb_dbi)) != 0)
|
||||
{
|
||||
LOG_ERROR("mdb_dbi_open error %s\n", mdb_strerror(err));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
value.mv_size = sizeof(uuid_t);
|
||||
value.mv_data = schedule->id;
|
||||
if(schedule_save_single(mdb_txn, mdb_dbi, DB_KEY_SCHEDULE_ID, relay_num, schedule->weekday, value))
|
||||
{
|
||||
LOG_ERROR("failed to save ID\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// save periods blob
|
||||
uint16_t *periods_blob = schedule_periods_to_blob(schedule);
|
||||
value.mv_size = sizeof(uint16_t) * ((periods_blob[0] * 2) + 1);
|
||||
value.mv_data = periods_blob;
|
||||
if(schedule_save_single(mdb_txn, mdb_dbi, DB_KEY_SCHEDULE_PERIODS, relay_num, schedule->weekday, value))
|
||||
{
|
||||
free(periods_blob);
|
||||
LOG_ERROR("failed to save periods\n");
|
||||
return 1;
|
||||
}
|
||||
free(periods_blob);
|
||||
|
||||
mdb_txn_commit(mdb_txn);
|
||||
|
||||
return 0;
|
||||
}
|
6440
src/mpack.c
Normal file
6440
src/mpack.c
Normal file
File diff suppressed because it is too large
Load diff
35
src/runners/test.c
Normal file
35
src/runners/test.c
Normal file
|
@ -0,0 +1,35 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#include <runners.h>
|
||||
#include <logger.h>
|
||||
#include <drivers.h>
|
||||
#include <drivers.h>
|
||||
|
||||
void
|
||||
runner_test(controller_t *controller)
|
||||
{
|
||||
// from x down to 0 to turn all relays off in the end
|
||||
for(uint_fast8_t i = 0; i < controller->relay_count; ++i)
|
||||
{
|
||||
for(int test_run = 2; test_run >= 0; --test_run)
|
||||
{
|
||||
int is_active = test_run % 2;
|
||||
if(global_config.relay_configs[i].inverted)
|
||||
{
|
||||
is_active = !is_active;
|
||||
}
|
||||
switch(global_config.relay_configs[i].driver)
|
||||
{
|
||||
case RELAY_DRIVER_GPIO:
|
||||
driver_gpio_set(global_config.relay_configs[i].pin, is_active);
|
||||
break;
|
||||
case RELAY_DRIVER_PIFACE:
|
||||
driver_piface_set(global_config.relay_configs[i].pin, is_active);
|
||||
break;
|
||||
default:
|
||||
LOG_WARN("relay %d is not using a driver\n", i);
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue