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
67 GENERAL_WARNING(
"SDO-access is not yet available. Probably the bus was not started yet");
74 BusIO::sendAndReceiveEtherCATFrame(
const EtherCATFrame* frame,
77 const std::vector<size_t>& slaveAddressOffsets)
79 int bufferIndex = ecx_getindex(ecx_context.port);
81 reinterpret_cast<uint8_t*
>(&(ecx_context.port->txbuf[bufferIndex]));
84 EtherCATFrame* framePlacement =
85 reinterpret_cast<EtherCATFrame*
>(buffer + ETH_HEADERSIZE);
86 std::memcpy(framePlacement, frame, frameLength);
88 framePlacement->pduArea[1] =
static_cast<std::uint8_t
>(bufferIndex);
89 for (
size_t offset : slaveAddressOffsets)
91 *
reinterpret_cast<uint16_t*
>(framePlacement->pduArea + offset)
95 ecx_context.port->txbuflength[bufferIndex] =
96 static_cast<int>(ETH_HEADERSIZE + frameLength);
99 int const workingCounter =
102 if (workingCounter == EC_NOFRAME)
104 GENERAL_ERROR(
"Sending and receiving EtherCAT frame timed out after %i µs (EC_NOFRAME). "
105 "This timeout will most likely result in consecutive errors.",
109 return {workingCounter, bufferIndex};
117 pdoUpdateRequested.store(
true, std::memory_order_relaxed);
120 ec_send_processdata();
126 pdoUpdateRequested.store(
false, std::memory_order_relaxed);
132 BusIO::requestHandlerLoop()
137 while (requestHandlerThreadRunning.load() ==
true)
139 if (!pdoUpdateRequested.load(std::memory_order_relaxed))
143 using namespace std::literals;
144 std::this_thread::sleep_for(10us);
149 BusIO::handleNextRequest()
155 if (
auto r = std::dynamic_pointer_cast<SDOUpdateRequest>(request))
157 handleSDOUpdateRequest(r);
160 else if (
auto r = std::dynamic_pointer_cast<ChangeStateRequest>(request))
162 handleChangeStateRequest(r);
165 else if (
auto r = std::dynamic_pointer_cast<ReadStatesRequest>(request))
167 handleReadStatesRequest(r);
170 else if (
auto r = std::dynamic_pointer_cast<RegisterResetRequest>(request))
172 handleRegisterResetRequest(r);
175 else if (
auto r = std::dynamic_pointer_cast<RegisterReadRequest>(request))
177 handleRegisterReadRequest(r);
181 requestQueues.
postReply(std::move(request));
189 BusIO::handleChangeStateRequest(
const std::shared_ptr<ChangeStateRequest>& request)
191 ec_slave[request->slaveIndex].state = request->state;
192 int ret = ec_writestate(request->slaveIndex);
193 if (
ret == EC_NOFRAME)
195 GENERAL_ERROR(
"Writing EtherCAT state %s to slave at index %u (0 = all slaves) timed out "
196 "(EC_NOFRAME). The timeout for this operation was %i µs, is hardcoded by "
197 "SOEM, and thus cannot be configured.",
199 request->state.c_str(),
202 request->setFailed();
206 if (request->validate)
209 EtherCATState actualState =
213 if (actualState != request->state)
218 bool const probablyTimedOut =
222 "Could not validate EtherCAT state of slave at index %u (0 = all slaves).\n"
223 "Requested state is %s and the actual state is %s.\n"
224 "If the SOEM operation to check a slave's state timed out, the actual "
225 "state might just be the minimum state SOEM was able to derive within that "
227 "The configured timeout is %i µs and the operation took %i µs%s.",
229 request->state.c_str(),
232 statecheckDuration.toMicroSeconds(),
233 probablyTimedOut ?
", so the operation probably timed out" :
"");
234 request->setFailed();
237 request->setActualState(actualState);
243 BusIO::handleReadStatesRequest(
const std::shared_ptr<ReadStatesRequest>& request)
245 EtherCATState state =
static_cast<std::uint16_t
>(ec_readstate());
246 request->setState(state);
250 BusIO::handleRegisterResetRequest(
const std::shared_ptr<RegisterResetRequest>& request)
252 static constexpr
size_t lengthOfPreMadeFrame = 44;
253 static const std::vector<size_t> slaveAddressOffsets{2, 28};
255 static constexpr EtherCATFrame preMadeResetFrame{
256 (lengthOfPreMadeFrame - 2) | 0x1000 ,
259 0x05 , 0xff , 0xff, 0xff ,
260 0x00, 0x03 , 0x0e, 0x00 ,
261 0x00, 0x00 , 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
262 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 , 0x00, 0x00 ,
267 0x05 , 0xff , 0xff, 0xff ,
268 0x10, 0x03 , 0x04, 0x00 ,
269 0x00, 0x00 , 0x00, 0x00, 0x00, 0x00 ,
274 auto [workingCounter, bufferIndex] =
275 sendAndReceiveEtherCATFrame(&preMadeResetFrame,
276 lengthOfPreMadeFrame,
277 ec_slave[request->slaveIndex].configadr,
278 slaveAddressOffsets);
280 if (workingCounter == EC_NOFRAME)
282 GENERAL_ERROR(
"Failed to reset error counters for slave at index %u.", request->slaveIndex);
283 request->setFailed();
286 ecx_setbufstat(ecx_context.port, bufferIndex, EC_BUF_EMPTY);
290 BusIO::handleRegisterReadRequest(
const std::shared_ptr<RegisterReadRequest>& request)
292 auto [frameList, count] = request->getFrames();
294 for (EtherCATFrameIterator it =
295 EtherCATFrameIterator(frameList, frameList->nextIndex, count);
299 auto [frame, metaData] = *it;
300 auto [workingCounter, bufferIndex] =
301 sendAndReceiveEtherCATFrame(frame, metaData->lengthOfFrame, 0, {});
302 if (workingCounter != EC_NOFRAME)
307 reinterpret_cast<EtherCATFrame*
>(
308 &(ecx_context.port->rxbuf[bufferIndex])),
309 metaData->lengthOfFrame);
313 std::stringstream ss;
314 ss <<
"EtherCATFrame containing requests for reading registers from following "
315 "slaves returned with EC_NOFRAME:\n";
316 for (
const auto& pdu : metaData->pdus)
318 std::uint16_t slaveIndex = 0;
319 for (
int i = 1; i <= ec_slavecount; i++)
321 if (ec_slave[i].configadr == pdu.slaveConfiguredAddress)
323 slaveIndex =
static_cast<std::uint16_t
>(i);
330 ss.seekp(ss.str().length() - 2);
331 ss <<
"\n This happened on frame " << it.getCurrentIndex() <<
" out of " << count <<
" requested Frames. \n"; ss.seekp(ss.str().length() - 2);
334 request->setFailed();
336 ecx_setbufstat(ecx_context.port, bufferIndex, EC_BUF_EMPTY);
339 request->updateRequestedRegisters();
343 BusIO::handleSDOUpdateRequest(
const std::shared_ptr<SDOUpdateRequest>& request)
345 if (!isSDOAccessAvailable())
347 request->setFailed();
353 int len =
static_cast<int>(*request->buflen);
354 if (request->readRequest)
357 wkc = ec_SDOread(request->sdoIdentifier.slaveIndex,
358 request->sdoIdentifier.index,
359 request->sdoIdentifier.subIndex,
360 request->completeAccess,
364 *request->buflen = len;
370 wkc = ec_SDOwrite(request->sdoIdentifier.slaveIndex,
371 request->sdoIdentifier.index,
372 request->sdoIdentifier.subIndex,
373 request->completeAccess,
382 GENERAL_ERROR(
"%s for slave at index %u at 0x%X:%u failed:\n"
384 "SOEM-errorlist:\n%s",
385 request->readRequest ?
"SDORead" :
"SDOWrite",
386 request->sdoIdentifier.slaveIndex,
387 request->sdoIdentifier.index,
388 request->sdoIdentifier.subIndex,
392 request->setFailed();
398 requestHandlerThreadRunning.store(
false);
399 requestHandlerThread.join();
447 ARMARX_DEBUG <<
"Deactivation CoE Complete Access for slave at index " << slaveIndex;
448 if (slaveIndex <= ec_slavecount)
450 std::uint8_t config = ec_slave[slaveIndex].CoEdetails;
451 config &= ~ECT_COEDET_SDOCA;
452 ec_slave[slaveIndex].CoEdetails = config;
456 GENERAL_WARNING(
"Trying to deactivate CoE Complete Access for slave at index %u which does "
457 "not exist on the bus.",