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

Merge branch 'feature-curlio' into develop

This commit is contained in:
Steffen Vogel 2017-03-29 04:11:50 +02:00
commit bf05909e5e
651 changed files with 16196 additions and 82567 deletions

16
.distignore Normal file
View file

@ -0,0 +1,16 @@
build
thirdparty
clients
.git
.gitmodules
.gitignore
.dockerignore
.DS_Store
*~
*.o
*.d
villas-*

View file

@ -1,4 +1,5 @@
*
!build/release/packaging/rpm/*
!thirdparty/libxil/
!thirdparty/criterion/
!thirdparty/libwebsockets/

View file

@ -2,7 +2,6 @@ variables:
# This registry is a linked docker container running on the same host
# and configured in gitlab-ci-runner config.toml as a linked service
DOCKER_REGISTRY: acs-public:5000
DOCKER_IMAGE: villas:latest
PREFIX: /usr/
stages:
@ -15,16 +14,15 @@ stages:
##############################################################################
# Build docker image which is used to build & test VILLASnode
docker-image:
docker-images:
stage: prepare
# Must match the docker version on the build machine!
before_script:
- git submodule sync --recursive
- git submodule update --recursive --init
- docker info
script:
- docker build -t $DOCKER_REGISTRY/$DOCKER_IMAGE .
- docker push $DOCKER_REGISTRY/$DOCKER_IMAGE
- make docker-dev
- docker push $DOCKER_REGISTRY/docker-dev
tags:
- shell
- linux
@ -107,14 +105,25 @@ website:
tags:
- villas-deploy
packaging:
stage: deploy
script:
- make rpm
tags:
- shell
- fedora
deliver:
stage: deploy
script:
- rsync -r build/release/doc/html/ $DEPLOY_PATH/doc/$CI_BUILD_REF_NAME/
- rsync -r build/release-coverage/coverage/ $DEPLOY_PATH/coverage/$CI_BUILD_REF_NAME/
- rsync -r build/release/doc/html/ $DEPLOY_HOST:$DEPLOY_PATH/doc/$CI_BUILD_REF_NAME/
- rsync -r build/release-coverage/coverage/ $DEPLOY_HOST:$DEPLOY_PATH/coverage/$CI_BUILD_REF_NAME/
- rsync -r build/release/packaging/rpm/RPMS $DEPLOY_HOST:$DEPLOY_PATH/../packages
- ssh $DEPLOY_HOST createrepo $DEPLOY_PATH/../packages
dependencies:
- docs
- coverage
- packaging
tags:
- villas-deploy

3
.gitmodules vendored
View file

@ -19,3 +19,6 @@
[submodule "thirdparty/libcurl"]
path = thirdparty/libcurl
url = https://github.com/curl/curl.git
[submodule "thirdparty/libxil"]
path = thirdparty/libxil
url = git@git.rwth-aachen.de:VILLASframework/libxil.git

View file

@ -1,86 +1,37 @@
# Dockerfile for VILLASnode development and testing
# Dockerfile for VILLASnode dependencies.
#
# Use this Dockerfile running:
# $ make docker
# This Dockerfile builds an image which contains all library dependencies
# and tools to build VILLASnode.
# However, VILLASnode itself it not part of the image.
#
# @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
# @copyright 2016, Institute for Automation of Complex Power Systems, EONERC
# @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
###################################################################################
FROM fedora:latest
MAINTAINER Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
# Expose ports for HTTP and WebSocket frontend
# Install dependencies
RUN dnf -y update && \
dnf -y install \
libconfig \
libnl3 \
libcurl \
jansson
# Some additional tools required for running VILLASnode
RUN dnf -y update && \
dnf -y install \
iproute \
openssl \
kernel-modules-extra
# Install our own RPMs
COPY build/release/packaging/rpm/RPMS/ /rpms/
RUN rpm -i /rpms/x86_64/{libxil,libwebsockets,villas-node,villas-node-doc}-[0-9]*; rm -rf /rpms/
# For WebSocket / API access
EXPOSE 80
EXPOSE 443
# Toolchain & dependencies
RUN dnf -y update && \
dnf -y install \
pkgconfig \
gcc \
make \
wget \
tar \
cmake \
openssl-devel \
libconfig-devel \
libnl3-devel \
libcurl-devel \
jansson-devel
# Tools for documentation
RUN dnf -y update && \
dnf -y install \
doxygen \
dia \
graphviz
# Tools for deployment / packaging
RUN dnf -y update && \
dnf -y install \
openssh-clients \
rpmdevtools \
rpm-build
# Tools to build dependencies
RUN dnf -y update && \
dnf -y install \
git \
gcc-c++ \
autoconf \
automake \
autogen \
libtool \
flex \
bison \
texinfo
# Tools for coverage and profiling
RUN dnf -y update && \
dnf -y install \
python-pip && \
pip install \
gcovr
ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig
ENV LD_LIBRARY_PATH /usr/local/lib
# Build & Install libxil
COPY thirdparty/libxil /tmp/libxil
RUN mkdir -p /tmp/libxil/build && cd /tmp/libxil/build && cmake .. && make install
# Build & Install Criterion
COPY thirdparty/criterion /tmp/criterion
RUN mkdir -p /tmp/criterion/build && cd /tmp/criterion/build && cmake .. && make install
# Build & Install libwebsockets
COPY thirdparty/libwebsockets /tmp/libwebsockets
RUN mkdir -p /tmp/libwebsockets/build && cd /tmp/libwebsockets/build && cmake .. && make install
# Cleanup intermediate files from builds
RUN rm -rf /tmp/*
WORKDIR /villas
ENTRYPOINT make clean && make install && villas node; bash
ENTRYPOINT ["villas"]

85
Dockerfile.dev Normal file
View file

@ -0,0 +1,85 @@
# 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
###################################################################################
FROM fedora:latest
MAINTAINER Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
# Toolchain
RUN dnf -y update && \
dnf -y install \
gcc pkgconfig make cmake \
git \
gcc-c++ \
autoconf automake autogen libtool \
flex bison \
texinfo
# Dependencies
RUN dnf -y update && \
dnf -y install \
openssl \
openssl-devel \
libconfig-devel \
libnl3-devel \
libcurl-devel \
jansson-devel
# Several tools only needed for developement and testing
RUN dnf -y update && \
dnf -y install \
doxygen \
dia \
graphviz \
openssh-clients \
rpmdevtools \
rpm-build \
jq \
iproute \
python-pip \
valgrind \
gdb
# Tools for debugging, coverage, profiling
RUN pip install \
gcovr
ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig
ENV LD_LIBRARY_PATH /usr/local/lib
# Build & Install libxil
COPY thirdparty/libxil /tmp/libxil
RUN mkdir -p /tmp/libxil/build && cd /tmp/libxil/build && cmake .. && make install
# Build & Install Criterion
COPY thirdparty/criterion /tmp/criterion
RUN mkdir -p /tmp/criterion/build && cd /tmp/criterion/build && cmake .. && make install
# Build & Install libwebsockets
COPY thirdparty/libwebsockets /tmp/libwebsockets
RUN mkdir -p /tmp/libwebsockets/build && cd /tmp/libwebsockets/build && cmake .. && make install
# Cleanup intermediate files from builds
RUN rm -rf /tmp/*
# Expose ports for HTTP and WebSocket frontend
EXPOSE 80
EXPOSE 443
ADD https://villas.0l.de/packages/villas.repo /etc/yum.repos.d/
ENTRYPOINT villas
WORKDIR /villas
ENTRYPOINT bash

View file

@ -51,7 +51,7 @@ PROJECT_BRIEF = "Connecting real-time power grid simulation equipment"
# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
# the logo to the output directory.
PROJECT_LOGO = doc/pictures/acs_eonerc_logo.svg
PROJECT_LOGO = doc/pictures/villas_node.svg
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
# into which the generated documentation will be written. If a relative path is
@ -765,7 +765,7 @@ WARN_LOGFILE = doc/warnings.log
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
INPUT = src/ lib/ include/ doc/
INPUT = README.md CONTRIBUTING.md COPYING.md src/ lib/ tests/ include/ doc/
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@ -906,7 +906,7 @@ FILTER_SOURCE_PATTERNS =
# (index.html). This can be useful if you have a project on for instance GitHub
# and want to reuse the introduction page also for the doxygen output.
USE_MDFILE_AS_MAINPAGE = doc/Mainpage.md
USE_MDFILE_AS_MAINPAGE = README.md
#---------------------------------------------------------------------------
# Configuration options related to source browsing
@ -1126,7 +1126,7 @@ HTML_EXTRA_STYLESHEET = doc/theme/style.css
# files will be copied as-is; there are no commands or markers available.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_FILES =
HTML_EXTRA_FILES = doc/pictures/acs_eonerc_logo.svg
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
# will adjust the colors in the style sheet and background images according to
@ -2196,7 +2196,7 @@ GROUP_GRAPHS = YES
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
UML_LOOK = NO
UML_LOOK = YES
# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
# class node. If there are many fields or methods and many nodes the graph may

View file

@ -15,7 +15,7 @@
###################################################################################
# Project modules
MODULES = lib plugins src tests thirdparty tools
MODULES = lib plugins src tests thirdparty tools packaging doc etc web
# Default prefix for install target
PREFIX ?= /usr/local
@ -28,20 +28,12 @@ V ?= 2
# Common flags
LDLIBS =
CFLAGS += -std=c11 -Iinclude -Iinclude/villas -I. -MMD -mcx16
CFLAGS += -I. -Iinclude -Iinclude/villas
CFLAGS += @$(BUILDDIR)/defines
CFLAGS += -std=c11 -MMD -mcx16
CFLAGS += -Wall -Werror -fdiagnostics-color=auto
CFLAGS += -D_POSIX_C_SOURCE=200809L -D_GNU_SOURCE=1 -DV=$(V)
LDFLAGS += -L$(BUILDDIR)
ifdef CI
CFLAGS += -D_GIT_REV='"${CI_BUILD_REF:0:7}~ci"'
else
GIT = $(shell type -p git)
ifneq ($(GIT),)
CFLAGS += -D_GIT_REV='"$(shell git rev-parse --short HEAD)"'
endif
endif
# We must compile without optimizations for gcov!
ifdef DEBUG
CFLAGS += -O0 -g
@ -69,49 +61,72 @@ ifdef COVERAGE
VARIANTS += coverage
endif
SPACE :=
SPACE +=
BUILDDIR := $(BUILDDIR)/$(subst $(SPACE),-,$(strip $(VARIANTS)))
EMPTY :=
SPACE := $(EMPTY) $(EMPTY)
VARIANT = $(subst $(SPACE),-,$(strip $(VARIANTS)))
BUILDDIR := $(BUILDDIR)/$(VARIANT)
SRCDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
# Add git revision and build variant defines
VERSION = $(shell git describe --tags --abbrev=0 --match v*)
VERSION_NUM = $(shell VERSION=$(VERSION); echo $${VERSION:1})
ifdef CI
GIT_REV = ${CI_BUILD_REF:0:7}
VARIANT = ci-$(VARIANT)
else
GIT_REV = $(shell REV=$$(git rev-parse HEAD); echo $${REV:0:7})
endif
# pkg-config dependencies
PKGS = libconfig
######## Targets ########
# Add flags by pkg-config
CFLAGS += $(addprefix -DWITH_, $(shell echo ${PKGS} | tr a-z- A-Z_ | tr -dc ' A-Z0-9_' ))
CFLAGS += $(shell pkg-config --cflags ${PKGS})
LDLIBS += $(shell pkg-config --libs ${PKGS})
all: src plugins | lib
all: src plugins tools
# Build all variants: debug, coverage, ...
everything:
$(MAKE) RELEASE=1
$(MAKE) DEBUG=1
$(MAKE) COVERAGE=1
$(MAKE) PROFILE=1
$(MAKE) doc
$(MAKE) tests
install: $(addprefix install-,$(MODULES))
clean: $(addprefix clean-,$(MODULES))
docker:
docker build -t villas .
docker run -it -p 80:80 -p 443:443 -p 1234:1234 --privileged --cap-add sys_nic --ulimit memlock=1073741824 --security-opt seccomp:unconfined -v $(PWD):/villas villas
doc: | $(BUILDDIR)/doc/
( cat Doxyfile ; echo "OUTPUT_DIRECTORY=$(BUILDDIR)/doc/" ) | doxygen -
# Create non-existent directories
.SECONDEXPANSION:
.PRECIOUS: %/
%/:
mkdir -p $@
.PHONY: all everything clean install docker doc
.PRECIOUS: %/
.SECONDEXPANSION:
define DEFINES
-DV=$(V)
-DPLUGIN_PATH=\"$(PREFIX)/share/villas/node/plugins\"
-DWEB_PATH=\"$(PREFIX)/share/villas/node/web\"
-DSYSFS_PATH=\"/sys\"
-DPROCFS_PATH=\"/proc\"
-DBUILDID=\"$(VERSION)-$(GIT_REV)-$(VARIANT)\"
-D_POSIX_C_SOURCE=200809L
-D_GNU_SOURCE=1
endef
export DEFINES
$(BUILDDIR)/defines: | $$(dir $$@)
echo "$${DEFINES}" > $@
echo -e "$(addprefix \n-DWITH_, $(shell echo ${PKGS} | tr a-z- A-Z_ | tr -dc ' A-Z0-9_' ))" >> $@
echo -e "$(addprefix \n-DWITH_, $(shell echo ${LIB_PKGS} | tr a-z- A-Z_ | tr -dc ' A-Z0-9_' ))" >> $@
install: $(addprefix install-,$(filter-out thirdparty doc,$(MODULES)))
clean: $(addprefix clean-,$(filter-out thirdparty doc,$(MODULES)))
.PHONY: all everything clean install
-include $(wildcard $(BUILDDIR)/**/*.d)
-include $(addsuffix /Makefile.inc,$(MODULES))
-include $(addsuffix /Makefile.inc,$(MODULES))

View file

@ -1,16 +1,53 @@
# VILLASnode [![build status](https://git.rwth-aachen.de/acs/VILLASnode/badges/develop/build.svg)](https://git.rwth-aachen.de/acs/VILLASnode/commits/develop) [![coverage report](https://git.rwth-aachen.de/acs/VILLASnode/badges/develop/coverage.svg)](https://git.rwth-aachen.de/acs/VILLASnode/commits/develop)
# VILLASnode
[![build status](https://git.rwth-aachen.de/VILLASframework/VILLASnode/badges/develop/build.svg)](https://git.rwth-aachen.de/acs/VILLASnode/commits/develop)
[![coverage report](https://git.rwth-aachen.de/VILLASframework/VILLASnode/badges/develop/coverage.svg)](https://git.rwth-aachen.de/acs/VILLASnode/commits/develop)
This is VILLASnode, a gateway for processing and forwardning simulation data between real-time simulators.
VILLASnode is a client/server application to connect simulation equipment and software such as:
- OPAL-RT eMegaSim,
- RTDS GTFPGA cards,
- Simulink,
- LabView,
- and FPGA models.
## Documentation
It's designed with a focus on very low latency to achieve almost realtime exchange of simulation data.
VILLASnode is used in distributed- and co-simulation scenarios and developed for the field of power grid simulation at the EON Energy Research Center in Aachen, Germany.
The documentation for this software is available at [doc/Mainpage](doc/Mainpage.md).
## Overview
The full documentation is available at [doc/html/index.html](doc/html/index.html) after running `doxygen` in this directory.
The project consists of a server daemon and several client modules which are documented here.
You can access the prebuild documentation at: http://134.130.169.32/villas/node/ (available only from ACS *Office network* and *Simulation Network*).
[TOC]
### Server
The server simply acts as a gateway to forward simulation data from one client to another.
Furthermore, it collects statistics, monitors the quality of service and handles encryption or tunneling through VPNs.
For optimal performance the server is implemented in low-level C and makes use of several Linux-specific realtime features.
The primary design goal was to make the behaviour of the system as deterministic as possible.
Therefore, it's advisable to run the server component on a [PREEMPT_RT](https://rt.wiki.kernel.org/index.php/CONFIG_PREEMPT_RT_Patch) patched version of Linux. In our environment, we use Fedora-based distribution which has been stripped to the bare minimum (no GUI, only a few background processes).
The server is a multi-threaded application.
### Clients
There are two types of clients:
1. The server handles diffrent types of nodes.
The most commonly used node-type is the 'socket' type which allows communication over network links (UDP, raw IP, raw Ethernet frames).
But there are also other specialized node types to retreive or send data to equipemnt, which is directly connected to or running on the server itself.
An example for such a node is the 'gtfpga' type which directly fetches and pushes data to a PCIe card.
Or the 'file' type which logs or replays simulation data from the harddisk.
2. An other way to connect simulation equipment is by using a client-application which itself sends the data over the network to VILLASnode.
In this scenario, VILLASnode uses the 'socket' node-type to communicate with the client-application.
Usually, new clients / equipemnt should be implemented as a new node-type as part of VILLASnode.
Using a dedicated client-application which communicates via the 'socket' type is deprecated because it leads to code duplication.
## Contact

View file

@ -2,7 +2,7 @@
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2016, Institute for Automation of Complex Power Systems, EONERC
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
*********************************************************************************/
#ifndef _MSG_H_

View file

@ -2,7 +2,7 @@
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2016, Institute for Automation of Complex Power Systems, EONERC
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
*********************************************************************************/
#ifndef _MSG_FORMAT_H_

View file

@ -4,7 +4,7 @@
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @author Mathieu Dubé-Dallaire
* @copyright 2003, OPAL-RT Technologies inc
* @copyright 2016, Institute for Automation of Complex Power Systems, EONERC
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
*********************************************************************************/
#ifndef _UTILS_H_

View file

@ -1,7 +1,7 @@
/** Message related functions.
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2016, Institute for Automation of Complex Power Systems, EONERC
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
*********************************************************************************/
#ifdef __linux__

View file

@ -4,7 +4,7 @@
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @author Mathieu Dubé-Dallaire
* @copyright 2003, OPAL-RT Technologies inc
* @copyright 2016, Institute for Automation of Complex Power Systems, EONERC
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
*********************************************************************************/
#include <errno.h>

View file

@ -5,23 +5,19 @@
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2016, Institute for Automation of Complex Power Systems, EONERC
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
*********************************************************************************/
#ifndef _CONFIG_H_
#define _CONFIG_H_
#ifndef _GIT_REV
#define _GIT_REV "nogit"
#endif
/** The version number of VILLASnode */
#define VERSION "v0.6-" _GIT_REV
#pragma once
/** Default number of values in a sample */
#define DEFAULT_VALUES 64
#define DEFAULT_QUEUELEN 1024
/** Number of hugepages which are requested from the the kernel.
* @see https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt */
#define DEFAULT_NR_HUGEPAGES 25
/** Width of log output in characters */
#define LOG_WIDTH 132
@ -32,24 +28,12 @@
#define IPPROTO_VILLAS 137
#define ETH_P_VILLAS 0xBABE
#define SYSFS_PATH "/sys"
#define PROCFS_PATH "/proc"
#define USER_AGENT "VILLASnode (" BUILDID ")"
/* Required kernel version */
/*ID Required kernel version */
#define KERNEL_VERSION_MAJ 3
#define KERNEL_VERSION_MIN 6
/* Some hard-coded configuration for the FPGA benchmarks */
#define BENCH_DM 3
// 1 FIFO
// 2 DMA SG
// 3 DMA Simple
#define BENCH_RUNS 3000000
#define BENCH_WARMUP 100
#define BENCH_DM_EXP_MIN 0
#define BENCH_DM_EXP_MAX 20
/** PCIe BAR number of VILLASfpga registers */
#define FPGA_PCI_BAR 0
#define FPGA_PCI_VID_XILINX 0x10ee
@ -57,14 +41,4 @@
/** AXI Bus frequency for all components
* except RTDS AXI Stream bridge which runs at RTDS_HZ (100 Mhz) */
#define FPGA_AXI_HZ 125000000 // 125 MHz
/** Global configuration */
struct settings {
int priority; /**< Process priority (lower is better) */
int affinity; /**< Process affinity of the server and all created threads */
int debug; /**< Debug log level */
double stats; /**< Interval for path statistics. Set to 0 to disable themo disable them. */
};
#endif /* _CONFIG_H_ */
#define FPGA_AXI_HZ 125000000 // 125 MHz

54
doc/AdvancedIO.md Normal file
View file

@ -0,0 +1,54 @@
# Advanced IO {#advio}
__This page is intended for developers only__
Implements an fopen() abstraction allowing reading from URIs.
This file introduces a c library buffered I/O interface to URI reads it supports fopen(), fread(), fgets(), feof(), fclose(), rewind(). Supported functions have identical prototypes to their normal libc namesakes and are preceaded by 'a'.
Using this code you can replace your program's fopen() with afopen() and fread() with afread() and it become possible to read remote streams instead of (only) local files. Local files (ie those that can be directly fopened) will drop back to using the underlying libc implementations.
Advanced IO (ADVIO) is an extension of the standard IO features (STDIO) which can operate on files stored remotely via a variety of different protocols.
As ADVIO is built upon libcurl, it supports all of [libcurl protocols](https://curl.haxx.se/libcurl/c/CURLOPT_PROTOCOLS.html).
When openeing a file with ADVIO, the file is requested from the remote server and stored as a local temporary file on the disk.
Subsequent operations use this local copy of the file.
When closing or flushing the file, the local copy gets checked for modifications and uploaded to the remote.
There are a few points to keep in mind:
- `afflush()` uploads the file contents to its origin URI if it was changed locally.
- `afclose()` flushes (and hence uploads) the file only if it has been modified. Modifications are detected by comparing the files SHA1 hashes.
## Extensions
The following functions have been added:
- `aupload()` uploads the file irrespectively if it was changed or not.
- `adownload()` downloads the file and discards any local changes.
- `auri()` returns a pointer to the URI.
- `ahash()` returns a pointer to a
## Example
#include <advio.h>
AFILE *f;
char buf[1024];
f = afopen("ftp://user:passwd@server/path/to/file.txt", "r");
if (!f)
serror("Failed to open file");
afread(buf, 1, 1024, f);
printf("%s", buf);
afclose(f);
## Usage for VILLASnode
VILLASnode uses ADVIO currently for:
- Fetchting the configuration file. This means VILLASnode can be started like `villas-node http://server/config.conf`
- As source and destination file for the `file` node-type. Please have a look at the example configuration file `etc/advio.conf`.

View file

@ -1,12 +1,14 @@
# Concept
The server is designed around the concept of _nodes_ and _paths_.
VILLASnode is designed around the concept of _nodes_, _super-nodes_ and _paths_.
It's the task of the server to forward real-time simulation data between multiple parties.
In doing so, the server has to perform simple checks and collects statistics.
From the viewpoint of the communication parters the server is nearly transparent.
Hence, it's cruical to keep the added overhead as low as possible (in terms of latency).
## Nodes
## @subpage supernode
## @subpage node
All communication partners are represented by nodes.
@ -19,7 +21,7 @@ Possible types of nodes are:
@see node for implementation details.
## Paths
## @subpage path
A path is a **uni-directional** connection between incoming and outgoing nodes.
@ -36,6 +38,8 @@ However every path supports optional hook/callback functions which allow user-de
@see path for implementation details.
## @subpage hook
## Interface
Interfaces are used to represent physical network ports on the server.

View file

@ -1 +0,0 @@
../CONTRIBUTING.md

View file

@ -1,40 +1,54 @@
# Development
Developement is currently coordinated by Steffen Vogel <stvogel@eonerc.rwth-aachen.de> using [GitHub](http://github.com/RWTH-ACS/VILLASnode).
Developement is currently coordinated by Steffen Vogel <stvogel@eonerc.rwth-aachen.de> using [GitLab](http://git.rwth-aachen.de/VILLASframework/VILLASnode).
Please feel free to submit pull requests or bug reports.
@todo Add link to contribution guidelines
## Programming Paradigm
VILLASnode is currently written in C using the ISO C99 standard.
Yet, it is heavily structured into modules / plugins and uses a C++-like object oriented style.
In the future, VILLASnode might switch to lightweight C++.
Main _classes_ in VILLASnode are struct node, struct path, struct hook and struct api_ressource.
In order to track the life cycle of those objects, each of them has an enum state member.
The following figure illustrates the state machine which is used:
@diafile states.dia
## Shared library: libvillas
VILLASnode is split into a shared library called libvillas and a couple of executables (`villas-server`, `villas-pipe`, `villas-test`, `villas-signal`) which are linked agains the library.
VILLASnode is split into a shared library called libvillas and a couple of executables (`villas-node`, `villas-pipe`, `villas-test`, `villas-signal`, ...) which are linked against this library.
## Extensibilty
## Extensibilty / Plugins
There are two main places where VILLASnode can easily extended:
There are many places where VILLASnode can easily extended with plugins:
#### New node-type
### Example of new node type
REGISTER_NODE_TYPE(struct node_type *vt)
See `include/villas/plugin.h`
#### New hook functions
See `lib/nodes/file.c`:
/** The type of a hook defines when a hook will be exectuted. This is used as a bitmask. */
enum hook_type {
HOOK_PATH_START = 1 << 0, /**< Called whenever a path is started; before threads are created. */
[...]
HOOK_ASYNC = 1 << 6, /**< Called asynchronously with fixed rate (see path::rate). */
static struct plugin p = {
.name = "file",
.description = "support for file log / replay node type",
.type = PLUGIN_TYPE_NODE,
.node = {
.vectorize = 1,
.size = sizeof(struct file),
.reverse = file_reverse,
.parse = file_parse,
.print = file_print,
.start = file_start,
.stop = file_stop,
.read = file_read,
.write = file_write,
.instances = LIST_INIT()
}
};
HOOK_PERIODIC = 1 << 7, /**< Called periodically. Period is set by global 'stats' option in the configuration file. */
/** @{ Classes of hooks */
/** Internal hooks are mandatory. */
HOOK_INTERNAL = 1 << 16,
/** All hooks */
HOOK_ALL = HOOK_INTERNAL - 1
/** @} */
};
typedef int (*hook_cb_t)(struct path *p, struct hook *h, int when);
REGISTER_HOOK(char *name, int priority, hook_cb_t cb, enum hook_type when)
REGISTER_PLUGIN(&p)

View file

@ -15,7 +15,9 @@ We prepared a image which you can download and run out of the box:
3. Start the latest VILLASnode container by running:
$ docker run rwthacs/villas
$ git clone --recursive git@git.rwth-aachen.de:VILLASframework/VILLASnode.git
$ cd VILLASnode
$ make docker
### To be added
@ -24,8 +26,7 @@ VILLASnode ...
- is written in object-oriented C11
- is compiled with Clang / LLVM or GCC
- is fully based on open source software
- stands for Simulator-to-Simulator-Server
- is extensible with new node types
- heavily multi-threaded
- is extensible with new node types & hooks
- is heavily multi-threaded
- follows the Unix philosophy
- is separated into a library (libvillas) and a few binaries (server, pipe, test, signal) which link against the lib.
- is separated into a library (libvillas) and a few binaries (villas-server, villas-pipe, villas-test-*, villas-signal, villas-hook) which link against the lib.

View file

@ -1,10 +1,8 @@
# Setup
## Compilation
## Prerequisites
### Prerequisites
Install libraries and developement headers for:
For all features VILLASnode currently requires the following list of dependencies:
- [libconfig](http://www.hyperrealm.com/libconfig/) for parsing the configuration file.
- [libnl3](http://www.infradead.org/~tgr/libnl/) for the network communication & emulation support of the `socket` node-type.
@ -13,6 +11,10 @@ Install libraries and developement headers for:
- [libwebsockets](http://libwebsockets.org) for the `websocket` node-type.
- [libcurl](https://curl.haxx.se/libcurl/) for HTTP REST requests by the `ngsi` node-type.
There are two ways to install these dependencies:
1. You can most of the dependencies using the package manger of your Linux distribution:
Use the following command to install the dependencies under Debian-based distributions:
$ sudo apt-get install build-essential pkg-config wget tar cmake doxygen dia graphviz libconfig-dev libnl-3-dev libnl-route-3-dev libjansson-dev libcurl4-openssl-dev
@ -20,8 +22,15 @@ Use the following command to install the dependencies under Debian-based distrib
or the following line for Fedora / CentOS / Redhat systems:
$ sudo yum install gcc pkgconfig make wget tar cmake openssl-devel doxygen dia graphviz libconfig-devel libnl3-devel libcurl-devel jansson-devel
2. Alternatively, you can use the make targets `make thirdparty` and `make install-thirdparty` which will compile and install all required dependencies from source.
## Fetching VILLASnode
### Compilation
$ git clone --recursive git@git.rwth-aachen.de:VILLASframework/VILLASnode.git
$ cd VILLASnode
## Compilation
Checkout the `Makefile` and `include/config.h` for some options which have to be specified at compile time.
@ -32,7 +41,7 @@ Afterwards, start the compilation with:
Append `V=5` to `make` for a more verbose debugging output.
Append `DEBUG=1` to `make` to add debug symbols.
### Installation
## Installation
Install the files to your search path:
@ -40,10 +49,10 @@ Install the files to your search path:
Append `PREFIX=/opt/local` to change the installation destination.
### Test
## Test
Verify everything is working and required node-types are compiled-in:
$ villas server
$ villas node
Will show you the current version of the server including a list of all supported node-types.

View file

@ -1 +0,0 @@
../COPYING.md

View file

@ -1,7 +0,0 @@
# LiveCD & LiveUSB
As described in the [Tuning](Tuning) page, a carefull setup of the underlying system is essential to get optimal performance.
At ACS, we are using a Fedora-based Live system which is optomized for real-time critical workloads.
Take a look at the `contrib/liveusb/` directory for the configuration and scripts we are using.

View file

@ -1,51 +0,0 @@
VILLASnode is a client/server application to connect simulation equipment and software such as:
- OPAL-RT eMegaSim,
- RTDS GTFPGA cards,
- Simulink,
- LabView,
- and FPGA models.
It's designed with a focus on very low latency to achieve almost realtime exchange of simulation data.
VILLASnode is used in distributed- and co-simulation scenarios and developed for the field of power grid simulation at the EON Energy Research Center in Aachen, Germany.
## Overview
The project consists of a server daemon and several client modules which are documented here.
[TOC]
### Server
The server simply acts as a gateway to forward simulation data from one client to another.
Furthermore, it collects statistics, monitors the quality of service and handles encryption or tunneling through VPNs.
For optimal performance the server is implemented in low-level C and makes use of several Linux-specific realtime features.
The primary design goal was to make the behaviour of the system as deterministic as possible.
Therefore, it's advisable to run the server component on a [PREEMPT_RT](https://rt.wiki.kernel.org/index.php/CONFIG_PREEMPT_RT_Patch) patched version of Linux. In our environment, we use Fedora-based distribution which has been stripped to the bare minimum (no GUI, only a few background processes).
The server is a multi-threaded application.
### Clients
There are two types of clients:
1. The server handles diffrent types of nodes.
The most commonly used node-type is the 'socket' type which allows communication over network links (UDP, raw IP, raw Ethernet frames).
But there are also other specialized node types to retreive or send data to equipemnt, which is directly connected to or running on the server itself.
An example for such a node is the 'gtfpga' type which directly fetches and pushes data to a PCIe card.
Or the 'file' type which logs or replays simulation data from the harddisk.
2. An other way to connect simulation equipment is by using a client-application which itself sends the data over the network to VILLASnode.
In this scenario, VILLASnode uses the 'socket' node-type to communicate with the client-application.
Usually, new clients / equipemnt should be implemented as a new node-type as part of VILLASnode.
Using a dedicated client-application which communicates via the 'socket' type is deprecated because it leads to code duplication.
## Contact
This project is developed at the [Institute for Automation of Complex Power Systems](www.acs.eonerc.rwth-aachen.de) (ACS), EON Energy Research Center (EONERC) at the [RWTH University](http://www.rwth-aachen.de) in Aachen, Germany.
- Steffen Vogel <StVogel@eonerc.rwth-aachen.de>
- Marija Stevic <MStevic@eonerc.rwth-aachen.de>

11
doc/Makefile.inc Normal file
View file

@ -0,0 +1,11 @@
doc: | $(BUILDDIR)/doc/
( cat Doxyfile ; echo "OUTPUT_DIRECTORY=$(BUILDDIR)/doc/" ) | doxygen -
install-doc: doc
mkdir -p $(DESTDIR)$(PREFIX)/share/villas/node/doc/
cp -R $(BUILDDIR)/doc/html/* $(DESTDIR)$(PREFIX)/share/villas/node/doc/
clean-doc:
rm -rf $(BUILDDIR)/doc/
.PHONY: doc install-doc clean-doc

View file

@ -3,8 +3,8 @@
Every server needs clients which act as sinks / sources for simulation data. In case of VILLASnode these clients are called _nodes_.
Every node is an instance of a node-type. VILLASnode currently supports the following node-types:
#### @subpage gtfpga
- RTDS via GTFPGA and PCIexpress (Linux vfio, uio)
#### @subpage villasfpga
- VILLASfpga sub-project connect RTDS via GTFPGA and PCIexpress (Linux vfio, uio)
#### @subpage opal
- OPAL via Asynchronous Process (libOpalAsyncApi)
@ -24,4 +24,7 @@ Every node is an instance of a node-type. VILLASnode currently supports the foll
- NGSI 9/10 a.k.a. FIRWARE context broker
#### @subpage labview
- NI LabView RT-targets
- NI LabView RT-targets
#### @subpage cbuilder
- RTDS CBuilder Control System components

View file

@ -1 +0,0 @@
../README.md

101
doc/RemoteAPI.md Normal file
View file

@ -0,0 +1,101 @@
#Remote Application Programming Interface (API) {#API}
VILLASnode can be controlled remotely over a HTTP REST / WebSocket API.
This page documents this API.
## Transports
The API is accessible via multiple transports:
- via HTTP POST requests
- via a WebSocket protocol
- via a Unix socket
All transports use the same JSON based protocol to handle requests.
### HTTP REST
**Endpoint URL:** http[s]://localhost:80/api/v1
**HTTP Method:** POST
### WebSockets
**WebSocket Protocol:** `api`
### Unix socket
_This transport is not implemented yet_
## Commands
### `reload`
Restart VILLASnode with a new configuration file.
**Request:** _application/json_
{
"request": "reload",
"id": "edei4shahNgucoo7paeth8chue0iyook"
"configuration": "smb://MY-WINDOWS-HOST/SHARE1/path/to/config.conf"
}
**Response:** _application/json_
{
"response": "reload",
"id": "edei4shahNgucoo7paeth8chue0iyook",
"status": "success"
}
### `config`
Retrieve the contents of the current configuration.
**Request:** _application/json_
{
"request": "config",
"id": "oom3lie7nee4Iepieng8ae4ueToebeki"
"configuration": "smb://MY-WINDOWS-HOST/SHARE1/path/to/config.conf"
}
**Response:** _application/json_
{
"response": "config",
"id": "oom3lie7nee4Iepieng8ae4ueToebeki",
"config" : {
"nodes" : {
"socket_node" : {
"type": "socket",
"layer": "udp",
...
}
},
"paths" : [
{
"in": "socket_node",
"out": "socket_node"
}
]
}
}
### `nodes`
Get a list of currently active nodes.
_This request is not implemented yet_
### `paths`
Get a list of currently active paths.
_This request is not implemented yet_
### `status`
The the status of this VILLASnode instance.
_This request is not implemented yet_

View file

@ -1,13 +1,42 @@
# Usage {#usage}
VILLASnode (`villas node`) expects the path to a configuration file as a single argument.
The core of VILLASnode is the `villas-node` server.
The folling usage information is provided when called like `villas-node --help`;
Usage: ./villas-node CONFIG
CONFIG is a required path to a configuration file
VILLASnode 0.1-d7de19c (Jun 4 2014 02:50:13)
Copyright 2015, Institute for Automation of Complex Power Systems, EONERC
Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
Usage: villas-node [CONFIG]
CONFIG is the path to an optional configuration file
if omitted, VILLASnode will start without a configuration
and wait for provisioning over the web interface.
Supported node types:
- file : support for file log / replay node type
- cbuilder : RTDS CBuilder model
- socket : BSD network sockets
- fpga : VILLASfpga PCIe card (libxil)
- ngsi : OMA Next Generation Services Interface 10 (libcurl, libjansson)
- websocket : Send and receive samples of a WebSocket connection (libwebsockets)
Supported hooks:
- restart : Call restart hooks for current path
- print : Print the message to stdout
- decimate : Downsamping by integer factor
- fix_ts : Update timestamps of sample if not set
- skip_first : Skip the first samples
- drop : Drop messages with reordered sequence numbers
- convert : Convert message from / to floating-point / integer
- shift : Shift the origin timestamp of samples
- ts : Update timestamp of message with current time
- stats : Collect statistics for the current path
- stats_send : Send path statistics to another node
Supported API commands:
- nodes : retrieve list of all known nodes
- config : retrieve current VILLASnode configuration
- reload : restart VILLASnode with new configuration
VILLASnode v0.7-0.2-646-g59756e7-dirty-debug (built on Mar 12 2017 21:37:40)
copyright 2014-2016, Institute for Automation of Complex Power Systems, EONERC
Steffen Vogel <StVogel@eonerc.rwth-aachen.de>
The server requires root privileges for:
@ -40,7 +69,7 @@ The server requires root privileges for:
4. Edit configuration file
$ nano etc/opal-test.conf
$ nano etc/loopback.conf
- Take a look at the examples and documentation for a detailed description
- Move with: cursor keys
@ -48,7 +77,7 @@ The server requires root privileges for:
5. Start server
$ villas node etc/opal-test.conf
$ villas node etc/loopback.conf
6. Terminate server by pressing Ctrl+C

View file

@ -1,4 +1,4 @@
# Hooks
# Hooks {#hook}
Hooks are simple callback functions which are called whenever a message is processed by a path.

1
doc/concept/Node.md Normal file
View file

@ -0,0 +1 @@
#Nodes {#node}

1
doc/concept/Path.md Normal file
View file

@ -0,0 +1 @@
#Paths {#path}

1
doc/concept/SuperNode.md Normal file
View file

@ -0,0 +1 @@
# Super Nodes {#supernode}

BIN
doc/figures/states.dia Normal file

Binary file not shown.

View file

@ -1,4 +1,4 @@
# RTDS CBuilder models for VILLASnode
# RTDS CBuilder Control System Components {#cbuilder}
RTDS's Component Builder creates user-defined components including graphical representation, data menus and real-time code.

View file

@ -1,34 +0,0 @@
# VILLASfpga {#fpga}
The GTFPGA card is an extension card for simulator racks from RTDS.
The manufacturer provides a VHDL module to exchange data via fiber optics between the GTFPGA and GPC cards.
The GTFPGA card is based on the Xilinx ML507 or ML605 evaluation boards.
This board consists of a Virtex 5 FPGA, Ethernet and Fiberoptic MACs and an hard macro PowerPC core.
This node type uses the PCIexpress bus to communicate with the FPGA cards.
## Configuration
Every `gtfpga` node support the following special settings:
#### `slot`
#### `id`
#### `rate`
### Example
nodes = {
gtfpga_node = {
type = "gtfpga",
### The following settings are specific to the gtfpga node-type!! ###
slot = "01:00.0", # The PCIe slot location (see first column in 'lspci' output)
id = "1ab8:4005", # The PCIe vendor:device ID (see third column in 'lspci -n' output)
rate = 1
}
}

View file

@ -7,9 +7,9 @@ The `file` node-type can be used to log or replay samples to / from disk.
Every `file` node can be configured to only read or write or to do both at the same time.
The node configuration is splitted in to groups: `in` and `out`.
#### `path` *(string: filesystem path)*
#### `uri` *(string: libcurl URI)*
Specifies the path to a file from which is written to or read from (depending in which group is used).
Specifies the URI to a file from which is written to or read from depending in which group (`in`or `out`) is used.
See below for a description of the file format.
This setting allows to add special paceholders for time and date values.
@ -17,7 +17,7 @@ See [strftime(3)](http://man7.org/linux/man-pages/man3/strftime.3.html) for a li
**Example**:
out = "logs/measurements_%Y-%m-%d_%H-%M-%S.log"
uri = "logs/measurements_%Y-%m-%d_%H-%M-%S.log"
will create a file called: *path_of_working_directory*/logs/measurements_2015-08-09_22-20-50.log
@ -29,7 +29,7 @@ Specifies the mode which should be used to open the output file.
See [open(2)](http://man7.org/linux/man-pages/man2/open.2.html) for an explanation of allowed values.
The default value is `w+` which will start writing at the beginning of the file and create it in case it does not exist yet.
#### `epoch_mode` *("direct"|"wait" | "relative"|"absolute")*
#### `epoch_mode` *("direct" | "wait" | "relative" | "absolute")*
The *epoch* describes the point in time when the first message will be read from the file.
This setting allows to select the behaviour of the following `epoch` setting.
@ -65,7 +65,7 @@ If this setting has a non-zero value, the default behaviour is overwritten with
Only valid for the `out` group.
Splits the output file every `split` mega-byte. This setting will append the chunk number to the `path` setting.
Splits the output file every `split` mega-byte. This setting will append the chunk number to the `uri` setting.
Example: `data/my_measurements.log_001`
@ -84,7 +84,7 @@ Expects the input data in splitted format.
### The following settings are specific to the file node-type!! ###
in = {
path = "logs/input.log", # These options specify the path prefix where the the files are stored
uri = "logs/input.log", # These options specify the URI where the the files are stored
mode = "w+", # The mode in which files should be opened (see open(2))
epoch_mode = "direct" # One of: direct (default), wait, relative, absolute
@ -98,7 +98,7 @@ Expects the input data in splitted format.
splitted = false
},
out = {
path = "logs/output_%F_%T.log" # The output path accepts all format tokens of (see strftime(3))
URI = "logs/output_%F_%T.log" # The output URI accepts all format tokens of (see strftime(3))
mode = "a+" # You might want to use "a+" to append to a file
split = 100, # Split output file every 100 MB

63
doc/nodes/VILLASfpga.md Normal file
View file

@ -0,0 +1,63 @@
# VILLASfpga {#villasfpga}
__This documentation is outdated!__
The GTFPGA card is an extension card for simulator racks from RTDS.
The manufacturer provides a VHDL module to exchange data via fiber optics between the GTFPGA and GPC cards.
The GTFPGA card is based on the Xilinx ML507 or ML605 evaluation boards.
This board consists of a Virtex 5 FPGA, Ethernet and Fiberoptic MACs and an hard macro PowerPC core.
This node type uses the PCIexpress bus to communicate with the FPGA cards.
## Configuration
Every `fpga` node support the following special settings:
#### `datamover`
### Example
fpgas = {
vc707 = {
/* Card identification */
id = "10ee:7022";
slot = "01:00.0";
intc = 0x5000;
reset = 0x2000;
do_reset = true;
ips = {
switch_0 = {
vlnv = "xilinx.com:ip:axis_interconnect:2.1"
baseaddr = 0x0000;
numports = 3;
},
rtds_0 = {
vlnv = "acs.eonerc.rwth-aachen.de:user:rtds_axis:1.0"
baseaddr = 0x3000;
port = 0;
},
dma_0 = {
vlnv = "xilinx.com:ip:axi_dma:7.1";
baseaddr = 0x1000;
port = 2;
irq = 0
}
}
/* Configure switch_0 */
paths = (
{ in = "dma_0", out = "rtds_0" },
{ in = "rtds_0", out = "dma_0" }
)
}
}
nodes = {
rtds = {
datamover = "dma_0";
use_irqs = false;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View file

@ -0,0 +1,670 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="65mm"
height="65mm"
viewBox="0 0 65 65"
version="1.1"
id="svg8"
inkscape:version="0.91+devel r14647 custom"
sodipodi:docname="villas_logo.svg"
inkscape:export-filename="/Users/stv0g/workspace/rwth/villas/node/doc/figures/villas_logo.png"
inkscape:export-xdpi="104.21535"
inkscape:export-ydpi="104.21535">
<defs
id="defs2">
<linearGradient
id="linearGradient3950"
inkscape:collect="always">
<stop
id="stop3946"
offset="0"
style="stop-color:#4e7bde;stop-opacity:1" />
<stop
id="stop3948"
offset="1"
style="stop-color:#10295e;stop-opacity:1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient3895"
x1="35.103584"
y1="108.68926"
x2="60.592823"
y2="123.53505"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient3919"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient3952"
gradientUnits="userSpaceOnUse"
x1="35.103584"
y1="108.68926"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient3954"
gradientUnits="userSpaceOnUse"
x1="35.103584"
y1="108.68926"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient3956"
gradientUnits="userSpaceOnUse"
x1="35.103584"
y1="108.68926"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient3958"
gradientUnits="userSpaceOnUse"
x1="35.103584"
y1="108.68926"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient3960"
gradientUnits="userSpaceOnUse"
x1="35.103584"
y1="108.68926"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient3962"
gradientUnits="userSpaceOnUse"
x1="35.103584"
y1="108.68926"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient3964"
gradientUnits="userSpaceOnUse"
x1="35.103584"
y1="108.68926"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient3966"
gradientUnits="userSpaceOnUse"
x1="35.103584"
y1="108.68926"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient3968"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient3970"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient3972"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient3974"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient3976"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient3978"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient3980"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient3982"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient3984"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient3986"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient3988"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient3990"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient3992"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient3994"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient3996"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient3998"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient4000"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient4002"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient4004"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient4006"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient4008"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient4010"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient4012"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient4014"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient4016"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient4018"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient4020"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient4022"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient4024"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3950"
id="linearGradient4026"
gradientUnits="userSpaceOnUse"
x1="4.5107765"
y1="79.043343"
x2="60.592823"
y2="123.53505" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.9664491"
inkscape:cx="122.83465"
inkscape:cy="122.83465"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:snap-object-midpoints="true"
inkscape:window-width="1440"
inkscape:window-height="851"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-maximized="1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:snap-page="true" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-3.6238461,-74.039206)">
<g
id="g4068"
transform="matrix(0.95260701,0,0,0.95260701,3.1918604,3.2274706)">
<g
inkscape:export-ydpi="110.49"
inkscape:export-xdpi="110.49"
transform="rotate(-180,34.852865,108.45158)"
style="stroke:url(#linearGradient3895);stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="g2353">
<path
style="fill:none;fill-rule:evenodd;stroke:url(#linearGradient3952);stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 60.592821,123.53505 63.68375,115.21776 63.950211,104.29671 59.95332,93.048355 51.479919,85.005051 34.426529,79.995628 21.796364,83.139839 11.031411,92.515434 6.2351465,108.187 l 3.8370115,15.55745 9.432658,9.64959 13.696001,4.31287 11.670914,-1.54169 z"
id="path2226"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:url(#linearGradient3954);stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 33.200816,137.70691 3.677139,-14.5449 7.993774,13.00321 -1.971796,-21.5832 9.645824,3.03764 8.047064,5.91539 -0.6395,-30.486697 -13.85588,0.213167 -11.670914,-13.265893 -9.592528,23.128653 -8.633279,-7.247687 5.595644,-12.736756 15.774384,7.883425 9.272781,12.470288 13.109789,-10.445197 -4.36993,7.993777 8.366821,3.25458 -11.404452,13.32295 -5.702228,-14.12611 -27.338713,29.90049 5.329183,-30.26976 -9.432658,11.56055 L 16.20072,95.876593 6.2351464,108.187 36.877955,123.16201 26.113004,115.3814 l -11.031413,5.06272 13.322959,7.45709 -8.899734,5.49283 -4.423225,-12.94992 -8.8464446,-12.25712 9.1661946,6.49783"
id="path2228"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:url(#linearGradient3956);stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 16.20072,95.876593 -5.169309,-3.361161 23.395116,-12.519805 3.144221,11.027635 -12.736749,12.101018 1.279005,12.25712 8.579985,-11.46152 -9.85899,-0.7956 12.043956,20.03773 6.021978,-8.57999 12.683455,-13.53989 -8.739859,2.45142 -0.746088,-10.23203 17.586309,21.95624 -8.100362,-14.17563 -3.037631,16.57753"
id="path2230"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:url(#linearGradient3958);stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 36.877955,123.16201 -2.184966,-19.24213 2.877759,-12.896618 5.329185,23.558758"
id="path2232"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:url(#linearGradient3960);stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 51.479918,85.005049 4.10347,16.037081"
id="path2234"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:url(#linearGradient3962);stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 46.843529,103.49355 -12.15054,0.42633 c 0,0 8.206944,10.66215 8.206944,10.66214 0,0 9.645824,3.03764 9.645824,3.03764 l -7.674028,18.54556"
id="path2236"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:url(#linearGradient3964);stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 36.877955,123.16201 -8.473405,4.7392 4.796266,9.8057"
id="path2238"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:url(#linearGradient3966);stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 15.401341,114.68483 -0.31975,5.75929"
id="path2329"
inkscape:connector-curvature="0" />
</g>
<g
transform="rotate(-180,34.852865,108.45158)"
style="fill:url(#linearGradient3919);fill-opacity:1;stroke:none"
id="g2318">
<g
style="fill:url(#linearGradient3978);fill-opacity:1;stroke:none"
id="g2260">
<circle
r="2.6113"
cy="83.139839"
cx="21.796364"
id="path1365"
style="opacity:1;fill:url(#linearGradient3968);fill-opacity:1;stroke:none;stroke-width:0.12894738;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<circle
style="opacity:1;fill:url(#linearGradient3970);fill-opacity:1;stroke:none;stroke-width:0.12894738;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="circle1367"
cx="6.2351465"
cy="108.187"
r="2.6113" />
<circle
r="2.6113"
cy="120.44412"
cx="15.081592"
id="circle1369"
style="opacity:1;fill:url(#linearGradient3972);fill-opacity:1;stroke:none;stroke-width:0.12894738;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<circle
style="opacity:1;fill:url(#linearGradient3974);fill-opacity:1;stroke:none;stroke-width:0.12894738;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="circle1371"
cx="42.899933"
cy="114.58202"
r="2.6113" />
<circle
r="2.6113"
cy="123.53505"
cx="60.592823"
id="circle1373"
style="opacity:1;fill:url(#linearGradient3976);fill-opacity:1;stroke:none;stroke-width:0.12894738;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
</g>
<g
style="fill:url(#linearGradient4000);fill-opacity:1;stroke:none"
id="g2272">
<circle
style="opacity:1;fill:url(#linearGradient3980);fill-opacity:1;stroke:none;stroke-width:0.10526317;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="circle1375"
cx="36.877956"
cy="123.16201"
r="2.1316733" />
<circle
r="2.1316733"
cy="115.3814"
cx="26.113005"
id="circle1377"
style="opacity:1;fill:url(#linearGradient3982);fill-opacity:1;stroke:none;stroke-width:0.10526317;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<circle
style="opacity:1;fill:url(#linearGradient3984);fill-opacity:1;stroke:none;stroke-width:0.10526317;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="circle1379"
cx="24.834"
cy="103.12428"
r="2.1316733" />
<circle
r="2.1316733"
cy="79.995628"
cx="34.426529"
id="circle1381"
style="opacity:1;fill:url(#linearGradient3986);fill-opacity:1;stroke:none;stroke-width:0.10526317;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<circle
style="opacity:1;fill:url(#linearGradient3988);fill-opacity:1;stroke:none;stroke-width:0.10526317;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="circle1383"
cx="63.950211"
cy="104.29671"
r="2.1316733" />
<circle
r="2.1316733"
cy="136.16522"
cx="44.871731"
id="circle1385"
style="opacity:1;fill:url(#linearGradient3990);fill-opacity:1;stroke:none;stroke-width:0.10526317;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<circle
style="opacity:1;fill:url(#linearGradient3992);fill-opacity:1;stroke:none;stroke-width:0.10526317;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="circle1387"
cx="52.545757"
cy="117.61966"
r="2.1316733" />
<circle
style="opacity:1;fill:url(#linearGradient3994);fill-opacity:1;stroke:none;stroke-width:0.10526317;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="circle1397"
cx="16.20072"
cy="95.876595"
r="2.1316733" />
<circle
r="2.1316733"
cy="133.39404"
cx="19.504816"
id="circle1413"
style="opacity:1;fill:url(#linearGradient3996);fill-opacity:1;stroke:none;stroke-width:0.10526317;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<circle
style="opacity:1;fill:url(#linearGradient3998);fill-opacity:1;stroke:none;stroke-width:0.10526317;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="circle1415"
cx="51.479919"
cy="85.005051"
r="2.1316733" />
</g>
<g
style="fill:url(#linearGradient4026);fill-opacity:1;stroke:none"
id="g2286">
<circle
r="1.3322958"
cy="103.91988"
cx="34.692989"
id="circle1389"
style="opacity:1;fill:url(#linearGradient4002);fill-opacity:1;stroke:none;stroke-width:0.06578948;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<circle
style="opacity:1;fill:url(#linearGradient4004);fill-opacity:1;stroke:none;stroke-width:0.06578948;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="circle1391"
cx="37.570747"
cy="91.023262"
r="1.3322958" />
<circle
r="1.3322958"
cy="93.26152"
cx="46.097443"
id="circle1393"
style="opacity:1;fill:url(#linearGradient4006);fill-opacity:1;stroke:none;stroke-width:0.06578948;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<circle
style="opacity:1;fill:url(#linearGradient4008);fill-opacity:1;stroke:none;stroke-width:0.06578948;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="circle1395"
cx="59.95332"
cy="93.048355"
r="1.3322958" />
<circle
r="1.3322958"
cy="92.515434"
cx="11.031411"
id="circle1399"
style="opacity:1;fill:url(#linearGradient4010);fill-opacity:1;stroke:none;stroke-width:0.06578948;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<circle
style="opacity:1;fill:url(#linearGradient4012);fill-opacity:1;stroke:none;stroke-width:0.06578948;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="circle1401"
cx="10.072158"
cy="123.74445"
r="1.3322958" />
<circle
r="1.3322958"
cy="127.90121"
cx="28.404551"
id="circle1403"
style="opacity:1;fill:url(#linearGradient4014);fill-opacity:1;stroke:none;stroke-width:0.06578948;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<circle
style="opacity:1;fill:url(#linearGradient4016);fill-opacity:1;stroke:none;stroke-width:0.06578948;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="circle1405"
cx="15.401341"
cy="114.68483"
r="1.3322958" />
<circle
r="1.3322958"
cy="103.49355"
cx="46.843529"
id="circle1407"
style="opacity:1;fill:url(#linearGradient4018);fill-opacity:1;stroke:none;stroke-width:0.06578948;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<circle
style="opacity:1;fill:url(#linearGradient4020);fill-opacity:1;stroke:none;stroke-width:0.06578948;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="circle1409"
cx="55.583389"
cy="101.04213"
r="1.3322958" />
<circle
r="1.3322958"
cy="115.21776"
cx="63.68375"
id="circle1411"
style="opacity:1;fill:url(#linearGradient4022);fill-opacity:1;stroke:none;stroke-width:0.06578948;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<circle
style="opacity:1;fill:url(#linearGradient4024);fill-opacity:1;stroke:none;stroke-width:0.06578948;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="circle1417"
cx="33.200817"
cy="137.70691"
r="1.3322958" />
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 27 KiB

View file

@ -32,6 +32,9 @@ $(document).ready(function(){
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<!--BEGIN PROJECT_LOGO-->
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></td>
<!--END PROJECT_LOGO-->
<!--BEGIN PROJECT_NAME-->
<td style="padding-left: 0.5em;">
<div id="projectname">$projectname
@ -47,9 +50,9 @@ $(document).ready(function(){
</td>
<!--END PROJECT_BRIEF-->
<!--END !PROJECT_NAME-->
<!--BEGIN PROJECT_LOGO-->
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></td>
<!--END PROJECT_LOGO-->
<!--BEGIN INSTITUTE_LOGO-->
<td id="institutelogo"><img alt="Logo" src="$relpath^acs_eonerc_logo.svg"/></td>
<!--END INSTITUTE_LOGO-->
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN SEARCHENGINE-->
<td>$searchbox</td>

14
doc/theme/style.css vendored
View file

@ -3,8 +3,14 @@
}
#projectlogo img {
height: 3em;
position: absolute;
right: 0.5em;
top: 0.5em;
height: 3.5em;
margin: 0.25em;
}
#institutelogo img {
height: 3em;
margin: 0.5em;
position: absolute;
right: 0;
top: 0;
}

8
etc/Makefile.inc Normal file
View file

@ -0,0 +1,8 @@
etc:
install-etc:
install -D -t $(DESTDIR)/etc/villas/node etc/*.conf
clean-etc:
.PHONY: etc install-etc clean-etc

24
etc/advio.conf Normal file
View file

@ -0,0 +1,24 @@
/** Test advanced file IO using libcurl.
*
* 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
*********************************************************************************/
nodes = {
remote_file = {
type = "file",
out = {
# The output path accepts all format tokens of (see strftime(3))
uri = "https://1Nrd46fZX8HbggT:badpass@rwth-aachen.sciebo.de/public.php/webdav/node/data/demo_%y-%m-%d_%H-%M-%S.dat",
mode = "a+" # You might want to use "a+" to append to a file
}
in = {
uri = "https://1Nrd46fZX8HbggT:badpass@rwth-aachen.sciebo.de/public.php/webdav/node/data/demo_in.dat",
rate = 1
}
}
}

View file

@ -1,16 +1,16 @@
# Example configuration file for VILLASnode
#
# This example includes all valid configuration options for the server.
# Please note, that using all options at the same time does not really
# makes sense. The purpose of this example is to serve as a reference.
#
# 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: 2016, Institute for Automation of Complex Power Systems, EONERC
##
/** Example configuration file for VILLASnode.
*
* This example includes all valid configuration options for the server.
* Please note, that using all options at the same time does not really
* makes sense. The purpose of this example is to serve as a reference.
*
* 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
*********************************************************************************/
# Some global settings are used by multiple configuration files
# and therefore defined in separate files
@ -84,7 +84,7 @@ nodes = {
### The following settings are specific to the file node-type!! ###
in = {
path = "logs/input.log", # These options specify the path prefix where the the files are stored
uri = "logs/input.log", # These options specify the path prefix where the the files are stored
mode = "r", # The mode in which files should be opened (see open(2))
epoch_mode = "direct" # One of: direct (default), wait, relative, absolute
@ -98,7 +98,7 @@ nodes = {
splitted = false
},
out = {
path = "logs/output_%F_%T.log" # The output path accepts all format tokens of (see strftime(3))
uri = "logs/output_%F_%T.log" # The output path accepts all format tokens of (see strftime(3))
mode = "a+" # You might want to use "a+" to append to a file
split = 100, # Split output file every 100 MB
@ -148,8 +148,6 @@ paths = (
rate = 100, # Send message over this path with a fixed (constant) rate (default: 0).
# Setting this value to 0 will disable this feature.
hook = "print", # Register custom hook funktion (see src/hooks.c)
poolsize = 30 # The amount of samples which are kept in a circular buffer.
# This number must be larger than the 'vectorize' settings of all
# associated input and output nodes!
@ -160,23 +158,31 @@ paths = (
in = "opal_node", # There's only a single source node allowed!
out = [ "udp_node", "tcp_node" ], # Multiple destination nodes are supported too.
hook = [ "print", "decimate:10" ] # Same is true for hooks.
# Multipe hook functions are executed in the order they are specified here.
},
{
in = "socket_node",
out = "file_node", # This path includes all available example hooks.
hook = [
"ts", # Replace the timestamp of messages with the time of reception
"skip_unchanged:0.1", # Skip messages whose values have not changed more than 0.1 from the previous message.
"skip_first:10", # Skip all messages which have been received in the first 10 seconds
"print", # Print all messages to stdout
"decimate:2", # Only forward every 2nd message
"convert:fixed", # Convert all values to fixed precission. Use 'float' to convert to floating point.
"fir:0" # Apply finite impulse response filter to first value.
# Coefficients are hard-coded in 'include/config.h'.
]
# A complete list of supported hooks
print = {
output = "stdout"
priority = 0
},
ts = {
priority = 1
}
decimate = {
ratio = 2 # Only forward every 2nd message
},
convert = {
mode = "fixed" # Convert all values to fixed precission. Use 'float' to convert to floating point.
},
skip_first = {
seconds = 10 # Skip the first 10 seconds of this path
},
shift = {
mode = "origin", # Shift origin timestam of samples by +10 seconds
offset = 10
}
}
);

View file

@ -1,66 +1,58 @@
# Example configuration file for VILLASnode
#
# This example includes all valid configuration options for the server.
# Please note, that using all options at the same time does not really
# makes sense. The purpose of this example is to serve as a reference.
#
# 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: 2016, Institute for Automation of Complex Power Systems, EONERC
##
/** Example 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
*********************************************************************************/
# Some global settings are used by multiple configuration files
# and therefore defined in separate files
@include "global.conf"
@include "plugins.conf"
fpga = {
/* Card identification */
id = "10ee:7022";
slot = "01:00.0";
intc = 0x5000;
reset = 0x2000;
do_reset = true;
ips = {
switch_0 = {
vlnv = "xilinx.com:ip:axis_interconnect:2.1"
baseaddr = 0x0000;
numports = 3;
},
rtds_0 = {
vlnv = "acs.eonerc.rwth-aachen.de:user:rtds_axis:1.0"
baseaddr = 0x3000;
port = 0;
},
dma_0 = {
vlnv = "xilinx.com:ip:axi_dma:7.1";
baseaddr = 0x1000;
port = 2;
irq = 0
},
hls_dft_0 = {
vlnv = "acs.eonerc.rwth-aachen.de:hls:hls_dft:1.0";
port = 1;
switch = "switch_0";
fpgas = {
vc707 = {
/* Card identification */
id = "10ee:7022";
slot = "01:00.0";
intc = 0x5000;
reset = 0x2000;
do_reset = true;
ips = {
switch_0 = {
vlnv = "xilinx.com:ip:axis_interconnect:2.1"
baseaddr = 0x0000;
numports = 3;
},
rtds_0 = {
vlnv = "acs.eonerc.rwth-aachen.de:user:rtds_axis:1.0"
baseaddr = 0x3000;
port = 0;
},
dma_0 = {
vlnv = "xilinx.com:ip:axi_dma:7.1";
baseaddr = 0x1000;
port = 2;
irq = 0
}
}
/* Configure switch_0 */
paths = (
{ in = "dma_0", out = "rtds_0" },
{ in = "rtds_0", out = "dma_0" }
)
}
/* Configure switch */
paths = (
{ in = "dma", out = "hls_dft" },
{ in = "hls_dft", out = "dma" }
)
}
nodes = {
dma = {
rtds = {
datamover = "dma_0";
use_irqs = false;
}
}
}

View file

@ -1,12 +1,12 @@
# Example configuration file for VILLASfpga / 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: 2016, Institute for Automation of Complex Power Systems, EONERC
##
/** Example configuration file for VILLASfpga / 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
*********************************************************************************/
# Some global settings are used by multiple configuration files
# and therefore defined in separate files

View file

@ -1,39 +1,44 @@
# Global configuration file for VILLASnode
#
# This example includes all valid configuration options for the server.
# Please note, that using all options at the same time does not really
# makes sense. The purpose of this example is to serve as a reference.
#
# 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: 2016, Institute for Automation of Complex Power Systems, EONERC
##
/** 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
*********************************************************************************/
############ Global Options ############
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.
//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
debug = 5; # The level of verbosity for debug messages
# Higher number => increased verbosity
stats = 3; # The interval in seconds to print path statistics.
# A value of 0 disables the statistics.
name = "villas-acs" # 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 = "/var/log/villas-node.log"; # File for logs
};
http = {
htdocs = "/villas/contrib/websocket", # Root directory of internal webserver
htdocs = "/villas/web/socket/", # Root directory of internal webserver
port = 80 # Port for HTTP connections
}

View file

@ -1,29 +1,12 @@
# This is an example for a minimal loopback configuration.
#
# All messages will be sent back to the origin using UDP packets.
#
# You can use this configuration in conjunction with the 'send', 'receive' and 'random'
# utilities as shown below (run all three steps in parallel).
#
# 0. Overview:
#
# ./signal --PIPE--> ./pipe --UDP--> ./node --UDP--> ./pipe
#
# 1. Start server:
#
# $ ./node etc/loopback.conf
#
# 2. Send random data to server:
#
# $ ./signal random -r 10 -v 4 | ./pipe etc/loopback.conf node1
#
# 3. Receive data from server:
#
# $ ./pipe etc/loopback.conf node2
#
# Author: Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
# Copyright: 2016, Institute for Automation of Complex Power Systems, EONERC
##
/** GTNET-SKT test configuration.
*
* 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
*********************************************************************************/
stats = 1;
debug = 10;
@ -57,6 +40,10 @@ paths = (
{
in = "node1", # Name of the node we listen to (see above)
out = "node1", # And we loop back to the origin
hook = ["print"]
# Hooks
print = {
output = "stdout"
}
}
);

View file

@ -1,29 +1,12 @@
# This is an example for a minimal loopback configuration.
#
# All messages will be sent back to the origin using UDP packets.
#
# You can use this configuration in conjunction with the 'send', 'receive' and 'random'
# utilities as shown below (run all three steps in parallel).
#
# 0. Overview:
#
# ./signal --PIPE--> ./pipe --UDP--> ./node --UDP--> ./pipe
#
# 1. Start server:
#
# $ ./node etc/loopback.conf
#
# 2. Send random data to server:
#
# $ ./signal random -r 10 -v 4 | ./pipe etc/loopback.conf node1
#
# 3. Receive data from server:
#
# $ ./pipe etc/loopback.conf node2
#
# Author: Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
# Copyright: 2016, Institute for Automation of Complex Power Systems, EONERC
##
/** GTNET-SKT test configuration.
*
* 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
*********************************************************************************/
stats = 1;
debug = 10;
@ -57,6 +40,10 @@ paths = (
{
in = "node1", # Name of the node we listen to (see above)
out = "node2", # And we loop back to the origin
hook = ["print"]
# Hooks
print = {
output = "stdout"
}
}
);

View file

@ -1,29 +1,12 @@
# This is an example for a minimal loopback configuration.
#
# All messages will be sent back to the origin using UDP packets.
#
# You can use this configuration in conjunction with the 'send', 'receive' and 'random'
# utilities as shown below (run all three steps in parallel).
#
# 0. Overview:
#
# ./signal --PIPE--> ./pipe --UDP--> ./node --UDP--> ./pipe
#
# 1. Start server:
#
# $ ./node etc/loopback.conf
#
# 2. Send random data to server:
#
# $ ./signal random -r 10 -v 4 | ./pipe etc/loopback.conf node1
#
# 3. Receive data from server:
#
# $ ./pipe etc/loopback.conf node2
#
# Author: Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
# Copyright: 2016, Institute for Automation of Complex Power Systems, EONERC
##
/** GTNET-SKT test configuration.
*
* 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
*********************************************************************************/
stats = 1;
debug = 10;
@ -57,6 +40,10 @@ paths = (
{
in = "node1", # Name of the node we listen to (see above)
out = "node2", # And we loop back to the origin
hook = ["print"]
# Hooks
print = {
output = "stdout"
}
}
);

View file

@ -1,29 +1,12 @@
# This is an example for a minimal loopback configuration.
#
# All messages will be sent back to the origin using UDP packets.
#
# You can use this configuration in conjunction with the 'send', 'receive' and 'random'
# utilities as shown below (run all three steps in parallel).
#
# 0. Overview:
#
# ./signal --PIPE--> ./pipe --UDP--> ./node --UDP--> ./pipe
#
# 1. Start server:
#
# $ ./node etc/loopback.conf
#
# 2. Send random data to server:
#
# $ ./signal random -r 10 -v 4 | ./pipe etc/loopback.conf node1
#
# 3. Receive data from server:
#
# $ ./pipe etc/loopback.conf node2
#
# Author: Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
# Copyright: 2016, Institute for Automation of Complex Power Systems, EONERC
##
/** GTNET-SKT test configuration.
*
* 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
*********************************************************************************/
stats = 1;
debug = 10;
@ -57,6 +40,10 @@ paths = (
{
in = "node1", # Name of the node we listen to (see above)
out = "node1", # And we loop back to the origin
hook = ["print"]
# Hooks
print = {
output = "stdout"
}
}
);

View file

@ -1,29 +1,12 @@
# This is an example for a minimal loopback configuration.
#
# All messages will be sent back to the origin using UDP packets.
#
# You can use this configuration in conjunction with the 'send', 'receive' and 'random'
# utilities as shown below (run all three steps in parallel).
#
# 0. Overview:
#
# ./signal --PIPE--> ./pipe --UDP--> ./node --UDP--> ./pipe
#
# 1. Start server:
#
# $ ./node etc/loopback.conf
#
# 2. Send random data to server:
#
# $ ./signal random -r 10 -v 4 | ./pipe etc/loopback.conf node1
#
# 3. Receive data from server:
#
# $ ./pipe etc/loopback.conf node2
#
# Author: Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
# Copyright: 2016, Institute for Automation of Complex Power Systems, EONERC
##
/** GTNET-SKT test configuration.
*
* 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
*********************************************************************************/
stats = 1;
debug = 10;
@ -58,6 +41,10 @@ paths = (
{
in = "node1", # Name of the node we listen to (see above)
out = "node1", # And we loop back to the origin
hook = ["print"]
# Hooks
print = {
output = "stdout"
}
}
);

View file

@ -1,29 +1,33 @@
# This is an example for a minimal loopback configuration.
#
# All messages will be sent back to the origin using UDP packets.
#
# You can use this configuration in conjunction with the 'send', 'receive' and 'random'
# utilities as shown below (run all three steps in parallel).
#
# 0. Overview:
#
# ./signal --PIPE--> ./pipe --UDP--> ./node --UDP--> ./pipe
#
# 1. Start server:
#
# $ ./node etc/loopback.conf
#
# 2. Send random data to server:
#
# $ ./signal random -r 10 -v 4 | ./pipe etc/loopback.conf node1
#
# 3. Receive data from server:
#
# $ ./pipe etc/loopback.conf node2
#
# Author: Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
# Copyright: 2016, Institute for Automation of Complex Power Systems, EONERC
##
/** This is an example for a minimal loopback configuration.
*
* All messages will be sent back to the origin using UDP packets.
*
* You can use this configuration in conjunction with the 'send', 'receive' and 'random'
* utilities as shown below (run all three steps in parallel).
*
* 0. Overview:
*
* ./signal --PIPE--> ./pipe --UDP--> ./node --UDP--> ./pipe
*
* 1. Start server:
*
* $ ./node etc/loopback.conf
*
* 2. Send random data to server:
*
* $ ./signal random -r 10 -v 4 | ./pipe etc/loopback.conf node1
*
* 3. Receive data from server:
*
* $ ./pipe etc/loopback.conf node2
*
* 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
*********************************************************************************/
stats = 1;
debug = 10;
@ -34,15 +38,14 @@ nodes = {
layer = "udp",
local = "*:12000", # Local ip:port, use '*' for random port
remote = "127.0.0.1:12001",
combine = 5,
header = "villas", # 'gtnet-skt' or 'villas'. If not provided, 'villas' header will be used
vectorize = 1, # Number of samples to fetch per iteration from the socket
netem = {
enabled = false,
delay = 1000000, # In micro seconds!
jitter = 300000,
distribution = "normal"
}
vectorize = 1 # Number of samples to fetch per iteration from the socket
},
node2 = {
type = "socket",
@ -58,6 +61,5 @@ paths = (
{
in = "node1", # Name of the node we listen to (see above)
out = "node2", # And we loop back to the origin
hook = ["decimate:2", "print"]
}
);

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