Integrate the J1939 features : address claiming and write. 10/22010/13
authorArthur Guyader <arthur.guyader@iot.bzh>
Mon, 29 Jul 2019 12:53:14 +0000 (14:53 +0200)
committerArthur Guyader <arthur.guyader@iot.bzh>
Mon, 12 Aug 2019 13:06:45 +0000 (15:06 +0200)
This commit allows to write J1939 messages and add the management
of the address claiming.

Bug-AGL: SPEC-2386

Signed-off-by: Arthur Guyader <arthur.guyader@iot.bzh>
Change-Id: I1bb95a7ba6f6ebe463319c3972d9d46897181d51

18 files changed:
conf.d/cmake/config.cmake
low-can-binding/CMakeLists.txt
low-can-binding/binding/application.cpp
low-can-binding/binding/application.hpp
low-can-binding/binding/low-can-cb.cpp
low-can-binding/binding/low-can-subscription.cpp
low-can-binding/binding/low-can-subscription.hpp
low-can-binding/can/message/can-message.hpp
low-can-binding/can/message/j1939-message.cpp
low-can-binding/can/message/j1939-message.hpp
low-can-binding/can/message/message.hpp
low-can-binding/utils/socketcan-j1939.hpp [deleted file]
low-can-binding/utils/socketcan-j1939/socketcan-j1939-addressclaiming.cpp [new file with mode: 0644]
low-can-binding/utils/socketcan-j1939/socketcan-j1939-addressclaiming.hpp [new file with mode: 0644]
low-can-binding/utils/socketcan-j1939/socketcan-j1939-data.cpp [new file with mode: 0644]
low-can-binding/utils/socketcan-j1939/socketcan-j1939-data.hpp [new file with mode: 0644]
low-can-binding/utils/socketcan-j1939/socketcan-j1939.cpp [new file with mode: 0644]
low-can-binding/utils/socketcan-j1939/socketcan-j1939.hpp [new file with mode: 0644]

index e6da31a..f657367 100644 (file)
@@ -53,6 +53,9 @@ else()
        message("Feature J1939 enabled")
        set(WITH_FEATURE_J1939 ON)
        add_definitions(-DUSE_FEATURE_J1939)
+       # Define name for ECU
+       set(J1939_NAME_ECU 0x1239)
+       add_definitions(-DJ1939_NAME_ECU=${J1939_NAME_ECU})
 endif()
 
 # Kernel selection if needed. You can choose between a
index 71e467d..63ed99c 100644 (file)
@@ -51,7 +51,9 @@ PROJECT_TARGET_ADD(low-can)
        if(WITH_FEATURE_J1939)
                set(SOURCES_J1939
                        can/message/j1939-message.cpp
-                       utils/socketcan-j1939.cpp
+                       utils/socketcan-j1939/socketcan-j1939.cpp
+                       utils/socketcan-j1939/socketcan-j1939-data.cpp
+                       utils/socketcan-j1939/socketcan-j1939-addressclaiming.cpp
                )
        endif()
 
index f0f008c..d93bf0c 100644 (file)
@@ -154,3 +154,22 @@ bool application_t::isEngineOn()
 
        return engine_on;
 }
+
+#ifdef USE_FEATURE_J1939
+std::shared_ptr<utils::socketcan_t> application_t::get_socket_address_claiming()
+{
+       return subscription_address_claiming_->get_socket();
+}
+
+std::shared_ptr<low_can_subscription_t> application_t::get_subscription_address_claiming()
+{
+       return subscription_address_claiming_;
+}
+
+
+void application_t::set_subscription_address_claiming(std::shared_ptr<low_can_subscription_t> new_subscription)
+{
+       subscription_address_claiming_ = new_subscription;
+}
+
+#endif
\ No newline at end of file
index b73ace7..a5d2285 100644 (file)
 #include "../can/message-set.hpp"
 #include "../can/signals.hpp"
 #include "../diagnostic/diagnostic-manager.hpp"
+#ifdef USE_FEATURE_J1939
+       #include "../utils/socketcan-j1939/socketcan-j1939-data.hpp"
+       #include "../utils/socketcan-j1939/socketcan-j1939-addressclaiming.hpp"
+#endif
 
 ///
 /// @brief Class represents a configuration attached to the binding.
@@ -48,7 +52,10 @@ class application_t
                std::vector<std::shared_ptr<message_set_t> > message_set_; ///< Vector holding all message set from JSON signals description file
 
                std::map<std::string, std::shared_ptr<low_can_subscription_t> > can_devices_; ///< Map containing all independant opened CAN sockets, key is the socket int value.
-
+#ifdef USE_FEATURE_J1939
+               std::shared_ptr<low_can_subscription_t> subscription_address_claiming_; ///< Subscription holding the socketcan J1939 which is in charge of handling the address claiming protocol
+               //std::shared_ptr<utils::socketcan_j1939_addressclaiming_t> socket_address_claiming_;
+#endif
                application_t(); ///< Private constructor with implementation generated by the AGL generator.
 
        public:
@@ -84,6 +91,13 @@ class application_t
 
                void set_active_message_set(uint8_t id);
 
+#ifdef USE_FEATURE_J1939
+               std::shared_ptr<utils::socketcan_t> get_socket_address_claiming();
+
+               std::shared_ptr<low_can_subscription_t> get_subscription_address_claiming();
+
+               void set_subscription_address_claiming(std::shared_ptr<low_can_subscription_t> new_subscription);
+#endif
 /*
                /// TODO: implement this function as method into can_bus class
                /// @brief Pre initialize actions made before CAN bus initialization
index 9c21b67..5fdd961 100644 (file)
@@ -26,7 +26,6 @@
 #include <thread>
 #include <wrap-json.h>
 #include <systemd/sd-event.h>
-
 #include "openxc.pb.h"
 #include "application.hpp"
 #include "../can/can-encoder.hpp"
 #include "../diagnostic/diagnostic-message.hpp"
 #include "../utils/openxc-utils.hpp"
 
+#ifdef USE_FEATURE_J1939
+       #include "../can/message/j1939-message.hpp"
+       #include <linux/can/j1939.h>
+#endif
 ///******************************************************************************
 ///
 ///            SystemD event loop Callbacks
@@ -82,18 +85,27 @@ static void push_n_notify(std::shared_ptr<message_t> m)
 
 int read_message(sd_event_source *event_source, int fd, uint32_t revents, void *userdata)
 {
+
        low_can_subscription_t* can_subscription = (low_can_subscription_t*)userdata;
 
 
        if ((revents & EPOLLIN) != 0)
        {
-               std::shared_ptr<utils::socketcan_t> s = can_subscription->get_socket();
-               std::shared_ptr<message_t> message = s->read_message();
-
-               // Sure we got a valid CAN message ?
-               if (! message->get_id() == 0 && ! message->get_length() == 0)
+               utils::signals_manager_t& sm = utils::signals_manager_t::instance();
+               std::lock_guard<std::mutex> subscribed_signals_lock(sm.get_subscribed_signals_mutex());
+               if(can_subscription->get_index() != -1)
                {
-                       push_n_notify(message);
+                       std::shared_ptr<utils::socketcan_t> s = can_subscription->get_socket();
+                       if(s->socket() && s->socket() != -1)
+                       {
+                               std::shared_ptr<message_t> message = s->read_message();
+
+                               // Sure we got a valid CAN message ?
+                               if (! message->get_id() == 0 && ! message->get_length() == 0 && message->get_msg_format() != message_format_t::INVALID)
+                               {
+                                       push_n_notify(message);
+                               }
+                       }
                }
        }
 
@@ -164,8 +176,8 @@ static int subscribe_unsubscribe_signal(afb_req_t request,
 
        if( (ret = s[sub_index]->unsubscribe(request)) < 0)
                return ret;
+       s.find(sub_index)->second->set_index(-1);
        s.erase(sub_index);
-
        return ret;
 }
 
@@ -227,7 +239,9 @@ static int subscribe_unsubscribe_diagnostic_messages(afb_req_t request,
                else
                {
                        if(sig->get_supported())
-                       {AFB_DEBUG("%s cancelled due to unsubscribe", sig->get_name().c_str());}
+                       {
+                               AFB_DEBUG("%s cancelled due to unsubscribe", sig->get_name().c_str());
+                       }
                        else
                        {
                                AFB_WARNING("signal: %s isn't supported. Canceling operation.", sig->get_name().c_str());
@@ -240,7 +254,6 @@ static int subscribe_unsubscribe_diagnostic_messages(afb_req_t request,
 
                rets++;
        }
-
        return rets;
 }
 
@@ -493,6 +506,10 @@ static int send_frame(struct canfd_frame& cfd, const std::string& bus_name, sock
        {
                return low_can_subscription_t::tx_send(*cd[bus_name], cfd, bus_name);
        }
+       else if(type == socket_type::J1939)
+       {
+               return low_can_subscription_t::j1939_send(*cd[bus_name], cfd, bus_name);
+       }
        else{
                return -1;
        }
@@ -517,6 +534,12 @@ static int send_message(message_t *message, const std::string& bus_name, socket_
        {
                return low_can_subscription_t::tx_send(*cd[bus_name], message, bus_name);
        }
+#ifdef USE_FEATURE_J1939
+       else if(type == socket_type::J1939)
+       {
+               return low_can_subscription_t::j1939_send(*cd[bus_name], message, bus_name);
+       }
+#endif
        else
        {
                return -1;
@@ -548,6 +571,10 @@ static void write_raw_frame(afb_req_t request, const std::string& bus_name, mess
                {
                        afb_req_fail(request, "Invalid", "Data array must hold 1 to 8 values.");
                }
+               else if(type == socket_type::J1939)
+               {
+                       afb_req_fail(request, "Invalid", "Data array too large");
+               }
                else
                {
                        afb_req_fail(request, "Invalid", "Invalid socket type");
@@ -583,6 +610,16 @@ static void write_frame(afb_req_t request, const std::string& bus_name, json_obj
                message = new can_message_t(CANFD_MAX_DLEN,(uint32_t)id,(uint32_t)length,message_format_t::STANDARD,false,0,data,0);
                write_raw_frame(request,bus_name,message,can_data,socket_type::BCM);
        }
+#ifdef USE_FEATURE_J1939
+       else if(!wrap_json_unpack(json_value, "{si, si, so !}",
+                             "pgn", &id,
+                             "length", &length,
+                             "data", &can_data))
+       {
+               message = new j1939_message_t(J1939_MAX_DLEN,(uint32_t)length,message_format_t::J1939,data,0,J1939_NO_NAME,(pgn_t)id,J1939_NO_ADDR);
+               write_raw_frame(request,bus_name,message,can_data,socket_type::J1939);
+       }
+#endif
        else
        {
                afb_req_fail(request, "Invalid", "Frame object malformed");
@@ -800,8 +837,9 @@ void list(afb_req_t request)
 /// @return Exit code, zero if success.
 int init_binding(afb_api_t api)
 {
-       uint32_t ret = 1;
-       can_bus_t& can_bus_manager = application_t::instance().get_can_bus_manager();
+       int ret = 1;
+       application_t& application = application_t::instance();
+       can_bus_t& can_bus_manager = application.get_can_bus_manager();
 
        can_bus_manager.set_can_devices();
        can_bus_manager.start_threads();
@@ -829,8 +867,38 @@ int init_binding(afb_api_t api)
                subscribe_unsubscribe_diagnostic_messages(request, true, sf.diagnostic_messages, event_filter, s, true);
        }
 
+
+#ifdef USE_FEATURE_J1939
+       std::vector<std::shared_ptr<message_definition_t>> current_messages_definition = application.get_messages_definition();
+       for(std::shared_ptr<message_definition_t> message_definition: current_messages_definition)
+       {
+               if(message_definition->is_j1939())
+               {
+                       std::shared_ptr<low_can_subscription_t> low_can_j1939 = std::make_shared<low_can_subscription_t>();
+
+                       application.set_subscription_address_claiming(low_can_j1939);
+
+                       ret = low_can_subscription_t::open_socket(*low_can_j1939,
+                                                                                                       message_definition->get_bus_device_name(),
+                                                                                                       socket_type::J1939_ADDR_CLAIM);
+                       if(ret < 0)
+                       {
+                               AFB_ERROR("Error open socket address claiming for j1939 protocol");
+                               return -1;
+                       }
+
+//                     std::shared_ptr<low_can_subscription_t> saddrclaim = application.get_subscription_address_claiming();
+
+                       add_to_event_loop(low_can_j1939);
+                       break;
+               }
+       }
+#endif
+
        if(ret)
+       {
                AFB_ERROR("There was something wrong with CAN device Initialization.");
+       }
 
        return ret;
 }
index b31fe48..bb48888 100644 (file)
@@ -20,9 +20,8 @@
 #include "application.hpp"
 #include "canutil/write.h"
 #include "../utils/socketcan-bcm.hpp"
-
 #ifdef USE_FEATURE_J1939
-       #include "../utils/socketcan-j1939.hpp"
+#include "../utils/socketcan-j1939/socketcan-j1939-data.hpp"
 #endif
 
 low_can_subscription_t::low_can_subscription_t()
@@ -226,40 +225,24 @@ void low_can_subscription_t::set_max(float max)
        event_filter_.max = max;
 }
 
+void low_can_subscription_t::set_index(int index)
+{
+       index_ = index;
+}
+
 /// @brief Based upon which object is a subscribed CAN signal or diagnostic message
 /// it will open the socket with the required CAN bus device name.
 ///
 /// @return INVALID_SOCKET on failure, else positive integer
-int low_can_subscription_t::open_socket(low_can_subscription_t &subscription, const std::string& bus_name)
+int low_can_subscription_t::open_socket(low_can_subscription_t &subscription, const std::string& bus_name, socket_type type)
 {
-       int ret = 0;
+       int ret = -1;
        if(! subscription.socket_)
        {
-
-               #ifdef USE_FEATURE_J1939
-               if((subscription.signal_ != nullptr || !bus_name.empty()) && subscription.signal_->get_message()->is_j1939())
+               switch (type)
                {
-                       name_t name = J1939_NO_NAME;
-                       pgn_t pgn = J1939_NO_PGN;
-                       uint8_t addr = J1939_NO_ADDR;
-                       pgn = subscription.signal_->get_message()->get_id();
-                       if( subscription.signal_ != nullptr)
-                       {
-                               std::shared_ptr<utils::socketcan_j1939_t> socket = std::make_shared<utils::socketcan_j1939_t>();
-                               ret = socket->open(subscription.signal_->get_message()->get_bus_device_name(), name, pgn, addr);
-                               subscription.socket_ = socket;
-               }
-               else if ( !bus_name.empty())
+               case socket_type::BCM:
                {
-                               std::shared_ptr<utils::socketcan_j1939_t> socket = std::make_shared<utils::socketcan_j1939_t>();
-                               ret = socket->open(bus_name, name, pgn, addr);
-                               subscription.socket_ = socket;
-               }
-               subscription.index_ = (int)subscription.socket_->socket();
-               }
-               else
-               {
-               #endif
                        if( subscription.signal_ != nullptr)
                        {
                                        subscription.socket_ = std::make_shared<utils::socketcan_bcm_t>();
@@ -276,13 +259,56 @@ int low_can_subscription_t::open_socket(low_can_subscription_t &subscription, co
                                        ret = subscription.socket_->open(bus_name);
                        }
                        subscription.index_ = (int)subscription.socket_->socket();
-               #ifdef USE_FEATURE_J1939
+                       break;
+               }
+#ifdef USE_FEATURE_J1939
+               case socket_type::J1939_ADDR_CLAIM:
+               {
+                       pgn_t pgn = J1939_NO_PGN;
+                       if(!bus_name.empty())
+                       {
+                               std::shared_ptr<utils::socketcan_j1939_addressclaiming_t> socket = std::make_shared<utils::socketcan_j1939_addressclaiming_t>();
+                               ret = socket->open(bus_name, pgn);
+                               subscription.socket_ = socket;
+                       }
+                       subscription.index_ = (int)subscription.socket_->socket();
+                       break;
+               }
+               case socket_type::J1939:
+               {
+                       pgn_t pgn = J1939_NO_PGN;
+                       if(subscription.signal_ != nullptr)
+                       {
+                               pgn = subscription.signal_->get_message()->get_id();
+                               std::shared_ptr<utils::socketcan_j1939_data_t> socket = std::make_shared<utils::socketcan_j1939_data_t>();
+                               ret = socket->open(subscription.signal_->get_message()->get_bus_device_name(), pgn);
+                               subscription.socket_ = socket;
+                       }
+                       else if(!bus_name.empty())
+                       {
+                               std::shared_ptr<utils::socketcan_j1939_data_t> socket = std::make_shared<utils::socketcan_j1939_data_t>();
+                               ret = socket->open(bus_name, pgn);
+                               subscription.socket_ = socket;
+                       }
+                       subscription.index_ = (int)subscription.socket_->socket();
+                       break;
+               }
+#endif
+               default:
+               {
+                       AFB_ERROR("Socket format not supported");
+                       return INVALID_SOCKET;
+                       break;
+               }
                }
-               #endif
+       }
+       else{
+               ret = subscription.socket_->socket();
        }
        return ret;
 }
 
+
 /// @brief Builds a BCM message head but doesn't set can_frame.
 ///
 /// @returns a bcm_msg with the msg_head parts set and can_frame
@@ -328,7 +354,7 @@ int low_can_subscription_t::create_rx_filter_j1939(low_can_subscription_t &subsc
        subscription.signal_= sig;
 
        // Make sure that socket is opened.
-       if(open_socket(subscription) < 0)
+       if(open_socket(subscription,"",socket_type::J1939) < 0)
        {
                        return -1;
        }
@@ -421,7 +447,7 @@ int low_can_subscription_t::create_rx_filter(std::shared_ptr<diagnostic_message_
 int low_can_subscription_t::create_rx_filter_bcm(low_can_subscription_t &subscription, struct bcm_msg& bcm_msg)
 {
        // Make sure that socket is opened.
-       if(subscription.open_socket(subscription) < 0)
+       if(subscription.open_socket(subscription,"",socket_type::BCM) < 0)
                {return -1;}
 
        // If it's not an OBD2 CAN ID then just add a simple RX_SETUP job
@@ -463,8 +489,10 @@ int low_can_subscription_t::tx_send(low_can_subscription_t &subscription, messag
        canfd_frame cfd = cm->convert_to_canfd_frame();
        subscription.add_one_bcm_frame(cfd, bcm_msg);
 
-       if(subscription.open_socket(subscription, bus_name) < 0)
-               {return -1;}
+       if(subscription.open_socket(subscription, bus_name,socket_type::BCM) < 0)
+       {
+                       return -1;
+       }
 
        cm->set_bcm_msg(bcm_msg);
        subscription.socket_->write_message(*cm);
@@ -474,4 +502,27 @@ int low_can_subscription_t::tx_send(low_can_subscription_t &subscription, messag
        }
 
        return 0;
-}
\ No newline at end of file
+}
+
+#ifdef USE_FEATURE_J1939
+int low_can_subscription_t::j1939_send(low_can_subscription_t &subscription, message_t *message, const std::string& bus_name)
+{
+       //struct bcm_msg bcm_msg = subscription.make_bcm_head(TX_SEND, cfd.can_id);
+       //subscription.add_one_bcm_frame(cfd, bcm_msg);
+
+       if(subscription.open_socket(subscription, bus_name, socket_type::J1939) < 0)
+       {
+               return -1;
+       }
+
+       j1939_message_t *jm = static_cast<j1939_message_t*>(message);
+       jm->set_sockname(jm->get_pgn(),J1939_NO_NAME,J1939_NO_ADDR);
+       if(subscription.socket_->write_message(*jm) < 0)
+       {
+               AFB_ERROR("Error write j1939 message");
+               return -1;
+       }
+
+       return 0;
+}
+#endif
\ No newline at end of file
index 5ea1cf1..d0462ec 100644 (file)
@@ -89,11 +89,12 @@ public:
        void set_frequency(float freq);
        void set_min(float min);
        void set_max(float max);
+       void set_index(int index);
 
        static struct bcm_msg make_bcm_head(uint32_t opcode, uint32_t can_id = 0, uint32_t flags = 0, const struct timeval& timeout = {0,0}, const struct timeval& frequency_thinning = {0,0});
        static void add_one_bcm_frame(struct canfd_frame& cfd, struct bcm_msg& bcm_msg);
 
-       static int open_socket(low_can_subscription_t &subscription, const std::string& bus_name = "");
+       static int open_socket(low_can_subscription_t &subscription, const std::string& bus_name = "", socket_type type = socket_type::INVALID);
 
        int create_rx_filter(std::shared_ptr<signal_t> sig);
        int create_rx_filter(std::shared_ptr<diagnostic_message_t> sig);
@@ -102,4 +103,5 @@ public:
        static int create_rx_filter_bcm(low_can_subscription_t &subscription, bcm_msg& bcm_msg);
 
        static int tx_send(low_can_subscription_t &subscription, message_t *message, const std::string& bus_name);
+       static int j1939_send(low_can_subscription_t &subscription, message_t *message, const std::string& bus_name);
 };
index 7625cc1..0f8bc9b 100644 (file)
 #pragma once
 #include "./message.hpp"
 
-
+struct bcm_msg
+{
+       struct bcm_msg_head msg_head;
+       union {
+               struct canfd_frame fd_frames[MAX_BCM_CAN_FRAMES];
+               struct can_frame frames[MAX_BCM_CAN_FRAMES];
+       };
+};
 
 /// @class can_message_t
 ///
index 08fc130..0d2320d 100644 (file)
@@ -184,16 +184,63 @@ uint32_t j1939_message_t::get_id() const
        return get_pgn();
 }
 
-
-struct bcm_msg j1939_message_t::get_bcm_msg()
+/**
+ * @brief Return the sockname of the message
+ *
+ * @return struct sockaddr_can The sockname of the message
+ */
+struct sockaddr_can j1939_message_t::get_sockname()
 {
-    AFB_WARNING("Not implemented");
-    struct bcm_msg bcm_msg;
-    ::memset(&bcm_msg, 0, sizeof(struct bcm_msg));
-       return bcm_msg;
+    return sockname_;
 }
 
-void j1939_message_t::set_bcm_msg(struct bcm_msg bcm_msg)
+/**
+ * @brief Allows to set a sockname at a message to send it after
+ *
+ * @param sockname The sockname of the message
+ */
+void j1939_message_t::set_sockname(struct sockaddr_can sockname)
 {
-       AFB_WARNING("Not implemented");
+    sockname_ = sockname;
 }
+
+/**
+ * @brief Allows to generate a sockname for the message
+ *
+ * @param pgn The pgn for the sockname
+ * @param name The name for the sockname
+ * @param addr The address for the sockname
+ */
+void j1939_message_t::set_sockname(pgn_t pgn, name_t name, uint8_t addr)
+{
+    memset(&sockname_, 0, sizeof(sockname_));
+    sockname_.can_family = AF_CAN;
+    sockname_.can_ifindex = 0;
+
+    if(addr <= 0 || addr >= UINT8_MAX )
+    {
+        sockname_.can_addr.j1939.addr = J1939_NO_ADDR;
+    }
+    else
+    {
+        sockname_.can_addr.j1939.addr = addr;
+    }
+
+    if(name <= 0 || name >= UINT64_MAX )
+    {
+        sockname_.can_addr.j1939.name = J1939_NO_NAME;
+    }
+    else
+    {
+        sockname_.can_addr.j1939.name = name;
+    }
+
+    if(pgn <= 0 || pgn > J1939_PGN_MAX)
+    {
+        sockname_.can_addr.j1939.pgn = J1939_NO_PGN;
+    }
+    else
+    {
+        sockname_.can_addr.j1939.pgn = pgn;
+    }
+}
\ No newline at end of file
index aad62a1..929c823 100644 (file)
@@ -75,6 +75,7 @@ class j1939_message_t : public message_t
         bool is_set();
         std::string get_debug_message();
         uint32_t get_id() const;
-        struct bcm_msg get_bcm_msg();
-               void set_bcm_msg(struct bcm_msg bcm_msg);
+        struct sockaddr_can get_sockname();
+               void set_sockname(struct sockaddr_can sockname);
+        void set_sockname(pgn_t pgn, name_t name, uint8_t addr);
 };
index 5be1ff1..48f4d98 100644 (file)
 
 #define MAX_BCM_CAN_FRAMES 257
 
-struct bcm_msg
-{
-       struct bcm_msg_head msg_head;
-       union {
-               struct canfd_frame fd_frames[MAX_BCM_CAN_FRAMES];
-               struct can_frame frames[MAX_BCM_CAN_FRAMES];
-       };
-};
 
 /**
  * @enum message_format_t
diff --git a/low-can-binding/utils/socketcan-j1939.hpp b/low-can-binding/utils/socketcan-j1939.hpp
deleted file mode 100644 (file)
index 2b37017..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2018, 2019 "IoT.bzh"
- * Author "Arthur Guyader" <arthur.guyader@iot.bzh>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <linux/can/j1939.h>
-
-#include "./socketcan.hpp"
-#include "../can/message/j1939-message.hpp"
-
-namespace utils
-{
-
-       class socketcan_j1939_t : public socketcan_t
-       {
-       public:
-               using socketcan_t::socketcan_t;
-               virtual int open(std::string device_name);
-               int open(std::string device_name,  name_t name, pgn_t pgn, uint8_t addr);
-               virtual std::shared_ptr<message_t> read_message();
-               virtual void write_message(std::shared_ptr<message_t> obj);
-               virtual void write_message(std::vector<std::shared_ptr<message_t>>& vobj);
-               virtual int write_message(message_t& obj);
-
-       private:
-               int connect(const struct sockaddr* addr, socklen_t len);
-               int bind(const struct sockaddr* addr, socklen_t len);
-               void filter(name_t name, pgn_t pgn, uint8_t addr, uint64_t name_mask=0, uint32_t pgn_mask=0, uint8_t addr_mask=0);
-       };
-}
diff --git a/low-can-binding/utils/socketcan-j1939/socketcan-j1939-addressclaiming.cpp b/low-can-binding/utils/socketcan-j1939/socketcan-j1939-addressclaiming.cpp
new file mode 100644 (file)
index 0000000..df994f5
--- /dev/null
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2018, 2019 "IoT.bzh"
+ * Author "Arthur Guyader" <arthur.guyader@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+#include "./socketcan-j1939-addressclaiming.hpp"
+
+namespace utils
+{
+       /**
+        * @brief Construct a new socketcan j1939 addressclaiming t::socketcan j1939 addressclaiming t object
+        *
+        */
+       socketcan_j1939_addressclaiming_t::socketcan_j1939_addressclaiming_t():
+       socketcan_j1939_t(),
+       table_j1939_address_{{std::make_pair(0,false)}},
+       signal_stop_{false},
+       claiming_state_{claiming_state::INITIAL}
+       {}
+
+       /**
+        * @brief Allows to read the claiming message and react to them
+        *
+        * @return std::shared_ptr<message_t> Return an INVALID j1939 message because no need of return (only signature of the function)
+        */
+       std::shared_ptr<message_t> socketcan_j1939_addressclaiming_t::read_message()
+       {
+               AFB_DEBUG("[socketcan_j1939_addressclaiming_t] read_message");
+               std::shared_ptr<message_t> invalid_message = std::make_shared<j1939_message_t>();
+               std::shared_ptr<message_t> m;
+               j1939_message_t* jm;
+
+               m = socketcan_j1939_t::read_message();
+               jm = static_cast<j1939_message_t*>(m.get());
+
+               if(jm->get_pgn() == J1939_PGN_ADDRESS_CLAIMED)
+               {
+                       if(jm->get_addr() >= J1939_IDLE_ADDR)
+                       {
+                               AFB_DEBUG("Get invalid address claiming by name : %x",(unsigned int)jm->get_name());
+                               return invalid_message;
+                       }
+
+                       if(jm->get_name() == htole64(J1939_NAME_ECU))
+                       {
+                               AFB_DEBUG("Get own address claiming");
+                               return invalid_message;
+                       }
+
+                       AFB_DEBUG("Get address claiming from %x",(unsigned int)jm->get_name());
+
+                       if(jm->get_addr() != current_address_)
+                       {
+                               save_addr_name(jm->get_addr(),jm->get_name());
+                               return invalid_message;
+                       }
+
+                       if(claiming_state_ == claiming_state::CLAIMING)
+                       {
+                               if(jm->get_name() > htole64(J1939_NAME_ECU))
+                               {
+                                       AFB_WARNING("Error from %x to use j1939 protocol",(unsigned int)htole64(J1939_NAME_ECU));
+                                       return invalid_message;
+                               }
+
+                               save_addr_name(jm->get_addr(),jm->get_name());
+
+
+                               if(timer_handle_->evtSource)
+                               {
+                                               TimerEvtStop(timer_handle_);
+                                               timer_handle_ = nullptr;
+                               }
+
+                               if(claim_address(false, true) < 0)
+                               {
+                                       AFB_ERROR("Claim address failed");
+                                       change_state(claiming_state::INVALID);
+                                       return invalid_message;
+                               }
+                       }
+                       else if(claiming_state_ == claiming_state::OPERATIONAL)
+                       {
+                               AFB_DEBUG("Address colision");
+                               if(jm->get_name() > htole64(J1939_NAME_ECU))
+                               {
+                                       if(claim_address(false,false) < 0)
+                                       {
+                                               AFB_ERROR("Claim address failed");
+                                               change_state(claiming_state::INVALID);
+                                               return invalid_message;
+                                       }
+                                       return invalid_message;
+                               }
+
+                               save_addr_name(jm->get_addr(),jm->get_name());
+
+                               if(claim_address(false,true) < 0)
+                               {
+                                               AFB_ERROR("Claim address failed");
+                                               change_state(claiming_state::INVALID);
+                                               return invalid_message;
+                               }
+                       }
+               }
+               return invalid_message;
+       }
+
+       /**
+        * @brief Initialize the table j1939 with the valid address posible
+        *
+        */
+       void socketcan_j1939_addressclaiming_t::initialize_table_j1939_address()
+       {
+               int start_addr = 128;
+               int end_addr = 247;
+               if(start_addr < 0)
+               {
+                       AFB_ERROR("[socketcan-j1939-addressclaiming][initialize_table_j1939_address] Invalid start address");
+               }
+               else
+               {
+                       if(end_addr >= J1939_IDLE_ADDR)
+                       {
+                               AFB_ERROR("[socketcan-j1939-addressclaiming][initialize_table_j1939_address] Invalid end address");
+                       }
+                       else
+                       {
+                               for (int i = start_addr; i <= end_addr; i++) {
+                                       table_j1939_address_[i] = std::make_pair(0,true);
+                               }
+                       }
+               }
+       }
+
+       /**
+        * @brief Save at an address a name
+        *
+        * @param addr The adress where you want to save name
+        * @param name The name of the ECU that is in the address
+        * @return int 0 if save is ok
+        */
+       int socketcan_j1939_addressclaiming_t::save_addr_name(uint8_t addr,name_t name)
+       {
+               if(addr < J1939_IDLE_ADDR)
+               {
+                       if(table_j1939_address_[addr].first < name)
+                       {
+                               table_j1939_address_[addr].first = name;
+                               AFB_DEBUG("[socketcan-j1939-addressclaiming][save_addr_name] NAME : %x <--> ADDR : %d",(unsigned int)name,addr);
+                       }
+                       else if(table_j1939_address_[addr].first == name)
+                       {
+                               AFB_WARNING("Name %x has already adress %d",(unsigned int)name,addr);
+                       }
+               }
+               else
+               {
+                       AFB_ERROR("Invalid address to save");
+                       return -1;
+               }
+               return 0;
+       }
+
+       /**
+        * @brief Choose new address for the ECU check in the table the best place to claim
+        *
+        * @return uint8_t The new address choosen
+        */
+       uint8_t socketcan_j1939_addressclaiming_t::choose_new_address()
+       {
+               for (int i = 0; i < J1939_IDLE_ADDR; i++)
+               {
+                       if(table_j1939_address_[i].second && i!=current_address_)
+                       {
+                               if(     table_j1939_address_[i].first >= htole64(J1939_NAME_ECU) || table_j1939_address_[i].first == 0)
+                               {
+                                       return (uint8_t) i;
+                               }
+                       }
+               }
+               return J1939_IDLE_ADDR;
+       }
+
+       /**
+        * @brief The function that destoy the timer
+        *
+        * @param timer_context The timer context to destroy
+        * @return int
+        */
+       int socketcan_j1939_addressclaiming_t::free_timer_handle(void *timer_context)
+       {
+               socketcan_j1939_addressclaiming_t *addressclaiming_socket = (socketcan_j1939_addressclaiming_t*) timer_context;
+               addressclaiming_socket->timer_handle_ = nullptr;
+               return 0;
+       }
+
+       /**
+        * @brief The function is call when at the end of the timer, the socket has don't receive
+        *
+        * @param timerhandle The timerhandle of the timer
+        * @return int 1 it's all it's ok
+        */
+       int socketcan_j1939_addressclaiming_t::no_response_claiming(TimerHandleT *timerhandle)
+       {
+               socketcan_j1939_addressclaiming_t *addressclaiming_socket = (socketcan_j1939_addressclaiming_t*) timerhandle->context;
+               // If the cache is cleared :
+               addressclaiming_socket->change_state(claiming_state::OPERATIONAL);
+               addressclaiming_socket->save_addr_name(addressclaiming_socket->current_address_,htole64(J1939_NAME_ECU));
+               AFB_DEBUG("Get address %d for this ecu", addressclaiming_socket->current_address_);
+               /*Else :
+
+               uint8_t data[3]= { 0, 0, 0, };
+               std::vector<uint8_t> data_v(data,data+3);
+               int res = addressclaiming_socket->write_j1939_message(J1939_PGN_REQUEST,data_v,3);
+               if(res < 0)
+               {
+                       if(res == -99)
+                       {
+                               addressclaiming_socket->save_addr_name(addressclaiming_socket->current_address_,htole64(1));
+                               AFB_DEBUG("Address busy but no claming request from other ECU");
+                               addressclaiming_socket->claim_address(false,true);
+                       }
+                       else
+                       {
+                               AFB_ERROR("ERROR");
+                       }
+               }
+               else
+               {
+                       addressclaiming_socket->change_state(claiming_state::OPERATIONAL);
+                       addressclaiming_socket->save_addr_name(addressclaiming_socket->current_address_,htole64(J1939_NAME_ECU));
+                       AFB_DEBUG("Get address %d for this ecu", addressclaiming_socket->current_address_);
+               }*/
+
+               return 1;
+       }
+
+       /**
+        * @brief Launch timer when an address is claimed
+        *
+        */
+       void socketcan_j1939_addressclaiming_t::launch_timer()
+       {
+               timer_handle_ = (TimerHandleT*) malloc(sizeof(TimerHandleT));
+               timer_handle_->uid = "claiming_wait";
+               timer_handle_->delay = 250;
+               timer_handle_->count = 1;
+               timer_handle_->freeCB = free_timer_handle;
+               TimerEvtStart(afbBindingV3root, timer_handle_, no_response_claiming, (void *) this);
+       }
+
+
+       /**
+        * @brief Allows to claim a new address
+        *
+        * @param first_claim If true, the socket is open
+        * @param new_address If true, claim a new address, else only resend a claim with same address
+        * @return int -1 if fail
+        */
+       int socketcan_j1939_addressclaiming_t::claim_address(bool first_claim,bool new_address)
+       {
+               if(new_address)
+               {
+                       AFB_DEBUG("New address");
+                       current_address_ = choose_new_address();
+                       change_state(claiming_state::CLAIMING);
+                       launch_timer();
+               }
+
+               if(current_address_ == J1939_IDLE_ADDR)
+               {
+                       AFB_ERROR("No address left");
+                       return -1;
+               }
+
+               if(first_claim)
+               {
+                       int ret = socketcan_j1939_t::open(device_name_,htole64(J1939_NAME_ECU),J1939_NO_PGN,current_address_);
+
+                       if(ret < 0)
+                       {
+                               AFB_ERROR("Error open socket address claiming");
+                               return -1;
+                       }
+
+                       AFB_DEBUG("[socketcan-j1939-addressclaiming][claim_address] Success open socket address claiming");
+                       add_filter(J1939_NO_NAME,J1939_PGN_ADDRESS_CLAIMED,J1939_NO_ADDR,J1939_NO_NAME,J1939_PGN_PDU1_MAX,J1939_NO_ADDR);
+                       define_opt();
+               }
+               else
+               {
+                       tx_address_.can_addr.j1939.addr = current_address_;
+                       if(bind((struct sockaddr *)&tx_address_, sizeof(tx_address_)) < 0)
+                       {
+                               AFB_ERROR("rebind() fail");
+                               return -1;
+                       }
+               }
+
+               uint64_t name = htole64(J1939_NAME_ECU);
+               uint8_t dat[8] = {0};
+               memcpy(dat, &name, 8);
+               struct sockaddr_can sockname;
+               memset(&sockname, 0, sizeof(sockname));
+               sockname.can_family = AF_CAN;
+               sockname.can_addr.j1939.pgn = J1939_PGN_ADDRESS_CLAIMED;
+               sockname.can_addr.j1939.addr = J1939_NO_ADDR;
+               socklen_t socklen = sizeof(sockname);
+
+               ssize_t ret = sendto(socket_, &dat, sizeof(dat), 0, (const struct sockaddr *)&sockname, socklen);
+
+               if(ret < 0 )
+               {
+                       AFB_ERROR("Address claimed fail : %s", strerror(errno));
+                       return -1;
+               }
+
+               AFB_DEBUG("[socketcan-j1939-addressclaiming][claim_address] Send address claiming request");
+
+               return (int)ret;
+       }
+
+/*     int socketcan_j1939_addressclaiming_t::pgn_request()
+       {
+               static const uint8_t dat[3] = { 0, 0xee, 0, };
+               static struct sockaddr_can peername;
+               peername.can_family = AF_CAN;
+               peername.can_addr.j1939.pgn = J1939_PGN_REQUEST;
+               peername.can_addr.j1939.addr = J1939_NO_ADDR;
+               int ret = sendto(socket_, dat, sizeof(dat), 0, (const struct sockaddr *)&peername, sizeof(peername));
+
+               if(ret < 0)
+               {
+                       AFB_ERROR("Error pgn_request()");
+               }
+               return ret;
+       }
+       */
+
+       /**
+        * @brief Return the address associate to a name
+        *
+        * @param name The name you are looking for
+        * @return uint8_t The address if it is present, else J1939_IDLE_ADDR
+        */
+       uint8_t socketcan_j1939_addressclaiming_t::get_addr_table(name_t name)
+       {
+               for(int i = 0; i < J1939_IDLE_ADDR; i++)
+               {
+                       if(table_j1939_address_[i].first == name)
+                       {
+                               return (uint8_t) i;
+                       }
+               }
+               return J1939_IDLE_ADDR;
+       }
+
+       /**
+        * @brief Allows to open a J1939 socket address claiming
+        *
+        * @param device_name The name of the device on which to open the socket
+        * @param pgn NO_PGN
+        * @return int Return 0 if ok else -1
+        */
+       int socketcan_j1939_addressclaiming_t::open(std::string device_name, pgn_t pgn)
+       {
+               device_name_ = device_name;
+               initialize_table_j1939_address();
+               if(claim_address(true,true) < 0)
+               {
+                       AFB_ERROR("Claim address failed");
+                       return -1;
+               }
+               return 0;
+       }
+
+       /**
+        * @brief Allows to change the state of the socket address claiming
+        * When the state change a mutex is lock
+        *
+        * @param new_state The new state
+        */
+       void socketcan_j1939_addressclaiming_t::change_state(claiming_state new_state)
+       {
+               std::unique_lock<std::mutex> lock(socketcan_j1939_t::mutex_claiming_);
+               claiming_state_ = new_state;
+               socketcan_j1939_t::signal_address_claiming_.notify_one();
+       }
+
+       /**
+        * @brief Allows to get the states of the socket
+        *
+        * @return claiming_state The state of the socket
+        */
+       claiming_state socketcan_j1939_addressclaiming_t::get_state()
+       {
+               return claiming_state_;
+       }
+
+}
\ No newline at end of file
diff --git a/low-can-binding/utils/socketcan-j1939/socketcan-j1939-addressclaiming.hpp b/low-can-binding/utils/socketcan-j1939/socketcan-j1939-addressclaiming.hpp
new file mode 100644 (file)
index 0000000..92c44c8
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018, 2019 "IoT.bzh"
+ * Author "Arthur Guyader" <arthur.guyader@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <afb-timer.h>
+
+#include "./socketcan-j1939.hpp"
+
+namespace utils
+{
+       class socketcan_j1939_addressclaiming_t : public socketcan_j1939_t
+       {
+        public:
+            socketcan_j1939_addressclaiming_t();
+            virtual std::shared_ptr<message_t> read_message();
+            virtual int open(std::string device_name, pgn_t pgn);
+            virtual claiming_state get_state();
+            TimerHandleT *timer_handle_;
+            std::pair<uint64_t, bool> table_j1939_address_[J1939_IDLE_ADDR];
+
+        private:
+            int claim_address(bool first_claim,bool new_address);
+            int pgn_request();
+            void initialize_table_j1939_address();
+            int save_addr_name(uint8_t addr,name_t name);
+            uint8_t choose_new_address();
+            uint8_t get_addr_table(name_t name);
+            void change_state(claiming_state new_state);
+            void launch_timer();
+            static int no_response_claiming(TimerHandleT *timerhandle);
+            static int free_timer_handle(void *timer_context);
+
+            uint8_t current_address_;
+            bool signal_stop_;
+            claiming_state claiming_state_;
+            std::string device_name_;
+       };
+}
diff --git a/low-can-binding/utils/socketcan-j1939/socketcan-j1939-data.cpp b/low-can-binding/utils/socketcan-j1939/socketcan-j1939-data.cpp
new file mode 100644 (file)
index 0000000..c9d3e0a
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018, 2019 "IoT.bzh"
+ * Author "Arthur Guyader" <arthur.guyader@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include "socketcan-j1939-data.hpp"
+#include "socketcan-j1939-addressclaiming.hpp"
+#include "../../binding/application.hpp"
+
+namespace utils
+{
+    /**
+     * @brief Open a socket for receive or send data
+     *
+     * @param device_name The device name to open the socket
+     * @param pgn The pgn to receive only him
+     * @return int Return the number of the socket
+     */
+    int socketcan_j1939_data_t::open(std::string device_name, pgn_t pgn)
+    {
+        int ret = socketcan_j1939_t::open(device_name,htole64(J1939_NAME_ECU),pgn,J1939_NO_ADDR);
+        if(ret >= 0)
+        {
+            if(tx_address_.can_addr.j1939.pgn != J1939_NO_PGN)
+            {
+                add_filter(J1939_NO_NAME,tx_address_.can_addr.j1939.pgn,J1939_NO_ADDR,J1939_NO_NAME,J1939_NO_PGN,J1939_NO_ADDR);
+            }
+            define_opt();
+        }
+        return ret;
+    }
+
+    /**
+     * @brief Write a message but check if the address claiming is operation before
+     *
+     * @param obj A j1939 message
+     * @return int 0 if the write is ok
+     */
+    int socketcan_j1939_data_t::write_message(message_t& obj)
+    {
+        std::unique_lock<std::mutex> lock(mutex_claiming_);
+        application_t &application = application_t::instance();
+        socketcan_j1939_addressclaiming_t *socket_addr_claimed = static_cast<socketcan_j1939_addressclaiming_t*>(application.get_socket_address_claiming().get());
+        while(socket_addr_claimed->get_state() != claiming_state::OPERATIONAL)
+        {
+            socketcan_j1939_t::signal_address_claiming_.wait(lock);
+            if(socket_addr_claimed->get_state() == claiming_state::INVALID)
+            {
+                AFB_ERROR("Invalid state");
+                return -1;
+            }
+        }
+        return socketcan_j1939_t::write_message(obj);
+    }
+}
diff --git a/low-can-binding/utils/socketcan-j1939/socketcan-j1939-data.hpp b/low-can-binding/utils/socketcan-j1939/socketcan-j1939-data.hpp
new file mode 100644 (file)
index 0000000..0cb0b8f
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018, 2019 "IoT.bzh"
+ * Author "Arthur Guyader" <arthur.guyader@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "./socketcan-j1939.hpp"
+
+namespace utils
+{
+       class socketcan_j1939_data_t : public socketcan_j1939_t
+       {
+               public:
+                       using socketcan_j1939_t::socketcan_j1939_t;
+                       virtual int open(std::string device_name, pgn_t pgn);
+                       virtual int write_message(message_t& obj);
+       };
+}
\ No newline at end of file
diff --git a/low-can-binding/utils/socketcan-j1939/socketcan-j1939.cpp b/low-can-binding/utils/socketcan-j1939/socketcan-j1939.cpp
new file mode 100644 (file)
index 0000000..25d0f76
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2018, 2019 "IoT.bzh"
+ * Author "Arthur Guyader" <arthur.guyader@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <iostream>
+#include <algorithm>
+#include <vector>
+#include "./socketcan-j1939.hpp"
+#include "socketcan-j1939-addressclaiming.hpp"
+
+namespace utils
+{
+       std::mutex socketcan_j1939_t::mutex_claiming_;
+       std::condition_variable socketcan_j1939_t::signal_address_claiming_;
+
+       /**
+        * @brief Apply a filter to the socket
+        *
+        * @param name - The name you want to receive
+        * @param pgn - The pgn you want to receive
+        * @param addr - The addr you want to receive
+        * @param name_mask - The mask to apply to the name (No mask : J1939_NO_NAME)
+        * @param pgn_mask - The mask to apply to the pgn (No mask : J1939_NO_PGN)
+        * @param addr_mask - The mask to apply to the addr (No mask : J1939_NO_ADDR)
+        */
+       void socketcan_j1939_t::add_filter(name_t name, pgn_t pgn, uint8_t addr, name_t name_mask, pgn_t pgn_mask, uint8_t addr_mask)
+       {
+       //      AFB_DEBUG("[socketcan_j1939_t][add_filter] PGN : %" PRIu32 " ; NAME : %" PRIu64 " ; ADDR : %" PRIu8,pgn,(long unsigned int)name,addr);
+       //      AFB_DEBUG("PGN_MASK : %" PRIu32 " ; NAME_MASK : %" PRIu64 "; ADDR_MASK : %" PRIu8,pgn_mask,(long unsigned int)name_mask,addr_mask);
+               int filter_on = 0;
+               struct j1939_filter filter;
+               memset(&filter, 0, sizeof(filter));
+               if (name)
+               {
+                       filter.name = name;
+                       if(name_mask != J1939_NO_NAME)
+                       {
+                               filter.name_mask = name_mask;
+                       } else
+                       {
+                               filter.name_mask = ~0ULL;
+                       }
+                       ++filter_on;
+               }
+
+               if (addr < 0xff)
+               {
+                       filter.addr = addr;
+                       if(addr_mask != J1939_NO_ADDR)
+                       {
+                               filter.addr_mask = addr_mask;
+                       } else
+                       {
+                               filter.addr_mask = ~0;
+                       }
+                       ++filter_on;
+               }
+               if (pgn <= J1939_PGN_MAX)
+               {
+                       filter.pgn = pgn;
+                       if(pgn_mask != J1939_NO_PGN)
+                       {
+                               filter.pgn_mask = pgn_mask;
+                       } else
+                       {
+                               filter.pgn_mask = ~0;
+                       }
+                       ++filter_on;
+               }
+               if(filter_on)
+               {
+                       setopt(SOL_CAN_J1939, SO_J1939_FILTER, &filter, sizeof(filter));
+               }
+       }
+
+       /**
+        * @brief Define some parameters on the socket
+        *
+        * @param promisc - Allows to accept all packets that the socket receives even if they are not addressed directly to it
+        * @param recv_own_msgs - Allows you to receive your own packets
+        * @param broadcast - Allows to write message with address brodcast (255)
+        */
+    void socketcan_j1939_t::define_opt(bool promisc, bool recv_own_msgs, bool broadcast)
+       {
+               int promisc_i = 0;
+               int recv_own_msgs_i = 0;
+               int broadcast_i = 0;
+
+               if(promisc) promisc_i = 1;
+               if(recv_own_msgs) recv_own_msgs_i=1;
+               if(broadcast) broadcast_i = 1;
+
+               setopt(SOL_CAN_J1939, SO_J1939_PROMISC, &promisc_i, sizeof(promisc_i));
+               setopt(SOL_CAN_J1939, SO_J1939_RECV_OWN, &recv_own_msgs_i, sizeof(recv_own_msgs_i));
+               setopt(SOL_SOCKET, SO_BROADCAST, &broadcast_i, sizeof(broadcast_i));
+       }
+
+
+       /**
+        * @brief Define the tx address for the bind function
+        *
+        * @param device_name - The device can that you want to bind
+        * @param name - The name that you want to bind
+        * @param pgn - The pgn that you want to bind
+        * @param addr - The addr that you want to bindthat you want to bind
+        */
+       void socketcan_j1939_t::define_tx_address(std::string device_name, name_t name, pgn_t pgn, uint8_t addr)
+       {
+
+               ::strcpy(ifr_.ifr_name, device_name.c_str());
+               AFB_DEBUG("ifr_name is : %s", ifr_.ifr_name);
+
+
+               if(::ioctl(socket_, SIOCGIFINDEX, &ifr_) < 0)
+               {
+                       AFB_ERROR("ioctl failed. Error was : %s", strerror(errno));
+                       close();
+               }
+               else
+               {
+                       tx_address_.can_ifindex = ifr_.ifr_ifindex;
+               }
+
+               tx_address_.can_family = AF_CAN;
+
+
+               if(addr <= 0 || addr >= UINT8_MAX )
+               {
+                       tx_address_.can_addr.j1939.addr = J1939_NO_ADDR;
+               }
+               else
+               {
+                       tx_address_.can_addr.j1939.addr = addr;
+               }
+
+               if(name <= 0 || name >= UINT64_MAX )
+               {
+                       tx_address_.can_addr.j1939.name = J1939_NO_NAME;
+               }
+               else
+               {
+                       tx_address_.can_addr.j1939.name = name;
+               }
+
+               if(pgn <= 0 || pgn > J1939_PGN_MAX)
+               {
+                       tx_address_.can_addr.j1939.pgn = J1939_NO_PGN;
+               }
+               else
+               {
+                       tx_address_.can_addr.j1939.pgn = pgn;
+               }
+
+       }
+
+
+       /**
+        * @brief Open default socket
+        *
+        * @param device_name The device name to open the socket
+        * @return int The number of the socket
+        */
+       int socketcan_j1939_t::open(std::string device_name)
+       {
+               return open(device_name,0,0,0);
+       }
+
+       /**
+        * @brief Open a socket with name pgn and address
+        *
+        * @param device_name The device name to open the socket
+        * @param name - The name that you want to bind
+        * @param pgn - The pgn that you want to bind
+        * @param addr - The addr that you want to bindthat you want to bind
+        * @return int The number of the socket
+        */
+       int socketcan_j1939_t::open(std::string device_name, name_t name, pgn_t pgn, uint8_t addr)
+       {
+
+               socket_ = socketcan_t::open(PF_CAN, SOCK_DGRAM, CAN_J1939);
+
+               define_tx_address(device_name,name,pgn,addr);
+
+               if(bind((struct sockaddr *)&tx_address_, sizeof(tx_address_)) < 0)
+               {
+                       AFB_ERROR("Bind failed. %s", strerror(errno));
+                       close();
+               }
+
+               return socket_;
+       }
+
+       /**
+        * @brief Launch recvfrom on the socket with blocking flag
+        *
+        * @return std::shared_ptr<message_t> The j1939 message that is read
+        */
+       std::shared_ptr<message_t> socketcan_j1939_t::read_message()
+       {
+               return read_message(0);
+       }
+
+       /**
+        * @brief Launch recvfrom on the socket with the flag you want
+        *
+        * @param flag The flag param for the recvfrom
+        * @return std::shared_ptr<message_t> The j1939 message that is read
+        */
+       std::shared_ptr<message_t> socketcan_j1939_t::read_message(int flag)
+       {
+               socklen_t peernamelen;
+               std::shared_ptr<j1939_message_t> jm = std::make_shared<j1939_message_t>();
+               uint8_t data[128] = {0};
+
+               struct sockaddr_can peername;
+               peernamelen = sizeof(peername);
+               ssize_t nbytes = recvfrom(socket_, &data, sizeof(data), flag, (struct sockaddr *)&peername,  &peernamelen);
+
+               if(nbytes < 0)
+               {
+                       return nullptr;
+               }
+               //AFB_DEBUG("Data available: %i bytes read", (int)nbytes);
+
+               struct timeval tv;
+               ioctl(socket(), SIOCGSTAMP, &tv);
+               uint64_t timestamp = 1000000 * tv.tv_sec + tv.tv_usec;
+               jm = j1939_message_t::convert_from_addr(peername, data , nbytes, timestamp);
+               jm->set_sub_id((int)socket());
+               return jm;
+       }
+
+       /**
+        * @brief Write a j1939 message
+        *
+        * @param pgn The pgn that you want to emmit
+        * @param data The data that you want to emmit
+        * @param len_data The size of the data to emmit
+        * @return int 0 if the message is correctly send
+        */
+       int socketcan_j1939_t::write_j1939_message(pgn_t pgn, std::vector<uint8_t> &data, uint32_t len_data)
+       {
+               j1939_message_t msg = j1939_message_t(J1939_MAX_DLEN, len_data, message_format_t::J1939, data, 0, 0, pgn, 0);
+               msg.set_sockname(pgn,J1939_NO_NAME,J1939_NO_ADDR);
+               return write_message(msg);
+       }
+
+       /**
+        * @brief Write a j1939 message
+        *
+        * @param m A j1939 message
+        * @return int 0 if the message is correctly send
+        */
+       int socketcan_j1939_t::write_message(message_t& m)
+       {
+               j1939_message_t& jm = reinterpret_cast<j1939_message_t&>(m);
+               sockaddr_can sockname =  jm.get_sockname();
+               uint8_t data[jm.get_data_vector().size()];
+
+               for(int i=0; i<jm.get_data_vector().size(); i++)
+               {
+                       data[i] = jm.get_data_vector()[i];
+               }
+
+               if (sendto(socket_, &data, sizeof(data), 0, (const struct sockaddr *)&sockname, sizeof(sockname)) < 0)
+               {
+                       AFB_ERROR("Error sending : %i %s", errno, ::strerror(errno));
+                       return -errno;
+               }
+               return 0;
+       }
+}
\ No newline at end of file
diff --git a/low-can-binding/utils/socketcan-j1939/socketcan-j1939.hpp b/low-can-binding/utils/socketcan-j1939/socketcan-j1939.hpp
new file mode 100644 (file)
index 0000000..6c2ea69
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018, 2019 "IoT.bzh"
+ * Author "Arthur Guyader" <arthur.guyader@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <linux/can/j1939.h>
+#include <net/if.h>
+#include "../socketcan.hpp"
+#include "../../can/message/j1939-message.hpp"
+
+#ifndef J1939_NAME_ECU
+#define J1939_NAME_ECU 0x1234
+#endif
+
+namespace utils
+{
+
+       /**
+     * @enum state
+     * @brief The state of the address claiming
+     */
+    enum class claiming_state {
+        INITIAL, ///< INITIAL - INITIAL state
+        CLAIMING, ///< CLAIMING - CLAIMING state
+        OPERATIONAL, ///< OPERATIONAL - OPERATIONAL state
+        INVALID
+    };
+
+       class socketcan_j1939_addressclaiming_t;
+       class socketcan_j1939_t : public socketcan_t
+       {
+               public:
+                       using socketcan_t::socketcan_t;
+                       virtual int open(std::string device_name);
+                       virtual int open(std::string device_name, name_t name, pgn_t pgn, uint8_t addr);
+                       virtual std::shared_ptr<message_t> read_message();
+                       virtual std::shared_ptr<message_t> read_message(int flag);
+                       virtual int write_message(message_t& obj);
+                       virtual int write_j1939_message(pgn_t pgn, std::vector<uint8_t> &data, uint32_t len_data);
+
+               protected:
+                       struct ifreq ifr_;
+                       static std::mutex mutex_claiming_;
+                       static std::condition_variable signal_address_claiming_;
+                       void define_tx_address(std::string device_name, name_t name, pgn_t pgn, uint8_t addr);
+                       void add_filter(name_t name, pgn_t pgn, uint8_t addr, name_t name_mask, pgn_t pgn_mask, uint8_t addr_mask);
+                       void define_opt(bool promisc = true, bool recv_own_msgs = true, bool broadcast = true);
+
+       };
+}
+