Skip to content

Commit

Permalink
Various improvements following industrial usage (#17)
Browse files Browse the repository at this point in the history
Fix: handle timeout by polling socket instead of delegating to Linux kernel (kernel precision is too low for a realtime behavior @ 1 kHz) 
Update: error handler now takes the error reason in argument to let the user takes the right decision (a lost datagram is different from a non responsive slave)
Various cleanup and fixes (gcc warnings, ASAN and UBSAN feedback).
  • Loading branch information
leducp authored Oct 6, 2021
1 parent 08ae889 commit d2d16bb
Show file tree
Hide file tree
Showing 17 changed files with 351 additions and 198 deletions.
14 changes: 9 additions & 5 deletions example/easycat_example.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ int main(int argc, char* argv[])
uint8_t io_buffer[2048];
try
{
socket->open(argv[1], 100us);
socket->open(argv[1], 2ms);
bus.init();

print_current_state();
Expand All @@ -53,7 +53,7 @@ int main(int argc, char* argv[])
return 1;
}

auto callback_error = [](){ THROW_ERROR("something bad happened"); };
auto callback_error = [](DatagramState const&){ THROW_ERROR("something bad happened"); };

try
{
Expand Down Expand Up @@ -82,6 +82,8 @@ int main(int argc, char* argv[])
return 1;
}

socket->setTimeout(500us);

constexpr int64_t LOOP_NUMBER = 12 * 3600 * 1000; // 12h
FILE* stat_file = fopen("stats.csv", "w");
fwrite("latency\n", 1, 8, stat_file);
Expand All @@ -90,17 +92,19 @@ int main(int argc, char* argv[])
int64_t last_error = 0;
for (int64_t i = 0; i < LOOP_NUMBER; ++i)
{
sleep(20ms);
sleep(1ms);

try
{
nanoseconds t1 = since_epoch();
bus.sendLogicalRead(callback_error);
bus.sendLogicalWrite(callback_error);
bus.sendrefreshErrorCounters(callback_error);
bus.sendMailboxesChecks(callback_error);
bus.sendRefreshErrorCounters(callback_error);
bus.sendMailboxesReadChecks(callback_error);
bus.sendMailboxesWriteChecks(callback_error);
bus.sendReadMessages(callback_error);
bus.sendWriteMessages(callback_error);
bus.finalizeDatagrams();
nanoseconds t2 = since_epoch();


Expand Down
1 change: 1 addition & 0 deletions include/kickcat/AbstractSocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ namespace kickcat
virtual ~AbstractSocket() = default;

virtual void open(std::string const& interface, microseconds timeout) = 0;
virtual void setTimeout(microseconds timeout) = 0;
virtual void close() noexcept = 0;
virtual int32_t read(uint8_t* frame, int32_t frame_size) = 0;
virtual int32_t write(uint8_t const* frame, int32_t frame_size) = 0;
Expand Down
38 changes: 20 additions & 18 deletions include/kickcat/Bus.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,27 +50,29 @@ namespace kickcat

// asynchrone read/write/mailbox/state methods
// It enable users to do one or multiple operations in a row, process something, and process all awaiting frames.
void sendGetALStatus(Slave& slave, std::function<void()> const& error);

void sendLogicalRead(std::function<void()> const& error);
void sendLogicalWrite(std::function<void()> const& error);
void sendLogicalReadWrite(std::function<void()> const& error);
void sendMailboxesChecks(std::function<void()> const& error); // Fetch in/out mailboxes states (full/empty) of compatible slaves
void sendNop(std::function<void()> const& error); // Send a NOP datagram
void sendGetALStatus(Slave& slave, std::function<void(DatagramState const&)> const& error);

void sendLogicalRead(std::function<void(DatagramState const&)> const& error);
void sendLogicalWrite(std::function<void(DatagramState const&)> const& error);
void sendLogicalReadWrite(std::function<void(DatagramState const&)> const& error);
void sendMailboxesReadChecks (std::function<void(DatagramState const&)> const& error); // Fetch in mailboxes states (full/empty) of compatible slaves
void sendMailboxesWriteChecks(std::function<void(DatagramState const&)> const& error); // Fetch out mailboxes states (full/empty) of compatible slaves
void sendNop(std::function<void(DatagramState const&)> const& error); // Send a NOP datagram
void processAwaitingFrames();

// Process messages (read or write slave mailbox) - one at once per slave.
void sendReadMessages(std::function<void()> const& error);
void sendWriteMessages(std::function<void()> const& error);
void sendrefreshErrorCounters(std::function<void()> const& error);

// helpers around start/finalize oeprations
void processDataRead(std::function<void()> const& error);
void processDataWrite(std::function<void()> const& error);
void processDataReadWrite(std::function<void()> const& error);

void checkMailboxes( std::function<void()> const& error);
void processMessages(std::function<void()> const& error);
void sendReadMessages(std::function<void(DatagramState const&)> const& error);
void sendWriteMessages(std::function<void(DatagramState const&)> const& error);
void sendRefreshErrorCounters(std::function<void(DatagramState const&)> const& error);

// helpers around start/finalize operations
void finalizeDatagrams(); // send a frame if there is awaiting datagram inside
void processDataRead(std::function<void(DatagramState const&)> const& error);
void processDataWrite(std::function<void(DatagramState const&)> const& error);
void processDataReadWrite(std::function<void(DatagramState const&)> const& error);

void checkMailboxes( std::function<void(DatagramState const&)> const& error);
void processMessages(std::function<void(DatagramState const&)> const& error);

enum Access
{
Expand Down
22 changes: 15 additions & 7 deletions include/kickcat/Link.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ namespace kickcat
{
class AbstractSocket;

enum class DatagramState
{
LOST,
INVALID_WKC,
NO_HANDLER,
OK
};

/// \brief Handle link layer
/// \details This class is responsible to handle frames and datagrams on the link layers:
/// - associate an id to each datagram to call the associate callback later without depending on the read order
Expand All @@ -25,12 +33,12 @@ namespace kickcat
void writeThenRead(Frame& frame);

void addDatagram(enum Command command, uint32_t address, void const* data, uint16_t data_size,
std::function<bool(DatagramHeader const*, uint8_t const* data, uint16_t wkc)> const& process,
std::function<void()> const& error);
std::function<DatagramState(DatagramHeader const*, uint8_t const* data, uint16_t wkc)> const& process,
std::function<void(DatagramState const& state)> const& error);
template<typename T>
void addDatagram(enum Command command, uint32_t address, T const& data,
std::function<bool(DatagramHeader const*, uint8_t const* data, uint16_t wkc)> const& process,
std::function<void()> const& error)
std::function<DatagramState(DatagramHeader const*, uint8_t const* data, uint16_t wkc)> const& process,
std::function<void(DatagramState const& state)> const& error)
{
addDatagram(command, address, &data, sizeof(data), process, error);
}
Expand All @@ -49,9 +57,9 @@ namespace kickcat

struct Callbacks
{
bool in_error{false};
std::function<bool(DatagramHeader const*, uint8_t const* data, uint16_t wkc)> process;
std::function<void()> error;
DatagramState status{DatagramState::LOST};
std::function<DatagramState(DatagramHeader const*, uint8_t const* data, uint16_t wkc)> process;
std::function<void(DatagramState const& state)> error;
};
std::array<Callbacks, 256> callbacks_{};
};
Expand Down
5 changes: 4 additions & 1 deletion include/kickcat/LinuxSocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,23 @@ namespace kickcat
class LinuxSocket : public AbstractSocket
{
public:
LinuxSocket(microseconds rx_coalescing = -1us);
LinuxSocket(microseconds rx_coalescing = -1us, microseconds polling_period = 20us);
virtual ~LinuxSocket()
{
close();
}

void open(std::string const& interface, microseconds timeout) override;
void setTimeout(microseconds timeout) override;
void close() noexcept override;
int32_t read(uint8_t* frame, int32_t frame_size) override;
int32_t write(uint8_t const* frame, int32_t frame_size) override;

private:
int fd_{-1};
microseconds rx_coalescing_;
microseconds timeout_;
microseconds polling_period_;
};
}

Expand Down
15 changes: 15 additions & 0 deletions include/kickcat/Slave.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <vector>
#include <string_view>
#include <sstream>

#include "protocol.h"
#include "Mailbox.h"
Expand All @@ -14,8 +15,21 @@ namespace kickcat
void parseSII();

void printInfo() const;
std::string getInfo() const;

void printPDOs() const;
std::string getPDOs() const;

void printErrorCounters() const;
std::string getErrorCounters() const;
int computeErrorCounters() const;

/// \return the number of new errors since last call.
int computeRelativeErrorCounters();

/// \brief Check the total number of errors since start of the slave
/// \return True if too many errors detected since start of the slave. Return false otherwise.
bool checkAbsoluteErrorCounters(int max_absolute_errors);

uint16_t address;
uint8_t al_status{State::INVALID};
Expand Down Expand Up @@ -62,6 +76,7 @@ namespace kickcat
PIMapping output;

ErrorCounters error_counters;
int previous_errors_sum{0};

private:
void parseStrings(uint8_t const* section_start);
Expand Down
Loading

0 comments on commit d2d16bb

Please sign in to comment.