Professional OPC
Development Tools

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. 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.

UnsubscribeItem stops updates

More
01 May 2014 11:40 #1912 by support
From: C.
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.

More
03 Apr 2014 07:17 #1811 by support
I have reviewed your code, and although it is always hard to say without actually running and debugging it, there seems to be nothing fundamentally wrong that could be causing the problem.

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.

More
02 Apr 2014 08:08 - 02 Apr 2014 09:08 #1809 by support
140000 OPC items is really a lot, so the initial subscription won't be blazingly fast.
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
Last edit: 02 Apr 2014 09:08 by support. Reason: added suggestion to do the same with UnsubscribeItem

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

More
01 Apr 2014 16:58 #1808 by atsolmon
I have total of around 140000 items being subscribed, and when the application starts the initial vlaue load of subscriptions take over 20 mins, can this be optimized further?

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

More
01 Apr 2014 16:43 #1806 by atsolmon
Ok to be more precise, my application is a Windows Service C# application. When I Unsubscribe from a single item it stops updates from the rest of the subscribed items in that client and updates resume after 6, 7 hours later automatically. Here is the exact code I have:
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.

More
01 Apr 2014 15:23 - 01 Apr 2014 15:24 #1803 by support
It looks like that a more complete code will be needed to resolve this. I have done a simple test (explained further below), and it works. There may be some problem with your code, but unfortunately only a part of it is listed.

I can see some suspicious things, but at the same time, none of them seems to be directly causing the problem:
  1. 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'?
  2. 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?
  3. 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
Last edit: 01 Apr 2014 15:24 by support.

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

More
01 Apr 2014 14:44 - 01 Apr 2014 14:54 #1802 by atsolmon
When I use UnsubscribeItem call it seems like it unsubscribes all the items in that client object:
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?
Last edit: 01 Apr 2014 14:54 by atsolmon.

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

Moderators: support
Time to create page: 0.080 seconds