Reorganize repository
* Move the project top-level CMakeLists.txt to the root of the project; this allows building Cyclone as part of ROS2 without any special tricks; * Clean up the build options: ENABLE_SSL: whether to check for and include OpenSSL support if a library can be found (default = ON); this used to be called DDSC_ENABLE_OPENSSL, the old name is deprecated but still works BUILD_DOCS: whether to build docs (default = OFF) BUILD_TESTING: whether to build test (default = OFF) * Collect all documentation into top-level "docs" directory; * Move the examples to the top-level directory; * Remove the unused and somewhat misleading pseudo-default cyclonedds.xml; * Remove unused cmake files Signed-off-by: Erik Boasson <eb@ilities.com>
This commit is contained in:
parent
4e80559763
commit
9cf4b97f1a
102 changed files with 627 additions and 1925 deletions
105
examples/CMakeLists.txt
Normal file
105
examples/CMakeLists.txt
Normal file
|
@ -0,0 +1,105 @@
|
|||
#
|
||||
# Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
|
||||
#
|
||||
# This program and the accompanying materials are made available under the
|
||||
# terms of the Eclipse Public License v. 2.0 which is available at
|
||||
# http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
# v. 1.0 which is available at
|
||||
# http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
#
|
||||
# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
#
|
||||
set(CMAKE_INSTALL_EXAMPLESDIR "${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME}/examples")
|
||||
|
||||
add_subdirectory(helloworld)
|
||||
add_subdirectory(roundtrip)
|
||||
add_subdirectory(throughput)
|
||||
|
||||
if (BUILD_DOCS)
|
||||
message(STATUS "${CMAKE_PROJECT_NAME} Examples documentation: Success (build)")
|
||||
set(EXAMPLES_HTML_ARCHIVE "${CMAKE_PROJECT_NAME}ExamplesHTML.tar.gz")
|
||||
# Process sphinx configuration file
|
||||
set(sph_conf_author "Eclipse Cyclone DDS project")
|
||||
string(TIMESTAMP sph_conf_copyright "%Y")
|
||||
set(sph_conf_version "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
|
||||
set(sph_conf_release "${PROJECT_VERSION}")
|
||||
configure_file(sphinx-conf.py.in conf.py @ONLY)
|
||||
|
||||
set(builder_output "examples_html_output")
|
||||
set(builder_log "sphinx-examples-html.log")
|
||||
add_custom_command(OUTPUT ${builder_output}
|
||||
COMMAND ${SPHINX_EXECUTABLE}
|
||||
-b html
|
||||
-d ${CMAKE_CURRENT_BINARY_DIR}/cache
|
||||
-c ${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR} > ${builder_log}
|
||||
COMMENT "Running Sphinx for examples html output"
|
||||
VERBATIM)
|
||||
|
||||
# OUTPUT is a fake target / symbolic name, not an actual file
|
||||
set_property(SOURCE ${builder_output} PROPERTY SYMBOLIC 1)
|
||||
add_custom_target(examples_docs ALL DEPENDS ${builder_output})
|
||||
|
||||
# Archive the output html files, will become a jenkins artifact for use in other jobs
|
||||
# TODO this hardcoded list and archiving should be replaced by ExternalData refs
|
||||
set(html_outputs
|
||||
"_static"
|
||||
"search.html"
|
||||
"searchindex.js"
|
||||
"genindex.html"
|
||||
"examples.html"
|
||||
"helloworld/readme.html"
|
||||
"roundtrip/readme.html"
|
||||
"throughput/readme.html")
|
||||
|
||||
add_custom_command(OUTPUT ${builder_output}
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-E tar "zcf"
|
||||
"${EXAMPLES_HTML_ARCHIVE}"
|
||||
${html_outputs}
|
||||
APPEND
|
||||
VERBATIM)
|
||||
|
||||
# Remove generated files when cleaning the build tree
|
||||
set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES
|
||||
"cache"
|
||||
"${builder_log}"
|
||||
"objects.inv"
|
||||
${html_outputs}
|
||||
"_sources"
|
||||
"${EXAMPLES_HTML_ARCHIVE}")
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
set(platform_exclude "examples/helloworld/Makefile")
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
set(platform_exclude "examples/helloworld/vs|examples/helloworld/HelloWorld\.sln")
|
||||
else()
|
||||
set(platform_exclude "this_is_a_placeholder")
|
||||
endif()
|
||||
|
||||
# Install example source-files
|
||||
install(
|
||||
DIRECTORY "${PROJECT_SOURCE_DIR}/examples/"
|
||||
DESTINATION "${CMAKE_INSTALL_EXAMPLESDIR}"
|
||||
COMPONENT dev
|
||||
PATTERN "CMakeLists.export" EXCLUDE
|
||||
PATTERN "examples/CMakeLists.txt" EXCLUDE
|
||||
REGEX ${platform_exclude} EXCLUDE
|
||||
REGEX "\.rst|\.py" EXCLUDE)
|
||||
|
||||
# Install example html docs files
|
||||
# TODO this should be replaced by install commands that use ExternalData refs (preferably in examples' CMakeLists.txt)
|
||||
install(
|
||||
DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
|
||||
DESTINATION "${CMAKE_INSTALL_EXAMPLESDIR}"
|
||||
COMPONENT dev
|
||||
FILES_MATCHING
|
||||
PATTERN "CMakeFiles" EXCLUDE
|
||||
PATTERN "cache" EXCLUDE
|
||||
PATTERN "_sources" EXCLUDE
|
||||
PATTERN "*.html"
|
||||
PATTERN "*.js"
|
||||
PATTERN "_static/*")
|
||||
|
48
examples/examples.rst
Normal file
48
examples/examples.rst
Normal file
|
@ -0,0 +1,48 @@
|
|||
..
|
||||
Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
|
||||
|
||||
This program and the accompanying materials are made available under the
|
||||
terms of the Eclipse Public License v. 2.0 which is available at
|
||||
http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
v. 1.0 which is available at
|
||||
http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
|
||||
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Contents:
|
||||
|
||||
helloworld/readme
|
||||
roundtrip/readme
|
||||
throughput/readme
|
||||
|
||||
Configuration
|
||||
*************
|
||||
|
||||
Eclipse Cyclone DDS has various configuration parameters and comes with a default built-in
|
||||
configuration. To run an example, or any application that uses Eclipse Cyclone DDS for its data
|
||||
exchange, this default configuration is usually fine and no further action is required.
|
||||
|
||||
Configuration parameters for Eclipse CycloneDDS are expressed in XML and grouped together in a
|
||||
single XML file. To use a custom XML configuration in an application, the ``CYCLONEDDS_URI``
|
||||
environment variable needs to be set prior to starting the application and pointed to the location
|
||||
of the configuration file to be used.
|
||||
|
||||
| *Example*
|
||||
| **Windows:** ``set CYCLONEDDS_URI=file://%USERPROFILE%/CycloneDDS/my-config.xml``
|
||||
| **Linux:** ``export CYCLONEDDS_URI="file://$HOME/CycloneDDS/my-config.xml"``
|
||||
|
||||
The Eclipse CycloneDDS installation comes with a configuration file that corresponds to the default
|
||||
behaviour. You can modify it or add your using any text or XML editor, or using by using the
|
||||
Eclipse CycloneDDS Configurator tool, which provides context-sensitive help on available
|
||||
configuration parameters and their applicability.
|
||||
|
||||
One very important part of the configuration settings are the "tracing" settings: these allow
|
||||
letting Eclipse Cyclone DDS trace very detailed information to a file, and this includes the actual
|
||||
configuration settings in use, including all those that are set at the default. When editing
|
||||
configuration files by hand, this overview can be very useful. Increasing the Verbosity from
|
||||
"warning" to, e.g., "config" already suffices for getting this information written to the log.
|
22
examples/helloworld/CMakeLists.export
Normal file
22
examples/helloworld/CMakeLists.export
Normal file
|
@ -0,0 +1,22 @@
|
|||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
if (NOT TARGET CycloneDDS::ddsc)
|
||||
# Find the CycloneDDS package. If it is not in a default location, try
|
||||
# finding it relative to the example where it most likely resides.
|
||||
find_package(CycloneDDS REQUIRED PATHS "${CMAKE_SOURCE_DIR}/../../")
|
||||
endif()
|
||||
|
||||
# This is a convenience function, provided by the CycloneDDS package,
|
||||
# that will supply a library target related the the given idl file.
|
||||
# In short, it takes the idl file, generates the source files with
|
||||
# the proper data types and compiles them into a library.
|
||||
idlc_generate(HelloWorldData_lib "HelloWorldData.idl")
|
||||
|
||||
# Both executables have only one related source file.
|
||||
add_executable(HelloworldPublisher publisher.c)
|
||||
add_executable(HelloworldSubscriber subscriber.c)
|
||||
|
||||
# Both executables need to be linked to the idl data type library and
|
||||
# the ddsc API library.
|
||||
target_link_libraries(HelloworldPublisher HelloWorldData_lib CycloneDDS::ddsc)
|
||||
target_link_libraries(HelloworldSubscriber HelloWorldData_lib CycloneDDS::ddsc)
|
58
examples/helloworld/CMakeLists.txt
Normal file
58
examples/helloworld/CMakeLists.txt
Normal file
|
@ -0,0 +1,58 @@
|
|||
#
|
||||
# Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
|
||||
#
|
||||
# This program and the accompanying materials are made available under the
|
||||
# terms of the Eclipse Public License v. 2.0 which is available at
|
||||
# http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
# v. 1.0 which is available at
|
||||
# http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
#
|
||||
# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
#
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
if (NOT TARGET CycloneDDS::ddsc)
|
||||
# Find the CycloneDDS package. If it is not in a default location, try
|
||||
# finding it relative to the example where it most likely resides.
|
||||
find_package(CycloneDDS REQUIRED PATHS ../../)
|
||||
endif()
|
||||
|
||||
# This is a convenience function, provided by the CycloneDDS package,
|
||||
# that will supply a library target related the the given idl file.
|
||||
# In short, it takes the idl file, generates the source files with
|
||||
# the proper data types and compiles them into a library.
|
||||
idlc_generate(HelloWorldData_lib "HelloWorldData.idl")
|
||||
|
||||
# Both executables have only one related source file.
|
||||
add_executable(HelloworldPublisher publisher.c)
|
||||
add_executable(HelloworldSubscriber subscriber.c)
|
||||
|
||||
# Both executables need to be linked to the idl data type library and
|
||||
# the ddsc API library.
|
||||
target_link_libraries(HelloworldPublisher HelloWorldData_lib CycloneDDS::ddsc)
|
||||
target_link_libraries(HelloworldSubscriber HelloWorldData_lib CycloneDDS::ddsc)
|
||||
|
||||
#
|
||||
# The helloworld example is the only example to have a different CMakeLists.txt
|
||||
# for building in source and install environments. This is because it has to
|
||||
# do some specific installations to support the Getting Started Guide.
|
||||
# The CMakelists.export will be used for the install environments.
|
||||
#
|
||||
|
||||
install(
|
||||
TARGETS HelloworldSubscriber HelloworldPublisher
|
||||
DESTINATION "${CMAKE_INSTALL_EXAMPLESDIR}/helloworld/bin"
|
||||
COMPONENT dev)
|
||||
|
||||
install(
|
||||
FILES "CMakeLists.export"
|
||||
DESTINATION "${CMAKE_INSTALL_EXAMPLESDIR}/helloworld"
|
||||
COMPONENT dev
|
||||
RENAME "CMakeLists.txt")
|
||||
|
||||
get_target_property(GENERATED_FILES HelloWorldData_lib INTERFACE_SOURCES)
|
||||
install(
|
||||
FILES ${GENERATED_FILES}
|
||||
DESTINATION "${CMAKE_INSTALL_EXAMPLESDIR}/helloworld/generated"
|
||||
COMPONENT dev)
|
||||
|
54
examples/helloworld/HelloWorld.sln
Normal file
54
examples/helloworld/HelloWorld.sln
Normal file
|
@ -0,0 +1,54 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25123.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HelloWorldSubscriber", "vs\HelloWorldSubscriber.vcxproj", "{D8B2F285-EB03-4657-A0DC-603172553BEA}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{28A7A97B-3CEF-4FD7-8DC5-A7586D6DAE1D} = {28A7A97B-3CEF-4FD7-8DC5-A7586D6DAE1D}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HelloWorldPublisher", "vs\HelloWorldPublisher.vcxproj", "{D8B2F285-EB03-4657-ACDC-603172553BEA}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{28A7A97B-3CEF-4FD7-8DC5-A7586D6DAE1D} = {28A7A97B-3CEF-4FD7-8DC5-A7586D6DAE1D}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HelloWorldType", "vs\HelloWorldType.vcxproj", "{28A7A97B-3CEF-4FD7-8DC5-A7586D6DAE1D}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{D8B2F285-EB03-4657-A0DC-603172553BEA}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{D8B2F285-EB03-4657-A0DC-603172553BEA}.Debug|x64.Build.0 = Debug|x64
|
||||
{D8B2F285-EB03-4657-A0DC-603172553BEA}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{D8B2F285-EB03-4657-A0DC-603172553BEA}.Debug|x86.Build.0 = Debug|Win32
|
||||
{D8B2F285-EB03-4657-A0DC-603172553BEA}.Release|x64.ActiveCfg = Release|x64
|
||||
{D8B2F285-EB03-4657-A0DC-603172553BEA}.Release|x64.Build.0 = Release|x64
|
||||
{D8B2F285-EB03-4657-A0DC-603172553BEA}.Release|x86.ActiveCfg = Release|Win32
|
||||
{D8B2F285-EB03-4657-A0DC-603172553BEA}.Release|x86.Build.0 = Release|Win32
|
||||
{D8B2F285-EB03-4657-ACDC-603172553BEA}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{D8B2F285-EB03-4657-ACDC-603172553BEA}.Debug|x64.Build.0 = Debug|x64
|
||||
{D8B2F285-EB03-4657-ACDC-603172553BEA}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{D8B2F285-EB03-4657-ACDC-603172553BEA}.Debug|x86.Build.0 = Debug|Win32
|
||||
{D8B2F285-EB03-4657-ACDC-603172553BEA}.Release|x64.ActiveCfg = Release|x64
|
||||
{D8B2F285-EB03-4657-ACDC-603172553BEA}.Release|x64.Build.0 = Release|x64
|
||||
{D8B2F285-EB03-4657-ACDC-603172553BEA}.Release|x86.ActiveCfg = Release|Win32
|
||||
{D8B2F285-EB03-4657-ACDC-603172553BEA}.Release|x86.Build.0 = Release|Win32
|
||||
{28A7A97B-3CEF-4FD7-8DC5-A7586D6DAE1D}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{28A7A97B-3CEF-4FD7-8DC5-A7586D6DAE1D}.Debug|x64.Build.0 = Debug|x64
|
||||
{28A7A97B-3CEF-4FD7-8DC5-A7586D6DAE1D}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{28A7A97B-3CEF-4FD7-8DC5-A7586D6DAE1D}.Debug|x86.Build.0 = Debug|Win32
|
||||
{28A7A97B-3CEF-4FD7-8DC5-A7586D6DAE1D}.Release|x64.ActiveCfg = Release|x64
|
||||
{28A7A97B-3CEF-4FD7-8DC5-A7586D6DAE1D}.Release|x64.Build.0 = Release|x64
|
||||
{28A7A97B-3CEF-4FD7-8DC5-A7586D6DAE1D}.Release|x86.ActiveCfg = Release|Win32
|
||||
{28A7A97B-3CEF-4FD7-8DC5-A7586D6DAE1D}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
9
examples/helloworld/HelloWorldData.idl
Normal file
9
examples/helloworld/HelloWorldData.idl
Normal file
|
@ -0,0 +1,9 @@
|
|||
module HelloWorldData
|
||||
{
|
||||
struct Msg
|
||||
{
|
||||
long userID;
|
||||
string message;
|
||||
};
|
||||
#pragma keylist Msg userID
|
||||
};
|
54
examples/helloworld/Makefile
Normal file
54
examples/helloworld/Makefile
Normal file
|
@ -0,0 +1,54 @@
|
|||
publisher_NAME := HelloworldPublisher
|
||||
publisher_SRCS := publisher.c generated/HelloWorldData.c
|
||||
publisher_OBJS := ${publisher_SRCS:.c=.o}
|
||||
|
||||
subscriber_NAME := HelloworldSubscriber
|
||||
subscriber_SRCS := subscriber.c generated/HelloWorldData.c
|
||||
subscriber_OBJS := ${subscriber_SRCS:.c=.o}
|
||||
|
||||
# Make sure that the location where dds.h is installed is known to the
|
||||
# compiler. If dds.h is not installed in a system default spot, then
|
||||
# it can be introduced by adding it to the CFLAGS, like:
|
||||
#CFLAGS += -I../../../../include
|
||||
|
||||
# Make sure that the location where libddsc.so is installed is known
|
||||
# to the compiler. If the lib is not installed in a system default
|
||||
# location, then it can be introduced by adding it to the LDFLAGS:
|
||||
#LDFLAGS += -L../../../../lib
|
||||
|
||||
CFLAGS += -Igenerated
|
||||
LDFLAGS += -lddsc
|
||||
|
||||
.PHONY: all clean distclean
|
||||
|
||||
all: $(publisher_NAME) $(subscriber_NAME)
|
||||
|
||||
$(publisher_NAME): $(publisher_OBJS)
|
||||
gcc $(CFLAGS) $(publisher_OBJS) $(LDFLAGS) -o $(publisher_NAME)
|
||||
|
||||
$(subscriber_NAME): $(subscriber_OBJS)
|
||||
gcc $(CFLAGS) $(subscriber_OBJS) $(LDFLAGS) -o $(subscriber_NAME)
|
||||
|
||||
clean:
|
||||
@- $(RM) $(publisher_NAME)
|
||||
@- $(RM) $(publisher_OBJS)
|
||||
@- $(RM) $(subscriber_NAME)
|
||||
@- $(RM) $(subscriber_OBJS)
|
||||
|
||||
distclean: clean
|
||||
|
||||
|
||||
|
||||
# Make sure that the idlc jar file is available and java can find its
|
||||
# location. In this example, it is assumed that the jar is located at
|
||||
# a specific relative directory. Change the classpath variable if
|
||||
# this is not the case on your system.
|
||||
classpath:= ../../idlc/idlc-jar-with-dependencies.jar
|
||||
ifneq ($(CLASSPATH),)
|
||||
classpath:= $(CLASSPATH):$(classpath)
|
||||
endif
|
||||
export CLASSPATH:=$(classpath)
|
||||
|
||||
datatype:
|
||||
java org.eclipse.cyclonedds.compilers.Idlc HelloWorldData.idl
|
||||
|
68
examples/helloworld/publisher.c
Normal file
68
examples/helloworld/publisher.c
Normal file
|
@ -0,0 +1,68 @@
|
|||
#include "dds/dds.h"
|
||||
#include "HelloWorldData.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main (int argc, char ** argv)
|
||||
{
|
||||
dds_entity_t participant;
|
||||
dds_entity_t topic;
|
||||
dds_entity_t writer;
|
||||
dds_return_t rc;
|
||||
HelloWorldData_Msg msg;
|
||||
uint32_t status = 0;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
/* Create a Participant. */
|
||||
participant = dds_create_participant (DDS_DOMAIN_DEFAULT, NULL, NULL);
|
||||
if (participant < 0)
|
||||
DDS_FATAL("dds_create_participant: %s\n", dds_strretcode(-participant));
|
||||
|
||||
/* Create a Topic. */
|
||||
topic = dds_create_topic (
|
||||
participant, &HelloWorldData_Msg_desc, "HelloWorldData_Msg", NULL, NULL);
|
||||
if (topic < 0)
|
||||
DDS_FATAL("dds_create_topic: %s\n", dds_strretcode(-topic));
|
||||
|
||||
/* Create a Writer. */
|
||||
writer = dds_create_writer (participant, topic, NULL, NULL);
|
||||
if (writer < 0)
|
||||
DDS_FATAL("dds_create_write: %s\n", dds_strretcode(-writer));
|
||||
|
||||
printf("=== [Publisher] Waiting for a reader to be discovered ...\n");
|
||||
fflush (stdout);
|
||||
|
||||
rc = dds_set_status_mask(writer, DDS_PUBLICATION_MATCHED_STATUS);
|
||||
if (rc != DDS_RETCODE_OK)
|
||||
DDS_FATAL("dds_set_status_mask: %s\n", dds_strretcode(-rc));
|
||||
|
||||
while(!(status & DDS_PUBLICATION_MATCHED_STATUS))
|
||||
{
|
||||
rc = dds_get_status_changes (writer, &status);
|
||||
if (rc != DDS_RETCODE_OK)
|
||||
DDS_FATAL("dds_get_status_changes: %s\n", dds_strretcode(-rc));
|
||||
|
||||
/* Polling sleep. */
|
||||
dds_sleepfor (DDS_MSECS (20));
|
||||
}
|
||||
|
||||
/* Create a message to write. */
|
||||
msg.userID = 1;
|
||||
msg.message = "Hello World";
|
||||
|
||||
printf ("=== [Publisher] Writing : ");
|
||||
printf ("Message (%"PRId32", %s)\n", msg.userID, msg.message);
|
||||
fflush (stdout);
|
||||
|
||||
rc = dds_write (writer, &msg);
|
||||
if (rc != DDS_RETCODE_OK)
|
||||
DDS_FATAL("dds_write: %s\n", dds_strretcode(-rc));
|
||||
|
||||
/* Deleting the participant will delete all its children recursively as well. */
|
||||
rc = dds_delete (participant);
|
||||
if (rc != DDS_RETCODE_OK)
|
||||
DDS_FATAL("dds_delete: %s\n", dds_strretcode(-rc));
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
46
examples/helloworld/readme.rst
Normal file
46
examples/helloworld/readme.rst
Normal file
|
@ -0,0 +1,46 @@
|
|||
..
|
||||
Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
|
||||
|
||||
This program and the accompanying materials are made available under the
|
||||
terms of the Eclipse Public License v. 2.0 which is available at
|
||||
http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
v. 1.0 which is available at
|
||||
http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
|
||||
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
|
||||
HelloWorld
|
||||
==========
|
||||
|
||||
Description
|
||||
***********
|
||||
|
||||
The basic HelloWorld example is used to illustrate the necessary steps to setup DCPS entities.
|
||||
Note it is also used in the Getting Started Guide to explain the usage of Eclipse Cyclone DDS.
|
||||
|
||||
Design
|
||||
******
|
||||
|
||||
It consists of 2 units:
|
||||
|
||||
- HelloworldPublisher: implements the publisher's main
|
||||
- HelloworldSubscriber: implements the subscriber's main
|
||||
|
||||
Scenario
|
||||
********
|
||||
|
||||
The publisher sends a single HelloWorld sample. The sample contains two fields:
|
||||
|
||||
- a userID field (long type)
|
||||
- a message field (string type)
|
||||
|
||||
When it receives the sample, the subscriber displays the userID and the message field.
|
||||
|
||||
Running the example
|
||||
*******************
|
||||
|
||||
It is recommended that you run the subscriber and publisher in separate terminals to avoid mixing the output.
|
||||
|
||||
- Open 2 terminals.
|
||||
- In the first terminal start the subscriber by running HelloWorldSubscriber
|
||||
- In the second terminal start the publisher by running HelloWorldPublisher
|
84
examples/helloworld/subscriber.c
Normal file
84
examples/helloworld/subscriber.c
Normal file
|
@ -0,0 +1,84 @@
|
|||
#include "dds/dds.h"
|
||||
#include "HelloWorldData.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* An array of one message (aka sample in dds terms) will be used. */
|
||||
#define MAX_SAMPLES 1
|
||||
|
||||
int main (int argc, char ** argv)
|
||||
{
|
||||
dds_entity_t participant;
|
||||
dds_entity_t topic;
|
||||
dds_entity_t reader;
|
||||
HelloWorldData_Msg *msg;
|
||||
void *samples[MAX_SAMPLES];
|
||||
dds_sample_info_t infos[MAX_SAMPLES];
|
||||
dds_return_t rc;
|
||||
dds_qos_t *qos;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
/* Create a Participant. */
|
||||
participant = dds_create_participant (DDS_DOMAIN_DEFAULT, NULL, NULL);
|
||||
if (participant < 0)
|
||||
DDS_FATAL("dds_create_participant: %s\n", dds_strretcode(-participant));
|
||||
|
||||
/* Create a Topic. */
|
||||
topic = dds_create_topic (
|
||||
participant, &HelloWorldData_Msg_desc, "HelloWorldData_Msg", NULL, NULL);
|
||||
if (topic < 0)
|
||||
DDS_FATAL("dds_create_topic: %s\n", dds_strretcode(-topic));
|
||||
|
||||
/* Create a reliable Reader. */
|
||||
qos = dds_create_qos ();
|
||||
dds_qset_reliability (qos, DDS_RELIABILITY_RELIABLE, DDS_SECS (10));
|
||||
reader = dds_create_reader (participant, topic, qos, NULL);
|
||||
if (reader < 0)
|
||||
DDS_FATAL("dds_create_reader: %s\n", dds_strretcode(-reader));
|
||||
dds_delete_qos(qos);
|
||||
|
||||
printf ("\n=== [Subscriber] Waiting for a sample ...\n");
|
||||
fflush (stdout);
|
||||
|
||||
/* Initialize sample buffer, by pointing the void pointer within
|
||||
* the buffer array to a valid sample memory location. */
|
||||
samples[0] = HelloWorldData_Msg__alloc ();
|
||||
|
||||
/* Poll until data has been read. */
|
||||
while (true)
|
||||
{
|
||||
/* Do the actual read.
|
||||
* The return value contains the number of read samples. */
|
||||
rc = dds_read (reader, samples, infos, MAX_SAMPLES, MAX_SAMPLES);
|
||||
if (rc < 0)
|
||||
DDS_FATAL("dds_read: %s\n", dds_strretcode(-rc));
|
||||
|
||||
/* Check if we read some data and it is valid. */
|
||||
if ((rc > 0) && (infos[0].valid_data))
|
||||
{
|
||||
/* Print Message. */
|
||||
msg = (HelloWorldData_Msg*) samples[0];
|
||||
printf ("=== [Subscriber] Received : ");
|
||||
printf ("Message (%"PRId32", %s)\n", msg->userID, msg->message);
|
||||
fflush (stdout);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Polling sleep. */
|
||||
dds_sleepfor (DDS_MSECS (20));
|
||||
}
|
||||
}
|
||||
|
||||
/* Free the data location. */
|
||||
HelloWorldData_Msg_free (samples[0], DDS_FREE_ALL);
|
||||
|
||||
/* Deleting the participant will delete all its children recursively as well. */
|
||||
rc = dds_delete (participant);
|
||||
if (rc != DDS_RETCODE_OK)
|
||||
DDS_FATAL("dds_delete: %s\n", dds_strretcode(-rc));
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
142
examples/helloworld/vs/HelloWorldPublisher.vcxproj
Normal file
142
examples/helloworld/vs/HelloWorldPublisher.vcxproj
Normal file
|
@ -0,0 +1,142 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{D8B2F285-EB03-4657-ACDC-603172553BEA}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectName>HelloWorldPublisher</ProjectName>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="directories.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="directories.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="directories.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="directories.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<IntDir>$(Platform)\$(Configuration)\Publisher\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Platform)'=='Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(CycloneDDS_inc_dir);..\generated;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalLibraryDirectories>$(CycloneDDS_lib_dir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>ddsc.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(CycloneDDS_inc_dir);..\generated;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>ddsc.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(CycloneDDS_lib_dir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(CycloneDDS_inc_dir);..\generated;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>ddsc.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(CycloneDDS_lib_dir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\generated\HelloWorldData.c" />
|
||||
<ClCompile Include="..\publisher.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\generated\HelloWorldData.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
</Project>
|
142
examples/helloworld/vs/HelloWorldSubscriber.vcxproj
Normal file
142
examples/helloworld/vs/HelloWorldSubscriber.vcxproj
Normal file
|
@ -0,0 +1,142 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{D8B2F285-EB03-4657-A0DC-603172553BEA}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectName>HelloWorldSubscriber</ProjectName>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="directories.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="directories.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="directories.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="directories.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<IntDir>$(Platform)\$(Configuration)\Subscriber\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Platform)'=='Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(CycloneDDS_inc_dir);..\generated;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalLibraryDirectories>$(CycloneDDS_lib_dir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>ddsc.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(CycloneDDS_inc_dir);..\generated;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>ddsc.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(CycloneDDS_lib_dir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(CycloneDDS_inc_dir);..\generated;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>ddsc.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(CycloneDDS_lib_dir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\generated\HelloWorldData.c" />
|
||||
<ClCompile Include="..\subscriber.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\generated\HelloWorldData.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
</Project>
|
86
examples/helloworld/vs/HelloWorldType.vcxproj
Normal file
86
examples/helloworld/vs/HelloWorldType.vcxproj
Normal file
|
@ -0,0 +1,86 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{28A7A97B-3CEF-4FD7-8DC5-A7586D6DAE1D}</ProjectGuid>
|
||||
<RootNamespace>HelloWorldType</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="directories.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="directories.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="directories.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="directories.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="..\HelloWorldData.idl">
|
||||
<Command>java -classpath "$(CycloneDDS_idlc_dir)\idlc-jar-with-dependencies.jar" org.eclipse.cyclonedds.compilers.Idlc -d "$(SolutionDir)generated" "%(FullPath)"</Command>
|
||||
<Message>Generating source files from IDL into "$(SolutionDir)generated"</Message>
|
||||
<Outputs>HelloWorldData.h;HelloWorldData.c</Outputs>
|
||||
</CustomBuild>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
30
examples/helloworld/vs/directories.props
Normal file
30
examples/helloworld/vs/directories.props
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ImportGroup Label="PropertySheets" />
|
||||
<PropertyGroup Label="UserMacros">
|
||||
<CycloneDDS_lib_dir>C:/Program Files/ADLINK/Cyclone DDS/lib</CycloneDDS_lib_dir>
|
||||
<CycloneDDS_inc_dir>C:/Program Files/ADLINK/Cyclone DDS/include</CycloneDDS_inc_dir>
|
||||
<CycloneDDS_idlc_dir>C:/Program Files/ADLINK/Cyclone DDS/share/CycloneDDS/idlc</CycloneDDS_idlc_dir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup />
|
||||
<ItemGroup>
|
||||
<BuildMacro Include="CycloneDDS_lib_dir">
|
||||
<Value>$(CycloneDDS_lib_dir)</Value>
|
||||
</BuildMacro>
|
||||
<BuildMacro Include="CycloneDDS_inc_dir">
|
||||
<Value>$(CycloneDDS_inc_dir)</Value>
|
||||
</BuildMacro>
|
||||
<BuildMacro Include="CycloneDDS_idlc_dir">
|
||||
<Value>$(CycloneDDS_idlc_dir)</Value>
|
||||
</BuildMacro>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Configuration">
|
||||
<!-->
|
||||
User macros are not expanded when starting the build applications in VS.
|
||||
The path is extended with a possible Cyclone DLL location when they are not
|
||||
installed in a system default location.
|
||||
<-->
|
||||
<LocalDebuggerEnvironment>PATH=../../../../../bin;%PATH%
|
||||
$(LocalDebuggerEnvironment)</LocalDebuggerEnvironment>
|
||||
</PropertyGroup>
|
||||
</Project>
|
48
examples/perfscript/ethload
Executable file
48
examples/perfscript/ethload
Executable file
|
@ -0,0 +1,48 @@
|
|||
#!/usr/bin/perl -w
|
||||
|
||||
use strict;
|
||||
use bignum;
|
||||
use Time::HiRes qw (time);
|
||||
|
||||
if (@ARGV != 2) {
|
||||
print STDERR "usage: $0 device {100|1000}\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
my $devname = $ARGV[0];
|
||||
my $speed = $ARGV[1] * 1e6;
|
||||
|
||||
$| = 1;
|
||||
my ($lt, $t, $lrecv, $lxmit);
|
||||
while (1) {
|
||||
open FH, "< /proc/net/dev" or die "can't open /proc/net/dev";
|
||||
$t = time;
|
||||
my $info = undef;
|
||||
while (<FH>) {
|
||||
chomp;
|
||||
if (s/^\s+$devname\s*:\s*//) {
|
||||
$info = $_;
|
||||
last;
|
||||
}
|
||||
}
|
||||
close FH;
|
||||
die "can't locate info for $devname" unless defined $info;
|
||||
|
||||
my @info = split ' ', $info;
|
||||
my $recv = $info[0] + 0;
|
||||
my $xmit = $info[8] + 0;
|
||||
|
||||
if (defined $lt) {
|
||||
my $dr = $recv - $lrecv;
|
||||
my $dx = $xmit - $lxmit;
|
||||
my $dt = $t - $lt;
|
||||
my $ur = 100 * (8 * $dr) / ($dt * $speed);
|
||||
my $xr = 100 * (8 * $dx) / ($dt * $speed);
|
||||
printf "r %5.1f x %5.1f (%8d %8d)\n", $ur, $xr, $dr, $dx;
|
||||
}
|
||||
|
||||
$lt = $t;
|
||||
$lrecv = $recv;
|
||||
$lxmit = $xmit;
|
||||
sleep 1;
|
||||
}
|
30
examples/perfscript/quick-microbenchmark
Normal file
30
examples/perfscript/quick-microbenchmark
Normal file
|
@ -0,0 +1,30 @@
|
|||
export CYCLONEDDS_URI='<Internal><MinimumSocketReceiveBufferSize>250kB</></><General><MaxMessageSize>65500B</><FragmentSize>65000B</>'
|
||||
set -x
|
||||
gen/ddsperf -D20 -L ping pon
|
||||
for x in 16 32 64 128 1024 4096 16384 ; do
|
||||
gen/ddsperf -D20 -TKS -z$x -L ping pong
|
||||
done
|
||||
gen/ddsperf -D20 -L pub sub
|
||||
for x in 16 32 64 128 1024 4096 16384 ; do
|
||||
gen/ddsperf -D20 -TKS -z$x -L pub sub
|
||||
done
|
||||
gen/ddsperf pong & pid=$!
|
||||
gen/ddsperf -D20 ping
|
||||
kill $pid
|
||||
wait
|
||||
gen/ddsperf -TKS pong & pid=$!
|
||||
for x in 16 32 64 128 1024 4096 16384 ; do
|
||||
gen/ddsperf -D20 -TKS -z$x ping
|
||||
done
|
||||
kill $pid
|
||||
wait
|
||||
gen/ddsperf sub & pid=$!
|
||||
gen/ddsperf -D20 pub
|
||||
kill $pid
|
||||
wait
|
||||
gen/ddsperf -TKS sub & pid=$!
|
||||
for x in 16 32 64 128 1024 4096 16384 ; do
|
||||
gen/ddsperf -D20 -TKS -z$x pub
|
||||
done
|
||||
kill $pid
|
||||
wait
|
73
examples/perfscript/throughput-fanout-test
Normal file
73
examples/perfscript/throughput-fanout-test
Normal file
|
@ -0,0 +1,73 @@
|
|||
#!/bin/bash
|
||||
|
||||
usage () {
|
||||
cat >&2 <<EOF
|
||||
usage: $0 [OPTIONS] user@remote [user@remote...]
|
||||
|
||||
OPTIONS
|
||||
-i IF use network interface IF (default: eth0)
|
||||
-b 100|1000 network bandwidth (100Mbps/1000Gbps) for calculating load
|
||||
% given load in bytes/second (default: 1000)
|
||||
-d DIR use DIR on remote (default: PWD)
|
||||
-p provision required binaries in DIR (default: false)
|
||||
first ssh's in to try mkdir -p DIR, then follows up with scp
|
||||
-t DUR run for DUR seconds per size (default 20)
|
||||
-a ASYNCLIST run for delivery async settings ASYNCLIST (default: "0 1")
|
||||
-m MODELIST run with subscriber mode settings MODELIST (default: "-1 0 1")
|
||||
-s SIZELIST run for sizes in SIZELIST (default: "0 16 32 64 128 256")
|
||||
-l LOOPBACK enable multicast loopback (true/false, default: true)
|
||||
-o DIR store results in dir.N where N is number of nodes
|
||||
|
||||
Runs throughput-test with the specified options for each prefix of remote nodes.
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
export nwif=eth0
|
||||
bandwidth=1000
|
||||
remotedir="$PWD"
|
||||
provision=false
|
||||
asynclist="0 1"
|
||||
modelist="-1 0 1"
|
||||
sizelist="0 16 32 64 128 256"
|
||||
timeout=20
|
||||
loopback=true
|
||||
resultdir="throughput-result"
|
||||
while getopts "i:b:d:pa:m:s:t:o:l:" opt ; do
|
||||
case $opt in
|
||||
i) nwif="$OPTARG" ;;
|
||||
b) bandwidth="$OPTARG" ;;
|
||||
d) remotedir="$OPTARG" ;;
|
||||
p) provision=true ;;
|
||||
a) asynclist="$OPTARG" ;;
|
||||
m) modelist="$OPTARG" ;;
|
||||
s) sizelist="$OPTARG" ;;
|
||||
l) loopback="OPTARG" ;;
|
||||
t) timeout="$OPTARG" ;;
|
||||
o) resultdir="$OPTARG" ;;
|
||||
h) usage ;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND-1))
|
||||
if [ $# -lt 1 ] ; then usage ; fi
|
||||
pubremote=$1
|
||||
shift
|
||||
|
||||
popt=
|
||||
$provision && popt=-p
|
||||
|
||||
n=0
|
||||
while [[ $n -le $# ]] ; do
|
||||
out=$resultdir.$(( $n + 1 ))
|
||||
mkdir $out
|
||||
|
||||
otherhosts=""
|
||||
j=1
|
||||
while [[ $j -le $n ]] ; do
|
||||
hostJ=`eval echo "\\$$j"`
|
||||
otherhosts="$otherhosts $hostJ"
|
||||
j=$(( $j + 1 ))
|
||||
done
|
||||
`dirname $0`/throughput-test -i "$nwif" -b "$bandwidth" -d "$remotedir" $popt -a "$asynclist" -m "$modelist" -s "$sizelist" -t "$timeout" -o $out $pubremote $otherhosts
|
||||
n=$(( $n + 1 ))
|
||||
done
|
159
examples/perfscript/throughput-test
Normal file
159
examples/perfscript/throughput-test
Normal file
|
@ -0,0 +1,159 @@
|
|||
#!/bin/bash
|
||||
|
||||
usage () {
|
||||
cat >&2 <<EOF
|
||||
usage: $0 [OPTIONS] user@remote [user@remote...]
|
||||
|
||||
OPTIONS
|
||||
-i IF use network interface IF (default: eth0)
|
||||
-b 100|1000 network bandwidth (100Mbps/1000Gbps) for calculating load
|
||||
% given load in bytes/second (default: 1000)
|
||||
-d DIR use DIR on remote (default: PWD)
|
||||
-p provision required binaries in DIR (default: false)
|
||||
first ssh's in to try mkdir -p DIR, then follows up with scp
|
||||
-t DUR run for DUR seconds per size (default 20)
|
||||
-a ASYNCLIST run for delivery async settings ASYNCLIST (default: "0 1")
|
||||
-m MODELIST run with subscriber mode settings MODELIST (default: "-1 0 1")
|
||||
-s SIZELIST run for sizes in SIZELIST (default: "0 16 32 64 128 256")
|
||||
-l LOOPBACK enable/disable multicast loopback (true/false, default: true)
|
||||
-o DIR store results in dir (default: throughput-result)
|
||||
|
||||
Local host runs ThroughputSubscriber, first remote runs ThroughputPublisher,
|
||||
further remotes also run ThroughputSubscriber. It assumes these are
|
||||
available in DIR/bin. It also assumes that ssh user@remote works without
|
||||
requiring a password.
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
export nwif=eth0
|
||||
bandwidth=1000
|
||||
remotedir="$PWD"
|
||||
provision=false
|
||||
asynclist="0 1"
|
||||
modelist="-1 0 1"
|
||||
sizelist="0 16 32 64 128 256"
|
||||
timeout=20
|
||||
loopback=true
|
||||
resultdir="throughput-result"
|
||||
while getopts "i:b:d:pa:m:s:t:o:l:" opt ; do
|
||||
case $opt in
|
||||
i) nwif="$OPTARG" ;;
|
||||
b) bandwidth="$OPTARG" ;;
|
||||
d) remotedir="$OPTARG" ;;
|
||||
p) provision=true ;;
|
||||
a) asynclist="$OPTARG" ;;
|
||||
m) modelist="$OPTARG" ;;
|
||||
s) sizelist="$OPTARG" ;;
|
||||
l) loopback="OPTARG" ;;
|
||||
t) timeout="$OPTARG" ;;
|
||||
o) resultdir="$OPTARG" ;;
|
||||
h) usage ;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND-1))
|
||||
if [ $# -lt 1 ] ; then usage ; fi
|
||||
ethload=`dirname $0`/ethload
|
||||
pubremote=$1
|
||||
shift
|
||||
|
||||
cfg=cdds-simple.xml
|
||||
cat >$cfg <<EOF
|
||||
<CycloneDDS>
|
||||
<Domain>
|
||||
<Id>17</Id>
|
||||
</Domain>
|
||||
<DDSI2E>
|
||||
<General>
|
||||
<NetworkInterfaceAddress>$nwif</NetworkInterfaceAddress>
|
||||
<EnableMulticastLoopback>$loopback</EnableMulticastLoopback>
|
||||
</General>
|
||||
<Internal>
|
||||
<Watermarks>
|
||||
<WhcHigh>500kB</WhcHigh>
|
||||
</Watermarks>
|
||||
<SynchronousDeliveryPriorityThreshold>${async:-0}</SynchronousDeliveryPriorityThreshold>
|
||||
<LeaseDuration>3s</LeaseDuration>
|
||||
</Internal>
|
||||
</DDSI2E>
|
||||
</CycloneDDS>
|
||||
EOF
|
||||
|
||||
if [ ! -x bin/ThroughputPublisher -o ! -x bin/ThroughputSubscriber -o ! -x $ethload ] ; then
|
||||
echo "some check for existence of a file failed on the local machine" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
[ -d $resultdir ] || { echo "output directory $resultdir doesn't exist" >&2 ; exit 1 ; }
|
||||
|
||||
if $provision ; then
|
||||
echo "provisioning ..."
|
||||
for r in $pubremote "$@" ; do
|
||||
ssh $r mkdir -p $remotedir $remotedir/bin $remotedir/lib
|
||||
scp lib/libddsc.so.0 $r:$remotedir/lib
|
||||
scp bin/ThroughputPublisher bin/ThroughputSubscriber $r:$remotedir/bin
|
||||
done
|
||||
fi
|
||||
|
||||
export CYCLONEDDS_URI=file://$PWD/$cfg
|
||||
for r in $pubremote "$@" ; do
|
||||
scp $cfg $r:$remotedir || { echo "failed to copy $cfg to $remote:$PWD" >&2 ; exit 1 ; }
|
||||
done
|
||||
|
||||
for async in $asynclist ; do
|
||||
export async
|
||||
for mode in $modelist ; do
|
||||
echo "======== ASYNC $async MODE $mode ========="
|
||||
|
||||
cat > run-publisher.tmp <<EOF
|
||||
export CYCLONEDDS_URI=file://$remotedir/$cfg
|
||||
export async=$async
|
||||
cd $remotedir
|
||||
rm -f pub-top.log
|
||||
for size in $sizelist ; do
|
||||
echo "size \$size"
|
||||
bin/ThroughputPublisher \$size > pub.log & ppid=\$!
|
||||
top -b -d1 -p \$ppid >> pub-top.log & tpid=\$!
|
||||
sleep $timeout
|
||||
kill \$tpid
|
||||
kill -2 \$ppid
|
||||
wait \$ppid
|
||||
sleep 5
|
||||
done
|
||||
wait
|
||||
EOF
|
||||
scp run-publisher.tmp $pubremote:$remotedir || { echo "failed to copy $cfg to $remote:$PWD" >&2 ; exit 2 ; }
|
||||
killremotesubs=""
|
||||
if [ $# -gt 0 ] ; then
|
||||
cat > run-subscriber.tmp <<EOF
|
||||
export CYCLONEDDS_URI=file://$remotedir/$cfg
|
||||
export async=$async
|
||||
cd $remotedir
|
||||
nohup bin/ThroughputSubscriber 0 $mode > /dev/null &
|
||||
echo \$!
|
||||
EOF
|
||||
for r in "$@" ; do
|
||||
scp run-subscriber.tmp $r:$remotedir
|
||||
rsubpid=`ssh $r ". $remotedir/run-subscriber.tmp"`
|
||||
killremotesubs="$killremotesubs ssh $r kill -9 $rsubpid &"
|
||||
done
|
||||
fi
|
||||
|
||||
outdir=$resultdir/data-async$async-mode$mode
|
||||
mkdir $outdir
|
||||
|
||||
rm -f sub-top.log
|
||||
$ethload $nwif $bandwidth > $outdir/sub-ethload.log & lpid=$!
|
||||
bin/ThroughputSubscriber 0 $mode > $outdir/sub.log & spid=$!
|
||||
top -b -d1 -p $spid >> $outdir/sub-top.log & tpid=$!
|
||||
tail -f $outdir/sub.log & xpid=$!
|
||||
ssh $pubremote ". $remotedir/run-publisher.tmp"
|
||||
kill $tpid
|
||||
kill -2 $spid
|
||||
eval $killremotesubs
|
||||
sleep 1
|
||||
kill $lpid $xpid
|
||||
wait
|
||||
scp $pubremote:$remotedir/{pub-top.log,pub.log} $outdir
|
||||
done
|
||||
done
|
59
examples/perfscript/throughput-test-extract
Executable file
59
examples/perfscript/throughput-test-extract
Executable file
|
@ -0,0 +1,59 @@
|
|||
#!/usr/bin/perl -w
|
||||
|
||||
use strict;
|
||||
|
||||
my @dirs = ("async0-mode-1", "async0-mode0", "async0-mode1",
|
||||
"async1-mode-1", "async1-mode0", "async1-mode1");
|
||||
|
||||
my $dataset = 0;
|
||||
my $basedir = "throughput-result";
|
||||
$basedir = $ARGV[0] if @ARGV== 1;
|
||||
my $load_threshold = 20;
|
||||
for my $dir (@dirs) {
|
||||
my @loads = ();
|
||||
|
||||
{
|
||||
open LH, "< $basedir/data-$dir/sub-ethload.log" or next; # die "can't open $basedir/data-$dir/sub-ethload.log";
|
||||
my @curload = ();
|
||||
while (<LH>) {
|
||||
next unless /^r +([0-9.]+).*\( *(\d+)/;
|
||||
push @curload, $2 if $1 > $load_threshold;
|
||||
if (@curload && $1 < $load_threshold) {
|
||||
push @loads, median (@curload);
|
||||
@curload = ();
|
||||
}
|
||||
}
|
||||
push @loads, median (@curload) if @curload;
|
||||
close LH;
|
||||
}
|
||||
|
||||
open FH, "< $basedir/data-$dir/sub.log" or next; # die "can't open $basedir/data-$dir/sub.log";
|
||||
print "\n\n" if $dataset++;
|
||||
print "# mode $dir\n";
|
||||
print "# payloadsize rate[samples/s] appl.bandwidth[Mb/s] raw.bandwidth[Mb/s]\n";
|
||||
my $psz;
|
||||
my @rate = ();
|
||||
while (<FH>) {
|
||||
next unless /Payload size: ([0-9]+).*Transfer rate: ([0-9.]+)/;
|
||||
my $psz_cur = $1; my $rate_cur = $2;
|
||||
$psz = $psz_cur unless defined $psz;
|
||||
if ($psz != $psz_cur) {
|
||||
my $load = shift @loads;
|
||||
my $rate = median (@rate);
|
||||
printf "%d %f %f %f\n", $psz, $rate, $rate * (8 + $psz) / 125e3, $load / 125e3;
|
||||
@rate = ();
|
||||
}
|
||||
$psz = $psz_cur;
|
||||
push @rate, ($rate_cur + 0.0);
|
||||
}
|
||||
my $load = shift @loads;
|
||||
my $rate = median (@rate);
|
||||
printf "%d %f %f %f\n", $psz, $rate, $rate * (8 + $psz) / 125e3, $load / 125e3;
|
||||
close FH;
|
||||
}
|
||||
|
||||
sub median {
|
||||
my @xs = sort { $a <=> $b } @_;
|
||||
return (@xs % 2) ? $xs[(@xs - 1) / 2] : ($xs[@xs/2 - 1] + $xs[@xs/2]) / 2;
|
||||
}
|
||||
|
14
examples/perfscript/throughput-test-plot
Executable file
14
examples/perfscript/throughput-test-plot
Executable file
|
@ -0,0 +1,14 @@
|
|||
#!/bin/bash
|
||||
|
||||
`dirname $0`/throughput-test-extract > data.txt
|
||||
gnuplot <<\EOF
|
||||
set term png size 1024,768
|
||||
set output "throughput-polling.png"
|
||||
set st d l
|
||||
set title "Throughput (polling with 1ms sleeps)"
|
||||
set ylabel "M sample/s"
|
||||
set y2label "Mbps"
|
||||
set y2tics
|
||||
set xlabel "payload size [bytes]"
|
||||
p "data.txt" i 5 u 1:($2/1e6) ti "rate [M sample/s]", "" i 5 u 1:3 axes x1y2 ti "app bandwidth [Mbps]", "" i 5 u 1:4 axes x1y2 ti "GbE bandwidth [Mbps]"
|
||||
EOF
|
33
examples/roundtrip/CMakeLists.txt
Normal file
33
examples/roundtrip/CMakeLists.txt
Normal file
|
@ -0,0 +1,33 @@
|
|||
#
|
||||
# Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
|
||||
#
|
||||
# This program and the accompanying materials are made available under the
|
||||
# terms of the Eclipse Public License v. 2.0 which is available at
|
||||
# http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
# v. 1.0 which is available at
|
||||
# http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
#
|
||||
# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
#
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
if (NOT TARGET CycloneDDS::ddsc)
|
||||
# Find the CycloneDDS package. If it is not in a default location, try
|
||||
# finding it relative to the example where it most likely resides.
|
||||
find_package(CycloneDDS REQUIRED PATHS "${CMAKE_SOURCE_DIR}/../../")
|
||||
endif()
|
||||
|
||||
# This is a convenience function, provided by the CycloneDDS package,
|
||||
# that will supply a library target related the the given idl file.
|
||||
# In short, it takes the idl file, generates the source files with
|
||||
# the proper data types and compiles them into a library.
|
||||
idlc_generate(RoundTrip_lib RoundTrip.idl)
|
||||
|
||||
# Both executables have only one related source file.
|
||||
add_executable(RoundtripPing ping.c)
|
||||
add_executable(RoundtripPong pong.c)
|
||||
|
||||
# Both executables need to be linked to the idl data type library and
|
||||
# the ddsc API library.
|
||||
target_link_libraries(RoundtripPing RoundTrip_lib CycloneDDS::ddsc)
|
||||
target_link_libraries(RoundtripPong RoundTrip_lib CycloneDDS::ddsc)
|
8
examples/roundtrip/RoundTrip.idl
Normal file
8
examples/roundtrip/RoundTrip.idl
Normal file
|
@ -0,0 +1,8 @@
|
|||
module RoundTripModule
|
||||
{
|
||||
struct DataType
|
||||
{
|
||||
sequence<octet> payload;
|
||||
};
|
||||
#pragma keylist DataType
|
||||
};
|
513
examples/roundtrip/ping.c
Normal file
513
examples/roundtrip/ping.c
Normal file
|
@ -0,0 +1,513 @@
|
|||
#include "dds/dds.h"
|
||||
#include "RoundTrip.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#define TIME_STATS_SIZE_INCREMENT 50000
|
||||
#define MAX_SAMPLES 100
|
||||
#define US_IN_ONE_SEC 1000000LL
|
||||
|
||||
/* Forward declaration */
|
||||
|
||||
static dds_entity_t prepare_dds(dds_entity_t *writer, dds_entity_t *reader, dds_entity_t *readCond, dds_listener_t *listener);
|
||||
static void finalize_dds(dds_entity_t participant);
|
||||
|
||||
typedef struct ExampleTimeStats
|
||||
{
|
||||
dds_time_t * values;
|
||||
unsigned long valuesSize;
|
||||
unsigned long valuesMax;
|
||||
double average;
|
||||
dds_time_t min;
|
||||
dds_time_t max;
|
||||
unsigned long count;
|
||||
} ExampleTimeStats;
|
||||
|
||||
static void exampleInitTimeStats (ExampleTimeStats *stats)
|
||||
{
|
||||
stats->values = (dds_time_t*) malloc (TIME_STATS_SIZE_INCREMENT * sizeof (dds_time_t));
|
||||
stats->valuesSize = 0;
|
||||
stats->valuesMax = TIME_STATS_SIZE_INCREMENT;
|
||||
stats->average = 0;
|
||||
stats->min = 0;
|
||||
stats->max = 0;
|
||||
stats->count = 0;
|
||||
}
|
||||
|
||||
static void exampleResetTimeStats (ExampleTimeStats *stats)
|
||||
{
|
||||
memset (stats->values, 0, stats->valuesMax * sizeof (dds_time_t));
|
||||
stats->valuesSize = 0;
|
||||
stats->average = 0;
|
||||
stats->min = 0;
|
||||
stats->max = 0;
|
||||
stats->count = 0;
|
||||
}
|
||||
|
||||
static void exampleDeleteTimeStats (ExampleTimeStats *stats)
|
||||
{
|
||||
free (stats->values);
|
||||
}
|
||||
|
||||
static ExampleTimeStats *exampleAddTimingToTimeStats
|
||||
(ExampleTimeStats *stats, dds_time_t timing)
|
||||
{
|
||||
if (stats->valuesSize > stats->valuesMax)
|
||||
{
|
||||
dds_time_t * temp = (dds_time_t*) realloc (stats->values, (stats->valuesMax + TIME_STATS_SIZE_INCREMENT) * sizeof (dds_time_t));
|
||||
stats->values = temp;
|
||||
stats->valuesMax += TIME_STATS_SIZE_INCREMENT;
|
||||
}
|
||||
if (stats->values != NULL && stats->valuesSize < stats->valuesMax)
|
||||
{
|
||||
stats->values[stats->valuesSize++] = timing;
|
||||
}
|
||||
stats->average = ((double)stats->count * stats->average + (double)timing) / (double)(stats->count + 1);
|
||||
stats->min = (stats->count == 0 || timing < stats->min) ? timing : stats->min;
|
||||
stats->max = (stats->count == 0 || timing > stats->max) ? timing : stats->max;
|
||||
stats->count++;
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
static int exampleCompareul (const void* a, const void* b)
|
||||
{
|
||||
dds_time_t ul_a = *((dds_time_t*)a);
|
||||
dds_time_t ul_b = *((dds_time_t*)b);
|
||||
|
||||
if (ul_a < ul_b) return -1;
|
||||
if (ul_a > ul_b) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static double exampleGetMedianFromTimeStats (ExampleTimeStats *stats)
|
||||
{
|
||||
double median = 0.0;
|
||||
|
||||
qsort (stats->values, stats->valuesSize, sizeof (dds_time_t), exampleCompareul);
|
||||
|
||||
if (stats->valuesSize % 2 == 0)
|
||||
{
|
||||
median = (double)(stats->values[stats->valuesSize / 2 - 1] + stats->values[stats->valuesSize / 2]) / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
median = (double)stats->values[stats->valuesSize / 2];
|
||||
}
|
||||
|
||||
return median;
|
||||
}
|
||||
|
||||
static dds_time_t exampleGet99PercentileFromTimeStats (ExampleTimeStats *stats)
|
||||
{
|
||||
qsort (stats->values, stats->valuesSize, sizeof (dds_time_t), exampleCompareul);
|
||||
return stats->values[stats->valuesSize - stats->valuesSize / 100];
|
||||
}
|
||||
|
||||
static dds_entity_t waitSet;
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
static bool CtrlHandler (DWORD fdwCtrlType)
|
||||
{
|
||||
dds_waitset_set_trigger (waitSet, true);
|
||||
return true; //Don't let other handlers handle this key
|
||||
}
|
||||
#elif !DDSRT_WITH_FREERTOS
|
||||
static void CtrlHandler (int sig)
|
||||
{
|
||||
(void)sig;
|
||||
dds_waitset_set_trigger (waitSet, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
static dds_entity_t writer;
|
||||
static dds_entity_t reader;
|
||||
static dds_entity_t participant;
|
||||
static dds_entity_t readCond;
|
||||
|
||||
static ExampleTimeStats roundTrip;
|
||||
static ExampleTimeStats writeAccess;
|
||||
static ExampleTimeStats readAccess;
|
||||
static ExampleTimeStats roundTripOverall;
|
||||
static ExampleTimeStats writeAccessOverall;
|
||||
static ExampleTimeStats readAccessOverall;
|
||||
|
||||
static RoundTripModule_DataType pub_data;
|
||||
static RoundTripModule_DataType sub_data[MAX_SAMPLES];
|
||||
static void *samples[MAX_SAMPLES];
|
||||
static dds_sample_info_t info[MAX_SAMPLES];
|
||||
|
||||
static dds_time_t startTime;
|
||||
static dds_time_t preWriteTime;
|
||||
static dds_time_t postWriteTime;
|
||||
static dds_time_t preTakeTime;
|
||||
static dds_time_t postTakeTime;
|
||||
static dds_time_t elapsed = 0;
|
||||
|
||||
static bool warmUp = true;
|
||||
|
||||
static void data_available(dds_entity_t rd, void *arg)
|
||||
{
|
||||
dds_time_t difference = 0;
|
||||
int status;
|
||||
(void)arg;
|
||||
/* Take sample and check that it is valid */
|
||||
preTakeTime = dds_time ();
|
||||
status = dds_take (rd, samples, info, MAX_SAMPLES, MAX_SAMPLES);
|
||||
if (status < 0)
|
||||
DDS_FATAL("dds_take: %s\n", dds_strretcode(-status));
|
||||
postTakeTime = dds_time ();
|
||||
|
||||
/* Update stats */
|
||||
difference = (postWriteTime - preWriteTime)/DDS_NSECS_IN_USEC;
|
||||
writeAccess = *exampleAddTimingToTimeStats (&writeAccess, difference);
|
||||
writeAccessOverall = *exampleAddTimingToTimeStats (&writeAccessOverall, difference);
|
||||
|
||||
difference = (postTakeTime - preTakeTime)/DDS_NSECS_IN_USEC;
|
||||
readAccess = *exampleAddTimingToTimeStats (&readAccess, difference);
|
||||
readAccessOverall = *exampleAddTimingToTimeStats (&readAccessOverall, difference);
|
||||
|
||||
difference = (postTakeTime - info[0].source_timestamp)/DDS_NSECS_IN_USEC;
|
||||
roundTrip = *exampleAddTimingToTimeStats (&roundTrip, difference);
|
||||
roundTripOverall = *exampleAddTimingToTimeStats (&roundTripOverall, difference);
|
||||
|
||||
if (!warmUp) {
|
||||
/* Print stats each second */
|
||||
difference = (postTakeTime - startTime)/DDS_NSECS_IN_USEC;
|
||||
if (difference > US_IN_ONE_SEC)
|
||||
{
|
||||
printf("%9" PRIi64 " %9lu %8.0f %8" PRIi64 " %8" PRIi64 " %8" PRIi64 " %10lu %8.0f %8" PRIi64 " %10lu %8.0f %8" PRIi64 "\n",
|
||||
elapsed + 1,
|
||||
roundTrip.count,
|
||||
exampleGetMedianFromTimeStats (&roundTrip) / 2,
|
||||
roundTrip.min / 2,
|
||||
exampleGet99PercentileFromTimeStats (&roundTrip) / 2,
|
||||
roundTrip.max / 2,
|
||||
writeAccess.count,
|
||||
exampleGetMedianFromTimeStats (&writeAccess),
|
||||
writeAccess.min,
|
||||
readAccess.count,
|
||||
exampleGetMedianFromTimeStats (&readAccess),
|
||||
readAccess.min);
|
||||
fflush (stdout);
|
||||
|
||||
exampleResetTimeStats (&roundTrip);
|
||||
exampleResetTimeStats (&writeAccess);
|
||||
exampleResetTimeStats (&readAccess);
|
||||
startTime = dds_time ();
|
||||
elapsed++;
|
||||
}
|
||||
}
|
||||
|
||||
preWriteTime = dds_time();
|
||||
status = dds_write_ts (writer, &pub_data, preWriteTime);
|
||||
if (status < 0)
|
||||
DDS_FATAL("dds_write_ts: %s\n", dds_strretcode(-status));
|
||||
postWriteTime = dds_time();
|
||||
}
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
printf ("Usage (parameters must be supplied in order):\n"
|
||||
"./ping [-l] [payloadSize (bytes, 0 - 100M)] [numSamples (0 = infinite)] [timeOut (seconds, 0 = infinite)]\n"
|
||||
"./ping quit - ping sends a quit signal to pong.\n"
|
||||
"Defaults:\n"
|
||||
"./ping 0 0 0\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
uint32_t payloadSize = 0;
|
||||
uint64_t numSamples = 0;
|
||||
bool invalidargs = false;
|
||||
dds_time_t timeOut = 0;
|
||||
dds_time_t time;
|
||||
dds_time_t difference = 0;
|
||||
|
||||
dds_attach_t wsresults[1];
|
||||
size_t wsresultsize = 1U;
|
||||
dds_time_t waitTimeout = DDS_SECS (1);
|
||||
unsigned long i;
|
||||
int status;
|
||||
|
||||
dds_listener_t *listener = NULL;
|
||||
bool use_listener = false;
|
||||
int argidx = 1;
|
||||
|
||||
/* poor man's getopt works even on Windows */
|
||||
if (argc > argidx && strcmp(argv[argidx], "-l") == 0)
|
||||
{
|
||||
argidx++;
|
||||
use_listener = true;
|
||||
}
|
||||
|
||||
/* Register handler for Ctrl-C */
|
||||
#ifdef _WIN32
|
||||
SetConsoleCtrlHandler ((PHANDLER_ROUTINE)CtrlHandler, TRUE);
|
||||
#elif !DDSRT_WITH_FREERTOS
|
||||
struct sigaction sat, oldAction;
|
||||
sat.sa_handler = CtrlHandler;
|
||||
sigemptyset (&sat.sa_mask);
|
||||
sat.sa_flags = 0;
|
||||
sigaction (SIGINT, &sat, &oldAction);
|
||||
#endif
|
||||
|
||||
exampleInitTimeStats (&roundTrip);
|
||||
exampleInitTimeStats (&writeAccess);
|
||||
exampleInitTimeStats (&readAccess);
|
||||
exampleInitTimeStats (&roundTripOverall);
|
||||
exampleInitTimeStats (&writeAccessOverall);
|
||||
exampleInitTimeStats (&readAccessOverall);
|
||||
|
||||
memset (&sub_data, 0, sizeof (sub_data));
|
||||
memset (&pub_data, 0, sizeof (pub_data));
|
||||
|
||||
for (i = 0; i < MAX_SAMPLES; i++)
|
||||
{
|
||||
samples[i] = &sub_data[i];
|
||||
}
|
||||
|
||||
participant = dds_create_participant (DDS_DOMAIN_DEFAULT, NULL, NULL);
|
||||
if (participant < 0)
|
||||
DDS_FATAL("dds_create_participant: %s\n", dds_strretcode(-participant));
|
||||
|
||||
if (use_listener)
|
||||
{
|
||||
listener = dds_create_listener(NULL);
|
||||
dds_lset_data_available(listener, data_available);
|
||||
}
|
||||
prepare_dds(&writer, &reader, &readCond, listener);
|
||||
|
||||
if (argc - argidx == 1 && strcmp (argv[argidx], "quit") == 0)
|
||||
{
|
||||
printf ("Sending termination request.\n");
|
||||
fflush (stdout);
|
||||
/* pong uses a waitset which is triggered by instance disposal, and
|
||||
quits when it fires. */
|
||||
dds_sleepfor (DDS_SECS (1));
|
||||
pub_data.payload._length = 0;
|
||||
pub_data.payload._buffer = NULL;
|
||||
pub_data.payload._release = true;
|
||||
pub_data.payload._maximum = 0;
|
||||
status = dds_writedispose (writer, &pub_data);
|
||||
if (status < 0)
|
||||
DDS_FATAL("dds_writedispose: %s\n", dds_strretcode(-status));
|
||||
dds_sleepfor (DDS_SECS (1));
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (argc - argidx == 0)
|
||||
{
|
||||
invalidargs = true;
|
||||
}
|
||||
if (argc - argidx >= 1)
|
||||
{
|
||||
payloadSize = (uint32_t) atol (argv[argidx]);
|
||||
|
||||
if (payloadSize > 100 * 1048576)
|
||||
{
|
||||
invalidargs = true;
|
||||
}
|
||||
}
|
||||
if (argc - argidx >= 2)
|
||||
{
|
||||
numSamples = (uint64_t) atol (argv[argidx+1]);
|
||||
}
|
||||
if (argc - argidx >= 3)
|
||||
{
|
||||
timeOut = atol (argv[argidx+2]);
|
||||
}
|
||||
if (invalidargs || (argc - argidx == 1 && (strcmp (argv[argidx], "-h") == 0 || strcmp (argv[argidx], "--help") == 0)))
|
||||
usage();
|
||||
printf ("# payloadSize: %" PRIu32 " | numSamples: %" PRIu64 " | timeOut: %" PRIi64 "\n\n", payloadSize, numSamples, timeOut);
|
||||
fflush (stdout);
|
||||
|
||||
pub_data.payload._length = payloadSize;
|
||||
pub_data.payload._buffer = payloadSize ? dds_alloc (payloadSize) : NULL;
|
||||
pub_data.payload._release = true;
|
||||
pub_data.payload._maximum = 0;
|
||||
for (i = 0; i < payloadSize; i++)
|
||||
{
|
||||
pub_data.payload._buffer[i] = 'a';
|
||||
}
|
||||
|
||||
startTime = dds_time ();
|
||||
printf ("# Waiting for startup jitter to stabilise\n");
|
||||
fflush (stdout);
|
||||
/* Write a sample that pong can send back */
|
||||
while (!dds_triggered (waitSet) && difference < DDS_SECS(5))
|
||||
{
|
||||
status = dds_waitset_wait (waitSet, wsresults, wsresultsize, waitTimeout);
|
||||
if (status < 0)
|
||||
DDS_FATAL("dds_waitset_wait: %s\n", dds_strretcode(-status));
|
||||
|
||||
if (status > 0 && listener == NULL) /* data */
|
||||
{
|
||||
status = dds_take (reader, samples, info, MAX_SAMPLES, MAX_SAMPLES);
|
||||
if (status < 0)
|
||||
DDS_FATAL("dds_take: %s\n", dds_strretcode(-status));
|
||||
}
|
||||
|
||||
time = dds_time ();
|
||||
difference = time - startTime;
|
||||
}
|
||||
if (!dds_triggered (waitSet))
|
||||
{
|
||||
warmUp = false;
|
||||
printf("# Warm up complete.\n\n");
|
||||
printf("# Latency measurements (in us)\n");
|
||||
printf("# Latency [us] Write-access time [us] Read-access time [us]\n");
|
||||
printf("# Seconds Count median min 99%% max Count median min Count median min\n");
|
||||
fflush (stdout);
|
||||
}
|
||||
|
||||
exampleResetTimeStats (&roundTrip);
|
||||
exampleResetTimeStats (&writeAccess);
|
||||
exampleResetTimeStats (&readAccess);
|
||||
startTime = dds_time ();
|
||||
/* Write a sample that pong can send back */
|
||||
preWriteTime = dds_time ();
|
||||
status = dds_write_ts (writer, &pub_data, preWriteTime);
|
||||
if (status < 0)
|
||||
DDS_FATAL("dds_write_ts: %s\n", dds_strretcode(-status));
|
||||
postWriteTime = dds_time ();
|
||||
for (i = 0; !dds_triggered (waitSet) && (!numSamples || i < numSamples) && !(timeOut && elapsed >= timeOut); i++)
|
||||
{
|
||||
status = dds_waitset_wait (waitSet, wsresults, wsresultsize, waitTimeout);
|
||||
if (status < 0)
|
||||
DDS_FATAL("dds_waitset_wait: %s\n", dds_strretcode(-status));
|
||||
if (status != 0 && listener == NULL) {
|
||||
data_available(reader, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (!warmUp)
|
||||
{
|
||||
printf
|
||||
(
|
||||
"\n%9s %9lu %8.0f %8" PRIi64 " %8" PRIi64 " %8" PRIi64 " %10lu %8.0f %8" PRIi64 " %10lu %8.0f %8" PRIi64 "\n",
|
||||
"# Overall",
|
||||
roundTripOverall.count,
|
||||
exampleGetMedianFromTimeStats (&roundTripOverall) / 2,
|
||||
roundTripOverall.min / 2,
|
||||
exampleGet99PercentileFromTimeStats (&roundTripOverall) / 2,
|
||||
roundTripOverall.max / 2,
|
||||
writeAccessOverall.count,
|
||||
exampleGetMedianFromTimeStats (&writeAccessOverall),
|
||||
writeAccessOverall.min,
|
||||
readAccessOverall.count,
|
||||
exampleGetMedianFromTimeStats (&readAccessOverall),
|
||||
readAccessOverall.min
|
||||
);
|
||||
fflush (stdout);
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
#ifdef _WIN32
|
||||
SetConsoleCtrlHandler (0, FALSE);
|
||||
#elif !DDSRT_WITH_FREERTOS
|
||||
sigaction (SIGINT, &oldAction, 0);
|
||||
#endif
|
||||
|
||||
finalize_dds(participant);
|
||||
|
||||
/* Clean up */
|
||||
exampleDeleteTimeStats (&roundTrip);
|
||||
exampleDeleteTimeStats (&writeAccess);
|
||||
exampleDeleteTimeStats (&readAccess);
|
||||
exampleDeleteTimeStats (&roundTripOverall);
|
||||
exampleDeleteTimeStats (&writeAccessOverall);
|
||||
exampleDeleteTimeStats (&readAccessOverall);
|
||||
|
||||
for (i = 0; i < MAX_SAMPLES; i++)
|
||||
{
|
||||
RoundTripModule_DataType_free (&sub_data[i], DDS_FREE_CONTENTS);
|
||||
}
|
||||
RoundTripModule_DataType_free (&pub_data, DDS_FREE_CONTENTS);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static dds_entity_t prepare_dds(dds_entity_t *wr, dds_entity_t *rd, dds_entity_t *rdcond, dds_listener_t *listener)
|
||||
{
|
||||
dds_return_t status;
|
||||
dds_entity_t topic;
|
||||
dds_entity_t publisher;
|
||||
dds_entity_t subscriber;
|
||||
|
||||
const char *pubPartitions[] = { "ping" };
|
||||
const char *subPartitions[] = { "pong" };
|
||||
dds_qos_t *pubQos;
|
||||
dds_qos_t *dwQos;
|
||||
dds_qos_t *drQos;
|
||||
dds_qos_t *subQos;
|
||||
|
||||
/* A DDS_Topic is created for our sample type on the domain participant. */
|
||||
topic = dds_create_topic (participant, &RoundTripModule_DataType_desc, "RoundTrip", NULL, NULL);
|
||||
if (topic < 0)
|
||||
DDS_FATAL("dds_create_topic: %s\n", dds_strretcode(-topic));
|
||||
|
||||
/* A DDS_Publisher is created on the domain participant. */
|
||||
pubQos = dds_create_qos ();
|
||||
dds_qset_partition (pubQos, 1, pubPartitions);
|
||||
|
||||
publisher = dds_create_publisher (participant, pubQos, NULL);
|
||||
if (publisher < 0)
|
||||
DDS_FATAL("dds_create_publisher: %s\n", dds_strretcode(-publisher));
|
||||
dds_delete_qos (pubQos);
|
||||
|
||||
/* A DDS_DataWriter is created on the Publisher & Topic with a modified Qos. */
|
||||
dwQos = dds_create_qos ();
|
||||
dds_qset_reliability (dwQos, DDS_RELIABILITY_RELIABLE, DDS_SECS (10));
|
||||
dds_qset_writer_data_lifecycle (dwQos, false);
|
||||
*wr = dds_create_writer (publisher, topic, dwQos, NULL);
|
||||
if (*wr < 0)
|
||||
DDS_FATAL("dds_create_writer: %s\n", dds_strretcode(-*wr));
|
||||
dds_delete_qos (dwQos);
|
||||
|
||||
/* A DDS_Subscriber is created on the domain participant. */
|
||||
subQos = dds_create_qos ();
|
||||
|
||||
dds_qset_partition (subQos, 1, subPartitions);
|
||||
|
||||
subscriber = dds_create_subscriber (participant, subQos, NULL);
|
||||
if (subscriber < 0)
|
||||
DDS_FATAL("dds_create_subscriber: %s\n", dds_strretcode(-subscriber));
|
||||
dds_delete_qos (subQos);
|
||||
/* A DDS_DataReader is created on the Subscriber & Topic with a modified QoS. */
|
||||
drQos = dds_create_qos ();
|
||||
dds_qset_reliability (drQos, DDS_RELIABILITY_RELIABLE, DDS_SECS(10));
|
||||
*rd = dds_create_reader (subscriber, topic, drQos, listener);
|
||||
if (*rd < 0)
|
||||
DDS_FATAL("dds_create_reader: %s\n", dds_strretcode(-*rd));
|
||||
dds_delete_qos (drQos);
|
||||
|
||||
waitSet = dds_create_waitset (participant);
|
||||
if (listener == NULL) {
|
||||
*rdcond = dds_create_readcondition (*rd, DDS_ANY_STATE);
|
||||
status = dds_waitset_attach (waitSet, *rdcond, *rd);
|
||||
if (status < 0)
|
||||
DDS_FATAL("dds_waitset_attach: %s\n", dds_strretcode(-status));
|
||||
} else {
|
||||
*rdcond = 0;
|
||||
}
|
||||
status = dds_waitset_attach (waitSet, waitSet, waitSet);
|
||||
if (status < 0)
|
||||
DDS_FATAL("dds_waitset_attach: %s\n", dds_strretcode(-status));
|
||||
|
||||
return participant;
|
||||
}
|
||||
|
||||
static void finalize_dds(dds_entity_t ppant)
|
||||
{
|
||||
dds_return_t status;
|
||||
status = dds_delete (ppant);
|
||||
if (status < 0)
|
||||
DDS_FATAL("dds_delete: %s\n", dds_strretcode(-status));
|
||||
}
|
227
examples/roundtrip/pong.c
Normal file
227
examples/roundtrip/pong.c
Normal file
|
@ -0,0 +1,227 @@
|
|||
#include "dds/dds.h"
|
||||
#include "RoundTrip.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static dds_entity_t waitSet;
|
||||
#define MAX_SAMPLES 10
|
||||
|
||||
/* Forward declarations */
|
||||
static dds_entity_t prepare_dds(dds_entity_t *writer, dds_entity_t *reader, dds_entity_t *readCond, dds_listener_t *listener);
|
||||
static void finalize_dds(dds_entity_t participant, RoundTripModule_DataType data[MAX_SAMPLES]);
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
static bool CtrlHandler (DWORD fdwCtrlType)
|
||||
{
|
||||
dds_waitset_set_trigger (waitSet, true);
|
||||
return true; //Don't let other handlers handle this key
|
||||
}
|
||||
#elif !DDSRT_WITH_FREERTOS
|
||||
static void CtrlHandler (int sig)
|
||||
{
|
||||
(void)sig;
|
||||
dds_waitset_set_trigger (waitSet, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
static RoundTripModule_DataType data[MAX_SAMPLES];
|
||||
static void * samples[MAX_SAMPLES];
|
||||
static dds_sample_info_t info[MAX_SAMPLES];
|
||||
|
||||
static dds_entity_t participant;
|
||||
static dds_entity_t reader;
|
||||
static dds_entity_t writer;
|
||||
static dds_entity_t readCond;
|
||||
|
||||
static void data_available(dds_entity_t rd, void *arg)
|
||||
{
|
||||
int status, samplecount;
|
||||
(void)arg;
|
||||
samplecount = dds_take (rd, samples, info, MAX_SAMPLES, MAX_SAMPLES);
|
||||
if (samplecount < 0)
|
||||
DDS_FATAL("dds_take: %s\n", dds_strretcode(-samplecount));
|
||||
for (int j = 0; !dds_triggered (waitSet) && j < samplecount; j++)
|
||||
{
|
||||
/* If writer has been disposed terminate pong */
|
||||
|
||||
if (info[j].instance_state == DDS_IST_NOT_ALIVE_DISPOSED)
|
||||
{
|
||||
printf ("Received termination request. Terminating.\n");
|
||||
dds_waitset_set_trigger (waitSet, true);
|
||||
break;
|
||||
}
|
||||
else if (info[j].valid_data)
|
||||
{
|
||||
/* If sample is valid, send it back to ping */
|
||||
RoundTripModule_DataType * valid_sample = &data[j];
|
||||
status = dds_write_ts (writer, valid_sample, info[j].source_timestamp);
|
||||
if (status < 0)
|
||||
DDS_FATAL("dds_write_ts: %d\n", -status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
dds_duration_t waitTimeout = DDS_INFINITY;
|
||||
unsigned int i;
|
||||
int status;
|
||||
dds_attach_t wsresults[1];
|
||||
size_t wsresultsize = 1U;
|
||||
|
||||
dds_listener_t *listener = NULL;
|
||||
bool use_listener = false;
|
||||
int argidx = 1;
|
||||
|
||||
if (argc > argidx && strcmp(argv[argidx], "-l") == 0)
|
||||
{
|
||||
argidx++;
|
||||
use_listener = true;
|
||||
}
|
||||
|
||||
/* Register handler for Ctrl-C */
|
||||
|
||||
#ifdef _WIN32
|
||||
SetConsoleCtrlHandler ((PHANDLER_ROUTINE)CtrlHandler, TRUE);
|
||||
#elif !DDSRT_WITH_FREERTOS
|
||||
struct sigaction sat, oldAction;
|
||||
sat.sa_handler = CtrlHandler;
|
||||
sigemptyset (&sat.sa_mask);
|
||||
sat.sa_flags = 0;
|
||||
sigaction (SIGINT, &sat, &oldAction);
|
||||
#endif
|
||||
|
||||
/* Initialize sample data */
|
||||
memset (data, 0, sizeof (data));
|
||||
for (i = 0; i < MAX_SAMPLES; i++)
|
||||
{
|
||||
samples[i] = &data[i];
|
||||
}
|
||||
|
||||
participant = dds_create_participant (DDS_DOMAIN_DEFAULT, NULL, NULL);
|
||||
if (participant < 0)
|
||||
DDS_FATAL("dds_create_participant: %s\n", dds_strretcode(-participant));
|
||||
|
||||
if (use_listener)
|
||||
{
|
||||
listener = dds_create_listener(NULL);
|
||||
dds_lset_data_available(listener, data_available);
|
||||
}
|
||||
|
||||
(void)prepare_dds(&writer, &reader, &readCond, listener);
|
||||
|
||||
while (!dds_triggered (waitSet))
|
||||
{
|
||||
/* Wait for a sample from ping */
|
||||
|
||||
status = dds_waitset_wait (waitSet, wsresults, wsresultsize, waitTimeout);
|
||||
if (status < 0)
|
||||
DDS_FATAL("dds_waitset_wait: %s\n", dds_strretcode(-status));
|
||||
|
||||
/* Take samples */
|
||||
if (listener == NULL) {
|
||||
data_available (reader, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
SetConsoleCtrlHandler (0, FALSE);
|
||||
#elif !DDSRT_WITH_FREERTOS
|
||||
sigaction (SIGINT, &oldAction, 0);
|
||||
#endif
|
||||
|
||||
/* Clean up */
|
||||
finalize_dds(participant, data);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static void finalize_dds(dds_entity_t pp, RoundTripModule_DataType xs[MAX_SAMPLES])
|
||||
{
|
||||
dds_return_t status;
|
||||
status = dds_delete (pp);
|
||||
if (status < 0)
|
||||
DDS_FATAL("dds_delete: %s\n", dds_strretcode(-status));
|
||||
for (unsigned int i = 0; i < MAX_SAMPLES; i++)
|
||||
{
|
||||
RoundTripModule_DataType_free (&xs[i], DDS_FREE_CONTENTS);
|
||||
}
|
||||
}
|
||||
|
||||
static dds_entity_t prepare_dds(dds_entity_t *wr, dds_entity_t *rd, dds_entity_t *rdcond, dds_listener_t *rdlist)
|
||||
{
|
||||
const char *pubPartitions[] = { "pong" };
|
||||
const char *subPartitions[] = { "ping" };
|
||||
dds_qos_t *qos;
|
||||
dds_entity_t subscriber;
|
||||
dds_entity_t publisher;
|
||||
dds_entity_t topic;
|
||||
dds_return_t status;
|
||||
|
||||
/* A DDS Topic is created for our sample type on the domain participant. */
|
||||
|
||||
topic = dds_create_topic (participant, &RoundTripModule_DataType_desc, "RoundTrip", NULL, NULL);
|
||||
if (topic < 0)
|
||||
DDS_FATAL("dds_create_topic: %s\n", dds_strretcode(-topic));
|
||||
|
||||
/* A DDS Publisher is created on the domain participant. */
|
||||
|
||||
qos = dds_create_qos ();
|
||||
dds_qset_partition (qos, 1, pubPartitions);
|
||||
|
||||
publisher = dds_create_publisher (participant, qos, NULL);
|
||||
if (publisher < 0)
|
||||
DDS_FATAL("dds_create_publisher: %s\n", dds_strretcode(-publisher));
|
||||
dds_delete_qos (qos);
|
||||
|
||||
/* A DDS DataWriter is created on the Publisher & Topic with a modififed Qos. */
|
||||
|
||||
qos = dds_create_qos ();
|
||||
dds_qset_reliability (qos, DDS_RELIABILITY_RELIABLE, DDS_SECS(10));
|
||||
dds_qset_writer_data_lifecycle (qos, false);
|
||||
*wr = dds_create_writer (publisher, topic, qos, NULL);
|
||||
if (*wr < 0)
|
||||
DDS_FATAL("dds_create_writer: %s\n", dds_strretcode(-*wr));
|
||||
dds_delete_qos (qos);
|
||||
|
||||
/* A DDS Subscriber is created on the domain participant. */
|
||||
|
||||
qos = dds_create_qos ();
|
||||
dds_qset_partition (qos, 1, subPartitions);
|
||||
|
||||
subscriber = dds_create_subscriber (participant, qos, NULL);
|
||||
if (subscriber < 0)
|
||||
DDS_FATAL("dds_create_subscriber: %s\n", dds_strretcode(-subscriber));
|
||||
dds_delete_qos (qos);
|
||||
|
||||
/* A DDS DataReader is created on the Subscriber & Topic with a modified QoS. */
|
||||
|
||||
qos = dds_create_qos ();
|
||||
dds_qset_reliability (qos, DDS_RELIABILITY_RELIABLE, DDS_SECS(10));
|
||||
*rd = dds_create_reader (subscriber, topic, qos, rdlist);
|
||||
if (*rd < 0)
|
||||
DDS_FATAL("dds_create_reader: %s\n", dds_strretcode(-*rd));
|
||||
dds_delete_qos (qos);
|
||||
|
||||
waitSet = dds_create_waitset (participant);
|
||||
if (rdlist == NULL) {
|
||||
*rdcond = dds_create_readcondition (*rd, DDS_ANY_STATE);
|
||||
status = dds_waitset_attach (waitSet, *rdcond, *rd);
|
||||
if (status < 0)
|
||||
DDS_FATAL("dds_waitset_attach: %s\n", dds_strretcode(-status));
|
||||
} else {
|
||||
*rdcond = 0;
|
||||
}
|
||||
status = dds_waitset_attach (waitSet, waitSet, waitSet);
|
||||
if (status < 0)
|
||||
DDS_FATAL("dds_waitset_attach: %s\n", dds_strretcode(-status));
|
||||
|
||||
printf ("Waiting for samples from ping to send back...\n");
|
||||
fflush (stdout);
|
||||
|
||||
return participant;
|
||||
}
|
83
examples/roundtrip/readme.rst
Normal file
83
examples/roundtrip/readme.rst
Normal file
|
@ -0,0 +1,83 @@
|
|||
..
|
||||
Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
|
||||
|
||||
This program and the accompanying materials are made available under the
|
||||
terms of the Eclipse Public License v. 2.0 which is available at
|
||||
http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
v. 1.0 which is available at
|
||||
http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
|
||||
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
|
||||
Roundtrip
|
||||
==========
|
||||
|
||||
Description
|
||||
***********
|
||||
|
||||
The Roundtrip example allows the measurement of roundtrip duration when sending and receiving back a single message.
|
||||
|
||||
Design
|
||||
******
|
||||
|
||||
It consists of 2 units:
|
||||
|
||||
- Pong: waits for messages from ping and sends the same message back.
|
||||
- Ping: Sends a message to pong and waits for its return.
|
||||
|
||||
Scenario
|
||||
********
|
||||
|
||||
A message is sent by the **ping** executable on the "PING" partition, which the **pong** executable is waiting for.
|
||||
The **pong** executable sends the same message back on the "PONG" partition, which the **ping** executable is waiting for.
|
||||
This sequence is repeated a configurable number of times.
|
||||
|
||||
The **ping** executable measures:
|
||||
|
||||
- writeAccess time: time the write() method took.
|
||||
- readAccess time: time the take() method took.
|
||||
- roundTrip time: time between the call to the write() method and the return of the take() method.
|
||||
- **ping** also calculates min/max/average statistics on these values over a configurable number of samples and/or time out period.
|
||||
|
||||
Configurable:
|
||||
|
||||
- payloadSize: the size of the payload in bytes.
|
||||
- numSamples: the number of samples to send.
|
||||
- timeOut: the number of seconds ping should run for.
|
||||
|
||||
|
||||
Running the example
|
||||
*******************
|
||||
|
||||
It is recommended that you run ping and pong in separate terminals to avoid mixing the output.
|
||||
|
||||
- Open 2 terminals.
|
||||
- In the first terminal start Pong by running pong.
|
||||
|
||||
pong usage:
|
||||
``./pong``
|
||||
|
||||
- In the second terminal start Ping by running ping.
|
||||
|
||||
ping usage (parameters must be supplied in order):
|
||||
``./ping [payloadSize (bytes, 0 - 655536)] [numSamples (0 = infinite)] [timeOut (seconds, 0 = infinite)]``
|
||||
|
||||
``./ping quit - ping sends a quit signal to pong.``
|
||||
defaults:
|
||||
``./ping 0 0 0``
|
||||
|
||||
- To achieve optimal performance it is recommended to set the CPU affinity so that ping and pong run on separate CPU cores,
|
||||
and use real-time scheduling. In a Linux environment this can be achieved as follows:
|
||||
|
||||
pong usage:
|
||||
``taskset -c 0 chrt -f 80 ./pong``
|
||||
ping usage:
|
||||
``taskset -c 1 chrt -f 80 ./ping [payloadSize (bytes, 0 - 655536)] [numSamples (0 = infinite)] [timeOut (seconds, 0 = infinite)]``
|
||||
|
||||
On Windows the CPU affinity and scheduling class can be set as follows:
|
||||
|
||||
pong usage:
|
||||
``START /affinity 1 /high cmd /k "pong.exe"``
|
||||
ping usage:
|
||||
``START /affinity 2 /high cmd /k "ping.exe" [payloadSize (bytes, 0 - 655536)] [numSamples (0 = infinite)] [timeOut (seconds, 0 = infinite)]``
|
||||
|
86
examples/sphinx-conf.py.in
Normal file
86
examples/sphinx-conf.py.in
Normal file
|
@ -0,0 +1,86 @@
|
|||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = ['sphinx.ext.autodoc',
|
||||
'sphinx.ext.intersphinx',
|
||||
'sphinx.ext.coverage',
|
||||
'sphinx.ext.imgmath',
|
||||
'sphinx.ext.ifconfig',
|
||||
'breathe']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
#templates_path = ['@CMAKE_CURRENT_SOuRCE_DIR@/_templates']
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'examples'
|
||||
|
||||
# General information about the project.
|
||||
project = u'@CMAKE_PROJECT_NAME@'
|
||||
copyright = u'@sph_conf_copyright@'
|
||||
author = u'@sph_conf_author@'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y.Z version.
|
||||
version = u'@sph_conf_version@'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = u'@sph_conf_release@'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This patterns also effect to html_static_path and html_extra_path
|
||||
exclude_patterns = []
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'alabaster'
|
||||
|
||||
html_show_sourcelink = False
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
html_theme_options = { 'show_powered_by': False }
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['@PROJECT_SOURCE_DIR@/docs/manual/_static']
|
||||
|
||||
# Custom sidebar templates, must be a dictionary that maps document names
|
||||
# to template names.
|
||||
#
|
||||
# This is required for the alabaster theme
|
||||
# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
|
||||
html_sidebars = {
|
||||
'**': [
|
||||
'about.html',
|
||||
'navigation.html',
|
||||
'relations.html', # needs 'show_related': True theme option to display
|
||||
'searchbox.html',
|
||||
'donate.html',
|
||||
]
|
||||
}
|
33
examples/throughput/CMakeLists.txt
Normal file
33
examples/throughput/CMakeLists.txt
Normal file
|
@ -0,0 +1,33 @@
|
|||
#
|
||||
# Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
|
||||
#
|
||||
# This program and the accompanying materials are made available under the
|
||||
# terms of the Eclipse Public License v. 2.0 which is available at
|
||||
# http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
# v. 1.0 which is available at
|
||||
# http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
#
|
||||
# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
#
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
if (NOT TARGET CycloneDDS::ddsc)
|
||||
# Find the CycloneDDS package. If it is not in a default location, try
|
||||
# finding it relative to the example where it most likely resides.
|
||||
find_package(CycloneDDS REQUIRED PATHS "${CMAKE_SOURCE_DIR}/../../")
|
||||
endif()
|
||||
|
||||
# This is a convenience function, provided by the CycloneDDS package,
|
||||
# that will supply a library target related the the given idl file.
|
||||
# In short, it takes the idl file, generates the source files with
|
||||
# the proper data types and compiles them into a library.
|
||||
idlc_generate(Throughput_lib Throughput.idl)
|
||||
|
||||
# Both executables have only one related source file.
|
||||
add_executable(ThroughputPublisher publisher.c)
|
||||
add_executable(ThroughputSubscriber subscriber.c)
|
||||
|
||||
# Both executables need to be linked to the idl data type library and
|
||||
# the ddsc API library.
|
||||
target_link_libraries(ThroughputPublisher Throughput_lib CycloneDDS::ddsc)
|
||||
target_link_libraries(ThroughputSubscriber Throughput_lib CycloneDDS::ddsc)
|
9
examples/throughput/Throughput.idl
Normal file
9
examples/throughput/Throughput.idl
Normal file
|
@ -0,0 +1,9 @@
|
|||
module ThroughputModule
|
||||
{
|
||||
struct DataType
|
||||
{
|
||||
unsigned long long count;
|
||||
sequence<octet> payload;
|
||||
};
|
||||
#pragma keylist DataType
|
||||
};
|
295
examples/throughput/publisher.c
Normal file
295
examples/throughput/publisher.c
Normal file
|
@ -0,0 +1,295 @@
|
|||
#include "dds/dds.h"
|
||||
#include "Throughput.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
/*
|
||||
* The Throughput example measures data throughput in bytes per second. The publisher
|
||||
* allows you to specify a payload size in bytes as well as allowing you to specify
|
||||
* whether to send data in bursts. The publisher will continue to send data forever
|
||||
* unless a time out is specified. The subscriber will receive data and output the
|
||||
* total amount received and the data rate in bytes per second. It will also indicate
|
||||
* if any samples were received out of order. A maximum number of cycles can be
|
||||
* specified and once this has been reached the subscriber will terminate and output
|
||||
* totals and averages.
|
||||
*/
|
||||
|
||||
#define MAX_SAMPLES 100
|
||||
|
||||
static bool done = false;
|
||||
|
||||
/* Forward declarations */
|
||||
static dds_return_t wait_for_reader(dds_entity_t writer, dds_entity_t participant);
|
||||
static void start_writing(dds_entity_t writer, ThroughputModule_DataType *sample,
|
||||
int burstInterval, uint32_t burstSize, int timeOut);
|
||||
static int parse_args(int argc, char **argv, uint32_t *payloadSize, int *burstInterval,
|
||||
uint32_t *burstSize, int *timeOut, char **partitionName);
|
||||
static dds_entity_t prepare_dds(dds_entity_t *writer, const char *partitionName);
|
||||
static void finalize_dds(dds_entity_t participant, dds_entity_t writer, ThroughputModule_DataType sample);
|
||||
|
||||
static void sigint (int sig)
|
||||
{
|
||||
(void)sig;
|
||||
done = true;
|
||||
}
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
uint32_t payloadSize = 8192;
|
||||
int burstInterval = 0;
|
||||
uint32_t burstSize = 1;
|
||||
int timeOut = 0;
|
||||
char * partitionName = "Throughput example";
|
||||
dds_entity_t participant;
|
||||
dds_entity_t writer;
|
||||
dds_return_t rc;
|
||||
ThroughputModule_DataType sample;
|
||||
|
||||
if (parse_args(argc, argv, &payloadSize, &burstInterval, &burstSize, &timeOut, &partitionName) == EXIT_FAILURE) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
participant = prepare_dds(&writer, partitionName);
|
||||
|
||||
/* Wait until have a reader */
|
||||
if (wait_for_reader(writer, participant) == 0) {
|
||||
printf ("=== [Publisher] Did not discover a reader.\n");
|
||||
fflush (stdout);
|
||||
rc = dds_delete (participant);
|
||||
if (rc < 0)
|
||||
DDS_FATAL("dds_delete: %s\n", dds_strretcode(-rc));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Fill the sample payload with data */
|
||||
sample.count = 0;
|
||||
sample.payload._buffer = dds_alloc (payloadSize);
|
||||
sample.payload._length = payloadSize;
|
||||
sample.payload._release = true;
|
||||
for (uint32_t i = 0; i < payloadSize; i++) {
|
||||
sample.payload._buffer[i] = 'a';
|
||||
}
|
||||
|
||||
/* Register handler for Ctrl-C */
|
||||
signal (SIGINT, sigint);
|
||||
|
||||
/* Register the sample instance and write samples repeatedly or until time out */
|
||||
start_writing(writer, &sample, burstInterval, burstSize, timeOut);
|
||||
|
||||
/* Cleanup */
|
||||
finalize_dds(participant, writer, sample);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static int parse_args(
|
||||
int argc,
|
||||
char **argv,
|
||||
uint32_t *payloadSize,
|
||||
int *burstInterval,
|
||||
uint32_t *burstSize,
|
||||
int *timeOut,
|
||||
char **partitionName)
|
||||
{
|
||||
int result = EXIT_SUCCESS;
|
||||
/*
|
||||
* Get the program parameters
|
||||
* Parameters: publisher [payloadSize] [burstInterval] [burstSize] [timeOut] [partitionName]
|
||||
*/
|
||||
if (argc == 2 && (strcmp (argv[1], "-h") == 0 || strcmp (argv[1], "--help") == 0))
|
||||
{
|
||||
printf ("Usage (parameters must be supplied in order):\n");
|
||||
printf ("./publisher [payloadSize (bytes)] [burstInterval (ms)] [burstSize (samples)] [timeOut (seconds)] [partitionName]\n");
|
||||
printf ("Defaults:\n");
|
||||
printf ("./publisher 8192 0 1 0 \"Throughput example\"\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (argc > 1)
|
||||
{
|
||||
*payloadSize = (uint32_t) atoi (argv[1]); /* The size of the payload in bytes */
|
||||
}
|
||||
if (argc > 2)
|
||||
{
|
||||
*burstInterval = atoi (argv[2]); /* The time interval between each burst in ms */
|
||||
}
|
||||
if (argc > 3)
|
||||
{
|
||||
*burstSize = (uint32_t) atoi (argv[3]); /* The number of samples to send each burst */
|
||||
}
|
||||
if (argc > 4)
|
||||
{
|
||||
*timeOut = atoi (argv[4]); /* The number of seconds the publisher should run for (0 = infinite) */
|
||||
}
|
||||
if (argc > 5)
|
||||
{
|
||||
*partitionName = argv[5]; /* The name of the partition */
|
||||
}
|
||||
|
||||
printf ("payloadSize: %"PRIu32" bytes burstInterval: %u ms burstSize: %"PRId32" timeOut: %u seconds partitionName: %s\n",
|
||||
*payloadSize, *burstInterval, *burstSize, *timeOut, *partitionName);
|
||||
fflush (stdout);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static dds_entity_t prepare_dds(dds_entity_t *writer, const char *partitionName)
|
||||
{
|
||||
dds_entity_t participant;
|
||||
dds_entity_t topic;
|
||||
dds_entity_t publisher;
|
||||
const char *pubParts[1];
|
||||
dds_qos_t *pubQos;
|
||||
dds_qos_t *dwQos;
|
||||
|
||||
/* A domain participant is created for the default domain. */
|
||||
participant = dds_create_participant (DDS_DOMAIN_DEFAULT, NULL, NULL);
|
||||
if (participant < 0)
|
||||
DDS_FATAL("dds_create_participant: %s\n", dds_strretcode(-participant));
|
||||
|
||||
/* A topic is created for our sample type on the domain participant. */
|
||||
topic = dds_create_topic (participant, &ThroughputModule_DataType_desc, "Throughput", NULL, NULL);
|
||||
if (topic < 0)
|
||||
DDS_FATAL("dds_create_topic: %s\n", dds_strretcode(-topic));
|
||||
|
||||
/* A publisher is created on the domain participant. */
|
||||
pubQos = dds_create_qos ();
|
||||
pubParts[0] = partitionName;
|
||||
dds_qset_partition (pubQos, 1, pubParts);
|
||||
publisher = dds_create_publisher (participant, pubQos, NULL);
|
||||
if (publisher < 0)
|
||||
DDS_FATAL("dds_create_publisher: %s\n", dds_strretcode(-publisher));
|
||||
dds_delete_qos (pubQos);
|
||||
|
||||
/* A DataWriter is created on the publisher. */
|
||||
dwQos = dds_create_qos ();
|
||||
dds_qset_reliability (dwQos, DDS_RELIABILITY_RELIABLE, DDS_SECS (10));
|
||||
dds_qset_history (dwQos, DDS_HISTORY_KEEP_ALL, 0);
|
||||
dds_qset_resource_limits (dwQos, MAX_SAMPLES, DDS_LENGTH_UNLIMITED, DDS_LENGTH_UNLIMITED);
|
||||
*writer = dds_create_writer (publisher, topic, dwQos, NULL);
|
||||
if (*writer < 0)
|
||||
DDS_FATAL("dds_create_writer: %s\n", dds_strretcode(-*writer));
|
||||
dds_delete_qos (dwQos);
|
||||
|
||||
/* Enable write batching */
|
||||
dds_write_set_batch (true);
|
||||
|
||||
return participant;
|
||||
}
|
||||
|
||||
static dds_return_t wait_for_reader(dds_entity_t writer, dds_entity_t participant)
|
||||
{
|
||||
printf ("\n=== [Publisher] Waiting for a reader ...\n");
|
||||
fflush (stdout);
|
||||
|
||||
dds_return_t rc;
|
||||
dds_entity_t waitset;
|
||||
|
||||
rc = dds_set_status_mask(writer, DDS_PUBLICATION_MATCHED_STATUS);
|
||||
if (rc < 0)
|
||||
DDS_FATAL("dds_set_status_mask: %s\n", dds_strretcode(-rc));
|
||||
|
||||
waitset = dds_create_waitset(participant);
|
||||
if (waitset < 0)
|
||||
DDS_FATAL("dds_create_waitset: %s\n", dds_strretcode(-waitset));
|
||||
|
||||
rc = dds_waitset_attach(waitset, writer, (dds_attach_t)NULL);
|
||||
if (rc < 0)
|
||||
DDS_FATAL("dds_waitset_attach: %s\n", dds_strretcode(-rc));
|
||||
|
||||
rc = dds_waitset_wait(waitset, NULL, 0, DDS_SECS(30));
|
||||
if (rc < 0)
|
||||
DDS_FATAL("dds_waitset_wait: %s\n", dds_strretcode(-rc));
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void start_writing(
|
||||
dds_entity_t writer,
|
||||
ThroughputModule_DataType *sample,
|
||||
int burstInterval,
|
||||
uint32_t burstSize,
|
||||
int timeOut)
|
||||
{
|
||||
bool timedOut = false;
|
||||
dds_time_t pubStart = dds_time ();
|
||||
dds_time_t now;
|
||||
dds_time_t deltaTv;
|
||||
dds_return_t status;
|
||||
|
||||
if (!done)
|
||||
{
|
||||
dds_time_t burstStart = pubStart;
|
||||
unsigned int burstCount = 0;
|
||||
|
||||
printf ("=== [Publisher] Writing samples...\n");
|
||||
fflush (stdout);
|
||||
|
||||
while (!done && !timedOut)
|
||||
{
|
||||
/* Write data until burst size has been reached */
|
||||
|
||||
if (burstCount < burstSize)
|
||||
{
|
||||
status = dds_write (writer, sample);
|
||||
if (status == DDS_RETCODE_TIMEOUT)
|
||||
{
|
||||
timedOut = true;
|
||||
}
|
||||
else if (status < 0)
|
||||
{
|
||||
DDS_FATAL("dds_write: %s\n", dds_strretcode(-status));
|
||||
}
|
||||
else
|
||||
{
|
||||
sample->count++;
|
||||
burstCount++;
|
||||
}
|
||||
}
|
||||
else if (burstInterval)
|
||||
{
|
||||
/* Sleep until burst interval has passed */
|
||||
|
||||
dds_time_t time = dds_time ();
|
||||
deltaTv = time - burstStart;
|
||||
if (deltaTv < DDS_MSECS (burstInterval))
|
||||
{
|
||||
dds_write_flush (writer);
|
||||
dds_sleepfor (DDS_MSECS (burstInterval) - deltaTv);
|
||||
}
|
||||
burstStart = dds_time ();
|
||||
burstCount = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
burstCount = 0;
|
||||
}
|
||||
|
||||
if (timeOut)
|
||||
{
|
||||
now = dds_time ();
|
||||
deltaTv = now - pubStart;
|
||||
if ((deltaTv) > DDS_SECS (timeOut))
|
||||
{
|
||||
timedOut = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
dds_write_flush (writer);
|
||||
|
||||
printf ("=== [Publisher] %s, %llu samples written.\n", done ? "Terminated" : "Timed out", (unsigned long long) sample->count);
|
||||
fflush (stdout);
|
||||
}
|
||||
}
|
||||
|
||||
static void finalize_dds(dds_entity_t participant, dds_entity_t writer, ThroughputModule_DataType sample)
|
||||
{
|
||||
dds_return_t status = dds_dispose (writer, &sample);
|
||||
if (status != DDS_RETCODE_TIMEOUT && status < 0)
|
||||
DDS_FATAL("dds_dispose: %s\n", dds_strretcode(-status));
|
||||
|
||||
dds_free (sample.payload._buffer);
|
||||
status = dds_delete (participant);
|
||||
if (status < 0)
|
||||
DDS_FATAL("dds_delete: %s\n", dds_strretcode(-status));
|
||||
}
|
99
examples/throughput/readme.rst
Normal file
99
examples/throughput/readme.rst
Normal file
|
@ -0,0 +1,99 @@
|
|||
..
|
||||
Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
|
||||
|
||||
This program and the accompanying materials are made available under the
|
||||
terms of the Eclipse Public License v. 2.0 which is available at
|
||||
http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
v. 1.0 which is available at
|
||||
http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
|
||||
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
|
||||
Throughput
|
||||
==========
|
||||
|
||||
Description
|
||||
***********
|
||||
|
||||
The Throughput example allows the measurement of data throughput when receiving samples from a publisher.
|
||||
|
||||
|
||||
Design
|
||||
******
|
||||
|
||||
It consists of 2 units:
|
||||
|
||||
- Publisher: sends samples at a specified size and rate.
|
||||
- Subscriber: Receives samples and outputs statistics about throughput
|
||||
|
||||
Scenario
|
||||
********
|
||||
|
||||
The **publisher** sends samples and allows you to specify a payload size in bytes as well as allowing you to specify whether
|
||||
to send data in bursts. The **publisher** will continue to send data forever unless a time-out is specified.
|
||||
|
||||
Configurable:
|
||||
|
||||
- payloadSize: the size of the payload in bytes
|
||||
- burstInterval: the time interval between each burst in ms
|
||||
- burstSize: the number of samples to send each burst
|
||||
- timeOut: the number of seconds the publisher should run for (0=infinite)
|
||||
- partitionName: the name of the partition
|
||||
|
||||
The **subscriber** will receive data and output the total amount received and the data-rate in bytes-per-second. It will
|
||||
also indicate if any samples were received out-of-order. A maximum number of cycles can be specified and once this has
|
||||
been reached the subscriber will terminate and output totals and averages.
|
||||
|
||||
The **subscriber** executable measures:
|
||||
|
||||
- transferred: the total amount of data transferred in bytes.
|
||||
- outOfOrder: the number of samples that were received out of order.
|
||||
- transfer rate: the data transfer rate in bytes per second.
|
||||
- subscriber also calculates statistics on these values over a configurable number of cycles.
|
||||
|
||||
Configurable:
|
||||
|
||||
- maxCycles: the number of times to output statistics before terminating
|
||||
- pollingDelay
|
||||
- partitionName: the name of the partition
|
||||
|
||||
|
||||
Running the example
|
||||
*******************
|
||||
|
||||
It is recommended that you run ping and pong in separate terminals to avoid mixing the output.
|
||||
|
||||
- Open 2 terminals.
|
||||
- In the first terminal start Publisher by running publisher
|
||||
|
||||
publisher usage (parameters must be supplied in order):
|
||||
``./publisher [payloadSize (bytes)] [burstInterval (ms)] [burstSize (samples)] [timeOut (seconds)] [partitionName]``
|
||||
defaults:
|
||||
``./publisher 8192 0 1 0 "Throughput example"``
|
||||
|
||||
- In the second terminal start Ping by running subscriber
|
||||
|
||||
subscriber usage (parameters must be supplied in order):
|
||||
``./subscriber [maxCycles (0=infinite)] [pollingDelay (ms, 0 = event based)] [partitionName]``
|
||||
defaults:
|
||||
``./subscriber 0 0 "Throughput example"``
|
||||
|
||||
- To achieve optimal performance it is recommended to set the CPU affinity so that ping and pong run on separate CPU cores,
|
||||
and use real-time scheduling. In a Linux environment this can be achieved as follows:
|
||||
|
||||
publisher usage:
|
||||
``taskset -c 0 chrt -f 80 ./publisher [payloadSize (bytes)] [burstInterval (ms)] [burstSize (samples)] [timeOut (seconds)] [partitionName]``
|
||||
subscriber usage:
|
||||
``taskset -c 1 chrt -f 80 ./subscriber [maxCycles (0 = infinite)] [pollingDelay (ms, 0 = event based)] [partitionName]``
|
||||
|
||||
On Windows the CPU affinity and prioritized scheduling class can be set as follows:
|
||||
|
||||
publisher usage:
|
||||
``START /affinity 1 /high cmd /k "publisher.exe" [payloadSize (bytes)] [burstInterval (ms)] [burstSize (samples)] [timeOut (seconds)] [partitionName]``
|
||||
subscriber usage:
|
||||
``START /affinity 2 /high cmd /k "subscriber.exe" [maxCycles (0 = infinite)] [pollingDelay (ms, 0 = event based)] [partitionName]``
|
||||
|
||||
|
||||
|
||||
|
||||
|
400
examples/throughput/subscriber.c
Normal file
400
examples/throughput/subscriber.c
Normal file
|
@ -0,0 +1,400 @@
|
|||
#include "dds/dds.h"
|
||||
#include "Throughput.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <assert.h>
|
||||
/*
|
||||
* The Throughput example measures data throughput in bytes per second. The publisher
|
||||
* allows you to specify a payload size in bytes as well as allowing you to specify
|
||||
* whether to send data in bursts. The publisher will continue to send data forever
|
||||
* unless a time out is specified. The subscriber will receive data and output the
|
||||
* total amount received and the data rate in bytes per second. It will also indicate
|
||||
* if any samples were received out of order. A maximum number of cycles can be
|
||||
* specified and once this has been reached the subscriber will terminate and output
|
||||
* totals and averages.
|
||||
*/
|
||||
|
||||
#define BYTES_PER_SEC_TO_MEGABITS_PER_SEC 125000
|
||||
#define MAX_SAMPLES 1000
|
||||
|
||||
typedef struct HandleEntry
|
||||
{
|
||||
dds_instance_handle_t handle;
|
||||
unsigned long long count;
|
||||
struct HandleEntry * next;
|
||||
} HandleEntry;
|
||||
|
||||
typedef struct HandleMap
|
||||
{
|
||||
HandleEntry *entries;
|
||||
} HandleMap;
|
||||
|
||||
static long pollingDelay = -1; /* i.e. use a listener */
|
||||
|
||||
static HandleMap * imap;
|
||||
static unsigned long long outOfOrder = 0;
|
||||
static unsigned long long total_bytes = 0;
|
||||
static unsigned long long total_samples = 0;
|
||||
|
||||
static dds_time_t startTime = 0;
|
||||
|
||||
static unsigned long payloadSize = 0;
|
||||
|
||||
static ThroughputModule_DataType data [MAX_SAMPLES];
|
||||
static void * samples[MAX_SAMPLES];
|
||||
|
||||
static dds_entity_t waitSet;
|
||||
|
||||
static volatile sig_atomic_t done = false;
|
||||
|
||||
/* Forward declarations */
|
||||
static HandleMap * HandleMap__alloc (void);
|
||||
static void HandleMap__free (HandleMap *map);
|
||||
static HandleEntry * store_handle (HandleMap *map, dds_instance_handle_t key);
|
||||
static HandleEntry * retrieve_handle (HandleMap *map, dds_instance_handle_t key);
|
||||
|
||||
static void data_available_handler (dds_entity_t reader, void *arg);
|
||||
static int parse_args(int argc, char **argv, unsigned long long *maxCycles, char **partitionName);
|
||||
static void process_samples(dds_entity_t reader, unsigned long long maxCycles);
|
||||
static dds_entity_t prepare_dds(dds_entity_t *reader, const char *partitionName);
|
||||
static void finalize_dds(dds_entity_t participant);
|
||||
|
||||
static void sigint (int sig)
|
||||
{
|
||||
(void) sig;
|
||||
done = true;
|
||||
}
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
unsigned long long maxCycles = 0;
|
||||
char *partitionName = "Throughput example";
|
||||
|
||||
dds_entity_t participant;
|
||||
dds_entity_t reader;
|
||||
|
||||
if (parse_args(argc, argv, &maxCycles, &partitionName) == EXIT_FAILURE)
|
||||
{
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf ("Cycles: %llu | PollingDelay: %ld | Partition: %s\n", maxCycles, pollingDelay, partitionName);
|
||||
fflush (stdout);
|
||||
|
||||
participant = prepare_dds(&reader, partitionName);
|
||||
|
||||
printf ("=== [Subscriber] Waiting for samples...\n");
|
||||
fflush (stdout);
|
||||
|
||||
/* Process samples until Ctrl-C is pressed or until maxCycles */
|
||||
/* has been reached (0 = infinite) */
|
||||
signal (SIGINT, sigint);
|
||||
process_samples(reader, maxCycles);
|
||||
|
||||
dds_set_status_mask (reader, 0);
|
||||
HandleMap__free (imap);
|
||||
finalize_dds (participant);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* This struct contains all of the entities used in the publisher and subscriber.
|
||||
*/
|
||||
static HandleMap * HandleMap__alloc (void)
|
||||
{
|
||||
HandleMap * map = malloc (sizeof (*map));
|
||||
assert(map);
|
||||
memset (map, 0, sizeof (*map));
|
||||
return map;
|
||||
}
|
||||
|
||||
static void HandleMap__free (HandleMap *map)
|
||||
{
|
||||
HandleEntry * entry;
|
||||
|
||||
while (map->entries)
|
||||
{
|
||||
entry = map->entries;
|
||||
map->entries = entry->next;
|
||||
free (entry);
|
||||
}
|
||||
free (map);
|
||||
}
|
||||
|
||||
static HandleEntry * store_handle (HandleMap *map, dds_instance_handle_t key)
|
||||
{
|
||||
HandleEntry * entry = malloc (sizeof (*entry));
|
||||
assert(entry);
|
||||
memset (entry, 0, sizeof (*entry));
|
||||
|
||||
entry->handle = key;
|
||||
entry->next = map->entries;
|
||||
map->entries = entry;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static HandleEntry * retrieve_handle (HandleMap *map, dds_instance_handle_t key)
|
||||
{
|
||||
HandleEntry * entry = map->entries;
|
||||
|
||||
while (entry)
|
||||
{
|
||||
if (entry->handle == key)
|
||||
{
|
||||
break;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
static int do_take (dds_entity_t reader)
|
||||
{
|
||||
int samples_received;
|
||||
dds_sample_info_t info [MAX_SAMPLES];
|
||||
dds_instance_handle_t ph = 0;
|
||||
HandleEntry * current = NULL;
|
||||
|
||||
if (startTime == 0)
|
||||
{
|
||||
startTime = dds_time ();
|
||||
}
|
||||
|
||||
/* Take samples and iterate through them */
|
||||
|
||||
samples_received = dds_take (reader, samples, info, MAX_SAMPLES, MAX_SAMPLES);
|
||||
if (samples_received < 0)
|
||||
DDS_FATAL("dds_take: %s\n", dds_strretcode(-samples_received));
|
||||
|
||||
for (int i = 0; !done && i < samples_received; i++)
|
||||
{
|
||||
if (info[i].valid_data)
|
||||
{
|
||||
ph = info[i].publication_handle;
|
||||
current = retrieve_handle (imap, ph);
|
||||
ThroughputModule_DataType * this_sample = &data[i];
|
||||
|
||||
if (current == NULL)
|
||||
{
|
||||
current = store_handle (imap, ph);
|
||||
current->count = this_sample->count;
|
||||
}
|
||||
|
||||
if (this_sample->count != current->count)
|
||||
{
|
||||
outOfOrder++;
|
||||
}
|
||||
current->count = this_sample->count + 1;
|
||||
|
||||
/* Add the sample payload size to the total received */
|
||||
|
||||
payloadSize = this_sample->payload._length;
|
||||
total_bytes += payloadSize + 8;
|
||||
total_samples++;
|
||||
}
|
||||
}
|
||||
return samples_received;
|
||||
}
|
||||
|
||||
static void data_available_handler (dds_entity_t reader, void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
do_take (reader);
|
||||
}
|
||||
|
||||
static int parse_args(int argc, char **argv, unsigned long long *maxCycles, char **partitionName)
|
||||
{
|
||||
/*
|
||||
* Get the program parameters
|
||||
* Parameters: subscriber [maxCycles] [pollingDelay] [partitionName]
|
||||
*/
|
||||
if (argc == 2 && (strcmp (argv[1], "-h") == 0 || strcmp (argv[1], "--help") == 0))
|
||||
{
|
||||
printf ("Usage (parameters must be supplied in order):\n");
|
||||
printf ("./subscriber [maxCycles (0 = infinite)] [pollingDelay (ms, 0 = waitset, -1 = listener)] [partitionName]\n");
|
||||
printf ("Defaults:\n");
|
||||
printf ("./subscriber 0 0 \"Throughput example\"\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
*maxCycles = (unsigned long long) atoi (argv[1]); /* The number of times to output statistics before terminating */
|
||||
}
|
||||
if (argc > 2)
|
||||
{
|
||||
pollingDelay = atoi (argv[2]); /* The number of ms to wait between reads (0 = waitset, -1 = listener) */
|
||||
}
|
||||
if (argc > 3)
|
||||
{
|
||||
*partitionName = argv[3]; /* The name of the partition */
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static void process_samples(dds_entity_t reader, unsigned long long maxCycles)
|
||||
{
|
||||
dds_return_t status;
|
||||
unsigned long long prev_bytes = 0;
|
||||
unsigned long long prev_samples = 0;
|
||||
dds_attach_t wsresults[2];
|
||||
dds_time_t deltaTv;
|
||||
bool first_batch = true;
|
||||
unsigned long cycles = 0;
|
||||
double deltaTime = 0;
|
||||
dds_time_t prev_time = 0;
|
||||
dds_time_t time_now = 0;
|
||||
|
||||
while (!done && (maxCycles == 0 || cycles < maxCycles))
|
||||
{
|
||||
if (pollingDelay > 0)
|
||||
dds_sleepfor (DDS_MSECS (pollingDelay));
|
||||
else
|
||||
{
|
||||
status = dds_waitset_wait (waitSet, wsresults, sizeof(wsresults)/sizeof(wsresults[0]), DDS_MSECS(100));
|
||||
if (status < 0)
|
||||
DDS_FATAL("dds_waitset_wait: %s\n", dds_strretcode(-status));
|
||||
}
|
||||
|
||||
if (pollingDelay >= 0)
|
||||
{
|
||||
while (do_take (reader))
|
||||
;
|
||||
}
|
||||
|
||||
time_now = dds_time();
|
||||
if (!first_batch)
|
||||
{
|
||||
deltaTv = time_now - prev_time;
|
||||
deltaTime = (double) deltaTv / DDS_NSECS_IN_SEC;
|
||||
|
||||
if (deltaTime >= 1.0 && total_samples != prev_samples)
|
||||
{
|
||||
printf ("=== [Subscriber] %5.3f Payload size: %lu | Total received: %llu samples, %llu bytes | Out of order: %llu samples "
|
||||
"Transfer rate: %.2lf samples/s, %.2lf Mbit/s\n",
|
||||
deltaTime, payloadSize, total_samples, total_bytes, outOfOrder,
|
||||
(deltaTime != 0.0) ? ((double)(total_samples - prev_samples) / deltaTime) : 0,
|
||||
(deltaTime != 0.0) ? ((double)((total_bytes - prev_bytes) / BYTES_PER_SEC_TO_MEGABITS_PER_SEC) / deltaTime) : 0);
|
||||
fflush (stdout);
|
||||
cycles++;
|
||||
prev_time = time_now;
|
||||
prev_bytes = total_bytes;
|
||||
prev_samples = total_samples;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
prev_time = time_now;
|
||||
first_batch = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Output totals and averages */
|
||||
deltaTv = time_now - startTime;
|
||||
deltaTime = (double) (deltaTv / DDS_NSECS_IN_SEC);
|
||||
printf ("\nTotal received: %llu samples, %llu bytes\n", total_samples, total_bytes);
|
||||
printf ("Out of order: %llu samples\n", outOfOrder);
|
||||
printf ("Average transfer rate: %.2lf samples/s, ", (double)total_samples / deltaTime);
|
||||
printf ("%.2lf Mbit/s\n", (double)(total_bytes / BYTES_PER_SEC_TO_MEGABITS_PER_SEC) / deltaTime);
|
||||
fflush (stdout);
|
||||
}
|
||||
|
||||
static dds_entity_t prepare_dds(dds_entity_t *reader, const char *partitionName)
|
||||
{
|
||||
dds_return_t status;
|
||||
dds_entity_t topic;
|
||||
dds_entity_t subscriber;
|
||||
dds_listener_t *rd_listener;
|
||||
dds_entity_t participant;
|
||||
|
||||
int32_t maxSamples = 4000;
|
||||
const char *subParts[1];
|
||||
dds_qos_t *subQos = dds_create_qos ();
|
||||
dds_qos_t *drQos = dds_create_qos ();
|
||||
|
||||
/* A Participant is created for the default domain. */
|
||||
|
||||
participant = dds_create_participant (DDS_DOMAIN_DEFAULT, NULL, NULL);
|
||||
if (participant < 0)
|
||||
DDS_FATAL("dds_create_particpant: %s\n", dds_strretcode(-participant));
|
||||
|
||||
/* A Topic is created for our sample type on the domain participant. */
|
||||
|
||||
topic = dds_create_topic (participant, &ThroughputModule_DataType_desc, "Throughput", NULL, NULL);
|
||||
if (topic < 0)
|
||||
DDS_FATAL("dds_create_topic: %s\n", dds_strretcode(-topic));
|
||||
|
||||
/* A Subscriber is created on the domain participant. */
|
||||
|
||||
subParts[0] = partitionName;
|
||||
dds_qset_partition (subQos, 1, subParts);
|
||||
subscriber = dds_create_subscriber (participant, subQos, NULL);
|
||||
if (subscriber < 0)
|
||||
DDS_FATAL("dds_create_subscriber: %s\n", dds_strretcode(-subscriber));
|
||||
dds_delete_qos (subQos);
|
||||
|
||||
/* A Reader is created on the Subscriber & Topic with a modified Qos. */
|
||||
|
||||
dds_qset_reliability (drQos, DDS_RELIABILITY_RELIABLE, DDS_SECS (10));
|
||||
dds_qset_history (drQos, DDS_HISTORY_KEEP_ALL, 0);
|
||||
dds_qset_resource_limits (drQos, maxSamples, DDS_LENGTH_UNLIMITED, DDS_LENGTH_UNLIMITED);
|
||||
|
||||
rd_listener = dds_create_listener(NULL);
|
||||
dds_lset_data_available(rd_listener, data_available_handler);
|
||||
|
||||
/* A Read Condition is created which is triggered when data is available to read */
|
||||
waitSet = dds_create_waitset (participant);
|
||||
if (waitSet < 0)
|
||||
DDS_FATAL("dds_create_waitset: %s\n", dds_strretcode(-waitSet));
|
||||
|
||||
status = dds_waitset_attach (waitSet, waitSet, waitSet);
|
||||
if (status < 0)
|
||||
DDS_FATAL("dds_waitset_attach: %s\n", dds_strretcode(-status));
|
||||
|
||||
imap = HandleMap__alloc ();
|
||||
|
||||
memset (data, 0, sizeof (data));
|
||||
for (unsigned int i = 0; i < MAX_SAMPLES; i++)
|
||||
{
|
||||
samples[i] = &data[i];
|
||||
}
|
||||
|
||||
*reader = dds_create_reader (subscriber, topic, drQos, pollingDelay < 0 ? rd_listener : NULL);
|
||||
if (*reader < 0)
|
||||
DDS_FATAL("dds_create_reader: %s\n", dds_strretcode(-*reader));
|
||||
|
||||
if (pollingDelay == 0)
|
||||
{
|
||||
status = dds_waitset_attach (waitSet, *reader, *reader);
|
||||
if (status < 0)
|
||||
DDS_FATAL("dds_waitset_attach: %s\n", dds_strretcode(-status));
|
||||
}
|
||||
|
||||
dds_delete_qos (drQos);
|
||||
dds_delete_listener(rd_listener);
|
||||
|
||||
return participant;
|
||||
}
|
||||
|
||||
static void finalize_dds(dds_entity_t participant)
|
||||
{
|
||||
dds_return_t status;
|
||||
|
||||
for (unsigned int i = 0; i < MAX_SAMPLES; i++)
|
||||
{
|
||||
ThroughputModule_DataType_free (&data[i], DDS_FREE_CONTENTS);
|
||||
}
|
||||
|
||||
status = dds_waitset_detach (waitSet, waitSet);
|
||||
if (status < 0)
|
||||
DDS_FATAL("dds_waitset_detach: %s\n", dds_strretcode(-status));
|
||||
status = dds_delete (waitSet);
|
||||
if (status < 0)
|
||||
DDS_FATAL("dds_delete: %s\n", dds_strretcode(-status));
|
||||
status = dds_delete (participant);
|
||||
if (status < 0)
|
||||
DDS_FATAL("dds_delete: %s\n", dds_strretcode(-status));
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue