1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-09 00:00:00 +01:00

Merge branch 'node-test' into develop

# Conflicts:
#	include/villas/api/session.h
#	lib/api.c
#	lib/super_node.c
#	lib/web.c
This commit is contained in:
Steffen Vogel 2017-08-27 18:44:03 +02:00
commit 5d0ba771c6
195 changed files with 6663 additions and 3635 deletions

View file

@ -1,7 +1,3 @@
*
!build/release/packaging/rpm/*
!thirdparty/libxil/
!thirdparty/criterion/
!thirdparty/libwebsockets/
!thirdparty/nanomsg/
!thirdparty/libzmq/
!thirdparty/

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
/build/
*~
.clang_complete

View file

@ -18,12 +18,12 @@
# 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.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
###################################################################################
@ -47,7 +47,7 @@ RUN dnf -y install \
jq \
iproute \
python-pip \
valgrind gdb \
valgrind gdb gdb-gdbserver \
xmlto rubygem-asciidoctor
# 32bit versions of some standard libraries for RT-LAB code
@ -70,6 +70,7 @@ RUN dnf -y install \
libnl3-devel \
libcurl-devel \
jansson-devel \
hdf5-devel \
libwebsockets-devel \
zeromq-devel \
nanomsg-devel \
@ -88,6 +89,5 @@ EXPOSE 443
ENV LD_LIBRARY_PATH /usr/local/lib:/usr/local/lib64
ENTRYPOINT villas
WORKDIR /villas
ENTRYPOINT bash

106
Dockerfile.dev-centos Normal file
View file

@ -0,0 +1,106 @@
# Dockerfile for VILLASnode development.
#
# This Dockerfile builds an image which contains all library dependencies
# and tools to build VILLASnode.
# However, VILLASnode itself it not part of the image.
#
# This image can be used for developing VILLASnode
# by running:
# make docker
#
# @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
# @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
# @license GNU General Public License (version 3)
#
# VILLASnode
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
###################################################################################
FROM centos:7
MAINTAINER Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
# Some of the dependencies are only available in our own repo
ADD https://villas.fein-aachen.org/packages/villas.repo /etc/yum.repos.d/
# Enable Extra Packages for Enterprise Linux (EPEL) and Software collection repo
RUN yum -y install epel-release centos-release-scl
# Toolchain
RUN yum -y install \
devtoolset-6-toolchain \
gcc gcc-c++ \
pkgconfig make cmake \
autoconf automake autogen libtool \
flex bison \
texinfo git curl tar
# Several tools only needed for developement and testing
RUN yum -y install \
doxygen dia graphviz \
openssh-clients \
rpmdevtools rpm-build \
jq \
iproute \
python-pip \
valgrind gdb gdb-gdbserver \
xmlto rubygem-asciidoctor
# 32bit versions of some standard libraries for RT-LAB code
RUN yum -y install \
libstdc++-devel.i686 \
libuuid-devel.i686 \
glibc-devel.i686
# Tools for debugging, coverage, profiling
RUN pip install \
gcovr
# Dependencies
RUN yum -y install \
openssl openssl-devel \
libconfig-devel \
libnl3-devel \
hdf5-devel \
zeromq-devel \
nanomsg-devel \
libxil-devel
# Build & Install Criterion
COPY thirdparty/criterion /tmp/criterion
RUN cd /tmp/criterion && cmake . && make install && rm -rf /tmp/*
# Build & Install Jansson
COPY thirdparty/jansson /tmp/jansson
RUN cd /tmp/jansson && cmake -DJANSSON_BUILD_DOCS=OFF . && make install && rm -rf /tmp/*
# Build & Install libwebsockets
COPY thirdparty/libwebsockets /tmp/libwebsockets
RUN cd /tmp/libwebsockets && cmake -DLWS_IPV6=1 -DLWS_WITH_STATIC=0 -DLWS_WITHOUT_TESTAPPS=1 -DLWS_WITH_HTTP2=1 . && make install && rm -rf /tmp/*
# Build & Install libcurl
COPY thirdparty/libcurl /tmp/libcurl
RUN cd /tmp/libcurl/ && ./buildconf && ./configure && make install && rm -rf /tmp/*
# Workaround for libnl3's search path for netem distributions
RUN ln -s /usr/lib64/tc /usr/lib/tc
# Expose ports for HTTP and WebSocket frontend
EXPOSE 80
EXPOSE 443
ENV LD_LIBRARY_PATH /usr/local/lib:/usr/local/lib64
WORKDIR /villas
ENTRYPOINT scl enable devtoolset-6 bash

View file

@ -18,12 +18,12 @@
# 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.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
###################################################################################
@ -50,7 +50,7 @@ RUN apt-get update && apt-get install -y \
jq \
iproute \
python-pip \
valgrind gdb \
valgrind gdb gdbserver \
xmlto asciidoctor
# 32bit versions of some standard libraries for RT-LAB code
@ -74,6 +74,7 @@ RUN apt-get update && apt-get install -y \
libnl-3-dev libnl-route-3-dev \
libcurl4-openssl-dev \
libjansson-dev \
libhdf5-dev \
libzmq3-dev \
libnanomsg-dev
@ -98,6 +99,5 @@ ENV LD_LIBRARY_PATH /usr/local/lib:/usr/local/lib64
# Workaround for libnl3's search path for netem distributions
RUN ln -s /usr/lib64/tc /usr/lib/tc
ENTRYPOINT villas
WORKDIR /villas
ENTRYPOINT bash

View file

@ -45,6 +45,9 @@ V ?= 2
# Platform
PLATFORM ?= $(shell uname)
# Enable supprot for libconfig configuration files
WITH_CONFIG ?= 1
# Common flags
LDLIBS =
CFLAGS += -I. -Iinclude -Iinclude/villas
@ -116,7 +119,11 @@ else
endif
# pkg-config dependencies
PKGS = libconfig openssl
PKGS = openssl jansson
ifeq ($(WITH_CONFIG),1)
PKGS += libconfig
endif
######## Targets ########
@ -148,7 +155,8 @@ CFLAGS += $(addprefix -DWITH_, $(call escape,$(PKGS)))
install: $(addprefix install-,$(filter-out thirdparty doc clients,$(MODULES)))
clean: $(addprefix clean-, $(filter-out thirdparty doc clients,$(MODULES)))
.PHONY: all everything clean install FORCE
.PHONY: all everything clean install
-include $(wildcard $(SRCDIR)/Makefile.*)
-include $(wildcard $(BUILDDIR)/**/*.d)
-include $(addsuffix /Makefile.inc,$(MODULES))
-include $(patsubst %,$(SRCDIR)/%/Makefile.inc,$(MODULES))

49
Makefile.complete Normal file
View file

@ -0,0 +1,49 @@
# Makefile for clang autocompletion
#
# This Makefile produces .clang_complete files containing compiler flags
# which are used by clang autocompletion tools such as:
#
# - https://atom.io/packages/autocomplete-clang
# - https://github.com/Rip-Rip/clang_complete
#
# @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
# @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
# @license GNU General Public License (version 3)
#
# VILLASnode
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
###################################################################################
SRCMODULES = src lib plugins tools tests/unit
CLANG_COMPLETES = $(patsubst %,$(SRCDIR)/%/.clang_complete,$(SRCMODULES))
tests/unit/.clang_complete: FLAGS = $(TEST_CFLAGS)
src/.clang_complete: FLAGS = $(SRC_CFLAGS)
tools/.clang_complete: FLAGS = $(TOOLS_CFLAGS)
plugins/.clang_complete: FLAGS = $(PLUGIN_CFLAGS)
lib/.clang_complete: FLAGS = $(LIB_CFLAGS)
%/.clang_complete:
echo "$(FLAGS)" > $@
clang-complete: $(CLANG_COMPLETES)
clean: clean-clang-complete
clean-clang-complete:
rm -f $(CLANG_COMPLETES)
.PHONY: clang-complete

View file

@ -22,8 +22,8 @@
etc:
install-etc:
install -D -t $(DESTDIR)/etc/villas/node etc/*.conf
install-etc: | $(DESTDIR)/etc/villas/node/
install -D -t $(DESTDIR)/etc/villas/node $(SRCDIR)/etc/*.conf
clean-etc:

View file

@ -193,7 +193,7 @@ nodes = {
signal_node = {
type = "signal",
signal = "sine", # One of "sine", "ramp", "triangle", "random", "mixed"
signal = "sine", # One of "sine", "square", "ramp", "triangle", "random", "mixed"
values = 4, # Number of values per sample
amplitude = 2.3, # Amplitude of generated signals
frequency = 10, # Frequency of generated signals
@ -204,6 +204,7 @@ nodes = {
type = "loopback", # A loopback node will receive exactly the same data which has been sent to it.
# The internal implementation is based on queue.
queuelen = 1024 # The queue length of the internal queue which buffers the samples.
samplelen = 64 # Each buffered sample can contain up to 64 values.
}
};

View file

@ -44,6 +44,11 @@ fpgas = {
vlnv = "xilinx.com:ip:axis_interconnect:2.1"
baseaddr = 0x0000;
numports = 3;
paths = (
{ in = "dma_0", out = "rtds_0" },
{ in = "rtds_0", out = "dma_0" }
)
},
rtds_0 = {
vlnv = "acs.eonerc.rwth-aachen.de:user:rtds_axis:1.0"
@ -57,12 +62,6 @@ fpgas = {
irq = 0
}
}
/* Configure switch_0 */
paths = (
{ in = "dma_0", out = "rtds_0" },
{ in = "rtds_0", out = "dma_0" }
)
}
}

View file

@ -56,6 +56,18 @@ fpgas = {
vlnv = "xilinx.com:ip:axis_interconnect:2.1"
baseaddr = 0x5000;
num_ports = 10;
paths = (
// { in = "fifo_mm_s_0", out = "fifo_mm_s_0" }, # Loopback fifo_mm_s_0
// { in = "dma_0", out = "dma_0" }, # Loopback dma_0
// { in = "dma_1", out = "dma_1" } # Loopback dma_1
// { in = "rtds_axis_0", out = "fifo_mm_s_0", reverse = true } # Linux <-> RTDS
// { in = "rtds_axis_0", out = "dma_0", reverse = true } # Linux (dma_0) <-> RTDS
{ in = "rtds_axis_0", out = "dma_1", reverse = true } # Linux (dma_1) <-> RTDS
// { in = "rtds_axis_0", out = "fifo_mm_s_0", reverse = true } # Linux (fifo_mm_s_0) <-> RTDS
// { in = "dma_0", out = "hls_dft_0", reverse = true } # DFT <-> Linux
// { in = "rtds_axis_0", out = "hls_dft_0", reverse = true }, # DFT <-> RTDS
)
},
axi_reset_0 = {
vlnv = "xilinx.com:ip:axi_gpio:2.0";
@ -117,21 +129,6 @@ fpgas = {
port = 6;
},
}
############ Switch config ############
# Requires a single IP core with VLNV:
# xilinx.com:ip:axis_interconnect
paths = (
// { in = "fifo_mm_s_0", out = "fifo_mm_s_0" }, # Loopback fifo_mm_s_0
// { in = "dma_0", out = "dma_0" }, # Loopback dma_0
// { in = "dma_1", out = "dma_1" } # Loopback dma_1
// { in = "rtds_axis_0", out = "fifo_mm_s_0", reverse = true } # Linux <-> RTDS
// { in = "rtds_axis_0", out = "dma_0", reverse = true } # Linux (dma_0) <-> RTDS
{ in = "rtds_axis_0", out = "dma_1", reverse = true } # Linux (dma_1) <-> RTDS
// { in = "rtds_axis_0", out = "fifo_mm_s_0", reverse = true } # Linux (fifo_mm_s_0) <-> RTDS
// { in = "dma_0", out = "hls_dft_0", reverse = true } # DFT <-> Linux
// { in = "rtds_axis_0", out = "hls_dft_0", reverse = true }, # DFT <-> RTDS
)
}
}

88
etc/js/config.js Normal file
View file

@ -0,0 +1,88 @@
/** Example Javascript config
*
* This example demonstrates how you can use Javascript code and NodeJS
* to script configuration files.
*
* To use this configuration, run the following command:
*
* villas node <(node /etc/villas/node/js/config.js)
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASnode
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
var glob = require('glob');
var global = require(__dirname + '/global.js')
/* List of plugins
*
* Additional node-types, hooks or VILLASfpga IP cores
* can be loaded by compiling them into a shared library and
* adding them to this list
*/
global.plugins = glob.sync('/usr?(/local)/share/villas/node/plugins/*.so');
global.nodes = {
loopback_node : {
vectorize : 1,
type : "loopback", // A loopback node will receive exactly the same data which has been sent to it.
// The internal implementation is based on queue.
queuelen : 10240 // The queue length of the internal queue which buffers the samples.
},
socket_node : {
type : "socket",
local : "*:12000",
remote : "127.0.0.1:12000"
}
};
global.paths = [
{
in : "test_node",
out : "socket_node",
queuelen : 10000
},
{
in : "socket_node",
out : "test_node",
queuelen : 10000,
hooks : [
{
type : "stats",
warmup : 100,
verbose : true,
format : "human",
output : "./stats.log"
},
{
type : "convert"
}
]
}
];
// Convert Javascript Object to JSON string
var json = JSON.stringify(global, null, 4);
// Some log message
process.stderr.write('Configuration file successfully generated\n');
// Print JSON to stdout
process.stdout.write(json);

65
etc/js/global.js Normal file
View file

@ -0,0 +1,65 @@
/** Global configuration file for VILLASnode.
*
* The syntax of this file is similar to JSON.
* A detailed description of the format can be found here:
* http://www.hyperrealm.com/libconfig/libconfig_manual.html//Configuration-Files
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASnode
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
os = require('os');
var logfile = "/var/log/villas-node_" + new Date().toISOString() + ".log"
module.exports = {
affinity : 0x01, // Mask of cores the server should run on
// This also maps the NIC interrupts to those cores!
priority : 50, // Priority for the server tasks.
// Usually the server is using a real-time FIFO
// scheduling algorithm
// See: https://github.com/docker/docker/issues/22380
// on why we cant use real-time scheduling in Docker
stats : 3, // The interval in seconds to print path statistics.
// A value of 0 disables the statistics.
name : os.hostname(), // The name of this VILLASnode. Might by used by node-types
// to identify themselves (default is the hostname).
log : {
level : 5, // The level of verbosity for debug messages
// Higher number => increased verbosity
faciltities : [ "path", "socket" ], // The list of enabled debug faciltities.
// If omitted, all faciltities are enabled
// For a full list of available faciltities, check lib/log.c
file : logfile, // File for logs
},
http : {
enabled : true, // Do not listen on port if true
htdocs : "/villas/web/socket/", // Root directory of internal webserver
port : 80 // Port for HTTP connections
}
};

View file

@ -47,6 +47,7 @@ typedef struct advio AFILE;
/* The remaining functions from stdio are just replaced macros */
#define afeof(af) feof((af)->file)
#define afgets(ln, sz, af) fgets(ln, sz, (af)->file)
#define aftell(af) ftell((af)->file)
#define afileno(af) fileno((af)->file)
#define afread(ptr, sz, nitems, af) fread(ptr, sz, nitems, (af)->file)
@ -60,6 +61,9 @@ typedef struct advio AFILE;
#define auri(af) ((af)->uri)
#define ahash(af) ((af)->hash)
/** Check if a URI is pointing to a local file. */
int aislocal(const char *uri);
AFILE *afopen(const char *url, const char *mode);
int afclose(AFILE *file);
@ -71,7 +75,6 @@ int afseek(AFILE *file, long offset, int origin);
void arewind(AFILE *file);
/** Download contens from remote file
*
*
* @param resume Do a partial download and append to the local file
*/

View file

@ -25,9 +25,11 @@
#include <libwebsockets.h>
#include <jansson.h>
#include <pthread.h>
#include "list.h"
#include "common.h"
#include "queue_signalled.h"
#include "api/session.h"
@ -48,10 +50,13 @@ struct api_action;
typedef int (*api_cb_t)(struct api_action *c, json_t *args, json_t **resp, struct api_session *s);
struct api {
struct list sessions; /**< List of currently active connections */
enum state state;
struct list sessions; /**< List of currently active connections */
struct queue_signalled pending; /**< A queue of api_sessions which have pending requests. */
pthread_t thread;
struct super_node *super_node;
};

View file

@ -27,7 +27,8 @@
#include <jansson.h>
#include "common.h"
#include "web/buffer.h"
#include "queue.h"
#include "buffer.h"
enum api_version {
API_VERSION_UNKOWN = 0,
@ -41,34 +42,28 @@ enum api_mode {
/** A connection via HTTP REST or WebSockets to issue API actions. */
struct api_session {
enum api_mode mode;
enum state state;
enum api_version version;
enum api_mode mode;
int runs;
struct {
struct web_buffer body; /**< HTTP body / WS payload */
} request;
struct {
struct web_buffer body; /**< HTTP body / WS payload */
struct web_buffer headers; /**< HTTP headers */
} response;
struct {
char name[64];
char ip[64];
} peer;
bool completed; /**< Did we receive the complete body yet? */
enum state state;
struct buffer buffer;
struct queue queue;
} request, response;
struct lws *wsi;
struct api *api;
char *_name;
};
int api_session_init(struct api_session *s, struct api *a, enum api_mode m);
int api_session_init(struct api_session *s, enum api_mode m);
int api_session_destroy(struct api_session *s);
int api_session_run_command(struct api_session *s, json_t *req, json_t **resp);
char * api_session_name(struct api_session *s);

50
include/villas/buffer.h Normal file
View file

@ -0,0 +1,50 @@
/** A simple growing buffer.
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASnode
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#pragma once
#include <stdlib.h>
#include <jansson.h>
#include "common.h"
struct buffer {
enum state state;
char *buf;
size_t len;
size_t size;
};
int buffer_init(struct buffer *b, size_t size);
int buffer_destroy(struct buffer *b);
void buffer_clear(struct buffer *b);
int buffer_append(struct buffer *b, const char *data, size_t len);
int buffer_parse_json(struct buffer *b, json_t **j);
int buffer_append_json(struct buffer *b, json_t *j);

View file

@ -31,11 +31,15 @@ size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags);
#define le16toh(x) OSSwapLittleToHostInt16(x)
#define le32toh(x) OSSwapLittleToHostInt32(x)
#define le64toh(x) OSSwapLittleToHostInt64(x)
#define be16toh(x) OSSwapBigToHostInt16(x)
#define be32toh(x) OSSwapBigToHostInt32(x)
#define be64toh(x) OSSwapBigToHostInt64(x)
#define htole16(x) OSSwapHostToLittleInt16(x)
#define htole32(x) OSSwapHostToLittleInt32(x)
#define htole64(x) OSSwapHostToLittleInt64(x)
#define htobe16(x) OSSwapHostToBigInt16(x)
#define htobe32(x) OSSwapHostToBigInt32(x)
#define htobe64(x) OSSwapHostToBigInt64(x)
#endif /* __MACH__ */

View file

@ -33,5 +33,12 @@ json_t * config_to_json(config_setting_t *cfg);
/* Convert a jansson object into a libconfig object. */
int json_to_config(json_t *json, config_setting_t *parent);
/* Create a libconfig object from command line parameters. */
int config_read_cli(config_t *cfg, int argc, char *argv[]);
/* Create a JSON object from command line parameters. */
json_t * json_load_cli(int argc, char *argv[]);
int json_object_extend_str(json_t *orig, const char *str);
void json_object_extend_key_value(json_t *obj, const char *key, const char *value);
/* Merge two JSON objects recursively. */
int json_object_extend(json_t *orig, json_t *merge);

View file

@ -29,8 +29,6 @@
#pragma once
#include <libconfig.h>
#include "common.h"
#include "kernel/pci.h"
#include "kernel/vfio.h"
@ -64,17 +62,15 @@ struct fpga_card {
struct fpga_ip *intc;
struct fpga_ip *reset;
struct fpga_ip *sw;
config_setting_t *cfg;
};
/** Initialize FPGA card and its IP components. */
int fpga_card_init(struct fpga_card *c, struct pci *pci, struct vfio_container *vc);
/** Parse configuration of FPGA card including IP cores from config. */
int fpga_card_parse(struct fpga_card *c, config_setting_t *cfg);
int fpga_card_parse(struct fpga_card *c, json_t *cfg, const char *name);
int fpga_card_parse_list(struct list *l, config_setting_t *cfg);
int fpga_card_parse_list(struct list *l, json_t *cfg);
/** Check if the FPGA card configuration is plausible. */
int fpga_card_check(struct fpga_card *c);

View file

@ -30,7 +30,6 @@
#pragma once
#include <stdint.h>
#include <libconfig.h>
#include "common.h"
@ -60,7 +59,7 @@ struct fpga_ip_type {
enum fpga_ip_types type;
int (*init)(struct fpga_ip *c);
int (*parse)(struct fpga_ip *c);
int (*parse)(struct fpga_ip *c, json_t *cfg);
int (*check)(struct fpga_ip *c);
int (*start)(struct fpga_ip *c);
int (*stop)(struct fpga_ip *c);
@ -87,15 +86,13 @@ struct fpga_ip {
int irq; /**< The interrupt number of the FPGA IP component. */
struct fpga_card *card; /**< The FPGA to which this IP instance belongs to. */
config_setting_t *cfg;
};
/** Initialize IP core. */
int fpga_ip_init(struct fpga_ip *c, struct fpga_ip_type *vt);
/** Parse IP core configuration from configuration file */
int fpga_ip_parse(struct fpga_ip *c, config_setting_t *cfg);
int fpga_ip_parse(struct fpga_ip *c, json_t *cfg, const char *name);
/** Check configuration of IP core. */
int fpga_ip_check(struct fpga_ip *c);

View file

@ -25,7 +25,7 @@ struct dft {
int decimation;
};
int dft_parse(struct fpga_ip *c);
int dft_parse(struct fpga_ip *c, json_t *cfg);
int dft_start(struct fpga_ip *c);

View file

@ -33,20 +33,20 @@ enum model_xsg_block_type {
XSG_BLOCK_INFO = 0x2000
};
enum model_param_type {
MODEL_PARAM_TYPE_UFIX,
MODEL_PARAM_TYPE_FIX,
MODEL_PARAM_TYPE_FLOAT,
MODEL_PARAM_TYPE_BOOLEAN
enum model_parameter_type {
MODEL_PARAMETER_TYPE_UFIX,
MODEL_PARAMETER_TYPE_FIX,
MODEL_PARAMETER_TYPE_FLOAT,
MODEL_PARAMETER_TYPE_BOOLEAN
};
enum model_param_direction {
MODEL_PARAM_IN,
MODEL_PARAM_OUT,
MODEL_PARAM_INOUT
enum model_parameter_direction {
MODEL_PARAMETER_IN,
MODEL_PARAMETER_OUT,
MODEL_PARAMETER_INOUT
};
union model_param_value {
union model_parameter_value {
uint32_t ufix;
int32_t fix;
float flt;
@ -75,16 +75,16 @@ struct model_info {
char *value;
};
struct model_param {
struct model_parameter {
char *name; /**< Name of the parameter */
enum model_param_direction direction; /**< Read / Write / Read-write? */
enum model_param_type type; /**< Data type. Integers are represented by MODEL_GW_TYPE_(U)FIX with model_gw::binpt == 0 */
enum model_parameter_direction direction; /**< Read / Write / Read-write? */
enum model_parameter_type type; /**< Data type. Integers are represented by MODEL_GW_TYPE_(U)FIX with model_gw::binpt == 0 */
int binpt; /**< Binary point for type == MODEL_GW_TYPE_(U)FIX */
uintptr_t offset; /**< Register offset to model::baseaddress */
union model_param_value default_value;
union model_parameter_value default_value;
struct fpga_ip *ip; /**< A pointer to the model structure to which this parameters belongs to. */
};
@ -93,7 +93,7 @@ struct model_param {
int model_init(struct fpga_ip *c);
/** Parse model */
int model_parse(struct fpga_ip *c);
int model_parse(struct fpga_ip *c, json_t *cfg);
/** Destroy a model */
int model_destroy(struct fpga_ip *c);
@ -102,17 +102,17 @@ int model_destroy(struct fpga_ip *c);
void model_dump(struct fpga_ip *c);
/** Add a new parameter to the model */
void model_param_add(struct fpga_ip *c, const char *name, enum model_param_direction dir, enum model_param_type type);
void model_parameter_add(struct fpga_ip *c, const char *name, enum model_parameter_direction dir, enum model_parameter_type type);
/** Remove an existing parameter by its name */
int model_param_remove(struct fpga_ip *c, const char *name);
int model_parameter_remove(struct fpga_ip *c, const char *name);
/** Read a model parameter.
*
* Note: the data type of the register is taken into account.
* All datatypes are converted to double.
*/
int model_param_read(struct model_param *p, double *v);
int model_parameter_read(struct model_parameter *p, double *v);
/** Update a model parameter.
*
@ -120,8 +120,8 @@ int model_param_read(struct model_param *p, double *v);
* The double argument will be converted to the respective data type of the
* GatewayIn/Out block.
*/
int model_param_write(struct model_param *p, double v);
int model_parameter_write(struct model_parameter *p, double v);
int model_param_update(struct model_param *p, struct model_param *u);
int model_parameter_update(struct model_parameter *p, struct model_parameter *u);
/** @} */

View file

@ -13,6 +13,7 @@
#pragma once
#include <jansson.h>
#include <xilinx/xaxis_switch.h>
#include "list.h"
@ -41,7 +42,7 @@ int switch_init_paths(struct fpga_ip *c);
int switch_destroy(struct fpga_ip *c);
int switch_parse(struct fpga_ip *c);
int switch_parse(struct fpga_ip *c, json_t *cfg);
int switch_connect(struct fpga_ip *c, struct fpga_ip *mi, struct fpga_ip *si);

View file

@ -93,12 +93,8 @@ char * hist_dump(struct hist *h);
/** Prints Matlab struct containing all infos to file. */
int hist_dump_matlab(struct hist *h, FILE *f);
#ifdef WITH_JSON
/** Write the histogram in JSON format to fiel \p f. */
int hist_dump_json(struct hist *h, FILE *f);
/** Build a libjansson / JSON object of the histogram. */
json_t * hist_json(struct hist *h);
#endif /* WITH_JSON */

View file

@ -52,21 +52,13 @@ struct hook {
void *_vd; /**< Private data for this hook. This pointer can be used to pass data between consecutive calls of the callback. */
int priority; /**< A priority to change the order of execution within one type of hook. */
json_t *cfg; /**< A JSON object containing the configuration of the hook. */
};
/** Save references to global nodes, paths and settings */
int hook_init(struct hook *h, struct hook_type *vt, struct path *p);
/** Parse a single hook.
*
* A hook definition is composed of the hook name and optional parameters
* seperated by a colon.
*
* Examples:
* "print:stdout"
*/
int hook_parse(struct hook *h, config_setting_t *cfg);
int hook_parse(struct hook *h, json_t *cfg);
int hook_parse_cli(struct hook *h, int argc, char *argv[]);
int hook_destroy(struct hook *h);
@ -77,11 +69,11 @@ int hook_stop(struct hook *h);
int hook_periodic(struct hook *h);
int hook_restart(struct hook *h);
int hook_read(struct hook *h, struct sample *smps[], size_t *cnt);
int hook_write(struct hook *h, struct sample *smps[], size_t *cnt);
int hook_read(struct hook *h, struct sample *smps[], unsigned *cnt);
int hook_write(struct hook *h, struct sample *smps[], unsigned *cnt);
size_t hook_read_list(struct list *hs, struct sample *smps[], size_t cnt);
size_t hook_write_list(struct list *hs, struct sample *smps[], size_t cnt);
int hook_read_list(struct list *hs, struct sample *smps[], unsigned cnt);
int hook_write_list(struct list *hs, struct sample *smps[], unsigned cnt);
/** Compare two hook functions with their priority. Used by list_sort() */
int hook_cmp_priority(const void *a, const void *b);
@ -100,4 +92,4 @@ int hook_cmp_priority(const void *a, const void *b);
* hooks = [ "print" ]
* }
*/
int hook_parse_list(struct list *list, config_setting_t *cfg, struct path *p);
int hook_parse_list(struct list *list, json_t *cfg, struct path *p);

View file

@ -37,7 +37,7 @@
#include <stdlib.h>
#include <stdbool.h>
#include <libconfig.h>
#include <jansson.h>
/* Forward declarations */
struct hook;
@ -49,7 +49,7 @@ struct hook_type {
size_t size; /**< Size of allocation for struct hook::_vd */
int (*parse)(struct hook *h, config_setting_t *cfg);
int (*parse)(struct hook *h, json_t *cfg);
int (*parse_cli)(struct hook *h, int argc, char *argv[]);
int (*init)(struct hook *h); /**< Called before path is started to parseHOOK_DESTROYs. */
@ -61,6 +61,6 @@ struct hook_type {
int (*periodic)(struct hook *h);/**< Called periodically. Period is set by global 'stats' option in the configuration file. */
int (*restart)(struct hook *h); /**< Called whenever a new simulation case is started. This is detected by a sequence no equal to zero. */
int (*read)(struct hook *h, struct sample *smps[], size_t *cnt); /**< Called for every single received samples. */
int (*write)(struct hook *h, struct sample *smps[], size_t *cnt); /**< Called for every single sample which will be sent. */
int (*read)(struct hook *h, struct sample *smps[], unsigned *cnt); /**< Called for every single received samples. */
int (*write)(struct hook *h, struct sample *smps[], unsigned *cnt); /**< Called for every single sample which will be sent. */
};

98
include/villas/io.h Normal file
View file

@ -0,0 +1,98 @@
/** Read / write sample data in different formats.
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASnode
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#pragma once
#include "advio.h"
#include "common.h"
/* Forward declarations */
struct sample;
struct io_format;
enum io_flags {
IO_FLUSH = (1 << 8) /**< Flush the output stream after each chunk of samples. */
};
struct io {
enum state state;
int flags;
enum {
IO_MODE_STDIO,
IO_MODE_ADVIO,
IO_MODE_CUSTOM
} mode;
/** A format type can use this file handle or overwrite the
* format::{open,close,eof,rewind} functions and the private
* data in io::_vd.
*/
union {
struct {
FILE *input;
FILE *output;
} stdio;
struct {
AFILE *input;
AFILE *output;
} advio;
};
struct {
char *input;
char *output;
} buffer;
void *_vd;
struct io_format *_vt;
};
int io_init(struct io *io, struct io_format *fmt, int flags);
int io_destroy(struct io *io);
int io_open(struct io *io, const char *uri);
int io_close(struct io *io);
int io_print(struct io *io, struct sample *smps[], unsigned cnt);
int io_scan(struct io *io, struct sample *smps[], unsigned cnt);
int io_eof(struct io *io);
void io_rewind(struct io *io);
int io_flush(struct io *io);
int io_stream_open(struct io *io, const char *uri);
int io_stream_close(struct io *io);
int io_stream_eof(struct io *io);
void io_stream_rewind(struct io *io);
int io_stream_flush(struct io *io);

View file

@ -1,4 +1,4 @@
/** Message related functions
/** Comma-separated values.
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
@ -23,29 +23,13 @@
#pragma once
#include <stdio.h>
/* Forward declarations. */
struct webmsg;
struct sample;
/** Swaps the byte-order of the message.
*
* Message are always transmitted in network (big endian) byte order.
*
* @param m A pointer to the message
*/
void webmsg_hdr_ntoh(struct webmsg *m);
#define CSV_SEPARATOR '\t'
void webmsg_hdr_hton(struct webmsg *m);
int csv_fprint(FILE *f, struct sample *smps[], unsigned cnt, int flags);
void webmsg_ntoh(struct webmsg *m);
void webmsg_hton(struct webmsg *m);
/** Check the consistency of a message.
*
* The functions checks the header fields of a message.
*
* @param m A pointer to the message
* @retval 0 The message header is valid.
* @retval <0 The message header is invalid.
*/
int msg_verify(struct webmsg *m);
int csv_fscan(FILE *f, struct sample *smps[], unsigned cnt, int *flags);

View file

@ -26,10 +26,10 @@
#include "sample.h"
int sample_io_json_pack(json_t **j, struct sample *s, int flags);
int json_pack_sample(json_t **j, struct sample *s, int flags);
int sample_io_json_unpack(json_t *j, struct sample *s, int *flags);
int json_unpack_sample(json_t *j, struct sample *s, int *flags);
int sample_io_json_fprint(FILE *f, struct sample *s, int flags);
int json_fprint(FILE *f, struct sample *smps[], unsigned cnt, int flags);
int sample_io_json_fscan(FILE *f, struct sample *s, int *flags);
int json_fscan(FILE *f, struct sample *smps[], unsigned cnt, int *flags);

View file

@ -23,12 +23,16 @@
#pragma once
#include <stdlib.h>
/* Forward declarations. */
struct msg;
struct sample;
struct io;
/** The maximum length of a packet which contains stuct msg. */
#define MSG_MAX_PACKET_LEN 1500
enum msg_flags {
MSG_WEB = (1 << 16) /**< Use webmsg format (everying little endian) */
};
/** Swaps the byte-order of the message.
*
@ -60,9 +64,8 @@ int msg_to_sample(struct msg *msg, struct sample *smp);
/** Copy fields form \p smp into \p msg. */
int msg_from_sample(struct msg *msg, struct sample *smp);
/** Copy / read struct msg's from buffer \p buf to / fram samples \p smps. */
ssize_t msg_buffer_from_samples(struct sample *smps[], unsigned cnt, char *buf, size_t len);
int msg_sprint(char *buf, size_t len, size_t *wbytes, struct sample *smps[], unsigned cnt, int flags);
/** Read struct sample's from buffer \p buf into samples \p smps. */
int msg_buffer_to_samples(struct sample *smps[], unsigned cnt, char *buf, size_t len);
int msg_sscan(char *buf, size_t len, size_t *rbytes, struct sample *smps[], unsigned cnt, int *flags);

60
include/villas/io/raw.h Normal file
View file

@ -0,0 +1,60 @@
/** RAW IO format
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASnode
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#pragma once
#include <stdlib.h>
/* Forward declarations */
struct sample;
enum raw_flags {
RAW_FAKE = (1 << 16), /**< Treat the first three values as: sequenceno, seconds, nanoseconds */
RAW_BE_INT = (1 << 17), /**< Byte-order for integer data: big-endian if set. */
RAW_BE_FLT = (1 << 18), /**< Byte-order for floating point data: big-endian if set. */
RAW_BE_HDR = (1 << 19), /**< Byte-order for fake header fields: big-endian if set. */
/** Byte-order for all fields: big-endian if set. */
RAW_BE = RAW_BE_INT | RAW_BE_FLT | RAW_BE_HDR,
/** Mix floating and integer types.
*
* io_raw_sscan() parses all values as single / double precission fp.
* io_raw_sprint() uses sample::format to determine the type.
*/
RAW_AUTO = (1 << 22),
RAW_FLT = (1 << 23), /**< Data-type: floating point otherwise integer. */
//RAW_1 = (0 << 24), /**< Pack each value as a single bit. */
RAW_8 = (3 << 24), /**< Pack each value as a byte. */
RAW_16 = (4 << 24), /**< Pack each value as a word. */
RAW_32 = (5 << 24), /**< Pack each value as a double word. */
RAW_64 = (6 << 24) /**< Pack each value as a quad word. */
};
/** Copy / read struct msg's from buffer \p buf to / fram samples \p smps. */
int raw_sprint(char *buf, size_t len, size_t *wbytes, struct sample *smps[], unsigned cnt, int flags);
/** Read struct sample's from buffer \p buf into samples \p smps. */
int raw_sscan(char *buf, size_t len, size_t *rbytes, struct sample *smps[], unsigned cnt, int *flags);

View file

@ -0,0 +1,36 @@
/** The VILLASframework sample format
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASnode
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#pragma once
#include <stdio.h>
#include "io.h"
int villas_print(struct io *io, struct sample *smps[], unsigned cnt);
int villas_scan(struct io *io, struct sample *smps[], unsigned cnt);
int villas_fprint(FILE *f, struct sample *smps[], unsigned cnt, int flags);
int villas_fscan(FILE *f, struct sample *smps[], unsigned cnt, int *flags);

146
include/villas/io_format.h Normal file
View file

@ -0,0 +1,146 @@
/** Read / write sample data in different formats.
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASnode
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#pragma once
#include <stdio.h>
/* Forward declarations */
struct sample;
struct io;
enum io_format_flags {
IO_FORMAT_NANOSECONDS = (1 << 0), /**< Include nanoseconds in output. */
IO_FORMAT_OFFSET = (1 << 1), /**< Include offset / delta between received and send timestamps. */
IO_FORMAT_SEQUENCE = (1 << 2), /**< Include sequence number in output. */
IO_FORMAT_VALUES = (1 << 3), /**< Include values in output. */
IO_FORMAT_ALL = 15, /**< Enable all output options. */
IO_FORMAT_BINARY = (1 << 8)
};
struct io_format {
int (*init)(struct io *io);
int (*destroy)(struct io *io);
/** @{
* High-level interface
*/
/** Open an IO stream.
*
* @see fopen()
*/
int (*open)(struct io *io, const char *uri);
/** Close an IO stream.
*
* @see fclose()
*/
int (*close)(struct io *io);
/** Check if end-of-file was reached.
*
* @see feof()
*/
int (*eof)(struct io *io);
/** Rewind an IO stream.
*
* @see rewind()
*/
void (*rewind)(struct io *io);
/** Flush buffered data to disk.
*
* @see fflush()
*/
int (*flush)(struct io *io);
int (*print)(struct io *io, struct sample *smps[], unsigned cnt);
int (*scan)( struct io *io, struct sample *smps[], unsigned cnt);
/** @} */
/** @{
* Low-level interface
*/
/** @see io_format_sscan */
int (*sscan)(char *buf, size_t len, size_t *rbytes, struct sample *smps[], unsigned cnt, int *flags);
/** @see io_format_sprint */
int (*sprint)(char *buf, size_t len, size_t *wbytes, struct sample *smps[], unsigned cnt, int flags);
/** @see io_format_fscan */
int (*fscan)(FILE *f, struct sample *smps[], unsigned cnt, int *flags);
/** @see io_format_fprint */
int (*fprint)(FILE *f, struct sample *smps[], unsigned cnt, int flags);
/** @} */
size_t size; /**< Number of bytes to allocate for io::_vd */
int flags; /**< A set of flags which is automatically used. */
};
struct io_format * io_format_lookup(const char *name);
/** Parse samples from the buffer \p buf with a length of \p len bytes.
*
* @param buf[in] The buffer of data which should be parsed / de-serialized.
* @param len[in] The length of the buffer \p buf.
* @param rbytes[out] The number of bytes which have been read from \p buf.
* @param smps[out] The array of pointers to samples.
* @param cnt[in] The number of pointers in the array \p smps.
*
* @retval >=0 The number of samples which have been parsed from \p buf and written into \p smps.
* @retval <0 Something went wrong.
*/
int io_format_sscan(struct io_format *fmt, char *buf, size_t len, size_t *rbytes, struct sample *smps[], unsigned cnt, int *flags);
/** Print \p cnt samples from \p smps into buffer \p buf of length \p len.
*
* @param buf[out] The buffer which should be filled with serialized data.
* @param len[in] The length of the buffer \p buf.
* @param rbytes[out] The number of bytes which have been written to \p buf. Ignored if NULL.
* @param smps[in] The array of pointers to samples.
* @param cnt[in] The number of pointers in the array \p smps.
*
* @retval >=0 The number of samples from \p smps which have been written into \p buf.
* @retval <0 Something went wrong.
*/
int io_format_sprint(struct io_format *fmt, char *buf, size_t len, size_t *wbytes, struct sample *smps[], unsigned cnt, int flags);
/** Parse up to \p cnt samples from stream \p f into array \p smps.
*
* @retval >=0 The number of samples which have been parsed from \p f and written into \p smps.
* @retval <0 Something went wrong.
*/
int io_format_fscan(struct io_format *fmt, FILE *f, struct sample *smps[], unsigned cnt, int *flags);
/** Print \p cnt samples from \p smps to stream \p f.
*
* @retval >=0 The number of samples from \p smps which have been written to \p f.
* @retval <0 Something went wrong.
*/
int io_format_fprint(struct io_format *fmt, FILE *f, struct sample *smps[], unsigned cnt, int flags);

View file

@ -1,5 +1,6 @@
/** Linux specific real-time optimizations
*
* @see: https://wiki.linuxfoundation.org/realtime/documentation/howto/applications/application_base
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
@ -31,6 +32,8 @@ int rt_set_affinity(int affinity);
int rt_set_priority(int priority);
int rt_lock_memory();
/** Checks for realtime (PREEMPT_RT) patched kernel.
*
* See https://rt.wiki.kernel.org

View file

@ -35,7 +35,7 @@
#include <netlink/route/qdisc.h>
#include <netlink/route/classifier.h>
#include <libconfig.h>
#include <jansson.h>
typedef uint32_t tc_hdl_t;
@ -43,12 +43,12 @@ struct interface;
/** Parse network emulator (netem) settings.
*
* @param cfg A libconfig object containing the settings.
* @param cfg A jansson object containing the settings.
* @param[out] ne A pointer to a libnl3 qdisc object where setting will be written to.
* @retval 0 Success. Everything went well.
* @retval <0 Error. Something went wrong.
*/
int tc_parse(config_setting_t *cfg, struct rtnl_qdisc **ne);
int tc_parse(struct rtnl_qdisc **ne, json_t *cfg);
/** Print network emulator (netem) setting into buffer.
*

View file

@ -98,6 +98,7 @@ struct log {
int level;
long facilities; /**< Debug facilities used by the debug() macro. */
const char *path; /**< Path of the log file. */
char *prefix; /**< Prefix each line with this string. */
FILE *file; /**< Send all log output to this file / stdout / stderr. */
};

View file

@ -1,4 +1,4 @@
/** Logging routines that depend on libconfig.
/** Logging routines that depend on jansson.
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
@ -25,14 +25,13 @@
struct log;
#include <libconfig.h>
#include <jansson.h>
#include "log.h"
/** Parse logging configuration. */
int log_parse(struct log *l, config_setting_t *cfg);
int log_parse(struct log *l, json_t *cfg);
/** Print configuration error and exit. */
void cerror(config_setting_t *cfg, const char *fmt, ...)
void jerror(json_error_t *err, const char *fmt, ...)
__attribute__ ((format(printf, 2, 3)));

View file

@ -23,7 +23,7 @@
#pragma once
#include <libconfig.h>
#include <jansson.h>
#include "stats.h"
#include "common.h"
@ -87,7 +87,7 @@ struct mapping {
int mapping_init(struct mapping *m);
int mapping_parse(struct mapping *m, config_setting_t *cfg);
int mapping_parse(struct mapping *m, json_t *cfg);
int mapping_check(struct mapping *m);
@ -95,6 +95,6 @@ int mapping_destroy(struct mapping *m);
int mapping_remap(struct mapping *m, struct sample *orig, struct sample *remapped, struct stats *s);
int mapping_entry_parse(struct mapping_entry *e, config_setting_t *cfg);
int mapping_entry_parse(struct mapping_entry *e, json_t *cfg);
int mapping_entry_parse_str(struct mapping_entry *e, const char *str);

View file

@ -28,7 +28,7 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <libconfig.h>
#include <jansson.h>
#include "node_type.h"
#include "sample.h"
@ -43,8 +43,7 @@
*/
struct node
{
const char *name; /**< A short identifier of the node, only used for configuration and logging */
char *name; /**< A short identifier of the node, only used for configuration and logging */
char *_name; /**< Singleton: A string used to print to screen. */
char *_name_long; /**< Singleton: A string used to print to screen. */
@ -60,18 +59,18 @@ struct node
struct node_type *_vt; /**< Virtual functions (C++ OOP style) */
void *_vd; /**< Virtual data (used by struct node::_vt functions) */
config_setting_t *cfg; /**< A pointer to the libconfig object which instantiated this node */
json_t *cfg; /**< A JSON object containing the configuration of the node. */
};
int node_init(struct node *n, struct node_type *vt);
/** Parse settings of a node.
*
* @param cfg A libconfig object pointing to the node.
* @param cfg A JSON object containing the configuration of the node.
* @retval 0 Success. Everything went well.
* @retval <0 Error. Something went wrong.
*/
int node_parse(struct node *n, config_setting_t *cfg);
int node_parse(struct node *n, json_t *cfg, const char *name);
/** Parse settings of a node from cmdline. */
int node_parse_cli(struct node *n, int argc, char *argv[]);
@ -131,10 +130,10 @@ int node_write(struct node *n, struct sample *smps[], unsigned cnt);
* out = [ "sintef", "scedu" ]
* out = "acs"
*
* @param cfg The libconfig object handle for "out".
* @param cfg A JSON array or string. See examples above.
* @param nodes The nodes will be added to this list.
* @param all This list contains all valid nodes.
*/
int node_parse_list(struct list *list, config_setting_t *cfg, struct list *all);
int node_parse_list(struct list *list, json_t *cfg, struct list *all);
/** @} */

View file

@ -26,7 +26,7 @@
#pragma once
#include <libconfig.h>
#include <jansson.h>
#include "list.h"
#include "common.h"
@ -78,11 +78,11 @@ struct node_type {
/** Parse node connection details.
*
* @param n A pointer to the node object.
* @param cfg A libconfig object pointing to the node.
* @param cfg A JSON object containing the configuration of the node.
* @retval 0 Success. Everything went well.
* @retval <0 Error. Something went wrong.
*/
int (*parse)(struct node *n, config_setting_t *cfg);
int (*parse)(struct node *n, json_t *cfg);
/** Parse node from command line arguments. */
int (*parse_cli)(struct node *n, int argc, char *argv[]);
@ -118,10 +118,10 @@ struct node_type {
* Indexes used to address @p m will wrap around after len messages.
* Some node-types might only support to receive one message at a time.
*
* @param n A pointer to the node object.
* @param smps An array of pointers to memory blocks where the function should store received samples.
* @param cnt The number of messages which should be received.
* @return The number of messages actually received.
* @param n A pointer to the node object.
* @param smps An array of pointers to memory blocks where the function should store received samples.
* @param cnt The number of messages which should be received.
* @return The number of messages actually received.
*/
int (*read) (struct node *n, struct sample *smps[], unsigned cnt);
@ -132,10 +132,10 @@ struct node_type {
* The messages have to be stored in a circular buffer / array m.
* So the indexes will wrap around after len.
*
* @param n A pointer to the node object.
* @param smps An array of pointers to memory blocks where samples read from.
* @param cnt The number of messages which should be sent.
* @return The number of messages actually sent.
* @param n A pointer to the node object.
* @param smps An array of pointers to memory blocks where samples read from.
* @param cnt The number of messages which should be sent.
* @return The number of messages actually sent.
*/
int (*write)(struct node *n, struct sample *smps[], unsigned cnt);
@ -143,7 +143,7 @@ struct node_type {
*
* This is not supported by all node-types!
*
* @param n A pointer to the node object.
* @param n A pointer to the node object.
*/
int (*reverse)(struct node *n);
};

View file

@ -31,54 +31,48 @@
#pragma once
#include "advio.h"
#include "io.h"
#include "node.h"
#include "task.h"
#define FILE_MAX_PATHLEN 512
enum {
FILE_READ,
FILE_WRITE
};
struct file {
struct file_direction {
AFILE *handle; /**< libc: stdio file handle. */
struct io io; /**< Format and file IO */
struct io_format *format;
const char *mode; /**< libc: fopen() mode. */
const char *fmt; /**< Format string for file name. */
char *uri; /**< Real file name. */
} read, write;
char *uri_tmpl; /**< Format string for file name. */
char *uri; /**< Real file name. */
char *mode; /**< File access mode. */
int flush; /**< Flush / upload file contents after each write. */
struct task task; /**< Timer file descriptor. Blocks until 1 / rate seconds are elapsed. */
double rate; /**< The read rate. */
enum read_epoch_mode {
enum epoch_mode {
FILE_EPOCH_DIRECT,
FILE_EPOCH_WAIT,
FILE_EPOCH_RELATIVE,
FILE_EPOCH_ABSOLUTE,
FILE_EPOCH_ORIGINAL
} read_epoch_mode; /**< Specifies how file::offset is calculated. */
struct timespec read_first; /**< The first timestamp in the file file::{read,write}::uri */
struct timespec read_epoch; /**< The epoch timestamp from the configuration. */
struct timespec read_offset; /**< An offset between the timestamp in the input file and the current time */
} epoch_mode; /**< Specifies how file::offset is calculated. */
enum {
FILE_EOF_EXIT, /**< Terminate when EOF is reached. */
FILE_EOF_REWIND, /**< Rewind the file when EOF is reached. */
FILE_EOF_WAIT /**< Blocking wait when EOF is reached. */
} read_eof; /**< Should we rewind the file when we reach EOF? */
int read_timer; /**< Timer file descriptor. Blocks until 1 / rate seconds are elapsed. */
double read_rate; /**< The read rate. */
} eof;
struct timespec first; /**< The first timestamp in the file file::{read,write}::uri */
struct timespec epoch; /**< The epoch timestamp from the configuration. */
struct timespec offset; /**< An offset between the timestamp in the input file and the current time */
};
/** @see node_type::print */
char * file_print(struct node *n);
/** @see node_type::parse */
int file_parse(struct node *n, config_setting_t *cfg);
int file_parse(struct node *n, json_t *cfg);
/** @see node_type::open */
int file_start(struct node *n);

View file

@ -44,7 +44,7 @@ int fpga_init(struct super_node *sn);
int fpga_deinit();
/** @see node_type::parse */
int fpga_parse(struct node *n, config_setting_t *cfg);
int fpga_parse(struct node *n, json_t *cfg);
struct fpga_card * fpga_lookup_card(const char *name);

View file

@ -29,8 +29,6 @@
#pragma once
#include <libconfig.h>
#include "queue_signalled.h"
#include "pool.h"
@ -53,7 +51,7 @@ struct loopback {
char * loopback_print(struct node *n);
/** @see node_type::parse */
int loopback_parse(struct node *n, config_setting_t *cfg);
int loopback_parse(struct node *n, json_t *cfg);
/** @see node_type::open */
int loopback_open(struct node *n);

View file

@ -34,6 +34,12 @@
#include "node.h"
#include "list.h"
/** The maximum length of a packet which contains stuct msg. */
#define NANOMSG_MAX_PACKET_LEN 1500
/* Forward declarations */
struct io_format;
struct nanomsg {
struct {
int socket;
@ -44,13 +50,15 @@ struct nanomsg {
int socket;
struct list endpoints;
} subscriber;
struct io_format *format;
};
/** @see node_type::print */
char * nanomsg_print(struct node *n);
/** @see node_type::parse */
int nanomsg_parse(struct node *n, config_setting_t *cfg);
int nanomsg_parse(struct node *n, json_t *cfg);
/** @see node_type::open */
int nanomsg_start(struct node *n);

View file

@ -39,9 +39,9 @@
#include <jansson.h>
#include "list.h"
#include "msg.h"
#include "super_node.h"
#include "node.h"
#include "task.h"
struct node;
@ -54,7 +54,7 @@ struct ngsi {
double timeout; /**< HTTP timeout in seconds */
double rate; /**< Rate used for polling. */
int tfd; /**< Timer */
struct task task; /**< Timer for periodic events. */
int ssl_verify; /**< Boolean flag whether SSL server certificates should be verified or not. */
struct curl_slist *headers; /**< List of HTTP request headers for libcurl */
@ -77,7 +77,7 @@ int ngsi_init(struct super_node *sn);
int ngsi_deinit();
/** @see node_type::parse */
int ngsi_parse(struct node *n, config_setting_t *cfg);
int ngsi_parse(struct node *n, json_t *cfg);
/** @see node_type::print */
char * ngsi_print(struct node *n);

View file

@ -67,7 +67,7 @@ int opal_init(struct super_node *sn);
int opal_deinit();
/** @see node_type::parse */
int opal_parse(struct node *n, config_setting_t *cfg);
int opal_parse(struct node *n, json_t *cfg);
/** @see node_type::print */
char * opal_print(struct node *n);

View file

@ -51,7 +51,7 @@ struct shmem {
char * shmem_print(struct node *n);
/** @see node_type::parse */
int shmem_parse(struct node *n, config_setting_t *cfg);
int shmem_parse(struct node *n, json_t *cfg);
/** @see node_type::open */
int shmem_open(struct node *n);

View file

@ -29,9 +29,8 @@
#pragma once
#include <libconfig.h>
#include "timing.h"
#include "task.h"
/* Forward declarations */
struct node;
@ -50,7 +49,7 @@ enum signal_type {
* @see node_type
*/
struct signal {
int tfd; /**< timerfd file descriptor. */
struct task task; /**< Timer for periodic events. */
int rt; /**< Real-time mode? */
enum signal_type type; /**< Signal type */
@ -71,7 +70,7 @@ struct signal {
char * signal_print(struct node *n);
/** @see node_type::parse */
int signal_parse(struct node *n, config_setting_t *cfg);
int signal_parse(struct node *n, json_t *cfg);
/** @see node_type::open */
int signal_open(struct node *n);

View file

@ -39,8 +39,22 @@
#include <linux/if_packet.h>
#endif
#ifdef WITH_LIBNL_ROUTE_30
#include "kernel/if.h"
#include "kernel/nl.h"
#include "kernel/tc.h"
#define WITH_NETEM
#endif /* WITH_LIBNL_ROUTE_30 */
#include "node.h"
/* Forward declarations */
struct io_format;
/** The maximum length of a packet which contains stuct msg. */
#define SOCKET_MAX_PACKET_LEN 1500
enum socket_layer {
SOCKET_LAYER_ETH,
SOCKET_LAYER_IP,
@ -80,6 +94,8 @@ struct socket {
union sockaddr_union local; /**< Local address of the socket */
union sockaddr_union remote; /**< Remote address of the socket */
struct io_format *format;
/* Multicast options */
struct multicast {
int enabled; /**< Is multicast enabled? */
@ -88,8 +104,10 @@ struct socket {
struct ip_mreq mreq; /**< A multicast group to join. */
} multicast;
#ifdef WITH_NETEM
struct rtnl_qdisc *tc_qdisc; /**< libnl3: Network emulator queuing discipline */
struct rtnl_cls *tc_classifier; /**< libnl3: Firewall mark classifier */
#endif /* WITH_NETEM */
};
@ -112,7 +130,7 @@ int socket_write(struct node *n, struct sample *smps[], unsigned cnt);
int socket_read(struct node *n, struct sample *smps[], unsigned cnt);
/** @see node_type::parse */
int socket_parse(struct node *n, config_setting_t *cfg);
int socket_parse(struct node *n, json_t *cfg);
/** @see node_type::print */
char * socket_print(struct node *n);

View file

@ -0,0 +1,73 @@
/** Node type: Node-type for testing Round-trip Time.
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASnode
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
/**
* @addtogroup test-rtt Node-type for testing Round-trip Time.
* @ingroup node
* @{
*/
#pragma once
#include "node.h"
#include "list.h"
#include "io.h"
#include "task.h"
struct test_rtt_case {
struct task task;
double rate;
int values;
int counter;
int limit; /**< The number of samples we take per test. */
};
struct test_rtt {
double cooldown; /**< Number of seconds to wait beween tests. */
int current; /**< Index of current test in test_rtt::cases */
struct io io; /**< The format of the output file */
struct list cases; /**< List of test cases */
const char *output; /**< The directory where we place the results. */
};
/** @see node_type::print */
char * test_rtt_print(struct node *n);
/** @see node_type::parse */
int test_rtt_parse(struct node *n, json_t *cfg);
/** @see node_type::open */
int test_rtt_start(struct node *n);
/** @see node_type::close */
int test_rtt_stop(struct node *n);
/** @see node_type::read */
int test_rtt_read(struct node *n, struct sample *smps[], unsigned cnt);
/** @see node_type::write */
int test_rtt_write(struct node *n, struct sample *smps[], unsigned cnt);
/** @} */

View file

@ -48,7 +48,6 @@ struct lws;
/** Internal data per websocket node */
struct websocket {
struct list connections; /**< List of active libwebsocket connections in server mode (struct websocket_connection). */
struct list destinations; /**< List of websocket servers connect to in client mode (struct websocket_destination). */
struct pool pool;
@ -57,24 +56,31 @@ struct websocket {
/* Internal datastructures */
struct websocket_connection {
struct node *node;
struct lws *wsi;
struct queue queue; /**< For samples which are sent to the WebSocket */
struct {
char name[64];
char ip[64];
} peer;
enum websocket_connection_state {
STATE_DISCONNECTED,
STATE_CONNECTING,
STATE_RECONNECTING,
STATE_ESTABLISHED,
STATE_SHUTDOWN,
STATE_ERROR
} state; /**< The current status of this connection. */
enum {
WEBSOCKET_MODE_CLIENT,
WEBSOCKET_MODE_SERVER,
} mode;
enum state state;
struct lws *wsi;
struct node *node;
struct io_format *format; /**< The IO format used for this connection. */
struct queue queue; /**< For samples which are sent to the WebSocket */
char *buf; /**< A buffer which is used to construct the messages. */
struct websocket_destination *destination;
struct {
struct buffer recv; /**< A buffer for reconstructing fragmented messags. */
struct buffer send; /**< A buffer for contsructing messages before calling lws_write() */
} buffers;
char *_name;
};

View file

@ -30,19 +30,26 @@
#pragma once
#include <stdint.h>
#include <jansson.h>
#include "node.h"
#include "list.h"
#if ZMQ_VERSION_MAJOR > 4 || (ZMQ_VERSION_MAJOR == 4 && ZMQ_VERSION_MINOR >= 2)
#define ZMQ_BUILD_DISH 1
#endif
/* Forward declarations */
struct io_format;
struct node;
struct sample;
struct zeromq {
int ipv6;
char *filter;
struct io_format *format;
struct {
int enabled;
struct {
@ -74,7 +81,7 @@ struct zeromq {
char * zeromq_print(struct node *n);
/** @see node_type::parse */
int zeromq_parse(struct node *n, config_setting_t *cfg);
int zeromq_parse(struct node *n, json_t *cfg);
/** @see node_type::init */
int zeromq_init();

View file

@ -30,7 +30,7 @@
#pragma once
#include <pthread.h>
#include <libconfig.h>
#include <jansson.h>
#include "list.h"
#include "queue.h"
@ -83,7 +83,8 @@ struct path
struct stats *stats; /**< Statistic counters. This is a pointer to the statistic hooks private data. */
struct super_node *super_node; /**< The super node this path belongs to. */
config_setting_t *cfg; /**< A pointer to the libconfig object which instantiated this path. */
json_t *cfg; /**< A JSON object containing the configuration of the path. */
};
/** Initialize internal data structures. */
@ -141,12 +142,12 @@ int path_uses_node(struct path *p, struct node *n);
/** Parse a single path and add it to the global configuration.
*
* @param cfg A libconfig object pointing to the path
* @param cfg A JSON object containing the configuration of the path.
* @param p Pointer to the allocated memory for this path
* @param nodes A linked list of all existing nodes
* @retval 0 Success. Everything went well.
* @retval <0 Error. Something went wrong.
*/
int path_parse(struct path *p, config_setting_t *cfg, struct list *nodes);
int path_parse(struct path *p, json_t *cfg, struct list *nodes);
/** @} */

View file

@ -23,6 +23,7 @@
#pragma once
#include "io_format.h"
#include "hook.h"
#include "api.h"
#include "common.h"
@ -53,6 +54,7 @@ enum plugin_type {
PLUGIN_TYPE_HOOK,
PLUGIN_TYPE_NODE,
PLUGIN_TYPE_API,
PLUGIN_TYPE_IO,
PLUGIN_TYPE_FPGA_IP,
PLUGIN_TYPE_MODEL_CBUILDER
};
@ -71,6 +73,7 @@ struct plugin {
int (*unload)(struct plugin *p);
union {
struct io_format io;
struct api_action api;
struct node_type node;
#ifdef WITH_FPGA
@ -91,7 +94,7 @@ int plugin_init(struct plugin *p);
int plugin_destroy(struct plugin *p);
int plugin_parse(struct plugin *p, config_setting_t *cfg);
int plugin_parse(struct plugin *p, json_t *cfg);
int plugin_load(struct plugin *p);

View file

@ -42,20 +42,22 @@ struct pool;
#define SAMPLE_LEN(len) (sizeof(struct sample) + SAMPLE_DATA_LEN(len))
/** The length of a sample data portion of a sample datastructure with \p values values in bytes. */
#define SAMPLE_DATA_LEN(len) ((len) * sizeof(float))
#define SAMPLE_DATA_LEN(len) ((len) * sizeof(double))
/** The offset to the beginning of the data section. */
#define SAMPLE_DATA_OFFSET(smp) ((char *) (smp) + offsetof(struct sample, data))
enum sample_data_format {
SAMPLE_DATA_FORMAT_FLOAT= 0,
SAMPLE_DATA_FORMAT_INT = 1
SAMPLE_DATA_FORMAT_FLOAT = 0,
SAMPLE_DATA_FORMAT_INT = 1
};
struct sample {
int sequence; /**< The sequence number of this sample. */
int length; /**< The number of values in sample::values which are valid. */
int capacity; /**< The number of values in sample::values for which memory is reserved. */
int id;
atomic_int refcnt; /**< Reference counter. */
off_t pool_off; /**< This sample belongs to this memory pool (relative pointer). */
@ -68,12 +70,16 @@ struct sample {
struct timespec sent; /**< The point in time when this data was send for the last time. */
} ts;
uint64_t format; /**< A long bitfield indicating the number representation of the first 64 values in sample::data[] */
/** A long bitfield indicating the number representation of the first 64 values in sample::data[].
*
* @see sample_data_format
*/
uint64_t format;
/** The values. */
union {
float f; /**< Floating point values. */
uint32_t i; /**< Integer values. */
double f; /**< Floating point values. */
uint64_t i; /**< Integer values. */
} data[]; /**< Data is in host endianess! */
};

View file

@ -1,89 +0,0 @@
/** Read / write sample data in different formats.
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASnode
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#pragma once
#include <stdio.h>
#include <jansson.h>
/* Forward declarations */
struct sample;
enum sample_io_format {
SAMPLE_IO_FORMAT_VILLAS,
SAMPLE_IO_FORMAT_JSON,
SAMPLE_IO_FORMAT_HDF5,
SAMPLE_IO_FORMAT_COMTRADE
};
/** These flags define the format which is used by sample_io_fscan() and sample_io_fprint(). */
enum sample_flags {
SAMPLE_IO_NANOSECONDS = (1 << 0),
SAMPLE_IO_OFFSET = (1 << 1),
SAMPLE_IO_SEQUENCE = (1 << 2),
SAMPLE_IO_VALUES = (1 << 3),
SAMPLE_IO_ALL = 16-1
};
/* Not implemented yet */
#if 0
struct sample_io_handle {
enum sample_io_format format;
int flags
FILE *file;
struct list fields;
};
int sample_io_init(struct sample_io *io, enum sample_io_format fmt, char *mode, int flags);
int sample_io_destroy(struct sample_io *io);
int sample_io_open(struct sample_io *io);
int sample_io_close(struct sample_io *io);
int sample_io_write(struct sample_io *io, struct sample *smps[], size_t cnt);
int sample_io_read(struct sample_io *io, struct sample *smps[], size_t cnt);
int sample_io_eof(struct sample_io *io);
int sample_io_rewind(struct sample_io *io);
#endif
/* Lowlevel interface */
int sample_io_fprint(FILE *f, struct sample *s, enum sample_io_format fmt, int flags);
int sample_io_fscan(FILE *f, struct sample *s, enum sample_io_format fmt, int *flags);
/* VILLASnode human readable format */
int sample_io_villas_print(char *buf, size_t len, struct sample *s, int flags);
int sample_io_villas_scan(const char *line, struct sample *s, int *fl);
int sample_io_villas_fprint(FILE *f, struct sample *s, int flags);
int sample_io_villas_fscan(FILE *f, struct sample *s, int *flags);

View file

@ -25,6 +25,7 @@
#define _STATS_H_
#include <stdint.h>
#include <jansson.h>
#include "hist.h"
@ -61,6 +62,8 @@ struct stats {
struct stats_delta *delta;
};
int stats_lookup_format(const char *str);
int stats_init(struct stats *s, int buckets, int warmup);
int stats_destroy(struct stats *s);
@ -71,11 +74,7 @@ void stats_collect(struct stats_delta *s, struct sample *smps[], size_t cnt);
int stats_commit(struct stats *s, struct stats_delta *d);
#ifdef WITH_JSON
#include <jansson.h>
json_t * stats_json(struct stats *s);
#endif
void stats_reset(struct stats *s);

View file

@ -1,8 +1,4 @@
/** Configuration file parser.
*
* The server program is configured by a single file.
* This config file is parsed with a third-party library:
* libconfig http://www.hyperrealm.com/libconfig/
/** The super node object holding the state of the application.
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
@ -27,8 +23,6 @@
#pragma once
#include <libconfig.h>
#include "list.h"
#include "api.h"
#include "web.h"
@ -50,6 +44,8 @@ struct super_node {
struct api api;
struct web web;
char *name; /**< A name of this super node. Usually the hostname. */
struct {
int argc;
char **argv;
@ -59,8 +55,7 @@ struct super_node {
char *uri; /**< URI of configuration */
config_t cfg; /**< Pointer to configuration file */
json_t *json; /**< JSON representation of the same config. */
json_t *cfg; /**< JSON representation of the configuration. */
};
/* Compatibility with libconfig < 1.5 */
@ -84,7 +79,7 @@ int super_node_parse_uri(struct super_node *sn, const char *uri);
* @retval 0 Success. Everything went well.
* @retval <0 Error. Something went wrong.
*/
int super_node_parse(struct super_node *sn, config_setting_t *cfg);
int super_node_parse_json(struct super_node *sn, json_t *cfg);
/** Check validity of super node configuration. */
int super_node_check(struct super_node *sn);

71
include/villas/task.h Normal file
View file

@ -0,0 +1,71 @@
/** Run tasks periodically.
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASnode
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#pragma once
#include <stdio.h>
#include <stdint.h>
#include <time.h>
/** We can choose between two periodic task implementations */
//#define PERIODIC_TASK_IMPL NANOSLEEP
#define TIMERFD 1
#define CLOCK_NANOSLEEP 2
#define NANOSLEEP 3
#if defined(__MACH__)
#define PERIODIC_TASK_IMPL NANOSLEEP
#else
#define PERIODIC_TASK_IMPL TIMERFD
#endif
struct task {
struct timespec period;
#if PERIODIC_TASK_IMPL == CLOCK_NANOSLEEP || PERIODIC_TASK_IMPL == NANOSLEEP
struct timespec next_period;
#elif PERIODIC_TASK_IMPL == TIMERFD
int fd;
#else
#error "Invalid period task implementation"
#endif
int clock;
};
/** Create a new task with the given rate. */
int task_init(struct task *t, double rate, int clock);
int task_destroy(struct task *t);
/** Wait until task elapsed
*
* @retval 0 An error occured. Maybe the task was stopped.
* @retval >0 The nummer of runs this task already fired.
*/
uint64_t task_wait_until_next_period(struct task *t);
/** Wait until a fixed time in the future is reached
*
* @param until A pointer to a time in the future.
*/
int task_wait_until(struct task *t, const struct timespec *until);

View file

@ -28,30 +28,6 @@
#include <time.h>
#ifdef __linux__
#include <sys/timerfd.h>
#endif
/** Create a new timer with the given rate. */
int timerfd_create_rate(double rate);
/** Wait until timer elapsed
*
* @param fd A file descriptor which was created by timerfd_create(3).
* @retval 0 An error occured. Maybe the timer was stopped.
* @retval >0 The nummer of runs this timer already fired.
*/
uint64_t timerfd_wait(int fd);
/** Wait until a fixed time in the future is reached
*
* @param fd A file descriptor which was created by timerfd_create(3).
* @param until A pointer to a time in the future.
* @retval 0 An error occured. Maybe the timer was stopped.
* @retval >0 The nummer of runs this timer already fired.
*/
uint64_t timerfd_wait_until(int fd, const struct timespec *until);
/** Get delta between two timespec structs */
struct timespec time_diff(const struct timespec *start, const struct timespec *end);

View file

@ -23,7 +23,6 @@
#pragma once
#include <libconfig.h>
#include <pthread.h>
#include "common.h"
@ -40,9 +39,9 @@ struct web {
struct lws_vhost *vhost; /**< The libwebsockets vhost. */
int port; /**< Port of the build in HTTP / WebSocket server. */
const char *htdocs; /**< The root directory for files served via HTTP. */
const char *ssl_cert; /**< Path to the SSL certitifcate for HTTPS / WSS. */
const char *ssl_private_key; /**< Path to the SSL private key for HTTPS / WSS. */
char *htdocs; /**< The root directory for files served via HTTP. */
char *ssl_cert; /**< Path to the SSL certitifcate for HTTPS / WSS. */
char *ssl_private_key; /**< Path to the SSL private key for HTTPS / WSS. */
pthread_t thread;
};
@ -60,4 +59,4 @@ int web_start(struct web *w);
int web_stop(struct web *w);
/** Parse HTTPd and WebSocket related options */
int web_parse(struct web *w, config_setting_t *lcs);
int web_parse(struct web *w, json_t *cfg);

View file

@ -1,72 +0,0 @@
/** WebSocket buffer.
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASnode
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#pragma once
#include <stddef.h>
#include <libwebsockets.h>
#include "common.h"
struct web_buffer {
char *buffer; /**< A pointer to the buffer. Usually resized via realloc() */
size_t size; /**< The allocated size of the buffer. */
size_t len; /**< The used length of the buffer. */
size_t prefix; /**< The used length of the buffer. */
enum lws_write_protocol protocol;
enum state state;
};
/** Initialize a libwebsockets buffer. */
int web_buffer_init(struct web_buffer *b, enum lws_write_protocol prot);
/** Destroy a libwebsockets buffer. */
int web_buffer_destroy(struct web_buffer *b);
/** Flush the buffers contents to lws_write() */
int web_buffer_write(struct web_buffer *b, struct lws *w);
/** Copy \p len bytes from the beginning of the buffer and copy them to \p out.
*
* @param out The destination buffer. If NULL, we just remove \p len bytes from the buffer.
*/
int web_buffer_read(struct web_buffer *b, char *out, size_t len);
/** Parse JSON from the beginning of the buffer.
*
* @retval -1 The buffer is empty.
* @retval -2 The buffer contains malformed JSON.
*/
int web_buffer_read_json(struct web_buffer *b, json_t **req);
/** Append \p len bytes of \p in at the end of the buffer.
*
* The buffer is automatically resized.
*/
int web_buffer_append(struct web_buffer *b, const char *in, size_t len);
/** Append the serialized represetnation of the JSON object \p res at the end of the buffer. */
int web_buffer_append_json(struct web_buffer *b, json_t *res);

View file

@ -1,89 +0,0 @@
/** Binary websocket message format.
*
* Note: Messages sent by the 'websocket' node-type are always send in little endian byte-order!
* This is different from the messages send with the 'socket' node-type!
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASnode
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#pragma once
#include <stdint.h>
/** The current version number for the message format */
#define WEBMSG_VERSION 2
/** @todo Implement more message types */
#define WEBMSG_TYPE_DATA 0 /**< Message contains float values */
#define WEBMSG_TYPE_START 1 /**< Message marks the beginning of a new simulation case */
#define WEBMSG_TYPE_STOP 2 /**< Message marks the end of a simulation case */
/** The total size in bytes of a message */
#define WEBMSG_LEN(values) (sizeof(struct webmsg) + WEBMSG_DATA_LEN(values))
/** The length of \p values values in bytes. */
#define WEBMSG_DATA_LEN(values) (sizeof(float) * (values))
/** The offset to the first data value in a message. */
#define WEBMSG_DATA_OFFSET(msg) ((char *) (msg) + offsetof(struct webmsg, data))
/** Initialize a message with default values */
#define WEBMSG_INIT(len, seq) (struct webmsg) {\
.version = WEBMSG_VERSION, \
.type = WEBMSG_TYPE_DATA, \
.length = len, \
.sequence = seq \
}
/** The timestamp of a message in struct timespec format */
#define WEBMSG_TS(msg) (struct timespec) {\
.tv_sec = (msg)->ts.sec, \
.tv_nsec = (msg)->ts.nsec \
}
/** This message format is used by all clients
*
* @diafile msg_format.dia
**/
struct webmsg
{
unsigned rsvd1 : 2; /**< Reserved bits */
unsigned type : 2; /**< Data or control message (see MSG_TYPE_*) */
unsigned version: 4; /**< Specifies the format of the remaining message (see MGS_VERSION) */
uint8_t id; /**< The node index from / to which this sample received / sent to.
* Corresponds to the index of the node in the http://localhost/nodes.json array. */
uint16_t length; /**< The number of values in msg::data[]. */
uint32_t sequence; /**< The sequence number is incremented by one for consecutive messages. */
/** A timestamp per message. */
struct {
uint32_t sec; /**< Seconds since 1970-01-01 00:00:00 */
uint32_t nsec; /**< Nanoseconds of the current second. */
} ts;
/** The message payload. */
union {
float f; /**< Floating point values. */
uint32_t i; /**< Integer values. */
} data[];
} __attribute__((packed));

View file

@ -28,6 +28,7 @@ LIB_CFLAGS = $(CFLAGS) -fPIC
-include lib/hooks/Makefile.inc
-include lib/nodes/Makefile.inc
-include lib/io/Makefile.inc
-include $(patsubst %, lib/Makefile.%.inc, $(SONAMES))

View file

@ -26,15 +26,14 @@ LIB = $(BUILDDIR)/$(LIB_NAME).so.$(LIB_ABI_VERSION)
WITH_WEB ?= 1
WITH_API ?= 1
WITH_JSON ?= 1
# Object files for libvillas
LIB_SRCS += $(addprefix lib/kernel/, kernel.c rt.c) \
$(addprefix lib/, sample.c path.c node.c hook.c log.c log_config.c \
utils.c super_node.c hist.c timing.c pool.c list.c queue.c \
queue_signalled.c memory.c advio.c plugin.c node_type.c stats.c \
mapping.c sample_io.c shmem.c config_helper.c crypt.c compat.c \
log_table.c log_helper.c \
mapping.c io.c shmem.c config_helper.c crypt.c compat.c \
log_table.c log_helper.c io_format.c task.c buffer.c \
)
LIB_LDFLAGS = -shared
@ -46,14 +45,10 @@ endif
LIB_PKGS += openssl libcurl
ifeq ($(WITH_JSON),1)
LIB_SRCS += lib/sample_io_json.c
LIB_PKGS += jansson
LIB_CFLAGS += -DWITH_JSON
endif
ifeq ($(WITH_WEB),1)
-include lib/web/Makefile.inc
LIB_SRCS += lib/web.c
LIB_PKGS += libwebsockets
LIB_CFLAGS += -DWITH_WEB
endif
ifeq ($(WITH_API),1)
@ -74,11 +69,11 @@ $(LIB): $(LIB_OBJS)
ln -srf $@ $(BUILDDIR)/$(LIB_NAME).so
# Install
install-libvillas: libvillas
install-libvillas: libvillas | $(DESTDIR)$(PREFIX)/include/villas/
install -m 0755 -D -T $(LIB) $(DESTDIR)$(PREFIX)/lib/$(LIB_NAME).so.$(LIB_ABI_VERSION)
install -m 0644 -D -t $(DESTDIR)$(PREFIX)/include/villas/ include/villas/*.h
ln -srf $(DESTDIR)$(PREFIX)/lib/$(LIB_NAME).so.$(LIB_ABI_VERSION) $(DESTDIR)$(PREFIX)/lib/$(LIB_NAME).so
ldconfig
if [ "$(PLATFORM)" == "Linux" ]; then ldconfig; fi
clean-libvillas:
rm -f $(LIB)

View file

@ -169,6 +169,23 @@ static int advio_xferinfo(void *p, curl_off_t dl_total_bytes, curl_off_t dl_byte
return 0;
}
int aislocal(const char *uri)
{
char *sep;
const char *supported_schemas[] = { "file", "http", "https", "tftp", "ftp", "scp", "sftp", "smb", "smbs" };
sep = strstr(uri, "://");
if (!sep)
return 1; /* no schema, we assume its a local file */
for (int i = 0; i < ARRAY_LEN(supported_schemas); i++) {
if (!strncmp(supported_schemas[i], uri, sep - uri))
return 0;
}
return -1; /* none of the supported schemas match. this is an invalid uri */
}
AFILE * afopen(const char *uri, const char *mode)
{
int ret;
@ -184,7 +201,7 @@ AFILE * afopen(const char *uri, const char *mode)
if (!af->uri)
goto out2;
}
else {
else { /* Open local file by prepending file:// schema. */
if (strlen(uri) <= 1)
return NULL;
@ -242,14 +259,19 @@ int afclose(AFILE *af)
int ret;
ret = afflush(af);
if (ret)
return ret;
curl_easy_cleanup(af->curl);
fclose(af->file);
ret = fclose(af->file);
if (ret)
return ret;
free(af->uri);
free(af);
return ret;
return 0;
}
int afseek(AFILE *af, long offset, int origin)
@ -416,6 +438,7 @@ int adownload(AFILE *af, int resume)
case CURLE_FILE_COULDNT_READ_FILE:
case CURLE_TFTP_NOTFOUND:
case CURLE_REMOTE_FILE_NOT_FOUND:
info("File does not exist.");
goto notexist;
/* If libcurl does not know the protocol, we will try it with the stdio */
@ -425,7 +448,7 @@ int adownload(AFILE *af, int resume)
return -1;
default:
error("ADVIO: Failed to download file: %s: %s", af->uri, curl_easy_strerror(res));
error("Failed to download file: %s: %s", af->uri, curl_easy_strerror(res));
return -1;
}

197
lib/api.c
View file

@ -30,9 +30,13 @@
#include "assert.h"
#include "compat.h"
/* Forward declarations */
static void * worker(void *ctx);
int api_ws_protocol_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len)
{
int ret;
int ret, pulled, pushed;
json_t *req, *resp;
struct web *w = lws_context_user(lws_get_context(wsi));
struct api_session *s = (struct api_session *) user;
@ -46,21 +50,23 @@ int api_ws_protocol_cb(struct lws *wsi, enum lws_callback_reasons reason, void *
/* Parse request URI */
char uri[64];
lws_hdr_copy(wsi, uri, sizeof(uri), WSI_TOKEN_GET_URI); /* The path component of the*/
lws_hdr_copy(wsi, uri, sizeof(uri), WSI_TOKEN_GET_URI);
ret = sscanf(uri, "/v%d", (int *) &s->version);
if (ret != 1)
return -1;
ret = api_session_init(s, w->api, API_MODE_WS);
ret = api_session_init(s, API_MODE_WS);
if (ret)
return -1;
list_push(&w->api->sessions, s);
s->wsi = wsi;
s->api = w->api;
lws_get_peer_addresses(wsi, lws_get_socket_fd(wsi), s->peer.name, sizeof(s->peer.name), s->peer.ip, sizeof(s->peer.ip));
list_push(&s->api->sessions, s);
debug(LOG_API, "Initiated API session: %s", api_session_name(s));
debug(LOG_API, "New API session initiated: version=%d, mode=websocket, remote=%s (%s)", s->version, s->peer.name, s->peer.ip);
break;
case LWS_CALLBACK_CLOSED:
@ -70,28 +76,44 @@ int api_ws_protocol_cb(struct lws *wsi, enum lws_callback_reasons reason, void *
list_remove(&w->api->sessions, s);
debug(LOG_API, "Closed API session");
break;
case LWS_CALLBACK_SERVER_WRITEABLE:
web_buffer_write(&s->response.body, wsi);
if (s->completed && s->response.body.len == 0)
return -1;
debug(LOG_API, "Closing API session: %s", api_session_name(s));
break;
case LWS_CALLBACK_RECEIVE:
web_buffer_append(&s->request.body, in, len);
if (lws_is_first_fragment(wsi))
buffer_clear(&s->request.buffer);
buffer_append(&s->request.buffer, in, len);
if (lws_is_final_fragment(wsi)) {
ret = buffer_parse_json(&s->request.buffer, &req);
if (ret)
break;
pushed = queue_push(&s->request.queue, req);
if (pushed != 1)
warn("Queue overun in API session");
json_t *req, *resp;
while (web_buffer_read_json(&s->request.body, &req) >= 0) {
api_session_run_command(s, req, &resp);
web_buffer_append_json(&s->response.body, resp);
lws_callback_on_writable(wsi);
pushed = queue_signalled_push(&w->api->pending, s);
if (pushed != 1)
warn("Queue overrun in API");
}
break;
case LWS_CALLBACK_SERVER_WRITEABLE:
pulled = queue_pull(&s->response.queue, (void **) &resp);
if (pulled < 1)
break;
char pad[LWS_PRE];
buffer_clear(&s->response.buffer);
buffer_append(&s->response.buffer, pad, sizeof(pad));
buffer_append_json(&s->response.buffer, resp);
lws_write(wsi, (unsigned char *) s->response.buffer.buf + LWS_PRE, s->response.buffer.len - LWS_PRE, LWS_WRITE_TEXT);
break;
default:
@ -103,7 +125,8 @@ int api_ws_protocol_cb(struct lws *wsi, enum lws_callback_reasons reason, void *
int api_http_protocol_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len)
{
int ret;
int ret, pulled, pushed;
json_t *resp, *req;
struct web *w = lws_context_user(lws_get_context(wsi));
struct api_session *s = (struct api_session *) user;
@ -120,32 +143,16 @@ int api_http_protocol_cb(struct lws *wsi, enum lws_callback_reasons reason, void
if (ret != 1)
return -1;
ret = api_session_init(s, w->api, API_MODE_HTTP);
ret = api_session_init(s, API_MODE_HTTP);
if (ret)
return -1;
s->wsi = wsi;
s->api = w->api;
list_push(&w->api->sessions, s);
lws_get_peer_addresses(wsi, lws_get_socket_fd(wsi), s->peer.name, sizeof(s->peer.name), s->peer.ip, sizeof(s->peer.ip));
list_push(&s->api->sessions, s);
debug(LOG_API, "New API session initiated: version=%d, mode=http, remote=%s (%s)", s->version, s->peer.name, s->peer.ip);
/* Prepare HTTP response header */
const char headers[] = "HTTP/1.1 200 OK\r\n"
"Content-type: application/json\r\n"
"User-agent: " USER_AGENT "\r\n"
"Access-Control-Allow-Origin: *\r\n"
"Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n"
"Access-Control-Allow-Headers: Content-Type\r\n"
"Access-Control-Max-Age: 86400\r\n"
"\r\n";
web_buffer_append(&s->response.headers, headers, sizeof(headers)-1);
lws_callback_on_writable(wsi);
/* Only HTTP POST requests wait for body */
if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) == 0)
s->completed = true;
debug(LOG_API, "Initiated API session: %s", api_session_name(s));
break;
@ -163,27 +170,50 @@ int api_http_protocol_cb(struct lws *wsi, enum lws_callback_reasons reason, void
break;
case LWS_CALLBACK_HTTP_BODY:
web_buffer_append(&s->request.body, in, len);
buffer_append(&s->request.buffer, in, len);
json_t *req, *resp;
while (web_buffer_read_json(&s->request.body, &req) == 1) {
api_session_run_command(s, req, &resp);
web_buffer_append_json(&s->response.body, resp);
lws_callback_on_writable(wsi);
}
break;
case LWS_CALLBACK_HTTP_BODY_COMPLETION:
s->completed = true;
ret = buffer_parse_json(&s->request.buffer, &req);
if (ret)
break;
buffer_clear(&s->request.buffer);
pushed = queue_push(&s->request.queue, req);
if (pushed != 1)
warn("Queue overrun for API session: %s", api_session_name(s));
pushed = queue_signalled_push(&w->api->pending, s);
if (pushed != 1)
warn("Queue overrun for API");
break;
case LWS_CALLBACK_HTTP_WRITEABLE:
web_buffer_write(&s->response.headers, wsi);
web_buffer_write(&s->response.body, wsi);
pulled = queue_pull(&s->response.queue, (void **) &resp);
if (pulled) {
const char headers[] = "HTTP/1.1 200 OK\r\n"
"Content-type: application/json\r\n"
"User-agent: " USER_AGENT "\r\n"
"Access-Control-Allow-Origin: *\r\n"
"Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n"
"Access-Control-Allow-Headers: Content-Type\r\n"
"Access-Control-Max-Age: 86400\r\n"
"\r\n";
buffer_clear(&s->response.buffer);
buffer_append_json(&s->response.buffer, resp);
lws_write(wsi, (unsigned char *) headers, strlen(headers), LWS_WRITE_HTTP_HEADERS);
lws_write(wsi, (unsigned char *) s->response.buffer.buf, s->response.buffer.len, LWS_WRITE_HTTP);
debug(LOG_API, "Closing API session: %s", api_session_name(s));
if (s->completed && s->response.body.len == 0)
return -1; /* Close connection */
}
break;
default:
@ -216,7 +246,13 @@ int api_destroy(struct api *a)
int api_start(struct api *a)
{
int ret;
info("Starting API sub-system");
ret = pthread_create(&a->thread, NULL, worker, a);
if (ret)
error("Failed to start API worker thread");
a->state = STATE_STARTED;
@ -225,16 +261,57 @@ int api_start(struct api *a)
int api_stop(struct api *a)
{
int ret;
info("Stopping API sub-system");
for (int i = 0; i < 10 && list_length(&a->sessions) > 0; i++) {
for (int i = 0; i < 10 && list_length(&a->sessions) > 0; i++) { INDENT
info("Wait for API requests to complete");
usleep(100 * 1e-3);
usleep(1 * 1e6);
}
if (a->state != STATE_STARTED)
return 0;
list_destroy(&a->sessions, (dtor_cb_t) api_session_destroy, false);
ret = list_destroy(&a->sessions, (dtor_cb_t) api_session_destroy, false);
if (ret)
return ret;
ret = pthread_cancel(a->thread);
if (ret)
serror("Failed to cancel API worker thread");
ret = pthread_join(a->thread, NULL);
if (ret)
serror("Failed to join API worker thread");
a->state = STATE_STOPPED;
return 0;
}
static void * worker(void *ctx)
{
int pulled;
struct api *a = ctx;
struct api_session *s;
json_t *req, *resp;
for (;;) {
pulled = queue_signalled_pull(&a->pending, (void **) &s);
if (pulled != 1)
continue;
queue_pull(&s->request.queue, (void **) &req);
api_session_run_command(s, req, &resp);
queue_push(&s->response.queue, resp);
lws_callback_on_writable(s->wsi);
}
return NULL;
}

View file

@ -20,10 +20,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
###################################################################################
ifndef WITH_JSON
$(error API support requires JSON)
endif
LIB_SRCS += lib/api.c
LIB_SRCS += $(wildcard lib/api/*.c)

View file

@ -27,6 +27,7 @@ static int api_capabilities(struct api_action *h, json_t *args, json_t **resp, s
json_t *json_hooks = json_array();
json_t *json_apis = json_array();
json_t *json_nodes = json_array();
json_t *json_ios = json_array();
json_t *json_name;
for (size_t i = 0; i < list_length(&plugins); i++) {
@ -38,15 +39,17 @@ static int api_capabilities(struct api_action *h, json_t *args, json_t **resp, s
case PLUGIN_TYPE_NODE: json_array_append_new(json_nodes, json_name); break;
case PLUGIN_TYPE_HOOK: json_array_append_new(json_hooks, json_name); break;
case PLUGIN_TYPE_API: json_array_append_new(json_apis, json_name); break;
default: { }
case PLUGIN_TYPE_IO: json_array_append_new(json_ios, json_name); break;
default: json_decref(json_name);
}
}
*resp = json_pack("{ s: s, s: o, s: o, s: o }",
*resp = json_pack("{ s: s, s: o, s: o, s: o, s: o }",
"build", BUILDID,
"hooks", json_hooks,
"node-types", json_nodes,
"apis", json_apis);
"apis", json_apis,
"formats", json_ios);
return 0;
}

View file

@ -20,19 +20,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#include <libconfig.h>
#include "api.h"
#include "utils.h"
#include "plugin.h"
#include "config_helper.h"
#include "super_node.h"
static int api_config(struct api_action *h, json_t *args, json_t **resp, struct api_session *s)
{
config_setting_t *cfg_root = config_root_setting(&s->api->super_node->cfg);
*resp = cfg_root ? config_to_json(cfg_root) : json_object();
*resp = s->api->super_node->cfg;
return 0;
}

View file

@ -26,8 +26,6 @@
#include "node.h"
#include "super_node.h"
#include "utils.h"
#include "config_helper.h"
#include "api.h"
static int api_nodes(struct api_action *r, json_t *args, json_t **resp, struct api_session *s)
@ -47,7 +45,7 @@ static int api_nodes(struct api_action *r, json_t *args, json_t **resp, struct a
/* Add all additional fields of node here.
* This can be used for metadata */
json_object_update(json_node, config_to_json(n->cfg));
json_object_update(json_node, n->cfg);
json_array_append_new(json_nodes, json_node);
}

View file

@ -25,7 +25,6 @@
#include "plugin.h"
#include "path.h"
#include "utils.h"
#include "config_helper.h"
#include "stats.h"
#include "super_node.h"
@ -47,7 +46,7 @@ static int api_paths(struct api_action *r, json_t *args, json_t **resp, struct a
/* Add all additional fields of node here.
* This can be used for metadata */
json_object_update(json_path, config_to_json(p->cfg));
json_object_update(json_path, p->cfg);
json_array_append_new(json_paths, json_path);
}

View file

@ -64,12 +64,10 @@ static int api_restart(struct api_action *h, json_t *args, json_t **resp, struct
/* We pass some env variables to the new process */
setenv("VILLAS_API_RESTART_COUNT", buf, 1);
setenv("VILLAS_API_RESTART_REMOTE", s->peer.ip, 1);
*resp = json_pack("{ s: i, s: s, s: s }",
*resp = json_pack("{ s: i, s: s }",
"restarts", cnt,
"config", config,
"remote", s->peer.ip
"config", config
);
/* Register exit handler */

View file

@ -32,7 +32,7 @@ static int api_shutdown(struct api_action *h, json_t *args, json_t **resp, struc
static struct plugin p = {
.name = "shutdown",
.description = "stop VILLASnode",
.description = "quit VILLASnode",
.type = PLUGIN_TYPE_API,
.api.cb = api_shutdown
};

View file

@ -24,33 +24,61 @@
#include "web.h"
#include "plugin.h"
#include "memory.h"
int api_session_init(struct api_session *s, struct api *a, enum api_mode m)
int api_session_init(struct api_session *s, enum api_mode m)
{
int ret;
s->runs = 0;
s->mode = m;
s->api = a;
ret = buffer_init(&s->request.buffer, 0);
if (ret)
return ret;
ret = buffer_init(&s->response.buffer, 0);
if (ret)
return ret;
ret = queue_init(&s->request.queue, 128, &memtype_heap);
if (ret)
return ret;
s->completed = false;
web_buffer_init(&s->request.body, s->mode == API_MODE_HTTP ? LWS_WRITE_HTTP : LWS_WRITE_TEXT);
web_buffer_init(&s->response.body, s->mode == API_MODE_HTTP ? LWS_WRITE_HTTP : LWS_WRITE_TEXT);
if (s->mode == API_MODE_HTTP)
web_buffer_init(&s->response.headers, LWS_WRITE_HTTP_HEADERS);
ret = queue_init(&s->response.queue, 128, &memtype_heap);
if (ret)
return ret;
s->_name = NULL;
return 0;
}
int api_session_destroy(struct api_session *s)
{
int ret;
if (s->state == STATE_DESTROYED)
return 0;
web_buffer_destroy(&s->request.body);
web_buffer_destroy(&s->response.body);
ret = buffer_destroy(&s->request.buffer);
if (ret)
return ret;
if (s->mode == API_MODE_HTTP)
web_buffer_destroy(&s->response.headers);
ret = buffer_destroy(&s->response.buffer);
if (ret)
return ret;
ret = queue_destroy(&s->request.queue);
if (ret)
return ret;
ret = queue_destroy(&s->response.queue);
if (ret)
return ret;
if (s->_name)
free(s->_name);
s->state = STATE_DESTROYED;
@ -90,7 +118,7 @@ int api_session_run_command(struct api_session *s, json_t *json_in, json_t **jso
goto out;
}
debug(LOG_API, "Running API request: %s", p->name);
debug(LOG_API, "Running API request: action=%s, id=%s", action, id);
ret = p->api.cb(&p->api, json_args, &json_resp, s);
if (ret)
@ -107,7 +135,31 @@ int api_session_run_command(struct api_session *s, json_t *json_in, json_t **jso
if (json_resp)
json_object_set(*json_out, "response", json_resp);
out: debug(LOG_API, "API request completed with code: %d", ret);
out: debug(LOG_API, "Completed API request: action=%s, id=%s, code=%d", action, id, ret);
s->runs++;
return 0;
}
char * api_session_name(struct api_session *s)
{
if (!s->_name) {
char *mode;
switch (s->mode) {
case API_MODE_WS: mode = "ws"; break;
case API_MODE_HTTP: mode = "http"; break;
default: mode = "unknown"; break;
}
char name[128];
char ip[128];
lws_get_peer_addresses(s->wsi, lws_get_socket_fd(s->wsi), name, sizeof(name), ip, sizeof(ip));
s->_name = strcatf(&s->_name, "version=%d, mode=%s, runs=%d, remote.name=%s, remote.ip=%s", s->version, mode, s->runs, name, ip);
}
return s->_name;
}

99
lib/buffer.c Normal file
View file

@ -0,0 +1,99 @@
/** A simple growing buffer.
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASnode
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#include <string.h>
#include "buffer.h"
#include "common.h"
int buffer_init(struct buffer *b, size_t size)
{
b->len = 0;
b->size = size;
b->buf = malloc(size);
if (!b->buf)
return -1;
b->state = STATE_INITIALIZED;
return 0;
}
int buffer_destroy(struct buffer *b)
{
if (b->buf)
free(b->buf);
b->state = STATE_DESTROYED;
return 0;
}
void buffer_clear(struct buffer *b)
{
b->len = 0;
}
int buffer_append(struct buffer *b, const char *data, size_t len)
{
if (b->len + len > b->size) {
b->size = b->len + len;
b->buf = realloc(b->buf, b->size);
if (!b->buf)
return -1;
}
memcpy(b->buf + b->len, data, len);
b->len += len;
return 0;
}
int buffer_parse_json(struct buffer *b, json_t **j)
{
*j = json_loadb(b->buf, b->len, 0, NULL);
if (!*j)
return -1;
return 0;
}
int buffer_append_json(struct buffer *b, json_t *j)
{
size_t len;
retry: len = json_dumpb(j, b->buf + b->len, b->size - b->len, 0);
if (b->size < b->len + len) {
b->buf = realloc(b->buf, b->len + len);
if (!b->buf)
return -1;
b->size = b->len + len;
goto retry;
}
b->len += len;
return 0;
}

View file

@ -20,10 +20,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#include <string.h>
#include "config_helper.h"
#include "utils.h"
#ifdef WITH_JSON
#ifdef WITH_LIBCONFIG
static int json_to_config_type(int type)
{
switch (type) {
@ -139,24 +142,180 @@ int json_to_config(json_t *json, config_setting_t *parent)
return 0;
}
#endif /* WITH_JSON */
#endif /* WITH_LIBCONFIG */
int config_read_cli(config_t *cfg, int argc, char *argv[])
void json_object_extend_key_value(json_t *obj, const char *key, const char *value)
{
int ret;
char *str = NULL;
char *end, *cpy, *key1, *key2;
for (int i = 0; i < argc; i++)
str = strcatf(&str, "%s", argv[i]);
double real;
long integer;
if (!str)
return 0;
json_t *arr, *new, *existing, *subobj;
config_set_auto_convert(cfg, 1);
/* Is the key pointing to an object? */
subobj = obj;
cpy = strdup(key);
ret = config_read_string(cfg, str);
key1 = strtok(cpy, ".");
key2 = strtok(NULL, ".");
free(str);
while (key1 && key2) {
existing = json_object_get(subobj, key1);
if (existing)
subobj = existing;
else {
new = json_object();
json_object_set(subobj, key1, new);
return ret != CONFIG_TRUE;
subobj = new;
}
key1 = key2;
key2 = strtok(NULL, ".");
}
/* Try to parse as integer */
integer = strtol(value, &end, 0);
if (*end == 0) {
new = json_integer(integer);
goto success;
}
/* Try to parse as floating point */
real = strtod(value, &end);
if (*end == 0) {
new = json_real(real);
goto success;
}
/* Try to parse special types */
if (!strcmp(value, "true")) {
new = json_true();
goto success;
}
if (!strcmp(value, "false")) {
new = json_false();
goto success;
}
if (!strcmp(value, "null")) {
new = json_null();
goto success;
}
/* Fallback to string */
new = json_string(value);
success:
/* Does the key already exist?
* If yes, transform to array. */
existing = json_object_get(subobj, key1);
if (existing) {
if (json_is_array(existing))
arr = existing;
else {
arr = json_array();
json_object_set(subobj, key1, arr);
json_array_append(arr, existing);
}
json_array_append(arr, new);
}
else
json_object_set(subobj, key1, new);
}
json_t * json_load_cli(int argc, char *argv[])
{
char *opt;
char *key = NULL;
char *value = NULL;
char *sep, *cpy;
json_t *json = json_object();
for (int i = 1; i < argc; i++) {
opt = argv[i];
/* Long Option */
if (opt[0] == '-' && opt[1] == '-') {
/* Option without value? Abort. */
if (key != NULL)
return NULL;
key = opt + 2;
/* Does this option has the form "--option=value"? */
sep = strchr(key, '=');
if (sep) {
cpy = strdup(key);
key = strtok(cpy, "=");
value = strtok(NULL, "");
json_object_extend_key_value(json, key, value);
free(cpy);
key = NULL;
}
}
/* Value */
else {
/* Value without key. Abort. */
if (key == NULL)
return NULL;
value = opt;
json_object_extend_key_value(json, key, value);
key = NULL;
}
}
return json;
}
int json_object_extend(json_t *obj, json_t *merge)
{
const char *key;
void *tmp;
int ret;
json_t *merge_value, *obj_value;
if (!json_is_object(obj) || !json_is_object(merge))
return -1;
json_object_foreach_safe(merge, tmp, key, merge_value) {
obj_value = json_object_get(obj, key);
if (obj_value && json_is_object(obj_value))
ret = json_object_extend(obj_value, merge_value);
else
ret = json_object_set(obj, key, merge_value);
if (ret)
return ret;
}
return 0;
}
int json_object_extend_str(json_t *obj, const char *str)
{
char *key, *value, *cpy;
cpy = strdup(str);
key = strtok(cpy, "=");
value = strtok(NULL, "");
if (!key || !value)
return -1;
json_object_extend_key_value(obj, key, value);
free(cpy);
return 0;
}

View file

@ -55,48 +55,59 @@ int fpga_card_init(struct fpga_card *c, struct pci *pci, struct vfio_container *
return 0;
}
int fpga_card_parse(struct fpga_card *c, config_setting_t *cfg)
int fpga_card_parse(struct fpga_card *c, json_t *cfg, const char *name)
{
int ret;
const char *slot, *id, *err;
config_setting_t *cfg_ips, *cfg_slot, *cfg_id;
c->name = config_setting_name(cfg);
json_t *cfg_ips;
json_t *cfg_slot = NULL;
json_t *cfg_id = NULL;
json_error_t err;
config_setting_lookup_int(cfg, "affinity", &c->affinity);
config_setting_lookup_bool(cfg, "do_reset", &c->do_reset);
c->name = strdup(name);
ret = json_unpack_ex(cfg, &err, 0, "{ s?: i, s?: b, s?: o, s?: o, s: o }"
"affinity", &c->affinity,
"do_reset", &c->do_reset,
"slot", &cfg_slot,
"id", &cfg_id,
"ips", &cfg_ips
);
if (ret)
jerror(&err, "Failed to parse FPGA vard configuration");
cfg_slot = config_setting_get_member(cfg, "slot");
if (cfg_slot) {
slot = config_setting_get_string(cfg_slot);
const char *err, *slot;
slot = json_string_value(cfg_slot);
if (slot) {
ret = pci_device_parse_slot(&c->filter, slot, &err);
if (ret)
cerror(cfg_slot, "Failed to parse PCI slot: %s", err);
error("Failed to parse PCI slot: %s", err);
}
else
cerror(cfg_slot, "PCI slot must be a string");
error("PCI slot must be a string");
}
cfg_id = config_setting_get_member(cfg, "id");
if (cfg_id) {
id = config_setting_get_string(cfg_id);
const char *err, *id;
id = json_string_value(cfg_id);
if (id) {
ret = pci_device_parse_id(&c->filter, (char*) id, &err);
if (ret)
cerror(cfg_id, "Failed to parse PCI id: %s", err);
error("Failed to parse PCI id: %s", err);
}
else
cerror(cfg_slot, "PCI ID must be a string");
error("PCI ID must be a string");
}
cfg_ips = config_setting_get_member(cfg, "ips");
if (!cfg_ips)
cerror(cfg, "FPGA configuration is missing ips section");
for (int i = 0; i < config_setting_length(cfg_ips); i++) {
config_setting_t *cfg_ip = config_setting_get_elem(cfg_ips, i);
if (!json_is_object(cfg_ips))
error("FPGA card IPs section must be an object");
const char *name_ip;
json_t *cfg_ip;
json_object_foreach(cfg_ips, name_ip, cfg_ip) {
const char *vlnv;
struct fpga_ip_type *vt;
@ -104,45 +115,45 @@ int fpga_card_parse(struct fpga_card *c, config_setting_t *cfg)
ip->card = c;
if (!config_setting_lookup_string(cfg, "vlnv", &vlnv))
cerror(cfg, "FPGA IP core %s is missing the VLNV identifier", c->name);
ret = json_unpack_ex(cfg_ip, &err, 0, "{ s: s }", "vlnv", &vlnv);
if (ret)
error("Failed to parse FPGA IP '%s' of card '%s'", name_ip, name);
vt = fpga_ip_type_lookup(vlnv);
if (!vt)
cerror(cfg, "FPGA IP core VLNV identifier '%s' is invalid", vlnv);
error("FPGA IP core VLNV identifier '%s' is invalid", vlnv);
ret = fpga_ip_init(ip, vt);
if (ret)
error("Failed to initalize FPGA IP core");
ret = fpga_ip_parse(ip, cfg_ip);
ret = fpga_ip_parse(ip, cfg_ip, name_ip);
if (ret)
cerror(cfg_ip, "Failed to parse FPGA IP core");
error("Failed to parse FPGA IP core");
list_push(&c->ips, ip);
}
c->cfg = cfg;
c->state = STATE_PARSED;
return 0;
}
int fpga_card_parse_list(struct list *cards, config_setting_t *cfg)
int fpga_card_parse_list(struct list *cards, json_t *cfg)
{
int ret;
if (!config_setting_is_group(cfg))
cerror(cfg, "FPGA configuration section must be a group");
for (int i = 0; i < config_setting_length(cfg); i++) {
config_setting_t *cfg_fpga = config_setting_get_elem(cfg, i);
if (!json_is_object(cfg))
error("FPGA card configuration section must be a JSON object");
const char *name;
json_t *cfg_fpga;
json_object_foreach(cfg, name, cfg_fpga) {
struct fpga_card *c = alloc(sizeof(struct fpga_card));
ret = fpga_card_parse(c, cfg_fpga);
ret = fpga_card_parse(c, cfg_fpga, name);
if (ret)
cerror(cfg_fpga, "Failed to parse FPGA card configuration");
error("Failed to parse FPGA card configuration");
list_push(cards, c);
}

View file

@ -20,8 +20,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#include <libconfig.h>
#include "log_config.h"
#include "log.h"
#include "plugin.h"
@ -46,34 +44,33 @@ int fpga_ip_init(struct fpga_ip *c, struct fpga_ip_type *vt)
return ret;
}
int fpga_ip_parse(struct fpga_ip *c, config_setting_t *cfg)
int fpga_ip_parse(struct fpga_ip *c, json_t *cfg, const char *name)
{
int ret;
long long baseaddr;
int ret, baseaddr = -1;
assert(c->state != STATE_STARTED && c->state != STATE_DESTROYED);
c->cfg = cfg;
c->name = strdup(name);
c->baseaddr = -1;
c->irq = -1;
c->port = -1;
c->name = config_setting_name(cfg);
if (!c->name)
cerror(cfg, "IP is missing a name");
json_error_t err;
/* Common settings */
if (config_setting_lookup_int64(cfg, "baseaddr", &baseaddr))
c->baseaddr = baseaddr;
else
c->baseaddr = -1;
ret = json_unpack_ex(cfg, &err, 0, "{ s?: i, s?: i, s?: i }",
"baseaddr", &baseaddr,
"irq", &c->irq,
"port", &c->port
);
if (ret)
jerror(&err, "Failed to parse configuration for FPGA IP '%s'", name);
if (!config_setting_lookup_int(cfg, "irq", &c->irq))
c->irq = -1;
if (!config_setting_lookup_int(cfg, "port", &c->port))
c->port = -1;
c->baseaddr = baseaddr;
/* Type sepecific settings */
ret = c->_vt && c->_vt->parse ? c->_vt->parse(c) : 0;
ret = c->_vt && c->_vt->parse ? c->_vt->parse(c, cfg) : 0;
if (ret)
error("Failed to parse settings for IP core '%s'", c->name);
error("Failed to parse settings for IP core '%s'", name);
c->state = STATE_PARSED;

View file

@ -13,33 +13,40 @@
#include "fpga/card.h"
#include "fpga/ips/dft.h"
int dft_parse(struct fpga_ip *c)
int dft_parse(struct fpga_ip *c, json_t *cfg)
{
struct dft *dft = c->_vd;
config_setting_t *cfg_harms;
int ret;
if (!config_setting_lookup_int(c->cfg, "period", &dft->period))
cerror(c->cfg, "DFT IP core requires 'period' setting");
json_t *cfg_harms;
json_error_t err;
if (!config_setting_lookup_int(c->cfg, "decimation", &dft->decimation))
cerror(c->cfg, "DFT IP core requires 'decimation' setting");
ret = json_unpack_ex(cfg, &err, 0, "{ s: i, s: i, s: o }",
"period", &dft->period,
"decimation", &dft->decimation,
"harmonics", &cfg_harms
);
if (ret)
jerror(&err, "Failed to parse configuration of FPGA IP '%s'", c->name);
cfg_harms = config_setting_get_member(c->cfg, "harmonics");
if (!cfg_harms)
cerror(c->cfg, "DFT IP core requires 'harmonics' setting!");
if (!json_is_array(cfg_harms))
error("DFT IP core requires 'harmonics' to be an array of integers!");
if (!config_setting_is_array(cfg_harms))
cerror(c->cfg, "DFT IP core requires 'harmonics' to be an array of integers!");
dft->num_harmonics = config_setting_length(cfg_harms);
dft->num_harmonics = json_array_size(cfg_harms);
if (dft->num_harmonics <= 0)
cerror(cfg_harms, "DFT IP core requires 'harmonics' to contain at least 1 integer!");
error("DFT IP core requires 'harmonics' to contain at least 1 value!");
dft->fharmonics = alloc(sizeof(float) * dft->num_harmonics);
for (int i = 0; i < dft->num_harmonics; i++)
dft->fharmonics[i] = (float) config_setting_get_int_elem(cfg_harms, i) / dft->period;
size_t index;
json_t *cfg_harm;
json_array_foreach(cfg_harms, index, cfg_harm) {
if (!json_is_real(cfg_harm))
error("DFT IP core requires all 'harmonics' values to be of floating point type");
dft->fharmonics[index] = (float) json_number_value(cfg_harm) / dft->period;
}
return 0;
}

View file

@ -91,16 +91,21 @@ ssize_t fifo_read(struct fpga_ip *c, char *buf, size_t len)
return nextlen;
}
int fifo_parse(struct fpga_ip *c)
int fifo_parse(struct fpga_ip *c, json_t *cfg)
{
struct fifo *fifo = c->_vd;
int baseaddr_axi4;
int baseaddr_axi4 = -1, ret;
if (config_setting_lookup_int(c->cfg, "baseaddr_axi4", &baseaddr_axi4))
fifo->baseaddr_axi4 = baseaddr_axi4;
else
fifo->baseaddr_axi4 = -1;
json_error_t err;
fifo->baseaddr_axi4 = -1;
ret = json_unpack_ex(cfg, &err, 0, "{ s?: i }", "baseaddr_axi4", &baseaddr_axi4);
if (ret)
jerror(&err, "Failed to parse configuration of FPGA IP '%s'", c->name);
fifo->baseaddr_axi4 = baseaddr_axi4;
return 0;
}

View file

@ -18,7 +18,7 @@
#include "fpga/card.h"
#include "fpga/ips/model.h"
static int model_param_destroy(struct model_param *p)
static int model_parameter_destroy(struct model_parameter *p)
{
free(p->name);
@ -64,7 +64,7 @@ static int model_xsg_map_parse(uint32_t *map, size_t len, struct list *parameter
if (length < 4)
break; /* block is to small to describe a gateway */
struct model_param *e, *p = alloc(sizeof(struct model_param));
struct model_parameter *e, *p = alloc(sizeof(struct model_parameter));
p->name = copy_string(3);
p->default_value.flt = *((float *) &data[1]);
@ -75,7 +75,7 @@ static int model_xsg_map_parse(uint32_t *map, size_t len, struct list *parameter
e = list_lookup(parameters, p->name);
if (e)
model_param_update(e, p);
model_parameter_update(e, p);
else
list_push(parameters, p);
break;
@ -133,28 +133,40 @@ static int model_xsg_map_read(uint32_t *map, size_t len, void *baseaddr)
return i;
}
int model_parse(struct fpga_ip *c)
int model_parse(struct fpga_ip *c, json_t *cfg)
{
struct model *m = c->_vd;
config_setting_t *cfg_params, *cfg_param;
int ret;
json_t *cfg_params;
json_error_t err;
if (strcmp(c->vlnv.library, "hls") == 0)
m->type = MODEL_TYPE_HLS;
else if (strcmp(c->vlnv.library, "sysgen") == 0)
m->type = MODEL_TYPE_XSG;
else
cerror(c->cfg, "Invalid model type: %s", c->vlnv.library);
error("Unsupported model type: %s", c->vlnv.library);
ret = json_unpack_ex(cfg, &err, 0, "{ s?: o }", "parameters", &cfg_params);
if (ret)
jerror(&err, "Failed to parse configuration of FPGA IP '%s'", c->name);
cfg_params = config_setting_get_member(c->cfg, "parameters");
if (cfg_params) {
for (int i = 0; i < config_setting_length(cfg_params); i++) {
cfg_param = config_setting_get_elem(cfg_params, i);
if (!json_is_object(cfg_params))
error("Setting 'parameters' must be a JSON object");
struct model_param *p = alloc(sizeof(struct model_param));
const char *name;
json_t *value;
json_object_foreach(cfg_params, name, value) {
if (!json_is_real(value))
error("Parameters of FPGA IP '%s' must be of type floating point", c->name);
p->name = config_setting_name(cfg_param);
p->default_value.flt = config_setting_get_float(cfg_param);
struct model_parameter *p = alloc(sizeof(struct model_parameter));
p->name = strdup(name);
p->default_value.flt = json_real_value(value);
list_push(&m->parameters, p);
}
@ -206,12 +218,12 @@ int model_init(struct fpga_ip *c)
/* Set default values for parameters */
for (size_t i = 0; i < list_length(&m->parameters); i++) {
struct model_param *p = list_at(&m->parameters, i);
struct model_parameter *p = list_at(&m->parameters, i);
p->ip = c;
if (p->direction == MODEL_PARAM_IN) {
model_param_write(p, p->default_value.flt);
if (p->direction == MODEL_PARAMETER_IN) {
model_parameter_write(p, p->default_value.flt);
info("Set parameter '%s' updated to default value: %f", p->name, p->default_value.flt);
}
}
@ -226,7 +238,7 @@ int model_destroy(struct fpga_ip *c)
{
struct model *m = c->_vd;
list_destroy(&m->parameters, (dtor_cb_t) model_param_destroy, true);
list_destroy(&m->parameters, (dtor_cb_t) model_parameter_destroy, true);
list_destroy(&m->infos, (dtor_cb_t) model_info_destroy, true);
if (m->xsg.map != NULL)
@ -245,9 +257,9 @@ void model_dump(struct fpga_ip *c)
{ INDENT
info("Parameters:");
for (size_t i = 0; i < list_length(&m->parameters); i++) { INDENT
struct model_param *p = list_at(&m->parameters, i);
struct model_parameter *p = list_at(&m->parameters, i);
if (p->direction == MODEL_PARAM_IN)
if (p->direction == MODEL_PARAMETER_IN)
info("%#jx: %s (%s) = %.3f %s %u",
p->offset,
p->name,
@ -256,7 +268,7 @@ void model_dump(struct fpga_ip *c)
param_type[p->type],
p->binpt
);
else if (p->direction == MODEL_PARAM_OUT)
else if (p->direction == MODEL_PARAMETER_OUT)
info("%#jx: %s (%s)",
p->offset,
p->name,
@ -273,52 +285,52 @@ void model_dump(struct fpga_ip *c)
}
}
int model_param_read(struct model_param *p, double *v)
int model_parameter_read(struct model_parameter *p, double *v)
{
struct fpga_ip *c = p->ip;
union model_param_value *ptr = (union model_param_value *) (c->card->map + c->baseaddr + p->offset);
union model_parameter_value *ptr = (union model_parameter_value *) (c->card->map + c->baseaddr + p->offset);
switch (p->type) {
case MODEL_PARAM_TYPE_UFIX:
case MODEL_PARAMETER_TYPE_UFIX:
*v = (double) ptr->ufix / (1 << p->binpt);
break;
case MODEL_PARAM_TYPE_FIX:
case MODEL_PARAMETER_TYPE_FIX:
*v = (double) ptr->fix / (1 << p->binpt);
break;
case MODEL_PARAM_TYPE_FLOAT:
case MODEL_PARAMETER_TYPE_FLOAT:
*v = (double) ptr->flt;
break;
case MODEL_PARAM_TYPE_BOOLEAN:
case MODEL_PARAMETER_TYPE_BOOLEAN:
*v = (double) ptr->ufix ? 1 : 0;
}
return 0;
}
int model_param_write(struct model_param *p, double v)
int model_parameter_write(struct model_parameter *p, double v)
{
struct fpga_ip *c = p->ip;
union model_param_value *ptr = (union model_param_value *) (c->card->map + c->baseaddr + p->offset);
union model_parameter_value *ptr = (union model_parameter_value *) (c->card->map + c->baseaddr + p->offset);
switch (p->type) {
case MODEL_PARAM_TYPE_UFIX:
case MODEL_PARAMETER_TYPE_UFIX:
ptr->ufix = (uint32_t) (v * (1 << p->binpt));
break;
case MODEL_PARAM_TYPE_FIX:
case MODEL_PARAMETER_TYPE_FIX:
ptr->fix = (int32_t) (v * (1 << p->binpt));
break;
case MODEL_PARAM_TYPE_FLOAT:
case MODEL_PARAMETER_TYPE_FLOAT:
ptr->flt = (float) v;
break;
case MODEL_PARAM_TYPE_BOOLEAN:
case MODEL_PARAMETER_TYPE_BOOLEAN:
ptr->bol = (bool) v;
break;
}
@ -326,10 +338,10 @@ int model_param_write(struct model_param *p, double v)
return 0;
}
void model_param_add(struct fpga_ip *c, const char *name, enum model_param_direction dir, enum model_param_type type)
void model_parameter_add(struct fpga_ip *c, const char *name, enum model_parameter_direction dir, enum model_parameter_type type)
{
struct model *m = c->_vd;
struct model_param *p = alloc(sizeof(struct model_param));
struct model_parameter *p = alloc(sizeof(struct model_parameter));
p->name = strdup(name);
p->type = type;
@ -338,10 +350,10 @@ void model_param_add(struct fpga_ip *c, const char *name, enum model_param_direc
list_push(&m->parameters, p);
}
int model_param_remove(struct fpga_ip *c, const char *name)
int model_parameter_remove(struct fpga_ip *c, const char *name)
{
struct model *m = c->_vd;
struct model_param *p;
struct model_parameter *p;
p = list_lookup(&m->parameters, name);
if (!p)
@ -352,7 +364,7 @@ int model_param_remove(struct fpga_ip *c, const char *name)
return 0;
}
int model_param_update(struct model_param *p, struct model_param *u)
int model_parameter_update(struct model_parameter *p, struct model_parameter *u)
{
if (strcmp(p->name, u->name) != 0)
return -1;

View file

@ -88,43 +88,41 @@ int switch_destroy(struct fpga_ip *c)
return 0;
}
int switch_parse(struct fpga_ip *c)
int switch_parse(struct fpga_ip *c, json_t *cfg)
{
struct fpga_card *f = c->card;
struct sw *sw = c->_vd;
int ret;
size_t index;
json_error_t err;
json_t *cfg_path, *cfg_paths = NULL;
list_init(&sw->paths);
config_setting_t *cfg_sw, *cfg_path;
ret = json_unpack_ex(cfg, &err, 0, "{ s: i, s?: o }",
"num_ports", &sw->num_ports,
"paths", &cfg_paths
);
if (ret)
jerror(&err, "Failed to parse configuration of FPGA IP '%s'", c->name);
if (!config_setting_lookup_int(c->cfg, "num_ports", &sw->num_ports))
cerror(c->cfg, "Switch IP '%s' requires 'num_ports' option", c->name);
cfg_sw = config_setting_get_member(f->cfg, "paths");
if (!cfg_sw)
if (!cfg_paths)
return 0; /* no switch config available */
for (int i = 0; i < config_setting_length(cfg_sw); i++) {
cfg_path = config_setting_get_elem(cfg_sw, i);
if (!json_is_array(cfg_paths))
error("Setting 'paths' of FPGA IP '%s' should be an array of JSON objects", c->name);
json_array_foreach(cfg_paths, index, cfg_path) {
struct sw_path *p = alloc(sizeof(struct sw_path));
int reverse;
int reverse = 0;
if (!config_setting_lookup_bool(cfg_path, "reverse", &reverse))
reverse = 0;
if (!config_setting_lookup_string(cfg_path, "in", &p->in) &&
!config_setting_lookup_string(cfg_path, "from", &p->in) &&
!config_setting_lookup_string(cfg_path, "src", &p->in) &&
!config_setting_lookup_string(cfg_path, "source", &p->in))
cerror(cfg_path, "Path is missing 'in' setting");
if (!config_setting_lookup_string(cfg_path, "out", &p->out) &&
!config_setting_lookup_string(cfg_path, "to", &p->out) &&
!config_setting_lookup_string(cfg_path, "dst", &p->out) &&
!config_setting_lookup_string(cfg_path, "dest", &p->out) &&
!config_setting_lookup_string(cfg_path, "sink", &p->out))
cerror(cfg_path, "Path is missing 'out' setting");
ret = json_unpack_ex(cfg_path, &err, 0, "{ s?: b, s: s, s: s }",
"reverse", &reverse,
"in", &p->in,
"out", &p->out
);
if (ret)
jerror(&err, "Failed to parse path %zu of FPGA IP '%s'", index, c->name);
list_push(&sw->paths, p);

View file

@ -139,11 +139,11 @@ void hist_print(struct hist *h, int details)
hist_cnt_t missed = h->total - h->higher - h->lower;
stats("Counted values: %ju (%ju between %f and %f)", h->total, missed, h->low, h->high);
stats("Highest: %f", h->highest);
stats("Lowest: %f", h->lowest);
stats("Mu: %f", hist_mean(h));
stats("Variance: %f", hist_var(h));
stats("Stddev: %f", hist_stddev(h));
stats("Highest: %g", h->highest);
stats("Lowest: %g", h->lowest);
stats("Mu: %g", hist_mean(h));
stats("Variance: %g", hist_var(h));
stats("Stddev: %g", hist_stddev(h));
if (details > 0 && h->total - h->higher - h->lower > 0) {
char *buf = hist_dump(h);
@ -212,7 +212,6 @@ char * hist_dump(struct hist *h)
return buf;
}
#ifdef WITH_JSON
json_t * hist_json(struct hist *h)
{
json_t *json_buckets, *json_hist;
@ -257,7 +256,6 @@ int hist_dump_json(struct hist *h, FILE *f)
return ret;
}
#endif /* WITH_JSON */
int hist_dump_matlab(struct hist *h, FILE *f)
{

View file

@ -21,11 +21,10 @@
*********************************************************************************/
#include <string.h>
#include <math.h>
#include <libconfig.h>
#include "timing.h"
#include "config.h"
#include "msg.h"
#include "io/msg.h"
#include "hook.h"
#include "path.h"
#include "utils.h"
@ -54,18 +53,24 @@ int hook_init(struct hook *h, struct hook_type *vt, struct path *p)
return 0;
}
int hook_parse(struct hook *h, config_setting_t *cfg)
int hook_parse(struct hook *h, json_t *cfg)
{
int ret;
json_error_t err;
assert(h->state != STATE_DESTROYED);
config_setting_lookup_int(cfg, "priority", &h->priority);
ret = json_unpack_ex(cfg, &err, 0, "{ s?: i }",
"priority", &h->priority
);
if (ret)
jerror(&err, "Failed to parse configuration of hook '%s'", plugin_name(h->_vt));
ret = h->_vt->parse ? h->_vt->parse(h, cfg) : 0;
if (ret)
return ret;
h->cfg = cfg;
h->state = STATE_PARSED;
return 0;
@ -83,21 +88,11 @@ int hook_parse_cli(struct hook *h, int argc, char *argv[])
h->state = STATE_PARSED;
}
else {
config_t cfg;
config_setting_t *cfg_root;
h->cfg = json_load_cli(argc, argv);
if (!h->cfg)
return -1;
config_init(&cfg);
ret = config_read_cli(&cfg, argc, argv);
if (ret)
goto out;
cfg_root = config_root_setting(&cfg);
ret = hook_parse(h, cfg_root);
out:
config_destroy(&cfg);
ret = hook_parse(h, h->cfg);
}
return ret;
@ -149,23 +144,23 @@ int hook_restart(struct hook *h)
return h->_vt->restart ? h->_vt->restart(h) : 0;
}
int hook_read(struct hook *h, struct sample *smps[], size_t *cnt)
int hook_read(struct hook *h, struct sample *smps[], unsigned *cnt)
{
debug(LOG_HOOK | 10, "Running hook %s: type=read, priority=%d, cnt=%zu", plugin_name(h->_vt), h->priority, *cnt);
debug(LOG_HOOK | 10, "Running hook %s: type=read, priority=%d, cnt=%u", plugin_name(h->_vt), h->priority, *cnt);
return h->_vt->read ? h->_vt->read(h, smps, cnt) : 0;
}
int hook_write(struct hook *h, struct sample *smps[], size_t *cnt)
int hook_write(struct hook *h, struct sample *smps[], unsigned *cnt)
{
debug(LOG_HOOK | 10, "Running hook %s: type=write, priority=%d, cnt=%zu", plugin_name(h->_vt), h->priority, *cnt);
debug(LOG_HOOK | 10, "Running hook %s: type=write, priority=%d, cnt=%u", plugin_name(h->_vt), h->priority, *cnt);
return h->_vt->write ? h->_vt->write(h, smps, cnt) : 0;
}
size_t hook_read_list(struct list *hs, struct sample *smps[], size_t cnt)
int hook_read_list(struct list *hs, struct sample *smps[], unsigned cnt)
{
size_t ret;
unsigned ret;
for (size_t i = 0; i < list_length(hs); i++) {
struct hook *h = list_at(hs, i);
@ -180,9 +175,9 @@ size_t hook_read_list(struct list *hs, struct sample *smps[], size_t cnt)
return cnt;
}
size_t hook_write_list(struct list *hs, struct sample *smps[], size_t cnt)
int hook_write_list(struct list *hs, struct sample *smps[], unsigned cnt)
{
size_t ret;
unsigned ret;
for (size_t i = 0; i < list_length(hs); i++) {
struct hook *h = list_at(hs, i);
@ -205,38 +200,36 @@ int hook_cmp_priority(const void *a, const void *b)
return ha->priority - hb->priority;
}
int hook_parse_list(struct list *list, config_setting_t *cfg, struct path *o)
int hook_parse_list(struct list *list, json_t *cfg, struct path *o)
{
struct plugin *p;
if (!json_is_array(cfg))
error("Hooks must be configured as a list of objects");
int ret;
const char *type;
size_t index;
json_t *cfg_hook;
json_array_foreach(cfg, index, cfg_hook) {
int ret;
const char *type;
struct plugin *p;
json_error_t err;
if (!config_setting_is_list(cfg))
cerror(cfg, "Hooks must be configured as a list of objects");
for (int i = 0; i < config_setting_length(cfg); i++) {
config_setting_t *cfg_hook = config_setting_get_elem(cfg, i);
if (!config_setting_is_group(cfg_hook))
cerror(cfg_hook, "The 'hooks' setting must be an array of strings.");
if (!config_setting_lookup_string(cfg_hook, "type", &type))
cerror(cfg_hook, "Missing setting 'type' for hook");
ret = json_unpack_ex(cfg_hook, &err, 0, "{ s: s }", "type", &type);
if (ret)
jerror(&err, "Failed to parse hook");
p = plugin_lookup(PLUGIN_TYPE_HOOK, type);
if (!p)
continue; /* We ignore all non hook settings in this libconfig object setting */
jerror(&err, "Unkown hook type '%s'", type);
struct hook *h = alloc(sizeof(struct hook));
ret = hook_init(h, &p->hook, o);
if (ret)
cerror(cfg_hook, "Failed to initialize hook");
jerror(&err, "Failed to initialize hook");
ret = hook_parse(h, cfg_hook);
if (ret)
cerror(cfg_hook, "Failed to parse hook configuration");
jerror(&err, "Failed to parse hook configuration");
list_push(list, h);
}

View file

@ -49,29 +49,33 @@ static int convert_init(struct hook *h)
return 0;
}
static int convert_parse(struct hook *h, config_setting_t *cfg)
static int convert_parse(struct hook *h, json_t *cfg)
{
struct convert *p = h->_vd;
const char *mode;
int ret;
json_error_t err;
const char *mode = NULL;
config_setting_lookup_float(cfg, "scale", &p->scale);
config_setting_lookup_int64(cfg, "mask", &p->mask);
if (!config_setting_lookup_string(cfg, "mode", &mode))
cerror(cfg, "Missing setting 'mode' for hook '%s'", plugin_name(h->_vt));
ret = json_unpack_ex(cfg, &err, 0, "{ s?: F, s?: i, s: s }",
"scale", &p->scale,
"mask", &p->mask,
"mode", &mode
);
if (ret)
jerror(&err, "Failed to parse configuration of hook '%s'", plugin_name(h->_vt));
if (!strcmp(mode, "fixed"))
p->mode = TO_FIXED;
else if (!strcmp(mode, "float"))
p->mode = TO_FLOAT;
else
error("Invalid parameter '%s' for hook 'convert'", mode);
error("Invalid 'mode' setting '%s' for hook '%s'", mode, plugin_name(h->_vt));
return 0;
}
static int convert_read(struct hook *h, struct sample *smps[], size_t *cnt)
static int convert_read(struct hook *h, struct sample *smps[], unsigned *cnt)
{
struct convert *p = h->_vd;

View file

@ -41,20 +41,23 @@ static int decimate_init(struct hook *h)
return 0;
}
static int decimate_parse(struct hook *h, config_setting_t *cfg)
static int decimate_parse(struct hook *h, json_t *cfg)
{
struct decimate *p = h->_vd;
if (!cfg)
error("Missing configuration for hook: '%s'", plugin_name(h->_vt));
int ret;
json_error_t err;
if (!config_setting_lookup_int(cfg, "ratio", &p->ratio))
cerror(cfg, "Missing setting 'ratio' for hook '%s'", plugin_name(h->_vt));
ret = json_unpack_ex(cfg, &err, 0, "{ s: i }",
"ratio", &p->ratio
);
if (ret)
jerror(&err, "Failed to parse configuration of hook '%s'", plugin_name(h->_vt));
return 0;
}
static int decimate_read(struct hook *h, struct sample *smps[], size_t *cnt)
static int decimate_read(struct hook *h, struct sample *smps[], unsigned *cnt)
{
struct decimate *p = h->_vd;

View file

@ -53,7 +53,7 @@ static int drop_stop(struct hook *h)
return 0;
}
static int drop_read(struct hook *h, struct sample *smps[], size_t *cnt)
static int drop_read(struct hook *h, struct sample *smps[], unsigned *cnt)
{
int i, ok, dist;

View file

@ -29,7 +29,7 @@
#include "timing.h"
#include "sample.h"
int fix_ts_read(struct hook *h, struct sample *smps[], size_t *cnt)
int fix_ts_read(struct hook *h, struct sample *smps[], unsigned *cnt)
{
struct timespec now;

View file

@ -31,15 +31,52 @@
#include "timing.h"
#include "sample.h"
#define CALC_GPS_NTP_DELAY 0 /* @todo move to global config file */
#define GPS_NTP_DELAY_WIN_SIZE 16
static int64_t jitter_val[GPS_NTP_DELAY_WIN_SIZE] = {0};
static int64_t delay_series[GPS_NTP_DELAY_WIN_SIZE] = {0};
static int64_t moving_avg[GPS_NTP_DELAY_WIN_SIZE] = {0};
static int64_t moving_var[GPS_NTP_DELAY_WIN_SIZE] = {0};
static int64_t delay_mov_sum = 0, delay_mov_sum_sqrd = 0;
static int curr_count = 0;
struct jitter_calc {
int64_t *jitter_val;
int64_t *delay_series;
int64_t *moving_avg;
int64_t *moving_var;
int64_t delay_mov_sum;
int64_t delay_mov_sum_sqrd;
int curr_count;
};
int jitter_calc_init(struct hook *h)
{
struct jitter_calc *j = h->_vd;
size_t sz = GPS_NTP_DELAY_WIN_SIZE * sizeof(int64_t);
j->jitter_val = alloc(sz);
j->delay_series = alloc(sz);
j->moving_avg = alloc(sz);
j->moving_var = alloc(sz);
memset(j->jitter_val, 0, sz);
memset(j->delay_series, 0, sz);
memset(j->moving_avg, 0, sz);
memset(j->moving_var, 0, sz);
j->delay_mov_sum = 0;
j->delay_mov_sum_sqrd = 0;
j->curr_count = 0;
return 0;
}
int jitter_calc_deinit(struct hook *h)
{
struct jitter_calc *j = h->_vd;
free(j->jitter_val);
free(j->delay_series);
free(j->moving_avg);
free(j->moving_var);
return 0;
}
/**
* Hook to calculate jitter between GTNET-SKT GPS timestamp and Villas node NTP timestamp.
@ -49,9 +86,10 @@ static int curr_count = 0;
* is high (i.e. several mins depending on GPS_NTP_DELAY_WIN_SIZE),
* the variance value will overrun the 64bit value.
*/
int hook_jitter_ts(struct hook *h, struct sample *smps[], size_t *cnt)
int jitter_calc_read(struct hook *h, struct sample *smps[], unsigned *cnt)
{
/* @todo save data for each node, not just displaying on the screen, doesn't work for more than one node!!! */
struct jitter_calc *j = h->_vd;
struct timespec now = time_now();
int64_t delay_sec, delay_nsec, curr_delay_us;
@ -60,44 +98,45 @@ int hook_jitter_ts(struct hook *h, struct sample *smps[], size_t *cnt)
delay_nsec = now.tv_nsec - smps[i]->ts.origin.tv_nsec;
/* Calc on microsec instead of nenosec delay as variance formula overflows otherwise.*/
curr_delay_us = delay_sec*1000000 + delay_nsec/1000;
curr_delay_us = delay_sec * 1000000 + delay_nsec / 1000;
delay_mov_sum = delay_mov_sum + curr_delay_us - delay_series[curr_count];
moving_avg[curr_count] = delay_mov_sum/(GPS_NTP_DELAY_WIN_SIZE); /* Will be valid after GPS_NTP_DELAY_WIN_SIZE initial values */
j->delay_mov_sum = j->delay_mov_sum + curr_delay_us - j->delay_series[j->curr_count];
j->moving_avg[j->curr_count] = j->delay_mov_sum / GPS_NTP_DELAY_WIN_SIZE; /* Will be valid after GPS_NTP_DELAY_WIN_SIZE initial values */
delay_mov_sum_sqrd = delay_mov_sum_sqrd + (curr_delay_us*curr_delay_us) - (delay_series[curr_count]*delay_series[curr_count]);
moving_var[curr_count] = (delay_mov_sum_sqrd - (delay_mov_sum*delay_mov_sum)/GPS_NTP_DELAY_WIN_SIZE)/(GPS_NTP_DELAY_WIN_SIZE-1);
j->delay_mov_sum_sqrd = j->delay_mov_sum_sqrd + (curr_delay_us * curr_delay_us) - (j->delay_series[j->curr_count] * j->delay_series[j->curr_count]);
j->moving_var[j->curr_count] = (j->delay_mov_sum_sqrd - (j->delay_mov_sum * j->delay_mov_sum) / GPS_NTP_DELAY_WIN_SIZE) / (GPS_NTP_DELAY_WIN_SIZE - 1);
delay_series[curr_count] = curr_delay_us; /* Update the last delay value */
j->delay_series[j->curr_count] = curr_delay_us; /* Update the last delay value */
/* Jitter calc formula as used in Wireshark according to RFC3550 (RTP)
D(i,j) = (Rj-Ri)-(Sj-Si) = (Rj-Sj)-(Ri-Si)
J(i) = J(i-1)+(|D(i-1,i)|-J(i-1))/16
*/
jitter_val[(curr_count+1)%GPS_NTP_DELAY_WIN_SIZE] = jitter_val[curr_count] + (labs(curr_delay_us) - jitter_val[curr_count])/16;
j->jitter_val[(j->curr_count + 1) % GPS_NTP_DELAY_WIN_SIZE] = j->jitter_val[j->curr_count] + (labs(curr_delay_us) - j->jitter_val[j->curr_count]) / 16;
stats("%s: jitter=%" PRId64 " usec, moving average=%" PRId64 " usec, moving variance=%" PRId64 " usec", __FUNCTION__, jitter_val[(curr_count+1)%GPS_NTP_DELAY_WIN_SIZE], moving_avg[curr_count], moving_var[curr_count]);
stats("%s: jitter=%" PRId64 " usec, moving average=%" PRId64 " usec, moving variance=%" PRId64 " usec", __FUNCTION__, j->jitter_val[(j->curr_count + 1) % GPS_NTP_DELAY_WIN_SIZE], j->moving_avg[j->curr_count], j->moving_var[j->curr_count]);
curr_count++;
if(curr_count >= GPS_NTP_DELAY_WIN_SIZE)
curr_count = 0;
j->curr_count++;
if(j->curr_count >= GPS_NTP_DELAY_WIN_SIZE)
j->curr_count = 0;
}
return 0;
}
#if CALC_GPS_NTP_DELAY == 1
static struct plugin p = {
.name = "jitter_calc",
.description = "Calc jitter, mean and variance of GPS vs NTP TS",
.type = PLUGIN_TYPE_HOOK,
.hook = {
.priority = 0,
.builtin = true,
.read = hook_jitter_ts,
.init = jitter_calc_init,
.destroy= jitter_calc_deinit,
.read = jitter_calc_read,
.size = sizeof(struct jitter_calc)
}
};
REGISTER_PLUGIN(&p)
#endif
/** @} */

View file

@ -51,15 +51,18 @@ static int map_destroy(struct hook *h)
return mapping_destroy(&p->mapping);
}
static int map_parse(struct hook *h, config_setting_t *cfg)
static int map_parse(struct hook *h, json_t *cfg)
{
int ret;
struct map *p = h->_vd;
config_setting_t *cfg_mapping;
json_error_t err;
json_t *cfg_mapping;
cfg_mapping = config_setting_lookup(cfg, "mapping");
if (!cfg_mapping || !config_setting_is_array(cfg_mapping))
return -1;
ret = json_unpack_ex(cfg, &err, 0, "{ s: o }",
"map", &cfg_mapping
);
if (ret)
jerror(&err, "Failed to parse configuration of hook '%s'", plugin_name(h->_vt));
ret = mapping_parse(&p->mapping, cfg_mapping);
if (ret)
@ -68,7 +71,7 @@ static int map_parse(struct hook *h, config_setting_t *cfg)
return 0;
}
static int map_read(struct hook *h, struct sample *smps[], size_t *cnt)
static int map_read(struct hook *h, struct sample *smps[], unsigned *cnt)
{
int ret;
struct map *p = h->_vd;

View file

@ -27,10 +27,12 @@
#include "hook.h"
#include "plugin.h"
#include "sample.h"
#include "sample_io.h"
#include "io.h"
struct print {
FILE *output;
struct io io;
struct io_format *format;
const char *uri;
};
@ -38,8 +40,8 @@ static int print_init(struct hook *h)
{
struct print *p = h->_vd;
p->output = stdout;
p->uri = NULL;
p->format = io_format_lookup("villas");
return 0;
}
@ -47,12 +49,15 @@ static int print_init(struct hook *h)
static int print_start(struct hook *h)
{
struct print *p = h->_vd;
int ret;
if (p->uri) {
p->output = fopen(p->uri, "w+");
if (!p->output)
error("Failed to open file %s for writing", p->uri);
}
ret = io_init(&p->io, p->format, IO_FORMAT_ALL);
if (ret)
return ret;
ret = io_open(&p->io, p->uri);
if (ret)
return ret;
return 0;
}
@ -60,28 +65,47 @@ static int print_start(struct hook *h)
static int print_stop(struct hook *h)
{
struct print *p = h->_vd;
int ret;
if (p->uri)
fclose(p->output);
ret = io_close(&p->io);
if (ret)
return ret;
ret = io_destroy(&p->io);
if (ret)
return ret;
return 0;
}
static int print_parse(struct hook *h, config_setting_t *cfg)
static int print_parse(struct hook *h, json_t *cfg)
{
struct print *p = h->_vd;
const char *format = NULL;
int ret;
json_error_t err;
config_setting_lookup_string(cfg, "output", &p->uri);
ret = json_unpack_ex(cfg, &err, 0, "{ s?: s, s?: s }",
"output", &p->uri,
"format", &format
);
if (ret)
jerror(&err, "Failed to parse configuration of hook '%s'", plugin_name(h->_vt));
if (format) {
p->format = io_format_lookup(format);
if (!p->format)
jerror(&err, "Invalid format '%s'", format);
}
return 0;
}
static int print_read(struct hook *h, struct sample *smps[], size_t *cnt)
static int print_read(struct hook *h, struct sample *smps[], unsigned *cnt)
{
struct print *p = h->_vd;
for (int i = 0; i < *cnt; i++)
sample_io_villas_fprint(p->output, smps[i], SAMPLE_IO_ALL);
io_print(&p->io, smps, *cnt);
return 0;
}

View file

@ -52,7 +52,7 @@ static int restart_stop(struct hook *h)
return 0;
}
static int restart_read(struct hook *h, struct sample *smps[], size_t *cnt)
static int restart_read(struct hook *h, struct sample *smps[], unsigned *cnt)
{
int i;
struct restart *r = h->_vd;

View file

@ -32,17 +32,23 @@ struct shift {
int offset;
};
static int shift_seq_parse(struct hook *h, config_setting_t *cfg)
static int shift_seq_parse(struct hook *h, json_t *cfg)
{
struct shift *p = h->_vd;
if (!config_setting_lookup_int(cfg, "offset", &p->offset))
cerror(cfg, "Missing setting 'offset' for hook '%s'", plugin_name(h->_vt));
json_error_t err;
int ret;
ret = json_unpack_ex(cfg, &err, 0, "{ s: i }",
"offset", &p->offset
);
if (ret)
jerror(&err, "Failed to parse configuration of hook '%s'", plugin_name(h->_vt));
return 0;
}
static int shift_seq_read(struct hook *h, struct sample *smps[], size_t *cnt)
static int shift_seq_read(struct hook *h, struct sample *smps[], unsigned *cnt)
{
struct shift *p = h->_vd;

View file

@ -47,13 +47,22 @@ static int shift_ts_init(struct hook *h)
return 0;
}
static int shift_ts_parse(struct hook *h, config_setting_t *cfg)
static int shift_ts_parse(struct hook *h, json_t *cfg)
{
struct shift_ts *p = h->_vd;
double offset;
const char *mode = NULL;
int ret;
json_error_t err;
const char *mode;
ret = json_unpack_ex(cfg, &err, 0, "{ s?: s, s: f }",
"mode", &mode,
"offset", &offset
);
if (ret)
jerror(&err, "Failed to parse configuration of hook '%s'", plugin_name(h->_vt));
if (config_setting_lookup_string(cfg, "mode", &mode)) {
if (mode) {
if (!strcmp(mode, "origin"))
p->mode = SHIFT_ORIGIN;
else if (!strcmp(mode, "received"))
@ -61,19 +70,15 @@ static int shift_ts_parse(struct hook *h, config_setting_t *cfg)
else if (!strcmp(mode, "sent"))
p->mode = SHIFT_SENT;
else
cerror(cfg, "Invalid mode parameter '%s' for hook '%s'", mode, plugin_name(h->_vt));
jerror(&err, "Invalid mode parameter '%s' for hook '%s'", mode, plugin_name(h->_vt));
}
double offset;
if (!config_setting_lookup_float(cfg, "offset", &offset))
cerror(cfg, "Missing setting 'offset' for hook '%s'", plugin_name(h->_vt));
p->offset = time_from_double(offset);
return 0;
}
static int shift_ts_read(struct hook *h, struct sample *smps[], size_t *cnt)
static int shift_ts_read(struct hook *h, struct sample *smps[], unsigned *cnt)
{
struct shift_ts *p = h->_vd;

View file

@ -24,8 +24,6 @@
* @{
*/
#include <libconfig.h>
#include "hook.h"
#include "plugin.h"
#include "timing.h"
@ -53,23 +51,33 @@ struct skip_first {
};
};
static int skip_first_parse(struct hook *h, config_setting_t *cfg)
static int skip_first_parse(struct hook *h, json_t *cfg)
{
struct skip_first *p = h->_vd;
double seconds;
if (config_setting_lookup_float(cfg, "seconds", &seconds)) {
int ret;
json_error_t err;
ret = json_unpack_ex(cfg, &err, 0, "{ s: F }", "seconds", &seconds);
if (!ret) {
p->seconds.wait = time_from_double(seconds);
p->mode = HOOK_SKIP_MODE_SECONDS;
}
else if (config_setting_lookup_int(cfg, "samples", &p->samples.wait)) {
p->mode = HOOK_SKIP_MODE_SAMPLES;
}
else
cerror(cfg, "Missing setting 'seconds' or 'samples' for hook '%s'", plugin_name(h->_vt));
return 0;
return 0;
}
ret = json_unpack_ex(cfg, &err, 0, "{ s: i }", "samples", &p->samples.wait);
if (!ret) {
p->mode = HOOK_SKIP_MODE_SAMPLES;
return 0;
}
jerror(&err, "Failed to parse configuration of hook '%s'", plugin_name(h->_vt));
return -1;
}
static int skip_first_restart(struct hook *h)
@ -81,7 +89,7 @@ static int skip_first_restart(struct hook *h)
return 0;
}
static int skip_first_read(struct hook *h, struct sample *smps[], size_t *cnt)
static int skip_first_read(struct hook *h, struct sample *smps[], unsigned *cnt)
{
struct skip_first *p = h->_vd;

View file

@ -25,6 +25,7 @@
*/
#include "common.h"
#include "advio.h"
#include "hook.h"
#include "plugin.h"
#include "stats.h"
@ -38,8 +39,8 @@ struct stats_collect {
int warmup;
int buckets;
FILE *output;
const char *uri;
AFILE *output;
char *uri;
};
static int stats_collect_init(struct hook *h)
@ -58,8 +59,7 @@ static int stats_collect_init(struct hook *h)
p->warmup = 500;
p->buckets = 20;
p->uri = NULL;
p->output = stdout;
return 0;
}
@ -67,6 +67,9 @@ static int stats_collect_destroy(struct hook *h)
{
struct stats_collect *p = h->_vd;
if (p->uri)
free(p->uri);
return stats_destroy(&p->stats);
}
@ -75,11 +78,11 @@ static int stats_collect_start(struct hook *h)
struct stats_collect *p = h->_vd;
if (p->uri) {
p->output = fopen(p->uri, "w+");
p->output = afopen(p->uri, "w+");
if (!p->output)
error("Failed to open file %s for writing", p->uri);
}
return stats_init(&p->stats, p->buckets, p->warmup);
}
@ -87,10 +90,10 @@ static int stats_collect_stop(struct hook *h)
{
struct stats_collect *p = h->_vd;
stats_print(&p->stats, p->output, p->format, p->verbose);
stats_print(&p->stats, p->uri ? p->output->file : stdout, p->format, p->verbose);
if (p->uri)
fclose(p->output);
afclose(p->output);
return 0;
}
@ -108,36 +111,46 @@ static int stats_collect_periodic(struct hook *h)
{
struct stats_collect *p = h->_vd;
stats_print_periodic(&p->stats, p->output, p->format, p->verbose, h->path);
stats_print_periodic(&p->stats, p->uri ? p->output->file : stdout, p->format, p->verbose, h->path);
return 0;
}
static int stats_collect_parse(struct hook *h, config_setting_t *cfg)
static int stats_collect_parse(struct hook *h, json_t *cfg)
{
struct stats_collect *p = h->_vd;
const char *format;
if (config_setting_lookup_string(cfg, "format", &format)) {
if (!strcmp(format, "human"))
p->format = STATS_FORMAT_HUMAN;
else if (!strcmp(format, "json"))
p->format = STATS_FORMAT_JSON;
else if (!strcmp(format, "matlab"))
p->format = STATS_FORMAT_MATLAB;
else
cerror(cfg, "Invalid statistic output format: %s", format);
int ret, fmt;
json_error_t err;
const char *format = NULL;
const char *uri = NULL;
ret = json_unpack_ex(cfg, &err, 0, "{ s?: s, s?: b, s?: i, s?: i, s?: s }",
"format", &format,
"verbose", &p->verbose,
"warmup", &p->warmup,
"buckets", &p->buckets,
"output", &uri
);
if (ret)
jerror(&err, "Failed to parse configuration of hook '%s'", plugin_name(h->_vt));
if (format) {
fmt = stats_lookup_format(format);
if (fmt < 0)
jerror(&err, "Invalid statistic output format: %s", format);
p->format = fmt;
}
config_setting_lookup_bool(cfg, "verbose", &p->verbose);
config_setting_lookup_int(cfg, "warmup", &p->warmup);
config_setting_lookup_int(cfg, "buckets", &p->buckets);
config_setting_lookup_string(cfg, "output", &p->uri);
if (uri)
p->uri = strdup(uri);
return 0;
}
static int stats_collect_read(struct hook *h, struct sample *smps[], size_t *cnt)
static int stats_collect_read(struct hook *h, struct sample *smps[], unsigned *cnt)
{
struct stats_collect *p = h->_vd;

View file

@ -53,35 +53,45 @@ static int stats_send_init(struct hook *h)
return 0;
}
static int stats_send_parse(struct hook *h, config_setting_t *cfg)
static int stats_send_parse(struct hook *h, json_t *cfg)
{
struct stats_send *p = h->_vd;
assert(h->path && h->path->super_node);
const char *dest, *mode;
const char *dest = NULL;
const char *mode = NULL;
if (config_setting_lookup_string(cfg, "destination", &dest)) {
int ret;
json_error_t err;
ret = json_unpack_ex(cfg, &err, 0, "{ s: s, s?: s, s?: i }"
"destination", &dest,
"mode", &mode,
"decimation", &p->decimation
);
if (ret)
jerror(&err, "Failed to parse configuration of hook '%s'", plugin_name(h->_vt));
if (dest) {
assert(h->path);
p->dest = list_lookup(&h->path->super_node->nodes, dest);
if (!p->dest)
cerror(cfg, "Invalid destination node '%s' for hook '%s'", dest, plugin_name(h->_vt));
jerror(&err, "Invalid destination node '%s' for hook '%s'", dest, plugin_name(h->_vt));
}
else
cerror(cfg, "Missing setting 'destination' for hook '%s'", plugin_name(h->_vt));
jerror(&err, "Missing setting 'destination' for hook '%s'", plugin_name(h->_vt));
if (config_setting_lookup_string(cfg, "mode", &mode)) {
if (mode) {
if (!strcmp(mode, "periodic"))
p->mode = STATS_SEND_MODE_PERIODIC;
else if (!strcmp(mode, "read"))
p->mode = STATS_SEND_MODE_READ;
else
cerror(cfg, "Invalid value '%s' for setting 'mode' of hook '%s'", mode, plugin_name(h->_vt));
jerror(&err, "Invalid value '%s' for setting 'mode' of hook '%s'", mode, plugin_name(h->_vt));
}
config_setting_lookup_int(cfg, "decimation", &p->decimation);
return 0;
}
@ -115,7 +125,7 @@ static int stats_send_periodic(struct hook *h)
return 0;
}
static int stats_send_read(struct hook *h, struct sample *smps[], size_t *cnt)
static int stats_send_read(struct hook *h, struct sample *smps[], unsigned *cnt)
{
struct stats_send *p = h->_vd;

Some files were not shown because too many files have changed in this diff Show more