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:
commit
5d0ba771c6
195 changed files with 6663 additions and 3635 deletions
|
@ -1,7 +1,3 @@
|
|||
*
|
||||
!build/release/packaging/rpm/*
|
||||
!thirdparty/libxil/
|
||||
!thirdparty/criterion/
|
||||
!thirdparty/libwebsockets/
|
||||
!thirdparty/nanomsg/
|
||||
!thirdparty/libzmq/
|
||||
!thirdparty/
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
|||
/build/
|
||||
*~
|
||||
.clang_complete
|
||||
|
|
|
@ -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
106
Dockerfile.dev-centos
Normal 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
|
|
@ -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
|
||||
|
|
14
Makefile
14
Makefile
|
@ -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
49
Makefile.complete
Normal 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
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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" }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
88
etc/js/config.js
Normal 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
65
etc/js/global.js
Normal 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
|
||||
}
|
||||
};
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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
50
include/villas/buffer.h
Normal 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);
|
|
@ -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__ */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
/** @} */
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
98
include/villas/io.h
Normal 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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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
60
include/villas/io/raw.h
Normal 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);
|
36
include/villas/io/villas.h
Normal file
36
include/villas/io/villas.h
Normal 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
146
include/villas/io_format.h
Normal 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);
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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. */
|
||||
};
|
||||
|
||||
|
|
|
@ -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)));
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
/** @} */
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
73
include/villas/nodes/test_rtt.h
Normal file
73
include/villas/nodes/test_rtt.h
Normal 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);
|
||||
|
||||
/** @} */
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
/** @} */
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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! */
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
71
include/villas/task.h
Normal 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);
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
|
@ -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));
|
|
@ -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))
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
33
lib/advio.c
33
lib/advio.c
|
@ -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
197
lib/api.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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
99
lib/buffer.c
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
12
lib/hist.c
12
lib/hist.c
|
@ -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)
|
||||
{
|
||||
|
|
81
lib/hook.c
81
lib/hook.c
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
/** @} */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
Loading…
Add table
Reference in a new issue