/*
* modbuslog
*
* Copyright (C) 2011 Jonathan McCrohan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
// gcc modbuslog.c -o modbuslog `pkg-config --libs --cflags libmodbus libconfig`
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// handle SIGALRM by resetting it
void minute_check(int signum) {
alarm(60);
}
// get mac address of primary interface eth0
char *mac_address() {
int s;
struct ifreq ifr;
s = socket(PF_INET, SOCK_DGRAM, 0);
memset(&ifr, 0x00, sizeof(ifr));
strcpy(ifr.ifr_name, "eth0");
ioctl(s, SIOCGIFHWADDR, &ifr);
close(s);
static char mac_address[12];
sprintf(mac_address, "%.2X%.2X%.2X%.2X%.2X%.2X",
(unsigned char) ifr.ifr_hwaddr.sa_data[0],
(unsigned char) ifr.ifr_hwaddr.sa_data[1],
(unsigned char) ifr.ifr_hwaddr.sa_data[2],
(unsigned char) ifr.ifr_hwaddr.sa_data[3],
(unsigned char) ifr.ifr_hwaddr.sa_data[4],
(unsigned char) ifr.ifr_hwaddr.sa_data[5]);
return mac_address;
}
int main(int argc, char *argv[]) {
int DEBUG = 0;
int SYSLOG_CONSOLE_OUTPUT = 0;
int k;
// check the argv array for strings matching -d
for (k = 1; k < argc; k++) {
if (strcmp(argv[k], "-d") == 0) {
DEBUG = 1;
SYSLOG_CONSOLE_OUTPUT = LOG_PERROR;
}
}
openlog("modbuslog", SYSLOG_CONSOLE_OUTPUT | LOG_PID | LOG_CONS, LOG_USER);
syslog(LOG_INFO, "modbuslog starting");
const char *configfile = "/etc/modbuslog.cfg";
config_t cfg;
//config_setting_t *setting;
const char *modbus_device_address;
int modbus_baud_rate;
int modbus_data_bits;
const char *modbus_parity;
int modbus_stop_bits;
int modbus_retry;
config_init(&cfg);
// attempt to read config
// loads entire file to memory and destroys file descriptor
if (!config_read_file(&cfg, configfile)) {
fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg),
config_error_line(&cfg), config_error_text(&cfg));
config_destroy(&cfg);
syslog(LOG_ERR, "Unable to find configfile");
return -1;
} else {
syslog(LOG_INFO, "configfile found successfully");
}
// die if core config file options aren't there
if (!(config_lookup_string(&cfg, "modbus.device", &modbus_device_address)
&& config_lookup_int(&cfg, "modbus.baud", &modbus_baud_rate)
&& config_lookup_int(&cfg, "modbus.data_bits", &modbus_data_bits)
&& config_lookup_string(&cfg, "modbus.parity", &modbus_parity)
&& config_lookup_int(&cfg, "modbus.stop_bits", &modbus_stop_bits)
&& config_lookup_int(&cfg, "modbus.retry", &modbus_retry))) {
syslog(LOG_ERR,
"Incomplete modbus configuration. Check configuration file");
closelog();
return -1;
}
syslog(LOG_INFO, "%s %s %d %c %d %d", mac_address(), modbus_device_address,
modbus_baud_rate, modbus_parity[0], modbus_data_bits,
modbus_stop_bits);
modbus_t *ctx;
uint16_t tab_reg[4];
uint8_t byte[8];
int rc;
int i;
// SIGALRM used to wake for loop up every 60 secs
// sleep puts whole thread to sleep which isn't what we want
// other methods involve CPU spinlocks which are inefficient
signal(SIGALRM, minute_check);
alarm(60);
for (;;) {
// block until SIGARLM
select(0, NULL, NULL, NULL, NULL);
time_t t = time(NULL);
time_t unixtime_min = (((int) t) / 60 * 60);
//int unixtime = (int) t;
//printf("%d\n", unixtime);
config_setting_t *readings;
readings = config_lookup(&cfg, "reading");
// find number of required readings
unsigned int num_readings = config_setting_length(readings);
int i;
// cycle through each reading and pull info from config file
for (i = 0; i < num_readings; ++i) {
config_setting_t *register_element = config_setting_get_elem(
readings, i);
int slaveid;
config_setting_lookup_int(register_element, "slaveid", &slaveid);
int intervalvalue;
config_setting_lookup_int(register_element, "intervalvalue",
&intervalvalue);
//printf("%d", intervalvalue);
int registertype;
config_setting_lookup_int(register_element, "registertype",
®istertype);
int startaddress;
config_setting_lookup_int(register_element, "startaddress",
&startaddress);
int numregisters;
config_setting_lookup_int(register_element, "numregisters",
&numregisters);
const char *intervalunit;
config_setting_lookup_string(register_element, "intervalunit",
&intervalunit);
//printf("%c", intervalunit[0]);
int intervalduration;
if (intervalunit[0] == 'h')
intervalduration = intervalvalue * 3600;
else
intervalduration = intervalvalue * 60;
// if we match the required time for the reading
if (unixtime_min % intervalduration == 0) {
//printf("specified minute. %d %d\n", unixtime_min,
// intervalduration);
// attempt to create new modbus connection
ctx = modbus_new_rtu(modbus_device_address, modbus_baud_rate,
modbus_parity[0], modbus_data_bits, modbus_stop_bits);
modbus_set_slave(ctx, slaveid);
modbus_set_debug(ctx, DEBUG);
if (ctx == NULL) {
syslog(LOG_ERR, "Unable to create libmodbus object");
}
if (modbus_connect(ctx) == -1) {
syslog(LOG_ERR, "libmodbus: Connection failed: %s",
modbus_strerror(errno));
modbus_free(ctx);
}
int retry = 0;
// handle timeouts and retries
do {
switch (registertype) {
case 3:
rc = modbus_read_registers(ctx, startaddress,
numregisters, tab_reg);
break;
case 4:
rc = modbus_read_input_registers(ctx, startaddress,
numregisters, tab_reg);
break;
default:
rc = modbus_read_registers(ctx, startaddress,
numregisters, tab_reg);
break;
}
if (rc == -1) {
syslog(LOG_ERR, "libmodbus error: %s",
modbus_strerror(errno));
}
retry++;
} while ((rc != -1) && (retry < (modbus_retry + 1)));
//MODBUS_GET_HIGH_BYTE(data);
//MODBUS_GET_LOW_BYTE(data);
//MODBUS_SET_INT16_TO_INT8(tab_int8, index, value);
// round forward to next midnight
time_t unixtime_day = ((((int) t) / 86400 * 86400) + 86400);
struct tm midnight = *localtime(&unixtime_day);
char filename[50];
//_YYYY_MM_DD_HH_MM_SS.log
sprintf(filename,
"/var/modbuslog/%s_%04i_%02i_%02i_%02i_%02i_%02i.log",
mac_address(), midnight.tm_year + 1900,
midnight.tm_mon + 1, midnight.tm_mday, midnight.tm_hour,
midnight.tm_min, midnight.tm_sec);
//printf("%s\n",filename);
FILE *fp = fopen(filename, "r");
if (fp) {
fclose(fp);
} else {
// file doesn't exist. create it.
syslog(LOG_NOTICE, "logfile does not exist");
syslog(LOG_INFO, "creating file: %s", filename);
FILE *fp = fopen(filename, "w");
fprintf(
fp,
"IntervalID|UTCDate|UTCTime|LOCALDate|LOCALTime|SensorID|RegisterID|Reading\n");
fclose(fp);
}
FILE *filehandle = fopen(filename, "a+");
syslog(LOG_DEBUG, "opening file for append: %s", filename);
int16_t registervalue = 0;
int p;
// data arrives in words, split into bytes
for (p = 0; p < numregisters; p++) {
MODBUS_SET_INT16_TO_INT8(byte, p * 2, tab_reg[p]);
//registervalue += tab_reg[i] + tab_reg[i + 1];
}
// bitshifting magic
switch (numregisters) {
case 1:
registervalue = byte[0];
break;
case 2:
registervalue = ((byte[0]) << 8) + byte[1];
break;
case 4:
registervalue = ((byte[0]) << 24) + ((byte[1]) << 16)
+ ((byte[2]) << 8) + byte[3];
break;
default:
registervalue = byte[0];
break;
}
struct tm utc = *gmtime(&unixtime_min);
struct tm lc = *localtime(&unixtime_min);
int intervalid;
FILE *intervalfile = fopen(
"/var/modbuslog/interval/interval.txt", "r+");
if (intervalfile) {
fclose(intervalfile);
} else {
// file doesn't exist. create it.
syslog(LOG_NOTICE, "interval file does not exist");
syslog(LOG_INFO, "attempting to create file: %s",
intervalfile);
FILE *intervalfile = fopen(filename, "w");
fprintf(intervalfile, "0\n");
fclose(intervalfile);
}
fscanf(intervalfile, "%d", &intervalid);
//handle 32bit signed overflow
if (intervalid == 2147483647) {
intervalid = 0;
} else {
intervalid++;
}
rewind(intervalfile);
fprintf(intervalfile, "%d", intervalid);
fclose(intervalfile);
fprintf(
filehandle,
"%i|%04i%02i%02i|%02i%02i%02i|%04i%02i%02i|%02i%02i%02i|%i|%i|%i\n",
intervalid, utc.tm_year + 1900, utc.tm_mon + 1,
utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec,
lc.tm_year + 1900, lc.tm_mon + 1, lc.tm_mday,
lc.tm_hour, lc.tm_min, lc.tm_sec, slaveid, startaddress,
registervalue);
fclose(filehandle);
syslog(
LOG_DEBUG,
"%i|%04i%02i%02i|%02i%02i%02i|%04i%02i%02i|%02i%02i%02i|%i|%i|%i\n",
intervalid, utc.tm_year + 1900, utc.tm_mon + 1,
utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec,
lc.tm_year + 1900, lc.tm_mon + 1, lc.tm_mday,
lc.tm_hour, lc.tm_min, lc.tm_sec, slaveid, startaddress,
registervalue);
modbus_close(ctx);
modbus_free(ctx);
sleep(1);
//return 0;
}
//printf("%d ", slaveid);
}
//printf("%d\n", unixtime_min);
}
}