11// Copyright 2019 Project March.
22
3- //
4- // EtherCAT master class source. Interfaces with SOEM
5- //
63#include < string>
74#include < vector>
85
2421
2522namespace march4cpp
2623{
27- // Constructor
2824EthercatMaster::EthercatMaster (std::vector<Joint>* jointListPtr, std::string ifname, int maxSlaveIndex,
2925 int ecatCycleTime)
3026 : jointListPtr(jointListPtr)
@@ -34,20 +30,33 @@ EthercatMaster::EthercatMaster(std::vector<Joint>* jointListPtr, std::string ifn
3430 this ->ecatCycleTimems = ecatCycleTime;
3531}
3632
33+ EthercatMaster::~EthercatMaster ()
34+ {
35+ this ->stop ();
36+ }
37+
38+ /* *
39+ * Initiate the ethercat train and start the loop
40+ */
3741void EthercatMaster::start ()
3842{
39- // TODO(Isha, Martijn) this method is really long, split into more methods
40- ROS_INFO (" Trying to start EtherCAT" );
43+ EthercatMaster::ethercatMasterInitiation ();
44+ EthercatMaster::ethercatSlaveInitiation ();
45+ }
4146
42- // Initialise SOEM, bind socket to ifname
47+ /* *
48+ * Open the ethernet port with the given ifname and check amount of slaves
49+ */
50+ void EthercatMaster::ethercatMasterInitiation ()
51+ {
52+ ROS_INFO (" Trying to start EtherCAT" );
4353 if (!ec_init (&ifname[0 ]))
4454 {
4555 ROS_FATAL (" No socket connection on %s. Confirm that you have selected the right ifname" , ifname.c_str ());
4656 throw std::runtime_error (" No socket connection on %s. Confirm that you have selected the right ifname" );
4757 }
4858 ROS_INFO (" ec_init on %s succeeded" , ifname.c_str ());
4959
50- // Find and auto-config slaves
5160 if (ec_config_init (FALSE ) <= 0 )
5261 {
5362 ROS_FATAL (" No slaves found, shutting down. Confirm that you have selected the right ifname." );
@@ -61,37 +70,38 @@ void EthercatMaster::start()
6170 ROS_FATAL (" Slave configured with index %d while soem only found %d slave(s)" , this ->maxSlaveIndex , ec_slavecount);
6271 throw std::runtime_error (" More slaves configured than soem could detect." );
6372 }
64- // TODO(Martijn) Check on type of slaves
73+ }
6574
66- // Request and wait for slaves to be in preOP state
75+ /* *
76+ * Set the found slaves to pre-operational state, configure the slaves and move to safe-operational state. If everything
77+ * went good move to operational state.
78+ */
79+ void EthercatMaster::ethercatSlaveInitiation ()
80+ {
81+ ROS_INFO (" Request pre-operational state for all slaves" );
6782 ec_statecheck (0 , EC_STATE_PRE_OP, EC_TIMEOUTSTATE * 4 );
6883
69- for (int i = 0 ; i < jointListPtr-> size (); i++ )
84+ for (auto & joint : * jointListPtr)
7085 {
71- jointListPtr-> at (i) .initialize (ecatCycleTimems);
86+ joint .initialize (ecatCycleTimems);
7287 }
7388
74- // Configure the EtherCAT message structure depending on the PDO mapping of all the slaves
7589 ec_config_map (&IOmap);
76-
7790 ec_configdc ();
7891
79- // Wait for all slaves to reach SAFE_OP state
92+ ROS_INFO ( " Request safe-operational state for all slaves" );
8093 ec_statecheck (0 , EC_STATE_SAFE_OP, EC_TIMEOUTSTATE * 4 );
8194
82- ROS_INFO (" Request operational state for all slaves" );
8395 expectedWKC = (ec_group[0 ].outputsWKC * 2 ) + ec_group[0 ].inputsWKC ;
8496 ec_slave[0 ].state = EC_STATE_OPERATIONAL;
8597
86- // send one valid process data to make outputs in slaves happy
8798 ec_send_processdata ();
8899 ec_receive_processdata (EC_TIMEOUTRET);
89100
90- // request OP state for all slaves
101+ ROS_INFO ( " Request operational state for all slaves" );
91102 ec_writestate (0 );
92103 int chk = 40 ;
93104
94- /* wait for all slaves to reach OP state */
95105 do
96106 {
97107 ec_send_processdata ();
@@ -101,14 +111,12 @@ void EthercatMaster::start()
101111
102112 if (ec_slave[0 ].state == EC_STATE_OPERATIONAL)
103113 {
104- // All slaves in operational state
105114 ROS_INFO (" Operational state reached for all slaves" );
106115 isOperational = true ;
107116 EcatThread = std::thread (&EthercatMaster::ethercatLoop, this );
108117 }
109118 else
110119 {
111- // Not all slaves in operational state
112120 ROS_FATAL (" Not all slaves reached operational state. Non-operational slave(s) listed below." );
113121 ec_readstate ();
114122 for (int i = 1 ; i <= ec_slavecount; i++)
@@ -123,29 +131,26 @@ void EthercatMaster::start()
123131 }
124132}
125133
126- void EthercatMaster::stop ()
127- {
128- ROS_INFO (" Stopping EtherCAT" );
129- isOperational = false ;
130- EcatThread.join ();
131- ec_slave[0 ].state = EC_STATE_INIT;
132- ec_writestate (0 );
133- ec_close ();
134- }
135-
134+ /* *
135+ * The ethercat train PDO loop. If the working counter is lower then expected 5% of the time the program displays an
136+ * error
137+ */
136138void EthercatMaster::ethercatLoop ()
137139{
138140 uint32_t totalLoops = 0 ;
139141 uint32_t rateNotAchievedCount = 0 ;
140142 int rate = 1000 / ecatCycleTimems;
143+
141144 while (isOperational)
142145 {
143146 auto start = boost::chrono::high_resolution_clock::now ();
144- sendProcessData ();
145- receiveProcessData ();
147+
148+ SendReceivePDO ();
146149 monitorSlaveConnection ();
150+
147151 auto stop = boost::chrono::high_resolution_clock::now ();
148152 auto duration = boost::chrono::duration_cast<boost::chrono::microseconds>(stop - start);
153+
149154 if (duration.count () > ecatCycleTimems * 1000 )
150155 {
151156 rateNotAchievedCount++;
@@ -155,10 +160,11 @@ void EthercatMaster::ethercatLoop()
155160 usleep (ecatCycleTimems * 1000 - duration.count ());
156161 }
157162 totalLoops++;
158- if (totalLoops >= 10 * rate) // Every 10 seconds
163+
164+ if (totalLoops >= (10 * rate))
159165 {
160166 float rateNotAchievedPercentage = 100 * (static_cast <float >(rateNotAchievedCount) / totalLoops);
161- if (rateNotAchievedPercentage > 5 ) // If percentage greater than 5 percent, do ROS_WARN instead of ROS_DEBUG
167+ if (rateNotAchievedPercentage > 5 )
162168 {
163169 ROS_WARN (" EtherCAT rate of %d milliseconds per cycle was not achieved for %f percent of all cycles" ,
164170 ecatCycleTimems, rateNotAchievedPercentage);
@@ -174,33 +180,50 @@ void EthercatMaster::ethercatLoop()
174180 }
175181}
176182
177- void EthercatMaster::sendProcessData ()
183+ /* *
184+ * Send the PDO and receive the working counter and check if this is lower then expected.
185+ */
186+ void EthercatMaster::SendReceivePDO ()
178187{
179188 ec_send_processdata ();
180- }
181-
182- int EthercatMaster::receiveProcessData ()
183- {
184189 int wkc = ec_receive_processdata (EC_TIMEOUTRET);
185190 if (wkc < this ->expectedWKC )
186191 {
187192 ROS_WARN_THROTTLE (1 , " Working counter lower than expected. EtherCAT connection may not be optimal" );
188193 }
189- return wkc;
190194}
191195
196+ /* *
197+ * Check if all the slaves are connected and in operational state.
198+ */
192199void EthercatMaster::monitorSlaveConnection ()
193200{
194201 for (int slave = 1 ; slave <= ec_slavecount; slave++)
195202 {
196203 ec_statecheck (slave, EC_STATE_OPERATIONAL, EC_TIMEOUTRET);
197204 if (!ec_slave[slave].state )
198205 {
199- // TODO(@Tim, @Isha, @Martijn) throw error when it happens multiple times in a short period of time.
200206 ROS_WARN_THROTTLE (1 , " EtherCAT train lost connection from slave %d onwards" , slave);
201207 return ;
202208 }
203209 }
204210}
205211
212+ /* *
213+ * Stop the ethercat loop and terminate the thread
214+ */
215+ void EthercatMaster::stop ()
216+ {
217+ if (this ->isOperational )
218+ {
219+ ROS_INFO (" Stopping EtherCAT" );
220+ isOperational = false ;
221+ EcatThread.join ();
222+
223+ ec_slave[0 ].state = EC_STATE_INIT;
224+ ec_writestate (0 );
225+ ec_close ();
226+ }
227+ }
228+
206229} // namespace march4cpp
0 commit comments