45 TPCDistributeCMVSpec(
const std::vector<uint32_t>& crus,
const unsigned int timeframes,
const int nTFsBuffer,
const unsigned int outlanes,
const int firstTF, std::shared_ptr<o2::base::GRPGeomRequest> req)
47 mTimeFrames{timeframes},
48 mNTFsBuffer{nTFsBuffer},
50 mProcessedCRU{{std::vector<unsigned int>(timeframes), std::vector<unsigned int>(timeframes)}},
51 mTFStart{{firstTF, firstTF +
static_cast<long>(timeframes) * nTFsBuffer}},
52 mTFEnd{{firstTF +
static_cast<long>(timeframes) * nTFsBuffer - 1, firstTF + 2LL * timeframes * nTFsBuffer - 1}},
54 mSendCCDBOutputOrbitReset(outlanes),
55 mSendCCDBOutputGRPECS(outlanes),
56 mOrbitInfoForwarded{{std::vector<bool>(timeframes,
false), std::vector<bool>(timeframes,
false)}}
58 mDataDescrOut.reserve(mOutLanes);
59 mOrbitDescrOut.reserve(mOutLanes);
60 for (
unsigned int i = 0;
i < mOutLanes; ++
i) {
61 mDataDescrOut.emplace_back(getDataDescriptionCMV(
i));
62 mOrbitDescrOut.emplace_back(getDataDescriptionCMVOrbitInfo(
i));
65 std::sort(mCRUs.begin(), mCRUs.end());
67 for (
auto& processedCRUbuffer : mProcessedCRUs) {
68 processedCRUbuffer.resize(mTimeFrames);
69 for (
auto& crusMap : processedCRUbuffer) {
70 crusMap.reserve(mCRUs.size());
71 for (
const auto cruID : mCRUs) {
72 crusMap.emplace(cruID,
false);
84 mNFactorTFs = ic.options().get<
int>(
"nFactorTFs");
85 mNTFsDataDrop = ic.options().get<
int>(
"drop-data-after-nTFs");
86 mCheckEveryNData = ic.options().get<
int>(
"check-data-every-n");
87 if (mCheckEveryNData == 0) {
88 mCheckEveryNData = mTimeFrames / 2;
89 if (mCheckEveryNData == 0) {
92 mNTFsDataDrop = mCheckEveryNData;
100 LOGP(
debug,
"Updating ORBITRESET");
101 std::fill(mSendCCDBOutputOrbitReset.begin(), mSendCCDBOutputOrbitReset.end(),
true);
105 LOGP(
debug,
"Updating GRPECS");
106 std::fill(mSendCCDBOutputGRPECS.begin(), mSendCCDBOutputGRPECS.end(),
true);
108 LOGP(
debug,
"Detected default GRPECS object");
116 if (mCCDBRequest->askTime) {
117 const bool grpecsValid = pc.inputs().isValid(
"grpecs");
118 const bool orbitResetValid = pc.inputs().isValid(
"orbitReset");
122 if (orbitResetValid) {
123 pc.inputs().get<std::vector<Long64_t>*>(
"orbitReset");
125 if (pc.inputs().countValidInputs() == (grpecsValid + orbitResetValid)) {
131 if (
tf == std::numeric_limits<uint32_t>::max()) {
137 if (mTFStart.front() <= -1) {
138 const auto firstTFDetected =
tf;
139 const long offsetTF = std::abs(mTFStart.front() + 1);
140 const auto nTotTFs = getNRealTFs();
142 const long firstRealTF =
static_cast<long>(firstTFDetected) - (mNTFsBuffer - 1) + offsetTF;
143 mTFStart = {firstRealTF, firstRealTF + nTotTFs};
144 mTFEnd = {mTFStart[1] - 1, mTFStart[1] - 1 + nTotTFs};
145 LOGP(detail,
"Setting {} as first TF", mTFStart[0]);
146 LOGP(detail,
"Using offset of {} TFs for setting the first TF", offsetTF);
150 const bool currentBuffer = (
tf > mTFEnd[mBuffer]) ? !mBuffer : mBuffer;
151 if (mTFStart[currentBuffer] >
tf) {
152 LOGP(warning,
"Current TF {} is older than start of currentBuffer {}. Skipping this TF",
tf, mTFStart[currentBuffer]);
156 const unsigned int currentOutLane = getOutLane(
tf);
157 const unsigned int relTF = (
tf - mTFStart[currentBuffer]) / mNTFsBuffer;
158 LOGP(
debug,
"Current TF: {}, relative TF: {}, current buffer: {}, current output lane: {}, mTFStart: {}",
tf, relTF, currentBuffer, currentOutLane, mTFStart[currentBuffer]);
160 if (relTF >= mProcessedCRU[currentBuffer].
size()) {
161 LOGP(warning,
"Skipping tf {} for lane {}: relative tf {} is larger than size of buffer [{}, {}]: {}",
tf, currentOutLane, relTF, mTFStart[currentBuffer], mTFEnd[currentBuffer], mProcessedCRU[currentBuffer].
size());
163 mProcessedTotalData = mCheckEveryNData;
164 checkIntervalsForMissingData(pc, currentBuffer, relTF, currentOutLane,
tf);
168 if (mProcessedCRU[currentBuffer][relTF] == mCRUs.size()) {
169 LOGP(warning,
"All CRUs for current TF {} (relTF {}, lane {}) already received. Skipping this TF",
tf, relTF, currentOutLane);
173 if (mSendOutputStartInfo[currentBuffer]) {
174 mSendOutputStartInfo[currentBuffer] =
false;
178 if (mSendCCDBOutputOrbitReset[currentOutLane] && mSendCCDBOutputGRPECS[currentOutLane]) {
179 mSendCCDBOutputOrbitReset[currentOutLane] =
false;
180 mSendCCDBOutputGRPECS[currentOutLane] =
false;
184 forwardOrbitInfo(pc, currentBuffer, relTF, currentOutLane);
187 for (
auto it = inputs.begin(); it != inputs.end(); ++it) {
188 auto const* tpcCRUHeader = o2::framework::DataRefUtils::getHeader<o2::header::DataHeader*>(*it);
189 const unsigned int cru = tpcCRUHeader->subSpecification >> 7;
192 if (!std::binary_search(mCRUs.begin(), mCRUs.end(), cru)) {
193 LOGP(
debug,
"Received data from CRU: {} which was not specified as input. Skipping", cru);
197 if (mProcessedCRUs[currentBuffer][relTF][cru]) {
198 LOGP(warning,
"CRU {} for current TF {} (relTF {}, lane {}) already processed. Skipping ...", cru,
tf, relTF, currentOutLane);
202 ++mProcessedCRU[currentBuffer][relTF];
204 mProcessedCRUs[currentBuffer][relTF][cru] =
true;
209 LOGP(detail,
"Number of received CRUs for current TF: {} Needed a total number of processed CRUs of: {} Current TF: {}", mProcessedCRU[currentBuffer][relTF], mCRUs.size(),
tf);
212 if (mNTFsDataDrop > 0) {
213 checkIntervalsForMissingData(pc, currentBuffer, relTF, currentOutLane,
tf);
216 if (mProcessedCRU[currentBuffer][relTF] == mCRUs.size()) {
217 ++mProcessedTFs[currentBuffer];
220 if (mProcessedTFs[currentBuffer] == mTimeFrames) {
221 finishInterval(pc, currentOutLane, currentBuffer,
tf);
230 const std::string
name = fmt::format(
"CMVAGG{}", lane);
239 const std::string
name = fmt::format(
"CMVORB{}", lane);
249 std::vector<uint32_t> mCRUs{};
250 const unsigned int mTimeFrames{};
251 const int mNTFsBuffer{1};
252 const unsigned int mOutLanes{};
253 std::array<unsigned int, 2> mProcessedTFs{{0, 0}};
254 std::array<std::vector<unsigned int>, 2> mProcessedCRU{};
255 std::array<std::vector<std::unordered_map<unsigned int, bool>>, 2> mProcessedCRUs{};
256 std::array<long, 2> mTFStart{};
257 std::array<long, 2> mTFEnd{};
258 std::array<bool, 2> mSendOutputStartInfo{
true,
true};
259 std::shared_ptr<o2::base::GRPGeomRequest> mCCDBRequest;
260 std::vector<bool> mSendCCDBOutputOrbitReset{};
261 std::vector<bool> mSendCCDBOutputGRPECS{};
262 unsigned int mCurrentOutLane{0};
265 int mNTFsDataDrop{0};
266 std::array<int, 2> mStartNTFsDataDrop{0};
267 long mProcessedTotalData{0};
268 int mCheckEveryNData{1};
269 std::vector<o2::framework::InputSpec> mFilter{};
270 std::vector<o2::framework::InputSpec> mOrbitFilter{};
271 std::vector<header::DataDescription> mDataDescrOut{};
272 std::vector<header::DataDescription> mOrbitDescrOut{};
273 std::array<std::vector<bool>, 2> mOrbitInfoForwarded{};
276 unsigned int getOutLane(
const uint32_t
tf)
const {
return (
tf > mTFEnd[mBuffer]) ? (mCurrentOutLane + 1) % mOutLanes : mCurrentOutLane; }
278 unsigned int getNRealTFs()
const {
return mNTFsBuffer * mTimeFrames; }
290 template <
typename EmptyFn>
293 if (
auto* payloadMsg = it.getPayload()) {
295 }
else [[unlikely]] {
297 LOGP(warning,
"No payload for CRU {}; sending empty {} payload", cru, outSpec.
description.
as<std::string>());
304 if (mOrbitInfoForwarded[currentBuffer][relTF]) {
309 for (
auto it = inputs.begin(); it != inputs.end(); ++it) {
310 auto const* tpcCRUHeader = o2::framework::DataRefUtils::getHeader<o2::header::DataHeader*>(*it);
311 const unsigned int cru = tpcCRUHeader->subSpecification >> 7;
312 if (!std::binary_search(mCRUs.begin(), mCRUs.end(), cru)) {
317 mOrbitInfoForwarded[currentBuffer][relTF] =
true;
324 const unsigned int currentOutLane = mCurrentOutLane;
326 if (mSendOutputStartInfo[mBuffer] && (mTFStart[mBuffer] >= 0)) {
327 mSendOutputStartInfo[mBuffer] =
false;
331 if (mSendCCDBOutputOrbitReset[currentOutLane] && mSendCCDBOutputGRPECS[currentOutLane]) {
332 mSendCCDBOutputOrbitReset[currentOutLane] =
false;
333 mSendCCDBOutputGRPECS[currentOutLane] =
false;
337 if (!mOrbitInfoForwarded[mBuffer].
empty()) {
339 for (
auto it = inputs.begin(); it != inputs.end(); ++it) {
340 auto const* tpcCRUHeader = o2::framework::DataRefUtils::getHeader<o2::header::DataHeader*>(*it);
341 const unsigned int cru = tpcCRUHeader->subSpecification >> 7;
342 if (!std::binary_search(mCRUs.begin(), mCRUs.end(), cru)) {
352 for (
auto it = inputs.begin(); it != inputs.end(); ++it) {
353 auto const* tpcCRUHeader = o2::framework::DataRefUtils::getHeader<o2::header::DataHeader*>(*it);
354 const unsigned int cru = tpcCRUHeader->subSpecification >> 7;
357 if (!std::binary_search(mCRUs.begin(), mCRUs.end(), cru)) {
365 void clearBuffer(
const bool currentBuffer)
368 for (
auto& crusMap : mProcessedCRUs[currentBuffer]) {
369 for (
auto& it : crusMap) {
374 mProcessedTFs[currentBuffer] = 0;
375 std::fill(mProcessedCRU[currentBuffer].
begin(), mProcessedCRU[currentBuffer].
end(), 0);
376 std::fill(mOrbitInfoForwarded[currentBuffer].
begin(), mOrbitInfoForwarded[currentBuffer].
end(),
false);
378 mTFStart[mBuffer] = mTFEnd[!mBuffer] + 1;
379 mTFEnd[mBuffer] = mTFStart[mBuffer] + getNRealTFs() - 1;
383 mCurrentOutLane = ++mCurrentOutLane % mOutLanes;
386 void checkIntervalsForMissingData(
o2::framework::ProcessingContext& pc,
const bool currentBuffer,
const long relTF,
const unsigned int currentOutLane,
const uint32_t
tf)
388 if (!(mProcessedTotalData++ % mCheckEveryNData)) {
389 LOGP(detail,
"Checking for dropped packages...");
392 if ((mTFStart[currentBuffer] > mTFStart[!currentBuffer]) && (relTF > mNTFsDataDrop)) {
393 LOGP(warning,
"Checking last buffer from relTF {} to {}", mStartNTFsDataDrop[!currentBuffer], mProcessedCRU[!currentBuffer].
size());
394 const unsigned int lastLane = (currentOutLane == 0) ? (mOutLanes - 1) : (currentOutLane - 1);
395 checkMissingData(pc, !currentBuffer, mStartNTFsDataDrop[!currentBuffer], mProcessedCRU[!currentBuffer].
size(), lastLane);
396 LOGP(warning,
"All empty TFs of last buffer [{}, {}] filled with dummy and sent, triggered by data from TF {} (relTF {}). Clearing buffer", mTFStart[!currentBuffer], mTFEnd[!currentBuffer],
tf, relTF);
397 finishInterval(pc, lastLane, !currentBuffer,
tf);
400 const int tfEndCheck = std::clamp(
static_cast<int>(relTF) - mNTFsDataDrop, 0,
static_cast<int>(mProcessedCRU[currentBuffer].
size()));
401 LOGP(detail,
"Checking current buffer from relTF {} to {}", mStartNTFsDataDrop[currentBuffer], tfEndCheck);
402 checkMissingData(pc, currentBuffer, mStartNTFsDataDrop[currentBuffer], tfEndCheck, currentOutLane);
403 mStartNTFsDataDrop[currentBuffer] = tfEndCheck;
409 for (
int iTF = startTF; iTF < endTF; ++iTF) {
410 if (mProcessedCRU[currentBuffer][iTF] != mCRUs.size()) {
411 LOGP(warning,
"CRUs for lane {} rel. TF: {} curr TF {} are missing! Processed {} CRUs out of {}", outLane, iTF, mTFStart[currentBuffer] +
static_cast<long>(iTF) * mNTFsBuffer + mNTFsBuffer - 1, mProcessedCRU[currentBuffer][iTF], mCRUs.size());
412 ++mProcessedTFs[currentBuffer];
413 mProcessedCRU[currentBuffer][iTF] = mCRUs.size();
416 for (
auto& it : mProcessedCRUs[currentBuffer][iTF]) {
419 sendEmptyCMVOutput(pc, outLane, it.first);
424 if (!mOrbitInfoForwarded[currentBuffer][iTF]) {
425 sendEmptyOrbitInfo(pc, outLane);
426 mOrbitInfoForwarded[currentBuffer][iTF] =
true;
434 if (mNFactorTFs > 0) {
437 for (
unsigned int ilane = 0; ilane < mOutLanes; ++ilane) {
439 auto&
state = deviceProxy.getOutputChannelState({
static_cast<int>(ilane)});
440 size_t oldest = std::numeric_limits<size_t>::max() - 1;
441 state.oldestForChannel = {oldest};
445 LOGP(info,
"All TFs for buffer [{}, {}] (lane {}) received at data from TF {}. Clearing buffer", mTFStart[
buffer], mTFEnd[
buffer], currentOutLane,
tf);
447 mStartNTFsDataDrop[
buffer] = 0;
448 mSendOutputStartInfo[
buffer] =
true;
454 std::vector<o2::framework::InputSpec> inputSpecs;
458 std::vector<o2::framework::OutputSpec> outputSpecs;
459 outputSpecs.reserve(3 * outlanes);
460 for (
unsigned int lane = 0; lane < outlanes; ++lane) {
467 bool fetchCCDB =
false;
468 if (sendPrecisetimeStamp && (ilane == 0)) {
470 for (
unsigned int lane = 0; lane < outlanes; ++lane) {
475 auto ccdbRequest = std::make_shared<o2::base::GRPGeomRequest>(fetchCCDB,
483 const auto id = fmt::format(
"tpc-distribute-cmv-{:02}", ilane);
488 o2::framework::AlgorithmSpec{o2::framework::adaptFromTask<TPCDistributeCMVSpec>(crus, timeframes, nTFsBuffer, outlanes, firstTF, ccdbRequest)},
490 {
"check-data-every-n",
o2::framework::VariantType::Int, 0, {
"Number of run function called after which to check for missing data (-1 for no checking, 0 for default checking)."}},