Add sw Folder with related source code for practical testing
This commit is contained in:
parent
df67cf626d
commit
d73fe79b60
41
sw/CycloneDDS/Dockerfile/Dockerfile
Normal file
41
sw/CycloneDDS/Dockerfile/Dockerfile
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
FROM debian:bullseye
|
||||||
|
|
||||||
|
# Install Build Environment
|
||||||
|
RUN set -ex \
|
||||||
|
&& apt-get update \
|
||||||
|
&& apt-get install --yes --no-install-recommends \
|
||||||
|
cmake \
|
||||||
|
bison \
|
||||||
|
g++ \
|
||||||
|
make \
|
||||||
|
openssl \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Build and Install CycloneDDS
|
||||||
|
RUN set -ex \
|
||||||
|
&& apt-get update \
|
||||||
|
&& apt-get install --yes --no-install-recommends \
|
||||||
|
ca-certificates \
|
||||||
|
git \
|
||||||
|
&& cd /tmp \
|
||||||
|
&& git clone https://github.com/eclipse-cyclonedds/cyclonedds.git \
|
||||||
|
&& cd cyclonedds \
|
||||||
|
&& git checkout 0.8.0 \
|
||||||
|
&& mkdir build \
|
||||||
|
&& cd build \
|
||||||
|
&& cmake -DCMAKE_INSTALL_PREFIX=/opt/CycloneDDS -DBUILD_IDLC=ON .. \
|
||||||
|
&& cmake --build . \
|
||||||
|
&& cmake --build . --target install \
|
||||||
|
&& cd /tmp \
|
||||||
|
&& rm -rf * \
|
||||||
|
&& apt-get purge --yes \
|
||||||
|
ca-certificates \
|
||||||
|
git \
|
||||||
|
&& apt-get autoremove --yes \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY entrypoint.sh /entrypoint.sh
|
||||||
|
ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]
|
||||||
|
CMD ["bash"]
|
||||||
3
sw/CycloneDDS/Dockerfile/build.sh
Normal file
3
sw/CycloneDDS/Dockerfile/build.sh
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
docker build --progress=plain --tag cyclonedds:0.8.0 ./
|
||||||
6
sw/CycloneDDS/Dockerfile/entrypoint.sh
Normal file
6
sw/CycloneDDS/Dockerfile/entrypoint.sh
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
export PATH=/opt/CycloneDDS/bin:$PATH
|
||||||
|
export LD_LIBRARY_PATH=/opt/CycloneDDS/lib:$LD_LIBRARY_PATH
|
||||||
|
|
||||||
|
exec $1
|
||||||
20
sw/CycloneDDS/README.txt
Normal file
20
sw/CycloneDDS/README.txt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
Tested with Docker Image build from ./Dockerfile/Dockerfile
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
docker run --rm -it -v /home/john64/Desktop/rtps-fpga/sw/CycloneDDS/src:/test cyclonedds:0.8.0 bash
|
||||||
|
$ cd /test/helloworld
|
||||||
|
$ mkdir build
|
||||||
|
$ cd build
|
||||||
|
$ cmake ..
|
||||||
|
$ cmake --build .
|
||||||
|
$ ./HelloworldSubscriber
|
||||||
|
$ ./HelloworldPublisher
|
||||||
|
|
||||||
|
docker run --rm -it --net host -v /home/john64/Desktop/rtps-fpga/sw/CycloneDDS/src:/test cyclonedds:0.8.0 bash
|
||||||
|
$ cd /test/Loopback
|
||||||
|
$ mkdir build
|
||||||
|
$ cd build
|
||||||
|
$ cmake ..
|
||||||
|
$ cmake --build .
|
||||||
|
$ ./Loopback
|
||||||
1
sw/CycloneDDS/src/Loopback/.gitignore
vendored
Normal file
1
sw/CycloneDDS/src/Loopback/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
build/**
|
||||||
31
sw/CycloneDDS/src/Loopback/CMakeLists.txt
Normal file
31
sw/CycloneDDS/src/Loopback/CMakeLists.txt
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#
|
||||||
|
# Copyright(c) 2021 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
|
||||||
|
#
|
||||||
|
project(Loopback LANGUAGES C)
|
||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
|
if(NOT TARGET CycloneDDS::ddsc)
|
||||||
|
# Find the CycloneDDS package.
|
||||||
|
find_package(CycloneDDS REQUIRED)
|
||||||
|
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(TARGET Type1_lib FILES "Type1.idl")
|
||||||
|
|
||||||
|
# Both executables have only one related source file.
|
||||||
|
add_executable(Loopback loopback.c)
|
||||||
|
|
||||||
|
# Both executables need to be linked to the idl data type library and
|
||||||
|
# the ddsc API library.
|
||||||
|
target_link_libraries(Loopback Type1_lib CycloneDDS::ddsc)
|
||||||
5
sw/CycloneDDS/src/Loopback/Type1.idl
Normal file
5
sw/CycloneDDS/src/Loopback/Type1.idl
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
@extensibility(FINAL)
|
||||||
|
struct Type1 {
|
||||||
|
@key long id;
|
||||||
|
long a;
|
||||||
|
};
|
||||||
138
sw/CycloneDDS/src/Loopback/loopback.c
Normal file
138
sw/CycloneDDS/src/Loopback/loopback.c
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
#include "dds/dds.h"
|
||||||
|
#include "Type1.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define MAX_SAMPLES 1
|
||||||
|
|
||||||
|
int main (int argc, char ** argv){
|
||||||
|
dds_qos_t* qos;
|
||||||
|
dds_entity_t domain;
|
||||||
|
dds_entity_t participant;
|
||||||
|
dds_entity_t topic;
|
||||||
|
dds_entity_t writer;
|
||||||
|
dds_entity_t reader;
|
||||||
|
dds_return_t rc;
|
||||||
|
dds_instance_handle_t inst1, inst2;
|
||||||
|
void *samples[MAX_SAMPLES];
|
||||||
|
dds_sample_info_t infos[MAX_SAMPLES];
|
||||||
|
Type1 msg;
|
||||||
|
Type1* msgp;
|
||||||
|
uint32_t status = 0;
|
||||||
|
(void)argc;
|
||||||
|
(void)argv;
|
||||||
|
|
||||||
|
// Create Domain
|
||||||
|
domain = dds_create_domain(1, "<CycloneDDS> <Domain> <Compatibility> <ManySocketsMode> many </ManySocketsMode> </Compatibility> <Tracing> <PacketCaptureFile> cyclonedds.pcap </PacketCaptureFile> <Verbosity> finest </Verbosity> </Tracing> </Domain> </CycloneDDS>");
|
||||||
|
if (domain < 0) {
|
||||||
|
DDS_FATAL("dds_create_domain: %s\n", dds_strretcode(-domain));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Create QoS
|
||||||
|
qos = dds_create_qos();
|
||||||
|
if (qos == NULL) {
|
||||||
|
DDS_FATAL("dds_create_qos failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
//**RxO QoS**
|
||||||
|
dds_qset_durability(qos, DDS_DURABILITY_TRANSIENT_LOCAL);
|
||||||
|
dds_qset_durability_service(qos, 0, DDS_HISTORY_KEEP_LAST, 5, DDS_LENGTH_UNLIMITED, DDS_LENGTH_UNLIMITED, DDS_LENGTH_UNLIMITED);
|
||||||
|
//dds_qset_presentation(qos, DDS_PRESENTATION_INSTANCE, FALSE, FALSE);
|
||||||
|
//dds_qset_deadline(qos, DDS_INFINITY);
|
||||||
|
//dds_qset_latency_budget(qos, 0);
|
||||||
|
//dds_qset_ownership(qos, DDS_OWNERSHIP_SHARED);
|
||||||
|
//dds_qset_liveliness(qos, DDS_LIVELINESS_AUTOMATIC, DDS_INFINITY);
|
||||||
|
//dds_qset_reliability(qos, DDS_RELIABILITY_RELIABLE, DDS_MSECS(100));
|
||||||
|
//dds_qset_destination_order(qos, DDS_DESTINATIONORDER_BY_RECEPTION_TIMESTAMP);
|
||||||
|
|
||||||
|
//dds_qset_data_representation(qos, 1, (dds_data_representation_id_t[]) { DDS_DATA_REPRESENTATION_XCDR1 });
|
||||||
|
dds_qset_history(qos, DDS_HISTORY_KEEP_LAST, 5);
|
||||||
|
dds_qset_resource_limits(qos, 20, 5, 20);
|
||||||
|
|
||||||
|
//Create a Participant
|
||||||
|
participant = dds_create_participant(1, qos, NULL);
|
||||||
|
if (participant < 0) {
|
||||||
|
DDS_FATAL("dds_create_participant: %s\n", dds_strretcode(-participant));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Create a Topic
|
||||||
|
topic = dds_create_topic(participant, &Type1_desc, "Topic1", qos, NULL);
|
||||||
|
if (topic < 0) {
|
||||||
|
DDS_FATAL("dds_create_topic: %s\n", dds_strretcode(-topic));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Create a Writer
|
||||||
|
writer = dds_create_writer(participant, topic, qos, NULL);
|
||||||
|
if (writer < 0) {
|
||||||
|
DDS_FATAL("dds_create_writer: %s\n", dds_strretcode(-writer));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Create a Reader
|
||||||
|
reader = dds_create_reader (participant, topic, qos, NULL);
|
||||||
|
if (reader < 0) {
|
||||||
|
DDS_FATAL("dds_create_reader: %s\n", dds_strretcode(-reader));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register Instance
|
||||||
|
msg.id = 2;
|
||||||
|
rc = dds_register_instance(writer, &inst2, &msg);
|
||||||
|
if (rc != DDS_RETCODE_OK) {
|
||||||
|
DDS_FATAL("dds_register_instance: %s\n", dds_strretcode(-rc));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write 5 samples with Instance 1 (msg.id=1)
|
||||||
|
printf ("=== [Publisher] Writing:\n");
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
//Create a message to write
|
||||||
|
msg.id = 1;
|
||||||
|
msg.a = i;
|
||||||
|
|
||||||
|
printf ("Message id:%d, a:%d\n", msg.id, msg.a);
|
||||||
|
fflush (stdout);
|
||||||
|
|
||||||
|
rc = dds_write (writer, &msg);
|
||||||
|
if (rc != DDS_RETCODE_OK) {
|
||||||
|
DDS_FATAL("dds_write: %s\n", dds_strretcode(-rc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize Buffer
|
||||||
|
samples[0] = Type1__alloc();
|
||||||
|
|
||||||
|
// Read 5 samples with Instance 2 (msg.id=2)
|
||||||
|
printf ("=== [Subscriber] Received :\n");
|
||||||
|
for (int i = 0; i < 5;) {
|
||||||
|
|
||||||
|
rc = dds_take(reader, samples, infos, MAX_SAMPLES, MAX_SAMPLES);
|
||||||
|
// Error
|
||||||
|
if (rc < 0) {
|
||||||
|
DDS_FATAL("dds_read: %s\n", dds_strretcode(-rc));
|
||||||
|
}
|
||||||
|
// Sample Available (Only select relevant Instance)
|
||||||
|
else if ((rc > 0) && (infos[0].instance_handle == inst2) && infos[0].valid_data) {
|
||||||
|
msgp = (Type1*)samples[0];
|
||||||
|
printf ("Message id:%d, a:%d\n", msgp->id, msgp->a);
|
||||||
|
fflush (stdout);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
// No Data
|
||||||
|
else {
|
||||||
|
// Polling sleep
|
||||||
|
dds_sleepfor(DDS_MSECS(20));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free buffer
|
||||||
|
Type1_free(samples[0], DDS_FREE_ALL);
|
||||||
|
|
||||||
|
// Delete QoS
|
||||||
|
dds_delete_qos(qos);
|
||||||
|
|
||||||
|
// Deleting the participant will delete all its children recursively as well
|
||||||
|
rc = dds_delete(participant);
|
||||||
|
if (rc != DDS_RETCODE_OK) {
|
||||||
|
DDS_FATAL("dds_delete: %s\n", dds_strretcode(-rc));
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
1
sw/CycloneDDS/src/helloworld/.gitignore
vendored
Normal file
1
sw/CycloneDDS/src/helloworld/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
build/**
|
||||||
33
sw/CycloneDDS/src/helloworld/CMakeLists.txt
Normal file
33
sw/CycloneDDS/src/helloworld/CMakeLists.txt
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#
|
||||||
|
# Copyright(c) 2021 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
|
||||||
|
#
|
||||||
|
project(helloword LANGUAGES C)
|
||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
|
if(NOT TARGET CycloneDDS::ddsc)
|
||||||
|
# Find the CycloneDDS package.
|
||||||
|
find_package(CycloneDDS REQUIRED)
|
||||||
|
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(TARGET HelloWorldData_lib FILES "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)
|
||||||
9
sw/CycloneDDS/src/helloworld/HelloWorldData.idl
Normal file
9
sw/CycloneDDS/src/helloworld/HelloWorldData.idl
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module HelloWorldData
|
||||||
|
{
|
||||||
|
struct Msg
|
||||||
|
{
|
||||||
|
long userID;
|
||||||
|
string message;
|
||||||
|
};
|
||||||
|
#pragma keylist Msg userID
|
||||||
|
};
|
||||||
74
sw/CycloneDDS/src/helloworld/publisher.c
Normal file
74
sw/CycloneDDS/src/helloworld/publisher.c
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
#include "dds/dds.h"
|
||||||
|
#include "HelloWorldData.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
int main (int argc, char ** argv)
|
||||||
|
{
|
||||||
|
dds_entity_t domain;
|
||||||
|
dds_entity_t participant;
|
||||||
|
dds_entity_t topic;
|
||||||
|
dds_entity_t writer;
|
||||||
|
dds_return_t rc;
|
||||||
|
HelloWorldData_Msg msg;
|
||||||
|
uint32_t status = 0;
|
||||||
|
(void)argc;
|
||||||
|
(void)argv;
|
||||||
|
|
||||||
|
/* Create a Domain. */
|
||||||
|
domain = dds_create_domain(1, "<CycloneDDS> <Domain> <Tracing> <PacketCaptureFile> cyclonedds.pcap </PacketCaptureFile> <Verbosity> finest </Verbosity> </Tracing> </Domain> </CycloneDDS>");
|
||||||
|
if (domain < 0)
|
||||||
|
DDS_FATAL("dds_create_domain: %s\n", dds_strretcode(-domain));
|
||||||
|
|
||||||
|
/* Create a Participant. */
|
||||||
|
participant = dds_create_participant (1, NULL, NULL);
|
||||||
|
if (participant < 0)
|
||||||
|
DDS_FATAL("dds_create_participant: %s\n", dds_strretcode(-participant));
|
||||||
|
|
||||||
|
/* Create a Topic. */
|
||||||
|
topic = dds_create_topic (
|
||||||
|
participant, &HelloWorldData_Msg_desc, "HelloWorldData_Msg", NULL, NULL);
|
||||||
|
if (topic < 0)
|
||||||
|
DDS_FATAL("dds_create_topic: %s\n", dds_strretcode(-topic));
|
||||||
|
|
||||||
|
/* Create a Writer. */
|
||||||
|
writer = dds_create_writer (participant, topic, NULL, NULL);
|
||||||
|
if (writer < 0)
|
||||||
|
DDS_FATAL("dds_create_writer: %s\n", dds_strretcode(-writer));
|
||||||
|
|
||||||
|
printf("=== [Publisher] Waiting for a reader to be discovered ...\n");
|
||||||
|
fflush (stdout);
|
||||||
|
|
||||||
|
rc = dds_set_status_mask(writer, DDS_PUBLICATION_MATCHED_STATUS);
|
||||||
|
if (rc != DDS_RETCODE_OK)
|
||||||
|
DDS_FATAL("dds_set_status_mask: %s\n", dds_strretcode(-rc));
|
||||||
|
|
||||||
|
while(!(status & DDS_PUBLICATION_MATCHED_STATUS))
|
||||||
|
{
|
||||||
|
rc = dds_get_status_changes (writer, &status);
|
||||||
|
if (rc != DDS_RETCODE_OK)
|
||||||
|
DDS_FATAL("dds_get_status_changes: %s\n", dds_strretcode(-rc));
|
||||||
|
|
||||||
|
/* Polling sleep. */
|
||||||
|
dds_sleepfor (DDS_MSECS (20));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create a message to write. */
|
||||||
|
msg.userID = 1;
|
||||||
|
msg.message = "Hello World";
|
||||||
|
|
||||||
|
printf ("=== [Publisher] Writing : ");
|
||||||
|
printf ("Message (%"PRId32", %s)\n", msg.userID, msg.message);
|
||||||
|
fflush (stdout);
|
||||||
|
|
||||||
|
rc = dds_write (writer, &msg);
|
||||||
|
if (rc != DDS_RETCODE_OK)
|
||||||
|
DDS_FATAL("dds_write: %s\n", dds_strretcode(-rc));
|
||||||
|
|
||||||
|
/* Deleting the participant will delete all its children recursively as well. */
|
||||||
|
rc = dds_delete (participant);
|
||||||
|
if (rc != DDS_RETCODE_OK)
|
||||||
|
DDS_FATAL("dds_delete: %s\n", dds_strretcode(-rc));
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
46
sw/CycloneDDS/src/helloworld/readme.rst
Normal file
46
sw/CycloneDDS/src/helloworld/readme.rst
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
..
|
||||||
|
Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
|
||||||
|
|
||||||
|
This program and the accompanying materials are made available under the
|
||||||
|
terms of the Eclipse Public License v. 2.0 which is available at
|
||||||
|
http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||||
|
v. 1.0 which is available at
|
||||||
|
http://www.eclipse.org/org/documents/edl-v10.php.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||||
|
|
||||||
|
HelloWorld
|
||||||
|
==========
|
||||||
|
|
||||||
|
Description
|
||||||
|
***********
|
||||||
|
|
||||||
|
The basic HelloWorld example is used to illustrate the necessary steps to setup DCPS entities.
|
||||||
|
Note it is also used in the Getting Started Guide to explain the usage of Eclipse Cyclone DDS.
|
||||||
|
|
||||||
|
Design
|
||||||
|
******
|
||||||
|
|
||||||
|
It consists of 2 units:
|
||||||
|
|
||||||
|
- HelloworldPublisher: implements the publisher's main
|
||||||
|
- HelloworldSubscriber: implements the subscriber's main
|
||||||
|
|
||||||
|
Scenario
|
||||||
|
********
|
||||||
|
|
||||||
|
The publisher sends a single HelloWorld sample. The sample contains two fields:
|
||||||
|
|
||||||
|
- a userID field (long type)
|
||||||
|
- a message field (string type)
|
||||||
|
|
||||||
|
When it receives the sample, the subscriber displays the userID and the message field.
|
||||||
|
|
||||||
|
Running the example
|
||||||
|
*******************
|
||||||
|
|
||||||
|
It is recommended that you run the subscriber and publisher in separate terminals to avoid mixing the output.
|
||||||
|
|
||||||
|
- Open 2 terminals.
|
||||||
|
- In the first terminal start the subscriber by running HelloWorldSubscriber
|
||||||
|
- In the second terminal start the publisher by running HelloWorldPublisher
|
||||||
84
sw/CycloneDDS/src/helloworld/subscriber.c
Normal file
84
sw/CycloneDDS/src/helloworld/subscriber.c
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
#include "dds/dds.h"
|
||||||
|
#include "HelloWorldData.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/* An array of one message (aka sample in dds terms) will be used. */
|
||||||
|
#define MAX_SAMPLES 1
|
||||||
|
|
||||||
|
int main (int argc, char ** argv)
|
||||||
|
{
|
||||||
|
dds_entity_t participant;
|
||||||
|
dds_entity_t topic;
|
||||||
|
dds_entity_t reader;
|
||||||
|
HelloWorldData_Msg *msg;
|
||||||
|
void *samples[MAX_SAMPLES];
|
||||||
|
dds_sample_info_t infos[MAX_SAMPLES];
|
||||||
|
dds_return_t rc;
|
||||||
|
dds_qos_t *qos;
|
||||||
|
(void)argc;
|
||||||
|
(void)argv;
|
||||||
|
|
||||||
|
/* Create a Participant. */
|
||||||
|
participant = dds_create_participant (1, NULL, NULL);
|
||||||
|
if (participant < 0)
|
||||||
|
DDS_FATAL("dds_create_participant: %s\n", dds_strretcode(-participant));
|
||||||
|
|
||||||
|
/* Create a Topic. */
|
||||||
|
topic = dds_create_topic (
|
||||||
|
participant, &HelloWorldData_Msg_desc, "HelloWorldData_Msg", NULL, NULL);
|
||||||
|
if (topic < 0)
|
||||||
|
DDS_FATAL("dds_create_topic: %s\n", dds_strretcode(-topic));
|
||||||
|
|
||||||
|
/* Create a reliable Reader. */
|
||||||
|
qos = dds_create_qos ();
|
||||||
|
dds_qset_reliability (qos, DDS_RELIABILITY_RELIABLE, DDS_SECS (10));
|
||||||
|
reader = dds_create_reader (participant, topic, qos, NULL);
|
||||||
|
if (reader < 0)
|
||||||
|
DDS_FATAL("dds_create_reader: %s\n", dds_strretcode(-reader));
|
||||||
|
dds_delete_qos(qos);
|
||||||
|
|
||||||
|
printf ("\n=== [Subscriber] Waiting for a sample ...\n");
|
||||||
|
fflush (stdout);
|
||||||
|
|
||||||
|
/* Initialize sample buffer, by pointing the void pointer within
|
||||||
|
* the buffer array to a valid sample memory location. */
|
||||||
|
samples[0] = HelloWorldData_Msg__alloc ();
|
||||||
|
|
||||||
|
/* Poll until data has been read. */
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
/* Do the actual read.
|
||||||
|
* The return value contains the number of read samples. */
|
||||||
|
rc = dds_read (reader, samples, infos, MAX_SAMPLES, MAX_SAMPLES);
|
||||||
|
if (rc < 0)
|
||||||
|
DDS_FATAL("dds_read: %s\n", dds_strretcode(-rc));
|
||||||
|
|
||||||
|
/* Check if we read some data and it is valid. */
|
||||||
|
if ((rc > 0) && (infos[0].valid_data))
|
||||||
|
{
|
||||||
|
/* Print Message. */
|
||||||
|
msg = (HelloWorldData_Msg*) samples[0];
|
||||||
|
printf ("=== [Subscriber] Received : ");
|
||||||
|
printf ("Message (%"PRId32", %s)\n", msg->userID, msg->message);
|
||||||
|
fflush (stdout);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Polling sleep. */
|
||||||
|
dds_sleepfor (DDS_MSECS (20));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free the data location. */
|
||||||
|
HelloWorldData_Msg_free (samples[0], DDS_FREE_ALL);
|
||||||
|
|
||||||
|
/* Deleting the participant will delete all its children recursively as well. */
|
||||||
|
rc = dds_delete (participant);
|
||||||
|
if (rc != DDS_RETCODE_OK)
|
||||||
|
DDS_FATAL("dds_delete: %s\n", dds_strretcode(-rc));
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
8
sw/ros/.gitignore
vendored
Normal file
8
sw/ros/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#Ignore Everything
|
||||||
|
*
|
||||||
|
|
||||||
|
#WHITELIST
|
||||||
|
!.gitignore
|
||||||
|
!README.txt
|
||||||
|
!src
|
||||||
|
!src/**
|
||||||
50
sw/ros/README.txt
Normal file
50
sw/ros/README.txt
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
Tested with 'ros:galactic-ros-base' Docker Image
|
||||||
|
|
||||||
|
Open ROS Docker Container inside /test directory
|
||||||
|
|
||||||
|
Install missing dependencies (May need "apt update" before, sudo pass is "x11docker")
|
||||||
|
$ apt update
|
||||||
|
$ rosdep update --rosdistro=galactic
|
||||||
|
$ rosdep install -i --from-path src --rosdistro galactic -y
|
||||||
|
|
||||||
|
Build ROS packages
|
||||||
|
$ colcon build
|
||||||
|
|
||||||
|
Source Setup Files
|
||||||
|
$ . install/setup.bash
|
||||||
|
|
||||||
|
CPP_SRVCLI
|
||||||
|
----------
|
||||||
|
|
||||||
|
$ export CYCLONEDDS_URI='<Compatibility><ManySocketsMode>many</ManySocketsMode></Compatibility><Tracing><Verbosity>finest</Verbosity><Out>/home/john64/Desktop/rtps-fpga/download/rtps-test/ros/cyclonedds.log</Out></Tracing>'
|
||||||
|
$ export CYCLONEDDS_URI='<Tracing><Verbosity>finest</Verbosity><Out>/home/john64/Desktop/rtps-fpga/download/rtps-test/ros/cyclonedds.log</Out></Tracing>'
|
||||||
|
|
||||||
|
$ ros2 run cpp_srvcli server
|
||||||
|
|
||||||
|
$ ros2 run cpp_srvcli client 2 3
|
||||||
|
|
||||||
|
ACTION_TUTORIALS_CPP
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
$ export CYCLONEDDS_URI='<Compatibility><ManySocketsMode>many</ManySocketsMode></Compatibility>'
|
||||||
|
|
||||||
|
$ ros2 run action_tutorials_cpp fibonacci_action_server
|
||||||
|
|
||||||
|
$ ros2 run action_tutorials_cpp fibonacci_action_client
|
||||||
|
|
||||||
|
CPP_LOOPBACK
|
||||||
|
------------
|
||||||
|
|
||||||
|
$ export CYCLONEDDS_URI='<Compatibility><ManySocketsMode>many</ManySocketsMode></Compatibility><Tracing><Verbosity>finest</Verbosity><Out>/home/john64/Desktop/rtps-fpga/download/rtps-test/ros/cyclonedds.log</Out></Tracing>'
|
||||||
|
$ export CYCLONEDDS_URI='<Compatibility><ManySocketsMode>many</ManySocketsMode></Compatibility>'
|
||||||
|
|
||||||
|
$ ros2 run cpp_loopback server
|
||||||
|
|
||||||
|
$ ros2 run cpp_loopback client
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
MISC
|
||||||
|
====
|
||||||
|
x11docker -it --network=host --sudouser --home='/home/john64' -- '--' 'ros:galactic-ros-base' 'bash'
|
||||||
98
sw/ros/src/action_tutorials_cpp/CMakeLists.txt
Normal file
98
sw/ros/src/action_tutorials_cpp/CMakeLists.txt
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.8)
|
||||||
|
project(action_tutorials_cpp)
|
||||||
|
|
||||||
|
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
|
add_compile_options(-Wall -Wextra -Wpedantic)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# find dependencies
|
||||||
|
find_package(ament_cmake REQUIRED)
|
||||||
|
find_package(action_tutorials_interfaces REQUIRED)
|
||||||
|
find_package(rclcpp REQUIRED)
|
||||||
|
find_package(rclcpp_action REQUIRED)
|
||||||
|
find_package(rclcpp_components REQUIRED)
|
||||||
|
|
||||||
|
if(BUILD_TESTING)
|
||||||
|
find_package(ament_lint_auto REQUIRED)
|
||||||
|
# the following line skips the linter which checks for copyrights
|
||||||
|
# uncomment the line when a copyright and license is not present in all source files
|
||||||
|
#set(ament_cmake_copyright_FOUND TRUE)
|
||||||
|
# the following line skips cpplint (only works in a git repo)
|
||||||
|
# uncomment the line when this package is not in a git repo
|
||||||
|
#set(ament_cmake_cpplint_FOUND TRUE)
|
||||||
|
ament_lint_auto_find_test_dependencies()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library(
|
||||||
|
action_server
|
||||||
|
SHARED
|
||||||
|
src/fibonacci_action_server.cpp
|
||||||
|
)
|
||||||
|
add_library(
|
||||||
|
action_client
|
||||||
|
SHARED
|
||||||
|
src/fibonacci_action_client.cpp
|
||||||
|
)
|
||||||
|
target_include_directories(
|
||||||
|
action_server
|
||||||
|
PRIVATE
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||||
|
$<INSTALL_INTERFACE:include>
|
||||||
|
)
|
||||||
|
target_include_directories(
|
||||||
|
action_client
|
||||||
|
PRIVATE
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||||
|
$<INSTALL_INTERFACE:include>
|
||||||
|
)
|
||||||
|
target_compile_definitions(
|
||||||
|
action_server
|
||||||
|
PRIVATE
|
||||||
|
"ACTION_TUTORIALS_CPP_BUILDING_DLL"
|
||||||
|
)
|
||||||
|
target_compile_definitions(
|
||||||
|
action_client
|
||||||
|
PRIVATE
|
||||||
|
"ACTION_TUTORIALS_CPP_BUILDING_DLL"
|
||||||
|
)
|
||||||
|
ament_target_dependencies(
|
||||||
|
action_server
|
||||||
|
"action_tutorials_interfaces"
|
||||||
|
"rclcpp"
|
||||||
|
"rclcpp_action"
|
||||||
|
"rclcpp_components"
|
||||||
|
)
|
||||||
|
ament_target_dependencies(
|
||||||
|
action_client
|
||||||
|
"action_tutorials_interfaces"
|
||||||
|
"rclcpp"
|
||||||
|
"rclcpp_action"
|
||||||
|
"rclcpp_components"
|
||||||
|
)
|
||||||
|
rclcpp_components_register_node(
|
||||||
|
action_server
|
||||||
|
PLUGIN
|
||||||
|
"action_tutorials_cpp::FibonacciActionServer"
|
||||||
|
EXECUTABLE
|
||||||
|
fibonacci_action_server
|
||||||
|
)
|
||||||
|
rclcpp_components_register_node(
|
||||||
|
action_client
|
||||||
|
PLUGIN
|
||||||
|
"action_tutorials_cpp::FibonacciActionClient"
|
||||||
|
EXECUTABLE
|
||||||
|
fibonacci_action_client
|
||||||
|
)
|
||||||
|
install(
|
||||||
|
TARGETS
|
||||||
|
action_server
|
||||||
|
action_client
|
||||||
|
ARCHIVE DESTINATION
|
||||||
|
lib
|
||||||
|
LIBRARY DESTINATION
|
||||||
|
lib
|
||||||
|
RUNTIME DESTINATION
|
||||||
|
bin
|
||||||
|
)
|
||||||
|
|
||||||
|
ament_package()
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
#ifndef ACTION_TUTORIALS_CPP__VISIBILITY_CONTROL_H_
|
||||||
|
#define ACTION_TUTORIALS_CPP__VISIBILITY_CONTROL_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// This logic was borrowed (then namespaced) from the examples on the gcc wiki:
|
||||||
|
// https://gcc.gnu.org/wiki/Visibility
|
||||||
|
|
||||||
|
#if defined _WIN32 || defined __CYGWIN__
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#define ACTION_TUTORIALS_CPP_EXPORT __attribute__ ((dllexport))
|
||||||
|
#define ACTION_TUTORIALS_CPP_IMPORT __attribute__ ((dllimport))
|
||||||
|
#else
|
||||||
|
#define ACTION_TUTORIALS_CPP_EXPORT __declspec(dllexport)
|
||||||
|
#define ACTION_TUTORIALS_CPP_IMPORT __declspec(dllimport)
|
||||||
|
#endif
|
||||||
|
#ifdef ACTION_TUTORIALS_CPP_BUILDING_DLL
|
||||||
|
#define ACTION_TUTORIALS_CPP_PUBLIC ACTION_TUTORIALS_CPP_EXPORT
|
||||||
|
#else
|
||||||
|
#define ACTION_TUTORIALS_CPP_PUBLIC ACTION_TUTORIALS_CPP_IMPORT
|
||||||
|
#endif
|
||||||
|
#define ACTION_TUTORIALS_CPP_PUBLIC_TYPE ACTION_TUTORIALS_CPP_PUBLIC
|
||||||
|
#define ACTION_TUTORIALS_CPP_LOCAL
|
||||||
|
#else
|
||||||
|
#define ACTION_TUTORIALS_CPP_EXPORT __attribute__ ((visibility("default")))
|
||||||
|
#define ACTION_TUTORIALS_CPP_IMPORT
|
||||||
|
#if __GNUC__ >= 4
|
||||||
|
#define ACTION_TUTORIALS_CPP_PUBLIC __attribute__ ((visibility("default")))
|
||||||
|
#define ACTION_TUTORIALS_CPP_LOCAL __attribute__ ((visibility("hidden")))
|
||||||
|
#else
|
||||||
|
#define ACTION_TUTORIALS_CPP_PUBLIC
|
||||||
|
#define ACTION_TUTORIALS_CPP_LOCAL
|
||||||
|
#endif
|
||||||
|
#define ACTION_TUTORIALS_CPP_PUBLIC_TYPE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // ACTION_TUTORIALS_CPP__VISIBILITY_CONTROL_H_
|
||||||
23
sw/ros/src/action_tutorials_cpp/package.xml
Normal file
23
sw/ros/src/action_tutorials_cpp/package.xml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||||
|
<package format="3">
|
||||||
|
<name>action_tutorials_cpp</name>
|
||||||
|
<version>0.0.0</version>
|
||||||
|
<description>TODO: Package description</description>
|
||||||
|
<maintainer email="greek64.mail@gmail.com">john64</maintainer>
|
||||||
|
<license>TODO: License declaration</license>
|
||||||
|
|
||||||
|
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||||
|
|
||||||
|
<depend>action_tutorials_interfaces</depend>
|
||||||
|
<depend>rclcpp</depend>
|
||||||
|
<depend>rclcpp_action</depend>
|
||||||
|
<depend>rclcpp_components</depend>
|
||||||
|
|
||||||
|
<test_depend>ament_lint_auto</test_depend>
|
||||||
|
<test_depend>ament_lint_common</test_depend>
|
||||||
|
|
||||||
|
<export>
|
||||||
|
<build_type>ament_cmake</build_type>
|
||||||
|
</export>
|
||||||
|
</package>
|
||||||
110
sw/ros/src/action_tutorials_cpp/src/fibonacci_action_client.cpp
Normal file
110
sw/ros/src/action_tutorials_cpp/src/fibonacci_action_client.cpp
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
#include <functional>
|
||||||
|
#include <future>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "action_tutorials_interfaces/action/fibonacci.hpp"
|
||||||
|
|
||||||
|
#include "rclcpp/rclcpp.hpp"
|
||||||
|
#include "rclcpp_action/rclcpp_action.hpp"
|
||||||
|
#include "rclcpp_components/register_node_macro.hpp"
|
||||||
|
|
||||||
|
namespace action_tutorials_cpp
|
||||||
|
{
|
||||||
|
class FibonacciActionClient : public rclcpp::Node
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Fibonacci = action_tutorials_interfaces::action::Fibonacci;
|
||||||
|
using GoalHandleFibonacci = rclcpp_action::ClientGoalHandle<Fibonacci>;
|
||||||
|
|
||||||
|
explicit FibonacciActionClient(const rclcpp::NodeOptions & options)
|
||||||
|
: Node("fibonacci_action_client", options)
|
||||||
|
{
|
||||||
|
this->client_ptr_ = rclcpp_action::create_client<Fibonacci>(this,"fibonacci");
|
||||||
|
|
||||||
|
this->timer_ = this->create_wall_timer(
|
||||||
|
std::chrono::milliseconds(500),
|
||||||
|
std::bind(&FibonacciActionClient::send_goal, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_goal()
|
||||||
|
{
|
||||||
|
using namespace std::placeholders;
|
||||||
|
|
||||||
|
this->timer_->cancel();
|
||||||
|
|
||||||
|
if (!this->client_ptr_->wait_for_action_server()) {
|
||||||
|
RCLCPP_ERROR(this->get_logger(), "Action server not available after waiting");
|
||||||
|
rclcpp::shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto goal_msg = Fibonacci::Goal();
|
||||||
|
goal_msg.order = 10;
|
||||||
|
|
||||||
|
RCLCPP_INFO(this->get_logger(), "Sending goal");
|
||||||
|
|
||||||
|
auto send_goal_options = rclcpp_action::Client<Fibonacci>::SendGoalOptions();
|
||||||
|
send_goal_options.goal_response_callback =
|
||||||
|
std::bind(&FibonacciActionClient::goal_response_callback, this, _1);
|
||||||
|
send_goal_options.feedback_callback =
|
||||||
|
std::bind(&FibonacciActionClient::feedback_callback, this, _1, _2);
|
||||||
|
send_goal_options.result_callback =
|
||||||
|
std::bind(&FibonacciActionClient::result_callback, this, _1);
|
||||||
|
this->client_ptr_->async_send_goal(goal_msg, send_goal_options);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
rclcpp_action::Client<Fibonacci>::SharedPtr client_ptr_;
|
||||||
|
rclcpp::TimerBase::SharedPtr timer_;
|
||||||
|
|
||||||
|
void goal_response_callback(std::shared_future<GoalHandleFibonacci::SharedPtr> future)
|
||||||
|
{
|
||||||
|
auto goal_handle = future.get();
|
||||||
|
if (!goal_handle) {
|
||||||
|
RCLCPP_ERROR(this->get_logger(), "Goal was rejected by server");
|
||||||
|
} else {
|
||||||
|
RCLCPP_INFO(this->get_logger(), "Goal accepted by server, waiting for result");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void feedback_callback(
|
||||||
|
GoalHandleFibonacci::SharedPtr,
|
||||||
|
const std::shared_ptr<const Fibonacci::Feedback> feedback)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "Next number in sequence received: ";
|
||||||
|
for (auto number : feedback->partial_sequence) {
|
||||||
|
ss << number << " ";
|
||||||
|
}
|
||||||
|
RCLCPP_INFO(this->get_logger(), ss.str().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void result_callback(const GoalHandleFibonacci::WrappedResult & result)
|
||||||
|
{
|
||||||
|
switch (result.code) {
|
||||||
|
case rclcpp_action::ResultCode::SUCCEEDED:
|
||||||
|
break;
|
||||||
|
case rclcpp_action::ResultCode::ABORTED:
|
||||||
|
RCLCPP_ERROR(this->get_logger(), "Goal was aborted");
|
||||||
|
return;
|
||||||
|
case rclcpp_action::ResultCode::CANCELED:
|
||||||
|
RCLCPP_ERROR(this->get_logger(), "Goal was canceled");
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
RCLCPP_ERROR(this->get_logger(), "Unknown result code");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "Result received: ";
|
||||||
|
for (auto number : result.result->sequence) {
|
||||||
|
ss << number << " ";
|
||||||
|
}
|
||||||
|
RCLCPP_INFO(this->get_logger(), ss.str().c_str());
|
||||||
|
rclcpp::shutdown();
|
||||||
|
}
|
||||||
|
}; // class FibonacciActionClient
|
||||||
|
|
||||||
|
} // namespace action_tutorials_cpp
|
||||||
|
|
||||||
|
RCLCPP_COMPONENTS_REGISTER_NODE(action_tutorials_cpp::FibonacciActionClient)
|
||||||
100
sw/ros/src/action_tutorials_cpp/src/fibonacci_action_server.cpp
Normal file
100
sw/ros/src/action_tutorials_cpp/src/fibonacci_action_server.cpp
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "action_tutorials_interfaces/action/fibonacci.hpp"
|
||||||
|
#include "rclcpp/rclcpp.hpp"
|
||||||
|
#include "rclcpp_action/rclcpp_action.hpp"
|
||||||
|
#include "rclcpp_components/register_node_macro.hpp"
|
||||||
|
|
||||||
|
#include "action_tutorials_cpp/visibility_control.h"
|
||||||
|
|
||||||
|
namespace action_tutorials_cpp
|
||||||
|
{
|
||||||
|
class FibonacciActionServer : public rclcpp::Node
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Fibonacci = action_tutorials_interfaces::action::Fibonacci;
|
||||||
|
using GoalHandleFibonacci = rclcpp_action::ServerGoalHandle<Fibonacci>;
|
||||||
|
|
||||||
|
ACTION_TUTORIALS_CPP_PUBLIC
|
||||||
|
explicit FibonacciActionServer(const rclcpp::NodeOptions & options = rclcpp::NodeOptions())
|
||||||
|
: Node("fibonacci_action_server", options)
|
||||||
|
{
|
||||||
|
using namespace std::placeholders;
|
||||||
|
|
||||||
|
this->action_server_ = rclcpp_action::create_server<Fibonacci>(
|
||||||
|
this,
|
||||||
|
"fibonacci",
|
||||||
|
std::bind(&FibonacciActionServer::handle_goal, this, _1, _2),
|
||||||
|
std::bind(&FibonacciActionServer::handle_cancel, this, _1),
|
||||||
|
std::bind(&FibonacciActionServer::handle_accepted, this, _1));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
rclcpp_action::Server<Fibonacci>::SharedPtr action_server_;
|
||||||
|
|
||||||
|
rclcpp_action::GoalResponse handle_goal(
|
||||||
|
const rclcpp_action::GoalUUID & uuid,
|
||||||
|
std::shared_ptr<const Fibonacci::Goal> goal)
|
||||||
|
{
|
||||||
|
RCLCPP_INFO(this->get_logger(), "Received goal request with order %d", goal->order);
|
||||||
|
(void)uuid;
|
||||||
|
return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
|
||||||
|
}
|
||||||
|
|
||||||
|
rclcpp_action::CancelResponse handle_cancel(
|
||||||
|
const std::shared_ptr<GoalHandleFibonacci> goal_handle)
|
||||||
|
{
|
||||||
|
RCLCPP_INFO(this->get_logger(), "Received request to cancel goal");
|
||||||
|
(void)goal_handle;
|
||||||
|
return rclcpp_action::CancelResponse::ACCEPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_accepted(const std::shared_ptr<GoalHandleFibonacci> goal_handle)
|
||||||
|
{
|
||||||
|
using namespace std::placeholders;
|
||||||
|
// this needs to return quickly to avoid blocking the executor, so spin up a new thread
|
||||||
|
std::thread{std::bind(&FibonacciActionServer::execute, this, _1), goal_handle}.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute(const std::shared_ptr<GoalHandleFibonacci> goal_handle)
|
||||||
|
{
|
||||||
|
RCLCPP_INFO(this->get_logger(), "Executing goal");
|
||||||
|
rclcpp::Rate loop_rate(1);
|
||||||
|
const auto goal = goal_handle->get_goal();
|
||||||
|
auto feedback = std::make_shared<Fibonacci::Feedback>();
|
||||||
|
auto & sequence = feedback->partial_sequence;
|
||||||
|
sequence.push_back(0);
|
||||||
|
sequence.push_back(1);
|
||||||
|
auto result = std::make_shared<Fibonacci::Result>();
|
||||||
|
|
||||||
|
for (int i = 1; (i < goal->order) && rclcpp::ok(); ++i) {
|
||||||
|
// Check if there is a cancel request
|
||||||
|
if (goal_handle->is_canceling()) {
|
||||||
|
result->sequence = sequence;
|
||||||
|
goal_handle->canceled(result);
|
||||||
|
RCLCPP_INFO(this->get_logger(), "Goal canceled");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Update sequence
|
||||||
|
sequence.push_back(sequence[i] + sequence[i - 1]);
|
||||||
|
// Publish feedback
|
||||||
|
goal_handle->publish_feedback(feedback);
|
||||||
|
RCLCPP_INFO(this->get_logger(), "Publish feedback");
|
||||||
|
|
||||||
|
loop_rate.sleep();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if goal is done
|
||||||
|
if (rclcpp::ok()) {
|
||||||
|
result->sequence = sequence;
|
||||||
|
goal_handle->succeed(result);
|
||||||
|
RCLCPP_INFO(this->get_logger(), "Goal succeeded");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}; // class FibonacciActionServer
|
||||||
|
|
||||||
|
} // namespace action_tutorials_cpp
|
||||||
|
|
||||||
|
RCLCPP_COMPONENTS_REGISTER_NODE(action_tutorials_cpp::FibonacciActionServer)
|
||||||
35
sw/ros/src/cpp_loopback/CMakeLists.txt
Normal file
35
sw/ros/src/cpp_loopback/CMakeLists.txt
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.8)
|
||||||
|
project(cpp_loopback)
|
||||||
|
|
||||||
|
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
|
add_compile_options(-Wall -Wextra -Wpedantic)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# find dependencies
|
||||||
|
find_package(ament_cmake REQUIRED)
|
||||||
|
find_package(rclcpp REQUIRED)
|
||||||
|
find_package(tutorial_interfaces REQUIRED)
|
||||||
|
|
||||||
|
if(BUILD_TESTING)
|
||||||
|
find_package(ament_lint_auto REQUIRED)
|
||||||
|
# the following line skips the linter which checks for copyrights
|
||||||
|
# uncomment the line when a copyright and license is not present in all source files
|
||||||
|
#set(ament_cmake_copyright_FOUND TRUE)
|
||||||
|
# the following line skips cpplint (only works in a git repo)
|
||||||
|
# uncomment the line when this package is not in a git repo
|
||||||
|
#set(ament_cmake_cpplint_FOUND TRUE)
|
||||||
|
ament_lint_auto_find_test_dependencies()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_executable(server src/loopback_server.cpp)
|
||||||
|
ament_target_dependencies(server rclcpp tutorial_interfaces)
|
||||||
|
|
||||||
|
add_executable(client src/loopback_client.cpp)
|
||||||
|
ament_target_dependencies(client rclcpp tutorial_interfaces)
|
||||||
|
|
||||||
|
install(TARGETS
|
||||||
|
server
|
||||||
|
client
|
||||||
|
DESTINATION lib/${PROJECT_NAME})
|
||||||
|
|
||||||
|
ament_package()
|
||||||
21
sw/ros/src/cpp_loopback/package.xml
Normal file
21
sw/ros/src/cpp_loopback/package.xml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||||
|
<package format="3">
|
||||||
|
<name>cpp_loopback</name>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
<description>A Loopback implementation using ROS Publisher/Subscriber used to compare the RTT between SW and FPGA implementation.</description>
|
||||||
|
<maintainer email="greek64.mail@gmail.com">john64</maintainer>
|
||||||
|
<license>TODO: License declaration</license>
|
||||||
|
|
||||||
|
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||||
|
|
||||||
|
<depend>rclcpp</depend>
|
||||||
|
<depend>tutorial_interfaces</depend>
|
||||||
|
|
||||||
|
<test_depend>ament_lint_auto</test_depend>
|
||||||
|
<test_depend>ament_lint_common</test_depend>
|
||||||
|
|
||||||
|
<export>
|
||||||
|
<build_type>ament_cmake</build_type>
|
||||||
|
</export>
|
||||||
|
</package>
|
||||||
89
sw/ros/src/cpp_loopback/src/loopback_client.cpp
Normal file
89
sw/ros/src/cpp_loopback/src/loopback_client.cpp
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
#include "rclcpp/rclcpp.hpp"
|
||||||
|
#include "rmw/types.h"
|
||||||
|
#include "tutorial_interfaces/msg/type1.hpp"
|
||||||
|
#include "rcl/time.h"
|
||||||
|
|
||||||
|
rclcpp::Node::SharedPtr node;
|
||||||
|
rclcpp::Publisher<tutorial_interfaces::msg::Type1>::SharedPtr pub;
|
||||||
|
rclcpp::Subscription<tutorial_interfaces::msg::Type1>::SharedPtr sub;
|
||||||
|
rclcpp::Clock::SharedPtr steady_clock;
|
||||||
|
rclcpp::TimerBase::SharedPtr timer;
|
||||||
|
|
||||||
|
void sub_callback(const tutorial_interfaces::msg::Type1 & msg){
|
||||||
|
if (msg.id == 1){
|
||||||
|
// Send Response
|
||||||
|
auto resp = tutorial_interfaces::msg::Type1();
|
||||||
|
resp.id = 2;
|
||||||
|
resp.a = msg.a;
|
||||||
|
pub->publish(resp);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
RCLCPP_WARN(node->get_logger(), "Unexpected ID received: %d", msg.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void timer_callback(){
|
||||||
|
auto resp = tutorial_interfaces::msg::Type1();
|
||||||
|
// Publish Input Utilization
|
||||||
|
resp.id = 3;
|
||||||
|
resp.a = 16384;
|
||||||
|
pub->publish(resp);
|
||||||
|
// Publish Ouput Utilization
|
||||||
|
resp.id = 4;
|
||||||
|
resp.a = 8192;
|
||||||
|
pub->publish(resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv){
|
||||||
|
rclcpp::init(argc, argv);
|
||||||
|
|
||||||
|
//Create Node
|
||||||
|
rclcpp::NodeOptions node_options = rclcpp::NodeOptions();
|
||||||
|
node_options.use_global_arguments(false);
|
||||||
|
node_options.enable_rosout(false);
|
||||||
|
node_options.use_intra_process_comms(false);
|
||||||
|
node_options.enable_topic_statistics(false);
|
||||||
|
node_options.start_parameter_services(false);
|
||||||
|
node_options.start_parameter_event_publisher(false);
|
||||||
|
node_options.use_clock_thread(false);
|
||||||
|
node_options.allow_undeclared_parameters(false);
|
||||||
|
node_options.automatically_declare_parameters_from_overrides(false);
|
||||||
|
|
||||||
|
node = rclcpp::Node::make_shared("loopback_client",node_options);
|
||||||
|
|
||||||
|
//Create Executor
|
||||||
|
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||||
|
executor.add_node(node);
|
||||||
|
|
||||||
|
//Create QoS
|
||||||
|
const rmw_qos_profile_t custom_qos_profile = {
|
||||||
|
RMW_QOS_POLICY_HISTORY_KEEP_ALL,
|
||||||
|
10,
|
||||||
|
RMW_QOS_POLICY_RELIABILITY_RELIABLE,
|
||||||
|
RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL,
|
||||||
|
RMW_QOS_DEADLINE_DEFAULT,
|
||||||
|
RMW_QOS_LIFESPAN_DEFAULT,
|
||||||
|
RMW_QOS_POLICY_LIVELINESS_SYSTEM_DEFAULT,
|
||||||
|
RMW_QOS_LIVELINESS_LEASE_DURATION_DEFAULT,
|
||||||
|
false
|
||||||
|
};
|
||||||
|
const rclcpp::QoS custom_qos = rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(custom_qos_profile),custom_qos_profile);
|
||||||
|
|
||||||
|
//Create Publisher
|
||||||
|
pub = node->create_publisher<tutorial_interfaces::msg::Type1>("Loopback_2",custom_qos);
|
||||||
|
|
||||||
|
//Create Subscriber
|
||||||
|
sub = node->create_subscription<tutorial_interfaces::msg::Type1>("Loopback_1",custom_qos, std::bind(&sub_callback,std::placeholders::_1));
|
||||||
|
|
||||||
|
//Create Clock
|
||||||
|
steady_clock = rclcpp::Clock::make_shared(RCL_STEADY_TIME);
|
||||||
|
|
||||||
|
//Create Timer
|
||||||
|
timer = rclcpp::create_timer(node,steady_clock,std::chrono::nanoseconds(4096 * 4096 * 20),std::bind(&timer_callback));
|
||||||
|
|
||||||
|
//DONE
|
||||||
|
RCLCPP_INFO(node->get_logger(), "Spinning Node %s", node->get_name());
|
||||||
|
executor.spin();
|
||||||
|
RCLCPP_INFO(node->get_logger(), "Node %s exited. Shuting down", node->get_name());
|
||||||
|
rclcpp::shutdown();
|
||||||
|
}
|
||||||
210
sw/ros/src/cpp_loopback/src/loopback_server.cpp
Normal file
210
sw/ros/src/cpp_loopback/src/loopback_server.cpp
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
#include <map>
|
||||||
|
#include "rclcpp/rclcpp.hpp"
|
||||||
|
#include "rmw/types.h"
|
||||||
|
#include "tutorial_interfaces/msg/type1.hpp"
|
||||||
|
#include "rcl/time.h"
|
||||||
|
|
||||||
|
#define MAX_MAP_SIZE 10
|
||||||
|
#define INPUT_FIFO_SIZE 16384
|
||||||
|
#define OUTPUT_FIFO_SIZE 16384
|
||||||
|
#define ENABLE_AVERAGE true
|
||||||
|
#define DEBUG true
|
||||||
|
|
||||||
|
rclcpp::Node::SharedPtr node;
|
||||||
|
rclcpp::Publisher<tutorial_interfaces::msg::Type1>::SharedPtr pub;
|
||||||
|
rclcpp::Subscription<tutorial_interfaces::msg::Type1>::SharedPtr sub;
|
||||||
|
rclcpp::Clock::SharedPtr steady_clock;
|
||||||
|
rclcpp::TimerBase::SharedPtr timer1;
|
||||||
|
rclcpp::TimerBase::SharedPtr timer2;
|
||||||
|
std::map<int, rclcpp::Time> start_time_map;
|
||||||
|
std::shared_mutex mutex1;
|
||||||
|
std::shared_mutex mutex2;
|
||||||
|
rclcpp::CallbackGroup::SharedPtr cb_group1;
|
||||||
|
rclcpp::CallbackGroup::SharedPtr cb_group2;
|
||||||
|
int avg_count[3] = {0, 0, 0};
|
||||||
|
long long avg_acc[3] = {0, 0, 0};
|
||||||
|
|
||||||
|
void sub_callback(const tutorial_interfaces::msg::Type1 & msg){
|
||||||
|
switch(msg.id){
|
||||||
|
case 2:{
|
||||||
|
mutex1.lock();
|
||||||
|
std::map<int, rclcpp::Time>::iterator iter = start_time_map.find(msg.a);
|
||||||
|
if(iter != start_time_map.end()) {
|
||||||
|
rclcpp::Duration rtt = steady_clock->now() - iter->second;
|
||||||
|
start_time_map.erase(iter);
|
||||||
|
mutex1.unlock();
|
||||||
|
if(ENABLE_AVERAGE){
|
||||||
|
mutex2.lock();
|
||||||
|
avg_acc[0] += rtt.nanoseconds();
|
||||||
|
avg_count[0]++;
|
||||||
|
mutex2.unlock();
|
||||||
|
} else {
|
||||||
|
RCLCPP_INFO(node->get_logger(), "Message: %d RTT: %ld ns", msg.a, rtt.nanoseconds());
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (false){
|
||||||
|
RCLCPP_INFO(node->get_logger(), "Current contents of start_time_map:");
|
||||||
|
for(auto it = start_time_map.cbegin(); it != start_time_map.cend(); ++it){
|
||||||
|
RCLCPP_INFO(node->get_logger(), "A: %d", it->first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex1.unlock();
|
||||||
|
RCLCPP_WARN(node->get_logger(), "A: %d not found in start_time_map", msg.a);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3:{
|
||||||
|
//INPUT
|
||||||
|
if(ENABLE_AVERAGE){
|
||||||
|
mutex2.lock();
|
||||||
|
avg_acc[1] += msg.a;
|
||||||
|
avg_count[1]++;
|
||||||
|
mutex2.unlock();
|
||||||
|
} else {
|
||||||
|
float util = ((INPUT_FIFO_SIZE - msg.a) * 100.0) / INPUT_FIFO_SIZE;
|
||||||
|
RCLCPP_INFO(node->get_logger(), "Input Utilization: %f %%", util);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 4:{
|
||||||
|
//OUTPUT
|
||||||
|
if(ENABLE_AVERAGE){
|
||||||
|
mutex2.lock();
|
||||||
|
avg_acc[2] += msg.a;
|
||||||
|
avg_count[2]++;
|
||||||
|
mutex2.unlock();
|
||||||
|
} else {
|
||||||
|
float util = ((OUTPUT_FIFO_SIZE - msg.a) * 100.0) / OUTPUT_FIFO_SIZE;
|
||||||
|
RCLCPP_INFO(node->get_logger(), "Output Utilization: %f %%", util);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:{
|
||||||
|
RCLCPP_WARN(node->get_logger(), "Unexpected ID received: %d", msg.id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main_loop() {
|
||||||
|
//Cancel Timer
|
||||||
|
timer1->cancel();
|
||||||
|
|
||||||
|
//Wait for Server
|
||||||
|
RCLCPP_INFO(node->get_logger(), "Waiting for client");
|
||||||
|
while (node->count_subscribers("Loopback_2") < 1 || node->count_publishers("Loopback_1") < 1){
|
||||||
|
rclcpp::sleep_for(std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
RCLCPP_INFO(node->get_logger(), "Client endpoints available.");
|
||||||
|
|
||||||
|
uint32_t index = 0;
|
||||||
|
|
||||||
|
//Main Loop
|
||||||
|
while (rclcpp::ok()) {
|
||||||
|
mutex1.lock();
|
||||||
|
long size = start_time_map.size();
|
||||||
|
mutex1.unlock();
|
||||||
|
if (size >= MAX_MAP_SIZE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto resp = tutorial_interfaces::msg::Type1();
|
||||||
|
// Publish Input Utilization
|
||||||
|
resp.id = 1;
|
||||||
|
resp.a = index;
|
||||||
|
mutex1.lock();
|
||||||
|
pub->publish(resp);
|
||||||
|
rclcpp::Time t = steady_clock->now();
|
||||||
|
start_time_map[index] = t;
|
||||||
|
mutex1.unlock();
|
||||||
|
index++;
|
||||||
|
//rclcpp::sleep_for(std::chrono::nanoseconds(500));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void average(){
|
||||||
|
long avg[3];
|
||||||
|
int cnt = avg_count[0];
|
||||||
|
|
||||||
|
mutex2.lock();
|
||||||
|
for (int i = 0; i < 3; i++){
|
||||||
|
if (avg_count[i] != 0){
|
||||||
|
avg[i] = avg_acc[i] / avg_count[i];
|
||||||
|
} else {
|
||||||
|
avg[i] = 0;
|
||||||
|
}
|
||||||
|
//Reset
|
||||||
|
avg_acc[i] = 0;
|
||||||
|
avg_count[i] = 0;
|
||||||
|
}
|
||||||
|
mutex2.unlock();
|
||||||
|
RCLCPP_INFO(node->get_logger(), "Average message RTT: %ld ns, Throughput: %d", avg[0], cnt);
|
||||||
|
RCLCPP_INFO(node->get_logger(), "Input Utilization: %f %%", ((INPUT_FIFO_SIZE - avg[1]) * 100.0) / INPUT_FIFO_SIZE);
|
||||||
|
RCLCPP_INFO(node->get_logger(), "Output Utilization: %f %%", ((OUTPUT_FIFO_SIZE - avg[2]) * 100.0) / OUTPUT_FIFO_SIZE);
|
||||||
|
if(DEBUG) {
|
||||||
|
mutex1.lock();
|
||||||
|
RCLCPP_INFO(node->get_logger(), "Map Size: %ld", start_time_map.size());
|
||||||
|
mutex1.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv){
|
||||||
|
rclcpp::init(argc, argv);
|
||||||
|
|
||||||
|
//Create Node
|
||||||
|
rclcpp::NodeOptions node_options = rclcpp::NodeOptions();
|
||||||
|
node_options.use_global_arguments(false);
|
||||||
|
node_options.enable_rosout(false);
|
||||||
|
node_options.use_intra_process_comms(false);
|
||||||
|
node_options.enable_topic_statistics(false);
|
||||||
|
node_options.start_parameter_services(false);
|
||||||
|
node_options.start_parameter_event_publisher(false);
|
||||||
|
node_options.use_clock_thread(false);
|
||||||
|
node_options.allow_undeclared_parameters(false);
|
||||||
|
node_options.automatically_declare_parameters_from_overrides(false);
|
||||||
|
|
||||||
|
node = rclcpp::Node::make_shared("loopback_server",node_options);
|
||||||
|
|
||||||
|
//Create Executor
|
||||||
|
rclcpp::executors::MultiThreadedExecutor executor;
|
||||||
|
executor.add_node(node);
|
||||||
|
|
||||||
|
//Create QoS
|
||||||
|
const rmw_qos_profile_t custom_qos_profile = {
|
||||||
|
RMW_QOS_POLICY_HISTORY_KEEP_ALL,
|
||||||
|
10,
|
||||||
|
RMW_QOS_POLICY_RELIABILITY_RELIABLE,
|
||||||
|
RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL,
|
||||||
|
RMW_QOS_DEADLINE_DEFAULT,
|
||||||
|
RMW_QOS_LIFESPAN_DEFAULT,
|
||||||
|
RMW_QOS_POLICY_LIVELINESS_SYSTEM_DEFAULT,
|
||||||
|
RMW_QOS_LIVELINESS_LEASE_DURATION_DEFAULT,
|
||||||
|
false
|
||||||
|
};
|
||||||
|
const rclcpp::QoS custom_qos = rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(custom_qos_profile),custom_qos_profile);
|
||||||
|
|
||||||
|
//Create Publisher
|
||||||
|
pub = node->create_publisher<tutorial_interfaces::msg::Type1>("Loopback_1",custom_qos);
|
||||||
|
|
||||||
|
//Create Subscriber
|
||||||
|
sub = node->create_subscription<tutorial_interfaces::msg::Type1>("Loopback_2",custom_qos, std::bind(&sub_callback,std::placeholders::_1));
|
||||||
|
|
||||||
|
//Create Clock
|
||||||
|
steady_clock = rclcpp::Clock::make_shared(RCL_STEADY_TIME);
|
||||||
|
|
||||||
|
//Create Callbackgroups
|
||||||
|
cb_group1 = node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||||
|
cb_group2 = node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||||
|
|
||||||
|
//Create Timer
|
||||||
|
timer1 = rclcpp::create_timer(node,steady_clock,std::chrono::seconds(1),std::bind(&main_loop),cb_group1);
|
||||||
|
if(ENABLE_AVERAGE) {
|
||||||
|
timer2 = rclcpp::create_timer(node,steady_clock,std::chrono::seconds(1),std::bind(&average),cb_group2);
|
||||||
|
}
|
||||||
|
|
||||||
|
//DONE
|
||||||
|
RCLCPP_INFO(node->get_logger(), "Spinning Node %s", node->get_name());
|
||||||
|
executor.spin();
|
||||||
|
RCLCPP_INFO(node->get_logger(), "Node %s exited. Shuting down", node->get_name());
|
||||||
|
rclcpp::shutdown();
|
||||||
|
}
|
||||||
39
sw/ros/src/cpp_srvcli/CMakeLists.txt
Normal file
39
sw/ros/src/cpp_srvcli/CMakeLists.txt
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.8)
|
||||||
|
project(cpp_srvcli)
|
||||||
|
|
||||||
|
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
|
add_compile_options(-Wall -Wextra -Wpedantic)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# find dependencies
|
||||||
|
find_package(ament_cmake REQUIRED)
|
||||||
|
find_package(rclcpp REQUIRED)
|
||||||
|
find_package(example_interfaces REQUIRED)
|
||||||
|
|
||||||
|
if(BUILD_TESTING)
|
||||||
|
find_package(ament_lint_auto REQUIRED)
|
||||||
|
# the following line skips the linter which checks for copyrights
|
||||||
|
# uncomment the line when a copyright and license is not present in all source files
|
||||||
|
#set(ament_cmake_copyright_FOUND TRUE)
|
||||||
|
# the following line skips cpplint (only works in a git repo)
|
||||||
|
# uncomment the line when this package is not in a git repo
|
||||||
|
#set(ament_cmake_cpplint_FOUND TRUE)
|
||||||
|
ament_lint_auto_find_test_dependencies()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_executable(server src/add_two_ints_server.cpp)
|
||||||
|
ament_target_dependencies(server rclcpp example_interfaces)
|
||||||
|
|
||||||
|
add_executable(client src/add_two_ints_client.cpp)
|
||||||
|
ament_target_dependencies(client rclcpp example_interfaces)
|
||||||
|
|
||||||
|
|
||||||
|
install(
|
||||||
|
TARGETS
|
||||||
|
server
|
||||||
|
client
|
||||||
|
DESTINATION
|
||||||
|
lib/${PROJECT_NAME}
|
||||||
|
)
|
||||||
|
|
||||||
|
ament_package()
|
||||||
21
sw/ros/src/cpp_srvcli/package.xml
Normal file
21
sw/ros/src/cpp_srvcli/package.xml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||||
|
<package format="3">
|
||||||
|
<name>cpp_srvcli</name>
|
||||||
|
<version>0.0.0</version>
|
||||||
|
<description>TODO: Package description</description>
|
||||||
|
<maintainer email="greek64.mail@gmail.com">john64</maintainer>
|
||||||
|
<license>TODO: License declaration</license>
|
||||||
|
|
||||||
|
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||||
|
|
||||||
|
<depend>rclcpp</depend>
|
||||||
|
<depend>example_interfaces</depend>
|
||||||
|
|
||||||
|
<test_depend>ament_lint_auto</test_depend>
|
||||||
|
<test_depend>ament_lint_common</test_depend>
|
||||||
|
|
||||||
|
<export>
|
||||||
|
<build_type>ament_cmake</build_type>
|
||||||
|
</export>
|
||||||
|
</package>
|
||||||
55
sw/ros/src/cpp_srvcli/src/add_two_ints_client.cpp
Normal file
55
sw/ros/src/cpp_srvcli/src/add_two_ints_client.cpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#include "rclcpp/rclcpp.hpp"
|
||||||
|
#include "example_interfaces/srv/add_two_ints.hpp"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
rclcpp::init(argc, argv);
|
||||||
|
|
||||||
|
if (argc != 3) {
|
||||||
|
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "usage: add_two_ints_client X Y");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_two_ints_client");
|
||||||
|
rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedPtr client =
|
||||||
|
node->create_client<example_interfaces::srv::AddTwoInts>("add_two_ints");
|
||||||
|
|
||||||
|
auto request = std::make_shared<example_interfaces::srv::AddTwoInts::Request>();
|
||||||
|
request->a = atoll(argv[1]);
|
||||||
|
request->b = atoll(argv[2]);
|
||||||
|
|
||||||
|
while (!client->wait_for_service(1s)) {
|
||||||
|
if (!rclcpp::ok()) {
|
||||||
|
RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Interrupted while waiting for the service. Exiting.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "service not available, waiting again...");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
NOTE
|
||||||
|
|
||||||
|
There is a race condition happening with CycloneDDS, where if a Sample/Heartbeat arrives before CycloneDDS had time
|
||||||
|
to store the remote endpoint, the sample/SN is discarded.
|
||||||
|
In order to prevent that we add another sleep to make sure that CycloneDDS has discovered all remote endpoints
|
||||||
|
before the service request is sent out.
|
||||||
|
*/
|
||||||
|
rclcpp::sleep_for(std::chrono::seconds(1));
|
||||||
|
|
||||||
|
auto result = client->async_send_request(request);
|
||||||
|
// Wait for the result.
|
||||||
|
if (rclcpp::spin_until_future_complete(node, result) == rclcpp::FutureReturnCode::SUCCESS){
|
||||||
|
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Sum: %ld", result.get()->sum);
|
||||||
|
} else {
|
||||||
|
RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Failed to call service add_two_ints");
|
||||||
|
}
|
||||||
|
|
||||||
|
rclcpp::shutdown();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
24
sw/ros/src/cpp_srvcli/src/add_two_ints_server.cpp
Normal file
24
sw/ros/src/cpp_srvcli/src/add_two_ints_server.cpp
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#include "rclcpp/rclcpp.hpp"
|
||||||
|
#include "example_interfaces/srv/add_two_ints.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
void add(const std::shared_ptr<example_interfaces::srv::AddTwoInts::Request> request, std::shared_ptr<example_interfaces::srv::AddTwoInts::Response> response){
|
||||||
|
response->sum = request->a + request->b;
|
||||||
|
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Incoming request\na: %ld" " b: %ld", request->a, request->b);
|
||||||
|
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "sending back response: [%ld]", (long int)response->sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv){
|
||||||
|
rclcpp::init(argc, argv);
|
||||||
|
|
||||||
|
std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_two_ints_server");
|
||||||
|
|
||||||
|
rclcpp::Service<example_interfaces::srv::AddTwoInts>::SharedPtr service =
|
||||||
|
node->create_service<example_interfaces::srv::AddTwoInts>("add_two_ints", &add);
|
||||||
|
|
||||||
|
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Ready to add two ints.");
|
||||||
|
|
||||||
|
rclcpp::spin(node);
|
||||||
|
rclcpp::shutdown();
|
||||||
|
}
|
||||||
27
sw/ros/src/tutorial_interfaces/CMakeLists.txt
Normal file
27
sw/ros/src/tutorial_interfaces/CMakeLists.txt
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.8)
|
||||||
|
project(tutorial_interfaces)
|
||||||
|
|
||||||
|
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
|
add_compile_options(-Wall -Wextra -Wpedantic)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# find dependencies
|
||||||
|
find_package(ament_cmake REQUIRED)
|
||||||
|
find_package(rosidl_default_generators REQUIRED)
|
||||||
|
|
||||||
|
rosidl_generate_interfaces(${PROJECT_NAME}
|
||||||
|
"msg/Type1.msg"
|
||||||
|
)
|
||||||
|
|
||||||
|
if(BUILD_TESTING)
|
||||||
|
find_package(ament_lint_auto REQUIRED)
|
||||||
|
# the following line skips the linter which checks for copyrights
|
||||||
|
# uncomment the line when a copyright and license is not present in all source files
|
||||||
|
#set(ament_cmake_copyright_FOUND TRUE)
|
||||||
|
# the following line skips cpplint (only works in a git repo)
|
||||||
|
# uncomment the line when this package is not in a git repo
|
||||||
|
#set(ament_cmake_cpplint_FOUND TRUE)
|
||||||
|
ament_lint_auto_find_test_dependencies()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
ament_package()
|
||||||
2
sw/ros/src/tutorial_interfaces/msg/Type1.msg
Normal file
2
sw/ros/src/tutorial_interfaces/msg/Type1.msg
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
uint32 id
|
||||||
|
uint32 a
|
||||||
20
sw/ros/src/tutorial_interfaces/package.xml
Normal file
20
sw/ros/src/tutorial_interfaces/package.xml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||||
|
<package format="3">
|
||||||
|
<name>tutorial_interfaces</name>
|
||||||
|
<version>0.0.0</version>
|
||||||
|
<description>TODO: Package description</description>
|
||||||
|
<maintainer email="john64@todo.todo">john64</maintainer>
|
||||||
|
<license>TODO: License declaration</license>
|
||||||
|
|
||||||
|
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||||
|
<build_depend>rosidl_default_generators</build_depend>
|
||||||
|
<exec_depend>rosidl_default_runtime</exec_depend>
|
||||||
|
<member_of_group>rosidl_interface_packages</member_of_group>
|
||||||
|
<test_depend>ament_lint_auto</test_depend>
|
||||||
|
<test_depend>ament_lint_common</test_depend>
|
||||||
|
|
||||||
|
<export>
|
||||||
|
<build_type>ament_cmake</build_type>
|
||||||
|
</export>
|
||||||
|
</package>
|
||||||
123
sw/standalone/test_fpga.c
Normal file
123
sw/standalone/test_fpga.c
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
#include <stdio.h> //printf
|
||||||
|
#include <sys/types.h> //open
|
||||||
|
#include <sys/stat.h> //open
|
||||||
|
#include <fcntl.h> //open
|
||||||
|
#include <unistd.h> //close
|
||||||
|
#include <errno.h> //perror
|
||||||
|
#include <stdlib.h> //exit
|
||||||
|
#include <sys/mman.h> //mmap
|
||||||
|
#include <signal.h> //signal
|
||||||
|
#include <assert.h> //assert
|
||||||
|
#include <sys/socket.h> //socket, inet_aton
|
||||||
|
#include <netinet/in.h> //socket, inet_aton
|
||||||
|
#include <netinet/udp.h> //socket
|
||||||
|
#include <arpa/inet.h> //htons, ntohs, htonl, ntohl, inet_aton
|
||||||
|
|
||||||
|
#include "hps_0.h"
|
||||||
|
|
||||||
|
#define LW_FPGA_SLAVES_BASE 0xFF200000
|
||||||
|
#define LW_FPGA_SVAVES_END 0xFF3FFFFF
|
||||||
|
#define LW_FPGA_SLAVES_SPAN 0x00200000
|
||||||
|
|
||||||
|
void bail();
|
||||||
|
void sig_handler(int signum);
|
||||||
|
|
||||||
|
//NOTE: use void* Pointer to avoid pointer arithmetic during offset addition
|
||||||
|
void* lw_bridge = NULL;
|
||||||
|
int mem_fd = -1;
|
||||||
|
|
||||||
|
int main(int argc, char** argv){
|
||||||
|
|
||||||
|
//Register Signal Handling
|
||||||
|
signal(SIGHUP, sig_handler);
|
||||||
|
signal(SIGINT, sig_handler);
|
||||||
|
signal(SIGQUIT, sig_handler);
|
||||||
|
signal(SIGABRT, sig_handler);
|
||||||
|
|
||||||
|
mem_fd = open("/dev/mem", O_RDWR | O_SYNC);
|
||||||
|
if(mem_fd < 0) {
|
||||||
|
perror("Could not open /dev/mem");
|
||||||
|
bail(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
lw_bridge = (uint32_t*)mmap(NULL, LW_FPGA_SLAVES_SPAN, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, LW_FPGA_SLAVES_BASE);
|
||||||
|
if(lw_bridge == MAP_FAILED) {
|
||||||
|
perror("Could not mmap /dev/mem");
|
||||||
|
bail(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Polling Address
|
||||||
|
uint32_t* ADDR_0 = (uint32_t*)(lw_bridge + TEST_FPGA_0_BASE);
|
||||||
|
// Read Address
|
||||||
|
uint32_t* ADDR_1 = ADDR_0 + 1; //Pointer Arithmetic
|
||||||
|
// Write Address
|
||||||
|
uint32_t* ADDR_2 = ADDR_0 + 2; //Pointer Arithmetic
|
||||||
|
|
||||||
|
struct sockaddr_in src, dest;
|
||||||
|
|
||||||
|
src.sin_family = AF_INET;
|
||||||
|
src.sin_port = htons(5000);
|
||||||
|
inet_aton("192.168.0.10", &(src.sin_addr));
|
||||||
|
|
||||||
|
dest.sin_family = AF_INET;
|
||||||
|
dest.sin_port = htons(4000);
|
||||||
|
inet_aton("192.168.0.15", &(dest.sin_addr));
|
||||||
|
|
||||||
|
uint32_t tmp;
|
||||||
|
struct in_addr tmp_addr;
|
||||||
|
|
||||||
|
assert(*(ADDR_0) == 0);
|
||||||
|
// Write SRC Address
|
||||||
|
*ADDR_2 = src.sin_addr.s_addr;
|
||||||
|
// Write DEST Address
|
||||||
|
*ADDR_2 = dest.sin_addr.s_addr;
|
||||||
|
// Write UDP Ports
|
||||||
|
*ADDR_2 = ((src.sin_port << 16) & 0xFFFF0000) | (dest.sin_port & 0xFFFF);
|
||||||
|
// Write Packet Length
|
||||||
|
*ADDR_2 = htonl(2);
|
||||||
|
// Write BYTE 1
|
||||||
|
*ADDR_2 = 0xAAAABBBB;
|
||||||
|
// Write BYTE 2
|
||||||
|
*ADDR_2 = 0xCCCCDDDD;
|
||||||
|
|
||||||
|
// Read SRC Address
|
||||||
|
tmp_addr.s_addr = *ADDR_1;
|
||||||
|
printf("SRC Address: %s\n", inet_ntoa(tmp_addr));
|
||||||
|
assert(*ADDR_0 == 1);
|
||||||
|
// Read DEST Address
|
||||||
|
tmp_addr.s_addr = *ADDR_1;
|
||||||
|
printf("DEST Address: %s\n", inet_ntoa(tmp_addr));
|
||||||
|
// Read UDP Ports
|
||||||
|
tmp = *ADDR_1;
|
||||||
|
printf("UDP SRC Port: %d, UDP DEST Port: %d\n", ntohs((tmp >> 16) & 0xFFFF), ntohs(tmp & 0xFFFF));
|
||||||
|
// Read Packet Length
|
||||||
|
printf("Packet Length: %d\n", ntohl(*ADDR_1));
|
||||||
|
// Read BYTE1
|
||||||
|
printf("Byte 1: %X\n", *ADDR_1);
|
||||||
|
// Read BYTE2
|
||||||
|
printf("Byte 2: %X\n", *ADDR_1);
|
||||||
|
// Read BYTE3
|
||||||
|
printf("Byte 3: %X\n", *ADDR_1);
|
||||||
|
|
||||||
|
assert(*(ADDR_0) == 0);
|
||||||
|
printf("Done.\n");
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bail(int code){
|
||||||
|
// Close File Descriptor
|
||||||
|
if (mem_fd != -1){
|
||||||
|
close(mem_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmap /dev/mem
|
||||||
|
if (lw_bridge != NULL){
|
||||||
|
munmap(lw_bridge, LW_FPGA_SLAVES_SPAN);
|
||||||
|
}
|
||||||
|
exit(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sig_handler(int signum){
|
||||||
|
printf("Received Signal %d.\n Exiting...\n", signum);
|
||||||
|
bail(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
121
sw/standalone/udp_client.cpp
Normal file
121
sw/standalone/udp_client.cpp
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
#include <sys/socket.h> //socket, inet_aton
|
||||||
|
#include <netinet/in.h> //socket, inet_aton
|
||||||
|
#include <netinet/udp.h> //socket
|
||||||
|
#include <arpa/inet.h> //htons, ntohs, htonl, ntohl, inet_aton
|
||||||
|
#include <signal.h> //signal
|
||||||
|
#include <stdio.h> //fprintf
|
||||||
|
#include <errno.h> //perror
|
||||||
|
#include <stdlib.h> //strtol
|
||||||
|
#include <unistd.h> //close
|
||||||
|
|
||||||
|
/*PROTOTYPES*/
|
||||||
|
void bail(int code);
|
||||||
|
void sig_handler(int signum);
|
||||||
|
void usage(char* name);
|
||||||
|
|
||||||
|
/*DEFINES*/
|
||||||
|
//Magic Number used to distinguish our UDP Packets (First 8 Bytes of Payload)
|
||||||
|
#define MAGIC 0xDEADBEEFCAFEFEED
|
||||||
|
//Receive Buffer Size
|
||||||
|
#define BUFFER_SIZE 1000
|
||||||
|
|
||||||
|
/*GLOBAL VARIABLES*/
|
||||||
|
long port = 0;
|
||||||
|
int sock_fd = -1;
|
||||||
|
struct sockaddr_in src_addr;
|
||||||
|
|
||||||
|
int main(int argc, char **argv){
|
||||||
|
|
||||||
|
/*Parse Arguments*/
|
||||||
|
if (argc < 3) {
|
||||||
|
fprintf(stderr, "Insufficient arguments\n");
|
||||||
|
usage(argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Source Address
|
||||||
|
in_addr_t src_ip_addr = inet_addr(argv[1]);
|
||||||
|
if (src_ip_addr == INADDR_NONE) {
|
||||||
|
fprintf(stderr, "Invalid SRC IP Address\n");
|
||||||
|
usage(argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Port Number
|
||||||
|
errno = 0;
|
||||||
|
port = strtol(argv[2], NULL, 10);
|
||||||
|
if (errno != 0) {
|
||||||
|
fprintf(stderr, "Invalid Port Number\n");
|
||||||
|
usage(argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*MISC MAIN THREAD*/
|
||||||
|
//Register Signal Handling
|
||||||
|
signal(SIGHUP, sig_handler);
|
||||||
|
signal(SIGINT, sig_handler);
|
||||||
|
signal(SIGQUIT, sig_handler);
|
||||||
|
signal(SIGABRT, sig_handler);
|
||||||
|
|
||||||
|
/*SOCKET OPERATIONS*/
|
||||||
|
src_addr = {AF_INET, htons(port), {src_ip_addr}};
|
||||||
|
|
||||||
|
//Open Socket
|
||||||
|
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
//Bind Socket
|
||||||
|
if (bind(sock_fd, (const struct sockaddr*) &src_addr, sizeof(src_addr)) < 0) {
|
||||||
|
perror("Bind failed");
|
||||||
|
bail(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
//Set Receive Timeout
|
||||||
|
const struct timeval tv = {.tv_usec = 1000}; //1 ms
|
||||||
|
setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
||||||
|
|
||||||
|
char buffer[BUFFER_SIZE];
|
||||||
|
uint64_t* p = (uint64_t*) buffer;
|
||||||
|
socklen_t addrlen;
|
||||||
|
struct sockaddr_in src;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
printf("Entering Main loop\n");
|
||||||
|
|
||||||
|
while(true){
|
||||||
|
//Receive Packet
|
||||||
|
n = recvfrom(sock_fd, buffer, BUFFER_SIZE, MSG_TRUNC, (struct sockaddr*) &src, &addrlen);
|
||||||
|
if (n < 0){
|
||||||
|
// Error
|
||||||
|
if (errno != EAGAIN && errno != EWOULDBLOCK) {
|
||||||
|
perror("rcvfrom() error");
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//Packet not relevant
|
||||||
|
if (src.sin_family != src_addr.sin_family && src.sin_port != src_addr.sin_port && n >= 8 && p[0] != MAGIC){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//Sent Packet Back
|
||||||
|
if (sendto(sock_fd, buffer, n, 0, (const struct sockaddr*) &src, addrlen) < 0) {
|
||||||
|
perror("sendto failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void bail(int code){
|
||||||
|
//Close Sockets
|
||||||
|
if (sock_fd != -1) {
|
||||||
|
close(sock_fd);
|
||||||
|
}
|
||||||
|
//DONE
|
||||||
|
exit(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sig_handler(int signum){
|
||||||
|
fprintf(stderr, "Received Signal %d.\n Exiting...\n", signum);
|
||||||
|
bail(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void usage(char* name){
|
||||||
|
printf("USAGE: %s SRC_ADDRESS PORT\n", name);
|
||||||
|
printf("SRC_ADDRESS The local source IPv4 Address from which to send and listen to (In x.x.x.x format)\n");
|
||||||
|
printf("PORT The port number of the Addresses\n");
|
||||||
|
|
||||||
|
bail(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
402
sw/standalone/udp_forwarder.MT.c
Normal file
402
sw/standalone/udp_forwarder.MT.c
Normal file
@ -0,0 +1,402 @@
|
|||||||
|
#include <stdio.h> //printf
|
||||||
|
#include <sys/types.h> //open
|
||||||
|
#include <sys/stat.h> //open
|
||||||
|
#include <fcntl.h> //open
|
||||||
|
#include <unistd.h> //close, getopt
|
||||||
|
#include <errno.h> //perror
|
||||||
|
#include <stdlib.h> //exit
|
||||||
|
#include <sys/mman.h> //mmap
|
||||||
|
#include <signal.h> //signal
|
||||||
|
#include <assert.h> //assert
|
||||||
|
#include <sys/socket.h> //socket, inet_aton
|
||||||
|
#include <netinet/in.h> //socket, inet_aton
|
||||||
|
#include <netinet/udp.h> //socket
|
||||||
|
#include <arpa/inet.h> //htons, ntohs, htonl, ntohl, inet_aton
|
||||||
|
#include <time.h> //nanosleep
|
||||||
|
#include <stdbool.h> //bool
|
||||||
|
#include <pthread.h> //Multithreading
|
||||||
|
#include "hps_0.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
DEVELOPER NOTE
|
||||||
|
|
||||||
|
This is an attempt to make udp_forwarder multithreaded, but for some reason I cannot make it faster than the single thread variant.
|
||||||
|
|
||||||
|
Initially I thought the slow down was due to reading and writing to/from the FPGA happening at the same time, since
|
||||||
|
there is only one AXI master (meaning that only one operation can happen at any time) and mixing of read and write
|
||||||
|
operations somehow interferes with each other.
|
||||||
|
But adding mutexes makes it actually slower!
|
||||||
|
|
||||||
|
After much experimenting this are the results I got:
|
||||||
|
* Single Thread Variant
|
||||||
|
16k packets/s
|
||||||
|
* Multi Thread Variant
|
||||||
|
7.1k packets/s
|
||||||
|
* Multi Thread Variant with no sleep
|
||||||
|
13.6k packets/s
|
||||||
|
* Multi Thread Variant with mutexes
|
||||||
|
6.9k packets/s
|
||||||
|
* Multi Thread Variant with mutexes and no sleep
|
||||||
|
12.1k packets/s
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*PROTOTYPES*/
|
||||||
|
void usage(char* name);
|
||||||
|
void bail(int code);
|
||||||
|
void sig_handler(int signum);
|
||||||
|
void* send_thread(void* vargp);
|
||||||
|
|
||||||
|
/*DEFINES*/
|
||||||
|
#define LW_FPGA_SLAVES_BASE 0xFF200000
|
||||||
|
#define LW_FPGA_SLAVES_END 0xFF3FFFFF
|
||||||
|
#define LW_FPGA_SLAVES_SPAN 0x00200000
|
||||||
|
#define BUF_LENGTH 65536
|
||||||
|
#define NUM_SOCKETS 5
|
||||||
|
#define PORT_CONFIG_PB 7400
|
||||||
|
#define PORT_CONFIG_DG 250
|
||||||
|
#define PORT_CONFIG_PG 2
|
||||||
|
#define PORT_CONFIG_D0 0
|
||||||
|
#define PORT_CONFIG_D1 10
|
||||||
|
#define PORT_CONFIG_D2 1
|
||||||
|
#define PORT_CONFIG_D3 11
|
||||||
|
|
||||||
|
/*GLOBAL VARIABLES*/
|
||||||
|
//NOTE: use void* Pointer to avoid pointer arithmetic during offset addition
|
||||||
|
volatile sig_atomic_t done = false;
|
||||||
|
void* lw_bridge = NULL;
|
||||||
|
int mem_fd = -1;
|
||||||
|
int sock_fd[NUM_SOCKETS];
|
||||||
|
uint8_t endian = -1; //0 Big Endian, 1 Little Endian
|
||||||
|
in_addr_t src_addr;
|
||||||
|
long src_did;
|
||||||
|
bool quiet = false;
|
||||||
|
struct sockaddr_in bind_addr[NUM_SOCKETS];
|
||||||
|
uint32_t* ADDR_0;
|
||||||
|
uint32_t* ADDR_1;
|
||||||
|
uint32_t* ADDR_2;
|
||||||
|
pthread_mutex_t mutex;
|
||||||
|
|
||||||
|
/*CONSTANTS*/
|
||||||
|
const struct timespec ms = {0, 10^6}; // 1ms
|
||||||
|
const struct timespec us = {0, 10^3}; // 1us
|
||||||
|
const struct timespec sec = {1, 0}; // 1sec
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
|
||||||
|
int opt;
|
||||||
|
|
||||||
|
while ((opt = getopt(argc, argv, "q")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'q':
|
||||||
|
quiet = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* invalid option */
|
||||||
|
fprintf(stderr, "Invalid Option\n");
|
||||||
|
usage(argv[0]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (argc-optind < 2) {
|
||||||
|
fprintf(stderr, "Insufficient arguments\n");
|
||||||
|
usage(argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
src_addr = inet_addr(argv[optind]);
|
||||||
|
if (src_addr == INADDR_NONE) {
|
||||||
|
fprintf(stderr, "Invalid IP Address\n");
|
||||||
|
usage(argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
src_did = strtol(argv[optind+1], NULL, 10);
|
||||||
|
if (errno != 0) {
|
||||||
|
fprintf(stderr, "Invalid Domain ID\n");
|
||||||
|
usage(argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize Sockets to aid bail function
|
||||||
|
for (int i = 0; i < NUM_SOCKETS; i++) {
|
||||||
|
sock_fd[i] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Register Signal Handling
|
||||||
|
signal(SIGHUP, sig_handler);
|
||||||
|
signal(SIGINT, sig_handler);
|
||||||
|
signal(SIGQUIT, sig_handler);
|
||||||
|
signal(SIGABRT, sig_handler);
|
||||||
|
|
||||||
|
// Check Endianness
|
||||||
|
{
|
||||||
|
int i = 1;
|
||||||
|
endian = (* (char*) &i == 1) ? 1 : 0;
|
||||||
|
printf("ENDIANNESS: %d\n", endian);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endian != 1) {
|
||||||
|
fprintf(stderr, "Program is tested on Little Endian Systems\n");
|
||||||
|
bail(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
mem_fd = open("/dev/mem", O_RDWR | O_SYNC);
|
||||||
|
if (mem_fd < 0) {
|
||||||
|
perror("Could not open /dev/mem");
|
||||||
|
bail(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
lw_bridge = (uint32_t*)mmap(NULL, LW_FPGA_SLAVES_SPAN, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, LW_FPGA_SLAVES_BASE);
|
||||||
|
if (lw_bridge == MAP_FAILED) {
|
||||||
|
perror("Could not mmap /dev/mem");
|
||||||
|
bail(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Polling Address
|
||||||
|
ADDR_0 = (uint32_t*)(lw_bridge + TEST_FPGA_0_BASE);
|
||||||
|
// Read Address
|
||||||
|
ADDR_1 = ADDR_0 + 1; //Pointer Arithmetic
|
||||||
|
// Write Address
|
||||||
|
ADDR_2 = ADDR_0 + 2; //Pointer Arithmetic
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_SOCKETS; i++) {
|
||||||
|
sock_fd[i] = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
if (sock_fd[i] < 0) {
|
||||||
|
fprintf(stderr, "Socket %d", i);
|
||||||
|
perror("Could not open socket");
|
||||||
|
bail(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bind_addr[0] = (struct sockaddr_in){AF_INET, htons(PORT_CONFIG_PB + PORT_CONFIG_D0 + PORT_CONFIG_DG * src_did), {inet_addr("239.255.0.1")}};
|
||||||
|
bind_addr[1] = (struct sockaddr_in){AF_INET, htons(PORT_CONFIG_PB + PORT_CONFIG_D0 + PORT_CONFIG_DG * src_did), {src_addr}};
|
||||||
|
bind_addr[2] = (struct sockaddr_in){AF_INET, htons(PORT_CONFIG_PB + PORT_CONFIG_D1 + PORT_CONFIG_DG * src_did), {src_addr}};
|
||||||
|
bind_addr[3] = (struct sockaddr_in){AF_INET, htons(PORT_CONFIG_PB + PORT_CONFIG_D2 + PORT_CONFIG_DG * src_did), {src_addr}};
|
||||||
|
bind_addr[4] = (struct sockaddr_in){AF_INET, htons(PORT_CONFIG_PB + PORT_CONFIG_D3 + PORT_CONFIG_DG * src_did), {src_addr}};
|
||||||
|
printf("bind_addr[0]: %s:%d\n",inet_ntoa(bind_addr[0].sin_addr),ntohs(bind_addr[0].sin_port));
|
||||||
|
printf("bind_addr[1]: %s:%d\n",inet_ntoa(bind_addr[1].sin_addr),ntohs(bind_addr[1].sin_port));
|
||||||
|
printf("bind_addr[2]: %s:%d\n",inet_ntoa(bind_addr[2].sin_addr),ntohs(bind_addr[2].sin_port));
|
||||||
|
printf("bind_addr[3]: %s:%d\n",inet_ntoa(bind_addr[3].sin_addr),ntohs(bind_addr[3].sin_port));
|
||||||
|
printf("bind_addr[4]: %s:%d\n",inet_ntoa(bind_addr[4].sin_addr),ntohs(bind_addr[4].sin_port));
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_SOCKETS; i++) {
|
||||||
|
if (bind(sock_fd[i], (const struct sockaddr*) &bind_addr[i], sizeof(bind_addr[i])) < 0) {
|
||||||
|
fprintf(stderr, "bind_addr[%d]", i);
|
||||||
|
perror("Bind failed");
|
||||||
|
bail(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_mutex_init(&mutex,NULL) != 0){
|
||||||
|
perror("pthread_mutex_init failed");
|
||||||
|
bail(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_t thread_id;
|
||||||
|
if (pthread_create(&thread_id, NULL, send_thread, NULL) != 0){
|
||||||
|
perror("pthread_create failed");
|
||||||
|
bail(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_in src, dest;
|
||||||
|
char buffer[BUF_LENGTH];
|
||||||
|
int n, addrlen, j;
|
||||||
|
uint32_t* p;
|
||||||
|
|
||||||
|
printf("Entering Receive Loop\n");
|
||||||
|
|
||||||
|
while (!done) {
|
||||||
|
/*UDP INPUT*/
|
||||||
|
for (j = 0; j < NUM_SOCKETS; j++) {
|
||||||
|
addrlen = sizeof(src);
|
||||||
|
n = recvfrom(sock_fd[j], buffer, BUF_LENGTH, MSG_DONTWAIT | MSG_TRUNC, (struct sockaddr*) &src, &addrlen);
|
||||||
|
if (n < 0) {
|
||||||
|
// Error
|
||||||
|
if (errno != EAGAIN && errno != EWOULDBLOCK) {
|
||||||
|
perror("rcvfrom() error");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Exit Condition
|
||||||
|
else if (n > 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n > 0) {
|
||||||
|
//Buffer not 4-Byte aligned
|
||||||
|
if ((n % 4) != 0) {
|
||||||
|
// Add zero byte padding
|
||||||
|
for(int offset = 4 - (n % 4); offset > 0; offset--){
|
||||||
|
buffer[n+offset-1] = 0;
|
||||||
|
}
|
||||||
|
// Convert n to Word Count
|
||||||
|
n = (n / 4) + 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Convert n to Word Count
|
||||||
|
n = n / 4;
|
||||||
|
}
|
||||||
|
//SANITY CHECK
|
||||||
|
if (addrlen != sizeof(src)){
|
||||||
|
fprintf(stderr, "rcvfrom() returned unexpected addrlen.\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
while (pthread_mutex_lock(&mutex) != 0){
|
||||||
|
perror("pthread_mutex_lock failed");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// Write SRC Address
|
||||||
|
*ADDR_2 = src.sin_addr.s_addr;
|
||||||
|
// Write DEST Address
|
||||||
|
*ADDR_2 = bind_addr[j].sin_addr.s_addr;
|
||||||
|
// Write UDP Ports
|
||||||
|
*ADDR_2 = ((bind_addr[j].sin_port << 16) & 0xFFFF0000) | (src.sin_port & 0xFFFF);
|
||||||
|
// Write Packet Length
|
||||||
|
*ADDR_2 = htonl(n);
|
||||||
|
// Write Packet
|
||||||
|
p = (uint32_t*) buffer;
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
*ADDR_2 = p[i];
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
while (pthread_mutex_unlock(&mutex) != 0){
|
||||||
|
perror("pthread_mutex_lock failed");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
if (!quiet){
|
||||||
|
printf("Packet received (%d Words)\n", n);
|
||||||
|
printf(" Src: %s:%d\n",inet_ntoa(bind_addr[j].sin_addr),ntohs(bind_addr[j].sin_port));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
else {
|
||||||
|
nanosleep(&us, NULL); //Wait 1 us
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
printf("Receive Loop exited.\n");
|
||||||
|
|
||||||
|
printf("Waiting for threads...\n");
|
||||||
|
pthread_join(thread_id, NULL);
|
||||||
|
|
||||||
|
bail(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* send_thread(void* vargp){
|
||||||
|
|
||||||
|
struct sockaddr_in src, dest;
|
||||||
|
char buffer[BUF_LENGTH];
|
||||||
|
int n, addrlen, j;
|
||||||
|
uint32_t* p;
|
||||||
|
uint32_t tmp;
|
||||||
|
|
||||||
|
printf("Entering Send Loop\n");
|
||||||
|
|
||||||
|
while (!done) {
|
||||||
|
/*UDP OUTPUT*/
|
||||||
|
// FPGA has output
|
||||||
|
/*
|
||||||
|
while (pthread_mutex_lock(&mutex) != 0){
|
||||||
|
perror("pthread_mutex_lock failed");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
if (*ADDR_0 != 0) {
|
||||||
|
src.sin_family = AF_INET;
|
||||||
|
dest.sin_family = AF_INET;
|
||||||
|
// Read SRC Address
|
||||||
|
src.sin_addr.s_addr = *ADDR_1;
|
||||||
|
// Read DEST Address
|
||||||
|
dest.sin_addr.s_addr = *ADDR_1;
|
||||||
|
// Read UDP Ports
|
||||||
|
tmp = *ADDR_1;
|
||||||
|
dest.sin_port = ((tmp >> 16) & 0xFFFF);
|
||||||
|
src.sin_port = (tmp & 0xFFFF);
|
||||||
|
// Read Packet Length
|
||||||
|
n = ntohl(*ADDR_1);
|
||||||
|
// Read Packet
|
||||||
|
p = (uint32_t*) buffer;
|
||||||
|
for(int i = 0; i < n; i++) {
|
||||||
|
p[i] = *ADDR_1;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
while (pthread_mutex_unlock(&mutex) != 0){
|
||||||
|
perror("pthread_mutex_lock failed");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// Convert n to Byte Count
|
||||||
|
n = n * 4;
|
||||||
|
|
||||||
|
// Select correct socket
|
||||||
|
j = NUM_SOCKETS;
|
||||||
|
for (int i = 0; i < NUM_SOCKETS; i++) {
|
||||||
|
if (bind_addr[i].sin_addr.s_addr == src.sin_addr.s_addr && bind_addr[i].sin_port == src.sin_port) {
|
||||||
|
j = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// No bind_addr Match
|
||||||
|
if (j == NUM_SOCKETS) {
|
||||||
|
fprintf(stderr, "Provided SRC does not have respective bind_addr\n");
|
||||||
|
fprintf(stderr, "SRC: %s %d\n", inet_ntoa(src.sin_addr), ntohs(src.sin_port));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
n = sendto(sock_fd[j], buffer, n, 0, (const struct sockaddr*) &dest, sizeof(dest));
|
||||||
|
if (n < 0) {
|
||||||
|
perror("sendto failed");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!quiet) {
|
||||||
|
printf("Packet sent (%d Words)\n", n/4);
|
||||||
|
printf(" Src: %s:%d\n",inet_ntoa(bind_addr[j].sin_addr),ntohs(bind_addr[j].sin_port));
|
||||||
|
printf(" Dest: %s:%d\n",inet_ntoa(dest.sin_addr),ntohs(dest.sin_port));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
else {
|
||||||
|
while (pthread_mutex_unlock(&mutex) != 0){
|
||||||
|
perror("pthread_mutex_lock failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
nanosleep(&us, NULL); //Wait 1 us
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
printf("Send Loop exited.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void bail(int code){
|
||||||
|
// Close File Descriptors
|
||||||
|
if (mem_fd != -1){
|
||||||
|
close(mem_fd);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < NUM_SOCKETS; i++) {
|
||||||
|
if (sock_fd[i] != -1) {
|
||||||
|
close(sock_fd[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmap /dev/mem
|
||||||
|
if (lw_bridge != NULL){
|
||||||
|
munmap(lw_bridge, LW_FPGA_SLAVES_SPAN);
|
||||||
|
}
|
||||||
|
exit(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sig_handler(int signum){
|
||||||
|
fprintf(stderr, "Received Signal %d.\n Exiting...\n", signum);
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void usage(char* name){
|
||||||
|
printf("USAGE: %s [-q] ADDRESS DOMAIN_ID\n", name);
|
||||||
|
printf("-q Quiet Flag. When specified, no information is printed during the main loops\n");
|
||||||
|
printf("ADDRESS The SRC IPv4 Address of the FPGA implementation (In x.x.x.x format)\n");
|
||||||
|
printf(" NOTE: The system has to have a interface with the same address\n");
|
||||||
|
printf("DOMAIN_ID The Domain ID of the FPGA implementation\n");
|
||||||
|
|
||||||
|
bail(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
315
sw/standalone/udp_forwarder.c
Normal file
315
sw/standalone/udp_forwarder.c
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
#include <stdio.h> //printf
|
||||||
|
#include <sys/types.h> //open
|
||||||
|
#include <sys/stat.h> //open
|
||||||
|
#include <fcntl.h> //open
|
||||||
|
#include <unistd.h> //close, getopt
|
||||||
|
#include <errno.h> //perror
|
||||||
|
#include <stdlib.h> //exit
|
||||||
|
#include <sys/mman.h> //mmap
|
||||||
|
#include <signal.h> //signal
|
||||||
|
#include <assert.h> //assert
|
||||||
|
#include <sys/socket.h> //socket, inet_aton
|
||||||
|
#include <netinet/in.h> //socket, inet_aton
|
||||||
|
#include <netinet/udp.h> //socket
|
||||||
|
#include <arpa/inet.h> //htons, ntohs, htonl, ntohl, inet_aton
|
||||||
|
#include <time.h> // nanosleep
|
||||||
|
#include <stdbool.h> // bool
|
||||||
|
#include "hps_0.h"
|
||||||
|
|
||||||
|
/*PROTOTYPES*/
|
||||||
|
void usage(char* name);
|
||||||
|
void bail(int code);
|
||||||
|
void sig_handler(int signum);
|
||||||
|
|
||||||
|
/*DEFINES*/
|
||||||
|
#define LW_FPGA_SLAVES_BASE 0xFF200000
|
||||||
|
#define LW_FPGA_SLAVES_END 0xFF3FFFFF
|
||||||
|
#define LW_FPGA_SLAVES_SPAN 0x00200000
|
||||||
|
#define BUF_LENGTH 65536
|
||||||
|
#define NUM_SOCKETS 5
|
||||||
|
#define PORT_CONFIG_PB 7400
|
||||||
|
#define PORT_CONFIG_DG 250
|
||||||
|
#define PORT_CONFIG_PG 2
|
||||||
|
#define PORT_CONFIG_D0 0
|
||||||
|
#define PORT_CONFIG_D1 10
|
||||||
|
#define PORT_CONFIG_D2 1
|
||||||
|
#define PORT_CONFIG_D3 11
|
||||||
|
|
||||||
|
/*GLOBAL VARIABLES*/
|
||||||
|
//NOTE: use void* Pointer to avoid pointer arithmetic during offset addition
|
||||||
|
void* lw_bridge = NULL;
|
||||||
|
int mem_fd = -1;
|
||||||
|
int sock_fd[NUM_SOCKETS];
|
||||||
|
uint8_t endian = -1; //0 Big Endian, 1 Little Endian
|
||||||
|
in_addr_t src_addr;
|
||||||
|
long src_did;
|
||||||
|
bool quiet = false;
|
||||||
|
|
||||||
|
/*CONSTANTS*/
|
||||||
|
const struct timespec ms = {0, 10^6}; // 1ms
|
||||||
|
const struct timespec us = {0, 10^3}; // 1us
|
||||||
|
const struct timespec sec = {1, 0}; // 1sec
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
|
||||||
|
int opt;
|
||||||
|
|
||||||
|
while ((opt = getopt(argc, argv, "q")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'q':
|
||||||
|
quiet = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* invalid option */
|
||||||
|
fprintf(stderr, "Invalid Option\n");
|
||||||
|
usage(argv[0]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (argc-optind < 2) {
|
||||||
|
fprintf(stderr, "Insufficient arguments\n");
|
||||||
|
usage(argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
src_addr = inet_addr(argv[optind]);
|
||||||
|
if (src_addr == INADDR_NONE) {
|
||||||
|
fprintf(stderr, "Invalid IP Address\n");
|
||||||
|
usage(argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
src_did = strtol(argv[optind+1], NULL, 10);
|
||||||
|
if (errno != 0) {
|
||||||
|
fprintf(stderr, "Invalid Domain ID\n");
|
||||||
|
usage(argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize Sockets to aid bail function
|
||||||
|
for (int i = 0; i < NUM_SOCKETS; i++) {
|
||||||
|
sock_fd[i] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Register Signal Handling
|
||||||
|
signal(SIGHUP, sig_handler);
|
||||||
|
signal(SIGINT, sig_handler);
|
||||||
|
signal(SIGQUIT, sig_handler);
|
||||||
|
signal(SIGABRT, sig_handler);
|
||||||
|
|
||||||
|
// Check Endianness
|
||||||
|
{
|
||||||
|
int i = 1;
|
||||||
|
endian = (* (char*) &i == 1) ? 1 : 0;
|
||||||
|
printf("ENDIANNESS: %d\n", endian);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endian != 1) {
|
||||||
|
fprintf(stderr, "Program is tested on Little Endian Systems\n");
|
||||||
|
bail(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
mem_fd = open("/dev/mem", O_RDWR | O_SYNC);
|
||||||
|
if (mem_fd < 0) {
|
||||||
|
perror("Could not open /dev/mem");
|
||||||
|
bail(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
lw_bridge = (uint32_t*)mmap(NULL, LW_FPGA_SLAVES_SPAN, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, LW_FPGA_SLAVES_BASE);
|
||||||
|
if (lw_bridge == MAP_FAILED) {
|
||||||
|
perror("Could not mmap /dev/mem");
|
||||||
|
bail(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Polling Address
|
||||||
|
uint32_t* ADDR_0 = (uint32_t*)(lw_bridge + TEST_FPGA_0_BASE);
|
||||||
|
// Read Address
|
||||||
|
uint32_t* ADDR_1 = ADDR_0 + 1; //Pointer Arithmetic
|
||||||
|
// Write Address
|
||||||
|
uint32_t* ADDR_2 = ADDR_0 + 2; //Pointer Arithmetic
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_SOCKETS; i++) {
|
||||||
|
sock_fd[i] = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
if (sock_fd[i] < 0) {
|
||||||
|
fprintf(stderr, "Socket %d", i);
|
||||||
|
perror("Could not open socket");
|
||||||
|
bail(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct sockaddr_in bind_addr[NUM_SOCKETS] = {
|
||||||
|
{AF_INET, htons(PORT_CONFIG_PB + PORT_CONFIG_D0 + PORT_CONFIG_DG * src_did), {inet_addr("239.255.0.1")}},
|
||||||
|
{AF_INET, htons(PORT_CONFIG_PB + PORT_CONFIG_D0 + PORT_CONFIG_DG * src_did), {src_addr}},
|
||||||
|
{AF_INET, htons(PORT_CONFIG_PB + PORT_CONFIG_D1 + PORT_CONFIG_DG * src_did), {src_addr}},
|
||||||
|
{AF_INET, htons(PORT_CONFIG_PB + PORT_CONFIG_D2 + PORT_CONFIG_DG * src_did), {src_addr}},
|
||||||
|
{AF_INET, htons(PORT_CONFIG_PB + PORT_CONFIG_D3 + PORT_CONFIG_DG * src_did), {src_addr}}
|
||||||
|
};
|
||||||
|
printf("bind_addr[0]: %s:%d\n",inet_ntoa(bind_addr[0].sin_addr),ntohs(bind_addr[0].sin_port));
|
||||||
|
printf("bind_addr[1]: %s:%d\n",inet_ntoa(bind_addr[1].sin_addr),ntohs(bind_addr[1].sin_port));
|
||||||
|
printf("bind_addr[2]: %s:%d\n",inet_ntoa(bind_addr[2].sin_addr),ntohs(bind_addr[2].sin_port));
|
||||||
|
printf("bind_addr[3]: %s:%d\n",inet_ntoa(bind_addr[3].sin_addr),ntohs(bind_addr[3].sin_port));
|
||||||
|
printf("bind_addr[4]: %s:%d\n",inet_ntoa(bind_addr[4].sin_addr),ntohs(bind_addr[4].sin_port));
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_SOCKETS; i++) {
|
||||||
|
if (bind(sock_fd[i], (const struct sockaddr*) &bind_addr[i], sizeof(bind_addr[i])) < 0) {
|
||||||
|
fprintf(stderr, "bind_addr[%d]", i);
|
||||||
|
perror("Bind failed");
|
||||||
|
bail(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_in src, dest;
|
||||||
|
|
||||||
|
char buffer[BUF_LENGTH];
|
||||||
|
int n, addrlen, j;
|
||||||
|
uint32_t* p;
|
||||||
|
uint32_t tmp;
|
||||||
|
|
||||||
|
printf("Entering Loop\n");
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
/*UDP OUTPUT*/
|
||||||
|
// FPGA has output
|
||||||
|
if (*ADDR_0 != 0) {
|
||||||
|
src.sin_family = AF_INET;
|
||||||
|
dest.sin_family = AF_INET;
|
||||||
|
// Read SRC Address
|
||||||
|
src.sin_addr.s_addr = *ADDR_1;
|
||||||
|
// Read DEST Address
|
||||||
|
dest.sin_addr.s_addr = *ADDR_1;
|
||||||
|
// Read UDP Ports
|
||||||
|
tmp = *ADDR_1;
|
||||||
|
dest.sin_port = ((tmp >> 16) & 0xFFFF);
|
||||||
|
src.sin_port = (tmp & 0xFFFF);
|
||||||
|
// Read Packet Length
|
||||||
|
n = ntohl(*ADDR_1);
|
||||||
|
// Read Packet
|
||||||
|
p = (uint32_t*) buffer;
|
||||||
|
for(int i = 0; i < n; i++) {
|
||||||
|
p[i] = *ADDR_1;
|
||||||
|
}
|
||||||
|
// Convert n to Byte Count
|
||||||
|
n = n * 4;
|
||||||
|
|
||||||
|
// Select correct socket
|
||||||
|
j = NUM_SOCKETS;
|
||||||
|
for (int i = 0; i < NUM_SOCKETS; i++) {
|
||||||
|
if (bind_addr[i].sin_addr.s_addr == src.sin_addr.s_addr && bind_addr[i].sin_port == src.sin_port) {
|
||||||
|
j = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// No bind_addr Match
|
||||||
|
if (j == NUM_SOCKETS) {
|
||||||
|
fprintf(stderr, "Provided SRC does not have respective bind_addr\n");
|
||||||
|
fprintf(stderr, "SRC: %s %d\n", inet_ntoa(src.sin_addr), ntohs(src.sin_port));
|
||||||
|
bail(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
n = sendto(sock_fd[j], buffer, n, 0, (const struct sockaddr*) &dest, sizeof(dest));
|
||||||
|
if (n < 0) {
|
||||||
|
perror("sendto failed");
|
||||||
|
bail(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
if (!quiet) {
|
||||||
|
printf("Packet sent (%d Words)\n", n/4);
|
||||||
|
printf(" Src: %s:%d\n",inet_ntoa(bind_addr[j].sin_addr),ntohs(bind_addr[j].sin_port));
|
||||||
|
printf(" Dest: %s:%d\n",inet_ntoa(dest.sin_addr),ntohs(dest.sin_port));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*UDP INPUT*/
|
||||||
|
for (j = 0; j < NUM_SOCKETS; j++) {
|
||||||
|
addrlen = sizeof(src);
|
||||||
|
n = recvfrom(sock_fd[j], buffer, BUF_LENGTH, MSG_DONTWAIT | MSG_TRUNC, (struct sockaddr*) &src, &addrlen);
|
||||||
|
if (n < 0) {
|
||||||
|
// Error
|
||||||
|
if (errno != EAGAIN && errno != EWOULDBLOCK) {
|
||||||
|
perror("rcvfrom() error");
|
||||||
|
bail(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Exit Condition
|
||||||
|
else if (n > 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n > 0) {
|
||||||
|
//Buffer not 4-Byte aligned
|
||||||
|
if ((n % 4) != 0) {
|
||||||
|
// Add zero byte padding
|
||||||
|
for(int offset = 4 - (n % 4); offset > 0; offset--){
|
||||||
|
buffer[n+offset-1] = 0;
|
||||||
|
}
|
||||||
|
// Convert n to Word Count
|
||||||
|
n = (n / 4) + 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Convert n to Word Count
|
||||||
|
n = n / 4;
|
||||||
|
}
|
||||||
|
//SANITY CHECK
|
||||||
|
if (addrlen != sizeof(src)){
|
||||||
|
fprintf(stderr, "rcvfrom() returned unexpected addrlen.\n");
|
||||||
|
bail(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
// Write SRC Address
|
||||||
|
*ADDR_2 = src.sin_addr.s_addr;
|
||||||
|
// Write DEST Address
|
||||||
|
*ADDR_2 = bind_addr[j].sin_addr.s_addr;
|
||||||
|
// Write UDP Ports
|
||||||
|
*ADDR_2 = ((bind_addr[j].sin_port << 16) & 0xFFFF0000) | (src.sin_port & 0xFFFF);
|
||||||
|
// Write Packet Length
|
||||||
|
*ADDR_2 = htonl(n);
|
||||||
|
// Write Packet
|
||||||
|
p = (uint32_t*) buffer;
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
*ADDR_2 = p[i];
|
||||||
|
}
|
||||||
|
if (!quiet){
|
||||||
|
printf("Packet received (%d Words)\n", n);
|
||||||
|
printf(" Src: %s:%d\n",inet_ntoa(bind_addr[j].sin_addr),ntohs(bind_addr[j].sin_port));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
else {
|
||||||
|
nanosleep(&ms, NULL); //Wait 1 ms
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bail(int code){
|
||||||
|
// Close File Descriptors
|
||||||
|
if (mem_fd != -1){
|
||||||
|
close(mem_fd);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < NUM_SOCKETS; i++) {
|
||||||
|
if (sock_fd[i] != -1) {
|
||||||
|
close(sock_fd[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmap /dev/mem
|
||||||
|
if (lw_bridge != NULL){
|
||||||
|
munmap(lw_bridge, LW_FPGA_SLAVES_SPAN);
|
||||||
|
}
|
||||||
|
exit(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sig_handler(int signum){
|
||||||
|
fprintf(stderr, "Received Signal %d.\n Exiting...\n", signum);
|
||||||
|
bail(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void usage(char* name){
|
||||||
|
printf("USAGE: %s [-q] ADDRESS DOMAIN_ID\n", name);
|
||||||
|
printf("-q Quiet Flag. When specified, no information is printed during the main loops\n");
|
||||||
|
printf("ADDRESS The SRC IPv4 Address of the FPGA implementation (In x.x.x.x format)\n");
|
||||||
|
printf(" NOTE: The system has to have a interface with the same address\n");
|
||||||
|
printf("DOMAIN_ID The Domain ID of the FPGA implementation\n");
|
||||||
|
|
||||||
|
bail(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
218
sw/standalone/udp_server.cpp
Normal file
218
sw/standalone/udp_server.cpp
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
#include <sys/socket.h> //socket, inet_aton
|
||||||
|
#include <netinet/in.h> //socket, inet_aton
|
||||||
|
#include <netinet/udp.h> //socket
|
||||||
|
#include <arpa/inet.h> //htons, ntohs, htonl, ntohl, inet_aton
|
||||||
|
#include <signal.h> //signal
|
||||||
|
#include <thread> //std::thread
|
||||||
|
#include <unordered_set> //std::unordered_set
|
||||||
|
#include <shared_mutex> //std::shared_mutex
|
||||||
|
#include <unistd.h> //close
|
||||||
|
|
||||||
|
/*PROTOTYPES*/
|
||||||
|
void send_thread();
|
||||||
|
void receive_thread();
|
||||||
|
void bail(int code);
|
||||||
|
void sig_handler(int signum);
|
||||||
|
void usage(char* name);
|
||||||
|
|
||||||
|
/*DEFINES*/
|
||||||
|
//Magic Number used to distinguish our UDP Packets (First 8 Bytes of Payload)
|
||||||
|
#define MAGIC 0xDEADBEEFCAFEFEED
|
||||||
|
//Size Limit of set (This implicitly is the limit of in transit messages expected to return) [0 means no limit]
|
||||||
|
#define MSG_LIMIT 100
|
||||||
|
//Payload size of UDP messages in Bytes [Has to be >= 16 Bytes]
|
||||||
|
#define PAYLOAD_SIZE 100
|
||||||
|
//Print extra debug messages
|
||||||
|
#define DEBUG true
|
||||||
|
|
||||||
|
/*GLOBAL VARIABLES*/
|
||||||
|
long port = 0;
|
||||||
|
int sock_fd = -1;
|
||||||
|
volatile sig_atomic_t done = false;
|
||||||
|
std::unordered_set<uint64_t> set;
|
||||||
|
std::shared_mutex mutex1;
|
||||||
|
std::shared_mutex mutex2;
|
||||||
|
struct sockaddr_in src_addr, dest_addr;
|
||||||
|
long pkt_cnt = 0;
|
||||||
|
|
||||||
|
const struct timespec ms = {0, 10^6}; // 1ms
|
||||||
|
const struct timespec us = {0, 10^3}; // 1us
|
||||||
|
const struct timespec sec = {1, 0}; // 1sec
|
||||||
|
|
||||||
|
int main(int argc, char **argv){
|
||||||
|
|
||||||
|
/*Parse Arguments*/
|
||||||
|
if (argc < 4) {
|
||||||
|
fprintf(stderr, "Insufficient arguments\n");
|
||||||
|
usage(argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Source Address
|
||||||
|
in_addr_t src_ip_addr = inet_addr(argv[1]);
|
||||||
|
if (src_ip_addr == INADDR_NONE) {
|
||||||
|
fprintf(stderr, "Invalid SRC IP Address\n");
|
||||||
|
usage(argv[0]);
|
||||||
|
}
|
||||||
|
//Destination Address
|
||||||
|
in_addr_t dest_ip_addr = inet_addr(argv[2]);
|
||||||
|
if (dest_ip_addr == INADDR_NONE) {
|
||||||
|
fprintf(stderr, "Invalid DEST IP Address\n");
|
||||||
|
usage(argv[0]);
|
||||||
|
}
|
||||||
|
//Port Number
|
||||||
|
errno = 0;
|
||||||
|
port = strtol(argv[3], NULL, 10);
|
||||||
|
if (errno != 0) {
|
||||||
|
fprintf(stderr, "Invalid Port Number\n");
|
||||||
|
usage(argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*MISC MAIN THREAD*/
|
||||||
|
//Register Signal Handling
|
||||||
|
signal(SIGHUP, sig_handler);
|
||||||
|
signal(SIGINT, sig_handler);
|
||||||
|
signal(SIGQUIT, sig_handler);
|
||||||
|
signal(SIGABRT, sig_handler);
|
||||||
|
|
||||||
|
/*SOCKET OPERATIONS*/
|
||||||
|
src_addr = {AF_INET, htons(port), {src_ip_addr}};
|
||||||
|
dest_addr = {AF_INET, htons(port), {dest_ip_addr}};
|
||||||
|
|
||||||
|
//Open Socket
|
||||||
|
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
//Bind Socket
|
||||||
|
if (bind(sock_fd, (const struct sockaddr*) &src_addr, sizeof(src_addr)) < 0) {
|
||||||
|
perror("Bind failed");
|
||||||
|
bail(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
//Set Receive Timeout
|
||||||
|
const struct timeval tv = {.tv_usec = 1000}; //1 ms
|
||||||
|
setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
||||||
|
|
||||||
|
/*MAIN STUFF*/
|
||||||
|
|
||||||
|
//Start Threads
|
||||||
|
std::thread t1(send_thread);
|
||||||
|
std::thread t2(receive_thread);
|
||||||
|
|
||||||
|
printf("Entering Main loop\n");
|
||||||
|
|
||||||
|
//MAIN LOOP
|
||||||
|
while(!done){
|
||||||
|
//Sleep
|
||||||
|
nanosleep(&sec, NULL); //Wait 1 sec
|
||||||
|
//Read and reset Packet Counter
|
||||||
|
mutex2.lock();
|
||||||
|
long cnt = pkt_cnt;
|
||||||
|
pkt_cnt = 0;
|
||||||
|
mutex2.unlock();
|
||||||
|
//Print Results
|
||||||
|
printf("Throughput: %ld\n", cnt);
|
||||||
|
//Debug Messages
|
||||||
|
if(DEBUG){
|
||||||
|
mutex1.lock();
|
||||||
|
cnt = set.size();
|
||||||
|
mutex1.unlock();
|
||||||
|
printf("Current set size: %ld\n", cnt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Main loop exited. Waiting for threads...\n");
|
||||||
|
|
||||||
|
//Wait for Threads
|
||||||
|
t1.join();
|
||||||
|
t2.join();
|
||||||
|
|
||||||
|
bail(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_thread(){
|
||||||
|
uint64_t id = 0;
|
||||||
|
char buffer[PAYLOAD_SIZE];
|
||||||
|
uint64_t* p = (uint64_t*) buffer;
|
||||||
|
long size;
|
||||||
|
|
||||||
|
//Magic Number
|
||||||
|
p[0] = MAGIC;
|
||||||
|
|
||||||
|
while(!done){
|
||||||
|
mutex1.lock();
|
||||||
|
set.insert(id);
|
||||||
|
size = set.size();
|
||||||
|
mutex1.unlock();
|
||||||
|
p[1] = id;
|
||||||
|
if (sendto(sock_fd, buffer, PAYLOAD_SIZE, 0, (const struct sockaddr*) &dest_addr, sizeof(dest_addr)) < 0) {
|
||||||
|
perror("sendto failed");
|
||||||
|
mutex1.lock();
|
||||||
|
set.erase(id);
|
||||||
|
mutex1.unlock();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
id++;
|
||||||
|
//Reached Message Limit
|
||||||
|
while (!done && MSG_LIMIT != 0 && size >= MSG_LIMIT){
|
||||||
|
//Wait
|
||||||
|
nanosleep(&us, NULL); //Wait 1 us
|
||||||
|
mutex1.lock();
|
||||||
|
size = set.size();
|
||||||
|
mutex1.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void receive_thread(){
|
||||||
|
char buffer[PAYLOAD_SIZE];
|
||||||
|
uint64_t* p = (uint64_t*) buffer;
|
||||||
|
socklen_t addrlen;
|
||||||
|
struct sockaddr_in src;
|
||||||
|
|
||||||
|
while(!done){
|
||||||
|
//Receive
|
||||||
|
if (recvfrom(sock_fd, buffer, PAYLOAD_SIZE, MSG_TRUNC, (struct sockaddr*) &src, &addrlen) < 0){
|
||||||
|
// Error
|
||||||
|
if (errno != EAGAIN && errno != EWOULDBLOCK) {
|
||||||
|
perror("rcvfrom() error");
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//Packet not relevant
|
||||||
|
if (addrlen != sizeof(dest_addr) && src.sin_family != dest_addr.sin_family && src.sin_addr.s_addr != dest_addr.sin_addr.s_addr && src.sin_port != dest_addr.sin_port && p[0] != MAGIC){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
mutex1.lock();
|
||||||
|
auto it = set.find(p[1]);
|
||||||
|
if (it != set.end()){
|
||||||
|
set.erase(it);
|
||||||
|
mutex1.unlock();
|
||||||
|
mutex2.lock();
|
||||||
|
pkt_cnt++;
|
||||||
|
mutex2.unlock();
|
||||||
|
} else {
|
||||||
|
mutex1.unlock();
|
||||||
|
fprintf(stderr, "ID not in set.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bail(int code){
|
||||||
|
//Close Sockets
|
||||||
|
if (sock_fd != -1) {
|
||||||
|
close(sock_fd);
|
||||||
|
}
|
||||||
|
//DONE
|
||||||
|
exit(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sig_handler(int signum){
|
||||||
|
fprintf(stderr, "Received Signal %d.\n Exiting...\n", signum);
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void usage(char* name){
|
||||||
|
printf("USAGE: %s SRC_ADDRESS DEST_ADDRESS PORT\n", name);
|
||||||
|
printf("SRC_ADDRESS The local source IPv4 Address from which to send and listen to (In x.x.x.x format)\n");
|
||||||
|
printf("DEST_ADDRESS The remote destination IPv4 Address to which to send to (In x.x.x.x format)\n");
|
||||||
|
printf("PORT The port number used for both Addresses\n");
|
||||||
|
|
||||||
|
bail(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
10
syn/DE10_NANO_SoC_GHRD/output_files/BACKUP/README.txt
Normal file
10
syn/DE10_NANO_SoC_GHRD/output_files/BACKUP/README.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
NAME RELATED_ENTITY RELATED_SW
|
||||||
|
test_fpga /syn/test_fpga.vhd /sw/standalone/test_fpga.c
|
||||||
|
dds_loopback /src/Tests/Level_2/L2_Testbench_Lib4.vhd /sw/CycloneDDS/src/Loopback
|
||||||
|
ros_service /src/ros2/Tests/Level_2/L2_Testbench_ROS_Lib2.vhd /sw/ros/src/cpp_srvcli
|
||||||
|
ros_action /src/ros2/Tests/Level_2/L2_Testbench_ROS_Lib4.vhd /sw/ros/src/action_tutorials_cpp
|
||||||
|
ros_rtt /src/ros2/Tests/Level_2/L2_Testbench_ROS_Lib6.vhd /sw/ros/src/cpp_loopback
|
||||||
|
loopback /syn/loopback.vhd /sw/standalone/udp_server.cpp
|
||||||
|
/sw/standalone/udp_client.cpp
|
||||||
|
|
||||||
|
NOTE: /sw/standalone/udp_forwarder.c is to be used in the HPS to forward UDP Packets to/from the FPGA implementation.
|
||||||
Loading…
Reference in New Issue
Block a user