Skip to content
This repository was archived by the owner on Dec 1, 2020. It is now read-only.

Commit c1a3c70

Browse files
Merge pull request #187 from project-march/fix/PM-215-fix-destructor-ethercat
destructor ethercat
2 parents a02c81d + 6b5128a commit c1a3c70

File tree

5 files changed

+114
-78
lines changed

5 files changed

+114
-78
lines changed

march_hardware/include/march_hardware/EtherCAT/EthercatMaster.h

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,41 @@
99

1010
namespace march4cpp
1111
{
12+
/**
13+
* Base class of the ethercat master supported with the SOEM library
14+
* @param ifname Network interface name, check ifconfig.
15+
* @param IOmap Holds the mapping of the SOEM message.
16+
* @param expectedWKC The expected working counter of the ethercat train.
17+
* @param ecatCycleTimems The ethercat cycle time.
18+
* @param maxSlaveIndex The maximum amount of slaves connected to the train.
19+
*/
1220
class EthercatMaster
1321
{
14-
std::string ifname; // Network interface name, check ifconfig
15-
char IOmap[4096]; // Holds the mapping of the SOEM message
16-
int expectedWKC; // Expected working counter
17-
std::thread EcatThread; // Handler for parallel thread
22+
std::string ifname;
23+
char IOmap[4096];
24+
int expectedWKC;
1825

26+
std::thread EcatThread;
1927
std::vector<Joint>* jointListPtr;
28+
2029
int maxSlaveIndex;
2130
int ecatCycleTimems;
2231

2332
public:
24-
bool isOperational = false; // Is SOEM in operational state
33+
bool isOperational = false;
2534

2635
explicit EthercatMaster(std::vector<Joint>* jointListPtr, std::string ifname, int maxSlaveIndex, int ecatCycleTime);
36+
~EthercatMaster();
2737

2838
void start();
29-
void stop();
39+
void ethercatMasterInitiation();
40+
void ethercatSlaveInitiation();
3041

31-
// Parallel thread
3242
void ethercatLoop();
43+
void SendReceivePDO();
44+
static void monitorSlaveConnection();
3345

34-
void sendProcessData();
35-
36-
int receiveProcessData();
37-
38-
void monitorSlaveConnection();
46+
void stop();
3947
};
4048

4149
} // namespace march4cpp
Lines changed: 65 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
// Copyright 2019 Project March.
22

3-
//
4-
// EtherCAT master class source. Interfaces with SOEM
5-
//
63
#include <string>
74
#include <vector>
85

@@ -24,7 +21,6 @@
2421

2522
namespace march4cpp
2623
{
27-
// Constructor
2824
EthercatMaster::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+
*/
3741
void 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+
*/
136138
void 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+
*/
192199
void 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

march_hardware_interface/include/march_hardware_interface/march_hardware_interface.h

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,18 @@ class MarchHardwareInterface : public march_hardware_interface::MarchHardware
3636
{
3737
public:
3838
MarchHardwareInterface(ros::NodeHandle& nh, AllowedRobot robotName);
39-
~MarchHardwareInterface();
4039

4140
/**
4241
* @brief Initialize the HardwareInterface by registering position interfaces
4342
* for each joint.
4443
*/
4544
void init();
46-
void update(const ros::TimerEvent& e);
45+
void update(const ros::Duration& elapsed_time);
4746

4847
/**
4948
* @brief Read actual postion from the hardware.
5049
*/
51-
void read(ros::Duration elapsed_time = ros::Duration(0.01));
50+
void read(const ros::Duration& elapsed_time = ros::Duration(0.01));
5251

5352
/**
5453
* @brief Perform all safety checks that might crash the exoskeleton.
@@ -59,18 +58,16 @@ class MarchHardwareInterface : public march_hardware_interface::MarchHardware
5958
* @brief Write position commands to the hardware.
6059
* @param elapsed_time Duration since last write action
6160
*/
62-
void write(ros::Duration elapsed_time);
61+
void write(const ros::Duration& elapsed_time);
6362

6463
protected:
6564
::march4cpp::MarchRobot marchRobot;
6665
ros::NodeHandle nh_;
67-
ros::Timer non_realtime_loop_;
6866
ros::Duration control_period_;
6967
ros::Duration elapsed_time_;
7068
PositionJointInterface positionJointInterface;
7169
PositionJointSoftLimitsInterface positionJointSoftLimitsInterface;
7270
EffortJointSoftLimitsInterface effortJointSoftLimitsInterface;
73-
double loop_hz_;
7471
bool hasPowerDistributionBoard = false;
7572
boost::shared_ptr<controller_manager::ControllerManager> controller_manager_;
7673
typedef boost::shared_ptr<realtime_tools::RealtimePublisher<march_shared_resources::ImcErrorState> > RtPublisherPtr;

march_hardware_interface/src/march_hardware_interface.cpp

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,6 @@ namespace march_hardware_interface
2929
MarchHardwareInterface::MarchHardwareInterface(ros::NodeHandle& nh, AllowedRobot robotName)
3030
: nh_(nh), marchRobot(HardwareBuilder(robotName).createMarchRobot())
3131
{
32-
init();
33-
controller_manager_.reset(new controller_manager::ControllerManager(this, nh_));
34-
nh_.param("/march/hardware_interface/loop_hz", loop_hz_, 100.0);
35-
ros::Duration update_freq = ros::Duration(1.0 / loop_hz_);
36-
non_realtime_loop_ = nh_.createTimer(update_freq, &MarchHardwareInterface::update, this);
37-
}
38-
39-
MarchHardwareInterface::~MarchHardwareInterface()
40-
{
41-
this->marchRobot.stopEtherCAT();
4232
}
4333

4434
void MarchHardwareInterface::init()
@@ -218,15 +208,15 @@ void MarchHardwareInterface::init()
218208
}
219209
}
220210
ROS_INFO("Successfully actuated all joints");
211+
controller_manager_.reset(new controller_manager::ControllerManager(this, nh_));
221212
}
222213

223-
void MarchHardwareInterface::update(const ros::TimerEvent& e)
214+
void MarchHardwareInterface::update(const ros::Duration& elapsed_time)
224215
{
225-
elapsed_time_ = ros::Duration(e.current_real - e.last_real);
226-
read(elapsed_time_);
216+
read(elapsed_time);
227217
validate();
228-
controller_manager_->update(ros::Time::now(), elapsed_time_);
229-
write(elapsed_time_);
218+
controller_manager_->update(ros::Time::now(), elapsed_time);
219+
write(elapsed_time);
230220
}
231221

232222
void MarchHardwareInterface::validate()
@@ -238,7 +228,7 @@ void MarchHardwareInterface::validate()
238228
}
239229
}
240230

241-
void MarchHardwareInterface::read(ros::Duration elapsed_time)
231+
void MarchHardwareInterface::read(const ros::Duration& elapsed_time)
242232
{
243233
for (int i = 0; i < num_joints_; i++)
244234
{
@@ -278,7 +268,7 @@ void MarchHardwareInterface::read(ros::Duration elapsed_time)
278268
}
279269
}
280270

281-
void MarchHardwareInterface::write(ros::Duration elapsed_time)
271+
void MarchHardwareInterface::write(const ros::Duration& elapsed_time)
282272
{
283273
joint_effort_command_copy.clear();
284274
joint_effort_command_copy.resize(joint_effort_command_.size());

0 commit comments

Comments
 (0)