Professional Communication
Software Development Tools

OPC Foundation member and certified logos

Online Forums

Technical support is provided through Support Forums below. Anybody can view them; you need to Register/Login to our site (see links in upper right corner) in order to Post questions or issues. You do not have to own a commercial license in order to use the OPC Labs supportOur team is actively monitoring the forums, and provides replies as soon as possible.

Please read Rules for forum posts before reporting your issue or asking a question. OPC Labs team is actively monitoring the forums, and replies as soon as possible.

Various technical information can also be found in our Knowledge Base. For your convenience, we have also assembled a Frequently Asked Questions page.

Do not use the Contact page for technical issues.

OPC-UA digital channel subscription drops or goes stale

More
01 Sep 2026 09:46 - 01 Sep 2026 09:47 #14677 by support
Hello.

You wrote "...there are no reported issues from the library or our software (such as communication issues).".
However, your code is missing the relevant error handling . Specifically, both the data change notification handlers for analog and digital items start with
Code:
if (!args.Succeeded) return;

Errors in subscriptions are reported though this channel too. When .Succeeded == false, there is always .Exception that is != null. It is quite possible that there are errors being reported to you in this way, but we do not see them. Please modify your code to log or otherwise capture the .Exception property (and its .InnerException etc.) when present. Hopefully, this will move us forward.

Best regards


 
Last edit: 01 Sep 2026 09:47 by support.

Please Log in or Create an account to join the conversation.

More
31 Aug 2026 20:11 #14676 by jeremyvnc
Greetings,
   I have implemented the EasyUA library into our production software and for the most part it was easy to do so. We use OPC-UA to communicate to a Beckhoff EK9160 I/O Controller.  We have been witnessing random stations loose their digital channel subscription (the data no longer updates) after a long idle period such as a weekend but there are no reported issues from the library or our software (such as communication issues). The analog channels continue chugging along just fine. The analog and digital data rates are both set to 50ms. We added a monitored item (OPC-UA Server Time) to each list of monitored items to act as a heartbeat but that didn't seem to help either.
Here is the section we use to start our data collection and receive value updates from the library.

private bool StartDataCollectionInternal()
{
if (DataCollectionState == DataCollectionState.Collecting) return true;
 
Logger.WithProperty(LoggerProperties.CollectionState, DataCollectionState)
.Debug("Start Data Collection");
 
DataCollectionState = DataCollectionState.Initializing;
_digitalChannelsToSubscribe.Clear();
_digitalChannelsToSubscribe.AddRange(DigitalChannels);
_analogChannelsToSubscribe.Clear();
_analogChannelsToSubscribe.AddRange(AnalogChannels);
 
var stopwatch = Stopwatch.StartNew();
//Analog Channel MonitoredItems
_analogMonitoredItems.ForEach(m =>
{
m.DataChangeCallback -= AnalogChannelDataChanged;
m.EventCallback -= AnalogChannelUaEvent;
});
_analogMonitoredItems.Clear();
_analogMonitoredItems.Add(new EasyUAMonitoredItemArguments(
(_, args) =>
{
if (!args.Succeeded) return;
AnalogChannels.ForEach(c => c.LastValueUpdate = DateTime.Now);
if (!DateTime.TryParse(args.AttributeData?.Value?.ToString(), out var dateTime)) return;
_opcuaControllerStatistics.RecordLastAnalogSubscription(dateTime);
_opcuaControllerStatistics.RecordOpcuaServerDateTime(dateTime);
}, null, _endpointDescriptor, _opcuaCommunicationConfig.WatchdogNodeSubscription.NodeId,
new UAMonitoringParameters(_opcuaCommunicationConfig.WatchdogNodeSubscription.SamplingInterval),
new UASubscriptionParameters(_opcuaCommunicationConfig.WatchdogNodeSubscription.PublishingInterval)));
AnalogChannels.ForEach(InitializeAnalogChannel);
var analogSubscriptions = _client.SubscribeMultipleMonitoredItems(_analogMonitoredItems.ToArray());
_opcuaControllerStatistics.RecordAnalogSubscriptionCount(analogSubscriptions.Length);
stopwatch.Stop();
var analogTime = stopwatch.ElapsedMilliseconds;
Logger.Debug($"Creating analog subscription with monitored items took {stopwatch.ElapsedMilliseconds}");
 
//Digital Channel MonitoredItems
stopwatch.Restart();
_digitalMonitoredItems.ForEach(m =>
{
m.DataChangeCallback -= DigitalChannelDataChanged;
m.EventCallback -= DigitalChannelUaEvent;
});
_digitalMonitoredItems.Clear();
_digitalMonitoredItems.Add(new EasyUAMonitoredItemArguments(
(_, args) =>
{
if (!args.Succeeded) return;
DigitalChannels.ForEach(c => c.LastValueUpdate = DateTime.Now);
if (!DateTime.TryParse(args.AttributeData?.Value?.ToString(), out var dateTime)) return;
_opcuaControllerStatistics.RecordLastDigitalSubscription(dateTime);
_opcuaControllerStatistics.RecordOpcuaServerDateTime(dateTime);
}, null, _endpointDescriptor, _opcuaCommunicationConfig.WatchdogNodeSubscription.NodeId,
new UAMonitoringParameters(_opcuaCommunicationConfig.WatchdogNodeSubscription.SamplingInterval),
new UASubscriptionParameters(_opcuaCommunicationConfig.WatchdogNodeSubscription.PublishingInterval)));
DigitalChannels.ForEach(InitializeDigitalChannel);
var digitalSubscriptions = _client.SubscribeMultipleMonitoredItems(_digitalMonitoredItems.ToArray());
_opcuaControllerStatistics.RecordDigitalSubscriptionCount(digitalSubscriptions.Length);
stopwatch.Stop();
Logger.Debug($"Creating digital subscription with monitored items took {stopwatch.ElapsedMilliseconds}");
Logger.Debug($"Adding monitored items ({_analogMonitoredItems.Count} Analog/{_digitalMonitoredItems.Count} Digital) took {analogTime + stopwatch.ElapsedMilliseconds}");
DataCollectionState = DataCollectionState.Collecting;
return true;
}private void AnalogChannelDataChanged(object sender, EasyUADataChangeNotificationEventArgs e)
{
if (e.Arguments.State is not AnalogChannel channel)
{
Logger.Error($"Data state returned not {nameof(AnalogChannel)}");
return;
}
 
if (!e.Succeeded)
{
Logger.WithProperty(LoggerProperties.Channel, e.Arguments.State?.ToString() ?? "Unknown")
.Error(e.Exception, "Error in Data Change Subscription for channel");
channel.ChannelState = ChannelState.Error;
return;
}
 
if (!double.TryParse(e.AttributeData?.Value?.ToString() ?? "", out var newValue))
{
Logger.Error($"Value '{e.AttributeData?.Value?.ToString() ?? ""}' wasn't convertable to a decimal");
IoControllerStatistics.RecordChannelOperation(false, channel.ChannelConfig, false);
channel.ChannelState = ChannelState.Error;
return;
}
channel.UpdateValue(newValue);
channel.ChannelState = ChannelState.Normal;
IoControllerStatistics.RecordChannelOperation(true, channel.ChannelConfig, false);
}private void DigitalChannelDataChanged(object sender, EasyUADataChangeNotificationEventArgs e)
{
if (e.Arguments.State is not DigitalChannel channel)
{
Logger.Error($"Data state returned not {nameof(DigitalChannel)}");
return;
}
 
if (!e.Succeeded)
{
Logger.WithProperty(LoggerProperties.Channel, e.Arguments.State?.ToString() ?? "Unknown")
.Error(e.Exception, "Error in Data Change Subscription for channel");
channel.ChannelState = ChannelState.Error;
return;
}
 
if (!bool.TryParse(e.AttributeData?.Value?.ToString() ?? "", out var newValue))
{
Logger.Error($"Value '{e.AttributeData?.Value?.ToString() ?? ""}' wasn't convertable to a boolean");
IoControllerStatistics.RecordChannelOperation(false, channel.ChannelConfig, false);
channel.ChannelState = ChannelState.Error;
return;
}
 
channel.Value = newValue;
channel.ChannelState = ChannelState.Normal;
IoControllerStatistics.RecordChannelOperation(true, channel.ChannelConfig, false);
}
 
private void InitializeAnalogChannel(IAnalogChannel channel)
{
if (channel == null)
{
Logger.Warn("Channel cannot be null for InitializeAnalogChannel");
return;
}
 
var logger = Logger.WithProperty(LoggerProperties.Channel, channel.ChannelConfig.ChannelName);
try
{
channel.ChannelState = ChannelState.Initializing;
channel.LastValueUpdate = DateTime.Now;
if (channel.ChannelConfig is not AnalogChannelOpcuaConfig config)
{
var error = $"Channel must be of '{nameof(AnalogChannelOpcuaConfig)}' type but is {channel.ChannelConfig.GetType().Name}";
Logger.WithProperty(LoggerProperties.Channel, channel.ChannelConfig.ChannelName)
.Error(error);
channel.SetConfigurationFault(error);
channel.ChannelState = ChannelState.Misconfigured;
_analogChannelsToSubscribe.Remove(channel);
return;
}
 
logger.Trace("Reading initial value");
var readStatus = ReadAnalog(channel.ChannelConfig.ChannelName, out var value, true);
if (!readStatus)
{
var error = $"Error reading channel value for channel '{channel.ChannelConfig.ChannelName}'";
logger.Error(error);
channel.SetChannelInitializationFault(error);
channel.ChannelState = ChannelState.Error;
return;
}
logger.WithProperty(LoggerProperties.Value, value).Trace($"Updating value ({value})");
channel.UpdateValue(value);
 
var monitoredItem = new EasyUAMonitoredItemArguments(AnalogChannelDataChanged, channel,
_endpointDescriptor, config.NodeID,
new UAMonitoringParameters(config.SamplingRateMs, IoControllerConfig.Analog.Deadband),
new UASubscriptionParameters(IoControllerConfig.Analog.DataRateMs));
monitoredItem.EventCallback += AnalogChannelUaEvent;
_analogMonitoredItems.Add(monitoredItem);
 
channel.ClearInitializationFault();
_analogChannelsToSubscribe.Remove(channel);
}
catch (Exception e)
{
logger.Error(e, "Error initializing analog channel");
}
}
 
private void InitializeDigitalChannel(IDigitalChannel channel)
{
if (channel == null)
{
Logger.Warn("Channel cannot be null for InitializeDigitalChannel");
return;
}
 
var logger = Logger.WithProperty(LoggerProperties.Channel, channel.ChannelConfig.ChannelName);
try
{
channel.ChannelState = ChannelState.Initializing;
channel.LastValueUpdate = DateTime.Now;
if (channel.ChannelConfig is not DigitalChannelOpcuaConfig config)
{
var error = $"Channel must be of '{nameof(DigitalChannelOpcuaConfig)}' type but is {channel.ChannelConfig.GetType().Name}";
logger.Error(error);
channel.SetConfigurationFault(error);
channel.ChannelState = ChannelState.Misconfigured;
_digitalChannelsToSubscribe.Remove(channel);
return;
}
 
logger.Trace("Reading initial value");
var readStatus = ReadDigital(channel.ChannelConfig.ChannelName, out var value, true);
if (!readStatus)
{
var error = $"Error reading channel value for channel '{channel.ChannelConfig.ChannelName}'";
logger.Error(error);
channel.SetChannelInitializationFault(error);
channel.ChannelState = ChannelState.Error;
return;
}
 
logger.WithProperty(LoggerProperties.Value, value).Trace($"Updating value ({value})");
channel.Value = value;
 
var monitoredItem = new EasyUAMonitoredItemArguments(DigitalChannelDataChanged, channel,
_endpointDescriptor, config.NodeID,
new UAMonitoringParameters(config.SamplingRateMs),
new UASubscriptionParameters(IoControllerConfig.Digital.DataRateMs));
monitoredItem.EventCallback += DigitalChannelUaEvent;
_digitalMonitoredItems.Add(monitoredItem);
 
channel.ClearInitializationFault();
_digitalChannelsToSubscribe.Remove(channel);
}
catch (Exception e)
{
logger.Error(e, "Error initializing digital channel");
}
}

Please Log in or Create an account to join the conversation.

Moderators: supportvaclav.zaloudek
Time to create page: 2.079 seconds