23 static constexpr int TIMEOUT_UPDATE_PDO_US = 600;
30 requestHandlerThread = std::thread(&BusIO::requestHandlerLoop,
this);
34 BusIO::generalSDOWrite(std::uint16_t slaveIndex,
36 std::uint8_t subindex,
42 {slaveIndex,
index, subindex},
44 static_cast<unsigned char*
>(
const_cast<void*
>(buf)),
49 BusIO::generalSDORead(std::uint16_t slaveIndex,
51 std::uint8_t subindex,
58 static_cast<unsigned char*
>(buf),
63 BusIO::isSDOAccessAvailable()
const
68 "SDO-access is not yet available. Probably the bus was not started yet");
75 BusIO::sendAndReceiveEtherCATFrame(
const EtherCATFrame* frame,
78 const std::vector<size_t>& slaveAddressOffsets)
80 int bufferIndex = ecx_getindex(ecx_context.port);
82 reinterpret_cast<uint8_t*
>(&(ecx_context.port->txbuf[bufferIndex]));
85 EtherCATFrame* framePlacement =
86 reinterpret_cast<EtherCATFrame*
>(buffer + ETH_HEADERSIZE);
87 std::memcpy(framePlacement, frame, frameLength);
89 framePlacement->pduArea[1] =
static_cast<std::uint8_t
>(bufferIndex);
90 for (
size_t offset : slaveAddressOffsets)
92 *
reinterpret_cast<uint16_t*
>(framePlacement->pduArea + offset)
96 ecx_context.port->txbuflength[bufferIndex] =
97 static_cast<int>(ETH_HEADERSIZE + frameLength);
100 int const workingCounter =
101 ecx_srconfirm(ecx_context.port, bufferIndex,
timeouts.registerRead_us);
103 if (workingCounter == EC_NOFRAME)
106 "Sending and receiving EtherCAT frame timed out after %i µs (EC_NOFRAME). "
107 "This timeout will most likely result in consecutive errors.",
111 return {workingCounter, bufferIndex};
119 pdoUpdateRequested.store(
true, std::memory_order_relaxed);
122 ec_send_processdata();
128 pdoUpdateRequested.store(
false, std::memory_order_relaxed);
134 BusIO::requestHandlerLoop()
139 while (requestHandlerThreadRunning.load() ==
true)
141 if (!pdoUpdateRequested.load(std::memory_order_relaxed))
145 using namespace std::literals;
146 std::this_thread::sleep_for(10us);
151 BusIO::handleNextRequest()
157 if (
auto r = std::dynamic_pointer_cast<SDOUpdateRequest>(request))
159 handleSDOUpdateRequest(r);
162 else if (
auto r = std::dynamic_pointer_cast<ChangeStateRequest>(request))
164 handleChangeStateRequest(r);
167 else if (
auto r = std::dynamic_pointer_cast<ReadStatesRequest>(request))
169 handleReadStatesRequest(r);
172 else if (
auto r = std::dynamic_pointer_cast<RegisterResetRequest>(request))
174 handleRegisterResetRequest(r);
177 else if (
auto r = std::dynamic_pointer_cast<RegisterReadRequest>(request))
179 handleRegisterReadRequest(r);
183 requestQueues.postReply(std::move(request));
191 BusIO::handleChangeStateRequest(
const std::shared_ptr<ChangeStateRequest>& request)
193 ec_slave[request->slaveIndex].state = request->state;
194 int ret = ec_writestate(request->slaveIndex);
195 if (ret == EC_NOFRAME)
198 "Writing EtherCAT state %s to slave at index %u (0 = all slaves) timed out "
199 "(EC_NOFRAME). The timeout for this operation was %i µs, is hardcoded by "
200 "SOEM, and thus cannot be configured.",
202 request->state.c_str(),
205 request->setFailed();
209 if (request->validate)
212 EtherCATState actualState =
213 ec_statecheck(request->slaveIndex, request->state,
timeouts.stateCheck_us);
216 if (actualState != request->state)
221 bool const probablyTimedOut =
222 statecheckDuration.toMicroSeconds() >
timeouts.stateCheck_us;
225 "Could not validate EtherCAT state of slave at index %u (0 = all slaves).\n"
226 "Requested state is %s and the actual state is %s.\n"
227 "If the SOEM operation to check a slave's state timed out, the actual "
228 "state might just be the minimum state SOEM was able to derive within that "
230 "The configured timeout is %i µs and the operation took %i µs%s.",
232 request->state.c_str(),
235 statecheckDuration.toMicroSeconds(),
236 probablyTimedOut ?
", so the operation probably timed out" :
"");
237 request->setFailed();
240 request->setActualState(actualState);
246 BusIO::handleReadStatesRequest(
const std::shared_ptr<ReadStatesRequest>& request)
248 EtherCATState state =
static_cast<std::uint16_t
>(ec_readstate());
249 request->setState(state);
253 BusIO::handleRegisterResetRequest(
const std::shared_ptr<RegisterResetRequest>& request)
255 static constexpr size_t lengthOfPreMadeFrame = 44;
256 static const std::vector<size_t> slaveAddressOffsets{2, 28};
258 static constexpr EtherCATFrame preMadeResetFrame{
259 (lengthOfPreMadeFrame - 2) | 0x1000 ,
262 0x05 , 0xff , 0xff, 0xff ,
263 0x00, 0x03 , 0x0e, 0x00 ,
264 0x00, 0x00 , 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
265 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 , 0x00, 0x00 ,
270 0x05 , 0xff , 0xff, 0xff ,
271 0x10, 0x03 , 0x04, 0x00 ,
272 0x00, 0x00 , 0x00, 0x00, 0x00, 0x00 ,
277 auto [workingCounter, bufferIndex] =
278 sendAndReceiveEtherCATFrame(&preMadeResetFrame,
279 lengthOfPreMadeFrame,
280 ec_slave[request->slaveIndex].configadr,
281 slaveAddressOffsets);
283 if (workingCounter == EC_NOFRAME)
285 GENERAL_ERROR(
"Failed to reset error counters for slave at index %u.",
286 request->slaveIndex);
287 request->setFailed();
290 ecx_setbufstat(ecx_context.port, bufferIndex, EC_BUF_EMPTY);
294 BusIO::handleRegisterReadRequest(
const std::shared_ptr<RegisterReadRequest>& request)
296 auto [frameList, count] = request->getFrames();
298 for (EtherCATFrameIterator it =
299 EtherCATFrameIterator(frameList, frameList->nextIndex, count);
303 auto [frame, metaData] = *it;
304 auto [workingCounter, bufferIndex] =
305 sendAndReceiveEtherCATFrame(frame, metaData->lengthOfFrame, 0, {});
306 if (workingCounter != EC_NOFRAME)
311 reinterpret_cast<EtherCATFrame*
>(
312 &(ecx_context.port->rxbuf[bufferIndex])),
313 metaData->lengthOfFrame);
317 std::stringstream ss;
318 ss <<
"EtherCATFrame containing requests for reading registers from following "
319 "slaves returned with EC_NOFRAME:\n";
320 for (
const auto& pdu : metaData->pdus)
322 std::uint16_t slaveIndex = 0;
323 for (
int i = 1; i <= ec_slavecount; i++)
325 if (ec_slave[i].configadr == pdu.slaveConfiguredAddress)
327 slaveIndex =
static_cast<std::uint16_t
>(i);
332 ss << std::to_string(slaveIndex) <<
", ";
334 ss.seekp(ss.str().length() - 2);
335 ss <<
"\n This happened on frame " << it.getCurrentIndex() <<
" out of " << count
336 <<
" requested Frames. \n";
337 ss.seekp(ss.str().length() - 2);
340 request->setFailed();
342 ecx_setbufstat(ecx_context.port, bufferIndex, EC_BUF_EMPTY);
345 request->updateRequestedRegisters();
349 BusIO::handleSDOUpdateRequest(
const std::shared_ptr<SDOUpdateRequest>& request)
351 if (!isSDOAccessAvailable())
353 request->setFailed();
359 int len =
static_cast<int>(*request->buflen);
360 if (request->readRequest)
363 wkc = ec_SDOread(request->sdoIdentifier.slaveIndex,
364 request->sdoIdentifier.index,
365 request->sdoIdentifier.subIndex,
366 request->completeAccess,
370 *request->buflen = len;
376 wkc = ec_SDOwrite(request->sdoIdentifier.slaveIndex,
377 request->sdoIdentifier.index,
378 request->sdoIdentifier.subIndex,
379 request->completeAccess,
388 GENERAL_ERROR(
"%s for slave at index %u at 0x%X:%u failed:\n"
390 "SOEM-errorlist:\n%s",
391 request->readRequest ?
"SDORead" :
"SDOWrite",
392 request->sdoIdentifier.slaveIndex,
393 request->sdoIdentifier.index,
394 request->sdoIdentifier.subIndex,
398 request->setFailed();
404 requestHandlerThreadRunning.store(
false);
405 requestHandlerThread.join();
412 requestQueues.postChangeStateRequest(slaveIndex, state, validate, &actualState);
420 requestQueues.postChangeStateRequest(0, state, validate, &actualState);
428 requestQueues.postReadStatesRequest(&state);
435 return requestQueues.postRegisterResetRequest(slaveIndex);
441 return requestQueues.postRegisterReadRequest(registerData);
447 return requestQueues.postRegisterReadRequest(frames, amountFramesToRead);
453 ARMARX_DEBUG <<
"Deactivation CoE Complete Access for slave at index " << slaveIndex;
454 if (slaveIndex <= ec_slavecount)
456 std::uint8_t config = ec_slave[slaveIndex].CoEdetails;
457 config &= ~ECT_COEDET_SDOCA;
458 ec_slave[slaveIndex].CoEdetails = config;
463 "Trying to deactivate CoE Complete Access for slave at index %u which does "
464 "not exist on the bus.",
#define GENERAL_ERROR(...)
#define GENERAL_WARNING(...)
uint16_t slaveConfiguredAddress
#define GENERAL_TIMING_START(name)
#define GENERAL_TIMING_CEND(name, thresholdMs)
void deactivateCOECA(std::uint16_t slaveIndex)
This deactivates the Complete access mode in CoE for the given slave.
EtherCATState readStates()
void setTimeouts(Timeouts const &timeouts)
EtherCATState changeStateOfSlave(std::uint16_t slaveIndex, EtherCATState state, bool validate=true)
void rtUpdatePDO()
Updates the PDO of all slaves.
bool readRegisters(std::vector< RegisterDataList > ®isterData)
EtherCATState changeStateOfBus(EtherCATState state, bool validate=true)
bool resetErrorRegisters(std::uint16_t slaveIndex)
This class is a wrapper around an enum containing the different EtherCAT states.
static bool pinThreadToCPU(unsigned int cpu)
Pins the calling thread to the CPU with the given id.
static bool elevateThreadPriority(int priority)
Elevate the thread priority of the calling thread to the given priority.
static constexpr int RT_THREAD_PRIORITY
The thread priority for rt-threads.
bool postSDOReadRequest(SDOIdentifier sdoIdentifier, int *buflen, unsigned char *buf, bool completeAccess)
std::shared_ptr< RequestBase > getNextRequest()
bool postSDOWriteRequest(SDOIdentifier sdoIdentifier, int buflen, unsigned char *buf, bool completeAccess)
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
#define TIMING_START(name)
Helper macro to do timing tests.
#define TIMING_CEND_COMMENT(name, comment, thresholdMs)
Prints duration with comment in front of it if it took longer than threshold.
The EtherCATFrameList struct holds a list of EtherCAT frames that can be scheduled in round-robin-sty...
The EtherCATFrame struct represents an EtherCAT frame according to the EtherCAT spec.