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. You do not have to be a licensed user of our product.
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.
- Forum
- Discussions
- QuickOPC-Classic in .NET
- Reading, Writing, Subscriptions, Property Access
- UnsubscribeItem stops updates
UnsubscribeItem stops updates
Sent: Thursday, May 01, 2014 5:37 AM
To: Zbynek Zahradnik
Subject: FW: OPC Client Settings
Hi Z.,
I hope all is well.
I have been in contact with the customers from this case: opclabs.com/forum/reading-writing-subscriptions-property-acc...-unsubscribeitem-stops-updates again. They say that the issue was with the server and apparently are not having that problem anymore.
...
Please Log in or Create an account to join the conversation.
I think there are generally following possibilities:
1. Some hidden problem in your code (unlikely - see above).
2. A problem in the EasyDAClient:
2a) Either it does a wrong it and truly removes all items from the server instead of just one, or
2b) it may remove just one item from the server, but internally stop sending you notifications for all of them.
3. It can be caused by a bug in the OPC Server.
The best way to diagnose further would be to monitor the communication between the OPC Client (your app) and the OPC Server. The tool that can be used is the OPC Analyzer from OPC Foundation (available to members). The problem with it is that with 140,000 or so items, it would either not work or will be extremely difficult to use. I therefore suggest to reduce the number of items first, to just a handful (ideally: subscribe to 2 items, unsubscribe from one), and see if the problem persists. Assuming that it does, we can then put the OPC Analyzer into action.
I would actually be able to do all this myself, if I can get access to the OPC Server installation. Is there something like a demo version?
Please Log in or Create an account to join the conversation.
But I think it can be improved, and the main thing is to collect your subscription requirements (from the SQL SELECT you have) first, into an in-memory array, and then call SubscribeMultipleItems with that array just once, instead of calling SubscribeItem in a loop. (of course if you have 4 types of info to deal with, as I have noticed, you do *not* have go that far and really try to have just 1 call: 4 calls are perfectly fine as well; the point is to avoid having hundreds or thousands of calls).
Same applies to the use of UnsubscribeItem: Replace it with a single call to UnsubscribeMultipleItems if it's being called for larger amounts of items in a loop.
I will reply to the unsubscribe issue separately later.
Best regards
Please Log in or Create an account to join the conversation.
Please Log in or Create an account to join the conversation.
namespace DES
{
public partial class DataExport : ServiceBase
{
private AutoResetEvent areServiceMain; //Service AutoReset event handler.
private System.Timers.Timer SubscriptionTimer; //Timer to subscribe to new points
private System.Timers.Timer RemoveSubTimer; //Timer to remove subscriptions once a point is deleted.
public String strConnString = ConfigurationManager.ConnectionStrings["ConnectionString"].ToString(); //Database connection string
public SqlCommand cmdCommand;
private EasyDAClient AnalogDAV; //OPC object for subscribing to Analog point values
private EasyDAClient AnalogDAS; //OPC Object for subscribing to Analog point states
private EasyDAClient StatusDAV; //OPC object for subscribing to Status point values
private EasyDAClient StatusDAS; //OPC Object for subscribing to Status point states
private EasyDAClient RemoteDAV; //OPC object for subscribing to Remote Device point values
private EasyDAClient RemoteDAS; //OPC Object for subscribing to Remote Device point states
private EasyDAClient CommDAV; //OPC object for subscribing to Communication point values
private Dictionary<string, string> dicAnalogStatus = new Dictionary<string, string>();
private Dictionary<string, string> dicStatusStatus = new Dictionary<string, string>();
private Dictionary<string, string> dicRemoteStatus = new Dictionary<string, string>();
private Dictionary<string, string> dicCommStatus = new Dictionary<string, string>();
private Dictionary<string, bool> dicUpdatedFlag = new Dictionary<string, bool>();
private Dictionary<string, string> dct = new Dictionary<string, string>(); //Dictionary object to hold point ID's
private Dictionary<string, string> dctStatus = new Dictionary<string, string>(); //Dictionary object to hold status values
private Dictionary<string, string> dctType = new Dictionary<string, string>(); //Dictionary object to hold point types
private Dictionary<string, string> dctADAVH = new Dictionary<string, string>(); //Dictionary object to hold analog value handles returned when subscribing to the point
private Dictionary<string, string> dctADASH = new Dictionary<string, string>(); //Dictionary object to hold analog state value handles returned when subscribing to the point
private Dictionary<string, string> dctSDAVH = new Dictionary<string, string>(); //Dictionary object to hold status value handles returned when subscribing to the point
private Dictionary<string, string> dctSDASH = new Dictionary<string, string>(); //Dictionary object to hold status state value handles returned when subscribing to the point
private Dictionary<string, string> dctRDAVH = new Dictionary<string, string>();
private Dictionary<string, string> dctCDAVH = new Dictionary<string, string>();
public DateTime dStart;
public List<EasyDAItemSubscriptionArguments> itemSubList = new List<EasyDAItemSubscriptionArguments>();
public string theState = "";
EventLog evtLog = new EventLog();
public DataExport()
{
InitializeComponent();
try
{
areServiceMain = new AutoResetEvent(false);
CanPauseAndContinue = false;
EasyDAClient.ClientParameters.TopicProcessingIntervalTimeout = 10;
EasyDAClient.ClientParameters.TopicProcessingTotalTimeout = 500;
EasyDAClient.ClientParameters.LinkCallbackQueueSize = 300000;
EasyDAClient.ClientParameters.RequestQueueSize = 300000;
EasyDAClient.ClientParameters.ResponseQueueSize = 300000;
EasyDAClient.ClientParameters.ClientReconnectDelay = 10000;
EasyDAClient.ClientParameters.TopicRetrialPeriod = 1000;
EasyDAClient.TopicParameters.ExactManualGroupMatch = true;
EasyDAClient.TopicParameters.FastestAutomaticUpdateRate = 10;
EasyDAClient.TopicParameters.SlowestAutomaticUpdateRate = 10000;
EasyDAClient.TopicParameters.TopicRetrialDelay = 10000;
EasyDAClient.EngineParameters.ClientLruSize = 200;
EasyDAClient.EngineParameters.AutoAdjustmentPeriod = 1000;
EasyDAClient.EngineParameters.MaxClientAge = 5000;
EasyDAClient.EngineParameters.TopicLruSize = 300000;
EasyDAClient.EngineParameters.DefaultEventQueueSize = 300000;
AnalogDAV = new EasyDAClient(); //OPC object for subscribing to Analog point values
AnalogDAS = new EasyDAClient(); //OPC Object for subscribing to Analog point states
StatusDAV = new EasyDAClient(); //OPC object for subscribing to Status point values
StatusDAS = new EasyDAClient(); //OPC Object for subscribing to Status point states
RemoteDAV = new EasyDAClient(); //OPC object for subscribing to Remote Device point values
RemoteDAS = new EasyDAClient(); //OPC Object for subscribing to Remote Device point states
CommDAV = new EasyDAClient(); //OPC object for subscribing to Communication point values
}
catch (Exception ex)
{
EventLog.WriteEntry("Application", ex.ToString(), EventLogEntryType.Error);
}
}
#region [Service Events]
protected override void OnStart(string[] args)
{
try
{
evtLog.Source = "DES";
WriteLogEntry("Starting CygNet Data Export Service");
ServiceThread = new Thread(this.ServiceMain);
ServiceThread.Name = "ServiceMain";
ServiceThread.Start();
Thread.Sleep(1000);
}
catch (Exception ex)
{
WriteLogEntry("Service Start Error: " + ex.Message);
}
}
protected override void OnStop()
{
try
{
evtLog.WriteEntry("CygNet Data Export Stopping", EventLogEntryType.Information);
intStopRetry = 1;
intServiceState = ServiceState.isStopping;
areServiceMain.Set(); //Set the signal on the event incase the service is paused before stopping.
//AnalogDAV.Dispose();
//AnalogDAS.Dispose();
//StatusDAV.Dispose();
//StatusDAS.Dispose();
//RemoteDAV.Dispose();
//RemoteDAS.Dispose();
//CommDAV.Dispose();
WriteLogEntry("CygNet Data Export Unsubscribing from all Analog items.");
evtLog.WriteEntry("CygNet Data Export Unsubscribing from all Analog items.", EventLogEntryType.Information);
AnalogDAV.UnsubscribeAllItems();
WriteLogEntry("CygNet Data Export Unsubscribing from all Analog;STATUS items.");
evtLog.WriteEntry("CygNet Data Export Unsubscribing from all Analog;STATUS items.", EventLogEntryType.Information);
AnalogDAS.UnsubscribeAllItems();
WriteLogEntry("CygNet Data Export Unsubscribing from all Status items.");
evtLog.WriteEntry("CygNet Data Export Unsubscribing from all Status items.", EventLogEntryType.Information);
StatusDAV.UnsubscribeAllItems();
WriteLogEntry("CygNet Data Export Unsubscribing from all Status;STATUS items.");
evtLog.WriteEntry("CygNet Data Export Unsubscribing from all Status;STATUS items.", EventLogEntryType.Information);
StatusDAS.UnsubscribeAllItems();
WriteLogEntry("CygNet Data Export Unsubscribing from all Remote items.");
evtLog.WriteEntry("CygNet Data Export Unsubscribing from all Remote items.", EventLogEntryType.Information);
RemoteDAV.UnsubscribeAllItems();
WriteLogEntry("CygNet Data Export Unsubscribing from all Comm items.");
evtLog.WriteEntry("CygNet Data Export Unsubscribing from all Comm items.", EventLogEntryType.Information);
CommDAV.UnsubscribeAllItems();
while (intStopRetry < 91)
{
WriteLogEntry("CygNet Data Export Stopping: " + intStopRetry.ToString());
evtLog.WriteEntry("CygNet Data Export Stopping: " + intStopRetry.ToString(), EventLogEntryType.Information);
if (intServiceState == ServiceState.isStopped)
{
WriteLogEntry("Service is stopped");
break;
}
else
{
RequestAdditionalTime(3000);
Thread.Sleep(1000);
intStopRetry = intStopRetry + 1;
}
}
if (intServiceState != ServiceState.isStopped)
{
WriteLogEntry("CygNet Data Export Aborting");
ServiceThread.Abort();
}
}
catch (Exception ex)
{
WriteLogEntry("CygNet Data Export Service stop error: " + ex.Message);
}
finally
{
//StopTimer.Dispose();
}
}
#endregion
#region [Timer Functions]
private void SubscriptionTimerEvent(object obj, ElapsedEventArgs e)
{
//This timer calls the subscribe subroutine to subscribe to new points inserted in the database
//WriteLogEntry("Attempting to subscribe to new items...");
try
{
Console.WriteLine(DateTime.Now.ToString() + ": SubscriptionTimerEvent fired.");
WriteLogEntry("SubscriptionTimerEvent fired.");
Subscribe(true, false, "ANALOG");
Subscribe(true, false, "STATUS");
Subscribe(true, false, "REMOTE");
Subscribe(true, false, "COMM");
}
catch (Exception ex)
{
WriteLogEntry("EXCEPTION: SubscriptionTimerEvent: " + ex.Message);
}
}
private void RemoveSubTimerEvent(object obj, ElapsedEventArgs e)
{
//This timer attempts to remove points from subscription if they are marked as deleted.
WriteLogEntry("RemoveSubTimerEvent fired.");
try
{
RemoveSubscriptions("ANALOG");
RemoveSubscriptions("STATUS");
RemoveSubscriptions("REMOTE");
RemoveSubscriptions("COMM");
}
catch (Exception ex)
{
WriteLogEntry("EXCEPTION: RemoveSubTimerEvent: " + ex.Message);
}
}
#endregion
#region [Service Main Process]
public void ServiceMain()
{
string strServerName = Environment.MachineName; //Hold variable for processing changes in the app.config file.
ConnectionOptions connOptions = new ConnectionOptions();
ManagementScope mgntScope = new ManagementScope();
ManagementPath mgntPath = new ManagementPath();
try
{
AnalogDAV.ClientMode.AllowAsynchronousMethod = true;
AnalogDAV.ClientMode.DesiredMethod = DAReadWriteMethod.Asynchronous;
AnalogDAV.HoldPeriods.ItemDetach = 0;
AnalogDAV.HoldPeriods.ServerDetach = 30000;
AnalogDAV.HoldPeriods.TopicRead = 120000;
AnalogDAV.UpdateRates.ReadAutomatic = 10;
AnalogDAV.Timeouts.ReadItem = 20000;
AnalogDAV.Timeouts.GetProperty = 10000;
//AnalogDAV.SynchronizationContext = null;
AnalogDAV.Isolated = true;
AnalogDAS.ClientMode.AllowAsynchronousMethod = true;
AnalogDAS.ClientMode.DesiredMethod = DAReadWriteMethod.Asynchronous;
AnalogDAS.HoldPeriods.ItemDetach = 0;
AnalogDAS.HoldPeriods.ServerDetach = 30000;
AnalogDAS.HoldPeriods.TopicRead = 120000;
AnalogDAS.UpdateRates.ReadAutomatic = 10;
AnalogDAS.Timeouts.ReadItem = 20000;
AnalogDAS.Timeouts.GetProperty = 10000;
//AnalogDAS.SynchronizationContext = null;
AnalogDAS.Isolated = true;
StatusDAV.ClientMode.AllowAsynchronousMethod = true;
StatusDAV.ClientMode.DesiredMethod = DAReadWriteMethod.Asynchronous;
StatusDAV.HoldPeriods.ItemDetach = 0;
StatusDAV.HoldPeriods.ServerDetach = 30000;
StatusDAV.HoldPeriods.TopicRead = 120000;
StatusDAV.UpdateRates.ReadAutomatic = 10;
StatusDAV.Timeouts.ReadItem = 20000;
StatusDAV.Timeouts.GetProperty = 10000;
//StatusDAV.SynchronizationContext = null;
StatusDAV.Isolated = true;
StatusDAS.ClientMode.AllowAsynchronousMethod = true;
StatusDAS.ClientMode.DesiredMethod = DAReadWriteMethod.Asynchronous;
StatusDAS.HoldPeriods.ItemDetach = 0;
StatusDAS.HoldPeriods.ServerDetach = 30000;
StatusDAS.HoldPeriods.TopicRead = 120000;
StatusDAS.UpdateRates.ReadAutomatic = 10;
StatusDAS.Timeouts.ReadItem = 20000;
StatusDAS.Timeouts.GetProperty = 10000;
//StatusDAS.SynchronizationContext = null;
StatusDAS.Isolated = true;
RemoteDAV.ClientMode.AllowAsynchronousMethod = true;
RemoteDAV.ClientMode.DesiredMethod = DAReadWriteMethod.Asynchronous;
RemoteDAV.HoldPeriods.ItemDetach = 0;
RemoteDAV.HoldPeriods.ServerDetach = 30000;
RemoteDAV.HoldPeriods.TopicRead = 120000;
RemoteDAV.UpdateRates.ReadAutomatic = 10;
RemoteDAV.Timeouts.ReadItem = 20000;
RemoteDAV.Timeouts.GetProperty = 10000;
//RemoteDAV.SynchronizationContext = null;
RemoteDAV.Isolated = true;
RemoteDAS.ClientMode.AllowAsynchronousMethod = true;
RemoteDAS.ClientMode.DesiredMethod = DAReadWriteMethod.Asynchronous;
RemoteDAS.HoldPeriods.ItemDetach = 0;
RemoteDAS.HoldPeriods.ServerDetach = 30000;
RemoteDAS.HoldPeriods.TopicRead = 120000;
RemoteDAS.UpdateRates.ReadAutomatic = 10;
RemoteDAS.Timeouts.ReadItem = 20000;
RemoteDAS.Timeouts.GetProperty = 10000;
//RemoteDAS.SynchronizationContext = null;
RemoteDAS.Isolated = true;
CommDAV.ClientMode.AllowAsynchronousMethod = true;
CommDAV.ClientMode.DesiredMethod = DAReadWriteMethod.Asynchronous;
CommDAV.HoldPeriods.ItemDetach = 0;
CommDAV.HoldPeriods.ServerDetach = 30000;
CommDAV.HoldPeriods.TopicRead = 120000;
CommDAV.UpdateRates.ReadAutomatic = 10;
CommDAV.Timeouts.ReadItem = 20000;
CommDAV.Timeouts.GetProperty = 10000;
//CommDAV.SynchronizationContext = null;
CommDAV.Isolated = true;
intServiceState = ServiceState.isRunning;
evtLog.Source = "DES";
//Initialize the Subscription Timer.
SubscriptionTimer = new System.Timers.Timer();
SubscriptionTimer.Elapsed += new System.Timers.ElapsedEventHandler(SubscriptionTimerEvent);
SubscriptionTimer.Enabled = false;
string strSubInt = ConfigurationManager.AppSettings.Get("SubscriptionTimer");
string strSubEn = ConfigurationManager.AppSettings.Get("SubscriptionTimerEnable");
int SubInt = 0;
if (Information.IsNumeric(strSubInt))
SubInt = Convert.ToInt32(strSubInt);
SubscriptionTimer.Interval = (SubInt * 60000);
//Initialize the Remove Subscription Timer.
RemoveSubTimer = new System.Timers.Timer();
RemoveSubTimer.Elapsed += new System.Timers.ElapsedEventHandler(RemoveSubTimerEvent);
RemoveSubTimer.Enabled = false;
string strRemSubInt = ConfigurationManager.AppSettings.Get("UnsubscribeTimer");
string strRemSubEn = ConfigurationManager.AppSettings.Get("UnsubscribeTimerEnable");
int RemSubInt = 0;
if (Information.IsNumeric(strRemSubInt))
RemSubInt = Convert.ToInt32(strRemSubInt);
RemoveSubTimer.Interval = (RemSubInt * 60000);
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
Subscribe(false, false, "ANALOG");
});
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
Subscribe(false, false, "STATUS");
});
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
Subscribe(false, false, "REMOTE");
});
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
Subscribe(false, false, "COMM");
}).Wait();
while (true)
{
try
{
if (!SubscriptionTimer.Enabled & strSubEn == "true")
{
WriteLogEntry("Subscription Timer Started...");
SubscriptionTimer.Enabled = true;
SubscriptionTimer.Start();
}
if (!RemoveSubTimer.Enabled & strRemSubEn == "true")
{
WriteLogEntry("Remove Subscription Timer Started...");
RemoveSubTimer.Enabled = true;
RemoveSubTimer.Start();
}
if (intServiceState == ServiceState.isStopping)
intServiceState = ServiceState.isStopped;
if (intServiceState == ServiceState.isStopped) //If the service is marked as stopped exit the while(true) loop.
break;
ConfigurationManager.RefreshSection("appSettings");
}
catch (Exception ex)
{
evtLog.WriteEntry("Failed (while(true)): " + ex.Message, EventLogEntryType.Error);
}
finally
{
if (bolDebug)
Console.WriteLine("Ping " + System.DateTime.Now.ToString());
areServiceMain.WaitOne(20000);
}
} //while (true)
WriteLogEntry("Exited while loop");
areServiceMain.Set();
}
catch (Exception ex)
{
evtLog.WriteEntry("Data Export failed (Main Loop): " + ex.Message, EventLogEntryType.Error);
}
finally
{
System.Environment.Exit(0);
}
}
#endregion
#region [Point Subscriptions]
public void Subscribe(bool onTimer, bool isPointState, string strType)
{
//This routine subscribes to points by getting the records in 1 of 4 tables. It is called on startup and when subscribing to new points
dStart = DateTime.Now;
string strTag = "";
string strSQL = "";
string strSiteService = ConfigurationManager.AppSettings.Get("SiteService");
string strFacility = "";
string strUDC = "";
string strPointId = "";
string strPointType = "";
int Result = 0;
int ResultStatus = 0;
int i = 0;
string strCommStatusUDC = ConfigurationManager.AppSettings.Get("CommUDC.Status");
string strCommStateUDC = ConfigurationManager.AppSettings.Get("CommUDC.State");
string strRemoteStateUDC = ConfigurationManager.AppSettings.Get("RemoteUDC.State");
try
{
//Identify the proper SQL statement to use
switch (strType)
{
case "ANALOG":
if (onTimer)
{
strSQL = "SELECT * from [TABLE_ANA] WHERE [SubFlag] = 'Add'";
}
else
{
strSQL = "SELECT * from [TABLE_ANA] WHERE [SubFlag]='Yes'";
}
break;
case "STATUS":
if (onTimer)
{
strSQL = "SELECT * from [TABLE_ST] WHERE [SubFlag] = 'Add'";
}
else
{
strSQL = "SELECT * from [TABLE_ST] WHERE [SubFlag]='Yes'";
}
break;
case "COMM":
if (onTimer)
{
strSQL = "SELECT * from [TABLE_COMM] WHERE [SubFlag] = 'Add'";
}
else
{
strSQL = "SELECT * from [TABLE_COMM] WHERE [SubFlag]='Yes'";
}
break;
case "REMOTE":
if (onTimer)
{
strSQL = "SELECT * from [TABLE_RMT] WHERE [SubFlag] = 'Add'";
}
else
{
strSQL = "SELECT * from [TABLE_RMT] WHERE [SubFlag]='Yes'";
}
break;
}
SqlDataAdapter AnalogDataAdapter = new SqlDataAdapter(strSQL, strConnString);
DataSet AnalogDataSet = new DataSet("Analog");
AnalogDataAdapter.Fill(AnalogDataSet);
if (AnalogDataSet.Tables[0].Rows.Count > 0)
{
foreach (DataRow dRow in AnalogDataSet.Tables[0].Rows)
{
if (strType == "ANALOG" | strType == "STATUS")
{
strSiteService = dRow["SiteService"].ToString();
strFacility = dRow["Facility"].ToString();
strUDC = dRow["PointCode"].ToString();
}
else
{
//Added a dummy UDC to get past the next if block
strUDC = "XXX";
}
if (strUDC != "") //The process will error out if the UDC is blank
{
switch (strType)
{
case "ANALOG":
strPointType = dRow["PointType"].ToString();
strPointId = dRow["ANA_ID"].ToString();
strTag = String.Format("{0}::{1}.{2}", strSiteService, strFacility, strUDC);
if (dct.ContainsKey(strTag))
dct[strTag] = strPointId;
else
dct.Add(strTag, strPointId);
if (dctType.ContainsKey(strTag + ";TYPE"))
dctType[strTag + ";TYPE"] = strPointType;
else
dctType.Add(strTag + ";TYPE", strPointType);
Result = AnalogDAV.SubscribeItem("", "CygNet.OPCServer.1", strTag, 500, new System.EventHandler<EasyDAItemChangedEventArgs>(AnalogItemChanged));
ResultStatus = AnalogDAS.SubscribeItem("", "CygNet.OPCServer.1", strTag + ";STATUS", 1000, new System.EventHandler<EasyDAItemChangedEventArgs>(AnalogItemStatusChanged));
//Add the resulting index to the dictionary so we can unsubscribe later if needed
if (dctADAVH.ContainsKey(strTag))
dctADAVH[strTag] = Result.ToString();
else
dctADAVH.Add(strTag, Result.ToString());
if (dctADASH.ContainsKey(strTag))
dctADASH[strTag] = ResultStatus.ToString();
else
dctADASH.Add(strTag, ResultStatus.ToString());
if (onTimer)
//Mark the new records so we don't subscribe to them again
UpdateSubscriptionFlag(strType, strPointId, "Yes");
break;
case "STATUS":
strPointType = dRow["DATATYPE"].ToString();
strPointId = dRow["Status_id"].ToString();
strTag = String.Format("{0}::{1}.{2}", strSiteService, strFacility, strUDC);
if (dct.ContainsKey(strTag))
dct[strTag] = strPointId;
else
dct.Add(strTag, strPointId);
if (dctType.ContainsKey(strTag + ";TYPE"))
dctType[strTag + ";TYPE"] = strPointType;
else
dctType.Add(strTag + ";TYPE", strPointType);
Result = StatusDAV.SubscribeItem("", "CygNet.OPCServer.1", strTag, 1000, new System.EventHandler<EasyDAItemChangedEventArgs>(StatusItemChanged));
ResultStatus = StatusDAS.SubscribeItem("", "CygNet.OPCServer.1", strTag + ";STATUS", 1000, new System.EventHandler<EasyDAItemChangedEventArgs>(StatusItemStatusChanged));
//Add the resulting index to the dictionary so we can unsubscribe later if needed
if (dctSDAVH.ContainsKey(strTag))
dctSDAVH[strTag] = Result.ToString();
else
dctSDAVH.Add(strTag, Result.ToString());
if (dctSDASH.ContainsKey(strTag))
dctSDASH[strTag] = ResultStatus.ToString();
else
dctSDASH.Add(strTag, ResultStatus.ToString());
if (onTimer)
//Mark the new records so we don't subscribe to them again
UpdateSubscriptionFlag(strType, strPointId, "Yes");
break;
case "COMM":
strFacility = dRow["NAME"].ToString();
//Add the Comm State tag
strTag = String.Format("{0}::{1}.{2}", strSiteService, strFacility, strCommStateUDC);
Result = CommDAV.SubscribeItem("", "CygNet.OPCServer.1", strTag, 1000, new System.EventHandler<EasyDAItemChangedEventArgs>(CommItemChanged));
//Add the resulting index to the dictionary so we can unsubscribe later if needed
if (dctCDAVH.ContainsKey(strTag))
dctCDAVH[strTag] = Result.ToString();
else
dctCDAVH.Add(strTag, Result.ToString());
i++;
//Add the Comm Status tag
strTag = String.Format("{0}::{1}.{2}", strSiteService, strFacility, strCommStatusUDC);
Result = CommDAV.SubscribeItem("", "CygNet.OPCServer.1", strTag, 1000, new System.EventHandler<EasyDAItemChangedEventArgs>(CommItemChanged));
//Add the resulting index to the dictionary so we can unsubscribe later if needed
if (dctCDAVH.ContainsKey(strTag))
dctCDAVH[strTag] = Result.ToString();
else
dctCDAVH.Add(strTag, Result.ToString());
if (onTimer)
//Mark the new records so we don't subscribe to them again
UpdateSubscriptionFlag(strType, strFacility, "Yes");
break;
case "REMOTE":
strFacility = dRow["NAME"].ToString();
//Add the Remote State tag
strTag = String.Format("{0}::{1}.{2}", strSiteService, strFacility, strRemoteStateUDC);
Result = RemoteDAV.SubscribeItem("", "CygNet.OPCServer.1", strTag, 1000, new System.EventHandler<EasyDAItemChangedEventArgs>(RemoteItemChanged));
//Add the resulting index to the dictionary so we can unsubscribe later if needed
if (dctRDAVH.ContainsKey(strTag))
dctRDAVH[strTag] = Result.ToString();
else
dctRDAVH.Add(strTag, Result.ToString());
if (onTimer)
//Mark the new records so we don't subscribe to them again
UpdateSubscriptionFlag(strType, strFacility, "Yes");
break;
}
i++;
}
}
//AnalogDAV.SubscribeMultipleItems(itemSubList.ToArray());
if (!onTimer & i > 0) //Log the first run. After that, only log if new points are added.
{
WriteLogEntry("Added " + i.ToString() + " " + strType + " points to monitor in " + DateAndTime.DateDiff(DateInterval.Second, dStart, DateTime.Now).ToString() + " seconds");
evtLog.WriteEntry("Added " + i.ToString() + " " + strType + " points to monitor in " + DateAndTime.DateDiff(DateInterval.Second, dStart, DateTime.Now).ToString() + " seconds");
}
}
}
catch(Exception ex)
{
WriteLogEntry("EXCEPTION: SubcribeFromTable: " + ex.ToString() + ": " + strTag);
}
}
#endregion
#region[Event Handlers]
private void AnalogItemChanged(object sender, EasyDAItemChangedEventArgs e)
{
//This handles the Analog point value change
string strTag = "";
string strStatusValue = "";
string strIsManualInput = "";
try
{
DAVtq analogVtq = e.Vtq;
strTag = e.ItemDescriptor.ItemId;
if (e.Exception != null)
{
Console.WriteLine("Analog Error: " + e.ErrorMessage + " " + e.Exception.Source + " " + e.Exception.ToString());
WriteLogEntry("Analog Error: " + e.ErrorMessage + " " + e.Exception.Source + " " + e.Exception.ToString());
}
else
{
if (analogVtq.HasValue()) //Check to see if the object has a valid value. Otherwise several unnecessary errors are thrown
{
strStatusValue = AnalogDAV.GetPropertyValueString("", "CygNet.OPCServer.1", e.ItemDescriptor.ItemId, 5008);
if (dicAnalogStatus.ContainsKey(strTag))
dicAnalogStatus[strTag] = strStatusValue.ToString().Trim();
else
dicAnalogStatus.Add(strTag, strStatusValue.ToString().Trim());
UpdateAnalog(e.ItemDescriptor.ItemId, analogVtq.Value.ToString().Trim(), analogVtq.Timestamp.ToString(), strStatusValue, strIsManualInput);//updates the database with values
}
else
{
Console.WriteLine("Analog Item has no value. ItemId: " + e.ItemDescriptor.ItemId);
}
}
}
catch (Exception ex)
{
String errorStr = "";
errorStr = ex.GetBaseException().Message + "\n" + ex.Source + "\n" + ex.StackTrace + "\n" + ex.InnerException.StackTrace;
WriteLogEntry("EXCEPTION: Failed on AnalogItemChanged: " + errorStr + " ItemId: " + e.ItemDescriptor.ItemId);
}
}
private void AnalogItemStatusChanged(object sender, EasyDAItemChangedEventArgs e)
{
//This handles the Analog point status value change
string strTag = "";
int intStatus = 0;
int intPrevStatus = -99;
string strCygVal = "";
try
{
DAVtq analogVtq = e.Vtq;
strTag = e.ItemDescriptor.ItemId;
if (e.Exception != null)
{
Console.WriteLine("Analog Status Error: " + e.ErrorMessage + " " + e.Exception.Source + " " + e.Exception.ToString());
WriteLogEntry("Analog Status Error: " + e.ErrorMessage + " " + e.Exception.Source + " " + e.Exception.ToString());
}
else
{
//{
if (analogVtq.HasValue()) //Check to see if the object has a valid value. Otherwise several unnecessary errors are thrown
{
strCygVal = AnalogDAV.GetPropertyValueString("", "CygNet.OPCServer.1", e.ItemDescriptor.ItemId.Replace(";STATUS", ""), 2);
intStatus = Convert.ToInt32(analogVtq.Value.ToString().Trim());
if (dicAnalogStatus.ContainsKey(strTag))
{
intPrevStatus = Convert.ToInt32(dicAnalogStatus[strTag]);
dicAnalogStatus[strTag] = analogVtq.Value.ToString().Trim();
}
else
dicAnalogStatus.Add(strTag, analogVtq.Value.ToString().Trim());
UpdateItemStatus(e.ItemDescriptor.ItemId, analogVtq.Value.ToString().Trim(), analogVtq.Timestamp.ToString(), "ANALOG", strCygVal);//updates the database with values
}
else
{
Console.WriteLine("Analog Item Status has no value. ItemId: " + e.ItemDescriptor.ItemId);
}
}
}
catch (Exception ex)
{
String errorStr = "";
errorStr = ex.GetBaseException().Message + "\n" + ex.Source + "\n" + ex.StackTrace;
WriteLogEntry("EXCEPTION: Failed on AnalogItemStatusChanged: " + errorStr + " ItemId: " + e.ItemDescriptor.ItemId);
}
}
private void StatusItemChanged(object sender, EasyDAItemChangedEventArgs e)
{
//This handles the status point value change
string strStatusValue = "";
string strTag = "";
string strIsManualInput = "";
try
{
DAVtq statusVtq = e.Vtq;
strTag = e.ItemDescriptor.ItemId;
if (e.Exception != null)
{
Console.WriteLine("Status Error: " + e.ErrorMessage + " " + e.Exception.Source + " " + e.Exception.ToString());
}
else
{
if (statusVtq.HasValue()) //Check to see if the object has a valid value. Otherwise several unnecessary errors are thrown
{
strStatusValue = StatusDAV.GetPropertyValueString("", "CygNet.OPCServer.1", e.ItemDescriptor.ItemId, 5008);
if (dicStatusStatus.ContainsKey(strTag))
dicStatusStatus[strTag] = strStatusValue.ToString().Trim();
else
dicStatusStatus.Add(strTag, strStatusValue.ToString().Trim());
UpdateStatus(e.ItemDescriptor.ItemId, statusVtq.Value.ToString().Trim(), statusVtq.Timestamp.ToString(), strStatusValue, strIsManualInput);//updates the database with values
}
else
{
Console.WriteLine("Status Item has no value. ItemId: " + e.ItemDescriptor.ItemId);
}
}
}
catch (Exception ex)
{
String errorStr = "";
errorStr = ex.GetBaseException().Message + "\n" + ex.Source + "\n" + ex.StackTrace;
WriteLogEntry("EXCEPTION: Failed on StatusItemChanged: " + errorStr + ": " + e.ItemDescriptor.ItemId);
}
}
private void StatusItemStatusChanged(object sender, EasyDAItemChangedEventArgs e)
{
//This handles the Status point status value change
string strTag = "";
int intStatus = 0;
int intPrevStatus = 0;
string strCygVal = "";
try
{
DAVtq statusVtq = e.Vtq;
strTag = e.ItemDescriptor.ItemId;
if (e.Exception != null)
{
Console.WriteLine("Status Status Error: " + e.ErrorMessage + " " + e.Exception.Source + " " + e.Exception.ToString());
}
else
{
if (statusVtq.HasValue()) //Check to see if the object has a valid value. Otherwise several unnecessary errors are thrown
{
strCygVal = StatusDAV.GetPropertyValueString("", "CygNet.OPCServer.1", e.ItemDescriptor.ItemId.Replace(";STATUS", ""), 2);
intStatus = Convert.ToInt32(statusVtq.Value.ToString().Trim());
if (dicStatusStatus.ContainsKey(strTag))
{
intPrevStatus = Convert.ToInt32(dicStatusStatus[strTag]);
dicStatusStatus[strTag] = statusVtq.Value.ToString().Trim();
}
else
dicStatusStatus.Add(strTag, statusVtq.Value.ToString().Trim());
UpdateItemStatus(e.ItemDescriptor.ItemId, statusVtq.Value.ToString().Trim(), statusVtq.Timestamp.ToString(), "STATUS", strCygVal);//updates the database with values
}
else
{
Console.WriteLine("Status Item Status has no value. ItemId: " + e.ItemDescriptor.ItemId);
}
}
}
catch (Exception ex)
{
String errorStr = "";
errorStr = ex.GetBaseException().Message + "\n" + ex.Source + "\n" + ex.StackTrace;
WriteLogEntry("EXCEPTION: Failed on StatusItemStatusChanged: " + errorStr + " ItemId: " + e.ItemDescriptor.ItemId);
}
}
private void RemoteItemChanged(object sender, EasyDAItemChangedEventArgs e)
{
string strStatusValue = "";
string strTag = "";
string strIsManualInput = "";
try
{
DAVtq remoteVtq = e.Vtq;
strTag = e.ItemDescriptor.ItemId;
if (e.Exception != null)
{
Console.WriteLine("Remote Error: " + e.ErrorMessage + " " + e.Exception.Source + " " + e.Exception.ToString());
WriteLogEntry("Remote Error: " + e.ErrorMessage + " " + e.Exception.Source + " " + e.Exception.ToString());
}
else
{
if (remoteVtq.HasValue())
{
RemoteDAS.UpdateRates.ReadAutomatic = 10;
strStatusValue = RemoteDAS.GetPropertyValueString("", "CygNet.OPCServer.1", e.ItemDescriptor.ItemId, 5008);
UpdateRemote(e.ItemDescriptor.ItemId, remoteVtq.Value.ToString().Trim(), remoteVtq.Timestamp.ToString(), strStatusValue, strIsManualInput);//updates the database with values
}
else
{
Console.WriteLine("Remote Item has no value. ItemId: " + e.ItemDescriptor.ItemId);
}
}
}
catch (Exception ex)
{
String errorStr = "";
errorStr = ex.GetBaseException().Message + "\n" + ex.Source + "\n" + ex.StackTrace;
WriteLogEntry("EXCEPTION: Failed on RemoteItemChanged: " + errorStr + ": " + e.ItemDescriptor.ItemId);
}
}
private void CommItemChanged(object sender, EasyDAItemChangedEventArgs e)
{
string strStatusValue = "";
string strTag = "";
string strIsManualInput = "";
try
{
DAVtq commVtq = e.Vtq;
strTag = e.ItemDescriptor.ItemId;
if (e.Exception != null)
{
Console.WriteLine("Comm Error: " + e.ErrorMessage + " " + e.Exception.Source + " " + e.Exception.ToString());
WriteLogEntry("Comm Error: " + e.ErrorMessage + " " + e.Exception.Source + " " + e.Exception.ToString());
}
else
{
if (commVtq.HasValue())
{
UpdateComm(e.ItemDescriptor.ItemId, commVtq.Value.ToString().Trim(), commVtq.Timestamp.ToString(), strStatusValue, strIsManualInput);//updates the database with values
}
else
{
Console.WriteLine("Comm Item has no value. ItemId: " + e.ItemDescriptor.ItemId);
}
}
}
catch (Exception ex)
{
String errorStr = "";
errorStr = ex.GetBaseException().Message + "\n" + ex.Source + "\n" + ex.StackTrace;
WriteLogEntry("EXCEPTION: Failed on CommItemChanged: " + errorStr + ": " + e.ItemDescriptor.ItemId);
}
}
#endregion
#region[Database Updates]
private void UpdateSubscriptionFlag(string strType, string strPointId, string flagStr)
{
try
{
string strSQL = "";
int retries = 0;
switch (strType)
{
case "ANALOG":
strSQL = String.Format("UPDATE [TABLE_ANA] SET [SubFlag] = '" + flagStr + "' WHERE [name] = {0}", strPointId);
break;
case "STATUS":
strSQL = String.Format("UPDATE [TABLE_ST] SET [SubFlag] = '" + flagStr + "' WHERE [name] = {0}", strPointId);
break;
case "COMM":
strSQL = String.Format("UPDATE [TABLE_COMM] SET [SubFlag] = '" + flagStr + "' WHERE [name] = {0}", strPointId);
break;
case "REMOTE":
strSQL = String.Format("UPDATE [TABLE_RMT] SET [SubFlag] = '" + flagStr + "' WHERE [name] = {0}", strPointId);
break;
}
using (SqlConnection sqlConn = new SqlConnection(strConnString))
{
while (true)
{
try
{
if (sqlConn.State == ConnectionState.Closed)
sqlConn.Open();
else if (sqlConn.State == ConnectionState.Broken)
{
sqlConn.Close();
sqlConn.Open();
}
SqlCommand command = new SqlCommand(strSQL, sqlConn);
command.CommandTimeout = 120;
command.ExecuteNonQuery();
if (sqlConn.State != ConnectionState.Closed)
sqlConn.Close();
break;
}
catch (Exception)
{
retries++;
if (retries > 2) throw;
}
}
}
//WriteLogEntry(strSQL);
WriteLogEntry("UpdateSubscriptionFlag: Flagged " + strPointId + " of type " + strType + " as: " + flagStr);
}
catch(Exception ex)
{
WriteLogEntry("EXCEPTION: UpdateSubscriptionFlag: " + ex.Message);
}
}
private void RemoveSubscriptions(string strType)
{
//This routine attempts to remove points from the subscription
//*** Note: When this was originally tested, it appeared that if a point was unsubscribed, then none of the other subscribed points would continue updating
try
{
string strSQL = "";
string strSiteService = ConfigurationManager.AppSettings.Get("SiteService");
string strFacility = "";
string strUDC = "";
string strTag = "";
string strPointId = "";
string strTagStatus = "";
string strResult = "";
string strResultStatus = "";
int intResult = 0;
int intResultStatus = 0;
int x = 0;
string strCommStatusUDC = ConfigurationManager.AppSettings.Get("CommUDC.Status");
string strCommStateUDC = ConfigurationManager.AppSettings.Get("CommUDC.State");
string strRemoteStateUDC = ConfigurationManager.AppSettings.Get("RemoteUDC.State");
switch (strType)
{
case "ANALOG":
strSQL = "SELECT * from [TABLE_ANA] WHERE [SubFlag] = 'Rem'";
break;
case "STATUS":
strSQL = "SELECT * from [TABLE_ST] WHERE [SubFlag] = 'Rem'";
break;
case "COMM":
strSQL = "SELECT * from [TABLE_COMM] WHERE [SubFlag] = 'Rem'";
break;
case "REMOTE":
strSQL = "SELECT * from [TABLE_RMT] WHERE [SubFlag] = 'Rem'";
break;
}
SqlDataAdapter DA = new SqlDataAdapter(strSQL, strConnString);
DataSet DS = new DataSet("Analog");
DA.Fill(DS);
foreach (DataRow dRow in DS.Tables[0].Rows)
{
switch (strType)
{
case "ANALOG":
strSiteService = dRow["SiteService"].ToString();
strFacility = dRow["Facility"].ToString();
strUDC = dRow["PointCode"].ToString();
strPointId = dRow["ANA_ID"].ToString();
strTag = String.Format("{0}::{1}.{2}", strSiteService, strFacility, strUDC);
if (dctADAVH.TryGetValue(strTag, out strResult) && dctADASH.TryGetValue(strTag, out strResultStatus))
{
if (Information.IsNumeric(strResult) && Information.IsNumeric(strResultStatus))
{
intResult = Convert.ToInt32(strResult);
intResultStatus = Convert.ToInt32(strResultStatus);
AnalogDAV.UnsubscribeItem(intResult);
AnalogDAS.UnsubscribeItem(intResultStatus);
dctADAVH.Remove(strTag);
dctADASH.Remove(strTag);
UpdateSubscriptionFlag(strType, strPointId, "No");
WriteLogEntry("RemoveSubscriptions: Removed Analog subscription for " + strTag);
}
}
else
{
WriteLogEntry("RemoveSubscriptions: Could not remove Analog " + strTag + ", tag not in subscription dictionary.");
}
break;
case "STATUS":
strSiteService = dRow["SiteService"].ToString();
strFacility = dRow["Facility"].ToString();
strUDC = dRow["PointCode"].ToString();
strPointId = dRow["Status_id"].ToString();
strTag = String.Format("{0}::{1}.{2}", strSiteService, strFacility, strUDC);
if (dctSDAVH.TryGetValue(strTag, out strResult) && dctSDASH.TryGetValue(strTag, out strResultStatus))
{
if (Information.IsNumeric(strResult) && Information.IsNumeric(strResultStatus))
{
intResult = Convert.ToInt32(strResult);
intResultStatus = Convert.ToInt32(strResultStatus);
StatusDAV.UnsubscribeItem(intResult);
StatusDAS.UnsubscribeItem(intResultStatus);
dctSDAVH.Remove(strTag);
dctSDASH.Remove(strTag);
UpdateSubscriptionFlag(strType, strPointId, "No");
WriteLogEntry("RemoveSubscriptions: Removed Status subscription for " + strTag);
}
}
else
{
WriteLogEntry("RemoveSubscriptions: Could not remove Status " + strTag + ", tag not in subscription dictionary.");
}
break;
case "COMM":
strFacility = dRow["NAME"].ToString();
strTag = String.Format("{0}::{1}.{2}", strSiteService, strFacility, strCommStateUDC);
strTagStatus = String.Format("{0}::{1}.{2}", strSiteService, strFacility, strCommStatusUDC);
if (dctCDAVH.TryGetValue(strTag, out strResult) && dctCDAVH.TryGetValue(strTagStatus, out strResultStatus))
{
if (Information.IsNumeric(strResult) && Information.IsNumeric(strResultStatus))
{
intResult = Convert.ToInt32(strResult);
intResultStatus = Convert.ToInt32(strResultStatus);
CommDAV.UnsubscribeItem(intResult);
CommDAV.UnsubscribeItem(intResultStatus);
dctCDAVH.Remove(strTag);
dctCDAVH.Remove(strTagStatus);
UpdateSubscriptionFlag(strType, strFacility, "No");
WriteLogEntry("RemoveSubscriptions: Removed Comm subscription for " + strTag + " and " + strTagStatus);
}
}
else
{
WriteLogEntry("RemoveSubscriptions: Could not remove Comm " + strTag + ", tag not in subscription dictionary.");
}
break;
case "REMOTE":
strFacility = dRow["NAME"].ToString();
strTag = String.Format("{0}::{1}.{2}", strSiteService, strFacility, strRemoteStateUDC);
if (dctRDAVH.TryGetValue(strTag, out strResult))
{
if (Information.IsNumeric(strResult))
{
intResult = Convert.ToInt32(strResult);
RemoteDAV.UnsubscribeItem(intResult);
dctRDAVH.Remove(strTag);
UpdateSubscriptionFlag(strType, strFacility, "No");
WriteLogEntry("RemoveSubscriptions: Removed Remote subscription for " + strTag);
}
}
else
{
WriteLogEntry("RemoveSubscriptions: Could not remove Remote " + strTag + ", tag not in 'dctRDAVH' subscription dictionary.");
}
break;
}
x++;
}
WriteLogEntry("RemoveSubscriptions: Removed " + x + " " + strType + " points from subscription");
}
catch (Exception ex)
{
WriteLogEntry("EXCEPTION: RemoveSubscriptions: " + ex.Message);
}
}
private void UpdateAnalog(string Tag, string Value, string Timestamp, string cygStatus, string cygIsManual)
{
//This routine updates the Analog table with current values and status
}
public void UpdateItemStatus(string Tag, string Value, string Timestamp, string recordType, string cygValue)
{
//This routine updates the table with current status
}
private void UpdateStatus(string Tag, string Value, string Timestamp, string cygStatus, string cygIsManual)
{
//This routine updates the Status table with values and status for Enumeration and Digital points
}
private void UpdateRemote(string Tag, string Value, string Timestamp, string cygStatus, string cygIsManual)
{
//This routine updates the current state for the Remote devices
}
private void UpdateComm(string strTag, string strValue, string strTimestamp, string cygStatus, string cygIsManual)
{
//This routine updates the Comm table with current state values
}
#endregion
private void InitializeComponent()
{
//
// DataExport
//
this.ServiceName = "DES";
}
}
}
Please Log in or Create an account to join the conversation.
I can see some suspicious things, but at the same time, none of them seems to be directly causing the problem:
- There should be no need to convert the subscription handles between integers and strings. You have not included a declaration of dctADAVH, but why don't you make it a dictionary where the value type is 'int'?
- Is what you have posted actually two separate pieces of code? Because, it was one piece, it would be subscribing the item and then immediately unsubscribing it?
- Just a note: If the pieces of code you have listed are executed in a loop, they will be *very* inefficient, with larger amounts of items. SubscribeMultipleItems and UnsubscribeMultipleItems should be used in such cases.
In my test, the following piece of code subscribes to 4 items, and then successfully unsubscribes from one of them, leaving the remaining 3:
public static void Main()
{
using (var easyDAClient = new EasyDAClient())
{
easyDAClient.ItemChanged += easyDAClient_ItemChanged;
int[] handleArray = easyDAClient.SubscribeMultipleItems(
new[] {
new DAItemGroupArguments("", "OPCLabs.KitServer.2", "Simulation.Random", 1000, null),
new DAItemGroupArguments("", "OPCLabs.KitServer.2", "Trends.Ramp (1 min)", 1000, null),
new DAItemGroupArguments("", "OPCLabs.KitServer.2", "Trends.Sine (1 min)", 1000, null),
new DAItemGroupArguments("", "OPCLabs.KitServer.2", "Simulation.Register_I4", 1000, null)
});
Console.WriteLine("Processing item changed events for 30 seconds...");
Thread.Sleep(30 * 1000);
Console.WriteLine("Unsubscribing from the first item...");
easyDAClient.UnsubscribeItem(handleArray[0]);
Console.WriteLine();
Console.WriteLine("Processing item changed events for 30 seconds...");
Thread.Sleep(30 * 1000);
}
}
// Item changed event handler
static void easyDAClient_ItemChanged([NotNull] object sender, [NotNull] EasyDAItemChangedEventArgs e)
{
Console.WriteLine("{0}: {1}", e.ItemDescriptor.ItemId, e.Vtq);
}
Can you please comment on the follow-up questions and/or provide more complete, we can then go from there.
Best regards
Please Log in or Create an account to join the conversation.
Result = AnalogDAV.SubscribeItem("", "CygNet.OPCServer.1", strTag, 500, new System.EventHandler<EasyDAItemChangedEventArgs>(AnalogItemChanged));
//Add the resulting index to the dictionary so we can unsubscribe later if needed
if (dctADAVH.ContainsKey(strTag))
dctADAVH[strTag] = Result.ToString();
else
dctADAVH.Add(strTag, Result.ToString());
if (dctADAVH.TryGetValue(strTag, out strResult))
{
if (Information.IsNumeric(strResult))
{
intResult = Convert.ToInt32(strResult);
AnalogDAV.UnsubscribeItem(intResult);
dctADAVH.Remove(strTag);
WriteLogEntry("RemoveSubscriptions: Removed Analog subscription for " + strTag);
}
}
I am using OPCData5.2 and CygNet OPC Server. Any idea what is causing the items not to generate the events?
Please Log in or Create an account to join the conversation.
- Forum
- Discussions
- QuickOPC-Classic in .NET
- Reading, Writing, Subscriptions, Property Access
- UnsubscribeItem stops updates