From c217a9f1d77b2ea373705ff2a47e321d7949063d Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Sat, 10 Sep 2011 00:00:26 +0200 Subject: [PATCH] just to much to describe it ;) - added new structure - added SML support --- Makefile | 12 +- Makefile.am | 3 +- bin/reader/Makefile | 484 ++++++++++++ bin/reader/Makefile.am | 13 + bin/reader/smlreader.c | 153 ++++ bin/vzlogger/Makefile | 494 +++++++++++++ bin/vzlogger/Makefile.am | 22 + {src => bin/vzlogger}/api.c | 102 +-- {src => bin/vzlogger}/api.h | 7 +- bin/vzlogger/buffer.c | 123 ++++ bin/vzlogger/buffer.h | 57 ++ bin/vzlogger/channel.c | 91 +++ bin/vzlogger/channel.h | 60 ++ src/queue.h => bin/vzlogger/list.c | 65 +- src/protocols/random.h => bin/vzlogger/list.h | 24 +- {src => bin/vzlogger}/local.c | 39 +- {src => bin/vzlogger}/local.h | 0 bin/vzlogger/options.c | 231 ++++++ bin/vzlogger/options.h | 48 ++ bin/vzlogger/vzlogger | Bin 0 -> 151026 bytes bin/vzlogger/vzlogger.c | 192 +++++ bin/vzlogger/vzlogger.h | 45 ++ config.h.in | 8 +- configure | 696 ++++++++++++++---- configure.ac | 37 +- docs/Makefile | 7 +- etc/vzlogger.conf | 14 +- src/protocols/obis.h => include/d0.h | 22 +- include/meter.h | 90 +++ include/obis.h | 118 +++ include/onewire.h | 41 ++ include/random.h | 41 ++ src/protocols/1wire.h => include/reading.h | 21 +- src/protocols/rawS0.h => include/s0.h | 16 +- {src/protocols => include}/sml.h | 24 +- include/unit.h | 115 +++ src/Makefile | 214 ++---- src/Makefile.am | 25 +- src/{protocols/obis.c => d0.c} | 41 +- src/main.c | 464 ------------ src/main.h | 92 --- src/meter.c | 126 ++++ src/obis.c | 110 +++ src/{protocols/1wire.c => onewire.c} | 31 +- src/protocol.h | 29 - src/protocols/rawS0.c | 99 --- src/protocols/sml.c | 115 --- src/queue.c | 63 -- src/{protocols => }/random.c | 56 +- src/s0.c | 91 +++ src/sml.c | 191 +++++ 51 files changed, 3854 insertions(+), 1408 deletions(-) create mode 100644 bin/reader/Makefile create mode 100644 bin/reader/Makefile.am create mode 100644 bin/reader/smlreader.c create mode 100644 bin/vzlogger/Makefile create mode 100644 bin/vzlogger/Makefile.am rename {src => bin/vzlogger}/api.c (72%) rename {src => bin/vzlogger}/api.h (90%) create mode 100644 bin/vzlogger/buffer.c create mode 100644 bin/vzlogger/buffer.h create mode 100644 bin/vzlogger/channel.c create mode 100644 bin/vzlogger/channel.h rename src/queue.h => bin/vzlogger/list.c (57%) rename src/protocols/random.h => bin/vzlogger/list.h (74%) rename {src => bin/vzlogger}/local.c (74%) rename {src => bin/vzlogger}/local.h (100%) create mode 100644 bin/vzlogger/options.c create mode 100644 bin/vzlogger/options.h create mode 100755 bin/vzlogger/vzlogger create mode 100644 bin/vzlogger/vzlogger.c create mode 100644 bin/vzlogger/vzlogger.h rename src/protocols/obis.h => include/d0.h (72%) create mode 100644 include/meter.h create mode 100644 include/obis.h create mode 100644 include/onewire.h create mode 100644 include/random.h rename src/protocols/1wire.h => include/reading.h (77%) rename src/protocols/rawS0.h => include/s0.h (80%) rename {src/protocols => include}/sml.h (68%) create mode 100644 include/unit.h rename src/{protocols/obis.c => d0.c} (75%) delete mode 100644 src/main.c delete mode 100644 src/main.h create mode 100644 src/meter.c create mode 100644 src/obis.c rename src/{protocols/1wire.c => onewire.c} (71%) delete mode 100644 src/protocol.h delete mode 100644 src/protocols/rawS0.c delete mode 100644 src/protocols/sml.c delete mode 100644 src/queue.c rename src/{protocols => }/random.c (58%) create mode 100644 src/s0.c create mode 100644 src/sml.c diff --git a/Makefile b/Makefile index 22a1cca..4f2e804 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,7 @@ subdir = . DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \ $(srcdir)/Makefile.in $(srcdir)/config.h.in \ $(top_srcdir)/configure AUTHORS COPYING ChangeLog INSTALL NEWS \ - depcomp install-sh missing + compile depcomp install-sh missing ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ @@ -137,10 +137,12 @@ CPPFLAGS = CYGPATH_W = echo DEFS = -DHAVE_CONFIG_H DEPDIR = .deps -DEPS_CFLAGS = -I/usr/include/json -DEPS_LIBS = -ljson -lcurl DEPS_LOCAL_CFLAGS = DEPS_LOCAL_LIBS = +DEPS_SML_CFLAGS = -I/usr/include/sml -I/usr/include/uuid +DEPS_SML_LIBS = -lsml -luuid +DEPS_VZ_CFLAGS = -I/usr/include/json +DEPS_VZ_LIBS = -ljson -lcurl ECHO_C = ECHO_N = -n ECHO_T = @@ -170,6 +172,7 @@ PATH_SEPARATOR = : PKG_CONFIG = /usr/bin/pkg-config PKG_CONFIG_LIBDIR = PKG_CONFIG_PATH = +RANLIB = ranlib SET_MAKE = SHELL = /bin/bash STRIP = @@ -217,7 +220,7 @@ top_build_prefix = top_builddir = . top_srcdir = . sysconf_DATA = etc/vzlogger.conf -SUBDIRS = src docs +SUBDIRS = src bin/vzlogger all: config.h $(MAKE) $(AM_MAKEFLAGS) all-recursive @@ -725,6 +728,7 @@ uninstall-am: uninstall-sysconfDATA mostlyclean-generic pdf pdf-am ps ps-am tags tags-recursive \ uninstall uninstall-am uninstall-sysconfDATA +# bin/reader docs # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/Makefile.am b/Makefile.am index 290818e..e3a536b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,3 +1,4 @@ sysconf_DATA = etc/vzlogger.conf -SUBDIRS = src docs +SUBDIRS = src bin/vzlogger +# bin/reader docs diff --git a/bin/reader/Makefile b/bin/reader/Makefile new file mode 100644 index 0000000..c95b155 --- /dev/null +++ b/bin/reader/Makefile @@ -0,0 +1,484 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# bin/reader/Makefile. Generated from Makefile.in by configure. + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + + + +pkgdatadir = $(datadir)/vzlogger +pkgincludedir = $(includedir)/vzlogger +pkglibdir = $(libdir)/vzlogger +pkglibexecdir = $(libexecdir)/vzlogger +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +bin_PROGRAMS = smlreader$(EXEEXT) +subdir = bin/reader +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +PROGRAMS = $(bin_PROGRAMS) +am__smlreader_SOURCES_DIST = smlreader.c +am_smlreader_OBJECTS = \ + smlreader-smlreader.$(OBJEXT) +smlreader_OBJECTS = $(am_smlreader_OBJECTS) +am__DEPENDENCIES_1 = +smlreader_DEPENDENCIES = $(am__DEPENDENCIES_1) +smlreader_LINK = $(CCLD) $(smlreader_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I. -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(smlreader_SOURCES) +DIST_SOURCES = $(am__smlreader_SOURCES_DIST) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = ${SHELL} /home/stv0g/workspace/volkszaehler.org/vzlogger/missing --run aclocal-1.11 +AMTAR = ${SHELL} /home/stv0g/workspace/volkszaehler.org/vzlogger/missing --run tar +AUTOCONF = ${SHELL} /home/stv0g/workspace/volkszaehler.org/vzlogger/missing --run autoconf +AUTOHEADER = ${SHELL} /home/stv0g/workspace/volkszaehler.org/vzlogger/missing --run autoheader +AUTOMAKE = ${SHELL} /home/stv0g/workspace/volkszaehler.org/vzlogger/missing --run automake-1.11 +AWK = gawk +CC = gcc +CCDEPMODE = depmode=gcc3 +CFLAGS = -g -O2 +CPP = gcc -E +CPPFLAGS = +CYGPATH_W = echo +DEFS = -DHAVE_CONFIG_H +DEPDIR = .deps +DEPS_LOCAL_CFLAGS = +DEPS_LOCAL_LIBS = +DEPS_SML_CFLAGS = -I/usr/include/sml -I/usr/include/uuid +DEPS_SML_LIBS = -lsml -luuid +DEPS_VZ_CFLAGS = -I/usr/include/json +DEPS_VZ_LIBS = -ljson -lcurl +ECHO_C = +ECHO_N = -n +ECHO_T = +EGREP = /bin/grep -E +EXEEXT = +GREP = /bin/grep +INSTALL = /usr/bin/install -c +INSTALL_DATA = ${INSTALL} -m 644 +INSTALL_PROGRAM = ${INSTALL} +INSTALL_SCRIPT = ${INSTALL} +INSTALL_STRIP_PROGRAM = $(install_sh) -c -s +LDFLAGS = +LIBOBJS = +LIBS = +LTLIBOBJS = +MAKEINFO = ${SHELL} /home/stv0g/workspace/volkszaehler.org/vzlogger/missing --run makeinfo +MKDIR_P = /bin/mkdir -p +OBJEXT = o +PACKAGE = vzlogger +PACKAGE_BUGREPORT = http://bugs.volkszaehler.org +PACKAGE_NAME = vzlogger +PACKAGE_STRING = vzlogger 0.2 +PACKAGE_TARNAME = vzlogger +PACKAGE_URL = +PACKAGE_VERSION = 0.2 +PATH_SEPARATOR = : +PKG_CONFIG = /usr/bin/pkg-config +PKG_CONFIG_LIBDIR = +PKG_CONFIG_PATH = +RANLIB = ranlib +SET_MAKE = +SHELL = /bin/bash +STRIP = +VERSION = 0.2 +abs_builddir = /home/stv0g/workspace/volkszaehler.org/vzlogger/bin/reader +abs_srcdir = /home/stv0g/workspace/volkszaehler.org/vzlogger/bin/reader +abs_top_builddir = /home/stv0g/workspace/volkszaehler.org/vzlogger +abs_top_srcdir = /home/stv0g/workspace/volkszaehler.org/vzlogger +ac_ct_CC = gcc +am__include = include +am__leading_dot = . +am__quote = +am__tar = ${AMTAR} chof - "$$tardir" +am__untar = ${AMTAR} xf - +bindir = ${exec_prefix}/bin +build_alias = +builddir = . +datadir = ${datarootdir} +datarootdir = ${prefix}/share +docdir = ${datarootdir}/doc/${PACKAGE_TARNAME} +dvidir = ${docdir} +exec_prefix = ${prefix} +host_alias = +htmldir = ${docdir} +includedir = ${prefix}/include +infodir = ${datarootdir}/info +install_sh = ${SHELL} /home/stv0g/workspace/volkszaehler.org/vzlogger/install-sh +libdir = ${exec_prefix}/lib +libexecdir = ${exec_prefix}/libexec +localedir = ${datarootdir}/locale +localstatedir = ${prefix}/var +mandir = ${datarootdir}/man +mkdir_p = /bin/mkdir -p +oldincludedir = /usr/include +pdfdir = ${docdir} +prefix = /usr/local +program_transform_name = s,x,x, +psdir = ${docdir} +sbindir = ${exec_prefix}/sbin +sharedstatedir = ${prefix}/com +srcdir = . +sysconfdir = ${prefix}/etc +target_alias = +top_build_prefix = ../../ +top_builddir = ../.. +top_srcdir = ../.. + +# what flags you want to pass to the C compiler & linker +AM_CFLAGS = -Wall -D_REENTRANT -std=gnu99 $(DEPS_CFLAGS) +AM_LDFLAGS = +smlreader_SOURCES = smlreader.c +smlreader_LDADD = -lm $(DEPS_SML_LIBS) +smlreader_CFLAGS = $(AM_CFLAGS) $(DEPS_SML_CFLAGS) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu bin/reader/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu bin/reader/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p; \ + then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) +smlreader$(EXEEXT): $(smlreader_OBJECTS) $(smlreader_DEPENDENCIES) + @rm -f smlreader$(EXEEXT) + $(smlreader_LINK) $(smlreader_OBJECTS) $(smlreader_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +include ./$(DEPDIR)/smlreader-smlreader.Po + +.c.o: + $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< + $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +# source='$<' object='$@' libtool=no \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(COMPILE) -c $< + +.c.obj: + $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` + $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +# source='$<' object='$@' libtool=no \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(COMPILE) -c `$(CYGPATH_W) '$<'` + +smlreader-smlreader.o: smlreader.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(smlreader_CFLAGS) $(CFLAGS) -MT smlreader-smlreader.o -MD -MP -MF $(DEPDIR)/smlreader-smlreader.Tpo -c -o smlreader-smlreader.o `test -f 'smlreader.c' || echo '$(srcdir)/'`smlreader.c + $(am__mv) $(DEPDIR)/smlreader-smlreader.Tpo $(DEPDIR)/smlreader-smlreader.Po +# source='smlreader.c' object='smlreader-smlreader.o' libtool=no \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(smlreader_CFLAGS) $(CFLAGS) -c -o smlreader-smlreader.o `test -f 'smlreader.c' || echo '$(srcdir)/'`smlreader.c + +smlreader-smlreader.obj: smlreader.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(smlreader_CFLAGS) $(CFLAGS) -MT smlreader-smlreader.obj -MD -MP -MF $(DEPDIR)/smlreader-smlreader.Tpo -c -o smlreader-smlreader.obj `if test -f 'smlreader.c'; then $(CYGPATH_W) 'smlreader.c'; else $(CYGPATH_W) '$(srcdir)/smlreader.c'; fi` + $(am__mv) $(DEPDIR)/smlreader-smlreader.Tpo $(DEPDIR)/smlreader-smlreader.Po +# source='smlreader.c' object='smlreader-smlreader.obj' libtool=no \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(smlreader_CFLAGS) $(CFLAGS) -c -o smlreader-smlreader.obj `if test -f 'smlreader.c'; then $(CYGPATH_W) 'smlreader.c'; else $(CYGPATH_W) '$(srcdir)/smlreader.c'; fi` + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic ctags distclean distclean-compile \ + distclean-generic distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-binPROGRAMS + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/bin/reader/Makefile.am b/bin/reader/Makefile.am new file mode 100644 index 0000000..b0181c5 --- /dev/null +++ b/bin/reader/Makefile.am @@ -0,0 +1,13 @@ +# what flags you want to pass to the C compiler & linker +AM_CFLAGS = -Wall -D_REENTRANT -std=gnu99 $(DEPS_CFLAGS) +AM_LDFLAGS = + +# SML +#################################################################### +if SML_SUPPORT +bin_PROGRAMS = smlreader +smlreader_SOURCES = smlreader.c +smlreader_LDADD = -lm $(DEPS_SML_LIBS) +smlreader_CFLAGS = $(AM_CFLAGS) $(DEPS_SML_CFLAGS) +endif + diff --git a/bin/reader/smlreader.c b/bin/reader/smlreader.c new file mode 100644 index 0000000..0e33cf9 --- /dev/null +++ b/bin/reader/smlreader.c @@ -0,0 +1,153 @@ +/** + * SML protocol parsing + * + * @package vzlogger + * @copyright Copyright (c) 2011, The volkszaehler.org project + * @copyright Copyright (c) 2011, Juri Glass, Mathias Runge, Nadim El Sayed, DAI-Labor, TU-Berlin + * @license http://www.gnu.org/licenses/gpl.txt GNU Public License + * @author Steffen Vogel + */ +/* + * This file is part of volkzaehler.org + * + * volkzaehler.org 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 + * any later version. + * + * volkzaehler.org 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 volkszaehler.org. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include +#include + +#include "obis.h" +#include "unit.h" + +obis_id_t filter; + +void transport_receiver(unsigned char *buffer, size_t buffer_len) { + /* strip escape sequences */ + sml_file *file = sml_file_parse(buffer + 8, buffer_len - 16); + + for (int i = 0; i < file->messages_len; i++) { + sml_message *message = file->messages[i]; + + if (*message->message_body->tag == SML_MESSAGE_GET_LIST_RESPONSE) { + sml_list *entry; + sml_get_list_response *body; + + body = (sml_get_list_response *) message->message_body->data; + + printf("new message from: %*s\n", body->server_id->len, body->server_id->str); + + for (entry = body->val_list; entry != NULL; entry = entry->next) { /* linked list */ + obis_id_t id = obis_init(entry->obj_name->str); + + if (memcmp(&id, &filter, sizeof(obis_id_t)) == 0) { + struct timeval time; + int unit = (entry->unit) ? *entry->unit : 0; + int scaler = (entry->scaler) ? *entry->scaler : 1; + double value; + + switch (entry->value->type) { + case 0x51: value = *entry->value->data.int8; break; + case 0x52: value = *entry->value->data.int16; break; + case 0x54: value = *entry->value->data.int32; break; + case 0x58: value = *entry->value->data.int64; break; + case 0x61: value = *entry->value->data.uint8; break; + case 0x62: value = *entry->value->data.uint16; break; + case 0x64: value = *entry->value->data.uint32; break; + case 0x68: value = *entry->value->data.uint64; break; + + default: + fprintf(stderr, "Unknown value type: %x", type); + value = 0; + } + + /* apply scaler */ + value *= pow(10, scaler); + + + /* get time */ + if (entry->val_time) { // TODO handle SML_TIME_SEC_INDEX + time.tv_sec = *entry->val_time->data.timestamp; + time.tv_usec = 0; + } + else { + gettimeofday(&time, NULL); + } + + printf("%lu.%lu\t%.2f %s\n", time.tv_sec, time.tv_usec, value, dlms_get_unit(unit)); + } + } + } + } + + // free the malloc'd memory + sml_file_free(file); +} + +int open_socket(char *host, char *port) { + struct sockaddr_in sin; + struct addrinfo *ais; + int fd; + + getaddrinfo(host, port, NULL, &ais); + memcpy(&sin, ais->ai_addr, ais->ai_addrlen); + + printf("Opening socket\n"); + fd = socket(PF_INET, SOCK_STREAM, 0); + + printf("Conntecting to %s:%s\n", host, port); + if (connect(fd, (struct sockaddr *) &sin, sizeof(sin)) < 0) { + perror(host); + exit(EXIT_FAILURE); + } + + return fd; +} + +int main(int argc, char **argv) { + char *host, *port, *obis; + char buffer[16]; + int fd; + + host = (argc >= 2) ? argv[1] : "localhost"; + port = (argc >= 3) ? argv[2] : "7331"; + obis = (argc >= 4) ? argv[3] : "1-0:1.7.0"; /* total power */ + + filter = obis_parse(obis); + + obis_unparse(filter, buffer); + printf("Using OBIS Id: %s\n", buffer); + + fd = open_socket(host, port); + + if (fd > 0) { + sml_transport_listen(fd, &transport_receiver); + close(fd); + } + + return 0; +} diff --git a/bin/vzlogger/Makefile b/bin/vzlogger/Makefile new file mode 100644 index 0000000..2e7cb11 --- /dev/null +++ b/bin/vzlogger/Makefile @@ -0,0 +1,494 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# bin/vzlogger/Makefile. Generated from Makefile.in by configure. + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + + + +pkgdatadir = $(datadir)/vzlogger +pkgincludedir = $(includedir)/vzlogger +pkglibdir = $(libdir)/vzlogger +pkglibexecdir = $(libexecdir)/vzlogger +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +bin_PROGRAMS = vzlogger$(EXEEXT) + +# local interface support +#################################################################### +#am__append_1 = local.c +#am__append_2 = $(DEPS_LOCAL_LIBS) +#am__append_3 = $(DEPS_LOCAL_CFLAGS) + +# sml support +#################################################################### +am__append_4 = $(DEPS_SML_LIBS) +am__append_5 = $(DEPS_SML_CFLAGS) +subdir = bin/vzlogger +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +PROGRAMS = $(bin_PROGRAMS) +am__vzlogger_SOURCES_DIST = vzlogger.c channel.c api.c options.c \ + list.c buffer.c local.c +#am__objects_1 = local.$(OBJEXT) +am_vzlogger_OBJECTS = vzlogger.$(OBJEXT) channel.$(OBJEXT) \ + api.$(OBJEXT) options.$(OBJEXT) list.$(OBJEXT) \ + buffer.$(OBJEXT) $(am__objects_1) +vzlogger_OBJECTS = $(am_vzlogger_OBJECTS) +am__DEPENDENCIES_1 = +#am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) +am__DEPENDENCIES_3 = $(am__DEPENDENCIES_1) +vzlogger_DEPENDENCIES = ../../src/libmeter.a ../../src/libobis.a \ + $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_3) +vzlogger_LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(vzlogger_LDFLAGS) \ + $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I. -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(vzlogger_SOURCES) +DIST_SOURCES = $(am__vzlogger_SOURCES_DIST) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = ${SHELL} /home/stv0g/workspace/volkszaehler.org/vzlogger/missing --run aclocal-1.11 +AMTAR = ${SHELL} /home/stv0g/workspace/volkszaehler.org/vzlogger/missing --run tar +AUTOCONF = ${SHELL} /home/stv0g/workspace/volkszaehler.org/vzlogger/missing --run autoconf +AUTOHEADER = ${SHELL} /home/stv0g/workspace/volkszaehler.org/vzlogger/missing --run autoheader +AUTOMAKE = ${SHELL} /home/stv0g/workspace/volkszaehler.org/vzlogger/missing --run automake-1.11 +AWK = gawk +CC = gcc +CCDEPMODE = depmode=gcc3 +CFLAGS = -g -O2 +CPP = gcc -E +CPPFLAGS = +CYGPATH_W = echo +DEFS = -DHAVE_CONFIG_H +DEPDIR = .deps +DEPS_LOCAL_CFLAGS = +DEPS_LOCAL_LIBS = +DEPS_SML_CFLAGS = -I/usr/include/sml -I/usr/include/uuid +DEPS_SML_LIBS = -lsml -luuid +DEPS_VZ_CFLAGS = -I/usr/include/json +DEPS_VZ_LIBS = -ljson -lcurl +ECHO_C = +ECHO_N = -n +ECHO_T = +EGREP = /bin/grep -E +EXEEXT = +GREP = /bin/grep +INSTALL = /usr/bin/install -c +INSTALL_DATA = ${INSTALL} -m 644 +INSTALL_PROGRAM = ${INSTALL} +INSTALL_SCRIPT = ${INSTALL} +INSTALL_STRIP_PROGRAM = $(install_sh) -c -s +LDFLAGS = +LIBOBJS = +LIBS = +LTLIBOBJS = +MAKEINFO = ${SHELL} /home/stv0g/workspace/volkszaehler.org/vzlogger/missing --run makeinfo +MKDIR_P = /bin/mkdir -p +OBJEXT = o +PACKAGE = vzlogger +PACKAGE_BUGREPORT = http://bugs.volkszaehler.org +PACKAGE_NAME = vzlogger +PACKAGE_STRING = vzlogger 0.2 +PACKAGE_TARNAME = vzlogger +PACKAGE_URL = +PACKAGE_VERSION = 0.2 +PATH_SEPARATOR = : +PKG_CONFIG = /usr/bin/pkg-config +PKG_CONFIG_LIBDIR = +PKG_CONFIG_PATH = +RANLIB = ranlib +SET_MAKE = +SHELL = /bin/bash +STRIP = +VERSION = 0.2 +abs_builddir = /home/stv0g/workspace/volkszaehler.org/vzlogger/bin/vzlogger +abs_srcdir = /home/stv0g/workspace/volkszaehler.org/vzlogger/bin/vzlogger +abs_top_builddir = /home/stv0g/workspace/volkszaehler.org/vzlogger +abs_top_srcdir = /home/stv0g/workspace/volkszaehler.org/vzlogger +ac_ct_CC = gcc +am__include = include +am__leading_dot = . +am__quote = +am__tar = ${AMTAR} chof - "$$tardir" +am__untar = ${AMTAR} xf - +bindir = ${exec_prefix}/bin +build_alias = +builddir = . +datadir = ${datarootdir} +datarootdir = ${prefix}/share +docdir = ${datarootdir}/doc/${PACKAGE_TARNAME} +dvidir = ${docdir} +exec_prefix = ${prefix} +host_alias = +htmldir = ${docdir} +includedir = ${prefix}/include +infodir = ${datarootdir}/info +install_sh = ${SHELL} /home/stv0g/workspace/volkszaehler.org/vzlogger/install-sh +libdir = ${exec_prefix}/lib +libexecdir = ${exec_prefix}/libexec +localedir = ${datarootdir}/locale +localstatedir = ${prefix}/var +mandir = ${datarootdir}/man +mkdir_p = /bin/mkdir -p +oldincludedir = /usr/include +pdfdir = ${docdir} +prefix = /usr/local +program_transform_name = s,x,x, +psdir = ${docdir} +sbindir = ${exec_prefix}/sbin +sharedstatedir = ${prefix}/com +srcdir = . +sysconfdir = ${prefix}/etc +target_alias = +top_build_prefix = ../../ +top_builddir = ../.. +top_srcdir = ../.. +AM_CFLAGS = -Wall -D_REENTRANT -std=gnu99 $(DEPS_VZ_CFLAGS) \ + $(am__append_3) $(am__append_5) +AM_CPPFLAGS = -I$(top_srcdir)/include +vzlogger_SOURCES = vzlogger.c channel.c api.c options.c list.c \ + buffer.c $(am__append_1) +vzlogger_LDADD = ../../src/libmeter.a ../../src/libobis.a \ + $(am__append_2) $(am__append_4) +vzlogger_LDFLAGS = -lpthread -lm $(DEPS_VZ_LIBS) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu bin/vzlogger/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu bin/vzlogger/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p; \ + then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) +vzlogger$(EXEEXT): $(vzlogger_OBJECTS) $(vzlogger_DEPENDENCIES) + @rm -f vzlogger$(EXEEXT) + $(vzlogger_LINK) $(vzlogger_OBJECTS) $(vzlogger_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +include ./$(DEPDIR)/api.Po +include ./$(DEPDIR)/buffer.Po +include ./$(DEPDIR)/channel.Po +include ./$(DEPDIR)/list.Po +include ./$(DEPDIR)/local.Po +include ./$(DEPDIR)/options.Po +include ./$(DEPDIR)/vzlogger.Po + +.c.o: + $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< + $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +# source='$<' object='$@' libtool=no \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(COMPILE) -c $< + +.c.obj: + $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` + $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +# source='$<' object='$@' libtool=no \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(COMPILE) -c `$(CYGPATH_W) '$<'` + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic ctags distclean distclean-compile \ + distclean-generic distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-binPROGRAMS + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/bin/vzlogger/Makefile.am b/bin/vzlogger/Makefile.am new file mode 100644 index 0000000..dd056f3 --- /dev/null +++ b/bin/vzlogger/Makefile.am @@ -0,0 +1,22 @@ +AM_CFLAGS = -Wall -D_REENTRANT -std=gnu99 $(DEPS_VZ_CFLAGS) +AM_CPPFLAGS = -I$(top_srcdir)/include + +bin_PROGRAMS = vzlogger +vzlogger_SOURCES = vzlogger.c channel.c api.c options.c list.c buffer.c +vzlogger_LDADD = ../../src/libmeter.a ../../src/libobis.a +vzlogger_LDFLAGS = -lpthread -lm $(DEPS_VZ_LIBS) + +# local interface support +#################################################################### +if LOCAL_SUPPORT +vzlogger_SOURCES += local.c +vzlogger_LDADD += $(DEPS_LOCAL_LIBS) +AM_CFLAGS += $(DEPS_LOCAL_CFLAGS) +endif + +# sml support +#################################################################### +if SML_SUPPORT +vzlogger_LDADD += $(DEPS_SML_LIBS) +AM_CFLAGS += $(DEPS_SML_CFLAGS) +endif diff --git a/src/api.c b/bin/vzlogger/api.c similarity index 72% rename from src/api.c rename to bin/vzlogger/api.c index b6dec7e..be451a9 100644 --- a/src/api.c +++ b/bin/vzlogger/api.c @@ -27,16 +27,21 @@ #include #include #include +#include + +#include #include "api.h" -#include "main.h" +#include "vzlogger.h" +#include "options.h" extern options_t opts; /** * Reformat CURLs debugging output */ -int curl_custom_debug_callback(CURL *curl, curl_infotype type, char *data, size_t size, void *ch) { +int curl_custom_debug_callback(CURL *curl, curl_infotype type, char *data, size_t size, void *arg) { + channel_t *ch = (channel_t *) ch; char *end = strchr(data, '\n'); if (data == end) return 0; /* skip empty line */ @@ -45,17 +50,17 @@ int curl_custom_debug_callback(CURL *curl, curl_infotype type, char *data, size_ case CURLINFO_TEXT: case CURLINFO_END: if (end) *end = '\0'; /* terminate without \n */ - print(3, "CURL: %.*s", (channel_t *) ch, (int) size, data); + print(7, "CURL: %.*s", ch, (int) size, data); break; case CURLINFO_SSL_DATA_IN: case CURLINFO_DATA_IN: - print(6, "CURL: Received %lu bytes", (channel_t *) ch, (unsigned long) size); + print(9, "CURL: Received %lu bytes", ch, (unsigned long) size); break; case CURLINFO_SSL_DATA_OUT: case CURLINFO_DATA_OUT: - print(6, "CURL: Sent %lu bytes.. ", (channel_t *) ch, (unsigned long) size); + print(9, "CURL: Sent %lu bytes.. ", ch, (unsigned long) size); break; case CURLINFO_HEADER_IN: @@ -83,33 +88,32 @@ size_t curl_custom_write_callback(void *ptr, size_t size, size_t nmemb, void *da return realsize; } -json_object * api_json_tuples(channel_t *ch, bool_t all) { - reading_t rd; +double api_tvtof(struct timeval tv) { + return round(tv.tv_sec * 1000.0 + tv.tv_usec / 1000.0); +} +/** + * Create JSON object of tuples + * + * @param buf the buffer our readings are stored in (required for mutex) + * @param start the first tuple of our linked list which should be encoded + */ +json_object * api_json_tuples(buffer_t *buf, meter_reading_t *start) { json_object *json_tuples = json_object_new_array(); - size_t index = ch->queue.read_p; - size_t end = (all) ? ch->queue.read_p : ch->queue.write_p; - do { - pthread_mutex_lock(&ch->mutex); - queue_get(&ch->queue, index, &rd); - pthread_mutex_unlock(&ch->mutex); + for (meter_reading_t *rd = start; rd != NULL; rd = rd->next) { + struct json_object *json_tuple = json_object_new_array(); - if (rd.tv.tv_sec) { /* skip empty buffers */ - struct json_object *json_tuple = json_object_new_array(); + pthread_mutex_lock(&buf->mutex); + double timestamp = api_tvtof(rd->tv); // TODO use long int of new json-c version + double value = rd->value; + pthread_mutex_unlock(&buf->mutex); - double timestamp = rd.tv.tv_sec * 1000.0 + rd.tv.tv_usec / 1000.0; + json_object_array_add(json_tuple, json_object_new_double(timestamp)); + json_object_array_add(json_tuple, json_object_new_double(value)); - json_object_array_add(json_tuple, json_object_new_double(timestamp)); - json_object_array_add(json_tuple, json_object_new_double(rd.value)); - - json_object_array_add(json_tuples, json_tuple); - } - - index++; - index %= ch->queue.size; /* increment pointer */ - - } while (index != end); + json_object_array_add(json_tuples, json_tuple); + } return json_tuples; } @@ -119,7 +123,7 @@ CURL * api_curl_init(channel_t *ch) { struct curl_slist *header = NULL; char url[255], agent[255]; - /* prepare header & url */ + /* prepare header, uuid & url */ sprintf(agent, "User-Agent: %s/%s (%s)", PACKAGE, VERSION, curl_version()); /* build user agent */ sprintf(url, "%s/data/%s.json", ch->middleware, ch->uuid); /* build url */ @@ -152,7 +156,7 @@ void api_parse_exception(CURLresponse response, char *err) { json_obj = json_object_object_get(json_obj, "exception"); if (json_obj) { - sprintf(err, "[%s] %s", + sprintf(err, "%s: %s", json_object_get_string(json_object_object_get(json_obj, "type")), json_object_get_string(json_object_object_get(json_obj, "message")) ); @@ -179,30 +183,28 @@ void * api_thread(void *arg) { CURL *curl; channel_t *ch = (channel_t *) arg; /* casting argument */ - print(1, "Started logging thread", ch); - curl = api_curl_init(ch); - do { /* start thread mainloop */ + while (TRUE) { /* start thread mainloop */ CURLresponse response; json_object *json_obj; - char *json_str; + const char *json_str; long int http_code, curl_code; /* initialize response */ response.data = NULL; response.size = 0; - pthread_mutex_lock(&ch->mutex); - while (queue_is_empty(&ch->queue)) { /* detect spurious wakeups */ - pthread_cond_wait(&ch->condition, &ch->mutex); /* sleep until new data has been read */ + pthread_mutex_lock(&ch->buffer.mutex); + while (ch->buffer.sent == NULL) { /* detect spurious wakeups */ + pthread_cond_wait(&ch->condition, &ch->buffer.mutex); /* sleep until new data has been read */ } - pthread_mutex_unlock(&ch->mutex); + pthread_mutex_unlock(&ch->buffer.mutex); - json_obj = api_json_tuples(ch, FALSE); + json_obj = api_json_tuples(&ch->buffer, ch->buffer.sent); json_str = json_object_to_json_string(json_obj); - print(8, "JSON request body: %s", ch, json_str); + print(10, "JSON request body: %s", ch, json_str); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_str); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_custom_write_callback); @@ -211,9 +213,10 @@ void * api_thread(void *arg) { curl_code = curl_easy_perform(curl); curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); + /* check response */ if (curl_code == CURLE_OK && http_code == 200) { /* everything is ok */ - print(3, "Request succeeded with code: %i", ch, http_code); - queue_clear(&ch->queue); + print(4, "Request succeeded with code: %i", ch, http_code); + ch->buffer.sent = NULL; } else { /* error */ if (curl_code != CURLE_OK) { @@ -222,19 +225,22 @@ void * api_thread(void *arg) { else if (http_code != 200) { char err[255]; api_parse_exception(response, err); - print(-1, "Invalid middlware response: %s", ch, err); + print(-1, "Error from middleware: %s", ch, err); } - - print(2, "Sleeping %i seconds due to previous failure", ch, RETRY_PAUSE); - sleep(RETRY_PAUSE); } - + /* householding */ free(response.data); json_object_put(json_obj); - - pthread_testcancel(); /* test for cancelation request */ - } while (opts.daemon); + + if (!opts.daemon) { + break; + } + else if (curl_code != CURLE_OK || http_code != 200) { + print(2, "Sleeping %i seconds due to previous failure", ch, RETRY_PAUSE); + sleep(RETRY_PAUSE); + } + } curl_easy_cleanup(curl); /* always cleanup */ diff --git a/src/api.h b/bin/vzlogger/api.h similarity index 90% rename from src/api.h rename to bin/vzlogger/api.h index 54b9dce..e4d8789 100644 --- a/src/api.h +++ b/bin/vzlogger/api.h @@ -29,9 +29,9 @@ #include #include #include +#include -#include "main.h" -#include "protocol.h" +#include "buffer.h" typedef struct { char *data; @@ -42,7 +42,8 @@ typedef struct { int curl_custom_debug_callback(CURL *curl, curl_infotype type, char *data, size_t size, void *custom); size_t curl_custom_write_callback(void *ptr, size_t size, size_t nmemb, void *data); -json_object * api_json_tuples(channel_t *ch, bool_t all); +json_object * api_json_tuples(buffer_t *buf, meter_reading_t *start); void * api_thread(void *arg); +double api_tvtof(struct timeval tv); #endif /* _API_H_ */ diff --git a/bin/vzlogger/buffer.c b/bin/vzlogger/buffer.c new file mode 100644 index 0000000..bb04afe --- /dev/null +++ b/bin/vzlogger/buffer.c @@ -0,0 +1,123 @@ +/** + * Circular buffer (double linked) + * + * Used to store recent readings and buffer in case of net inconnectivity + * + * @author Steffen Vogel + * @copyright Copyright (c) 2011, The volkszaehler.org project + * @package vzlogger + * @license http://opensource.org/licenses/gpl-license.php GNU Public License + */ +/* + * This file is part of volkzaehler.org + * + * volkzaehler.org 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 + * any later version. + * + * volkzaehler.org 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 volkszaehler.org. If not, see . + */ + +#include +#include +#include + +#include "buffer.h" + +void buffer_init(buffer_t *buf, int mem) { + pthread_mutex_init(&buf->mutex, NULL); + + pthread_mutex_lock(&buf->mutex); + buf->last = NULL; + buf->start = NULL; + buf->sent = NULL; + buf->size = 0; + buf->memory = mem; + pthread_mutex_unlock(&buf->mutex); +} + +int buffer_push(buffer_t *buf, meter_reading_t rd) { + meter_reading_t *new = malloc(sizeof(meter_reading_t)); + + if (!new) { + return 0; /* cannot allocate memory */ + } + + memcpy(new, &rd, sizeof(meter_reading_t)); + + pthread_mutex_lock(&buf->mutex); + if (buf->last == NULL) { /* empty buffer */ + buf->start = new; + } + else { + buf->last->next = new; + } + + if (buf->sent == NULL) { /* add reading to send queue */ + buf->sent = new; + } + + new->next = NULL; + buf->last = new; + buf->size++; + pthread_mutex_unlock(&buf->mutex); + + buffer_clean(buf); + + return buf->size; +} + +void buffer_clean(buffer_t *buf) { + pthread_mutex_lock(&buf->mutex); + while(buf->size > buf->memory && buf->start != buf->sent) { + meter_reading_t *pop = buf->start; + + buf->start = buf->start->next; + buf->size--; + + free(pop); + } + pthread_mutex_unlock(&buf->mutex); +} + +char * buffer_dump(buffer_t *buf, char *dump, int len) { + strcpy(dump, "|"); + + for (meter_reading_t *rd = buf->start; rd != NULL; rd = rd->next) { + char tmp[16]; + sprintf(tmp, "%.2f|", rd->value); + + if (strlen(dump)+strlen(tmp) < len) { + if (buf->sent == rd) { /* indicate last sent reading */ + strcat(dump, "!"); + } + + strcat(dump, tmp); + } + else { + return NULL; /* dump buffer is full! */ + } + } + + return dump; +} + +void buffer_free(buffer_t *buf) { + pthread_mutex_destroy(&buf->mutex); + + meter_reading_t *rd = buf->start; + do { + meter_reading_t *tmp = rd; + rd = rd->next; + free(tmp); + } while (rd); + + memset(buf, 0, sizeof(buffer_t)); +} diff --git a/bin/vzlogger/buffer.h b/bin/vzlogger/buffer.h new file mode 100644 index 0000000..3de53c4 --- /dev/null +++ b/bin/vzlogger/buffer.h @@ -0,0 +1,57 @@ +/** + * Circular buffer (double linked, threadsafe) + * + * Used to store recent readings and buffer in case of net inconnectivity + * + * @author Steffen Vogel + * @copyright Copyright (c) 2011, The volkszaehler.org project + * @package vzlogger + * @license http://opensource.org/licenses/gpl-license.php GNU Public License + */ +/* + * This file is part of volkzaehler.org + * + * volkzaehler.org 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 + * any later version. + * + * volkzaehler.org 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 volkszaehler.org. If not, see . + */ + +#ifndef _BUFFER_H_ +#define _BUFFER_H_ + +#include +#include + +#include + +typedef struct { + meter_reading_t *last; + meter_reading_t *start; + meter_reading_t *sent; + + int size; /* number of readings currently in the buffer */ + int memory; /* number of readings to keep in mind for local interface */ + + pthread_mutex_t mutex; +} buffer_t; + +/* Prototypes */ +void buffer_init(buffer_t *buf, int mem); +int buffer_push(buffer_t *buf, meter_reading_t rd); +void buffer_free(buffer_t *buf); +void buffer_clean(buffer_t *buf); +void buffer_clear(buffer_t *buf); +char * buffer_dump(buffer_t *buf, char *dump, int len); + + +#endif /* _BUFFER_H_ */ + diff --git a/bin/vzlogger/channel.c b/bin/vzlogger/channel.c new file mode 100644 index 0000000..d61a77e --- /dev/null +++ b/bin/vzlogger/channel.c @@ -0,0 +1,91 @@ +/** + * Channel class + * + * @package vzlogger + * @copyright Copyright (c) 2011, The volkszaehler.org project + * @license http://www.gnu.org/licenses/gpl.txt GNU Public License + * @author Steffen Vogel + */ +/* + * This file is part of volkzaehler.org + * + * volkzaehler.org 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 + * any later version. + * + * volkzaehler.org 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 volkszaehler.org. If not, see . + */ + +#include +#include +#include + +#include "vzlogger.h" +#include "api.h" +#include "channel.h" +#include "options.h" + +extern options_t opts; + +void reading_thread(void *arg) { + channel_t *ch = (channel_t *) arg; + + while (1) { + meter_reading_t rd = meter_read(&ch->meter); + print(1, "Value read: %.2f at %.0f", ch, rd.value, api_tvtof(rd.tv)); + + buffer_push(&ch->buffer, rd); + pthread_mutex_lock(&ch->buffer.mutex); + pthread_cond_broadcast(&ch->condition); /* notify webserver and logging thread */ + pthread_mutex_unlock(&ch->buffer.mutex); + + /* Debugging */ + if (opts.verbose >= 10) { + char dump[1024]; + buffer_dump(&ch->buffer, dump, 1024); + print(10, "Buffer dump: %s (size=%i, memory=%i)", ch, dump, ch->buffer.size, ch->buffer.memory); + } + + if (ch->meter.type->periodical) { + print(8, "Next reading in %i seconds", ch, ch->interval); + sleep(ch->interval); + } + } +} + +void channel_init(channel_t *ch, char *uuid, char *middleware, unsigned long interval, char *options, meter_type_t *type) { + static int instances; /* static to generate channel ids */ + + int buffer_size = (type->periodical) ? (BUFFER_DURATION / interval) + 1 : BUFFER_LENGTH; + + ch->id = instances++; + ch->interval = interval; + ch->uuid = strdup(uuid); + ch->middleware = strdup(middleware); + ch->options = strdup(options); + + meter_init(&ch->meter, type, options); + buffer_init(&ch->buffer, buffer_size); /* initialize buffer */ + + pthread_cond_init(&ch->condition, NULL); /* initialize thread syncronization helpers */ +} + +/** + * Free all allocated memory recursivly + */ +void channel_free(channel_t *ch) { + buffer_free(&ch->buffer); + meter_free(&ch->meter); + pthread_cond_destroy(&ch->condition); + + free(ch->uuid); + free(ch->options); + free(ch->middleware); +} diff --git a/bin/vzlogger/channel.h b/bin/vzlogger/channel.h new file mode 100644 index 0000000..0e379ee --- /dev/null +++ b/bin/vzlogger/channel.h @@ -0,0 +1,60 @@ +/** + * Channel handling + * + * @package vzlogger + * @copyright Copyright (c) 2011, The volkszaehler.org project + * @license http://www.gnu.org/licenses/gpl.txt GNU Public License + * @author Steffen Vogel + */ +/* + * This file is part of volkzaehler.org + * + * volkzaehler.org 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 + * any later version. + * + * volkzaehler.org 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 volkszaehler.org. If not, see . + */ + +#ifndef _CHANNEL_H_ +#define _CHANNEL_H_ + +#include +#include + +#include + +#include "buffer.h" + +typedef struct channel { + unsigned int id; /* only for internal usage & debugging */ + char *middleware; /* url to middleware */ + char *options; /* protocols specific configuration */ + char *uuid; /* unique identifier for middleware */ + + unsigned long interval; /* polling interval (for sensors only) */ + + meter_t meter; /* handle to store connection status */ + buffer_t buffer; /* circular queue to buffer readings */ + + pthread_t logging_thread; /* pthread for asynchronus logging */ + pthread_t reading_thread; /* pthread for asynchronus reading */ + pthread_cond_t condition; /* pthread syncronization to notify logging thread and local webserver */ + + struct channel *next; /* pointer for linked list */ +} channel_t; + +/* Prototypes */ +void channel_init(channel_t *ch, char *uuid, char *middleware, unsigned long interval, char *options, meter_type_t *type); +void channel_free(channel_t *ch); + +void reading_thread(void *arg); + +#endif /* _CHANNEL_H_ */ diff --git a/src/queue.h b/bin/vzlogger/list.c similarity index 57% rename from src/queue.h rename to bin/vzlogger/list.c index 3832f22..443f94c 100644 --- a/src/queue.h +++ b/bin/vzlogger/list.c @@ -1,5 +1,5 @@ /** - * Circular queue to buffer readings + * Linked list to manage channels * * @package vzlogger * @copyright Copyright (c) 2011, The volkszaehler.org project @@ -22,38 +22,47 @@ * You should have received a copy of the GNU General Public License * along with volkszaehler.org. If not, see . */ - -#ifndef _QUEUE_H_ -#define _QUEUE_H_ -#include "protocol.h" +#include +#include -#ifndef TRUE -#define TRUE 1 -#endif +#include "list.h" -#ifndef FALSE -#define FALSE 0 -#endif +void list_init(list_t *ls) { + ls->start = NULL; + ls->size = 0; +} -typedef char bool_t; +int list_push(list_t *ls, channel_t ch) { + channel_t *new = malloc(sizeof(channel_t)); -typedef struct { - size_t size; - - size_t read_p; - size_t write_p; - - reading_t *buf; -} queue_t; + if (!new) { + return 0; /* cannot allocate memory */ + } -bool_t queue_init(queue_t *q, size_t size); -bool_t queue_is_empty(queue_t *q); -bool_t queue_get(queue_t *q, size_t index, reading_t *rd); -bool_t queue_push(queue_t *q, reading_t rd); -void queue_clear(queue_t *q); -void queue_free(queue_t *q); -char * queue_print(queue_t *q); + memcpy(new, &ch, sizeof(channel_t)); -#endif /* _QUEUE_H_ */ + if (ls->start == NULL) { /* empty list */ + new->next = NULL; + } + else { + new->next = ls->start; + } + ls->start = new; + ls->size++; + + return ls->size; +} + +void list_free(list_t *ls) { + channel_t *ch = ls->start; + do { + channel_t *tmp = ch; + ch = ch->next; + channel_free(tmp); + } while (ch); + + ls->start = NULL; + ls->size = 0; +} diff --git a/src/protocols/random.h b/bin/vzlogger/list.h similarity index 74% rename from src/protocols/random.h rename to bin/vzlogger/list.h index eea173d..7ba93ab 100644 --- a/src/protocols/random.h +++ b/bin/vzlogger/list.h @@ -1,5 +1,5 @@ /** - * Generate pseudo random data series with a random walk + * Linked list to manage channels * * @package vzlogger * @copyright Copyright (c) 2011, The volkszaehler.org project @@ -23,19 +23,19 @@ * along with volkszaehler.org. If not, see . */ -#ifndef _RANDOM_H_ -#define _RANDOM_H_ +#ifndef _LIST_H_ +#define _LIST_H_ -#include "../protocol.h" +#include "channel.h" typedef struct { - float min, max, last; -} random_state_t; + channel_t *start; + int size; +} list_t; -double ltqnorm(double p); +/* Prototypes */ +void list_init(list_t *ls); +int list_push(list_t *ls, channel_t ch); +void list_free(list_t *ls); -void * random_init(char *port); -void random_close(void *handle); -reading_t random_get(void *handle); - -#endif /* _RANDOM_H_ */ +#endif /* _LIST_H_ */ diff --git a/src/local.c b/bin/vzlogger/local.c similarity index 74% rename from src/local.c rename to bin/vzlogger/local.c index 65b100f..c0a13c7 100644 --- a/src/local.c +++ b/bin/vzlogger/local.c @@ -26,39 +26,50 @@ #include #include #include +#include -#include "main.h" +#include "vzlogger.h" #include "local.h" +#include "options.h" +#include "api.h" -extern channel_t chans[MAX_CHANNELS]; +extern list_t chans; extern options_t opts; int handle_request(void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls) { - const char * json_str; + int ret; - int num_chans = *(int *) cls; - print(2, "Local request received: %s %s %s", NULL, version, method, url); + const char *json_str; + const char *uuid = url+1; + + struct timespec ts; + struct timeval tp; struct MHD_Response *response; struct json_object *json_obj = json_object_new_object(); struct json_object *json_data = json_object_new_object(); - for (int i = 0; i < num_chans; i++) { - channel_t *ch = &chans[i]; - reading_t rd; + print(2, "Local request received: %s %s %s", NULL, version, method, url); - if (strcmp(url, "/") == 0 || strcmp(ch->uuid, url + 1) == 0) { - pthread_mutex_lock(&ch->mutex); - /* wait for new data comet-like blocking of HTTP response */ - pthread_cond_wait(&ch->condition, &ch->mutex); // TODO use pthread_cond_timedwait() - pthread_mutex_unlock(&ch->mutex); + for (channel_t *ch = chans.start; ch != NULL; ch = ch->next) { + if (strcmp(url, "/") == 0 || strcmp(ch->uuid, uuid) == 0) { + /* convert from timeval to timespec */ + gettimeofday(&tp, NULL); + ts.tv_sec = tp.tv_sec; + ts.tv_nsec = tp.tv_usec * 1000; + ts.tv_sec += COMET_TIMEOUT; - struct json_object *json_tuples = api_json_tuples(ch, TRUE); + /* blocking until new data arrives (comet-like blocking of HTTP response) */ + pthread_mutex_lock(&ch->buffer.mutex); + pthread_cond_timedwait(&ch->condition, &ch->buffer.mutex, &ts); + pthread_mutex_unlock(&ch->buffer.mutex); json_object_object_add(json_data, "uuid", json_object_new_string(ch->uuid)); json_object_object_add(json_data, "interval", json_object_new_int(ch->interval)); + + struct json_object *json_tuples = api_json_tuples(&ch->buffer, ch->buffer.start); json_object_object_add(json_data, "tuples", json_tuples); } } diff --git a/src/local.h b/bin/vzlogger/local.h similarity index 100% rename from src/local.h rename to bin/vzlogger/local.h diff --git a/bin/vzlogger/options.c b/bin/vzlogger/options.c new file mode 100644 index 0000000..b03d578 --- /dev/null +++ b/bin/vzlogger/options.c @@ -0,0 +1,231 @@ +/** + * Parsing commandline options and channel list + * + * @author Steffen Vogel + * @copyright Copyright (c) 2011, The volkszaehler.org project + * @package vzlogger + * @license http://opensource.org/licenses/gpl-license.php GNU Public License + */ +/* + * This file is part of volkzaehler.org + * + * volkzaehler.org 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 + * any later version. + * + * volkzaehler.org 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 volkszaehler.org. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "../../config.h" + +#include "list.h" +#include "options.h" +#include "channel.h" +#include "vzlogger.h" + +extern meter_type_t meter_types[]; + +options_t opts = { /* setting default options */ + "/etc/vzlogger.conf", /* config file */ + 8080, /* port for local interface */ + 0, /* verbosity level */ + 0, /* daemon mode */ + 0 /* local interface */ +}; + +/** + * Command line options + */ +struct option long_options[] = { + {"config", required_argument, 0, 'c'}, + {"daemon", required_argument, 0, 'd'}, +#ifdef LOCAL_SUPPORT + {"local", no_argument, 0, 'l'}, + {"local-port", required_argument, 0, 'p'}, +#endif /* LOCAL_SUPPORT */ + {"verbose", optional_argument, 0, 'v'}, + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'V'}, + {} /* stop condition for iterator */ +}; + +/** + * Descriptions vor command line options + */ +char *long_options_descs[] = { + "config file with channel -> uuid mapping", + "run as daemon", +#ifdef LOCAL_SUPPORT + "activate local interface (tiny webserver)", + "TCP port for local interface", +#endif /* LOCAL_SUPPORT */ + "enable verbose output", + "show this help", + "show version of vzlogger", + "" /* stop condition for iterator */ +}; + +/** + * Parse options from command line + */ +void parse_options(int argc, char * argv[], options_t * opts) { + while (1) { + /* getopt_long stores the option index here. */ + int option_index = 0; + + int c = getopt_long(argc, argv, "i:c:p:lhVdv::", long_options, &option_index); + + /* detect the end of the options. */ + if (c == -1) + break; + + switch (c) { + case 'v': + opts->verbose = (optarg == NULL) ? 1 : atoi(optarg); + break; + +#ifdef LOCAL_SUPPORT + case 'l': + opts->local = 1; + break; + + case 'p': /* port for local interface */ + opts->port = atoi(optarg); + break; +#endif /* LOCAL_SUPPORT */ + + case 'd': + opts->daemon = 1; + break; + + case 'c': /* read config file */ + opts->config = (char *) malloc(strlen(optarg)+1); + strcpy(opts->config, optarg); + break; + + case 'V': + printf("%s\n", VERSION); + exit(EXIT_SUCCESS); + break; + + case 'h': + case '?': + usage(argv); + exit((c == '?') ? EXIT_FAILURE : EXIT_SUCCESS); + } + } +} + +void parse_channels(char *filename, list_t *chans) { + FILE *file = fopen(filename, "r"); /* open configuration */ + + if (!filename) { /* nothing found */ + print(-1, "No config file found! Please specify with --config!\n", NULL); + exit(EXIT_FAILURE); + } + + if (file == NULL) { + perror(filename); /* why didn't the file open? */ + exit(EXIT_FAILURE); + } + else { + print(2, "Start parsing configuration from %s", NULL, filename); + } + + int lineno = 1; + char *buffer = malloc(256); + char *tokens[5]; + char *line; + + /* compile regular expressions */ + regex_t re_uuid, re_middleware; + regcomp(&re_uuid, "^[a-f0-9]{8}-([a-f0-9]{4}-){3,3}[a-f0-9]{12}$", REG_EXTENDED | REG_ICASE | REG_NOSUB); + regcomp(&re_middleware, "^https?://[a-z0-9.-]+\\.[a-z]{2,6}(/\\S*)?$", REG_EXTENDED | REG_ICASE | REG_NOSUB); + + /*regerror(err, &re_uuid, buffer, 256); + printf("Error analyzing regular expression: %s.\n", buffer);*/ + + while ((line = fgets(buffer, 256, file)) != NULL) { /* read a line */ + line[strcspn(line, "\n\r;")] = '\0'; /* strip newline and comments */ + if (strlen(line) == 0) continue; /* skip empty lines */ + + /* channel properties */ + char *middleware, *options, *uuid; + unsigned long interval; + channel_t ch; + meter_type_t *type; + + /* parse tokens (required) */ + memset(tokens, 0, 5); + for (int i = 0; i < 5; i++) { + do { + tokens[i] = strsep(&line, " \t"); + } while (tokens[i] == NULL && line != NULL); + } + + /* protocol (required) */ + for (type = meter_types; type->name != NULL; type++) { /* linear search */ + if (strcmp(type->name, tokens[0]) == 0) break; + } + + if (type->name == NULL) { /* reached end */ + print(-1, "Invalid protocol: %s in %s:%i", NULL, tokens[0], filename, lineno); + exit(EXIT_FAILURE); + } + + /* middleware (required) */ + middleware = tokens[1]; + if (regexec(&re_middleware, middleware, 0, NULL, 0) == 0) { + print(-1, "Invalid interval: %s in %s:%i", NULL, tokens[1], filename, lineno); + exit(EXIT_FAILURE); + } + + /* uuid (required) */ + uuid = tokens[2]; + if (regexec(&re_uuid, uuid, 0, NULL, 0) != 0) { + print(-1, "Invalid uuid: %s in %s:%i", NULL, tokens[2], filename, lineno); + exit(EXIT_FAILURE); + } + + /* interval (only if protocol is sensor) */ + if (type->periodical) { + interval = strtol(tokens[3], (char **) NULL, 10); + if (errno == EINVAL || errno == ERANGE) { + print(-1, "Invalid interval: %s in %s:%i", NULL, tokens[3], filename, lineno); + exit(EXIT_FAILURE); + } + } + else { + interval = 0; + } + + /* options (optional) */ + options = tokens[type->periodical ? 4 : 3]; + + channel_init(&ch, uuid, middleware, interval, options, type); + print(1, "Parsed (protocol=%s interval=%i uuid=%s middleware=%s options=%s)", &ch, type->name, interval, uuid, middleware, options); + + list_push(chans, ch); + lineno++; + } + + fclose(file); + free(buffer); + regfree(&re_middleware); + regfree(&re_uuid); +} diff --git a/bin/vzlogger/options.h b/bin/vzlogger/options.h new file mode 100644 index 0000000..ccb332d --- /dev/null +++ b/bin/vzlogger/options.h @@ -0,0 +1,48 @@ +/** + * Parsing commandline options and channel list + * + * @author Steffen Vogel + * @copyright Copyright (c) 2011, The volkszaehler.org project + * @package vzlogger + * @license http://opensource.org/licenses/gpl-license.php GNU Public License + */ +/* + * This file is part of volkzaehler.org + * + * volkzaehler.org 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 + * any later version. + * + * volkzaehler.org 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 volkszaehler.org. If not, see . + */ + +#ifndef _OPTIONS_H_ +#define _OPTIONS_H_ + +#include "list.h" + +/** + * Options from command line + */ +typedef struct { + char *config; /* path to config file */ + unsigned int port; /* tcp port for local interface */ + int verbose; /* verbosity level */ + + /* boolean bitfields, at the end of struct */ + int daemon:1; + int local:1; /* enable local interface */ +} options_t; + +/* Prototypes */ +void parse_options(int argc, char *argv[], options_t *opts); +void parse_channels(char *filename, list_t *chans); + +#endif /* _OPTIONS_H_ */ diff --git a/bin/vzlogger/vzlogger b/bin/vzlogger/vzlogger new file mode 100755 index 0000000000000000000000000000000000000000..770d4cc6e1a19c29c67ccbb7ff0046c61c63bf32 GIT binary patch literal 151026 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 + */ +/* + * This file is part of volkzaehler.org + * + * volkzaehler.org 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 + * any later version. + * + * volkzaehler.org 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 volkszaehler.org. If not, see . + */ + + +#include /* for print() */ +#include + +#include +#include +#include + +#include "vzlogger.h" + +#include "list.h" +#include "buffer.h" +#include "channel.h" +#include "api.h" +#include "options.h" + +#ifdef LOCAL_SUPPORT +#include +#include "local.h" +#endif /* LOCAL_SUPPORT */ + +/* global variables */ +list_t chans; +extern options_t opts; +extern char *long_options_descs[]; +extern struct option long_options[]; +extern meter_type_t meter_types[]; + +/** + * Print available options and some other usefull information + */ +void usage(char *argv[]) { + char **desc = long_options_descs; + struct option *op = long_options; + meter_type_t *type = meter_types; + + printf("Usage: %s [options]\n\n", argv[0]); + printf(" following options are available:\n"); + + while (op->name && *desc) { + printf("\t-%c, --%-12s\t%s\n", op->val, op->name, *desc); + op++; desc++; + } + + printf("\n"); + printf(" following protocol types are supported:\n"); + + while (type->name) { + printf("\t%-12s\t%s\n", type->name, type->desc); + type++; + } + + printf("\n%s - volkszaehler.org logging utility\n", PACKAGE_STRING); + printf("by Steffen Vogel \n"); + printf("send bugreports to %s\n", PACKAGE_BUGREPORT); +} + +/** + * Wrapper to log notices and errors + * + * @param ch could be NULL for general messages + * @todo integrate into syslog + */ +void print(int level, char * format, channel_t *ch, ... ) { + va_list args; + + struct timeval now; + struct tm * timeinfo; + char buffer[1024] = "[", *pos = buffer+1; + + if (level <= opts.verbose) { + gettimeofday(&now, NULL); + timeinfo = localtime(&now.tv_sec); + pos += strftime(pos, 16, "%b %d %H:%M:%S", timeinfo); + + pos += sprintf(pos, ".%03lu] ", now.tv_usec / 1000); + + if (ch != NULL) { + pos += sprintf(pos, "[ch#%i] ", ch->id); + } + + va_start(args, ch); + pos += vsprintf(pos, format, args); + va_end(args); + + fprintf((level > 0) ? stdout : stderr, "%s\n", buffer); + } +} + +/** + * Cancel threads + * + * Threads gets joined in main() + */ +void quit(int sig) { + print(2, "Closing connections to terminate", NULL); + for (channel_t *ch = chans.start; ch != NULL; ch = ch->next) { + pthread_cancel(ch->logging_thread); + pthread_cancel(ch->reading_thread); + } +} + +/** + * The main loop + */ +int main(int argc, char *argv[]) { + /* bind signal handler */ + struct sigaction action; + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + action.sa_handler = quit; + sigaction(SIGINT, &action, NULL); + + list_init(&chans); + parse_options(argc, argv, &opts); /* parse command line arguments */ + parse_channels(opts.config, &chans); /* parse channels from configuration */ + + curl_global_init(CURL_GLOBAL_ALL); /* global intialization for all threads */ + + for (channel_t *ch = chans.start; ch != NULL; ch = ch->next) { + print(5, "Opening connection to meter", ch); + meter_open(&ch->meter); + + print(5, "Starting threads", ch); + pthread_create(&ch->logging_thread, NULL, &api_thread, (void *) ch); + pthread_create(&ch->reading_thread, NULL, &reading_thread, (void *) ch); + } + +#ifdef LOCAL_SUPPORT + struct MHD_Daemon *httpd_handle = NULL; + if (opts.local) { /* start webserver for local interface */ + print(5, "Starting local interface HTTPd on port %i", NULL, opts.port); + httpd_handle = MHD_start_daemon( + MHD_USE_THREAD_PER_CONNECTION, + opts.port, + NULL, NULL, + handle_request, + NULL, + MHD_OPTION_END + ); + } +#endif /* LOCAL_SUPPORT */ + + /* wait for all threads to terminate */ + for (channel_t *ch = chans.start; ch != NULL; ch = ch->next) { + pthread_join(ch->logging_thread, NULL); + pthread_join(ch->reading_thread, NULL); + + meter_close(&ch->meter); /* closing connection */ + } + +#ifdef LOCAL_SUPPORT + /* stop webserver */ + if (httpd_handle) { + print(8, "Stopping local interface HTTPd on port %i", NULL, opts.port); + MHD_stop_daemon(httpd_handle); + } +#endif /* LOCAL_SUPPORT */ + + /* householding */ + list_free(&chans); + curl_global_cleanup(); + + print(10, "Bye bye!", NULL); + + return EXIT_SUCCESS; +} diff --git a/bin/vzlogger/vzlogger.h b/bin/vzlogger/vzlogger.h new file mode 100644 index 0000000..6e75cda --- /dev/null +++ b/bin/vzlogger/vzlogger.h @@ -0,0 +1,45 @@ +/** + * Main header file + * + * @package vzlogger + * @copyright Copyright (c) 2011, The volkszaehler.org project + * @license http://www.gnu.org/licenses/gpl.txt GNU Public License + * @author Steffen Vogel + */ +/* + * This file is part of volkzaehler.org + * + * volkzaehler.org 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 + * any later version. + * + * volkzaehler.org 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 volkszaehler.org. If not, see . + */ + +#ifndef _VZLOGGER_H_ +#define _VZLOGGER_H_ + +#include "../../config.h" + +#include + +#include "channel.h" + +/* some hard coded configuration */ +#define RETRY_PAUSE 10 //600 /* seconds to wait after failed request */ +#define BUFFER_DURATION 60 /* in seconds */ +#define BUFFER_LENGTH 256 /* in readings */ +#define COMET_TIMEOUT 30 /* seconds */ + +/* Prototypes */ +void print(int level, char *format, channel_t *ch, ... ); +void usage(char ** argv); + +#endif /* _VZLOGGER_H_ */ diff --git a/config.h.in b/config.h.in index b17f255..f68d49d 100644 --- a/config.h.in +++ b/config.h.in @@ -73,7 +73,10 @@ #undef HAVE_UNISTD_H /* Local interface */ -#undef LOCAL +#undef LOCAL_SUPPORT + +/* Define to 1 if your C compiler doesn't accept -c and -o together. */ +#undef NO_MINUS_C_MINUS_O /* Name of package */ #undef PACKAGE @@ -96,6 +99,9 @@ /* Define to the version of this package. */ #undef PACKAGE_VERSION +/* Smart Messaging Language */ +#undef SML_SUPPORT + /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS diff --git a/configure b/configure index c83a134..2971782 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.67 for vzlogger 0.2. +# Generated by GNU Autoconf 2.68 for vzlogger 0.2. # # Report bugs to . # @@ -91,6 +91,7 @@ fi IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. +as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -216,11 +217,18 @@ IFS=$as_save_IFS # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. + # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV export CONFIG_SHELL - exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} + case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; + esac + exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"} fi if test x$as_have_required = xno; then : @@ -557,7 +565,7 @@ PACKAGE_STRING='vzlogger 0.2' PACKAGE_BUGREPORT='http://bugs.volkszaehler.org' PACKAGE_URL='' -ac_unique_file="src/main.c" +ac_unique_file="src/meter.c" # Factoring default headers for most tests. ac_includes_default="\ #include @@ -599,17 +607,22 @@ am__EXEEXT_TRUE LTLIBOBJS DEPS_LOCAL_LIBS DEPS_LOCAL_CFLAGS -LOCAL_FALSE -LOCAL_TRUE +LOCAL_SUPPORT_FALSE +LOCAL_SUPPORT_TRUE +DEPS_SML_LIBS +DEPS_SML_CFLAGS +SML_SUPPORT_FALSE +SML_SUPPORT_TRUE LIBOBJS EGREP GREP CPP -DEPS_LIBS -DEPS_CFLAGS +DEPS_VZ_LIBS +DEPS_VZ_CFLAGS PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG +RANLIB am__fastdepCC_FALSE am__fastdepCC_TRUE CCDEPMODE @@ -691,7 +704,8 @@ ac_subst_files='' ac_user_opts=' enable_option_checking enable_dependency_tracking -enable_local_interface +enable_sml +enable_local enable_debug ' ac_precious_vars='build_alias @@ -705,9 +719,11 @@ CPPFLAGS PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR -DEPS_CFLAGS -DEPS_LIBS +DEPS_VZ_CFLAGS +DEPS_VZ_LIBS CPP +DEPS_SML_CFLAGS +DEPS_SML_LIBS DEPS_LOCAL_CFLAGS DEPS_LOCAL_LIBS' @@ -1114,7 +1130,7 @@ Try \`$0 --help' for more information" $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 - : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac @@ -1328,6 +1344,7 @@ Optional Features: --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --disable-dependency-tracking speeds up one-time build --enable-dependency-tracking do not reject slow dependency extractors + --enable-sml enable support for smart messaging language (def=no) --enable-local enable support for local HTTPd (def=no) --enable-debug enable debug data generation (def=no) @@ -1344,9 +1361,15 @@ Some influential environment variables: directories to add to pkg-config's search path PKG_CONFIG_LIBDIR path overriding pkg-config's built-in search path - DEPS_CFLAGS C compiler flags for DEPS, overriding pkg-config - DEPS_LIBS linker flags for DEPS, overriding pkg-config + DEPS_VZ_CFLAGS + C compiler flags for DEPS_VZ, overriding pkg-config + DEPS_VZ_LIBS + linker flags for DEPS_VZ, overriding pkg-config CPP C preprocessor + DEPS_SML_CFLAGS + C compiler flags for DEPS_SML, overriding pkg-config + DEPS_SML_LIBS + linker flags for DEPS_SML, overriding pkg-config DEPS_LOCAL_CFLAGS C compiler flags for DEPS_LOCAL, overriding pkg-config DEPS_LOCAL_LIBS @@ -1419,7 +1442,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF vzlogger configure 0.2 -generated by GNU Autoconf 2.67 +generated by GNU Autoconf 2.68 Copyright (C) 2010 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation @@ -1465,7 +1488,7 @@ sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi - eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile @@ -1502,7 +1525,7 @@ sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi - eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp @@ -1515,10 +1538,10 @@ fi ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if eval "test \"\${$3+set}\"" = set; then : + if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if eval "test \"\${$3+set}\"" = set; then : +if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 @@ -1585,7 +1608,7 @@ $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if eval "test \"\${$3+set}\"" = set; then : +if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" @@ -1594,7 +1617,7 @@ eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi - eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_mongrel @@ -1635,7 +1658,7 @@ sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run @@ -1649,7 +1672,7 @@ ac_fn_c_check_header_compile () as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if eval "test \"\${$3+set}\"" = set; then : +if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -1667,7 +1690,7 @@ fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } - eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile @@ -1680,7 +1703,7 @@ ac_fn_c_check_type () as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if eval "test \"\${$3+set}\"" = set; then : +if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=no" @@ -1721,7 +1744,7 @@ fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } - eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_type @@ -1736,7 +1759,7 @@ ac_fn_c_check_decl () as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 $as_echo_n "checking whether $as_decl_name is declared... " >&6; } -if eval "test \"\${$3+set}\"" = set; then : +if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -1767,7 +1790,7 @@ fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } - eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_decl @@ -1812,7 +1835,7 @@ fi # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link @@ -1825,7 +1848,7 @@ ac_fn_c_check_func () as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if eval "test \"\${$3+set}\"" = set; then : +if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -1880,7 +1903,7 @@ fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } - eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func cat >config.log <<_ACEOF @@ -1888,7 +1911,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by vzlogger $as_me 0.2, which was -generated by GNU Autoconf 2.67. Invocation command line was +generated by GNU Autoconf 2.68. Invocation command line was $ $0 $@ @@ -2146,7 +2169,7 @@ $as_echo "$as_me: loading site script $ac_site_file" >&6;} || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file -See \`config.log' for more details" "$LINENO" 5 ; } +See \`config.log' for more details" "$LINENO" 5; } fi done @@ -2283,7 +2306,7 @@ ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 $as_echo_n "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then -if test "${ac_cv_path_install+set}" = set; then : +if ${ac_cv_path_install+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -2370,11 +2393,11 @@ am_lf=' ' case `pwd` in *[\\\"\#\$\&\'\`$am_lf]*) - as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5 ;; + as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; esac case $srcdir in *[\\\"\#\$\&\'\`$am_lf\ \ ]*) - as_fn_error $? "unsafe srcdir value: \`$srcdir'" "$LINENO" 5 ;; + as_fn_error $? "unsafe srcdir value: \`$srcdir'" "$LINENO" 5;; esac # Do `set' in a subshell so we don't clobber the current shell's @@ -2460,7 +2483,7 @@ if test "$cross_compiling" != no; then set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_STRIP+set}" = set; then : +if ${ac_cv_prog_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then @@ -2500,7 +2523,7 @@ if test -z "$ac_cv_prog_STRIP"; then set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then : +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then @@ -2553,7 +2576,7 @@ INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 $as_echo_n "checking for a thread-safe mkdir -p... " >&6; } if test -z "$MKDIR_P"; then - if test "${ac_cv_path_mkdir+set}" = set; then : + if ${ac_cv_path_mkdir+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -2604,7 +2627,7 @@ do set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_AWK+set}" = set; then : +if ${ac_cv_prog_AWK+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AWK"; then @@ -2644,7 +2667,7 @@ done $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` -if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\"" = set; then : +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF @@ -2745,7 +2768,7 @@ am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -' ac_config_headers="$ac_config_headers config.h" -ac_config_files="$ac_config_files Makefile src/Makefile docs/Makefile" +ac_config_files="$ac_config_files Makefile docs/Makefile src/Makefile bin/reader/Makefile bin/vzlogger/Makefile" # Checks for programs. @@ -2759,7 +2782,7 @@ if test -n "$ac_tool_prefix"; then set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_CC+set}" = set; then : +if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then @@ -2799,7 +2822,7 @@ if test -z "$ac_cv_prog_CC"; then set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_ac_ct_CC+set}" = set; then : +if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then @@ -2852,7 +2875,7 @@ if test -z "$CC"; then set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_CC+set}" = set; then : +if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then @@ -2892,7 +2915,7 @@ if test -z "$CC"; then set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_CC+set}" = set; then : +if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then @@ -2951,7 +2974,7 @@ if test -z "$CC"; then set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_CC+set}" = set; then : +if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then @@ -2995,7 +3018,7 @@ do set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_ac_ct_CC+set}" = set; then : +if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then @@ -3050,7 +3073,7 @@ fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH -See \`config.log' for more details" "$LINENO" 5 ; } +See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 @@ -3165,7 +3188,7 @@ sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables -See \`config.log' for more details" "$LINENO" 5 ; } +See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } @@ -3208,7 +3231,7 @@ else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details" "$LINENO" 5 ; } +See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 @@ -3267,7 +3290,7 @@ $as_echo "$ac_try_echo"; } >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. -See \`config.log' for more details" "$LINENO" 5 ; } +See \`config.log' for more details" "$LINENO" 5; } fi fi fi @@ -3278,7 +3301,7 @@ rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } -if test "${ac_cv_objext+set}" = set; then : +if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -3319,7 +3342,7 @@ sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile -See \`config.log' for more details" "$LINENO" 5 ; } +See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi @@ -3329,7 +3352,7 @@ OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } -if test "${ac_cv_c_compiler_gnu+set}" = set; then : +if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -3366,7 +3389,7 @@ ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } -if test "${ac_cv_prog_cc_g+set}" = set; then : +if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag @@ -3444,7 +3467,7 @@ else fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } -if test "${ac_cv_prog_cc_c89+set}" = set; then : +if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no @@ -3605,7 +3628,7 @@ depcc="$CC" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } -if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then : +if ${am_cv_CC_dependencies_compiler_type+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then @@ -3729,6 +3752,228 @@ fi +# We want to build a lib +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 +$as_echo "$RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 +$as_echo "$ac_ct_RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_RANLIB" = x; then + RANLIB=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + RANLIB=$ac_ct_RANLIB + fi +else + RANLIB="$ac_cv_prog_RANLIB" +fi + + +# We use per target compiler flags +if test "x$CC" != xcc; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC and cc understand -c and -o together" >&5 +$as_echo_n "checking whether $CC and cc understand -c and -o together... " >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether cc understands -c and -o together" >&5 +$as_echo_n "checking whether cc understands -c and -o together... " >&6; } +fi +set dummy $CC; ac_cc=`$as_echo "$2" | + sed 's/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/'` +if eval \${ac_cv_prog_cc_${ac_cc}_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +# Make sure it works both with $CC and with simple cc. +# We do the test twice because some compilers refuse to overwrite an +# existing .o file with -o, though they will create one. +ac_try='$CC -c conftest.$ac_ext -o conftest2.$ac_objext >&5' +rm -f conftest2.* +if { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && + test -f conftest2.$ac_objext && { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; +then + eval ac_cv_prog_cc_${ac_cc}_c_o=yes + if test "x$CC" != xcc; then + # Test first that cc exists at all. + if { ac_try='cc -c conftest.$ac_ext >&5' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + ac_try='cc -c conftest.$ac_ext -o conftest2.$ac_objext >&5' + rm -f conftest2.* + if { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && + test -f conftest2.$ac_objext && { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; + then + # cc works too. + : + else + # cc exists but doesn't like -o. + eval ac_cv_prog_cc_${ac_cc}_c_o=no + fi + fi + fi +else + eval ac_cv_prog_cc_${ac_cc}_c_o=no +fi +rm -f core conftest* + +fi +if eval test \$ac_cv_prog_cc_${ac_cc}_c_o = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +$as_echo "#define NO_MINUS_C_MINUS_O 1" >>confdefs.h + +fi + +# FIXME: we rely on the cache variable name because +# there is no other way. +set dummy $CC +am_cc=`echo $2 | sed 's/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/'` +eval am_t=\$ac_cv_prog_cc_${am_cc}_c_o +if test "$am_t" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi + + + # Checks for libraries. @@ -3736,13 +3981,14 @@ fi + if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_path_PKG_CONFIG+set}" = set; then : +if ${ac_cv_path_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $PKG_CONFIG in @@ -3785,7 +4031,7 @@ if test -z "$ac_cv_path_PKG_CONFIG"; then set dummy pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_path_ac_pt_PKG_CONFIG+set}" = set; then : +if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_PKG_CONFIG in @@ -3851,11 +4097,11 @@ $as_echo "no" >&6; } fi pkg_failed=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for DEPS" >&5 -$as_echo_n "checking for DEPS... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for DEPS_VZ" >&5 +$as_echo_n "checking for DEPS_VZ... " >&6; } -if test -n "$DEPS_CFLAGS"; then - pkg_cv_DEPS_CFLAGS="$DEPS_CFLAGS" +if test -n "$DEPS_VZ_CFLAGS"; then + pkg_cv_DEPS_VZ_CFLAGS="$DEPS_VZ_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"json >= 0.9 libcurl >= 7.21.0\""; } >&5 @@ -3863,15 +4109,16 @@ if test -n "$DEPS_CFLAGS"; then ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - pkg_cv_DEPS_CFLAGS=`$PKG_CONFIG --cflags "json >= 0.9 libcurl >= 7.21.0" 2>/dev/null` + pkg_cv_DEPS_VZ_CFLAGS=`$PKG_CONFIG --cflags "json >= 0.9 libcurl >= 7.21.0" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi -if test -n "$DEPS_LIBS"; then - pkg_cv_DEPS_LIBS="$DEPS_LIBS" +if test -n "$DEPS_VZ_LIBS"; then + pkg_cv_DEPS_VZ_LIBS="$DEPS_VZ_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"json >= 0.9 libcurl >= 7.21.0\""; } >&5 @@ -3879,7 +4126,8 @@ if test -n "$DEPS_LIBS"; then ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - pkg_cv_DEPS_LIBS=`$PKG_CONFIG --libs "json >= 0.9 libcurl >= 7.21.0" 2>/dev/null` + pkg_cv_DEPS_VZ_LIBS=`$PKG_CONFIG --libs "json >= 0.9 libcurl >= 7.21.0" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi @@ -3899,22 +4147,22 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - DEPS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "json >= 0.9 libcurl >= 7.21.0" 2>&1` + DEPS_VZ_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "json >= 0.9 libcurl >= 7.21.0" 2>&1` else - DEPS_PKG_ERRORS=`$PKG_CONFIG --print-errors "json >= 0.9 libcurl >= 7.21.0" 2>&1` + DEPS_VZ_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "json >= 0.9 libcurl >= 7.21.0" 2>&1` fi # Put the nasty error message in config.log where it belongs - echo "$DEPS_PKG_ERRORS" >&5 + echo "$DEPS_VZ_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (json >= 0.9 libcurl >= 7.21.0) were not met: -$DEPS_PKG_ERRORS +$DEPS_VZ_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. -Alternatively, you may set the environment variables DEPS_CFLAGS -and DEPS_LIBS to avoid the need to call pkg-config. +Alternatively, you may set the environment variables DEPS_VZ_CFLAGS +and DEPS_VZ_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 @@ -3925,15 +4173,15 @@ as_fn_error $? "The pkg-config script could not be found or is too old. Make su is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. -Alternatively, you may set the environment variables DEPS_CFLAGS -and DEPS_LIBS to avoid the need to call pkg-config. +Alternatively, you may set the environment variables DEPS_VZ_CFLAGS +and DEPS_VZ_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . -See \`config.log' for more details" "$LINENO" 5 ; } +See \`config.log' for more details" "$LINENO" 5; } else - DEPS_CFLAGS=$pkg_cv_DEPS_CFLAGS - DEPS_LIBS=$pkg_cv_DEPS_LIBS + DEPS_VZ_CFLAGS=$pkg_cv_DEPS_VZ_CFLAGS + DEPS_VZ_LIBS=$pkg_cv_DEPS_VZ_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } @@ -3953,7 +4201,7 @@ if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then - if test "${ac_cv_prog_CPP+set}" = set; then : + if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded @@ -4069,7 +4317,7 @@ else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details" "$LINENO" 5 ; } +See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c @@ -4081,7 +4329,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } -if test "${ac_cv_path_GREP+set}" = set; then : +if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then @@ -4144,7 +4392,7 @@ $as_echo "$ac_cv_path_GREP" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } -if test "${ac_cv_path_EGREP+set}" = set; then : +if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 @@ -4211,7 +4459,7 @@ $as_echo "$ac_cv_path_EGREP" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } -if test "${ac_cv_header_stdc+set}" = set; then : +if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -4354,7 +4602,7 @@ done # Checks for typedefs, structures, and compiler characteristics. ac_fn_c_check_type "$LINENO" "mode_t" "ac_cv_type_mode_t" "$ac_includes_default" -if test "x$ac_cv_type_mode_t" = x""yes; then : +if test "x$ac_cv_type_mode_t" = xyes; then : else @@ -4365,7 +4613,7 @@ _ACEOF fi ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" -if test "x$ac_cv_type_size_t" = x""yes; then : +if test "x$ac_cv_type_size_t" = xyes; then : else @@ -4380,7 +4628,7 @@ fi for ac_header in stdlib.h do : ac_fn_c_check_header_mongrel "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default" -if test "x$ac_cv_header_stdlib_h" = x""yes; then : +if test "x$ac_cv_header_stdlib_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_STDLIB_H 1 _ACEOF @@ -4391,7 +4639,7 @@ done { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU libc compatible malloc" >&5 $as_echo_n "checking for GNU libc compatible malloc... " >&6; } -if test "${ac_cv_func_malloc_0_nonnull+set}" = set; then : +if ${ac_cv_func_malloc_0_nonnull+:} false; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : @@ -4447,7 +4695,7 @@ fi for ac_header in stdlib.h do : ac_fn_c_check_header_mongrel "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default" -if test "x$ac_cv_header_stdlib_h" = x""yes; then : +if test "x$ac_cv_header_stdlib_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_STDLIB_H 1 _ACEOF @@ -4458,7 +4706,7 @@ done { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU libc compatible realloc" >&5 $as_echo_n "checking for GNU libc compatible realloc... " >&6; } -if test "${ac_cv_func_realloc_0_nonnull+set}" = set; then : +if ${ac_cv_func_realloc_0_nonnull+:} false; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : @@ -4512,7 +4760,7 @@ fi ac_fn_c_check_decl "$LINENO" "strerror_r" "ac_cv_have_decl_strerror_r" "$ac_includes_default" -if test "x$ac_cv_have_decl_strerror_r" = x""yes; then : +if test "x$ac_cv_have_decl_strerror_r" = xyes; then : ac_have_decl=1 else ac_have_decl=0 @@ -4525,7 +4773,7 @@ _ACEOF for ac_func in strerror_r do : ac_fn_c_check_func "$LINENO" "strerror_r" "ac_cv_func_strerror_r" -if test "x$ac_cv_func_strerror_r" = x""yes; then : +if test "x$ac_cv_func_strerror_r" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_STRERROR_R 1 _ACEOF @@ -4535,7 +4783,7 @@ done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether strerror_r returns char *" >&5 $as_echo_n "checking whether strerror_r returns char *... " >&6; } -if test "${ac_cv_func_strerror_r_char_p+set}" = set; then : +if ${ac_cv_func_strerror_r_char_p+:} false; then : $as_echo_n "(cached) " >&6 else @@ -4616,59 +4864,61 @@ fi done -# local interface support -# Check whether --enable-local-interface was given. -if test "${enable_local_interface+set}" = set; then : - enableval=$enable_local_interface; local=true +# SML support +# Check whether --enable-sml was given. +if test "${enable_sml+set}" = set; then : + enableval=$enable_sml; sml=true else - local=false + sml=false fi - if test x$local = xtrue; then - LOCAL_TRUE= - LOCAL_FALSE='#' + if test x"$sml" = x"true"; then + SML_SUPPORT_TRUE= + SML_SUPPORT_FALSE='#' else - LOCAL_TRUE='#' - LOCAL_FALSE= + SML_SUPPORT_TRUE='#' + SML_SUPPORT_FALSE= fi -if test x"$local" = x"true"; then +if test x"$sml" = x"true"; then -$as_echo "#define LOCAL /**/" >>confdefs.h +$as_echo "#define SML_SUPPORT /**/" >>confdefs.h pkg_failed=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for DEPS_LOCAL" >&5 -$as_echo_n "checking for DEPS_LOCAL... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for DEPS_SML" >&5 +$as_echo_n "checking for DEPS_SML... " >&6; } -if test -n "$DEPS_LOCAL_CFLAGS"; then - pkg_cv_DEPS_LOCAL_CFLAGS="$DEPS_LOCAL_CFLAGS" +if test -n "$DEPS_SML_CFLAGS"; then + pkg_cv_DEPS_SML_CFLAGS="$DEPS_SML_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libmicrohttpd >= 0.9.3\""; } >&5 - ($PKG_CONFIG --exists --print-errors "libmicrohttpd >= 0.9.3") 2>&5 + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sml >= 0.1\""; } >&5 + ($PKG_CONFIG --exists --print-errors "sml >= 0.1") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - pkg_cv_DEPS_LOCAL_CFLAGS=`$PKG_CONFIG --cflags "libmicrohttpd >= 0.9.3" 2>/dev/null` + pkg_cv_DEPS_SML_CFLAGS=`$PKG_CONFIG --cflags "sml >= 0.1" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi -if test -n "$DEPS_LOCAL_LIBS"; then - pkg_cv_DEPS_LOCAL_LIBS="$DEPS_LOCAL_LIBS" +if test -n "$DEPS_SML_LIBS"; then + pkg_cv_DEPS_SML_LIBS="$DEPS_SML_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libmicrohttpd >= 0.9.3\""; } >&5 - ($PKG_CONFIG --exists --print-errors "libmicrohttpd >= 0.9.3") 2>&5 + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sml >= 0.1\""; } >&5 + ($PKG_CONFIG --exists --print-errors "sml >= 0.1") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - pkg_cv_DEPS_LOCAL_LIBS=`$PKG_CONFIG --libs "libmicrohttpd >= 0.9.3" 2>/dev/null` + pkg_cv_DEPS_SML_LIBS=`$PKG_CONFIG --libs "sml >= 0.1" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi @@ -4688,9 +4938,124 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - DEPS_LOCAL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libmicrohttpd >= 0.9.3" 2>&1` + DEPS_SML_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "sml >= 0.1" 2>&1` else - DEPS_LOCAL_PKG_ERRORS=`$PKG_CONFIG --print-errors "libmicrohttpd >= 0.9.3" 2>&1` + DEPS_SML_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "sml >= 0.1" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$DEPS_SML_PKG_ERRORS" >&5 + + as_fn_error $? "Package requirements (sml >= 0.1) were not met: + +$DEPS_SML_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +Alternatively, you may set the environment variables DEPS_SML_CFLAGS +and DEPS_SML_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details." "$LINENO" 5 +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +Alternatively, you may set the environment variables DEPS_SML_CFLAGS +and DEPS_SML_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details. + +To get pkg-config, see . +See \`config.log' for more details" "$LINENO" 5; } +else + DEPS_SML_CFLAGS=$pkg_cv_DEPS_SML_CFLAGS + DEPS_SML_LIBS=$pkg_cv_DEPS_SML_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +fi +fi + +# local interface support +# Check whether --enable-local was given. +if test "${enable_local+set}" = set; then : + enableval=$enable_local; local=true +else + local=false + +fi + + + if test x"$local" = x"true"; then + LOCAL_SUPPORT_TRUE= + LOCAL_SUPPORT_FALSE='#' +else + LOCAL_SUPPORT_TRUE='#' + LOCAL_SUPPORT_FALSE= +fi + +if test x"$local" = x"true"; then + +$as_echo "#define LOCAL_SUPPORT /**/" >>confdefs.h + + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for DEPS_LOCAL" >&5 +$as_echo_n "checking for DEPS_LOCAL... " >&6; } + +if test -n "$DEPS_LOCAL_CFLAGS"; then + pkg_cv_DEPS_LOCAL_CFLAGS="$DEPS_LOCAL_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libmicrohttpd >= 0.9.3\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libmicrohttpd >= 0.9.3") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_DEPS_LOCAL_CFLAGS=`$PKG_CONFIG --cflags "libmicrohttpd >= 0.9.3" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$DEPS_LOCAL_LIBS"; then + pkg_cv_DEPS_LOCAL_LIBS="$DEPS_LOCAL_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libmicrohttpd >= 0.9.3\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libmicrohttpd >= 0.9.3") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_DEPS_LOCAL_LIBS=`$PKG_CONFIG --libs "libmicrohttpd >= 0.9.3" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + DEPS_LOCAL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libmicrohttpd >= 0.9.3" 2>&1` + else + DEPS_LOCAL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libmicrohttpd >= 0.9.3" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$DEPS_LOCAL_PKG_ERRORS" >&5 @@ -4719,7 +5084,7 @@ and DEPS_LOCAL_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . -See \`config.log' for more details" "$LINENO" 5 ; } +See \`config.log' for more details" "$LINENO" 5; } else DEPS_LOCAL_CFLAGS=$pkg_cv_DEPS_LOCAL_CFLAGS DEPS_LOCAL_LIBS=$pkg_cv_DEPS_LOCAL_LIBS @@ -4812,10 +5177,21 @@ $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then - test "x$cache_file" != "x/dev/null" && + if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} - cat confcache >$cache_file + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} @@ -4862,12 +5238,16 @@ if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi -if test -z "${LOCAL_TRUE}" && test -z "${LOCAL_FALSE}"; then - as_fn_error $? "conditional \"LOCAL\" was never defined. +if test -z "${SML_SUPPORT_TRUE}" && test -z "${SML_SUPPORT_FALSE}"; then + as_fn_error $? "conditional \"SML_SUPPORT\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${LOCAL_SUPPORT_TRUE}" && test -z "${LOCAL_SUPPORT_FALSE}"; then + as_fn_error $? "conditional \"LOCAL_SUPPORT\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi -: ${CONFIG_STATUS=./config.status} +: "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" @@ -4968,6 +5348,7 @@ fi IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. +as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -5275,7 +5656,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # values after options handling. ac_log=" This file was extended by vzlogger $as_me 0.2, which was -generated by GNU Autoconf 2.67. Invocation command line was +generated by GNU Autoconf 2.68. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS @@ -5341,7 +5722,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ vzlogger config.status 0.2 -configured by $0, generated by GNU Autoconf 2.67, +configured by $0, generated by GNU Autoconf 2.68, with options \\"\$ac_cs_config\\" Copyright (C) 2010 Free Software Foundation, Inc. @@ -5471,11 +5852,13 @@ do case $ac_config_target in "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; - "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; "docs/Makefile") CONFIG_FILES="$CONFIG_FILES docs/Makefile" ;; + "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; + "bin/reader/Makefile") CONFIG_FILES="$CONFIG_FILES bin/reader/Makefile" ;; + "bin/vzlogger/Makefile") CONFIG_FILES="$CONFIG_FILES bin/vzlogger/Makefile" ;; "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; - *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5 ;; + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done @@ -5498,9 +5881,10 @@ fi # after its creation but before its name has been assigned to `$tmp'. $debug || { - tmp= + tmp= ac_tmp= trap 'exit_status=$? - { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } @@ -5508,12 +5892,13 @@ $debug || { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && - test -n "$tmp" && test -d "$tmp" + test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. @@ -5535,7 +5920,7 @@ else ac_cs_awk_cr=$ac_cr fi -echo 'BEGIN {' >"$tmp/subs1.awk" && +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF @@ -5563,7 +5948,7 @@ done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -cat >>"\$tmp/subs1.awk" <<\\_ACAWK && +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h @@ -5611,7 +5996,7 @@ t delim rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK -cat >>"\$tmp/subs1.awk" <<_ACAWK && +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" @@ -5643,7 +6028,7 @@ if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat -fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \ +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF @@ -5677,7 +6062,7 @@ fi # test -n "$CONFIG_FILES" # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then -cat >"$tmp/defines.awk" <<\_ACAWK || +cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF @@ -5689,8 +6074,8 @@ _ACEOF # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do - ac_t=`sed -n "/$ac_delim/p" confdefs.h` - if test -z "$ac_t"; then + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 @@ -5791,7 +6176,7 @@ do esac case $ac_mode$ac_tag in :[FHL]*:*);; - :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5 ;; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac @@ -5810,7 +6195,7 @@ do for ac_f do case $ac_f in - -) ac_f="$tmp/stdin";; + -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. @@ -5819,7 +6204,7 @@ do [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || - as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5 ;; + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" @@ -5845,8 +6230,8 @@ $as_echo "$as_me: creating $ac_file" >&6;} esac case $ac_tag in - *:-:* | *:-) cat >"$tmp/stdin" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac @@ -5982,21 +6367,22 @@ s&@INSTALL@&$ac_INSTALL&;t t s&@MKDIR_P@&$ac_MKDIR_P&;t t $ac_datarootdir_hack " -eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && - { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && - { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} - rm -f "$tmp/stdin" + rm -f "$ac_tmp/stdin" case $ac_file in - -) cat "$tmp/out" && rm -f "$tmp/out";; - *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";; + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; @@ -6007,20 +6393,20 @@ which seems to be undefined. Please make sure it is defined" >&2;} if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ - && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" - } >"$tmp/config.h" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" - mv "$tmp/config.h" "$ac_file" \ + mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ - && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi # Compute "$ac_file"'s index in $config_headers. diff --git a/configure.ac b/configure.ac index 110af2d..0da8656 100644 --- a/configure.ac +++ b/configure.ac @@ -4,15 +4,26 @@ AC_PREREQ([2.67]) AC_INIT([vzlogger], [0.2], [http://bugs.volkszaehler.org]) AM_INIT_AUTOMAKE(vzlogger, 0.2) -AC_CONFIG_SRCDIR([src/main.c]) +AC_CONFIG_SRCDIR([src/meter.c]) AC_CONFIG_HEADERS([config.h]) -AC_CONFIG_FILES([Makefile src/Makefile docs/Makefile]) +AC_CONFIG_FILES([Makefile + docs/Makefile + src/Makefile + bin/reader/Makefile + bin/vzlogger/Makefile + ]) # Checks for programs. AC_PROG_CC +# We want to build a lib +AC_PROG_RANLIB + +# We use per target compiler flags +AM_PROG_CC_C_O + # Checks for libraries. -PKG_CHECK_MODULES([DEPS], [json >= 0.9 libcurl >= 7.21.0]) +PKG_CHECK_MODULES([DEPS_VZ], [json >= 0.9 libcurl >= 7.21.0]) # Checks for header files. AC_CHECK_HEADERS([fcntl.h stddef.h stdint.h stdlib.h string.h sys/time.h termios.h unistd.h]) @@ -27,17 +38,31 @@ AC_FUNC_REALLOC AC_FUNC_STRERROR_R AC_CHECK_FUNCS([gettimeofday memset sqrt strchr strtol]) +# SML support +AC_ARG_ENABLE( + [sml], + [AS_HELP_STRING([--enable-sml], [enable support for smart messaging language (def=no)])], + [sml=true], + [sml=false] +) + +AM_CONDITIONAL([SML_SUPPORT], [test x"$sml" = x"true"]) +if test x"$sml" = x"true"; then + AC_DEFINE([SML_SUPPORT], [], [Smart Messaging Language]) + PKG_CHECK_MODULES([DEPS_SML], [sml >= 0.1]) +fi + # local interface support AC_ARG_ENABLE( - [local-interface], + [local], [AS_HELP_STRING([--enable-local], [enable support for local HTTPd (def=no)])], [local=true], [local=false] ) -AM_CONDITIONAL([LOCAL], [test x$local = xtrue]) +AM_CONDITIONAL([LOCAL_SUPPORT], [test x"$local" = x"true"]) if test x"$local" = x"true"; then - AC_DEFINE([LOCAL],[],[Local interface]) + AC_DEFINE([LOCAL_SUPPORT], [], [Local interface]) PKG_CHECK_MODULES([DEPS_LOCAL], [libmicrohttpd >= 0.9.3]) fi diff --git a/docs/Makefile b/docs/Makefile index 9a024f1..50d92b5 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -83,10 +83,12 @@ CPPFLAGS = CYGPATH_W = echo DEFS = -DHAVE_CONFIG_H DEPDIR = .deps -DEPS_CFLAGS = -I/usr/include/json -DEPS_LIBS = -ljson -lcurl DEPS_LOCAL_CFLAGS = DEPS_LOCAL_LIBS = +DEPS_SML_CFLAGS = -I/usr/include/sml -I/usr/include/uuid +DEPS_SML_LIBS = -lsml -luuid +DEPS_VZ_CFLAGS = -I/usr/include/json +DEPS_VZ_LIBS = -ljson -lcurl ECHO_C = ECHO_N = -n ECHO_T = @@ -116,6 +118,7 @@ PATH_SEPARATOR = : PKG_CONFIG = /usr/bin/pkg-config PKG_CONFIG_LIBDIR = PKG_CONFIG_PATH = +RANLIB = ranlib SET_MAKE = SHELL = /bin/bash STRIP = diff --git a/etc/vzlogger.conf b/etc/vzlogger.conf index c228748..7c6f896 100644 --- a/etc/vzlogger.conf +++ b/etc/vzlogger.conf @@ -2,9 +2,13 @@ ; use tabs, spaces as delimiter ; use ; to introduce a comment -;prot intval uuid middleware options -1wire 10 371dd700-beb3-11e0-8dda-b961518a06f4 http://demo.volkszaehler.org/middleware.php /mnt/1wire/10.12E6D3000800/temperature -;obis 10 ef0e9adf-cd9e-4d9a-92c5-b4fb4c89ff98 http://volkszaehler.org/demo/middleware.php /dev/ttyS0 -;rawS0 10 27a1b4c0-8f8a-11e0-ad82-db6efbc4ba2e http://volkszaehler.org/demo/middleware.php /dev/ttyUSB2 -random 5 e4ff8f70-9f79-11e0-b44c-d1ce78df8288 http://localhost/workspace/volkszaehler.org/htdocs/middleware 40 +;prot middleware uuid intval options +;1wire http://demo.volkszaehler.org/middleware.php 371dd700-beb3-11e0-8dda-b961518a06f4 10 /mnt/1wire/10.12E6D3000800/temperature +;obis http://volkszaehler.org/demo/middleware.php ef0e9adf-cd9e-4d9a-92c5-b4fb4c89ff98 10 /dev/ttyS0 +;rawS0 http://volkszaehler.org/demo/middleware.php 27a1b4c0-8f8a-11e0-ad82-db6efbc4ba2e 10 /dev/ttyUSB2 +;random http://localhost/workspace/volkszaehler.org/volkszaehler.org/htdocs/middleware fde8f1d0-c5d0-11e0-856e-f9e4360ced10 5 40 +sml http://localhost/workspace/volkszaehler.org/volkszaehler.org/htdocs/middleware c853d890-daf3-11e0-86c4-875e5bd610cf 76h9vv5835w5.dyndns.info:7331:power +;sml http://localhost/workspace/volkszaehler.org/volkszaehler.org/htdocs/middleware 5584d3e0-daf5-11e0-a1c8-3930962bdbf4 76h9vv5835w5.dyndns.info:7331:power-l1 +;sml http://localhost/workspace/volkszaehler.org/volkszaehler.org/htdocs/middleware 6d17cd80-daf5-11e0-916c-bfb24b4b6f01 76h9vv5835w5.dyndns.info:7331:power-l2 +;sml http://localhost/workspace/volkszaehler.org/volkszaehler.org/htdocs/middleware 78c09290-daf5-11e0-81e9-2f2eb65081d7 76h9vv5835w5.dyndns.info:7331:power-l3 diff --git a/src/protocols/obis.h b/include/d0.h similarity index 72% rename from src/protocols/obis.h rename to include/d0.h index ac511a4..669d0bf 100644 --- a/src/protocols/obis.h +++ b/include/d0.h @@ -1,8 +1,8 @@ /** - * OBIS protocol parser + * Plaintext protocol according to DIN EN 62056-21 * - * This is our example protocol. Use this skeleton to add your own - * protocols and meters. + * This protocol uses OBIS to identify the readout data + * And is also sometimes called "D0" * * @package vzlogger * @copyright Copyright (c) 2011, The volkszaehler.org project @@ -26,20 +26,20 @@ * along with volkszaehler.org. If not, see . */ -#ifndef _OBIS_H_ -#define _OBIS_H_ +#ifndef _D0_H_ +#define _D0_H_ #include -#include "../protocol.h" +#include "reading.h" typedef struct { int fd; /* file descriptor of port */ struct termios oldtio; /* required to reset port */ -} obis_state_t; +} meter_handle_d0_t; -void * obis_init(char *port); -void obis_close(void *handle); -reading_t obis_get(void *handle); +int meter_d0_open(meter_handle_d0_t *handle, char *options); +void meter_d0_close(meter_handle_d0_t *handle); +meter_reading_t meter_d0_read(meter_handle_d0_t *handle); -#endif /* _OBIS_H_ */ +#endif /* _D0_H_ */ diff --git a/include/meter.h b/include/meter.h new file mode 100644 index 0000000..e815a1b --- /dev/null +++ b/include/meter.h @@ -0,0 +1,90 @@ +/** + * Protocol interface + * + * @package vzlogger + * @copyright Copyright (c) 2011, The volkszaehler.org project + * @license http://www.gnu.org/licenses/gpl.txt GNU Public License + * @author Steffen Vogel + */ +/* + * This file is part of volkzaehler.org + * + * volkzaehler.org 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 + * any later version. + * + * volkzaehler.org 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 volkszaehler.org. If not, see . + */ + +#ifndef _METER_H_ +#define _METER_H_ + +/** + * We have 2 diffrent protocol types: + * - SENSOR: a readout is triggered in equidistant intervals by calling + * the read function with an POSIX timer. + * The interval is set in the configuration. + * - METER: the meter itselfs triggers a readout. + * The pointer to the read function shoul be NULL. + * The 'interval' column in the configuration as no meaning. + */ + +#include "../config.h" +#include "reading.h" + +/* meter types */ +#include "random.h" +#include "s0.h" +#include "d0.h" +#include "onewire.h" +#ifdef SML_SUPPORT +#include "sml.h" +#endif /* SML_SUPPORT */ + +typedef enum { + S0, + D0, + ONEWIRE, + RANDOM, +#ifdef SML_SUPPORT + SML +#endif /* SML_SUPPORT */ +} meter_tag_t; + +typedef struct { + meter_tag_t tag; + char *name; /* short identifier for protocol */ + char *desc; /* more detailed description */ + int periodical:1; /* does this meter has be triggered periodically? */ +} meter_type_t; + +typedef struct { + meter_type_t *type; + char *options; + union { + meter_handle_s0_t s0; + meter_handle_d0_t d0; + meter_handle_onewire_t onewire; + meter_handle_random_t random; +#ifdef SML_SUPPORT + meter_handle_sml_t sml; +#endif /* SML_SUPPORT */ + } handle; +} meter_t; + +/* prototypes */ +void meter_init(meter_t *meter, meter_type_t *type, char *options); +void meter_free(meter_t *meter); +meter_reading_t meter_read(meter_t *meter); + +int meter_open(meter_t *meter); +void meter_close(meter_t *meter); + +#endif /* _METER_H_ */ diff --git a/include/obis.h b/include/obis.h new file mode 100644 index 0000000..a044bd8 --- /dev/null +++ b/include/obis.h @@ -0,0 +1,118 @@ +/** + * OBIS IDs as specified in DIN EN 62056-61 + * + * @package vzlogger + * @copyright Copyright (c) 2011, The volkszaehler.org project + * @license http://www.gnu.org/licenses/gpl.txt GNU Public License + * @author Steffen Vogel + */ +/* + * This file is part of volkzaehler.org + * + * volkzaehler.org 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 + * any later version. + * + * volkzaehler.org 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 volkszaehler.org. If not, see . + */ + +#ifndef _OBIS_H_ +#define _OBIS_H_ + +#include + +typedef enum { + ABSTRACT = 0, + ELECTRIC = 1, + HEAT_COST = 4, + COOLING = 5, + HEATING = 6, + GAS = 7, + WATER_COLD = 8, + WATER_HOT = 9 +} obis_media_t; + +typedef union { + enum { + GENERAL_PURPOSE = 0, + ACTIVE_POWER_IN = 1, + ACTIVE_POWER_OUT = 2, + REACTIVE_POWER_IN = 3, + REACTIVE_POWER_OUT = 4, + REACTIVE_POWER_Q1 = 5, + REACTIVE_POWER_Q2 = 6, + REACTIVE_POWER_Q3 = 7, + REACTIVE_POWER_Q4 = 8, + APPARENT_POWER_IN = 9, + APPARENT_POWER_OUT = 10, + CURRENT_ANY = 11, + VOLTAGE_ANY = 12, + POWER_FACTOR_OUT = 13, + SUPPLY_FREQUENCY = 14, + ACTIVE_POWER_Q1 = 17, + ACTIVE_POWER_Q2 = 18, + ACTIVE_POWER_Q3 = 19, + ACTIVE_POWER_Q4 = 20, + + L1_ACTIVE_POWER_INT = 21, + L1_ACTIVE_POWER_OUT = 22, + + ANGLES = 81, + UNITLESS = 82, + LOSS = 83, + + L1_POWER_FACTOR_OUT = 85, + L2_POWER_FACTOR_OUT = 86, + L3_POWER_FACTOR_OUT = 87, + + AMPERE_SQUARE_HOURS = 88, + VOLT_SQUARE_HOURS = 89, + + SERVICE = 96, + ERROR = 97, + LIST = 98, + DATA_PROFILE = 99 + + } electric; + + int gas; /* as defined in DIN EN 13757-1 */ +} obis_indicator_t; + +/* regex: A-BB:CC.DD.EE(*FF)? */ +typedef union { + unsigned char raw[6]; + struct { + unsigned char media; + unsigned char channel; + unsigned char indicator; + unsigned char mode; + unsigned char quantities; + unsigned char storage; /* not used in Germany */ + } groups; +} obis_id_t; + +typedef struct { + obis_id_t id; + char *name; + char *desc; +} obis_alias_t; + +/* prototypes */ +obis_id_t obis_init(unsigned char *raw); +obis_id_t obis_parse(char *str); +int obis_unparse(obis_id_t id, char *buffer); +char obis_is_manufacturer_specific(obis_id_t id); + +/* inline functions */ +inline int obis_compare(obis_id_t a, obis_id_t b) { + return memcmp(&a, &b, sizeof(obis_id_t)); +} + +#endif /* _OBIS_H_ */ diff --git a/include/onewire.h b/include/onewire.h new file mode 100644 index 0000000..7b03585 --- /dev/null +++ b/include/onewire.h @@ -0,0 +1,41 @@ +/** + * Wrapper to read Dallas 1-wire Sensors via the 1-wire Filesystem (owfs) + * + * @package vzlogger + * @copyright Copyright (c) 2011, The volkszaehler.org project + * @license http://www.gnu.org/licenses/gpl.txt GNU Public License + * @author Steffen Vogel + */ +/* + * This file is part of volkzaehler.org + * + * volkzaehler.org 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 + * any later version. + * + * volkzaehler.org 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 volkszaehler.org. If not, see . + */ + +#ifndef _ONEWIRE_H_ +#define _ONEWIRE_H_ + +#include + +#include "reading.h" + +typedef struct { + FILE *file; +} meter_handle_onewire_t; + +int meter_onewire_open(meter_handle_onewire_t *handle, char *options); +void meter_onewire_close(meter_handle_onewire_t *handle); +meter_reading_t meter_onewire_read(meter_handle_onewire_t *handle); + +#endif /* _ONEWIRE_H_ */ diff --git a/include/random.h b/include/random.h new file mode 100644 index 0000000..dab1aa9 --- /dev/null +++ b/include/random.h @@ -0,0 +1,41 @@ +/** + * Generate pseudo random data series by a random walk + * + * @package vzlogger + * @copyright Copyright (c) 2011, The volkszaehler.org project + * @license http://www.gnu.org/licenses/gpl.txt GNU Public License + * @author Steffen Vogel + */ +/* + * This file is part of volkzaehler.org + * + * volkzaehler.org 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 + * any later version. + * + * volkzaehler.org 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 volkszaehler.org. If not, see . + */ + +#ifndef _RANDOM_H_ +#define _RANDOM_H_ + +#include "reading.h" + +typedef struct { + double min, max, last; +} meter_handle_random_t; + +double ltqnorm(double p); /* forward declaration */ + +int meter_random_open(meter_handle_random_t *handle, char *options); +void meter_random_close(meter_handle_random_t *handle); +meter_reading_t meter_random_read(meter_handle_random_t *handle); + +#endif /* _RANDOM_H_ */ diff --git a/src/protocols/1wire.h b/include/reading.h similarity index 77% rename from src/protocols/1wire.h rename to include/reading.h index 32749af..b4b75ec 100644 --- a/src/protocols/1wire.h +++ b/include/reading.h @@ -1,5 +1,5 @@ /** - * Wrapper to read Dallas 1-wire Sensors via the 1-wire Filesystem (owfs) + * Meter readings * * @package vzlogger * @copyright Copyright (c) 2011, The volkszaehler.org project @@ -22,14 +22,17 @@ * You should have received a copy of the GNU General Public License * along with volkszaehler.org. If not, see . */ - -#ifndef _1WIRE_H_ -#define _1WIRE_H_ -#include "../protocol.h" +#ifndef _READING_H_ +#define _READING_H_ -void * onewire_init(char *port); -void onewire_close(void *handle); -reading_t onewire_get(void *handle); +#include -#endif /* _1WIRE_H_ */ +typedef struct meter_reading { + float value; + struct timeval tv; + + struct meter_reading *next, *prev; /* pointers for linked list */ +} meter_reading_t; + +#endif /* _READING_H_ */ diff --git a/src/protocols/rawS0.h b/include/s0.h similarity index 80% rename from src/protocols/rawS0.h rename to include/s0.h index fc1f6f5..85eaa06 100644 --- a/src/protocols/rawS0.h +++ b/include/s0.h @@ -23,20 +23,20 @@ * along with volkszaehler.org. If not, see . */ -#ifndef _RAWS0_H_ -#define _RAWS0_H_ +#ifndef _S0_H_ +#define _S0_H_ #include -#include "../protocol.h" +#include "reading.h" typedef struct { int fd; /* file descriptor of port */ struct termios oldtio; /* required to reset port */ -} rawS0_state_t; +} meter_handle_s0_t; -void * rawS0_init(char *port); -void rawS0_close(void *handle); -reading_t rawS0_get(void *handle); +int meter_s0_open(meter_handle_s0_t *handle, char *options); +void meter_s0_close(meter_handle_s0_t *handle); +meter_reading_t meter_s0_read(meter_handle_s0_t *handle); -#endif /* _RAWS0_H_ */ +#endif /* _S0_H_ */ diff --git a/src/protocols/sml.h b/include/sml.h similarity index 68% rename from src/protocols/sml.h rename to include/sml.h index fcfe3dd..d29494a 100644 --- a/src/protocols/sml.h +++ b/include/sml.h @@ -30,17 +30,27 @@ #ifndef _SML_H_ #define _SML_H_ -#include "../protocol.h" +#define SML_BUFFER_LEN 8096 + +#include + +#include "reading.h" +#include "obis.h" typedef struct { int fd; + obis_id_t id; /* which OBIS we want to log */ + float counter; /* Zählerstand */ // termios etc.. -} sml_state_t; +} meter_handle_sml_t; -void * sml_init(char *port); -void sml_close(void *handle); -reading_t sml_get(void *handle); -int sml_open_port(char *device); -void sml_transport_receiver(unsigned char *buffer, size_t buffer_len); +int meter_sml_open(meter_handle_sml_t *handle, char *options); +void meter_sml_close(meter_handle_sml_t *handle); +meter_reading_t meter_sml_read(meter_handle_sml_t *handle); + +meter_reading_t meter_sml_parse(sml_file *file, obis_id_t which); + +int meter_sml_open_port(char *device); +int meter_sml_open_socket(char *node, char *service); #endif /* _SML_H_ */ diff --git a/include/unit.h b/include/unit.h new file mode 100644 index 0000000..16e5f24 --- /dev/null +++ b/include/unit.h @@ -0,0 +1,115 @@ +/** + * DLMS Units as specified in ISO EN 62056-62 and used by SML + * + * @package vzlogger + * @copyright Copyright (c) 2011, The volkszaehler.org project + * @license http://www.gnu.org/licenses/gpl.txt GNU Public License + * @author Steffen Vogel + */ +/* + * This file is part of volkzaehler.org + * + * volkzaehler.org 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 + * any later version. + * + * volkzaehler.org 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 volkszaehler.org. If not, see . + */ + +typedef struct { + char code; + char *unit; +} dlms_unit_t; + +/** + * Static lookup table + */ +dlms_unit_t dlms_units[] = { +// code, unit // Quantity Unit name SI definition (comment) +//===================================================================================================== +{1, "a"}, // time year 52*7*24*60*60 s +{2, "mo"}, // time month 31*24*60*60 s +{3, "wk"}, // time week 7*24*60*60 s +{4, "d"}, // time day 24*60*60 s +{5, "h"}, // time hour 60*60 s +{6, "min."}, // time min 60 s +{7, "s"}, // time (t) second s +{8, "°"}, // (phase) angle degree rad*180/π +{9, "°C"}, // temperature (T) degree celsius K-273.15 +{10, "currency"}, // (local) currency +{11, "m"}, // length (l) metre m +{12, "m/s"}, // speed (v) metre per second m/s +{13, "m³"}, // volume (V) cubic metre m³ +{14, "m³"}, // corrected volume cubic metre m³ +{15, "m³/h"}, // volume flux cubic metre per hour m³/(60*60s) +{16, "m³/h"}, // corrected volume flux cubic metre per hour m³/(60*60s) +{17, "m³/d"}, // volume flux m³/(24*60*60s) +{18, "m³/d"}, // corrected volume flux m³/(24*60*60s) +{19, "l"}, // volume litre 10-3 m³ +{20, "kg"}, // mass (m) kilogram +{21, "N"}, // force (F) newton +{22, "Nm"}, // energy newtonmeter J = Nm = Ws +{23, "Pa"}, // pressure (p) pascal N/m² +{24, "bar"}, // pressure (p) bar 10⁵ N/m² +{25, "J"}, // energy joule J = Nm = Ws +{26, "J/h"}, // thermal power joule per hour J/(60*60s) +{27, "W"}, // active power (P) watt W = J/s +{28, "VA"}, // apparent power (S) volt-ampere +{29, "var"}, // reactive power (Q) var +{30, "Wh"}, // active energy watt-hour W*(60*60s) +{31, "VAh"}, // apparent energy volt-ampere-hour VA*(60*60s) +{32, "varh"}, // reactive energy var-hour var*(60*60s) +{33, "A"}, // current (I) ampere A +{34, "C"}, // electrical charge (Q) coulomb C = As +{35, "V"}, // voltage (U) volt V +{36, "V/m"}, // electr. field strength (E) volt per metre +{37, "F"}, // capacitance (C) farad C/V = As/V +{38, "Ω"}, // resistance (R) ohm Ω = V/A +{39, "Ωm²/m"}, // resistivity (ρ) Ωm +{40, "Wb"}, // magnetic flux (Φ) weber Wb = Vs +{41, "T"}, // magnetic flux density (B) tesla Wb/m2 +{42, "A/m"}, // magnetic field strength (H) ampere per metre A/m +{43, "H"}, // inductance (L) henry H = Wb/A +{44, "Hz"}, // frequency (f, ω) hertz 1/s +{45, "1/(Wh)"}, // R_W (Active energy meter constant or pulse value) +{46, "1/(varh)"}, // R_B (reactive energy meter constant or pulse value) +{47, "1/(VAh)"}, // R_S (apparent energy meter constant or pulse value) +{48, "V²h"}, // volt-squared hour volt-squaredhours V²(60*60s) +{49, "A²h"}, // ampere-squared hour ampere-squaredhours A²(60*60s) +{50, "kg/s"}, // mass flux kilogram per second kg/s +{51, "S, mho"}, // conductance siemens 1/Ω +{52, "K"}, // temperature (T) kelvin +{53, "1/(V²h)"}, // R_U²h (Volt-squared hour meter constant or pulse value) +{54, "1/(A²h)"}, // R_I²h (Ampere-squared hour meter constant or pulse value) +{55, "1/m³"}, // R_V, meter constant or pulse value (volume) +{56, "%"}, // percentage % +{57, "Ah"}, // ampere-hours ampere-hour +{60, "Wh/m³"}, // energy per volume 3,6*103 J/m³ +{61, "J/m³"}, // calorific value, wobbe +{62, "Mol %"}, // molar fraction of mole percent (Basic gas composition unit) + // gas composition +{63, "g/m³"}, // mass density, quantity of material (Gas analysis, accompanying elements) +{64, "Pa s"}, // dynamic viscosity pascal second (Characteristic of gas stream) +{253, "(reserved)"}, // reserved +{254, "(other)"}, // other unit +{255, "(unitless)"}, // no unit, unitless, count +{} // stop condition for iterator +}; + +char * dlms_get_unit(unsigned char code) { + dlms_unit_t *it = dlms_units; + do { /* linear search */ + if (it->code == code) { + return it->unit; + } + } while ((++it)->code); + + return NULL; /* not found */ +} diff --git a/src/Makefile b/src/Makefile index ff15e28..4e3b592 100644 --- a/src/Makefile +++ b/src/Makefile @@ -32,10 +32,12 @@ POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : -bin_PROGRAMS = vzlogger$(EXEEXT) -#am__append_1 = local.c -#am__append_2 = $(DEPS_LOCAL_LIBS) -#am__append_3 = $(DEPS_LOCAL_CFLAGS) +#libobis_a_HEADERS = obis.h + +# SML support +#################################################################### +am__append_1 = sml.c +am__append_2 = $(DEPS_SML_CFLAGS) subdir = src DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 @@ -46,20 +48,22 @@ mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = -am__installdirs = "$(DESTDIR)$(bindir)" -PROGRAMS = $(bin_PROGRAMS) -am__vzlogger_SOURCES_DIST = main.c buffer.c api.c channel.c ltqnorm.c \ - options.c protocols/onewire.c protocols/din62056.c \ - protocols/rawS0.c protocols/random.c local.c -#am__objects_1 = local.$(OBJEXT) -am_vzlogger_OBJECTS = main.$(OBJEXT) buffer.$(OBJEXT) api.$(OBJEXT) \ - channel.$(OBJEXT) ltqnorm.$(OBJEXT) options.$(OBJEXT) \ - onewire.$(OBJEXT) din62056.$(OBJEXT) rawS0.$(OBJEXT) \ - random.$(OBJEXT) $(am__objects_1) -vzlogger_OBJECTS = $(am_vzlogger_OBJECTS) -am__DEPENDENCIES_1 = -#am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) -vzlogger_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +libmeter_a_AR = $(AR) $(ARFLAGS) +libmeter_a_DEPENDENCIES = libobis.a +am__libmeter_a_SOURCES_DIST = meter.c d0.c s0.c random.c onewire.c \ + ltqnorm.c sml.c +am__objects_1 = sml.$(OBJEXT) +am_libmeter_a_OBJECTS = meter.$(OBJEXT) d0.$(OBJEXT) s0.$(OBJEXT) \ + random.$(OBJEXT) onewire.$(OBJEXT) ltqnorm.$(OBJEXT) \ + $(am__objects_1) +libmeter_a_OBJECTS = $(am_libmeter_a_OBJECTS) +libobis_a_AR = $(AR) $(ARFLAGS) +libobis_a_LIBADD = +am_libobis_a_OBJECTS = obis.$(OBJEXT) +libobis_a_OBJECTS = $(am_libobis_a_OBJECTS) DEFAULT_INCLUDES = -I. -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles @@ -68,8 +72,8 @@ COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ -SOURCES = $(vzlogger_SOURCES) -DIST_SOURCES = $(am__vzlogger_SOURCES_DIST) +SOURCES = $(libmeter_a_SOURCES) $(libobis_a_SOURCES) +DIST_SOURCES = $(am__libmeter_a_SOURCES_DIST) $(libobis_a_SOURCES) ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) @@ -87,10 +91,12 @@ CPPFLAGS = CYGPATH_W = echo DEFS = -DHAVE_CONFIG_H DEPDIR = .deps -DEPS_CFLAGS = -I/usr/include/json -DEPS_LIBS = -ljson -lcurl DEPS_LOCAL_CFLAGS = DEPS_LOCAL_LIBS = +DEPS_SML_CFLAGS = -I/usr/include/sml -I/usr/include/uuid +DEPS_SML_LIBS = -lsml -luuid +DEPS_VZ_CFLAGS = -I/usr/include/json +DEPS_VZ_LIBS = -ljson -lcurl ECHO_C = ECHO_N = -n ECHO_T = @@ -120,6 +126,7 @@ PATH_SEPARATOR = : PKG_CONFIG = /usr/bin/pkg-config PKG_CONFIG_LIBDIR = PKG_CONFIG_PATH = +RANLIB = ranlib SET_MAKE = SHELL = /bin/bash STRIP = @@ -166,15 +173,15 @@ target_alias = top_build_prefix = ../ top_builddir = .. top_srcdir = .. - -# what flags you want to pass to the C compiler & linker -AM_CFLAGS = -Wall -D_REENTRANT -std=gnu99 $(DEPS_CFLAGS) \ - $(am__append_3) +AM_CFLAGS = -Wall -D_REENTRANT -std=gnu99 $(am__append_2) +AM_CPPFLAGS = -I$(top_srcdir)/include AM_LDFLAGS = -vzlogger_SOURCES = main.c buffer.c api.c channel.c ltqnorm.c options.c \ - protocols/onewire.c protocols/din62056.c protocols/rawS0.c \ - protocols/random.c $(am__append_1) -vzlogger_LDADD = -lpthread -lm $(DEPS_LIBS) $(am__append_2) +noinst_LIBRARIES = libmeter.a libobis.a +libmeter_a_SOURCES = meter.c d0.c s0.c random.c onewire.c ltqnorm.c \ + $(am__append_1) +#libmeter_a_HEADERS = meter.h +libobis_a_SOURCES = obis.c +libmeter_a_LIBADD = libobis.a all: all-am .SUFFIXES: @@ -209,46 +216,17 @@ $(top_srcdir)/configure: $(am__configure_deps) $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): -install-binPROGRAMS: $(bin_PROGRAMS) - @$(NORMAL_INSTALL) - test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)" - @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ - for p in $$list; do echo "$$p $$p"; done | \ - sed 's/$(EXEEXT)$$//' | \ - while read p p1; do if test -f $$p; \ - then echo "$$p"; echo "$$p"; else :; fi; \ - done | \ - sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ - -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ - sed 'N;N;N;s,\n, ,g' | \ - $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ - { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ - if ($$2 == $$4) files[d] = files[d] " " $$1; \ - else { print "f", $$3 "/" $$4, $$1; } } \ - END { for (d in files) print "f", d, files[d] }' | \ - while read type dir files; do \ - if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ - test -z "$$files" || { \ - echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ - $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ - } \ - ; done -uninstall-binPROGRAMS: - @$(NORMAL_UNINSTALL) - @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ - files=`for p in $$list; do echo "$$p"; done | \ - sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ - -e 's/$$/$(EXEEXT)/' `; \ - test -n "$$list" || exit 0; \ - echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ - cd "$(DESTDIR)$(bindir)" && rm -f $$files - -clean-binPROGRAMS: - -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) -vzlogger$(EXEEXT): $(vzlogger_OBJECTS) $(vzlogger_DEPENDENCIES) - @rm -f vzlogger$(EXEEXT) - $(LINK) $(vzlogger_OBJECTS) $(vzlogger_LDADD) $(LIBS) +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libmeter.a: $(libmeter_a_OBJECTS) $(libmeter_a_DEPENDENCIES) + -rm -f libmeter.a + $(libmeter_a_AR) libmeter.a $(libmeter_a_OBJECTS) $(libmeter_a_LIBADD) + $(RANLIB) libmeter.a +libobis.a: $(libobis_a_OBJECTS) $(libobis_a_DEPENDENCIES) + -rm -f libobis.a + $(libobis_a_AR) libobis.a $(libobis_a_OBJECTS) $(libobis_a_LIBADD) + $(RANLIB) libobis.a mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -256,17 +234,14 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c -include ./$(DEPDIR)/api.Po -include ./$(DEPDIR)/buffer.Po -include ./$(DEPDIR)/channel.Po -include ./$(DEPDIR)/din62056.Po -include ./$(DEPDIR)/local.Po +include ./$(DEPDIR)/d0.Po include ./$(DEPDIR)/ltqnorm.Po -include ./$(DEPDIR)/main.Po +include ./$(DEPDIR)/meter.Po +include ./$(DEPDIR)/obis.Po include ./$(DEPDIR)/onewire.Po -include ./$(DEPDIR)/options.Po include ./$(DEPDIR)/random.Po -include ./$(DEPDIR)/rawS0.Po +include ./$(DEPDIR)/s0.Po +include ./$(DEPDIR)/sml.Po .c.o: $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @@ -282,62 +257,6 @@ include ./$(DEPDIR)/rawS0.Po # DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ # $(COMPILE) -c `$(CYGPATH_W) '$<'` -onewire.o: protocols/onewire.c - $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT onewire.o -MD -MP -MF $(DEPDIR)/onewire.Tpo -c -o onewire.o `test -f 'protocols/onewire.c' || echo '$(srcdir)/'`protocols/onewire.c - $(am__mv) $(DEPDIR)/onewire.Tpo $(DEPDIR)/onewire.Po -# source='protocols/onewire.c' object='onewire.o' libtool=no \ -# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ -# $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o onewire.o `test -f 'protocols/onewire.c' || echo '$(srcdir)/'`protocols/onewire.c - -onewire.obj: protocols/onewire.c - $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT onewire.obj -MD -MP -MF $(DEPDIR)/onewire.Tpo -c -o onewire.obj `if test -f 'protocols/onewire.c'; then $(CYGPATH_W) 'protocols/onewire.c'; else $(CYGPATH_W) '$(srcdir)/protocols/onewire.c'; fi` - $(am__mv) $(DEPDIR)/onewire.Tpo $(DEPDIR)/onewire.Po -# source='protocols/onewire.c' object='onewire.obj' libtool=no \ -# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ -# $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o onewire.obj `if test -f 'protocols/onewire.c'; then $(CYGPATH_W) 'protocols/onewire.c'; else $(CYGPATH_W) '$(srcdir)/protocols/onewire.c'; fi` - -din62056.o: protocols/din62056.c - $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT din62056.o -MD -MP -MF $(DEPDIR)/din62056.Tpo -c -o din62056.o `test -f 'protocols/din62056.c' || echo '$(srcdir)/'`protocols/din62056.c - $(am__mv) $(DEPDIR)/din62056.Tpo $(DEPDIR)/din62056.Po -# source='protocols/din62056.c' object='din62056.o' libtool=no \ -# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ -# $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o din62056.o `test -f 'protocols/din62056.c' || echo '$(srcdir)/'`protocols/din62056.c - -din62056.obj: protocols/din62056.c - $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT din62056.obj -MD -MP -MF $(DEPDIR)/din62056.Tpo -c -o din62056.obj `if test -f 'protocols/din62056.c'; then $(CYGPATH_W) 'protocols/din62056.c'; else $(CYGPATH_W) '$(srcdir)/protocols/din62056.c'; fi` - $(am__mv) $(DEPDIR)/din62056.Tpo $(DEPDIR)/din62056.Po -# source='protocols/din62056.c' object='din62056.obj' libtool=no \ -# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ -# $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o din62056.obj `if test -f 'protocols/din62056.c'; then $(CYGPATH_W) 'protocols/din62056.c'; else $(CYGPATH_W) '$(srcdir)/protocols/din62056.c'; fi` - -rawS0.o: protocols/rawS0.c - $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rawS0.o -MD -MP -MF $(DEPDIR)/rawS0.Tpo -c -o rawS0.o `test -f 'protocols/rawS0.c' || echo '$(srcdir)/'`protocols/rawS0.c - $(am__mv) $(DEPDIR)/rawS0.Tpo $(DEPDIR)/rawS0.Po -# source='protocols/rawS0.c' object='rawS0.o' libtool=no \ -# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ -# $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rawS0.o `test -f 'protocols/rawS0.c' || echo '$(srcdir)/'`protocols/rawS0.c - -rawS0.obj: protocols/rawS0.c - $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rawS0.obj -MD -MP -MF $(DEPDIR)/rawS0.Tpo -c -o rawS0.obj `if test -f 'protocols/rawS0.c'; then $(CYGPATH_W) 'protocols/rawS0.c'; else $(CYGPATH_W) '$(srcdir)/protocols/rawS0.c'; fi` - $(am__mv) $(DEPDIR)/rawS0.Tpo $(DEPDIR)/rawS0.Po -# source='protocols/rawS0.c' object='rawS0.obj' libtool=no \ -# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ -# $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rawS0.obj `if test -f 'protocols/rawS0.c'; then $(CYGPATH_W) 'protocols/rawS0.c'; else $(CYGPATH_W) '$(srcdir)/protocols/rawS0.c'; fi` - -random.o: protocols/random.c - $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT random.o -MD -MP -MF $(DEPDIR)/random.Tpo -c -o random.o `test -f 'protocols/random.c' || echo '$(srcdir)/'`protocols/random.c - $(am__mv) $(DEPDIR)/random.Tpo $(DEPDIR)/random.Po -# source='protocols/random.c' object='random.o' libtool=no \ -# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ -# $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o random.o `test -f 'protocols/random.c' || echo '$(srcdir)/'`protocols/random.c - -random.obj: protocols/random.c - $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT random.obj -MD -MP -MF $(DEPDIR)/random.Tpo -c -o random.obj `if test -f 'protocols/random.c'; then $(CYGPATH_W) 'protocols/random.c'; else $(CYGPATH_W) '$(srcdir)/protocols/random.c'; fi` - $(am__mv) $(DEPDIR)/random.Tpo $(DEPDIR)/random.Po -# source='protocols/random.c' object='random.obj' libtool=no \ -# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ -# $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o random.obj `if test -f 'protocols/random.c'; then $(CYGPATH_W) 'protocols/random.c'; else $(CYGPATH_W) '$(srcdir)/protocols/random.c'; fi` - ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ @@ -422,11 +341,8 @@ distdir: $(DISTFILES) done check-am: all-am check: check-am -all-am: Makefile $(PROGRAMS) +all-am: Makefile $(LIBRARIES) installdirs: - for dir in "$(DESTDIR)$(bindir)"; do \ - test -z "$$dir" || $(MKDIR_P) "$$dir"; \ - done install: install-am install-exec: install-exec-am install-data: install-data-am @@ -454,7 +370,7 @@ maintainer-clean-generic: @echo "it deletes files that may require special tools to rebuild." clean: clean-am -clean-am: clean-binPROGRAMS clean-generic mostlyclean-am +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) @@ -480,7 +396,7 @@ install-dvi: install-dvi-am install-dvi-am: -install-exec-am: install-binPROGRAMS +install-exec-am: install-html: install-html-am @@ -519,22 +435,22 @@ ps: ps-am ps-am: -uninstall-am: uninstall-binPROGRAMS +uninstall-am: .MAKE: install-am install-strip -.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ - clean-generic ctags distclean distclean-compile \ +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES ctags distclean distclean-compile \ distclean-generic distclean-tags distdir dvi dvi-am html \ - html-am info info-am install install-am install-binPROGRAMS \ - install-data install-data-am install-dvi install-dvi-am \ - install-exec install-exec-am install-html install-html-am \ - install-info install-info-am install-man install-pdf \ - install-pdf-am install-ps install-ps-am install-strip \ - installcheck installcheck-am installdirs maintainer-clean \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ - uninstall-am uninstall-binPROGRAMS + uninstall-am # Tell versions [3.59,3.63) of GNU make to not export all variables. diff --git a/src/Makefile.am b/src/Makefile.am index 5a92aea..4d07c75 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,16 +1,19 @@ -# what flags you want to pass to the C compiler & linker -AM_CFLAGS = -Wall -D_REENTRANT -std=gnu99 $(DEPS_CFLAGS) +AM_CFLAGS = -Wall -D_REENTRANT -std=gnu99 +AM_CPPFLAGS = -I$(top_srcdir)/include AM_LDFLAGS = -# this lists the binaries to produce, the (non-PHONY, binary) targets in -# the previous manual Makefile -bin_PROGRAMS = vzlogger +noinst_LIBRARIES = libmeter.a libobis.a -vzlogger_SOURCES = main.c api.c queue.c ltqnorm.c protocols/1wire.c protocols/obis.c protocols/rawS0.c protocols/random.c -vzlogger_LDADD = -lpthread -lm $(DEPS_LIBS) +libmeter_a_SOURCES = meter.c d0.c s0.c random.c onewire.c ltqnorm.c +#libmeter_a_HEADERS = meter.h -if LOCAL -vzlogger_SOURCES += local.c -vzlogger_LDADD += $(DEPS_LOCAL_LIBS) -AM_CFLAGS += $(DEPS_LOCAL_CFLAGS) +libobis_a_SOURCES = obis.c +#libobis_a_HEADERS = obis.h + +# SML support +#################################################################### +if SML_SUPPORT +libmeter_a_SOURCES += sml.c +libmeter_a_LIBADD = libobis.a +AM_CFLAGS += $(DEPS_SML_CFLAGS) endif diff --git a/src/protocols/obis.c b/src/d0.c similarity index 75% rename from src/protocols/obis.c rename to src/d0.c index b49fa86..1690122 100644 --- a/src/protocols/obis.c +++ b/src/d0.c @@ -1,5 +1,7 @@ /** - * OBIS protocol parser + * Plaintext protocol according to DIN EN 62056-21 + * + * This protocol uses OBIS to identify the readout data * * This is our example protocol. Use this skeleton to add your own * protocols and meters. @@ -33,14 +35,21 @@ #include #include -#include "obis.h" +#include "../include/d0.h" -void * obis_init(char *port) { +int meter_d0_open(meter_handle_d0_t *handle, char *options) { struct termios tio; - int *fd = malloc(sizeof(int)); - memset(&tio, 0, sizeof(tio)); - + + /* open serial port */ + handle->fd = open(options, O_RDWR); // | O_NONBLOCK); + + if (handle->fd < 0) { + return -1; + } + + // TODO save oldtio + tio.c_iflag = 0; tio.c_oflag = 0; tio.c_cflag = CS7|CREAD|CLOCAL; // 7n1, see termios.h for more information @@ -48,26 +57,22 @@ void * obis_init(char *port) { tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 5; - *fd = open(port, O_RDWR); // | O_NONBLOCK); cfsetospeed(&tio, B9600); // 9600 baud cfsetispeed(&tio, B9600); // 9600 baud - - return (void *) fd; + + return 0; } -void obis_close(void *handle) { - int *fd = (int *) handle; - - close(*fd); - free(handle); +void meter_d0_close(meter_handle_d0_t *handle) { + close(handle->fd); } -reading_t obis_get(void *handle) { - reading_t rd; - +meter_reading_t meter_d0_read(meter_handle_d0_t *handle) { + meter_reading_t rd; + rd.value = 33.3334; gettimeofday(&rd.tv, NULL); - + return rd; } diff --git a/src/main.c b/src/main.c deleted file mode 100644 index 4ba3cb7..0000000 --- a/src/main.c +++ /dev/null @@ -1,464 +0,0 @@ -/** - * Main source file - * - * @package vzlogger - * @copyright Copyright (c) 2011, The volkszaehler.org project - * @license http://www.gnu.org/licenses/gpl.txt GNU Public License - * @author Steffen Vogel - */ -/* - * This file is part of volkzaehler.org - * - * volkzaehler.org 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 - * any later version. - * - * volkzaehler.org 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 volkszaehler.org. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef LOCAL -#include -#include "local.h" -#endif - -#include "main.h" -#include "queue.h" -#include "api.h" - -#include "protocols/obis.h" -#include "protocols/1wire.h" -#include "protocols/rawS0.h" -#include "protocols/random.h" - -/** - * List of available protocols - * incl. function pointers - */ -static protocol_t protocols[] = { - {"1wire", "Dallas 1-Wire sensors (via OWFS)", onewire_get, onewire_init, onewire_close, SENSOR}, -// {"obis", "Plaintext OBIS", obis_get, obis_init, obis_close, SENSOR}, - {"random", "Random walk", random_get, random_init, random_close, SENSOR}, - {"rawS0", "S0 on RS232", rawS0_get, rawS0_init, rawS0_close, METER}, -// {"sml", "Smart Meter Language", sml_get, sml_init, sml_close, SENSOR}, -// {"fluksousb", "FluksoUSB board", flukso_get, flukso_init, flukso_close, SENSOR}, - {NULL} /* stop condition for iterator */ -}; - - -/** - * Command line options - */ -static struct option long_options[] = { - {"config", required_argument, 0, 'c'}, - {"daemon", required_argument, 0, 'd'}, -#ifdef LOCAL - {"local", no_argument, 0, 'l'}, - {"local-port", required_argument, 0, 'p'}, -#endif /* LOCAL */ - {"verbose", optional_argument, 0, 'v'}, - {"help", no_argument, 0, 'h'}, - {"version", no_argument, 0, 'V'}, - {NULL} /* stop condition for iterator */ -}; - -/** - * Descriptions vor command line options - */ -static char *long_options_descs[] = { - "config file with channel -> uuid mapping", - "run as daemon", -#ifdef LOCAL - "activate local interface (tiny webserver)", - "TCP port for local interface", -#endif /* LOCAL */ - "enable verbose output", - "show this help", - "show version of vzlogger", - NULL /* stop condition for iterator */ -}; - -/* - * Global variables - */ -channel_t chans[MAX_CHANNELS]; // TODO use dynamic allocation -options_t opts = { /* setting default options */ - NULL, /* config file */ - 8080, /* port for local interface */ - 0, /* debug level / verbosity */ - FALSE, /* daemon mode */ - FALSE /* local interface */ -}; - -/** - * Print available options and some other usefull information - */ -void usage(char * argv[]) { - char ** desc = long_options_descs; - struct option * op = long_options; - protocol_t * prot = protocols; - - printf("Usage: %s [options]\n\n", argv[0]); - printf(" following options are available:\n"); - - while (op->name && desc) { - printf("\t-%c, --%-12s\t%s\n", op->val, op->name, *desc); - op++; - desc++; - } - - printf("\n"); - printf(" following protocol types are supported:\n"); - - while (prot->name) { - printf("\t%-12s\t%s\n", prot->name, prot->desc); - prot++; - } - - printf("\n%s - volkszaehler.org logging utility\n", PACKAGE_STRING); - printf("by Steffen Vogel \n"); - printf("send bugreports to %s\n", PACKAGE_BUGREPORT); -} - -/** - * Wrapper to log notices and errors - * - * @param ch could be NULL for general messages - * @todo integrate into syslog - */ -void print(int level, char * format, channel_t *ch, ... ) { - static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; - va_list args; - - struct timeval now; - struct tm * timeinfo; - char buffer[16]; - - if (level <= (signed int) opts.verbose) { - gettimeofday(&now, NULL); - timeinfo = localtime(&now.tv_sec); - - strftime(buffer, 16, "%b %d %H:%M:%S", timeinfo); - - pthread_mutex_lock(&mutex); - fprintf((level > 0) ? stdout : stderr, "[%s.%3lu]", buffer, now.tv_usec / 1000); - - if (ch != NULL) { - fprintf((level > 0) ? stdout : stderr, "[ch#%i]\t", ch->id); - } - else { - fprintf((level > 0) ? stdout : stderr, "\t\t"); - } - - va_start(args, ch); - vfprintf((level > 0) ? stdout : stderr, format, args); - va_end(args); - fprintf((level > 0) ? stdout : stderr, "\n"); - pthread_mutex_unlock(&mutex); - } -} - -/** - * Parse options from command line - */ -void parse_options(int argc, char * argv[], options_t * opts) { - while (TRUE) { - /* getopt_long stores the option index here. */ - int option_index = 0; - - int c = getopt_long(argc, argv, "i:c:p:lhVdv::", long_options, &option_index); - - /* detect the end of the options. */ - if (c == -1) - break; - - switch (c) { - case 'v': - opts->verbose = (optarg == NULL) ? 1 : atoi(optarg); - break; - - case 'l': - opts->local = TRUE; - break; - - case 'd': - opts->daemon = TRUE; - break; - - case 'p': /* port for local interface */ - opts->port = atoi(optarg); - break; - - case 'c': /* read config file */ - opts->config = (char *) malloc(strlen(optarg)+1); - strcpy(opts->config, optarg); - break; - - case 'V': - printf("%s\n", VERSION); - exit(EXIT_SUCCESS); - break; - - case 'h': - case '?': - usage(argv); - exit((c == '?') ? EXIT_FAILURE : EXIT_SUCCESS); - } - } - - if (opts->config == NULL) { /* search for config file */ - if (access("vzlogger.conf", R_OK) == 0) { - opts->config = "vzlogger.conf"; - } - else if (access("/etc/vzlogger.conf", R_OK) == 0) { - opts->config = "/etc/vzlogger.conf"; - } - else { /* search in home directory */ - char *home_config = malloc(255); - strcat(home_config, getenv("HOME")); /* get home dir */ - strcat(home_config, "/.vzlogger.conf"); /* append my filename */ - - if (access(home_config, R_OK) == 0) { - opts->config = home_config; - } - } - } -} - -int parse_channels(char *filename, channel_t *chans) { - FILE *file = fopen(filename, "r"); /* open configuration */ - - if (!filename) { /* nothing found */ - print(-1, "No config file found! Please specify with --config!\n", NULL); - exit(EXIT_FAILURE); - } - - if (file == NULL) { - perror(filename); /* why didn't the file open? */ - exit(EXIT_FAILURE); - } - else { - print(2, "Start parsing configuration from %s", NULL, filename); - } - - char line[256]; - int chan_num = 0, line_num = 1; - - while (chan_num < MAX_CHANNELS && fgets(line, sizeof line, file) != NULL) { /* read a line */ - if (line[0] == ';' || line[0] == '\n') continue; /* skip comments */ - - channel_t ch = { - chan_num, - NULL, - NULL, - NULL, - 0, - NULL, - protocols - }; - - char *tok = strtok(line, " \t"); - - for (int i = 0; i < 7 && tok != NULL; i++) { - size_t len = strlen(tok); - - switch(i) { - case 0: /* protocol */ - while (ch.prot->name && strcmp(ch.prot->name, tok) != 0) ch.prot++; /* linear search */ - - if (ch.prot == NULL) { - print(-1, "Invalid protocol: %s in %s:%i", NULL, tok, filename, line_num); - exit(EXIT_FAILURE); - } - break; - - case 1: /* interval */ - ch.interval = strtol(tok, (char **) NULL, 10); - - if (errno == EINVAL || errno == ERANGE) { - print(-1, "Invalid interval: %s in %s:%i", NULL, tok, filename, line_num); - exit(EXIT_FAILURE); - } - break; - - case 2: /* uuid */ - if (len == 0) { // TODO add uuid validation - print(-1, "Missing uuid in %s:%i", NULL, filename, line_num); - exit(EXIT_FAILURE); - } - - ch.uuid = (char *) malloc(len+1); /* including string termination */ - strcpy(ch.uuid, tok); - break; - - case 3: /* middleware */ - if (len == 0) { // TODO add uuid validation - print(-1, "Missing middleware in %s:%i", NULL, filename, line_num); - exit(EXIT_FAILURE); - } - - ch.middleware = (char *) malloc(len+1); /* including string termination */ - strcpy(ch.middleware, tok); - break; - - case 4: /* options */ - ch.options = (char *) malloc(len); - strncpy(ch.options, tok, len-1); - ch.options[len-1] = '\0'; /* replace \n by \0 */ - break; - } - - tok = strtok(NULL, " \t"); - } - - print(1, "Parsed ch#%i (protocol=%s interval=%i uuid=%s middleware=%s options=%s)", &ch, ch.id, ch.prot->name, ch.interval, ch.uuid, ch.middleware, ch.options); - chans[chan_num] = ch; - - chan_num++; - line_num++; - } - - fclose(file); - - return chan_num; -} - -/** - * Read thread - * - * Aquires reading from meters/sensors - */ -void *read_thread(void *arg) { - channel_t *ch = (channel_t *) arg; /* casting argument */ - print(1, "Started reading thread", ch); - - /* initalize channel */ - ch->handle = ch->prot->init_func(ch->options); /* init sensor/meter */ - - do { - /** - * Aquire reading, - * may be blocking if interpreter == METER - */ - reading_t rd = ch->prot->read_func(ch->handle); - - pthread_mutex_lock(&ch->mutex); - if (!queue_push(&ch->queue, rd)) { - print(6, "Warning queue is full, discarding first tuple!", ch); - } - pthread_cond_broadcast(&ch->condition); /* notify webserver and logging thread */ - pthread_mutex_unlock(&ch->mutex); - - print(1, "Value read: %.1f", ch, rd.value); - - /* Debugging */ - if (opts.verbose >= 10) { - char *queue_str = queue_print(&ch->queue); - print(10, "Queue dump: %s write_p = %lu\t read_p = %lu", ch, queue_str, ch->queue.write_p, ch->queue.read_p); - free(queue_str); - } - - if (ch->prot->interpreter != METER) { /* for meters, the read_func call is blocking */ - print(5, "Next reading in %i seconds", ch, ch->interval); - sleep(ch->interval); /* else sleep and restart aquisition */ - } - - pthread_testcancel(); /* test for cancelation request */ - } while (opts.daemon || opts.local); - - /* close channel */ - ch->prot->close_func(ch->handle); - - return NULL; -} - -/** - * The main loop - */ -int main(int argc, char *argv[]) { - int num_chans; - -#ifdef LOCAL - struct MHD_Daemon *httpd_handle = NULL; -#endif /* LOCAL */ - - parse_options(argc, argv, &opts); /* parse command line arguments */ - num_chans = parse_channels(opts.config, chans); /* parse channels from configuration */ - - print(1, "Started %s with verbosity level %i", NULL, argv[0], opts.verbose); - - curl_global_init(CURL_GLOBAL_ALL); /* global intialization for all threads */ - - for (int i = 0; i < num_chans; i++) { - channel_t *ch = &chans[i]; - - /* initialize queue to buffer data */ - queue_init(&ch->queue, (BUFFER_LENGTH / ch->interval) + 1); - - /* initialize thread syncronization helpers */ - pthread_mutex_init(&ch->mutex, NULL); - pthread_cond_init(&ch->condition, NULL); - - /* start threads */ - pthread_create(&ch->reading_thread, NULL, read_thread, (void *) ch); - pthread_create(&ch->logging_thread, NULL, api_thread, (void *) ch); - } - -#ifdef LOCAL - /* start webserver for local interface */ - if (opts.local) { - httpd_handle = MHD_start_daemon( - MHD_USE_THREAD_PER_CONNECTION, - opts.port, - NULL, NULL, - handle_request, - &num_chans, - MHD_OPTION_END - ); - } -#endif /* LOCAL */ - - /* wait for all threads to terminate */ - // TODO bind signal for termination - for (int i = 0; i < num_chans; i++) { - channel_t * ch = &chans[i]; - - pthread_join(ch->reading_thread, NULL); - pthread_join(ch->logging_thread, NULL); - - // TODO close protocol handles - - free(ch->middleware); - free(ch->uuid); - free(ch->options); - queue_free(&ch->queue); - - pthread_cond_destroy(&ch->condition); - pthread_mutex_destroy(&ch->mutex); - } - -#ifdef LOCAL - /* stop webserver */ - if (httpd_handle) { - MHD_stop_daemon(httpd_handle); - } -#endif /* LOCAL */ - - return EXIT_SUCCESS; -} diff --git a/src/main.h b/src/main.h deleted file mode 100644 index 8db99b1..0000000 --- a/src/main.h +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Main header file - * - * @package vzlogger - * @copyright Copyright (c) 2011, The volkszaehler.org project - * @license http://www.gnu.org/licenses/gpl.txt GNU Public License - * @author Steffen Vogel - */ -/* - * This file is part of volkzaehler.org - * - * volkzaehler.org 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 - * any later version. - * - * volkzaehler.org 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 volkszaehler.org. If not, see . - */ - -#ifndef _MAIN_H_ -#define _MAIN_H_ - -#include -#include -#include - -#include "../config.h" - -#include "protocol.h" -#include "queue.h" - -#define MAX_CHANNELS 16 - -#define RETRY_PAUSE 10 //600 /* seconds to wait after failed request */ -#define BUFFER_LENGTH 60 //600 /* in seconds */ - -#ifndef TRUE -#define TRUE 1 -#endif - -#ifndef FALSE -#define FALSE 0 -#endif - -/** - * Datatype for every channel - */ -typedef struct { - int id; /* only for internal usage & debugging */ - - char *middleware; - char *uuid; - char *options; - - unsigned int interval; - - void *handle; /* handle to store connection status */ - protocol_t *prot; /* pointer to protocol */ - queue_t queue; /* circular queue to buffer readings */ - - pthread_t reading_thread; /* pthread for asynchronus reading */ - pthread_t logging_thread; /* pthread for asynchronus logging */ - pthread_mutex_t mutex; - pthread_cond_t condition; -} channel_t; - -/** - * Options from command line - */ -typedef struct { - char * config; /* path to config file */ - unsigned int port; /* tcp port for local interface */ - unsigned int verbose; /* verbosity level */ - - /* boolean bitfields, at the end of struct */ - int daemon:1; - int local:1; /* enable local interface */ -} options_t; - -/* Prototypes */ -void parse_options(int argc, char * argv[], options_t *opts); -int parse_channels(char * filename, channel_t *chans); -void print(int level, char * format, channel_t *ch, ... ); -void usage(char ** argv); - -#endif /* _MAIN_H_ */ diff --git a/src/meter.c b/src/meter.c new file mode 100644 index 0000000..fdf514f --- /dev/null +++ b/src/meter.c @@ -0,0 +1,126 @@ +/** + * Protocol interface + * + * @package vzlogger + * @copyright Copyright (c) 2011, The volkszaehler.org project + * @license http://www.gnu.org/licenses/gpl.txt GNU Public License + * @author Steffen Vogel + */ +/* + * This file is part of volkzaehler.org + * + * volkzaehler.org 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 + * any later version. + * + * volkzaehler.org 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 volkszaehler.org. If not, see . + */ + +#include +#include + +#include "../include/meter.h" + +/* List of available meter types */ +meter_type_t meter_types[] = { + {ONEWIRE, "onewire", "Dallas 1-Wire sensors (via OWFS)", 1}, + {RANDOM, "random", "Random walk", 1}, + {S0, "S0", "S0 on RS232", 0}, +// {D0, "D0", "On-site plaintext protocol (DIN EN 62056-21)", 0}, +#ifdef SML_SUPPORT + {SML, "sml", "Smart Meter Language", 0}, +#endif /* SML_SUPPORT */ + {} /* stop condition for iterator */ +}; + +void meter_init(meter_t *meter, meter_type_t *type, char *options) { + meter->type = type; + meter->options = strdup(options); +} + +void meter_free(meter_t *meter) { + free(meter->options); +} + +int meter_open(meter_t *meter) { + switch (meter->type->tag) { + case RANDOM: + return meter_random_open(&meter->handle.random, meter->options); + + case S0: + return meter_s0_open(&meter->handle.s0, meter->options); + + case D0: + return meter_d0_open(&meter->handle.d0, meter->options); + +#ifdef SML_SUPPORT + case SML: + return meter_sml_open(&meter->handle.sml, meter->options); +#endif /* SML_SUPPORT */ + + case ONEWIRE: + return meter_onewire_open(&meter->handle.onewire, meter->options); + + default: + return -1; + } +} + +void meter_close(meter_t *meter) { + switch (meter->type->tag) { + case RANDOM: + meter_random_close(&meter->handle.random); + break; + + case S0: + meter_s0_close(&meter->handle.s0); + break; + + case D0: + meter_d0_close(&meter->handle.d0); + break; + +#ifdef SML_SUPPORT + case SML: + meter_sml_close(&meter->handle.sml); + break; +#endif /* SML_SUPPORT */ + + case ONEWIRE: + meter_onewire_close(&meter->handle.onewire); + break; + } +} + +meter_reading_t meter_read(meter_t *meter) { + meter_reading_t rd; + + switch (meter->type->tag) { + case RANDOM: + return meter_random_read(&meter->handle.random); + + case S0: + return meter_s0_read(&meter->handle.s0); + + case D0: + return meter_d0_read(&meter->handle.d0); + +#ifdef SML_SUPPORT + case SML: + return meter_sml_read(&meter->handle.sml); +#endif /* SML_SUPPORT */ + + case ONEWIRE: + return meter_onewire_read(&meter->handle.onewire); + + default: + return rd; + } +} diff --git a/src/obis.c b/src/obis.c new file mode 100644 index 0000000..8b7b2c8 --- /dev/null +++ b/src/obis.c @@ -0,0 +1,110 @@ +/** + * OBIS IDs as specified in DIN EN 62056-61 + * + * @package vzlogger + * @copyright Copyright (c) 2011, The volkszaehler.org project + * @license http://www.gnu.org/licenses/gpl.txt GNU Public License + * @author Steffen Vogel + */ +/* + * This file is part of volkzaehler.org + * + * volkzaehler.org 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 + * any later version. + * + * volkzaehler.org 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 volkszaehler.org. If not, see . + */ + +#include +#include +#include +#include + +#include "../include/obis.h" + +obis_alias_t obis_aliases[] = { +// A B C D E F abbreviation description +//===================================================================================== +{{"\x81\x81\xC7\x82\x03\xFF"}, "voltage", ""}, +{{"\x81\x81\xC7\x82\x03\xFF"}, "current", ""}, +{{"\x81\x81\xC7\x82\x03\xFF"}, "frequency", ""}, +{{"\x81\x81\xC7\x82\x03\xFF"}, "powerfactor", ""}, + +/* ESYQ3B (Easymeter Q3B) */ +{{"\x81\x81\xC7\x82\x03\xFF"}, "vendor", "vendor specific"}, +{{"\x01\x00\x01\x08\x00\xFF"}, "counter", "Active Power Counter Total"}, +{{"\x01\x00\x01\x08\x01\xFF"}, "counter-tarif1","Active Power Counter Tariff 1"}, +{{"\x01\x00\x01\x08\x02\xFF"}, "counter-tarif2","Active Power Counter Tariff 2"}, +{{"\x01\x00\x01\x07\x00\xFF"}, "power", "Active Power Instantaneous value Total"}, +{{"\x01\x00\x15\x07\x00\xFF"}, "power-l1", "L1 Active Power Instantaneous value Total"}, +{{"\x01\x00\x29\x07\x00\xFF"}, "power-l2", "L1 Active Power Instantaneous value Total"}, +{{"\x01\x00\x3D\x07\x00\xFF"}, "power-l2", "L3 Active Power Instantaneous value Total"}, +{{"\x01\x00\x60\x05\x05\xFF"}, "status", "Meter status flag"}, +{} /* stop condition for iterator */ +}; + +obis_id_t obis_init(unsigned char *raw) { + obis_id_t id; + memcpy(id.raw, raw, 6); + return id; +} + +obis_id_t obis_parse(char *str) { + obis_id_t id; + regex_t expr; + regmatch_t matches[7]; + + regcomp(&expr, "^([0-9])-([a-f0-9]{,2}):([a-f0-9]{,2})\\.([a-f0-9]{,2})\\.([a-f0-9]{,2})(\\*[a-f0-9]{,2})?$", REG_EXTENDED | REG_ICASE); + + if (regexec(&expr, str, 7, matches, 0) == 0) { /* found string in OBIS notation */ + for (int i=0; i<6; i++) { + if (matches[i+1].rm_so != -1) { + id.raw[i] = strtoul(str+matches[i+1].rm_so, NULL, 16); + } + else { + id.raw[i] = 0xff; /* default value */ + } + } + } + else { /* looking for alias */ + obis_alias_t *it = obis_aliases; + do { /* linear search */ + if (strcmp(it->name, str) == 0) { + return it->id; + } + } while ((++it)->name); + } + + return id; +} + +char obis_is_manufacturer_specific(obis_id_t id) { + return ( + (id.groups.channel >= 128 && id.groups.channel <= 199) || + (id.groups.indicator >= 128 && id.groups.indicator <= 199) || + (id.groups.indicator == 240) || + (id.groups.mode >= 128 && id.groups.mode <= 254) || + (id.groups.quantities >= 128 && id.groups.quantities <= 254) || + (id.groups.storage >= 128 && id.groups.storage <= 254) + ); +} + +int obis_unparse(obis_id_t id, char *buffer) { + return sprintf(buffer, "%x-%x:%x.%x.%x*%x", + id.groups.media, + id.groups.channel, + id.groups.indicator, + id.groups.mode, + id.groups.quantities, + id.groups.storage + ); +} + diff --git a/src/protocols/1wire.c b/src/onewire.c similarity index 71% rename from src/protocols/1wire.c rename to src/onewire.c index b902f38..576ce74 100644 --- a/src/protocols/1wire.c +++ b/src/onewire.c @@ -23,12 +23,9 @@ * along with volkszaehler.org. If not, see . */ -#include #include -#include "../main.h" -#include "../protocol.h" -#include "1wire.h" +#include "../include/onewire.h" /** * Initialize sensor @@ -36,35 +33,27 @@ * @param address path to the sensor in the owfs * @return pointer to file descriptor */ -void * onewire_init(char *address) { - FILE * fd = fopen(address, "r"); +int meter_onewire_open(meter_handle_onewire_t *handle, char *options) { + handle->file = fopen(options, "r"); - if (fd == NULL) { - perror(address); - print(-1, "Failed to open sensor: %s", NULL, address); - exit(EXIT_FAILURE); - } - - return (void *) fd; + return (handle->file == NULL) ? -1 : 0; } -void onewire_close(void *handle) { - fclose((FILE *) handle); +void meter_onewire_close(meter_handle_onewire_t *handle) { + fclose(handle->file); } -reading_t onewire_get(void *handle) { - reading_t rd; +meter_reading_t meter_onewire_read(meter_handle_onewire_t *handle) { + meter_reading_t rd; char buffer[16]; int bytes; do { - rewind((FILE *) handle); - bytes = fread(buffer, 1, 16, (FILE *) handle); + rewind(handle->file); + bytes = fread(buffer, 1, 16, handle->file); buffer[bytes] = '\0'; /* zero terminated, required? */ if (bytes) { - print(4, "Read from sensor file: %s", NULL, buffer); - rd.value = strtof(buffer, NULL); gettimeofday(&rd.tv, NULL); } diff --git a/src/protocol.h b/src/protocol.h deleted file mode 100644 index be6ec0c..0000000 --- a/src/protocol.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef _PROTOCOL_H_ -#define _PROTOCOL_H_ - -#include - -typedef struct { - float value; - struct timeval tv; -} reading_t; - -typedef void *(*ifp_t)(char *options); -typedef void (*cfp_t)(void *handle); -typedef reading_t (*rfp_t)(void *handle); - -typedef enum { - MODE_METER, - MODE_SENSOR -} mode_t; - -typedef struct { - char * name; /* short identifier for protocol */ - char * desc; /* more detailed description */ - rfp_t read_func; /* function pointer to read data */ - ifp_t init_func; /* function pointer to init a channel */ - cfp_t close_func; /* function pointer to close a channel */ - mode_t mode; /* should we wait for next pulse? */ -} protocol_t; - -#endif /* _PROTOCOL_H_ */ diff --git a/src/protocols/rawS0.c b/src/protocols/rawS0.c deleted file mode 100644 index 1b211bb..0000000 --- a/src/protocols/rawS0.c +++ /dev/null @@ -1,99 +0,0 @@ -/** - * S0 Hutschienenzähler directly connected to an rs232 port - * - * @package vzlogger - * @copyright Copyright (c) 2011, The volkszaehler.org project - * @license http://www.gnu.org/licenses/gpl.txt GNU Public License - * @author Steffen Vogel - */ -/* - * This file is part of volkzaehler.org - * - * volkzaehler.org 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 - * any later version. - * - * volkzaehler.org 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 volkszaehler.org. If not, see . - */ - -#include -#include -#include -#include - -#include "../main.h" -#include "../protocol.h" -#include "rawS0.h" - -/** - * Setup serial port - */ -void * rawS0_init(char *port) { - rawS0_state_t *state; - struct termios newtio; - - /* initialize handle */ - state = malloc(sizeof(rawS0_state_t)); - - state->fd = open(port, O_RDWR | O_NOCTTY); - if (state->fd < 0) { - char err[255]; - strerror_r(errno, err, 255); - print(-1, "%s: %s", NULL, port, err); - exit(EXIT_FAILURE); - } - - tcgetattr(state->fd, &state->oldtio); /* save current port settings */ - - - /* configure port */ - memset(&newtio, 0, sizeof(struct termios)); - - newtio.c_cflag = B300 | CS8 | CLOCAL | CREAD; - newtio.c_iflag = IGNPAR; - newtio.c_oflag = 0; - newtio.c_lflag = 0; /* set input mode (non-canonical, no echo,...) */ - newtio.c_cc[VTIME] = 0; /* inter-character timer unused */ - newtio.c_cc[VMIN] = 1; /* blocking read until data is received */ - - /* apply configuration */ - tcsetattr(state->fd, TCSANOW, &newtio); - - return (void *) state; -} - -void rawS0_close(void *handle) { - rawS0_state_t *state = (rawS0_state_t *) handle; - - tcsetattr(state->fd, TCSANOW, &state->oldtio); - - close(state->fd); - free(handle); -} - -reading_t rawS0_get(void *handle) { - char buf[255]; - - rawS0_state_t *state = (rawS0_state_t *) handle; - reading_t rd; - - rd.value = 1; - - tcflush(state->fd, TCIOFLUSH); - - read(state->fd, buf, 255); /* blocking until one character/pulse is read */ - gettimeofday(&rd.tv, NULL); - - /* wait some ms for debouncing */ - usleep(30000); - - return rd; -} - diff --git a/src/protocols/sml.c b/src/protocols/sml.c deleted file mode 100644 index 45d91a8..0000000 --- a/src/protocols/sml.c +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Wrapper around libsml - * - * @package vzlogger - * @copyright Copyright (c) 2011, The volkszaehler.org project - * @copyright Copyright (c) 2011, DAI-Labor, TU-Berlin - * @license http://www.gnu.org/licenses/gpl.txt GNU Public License - * @author Steffen Vogel - * @author Juri Glass - * @author Mathias Runge - * @author Nadim El Sayed - */ -/* - * This file is part of volkzaehler.org - * - * volkzaehler.org 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 - * any later version. - * - * volkzaehler.org 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 volkszaehler.org. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -int sml_init(char *device) { - sml_state_t *state; - - /* initialize handle */ - state = malloc(sizeof(sml_state_t)); - - /* this example assumes that a EDL21 meter sending SML messages via a - * serial device. Adjust as needed. */ - int fd = serial_port_open(device); - - if (fd > 0) { - // start thread - - /* listen on the serial device, this call is blocking */ - sml_transport_listen(fd, &transport_receiver); - close(fd); - } -} - -void sml_close(void *handle) { - -} - -reading_t sml_get(void *handle) { - -} - -void sml_transport_receiver(unsigned char *buffer, size_t buffer_len) { - // the buffer contains the whole message, with transport escape sequences. - // these escape sequences are stripped here. - sml_file *file = sml_file_parse(buffer + 8, buffer_len - 16); - // the sml file is parsed now - - // read here some values .. - - // this prints some information about the file - sml_file_print(file); - - // free the malloc'd memory - sml_file_free(file); -} - -int sml_open_port(char *device) { - int bits; - struct termios config; - memset(&config, 0, sizeof(config)); - - int fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY); - if (fd < 0) { - printf("error: open(%s): %s\n", device, strerror(errno)); - return -1; - } - - // set RTS - ioctl(fd, TIOCMGET, &bits); - bits |= TIOCM_RTS; - ioctl(fd, TIOCMSET, &bits); - - tcgetattr( fd, &config ) ; - - // set 8-N-1 - config.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); - config.c_oflag &= ~OPOST; - config.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); - config.c_cflag &= ~(CSIZE | PARENB | PARODD | CSTOPB); - config.c_cflag |= CS8; - - // set speed to 9600 baud - cfsetispeed( &config, B9600); - cfsetospeed( &config, B9600); - - tcsetattr(fd, TCSANOW, &config); - return fd; -} diff --git a/src/queue.c b/src/queue.c deleted file mode 100644 index 37435c8..0000000 --- a/src/queue.c +++ /dev/null @@ -1,63 +0,0 @@ -#include -#include - -#include "queue.h" - -bool_t queue_init(queue_t *q, size_t size) { - q->buf = malloc(sizeof(reading_t) * size); /* keep one slot open */ - - if (q->buf) { - q->size = size; - q->read_p = q->write_p = 0; /* queue is empty */ - - return TRUE; - } - else { /* cannot allocate memory */ - return FALSE; - } -} - -bool_t queue_is_empty(queue_t *q) { - return (q->read_p == q->write_p); -} - -void queue_free(queue_t *q) { - queue_clear(q); - free(q->buf); -} - -void queue_clear(queue_t *q) { - q->read_p = q->write_p; -} - -bool_t queue_push(queue_t *q, reading_t rd) { - q->buf[q->write_p] = rd; /* copy data to buffer */ - q->write_p++; /* increment write pointer */ - q->write_p %= q->size; - - if (q->read_p == q->write_p) { /* queue full */ - q->read_p++; /* discarding first tuple */ - return FALSE; - } - - return TRUE; -} - -bool_t queue_get(queue_t *q, size_t index, reading_t *rd) { - *rd = q->buf[index]; - - return (index < q->size); -} - -char * queue_print(queue_t *q) { - char *buf = malloc(q->size * 6); - char *ret = buf; - - buf += sprintf(buf, "[%.1f", q->buf[0].value); - for (int i = 1; i < q->size; i++) { - buf += sprintf(buf, "|%.2f", q->buf[i].value); - } - buf += sprintf(buf, "]"); - - return ret; -} diff --git a/src/protocols/random.c b/src/random.c similarity index 58% rename from src/protocols/random.c rename to src/random.c index 56c80de..72ba760 100644 --- a/src/protocols/random.c +++ b/src/random.c @@ -22,52 +22,42 @@ * You should have received a copy of the GNU General Public License * along with volkszaehler.org. If not, see . */ - + #include #include #include -#include "../main.h" -#include "../protocol.h" -#include "random.h" +#include "../include/random.h" -/** - * Initialize prng - * @return random_state_t - */ -void * random_init(char *options) { - random_state_t *state; - state = malloc(sizeof(random_state_t)); +int meter_random_open(meter_handle_random_t *handle, char *options) { + srand(time(NULL)); /* initialize PNRG */ - srand(time(NULL)); - - state->min = 0; // TODO parse from options - state->max = strtof(options, NULL); - state->last = state->max * ((float) rand() / RAND_MAX); /* start value */ - - return (void *) state; + handle->min = 0; // TODO parse from options + handle->max = strtof(options, NULL); + handle->last = handle->max * ((float) rand() / RAND_MAX); /* start value */ + + return 0; /* always succeeds */ } -void random_close(void *handle) { - free(handle); +void meter_random_close(meter_handle_random_t *handle) { + /* nothing todo */ } -reading_t random_get(void *handle) { - random_state_t *state = (random_state_t *) handle; - reading_t rd; - - state->last += ltqnorm((float) rand() / RAND_MAX); - +meter_reading_t meter_random_read(meter_handle_random_t *handle) { + meter_reading_t rd; + + handle->last += ltqnorm((float) rand() / RAND_MAX); + /* check bounaries */ - if (state->last > state->max) { - state->last = state->max; + if (handle->last > handle->max) { + handle->last = handle->max; } - else if (state->last < state->min) { - state->last = state->min; + else if (handle->last < handle->min) { + handle->last = handle->min; } - - rd.value = state->last; + + rd.value = handle->last; gettimeofday(&rd.tv, NULL); - + return rd; } diff --git a/src/s0.c b/src/s0.c new file mode 100644 index 0000000..0b4ed74 --- /dev/null +++ b/src/s0.c @@ -0,0 +1,91 @@ +/** + * S0 Hutschienenzähler directly connected to an rs232 port + * + * @package vzlogger + * @copyright Copyright (c) 2011, The volkszaehler.org project + * @license http://www.gnu.org/licenses/gpl.txt GNU Public License + * @author Steffen Vogel + */ +/* + * This file is part of volkzaehler.org + * + * volkzaehler.org 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 + * any later version. + * + * volkzaehler.org 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 volkszaehler.org. If not, see . + */ + +#include +#include +#include +#include +#include + +#include "../include/s0.h" + +/** + * Setup serial port + */ +int meter_s0_open(meter_handle_s0_t *handle, char *options) { + /* open port */ + handle->fd = open(options, O_RDWR | O_NOCTTY); + + if (handle->fd < 0) { + return -1; + } + + /* save current port settings */ + tcgetattr(handle->fd, &handle->oldtio); + + + /* configure port */ + struct termios tio; + memset(&tio, 0, sizeof(struct termios)); + + tio.c_cflag = B300 | CS8 | CLOCAL | CREAD; + tio.c_iflag = IGNPAR; + tio.c_oflag = 0; + tio.c_lflag = 0; /* set input mode (non-canonical, no echo,...) */ + tio.c_cc[VTIME] = 0; /* inter-character timer unused */ + tio.c_cc[VMIN] = 1; /* blocking read until data is received */ + + /* apply configuration */ + tcsetattr(handle->fd, TCSANOW, &tio); + + return 0; +} + +void meter_s0_close(meter_handle_s0_t *handle) { + /* reset serial port */ + tcsetattr(handle->fd, TCSANOW, &handle->oldtio); + + /* close serial port */ + close(handle->fd); +} + +meter_reading_t meter_s0_read(meter_handle_s0_t *handle) { + char buf[8]; + + meter_reading_t rd; + + rd.value = 1; + + tcflush(handle->fd, TCIOFLUSH); + + read(handle->fd, buf, 8); /* blocking until one character/pulse is read */ + gettimeofday(&rd.tv, NULL); + + /* wait some ms for debouncing */ + usleep(30000); + + return rd; +} + diff --git a/src/sml.c b/src/sml.c new file mode 100644 index 0000000..63fc80c --- /dev/null +++ b/src/sml.c @@ -0,0 +1,191 @@ +/** + * Wrapper around libsml + * + * @package vzlogger + * @copyright Copyright (c) 2011, The volkszaehler.org project + * @copyright Copyright (c) 2011, DAI-Labor, TU-Berlin + * @license http://www.gnu.org/licenses/gpl.txt GNU Public License + * @author Steffen Vogel + * @author Juri Glass + * @author Mathias Runge + * @author Nadim El Sayed + */ +/* + * This file is part of volkzaehler.org + * + * volkzaehler.org 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 + * any later version. + * + * volkzaehler.org 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 volkszaehler.org. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +/* serial port */ +#include +#include +#include + +/* socket */ +#include +#include + +/* sml stuff */ +#include +#include + +#include "../include/sml.h" +#include "../include/obis.h" + +int meter_sml_open(meter_handle_sml_t *handle, char *options) { + char *node = strsep(&options, ":"); + char *service = strsep(&options, ":"); + + handle->id = obis_parse(options); + handle->fd = meter_sml_open_socket(node, service); + //handle->fd = meter_sml_open_port(options); + + return (handle->fd < 0) ? -1 : 0; +} + +void meter_sml_close(meter_handle_sml_t *handle) { + // TODO reset serial port + close(handle->fd); +} + +meter_reading_t meter_sml_read(meter_handle_sml_t *handle) { + unsigned char buffer[SML_BUFFER_LEN]; + size_t bytes; + sml_file *sml_file; + meter_reading_t rd; + + /* blocking read from fd */ + bytes = sml_transport_read(handle->fd, buffer, SML_BUFFER_LEN); + + /* sml parsing & stripping escape sequences */ + sml_file = sml_file_parse(buffer + 8, bytes - 16); + + /* extraction of readings */ + rd = meter_sml_parse(sml_file, handle->id); + + /* free the malloc'd memory */ + sml_file_free(sml_file); + + return rd; +} + +int meter_sml_open_socket(char *node, char *service) { + struct sockaddr_in sin; + struct addrinfo *ais; + int fd, res; + + getaddrinfo(node, service, NULL, &ais); + memcpy(&sin, ais->ai_addr, ais->ai_addrlen); + + fd = socket(PF_INET, SOCK_STREAM, 0); + + res = connect(fd, (struct sockaddr *) &sin, sizeof(sin)); + + return (res < 0) ? -1 : fd; +} + +int meter_sml_open_port(char *device) { + int bits; + struct termios config; + memset(&config, 0, sizeof(config)); + + int fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY); + if (fd < 0) { + printf("error: open(%s): %s\n", device, strerror(errno)); + return -1; + } + + // set RTS + ioctl(fd, TIOCMGET, &bits); + bits |= TIOCM_RTS; + ioctl(fd, TIOCMSET, &bits); + + tcgetattr( fd, &config ) ; + + // set 8-N-1 + config.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); + config.c_oflag &= ~OPOST; + config.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + config.c_cflag &= ~(CSIZE | PARENB | PARODD | CSTOPB); + config.c_cflag |= CS8; + + // set speed to 9600 baud + cfsetispeed( &config, B9600); + cfsetospeed( &config, B9600); + + tcsetattr(fd, TCSANOW, &config); + return fd; +} + +meter_reading_t meter_sml_parse(sml_file *file, obis_id_t which) { + meter_reading_t rd; + + for (int i = 0; i < file->messages_len; i++) { + sml_message *message = file->messages[i]; + + if (*message->message_body->tag == SML_MESSAGE_GET_LIST_RESPONSE) { + sml_list *entry; + sml_get_list_response *body; + + body = (sml_get_list_response *) message->message_body->data; + + for (entry = body->val_list; entry != NULL; entry = entry->next) { /* linked list */ + obis_id_t id = obis_init(entry->obj_name->str); + + if (obis_compare(which, id) == 0) { + //int unit = (entry->unit) ? *entry->unit : 0; + int scaler = (entry->scaler) ? *entry->scaler : 1; + + switch (entry->value->type) { + case 0x51: rd.value = *entry->value->data.int8; break; + case 0x52: rd.value = *entry->value->data.int16; break; + case 0x54: rd.value = *entry->value->data.int32; break; + case 0x58: rd.value = *entry->value->data.int64; break; + case 0x61: rd.value = *entry->value->data.uint8; break; + case 0x62: rd.value = *entry->value->data.uint16; break; + case 0x64: rd.value = *entry->value->data.uint32; break; + case 0x68: rd.value = *entry->value->data.uint64; break; + + default: + fprintf(stderr, "Unknown value type: %x", entry->value->type); + } + + /* apply scaler */ + rd.value *= pow(10, scaler); + + + /* get time */ + if (entry->val_time) { // TODO handle SML_TIME_SEC_INDEX + rd.tv.tv_sec = *entry->val_time->data.timestamp; + rd.tv.tv_usec = 0; + } + else { + gettimeofday(&rd.tv, NULL); + } + + return rd; /* skipping rest */ + } + } + } + } + + return rd; +}