public void UpdateIO(DateTime referenceDateTime) { lock (_lock) { if (_isReconfiguring) { return; } _counter = (int)((_counter + 1) % _settings.CycleFrequency); _actualWorkingCounter = EcHL.UpdateIo(this.Context, out _dcTime); #region "Diagnostics" // statistics if (_counter == 0) { Trace.WriteLine($"lost frames: {(double)_lostFrameCounter / _settings.CycleFrequency:P2} / wkc mismatch: {(double)_wkcMismatchCounter / _settings.CycleFrequency:P2}"); if (_lostFrameCounter == _settings.CycleFrequency) { _logger.LogWarning($"frame loss occured ({ _settings.CycleFrequency } frames)"); _lostFrameCounter = 0; } if (_wkcMismatchCounter == _settings.CycleFrequency) { _logger.LogWarning($"working counter mismatch { _actualWorkingCounter }/{ _expectedWorkingCounter }"); //Trace.WriteLine(EcUtilities.GetSlaveStateDescription(_ecSettings.RootSlaves.SelectMany(x => x.Descendants()).ToList())); _wkcMismatchCounter = 0; } _lostFrameCounter = 0; _wkcMismatchCounter = 0; } // no frame if (_actualWorkingCounter == -1) { _lostFrameCounter += 1; this.UtcDateTime = DateTime.MinValue; this.DcRingBufferAverage = 0; return; } // working counter mismatch if (_expectedWorkingCounter != _actualWorkingCounter) { _wkcMismatchCounter += 1; this.UtcDateTime = DateTime.MinValue; this.DcRingBufferAverage = 0; return; } #endregion // the UpdateIo timer tries to fire at discrete times. The timer is allowed to be early or delayed by < (CycleTime - Offset) and the resulting DC time will be floored to nearest 10 ms. this.UtcDateTime = _dcEpoch.AddTicks(Convert.ToInt64(_dcTime / _dateTime_To_Ns)); // for time loop control // for compensation, if DC is not initialized with real time or not initialized at all if (_offset == TimeSpan.Zero) { _offset = referenceDateTime - this.UtcDateTime; } else { this.UtcDateTime += _offset; } // dc drift compensation _dcRingBuffer[_dcRingBufferIndex] = Convert.ToInt64(referenceDateTime.Ticks - this.UtcDateTime.Ticks) * _dateTime_To_Ns; this.DcRingBufferAverage = Convert.ToInt64(_dcRingBuffer.Average()); if (!_isDcCompensationRunning && Math.Abs(this.DcRingBufferAverage) > 1500000) // 1.5 ms { _isDcCompensationRunning = true; _logger.LogInformation("DC drift compensation started"); } else if (_isDcCompensationRunning && Math.Abs(this.DcRingBufferAverage) < 1000000) // 1.0 ms { _isDcCompensationRunning = false; _logger.LogInformation("DC drift compensation finished"); } if (_isDcCompensationRunning) { EcHL.CompensateDcDrift(this.Context, Convert.ToInt32(Math.Min(Math.Max(this.DcRingBufferAverage, -_dcDriftCompensationRate), _dcDriftCompensationRate))); } _dcRingBufferIndex = (_dcRingBufferIndex + 1) % _dcRingBufferSize; } }