Initial contribution
This commit is contained in:
parent
7b5cc4fa59
commit
11d9ce37aa
580 changed files with 155133 additions and 162 deletions
149
src/examples/CMakeLists.txt
Normal file
149
src/examples/CMakeLists.txt
Normal file
|
@ -0,0 +1,149 @@
|
|||
#
|
||||
# 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)
|
||||
|
||||
# Examples HTML documentation
|
||||
set(EXAMPLES_HTML_ARCHIVE "${CMAKE_PROJECT_NAME}ExamplesHTML.tar.gz")
|
||||
set(EXAMPLES_HTML_URI "http://jenkins.prismtech.com:8080/job/BuildChameleonLinux64bit/lastSuccessfulBuild/artifact/cham/builds/examples/${EXAMPLES_HTML_ARCHIVE}")
|
||||
|
||||
if (BUILD_DOCS)
|
||||
find_program(SPHINX_EXECUTABLE NAMES sphinx-build DOC "Sphinx documentation builder")
|
||||
if (NOT SPHINX_EXECUTABLE)
|
||||
message(STATUS "Unable to find sphinx-build executable, downloading examples docs...")
|
||||
set(BUILD_DOCS off)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (BUILD_DOCS)
|
||||
message(STATUS "Examples html docs will be built using sphinx (${SPHINX_EXECUTABLE})")
|
||||
|
||||
# Process sphinx configuration file
|
||||
set(sph_conf_author "ADLINK")
|
||||
string(TIMESTAMP sph_conf_copyright "%Y, ADLINK")
|
||||
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}"
|
||||
)
|
||||
else()
|
||||
# Download example html docs
|
||||
|
||||
file(DOWNLOAD "${EXAMPLES_HTML_URI}" "${CMAKE_CURRENT_BINARY_DIR}/Downloaded${EXAMPLES_HTML_ARCHIVE}"
|
||||
TIMEOUT 10
|
||||
STATUS status
|
||||
)
|
||||
list(GET status 0 status_code)
|
||||
list(GET status 1 status_string)
|
||||
if (NOT status_code EQUAL 0)
|
||||
message(FATAL_ERROR
|
||||
"Failed to download ${EXAMPLES_HTML_URI} \
|
||||
(Code: ${status_code}, ${status_string})"
|
||||
)
|
||||
endif()
|
||||
|
||||
add_custom_target(examples_docs ALL)
|
||||
add_custom_command(TARGET examples_docs
|
||||
COMMAND ${CMAKE_COMMAND} -E tar "zxf" "Downloaded${EXAMPLES_HTML_ARCHIVE}"
|
||||
VERBATIM
|
||||
)
|
||||
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/*"
|
||||
)
|
||||
|
46
src/examples/examples.rst
Normal file
46
src/examples/examples.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
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Contents:
|
||||
|
||||
helloworld/readme
|
||||
roundtrip/readme
|
||||
throughput/readme
|
||||
|
||||
Configuration
|
||||
*************
|
||||
|
||||
Cyclone DDS has various configuration parameters and comes with a default built-in configuration.
|
||||
To run an example, or any application that uses Cyclone DDS for its data exchange, this default
|
||||
configuration is usually fine and no further action is required.
|
||||
|
||||
Configuration parameters for 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 CycloneDDS installation comes with a set of standard configuration files for common use cases.
|
||||
You update existing configuration files or create your own by using the CycloneDDS Configurator tool,
|
||||
which provides context-sensitive help on available configuration parameters and their applicability.
|
||||
|
||||
You can start the CycloneDDS Configuration tool through the CycloneDDS Launcher, or from your command-prompt
|
||||
by entering the tools directory and running ``java -jar cycloneddsconf.jar``. The default location of the tools
|
||||
directory is ``/usr/share/CycloneDDS/tools`` on Linux or ``C:\Program Files\ADLINK\Vortex DDS\share\CycloneDDS\tools``
|
||||
on Windows.
|
22
src/examples/helloworld/CMakeLists.export
Normal file
22
src/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
src/examples/helloworld/CMakeLists.txt
Normal file
58
src/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
src/examples/helloworld/HelloWorld.sln
Normal file
54
src/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
src/examples/helloworld/HelloWorldData.idl
Normal file
9
src/examples/helloworld/HelloWorldData.idl
Normal file
|
@ -0,0 +1,9 @@
|
|||
module HelloWorldData
|
||||
{
|
||||
struct Msg
|
||||
{
|
||||
long userID;
|
||||
string message;
|
||||
};
|
||||
#pragma keylist Msg userID
|
||||
};
|
54
src/examples/helloworld/Makefile
Normal file
54
src/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
|
||||
|
59
src/examples/helloworld/publisher.c
Normal file
59
src/examples/helloworld/publisher.c
Normal file
|
@ -0,0 +1,59 @@
|
|||
#include "ddsc/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 ret;
|
||||
HelloWorldData_Msg msg;
|
||||
|
||||
/* Create a Participant. */
|
||||
participant = dds_create_participant (DDS_DOMAIN_DEFAULT, NULL, NULL);
|
||||
DDS_ERR_CHECK (participant, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
/* Create a Topic. */
|
||||
topic = dds_create_topic (participant, &HelloWorldData_Msg_desc,
|
||||
"HelloWorldData_Msg", NULL, NULL);
|
||||
DDS_ERR_CHECK (topic, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
/* Create a Writer. */
|
||||
writer = dds_create_writer (participant, topic, NULL, NULL);
|
||||
|
||||
printf("=== [Publisher] Waiting for a reader to be discovered ...\n");
|
||||
|
||||
ret = dds_set_enabled_status(writer, DDS_PUBLICATION_MATCHED_STATUS);
|
||||
DDS_ERR_CHECK (ret, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
while(true)
|
||||
{
|
||||
uint32_t status;
|
||||
ret = dds_get_status_changes (writer, &status);
|
||||
DDS_ERR_CHECK (ret, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
if (status == DDS_PUBLICATION_MATCHED_STATUS) {
|
||||
break;
|
||||
}
|
||||
/* Polling sleep. */
|
||||
dds_sleepfor (DDS_MSECS (20));
|
||||
}
|
||||
|
||||
/* Create a message to write. */
|
||||
msg.userID = 1;
|
||||
msg.message = "Hello World";
|
||||
|
||||
printf ("=== [Publisher] Writing : ");
|
||||
printf ("Message (%d, %s)\n", msg.userID, msg.message);
|
||||
|
||||
ret = dds_write (writer, &msg);
|
||||
DDS_ERR_CHECK (ret, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
/* Deleting the participant will delete all its children recursively as well. */
|
||||
ret = dds_delete (participant);
|
||||
DDS_ERR_CHECK (ret, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
46
src/examples/helloworld/readme.rst
Normal file
46
src/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 CycloneDDS.
|
||||
|
||||
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
|
75
src/examples/helloworld/subscriber.c
Normal file
75
src/examples/helloworld/subscriber.c
Normal file
|
@ -0,0 +1,75 @@
|
|||
#include "ddsc/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 ret;
|
||||
dds_qos_t *qos;
|
||||
|
||||
/* Create a Participant. */
|
||||
participant = dds_create_participant (DDS_DOMAIN_DEFAULT, NULL, NULL);
|
||||
DDS_ERR_CHECK (participant, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
/* Create a Topic. */
|
||||
topic = dds_create_topic (participant, &HelloWorldData_Msg_desc,
|
||||
"HelloWorldData_Msg", NULL, NULL);
|
||||
DDS_ERR_CHECK (topic, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
/* Create a reliable Reader. */
|
||||
qos = dds_qos_create ();
|
||||
dds_qset_reliability (qos, DDS_RELIABILITY_RELIABLE, DDS_SECS (10));
|
||||
reader = dds_create_reader (participant, topic, qos, NULL);
|
||||
DDS_ERR_CHECK (reader, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
dds_qos_delete(qos);
|
||||
|
||||
printf ("\n=== [Subscriber] Waiting for a sample ...\n");
|
||||
|
||||
/* 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. */
|
||||
ret = dds_read (reader, samples, infos, MAX_SAMPLES, MAX_SAMPLES);
|
||||
DDS_ERR_CHECK (ret, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
/* Check if we read some data and it is valid. */
|
||||
if ((ret > 0) && (infos[0].valid_data))
|
||||
{
|
||||
/* Print Message. */
|
||||
msg = (HelloWorldData_Msg*) samples[0];
|
||||
printf ("=== [Subscriber] Received : ");
|
||||
printf ("Message (%d, %s)\n", msg->userID, msg->message);
|
||||
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. */
|
||||
ret = dds_delete (participant);
|
||||
DDS_ERR_CHECK (ret, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
142
src/examples/helloworld/vs/HelloWorldPublisher.vcxproj
Normal file
142
src/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
src/examples/helloworld/vs/HelloWorldSubscriber.vcxproj
Normal file
142
src/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
src/examples/helloworld/vs/HelloWorldType.vcxproj
Normal file
86
src/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
src/examples/helloworld/vs/directories.props
Normal file
30
src/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>
|
33
src/examples/roundtrip/CMakeLists.txt
Normal file
33
src/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
src/examples/roundtrip/RoundTrip.idl
Normal file
8
src/examples/roundtrip/RoundTrip.idl
Normal file
|
@ -0,0 +1,8 @@
|
|||
module RoundTripModule
|
||||
{
|
||||
struct DataType
|
||||
{
|
||||
sequence<octet> payload;
|
||||
};
|
||||
#pragma keylist DataType
|
||||
};
|
484
src/examples/roundtrip/ping.c
Normal file
484
src/examples/roundtrip/ping.c
Normal file
|
@ -0,0 +1,484 @@
|
|||
#include "ddsc/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);
|
||||
static void finalize_dds(dds_entity_t participant, dds_entity_t reader, dds_entity_t readCond);
|
||||
|
||||
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 = (stats->count * stats->average + timing) / (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_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
|
||||
}
|
||||
#else
|
||||
static void CtrlHandler (int fdwCtrlType)
|
||||
{
|
||||
dds_waitset_set_trigger (waitSet, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
dds_entity_t writer;
|
||||
dds_entity_t reader;
|
||||
dds_entity_t participant;
|
||||
dds_entity_t readCond;
|
||||
|
||||
ExampleTimeStats roundTrip;
|
||||
ExampleTimeStats writeAccess;
|
||||
ExampleTimeStats readAccess;
|
||||
ExampleTimeStats roundTripOverall;
|
||||
ExampleTimeStats writeAccessOverall;
|
||||
ExampleTimeStats readAccessOverall;
|
||||
|
||||
unsigned long payloadSize = 0;
|
||||
unsigned long long numSamples = 0;
|
||||
bool invalidargs = false;
|
||||
dds_time_t timeOut = 0;
|
||||
dds_time_t startTime;
|
||||
dds_time_t time;
|
||||
dds_time_t preWriteTime;
|
||||
dds_time_t postWriteTime;
|
||||
dds_time_t preTakeTime;
|
||||
dds_time_t postTakeTime;
|
||||
dds_time_t difference = 0;
|
||||
dds_time_t elapsed = 0;
|
||||
|
||||
RoundTripModule_DataType pub_data;
|
||||
RoundTripModule_DataType sub_data[MAX_SAMPLES];
|
||||
void *samples[MAX_SAMPLES];
|
||||
dds_sample_info_t info[MAX_SAMPLES];
|
||||
|
||||
dds_attach_t wsresults[1];
|
||||
size_t wsresultsize = 1U;
|
||||
dds_time_t waitTimeout = DDS_SECS (1);
|
||||
unsigned long i;
|
||||
int status;
|
||||
bool warmUp = true;
|
||||
|
||||
/* Register handler for Ctrl-C */
|
||||
#ifdef _WIN32
|
||||
SetConsoleCtrlHandler ((PHANDLER_ROUTINE)CtrlHandler, TRUE);
|
||||
#else
|
||||
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 = prepare_dds(&writer, &reader, &readCond);
|
||||
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
|
||||
if (argc == 2 && strcmp (argv[1], "quit") == 0)
|
||||
{
|
||||
printf ("Sending termination request.\n");
|
||||
/* 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);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
dds_sleepfor (DDS_SECS (1));
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (argc == 1)
|
||||
{
|
||||
invalidargs = true;
|
||||
}
|
||||
if (argc >= 2)
|
||||
{
|
||||
payloadSize = atol (argv[1]);
|
||||
|
||||
if (payloadSize > 65536)
|
||||
{
|
||||
invalidargs = true;
|
||||
}
|
||||
}
|
||||
if (argc >= 3)
|
||||
{
|
||||
numSamples = atol (argv[2]);
|
||||
}
|
||||
if (argc >= 4)
|
||||
{
|
||||
timeOut = atol (argv[3]);
|
||||
}
|
||||
if (invalidargs || (argc == 2 && (strcmp (argv[1], "-h") == 0 || strcmp (argv[1], "--help") == 0)))
|
||||
{
|
||||
printf ("Usage (parameters must be supplied in order):\n"
|
||||
"./ping [payloadSize (bytes, 0 - 65536)] [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");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
printf ("# payloadSize: %lu | numSamples: %llu | timeOut: %" PRIi64 "\n\n", payloadSize, numSamples, timeOut);
|
||||
|
||||
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");
|
||||
while (!dds_triggered (waitSet) && difference < DDS_SECS(5))
|
||||
{
|
||||
status = dds_write (writer, &pub_data);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
status = dds_waitset_wait (waitSet, wsresults, wsresultsize, waitTimeout);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
if (status > 0) /* data */
|
||||
{
|
||||
status = dds_take (reader, samples, info, MAX_SAMPLES, MAX_SAMPLES);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
}
|
||||
|
||||
time = dds_time ();
|
||||
difference = time - startTime;
|
||||
}
|
||||
if (!dds_triggered (waitSet))
|
||||
{
|
||||
warmUp = false;
|
||||
printf("# Warm up complete.\n\n");
|
||||
|
||||
printf("# Round trip measurements (in us)\n");
|
||||
printf("# Round trip time [us] Write-access time [us] Read-access time [us]\n");
|
||||
printf("# Seconds Count median min Count median min Count median min\n");
|
||||
|
||||
}
|
||||
|
||||
startTime = dds_time ();
|
||||
for (i = 0; !dds_triggered (waitSet) && (!numSamples || i < numSamples); i++)
|
||||
{
|
||||
/* Write a sample that pong can send back */
|
||||
preWriteTime = dds_time ();
|
||||
status = dds_write (writer, &pub_data);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
postWriteTime = dds_time ();
|
||||
|
||||
/* Wait for response from pong */
|
||||
status = dds_waitset_wait (waitSet, wsresults, wsresultsize, waitTimeout);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
if (status != 0)
|
||||
{
|
||||
/* Take sample and check that it is valid */
|
||||
preTakeTime = dds_time ();
|
||||
status = dds_take (reader, samples, info, MAX_SAMPLES, MAX_SAMPLES);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
postTakeTime = dds_time ();
|
||||
|
||||
if (!dds_triggered (waitSet))
|
||||
{
|
||||
if (status != 1)
|
||||
{
|
||||
fprintf (stdout, "%s%d%s", "ERROR: Ping received ", status,
|
||||
" samples but was expecting 1. Are multiple pong applications running?\n");
|
||||
|
||||
goto done;
|
||||
}
|
||||
else if (!info[0].valid_data)
|
||||
{
|
||||
printf ("ERROR: Ping received an invalid sample. Has pong terminated already?\n");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 - preWriteTime)/DDS_NSECS_IN_USEC;
|
||||
roundTrip = *exampleAddTimingToTimeStats (&roundTrip, difference);
|
||||
roundTripOverall = *exampleAddTimingToTimeStats (&roundTripOverall, difference);
|
||||
|
||||
/* Print stats each second */
|
||||
difference = (postTakeTime - startTime)/DDS_NSECS_IN_USEC;
|
||||
if (difference > US_IN_ONE_SEC || (i && i == numSamples))
|
||||
{
|
||||
printf
|
||||
(
|
||||
"%9" PRIi64 " %9lu %8.0f %8" PRIi64 " %10lu %8.0f %8" PRIi64 " %10lu %8.0f %8" PRIi64 "\n",
|
||||
elapsed + 1,
|
||||
roundTrip.count,
|
||||
exampleGetMedianFromTimeStats (&roundTrip),
|
||||
roundTrip.min,
|
||||
writeAccess.count,
|
||||
exampleGetMedianFromTimeStats (&writeAccess),
|
||||
writeAccess.min,
|
||||
readAccess.count,
|
||||
exampleGetMedianFromTimeStats (&readAccess),
|
||||
readAccess.min
|
||||
);
|
||||
|
||||
exampleResetTimeStats (&roundTrip);
|
||||
exampleResetTimeStats (&writeAccess);
|
||||
exampleResetTimeStats (&readAccess);
|
||||
startTime = dds_time ();
|
||||
elapsed++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
elapsed += waitTimeout / DDS_NSECS_IN_SEC;
|
||||
}
|
||||
if (timeOut && elapsed == timeOut)
|
||||
{
|
||||
dds_waitset_set_trigger (waitSet, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!warmUp)
|
||||
{
|
||||
printf
|
||||
(
|
||||
"\n%9s %9lu %8.0f %8" PRIi64 " %10lu %8.0f %8" PRIi64 " %10lu %8.0f %8" PRIi64 "\n",
|
||||
"# Overall",
|
||||
roundTripOverall.count,
|
||||
exampleGetMedianFromTimeStats (&roundTripOverall),
|
||||
roundTripOverall.min,
|
||||
writeAccessOverall.count,
|
||||
exampleGetMedianFromTimeStats (&writeAccessOverall),
|
||||
writeAccessOverall.min,
|
||||
readAccessOverall.count,
|
||||
exampleGetMedianFromTimeStats (&readAccessOverall),
|
||||
readAccessOverall.min
|
||||
);
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
#ifdef _WIN32
|
||||
SetConsoleCtrlHandler (0, FALSE);
|
||||
#else
|
||||
sigaction (SIGINT, &oldAction, 0);
|
||||
#endif
|
||||
|
||||
finalize_dds(participant, reader, readCond);
|
||||
|
||||
/* 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 *writer, dds_entity_t *reader, dds_entity_t *readCond)
|
||||
{
|
||||
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;
|
||||
|
||||
dds_entity_t participant = dds_create_participant (DDS_DOMAIN_DEFAULT, NULL, NULL);
|
||||
DDS_ERR_CHECK (participant, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
/* A DDS_Topic is created for our sample type on the domain participant. */
|
||||
topic = dds_create_topic (participant, &RoundTripModule_DataType_desc, "RoundTrip", NULL, NULL);
|
||||
DDS_ERR_CHECK (topic, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
/* A DDS_Publisher is created on the domain participant. */
|
||||
pubQos = dds_qos_create ();
|
||||
dds_qset_partition (pubQos, 1, pubPartitions);
|
||||
|
||||
publisher = dds_create_publisher (participant, pubQos, NULL);
|
||||
DDS_ERR_CHECK (publisher, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
dds_qos_delete (pubQos);
|
||||
|
||||
/* A DDS_DataWriter is created on the Publisher & Topic with a modified Qos. */
|
||||
dwQos = dds_qos_create ();
|
||||
dds_qset_reliability (dwQos, DDS_RELIABILITY_RELIABLE, DDS_SECS (10));
|
||||
dds_qset_writer_data_lifecycle (dwQos, false);
|
||||
*writer = dds_create_writer (publisher, topic, dwQos, NULL);
|
||||
DDS_ERR_CHECK (*writer, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
dds_qos_delete (dwQos);
|
||||
|
||||
/* A DDS_Subscriber is created on the domain participant. */
|
||||
subQos = dds_qos_create ();
|
||||
|
||||
dds_qset_partition (subQos, 1, subPartitions);
|
||||
|
||||
subscriber = dds_create_subscriber (participant, subQos, NULL);
|
||||
DDS_ERR_CHECK (subscriber, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
dds_qos_delete (subQos);
|
||||
/* A DDS_DataReader is created on the Subscriber & Topic with a modified QoS. */
|
||||
drQos = dds_qos_create ();
|
||||
dds_qset_reliability (drQos, DDS_RELIABILITY_RELIABLE, DDS_SECS(10));
|
||||
*reader = dds_create_reader (subscriber, topic, drQos, NULL);
|
||||
DDS_ERR_CHECK (*reader, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
dds_qos_delete (drQos);
|
||||
|
||||
waitSet = dds_create_waitset (participant);
|
||||
*readCond = dds_create_readcondition (*reader, DDS_ANY_STATE);
|
||||
|
||||
status = dds_waitset_attach (waitSet, *readCond, *reader);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
status = dds_waitset_attach (waitSet, waitSet, waitSet);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
return participant;
|
||||
}
|
||||
|
||||
static void finalize_dds(dds_entity_t participant, dds_entity_t reader, dds_entity_t readCond)
|
||||
{
|
||||
dds_return_t status;
|
||||
|
||||
/* Disable callbacks */
|
||||
dds_set_enabled_status (reader, 0);
|
||||
|
||||
status = dds_waitset_detach (waitSet, readCond);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
status = dds_waitset_detach (waitSet, waitSet);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
status = dds_delete (readCond);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
status = dds_delete (waitSet);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
status = dds_delete (participant);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
}
|
192
src/examples/roundtrip/pong.c
Normal file
192
src/examples/roundtrip/pong.c
Normal file
|
@ -0,0 +1,192 @@
|
|||
#include "ddsc/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);
|
||||
static void finalize_dds(dds_entity_t participant, dds_entity_t readCond, 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
|
||||
}
|
||||
#else
|
||||
static void CtrlHandler (int fdwCtrlType)
|
||||
{
|
||||
dds_waitset_set_trigger (waitSet, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
dds_duration_t waitTimeout = DDS_INFINITY;
|
||||
unsigned int i;
|
||||
int status, samplecount, j;
|
||||
dds_attach_t wsresults[1];
|
||||
size_t wsresultsize = 1U;
|
||||
dds_entity_t participant;
|
||||
dds_entity_t reader;
|
||||
dds_entity_t writer;
|
||||
dds_entity_t readCond;
|
||||
|
||||
RoundTripModule_DataType data[MAX_SAMPLES];
|
||||
void * samples[MAX_SAMPLES];
|
||||
dds_sample_info_t info[MAX_SAMPLES];
|
||||
|
||||
/* Register handler for Ctrl-C */
|
||||
|
||||
#ifdef _WIN32
|
||||
SetConsoleCtrlHandler ((PHANDLER_ROUTINE)CtrlHandler, TRUE);
|
||||
#else
|
||||
struct sigaction sat, oldAction;
|
||||
sat.sa_handler = CtrlHandler;
|
||||
sigemptyset (&sat.sa_mask);
|
||||
sat.sa_flags = 0;
|
||||
sigaction (SIGINT, &sat, &oldAction);
|
||||
#endif
|
||||
|
||||
participant = prepare_dds(&writer, &reader, &readCond);
|
||||
|
||||
/* Initialize sample data */
|
||||
memset (data, 0, sizeof (data));
|
||||
for (i = 0; i < MAX_SAMPLES; i++)
|
||||
{
|
||||
samples[i] = &data[i];
|
||||
}
|
||||
|
||||
while (!dds_triggered (waitSet))
|
||||
{
|
||||
/* Wait for a sample from ping */
|
||||
|
||||
status = dds_waitset_wait (waitSet, wsresults, wsresultsize, waitTimeout);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
/* Take samples */
|
||||
samplecount = dds_take (reader, samples, info, MAX_SAMPLES, MAX_SAMPLES);
|
||||
DDS_ERR_CHECK (samplecount, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
for (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 (writer, valid_sample);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
SetConsoleCtrlHandler (0, FALSE);
|
||||
#else
|
||||
sigaction (SIGINT, &oldAction, 0);
|
||||
#endif
|
||||
|
||||
/* Clean up */
|
||||
finalize_dds(participant, readCond, data);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static void finalize_dds(dds_entity_t participant, dds_entity_t readCond, RoundTripModule_DataType data[MAX_SAMPLES])
|
||||
{
|
||||
dds_return_t status = dds_waitset_detach (waitSet, readCond);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
status = dds_waitset_detach (waitSet, waitSet);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
status = dds_delete (readCond);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
status = dds_delete (waitSet);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
status = dds_delete (participant);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
for (unsigned int i = 0; i < MAX_SAMPLES; i++)
|
||||
{
|
||||
RoundTripModule_DataType_free (&data[i], DDS_FREE_CONTENTS);
|
||||
}
|
||||
}
|
||||
|
||||
static dds_entity_t prepare_dds(dds_entity_t *writer, dds_entity_t *reader, dds_entity_t *readCond)
|
||||
{
|
||||
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;
|
||||
|
||||
dds_entity_t participant = dds_create_participant (DDS_DOMAIN_DEFAULT, NULL, NULL);
|
||||
DDS_ERR_CHECK (participant, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
/* A DDS Topic is created for our sample type on the domain participant. */
|
||||
|
||||
topic = dds_create_topic (participant, &RoundTripModule_DataType_desc, "RoundTrip", NULL, NULL);
|
||||
DDS_ERR_CHECK (topic, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
/* A DDS Publisher is created on the domain participant. */
|
||||
|
||||
qos = dds_qos_create ();
|
||||
dds_qset_partition (qos, 1, pubPartitions);
|
||||
|
||||
publisher = dds_create_publisher (participant, qos, NULL);
|
||||
DDS_ERR_CHECK (publisher, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
dds_qos_delete (qos);
|
||||
|
||||
/* A DDS DataWriter is created on the Publisher & Topic with a modififed Qos. */
|
||||
|
||||
qos = dds_qos_create ();
|
||||
dds_qset_reliability (qos, DDS_RELIABILITY_RELIABLE, DDS_SECS(10));
|
||||
dds_qset_writer_data_lifecycle (qos, false);
|
||||
*writer = dds_create_writer (publisher, topic, qos, NULL);
|
||||
DDS_ERR_CHECK (*writer, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
dds_qos_delete (qos);
|
||||
|
||||
/* A DDS Subscriber is created on the domain participant. */
|
||||
|
||||
qos = dds_qos_create ();
|
||||
dds_qset_partition (qos, 1, subPartitions);
|
||||
|
||||
subscriber = dds_create_subscriber (participant, qos, NULL);
|
||||
DDS_ERR_CHECK (subscriber, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
dds_qos_delete (qos);
|
||||
|
||||
/* A DDS DataReader is created on the Subscriber & Topic with a modified QoS. */
|
||||
|
||||
qos = dds_qos_create ();
|
||||
dds_qset_reliability (qos, DDS_RELIABILITY_RELIABLE, DDS_SECS(10));
|
||||
*reader = dds_create_reader (subscriber, topic, qos, NULL);
|
||||
DDS_ERR_CHECK (*reader, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
dds_qos_delete (qos);
|
||||
|
||||
waitSet = dds_create_waitset (participant);
|
||||
*readCond = dds_create_readcondition (*reader, DDS_ANY_STATE);
|
||||
status = dds_waitset_attach (waitSet, *readCond, *reader);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
status = dds_waitset_attach (waitSet, waitSet, waitSet);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
printf ("Waiting for samples from ping to send back...\n");
|
||||
fflush (stdout);
|
||||
|
||||
return participant;
|
||||
}
|
83
src/examples/roundtrip/readme.rst
Normal file
83
src/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
src/examples/sphinx-conf.py.in
Normal file
86
src/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/_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
src/examples/throughput/CMakeLists.txt
Normal file
33
src/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
src/examples/throughput/Throughput.idl
Normal file
9
src/examples/throughput/Throughput.idl
Normal file
|
@ -0,0 +1,9 @@
|
|||
module ThroughputModule
|
||||
{
|
||||
struct DataType
|
||||
{
|
||||
unsigned long long count;
|
||||
sequence<octet> payload;
|
||||
};
|
||||
#pragma keylist DataType
|
||||
};
|
309
src/examples/throughput/publisher.c
Normal file
309
src/examples/throughput/publisher.c
Normal file
|
@ -0,0 +1,309 @@
|
|||
#include "ddsc/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,
|
||||
unsigned int burstInterval, unsigned int burstSize, unsigned int timeOut);
|
||||
static int parse_args(int argc, char **argv, uint32_t *payloadSize, unsigned int *burstInterval,
|
||||
unsigned int *burstSize, unsigned 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);
|
||||
|
||||
/* Functions to handle Ctrl-C presses. */
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
static int CtrlHandler (DWORD fdwCtrlType)
|
||||
{
|
||||
done = true;
|
||||
return true; /* Don't let other handlers handle this key */
|
||||
}
|
||||
#else
|
||||
struct sigaction oldAction;
|
||||
static void CtrlHandler (int fdwCtrlType)
|
||||
{
|
||||
done = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
int result = EXIT_SUCCESS;
|
||||
uint32_t payloadSize = 8192;
|
||||
unsigned int burstInterval = 0;
|
||||
unsigned int burstSize = 1;
|
||||
unsigned int timeOut = 0;
|
||||
char * partitionName = "Throughput example";
|
||||
dds_entity_t participant;
|
||||
dds_entity_t writer;
|
||||
ThroughputModule_DataType sample;
|
||||
|
||||
/* Register handler for Ctrl-C */
|
||||
#ifdef _WIN32
|
||||
SetConsoleCtrlHandler ((PHANDLER_ROUTINE) CtrlHandler, true);
|
||||
#else
|
||||
struct sigaction sat;
|
||||
sat.sa_handler = CtrlHandler;
|
||||
sigemptyset (&sat.sa_mask);
|
||||
sat.sa_flags = 0;
|
||||
sigaction (SIGINT, &sat, &oldAction);
|
||||
#endif
|
||||
|
||||
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");
|
||||
DDS_ERR_CHECK (dds_delete (participant), DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
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 the sample instance and write samples repeatedly or until time out */
|
||||
start_writing(writer, &sample, burstInterval, burstSize, timeOut);
|
||||
|
||||
#ifdef _WIN32
|
||||
SetConsoleCtrlHandler (0, false);
|
||||
#else
|
||||
sigaction (SIGINT, &oldAction, 0);
|
||||
#endif
|
||||
|
||||
/* Cleanup */
|
||||
finalize_dds(participant, writer, sample);
|
||||
|
||||
}
|
||||
|
||||
static int parse_args(
|
||||
int argc,
|
||||
char **argv,
|
||||
uint32_t *payloadSize,
|
||||
unsigned int *burstInterval,
|
||||
unsigned int *burstSize,
|
||||
unsigned 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 = 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 = 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: %u bytes burstInterval: %u ms burstSize: %u timeOut: %u seconds partitionName: %s\n",
|
||||
*payloadSize, *burstInterval, *burstSize, *timeOut, *partitionName);
|
||||
|
||||
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);
|
||||
DDS_ERR_CHECK (participant, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
/* A topic is created for our sample type on the domain participant. */
|
||||
topic = dds_create_topic (participant, &ThroughputModule_DataType_desc, "Throughput", NULL, NULL);
|
||||
DDS_ERR_CHECK (topic, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
/* A publisher is created on the domain participant. */
|
||||
pubQos = dds_qos_create ();
|
||||
pubParts[0] = partitionName;
|
||||
dds_qset_partition (pubQos, 1, pubParts);
|
||||
publisher = dds_create_publisher (participant, pubQos, NULL);
|
||||
DDS_ERR_CHECK (publisher, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
dds_qos_delete (pubQos);
|
||||
|
||||
/* A DataWriter is created on the publisher. */
|
||||
dwQos = dds_qos_create ();
|
||||
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);
|
||||
DDS_ERR_CHECK (*writer, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
dds_qos_delete (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");
|
||||
|
||||
dds_return_t ret;
|
||||
dds_entity_t waitset;
|
||||
|
||||
ret = dds_set_enabled_status(writer, DDS_PUBLICATION_MATCHED_STATUS);
|
||||
DDS_ERR_CHECK (ret, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
waitset = dds_create_waitset(participant);
|
||||
DDS_ERR_CHECK (waitset, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
ret = dds_waitset_attach(waitset, writer, (dds_attach_t)NULL);
|
||||
DDS_ERR_CHECK (waitset, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
ret = dds_waitset_wait(waitset, NULL, 0, DDS_SECS(30));
|
||||
DDS_ERR_CHECK (ret, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void start_writing(
|
||||
dds_entity_t writer,
|
||||
ThroughputModule_DataType *sample,
|
||||
unsigned int burstInterval,
|
||||
unsigned int burstSize,
|
||||
unsigned 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");
|
||||
|
||||
while (!done && !timedOut)
|
||||
{
|
||||
/* Write data until burst size has been reached */
|
||||
|
||||
if (burstCount < burstSize)
|
||||
{
|
||||
status = dds_write (writer, sample);
|
||||
if (dds_err_nr(status) == DDS_RETCODE_TIMEOUT)
|
||||
{
|
||||
timedOut = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
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);
|
||||
|
||||
if (done)
|
||||
{
|
||||
printf ("=== [Publisher] Terminated, %llu samples written.\n", (unsigned long long) sample->count);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("=== [Publisher] Timed out, %llu samples written.\n", (unsigned long long) sample->count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void finalize_dds(dds_entity_t participant, dds_entity_t writer, ThroughputModule_DataType sample)
|
||||
{
|
||||
dds_return_t status = dds_dispose (writer, &sample);
|
||||
if (dds_err_nr (status) != DDS_RETCODE_TIMEOUT)
|
||||
{
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
}
|
||||
|
||||
dds_free (sample.payload._buffer);
|
||||
status = dds_delete (participant);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
}
|
99
src/examples/throughput/readme.rst
Normal file
99
src/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]``
|
||||
|
||||
|
||||
|
||||
|
||||
|
426
src/examples/throughput/subscriber.c
Normal file
426
src/examples/throughput/subscriber.c
Normal file
|
@ -0,0 +1,426 @@
|
|||
#include "ddsc/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 100
|
||||
|
||||
typedef struct HandleEntry
|
||||
{
|
||||
dds_instance_handle_t handle;
|
||||
unsigned long long count;
|
||||
struct HandleEntry * next;
|
||||
} HandleEntry;
|
||||
|
||||
typedef struct HandleMap
|
||||
{
|
||||
HandleEntry *entries;
|
||||
} HandleMap;
|
||||
|
||||
static unsigned long pollingDelay = 0;
|
||||
|
||||
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 dds_time_t time_now = 0;
|
||||
static dds_time_t prev_time = 0;
|
||||
|
||||
static unsigned long payloadSize = 0;
|
||||
|
||||
static ThroughputModule_DataType data [MAX_SAMPLES];
|
||||
static void * samples[MAX_SAMPLES];
|
||||
|
||||
static dds_entity_t waitSet;
|
||||
static dds_entity_t pollingWaitset;
|
||||
|
||||
static bool 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(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);
|
||||
|
||||
/* Functions to handle Ctrl-C presses. */
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
static int CtrlHandler (DWORD fdwCtrlType)
|
||||
{
|
||||
dds_waitset_set_trigger (waitSet, true);
|
||||
done = true;
|
||||
return true; /* Don't let other handlers handle this key */
|
||||
}
|
||||
#else
|
||||
struct sigaction oldAction;
|
||||
static void CtrlHandler (int fdwCtrlType)
|
||||
{
|
||||
dds_waitset_set_trigger (waitSet, true);
|
||||
done = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
unsigned long long maxCycles = 0;
|
||||
char *partitionName = "Throughput example";
|
||||
|
||||
dds_entity_t participant;
|
||||
dds_entity_t reader;
|
||||
|
||||
time_now = dds_time ();
|
||||
prev_time = time_now;
|
||||
|
||||
/* Register handler for Ctrl-C */
|
||||
#ifdef _WIN32
|
||||
SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, true);
|
||||
#else
|
||||
struct sigaction sat;
|
||||
sat.sa_handler = CtrlHandler;
|
||||
sigemptyset(&sat.sa_mask);
|
||||
sat.sa_flags = 0;
|
||||
sigaction (SIGINT, &sat, &oldAction);
|
||||
#endif
|
||||
|
||||
if (parse_args(argc, argv, &maxCycles, &partitionName) == EXIT_FAILURE)
|
||||
{
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf ("Cycles: %llu | PollingDelay: %lu | Partition: %s\n",
|
||||
maxCycles, pollingDelay, partitionName);
|
||||
|
||||
participant = prepare_dds(&reader, partitionName);
|
||||
|
||||
printf ("=== [Subscriber] Waiting for samples...\n");
|
||||
|
||||
/* Process samples until Ctrl-C is pressed or until maxCycles */
|
||||
/* has been reached (0 = infinite) */
|
||||
process_samples(maxCycles);
|
||||
|
||||
/* Finished, disable callbacks */
|
||||
dds_set_enabled_status (reader, 0);
|
||||
HandleMap__free (imap);
|
||||
|
||||
#ifdef _WIN32
|
||||
SetConsoleCtrlHandler (0, FALSE);
|
||||
#else
|
||||
sigaction (SIGINT, &oldAction, 0);
|
||||
#endif
|
||||
|
||||
/* Clean up */
|
||||
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 void data_available_handler (dds_entity_t reader, void *arg)
|
||||
{
|
||||
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);
|
||||
DDS_ERR_CHECK (samples_received, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
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++;
|
||||
}
|
||||
}
|
||||
time_now = dds_time ();
|
||||
if ((pollingDelay == 0) && (time_now > (prev_time + DDS_SECS (1))))
|
||||
{
|
||||
dds_waitset_set_trigger (pollingWaitset, true);
|
||||
}
|
||||
}
|
||||
|
||||
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 = event based)] [partitionName]\n");
|
||||
printf ("Defaults:\n");
|
||||
printf ("./subscriber 0 0 \"Throughput example\"\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
*maxCycles = 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 = event based) */
|
||||
}
|
||||
if (argc > 3)
|
||||
{
|
||||
*partitionName = argv[3]; /* The name of the partition */
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static void process_samples(unsigned long long maxCycles)
|
||||
{
|
||||
dds_return_t status;
|
||||
unsigned long long prev_bytes = 0;
|
||||
unsigned long long prev_samples = 0;
|
||||
dds_attach_t wsresults[1];
|
||||
size_t wsresultsize = 1U;
|
||||
dds_time_t deltaTv;
|
||||
bool first_batch = true;
|
||||
unsigned long cycles = 0;
|
||||
double deltaTime = 0;
|
||||
|
||||
while (!done && (maxCycles == 0 || cycles < maxCycles))
|
||||
{
|
||||
if (pollingDelay)
|
||||
{
|
||||
dds_sleepfor (DDS_MSECS (pollingDelay));
|
||||
}
|
||||
else
|
||||
{
|
||||
status = dds_waitset_wait (waitSet, wsresults, wsresultsize, DDS_INFINITY);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
if ((status > 0 ) && (dds_triggered (pollingWaitset)))
|
||||
{
|
||||
dds_waitset_set_trigger (pollingWaitset, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!first_batch)
|
||||
{
|
||||
deltaTv = time_now - prev_time;
|
||||
deltaTime = (double) deltaTv / DDS_NSECS_IN_SEC;
|
||||
prev_time = time_now;
|
||||
|
||||
printf
|
||||
(
|
||||
"=== [Subscriber] Payload size: %lu | Total received: %llu samples, %llu bytes | Out of order: %llu samples "
|
||||
"Transfer rate: %.2lf samples/s, %.2lf Mbit/s\n",
|
||||
payloadSize, total_samples, total_bytes, outOfOrder,
|
||||
(deltaTime) ? ((total_samples - prev_samples) / deltaTime) : 0,
|
||||
(deltaTime) ? (((total_bytes - prev_bytes) / BYTES_PER_SEC_TO_MEGABITS_PER_SEC) / deltaTime) : 0
|
||||
);
|
||||
cycles++;
|
||||
}
|
||||
else
|
||||
{
|
||||
prev_time = time_now;
|
||||
first_batch = false;
|
||||
}
|
||||
|
||||
/* Update the previous values for next iteration */
|
||||
|
||||
prev_bytes = total_bytes;
|
||||
prev_samples = total_samples;
|
||||
}
|
||||
|
||||
/* 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, ", total_samples / deltaTime);
|
||||
printf ("%.2lf Mbit/s\n", (total_bytes / BYTES_PER_SEC_TO_MEGABITS_PER_SEC) / deltaTime);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
uint32_t maxSamples = 400;
|
||||
const char *subParts[1];
|
||||
dds_qos_t *subQos = dds_qos_create ();
|
||||
dds_qos_t *drQos = dds_qos_create ();
|
||||
|
||||
/* A Participant is created for the default domain. */
|
||||
|
||||
participant = dds_create_participant (DDS_DOMAIN_DEFAULT, NULL, NULL);
|
||||
DDS_ERR_CHECK (participant, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
/* A Topic is created for our sample type on the domain participant. */
|
||||
|
||||
topic = dds_create_topic (participant, &ThroughputModule_DataType_desc, "Throughput", NULL, NULL);
|
||||
DDS_ERR_CHECK (topic, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
/* A Subscriber is created on the domain participant. */
|
||||
|
||||
subParts[0] = partitionName;
|
||||
dds_qset_partition (subQos, 1, subParts);
|
||||
subscriber = dds_create_subscriber (participant, subQos, NULL);
|
||||
DDS_ERR_CHECK (subscriber, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
dds_qos_delete (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_listener_create(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);
|
||||
DDS_ERR_CHECK (waitSet, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
pollingWaitset = dds_create_waitset (participant);
|
||||
DDS_ERR_CHECK (pollingWaitset, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
status = dds_waitset_attach (waitSet, pollingWaitset, pollingWaitset);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
status = dds_waitset_attach (waitSet, waitSet, waitSet);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
|
||||
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, rd_listener);
|
||||
DDS_ERR_CHECK (*reader, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
dds_qos_delete (drQos);
|
||||
dds_listener_delete(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);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
status = dds_waitset_detach (waitSet, pollingWaitset);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
status = dds_delete (pollingWaitset);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
status = dds_delete (waitSet);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
status = dds_delete (participant);
|
||||
DDS_ERR_CHECK (status, DDS_CHECK_REPORT | DDS_CHECK_EXIT);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue