我已經構建了一個 C# 應用程式來與作業中的控制系統進行互動。它計算一些值和/或從電子表格中提取它們,然后使用 EasyOPCClient 將它們寫入 OPC 服務器。當我運行這個應用程式時,它會在第一次運行 3-5 分鐘,然后關閉并重新打開,然后從那時起運行 30 秒到 2 分鐘。每次它關閉并重新啟動時都沒有列印到控制臺的錯誤(至少在它關閉之前我可以看到),并且我在事件日志中看不到任何可以關閉它的東西。有沒有人看到我在程式中做的任何事情會導致這種情況?這是我第一次嘗試多執行緒,所以如果它是這樣的話,我不會感到驚訝。
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);
}
}
}
}
uj5u.com熱心網友回復:
AC# 程式可以使用Process.Start. 此代碼不這樣做。它可以由任務調度程式重新啟動,或者如果它作為 Windows 服務或其他應用程式安裝。
但我看到你Thread.Sleep那里有幾個。這些可能是觀察到的行為的原因。因此,程式不會自動關閉和重新啟動。它只是時不時地睡覺。
尤其是在發生例外時Thread.Sleep(300000);被呼叫,使程式休眠 5 分鐘。如果例外隨機發生,這將解釋它以隨機間隔發生。
例如float.Parse,當字串不是有效的浮點數時拋出例外。考慮TryParse改用。例子
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}");
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/513758.html
標籤:C#多线程opc
