Use#

Getting Started#

This guide explains how to set up and use the Communication module of Eclipse S-CORE.

To integrate the communication module into your project, follow these steps:

1. Setup#

Start by creating a new project in your preferred IDE (e.g., Visual Studio Code). A ready-to-use devcontainer is available under:

Reference documentation for the communication module:

1.1 Add this to your MODULE.bazel:#

Be aware that the version numbers change over time. Always check the latest versions in the respective bazel registry

1.2 Insert this into your .bazelrc:#

1.3 Run Bazel#

If you start with an empty project, add an empty file named BUILD into your project root. Now you can now run build the project with the command bazel bazel build //...

(At this point nothing happens yet, because no targets are defined). You can now either continue with this guide to create a minimal consumer-producer example or start coding your own application

2. Use it :)#

Once your project is set up, you can start sending and receiving messages.

2.1 Basic Structure#

Create a folder named src in your root project directory. Inside src, create the following folders:

  • consumer

  • producer

additional folders if needed.

2.2 Message#

Before sending messages, define the data type to be exchanged. Create a file named message_data.h in your src directory.

#ifndef SCORE_MESSAGE_DATA_H
#define SCORE_MESSAGE_DATA_H

#include "score/mw/com/types.h"

namespace com_example
{

struct Message
{
  std::string message;
};

template <typename Trait>
class IPCInterface : public Trait::Base
{
  public:
    using Trait::Base::Base;

    typename Trait::template Event<Message> message_{*this, "message"};
};

using IPCInterfaceProxy = score::mw::com::AsProxy<IPCInterface>;
using IPCInterfaceSkeleton = score::mw::com::AsSkeleton<IPCInterface>;

} // namespace com_example

#endif //SCORE_MESSAGE_DATA_H

What this code does:

  • struct Message defines the payload - here is a simple message string.

  • IPCInterface defines the communication interface used by producer and consumer.

This interface is necessary so producer and consumer know what to send/receive. The interface provides two roles:

  • IPCInterfaceProxy: client-role

  • IPCInterfaceSkeleton: server-role

More details are available in the Eclipse S-Core Communication Doc.

2.3 Producer#

The producer sends data. Navigate to the producer directory and create a new file called producer.h.

#ifndef SCORE_PRODUCER_H
#define SCORE_PRODUCER_H

#include "score/mw/com/impl/instance_specifier.h"
#include "src/message_data.h"

class Producer
{
  public:
    Producer(const score::mw::com::impl::InstanceSpecifier& instance_specifier);
    ~Producer() = default;

    int RunProducer(const std::chrono::milliseconds cycle_time,
                    const std::size_t num_cycles);

  private:
    score::Result<com_example::IPCInterfaceSkeleton> create_result;
};

#endif //SCORE_PRODUCER_H

As you can see, the header is lightweight; we will only need to use the Constructor and RunProducer from outside.

create_result is our IPCInterfaceSkeleton specified with the instance_specifier from our score_mw_com.json.

After that, create the file producer.cpp.

#include "producer.h"
#include "src/message_data.h"

Producer::Producer(const score::mw::com::impl::InstanceSpecifier& instance_specifier)
  : create_result(com_example::IPCInterfaceSkeleton::Create(instance_specifier))
{
}

int Producer::RunProducer(const std::chrono::milliseconds cycle_time,
                      const std::size_t num_cycles)
{
  if (!create_result.has_value())
  {
    std::cerr << "Skeleton was not created. Cannot run producer!\n";
    return EXIT_FAILURE;
  }
  auto& skeleton = create_result.value();

  const auto offer_result = skeleton.OfferService();

  if (!offer_result.has_value())
  {
    std::cerr << "Unable to offer service for skeleton!\n";
    return EXIT_FAILURE;
  }

  std::cout << "Starting to send data\n";

  for (std::size_t cycle = 0U; cycle < num_cycles || num_cycles == 0U; ++cycle)
  {
    auto cycle_message = "Message " + std::to_string(cycle);
    auto message = com_example::Message{.message=cycle_message};
    std::cout << "Sending: " << cycle_message << std::endl;
    skeleton.message_.Send(std::move(message));

    std::this_thread::sleep_for(cycle_time);
  }

  skeleton.StopOfferService();

  return EXIT_SUCCESS;
}

What happens in the code:

The constructor initializes the communication skeleton create_result.

RunProducer:

  • checks if the initialization of create_result was successful

  • offers service

  • enters a loop and sends our messages

  • stops offering the service at the end

2.4 Consumer#

On the other side, the consumer will consume/receive the data.

Navigate to the consumer directory and create a new file called consumer.h.

2.5 Next steps#

For a complete example implementation, see the example folder

eclipse-score/communication