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 =
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 =
216 if (actualState != request->state)
221 bool const probablyTimedOut =
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);
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();
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.",