mirror of
https://git.rwth-aachen.de/acs/public/villas/node/
synced 2025-03-09 00:00:00 +01:00
Merge branch 'develop' of git.rwth-aachen.de:acs/public/villas/VILLASnode into develop
This commit is contained in:
commit
dc3a38e698
101 changed files with 2056 additions and 902 deletions
|
@ -4,7 +4,7 @@ variables:
|
|||
PREFIX: /usr/
|
||||
RSYNC_OPTS: --recursive --ignore-missing-args --chown ${DEPLOY_USER}:${DEPLOY_USER}
|
||||
CRITERION_OPTS: --ignore-warnings
|
||||
DOCKER_TAG: $CI_COMMIT_TAG
|
||||
DOCKER_TAG: ${CI_COMMIT_TAG}
|
||||
DOCKER_TAG_DEV: ${CI_COMMIT_REF_NAME}
|
||||
DOCKER_IMAGE: villas/node
|
||||
DOCKER_IMAGE_DEV: villas/node-dev
|
||||
|
@ -28,7 +28,9 @@ before_script:
|
|||
docker-dev:
|
||||
stage: prepare
|
||||
script:
|
||||
- docker build -f packaging/docker/Dockerfile.dev -t ${DOCKER_IMAGE_DEV}:${DOCKER_TAG_DEV} .
|
||||
- docker build
|
||||
--file packaging/docker/Dockerfile.dev
|
||||
--tag ${DOCKER_IMAGE_DEV}:${DOCKER_TAG_DEV} .
|
||||
tags:
|
||||
- shell
|
||||
- linux
|
||||
|
@ -105,18 +107,6 @@ test:unit:
|
|||
tags:
|
||||
- docker
|
||||
|
||||
test:unit-common:
|
||||
stage: test
|
||||
dependencies:
|
||||
- build:source
|
||||
script:
|
||||
- mkdir -p build && cd build
|
||||
- cmake .. && make unit-tests-common
|
||||
- "common/tests/unit-tests-common || true"
|
||||
image: ${DOCKER_IMAGE_DEV}:${DOCKER_TAG_DEV}
|
||||
tags:
|
||||
- docker
|
||||
|
||||
test:integration:
|
||||
stage: test
|
||||
dependencies:
|
||||
|
@ -129,7 +119,7 @@ test:integration:
|
|||
name: ${CI_PROJECT_NAME}-integration-tests-${CI_BUILD_REF}
|
||||
when: always
|
||||
paths:
|
||||
- build/release/tests/integration/
|
||||
- build/tests/integration/
|
||||
image: ${DOCKER_IMAGE_DEV}:${DOCKER_TAG_DEV}
|
||||
tags:
|
||||
- docker
|
||||
|
@ -170,8 +160,10 @@ deploy:packages:
|
|||
docker:
|
||||
stage: docker
|
||||
script:
|
||||
- docker build -f packaging/docker/Dockerfile.app -t ${DOCKER_IMAGE}:${DOCKER_TAG} .
|
||||
- docker push ${DOCKER_IMAGE}:${DOCKER_TAG}
|
||||
- docker build
|
||||
--build-arg BUILDER_IMAGE=${DOCKER_IMAGE_DEV}:${DOCKER_TAG_DEV}
|
||||
--file packaging/docker/Dockerfile.app
|
||||
--tag ${DOCKER_IMAGE}:${DOCKER_TAG_DEV} .
|
||||
- docker push ${DOCKER_IMAGE_DEV}:${DOCKER_TAG_DEV}
|
||||
tags:
|
||||
- shell
|
||||
|
|
|
@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.7.1] - Unreleased
|
||||
## [0.7.1] - 2019-01-23
|
||||
|
||||
### Fixed
|
||||
|
||||
|
@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||
- Node-types can now handle more than a single file-descriptor for poll() multiplexing.
|
||||
- Enable network emulation sub-system also for other node-types than `socket`.
|
||||
The `rtp` node-type will support it now as well.
|
||||
- Improve readabilty of log output
|
||||
|
||||
### Added
|
||||
|
||||
|
|
|
@ -22,8 +22,7 @@
|
|||
|
||||
cmake_minimum_required(VERSION 3.6)
|
||||
|
||||
project(villas-node
|
||||
DESCRIPTION "VILLASnode"
|
||||
project(villas-node
|
||||
LANGUAGES C CXX
|
||||
)
|
||||
|
||||
|
@ -92,7 +91,7 @@ find_program(PROTOBUF_COMPILER NAMES protoc)
|
|||
|
||||
set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig:/usr/local/lib64/pkgconfig:/usr/local/share/pkgconfig:/usr/lib64/pkgconfig")
|
||||
|
||||
pkg_check_modules(JANSSON IMPORTED_TARGET REQUIRED jansson>=2.7)
|
||||
pkg_check_modules(JANSSON IMPORTED_TARGET REQUIRED jansson>=2.9)
|
||||
pkg_check_modules(LIBWEBSOCKETS IMPORTED_TARGET REQUIRED libwebsockets>=2.3.0)
|
||||
pkg_check_modules(PROTOBUF IMPORTED_TARGET protobuf>=2.6.0)
|
||||
pkg_check_modules(PROTOBUFC IMPORTED_TARGET libprotobuf-c>=1.1.0)
|
||||
|
@ -104,14 +103,14 @@ pkg_check_modules(RABBITMQ_C IMPORTED_TARGET librabbitmq>=0.8.0)
|
|||
pkg_check_modules(COMEDILIB IMPORTED_TARGET comedilib>=0.11.0)
|
||||
pkg_check_modules(LIBZMQ IMPORTED_TARGET libzmq>=2.2.0)
|
||||
pkg_check_modules(LIBULDAQ IMPORTED_TARGET libuldaq>=1.0.0)
|
||||
pkg_check_modules(UUID IMPORTED_TARGET uuid)
|
||||
pkg_check_modules(UUID IMPORTED_TARGET REQUIRED uuid)
|
||||
pkg_check_modules(NANOMSG IMPORTED_TARGET nanomsg)
|
||||
if(NOT NANOMSG_FOUND)
|
||||
pkg_check_modules(NANOMSG IMPORTED_TARGET libnanomsg>=1.0.0)
|
||||
endif()
|
||||
pkg_check_modules(RE IMPORTED_TARGET re)
|
||||
pkg_check_modules(RE IMPORTED_TARGET re>=0.5.6)
|
||||
if(NOT RE_FOUND)
|
||||
pkg_check_modules(RE IMPORTED_TARGET libre>=0.5.9)
|
||||
pkg_check_modules(RE IMPORTED_TARGET libre>=0.5.6)
|
||||
endif()
|
||||
|
||||
# Build options
|
||||
|
@ -155,7 +154,7 @@ if(WITH_PLUGINS)
|
|||
add_subdirectory(plugins)
|
||||
endif()
|
||||
|
||||
if(WITH_DOC)
|
||||
if(WITH_DOC AND ${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.9.0")
|
||||
add_subdirectory(doc)
|
||||
endif()
|
||||
|
||||
|
|
|
@ -61,11 +61,11 @@ set(CPACK_RPM_PLUGINS_FILE_NAME "${CPACK_RPM_PLUGINS_PACKAGE_NAME}-${SUFFIX}")
|
|||
set(CPACK_RPM_TOOLS_FILE_NAME "${CPACK_RPM_TOOLS_PACKAGE_NAME}-${SUFFIX}")
|
||||
set(CPACK_RPM_DOC_FILE_NAME "${CPACK_RPM_DOC_PACKAGE_NAME}-${SUFFIX}")
|
||||
|
||||
set(CPACK_RPM_DEVEL_PACKAGE_REQUIRES "${CPACK_RPM_LIB_PACKAGE_NAME} openssl-devel >= 1.0.0, libuuid-devel, protobuf-devel >= 2.6.0, protobuf-c-devel >= 1.1.0, libconfig-devel >= 1.4.9, libnl3-devel >= 3.2.27, libcurl-devel >= 7.29.0, jansson-devel >= 2.7, libwebsockets-devel >= 2.3.0, zeromq-devel >= 2.2.0, nanomsg >= 1.0.0, libiec61850 >= 1.3.1, librabbitmq-devel >= 0.8.0, mosquitto-devel >= 1.4.15, comedilib-devel >= 0.11.0, libibverbs-devel >= 16.2, librdmacm-devel >= 16.2, re-devel >= 0.6.0, uldaq-devel >= 1.0.0")
|
||||
set(CPACK_RPM_LIB_PACKAGE_REQUIRES " openssl-libs >= 1.0.0, libuuid, protobuf >= 2.6.0, protobuf-c >= 1.1.0, libconfig >= 1.4.9, libnl3 >= 3.2.27, libcurl >= 7.29.0, jansson >= 2.7, libwebsockets >= 2.3.0, zeromq >= 2.2.0, nanomsg >= 1.0.0, libiec61850 >= 1.3.1, librabbitmq >= 0.8.0, mosquitto >= 1.4.15, comedilib >= 0.11.0, libibverbs >= 16.2, librdmacm >= 16.2, re >= 0.6.0, uldaq >= 1.0.0")
|
||||
set(CPACK_RPM_BIN_PACKAGE_REQUIRES ${CPACK_RPM_LIB_PACKAGE_NAME})
|
||||
set(CPACK_RPM_PLUGINS_PACKAGE_REQUIRES ${CPACK_RPM_LIB_PACKAGE_NAME})
|
||||
set(CPACK_RPM_TOOLS_PACKAGE_REQUIRES ${CPACK_RPM_LIB_PACKAGE_NAME})
|
||||
set(CPACK_RPM_DEVEL_PACKAGE_REQUIRES "${CPACK_RPM_LIB_PACKAGE_NAME} >= ${CPACK_PACKAGE_VERSION} openssl-devel >= 1.0.0, libuuid-devel, protobuf-devel >= 2.6.0, protobuf-c-devel >= 1.1.0, libconfig-devel >= 1.4.9, libnl3-devel >= 3.2.27, libcurl-devel >= 7.29.0, jansson-devel >= 2.7, libwebsockets-devel >= 2.3.0, zeromq-devel >= 2.2.0, nanomsg >= 1.0.0, libiec61850 >= 1.3.1, librabbitmq-devel >= 0.8.0, mosquitto-devel >= 1.4.15, comedilib-devel >= 0.11.0, libibverbs-devel >= 16.2, librdmacm-devel >= 16.2, re-devel >= 0.6.0, uldaq-devel >= 1.0.0")
|
||||
set(CPACK_RPM_LIB_PACKAGE_REQUIRES " openssl-libs >= 1.0.0, libuuid, protobuf >= 2.6.0, protobuf-c >= 1.1.0, libconfig >= 1.4.9, libnl3 >= 3.2.27, libcurl >= 7.29.0, jansson >= 2.7, libwebsockets >= 2.3.0, zeromq >= 2.2.0, nanomsg >= 1.0.0, libiec61850 >= 1.3.1, librabbitmq >= 0.8.0, mosquitto >= 1.4.15, comedilib >= 0.11.0, libibverbs >= 16.2, librdmacm >= 16.2, re >= 0.6.0, uldaq >= 1.0.0")
|
||||
set(CPACK_RPM_BIN_PACKAGE_REQUIRES "${CPACK_RPM_LIB_PACKAGE_NAME} >= ${CPACK_PACKAGE_VERSION}")
|
||||
set(CPACK_RPM_PLUGINS_PACKAGE_REQUIRES "${CPACK_RPM_LIB_PACKAGE_NAME} >= ${CPACK_PACKAGE_VERSION}")
|
||||
set(CPACK_RPM_TOOLS_PACKAGE_REQUIRES "${CPACK_RPM_LIB_PACKAGE_NAME} >= ${CPACK_PACKAGE_VERSION}")
|
||||
|
||||
set(CPACK_RPM_BIN_PACKAGE_SUGGESTS "villas-node-tools villas-node-plugins villas-node-doc")
|
||||
|
||||
|
|
2
common
2
common
|
@ -1 +1 @@
|
|||
Subproject commit e385c52fcdea122319eea95b2f8eea83129ea497
|
||||
Subproject commit 27751548785eebaeb160f32b923c7949de4faa5e
|
|
@ -56,6 +56,13 @@ if(DOXYGEN_FOUND)
|
|||
WORKING_DIRECTORY ${PROJECT_DIR}
|
||||
)
|
||||
|
||||
# Ensure that documentation is built before installing it
|
||||
install(CODE "execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} --build \"${CMAKE_CURRENT_BINARY_DIR}\" --target doc
|
||||
WORKING_DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}\"
|
||||
)"
|
||||
)
|
||||
|
||||
install(
|
||||
DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/doc/villas/node
|
||||
|
|
78
etc/labs/lab10_nodes.conf
Normal file
78
etc/labs/lab10_nodes.conf
Normal file
|
@ -0,0 +1,78 @@
|
|||
stats = 5.0;
|
||||
hugepages = 200;
|
||||
|
||||
nodes = {
|
||||
# Node names can be any alphanumeric value
|
||||
rpi-1 = {
|
||||
type = "socket";
|
||||
layer = "udp";
|
||||
format = "gtnet" # pre-built format to communicate in RTDS GTNET-SKT payload
|
||||
|
||||
in = {
|
||||
address = "*:12005" # villas node machine IP and port number
|
||||
|
||||
signals = {
|
||||
count = 8,
|
||||
type = "float"
|
||||
}
|
||||
},
|
||||
out = {
|
||||
address = "192.168.0.5:12005" # remote machine IP and port number
|
||||
},
|
||||
|
||||
hooks = (
|
||||
{
|
||||
type = "stats",
|
||||
warmup = 3000
|
||||
}
|
||||
)
|
||||
},
|
||||
rpi-2 = {
|
||||
type = "socket";
|
||||
layer = "udp";
|
||||
format = "gtnet" # pre-built format to communicate in RTDS GTNET-SKT payload
|
||||
|
||||
in = {
|
||||
address = "*:12006" # villas node machine IP and port number
|
||||
|
||||
signals = {
|
||||
count = 8,
|
||||
type = "float"
|
||||
}
|
||||
},
|
||||
out = {
|
||||
address = "192.168.0.6:12006" # remote machine IP and port number
|
||||
},
|
||||
|
||||
hooks = (
|
||||
{
|
||||
type = "stats",
|
||||
warmup = 3000
|
||||
}
|
||||
)
|
||||
},
|
||||
rtds-1 = {
|
||||
type = "socket";
|
||||
layer = "udp";
|
||||
format = "gtnet";
|
||||
|
||||
in = {
|
||||
address = "*:12083" # villas node machine IP and port number
|
||||
|
||||
signals = {
|
||||
count = 8,
|
||||
type = "float"
|
||||
}
|
||||
},
|
||||
out = {
|
||||
address = "192.168.0.4:12083" # remote machine IP and port number
|
||||
},
|
||||
|
||||
hooks = (
|
||||
{
|
||||
type = "stats",
|
||||
warmup = 3000
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
22
etc/labs/lab10_path_bidir.conf
Normal file
22
etc/labs/lab10_path_bidir.conf
Normal file
|
@ -0,0 +1,22 @@
|
|||
@include "lab10_nodes.conf"
|
||||
|
||||
paths = (
|
||||
# Each path dictionary corresponds to one way communnication
|
||||
{
|
||||
in = [ "rpi-1" ],
|
||||
out = [ "rtds-1" ]
|
||||
},
|
||||
{
|
||||
in = [ "rtds-1" ],
|
||||
out = [ "rpi-1" ]
|
||||
}
|
||||
|
||||
# Alternatively, you can use a single path specification
|
||||
# and set reverse = true
|
||||
# Example:
|
||||
# {
|
||||
# in = [ "rpi-1" ],
|
||||
# out = [ "rtds-1" ],
|
||||
# reverse = true
|
||||
# }
|
||||
)
|
12
etc/labs/lab10_path_hook.conf
Normal file
12
etc/labs/lab10_path_hook.conf
Normal file
|
@ -0,0 +1,12 @@
|
|||
@include "lab10_nodes.conf"
|
||||
|
||||
paths = (
|
||||
{
|
||||
in = [ "rpi-1" ],
|
||||
out = [ "rtds-1" ],
|
||||
|
||||
hooks = (
|
||||
{ type = "print", output = "stdout" }
|
||||
)
|
||||
}
|
||||
)
|
8
etc/labs/lab10_path_multiple_destinations.conf
Normal file
8
etc/labs/lab10_path_multiple_destinations.conf
Normal file
|
@ -0,0 +1,8 @@
|
|||
@include "lab10_nodes.conf"
|
||||
|
||||
paths = (
|
||||
{
|
||||
in = [ "rtds-1" ],
|
||||
out = [ "rpi-1", "rpi-2" ]
|
||||
}
|
||||
)
|
8
etc/labs/lab10_path_uni.conf
Normal file
8
etc/labs/lab10_path_uni.conf
Normal file
|
@ -0,0 +1,8 @@
|
|||
@include "lab10_nodes.conf"
|
||||
|
||||
paths = (
|
||||
{
|
||||
in = [ "rpi-1" ],
|
||||
out = [ "rtds-1" ]
|
||||
}
|
||||
)
|
83
etc/labs/lab11.conf
Normal file
83
etc/labs/lab11.conf
Normal file
|
@ -0,0 +1,83 @@
|
|||
nodes = {
|
||||
rtds_gtnet1 = {
|
||||
type = "socket",
|
||||
layer = "udp",
|
||||
header = "gtnet-skt",
|
||||
|
||||
in = {
|
||||
address = "*:12000",
|
||||
|
||||
signals = {
|
||||
count = 8,
|
||||
type = "float"
|
||||
}
|
||||
},
|
||||
out = {
|
||||
address = "134.130.169.89:12000"
|
||||
}
|
||||
},
|
||||
rtds_gtnet2 = {
|
||||
type = "socket",
|
||||
layer = "udp",
|
||||
header = "gtnet-skt",
|
||||
|
||||
in = {
|
||||
address = "*:12001",
|
||||
|
||||
signals = {
|
||||
count = 8,
|
||||
type = "float"
|
||||
}
|
||||
},
|
||||
out = {
|
||||
address = "134.130.169.90:12001"
|
||||
}
|
||||
},
|
||||
monitoring = {
|
||||
type = "websocket"
|
||||
},
|
||||
monitoring_log = {
|
||||
type = "file",
|
||||
|
||||
out = {
|
||||
uri = "ftp://acs:fake@134.130.169.32/var/villas/log/monitoring_%Y-%m-%d_%H_%M_%S.dat"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
paths = [
|
||||
{
|
||||
# Combine data from rtds_gtnet1 and rtds_gtnet2
|
||||
in = [
|
||||
"rtds_gtnet1.hdr.ts.origin",
|
||||
"rtds_gtnet1.hdr.sequence",
|
||||
"rtds_gtnet1.data[0-6]",
|
||||
|
||||
"rtds_gtnet2.hdr.ts.origin",
|
||||
"rtds_gtnet2.hdr.sequence",
|
||||
"rtds_gtnet2.data[0-6]",
|
||||
],
|
||||
|
||||
out = [
|
||||
"monitoring",
|
||||
"monitoring_log"
|
||||
],
|
||||
|
||||
reverse = false,
|
||||
|
||||
# The mode of a path determines when the path is triggered
|
||||
# and forwarding samples to its destintation nodes.
|
||||
mode = "any",
|
||||
|
||||
# List of nodes which trigger the path
|
||||
mask = [ "rtds_gtnet_1", "rtds_gtnet_2" ],
|
||||
|
||||
hooks = (
|
||||
# We dont want to overload the WebBrowsers
|
||||
{
|
||||
type = "decimate",
|
||||
ratio = 10
|
||||
}
|
||||
)
|
||||
}
|
||||
]
|
48
etc/labs/lab12.conf
Normal file
48
etc/labs/lab12.conf
Normal file
|
@ -0,0 +1,48 @@
|
|||
nodes = {
|
||||
udp_node1 = {
|
||||
type = "socket",
|
||||
layer = "udp",
|
||||
|
||||
in = {
|
||||
address = "*:12000"
|
||||
|
||||
signals = {
|
||||
count = 8,
|
||||
type = "float"
|
||||
}
|
||||
},
|
||||
out = {
|
||||
address = "127.0.0.1:12001"
|
||||
}
|
||||
},
|
||||
web_node1 = {
|
||||
type = "websocket",
|
||||
|
||||
vectorize = 2,
|
||||
series = (
|
||||
{ label = "Random walk", unit = "V" },
|
||||
{ label = "Sine", unit = "A" },
|
||||
{ label = "Rect", unit = "Var" },
|
||||
{ label = "Ramp", unit = "°C" }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
paths = (
|
||||
{
|
||||
in = [ "udp_node1" ],
|
||||
out = [ "web_node1" ],
|
||||
|
||||
hooks = (
|
||||
# We dont want to overload the WebBrowsers
|
||||
{ type = "decimate", ratio = 2 }
|
||||
)
|
||||
},
|
||||
|
||||
{
|
||||
in = [ "web_node1" ],
|
||||
out = [ "udp_node1" ]
|
||||
|
||||
# Web -> UDP does not require decimation
|
||||
}
|
||||
)
|
47
etc/labs/lab13.conf
Normal file
47
etc/labs/lab13.conf
Normal file
|
@ -0,0 +1,47 @@
|
|||
affinity = 0x8,
|
||||
|
||||
nodes = {
|
||||
rtds_gtnet1 = {
|
||||
type = "socket",
|
||||
layer = "udp",
|
||||
header = "gtnet-skt",
|
||||
|
||||
in = {
|
||||
address = "*:12000"
|
||||
|
||||
signals = {
|
||||
count = 8,
|
||||
type = "float"
|
||||
}
|
||||
},
|
||||
out = {
|
||||
address = "134.130.169.98:12000"
|
||||
}
|
||||
},
|
||||
rtds_gtnet2 = {
|
||||
type = "socket",
|
||||
layer = "udp",
|
||||
header = "gtnet-skt",
|
||||
|
||||
in = {
|
||||
address = "*:12001"
|
||||
|
||||
signals = {
|
||||
count = 8,
|
||||
type = "float"
|
||||
}
|
||||
},
|
||||
out = {
|
||||
address = "134.130.169.99:12001"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
paths = (
|
||||
{
|
||||
in = [ "rtds_gtnet1" ],
|
||||
out = [ "rtds_gtnet2" ],
|
||||
|
||||
reverse = true
|
||||
}
|
||||
)
|
18
etc/labs/lab3.conf
Normal file
18
etc/labs/lab3.conf
Normal file
|
@ -0,0 +1,18 @@
|
|||
nodes = {
|
||||
udp_node1 = {
|
||||
type = "socket",
|
||||
layer = "udp",
|
||||
|
||||
in = {
|
||||
address = "*:12000"
|
||||
|
||||
signals = {
|
||||
count = 3
|
||||
type = "float"
|
||||
}
|
||||
},
|
||||
out = {
|
||||
address = "127.0.0.1:12001"
|
||||
}
|
||||
}
|
||||
}
|
BIN
etc/labs/lab3.pcap
Normal file
BIN
etc/labs/lab3.pcap
Normal file
Binary file not shown.
18
etc/labs/lab4.conf
Normal file
18
etc/labs/lab4.conf
Normal file
|
@ -0,0 +1,18 @@
|
|||
nodes = {
|
||||
udp_node1 = {
|
||||
type = "socket",
|
||||
layer = "udp",
|
||||
|
||||
in = {
|
||||
address = "*:12000"
|
||||
|
||||
signals = {
|
||||
count = 8,
|
||||
type = "float"
|
||||
}
|
||||
},
|
||||
out = {
|
||||
address = "127.0.0.1:12001"
|
||||
}
|
||||
}
|
||||
}
|
19
etc/labs/lab5.conf
Normal file
19
etc/labs/lab5.conf
Normal file
|
@ -0,0 +1,19 @@
|
|||
nodes = {
|
||||
rtds_gtnet1 = {
|
||||
type = "socket",
|
||||
layer = "udp",
|
||||
header = "gtnet-skt",
|
||||
|
||||
in = {
|
||||
address = "*:12000"
|
||||
|
||||
signals = {
|
||||
count = 8,
|
||||
type = "float"
|
||||
}
|
||||
},
|
||||
out = {
|
||||
address = "134.130.169.89:12000"
|
||||
}
|
||||
}
|
||||
}
|
9
etc/labs/lab7.conf
Normal file
9
etc/labs/lab7.conf
Normal file
|
@ -0,0 +1,9 @@
|
|||
nodes = {
|
||||
file_node1 = {
|
||||
type = "file",
|
||||
|
||||
in = {
|
||||
uri = "file_send.dat"
|
||||
}
|
||||
}
|
||||
}
|
41
etc/labs/lab8.conf
Normal file
41
etc/labs/lab8.conf
Normal file
|
@ -0,0 +1,41 @@
|
|||
nodes = {
|
||||
udp_node1 = {
|
||||
type = "socket",
|
||||
layer = "udp",
|
||||
|
||||
in = {
|
||||
address = "*:12000"
|
||||
|
||||
signals = {
|
||||
count = 8,
|
||||
type = "float"
|
||||
}
|
||||
},
|
||||
out = {
|
||||
address = "127.0.0.1:12001"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
paths = [
|
||||
{
|
||||
in = [ "udp_node1" ],
|
||||
out = [ "udp_node1" ],
|
||||
|
||||
hooks = [
|
||||
{
|
||||
type = "decimate",
|
||||
priority = 1,
|
||||
|
||||
# Hook specific parameters follow
|
||||
# [paramter1] = [value1]
|
||||
ratio = 2
|
||||
},
|
||||
{
|
||||
type = "map",
|
||||
|
||||
mapping = [ "data[3]", "data[2]", "data[1]", "data[0]", "hdr.sequence", "ts.origin" ]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
28
etc/labs/lab9_netem.conf
Normal file
28
etc/labs/lab9_netem.conf
Normal file
|
@ -0,0 +1,28 @@
|
|||
nodes = {
|
||||
udp_node1 = {
|
||||
type = "socket",
|
||||
layer = "udp",
|
||||
|
||||
in = {
|
||||
address = "*:12000"
|
||||
|
||||
signals = {
|
||||
count = 8,
|
||||
type = "float"
|
||||
}
|
||||
},
|
||||
out = {
|
||||
address = "127.0.0.1:12001",
|
||||
|
||||
netem = {
|
||||
enabled = true,
|
||||
loss = 0, # in %
|
||||
corrupt = 0, # in %
|
||||
duplicate = 0, # in %
|
||||
delay = 100000, # in uS
|
||||
jitter = 5000, # in uS
|
||||
distribution = "normal"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
"idle_stop" : false,
|
||||
|
||||
"http" : {
|
||||
"port" : 8080
|
||||
},
|
||||
|
|
|
@ -28,9 +28,11 @@ nodes = {
|
|||
address = "127.0.0.1:12000"
|
||||
|
||||
netem = { # Network emulation settings
|
||||
delay = 100000, # Additional latency in microseconds
|
||||
loss = 10 # Packet loss in percent
|
||||
}
|
||||
enabled = false,
|
||||
|
||||
delay = 100000, # Additional latency in microseconds
|
||||
loss = 10 # Packet loss in percent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,40 +3,52 @@
|
|||
# via shared memory, and written back to an output file.
|
||||
|
||||
stats = 1;
|
||||
debug = 10;
|
||||
|
||||
logging = {
|
||||
level = "info"
|
||||
}
|
||||
|
||||
nodes = {
|
||||
file = {
|
||||
type = "file",
|
||||
in = {
|
||||
uri = "/var/log/villas/input.log",
|
||||
rate = 2.0,
|
||||
mode = "r",
|
||||
},
|
||||
out = {
|
||||
uri = "/var/log/villas/output.log",
|
||||
mode = "w"
|
||||
},
|
||||
vectorize = 1
|
||||
sig = {
|
||||
type = "signal"
|
||||
},
|
||||
shmem = {
|
||||
type = "shmem",
|
||||
out_name = "/villas1-out",
|
||||
in_name = "/villas1-in",
|
||||
signals = 4,
|
||||
queuelen = 32,
|
||||
polling = false,
|
||||
vectorize = 1
|
||||
out = {
|
||||
name = "/villas1",
|
||||
}
|
||||
in = {
|
||||
name = "/villas1",
|
||||
signals = {
|
||||
count = 1,
|
||||
type = "float"
|
||||
}
|
||||
}
|
||||
},
|
||||
lo = {
|
||||
type = "loopback",
|
||||
|
||||
format = "json"
|
||||
uri = "-"
|
||||
|
||||
out = {
|
||||
hooks = ( { type = "print", format = "json" })
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#
|
||||
# sig -> shmem -> lo
|
||||
#
|
||||
|
||||
paths = (
|
||||
{
|
||||
in = "file",
|
||||
out = "shmem",
|
||||
reverse = true,
|
||||
hooks = (
|
||||
{ priority = 10, type = "print" }
|
||||
)
|
||||
in = "sig",
|
||||
out = "shmem"
|
||||
},
|
||||
{
|
||||
in = "shmem",
|
||||
out = "lo",
|
||||
}
|
||||
);
|
||||
|
||||
)
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <poll.h>
|
||||
|
||||
#include <villas/common.h>
|
||||
|
@ -62,6 +64,8 @@ protected:
|
|||
void acceptNewSession();
|
||||
void closeSession(sessions::Socket *s);
|
||||
|
||||
struct sockaddr_un getSocketAddress();
|
||||
|
||||
public:
|
||||
Server(Api *a);
|
||||
~Server();
|
||||
|
|
|
@ -45,16 +45,6 @@ enum mapping_type {
|
|||
MAPPING_TYPE_TIMESTAMP
|
||||
};
|
||||
|
||||
enum mapping_stats_type {
|
||||
MAPPING_STATS_TYPE_LAST,
|
||||
MAPPING_STATS_TYPE_HIGHEST,
|
||||
MAPPING_STATS_TYPE_LOWEST,
|
||||
MAPPING_STATS_TYPE_MEAN,
|
||||
MAPPING_STATS_TYPE_VAR,
|
||||
MAPPING_STATS_TYPE_STDDEV,
|
||||
MAPPING_STATS_TYPE_TOTAL
|
||||
};
|
||||
|
||||
enum mapping_header_type {
|
||||
MAPPING_HEADER_TYPE_LENGTH,
|
||||
MAPPING_HEADER_TYPE_SEQUENCE
|
||||
|
@ -84,8 +74,8 @@ struct mapping_entry {
|
|||
} data;
|
||||
|
||||
struct {
|
||||
enum stats_id id;
|
||||
enum mapping_stats_type type;
|
||||
enum stats_metric metric;
|
||||
enum stats_type type;
|
||||
} stats;
|
||||
|
||||
struct {
|
||||
|
|
|
@ -58,6 +58,7 @@ struct node_direction {
|
|||
int vectorize; /**< Number of messages to send / recv at once (scatter / gather) */
|
||||
|
||||
struct vlist hooks; /**< List of write hooks (struct hook). */
|
||||
struct vlist signals; /**< Signal description. */
|
||||
|
||||
json_t *cfg; /**< A JSON object containing the configuration of the node. */
|
||||
};
|
||||
|
@ -67,8 +68,7 @@ struct node_direction {
|
|||
* Every entity which exchanges messages is represented by a node.
|
||||
* Nodes can be remote machines and simulators or locally running processes.
|
||||
*/
|
||||
struct node
|
||||
{
|
||||
struct node {
|
||||
char *name; /**< A short identifier of the node, only used for configuration and logging */
|
||||
|
||||
enum state state;
|
||||
|
@ -84,14 +84,14 @@ struct node
|
|||
|
||||
struct node_direction in, out;
|
||||
|
||||
struct vlist signals; /**< Signal meta data for data which is __received__ by node_read(). */
|
||||
#ifdef __linux__
|
||||
int fwmark; /**< Socket mark for netem, routing and filtering */
|
||||
|
||||
#ifdef WITH_NETEM
|
||||
int mark; /**< Socket mark for netem, routing and filtering */
|
||||
|
||||
struct rtnl_qdisc *tc_qdisc; /**< libnl3: Network emulator queuing discipline */
|
||||
struct rtnl_cls *tc_classifier; /**< libnl3: Firewall mark classifier */
|
||||
#endif /* WITH_NETEM */
|
||||
#endif /* __linux__ */
|
||||
|
||||
struct node_type *_vt; /**< Virtual functions (C++ OOP style) */
|
||||
void *_vd; /**< Virtual data (used by struct node::_vt functions) */
|
||||
|
@ -203,6 +203,8 @@ struct node_type * node_type(struct node *n);
|
|||
|
||||
struct memory_type * node_memory_type(struct node *n, struct memory_type *parent);
|
||||
|
||||
int node_is_valid_name(const char *name);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -62,7 +62,7 @@ struct file {
|
|||
} epoch_mode; /**< Specifies how file::offset is calculated. */
|
||||
|
||||
enum {
|
||||
FILE_EOF_EXIT, /**< Terminate when EOF is reached. */
|
||||
FILE_EOF_STOP, /**< Terminate when EOF is reached. */
|
||||
FILE_EOF_REWIND, /**< Rewind the file when EOF is reached. */
|
||||
FILE_EOF_WAIT /**< Blocking wait when EOF is reached. */
|
||||
} eof;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
*
|
||||
* @file
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @author Marvin Klimke <marvin.klimke@rwth-aachen.de>
|
||||
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
|
||||
* @license GNU General Public License (version 3)
|
||||
*
|
||||
|
@ -42,8 +43,9 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** The maximum length of a packet which contains stuct rtp. */
|
||||
/** The maximum length of a packet which contains rtp data. */
|
||||
#define RTP_INITIAL_BUFFER_LEN 1500
|
||||
#define RTP_PACKET_TYPE 21
|
||||
|
||||
/* Forward declarations */
|
||||
struct format_type;
|
||||
|
@ -64,6 +66,8 @@ struct rtp {
|
|||
struct {
|
||||
int enabled;
|
||||
|
||||
int num_rrs;
|
||||
|
||||
enum {
|
||||
RTCP_MODE_AIMD,
|
||||
} mode;
|
||||
|
@ -82,9 +86,12 @@ struct rtp {
|
|||
double b;
|
||||
|
||||
double last_rate;
|
||||
|
||||
FILE *log;
|
||||
} aimd; /** AIMD state */
|
||||
|
||||
struct queue_signalled recv_queue;
|
||||
struct mbuf *send_mb;
|
||||
};
|
||||
|
||||
/** @see node_type::print */
|
||||
|
|
|
@ -31,7 +31,9 @@
|
|||
|
||||
#include <jansson.h>
|
||||
|
||||
#include <villas/stats.h>
|
||||
#include <villas/task.h>
|
||||
#include <villas/list.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -42,13 +44,20 @@ struct node;
|
|||
struct sample;
|
||||
struct super_node;
|
||||
|
||||
struct stats_node_signal {
|
||||
struct node *node;
|
||||
char *node_str;
|
||||
|
||||
enum stats_metric metric;
|
||||
enum stats_type type;
|
||||
};
|
||||
|
||||
struct stats_node {
|
||||
double rate;
|
||||
char *node_str;
|
||||
|
||||
struct task task;
|
||||
|
||||
struct node *node;
|
||||
struct vlist signals; /** List of type struct stats_node_signal */
|
||||
};
|
||||
|
||||
/** @see node_type::print */
|
||||
|
@ -60,6 +69,8 @@ char *stats_node_print(struct node *n);
|
|||
/** @see node_type::parse */
|
||||
int stats_node_parse(struct node *n, json_t *cfg);
|
||||
|
||||
int stats_node_parse_signal(struct stats_node_signal *s, json_t *cfg);
|
||||
|
||||
/** @see node_type::start */
|
||||
int stats_node_start(struct node *n);
|
||||
|
||||
|
|
|
@ -52,7 +52,6 @@ struct uldaq {
|
|||
DaqDeviceInterface device_interface_type;
|
||||
|
||||
uint64_t sequence;
|
||||
uint32_t buffer_pos;
|
||||
|
||||
struct {
|
||||
double sample_rate;
|
||||
|
|
|
@ -172,6 +172,8 @@ int path_uses_node(struct path *p, struct node *n);
|
|||
*/
|
||||
int path_parse(struct path *p, json_t *cfg, struct vlist *nodes);
|
||||
|
||||
int path_is_simple(struct path *p);
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <jansson.h>
|
||||
|
||||
#include <villas/hist.h>
|
||||
#include <villas/signal.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -42,44 +43,68 @@ enum stats_format {
|
|||
STATS_FORMAT_MATLAB
|
||||
};
|
||||
|
||||
enum stats_id {
|
||||
STATS_SKIPPED, /**< Counter for skipped samples due to hooks. */
|
||||
STATS_REORDERED, /**< Counter for reordered samples. */
|
||||
STATS_GAP_SAMPLE, /**< Histogram for inter sample timestamps (as sent by remote). */
|
||||
STATS_GAP_RECEIVED, /**< Histogram for inter sample arrival time (as seen by this instance). */
|
||||
STATS_OWD, /**< Histogram for one-way-delay (OWD) of received samples. */
|
||||
STATS_COUNT /**< Just here to have an updated number of statistics. */
|
||||
enum stats_metric {
|
||||
STATS_METRIC_INVALID = -1,
|
||||
STATS_METRIC_SKIPPED, /**< Counter for skipped samples due to hooks. */
|
||||
STATS_METRIC_REORDERED, /**< Counter for reordered samples. */
|
||||
STATS_METRIC_GAP_SAMPLE, /**< Histogram for inter sample timestamps (as sent by remote). */
|
||||
STATS_METRIC_GAP_RECEIVED, /**< Histogram for inter sample arrival time (as seen by this instance). */
|
||||
STATS_METRIC_OWD, /**< Histogram for one-way-delay (OWD) of received samples. */
|
||||
STATS_METRIC_COUNT /**< Just here to have an updated number of statistics. */
|
||||
};
|
||||
|
||||
struct stats_desc {
|
||||
enum stats_type {
|
||||
STATS_TYPE_INVALID = -1,
|
||||
STATS_TYPE_LAST,
|
||||
STATS_TYPE_HIGHEST,
|
||||
STATS_TYPE_LOWEST,
|
||||
STATS_TYPE_MEAN,
|
||||
STATS_TYPE_VAR,
|
||||
STATS_TYPE_STDDEV,
|
||||
STATS_TYPE_TOTAL,
|
||||
STATS_TYPE_COUNT
|
||||
};
|
||||
|
||||
struct stats_metric_description {
|
||||
const char *name;
|
||||
enum stats_metric metric;
|
||||
const char *unit;
|
||||
const char *desc;
|
||||
int hist_buckets;
|
||||
};
|
||||
|
||||
struct stats_delta {
|
||||
double values[STATS_COUNT];
|
||||
struct stats_type_description {
|
||||
const char *name;
|
||||
enum stats_type type;
|
||||
enum signal_type signal_type;
|
||||
};
|
||||
|
||||
int update; /**< Bitmask of stats_id. Only those which are masked will be updated */
|
||||
struct stats_delta {
|
||||
double values[STATS_METRIC_COUNT];
|
||||
|
||||
int update; /**< Bitmask of stats_metric. Only those which are masked will be updated */
|
||||
};
|
||||
|
||||
struct stats {
|
||||
struct hist histograms[STATS_COUNT];
|
||||
struct hist histograms[STATS_METRIC_COUNT];
|
||||
|
||||
struct stats_delta *delta;
|
||||
};
|
||||
|
||||
extern
|
||||
struct stats_desc stats_metrics[];
|
||||
extern struct stats_metric_description stats_metrics[];
|
||||
extern struct stats_type_description stats_types[];
|
||||
|
||||
int stats_lookup_format(const char *str);
|
||||
|
||||
enum stats_metric stats_lookup_metric(const char *str);
|
||||
|
||||
enum stats_type stats_lookup_type(const char *str);
|
||||
|
||||
int stats_init(struct stats *s, int buckets, int warmup);
|
||||
|
||||
int stats_destroy(struct stats *s);
|
||||
|
||||
void stats_update(struct stats *s, enum stats_id id, double val);
|
||||
void stats_update(struct stats *s, enum stats_metric id, double val);
|
||||
|
||||
void stats_collect(struct stats *s, struct sample *smps[], size_t *cnt);
|
||||
|
||||
|
@ -95,7 +120,7 @@ void stats_print_periodic(struct stats *s, FILE *f, enum stats_format fmt, int v
|
|||
|
||||
void stats_print(struct stats *s, FILE *f, enum stats_format fmt, int verbose);
|
||||
|
||||
enum stats_id stats_lookup_id(const char *name);
|
||||
union signal_data stats_get_value(const struct stats *s, enum stats_metric sm, enum stats_type st);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -42,4 +42,6 @@ struct lws_vhost * web_get_vhost(struct web *w);
|
|||
|
||||
enum state web_get_state(struct web *w);
|
||||
|
||||
#ifdef WITH_WEB
|
||||
int web_callback_on_writable(struct web *w, struct lws *wsi);
|
||||
#endif
|
||||
|
|
|
@ -40,10 +40,11 @@ class SuperNode {
|
|||
protected:
|
||||
enum state state;
|
||||
|
||||
int idleStop;
|
||||
|
||||
int priority; /**< Process priority (lower is better) */
|
||||
int affinity; /**< Process affinity of the server and all created threads */
|
||||
int hugepages; /**< Number of hugepages to reserve. */
|
||||
double stats; /**< Interval for path statistics. Set to 0 to disable them. */
|
||||
|
||||
Logger logger;
|
||||
|
||||
|
@ -104,6 +105,11 @@ public:
|
|||
/** Run periodic hooks of this super node. */
|
||||
int periodic();
|
||||
|
||||
void setState(enum state st)
|
||||
{
|
||||
state = st;
|
||||
}
|
||||
|
||||
struct node * getNode(const std::string &name)
|
||||
{
|
||||
return (struct node *) vlist_lookup(&nodes, name.c_str());
|
||||
|
@ -122,6 +128,10 @@ public:
|
|||
return &interfaces;
|
||||
}
|
||||
|
||||
enum state getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
#ifdef WITH_API
|
||||
Api * getApi() {
|
||||
return &api;
|
||||
|
|
|
@ -21,14 +21,13 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <exception>
|
||||
#include <algorithm>
|
||||
|
||||
#if __GNUC__ <= 7
|
||||
#if __GNUC__ <= 7 && !defined(__clang__)
|
||||
#include <experimental/filesystem>
|
||||
#else
|
||||
#include <filesystem>
|
||||
|
@ -44,7 +43,7 @@
|
|||
using namespace villas;
|
||||
using namespace villas::node::api;
|
||||
|
||||
#if __GNUC__ <= 7
|
||||
#if __GNUC__ <= 7 && !defined(__clang__)
|
||||
namespace fs = std::experimental::filesystem;
|
||||
#else
|
||||
namespace fs = std::filesystem;
|
||||
|
@ -79,9 +78,36 @@ void Server::start()
|
|||
|
||||
pfds.push_back(pfd);
|
||||
|
||||
struct sockaddr_un sun = getSocketAddress();
|
||||
|
||||
ret = bind(sd, (struct sockaddr *) &sun, sizeof(struct sockaddr_un));
|
||||
if (ret)
|
||||
throw SystemError("Failed to bind API socket");
|
||||
|
||||
ret = listen(sd, 5);
|
||||
if (ret)
|
||||
throw SystemError("Failed to listen on API socket");
|
||||
|
||||
logger->info("Listening on UNIX socket: {}", sun.sun_path);
|
||||
|
||||
state = STATE_STARTED;
|
||||
}
|
||||
|
||||
struct sockaddr_un Server::getSocketAddress()
|
||||
{
|
||||
struct sockaddr_un sun = { .sun_family = AF_UNIX };
|
||||
|
||||
fs::path socketPath = PREFIX "/var/lib/villas";
|
||||
fs::path socketPath;
|
||||
|
||||
if (getuid() == 0) {
|
||||
socketPath = PREFIX "/var/lib/villas";
|
||||
}
|
||||
else {
|
||||
std::string homeDir = getenv("HOME");
|
||||
|
||||
socketPath = homeDir + "/.villas";
|
||||
}
|
||||
|
||||
if (!fs::exists(socketPath)) {
|
||||
logging.get("api")->info("Creating directory for API socket: {}", socketPath);
|
||||
fs::create_directories(socketPath);
|
||||
|
@ -96,17 +122,7 @@ void Server::start()
|
|||
|
||||
strncpy(sun.sun_path, socketPath.c_str(), sizeof(sun.sun_path) - 1);
|
||||
|
||||
ret = bind(sd, (struct sockaddr *) &sun, sizeof(struct sockaddr_un));
|
||||
if (ret)
|
||||
throw SystemError("Failed to bind API socket");
|
||||
|
||||
ret = listen(sd, 5);
|
||||
if (ret)
|
||||
throw SystemError("Failed to listen on API socket");
|
||||
|
||||
logger->info("Listening on UNIX socket: {}", socketPath);
|
||||
|
||||
state = STATE_STARTED;
|
||||
return sun;
|
||||
}
|
||||
|
||||
void Server::stop()
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include <sstream>
|
||||
|
||||
#include <villas/compat.h>
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/api/sessions/socket.hpp>
|
||||
|
||||
|
|
|
@ -121,7 +121,7 @@ static size_t csv_sscan_single(struct io *io, const char *buf, size_t len, struc
|
|||
if (sig->type == SIGNAL_TYPE_AUTO) {
|
||||
|
||||
/* Find end of the current column */
|
||||
next = strpbrk(ptr, (char[]) { io->separator, io->delimiter, 0 });
|
||||
next = strpbrk(ptr, ((char[]) { io->separator, io->delimiter, 0 }));
|
||||
if (next == NULL)
|
||||
goto out;
|
||||
|
||||
|
|
|
@ -146,7 +146,7 @@ static size_t villas_human_sscan_single(struct io *io, const char *buf, size_t l
|
|||
if (sig->type == SIGNAL_TYPE_AUTO) {
|
||||
|
||||
/* Find end of the current column */
|
||||
next = strpbrk(ptr, (char[]) { io->separator, io->delimiter, 0 });
|
||||
next = strpbrk(ptr, ((char[]) { io->separator, io->delimiter, 0 }));
|
||||
if (next == NULL)
|
||||
goto out;
|
||||
|
||||
|
@ -227,7 +227,7 @@ void villas_human_header(struct io *io, const struct sample *smp)
|
|||
fprintf(f, "(sequence)");
|
||||
|
||||
if (io->flags & SAMPLE_HAS_DATA) {
|
||||
for (int i = 0; i < smp->length; i++) {
|
||||
for (int i = 0; i < MIN(smp->length, vlist_length(smp->signals)); i++) {
|
||||
struct signal *sig = (struct signal *) vlist_at(smp->signals, i);
|
||||
|
||||
if (sig->name)
|
||||
|
|
|
@ -35,6 +35,7 @@ set(HOOK_SRC
|
|||
fix.c
|
||||
cast.c
|
||||
average.c
|
||||
dump.c
|
||||
)
|
||||
|
||||
if(WITH_IO)
|
||||
|
|
|
@ -46,7 +46,7 @@ static int cast_init(struct hook *h)
|
|||
struct vlist *orig_signals;
|
||||
|
||||
if (h->node)
|
||||
orig_signals = &h->node->signals;
|
||||
orig_signals = &h->node->in.signals;
|
||||
else if (h->path)
|
||||
orig_signals = &h->path->signals;
|
||||
else
|
||||
|
|
|
@ -72,7 +72,7 @@ static int decimate_process(struct hook *h, struct sample *smps[], unsigned *cnt
|
|||
|
||||
int i, ok;
|
||||
for (i = 0, ok = 0; i < *cnt; i++) {
|
||||
if (p->counter++ % p->ratio == 0) {
|
||||
if (p->ratio && p->counter++ % p->ratio == 0) {
|
||||
struct sample *tmp;
|
||||
|
||||
tmp = smps[ok];
|
||||
|
|
54
lib/hooks/dump.c
Normal file
54
lib/hooks/dump.c
Normal file
|
@ -0,0 +1,54 @@
|
|||
|
||||
/** Dump hook.
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2014-2019, 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 hooks Hook functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include <villas/hook.h>
|
||||
#include <villas/plugin.h>
|
||||
#include <villas/node.h>
|
||||
#include <villas/sample.h>
|
||||
|
||||
static int dump_process(struct hook *h, struct sample *smps[], unsigned *cnt)
|
||||
{
|
||||
for (int i = 0; i < *cnt; i++)
|
||||
sample_dump(smps[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct plugin p = {
|
||||
.name = "dump",
|
||||
.description = "dump data to stdout",
|
||||
.type = PLUGIN_TYPE_HOOK,
|
||||
.hook = {
|
||||
.flags = HOOK_NODE_READ | HOOK_NODE_WRITE | HOOK_PATH,
|
||||
.priority = 1,
|
||||
.process = dump_process
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_PLUGIN(&p)
|
||||
|
||||
/** @} */
|
|
@ -59,7 +59,7 @@ static int print_start(struct hook *h)
|
|||
struct vlist *signals;
|
||||
|
||||
if (h->node)
|
||||
signals = &h->node->signals;
|
||||
signals = &h->node->in.signals;
|
||||
else if (h->path)
|
||||
signals = &h->path->signals;
|
||||
else
|
||||
|
|
|
@ -168,18 +168,18 @@ static int stats_collect_process(struct hook *h, struct sample *smps[], unsigned
|
|||
|
||||
if (previous) {
|
||||
if (current->flags & previous->flags & SAMPLE_HAS_TS_RECEIVED)
|
||||
stats_update(s, STATS_GAP_RECEIVED, time_delta(&previous->ts.received, ¤t->ts.received));
|
||||
stats_update(s, STATS_METRIC_GAP_RECEIVED, time_delta(&previous->ts.received, ¤t->ts.received));
|
||||
|
||||
if (current->flags & previous->flags & SAMPLE_HAS_TS_ORIGIN)
|
||||
stats_update(s, STATS_GAP_SAMPLE, time_delta(&previous->ts.origin, ¤t->ts.origin));
|
||||
stats_update(s, STATS_METRIC_GAP_SAMPLE, time_delta(&previous->ts.origin, ¤t->ts.origin));
|
||||
|
||||
if ((current->flags & SAMPLE_HAS_TS_ORIGIN) && (current->flags & SAMPLE_HAS_TS_RECEIVED))
|
||||
stats_update(s, STATS_OWD, time_delta(¤t->ts.origin, ¤t->ts.received));
|
||||
stats_update(s, STATS_METRIC_OWD, time_delta(¤t->ts.origin, ¤t->ts.received));
|
||||
|
||||
if (current->flags & previous->flags & SAMPLE_HAS_SEQUENCE) {
|
||||
dist = current->sequence - (int32_t) previous->sequence;
|
||||
if (dist != 1)
|
||||
stats_update(s, STATS_REORDERED, dist);
|
||||
stats_update(s, STATS_METRIC_REORDERED, dist);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -73,23 +73,23 @@ int if_start(struct interface *i)
|
|||
//if_set_affinity(i, i->affinity);
|
||||
|
||||
/* Assign fwmark's to nodes which have netem options */
|
||||
int ret, mark = 0;
|
||||
int ret, fwmark = 0;
|
||||
for (size_t j = 0; j < vlist_length(&i->nodes); j++) {
|
||||
struct node *n = (struct node *) vlist_at(&i->nodes, j);
|
||||
|
||||
if (n->tc_qdisc)
|
||||
n->mark = 1 + mark++;
|
||||
if (n->tc_qdisc && n->fwmark < 0)
|
||||
n->fwmark = 1 + fwmark++;
|
||||
}
|
||||
|
||||
/* Abort if no node is using netem */
|
||||
if (mark == 0)
|
||||
if (fwmark == 0)
|
||||
return 0;
|
||||
|
||||
if (getuid() != 0)
|
||||
error("Network emulation requires super-user privileges!");
|
||||
|
||||
/* Replace root qdisc */
|
||||
ret = tc_prio(i, &i->tc_qdisc, TC_HANDLE(1, 0), TC_H_ROOT, mark);
|
||||
ret = tc_prio(i, &i->tc_qdisc, TC_HANDLE(1, 0), TC_H_ROOT, fwmark);
|
||||
if (ret)
|
||||
error("Failed to setup priority queuing discipline: %s", nl_geterror(ret));
|
||||
|
||||
|
@ -98,16 +98,16 @@ int if_start(struct interface *i)
|
|||
struct node *n = (struct node *) vlist_at(&i->nodes, j);
|
||||
|
||||
if (n->tc_qdisc) {
|
||||
ret = tc_mark(i, &n->tc_classifier, TC_HANDLE(1, n->mark), n->mark);
|
||||
ret = tc_mark(i, &n->tc_classifier, TC_HANDLE(1, n->fwmark), n->fwmark);
|
||||
if (ret)
|
||||
error("Failed to setup FW mark classifier: %s", nl_geterror(ret));
|
||||
|
||||
char *buf = tc_netem_print(n->tc_qdisc);
|
||||
debug(LOG_IF | 5, "Starting network emulation on interface '%s' for FW mark %u: %s",
|
||||
if_name(i), n->mark, buf);
|
||||
if_name(i), n->fwmark, buf);
|
||||
free(buf);
|
||||
|
||||
ret = tc_netem(i, &n->tc_qdisc, TC_HANDLE(0x1000+n->mark, 0), TC_HANDLE(1, n->mark));
|
||||
ret = tc_netem(i, &n->tc_qdisc, TC_HANDLE(0x1000+n->fwmark, 0), TC_HANDLE(1, n->fwmark));
|
||||
if (ret)
|
||||
error("Failed to setup netem qdisc: %s", nl_geterror(ret));
|
||||
}
|
||||
|
|
122
lib/mapping.c
122
lib/mapping.c
|
@ -32,8 +32,7 @@
|
|||
|
||||
int mapping_parse_str(struct mapping_entry *me, const char *str, struct vlist *nodes)
|
||||
{
|
||||
int id;
|
||||
char *cpy, *node, *type, *field, *subfield, *end;
|
||||
char *cpy, *node, *type, *field, *end;
|
||||
|
||||
cpy = strdup(str);
|
||||
if (!cpy)
|
||||
|
@ -68,44 +67,21 @@ int mapping_parse_str(struct mapping_entry *me, const char *str, struct vlist *n
|
|||
me->type = MAPPING_TYPE_STATS;
|
||||
me->length = 1;
|
||||
|
||||
field = strtok(NULL, ".");
|
||||
if (!field) {
|
||||
warning("Missing stats type");
|
||||
char *metric = strtok(NULL, ".");
|
||||
if (!metric)
|
||||
goto invalid_format;
|
||||
}
|
||||
|
||||
subfield = strtok(NULL, ".");
|
||||
if (!subfield) {
|
||||
warning("Missing stats sub-type");
|
||||
type = strtok(NULL, ".");
|
||||
if (!type)
|
||||
goto invalid_format;
|
||||
}
|
||||
|
||||
id = stats_lookup_id(field);
|
||||
if (id < 0) {
|
||||
warning("Invalid stats type");
|
||||
me->stats.metric = stats_lookup_metric(metric);
|
||||
if (me->stats.metric < 0)
|
||||
goto invalid_format;
|
||||
}
|
||||
|
||||
me->stats.id = id;
|
||||
|
||||
if (!strcmp(subfield, "total"))
|
||||
me->stats.type = MAPPING_STATS_TYPE_TOTAL;
|
||||
else if (!strcmp(subfield, "last"))
|
||||
me->stats.type = MAPPING_STATS_TYPE_LAST;
|
||||
else if (!strcmp(subfield, "lowest"))
|
||||
me->stats.type = MAPPING_STATS_TYPE_LOWEST;
|
||||
else if (!strcmp(subfield, "highest"))
|
||||
me->stats.type = MAPPING_STATS_TYPE_HIGHEST;
|
||||
else if (!strcmp(subfield, "mean"))
|
||||
me->stats.type = MAPPING_STATS_TYPE_MEAN;
|
||||
else if (!strcmp(subfield, "var"))
|
||||
me->stats.type = MAPPING_STATS_TYPE_VAR;
|
||||
else if (!strcmp(subfield, "stddev"))
|
||||
me->stats.type = MAPPING_STATS_TYPE_STDDEV;
|
||||
else {
|
||||
warning("Invalid stats sub-type");
|
||||
me->stats.type = stats_lookup_type(type);
|
||||
if (me->stats.type < 0)
|
||||
goto invalid_format;
|
||||
}
|
||||
}
|
||||
else if (!strcmp(type, "hdr")) {
|
||||
me->type = MAPPING_TYPE_HEADER;
|
||||
|
@ -154,7 +130,7 @@ int mapping_parse_str(struct mapping_entry *me, const char *str, struct vlist *n
|
|||
first_str = strtok(NULL, "-]");
|
||||
if (first_str) {
|
||||
if (me->node)
|
||||
first = vlist_lookup_index(&me->node->signals, first_str);
|
||||
first = vlist_lookup_index(&me->node->in.signals, first_str);
|
||||
|
||||
if (first < 0) {
|
||||
char *endptr;
|
||||
|
@ -168,14 +144,14 @@ int mapping_parse_str(struct mapping_entry *me, const char *str, struct vlist *n
|
|||
else {
|
||||
/* Map all signals */
|
||||
me->data.offset = 0;
|
||||
me->length = me->node ? vlist_length(&me->node->signals) : 0;
|
||||
me->length = me->node ? vlist_length(&me->node->in.signals) : 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
last_str = strtok(NULL, "]");
|
||||
if (last_str) {
|
||||
if (me->node)
|
||||
last = vlist_lookup_index(&me->node->signals, last_str);
|
||||
last = vlist_lookup_index(&me->node->in.signals, last_str);
|
||||
|
||||
if (last < 0) {
|
||||
char *endptr;
|
||||
|
@ -276,35 +252,9 @@ int mapping_update(const struct mapping_entry *me, struct sample *remapped, cons
|
|||
return -1;
|
||||
|
||||
switch (me->type) {
|
||||
case MAPPING_TYPE_STATS: {
|
||||
const struct hist *h = &s->histograms[me->stats.id];
|
||||
|
||||
switch (me->stats.type) {
|
||||
case MAPPING_STATS_TYPE_TOTAL:
|
||||
remapped->data[off++].i = h->total;
|
||||
break;
|
||||
case MAPPING_STATS_TYPE_LAST:
|
||||
remapped->data[off++].f = h->last;
|
||||
break;
|
||||
case MAPPING_STATS_TYPE_HIGHEST:
|
||||
remapped->data[off++].f = h->highest;
|
||||
break;
|
||||
case MAPPING_STATS_TYPE_LOWEST:
|
||||
remapped->data[off++].f = h->lowest;
|
||||
break;
|
||||
case MAPPING_STATS_TYPE_MEAN:
|
||||
remapped->data[off++].f = hist_mean(h);
|
||||
break;
|
||||
case MAPPING_STATS_TYPE_STDDEV:
|
||||
remapped->data[off++].f = hist_stddev(h);
|
||||
break;
|
||||
case MAPPING_STATS_TYPE_VAR:
|
||||
remapped->data[off++].f = hist_var(h);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
case MAPPING_TYPE_STATS:
|
||||
remapped->data[off++] = stats_get_value(s, me->stats.metric, me->stats.type);
|
||||
break;
|
||||
|
||||
case MAPPING_TYPE_TIMESTAMP: {
|
||||
const struct timespec *ts;
|
||||
|
@ -380,40 +330,10 @@ int mapping_to_str(const struct mapping_entry *me, unsigned index, char **str)
|
|||
|
||||
switch (me->type) {
|
||||
case MAPPING_TYPE_STATS:
|
||||
switch (me->stats.type) {
|
||||
case MAPPING_STATS_TYPE_TOTAL:
|
||||
type = "total";
|
||||
break;
|
||||
|
||||
case MAPPING_STATS_TYPE_LAST:
|
||||
type = "last";
|
||||
break;
|
||||
|
||||
case MAPPING_STATS_TYPE_LOWEST:
|
||||
type = "lowest";
|
||||
break;
|
||||
|
||||
case MAPPING_STATS_TYPE_HIGHEST:
|
||||
type = "highest";
|
||||
break;
|
||||
|
||||
case MAPPING_STATS_TYPE_MEAN:
|
||||
type = "mean";
|
||||
break;
|
||||
|
||||
case MAPPING_STATS_TYPE_VAR:
|
||||
type = "var";
|
||||
break;
|
||||
|
||||
case MAPPING_STATS_TYPE_STDDEV:
|
||||
type = "stddev";
|
||||
break;
|
||||
|
||||
default:
|
||||
type = NULL;
|
||||
}
|
||||
|
||||
strcatf(str, "stats.%s", type);
|
||||
strcatf(str, "stats.%s.%s",
|
||||
stats_metrics[me->stats.metric].name,
|
||||
stats_types[me->stats.type].name
|
||||
);
|
||||
break;
|
||||
|
||||
case MAPPING_TYPE_HEADER:
|
||||
|
@ -451,8 +371,8 @@ int mapping_to_str(const struct mapping_entry *me, unsigned index, char **str)
|
|||
break;
|
||||
|
||||
case MAPPING_TYPE_DATA:
|
||||
if (me->node && index < vlist_length(&me->node->signals)) {
|
||||
struct signal *s = vlist_at(&me->node->signals, index);
|
||||
if (me->node && index < vlist_length(&me->node->in.signals)) {
|
||||
struct signal *s = vlist_at(&me->node->in.signals, index);
|
||||
|
||||
strcatf(str, "data[%s]", s->name);
|
||||
}
|
||||
|
|
23
lib/memory.c
23
lib/memory.c
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <villas/log.h>
|
||||
#include <villas/memory.h>
|
||||
|
@ -70,10 +71,14 @@ int memory_init(int hugepages)
|
|||
|
||||
int memory_lock(size_t lock)
|
||||
{
|
||||
#if defined(__linux__) && defined(__x86_64__)
|
||||
int ret;
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#ifndef __arm__
|
||||
struct rlimit l;
|
||||
|
||||
/* Increase ressource limit for locked memory */
|
||||
ret = getrlimit(RLIMIT_MEMLOCK, &l);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -81,11 +86,10 @@ int memory_lock(size_t lock)
|
|||
if (l.rlim_cur < lock) {
|
||||
if (l.rlim_max < lock) {
|
||||
if (getuid() != 0) {
|
||||
warning("Failed to in increase ressource limit of locked memory from %lu to %zu bytes", l.rlim_cur, lock);
|
||||
warning("Please re-run as super-user or raise manually via:");
|
||||
warning("Failed to in increase ressource limit of locked memory. Please increase manually by running as root:");
|
||||
warning(" $ ulimit -Hl %zu", lock);
|
||||
|
||||
return -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
l.rlim_max = lock;
|
||||
|
@ -99,7 +103,16 @@ int memory_lock(size_t lock)
|
|||
|
||||
debug(LOG_MEM | 2, "Increased ressource limit of locked memory to %zd bytes", lock);
|
||||
}
|
||||
#endif
|
||||
#endif /* __arm__ */
|
||||
out:
|
||||
#ifdef _POSIX_MEMLOCK
|
||||
/* Lock all current and future memory allocations */
|
||||
ret = mlockall(MCL_CURRENT | MCL_FUTURE);
|
||||
if (ret)
|
||||
return -1;
|
||||
#endif /* _POSIX_MEMLOCK */
|
||||
|
||||
#endif /* __linux__ */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -65,10 +65,8 @@ int memory_hugepage_init(int hugepages)
|
|||
debug(LOG_MEM | 2, "Increased number of reserved hugepages from %d to %d", pagecnt, hugepages);
|
||||
}
|
||||
else {
|
||||
warning("Failed to reserved hugepages. Please re-run as super-user or reserve manually via:");
|
||||
warning("Failed to reserved hugepages. Please reserve manually by running as root:");
|
||||
warning(" $ echo %d > /proc/sys/vm/nr_hugepages", hugepages);
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -81,7 +79,7 @@ static struct memory_allocation * memory_hugepage_alloc(struct memory_type *m, s
|
|||
{
|
||||
static bool use_huge = true;
|
||||
|
||||
int ret, flags, fd;
|
||||
int flags, fd;
|
||||
size_t sz;
|
||||
|
||||
struct memory_allocation *ma = alloc(sizeof(struct memory_allocation));
|
||||
|
@ -120,7 +118,7 @@ retry: if (use_huge) {
|
|||
ma->address = mmap(NULL, ma->length, PROT_READ | PROT_WRITE, flags, fd, 0);
|
||||
if (ma->address == MAP_FAILED) {
|
||||
if (use_huge) {
|
||||
warning("Failed to map hugepages, try with normal pages instead");
|
||||
warning("Failed to map hugepages, try with normal pages instead!");
|
||||
use_huge = false;
|
||||
goto retry;
|
||||
}
|
||||
|
@ -130,12 +128,6 @@ retry: if (use_huge) {
|
|||
}
|
||||
}
|
||||
|
||||
if (getuid() == 0) {
|
||||
ret = mlock(ma->address, ma->length);
|
||||
if (ret)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ma;
|
||||
}
|
||||
|
||||
|
|
174
lib/node.c
174
lib/node.c
|
@ -21,6 +21,7 @@
|
|||
*********************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <villas/node/config.h>
|
||||
#include <villas/hook.h>
|
||||
|
@ -64,14 +65,22 @@ static int node_direction_init(struct node_direction *nd, struct node *n)
|
|||
{
|
||||
int ret;
|
||||
|
||||
nd->enabled = 0;
|
||||
nd->enabled = 1;
|
||||
nd->vectorize = 1;
|
||||
nd->builtin = 1;
|
||||
nd->hooks.state = STATE_DESTROYED;
|
||||
|
||||
nd->hooks.state = STATE_DESTROYED;
|
||||
nd->signals.state = STATE_DESTROYED;
|
||||
|
||||
#ifdef WITH_HOOKS
|
||||
ret = vlist_init(&nd->hooks);
|
||||
if (ret)
|
||||
return ret;
|
||||
#endif /* WITH_HOOKS */
|
||||
|
||||
ret = vlist_init(&nd->signals);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -84,7 +93,11 @@ static int node_direction_destroy(struct node_direction *nd, struct node *n)
|
|||
ret = vlist_destroy(&nd->hooks, (dtor_cb_t) hook_destroy, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
#endif
|
||||
#endif /* WITH_HOOKS */
|
||||
|
||||
ret = vlist_destroy(&nd->signals, (dtor_cb_t) signal_decref, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -95,12 +108,13 @@ static int node_direction_parse(struct node_direction *nd, struct node *n, json_
|
|||
|
||||
json_error_t err;
|
||||
json_t *json_hooks = NULL;
|
||||
json_t *json_signals = NULL;
|
||||
|
||||
nd->cfg = cfg;
|
||||
nd->enabled = 1;
|
||||
|
||||
ret = json_unpack_ex(cfg, &err, 0, "{ s?: o, s?: i, s?: b, s?: b }",
|
||||
ret = json_unpack_ex(cfg, &err, 0, "{ s?: o, s?: o, s?: i, s?: b, s?: b }",
|
||||
"hooks", &json_hooks,
|
||||
"signals", &json_signals,
|
||||
"vectorize", &nd->vectorize,
|
||||
"builtin", &nd->builtin,
|
||||
"enabled", &nd->enabled
|
||||
|
@ -108,6 +122,35 @@ static int node_direction_parse(struct node_direction *nd, struct node *n, json_
|
|||
if (ret)
|
||||
jerror(&err, "Failed to parse node %s", node_name(n));
|
||||
|
||||
if (n->_vt->flags & NODE_TYPE_PROVIDES_SIGNALS) {
|
||||
if (json_signals)
|
||||
error("Node %s does not support signal definitions", node_name(n));
|
||||
}
|
||||
else if (json_is_array(json_signals)) {
|
||||
ret = signal_list_parse(&nd->signals, json_signals);
|
||||
if (ret)
|
||||
error("Failed to parse signal definition of node %s", node_name(n));
|
||||
}
|
||||
else {
|
||||
int count = DEFAULT_SAMPLE_LENGTH;
|
||||
const char *type_str = "float";
|
||||
|
||||
if (json_is_object(json_signals)) {
|
||||
json_unpack_ex(json_signals, &err, 0, "{ s: i, s: s }",
|
||||
"count", &count,
|
||||
"type", &type_str
|
||||
);
|
||||
}
|
||||
else
|
||||
warning("No signal definition found for node %s. Using the default config of %d floating point signals.", node_name(n), DEFAULT_SAMPLE_LENGTH);
|
||||
|
||||
int type = signal_type_from_str(type_str);
|
||||
if (type < 0)
|
||||
error("Invalid signal type %s", type_str);
|
||||
|
||||
signal_list_generate(&nd->signals, count, type);
|
||||
}
|
||||
|
||||
#ifdef WITH_HOOKS
|
||||
int m = nd == &n->out
|
||||
? HOOK_NODE_WRITE
|
||||
|
@ -182,14 +225,15 @@ int node_init(struct node *n, struct node_type *vt)
|
|||
n->_name = NULL;
|
||||
n->_name_long = NULL;
|
||||
|
||||
#ifdef __linux__
|
||||
n->fwmark = -1;
|
||||
#endif /* __linux__ */
|
||||
|
||||
#ifdef WITH_NETEM
|
||||
n->tc_qdisc = NULL;
|
||||
n->tc_classifier = NULL;
|
||||
#endif /* WITH_NETEM */
|
||||
|
||||
n->signals.state = STATE_DESTROYED;
|
||||
vlist_init(&n->signals);
|
||||
|
||||
/* Default values */
|
||||
ret = node_direction_init(&n->in, n);
|
||||
if (ret)
|
||||
|
@ -240,19 +284,29 @@ int node_parse(struct node *n, json_t *json, const char *name)
|
|||
|
||||
n->name = strdup(name);
|
||||
|
||||
ret = json_unpack_ex(json, &err, 0, "{ s: s, s?: { s?: o }, s?: { s?: o } }",
|
||||
ret = json_unpack_ex(json, &err, 0, "{ s: s, s?: { s?: o } }",
|
||||
"type", &type,
|
||||
"in",
|
||||
"signals", &json_signals,
|
||||
"out",
|
||||
"netem", &json_netem
|
||||
"signals", &json_signals
|
||||
);
|
||||
if (ret)
|
||||
jerror(&err, "Failed to parse node %s", node_name(n));
|
||||
|
||||
#ifdef __linux__
|
||||
ret = json_unpack_ex(json, &err, 0, "{ s?: { s?: o, s?: i } }",
|
||||
"out",
|
||||
"netem", &json_netem,
|
||||
"fwmark", &n->fwmark
|
||||
);
|
||||
if (ret)
|
||||
jerror(&err, "Failed to parse node %s", node_name(n));
|
||||
#endif /* __linux__ */
|
||||
|
||||
nt = node_type_lookup(type);
|
||||
assert(nt == node_type(n));
|
||||
|
||||
n->_vt = nt;
|
||||
|
||||
if (json_netem) {
|
||||
#ifdef WITH_NETEM
|
||||
int enabled = 1;
|
||||
|
@ -268,38 +322,6 @@ int node_parse(struct node *n, json_t *json, const char *name)
|
|||
#endif /* WITH_NETEM */
|
||||
}
|
||||
|
||||
if (nt->flags & NODE_TYPE_PROVIDES_SIGNALS) {
|
||||
if (json_signals)
|
||||
error("Node %s does not support signal definitions", node_name(n));
|
||||
}
|
||||
else if (json_signals) {
|
||||
if (json_is_array(json_signals)) {
|
||||
ret = signal_list_parse(&n->signals, json_signals);
|
||||
if (ret)
|
||||
error("Failed to parse signal definition of node %s", node_name(n));
|
||||
}
|
||||
else {
|
||||
int count;
|
||||
const char *type_str;
|
||||
|
||||
json_unpack_ex(json_signals, &err, 0, "{ s: i, s: s }",
|
||||
"count", &count,
|
||||
"type", &type_str
|
||||
);
|
||||
|
||||
int type = signal_type_from_str(type_str);
|
||||
if (type < 0)
|
||||
error("Invalid signal type %s", type_str);
|
||||
|
||||
signal_list_generate(&n->signals, count, type);
|
||||
}
|
||||
}
|
||||
else {
|
||||
warning("No signal definition found for node %s. Using the default config of %d floating point signals.", node_name(n), DEFAULT_SAMPLE_LENGTH);
|
||||
|
||||
signal_list_generate(&n->signals, DEFAULT_SAMPLE_LENGTH, SIGNAL_TYPE_FLOAT);
|
||||
}
|
||||
|
||||
struct {
|
||||
const char *str;
|
||||
struct node_direction *dir;
|
||||
|
@ -308,7 +330,7 @@ int node_parse(struct node *n, json_t *json, const char *name)
|
|||
{ "out", &n->out }
|
||||
};
|
||||
|
||||
const char *fields[] = { "builtin", "vectorize", "hooks" };
|
||||
const char *fields[] = { "signals", "builtin", "vectorize", "hooks" };
|
||||
|
||||
for (int j = 0; j < ARRAY_LEN(dirs); j++) {
|
||||
json_t *json_dir = json_object_get(json, dirs[j].str);
|
||||
|
@ -386,18 +408,18 @@ int node_start(struct node *n)
|
|||
|
||||
#ifdef __linux__
|
||||
/* Set fwmark for outgoing packets if netem is enabled for this node */
|
||||
if (n->mark) {
|
||||
if (n->fwmark) {
|
||||
int fds[16];
|
||||
int num_sds = node_netem_fds(n, fds);
|
||||
|
||||
for (int i = 0; i < num_sds; i++) {
|
||||
int fd = fds[i];
|
||||
|
||||
ret = setsockopt(fd, SOL_SOCKET, SO_MARK, &n->mark, sizeof(n->mark));
|
||||
ret = setsockopt(fd, SOL_SOCKET, SO_MARK, &n->fwmark, sizeof(n->fwmark));
|
||||
if (ret)
|
||||
serror("Failed to set FW mark for outgoing packets");
|
||||
else
|
||||
debug(LOG_SOCKET | 4, "Set FW mark for socket (sd=%u) to %u", fd, n->mark);
|
||||
debug(LOG_SOCKET | 4, "Set FW mark for socket (sd=%u) to %u", fd, n->fwmark);
|
||||
}
|
||||
}
|
||||
#endif /* __linux__ */
|
||||
|
@ -412,7 +434,7 @@ int node_stop(struct node *n)
|
|||
{
|
||||
int ret;
|
||||
|
||||
if (n->state != STATE_STARTED && n->state != STATE_CONNECTED && n->state != STATE_PENDING_CONNECT)
|
||||
if (n->state != STATE_STOPPING && n->state != STATE_STARTED && n->state != STATE_CONNECTED && n->state != STATE_PENDING_CONNECT)
|
||||
return 0;
|
||||
|
||||
info("Stopping node %s", node_name(n));
|
||||
|
@ -492,16 +514,11 @@ int node_restart(struct node *n)
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int node_destroy(struct node *n)
|
||||
{
|
||||
int ret;
|
||||
assert(n->state != STATE_DESTROYED && n->state != STATE_STARTED);
|
||||
|
||||
ret = vlist_destroy(&n->signals, (dtor_cb_t) signal_decref, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = node_direction_destroy(&n->in, n);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -544,12 +561,13 @@ int node_read(struct node *n, struct sample *smps[], unsigned cnt, unsigned *rel
|
|||
{
|
||||
int readd, nread = 0;
|
||||
|
||||
if (n->state == STATE_PAUSED)
|
||||
return 0;
|
||||
|
||||
assert(n->state == STATE_STARTED || n->state == STATE_CONNECTED || n->state == STATE_PENDING_CONNECT);
|
||||
assert(node_type(n)->read);
|
||||
|
||||
if (n->state == STATE_PAUSED || n->state == STATE_PENDING_CONNECT)
|
||||
return 0;
|
||||
else if (n->state != STATE_STARTED && n->state != STATE_CONNECTED)
|
||||
return -1;
|
||||
|
||||
/* Send in parts if vector not supported */
|
||||
if (node_type(n)->vectorize > 0 && node_type(n)->vectorize < cnt) {
|
||||
while (cnt - nread > 0) {
|
||||
|
@ -572,7 +590,7 @@ int node_read(struct node *n, struct sample *smps[], unsigned cnt, unsigned *rel
|
|||
int skipped = nread - rread;
|
||||
|
||||
if (skipped > 0 && n->stats != NULL) {
|
||||
stats_update(n->stats, STATS_SKIPPED, skipped);
|
||||
stats_update(n->stats, STATS_METRIC_SKIPPED, skipped);
|
||||
}
|
||||
|
||||
debug(LOG_NODE | 5, "Received %u samples from node %s of which %d have been skipped", nread, node_name(n), skipped);
|
||||
|
@ -587,11 +605,15 @@ int node_read(struct node *n, struct sample *smps[], unsigned cnt, unsigned *rel
|
|||
|
||||
int node_write(struct node *n, struct sample *smps[], unsigned cnt, unsigned *release)
|
||||
{
|
||||
int sent, nsent = 0;
|
||||
int tosend, sent, nsent = 0;
|
||||
|
||||
assert(n->state == STATE_STARTED || n->state == STATE_CONNECTED);
|
||||
assert(node_type(n)->write);
|
||||
|
||||
if (n->state == STATE_PAUSED || n->state == STATE_PENDING_CONNECT)
|
||||
return 0;
|
||||
else if (n->state != STATE_STARTED && n->state != STATE_CONNECTED)
|
||||
return -1;
|
||||
|
||||
#ifdef WITH_HOOKS
|
||||
/* Run write hooks */
|
||||
cnt = hook_process_list(&n->out.hooks, smps, cnt);
|
||||
|
@ -602,7 +624,8 @@ int node_write(struct node *n, struct sample *smps[], unsigned cnt, unsigned *re
|
|||
/* Send in parts if vector not supported */
|
||||
if (node_type(n)->vectorize > 0 && node_type(n)->vectorize < cnt) {
|
||||
while (cnt - nsent > 0) {
|
||||
sent = node_type(n)->write(n, &smps[nsent], MIN(cnt - nsent, node_type(n)->vectorize), release);
|
||||
tosend = MIN(cnt - nsent, node_type(n)->vectorize);
|
||||
sent = node_type(n)->write(n, &smps[nsent], tosend, release);
|
||||
if (sent < 0)
|
||||
return sent;
|
||||
|
||||
|
@ -635,16 +658,19 @@ char * node_name_long(struct node *n)
|
|||
if (node_type(n)->print) {
|
||||
struct node_type *vt = node_type(n);
|
||||
|
||||
strcatf(&n->_name_long, "%s: #in.signals=%zu, #in.hooks=%zu, in.vectorize=%d, #out.hooks=%zu, out.vectorize=%d, out.netem=%s",
|
||||
strcatf(&n->_name_long, "%s: #in.signals=%zu, #out.signals=%zu, #in.hooks=%zu, #out.hooks=%zu, in.vectorize=%d, out.vectorize=%d",
|
||||
node_name(n),
|
||||
vlist_length(&n->signals),
|
||||
vlist_length(&n->in.hooks), n->in.vectorize,
|
||||
vlist_length(&n->out.hooks), n->out.vectorize,
|
||||
n->tc_qdisc ? "yes" : "no"
|
||||
vlist_length(&n->in.signals), vlist_length(&n->out.signals),
|
||||
vlist_length(&n->in.hooks), vlist_length(&n->out.hooks),
|
||||
n->in.vectorize, n->out.vectorize
|
||||
);
|
||||
|
||||
#ifdef WITH_NETEM
|
||||
strcatf(&n->_name_long, ", out.netem=%s", n->tc_qdisc ? "yes" : "no");
|
||||
|
||||
if (n->tc_qdisc)
|
||||
strcatf(&n->_name_long, ", mark=%d", n->mark);
|
||||
strcatf(&n->_name_long, ", fwmark=%d", n->fwmark);
|
||||
#endif /* WITH_NETEM */
|
||||
|
||||
/* Append node-type specific details */
|
||||
char *name_long = vt->print(n);
|
||||
|
@ -744,3 +770,15 @@ invalid2:
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int node_is_valid_name(const char *name)
|
||||
{
|
||||
for (const char *p = name; *p; p++) {
|
||||
if (isalnum(*p) || (*p == '_') || (*p == '-'))
|
||||
continue;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
set(NODE_SRC
|
||||
influxdb.c
|
||||
stats.c
|
||||
signal_generator.c
|
||||
signal_generator.c
|
||||
loopback.c
|
||||
)
|
||||
|
||||
|
@ -49,8 +49,8 @@ endif()
|
|||
# Enable Universal Library for Linux DAQ devices (libuldaq)
|
||||
if(LIBULDAQ_FOUND)
|
||||
list(APPEND NODE_SRC uldaq.c)
|
||||
list(APPEND INCLUDE_DIRS ${ULDAQ_INCLUDE_DIRS})
|
||||
list(APPEND LIBRARIES PkgConfig::LIBULDAQ)
|
||||
list(APPEND INCLUDE_DIRS ${LIBULDAQ_INCLUDE_DIRS})
|
||||
list(APPEND LIBRARIES PkgConfig::LIBULDAQ uldaq)
|
||||
endif()
|
||||
|
||||
# Enable shared memory node-type
|
||||
|
@ -66,7 +66,7 @@ endif()
|
|||
if(LIBIEC61850_FOUND)
|
||||
list(APPEND NODE_SRC iec61850_sv.c iec61850.c)
|
||||
list(APPEND INCLUDE_DIRS ${LIBIEC61850_INCLUDE_DIRS})
|
||||
list(APPEND LIBRARIES PkgConfig::LIBIEC61850)
|
||||
list(APPEND LIBRARIES PkgConfig::LIBIEC61850 ${LIBIEC61850_LIBRARIES})
|
||||
endif()
|
||||
|
||||
# Enable OPAL-RT Asynchronous Process support (will result in 32bit binary!!!)
|
||||
|
|
|
@ -239,7 +239,7 @@ int amqp_start(struct node *n)
|
|||
amqp_rpc_reply_t rep;
|
||||
amqp_queue_declare_ok_t *r;
|
||||
|
||||
ret = io_init(&a->io, a->format, &n->signals, SAMPLE_HAS_ALL & ~SAMPLE_HAS_OFFSET);
|
||||
ret = io_init(&a->io, a->format, &n->in.signals, SAMPLE_HAS_ALL & ~SAMPLE_HAS_OFFSET);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -89,7 +89,7 @@ int file_parse(struct node *n, json_t *cfg)
|
|||
|
||||
/* Default values */
|
||||
f->rate = 0;
|
||||
f->eof = FILE_EOF_EXIT;
|
||||
f->eof = FILE_EOF_STOP;
|
||||
f->epoch_mode = FILE_EPOCH_DIRECT;
|
||||
f->flush = 0;
|
||||
f->buffer_size_in = 0;
|
||||
|
@ -119,8 +119,8 @@ int file_parse(struct node *n, json_t *cfg)
|
|||
error("Invalid format '%s' for node %s", format, node_name(n));
|
||||
|
||||
if (eof) {
|
||||
if (!strcmp(eof, "exit"))
|
||||
f->eof = FILE_EOF_EXIT;
|
||||
if (!strcmp(eof, "exit") || !strcmp(eof, "stop"))
|
||||
f->eof = FILE_EOF_STOP;
|
||||
else if (!strcmp(eof, "rewind"))
|
||||
f->eof = FILE_EOF_REWIND;
|
||||
else if (!strcmp(eof, "wait"))
|
||||
|
@ -166,7 +166,7 @@ char * file_print(struct node *n)
|
|||
}
|
||||
|
||||
switch (f->eof) {
|
||||
case FILE_EOF_EXIT: eof_str = "exit"; break;
|
||||
case FILE_EOF_STOP: eof_str = "stop"; break;
|
||||
case FILE_EOF_WAIT: eof_str = "wait"; break;
|
||||
case FILE_EOF_REWIND: eof_str = "rewind"; break;
|
||||
}
|
||||
|
@ -243,7 +243,7 @@ int file_start(struct node *n)
|
|||
if (f->flush)
|
||||
flags |= IO_FLUSH;
|
||||
|
||||
ret = io_init(&f->io, f->format, &n->signals, flags);
|
||||
ret = io_init(&f->io, f->format, &n->in.signals, flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -369,11 +369,12 @@ retry: ret = io_scan(&f->io, smps, cnt);
|
|||
|
||||
goto retry;
|
||||
|
||||
case FILE_EOF_EXIT:
|
||||
info("Reached end-of-file of node %s", node_name(n));
|
||||
case FILE_EOF_STOP:
|
||||
info("Reached end-of-file.");
|
||||
|
||||
killme(SIGTERM);
|
||||
pause();
|
||||
n->state = STATE_STOPPING;
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
@ -101,55 +101,72 @@ int iec61850_parse_signals(json_t *json_signals, struct vlist *signals, struct v
|
|||
{
|
||||
int ret, total_size = 0;
|
||||
const char *iec_type;
|
||||
const struct iec61850_type_descriptor *td;
|
||||
struct signal *sig;
|
||||
|
||||
ret = vlist_init(signals);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
json_t *json_signal;
|
||||
size_t i;
|
||||
json_array_foreach(json_signals, i, json_signal) {
|
||||
const struct iec61850_type_descriptor *td;
|
||||
struct signal *sig;
|
||||
if (json_is_array(json_signals)) {
|
||||
|
||||
json_unpack(json_signal, "{ s?: s }",
|
||||
"iec_type", &iec_type
|
||||
);
|
||||
json_t *json_signal;
|
||||
size_t i;
|
||||
json_array_foreach(json_signals, i, json_signal) {
|
||||
json_unpack(json_signal, "{ s?: s }",
|
||||
"iec_type", &iec_type
|
||||
);
|
||||
|
||||
/* Try to deduct the IEC 61850 data type from VILLAS signal format */
|
||||
if (!iec_type) {
|
||||
if (!node_signals)
|
||||
return -1;
|
||||
|
||||
sig = vlist_at(node_signals, i);
|
||||
if (!sig)
|
||||
return -1;
|
||||
|
||||
switch (sig->type) {
|
||||
case SIGNAL_TYPE_BOOLEAN:
|
||||
iec_type = "boolean";
|
||||
break;
|
||||
|
||||
case SIGNAL_TYPE_FLOAT:
|
||||
iec_type = "float64";
|
||||
break;
|
||||
|
||||
case SIGNAL_TYPE_INTEGER:
|
||||
iec_type = "int64";
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Try to deduct the IEC 61850 data type from VILLAS signal format */
|
||||
if (!iec_type) {
|
||||
if (!node_signals)
|
||||
return -1;
|
||||
|
||||
sig = vlist_at(node_signals, i);
|
||||
if (!sig)
|
||||
return -1;
|
||||
|
||||
switch (sig->type) {
|
||||
case SIGNAL_TYPE_BOOLEAN:
|
||||
iec_type = "boolean";
|
||||
break;
|
||||
|
||||
case SIGNAL_TYPE_FLOAT:
|
||||
iec_type = "float64";
|
||||
break;
|
||||
|
||||
case SIGNAL_TYPE_INTEGER:
|
||||
iec_type = "int64";
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
td = iec61850_lookup_type(iec_type);
|
||||
if (!td)
|
||||
return -1;
|
||||
|
||||
vlist_push(signals, (void *) td);
|
||||
|
||||
total_size += td->size;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ret = json_unpack(json_signals, "{ s: s }", "iec_type", &iec_type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
td = iec61850_lookup_type(iec_type);
|
||||
if (!td)
|
||||
return -1;
|
||||
|
||||
vlist_push(signals, (void *) td);
|
||||
for (int i = 0; i < vlist_length(node_signals); i++) {
|
||||
vlist_push(signals, (void *) td);
|
||||
|
||||
total_size += td->size;
|
||||
total_size += td->size;
|
||||
}
|
||||
}
|
||||
|
||||
return total_size;
|
||||
|
|
|
@ -74,7 +74,7 @@ static void iec61850_sv_listener(SVSubscriber subscriber, void *ctx, SVSubscribe
|
|||
smp->sequence = smpcnt;
|
||||
smp->flags = SAMPLE_HAS_SEQUENCE | SAMPLE_HAS_DATA;
|
||||
smp->length = 0;
|
||||
smp->signals = &n->signals;
|
||||
smp->signals = &n->in.signals;
|
||||
|
||||
if (SVSubscriber_ASDU_hasRefrTm(asdu)) {
|
||||
uint64_t refrtm = SVSubscriber_ASDU_getRefrTmAsMs(asdu);
|
||||
|
@ -190,7 +190,7 @@ int iec61850_sv_parse(struct node *n, json_t *json)
|
|||
|
||||
i->out.svid = svid ? strdup(svid) : NULL;
|
||||
|
||||
ret = iec61850_parse_signals(json_signals, &i->out.signals, NULL);
|
||||
ret = iec61850_parse_signals(json_signals, &i->out.signals, &n->out.signals);
|
||||
if (ret <= 0)
|
||||
error("Failed to parse setting 'signals' of node %s", node_name(n));
|
||||
|
||||
|
@ -207,7 +207,7 @@ int iec61850_sv_parse(struct node *n, json_t *json)
|
|||
if (ret)
|
||||
jerror(&err, "Failed to parse configuration of node %s", node_name(n));
|
||||
|
||||
ret = iec61850_parse_signals(json_signals, &i->in.signals, &n->signals);
|
||||
ret = iec61850_parse_signals(json_signals, &i->in.signals, &n->in.signals);
|
||||
if (ret <= 0)
|
||||
error("Failed to parse setting 'signals' of node %s", node_name(n));
|
||||
|
||||
|
@ -290,7 +290,7 @@ int iec61850_sv_start(struct node *n)
|
|||
SVReceiver_addSubscriber(i->in.receiver, i->in.subscriber);
|
||||
|
||||
/* Initialize pool and queue to pass samples between threads */
|
||||
ret = pool_init(&i->in.pool, 1024, SAMPLE_LENGTH(vlist_length(&n->signals)), &memory_hugepage);
|
||||
ret = pool_init(&i->in.pool, 1024, SAMPLE_LENGTH(vlist_length(&n->in.signals)), &memory_hugepage);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -300,7 +300,7 @@ int iec61850_sv_start(struct node *n)
|
|||
|
||||
for (unsigned k = 0; k < vlist_length(&i->in.signals); k++) {
|
||||
struct iec61850_type_descriptor *m = (struct iec61850_type_descriptor *) vlist_at(&i->in.signals, k);
|
||||
struct signal *sig = (struct signal *) vlist_at(&n->signals, k);
|
||||
struct signal *sig = (struct signal *) vlist_at(&n->in.signals, k);
|
||||
|
||||
if (sig->type == SIGNAL_TYPE_AUTO)
|
||||
sig->type = m->format;
|
||||
|
@ -352,7 +352,7 @@ int iec61850_sv_read(struct node *n, struct sample *smps[], unsigned cnt, unsign
|
|||
struct sample *smpt[cnt];
|
||||
|
||||
if (!i->in.enabled)
|
||||
return 0;
|
||||
return -1;
|
||||
|
||||
pulled = queue_signalled_pull_many(&i->in.queue, (void **) smpt, cnt);
|
||||
|
||||
|
@ -367,7 +367,7 @@ int iec61850_sv_write(struct node *n, struct sample *smps[], unsigned cnt, unsig
|
|||
struct iec61850_sv *i = (struct iec61850_sv *) n->_vd;
|
||||
|
||||
if (!i->out.enabled)
|
||||
return 0;
|
||||
return -1;
|
||||
|
||||
for (unsigned j = 0; j < cnt; j++) {
|
||||
unsigned offset = 0;
|
||||
|
|
|
@ -74,7 +74,12 @@ int loopback_start(struct node *n)
|
|||
int ret;
|
||||
struct loopback *l = (struct loopback *) n->_vd;
|
||||
|
||||
ret = pool_init(&l->pool, l->queuelen, SAMPLE_LENGTH(vlist_length(&n->signals)), &memory_hugepage);
|
||||
int len = MAX(
|
||||
vlist_length(&n->in.signals),
|
||||
vlist_length(&n->out.signals)
|
||||
);
|
||||
|
||||
ret = pool_init(&l->pool, l->queuelen, SAMPLE_LENGTH(len), &memory_hugepage);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ static void mqtt_connect_cb(struct mosquitto *mosq, void *userdata, int result)
|
|||
if (m->subscribe) {
|
||||
ret = mosquitto_subscribe(m->client, NULL, m->subscribe, m->qos);
|
||||
if (ret)
|
||||
warning("MQTT: failed to subscribe to topic '%s' for node %s", m->subscribe, node_name(n));
|
||||
warning("MQTT: failed to subscribe to topic '%s' for node %s: %s", m->subscribe, node_name(n), mosquitto_strerror(ret));
|
||||
}
|
||||
else
|
||||
warning("MQTT: no subscribe for node %s as no subscribe topic is given", node_name(n));
|
||||
|
@ -207,11 +207,11 @@ int mqtt_parse(struct node *n, json_t *cfg)
|
|||
// Some checks
|
||||
ret = mosquitto_sub_topic_check(m->subscribe);
|
||||
if (ret != MOSQ_ERR_SUCCESS)
|
||||
error("Invalid subscribe topic: '%s' for node %s", m->subscribe, node_name(n));
|
||||
error("Invalid subscribe topic: '%s' for node %s: %s", m->subscribe, node_name(n), mosquitto_strerror(ret));
|
||||
|
||||
ret = mosquitto_pub_topic_check(m->publish);
|
||||
if (ret != MOSQ_ERR_SUCCESS)
|
||||
error("Invalid publish topic: '%s' for node %s", m->publish, node_name(n));
|
||||
error("Invalid publish topic: '%s' for node %s: %s", m->publish, node_name(n), mosquitto_strerror(ret));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -283,17 +283,17 @@ int mqtt_start(struct node *n)
|
|||
if (m->username && m->password) {
|
||||
ret = mosquitto_username_pw_set(m->client, m->username, m->password);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto mosquitto_error;
|
||||
}
|
||||
|
||||
if (m->ssl.enabled) {
|
||||
ret = mosquitto_tls_set(m->client, m->ssl.cafile, m->ssl.capath, m->ssl.certfile, m->ssl.keyfile, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto mosquitto_error;
|
||||
|
||||
ret = mosquitto_tls_insecure_set(m->client, m->ssl.insecure);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto mosquitto_error;
|
||||
}
|
||||
|
||||
mosquitto_log_callback_set(m->client, mqtt_log_cb);
|
||||
|
@ -302,7 +302,7 @@ int mqtt_start(struct node *n)
|
|||
mosquitto_message_callback_set(m->client, mqtt_message_cb);
|
||||
mosquitto_subscribe_callback_set(m->client, mqtt_subscribe_cb);
|
||||
|
||||
ret = io_init(&m->io, m->format, &n->signals, SAMPLE_HAS_ALL & ~SAMPLE_HAS_OFFSET);
|
||||
ret = io_init(&m->io, m->format, &n->in.signals, SAMPLE_HAS_ALL & ~SAMPLE_HAS_OFFSET);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -310,7 +310,7 @@ int mqtt_start(struct node *n)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pool_init(&m->pool, 1024, SAMPLE_LENGTH(vlist_length(&n->signals)), &memory_hugepage);
|
||||
ret = pool_init(&m->pool, 1024, SAMPLE_LENGTH(vlist_length(&n->in.signals)), &memory_hugepage);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -320,13 +320,18 @@ int mqtt_start(struct node *n)
|
|||
|
||||
ret = mosquitto_connect(m->client, m->host, m->port, m->keepalive);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto mosquitto_error;
|
||||
|
||||
ret = mosquitto_loop_start(m->client);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto mosquitto_error;
|
||||
|
||||
return 0;
|
||||
|
||||
mosquitto_error:
|
||||
warning("MQTT: %s", mosquitto_strerror(ret));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mqtt_stop(struct node *n)
|
||||
|
@ -336,17 +341,22 @@ int mqtt_stop(struct node *n)
|
|||
|
||||
ret = mosquitto_disconnect(m->client);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto mosquitto_error;
|
||||
|
||||
ret = mosquitto_loop_stop(m->client, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto mosquitto_error;
|
||||
|
||||
ret = io_destroy(&m->io);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
|
||||
mosquitto_error:
|
||||
warning("MQTT: %s", mosquitto_strerror(ret));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mqtt_type_start(struct super_node *sn)
|
||||
|
@ -355,9 +365,14 @@ int mqtt_type_start(struct super_node *sn)
|
|||
|
||||
ret = mosquitto_lib_init();
|
||||
if (ret)
|
||||
return ret;
|
||||
goto mosquitto_error;
|
||||
|
||||
return 0;
|
||||
|
||||
mosquitto_error:
|
||||
warning("MQTT: %s", mosquitto_strerror(ret));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mqtt_type_stop()
|
||||
|
@ -366,9 +381,14 @@ int mqtt_type_stop()
|
|||
|
||||
ret = mosquitto_lib_cleanup();
|
||||
if (ret)
|
||||
return ret;
|
||||
goto mosquitto_error;
|
||||
|
||||
return 0;
|
||||
|
||||
mosquitto_error:
|
||||
warning("MQTT: %s", mosquitto_strerror(ret));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mqtt_read(struct node *n, struct sample *smps[], unsigned cnt, unsigned *release)
|
||||
|
@ -399,8 +419,7 @@ int mqtt_write(struct node *n, struct sample *smps[], unsigned cnt, unsigned *re
|
|||
return ret;
|
||||
|
||||
if (m->publish) {
|
||||
ret = mosquitto_publish(m->client, NULL /* mid */, m->publish, wbytes, data, m->qos,
|
||||
m->retain);
|
||||
ret = mosquitto_publish(m->client, NULL /* mid */, m->publish, wbytes, data, m->qos, m->retain);
|
||||
if (ret != MOSQ_ERR_SUCCESS) {
|
||||
warning("MQTT: publish failed for node %s: %s", node_name(n), mosquitto_strerror(ret));
|
||||
return -abs(ret);
|
||||
|
|
|
@ -153,7 +153,7 @@ int nanomsg_start(struct node *n)
|
|||
int ret;
|
||||
struct nanomsg *m = (struct nanomsg *) n->_vd;
|
||||
|
||||
ret = io_init(&m->io, m->format, &n->signals, SAMPLE_HAS_ALL & ~SAMPLE_HAS_OFFSET);
|
||||
ret = io_init(&m->io, m->format, &n->in.signals, SAMPLE_HAS_ALL & ~SAMPLE_HAS_OFFSET);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
216
lib/nodes/rtp.c
216
lib/nodes/rtp.c
|
@ -52,21 +52,24 @@
|
|||
|
||||
static pthread_t re_pthread;
|
||||
|
||||
/* Forward declartions */
|
||||
/* Forward declarations */
|
||||
static struct plugin p;
|
||||
|
||||
static int rtp_set_rate(struct node *n, double rate)
|
||||
{
|
||||
struct rtp *r = (struct rtp *) n->_vd;
|
||||
int ratio;
|
||||
|
||||
switch (r->rtcp.throttle_mode) {
|
||||
case RTCP_THROTTLE_HOOK_LIMIT_RATE:
|
||||
limit_rate_set_rate(r->rtcp.throttle_hook, rate);
|
||||
|
||||
break;
|
||||
|
||||
case RTCP_THROTTLE_HOOK_DECIMATE:
|
||||
decimate_set_ratio(r->rtcp.throttle_hook, r->rate / rate);
|
||||
ratio = r->rate / rate;
|
||||
if (ratio == 0)
|
||||
ratio = 1;
|
||||
decimate_set_ratio(r->rtcp.throttle_hook, ratio);
|
||||
break;
|
||||
|
||||
case RTCP_THROTTLE_DISABLED:
|
||||
|
@ -76,6 +79,8 @@ static int rtp_set_rate(struct node *n, double rate)
|
|||
return -1;
|
||||
}
|
||||
|
||||
info("Set rate limiting for node %s to %f", node_name(n), rate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -86,19 +91,25 @@ static int rtp_aimd(struct node *n, double loss_frac)
|
|||
int ret;
|
||||
double rate;
|
||||
|
||||
if (loss_frac < 1e-3)
|
||||
if (!r->rtcp.enabled)
|
||||
return -1;
|
||||
|
||||
if (loss_frac < 0.01)
|
||||
rate = r->aimd.last_rate + r->aimd.a;
|
||||
else
|
||||
rate = r->aimd.last_rate * r->aimd.b;
|
||||
|
||||
debug(5, "Set rate limiting for node %s to %f", node_name(n), rate);
|
||||
|
||||
r->aimd.last_rate = rate;
|
||||
|
||||
ret = rtp_set_rate(n, rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (r->aimd.log)
|
||||
fprintf(r->aimd.log, "%d\t%f\t%f\n", r->rtcp.num_rrs, loss_frac, rate);
|
||||
|
||||
info("AIMD: %d\t%f\t%f", r->rtcp.num_rrs, loss_frac, rate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -111,8 +122,10 @@ int rtp_init(struct node *n)
|
|||
|
||||
r->aimd.a = 10;
|
||||
r->aimd.b = 0.5;
|
||||
r->aimd.last_rate = 1;
|
||||
r->aimd.last_rate = 2000;
|
||||
r->aimd.log = NULL;
|
||||
|
||||
r->rtcp.enabled = false;
|
||||
r->rtcp.throttle_mode = RTCP_THROTTLE_DISABLED;
|
||||
|
||||
return 0;
|
||||
|
@ -161,9 +174,10 @@ int rtp_parse(struct node *n, json_t *cfg)
|
|||
|
||||
/* AIMD */
|
||||
if (json_aimd) {
|
||||
ret = json_unpack_ex(json_rtcp, &err, 0, "{ s?: F, s?: F }",
|
||||
ret = json_unpack_ex(json_aimd, &err, 0, "{ s?: F, s?: F, s?: F }",
|
||||
"a", &r->aimd.a,
|
||||
"b", &r->aimd.b
|
||||
"b", &r->aimd.b,
|
||||
"start_rate", &r->aimd.last_rate
|
||||
);
|
||||
if (ret)
|
||||
jerror(&err, "Failed to parse configuration of node %s", node_name(n));
|
||||
|
@ -174,7 +188,10 @@ int rtp_parse(struct node *n, json_t *cfg)
|
|||
const char *mode = "aimd";
|
||||
const char *throttle_mode = "decimate";
|
||||
|
||||
ret = json_unpack_ex(json_rtcp, &err, 0, "{ s?: b, s?: s }",
|
||||
/* Enable if RTCP section is available */
|
||||
r->rtcp.enabled = 1;
|
||||
|
||||
ret = json_unpack_ex(json_rtcp, &err, 0, "{ s?: b, s?: s, s?: s }",
|
||||
"enabled", &r->rtcp.enabled,
|
||||
"mode", &mode,
|
||||
"throttle_mode", &throttle_mode
|
||||
|
@ -201,7 +218,7 @@ int rtp_parse(struct node *n, json_t *cfg)
|
|||
|
||||
/* Format */
|
||||
r->format = format_type_lookup(format);
|
||||
if(!r->format)
|
||||
if (!r->format)
|
||||
error("Invalid format '%s' for node %s", format, node_name(n));
|
||||
|
||||
/* Remote address */
|
||||
|
@ -259,15 +276,24 @@ char * rtp_print(struct node *n)
|
|||
switch (r->rtcp.throttle_mode) {
|
||||
case RTCP_THROTTLE_HOOK_DECIMATE:
|
||||
throttle_mode = "decimate";
|
||||
break;
|
||||
|
||||
case RTCP_THROTTLE_HOOK_LIMIT_RATE:
|
||||
throttle_mode = "limit_rate";
|
||||
break;
|
||||
|
||||
case RTCP_THROTTLE_DISABLED:
|
||||
throttle_mode = "disabled";
|
||||
break;
|
||||
|
||||
default:
|
||||
throttle_mode = "unknown";
|
||||
}
|
||||
|
||||
strcatf(&buf, ", rtcp.mode=%s, rtcp.throttle_mode=%s", mode, throttle_mode);
|
||||
|
||||
if (r->rtcp.mode == RTCP_MODE_AIMD)
|
||||
strcatf(&buf, ", aimd.a=%f, aimd.b=%f, aimd.start_rate=%f", r->aimd.a, r->aimd.b, r->aimd.last_rate);
|
||||
}
|
||||
|
||||
free(local);
|
||||
|
@ -278,30 +304,44 @@ char * rtp_print(struct node *n)
|
|||
|
||||
static void rtp_handler(const struct sa *src, const struct rtp_header *hdr, struct mbuf *mb, void *arg)
|
||||
{
|
||||
int ret;
|
||||
struct node *n = (struct node *) arg;
|
||||
struct rtp *r = (struct rtp *) n->_vd;
|
||||
|
||||
if (queue_signalled_push(&r->recv_queue, (void *) mbuf_alloc_ref(mb)) != 1)
|
||||
warning("Failed to push to queue");
|
||||
|
||||
/* source, header not yet used */
|
||||
/* source, header not used */
|
||||
(void) src;
|
||||
(void) hdr;
|
||||
|
||||
void *d = mem_ref((void *) mb);
|
||||
|
||||
ret = queue_signalled_push(&r->recv_queue, d);
|
||||
if (ret != 1) {
|
||||
warning("Failed to push to queue");
|
||||
mem_deref(d);
|
||||
}
|
||||
}
|
||||
|
||||
static void rtcp_handler(const struct sa *src, struct rtcp_msg *msg, void *arg)
|
||||
{
|
||||
struct node *n = (struct node *) arg;
|
||||
//struct rtp *r = (struct rtp *) n->_vd;
|
||||
struct rtp *r = (struct rtp *) n->_vd;
|
||||
|
||||
(void)src;
|
||||
/* source not used */
|
||||
(void) src;
|
||||
|
||||
printf("rtcp: recv %s\n", rtcp_type_name(msg->hdr.pt));
|
||||
debug(5, "RTCP: recv %s", rtcp_type_name(msg->hdr.pt));
|
||||
|
||||
/** @todo: parse receive report */
|
||||
double loss_frac = 0;
|
||||
if (msg->hdr.pt == RTCP_SR) {
|
||||
if (msg->hdr.count > 0) {
|
||||
const struct rtcp_rr *rr = &msg->r.sr.rrv[0];
|
||||
debug(5, "RTP: fraction lost = %d", rr->fraction);
|
||||
rtp_aimd(n, (double) rr->fraction / 256);
|
||||
}
|
||||
else
|
||||
debug(5, "RTCP: Received sender report with zero reception reports");
|
||||
}
|
||||
|
||||
rtp_aimd(n, loss_frac);
|
||||
r->rtcp.num_rrs++;
|
||||
}
|
||||
|
||||
int rtp_start(struct node *n)
|
||||
|
@ -309,16 +349,25 @@ int rtp_start(struct node *n)
|
|||
int ret;
|
||||
struct rtp *r = (struct rtp *) n->_vd;
|
||||
|
||||
/* Initialize Queue */
|
||||
/* Initialize queue */
|
||||
ret = queue_signalled_init(&r->recv_queue, 1024, &memory_heap, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Initialize IO */
|
||||
ret = io_init(&r->io, r->format, &n->signals, SAMPLE_HAS_ALL & ~SAMPLE_HAS_OFFSET);
|
||||
ret = io_init(&r->io, r->format, &n->in.signals, SAMPLE_HAS_ALL & ~SAMPLE_HAS_OFFSET);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Initialize memory buffer for sending */
|
||||
r->send_mb = mbuf_alloc(RTP_INITIAL_BUFFER_LEN);
|
||||
if (!r->send_mb)
|
||||
return -1;
|
||||
|
||||
ret = mbuf_fill(r->send_mb, 0, RTP_HEADER_SIZE);
|
||||
if (ret)
|
||||
return -1;
|
||||
|
||||
ret = io_check(&r->io);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -340,7 +389,8 @@ int rtp_start(struct node *n)
|
|||
throttle_hook_type = hook_type_lookup("limit_rate");
|
||||
break;
|
||||
|
||||
default: { }
|
||||
default:
|
||||
throttle_hook_type = NULL;
|
||||
}
|
||||
|
||||
if (!throttle_hook_type)
|
||||
|
@ -362,9 +412,30 @@ int rtp_start(struct node *n)
|
|||
ret = rtp_listen(&r->rs, IPPROTO_UDP, &r->in.saddr_rtp, port, port+1, r->rtcp.enabled, rtp_handler, rtcp_handler, n);
|
||||
|
||||
/* Start RTCP session */
|
||||
if (r->rtcp.enabled)
|
||||
if (r->rtcp.enabled) {
|
||||
r->rtcp.num_rrs = 0;
|
||||
|
||||
rtcp_start(r->rs, node_name(n), &r->out.saddr_rtcp);
|
||||
|
||||
if (r->rtcp.mode == RTCP_MODE_AIMD) {
|
||||
char date[32], fn[128];
|
||||
|
||||
time_t ts = time(NULL);
|
||||
struct tm tm;
|
||||
|
||||
/* Convert time */
|
||||
gmtime_r(&ts, &tm);
|
||||
strftime(date, sizeof(date), "%Y_%m_%d_%s", &tm);
|
||||
snprintf(fn, sizeof(fn), "aimd-rates-%s-%s.log", node_name_short(n), date);
|
||||
|
||||
r->aimd.log = fopen(fn, "w+");
|
||||
if (!r->aimd.log)
|
||||
return -1;
|
||||
|
||||
fprintf(r->aimd.log, "# cnt\tfrac_loss\trate\n");
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -383,6 +454,14 @@ int rtp_stop(struct node *n)
|
|||
if (ret)
|
||||
warning("Problem destroying queue");
|
||||
|
||||
mem_deref(r->send_mb);
|
||||
|
||||
if (r->rtcp.enabled && r->rtcp.mode == RTCP_MODE_AIMD) {
|
||||
ret = fclose(r->aimd.log);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return io_destroy(&r->io);
|
||||
}
|
||||
|
||||
|
@ -404,14 +483,14 @@ int rtp_type_start(struct super_node *sn)
|
|||
/* Initialize library */
|
||||
ret = libre_init();
|
||||
if (ret) {
|
||||
error("Error initializing libre");
|
||||
warning("Error initializing libre");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Add worker thread */
|
||||
ret = pthread_create(&re_pthread, NULL, th_func, NULL);
|
||||
if (ret) {
|
||||
error("Error creating rtp node type pthread");
|
||||
warning("Error creating rtp node type pthread");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -463,29 +542,20 @@ int rtp_read(struct node *n, struct sample *smps[], unsigned cnt, unsigned *rele
|
|||
{
|
||||
int ret;
|
||||
struct rtp *r = (struct rtp *) n->_vd;
|
||||
size_t bytes;
|
||||
char *buf;
|
||||
struct mbuf *mb;
|
||||
|
||||
/* Get data from queue */
|
||||
ret = queue_signalled_pull(&r->recv_queue, (void **) &mb);
|
||||
if (ret <= 0) {
|
||||
if (ret < 0)
|
||||
warning("Failed to pull from queue");
|
||||
if (ret < 0) {
|
||||
warning("Failed to pull from queue");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Read from mbuf */
|
||||
bytes = mbuf_get_left(mb);
|
||||
buf = (char *) alloc(bytes);
|
||||
mbuf_read_mem(mb, (uint8_t *) buf, bytes);
|
||||
|
||||
/* Unpack data */
|
||||
ret = io_sscan(&r->io, buf, bytes, NULL, smps, cnt);
|
||||
if (ret < 0)
|
||||
warning("Received invalid packet from node %s: reason=%d", node_name(n), ret);
|
||||
ret = io_sscan(&r->io, (char *) mb->buf + mb->pos, mbuf_get_left(mb), NULL, smps, cnt);
|
||||
|
||||
mem_deref(mb);
|
||||
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -494,70 +564,36 @@ int rtp_write(struct node *n, struct sample *smps[], unsigned cnt, unsigned *rel
|
|||
int ret;
|
||||
struct rtp *r = (struct rtp *) n->_vd;
|
||||
|
||||
char *buf;
|
||||
char pad[] = " ";
|
||||
size_t buflen;
|
||||
size_t wbytes;
|
||||
size_t avail;
|
||||
|
||||
buflen = RTP_INITIAL_BUFFER_LEN;
|
||||
buf = alloc(buflen);
|
||||
if (!buf) {
|
||||
warning("Error allocating buffer space");
|
||||
uint32_t ts = (uint32_t) time(NULL);
|
||||
|
||||
retry: mbuf_set_pos(r->send_mb, RTP_HEADER_SIZE);
|
||||
avail = mbuf_get_space(r->send_mb);
|
||||
cnt = io_sprint(&r->io, (char *) r->send_mb->buf + r->send_mb->pos, avail, &wbytes, smps, cnt);
|
||||
if (cnt < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
retry: cnt = io_sprint(&r->io, buf, buflen, &wbytes, smps, cnt);
|
||||
if (cnt < 0) {
|
||||
warning("Error from io_sprint, reason: %d", cnt);
|
||||
goto out1;
|
||||
}
|
||||
if (wbytes > avail) {
|
||||
ret = mbuf_resize(r->send_mb, wbytes + RTP_HEADER_SIZE);
|
||||
if (!ret)
|
||||
return -1;
|
||||
|
||||
if (wbytes <= 0) {
|
||||
warning("Error written bytes = %ld <= 0", wbytes);
|
||||
cnt = -1;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
if (wbytes > buflen) {
|
||||
buflen = wbytes;
|
||||
buf = realloc(buf, buflen);
|
||||
goto retry;
|
||||
}
|
||||
else
|
||||
mbuf_set_end(r->send_mb, r->send_mb->pos + wbytes);
|
||||
|
||||
/* Prepare mbuf */
|
||||
struct mbuf *mb = mbuf_alloc(buflen + 12);
|
||||
if (!mb) {
|
||||
warning("Failed to allocate memory");
|
||||
cnt = -1;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
ret = mbuf_write_str(mb, pad);
|
||||
if (ret) {
|
||||
warning("Error writing padding to mbuf");
|
||||
cnt = ret;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
ret = mbuf_write_mem(mb, (uint8_t*)buf, buflen);
|
||||
if (ret) {
|
||||
warning("Error writing data to mbuf");
|
||||
cnt = ret;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
mbuf_set_pos(mb, 12);
|
||||
mbuf_set_pos(r->send_mb, RTP_HEADER_SIZE);
|
||||
|
||||
/* Send dataset */
|
||||
ret = rtp_send(r->rs, &r->out.saddr_rtp, false, false, 21, (uint32_t) time(NULL), mb);
|
||||
ret = rtp_send(r->rs, &r->out.saddr_rtp, false, false, RTP_PACKET_TYPE, ts, r->send_mb);
|
||||
if (ret) {
|
||||
warning("Error from rtp_send, reason: %d", ret);
|
||||
cnt = ret;
|
||||
}
|
||||
|
||||
out2: mem_deref(mb);
|
||||
out1: free(buf);
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
|
|
|
@ -45,9 +45,11 @@ int shmem_parse(struct node *n, json_t *cfg)
|
|||
json_t *json_exec = NULL;
|
||||
json_error_t err;
|
||||
|
||||
int len = MAX(vlist_length(&n->in.signals), vlist_length(&n->out.signals));
|
||||
|
||||
/* Default values */
|
||||
shm->conf.queuelen = MAX(DEFAULT_SHMEM_QUEUELEN, n->in.vectorize);
|
||||
shm->conf.samplelen = vlist_length(&n->signals);
|
||||
shm->conf.samplelen = len;
|
||||
shm->conf.polling = false;
|
||||
shm->exec = NULL;
|
||||
|
||||
|
@ -134,8 +136,11 @@ int shmem_read(struct node *n, struct sample *smps[], unsigned cnt, unsigned *re
|
|||
if (recv < 0) {
|
||||
/* This can only really mean that the other process has exited, so close
|
||||
* the interface to make sure the shared memory object is unlinked */
|
||||
shmem_int_close(&shm->intf);
|
||||
warning("Shared memory segment has been closed for node: %s", node_name(n));
|
||||
|
||||
info("Shared memory segment has been closed.");
|
||||
|
||||
n->state = STATE_STOPPING;
|
||||
|
||||
return recv;
|
||||
}
|
||||
|
||||
|
@ -144,7 +149,7 @@ int shmem_read(struct node *n, struct sample *smps[], unsigned cnt, unsigned *re
|
|||
|
||||
/** @todo: signal descriptions are currently not shared between processes */
|
||||
for (int i = 0; i < recv; i++)
|
||||
smps[i]->signals = &n->signals;
|
||||
smps[i]->signals = &n->in.signals;
|
||||
|
||||
return recv;
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ static void signal_generator_init_signals(struct node *n)
|
|||
{
|
||||
struct signal_generator *s = (struct signal_generator *) n->_vd;
|
||||
|
||||
assert(vlist_length(&n->signals) == 0);
|
||||
assert(vlist_length(&n->in.signals) == 0);
|
||||
|
||||
for (int i = 0; i < s->values; i++) {
|
||||
struct signal *sig = alloc(sizeof(struct signal));
|
||||
|
@ -96,7 +96,7 @@ static void signal_generator_init_signals(struct node *n)
|
|||
sig->name = strdup(signal_generator_type_str(rtype));
|
||||
sig->type = SIGNAL_TYPE_FLOAT; /* All generated signals are of type float */
|
||||
|
||||
vlist_push(&n->signals, sig);
|
||||
vlist_push(&n->in.signals, sig);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,7 +226,7 @@ int signal_generator_read(struct node *n, struct sample *smps[], unsigned cnt, u
|
|||
t->ts.origin = ts;
|
||||
t->sequence = s->counter;
|
||||
t->length = MIN(s->values, t->capacity);
|
||||
t->signals = &n->signals;
|
||||
t->signals = &n->in.signals;
|
||||
|
||||
for (int i = 0; i < MIN(s->values, t->capacity); i++) {
|
||||
int rtype = (s->type != SIGNAL_GENERATOR_TYPE_MIXED) ? s->type : i % 7;
|
||||
|
@ -264,9 +264,11 @@ int signal_generator_read(struct node *n, struct sample *smps[], unsigned cnt, u
|
|||
}
|
||||
|
||||
if (s->limit > 0 && s->counter >= s->limit) {
|
||||
info("Reached limit of node %s", node_name(n));
|
||||
killme(SIGTERM);
|
||||
return 0;
|
||||
info("Reached limit.");
|
||||
|
||||
n->state = STATE_STOPPING;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->counter += steps;
|
||||
|
|
|
@ -163,7 +163,7 @@ int socket_start(struct node *n)
|
|||
int ret;
|
||||
|
||||
/* Initialize IO */
|
||||
ret = io_init(&s->io, s->format, &n->signals, SAMPLE_HAS_ALL & ~SAMPLE_HAS_OFFSET);
|
||||
ret = io_init(&s->io, s->format, &n->in.signals, SAMPLE_HAS_ALL & ~SAMPLE_HAS_OFFSET);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -38,55 +38,57 @@
|
|||
|
||||
static struct vlist *nodes; /** The global list of nodes */
|
||||
|
||||
static void stats_init_signals(struct node *n)
|
||||
int stats_node_signal_destroy(struct stats_node_signal *s)
|
||||
{
|
||||
struct stats_desc *desc;
|
||||
struct signal *sig;
|
||||
free(s->node_str);
|
||||
|
||||
for (int i = 0; i < STATS_COUNT; i++) {
|
||||
desc = &stats_metrics[i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Total */
|
||||
sig = alloc(sizeof(struct signal));
|
||||
sig->name = strf("%s.%s", desc->name, "total");
|
||||
sig->type = SIGNAL_TYPE_INTEGER;
|
||||
vlist_push(&n->signals, sig);
|
||||
int stats_node_signal_parse(struct stats_node_signal *s, json_t *cfg)
|
||||
{
|
||||
json_error_t err;
|
||||
|
||||
/* Last */
|
||||
sig = alloc(sizeof(struct signal));
|
||||
sig->name = strf("%s.%s", desc->name, "last");
|
||||
sig->unit = strdup(desc->unit);
|
||||
sig->type = SIGNAL_TYPE_FLOAT;
|
||||
vlist_push(&n->signals, sig);
|
||||
int ret;
|
||||
const char *stats;
|
||||
char *metric, *type, *node, *cpy;
|
||||
|
||||
/* Highest */
|
||||
sig = alloc(sizeof(struct signal));
|
||||
sig->name = strf("%s.%s", desc->name, "highest");
|
||||
sig->unit = strdup(desc->unit);
|
||||
sig->type = SIGNAL_TYPE_FLOAT;
|
||||
vlist_push(&n->signals, sig);
|
||||
ret = json_unpack_ex(cfg, &err, 0, "{ s: s }",
|
||||
"stats", &stats
|
||||
);
|
||||
if (ret)
|
||||
return -1;
|
||||
|
||||
/* Lowest */
|
||||
sig = alloc(sizeof(struct signal));
|
||||
sig->name = strf("%s.%s", desc->name, "lowest");
|
||||
sig->unit = strdup(desc->unit);
|
||||
sig->type = SIGNAL_TYPE_FLOAT;
|
||||
vlist_push(&n->signals, sig);
|
||||
cpy = strdup(stats);
|
||||
|
||||
/* Mean */
|
||||
sig = alloc(sizeof(struct signal));
|
||||
sig->name = strf("%s.%s", desc->name, "mean");
|
||||
sig->unit = strdup(desc->unit);
|
||||
sig->type = SIGNAL_TYPE_FLOAT;
|
||||
vlist_push(&n->signals, sig);
|
||||
node = strtok(cpy, ".");
|
||||
if (!node)
|
||||
goto invalid_format;
|
||||
|
||||
/* Variance */
|
||||
sig = alloc(sizeof(struct signal));
|
||||
sig->name = strf("%s.%s", desc->name, "var");
|
||||
sig->unit = strf("%s^2", desc->unit); // variance has squared unit of variable
|
||||
sig->type = SIGNAL_TYPE_FLOAT;
|
||||
vlist_push(&n->signals, sig);
|
||||
}
|
||||
metric = strtok(NULL, ".");
|
||||
if (!metric)
|
||||
goto invalid_format;
|
||||
|
||||
type = strtok(NULL, ".");
|
||||
if (!type)
|
||||
goto invalid_format;
|
||||
|
||||
s->metric = stats_lookup_metric(metric);
|
||||
if (s->metric < 0)
|
||||
goto invalid_format;
|
||||
|
||||
s->type = stats_lookup_type(type);
|
||||
if (s->type < 0)
|
||||
goto invalid_format;
|
||||
|
||||
s->node_str = strdup(node);
|
||||
|
||||
free(cpy);
|
||||
return 0;
|
||||
|
||||
invalid_format:
|
||||
free(cpy);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int stats_node_type_start(struct super_node *sn)
|
||||
|
@ -105,9 +107,13 @@ int stats_node_start(struct node *n)
|
|||
if (ret)
|
||||
serror("Failed to create task");
|
||||
|
||||
s->node = vlist_lookup(nodes, s->node_str);
|
||||
if (!s->node)
|
||||
error("Invalid reference node %s for setting 'node' of node %s", s->node_str, node_name(n));
|
||||
for (size_t i = 0; i < vlist_length(&s->signals); i++) {
|
||||
struct stats_node_signal *stats_sig = (struct stats_node_signal *) vlist_at(&s->signals, i);
|
||||
|
||||
stats_sig->node = vlist_lookup(nodes, stats_sig->node_str);
|
||||
if (!stats_sig->node)
|
||||
error("Invalid reference node %s for setting 'node' of node %s", stats_sig->node_str, node_name(n));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -128,7 +134,31 @@ char * stats_node_print(struct node *n)
|
|||
{
|
||||
struct stats_node *s = (struct stats_node *) n->_vd;
|
||||
|
||||
return strf("node=%s, rate=%f", s->node_str, s->rate);
|
||||
return strf("rate=%f", s->rate);
|
||||
}
|
||||
|
||||
int stats_node_init(struct node *n)
|
||||
{
|
||||
int ret;
|
||||
struct stats_node *s = (struct stats_node *) n->_vd;
|
||||
|
||||
ret = vlist_init(&s->signals);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int stats_node_destroy(struct node *n)
|
||||
{
|
||||
int ret;
|
||||
struct stats_node *s = (struct stats_node *) n->_vd;
|
||||
|
||||
ret = vlist_destroy(&s->signals, (dtor_cb_t) stats_node_signal_destroy, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int stats_node_parse(struct node *n, json_t *cfg)
|
||||
|
@ -136,13 +166,14 @@ int stats_node_parse(struct node *n, json_t *cfg)
|
|||
struct stats_node *s = (struct stats_node *) n->_vd;
|
||||
|
||||
int ret;
|
||||
size_t i;
|
||||
json_error_t err;
|
||||
json_t *json_signals, *json_signal;
|
||||
|
||||
const char *node;
|
||||
|
||||
ret = json_unpack_ex(cfg, &err, 0, "{ s: s, s: F }",
|
||||
"node", &node,
|
||||
"rate", &s->rate
|
||||
ret = json_unpack_ex(cfg, &err, 0, "{ s: F, s: { s: o } }",
|
||||
"rate", &s->rate,
|
||||
"in",
|
||||
"signals", &json_signals
|
||||
);
|
||||
if (ret)
|
||||
jerror(&err, "Failed to parse configuration of node %s", node_name(n));
|
||||
|
@ -150,50 +181,68 @@ int stats_node_parse(struct node *n, json_t *cfg)
|
|||
if (s->rate <= 0)
|
||||
error("Setting 'rate' of node %s must be positive", node_name(n));
|
||||
|
||||
s->node_str = strdup(node);
|
||||
if (!json_is_array(json_signals))
|
||||
error("Setting 'in.signals' of node %s must be an array", node_name(n));
|
||||
|
||||
stats_init_signals(n);
|
||||
json_array_foreach(json_signals, i, json_signal) {
|
||||
struct signal *sig = (struct signal *) vlist_at(&n->in.signals, i);
|
||||
struct stats_node_signal *stats_sig;
|
||||
|
||||
return 0;
|
||||
}
|
||||
stats_sig = alloc(sizeof(struct stats_node_signal));
|
||||
if (!stats_sig)
|
||||
return -1;
|
||||
|
||||
int stats_node_destroy(struct node *n)
|
||||
{
|
||||
struct stats_node *s = (struct stats_node *) n->_vd;
|
||||
ret = stats_node_signal_parse(stats_sig, json_signal);
|
||||
if (ret)
|
||||
error("Failed to parse signal definition of node %s", node_name(n));
|
||||
|
||||
if (s->node_str)
|
||||
free(s->node_str);
|
||||
if (!sig->name) {
|
||||
const char *metric = stats_metrics[stats_sig->metric].name;
|
||||
const char *type = stats_types[stats_sig->type].name;
|
||||
|
||||
sig->name = strf("%s.%s.%s", stats_sig->node_str, metric, type);
|
||||
}
|
||||
|
||||
if (!sig->unit)
|
||||
sig->unit = strdup(stats_metrics[stats_sig->metric].unit);
|
||||
|
||||
if (sig->type == SIGNAL_TYPE_AUTO)
|
||||
sig->type = stats_types[stats_sig->type].signal_type;
|
||||
else if (sig->type != stats_types[stats_sig->type].signal_type)
|
||||
error("Invalid type for signal %zu in node %s", i, node_name(n));
|
||||
|
||||
vlist_push(&s->signals, stats_sig);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int stats_node_read(struct node *n, struct sample *smps[], unsigned cnt, unsigned *release)
|
||||
{
|
||||
struct stats_node *sn = (struct stats_node *) n->_vd;
|
||||
struct stats *s = sn->node->stats;
|
||||
struct stats_node *s = (struct stats_node *) n->_vd;
|
||||
|
||||
if (!cnt)
|
||||
return 0;
|
||||
|
||||
if (!sn->node->stats)
|
||||
return 0;
|
||||
task_wait(&s->task);
|
||||
|
||||
task_wait(&sn->task);
|
||||
int len = MIN(vlist_length(&s->signals), smps[0]->capacity);
|
||||
|
||||
smps[0]->length = MIN(STATS_COUNT * 6, smps[0]->capacity);
|
||||
smps[0]->flags = SAMPLE_HAS_DATA;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
struct stats *st;
|
||||
struct stats_node_signal *sig = (struct stats_node_signal *) vlist_at(&s->signals, i);
|
||||
|
||||
for (int i = 0; i < 6 && (i+1)*STATS_METRICS <= smps[0]->length; i++) {
|
||||
int tot = hist_total(&s->histograms[i]);
|
||||
st = sig->node->stats;
|
||||
if (!st)
|
||||
return -1;
|
||||
|
||||
smps[0]->data[i*STATS_METRICS+0].f = tot ? hist_total(&s->histograms[i]) : 0;
|
||||
smps[0]->data[i*STATS_METRICS+1].f = tot ? hist_last(&s->histograms[i]) : 0;
|
||||
smps[0]->data[i*STATS_METRICS+2].f = tot ? hist_highest(&s->histograms[i]) : 0;
|
||||
smps[0]->data[i*STATS_METRICS+3].f = tot ? hist_lowest(&s->histograms[i]) : 0;
|
||||
smps[0]->data[i*STATS_METRICS+4].f = tot ? hist_mean(&s->histograms[i]) : 0;
|
||||
smps[0]->data[i*STATS_METRICS+5].f = tot ? hist_var(&s->histograms[i]) : 0;
|
||||
smps[0]->data[i] = stats_get_value(st, sig->metric, sig->type);
|
||||
}
|
||||
|
||||
smps[0]->length = len;
|
||||
smps[0]->flags = SAMPLE_HAS_DATA;
|
||||
smps[0]->signals = &n->in.signals;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -212,10 +261,11 @@ static struct plugin p = {
|
|||
.type = PLUGIN_TYPE_NODE,
|
||||
.node = {
|
||||
.vectorize = 1,
|
||||
.flags = NODE_TYPE_PROVIDES_SIGNALS,
|
||||
.flags = 0,
|
||||
.size = sizeof(struct stats_node),
|
||||
.type.start = stats_node_type_start,
|
||||
.parse = stats_node_parse,
|
||||
.init = stats_node_init,
|
||||
.destroy = stats_node_destroy,
|
||||
.print = stats_node_print,
|
||||
.start = stats_node_start,
|
||||
|
|
|
@ -247,7 +247,7 @@ int test_rtt_start(struct node *n)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = io_init(&t->io, t->format, &n->signals, SAMPLE_HAS_ALL & ~SAMPLE_HAS_DATA);
|
||||
ret = io_init(&t->io, t->format, &n->in.signals, SAMPLE_HAS_ALL & ~SAMPLE_HAS_DATA);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -307,9 +307,11 @@ int test_rtt_read(struct node *n, struct sample *smps[], unsigned cnt, unsigned
|
|||
}
|
||||
|
||||
if (t->current >= vlist_length(&t->cases)) {
|
||||
info("This was the last case. Terminating.");
|
||||
killme(SIGTERM);
|
||||
pause();
|
||||
info("This was the last case. Stopping node %s", node_name(n));
|
||||
|
||||
n->state = STATE_STOPPING;
|
||||
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
struct test_rtt_case *c = (struct test_rtt_case *) vlist_at(&t->cases, t->current);
|
||||
|
|
|
@ -152,6 +152,8 @@ static DaqDeviceDescriptor * uldaq_find_device(struct uldaq *u) {
|
|||
return &descriptors[0];
|
||||
|
||||
for (int i = 0; i < num_devs; i++) {
|
||||
d = &descriptors[i];
|
||||
|
||||
if (u->device_id) {
|
||||
if (strcmp(u->device_id, d->uniqueId))
|
||||
break;
|
||||
|
@ -176,7 +178,7 @@ static int uldaq_connect(struct node *n)
|
|||
/* Find Matching device */
|
||||
if (!u->device_descriptor) {
|
||||
u->device_descriptor = uldaq_find_device(u);
|
||||
if (u->device_descriptor) {
|
||||
if (!u->device_descriptor) {
|
||||
warning("Unable to find a matching device for node '%s'", node_name(n));
|
||||
return -1;
|
||||
}
|
||||
|
@ -224,7 +226,7 @@ int uldaq_type_start(struct super_node *sn)
|
|||
for (int i = 0; i < num_devs; i++) {
|
||||
DaqDeviceDescriptor *desc = &descriptors[i];
|
||||
|
||||
info(" %d: %s %s", i, desc->uniqueId, desc->devString);
|
||||
info(" %d: %s %s (%s)", i, desc->uniqueId, desc->devString, uldaq_print_interface_type(desc->devInterface));
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -307,7 +309,7 @@ int uldaq_parse(struct node *n, json_t *cfg)
|
|||
u->device_interface_type = iftype;
|
||||
}
|
||||
|
||||
u->in.channel_count = vlist_length(&n->signals);
|
||||
u->in.channel_count = vlist_length(&n->in.signals);
|
||||
u->in.queues = realloc(u->in.queues, sizeof(struct AiQueueElement) * u->in.channel_count);
|
||||
|
||||
json_array_foreach(json_signals, i, json_signal) {
|
||||
|
@ -358,11 +360,21 @@ char * uldaq_print(struct node *n)
|
|||
struct uldaq *u = (struct uldaq *) n->_vd;
|
||||
|
||||
char *buf = NULL;
|
||||
char *uid = u->device_descriptor->uniqueId;
|
||||
char *name = u->device_descriptor->productName;
|
||||
const char *iftype = uldaq_print_interface_type(u->device_descriptor->devInterface);
|
||||
|
||||
buf = strcatf(&buf, "device=%s (%s), interface=%s", uid, name, iftype);
|
||||
if (u->device_descriptor) {
|
||||
char *uid = u->device_descriptor->uniqueId;
|
||||
char *name = u->device_descriptor->productName;
|
||||
const char *iftype = uldaq_print_interface_type(u->device_descriptor->devInterface);
|
||||
|
||||
buf = strcatf(&buf, "device=%s (%s), interface=%s", uid, name, iftype);
|
||||
}
|
||||
else {
|
||||
const char *uid = u->device_id;
|
||||
const char *iftype = uldaq_print_interface_type(u->device_interface_type);
|
||||
|
||||
buf = strcatf(&buf, "device=%s, interface=%s", uid, iftype);
|
||||
}
|
||||
|
||||
buf = strcatf(&buf, ", in.sample_rate=%f", u->in.sample_rate);
|
||||
|
||||
return buf;
|
||||
|
@ -413,19 +425,15 @@ int uldaq_check(struct node *n)
|
|||
Range ranges_se[num_ranges_se];
|
||||
|
||||
for (int i = 0; i < num_ranges_diff; i++) {
|
||||
long long rng;
|
||||
|
||||
err = ulAIGetInfo(u->device_handle, AI_INFO_DIFF_RANGE, i, &rng);
|
||||
|
||||
ranges_diff[i] = *(Range *) rng;
|
||||
err = ulAIGetInfo(u->device_handle, AI_INFO_DIFF_RANGE, i, (long long *) &ranges_diff[i]);
|
||||
if (err != ERR_NO_ERROR)
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_ranges_se; i++) {
|
||||
long long rng;
|
||||
|
||||
err = ulAIGetInfo(u->device_handle, AI_INFO_SE_RANGE, i, &rng);
|
||||
|
||||
ranges_se[i] = *(Range *) rng;
|
||||
err = ulAIGetInfo(u->device_handle, AI_INFO_SE_RANGE, i, (long long *) &ranges_se[i]);
|
||||
if (err != ERR_NO_ERROR)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!has_ai) {
|
||||
|
@ -443,8 +451,8 @@ int uldaq_check(struct node *n)
|
|||
return -1;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < vlist_length(&n->signals); i++) {
|
||||
struct signal *s = (struct signal *) vlist_at(&n->signals, i);
|
||||
for (size_t i = 0; i < vlist_length(&n->in.signals); i++) {
|
||||
struct signal *s = (struct signal *) vlist_at(&n->in.signals, i);
|
||||
AiQueueElement *q = &u->in.queues[i];
|
||||
|
||||
if (s->type != SIGNAL_TYPE_FLOAT) {
|
||||
|
@ -521,7 +529,7 @@ int uldaq_start(struct node *n)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
err = ulAInLoadQueue(u->device_handle, u->in.queues, vlist_length(&n->signals));
|
||||
err = ulAInLoadQueue(u->device_handle, u->in.queues, vlist_length(&n->in.signals));
|
||||
if (err != ERR_NO_ERROR) {
|
||||
warning("Failed to load input queue to DAQ device for node '%s'", node_name(n));
|
||||
return -1;
|
||||
|
@ -608,23 +616,9 @@ int uldaq_read(struct node *n, struct sample *smps[], unsigned cnt, unsigned *re
|
|||
if (u->in.status != SS_RUNNING)
|
||||
return -1;
|
||||
|
||||
/* Wait for data available condition triggered by event callback */
|
||||
pthread_mutex_lock(&u->in.mutex);
|
||||
long long start_index = u->in.buffer_pos;
|
||||
|
||||
long long current_index = u->in.transfer_status.currentIndex + u->in.channel_count;
|
||||
long long start_index = u->buffer_pos;
|
||||
|
||||
if (start_index + n->in.vectorize * u->in.channel_count > current_index)
|
||||
pthread_cond_wait(&u->in.cv, &u->in.mutex);
|
||||
|
||||
#if 0
|
||||
debug(2, "total count = %lld", u->in.transfer_status.currentTotalCount);
|
||||
debug(2, "index = %lld", u->in.transfer_status.currentIndex);
|
||||
debug(2, "scan count = %lld", u->in.transfer_status.currentScanCount);
|
||||
debug(2, "start index= %lld", start_index);
|
||||
#endif
|
||||
|
||||
for (int j = 0; j < n->in.vectorize; j++) {
|
||||
for (int j = 0; j < cnt; j++) {
|
||||
struct sample *smp = smps[j];
|
||||
|
||||
long long scan_index = start_index + j * u->in.channel_count;
|
||||
|
@ -636,12 +630,12 @@ int uldaq_read(struct node *n, struct sample *smps[], unsigned cnt, unsigned *re
|
|||
}
|
||||
|
||||
smp->length = u->in.channel_count;
|
||||
smp->signals = &n->signals;
|
||||
smp->signals = &n->in.signals;
|
||||
smp->sequence = u->sequence++;
|
||||
smp->flags = SAMPLE_HAS_SEQUENCE | SAMPLE_HAS_DATA;
|
||||
}
|
||||
|
||||
u->buffer_pos += u->in.channel_count * cnt;
|
||||
u->in.buffer_pos += u->in.channel_count * cnt;
|
||||
|
||||
pthread_mutex_unlock(&u->in.mutex);
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ static int websocket_connection_init(struct websocket_connection *c)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = io_init(&c->io, c->format, &c->node->signals, SAMPLE_HAS_ALL & ~SAMPLE_HAS_OFFSET);
|
||||
ret = io_init(&c->io, c->format, &c->node->in.signals, SAMPLE_HAS_ALL & ~SAMPLE_HAS_OFFSET);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -257,7 +257,7 @@ int zeromq_start(struct node *n)
|
|||
int ret;
|
||||
struct zeromq *z = (struct zeromq *) n->_vd;
|
||||
|
||||
ret = io_init(&z->io, z->format, &n->signals, SAMPLE_HAS_ALL & ~SAMPLE_HAS_OFFSET);
|
||||
ret = io_init(&z->io, z->format, &n->in.signals, SAMPLE_HAS_ALL & ~SAMPLE_HAS_OFFSET);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
186
lib/path.c
186
lib/path.c
|
@ -51,7 +51,7 @@ static int path_source_init(struct path_source *ps)
|
|||
if (ps->node->_vt->pool_size)
|
||||
pool_size = ps->node->_vt->pool_size;
|
||||
|
||||
ret = pool_init(&ps->pool, pool_size, SAMPLE_LENGTH(vlist_length(&ps->node->signals)), node_memory_type(ps->node, &memory_hugepage));
|
||||
ret = pool_init(&ps->pool, pool_size, SAMPLE_LENGTH(vlist_length(&ps->node->in.signals)), node_memory_type(ps->node, &memory_hugepage));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -73,9 +73,9 @@ static int path_source_destroy(struct path_source *ps)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void path_source_read(struct path_source *ps, struct path *p, int i)
|
||||
static int path_source_read(struct path_source *ps, struct path *p, int i)
|
||||
{
|
||||
int recv, tomux, allocated, cnt;
|
||||
int recv, tomux, allocated, cnt, toenqueue, enqueued = 0;
|
||||
unsigned release;
|
||||
|
||||
cnt = ps->node->in.vectorize;
|
||||
|
@ -93,10 +93,20 @@ static void path_source_read(struct path_source *ps, struct path *p, int i)
|
|||
release = allocated;
|
||||
|
||||
recv = node_read(ps->node, read_smps, allocated, &release);
|
||||
if (recv == 0)
|
||||
if (recv == 0) {
|
||||
enqueued = 0;
|
||||
goto out2;
|
||||
else if (recv < 0)
|
||||
error("Failed to read samples from node %s", node_name(ps->node));
|
||||
}
|
||||
else if (recv < 0) {
|
||||
if (ps->node->state == STATE_STOPPING) {
|
||||
p->state = STATE_STOPPING;
|
||||
|
||||
enqueued = -1;
|
||||
goto out2;
|
||||
}
|
||||
else
|
||||
error("Failed to read samples from node %s", node_name(ps->node));
|
||||
}
|
||||
else if (recv < allocated)
|
||||
warning("Partial read for path %s: read=%u, expected=%u", path_name(p), recv, allocated);
|
||||
|
||||
|
@ -116,11 +126,8 @@ static void path_source_read(struct path_source *ps, struct path *p, int i)
|
|||
? sample_clone(p->last_sample)
|
||||
: sample_clone(muxed_smps[i-1]);
|
||||
|
||||
muxed_smps[i]->flags = 0;
|
||||
if (p->original_sequence_no) {
|
||||
if (p->original_sequence_no)
|
||||
muxed_smps[i]->sequence = tomux_smps[i]->sequence;
|
||||
muxed_smps[i]->flags |= tomux_smps[i]->flags & SAMPLE_HAS_SEQUENCE;
|
||||
}
|
||||
else {
|
||||
muxed_smps[i]->sequence = p->last_sequence++;
|
||||
muxed_smps[i]->flags |= SAMPLE_HAS_SEQUENCE;
|
||||
|
@ -137,30 +144,33 @@ static void path_source_read(struct path_source *ps, struct path *p, int i)
|
|||
debug(15, "Path %s received = %s", path_name(p), bitset_dump(&p->received));
|
||||
|
||||
#ifdef WITH_HOOKS
|
||||
int toenqueue = hook_process_list(&p->hooks, muxed_smps, tomux);
|
||||
toenqueue = hook_process_list(&p->hooks, muxed_smps, tomux);
|
||||
if (toenqueue != tomux) {
|
||||
int skipped = tomux - toenqueue;
|
||||
|
||||
debug(LOG_NODES | 10, "Hooks skipped %u out of %u samples for path %s", skipped, tomux, path_name(p));
|
||||
}
|
||||
#else
|
||||
int toenqueue = tomux;
|
||||
toenqueue = tomux;
|
||||
#endif
|
||||
|
||||
if (bitset_test(&p->mask, i)) {
|
||||
/* Check if we received an update from all nodes/ */
|
||||
if ((p->mode == PATH_MODE_ANY) ||
|
||||
(p->mode == PATH_MODE_ALL && !bitset_cmp(&p->mask, &p->received)))
|
||||
{
|
||||
(p->mode == PATH_MODE_ALL && !bitset_cmp(&p->mask, &p->received))) {
|
||||
path_destination_enqueue(p, muxed_smps, toenqueue);
|
||||
|
||||
/* Reset bitset of updated nodes */
|
||||
bitset_clear_all(&p->received);
|
||||
|
||||
enqueued = toenqueue;
|
||||
}
|
||||
}
|
||||
|
||||
sample_decref_many(muxed_smps, tomux);
|
||||
out2: sample_decref_many(read_smps, release);
|
||||
|
||||
return enqueued;
|
||||
}
|
||||
|
||||
static int path_destination_init(struct path_destination *pd, int queuelen)
|
||||
|
@ -247,21 +257,22 @@ static void path_destination_write(struct path_destination *pd, struct path *p)
|
|||
|
||||
static void * path_run_single(void *arg)
|
||||
{
|
||||
int ret;
|
||||
struct path *p = arg;
|
||||
struct path_source *ps = (struct path_source *) vlist_at(&p->sources, 0);
|
||||
|
||||
debug(1, "Started path %s in single mode", path_name(p));
|
||||
while (p->state == STATE_STARTED) {
|
||||
pthread_testcancel();
|
||||
|
||||
for (;;) {
|
||||
path_source_read(ps, p, 0);
|
||||
ret = path_source_read(ps, p, 0);
|
||||
if (ret <= 0)
|
||||
continue;
|
||||
|
||||
for (size_t i = 0; i < vlist_length(&p->destinations); i++) {
|
||||
struct path_destination *pd = (struct path_destination *) vlist_at(&p->destinations, i);
|
||||
|
||||
path_destination_write(pd, p);
|
||||
}
|
||||
|
||||
pthread_testcancel();
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
@ -273,9 +284,7 @@ static void * path_run_poll(void *arg)
|
|||
int ret;
|
||||
struct path *p = arg;
|
||||
|
||||
debug(1, "Started path %s in polling mode", path_name(p));
|
||||
|
||||
for (;;) {
|
||||
while (p->state == STATE_STARTED) {
|
||||
ret = poll(p->reader.pfds, p->reader.nfds, -1);
|
||||
if (ret < 0)
|
||||
serror("Failed to poll");
|
||||
|
@ -295,9 +304,8 @@ static void * path_run_poll(void *arg)
|
|||
path_destination_enqueue(p, &p->last_sample, 1);
|
||||
}
|
||||
/* A source is ready to receive samples */
|
||||
else {
|
||||
else
|
||||
path_source_read(ps, p, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -353,28 +361,28 @@ int path_init(struct path *p)
|
|||
|
||||
int path_init_poll(struct path *p)
|
||||
{
|
||||
int ret, nfds;
|
||||
int fds[16], ret, n = 0, m;
|
||||
|
||||
nfds = vlist_length(&p->sources);
|
||||
if (p->rate > 0)
|
||||
nfds++;
|
||||
|
||||
p->reader.nfds = nfds;
|
||||
p->reader.pfds = alloc(sizeof(struct pollfd) * p->reader.nfds);
|
||||
p->reader.pfds = NULL;
|
||||
p->reader.nfds = 0;
|
||||
|
||||
for (int i = 0; i < vlist_length(&p->sources); i++) {
|
||||
struct path_source *ps = (struct path_source *) vlist_at(&p->sources, i);
|
||||
|
||||
int fds[16];
|
||||
int num_fds = node_poll_fds(ps->node, fds);
|
||||
m = node_poll_fds(ps->node, fds);
|
||||
if (m < 0)
|
||||
continue;
|
||||
|
||||
for (int i = 0; i < num_fds; i++) {
|
||||
p->reader.nfds += m;
|
||||
p->reader.pfds = realloc(p->reader.pfds, p->reader.nfds * sizeof(struct pollfd));
|
||||
|
||||
for (int i = 0; i < m; i++) {
|
||||
if (fds[i] < 0)
|
||||
error("Failed to get file descriptor for node %s", node_name(ps->node));
|
||||
|
||||
/* This slot is only used if it is not masked */
|
||||
p->reader.pfds[i].events = POLLIN;
|
||||
p->reader.pfds[i].fd = fds[i];
|
||||
p->reader.pfds[n].events = POLLIN;
|
||||
p->reader.pfds[n++].fd = fds[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -384,9 +392,12 @@ int path_init_poll(struct path *p)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
p->reader.pfds[nfds-1].events = POLLIN;
|
||||
p->reader.pfds[nfds-1].fd = task_fd(&p->timeout);
|
||||
if (p->reader.pfds[nfds-1].fd < 0)
|
||||
p->reader.nfds++;
|
||||
p->reader.pfds = realloc(p->reader.pfds, p->reader.nfds * sizeof(struct pollfd));
|
||||
|
||||
p->reader.pfds[p->reader.nfds-1].events = POLLIN;
|
||||
p->reader.pfds[p->reader.nfds-1].fd = task_fd(&p->timeout);
|
||||
if (p->reader.pfds[p->reader.nfds-1].fd < 0)
|
||||
error("Failed to get file descriptor for timer of path %s", path_name(p));
|
||||
}
|
||||
|
||||
|
@ -453,7 +464,7 @@ int path_init2(struct path *p)
|
|||
/* For data mappings we simple refer to the existing
|
||||
* signal descriptors of the source node. */
|
||||
if (me->type == MAPPING_TYPE_DATA) {
|
||||
sig = (struct signal *) vlist_at_safe(&me->node->signals, me->data.offset + j);
|
||||
sig = (struct signal *) vlist_at_safe(&me->node->in.signals, me->data.offset + j);
|
||||
if (!sig) {
|
||||
warning("Failed to create signal description for path %s", path_name(p));
|
||||
continue;
|
||||
|
@ -521,7 +532,6 @@ int path_parse(struct path *p, json_t *cfg, struct vlist *nodes)
|
|||
"rate", &p->rate,
|
||||
"mask", &json_mask,
|
||||
"original_sequence_no", &p->original_sequence_no
|
||||
|
||||
);
|
||||
if (ret)
|
||||
jerror(&err, "Failed to parse path configuration");
|
||||
|
@ -644,12 +654,12 @@ int path_parse(struct path *p, json_t *cfg, struct vlist *nodes)
|
|||
|
||||
/* Autodetect whether to use poll() for this path or not */
|
||||
if (p->poll == -1) {
|
||||
struct path_source *ps = (struct path_source *) vlist_at(&p->sources, 0);
|
||||
|
||||
int fds[16];
|
||||
int num_fds = node_poll_fds(ps->node, fds);
|
||||
|
||||
p->poll = (p->rate > 0 || vlist_length(&p->sources) > 1) && num_fds > 0;
|
||||
if (p->rate > 0)
|
||||
p->poll = 1;
|
||||
else if (vlist_length(&p->sources) > 1)
|
||||
p->poll = 1;
|
||||
else
|
||||
p->poll = 0;
|
||||
}
|
||||
|
||||
ret = vlist_destroy(&sources, NULL, false);
|
||||
|
@ -673,11 +683,32 @@ int path_check(struct path *p)
|
|||
if (p->rate < 0)
|
||||
error("Setting 'rate' of path %s must be a positive number.", path_name(p));
|
||||
|
||||
if (p->poll) {
|
||||
if (p->rate <= 0) {
|
||||
/* Check that all path sources provide a file descriptor for polling */
|
||||
for (size_t i = 0; i < vlist_length(&p->sources); i++) {
|
||||
struct path_source *ps = (struct path_source *) vlist_at(&p->sources, i);
|
||||
|
||||
if (!ps->node->_vt->poll_fds)
|
||||
error("Node %s can not be used in polling mode with path %s", node_name(ps->node), path_name(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Check that we do not need to multiplex between multiple sources when polling is disabled */
|
||||
if (vlist_length(&p->sources) > 1)
|
||||
error("Setting 'poll' must be active if the path has more than one source");
|
||||
|
||||
/* Check that we do not use the fixed rate feature when polling is disabled */
|
||||
if (p->rate > 0)
|
||||
error("Setting 'poll' must be activated when used together with setting 'rate'");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < vlist_length(&p->sources); i++) {
|
||||
struct path_source *ps = (struct path_source *) vlist_at(&p->sources, i);
|
||||
|
||||
if (!ps->node->_vt->read)
|
||||
error("Source node %s is not supported as a source for path %s", node_name(ps->node), path_name(p));
|
||||
error("Node %s is not supported as a source for path %s", node_name(ps->node), path_name(p));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < vlist_length(&p->destinations); i++) {
|
||||
|
@ -712,7 +743,7 @@ int path_start(struct path *p)
|
|||
|
||||
mask = bitset_dump(&p->mask);
|
||||
|
||||
info("Starting path %s: #signals=%zu, mode=%s, poll=%s, mask=%s, rate=%.2f, enabled=%s, reversed=%s, queuelen=%d, #hooks=%zu, #sources=%zu, #destinations=%zu, #original_sequence_no=%s",
|
||||
info("Starting path %s: #signals=%zu, mode=%s, poll=%s, mask=%s, rate=%.2f, enabled=%s, reversed=%s, queuelen=%d, #hooks=%zu, #sources=%zu, #destinations=%zu, original_sequence_no=%s",
|
||||
path_name(p),
|
||||
vlist_length(&p->signals),
|
||||
mode,
|
||||
|
@ -779,11 +810,15 @@ int path_stop(struct path *p)
|
|||
{
|
||||
int ret;
|
||||
|
||||
if (p->state != STATE_STARTED)
|
||||
if (p->state != STATE_STARTED && p->state != STATE_STOPPING)
|
||||
return 0;
|
||||
|
||||
info("Stopping path: %s", path_name(p));
|
||||
|
||||
if (p->state != STATE_STOPPING)
|
||||
p->state = STATE_STOPPING;
|
||||
|
||||
/* Cancel the thread in case is currently in a blocking syscall */
|
||||
ret = pthread_cancel(p->tid);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -881,49 +916,22 @@ int path_uses_node(struct path *p, struct node *n)
|
|||
return -1;
|
||||
}
|
||||
|
||||
int path_reverse(struct path *p, struct path *r)
|
||||
int path_is_simple(struct path *p)
|
||||
{
|
||||
if (vlist_length(&p->destinations) != 1 || vlist_length(&p->sources) != 1)
|
||||
return -1;
|
||||
int ret;
|
||||
const char *in = NULL, *out = NULL;
|
||||
|
||||
/* General */
|
||||
r->enabled = p->enabled;
|
||||
ret = json_unpack(p->cfg, "{ s: s, s: s }", "in", &in, "out", &out);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Source / Destinations */
|
||||
struct path_destination *orig_pd = vlist_first(&p->destinations);
|
||||
struct path_source *orig_ps = vlist_first(&p->sources);
|
||||
ret = node_is_valid_name(in);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
struct path_destination *new_pd = (struct path_destination *) alloc(sizeof(struct path_destination));
|
||||
struct path_source *new_ps = (struct path_source *) alloc(sizeof(struct path_source));
|
||||
struct mapping_entry *new_me = alloc(sizeof(struct mapping_entry));
|
||||
new_pd->node = orig_ps->node;
|
||||
new_ps->node = orig_pd->node;
|
||||
new_ps->masked = true;
|
||||
ret = node_is_valid_name(out);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
new_me->node = new_ps->node;
|
||||
new_me->type = MAPPING_TYPE_DATA;
|
||||
new_me->data.offset = 0;
|
||||
new_me->length = 0;
|
||||
|
||||
vlist_init(&new_ps->mappings);
|
||||
vlist_push(&new_ps->mappings, new_me);
|
||||
|
||||
vlist_push(&r->destinations, new_pd);
|
||||
vlist_push(&r->sources, new_ps);
|
||||
|
||||
#ifdef WITH_HOOKS
|
||||
for (size_t i = 0; i < vlist_length(&p->hooks); i++) {
|
||||
int ret;
|
||||
|
||||
struct hook *h = (struct hook *) vlist_at(&p->hooks, i);
|
||||
struct hook *g = (struct hook *) alloc(sizeof(struct hook));
|
||||
|
||||
ret = hook_init(g, h->_vt, r, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
vlist_push(&r->hooks, g);
|
||||
}
|
||||
#endif /* WITH_HOOKS */
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -287,7 +287,7 @@ enum signal_type sample_format(const struct sample *s, unsigned idx)
|
|||
|
||||
void sample_dump(struct sample *s)
|
||||
{
|
||||
debug(5, "Sample: sequence=%zu, length=%d, capacity=%d, flags=%#x, signals=%p, #signals=%zu, "
|
||||
debug(5, "Sample: sequence=%" PRIu64 ", length=%d, capacity=%d, flags=%#x, signals=%p, #signals=%zu, "
|
||||
"refcnt=%d, pool_off=%zd",
|
||||
s->sequence, s->length, s->capacity, s->flags, s->signals,
|
||||
s->signals ? vlist_length(s->signals) : 0, atomic_load(&s->refcnt), s->pool_off);
|
||||
|
|
|
@ -76,6 +76,7 @@ retry: fd = shm_open(wname, O_RDWR|O_CREAT|O_EXCL, 0600);
|
|||
ret = shm_unlink(wname);
|
||||
if (ret)
|
||||
return -12;
|
||||
|
||||
goto retry;
|
||||
}
|
||||
|
||||
|
|
15
lib/signal.c
15
lib/signal.c
|
@ -55,20 +55,7 @@ int signal_init_from_mapping(struct signal *s, const struct mapping_entry *me, u
|
|||
|
||||
switch (me->type) {
|
||||
case MAPPING_TYPE_STATS:
|
||||
switch (me->stats.type) {
|
||||
case MAPPING_STATS_TYPE_TOTAL:
|
||||
s->type = SIGNAL_TYPE_INTEGER;
|
||||
break;
|
||||
|
||||
case MAPPING_STATS_TYPE_LAST:
|
||||
case MAPPING_STATS_TYPE_LOWEST:
|
||||
case MAPPING_STATS_TYPE_HIGHEST:
|
||||
case MAPPING_STATS_TYPE_MEAN:
|
||||
case MAPPING_STATS_TYPE_VAR:
|
||||
case MAPPING_STATS_TYPE_STDDEV:
|
||||
s->type = SIGNAL_TYPE_FLOAT;
|
||||
break;
|
||||
}
|
||||
s->type = stats_types[me->stats.type].signal_type;
|
||||
break;
|
||||
|
||||
case MAPPING_TYPE_HEADER:
|
||||
|
|
132
lib/stats.c
132
lib/stats.c
|
@ -31,12 +31,22 @@
|
|||
#include <villas/node.h>
|
||||
#include <villas/table.h>
|
||||
|
||||
struct stats_desc stats_metrics[] = {
|
||||
{ "skipped", "samples", "Skipped samples and the distance between them", 25 },
|
||||
{ "reordered", "samples", "Reordered samples and the distance between them", 25 },
|
||||
{ "gap_sent", "seconds", "Inter-message timestamps (as sent by remote)", 25 },
|
||||
{ "gap_received", "seconds", "Inter-message arrival time (as received by this instance)", 25 },
|
||||
{ "owd", "seconds", "One-way-delay (OWD) of received messages", 25 }
|
||||
struct stats_metric_description stats_metrics[] = {
|
||||
{ "skipped", STATS_METRIC_SKIPPED, "samples", "Skipped samples and the distance between them", 25 },
|
||||
{ "reordered", STATS_METRIC_REORDERED, "samples", "Reordered samples and the distance between them", 25 },
|
||||
{ "gap_sent", STATS_METRIC_GAP_SAMPLE, "seconds", "Inter-message timestamps (as sent by remote)", 25 },
|
||||
{ "gap_received", STATS_METRIC_GAP_RECEIVED, "seconds", "Inter-message arrival time (as received by this instance)", 25 },
|
||||
{ "owd", STATS_METRIC_OWD, "seconds", "One-way-delay (OWD) of received messages", 25 }
|
||||
};
|
||||
|
||||
struct stats_type_description stats_types[] = {
|
||||
{ "last", STATS_TYPE_LAST, SIGNAL_TYPE_FLOAT },
|
||||
{ "highest", STATS_TYPE_HIGHEST, SIGNAL_TYPE_FLOAT },
|
||||
{ "lowest", STATS_TYPE_LOWEST, SIGNAL_TYPE_FLOAT },
|
||||
{ "mean", STATS_TYPE_MEAN, SIGNAL_TYPE_FLOAT },
|
||||
{ "var", STATS_TYPE_VAR, SIGNAL_TYPE_FLOAT },
|
||||
{ "stddev", STATS_TYPE_STDDEV, SIGNAL_TYPE_FLOAT },
|
||||
{ "total", STATS_TYPE_TOTAL, SIGNAL_TYPE_INTEGER }
|
||||
};
|
||||
|
||||
int stats_lookup_format(const char *str)
|
||||
|
@ -51,9 +61,33 @@ int stats_lookup_format(const char *str)
|
|||
return -1;
|
||||
}
|
||||
|
||||
enum stats_metric stats_lookup_metric(const char *str)
|
||||
{
|
||||
for (int i = 0; i < STATS_METRIC_COUNT; i++) {
|
||||
struct stats_metric_description *d = &stats_metrics[i];
|
||||
|
||||
if (!strcmp(str, d->name))
|
||||
return d->metric;
|
||||
}
|
||||
|
||||
return STATS_METRIC_INVALID;
|
||||
}
|
||||
|
||||
enum stats_type stats_lookup_type(const char *str)
|
||||
{
|
||||
for (int i = 0; i < STATS_TYPE_COUNT; i++) {
|
||||
struct stats_type_description *d = &stats_types[i];
|
||||
|
||||
if (!strcmp(str, d->name))
|
||||
return d->type;
|
||||
}
|
||||
|
||||
return STATS_TYPE_INVALID;
|
||||
}
|
||||
|
||||
int stats_init(struct stats *s, int buckets, int warmup)
|
||||
{
|
||||
for (int i = 0; i < STATS_COUNT; i++)
|
||||
for (int i = 0; i < STATS_METRIC_COUNT; i++)
|
||||
hist_init(&s->histograms[i], buckets, warmup);
|
||||
|
||||
s->delta = alloc(sizeof(struct stats_delta));
|
||||
|
@ -63,7 +97,7 @@ int stats_init(struct stats *s, int buckets, int warmup)
|
|||
|
||||
int stats_destroy(struct stats *s)
|
||||
{
|
||||
for (int i = 0; i < STATS_COUNT; i++)
|
||||
for (int i = 0; i < STATS_METRIC_COUNT; i++)
|
||||
hist_destroy(&s->histograms[i]);
|
||||
|
||||
free(s->delta);
|
||||
|
@ -71,7 +105,7 @@ int stats_destroy(struct stats *s)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void stats_update(struct stats *s, enum stats_id id, double val)
|
||||
void stats_update(struct stats *s, enum stats_metric id, double val)
|
||||
{
|
||||
s->delta->values[id] = val;
|
||||
s->delta->update |= 1 << id;
|
||||
|
@ -79,7 +113,7 @@ void stats_update(struct stats *s, enum stats_id id, double val)
|
|||
|
||||
int stats_commit(struct stats *s)
|
||||
{
|
||||
for (int i = 0; i < STATS_COUNT; i++) {
|
||||
for (int i = 0; i < STATS_METRIC_COUNT; i++) {
|
||||
if (s->delta->update & 1 << i) {
|
||||
hist_put(&s->histograms[i], s->delta->values[i]);
|
||||
s->delta->update &= ~(1 << i);
|
||||
|
@ -93,8 +127,8 @@ json_t * stats_json(struct stats *s)
|
|||
{
|
||||
json_t *obj = json_object();
|
||||
|
||||
for (int i = 0; i < STATS_COUNT; i++) {
|
||||
struct stats_desc *d = &stats_metrics[i];
|
||||
for (int i = 0; i < STATS_METRIC_COUNT; i++) {
|
||||
struct stats_metric_description *d = &stats_metrics[i];
|
||||
struct hist *h = &s->histograms[i];
|
||||
|
||||
json_object_set_new(obj, d->name, hist_json(h));
|
||||
|
@ -107,17 +141,17 @@ json_t * stats_json_periodic(struct stats *s, struct node *n)
|
|||
{
|
||||
return json_pack("{ s: s, s: i, s: f, s: f, s: i, s: i }",
|
||||
"node", node_name(n),
|
||||
"processed", hist_total(&s->histograms[STATS_OWD]),
|
||||
"owd", hist_last(&s->histograms[STATS_OWD]),
|
||||
"rate", 1.0 / hist_last(&s->histograms[STATS_GAP_SAMPLE]),
|
||||
"dropped", hist_total(&s->histograms[STATS_REORDERED]),
|
||||
"skipped", hist_total(&s->histograms[STATS_SKIPPED])
|
||||
"processed", hist_total(&s->histograms[STATS_METRIC_OWD]),
|
||||
"owd", hist_last(&s->histograms[STATS_METRIC_OWD]),
|
||||
"rate", 1.0 / hist_last(&s->histograms[STATS_METRIC_GAP_SAMPLE]),
|
||||
"dropped", hist_total(&s->histograms[STATS_METRIC_REORDERED]),
|
||||
"skipped", hist_total(&s->histograms[STATS_METRIC_SKIPPED])
|
||||
);
|
||||
}
|
||||
|
||||
void stats_reset(struct stats *s)
|
||||
{
|
||||
for (int i = 0; i < STATS_COUNT; i++)
|
||||
for (int i = 0; i < STATS_METRIC_COUNT; i++)
|
||||
hist_reset(&s->histograms[i]);
|
||||
}
|
||||
|
||||
|
@ -155,13 +189,13 @@ void stats_print_periodic(struct stats *s, FILE *f, enum stats_format fmt, int v
|
|||
case STATS_FORMAT_HUMAN:
|
||||
table_row(&stats_table,
|
||||
node_name_short(n),
|
||||
hist_total(&s->histograms[STATS_OWD]),
|
||||
hist_last(&s->histograms[STATS_OWD]),
|
||||
hist_mean(&s->histograms[STATS_OWD]),
|
||||
1.0 / hist_last(&s->histograms[STATS_GAP_RECEIVED]),
|
||||
1.0 / hist_mean(&s->histograms[STATS_GAP_RECEIVED]),
|
||||
hist_total(&s->histograms[STATS_REORDERED]),
|
||||
hist_total(&s->histograms[STATS_SKIPPED])
|
||||
hist_total(&s->histograms[STATS_METRIC_OWD]),
|
||||
hist_last(&s->histograms[STATS_METRIC_OWD]),
|
||||
hist_mean(&s->histograms[STATS_METRIC_OWD]),
|
||||
1.0 / hist_last(&s->histograms[STATS_METRIC_GAP_RECEIVED]),
|
||||
1.0 / hist_mean(&s->histograms[STATS_METRIC_GAP_RECEIVED]),
|
||||
hist_total(&s->histograms[STATS_METRIC_REORDERED]),
|
||||
hist_total(&s->histograms[STATS_METRIC_SKIPPED])
|
||||
);
|
||||
break;
|
||||
|
||||
|
@ -179,10 +213,10 @@ void stats_print(struct stats *s, FILE *f, enum stats_format fmt, int verbose)
|
|||
{
|
||||
switch (fmt) {
|
||||
case STATS_FORMAT_HUMAN:
|
||||
for (int i = 0; i < STATS_COUNT; i++) {
|
||||
struct stats_desc *desc = &stats_metrics[i];
|
||||
for (int i = 0; i < STATS_METRIC_COUNT; i++) {
|
||||
struct stats_metric_description *d = &stats_metrics[i];
|
||||
|
||||
info("%s: %s", desc->name, desc->desc);
|
||||
info("%s: %s", d->name, d->desc);
|
||||
hist_print(&s->histograms[i], verbose);
|
||||
}
|
||||
break;
|
||||
|
@ -198,14 +232,44 @@ void stats_print(struct stats *s, FILE *f, enum stats_format fmt, int verbose)
|
|||
}
|
||||
}
|
||||
|
||||
enum stats_id stats_lookup_id(const char *name)
|
||||
union signal_data stats_get_value(const struct stats *s, enum stats_metric sm, enum stats_type st)
|
||||
{
|
||||
for (int i = 0; i < STATS_COUNT; i++) {
|
||||
struct stats_desc *desc = &stats_metrics[i];
|
||||
const struct hist *h = &s->histograms[sm];
|
||||
|
||||
if (!strcmp(desc->name, name))
|
||||
return i;
|
||||
union signal_data d;
|
||||
|
||||
switch (st) {
|
||||
case STATS_TYPE_TOTAL:
|
||||
d.i = h->total;
|
||||
break;
|
||||
|
||||
case STATS_TYPE_LAST:
|
||||
d.f = h->last;
|
||||
break;
|
||||
|
||||
case STATS_TYPE_HIGHEST:
|
||||
d.f = h->highest;
|
||||
break;
|
||||
|
||||
case STATS_TYPE_LOWEST:
|
||||
d.f = h->lowest;
|
||||
break;
|
||||
|
||||
case STATS_TYPE_MEAN:
|
||||
d.f = hist_mean(h);
|
||||
break;
|
||||
|
||||
case STATS_TYPE_STDDEV:
|
||||
d.f = hist_stddev(h);
|
||||
break;
|
||||
|
||||
case STATS_TYPE_VAR:
|
||||
d.f = hist_var(h);
|
||||
break;
|
||||
|
||||
default:
|
||||
d.f = -1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
return d;
|
||||
}
|
||||
|
|
|
@ -49,12 +49,16 @@ using namespace villas::node;
|
|||
|
||||
SuperNode::SuperNode() :
|
||||
state(STATE_INITIALIZED),
|
||||
idleStop(false),
|
||||
priority(0),
|
||||
affinity(0),
|
||||
hugepages(DEFAULT_NR_HUGEPAGES),
|
||||
stats(0),
|
||||
#ifdef WITH_API
|
||||
api(this),
|
||||
#ifdef WITH_WEB
|
||||
web(&api),
|
||||
#endif
|
||||
#endif
|
||||
json(nullptr)
|
||||
{
|
||||
nodes.state = STATE_DESTROYED;
|
||||
|
@ -110,9 +114,6 @@ int SuperNode::parseUri(const std::string &u)
|
|||
config_t cfg;
|
||||
config_setting_t *json_root = nullptr;
|
||||
|
||||
logger->warn("Failed to parse JSON configuration. Re-trying with old libconfig format.");
|
||||
logger->warn(" Please consider migrating to the new format using the 'conf2json' command.");
|
||||
|
||||
config_init(&cfg);
|
||||
config_set_auto_convert(&cfg, 1);
|
||||
|
||||
|
@ -178,7 +179,9 @@ int SuperNode::parseJson(json_t *j)
|
|||
|
||||
json_error_t err;
|
||||
|
||||
ret = json_unpack_ex(j, &err, 0, "{ s?: o, s?: o, s?: o, s?: o, s?: i, s?: i, s?: i, s?: F, s?: s }",
|
||||
idleStop = true;
|
||||
|
||||
ret = json_unpack_ex(j, &err, 0, "{ s?: o, s?: o, s?: o, s?: o, s?: i, s?: i, s?: i, s?: s, s?: b }",
|
||||
"http", &json_web,
|
||||
"logging", &json_logging,
|
||||
"nodes", &json_nodes,
|
||||
|
@ -186,8 +189,8 @@ int SuperNode::parseJson(json_t *j)
|
|||
"hugepages", &hugepages,
|
||||
"affinity", &affinity,
|
||||
"priority", &priority,
|
||||
"stats", &stats,
|
||||
"name", &nme
|
||||
"name", &nme,
|
||||
"idle_stop", &idleStop
|
||||
);
|
||||
if (ret)
|
||||
throw JsonError(err, "Failed to parse global configuration");
|
||||
|
@ -214,6 +217,10 @@ int SuperNode::parseJson(json_t *j)
|
|||
struct node_type *nt;
|
||||
const char *type;
|
||||
|
||||
ret = node_is_valid_name(name);
|
||||
if (ret)
|
||||
throw RuntimeError("Invalid name for node: {}", name);
|
||||
|
||||
ret = json_unpack_ex(json_node, &err, 0, "{ s: s }", "type", &type);
|
||||
if (ret)
|
||||
throw JsonError(err, "Failed to parse node");
|
||||
|
@ -244,7 +251,7 @@ int SuperNode::parseJson(json_t *j)
|
|||
size_t i;
|
||||
json_t *json_path;
|
||||
json_array_foreach(json_paths, i, json_path) {
|
||||
path *p = (path *) alloc(sizeof(path));
|
||||
parse: path *p = (path *) alloc(sizeof(path));
|
||||
|
||||
ret = path_init(p);
|
||||
if (ret)
|
||||
|
@ -257,17 +264,25 @@ int SuperNode::parseJson(json_t *j)
|
|||
vlist_push(&paths, p);
|
||||
|
||||
if (p->reverse) {
|
||||
path *r = (path *) alloc(sizeof(path));
|
||||
|
||||
ret = path_init(r);
|
||||
/* Only simple paths can be reversed */
|
||||
ret = path_is_simple(p);
|
||||
if (ret)
|
||||
throw RuntimeError("Failed to init path");
|
||||
throw RuntimeError("Complex paths can not be reversed!");
|
||||
|
||||
ret = path_reverse(p, r);
|
||||
if (ret)
|
||||
throw RuntimeError("Failed to reverse path {}", path_name(p));
|
||||
/* Parse a second time with in/out reversed */
|
||||
json_path = json_copy(json_path);
|
||||
|
||||
vlist_push(&paths, r);
|
||||
json_t *json_in = json_object_get(json_path, "in");
|
||||
json_t *json_out = json_object_get(json_path, "out");
|
||||
|
||||
if (json_equal(json_in, json_out))
|
||||
throw RuntimeError("Can not reverse path with identical in/out nodes!");
|
||||
|
||||
json_object_set(json_path, "reverse", json_false());
|
||||
json_object_set(json_path, "in", json_out);
|
||||
json_object_set(json_path, "out", json_in);
|
||||
|
||||
goto parse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -379,9 +394,14 @@ void SuperNode::startPaths()
|
|||
|
||||
void SuperNode::start()
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(state == STATE_CHECKED);
|
||||
|
||||
memory_init(hugepages);
|
||||
ret = memory_init(hugepages);
|
||||
if (ret)
|
||||
throw RuntimeError("Failed to initialize memory system");
|
||||
|
||||
kernel::rt::init(priority, affinity);
|
||||
|
||||
#ifdef WITH_API
|
||||
|
@ -397,17 +417,11 @@ void SuperNode::start()
|
|||
startNodes();
|
||||
startPaths();
|
||||
|
||||
#ifdef WITH_HOOKS
|
||||
int ret;
|
||||
ret = task_init(&task, 1.0, CLOCK_REALTIME);
|
||||
if (ret)
|
||||
throw RuntimeError("Failed to create timer");
|
||||
|
||||
if (stats > 0) {
|
||||
stats_print_header(STATS_FORMAT_HUMAN);
|
||||
|
||||
ret = task_init(&task, 1.0 / stats, CLOCK_REALTIME);
|
||||
if (ret)
|
||||
throw RuntimeError("Failed to create stats timer");
|
||||
}
|
||||
#endif /* WITH_HOOKS */
|
||||
stats_print_header(STATS_FORMAT_HUMAN);
|
||||
|
||||
state = STATE_STARTED;
|
||||
}
|
||||
|
@ -472,13 +486,9 @@ void SuperNode::stop()
|
|||
{
|
||||
int ret;
|
||||
|
||||
#ifdef WITH_HOOKS
|
||||
if (stats > 0) {
|
||||
ret = task_destroy(&task);
|
||||
if (ret)
|
||||
throw RuntimeError("Failed to stop stats timer");
|
||||
}
|
||||
#endif /* WITH_HOOKS */
|
||||
ret = task_destroy(&task);
|
||||
if (ret)
|
||||
throw RuntimeError("Failed to stop timer");
|
||||
|
||||
stopPaths();
|
||||
stopNodes();
|
||||
|
@ -497,12 +507,15 @@ void SuperNode::stop()
|
|||
|
||||
void SuperNode::run()
|
||||
{
|
||||
#ifdef WITH_HOOKS
|
||||
task_wait(&task);
|
||||
periodic();
|
||||
#else
|
||||
pause();
|
||||
#endif /* WITH_HOOKS */
|
||||
int ret;
|
||||
|
||||
while (state == STATE_STARTED) {
|
||||
task_wait(&task);
|
||||
|
||||
ret = periodic();
|
||||
if (ret)
|
||||
state = STATE_STOPPING;
|
||||
}
|
||||
}
|
||||
|
||||
SuperNode::~SuperNode()
|
||||
|
@ -519,21 +532,25 @@ SuperNode::~SuperNode()
|
|||
|
||||
int SuperNode::periodic()
|
||||
{
|
||||
#ifdef WITH_HOOKS
|
||||
int ret;
|
||||
|
||||
int started = 0;
|
||||
|
||||
for (size_t i = 0; i < vlist_length(&paths); i++) {
|
||||
auto *p = (struct path *) vlist_at(&paths, i);
|
||||
|
||||
if (p->state != STATE_STARTED)
|
||||
continue;
|
||||
if (p->state == STATE_STARTED) {
|
||||
started++;
|
||||
|
||||
for (size_t j = 0; j < vlist_length(&p->hooks); j++) {
|
||||
hook *h = (struct hook *) vlist_at(&p->hooks, j);
|
||||
#ifdef WITH_HOOKS
|
||||
for (size_t j = 0; j < vlist_length(&p->hooks); j++) {
|
||||
hook *h = (struct hook *) vlist_at(&p->hooks, j);
|
||||
|
||||
ret = hook_periodic(h);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = hook_periodic(h);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
#endif /* WITH_HOOKS */
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -543,6 +560,7 @@ int SuperNode::periodic()
|
|||
if (n->state != STATE_STARTED)
|
||||
continue;
|
||||
|
||||
#ifdef WITH_HOOKS
|
||||
for (size_t j = 0; j < vlist_length(&n->in.hooks); j++) {
|
||||
auto *h = (struct hook *) vlist_at(&n->in.hooks, j);
|
||||
|
||||
|
@ -558,8 +576,15 @@ int SuperNode::periodic()
|
|||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
#endif /* WITH_HOOKS */
|
||||
}
|
||||
#endif
|
||||
|
||||
if (idleStop && state == STATE_STARTED && started == 0) {
|
||||
info("No more active paths. Stopping super-node");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -586,7 +611,7 @@ extern "C" {
|
|||
|
||||
return ssn->getInterfaces();
|
||||
}
|
||||
|
||||
#ifdef WITH_WEB
|
||||
struct web * super_node_get_web(struct super_node *sn)
|
||||
{
|
||||
SuperNode *ssn = reinterpret_cast<SuperNode *>(sn);
|
||||
|
@ -594,7 +619,7 @@ extern "C" {
|
|||
|
||||
return reinterpret_cast<web *>(w);
|
||||
}
|
||||
|
||||
#endif
|
||||
struct lws_context * web_get_context(struct web *w)
|
||||
{
|
||||
Web *ws = reinterpret_cast<Web *>(w);
|
||||
|
@ -616,8 +641,10 @@ extern "C" {
|
|||
return ws->getState();
|
||||
}
|
||||
|
||||
#ifdef WITH_WEB
|
||||
int web_callback_on_writable(struct web *w, struct lws *wsi)
|
||||
{
|
||||
return lws_callback_on_writable(wsi);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ RUN dnf -y install \
|
|||
RUN cd /tmp && \
|
||||
git clone --recursive https://github.com/Snaipe/Criterion && \
|
||||
mkdir -p Criterion/build && cd Criterion/build && \
|
||||
cmake -DCMAKE_INSTALL_LIBDIR=/usr/local/lib64 .. && make install && \
|
||||
cmake -DCMAKE_INSTALL_LIBDIR=/usr/local/lib64 .. && make -j$(nproc) install && \
|
||||
rm -rf /tmp/*
|
||||
|
||||
# Workaround for libnl3's search path for netem distributions
|
||||
|
|
|
@ -64,37 +64,41 @@ RUN pip install \
|
|||
# Dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
libssl-dev \
|
||||
libprotobuf-dev \
|
||||
libprotobuf-c-dev \
|
||||
uuid-dev \
|
||||
libconfig-dev \
|
||||
libnl-3-dev libnl-route-3-dev \
|
||||
libcurl4-openssl-dev \
|
||||
libjansson-dev \
|
||||
libzmq3-dev \
|
||||
libnanomsg-dev \
|
||||
libprotobuf-dev \
|
||||
libprotobuf-c-dev \
|
||||
librabbitmq-dev \
|
||||
libmosquitto-dev \
|
||||
libcomedi-dev
|
||||
libcomedi-dev \
|
||||
libibverbs-dev \
|
||||
librdmacm-dev \
|
||||
libre-dev
|
||||
|
||||
# Build & Install Criterion
|
||||
RUN cd /tmp && \
|
||||
git clone --recursive https://github.com/Snaipe/Criterion && \
|
||||
mkdir -p Criterion/build && cd Criterion/build && \
|
||||
cmake -DCMAKE_INSTALL_LIBDIR=/usr/local/lib64 .. && make install && \
|
||||
cmake -DCMAKE_INSTALL_LIBDIR=/usr/local/lib64 .. && make -j$(nproc) install && \
|
||||
rm -rf /tmp/*
|
||||
|
||||
# Build & Install Criterion
|
||||
# Build & Install libwebsockets
|
||||
RUN cd /tmp && \
|
||||
git clone -b v2.4-stable http://github.com/warmcat/libwebsockets && \
|
||||
git clone -b v3.1-stable https://github.com/warmcat/libwebsockets && \
|
||||
mkdir -p libwebsockets/build && cd libwebsockets/build && \
|
||||
cmake -DCMAKE_INSTALL_LIBDIR=/usr/local/lib64 .. && make install && \
|
||||
cmake -DCMAKE_INSTALL_LIBDIR=/usr/local/lib64 .. && make -j$(nproc) install && \
|
||||
rm -rf /tmp/*
|
||||
|
||||
# Build & Install libiec61850
|
||||
RUN cd /tmp && \
|
||||
git clone -b v1.2 https://github.com/mz-automation/libiec61850 && \
|
||||
git clone -b v1.3.1 https://github.com/mz-automation/libiec61850 && \
|
||||
mkdir -p libiec61850/build && cd libiec61850/build && \
|
||||
cmake -DCMAKE_INSTALL_LIBDIR=/usr/local/lib64 .. && make install && \
|
||||
cmake -DCMAKE_INSTALL_LIBDIR=/usr/local/lib64 .. && make -j$(nproc) install && \
|
||||
rm -rf /tmp/*
|
||||
|
||||
# Expose ports for HTTP and WebSocket frontend
|
||||
|
|
|
@ -55,11 +55,22 @@ using namespace villas;
|
|||
using namespace villas::node;
|
||||
using namespace villas::plugin;
|
||||
|
||||
static std::atomic<bool> stop(false);
|
||||
SuperNode sn;
|
||||
|
||||
static void quit(int signal, siginfo_t *sinfo, void *ctx)
|
||||
{
|
||||
stop = true;
|
||||
Logger logger = logging.get("node");
|
||||
|
||||
switch (signal) {
|
||||
case SIGALRM:
|
||||
logger->info("Reached timeout. Terminating...");
|
||||
break;
|
||||
|
||||
default:
|
||||
logger->info("Received {} signal. Terminating...", strsignal(signal));
|
||||
}
|
||||
|
||||
sn.setState(STATE_STOPPING);
|
||||
}
|
||||
|
||||
static void usage()
|
||||
|
@ -108,7 +119,6 @@ int main(int argc, char *argv[])
|
|||
{
|
||||
int ret;
|
||||
|
||||
SuperNode sn;
|
||||
Logger logger = logging.get("node");
|
||||
|
||||
try {
|
||||
|
@ -175,10 +185,7 @@ int main(int argc, char *argv[])
|
|||
throw RuntimeError("Failed to verify configuration");
|
||||
|
||||
sn.start();
|
||||
|
||||
while (!stop)
|
||||
sn.run();
|
||||
|
||||
sn.run();
|
||||
sn.stop();
|
||||
|
||||
logger->info(CLR_GRN("Goodbye!"));
|
||||
|
@ -186,7 +193,7 @@ int main(int argc, char *argv[])
|
|||
return 0;
|
||||
|
||||
}
|
||||
catch (std::exception *e) {
|
||||
logger->error(e->what());
|
||||
catch (RuntimeError &e) {
|
||||
logger->error("{}", e.what());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,7 +68,8 @@ public:
|
|||
|
||||
|
||||
/* Initialize memory */
|
||||
unsigned pool_size = node_type(node)->pool_size ? node_type(node)->pool_size : LOG2_CEIL(node->out.vectorize);
|
||||
unsigned vec = LOG2_CEIL(MAX(node->out.vectorize, node->in.vectorize));
|
||||
unsigned pool_size = node_type(node)->pool_size ? node_type(node)->pool_size : vec;
|
||||
|
||||
int ret = pool_init(&pool, pool_size, SAMPLE_LENGTH(DEFAULT_SAMPLE_LENGTH), node_memory_type(node, &memory_hugepage));
|
||||
if (ret < 0)
|
||||
|
@ -106,8 +107,15 @@ static void quit(int signal, siginfo_t *sinfo, void *ctx)
|
|||
{
|
||||
Logger logger = logging.get("pipe");
|
||||
|
||||
if (signal == SIGALRM)
|
||||
logger->info("Reached timeout. Terminating...");
|
||||
switch (signal) {
|
||||
case SIGALRM:
|
||||
logger->info("Reached timeout. Terminating...");
|
||||
break;
|
||||
|
||||
default:
|
||||
logger->info("Received {} signal. Terminating...", strsignal(signal));
|
||||
break;
|
||||
}
|
||||
|
||||
stop = true;
|
||||
}
|
||||
|
@ -141,13 +149,14 @@ static void * send_loop(void *ctx)
|
|||
unsigned last_sequenceno = 0, release;
|
||||
int scanned, sent, allocated, cnt = 0;
|
||||
|
||||
struct sample *smps[dirs->send.node->out.vectorize];
|
||||
struct node *node = dirs->send.node;
|
||||
struct sample *smps[node->out.vectorize];
|
||||
|
||||
while (!io_eof(dirs->send.io)) {
|
||||
allocated = sample_alloc_many(&dirs->send.pool, smps, dirs->send.node->out.vectorize);
|
||||
while (node->state == STATE_STARTED && !io_eof(dirs->send.io)) {
|
||||
allocated = sample_alloc_many(&dirs->send.pool, smps, node->out.vectorize);
|
||||
if (allocated < 0)
|
||||
throw RuntimeError("Failed to get {} samples out of send pool.", dirs->send.node->out.vectorize);
|
||||
else if (allocated < dirs->send.node->out.vectorize)
|
||||
throw RuntimeError("Failed to get {} samples out of send pool.", node->out.vectorize);
|
||||
else if (allocated < node->out.vectorize)
|
||||
logger->warn("Send pool underrun");
|
||||
|
||||
scanned = io_scan(dirs->send.io, smps, allocated);
|
||||
|
@ -168,11 +177,7 @@ static void * send_loop(void *ctx)
|
|||
|
||||
release = allocated;
|
||||
|
||||
sent = node_write(dirs->send.node, smps, scanned, &release);
|
||||
if (sent < 0)
|
||||
logger->warn("Failed to sent samples to node {}: reason={}", node_name(dirs->send.node), sent);
|
||||
else if (sent < scanned)
|
||||
logger->warn("Failed to sent {} out of {} samples to node {}", scanned-sent, scanned, node_name(dirs->send.node));
|
||||
sent = node_write(node, smps, scanned, &release);
|
||||
|
||||
sample_decref_many(smps, release);
|
||||
|
||||
|
@ -186,14 +191,14 @@ static void * send_loop(void *ctx)
|
|||
leave: if (io_eof(dirs->send.io)) {
|
||||
if (dirs->recv.limit < 0) {
|
||||
logger->info("Reached end-of-file. Terminating...");
|
||||
killme(SIGTERM);
|
||||
stop = true;
|
||||
}
|
||||
else
|
||||
logger->info("Reached end-of-file. Wait for receive side...");
|
||||
}
|
||||
else {
|
||||
logger->info("Reached send limit. Terminating...");
|
||||
killme(SIGTERM);
|
||||
stop = true;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
@ -206,20 +211,25 @@ static void * recv_loop(void *ctx)
|
|||
|
||||
int recv, cnt = 0, allocated = 0;
|
||||
unsigned release;
|
||||
struct sample *smps[dirs->recv.node->in.vectorize];
|
||||
struct node *node = dirs->recv.node;
|
||||
struct sample *smps[node->in.vectorize];
|
||||
|
||||
for (;;) {
|
||||
allocated = sample_alloc_many(&dirs->recv.pool, smps, dirs->recv.node->in.vectorize);
|
||||
while (node->state == STATE_STARTED) {
|
||||
allocated = sample_alloc_many(&dirs->recv.pool, smps, node->in.vectorize);
|
||||
if (allocated < 0)
|
||||
throw RuntimeError("Failed to allocate {} samples from receive pool.", dirs->recv.node->in.vectorize);
|
||||
else if (allocated < dirs->recv.node->in.vectorize)
|
||||
logger->warn("Receive pool underrun: allocated only {} of {} samples", allocated, dirs->recv.node->in.vectorize);
|
||||
throw RuntimeError("Failed to allocate {} samples from receive pool.", node->in.vectorize);
|
||||
else if (allocated < node->in.vectorize)
|
||||
logger->warn("Receive pool underrun: allocated only {} of {} samples", allocated, node->in.vectorize);
|
||||
|
||||
release = allocated;
|
||||
|
||||
recv = node_read(dirs->recv.node, smps, allocated, &release);
|
||||
if (recv < 0)
|
||||
logger->warn("Failed to receive samples from node {}: reason={}", node_name(dirs->recv.node), recv);
|
||||
recv = node_read(node, smps, allocated, &release);
|
||||
if (recv < 0) {
|
||||
if (node->state == STATE_STOPPING)
|
||||
goto leave2;
|
||||
else
|
||||
logger->warn("Failed to receive samples from node {}: reason={}", node_name(node), recv);
|
||||
}
|
||||
else {
|
||||
io_print(dirs->recv.io, smps, recv);
|
||||
|
||||
|
@ -233,7 +243,7 @@ static void * recv_loop(void *ctx)
|
|||
}
|
||||
|
||||
leave: logger->info("Reached receive limit. Terminating...");
|
||||
killme(SIGTERM);
|
||||
leave2: stop = true;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -412,7 +422,7 @@ check: if (optarg == endptr)
|
|||
alarm(timeout);
|
||||
|
||||
while (!stop)
|
||||
pause();
|
||||
sleep(1);
|
||||
|
||||
if (dirs.recv.enabled) {
|
||||
pthread_cancel(dirs.recv.thread);
|
||||
|
|
|
@ -258,7 +258,7 @@ int protocol_cb(lws *wsi, enum lws_callback_reasons reason, void *user, void *in
|
|||
try {
|
||||
new (c) Connection(wsi);
|
||||
}
|
||||
catch (InvalidUrlException e) {
|
||||
catch (InvalidUrlException &e) {
|
||||
lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, (unsigned char *) "Invalid URL", strlen("Invalid URL"));
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -160,6 +160,17 @@ void usage()
|
|||
|
||||
static void quit(int signal, siginfo_t *sinfo, void *ctx)
|
||||
{
|
||||
Logger logger = logging.get("signal");
|
||||
|
||||
switch (signal) {
|
||||
case SIGALRM:
|
||||
logger->info("Reached timeout. Terminating...");
|
||||
break;
|
||||
|
||||
default:
|
||||
logger->info("Received {} signal. Terminating...", strsignal(signal));
|
||||
}
|
||||
|
||||
stop = true;
|
||||
}
|
||||
|
||||
|
@ -222,7 +233,7 @@ int main(int argc, char *argv[])
|
|||
if (ret)
|
||||
throw RuntimeError("Failed to verify node configuration");
|
||||
|
||||
ret = pool_init(&q, 16, SAMPLE_LENGTH(vlist_length(&n.signals)), &memory_heap);
|
||||
ret = pool_init(&q, 16, SAMPLE_LENGTH(vlist_length(&n.in.signals)), &memory_heap);
|
||||
if (ret)
|
||||
throw RuntimeError("Failed to initialize pool");
|
||||
|
||||
|
@ -234,7 +245,7 @@ int main(int argc, char *argv[])
|
|||
if (ret)
|
||||
throw RuntimeError("Failed to start node {}: reason={}", node_name(&n), ret);
|
||||
|
||||
ret = io_init(&io, ft, &n.signals, IO_FLUSH | (SAMPLE_HAS_ALL & ~SAMPLE_HAS_OFFSET));
|
||||
ret = io_init(&io, ft, &n.in.signals, IO_FLUSH | (SAMPLE_HAS_ALL & ~SAMPLE_HAS_OFFSET));
|
||||
if (ret)
|
||||
throw RuntimeError("Failed to initialize output");
|
||||
|
||||
|
@ -246,16 +257,20 @@ int main(int argc, char *argv[])
|
|||
if (ret)
|
||||
throw RuntimeError("Failed to open output");
|
||||
|
||||
while (!stop) {
|
||||
while (!stop && n.state == STATE_STARTED) {
|
||||
t = sample_alloc(&q);
|
||||
|
||||
unsigned release = 1; // release = allocated
|
||||
|
||||
ret = node_read(&n, &t, 1, &release);
|
||||
if (ret > 0)
|
||||
io_print(&io, &t, 1);
|
||||
retry: ret = node_read(&n, &t, 1, &release);
|
||||
if (ret == 0)
|
||||
goto retry;
|
||||
else if (ret < 0)
|
||||
goto out;
|
||||
|
||||
sample_decref(t);
|
||||
io_print(&io, &t, 1);
|
||||
|
||||
out: sample_decref(t);
|
||||
}
|
||||
|
||||
ret = node_stop(&n);
|
||||
|
|
|
@ -94,7 +94,7 @@ fi
|
|||
# Set paths
|
||||
SCRIPT=$(realpath $0)
|
||||
SCRIPTPATH=$(dirname ${SCRIPT})
|
||||
source ${SCRIPTPATH}/../../tools/integration-tests-helper.sh
|
||||
source ${SCRIPTPATH}/../../tools/villas-helper.sh
|
||||
|
||||
# Declare location of config files
|
||||
CONFIG=$(mktemp /tmp/nodetype-benchmark-config-XXXX.conf)
|
||||
|
|
20
tests/integration/README.md
Normal file
20
tests/integration/README.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Integration Tests
|
||||
|
||||
Run tests:
|
||||
|
||||
```
|
||||
$ BUILDDIR=/VILLASnode/build/ /VILLASnode/tools/integration-tests.sh
|
||||
```
|
||||
|
||||
There are two options for the test script:
|
||||
|
||||
```
|
||||
-v Show full test output
|
||||
-f FILTER Filter test cases
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
$ BUILDDIR=/VILLASnode/build/ /VILLASnode/tools/integration-tests.sh -f pipe-loopback-socket -v
|
||||
```
|
|
@ -43,7 +43,7 @@ fi
|
|||
|
||||
SCRIPT=$(realpath $0)
|
||||
SCRIPTPATH=$(dirname ${SCRIPT})
|
||||
source ${SCRIPTPATH}/../../tools/integration-tests-helper.sh
|
||||
source ${SCRIPTPATH}/../../tools/villas-helper.sh
|
||||
|
||||
CONFIG_FILE=$(mktemp /tmp/ib-configuration-XXXX.conf)
|
||||
CONFIG_FILE_TARGET=$(mktemp /tmp/ib-configuration-target-XXXX.conf)
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
SCRIPT=$(realpath $0)
|
||||
SCRIPTPATH=$(dirname ${SCRIPT})
|
||||
source ${SCRIPTPATH}/../../tools/integration-tests-helper.sh
|
||||
source ${SCRIPTPATH}/../../tools/villas-helper.sh
|
||||
|
||||
CONFIG_FILE=$(mktemp)
|
||||
INPUT_FILE=$(mktemp)
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
SCRIPT=$(realpath $0)
|
||||
SCRIPTPATH=$(dirname ${SCRIPT})
|
||||
source ${SCRIPTPATH}/../../tools/integration-tests-helper.sh
|
||||
source ${SCRIPTPATH}/../../tools/villas-helper.sh
|
||||
|
||||
CONFIG_FILE=$(mktemp)
|
||||
OUTPUT_FILE=$(mktemp)
|
||||
|
|
|
@ -24,42 +24,51 @@
|
|||
|
||||
SCRIPT=$(realpath $0)
|
||||
SCRIPTPATH=$(dirname ${SCRIPT})
|
||||
source ${SCRIPTPATH}/../../tools/integration-tests-helper.sh
|
||||
source ${SCRIPTPATH}/../../tools/villas-helper.sh
|
||||
|
||||
CONFIG_FILE=$(mktemp)
|
||||
STATS_LOG=$(mktemp)
|
||||
|
||||
RATE="33.0"
|
||||
|
||||
cat > ${CONFIG_FILE} <<EOF
|
||||
{
|
||||
"stats": 1.0,
|
||||
"nodes": {
|
||||
"stats_1": {
|
||||
"type": "stats",
|
||||
"node": "signal_1",
|
||||
"rate": 1.0
|
||||
"rate": 10.0,
|
||||
"in": {
|
||||
"signals": [
|
||||
{ "name": "gap", "stats": "signal_1.gap_sent.mean" },
|
||||
{ "name": "total", "stats": "signal_1.owd.total" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"signal_1": {
|
||||
"type": "signal",
|
||||
"limit": 100,
|
||||
"rate": 10.0,
|
||||
"rate": ${RATE},
|
||||
"in" : {
|
||||
"hooks": [
|
||||
{ "type": "stats", "verbose": true }
|
||||
]
|
||||
}
|
||||
},
|
||||
"stats_log_1": {
|
||||
"type": "file",
|
||||
"format": "json",
|
||||
|
||||
"uri": "${STATS_LOG}"
|
||||
}
|
||||
},
|
||||
"paths": [
|
||||
{
|
||||
"in": "signal_1",
|
||||
"hooks" : [
|
||||
{ "type" : "print", "enabled" : false }
|
||||
]
|
||||
"in": "signal_1"
|
||||
},
|
||||
{
|
||||
"in": "stats_1",
|
||||
"hooks" : [
|
||||
{ "type" : "print" }
|
||||
]
|
||||
"out": "stats_log_1"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -67,4 +76,16 @@ EOF
|
|||
|
||||
# Start node
|
||||
VILLAS_LOG_PREFIX=$(colorize "[Node] ") \
|
||||
villas-node ${CONFIG_FILE}
|
||||
villas-node ${CONFIG_FILE} &
|
||||
PID=$!
|
||||
|
||||
sleep 5
|
||||
|
||||
kill ${PID}
|
||||
|
||||
tail -n1 ${STATS_LOG} | jq -e "(.data[0] - 1/${RATE} | length) < 1e-4 and .data[1] == 99" > /dev/null
|
||||
RC=$?
|
||||
|
||||
rm ${STATS_LOG} ${CONFIG_FILE}
|
||||
|
||||
exit ${RC}
|
||||
|
|
|
@ -28,7 +28,7 @@ exit 99
|
|||
|
||||
SCRIPT=$(realpath $0)
|
||||
SCRIPTPATH=$(dirname ${SCRIPT})
|
||||
source ${SCRIPTPATH}/../../tools/integration-tests-helper.sh
|
||||
source ${SCRIPTPATH}/../../tools/villas-helper.sh
|
||||
|
||||
CONFIG_FILE=$(mktemp)
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
SCRIPT=$(realpath $0)
|
||||
SCRIPTPATH=$(dirname ${SCRIPT})
|
||||
source ${SCRIPTPATH}/../../tools/integration-tests-helper.sh
|
||||
source ${SCRIPTPATH}/../../tools/villas-helper.sh
|
||||
|
||||
CONFIG_FILE=$(mktemp)
|
||||
INPUT_FILE=$(mktemp)
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
SCRIPT=$(realpath $0)
|
||||
SCRIPTPATH=$(dirname ${SCRIPT})
|
||||
source ${SCRIPTPATH}/../../tools/integration-tests-helper.sh
|
||||
source ${SCRIPTPATH}/../../tools/villas-helper.sh
|
||||
|
||||
CONFIG_FILE=$(mktemp)
|
||||
INPUT_FILE=$(mktemp)
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
SCRIPT=$(realpath $0)
|
||||
SCRIPTPATH=$(dirname ${SCRIPT})
|
||||
source ${SCRIPTPATH}/../../tools/integration-tests-helper.sh
|
||||
source ${SCRIPTPATH}/../../tools/villas-helper.sh
|
||||
|
||||
CONFIG_FILE=$(mktemp)
|
||||
INPUT_FILE=$(mktemp)
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
SCRIPT=$(realpath $0)
|
||||
SCRIPTPATH=$(dirname ${SCRIPT})
|
||||
source ${SCRIPTPATH}/../../tools/integration-tests-helper.sh
|
||||
source ${SCRIPTPATH}/../../tools/villas-helper.sh
|
||||
|
||||
CONFIG_FILE=$(mktemp)
|
||||
INPUT_FILE=$(mktemp)
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
SCRIPT=$(realpath $0)
|
||||
SCRIPTPATH=$(dirname ${SCRIPT})
|
||||
source ${SCRIPTPATH}/../../tools/integration-tests-helper.sh
|
||||
source ${SCRIPTPATH}/../../tools/villas-helper.sh
|
||||
|
||||
CONFIG_FILE=$(mktemp)
|
||||
INPUT_FILE=$(mktemp)
|
||||
|
|
135
tests/integration/pipe-loopback-rtp-dual.sh
Executable file
135
tests/integration/pipe-loopback-rtp-dual.sh
Executable file
|
@ -0,0 +1,135 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Integration loopback test for villas-pipe.
|
||||
#
|
||||
# @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
# @author Marvin Klimke <marvin.klimke@rwth-aachen.de>
|
||||
# @copyright 2014-2019, 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/>.
|
||||
##################################################################################
|
||||
|
||||
if [ -n "${CI}" ]; then
|
||||
echo "RTP tests are not ready yet"
|
||||
exit 99
|
||||
fi
|
||||
|
||||
SCRIPT=$(realpath $0)
|
||||
SCRIPTPATH=$(dirname ${SCRIPT})
|
||||
source ${SCRIPTPATH}/../../tools/villas-helper.sh
|
||||
|
||||
CONFIG_FILE_SRC=$(mktemp)
|
||||
CONFIG_FILE_DEST=$(mktemp)
|
||||
INPUT_FILE=$(mktemp)
|
||||
OUTPUT_FILE=$(mktemp)
|
||||
|
||||
FORMAT="villas.binary"
|
||||
VECTORIZE="1"
|
||||
|
||||
RATE=100
|
||||
NUM_SAMPLES=100
|
||||
|
||||
cat > ${CONFIG_FILE_SRC} << EOF
|
||||
{
|
||||
"logging" : {
|
||||
"level" : "debug"
|
||||
},
|
||||
"nodes" : {
|
||||
"rtp_node" : {
|
||||
"type" : "rtp",
|
||||
"format" : "${FORMAT}",
|
||||
"vectorize" : ${VECTORIZE},
|
||||
"rate" : ${RATE},
|
||||
"rtcp" : {
|
||||
"enabled" : true,
|
||||
"mode" : "aimd",
|
||||
"throttle_mode" : "decimate"
|
||||
},
|
||||
"aimd" : {
|
||||
"a" : 10,
|
||||
"b" : 0.5
|
||||
},
|
||||
"in" : {
|
||||
"address" : "0.0.0.0:12002",
|
||||
"signals" : {
|
||||
"count" : 5,
|
||||
"type" : "float"
|
||||
}
|
||||
},
|
||||
"out" : {
|
||||
"address" : "127.0.0.1:12000"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
cat > ${CONFIG_FILE_DEST} << EOF
|
||||
{
|
||||
"logging" : {
|
||||
"level" : "debug"
|
||||
},
|
||||
"nodes" : {
|
||||
"rtp_node" : {
|
||||
"type" : "rtp",
|
||||
"format" : "${FORMAT}",
|
||||
"vectorize" : ${VECTORIZE},
|
||||
"rate" : ${RATE},
|
||||
"rtcp": {
|
||||
"enabled" : true,
|
||||
"mode" : "aimd",
|
||||
"throttle_mode" : "decimate"
|
||||
},
|
||||
"aimd" : {
|
||||
"a" : 10,
|
||||
"b" : 0.5
|
||||
},
|
||||
"in" : {
|
||||
"address" : "0.0.0.0:12000",
|
||||
"signals" : {
|
||||
"count" : 5,
|
||||
"type" : "float"
|
||||
}
|
||||
},
|
||||
"out" : {
|
||||
"address" : "127.0.0.1:12002"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
VILLAS_LOG_PREFIX="[DEST] " \
|
||||
villas-pipe -l ${NUM_SAMPLES} ${CONFIG_FILE_DEST} rtp_node > ${OUTPUT_FILE} &
|
||||
PID=$!
|
||||
|
||||
sleep 1
|
||||
|
||||
VILLAS_LOG_PREFIX="[SIGN] " \
|
||||
villas-signal mixed -v 5 -r ${RATE} -l ${NUM_SAMPLES} | tee ${INPUT_FILE} | \
|
||||
VILLAS_LOG_PREFIX="[SRC] " \
|
||||
villas-pipe ${CONFIG_FILE_SRC} rtp_node > ${OUTPUT_FILE}
|
||||
|
||||
# Compare data
|
||||
villas-test-cmp ${CMPFLAGS} ${INPUT_FILE} ${OUTPUT_FILE}
|
||||
RC=$?
|
||||
|
||||
rm ${OUTPUT_FILE} ${INPUT_FILE} ${CONFIG_FILE}
|
||||
|
||||
kill $PID
|
||||
|
||||
exit $RC
|
140
tests/integration/pipe-loopback-rtp-tbf.sh
Executable file
140
tests/integration/pipe-loopback-rtp-tbf.sh
Executable file
|
@ -0,0 +1,140 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Integration loopback test for villas-pipe.
|
||||
#
|
||||
# @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
# @author Marvin Klimke <marvin.klimke@rwth-aachen.de>
|
||||
# @copyright 2014-2019, 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/>.
|
||||
##################################################################################
|
||||
|
||||
if [ -n "${CI}" ]; then
|
||||
echo "RTP tests are not ready yet"
|
||||
exit 99
|
||||
fi
|
||||
|
||||
SCRIPT=$(realpath $0)
|
||||
SCRIPTPATH=$(dirname ${SCRIPT})
|
||||
source ${SCRIPTPATH}/../../tools/villas-helper.sh
|
||||
|
||||
CONFIG_FILE_SRC=$(mktemp)
|
||||
CONFIG_FILE_DEST=$(mktemp)
|
||||
INPUT_FILE=$(mktemp)
|
||||
OUTPUT_FILE=$(mktemp)
|
||||
|
||||
FORMAT="villas.binary"
|
||||
VECTORIZE="1"
|
||||
|
||||
RATE=500
|
||||
NUM_SAMPLES=10000000
|
||||
NUM_VALUES=5
|
||||
|
||||
cat > ${CONFIG_FILE_SRC} << EOF
|
||||
{
|
||||
"logging" : {
|
||||
"level" : "info"
|
||||
},
|
||||
"nodes" : {
|
||||
"rtp_node" : {
|
||||
"type" : "rtp",
|
||||
"format" : "${FORMAT}",
|
||||
"vectorize" : ${VECTORIZE},
|
||||
"rate" : ${RATE},
|
||||
"rtcp" : {
|
||||
"enabled" : true,
|
||||
"mode" : "aimd",
|
||||
"throttle_mode" : "decimate"
|
||||
},
|
||||
"aimd" : {
|
||||
"a" : 10,
|
||||
"b" : 0.75,
|
||||
"start_rate" : ${RATE}
|
||||
},
|
||||
"in" : {
|
||||
"address" : "0.0.0.0:12002",
|
||||
"signals" : {
|
||||
"count" : ${NUM_VALUES},
|
||||
"type" : "float"
|
||||
}
|
||||
},
|
||||
"out" : {
|
||||
"address" : "127.0.0.1:12000",
|
||||
"fwmark" : 123
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
cat > ${CONFIG_FILE_DEST} << EOF
|
||||
{
|
||||
"logging" : {
|
||||
"level" : "info"
|
||||
},
|
||||
"nodes" : {
|
||||
"rtp_node" : {
|
||||
"type" : "rtp",
|
||||
"format" : "${FORMAT}",
|
||||
"vectorize" : ${VECTORIZE},
|
||||
"rate" : ${RATE},
|
||||
"rtcp": {
|
||||
"enabled" : true,
|
||||
"mode" : "aimd",
|
||||
"throttle_mode" : "decimate"
|
||||
},
|
||||
"in" : {
|
||||
"address" : "0.0.0.0:12000",
|
||||
"signals" : {
|
||||
"count" : ${NUM_VALUES},
|
||||
"type" : "float"
|
||||
}
|
||||
},
|
||||
"out" : {
|
||||
"address" : "127.0.0.1:12002"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
tc qdisc del dev lo root
|
||||
tc qdisc add dev lo root handle 4000 prio bands 4 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
|
||||
tc qdisc add dev lo parent 4000:3 tbf rate 40kbps burst 32kbit latency 200ms #peakrate 40kbps mtu 1000 minburst 1520
|
||||
tc filter add dev lo protocol ip handle 123 fw flowid 4000:3
|
||||
|
||||
#exit
|
||||
|
||||
VILLAS_LOG_PREFIX="[DEST] " \
|
||||
villas-pipe -l ${NUM_SAMPLES} ${CONFIG_FILE_DEST} rtp_node > ${OUTPUT_FILE} &
|
||||
PID=$!
|
||||
|
||||
sleep 1
|
||||
|
||||
VILLAS_LOG_PREFIX="[SIGN] " \
|
||||
villas-signal mixed -v ${NUM_VALUES} -r ${RATE} -l ${NUM_SAMPLES} | tee ${INPUT_FILE} | \
|
||||
VILLAS_LOG_PREFIX="[SRC] " \
|
||||
villas-pipe ${CONFIG_FILE_SRC} rtp_node > ${OUTPUT_FILE}
|
||||
|
||||
# Compare data
|
||||
villas-test-cmp ${CMPFLAGS} ${INPUT_FILE} ${OUTPUT_FILE}
|
||||
RC=$?
|
||||
|
||||
rm ${OUTPUT_FILE} ${INPUT_FILE} ${CONFIG_FILE}
|
||||
|
||||
kill $PID
|
||||
exit $RC
|
|
@ -22,24 +22,30 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
##################################################################################
|
||||
|
||||
if [ -n "${CI}" ]; then
|
||||
echo "RTP tests are not ready yet"
|
||||
exit 99
|
||||
fi
|
||||
|
||||
SCRIPT=$(realpath $0)
|
||||
SCRIPTPATH=$(dirname ${SCRIPT})
|
||||
source ${SCRIPTPATH}/../../tools/integration-tests-helper.sh
|
||||
source ${SCRIPTPATH}/../../tools/villas-helper.sh
|
||||
|
||||
CONFIG_FILE=$(mktemp)
|
||||
INPUT_FILE=$(mktemp)
|
||||
OUTPUT_FILE=$(mktemp)
|
||||
|
||||
NUM_SAMPLES=${NUM_SAMPLES:-100}
|
||||
|
||||
# Generate test data
|
||||
villas-signal mixed -v 5 -l ${NUM_SAMPLES} -n > ${INPUT_FILE}
|
||||
|
||||
FORMAT="villas.binary"
|
||||
VECTORIZE="1"
|
||||
|
||||
RATE=1000
|
||||
NUM_SAMPLES=$((10*${RATE}))
|
||||
|
||||
cat > ${CONFIG_FILE} << EOF
|
||||
{
|
||||
"logging" : {
|
||||
"level" : "debug"
|
||||
},
|
||||
"nodes" : {
|
||||
"node1" : {
|
||||
"type" : "rtp",
|
||||
|
@ -47,6 +53,17 @@ cat > ${CONFIG_FILE} << EOF
|
|||
"format" : "${FORMAT}",
|
||||
"vectorize" : ${VECTORIZE},
|
||||
|
||||
"rtcp" : {
|
||||
"enabled" : true,
|
||||
"throttle_mode" : "limit_rate"
|
||||
},
|
||||
|
||||
"aimd" : {
|
||||
"start_rate" : 1,
|
||||
"a" : 10,
|
||||
"b" : 0.5
|
||||
},
|
||||
|
||||
"in" : {
|
||||
"address" : "127.0.0.1:12000",
|
||||
|
||||
|
@ -63,7 +80,8 @@ cat > ${CONFIG_FILE} << EOF
|
|||
}
|
||||
EOF
|
||||
|
||||
villas-pipe -l ${NUM_SAMPLES} ${CONFIG_FILE} node1 > ${OUTPUT_FILE} < ${INPUT_FILE}
|
||||
villas-signal mixed -v 5 -r ${RATE} -l ${NUM_SAMPLES} | tee ${INPUT_FILE} | \
|
||||
villas-pipe -l ${NUM_SAMPLES} ${CONFIG_FILE} node1 > ${OUTPUT_FILE}
|
||||
|
||||
# Compare data
|
||||
villas-test-cmp ${CMPFLAGS} ${INPUT_FILE} ${OUTPUT_FILE}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
SCRIPT=$(realpath $0)
|
||||
SCRIPTPATH=$(dirname ${SCRIPT})
|
||||
source ${SCRIPTPATH}/../../tools/integration-tests-helper.sh
|
||||
source ${SCRIPTPATH}/../../tools/villas-helper.sh
|
||||
|
||||
CONFIG_FILE=$(mktemp)
|
||||
INPUT_FILE=$(mktemp)
|
||||
|
@ -37,9 +37,8 @@ NUM_VALUES=${NUM_VALUES:-4}
|
|||
# Generate test data
|
||||
villas-signal -v ${NUM_VALUES} -l ${NUM_SAMPLES} -n random > ${INPUT_FILE}
|
||||
|
||||
for FORMAT in villas.human villas.binary villas.web csv json gtnet.fake raw.32.le raw.64.be protobuf; do
|
||||
for FORMAT in villas.human gtnet.fake protobuf; do
|
||||
for LAYER in udp ip eth unix; do
|
||||
for VERIFY_SOURCE in true false; do
|
||||
|
||||
VECTORIZES="1"
|
||||
|
||||
|
@ -88,7 +87,6 @@ cat > ${CONFIG_FILE} << EOF
|
|||
},
|
||||
"in" : {
|
||||
"address" : "${LOCAL}",
|
||||
"verify_source" : ${VERIFY_SOURCE},
|
||||
"signals" : {
|
||||
"count" : ${NUM_VALUES},
|
||||
"type" : "float"
|
||||
|
@ -111,7 +109,7 @@ villas-test-cmp ${CMPFLAGS} ${INPUT_FILE} ${OUTPUT_FILE}
|
|||
RC=$?
|
||||
|
||||
if (( ${RC} != 0 )); then
|
||||
echo "=========== Sub-test failed for: format=${FORMAT}, layer=${LAYER}, verify_source=${VERIFY_SOURCE}, vectorize=${VECTORIZE}"
|
||||
echo "=========== Sub-test failed for: format=${FORMAT}, layer=${LAYER}, vectorize=${VECTORIZE}"
|
||||
echo "Config:"
|
||||
cat ${CONFIG_FILE}
|
||||
echo
|
||||
|
@ -122,10 +120,10 @@ if (( ${RC} != 0 )); then
|
|||
cat ${OUTPUT_FILE}
|
||||
exit ${RC}
|
||||
else
|
||||
echo "=========== Sub-test succeeded for: format=${FORMAT}, layer=${LAYER}, verify_source=${VERIFY_SOURCE}, vectorize=${VECTORIZE}"
|
||||
echo "=========== Sub-test succeeded for: format=${FORMAT}, layer=${LAYER}, vectorize=${VECTORIZE}"
|
||||
fi
|
||||
|
||||
done; done; done; done
|
||||
done; done; done
|
||||
|
||||
rm ${OUTPUT_FILE} ${INPUT_FILE} ${CONFIG_FILE} ${THEORIES}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
SCRIPT=$(realpath $0)
|
||||
SCRIPTPATH=$(dirname ${SCRIPT})
|
||||
source ${SCRIPTPATH}/../../tools/integration-tests-helper.sh
|
||||
source ${SCRIPTPATH}/../../tools/villas-helper.sh
|
||||
|
||||
CONFIG_FILE=$(mktemp)
|
||||
CONFIG_FILE2=$(mktemp)
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
SCRIPT=$(realpath $0)
|
||||
SCRIPTPATH=$(dirname ${SCRIPT})
|
||||
source ${SCRIPTPATH}/../../tools/integration-tests-helper.sh
|
||||
source ${SCRIPTPATH}/../../tools/villas-helper.sh
|
||||
|
||||
CONFIG_FILE=$(mktemp)
|
||||
INPUT_FILE=$(mktemp)
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
SCRIPT=$(realpath $0)
|
||||
SCRIPTPATH=$(dirname ${SCRIPT})
|
||||
SRCDIR=$(realpath ${SCRIPTPATH}/../..)
|
||||
source ${SRCDIR}/tools/integration-tests-helper.sh
|
||||
source ${SRCDIR}/tools/villas-helper.sh
|
||||
|
||||
CONFIG_FILE=$(mktemp)
|
||||
INPUT_FILE=$(mktemp)
|
||||
|
|
|
@ -47,9 +47,9 @@ Test(mapping, parse_nodes)
|
|||
struct node *n = new struct node;
|
||||
|
||||
n->name = strdup(node_names[i]);
|
||||
n->signals.state = STATE_DESTROYED;
|
||||
n->in.signals.state = STATE_DESTROYED;
|
||||
|
||||
vlist_init(&n->signals);
|
||||
vlist_init(&n->in.signals);
|
||||
|
||||
for (unsigned j = 0; j < ARRAY_LEN(signal_names[i]); j++) {
|
||||
struct signal *sig;
|
||||
|
@ -57,7 +57,7 @@ Test(mapping, parse_nodes)
|
|||
sig = signal_create(signal_names[i][j], nullptr, SIGNAL_TYPE_AUTO);
|
||||
cr_assert_not_null(sig);
|
||||
|
||||
vlist_push(&n->signals, sig);
|
||||
vlist_push(&n->in.signals, sig);
|
||||
}
|
||||
|
||||
vlist_push(&nodes, n);
|
||||
|
@ -73,8 +73,8 @@ Test(mapping, parse_nodes)
|
|||
cr_assert_eq(ret, 0);
|
||||
cr_assert_eq(m.node, vlist_lookup(&nodes, "cherry"));
|
||||
cr_assert_eq(m.type, MAPPING_TYPE_STATS);
|
||||
cr_assert_eq(m.stats.id, STATS_OWD);
|
||||
cr_assert_eq(m.stats.type, MAPPING_STATS_TYPE_MEAN);
|
||||
cr_assert_eq(m.stats.metric, STATS_METRIC_OWD);
|
||||
cr_assert_eq(m.stats.type, STATS_TYPE_MEAN);
|
||||
|
||||
ret = mapping_parse_str(&m, "carrot.data[1-2]", &nodes);
|
||||
cr_assert_eq(ret, 0);
|
||||
|
@ -88,7 +88,7 @@ Test(mapping, parse_nodes)
|
|||
cr_assert_eq(m.node, vlist_lookup(&nodes, "carrot"));
|
||||
cr_assert_eq(m.type, MAPPING_TYPE_DATA);
|
||||
cr_assert_eq(m.data.offset, 0);
|
||||
cr_assert_eq(m.length, vlist_length(&m.node->signals));
|
||||
cr_assert_eq(m.length, vlist_length(&m.node->in.signals));
|
||||
|
||||
ret = mapping_parse_str(&m, "carrot.data[sole]", &nodes);
|
||||
cr_assert_eq(ret, 0);
|
||||
|
@ -126,8 +126,8 @@ Test(mapping, parse)
|
|||
ret = mapping_parse_str(&m, "stats.owd.mean", nullptr);
|
||||
cr_assert_eq(ret, 0);
|
||||
cr_assert_eq(m.type, MAPPING_TYPE_STATS);
|
||||
cr_assert_eq(m.stats.id, STATS_OWD);
|
||||
cr_assert_eq(m.stats.type, MAPPING_STATS_TYPE_MEAN);
|
||||
cr_assert_eq(m.stats.metric, STATS_METRIC_OWD);
|
||||
cr_assert_eq(m.stats.type, STATS_TYPE_MEAN);
|
||||
|
||||
ret = mapping_parse_str(&m, "data[1-2]", nullptr);
|
||||
cr_assert_eq(ret, 0);
|
||||
|
@ -171,10 +171,10 @@ Test(mapping, parse)
|
|||
cr_assert_neq(ret, 0);
|
||||
|
||||
/* Check for superfluous chars at the end */
|
||||
ret = mapping_parse_str(&m, "stats.ts.origin.bla", nullptr);
|
||||
ret = mapping_parse_str(&m, "hdr.ts.origin.bla", nullptr);
|
||||
cr_assert_neq(ret, 0);
|
||||
|
||||
ret = mapping_parse_str(&m, "stats.ts.origin.", nullptr);
|
||||
ret = mapping_parse_str(&m, "hdr.ts.origin.", nullptr);
|
||||
cr_assert_neq(ret, 0);
|
||||
|
||||
ret = mapping_parse_str(&m, "data[1-2]bla", nullptr);
|
||||
|
|
27
tools/plots/plot_aimd.py
Normal file
27
tools/plots/plot_aimd.py
Normal file
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import matplotlib as mpl
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
def read_datafile(file_name):
|
||||
# the skiprows keyword is for heading, but I don't know if trailing lines
|
||||
# can be specified
|
||||
data = np.loadtxt(file_name, delimiter='\t', skiprows=1)
|
||||
return data
|
||||
|
||||
filename = sys.argv[1]
|
||||
|
||||
data = read_datafile(filename)
|
||||
|
||||
print(data[:,0])
|
||||
|
||||
fig, ax1 = plt.subplots()
|
||||
ax1.plot(data[:,0], data[:,1])
|
||||
|
||||
ax2 = ax1.twinx()
|
||||
ax2.plot(data[:,0], data[:,2], c='red')
|
||||
|
||||
fig.tight_layout()
|
||||
plt.show()
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue