Home > Back-end >  C# Program Closing and Restarting Automatically at Random Intervals
C# Program Closing and Restarting Automatically at Random Intervals

Time:10-08

I've built a C# application to interact with a Control System at work. It calculates some values and/or pulls them from a spreadsheet and then writes them to an OPC server using EasyOPCClient. When I run this application, it will run for 3-5 minutes the first time, close, and reopen, then run 30 seconds to 2 minutes from that point on. Each time it closes and restarts with no errors printed to the console (at least that I can see before it closes), and I don't see anything in the event logs that would make it close. Does anyone see anything I'm doing in the program that would cause that? This is my first attempt at multi-threading, so it wouldn't surprise me if it were something with that.

using System;
using System.IO;
using System.Threading;
using OpcLabs.EasyOpc.DataAccess;
using Excel = Microsoft.Office.Interop.Excel;


 /// <summary>
 /// OPC Simulator:
 /// Used to assist in training operators on how to use Honeywell Experion System
 /// Simulates PID and DEVCTL inputs and outputs; uses historical data from ParcView to simulate DACA blocks
 /// 
 /// Date: 07/14/2022
 /// Authours: 
 /// </summary>

namespace OPCTest
{
    public class Program
    {
        public static double[] genFunction(int number_of_samples, float amplitude, float frequency_in_hz) 
        {
            /* 
             * Function: Generates an array of points sampled from a noisy sine wave function.
             * 
             * IN:  number_of_samples - The number of samples to generate
             *      amplitude - Multiplier to scale the function
             *      frequency_in_hz - Frequency of the generated signal
             *      
             * OUT: An array of integers sampled from the generated sine wave.
             */

            Random rand = new Random();

            // Initialize any needed variables
            double[] points = new double[number_of_samples];
            float time;

            int samples;
      

            // Generates a noisy signal
            for(samples = 0; samples < number_of_samples; samples  )
            {
                time = samples / (frequency_in_hz);

                points[samples] = amplitude * (Math.Sin(2.0f * Math.PI * frequency_in_hz * time / 5.0f   rand.Next(0, 100))
                                              1/5 * Math.Sin(2.0f * Math.PI * frequency_in_hz * time   rand.Next(0, 100))
                                              3 * (Math.Sin(2.0f * Math.PI * frequency_in_hz * time / 100.0f   rand.Next(0, 100))));
            }

            return points;
        }

        public static void DACA(EasyDAClient client)
        {
            /* 
             * Function: Simulates DACA block I/Os using a function generator
             * 
             * IN: None
             * 
             * OUT: None
             * 
             */
            //var client = new EasyDAClient();

            string textFile = @"C:\Temp\DACAs.txt";                         // Path to file with CMs containing DACAs in it. CM name only
            float pveulo, pveuhi, pvloalm, pvhialm, pvllalm, pvhhalm;       // Initialize all of the useful PV alarms and variables
            float pv, pvlo, pvhi;   // PVs that will be used in calculations
            float shift;            // Initialize the variable that will vertically shift the signal to the correct magnitude
            float amplitude;        // Initialize the variable that will determine the amplitude of each PV signal
            double[] signal;        // Initialize an array of points that will be used to store a generated signal



            // Read a text file line by line.  
            string[] lines = File.ReadAllLines(textFile);

            while (true)
            {
                if (File.Exists(textFile))
                {
                    // Iterate through DACAs in text file   
                    foreach (string line in lines)
                    {
                        try
                        {
                            // Store the necessary variables
                            pvhi = pveuhi = float.Parse(client.ReadItemValue("", "HWHsc.OPCServer", line   ".DACA.PVEUHI").ToString());
                            pvlo = pveulo = float.Parse(client.ReadItemValue("", "HWHsc.OPCServer", line   ".DACA.PVEULO").ToString());

                            if (float.TryParse(client.ReadItem("", "HWHsc.OPCServer", line   ".DACA.PVHHALM.TP").ToString().Split(' ')[0], out pvhhalm))
                            {
                                // Console.WriteLine("PVHIHI: "   pvhhalm);
                                pvhi = pvhhalm;
                            }

                            if (float.TryParse(client.ReadItem("", "HWHsc.OPCServer", line   ".DACA.PVHIALM.TP").ToString().Split(' ')[0], out pvhialm))
                            {
                                // Console.WriteLine("PVHI: "   pvhialm);
                                pvhi = pvhialm;
                            }

                            if (float.TryParse(client.ReadItem("", "HWHsc.OPCServer", line   ".DACA.PVLLALM.TP").ToString().Split(' ')[0], out pvllalm))
                            {
                                // Console.WriteLine("PVLOLO: "   pvllalm);
                                pvlo = pvllalm;
                            }

                            if (float.TryParse(client.ReadItem("", "HWHsc.OPCServer", line   ".DACA.PVLOALM.TP").ToString().Split(' ')[0], out pvloalm))
                            {
                                // Console.WriteLine("PVLO: "   pvloalm);
                                pvlo = pvloalm;
                            }

                            // Console.WriteLine("PVHI: "   pvhi   " PVLO: "   pvlo);

                            amplitude = ((pvhi - pvlo) / 2.0f) * 0.0375f;
                            shift = ((pvhi - pvlo) * 0.8f)   pvlo;     // Shift the signal up to oscillate between the bounds of the PV

                            // Generate the signal

                            signal = genFunction(10, amplitude, 0.003f);

                            foreach (float sig in signal)
                            {
                                pv = sig   shift;
                                client.WriteItemValue("", "HWHsc.OPCServer", line   ".DACA.PV", pv);
                                // Console.WriteLine(line   ": "   pv);

                            }

                        }
                        catch(Exception e)
                        {
                            // Note the failures
                            Console.WriteLine("DACA Failed at: "   line   "\n\t"   e   "\n");
                        }
                    }
                    System.Threading.Thread.Sleep(1000);
                }
            }

        }

        public static void DEVCTLA(EasyDAClient client)
        {
            /* 
             * Function: Simulates DEVCTLA block I/Os 
             * 
             * IN: client - OPC Client that will be used to read and write to the Experion server
             * 
             * OUT: None
             */

            string textFile = @"C:\Temp\DEVCTLAs.txt";
            string gop, gpv;

            // Read a text file line by line.  
            string[] tags = File.ReadAllLines(textFile);

            while (true)
            {
                foreach (string tagName in tags)
                {
                    try
                    {
                        gop = client.ReadItemValue("", "HWHsc.OPCServer2", tagName   ".DEVCTLA.GOP").ToString();
                        gpv = client.ReadItemValue("", "HWHsc.OPCServer2", tagName   ".DEVCTLA.GPV").ToString();
                        
                        if (gpv != gop) client.WriteItemValue("", "HWHsc.OPCServer2", tagName   ".DEVCTLA.GPV", gop);
                    }
                    catch(Exception e)
                    {
                        Console.WriteLine("DEVCTLA Failed at: "   tagName   "\n\t"   e   "\n");
                    }
                }
                System.Threading.Thread.Sleep(1000);
            }
        }

        public static void PID(EasyDAClient client)
        {
            /* 
             * Function: Simulates PID block I/Os 
             * 
             * IN: client - OPC Client that will be used to read and write to the Experion server
             * 
             * OUT: None
             */

            string textFile = @"C:\Temp\PIDs.txt";      // Path to file with CMs containing PIDs in it. CM name only - I plan to make this browsable by the user
            double OP = 0;                              // Place to store OP of PID
            double pveulo = 0;                          // Place to store PVEULO from DACA
            double pveuhi = 0;                          // Place to store PVEUHI from DACA
            double PV = 0;                              // Place to store calculated PV to write to DACA
            double ctlactn = 1;                         // Place to store Control Action - 0 = Direct, 1 = Reverse

            // Read a text file line by line.  
            string[] lines = File.ReadAllLines(textFile);

            foreach (string line in lines)
            {
                try
                {
                    client.WriteItemValue("", "HWHsc.OPCServer", line   "PIDA.MODE", "AUTO");       // Put PID Loops in Automatic
                }
                catch { }
            }
            while (true)
            {
                if (File.Exists(textFile))
                {
                    // Iterate through PIDs in text file   
                    foreach (string line in lines)
                    {

                        try
                        {
                            // Store the PVEULO
                            pveulo = float.Parse(client.ReadItemValue("", "HWHsc.OPCServer", line   ".DACA.PVEULO").ToString());

                            // Store the PVEUHI
                            pveuhi = float.Parse(client.ReadItemValue("", "HWHsc.OPCServer", line   ".DACA.PVEUHI").ToString());

                            // Store the OP
                            OP = float.Parse(client.ReadItemValue("", "HWHsc.OPCServer", line   ".PIDA.OP").ToString());

                            // Store the PV
                            try
                            {
                                PV = float.Parse(client.ReadItemValue("", "HWHsc.OPCServer", line   ".PIDA.PV").ToString());
                            }
                            catch
                            {
                                PV = 0;
                            }

                            // Store the Control Action
                            try
                            {
                                ctlactn = float.Parse(client.ReadItemValue("", "HWHsc.OPCServer", line   ".PIDA.CTLACTN").ToString());
                            }

                            catch (Exception e)
                            {
                                //ctlactn2 = client.ReadItemValue("", "HWHsc.OPCServer", line   "PIDA.CTLACTN").ToString();
                                //ctlactn2 = e.ToString();
                            }


                            // If PV is 0, write the halfway point to it so the PID doesn't start in a wound up position
                            if (PV == 0)
                            {
                                // Write the halfway point of the pv range to the PV
                                client.WriteItemValue("", "HWHsc.OPCServer", line   ".DACA.PV", (pveuhi - pveulo) / 2);
                            }

                            if (ctlactn == 1)
                            {
                                // Calculate the PV based off the OP * PV Range   some noise
                                PV = (OP * 0.01 * (pveuhi - pveulo)   pveulo)   0.008 * OP;
                            }

                            else
                            {
                                // Calculate the PV based off the OP * PV Range   some noise
                                PV = ((106.9 - OP) * 0.01 * (pveuhi - pveulo)   pveulo) - 0.008 * (106.9 - OP);
                            }
                            

                            // Write the calculated PV to the DACA
                            client.WriteItemValue("", "HWHsc.OPCServer", line   ".DACA.PV", PV);

                        }
                        catch(Exception e)
                        {
                            // Note the failures
                            Console.WriteLine("PID Failed at: "   line   "\n\t"   e   "\n");
                            Thread.Sleep(300000);
                         
                        }
                    }
                    System.Threading.Thread.Sleep(1000);
                }
            }
        }

        public static void PView(EasyDAClient client)
        {
            /* 
             * Function: Simulates DACA block I/Os using historical ParcView data 
             * 
             * IN: client - OPC Client that will be used to read and write to the Experion server
             *     xLWB - The excel workbook containing the ParcView data
             * OUT: None
             */

            // Connect to the Excel spreadsheet
            Excel.Application xLApp = new Excel.Application();
            Excel.Workbook xLWB = xLApp.Workbooks.Open(@"C:\Temp\ParcView_Data_Copy_2.xlsx");
            Excel.Worksheet rawData = xLWB.Sheets[1];
            Excel.Worksheet locData = xLWB.Sheets[2];
            Excel.Range rawDataRange = rawData.UsedRange;
            Excel.Range locDataRange = locData.UsedRange;

            // Determine the size of the spreadshees
            int rawDataRow = rawDataRange.Rows.Count;
            int rawDataCol = rawDataRange.Columns.Count;
            int locDataRow = locDataRange.Rows.Count;

            string tagName;
            float[] tagValues = new float[locDataRow];
            do
            {
                // Loop through each row of data
                for (int i = 1; i <= rawDataRow; i  )
                {
                    // Console.WriteLine((rawDataRow - i)   " Remaining");
                    // Get data for tag
                    for (int j = 1; j <= locDataRow; j  )
                    {
                        tagName = locDataRange.Cells[j, 1].Value2.ToString();
                        try
                        {
                            if (rawDataRange.Cells[i, j] != null && rawDataRange.Cells[i, j].Value2 != null)
                                tagValues[j - 1] = float.Parse(rawDataRange.Cells[i, j].Value2.ToString());
                            client.WriteItemValue("", "HWHsc.OPCServer", tagName   ".DACA.PV", tagValues[j - 1]);
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine("PVIEW Failed at: "   tagName   "\n\t"   e   "\n");
                            Thread.Sleep(300000);
                        }
                    }
                    Thread.Sleep(2000);
                }
            } while (true);
        }

        static void Main(string[] args)
        {
            try
            {
                // Initialize OPC Client
                EasyDAClient client = new EasyDAClient();
                EasyDAClient client2 = new EasyDAClient();

                // Initialize threads using Threads class
                Thread PViewThread = new Thread(() => PView(client));
                Thread PIDThread = new Thread(() => PID(client2));
                //Thread DACAThread = new Thread(() => DACA(client));
                //Thread DEVCTLAThread = new Thread(() => DEVCTLA(client));

                // Start the threads
                PViewThread.Start();
                PIDThread.Start();
                //DACAThread.Start();
                //DEVCTLAThread.Start();
               
            }
            catch (Exception e)
            {
                Console.WriteLine("MAIN: \n\t"   e);
                Thread.Sleep(300000);
            }
        }
    }
}

CodePudding user response:

A C# program can launch another instance of itself with Process.Start. This code does not do that. It can be restarted by a task scheduler or if it is installed as a Windows service or by another application.

But I see that you have several Thread.Sleep in there. Those are probably responsible for the observed behavior. So, the program is not closing and restarting automatically. It is simply sleeping from time to time.

Especially when an exception occurs Thread.Sleep(300000); is called, making the program sleep for 5 minutes. If the exceptions occur randomly, this would explain it happening at random intervals.

E.g. float.Parse throws an exception when the string is not a valid float. Consider using TryParse instead. Example

string s = client.ReadItemValue("", "HWHsc.OPCServer", line   ".DACA.PVEUHI").ToString();
if (float.TryParse(s, out float pvhi)) {
    pveuhi = pvhi;
    ...
} else {
    Console.WriteLine($"\"{s}\" is not a valid float in line {line}");
}
  • Related