@@ -156,8 +156,31 @@ class TPCAggregateCMVDevice : public o2::framework::Task
156156 }
157157
158158 const long relTF = (currTF - mTFFirst ) / mNTFsBuffer ;
159- if ((relTF < 0 ) || (relTF >= static_cast <long >(mTimeFrames ))) {
160- LOGP (warning, " relTF={} out of range [0, {}) for TF {}, skipping" , relTF, mTimeFrames , currTF);
159+ if (relTF < 0 ) {
160+ LOGP (warning, " relTF={} < 0 for TF {}, skipping" , relTF, currTF);
161+ return ;
162+ }
163+ if (relTF >= static_cast <long >(mTimeFrames )) {
164+ // The distribute has advanced past this interval (empty CRU placeholders sent by checkMissingData
165+ // arrive with the triggering TF's context, not the missing batch's context).
166+ // Force-complete whatever was buffered so the next TF starts a fresh interval.
167+ LOGP (warning, " relTF={} out of range [0, {}) for TF {}: force-completing stale interval and resetting" , relTF, mTimeFrames , currTF);
168+ if (mTimestampStart == 0 ) {
169+ mTimestampStart = static_cast <long >(pc.services ().get <o2::framework::TimingInfo>().creation );
170+ }
171+ materializeBufferedTFs (true );
172+ sendOutput (pc.outputs ());
173+ // Advance mTFFirst to the interval containing currTF so that after reset() clears it to -1
174+ // we can restore a valid value. Without this, the distribute won't resend CMVFIRSTTF (it was
175+ // already sent for the current interval), causing "firstTF not found" and further bad relTFs.
176+ long nextFirst = mIntervalFirstTF + static_cast <long >(mTimeFrames ) * mNTFsBuffer ;
177+ while (static_cast <long >(currTF) >= nextFirst + static_cast <long >(mTimeFrames ) * mNTFsBuffer ) {
178+ nextFirst += static_cast <long >(mTimeFrames ) * mNTFsBuffer ;
179+ }
180+ reset ();
181+ mTFFirst = nextFirst;
182+ mIntervalFirstTF = nextFirst;
183+ mHasIntervalFirstTF = true ;
161184 return ;
162185 }
163186
@@ -338,10 +361,15 @@ class TPCAggregateCMVDevice : public o2::framework::Task
338361 // / orbitStep is the dynamically measured per-sub-TF stride; when non-zero it is preferred over the GRP NHBFPerTF for the orbit-offset calculation.
339362 void setTimestampCCDB (const long relTF, const uint32_t orbitStep, o2::framework::ProcessingContext& pc)
340363 {
364+ const auto & tinfo = pc.services ().get <o2::framework::TimingInfo>();
341365 if (mUsePreciseTimestamp && !mTFInfo .second ) {
366+ // Orbit-reset info (NHBFPerTF) not yet received from the distribute lane.
367+ // Fall back to DPL wall-clock creation time so mTimestampStart is never
368+ // left at 0, which would cause successive intervals to overwrite each other.
369+ mTimestampStart = tinfo.creation ;
370+ LOGP (warning, " Orbit reset info not yet received; using DPL creation time {} ms as fallback timestamp for interval starting at TF {}" , mTimestampStart , mTFFirst );
342371 return ;
343372 }
344- const auto & tinfo = pc.services ().get <o2::framework::TimingInfo>();
345373 // prefer the measured stride; fall back to NHBFPerTF from GRPECS
346374 const int nHBFPerTF = (orbitStep > 0 ) ? static_cast <int >(orbitStep) : o2::base::GRPGeomHelper::instance ().getNHBFPerTF ();
347375 const auto nOrbitsOffset = (relTF * mNTFsBuffer + (mNTFsBuffer - 1 )) * nHBFPerTF;
0 commit comments