33#include " march_hardware/error/hardware_exception.h"
44
55#include < chrono>
6+ #include < exception>
67#include < sstream>
78#include < string>
89#include < vector>
1314
1415namespace march
1516{
16- EthercatMaster::EthercatMaster (std::string ifname, int max_slave_index, int cycle_time)
17- : is_operational_(false ), ifname_(std::move(ifname)), max_slave_index_(max_slave_index), cycle_time_ms_(cycle_time)
17+ EthercatMaster::EthercatMaster (std::string ifname, int max_slave_index, int cycle_time, int slave_timeout)
18+ : is_operational_(false )
19+ , ifname_(std::move(ifname))
20+ , max_slave_index_(max_slave_index)
21+ , cycle_time_ms_(cycle_time)
22+ , slave_watchdog_timeout_(slave_timeout)
1823{
1924}
2025
@@ -36,12 +41,18 @@ int EthercatMaster::getCycleTime() const
3641void EthercatMaster::waitForPdo ()
3742{
3843 std::unique_lock<std::mutex> lock (this ->wait_on_pdo_condition_mutex_ );
39- this ->wait_on_pdo_condition_var_ .wait (lock, [&] { return this ->pdo_received_ ; });
44+ this ->wait_on_pdo_condition_var_ .wait (lock, [&] { return this ->pdo_received_ || ! this -> is_operational_ ; });
4045 this ->pdo_received_ = false ;
4146}
4247
48+ std::exception_ptr EthercatMaster::getLastException () const noexcept
49+ {
50+ return this ->last_exception_ ;
51+ }
52+
4353bool EthercatMaster::start (std::vector<Joint>& joints)
4454{
55+ this ->last_exception_ = nullptr ;
4556 this ->ethercatMasterInitiation ();
4657 return this ->ethercatSlaveInitiation (joints);
4758}
@@ -152,15 +163,15 @@ void EthercatMaster::ethercatLoop()
152163 {
153164 const auto begin_time = std::chrono::high_resolution_clock::now ();
154165
155- this ->sendReceivePdo ();
156- monitorSlaveConnection ();
166+ const bool pdo_received = this ->sendReceivePdo ();
167+ this -> monitorSlaveConnection ();
157168
158169 const auto end_time = std::chrono::high_resolution_clock::now ();
159170 const auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - begin_time);
160171
161172 {
162173 std::lock_guard<std::mutex> lock (this ->wait_on_pdo_condition_mutex_ );
163- this ->pdo_received_ = true ;
174+ this ->pdo_received_ = pdo_received ;
164175 }
165176 this ->wait_on_pdo_condition_var_ .notify_one ();
166177
@@ -185,30 +196,132 @@ void EthercatMaster::ethercatLoop()
185196 total_loops = 0 ;
186197 not_achieved_count = 0 ;
187198 }
199+
200+ const auto delta_t = std::chrono::high_resolution_clock::now () - this ->valid_slaves_timestamp_ms_ ;
201+ const auto slave_lost_duration = std::chrono::duration_cast<std::chrono::milliseconds>(delta_t );
202+ const std::chrono::milliseconds slave_watchdog_timeout (this ->slave_watchdog_timeout_ );
203+
204+ if (slave_lost_duration > slave_watchdog_timeout)
205+ {
206+ this ->last_exception_ = std::make_exception_ptr (error::HardwareException (
207+ error::ErrorType::SLAVE_LOST_TIMOUT, " Slave connection lost for %i ms from slave %i and onwards." ,
208+ this ->slave_watchdog_timeout_ , this ->latest_lost_slave_ ));
209+ this ->is_operational_ = false ;
210+ this ->wait_on_pdo_condition_var_ .notify_one ();
211+
212+ this ->closeEthercat ();
213+ }
188214 }
189215}
190216
191- void EthercatMaster::sendReceivePdo ()
217+ bool EthercatMaster::sendReceivePdo ()
192218{
193- ec_send_processdata ();
194- int wkc = ec_receive_processdata (EC_TIMEOUTRET);
195- if (wkc < this ->expected_working_counter_ )
219+ if (this ->latest_lost_slave_ == -1 )
196220 {
197- ROS_WARN_THROTTLE (1 , " Working counter: %d is lower than expected: %d" , wkc, this ->expected_working_counter_ );
221+ ec_send_processdata ();
222+ const int wkc = ec_receive_processdata (EC_TIMEOUTRET);
223+ if (wkc < this ->expected_working_counter_ )
224+ {
225+ ROS_WARN_THROTTLE (1 , " Working counter: %d is lower than expected: %d" , wkc, this ->expected_working_counter_ );
226+ return false ;
227+ }
228+ return true ;
198229 }
230+ return false ;
199231}
200232
201233void EthercatMaster::monitorSlaveConnection ()
202234{
235+ ec_readstate ();
203236 for (int slave = 1 ; slave <= ec_slavecount; slave++)
204237 {
205- ec_statecheck (slave, EC_STATE_OPERATIONAL, EC_TIMEOUTRET);
206- if (!ec_slave[slave].state )
238+ if (ec_slave[slave].state != EC_STATE_OPERATIONAL)
207239 {
208240 ROS_WARN_THROTTLE (1 , " EtherCAT train lost connection from slave %d onwards" , slave);
209- return ;
241+
242+ if (!this ->attemptSlaveRecover (slave))
243+ {
244+ this ->latest_lost_slave_ = slave;
245+ return ;
246+ }
247+ }
248+ }
249+
250+ if (this ->latest_lost_slave_ > -1 )
251+ {
252+ ROS_INFO (" All slaves returned to operational state." );
253+ }
254+
255+ this ->latest_lost_slave_ = -1 ;
256+ this ->valid_slaves_timestamp_ms_ = std::chrono::high_resolution_clock::now ();
257+ }
258+
259+ bool EthercatMaster::attemptSlaveRecover (int slave)
260+ {
261+ if (ec_slave[slave].state != EC_STATE_OPERATIONAL)
262+ {
263+ if (ec_slave[slave].state == EC_STATE_PRE_OP)
264+ {
265+ ec_slave[slave].state = EC_STATE_SAFE_OP;
266+ ec_writestate (slave);
267+ }
268+
269+ if (ec_slave[slave].state == EC_STATE_SAFE_OP)
270+ {
271+ ec_slave[slave].state = EC_STATE_OPERATIONAL;
272+ ec_writestate (slave);
273+ }
274+
275+ if (ec_slave[slave].state > EC_STATE_NONE)
276+ {
277+ if (ec_reconfig_slave (slave, 500 ))
278+ {
279+ ec_slave[slave].islost = FALSE ;
280+ }
281+ }
282+
283+ if (!ec_slave[slave].islost )
284+ {
285+ ec_statecheck (slave, EC_STATE_OPERATIONAL, EC_TIMEOUTRET);
286+ if (ec_slave[slave].state == EC_STATE_NONE)
287+ {
288+ ec_slave[slave].islost = TRUE ;
289+ ROS_ERROR (" Ethercat lost connection to slave %d" , slave);
290+ }
291+ }
292+ }
293+
294+ if (ec_slave[slave].islost )
295+ {
296+ if (ec_slave[slave].state == EC_STATE_NONE)
297+ {
298+ if (ec_recover_slave (slave, 500 ))
299+ {
300+ ec_slave[slave].islost = FALSE ;
301+ }
302+ }
303+ else
304+ {
305+ ec_slave[slave].islost = FALSE ;
210306 }
211307 }
308+
309+ if (ec_slave[slave].state == EC_STATE_OPERATIONAL)
310+ {
311+ ROS_INFO (" Slave %i resumed operational state" , slave);
312+ return true ;
313+ }
314+ else
315+ {
316+ return false ;
317+ }
318+ }
319+
320+ void EthercatMaster::closeEthercat ()
321+ {
322+ ec_slave[0 ].state = EC_STATE_INIT;
323+ ec_writestate (0 );
324+ ec_close ();
212325}
213326
214327void EthercatMaster::stop ()
@@ -219,9 +332,7 @@ void EthercatMaster::stop()
219332 this ->is_operational_ = false ;
220333 this ->ethercat_thread_ .join ();
221334
222- ec_slave[0 ].state = EC_STATE_INIT;
223- ec_writestate (0 );
224- ec_close ();
335+ this ->closeEthercat ();
225336 }
226337}
227338
0 commit comments