fix metodi comunicator e renaming
This commit is contained in:
@@ -21,7 +21,6 @@ using Thermo.Active.Model.DTOModels.ThProd;
|
||||
using Thermo.Active.Model.DTOModels.ThRecipe;
|
||||
using Thermo.Active.Model.DTOModels.ThWarmers;
|
||||
using Thermo.Active.NC;
|
||||
using Thermo.Active.Thermocamera;
|
||||
using Thermo.Active.Utils;
|
||||
using static CMS_CORE_Library.Models.DataStructures;
|
||||
using static Thermo.Active.Config.ServerConfig;
|
||||
@@ -31,6 +30,7 @@ using System.Windows;
|
||||
using System.Drawing;
|
||||
using System.Configuration;
|
||||
using Thermo.Active.Model.DTOModels.ThIO;
|
||||
using Thermo.Active.Thermocamera;
|
||||
|
||||
public static class ThreadsFunctions
|
||||
{
|
||||
@@ -51,8 +51,109 @@ public static class ThreadsFunctions
|
||||
|
||||
#endregion Public Fields
|
||||
|
||||
#region Internal Properties
|
||||
|
||||
/// <summary>
|
||||
/// Swap x/y requesto from FLIR camera
|
||||
/// </summary>
|
||||
internal static bool cacheWarmers
|
||||
{
|
||||
get
|
||||
{
|
||||
bool answ = false;
|
||||
bool.TryParse(ConfigurationManager.AppSettings["cacheWarmers"], out answ);
|
||||
return answ;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Internal Properties
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private static int CalcSleepTime(int maxSleep, int execTime)
|
||||
{
|
||||
int sleep = 0;
|
||||
// Check if the execution time is greater than the half of the max sleep time
|
||||
if (maxSleep - execTime < maxSleep / 2)
|
||||
{
|
||||
sleep = maxSleep;
|
||||
}
|
||||
else
|
||||
{
|
||||
sleep = maxSleep - execTime;
|
||||
}
|
||||
|
||||
return sleep;
|
||||
}
|
||||
|
||||
private static bool ClientIsRunning()
|
||||
{
|
||||
Process[] p = Process.GetProcessesByName(CLIENT_EXE_NAME_NOEXT);
|
||||
foreach (Process pr in p)
|
||||
{
|
||||
if (pr.MainWindowHandle != IntPtr.Zero)
|
||||
{
|
||||
ShowWindow(pr.MainWindowHandle, 9);
|
||||
SetForegroundWindow(pr.MainWindowHandle);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void clientProcess()
|
||||
{
|
||||
string CMSClientPath = "";
|
||||
// Check if the system is 64/32 bit
|
||||
if (Environment.Is64BitOperatingSystem && File.Exists(CLIENT_PATH_64))
|
||||
CMSClientPath = CLIENT_PATH_64;
|
||||
else if (File.Exists(CLIENT_PATH_86))
|
||||
CMSClientPath = CLIENT_PATH_86;
|
||||
|
||||
if (!String.IsNullOrEmpty(CMSClientPath))
|
||||
{
|
||||
Process pr = Process.Start(CMSClientPath, null);
|
||||
|
||||
if (ServerStartupConfig.AutoOpenCmsClient)
|
||||
{
|
||||
pr.WaitForExit();
|
||||
MessageServices.Current.Publish(SEND_STOP_SERVER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> GetPlcAlarmsTranslations(string language)
|
||||
{
|
||||
using (NcAdapter ncAdapter = new NcAdapter())
|
||||
{
|
||||
Dictionary<string, string> returnValue = new Dictionary<string, string>();
|
||||
|
||||
Dictionary<int, string> messages = new Dictionary<int, string>();
|
||||
// Read data from CN
|
||||
ncAdapter.numericalControl.NC_GetTranslatedPlcMessages(language, ref messages); // Avoid checking error because in the worst case "messages" is empty
|
||||
|
||||
// Id start from 1
|
||||
for (int i = 1; i <= 1024; i++)
|
||||
{
|
||||
// Get configurated alarms
|
||||
var tmpAlarmConfig = InitialAlarmsConfig.Where(x => x.PlcId == i).FirstOrDefault();
|
||||
// Default string
|
||||
string message = string.Format(NOT_CONFIGURATED_ALARM_MESSAGE, i);
|
||||
// If is configurated
|
||||
if (tmpAlarmConfig != null)
|
||||
{
|
||||
// Find translated string
|
||||
message = messages.Where(x => x.Key == tmpAlarmConfig.AlarmId).FirstOrDefault().Value;
|
||||
if (message == null)
|
||||
message = string.Format(NOT_FOUND_ALARM_MESSAGE, i);
|
||||
}
|
||||
// Add to dictionary
|
||||
returnValue.Add(i.ToString("D6") + "|900", message);
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// restituisce il periodo di campionamento SE configurato, altrimenti 1000 ms
|
||||
/// </summary>
|
||||
@@ -68,9 +169,106 @@ public static class ThreadsFunctions
|
||||
return answ;
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool SetForegroundWindow(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
||||
|
||||
private static void StatReset()
|
||||
{
|
||||
foreach (var value in Counter)
|
||||
{
|
||||
Timers[value.Key] = 0;
|
||||
Counter[value.Key] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryNcConnection()
|
||||
{
|
||||
// Stop all the NC threads
|
||||
ThreadsHandler.Stop();
|
||||
StatReset();
|
||||
NcAdapter ncAdapter = new NcAdapter();
|
||||
CmsError libraryError = NO_ERROR;
|
||||
// Run loop until NC is connected
|
||||
while (!ncAdapter.numericalControl.NC_IsConnected())
|
||||
{
|
||||
// Try reconnection
|
||||
libraryError = ncAdapter.Connect();
|
||||
if (libraryError.errorCode == CMS_ERROR_CODES.SIEMENS_ENVIRONMENT_NOT_FOUND || libraryError.errorCode == CMS_ERROR_CODES.SIEMENS_HMI_NOT_RUNNING || libraryError.errorCode == CMS_ERROR_CODES.OSAI_TT_FOLDER_NOT_FOUND)
|
||||
ManageLibraryError(libraryError);
|
||||
else if (libraryError.errorCode != CMS_ERROR_CODES.OK)
|
||||
{
|
||||
ncAdapter.Dispose();
|
||||
}
|
||||
|
||||
// Send status to UI
|
||||
MessageServices.Current.Publish(SEND_NC_STATUS_UI, null, ncAdapter.numericalControl.NC_IsConnected());
|
||||
// Send status to signalr
|
||||
MessageServices.Current.Publish(SEND_NC_STATUS, null, ncAdapter.numericalControl.NC_IsConnected());
|
||||
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
|
||||
if (!libraryError.IsError())
|
||||
{
|
||||
if (ServerStartupConfig.AutoOpenCmsClient)
|
||||
StartCMSClient();
|
||||
|
||||
// Start/Restart NC threads
|
||||
ThreadsHandler.StartWorkers();
|
||||
reconnectionIsRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
|
||||
#region Functions
|
||||
#region Internal Methods
|
||||
|
||||
internal static void StatThread()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
foreach (var value in Counter)
|
||||
{
|
||||
if (ThreadsHandler.RunningThreadStatus.ContainsKey(value.Key) && Counter[value.Key] != 0)
|
||||
{
|
||||
ThreadsHandler.RunningThreadStatus[value.Key] = $"{(Timers[value.Key] / Counter[value.Key])} ms x {Counter[value.Key]}";
|
||||
Timers[value.Key] = 0;
|
||||
Counter[value.Key] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
MessageServices.Current.Publish(SEND_THREADS_STATUS, null, ThreadsHandler.RunningThreadStatus);
|
||||
|
||||
Thread.Sleep(2000);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void UpdateStat(string functionName, long timer)
|
||||
{
|
||||
if (!Timers.ContainsKey(functionName))
|
||||
Timers.TryAdd(functionName, timer);
|
||||
else
|
||||
Timers[functionName] += timer;
|
||||
|
||||
if (!Counter.ContainsKey(functionName))
|
||||
Counter.TryAdd(functionName, 1);
|
||||
else
|
||||
Counter[functionName]++;
|
||||
}
|
||||
|
||||
#endregion Internal Methods
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public static void AbortNcConnection()
|
||||
{
|
||||
if (ConnThread != null && ConnThread.IsAlive)
|
||||
ConnThread.Abort();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manage action for conf request
|
||||
@@ -138,7 +336,6 @@ public static class ThreadsFunctions
|
||||
{
|
||||
bool flirImageReq = false;
|
||||
sw.Restart();
|
||||
int tOut = 30000;
|
||||
|
||||
// Check if client is connected
|
||||
if (ncAdapter.numericalControl.NC_IsConnected())
|
||||
@@ -154,7 +351,8 @@ public static class ThreadsFunctions
|
||||
// if requested --> give ack!
|
||||
ncAdapter.ManageFlirStrobe();
|
||||
// requesto photo from library
|
||||
done = ThermocameraComunicator.getInstance().takePicture(tOut);
|
||||
ncAdapter.lastThermoImage = ThermoCamComunicator.getIstance().takePicture();
|
||||
done = !string.IsNullOrEmpty(ncAdapter.lastThermoImage);
|
||||
if (done)
|
||||
{
|
||||
// init
|
||||
@@ -163,7 +361,7 @@ public static class ThreadsFunctions
|
||||
// recupero punti centrali resistenze
|
||||
ncAdapter.GetWarmersChannelCenterPoints(out chPoints);
|
||||
// richiesta temperature per i punti
|
||||
done = ThermocameraComunicator.getInstance().readMultiTemperatures(chPoints, tOut, out actualTemp);
|
||||
done = ThermoCamComunicator.getIstance().readMultiTemperatures("", chPoints, out actualTemp);
|
||||
// salvo dati temp sul PLC
|
||||
ncAdapter.WriteRecipeWarmChTCamTempAct(actualTemp);
|
||||
// give PLC strobe for uploaded Actual TEMP from image
|
||||
@@ -188,6 +386,38 @@ public static class ThreadsFunctions
|
||||
}
|
||||
}
|
||||
|
||||
public static void ManageLibraryError(CmsError libraryError)
|
||||
{
|
||||
switch (libraryError.errorCode)
|
||||
{
|
||||
case CMS_ERROR_CODES.NC_PROD_ERROR:
|
||||
ManageError(ERROR_LEVEL.WARNING, libraryError.localizationKey);
|
||||
break;
|
||||
|
||||
case CMS_ERROR_CODES.NOT_CONNECTED:
|
||||
RestoreConnection(); // If not connected try reconnection
|
||||
break;
|
||||
|
||||
case CMS_ERROR_CODES.SIEMENS_ENVIRONMENT_NOT_FOUND:
|
||||
case CMS_ERROR_CODES.OSAI_TT_FOLDER_NOT_FOUND:
|
||||
case CMS_ERROR_CODES.OPTION_NOT_CONSISTENT:
|
||||
ManageError(ERROR_LEVEL.FATAL, libraryError.localizationKey);
|
||||
break;
|
||||
|
||||
case CMS_ERROR_CODES.SIEMENS_HMI_NOT_RUNNING:
|
||||
ManageError(ERROR_LEVEL.FATAL, "SIEMENS HMI NOT RUNNING");
|
||||
break;
|
||||
|
||||
case CMS_ERROR_CODES.SELECTED_PROCESS:
|
||||
ManageError(ERROR_LEVEL.WARNING, libraryError.localizationKey);
|
||||
break;
|
||||
|
||||
case CMS_ERROR_CODES.INTERNAL_ERROR:
|
||||
ManageException(ERROR_LEVEL.FATAL, libraryError.exception);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manage status/command words for actions
|
||||
/// </summary>
|
||||
@@ -413,6 +643,7 @@ public static class ThreadsFunctions
|
||||
ncAdapter.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lettura valorichannelsIO
|
||||
/// </summary>
|
||||
@@ -430,7 +661,6 @@ public static class ThreadsFunctions
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
||||
sw.Restart();
|
||||
|
||||
if (ncAdapter.numericalControl.NC_IsConnected())
|
||||
@@ -1247,19 +1477,6 @@ public static class ThreadsFunctions
|
||||
ncAdapter.Dispose();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Swap x/y requesto from FLIR camera
|
||||
/// </summary>
|
||||
internal static bool cacheWarmers
|
||||
{
|
||||
get
|
||||
{
|
||||
bool answ = false;
|
||||
bool.TryParse(ConfigurationManager.AppSettings["cacheWarmers"], out answ);
|
||||
return answ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void ReadWarmersData()
|
||||
{
|
||||
@@ -1313,6 +1530,21 @@ public static class ThreadsFunctions
|
||||
}
|
||||
}
|
||||
|
||||
public static void RestoreConnection()
|
||||
{
|
||||
if (reconnectionIsRunning == false)
|
||||
{ // Set thread as running state
|
||||
reconnectionIsRunning = true;
|
||||
|
||||
// Start reconnection thread
|
||||
ConnThread = new Thread(() =>
|
||||
TryNcConnection()
|
||||
);
|
||||
|
||||
ConnThread.Start();
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetupCmsConnect()
|
||||
{
|
||||
NcAdapter ncAdapter = new NcAdapter();
|
||||
@@ -1391,234 +1623,6 @@ public static class ThreadsFunctions
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Functions
|
||||
|
||||
#region SupportFunctions
|
||||
|
||||
private static int CalcSleepTime(int maxSleep, int execTime)
|
||||
{
|
||||
int sleep = 0;
|
||||
// Check if the execution time is greater than the half of the max sleep time
|
||||
if (maxSleep - execTime < maxSleep / 2)
|
||||
{
|
||||
sleep = maxSleep;
|
||||
}
|
||||
else
|
||||
{
|
||||
sleep = maxSleep - execTime;
|
||||
}
|
||||
|
||||
return sleep;
|
||||
}
|
||||
|
||||
private static bool ClientIsRunning()
|
||||
{
|
||||
Process[] p = Process.GetProcessesByName(CLIENT_EXE_NAME_NOEXT);
|
||||
foreach (Process pr in p)
|
||||
{
|
||||
if (pr.MainWindowHandle != IntPtr.Zero)
|
||||
{
|
||||
ShowWindow(pr.MainWindowHandle, 9);
|
||||
SetForegroundWindow(pr.MainWindowHandle);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void clientProcess()
|
||||
{
|
||||
string CMSClientPath = "";
|
||||
// Check if the system is 64/32 bit
|
||||
if (Environment.Is64BitOperatingSystem && File.Exists(CLIENT_PATH_64))
|
||||
CMSClientPath = CLIENT_PATH_64;
|
||||
else if (File.Exists(CLIENT_PATH_86))
|
||||
CMSClientPath = CLIENT_PATH_86;
|
||||
|
||||
if (!String.IsNullOrEmpty(CMSClientPath))
|
||||
{
|
||||
Process pr = Process.Start(CMSClientPath, null);
|
||||
|
||||
if (ServerStartupConfig.AutoOpenCmsClient)
|
||||
{
|
||||
pr.WaitForExit();
|
||||
MessageServices.Current.Publish(SEND_STOP_SERVER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> GetPlcAlarmsTranslations(string language)
|
||||
{
|
||||
using (NcAdapter ncAdapter = new NcAdapter())
|
||||
{
|
||||
Dictionary<string, string> returnValue = new Dictionary<string, string>();
|
||||
|
||||
Dictionary<int, string> messages = new Dictionary<int, string>();
|
||||
// Read data from CN
|
||||
ncAdapter.numericalControl.NC_GetTranslatedPlcMessages(language, ref messages); // Avoid checking error because in the worst case "messages" is empty
|
||||
|
||||
// Id start from 1
|
||||
for (int i = 1; i <= 1024; i++)
|
||||
{
|
||||
// Get configurated alarms
|
||||
var tmpAlarmConfig = InitialAlarmsConfig.Where(x => x.PlcId == i).FirstOrDefault();
|
||||
// Default string
|
||||
string message = string.Format(NOT_CONFIGURATED_ALARM_MESSAGE, i);
|
||||
// If is configurated
|
||||
if (tmpAlarmConfig != null)
|
||||
{
|
||||
// Find translated string
|
||||
message = messages.Where(x => x.Key == tmpAlarmConfig.AlarmId).FirstOrDefault().Value;
|
||||
if (message == null)
|
||||
message = string.Format(NOT_FOUND_ALARM_MESSAGE, i);
|
||||
}
|
||||
// Add to dictionary
|
||||
returnValue.Add(i.ToString("D6") + "|900", message);
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool SetForegroundWindow(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
||||
|
||||
private static void StatReset()
|
||||
{
|
||||
foreach (var value in Counter)
|
||||
{
|
||||
Timers[value.Key] = 0;
|
||||
Counter[value.Key] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryNcConnection()
|
||||
{
|
||||
// Stop all the NC threads
|
||||
ThreadsHandler.Stop();
|
||||
StatReset();
|
||||
NcAdapter ncAdapter = new NcAdapter();
|
||||
CmsError libraryError = NO_ERROR;
|
||||
// Run loop until NC is connected
|
||||
while (!ncAdapter.numericalControl.NC_IsConnected())
|
||||
{
|
||||
// Try reconnection
|
||||
libraryError = ncAdapter.Connect();
|
||||
if (libraryError.errorCode == CMS_ERROR_CODES.SIEMENS_ENVIRONMENT_NOT_FOUND || libraryError.errorCode == CMS_ERROR_CODES.SIEMENS_HMI_NOT_RUNNING || libraryError.errorCode == CMS_ERROR_CODES.OSAI_TT_FOLDER_NOT_FOUND)
|
||||
ManageLibraryError(libraryError);
|
||||
else if (libraryError.errorCode != CMS_ERROR_CODES.OK)
|
||||
{
|
||||
ncAdapter.Dispose();
|
||||
}
|
||||
|
||||
// Send status to UI
|
||||
MessageServices.Current.Publish(SEND_NC_STATUS_UI, null, ncAdapter.numericalControl.NC_IsConnected());
|
||||
// Send status to signalr
|
||||
MessageServices.Current.Publish(SEND_NC_STATUS, null, ncAdapter.numericalControl.NC_IsConnected());
|
||||
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
|
||||
if (!libraryError.IsError())
|
||||
{
|
||||
if (ServerStartupConfig.AutoOpenCmsClient)
|
||||
StartCMSClient();
|
||||
|
||||
// Start/Restart NC threads
|
||||
ThreadsHandler.StartWorkers();
|
||||
reconnectionIsRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void StatThread()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
foreach (var value in Counter)
|
||||
{
|
||||
if (ThreadsHandler.RunningThreadStatus.ContainsKey(value.Key) && Counter[value.Key] != 0)
|
||||
{
|
||||
ThreadsHandler.RunningThreadStatus[value.Key] = $"{(Timers[value.Key] / Counter[value.Key])} ms x {Counter[value.Key]}";
|
||||
Timers[value.Key] = 0;
|
||||
Counter[value.Key] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
MessageServices.Current.Publish(SEND_THREADS_STATUS, null, ThreadsHandler.RunningThreadStatus);
|
||||
|
||||
Thread.Sleep(2000);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void UpdateStat(string functionName, long timer)
|
||||
{
|
||||
if (!Timers.ContainsKey(functionName))
|
||||
Timers.TryAdd(functionName, timer);
|
||||
else
|
||||
Timers[functionName] += timer;
|
||||
|
||||
if (!Counter.ContainsKey(functionName))
|
||||
Counter.TryAdd(functionName, 1);
|
||||
else
|
||||
Counter[functionName]++;
|
||||
}
|
||||
|
||||
public static void AbortNcConnection()
|
||||
{
|
||||
if (ConnThread != null && ConnThread.IsAlive)
|
||||
ConnThread.Abort();
|
||||
}
|
||||
|
||||
public static void ManageLibraryError(CmsError libraryError)
|
||||
{
|
||||
switch (libraryError.errorCode)
|
||||
{
|
||||
case CMS_ERROR_CODES.NC_PROD_ERROR:
|
||||
ManageError(ERROR_LEVEL.WARNING, libraryError.localizationKey);
|
||||
break;
|
||||
|
||||
case CMS_ERROR_CODES.NOT_CONNECTED:
|
||||
RestoreConnection(); // If not connected try reconnection
|
||||
break;
|
||||
|
||||
case CMS_ERROR_CODES.SIEMENS_ENVIRONMENT_NOT_FOUND:
|
||||
case CMS_ERROR_CODES.OSAI_TT_FOLDER_NOT_FOUND:
|
||||
case CMS_ERROR_CODES.OPTION_NOT_CONSISTENT:
|
||||
ManageError(ERROR_LEVEL.FATAL, libraryError.localizationKey);
|
||||
break;
|
||||
|
||||
case CMS_ERROR_CODES.SIEMENS_HMI_NOT_RUNNING:
|
||||
ManageError(ERROR_LEVEL.FATAL, "SIEMENS HMI NOT RUNNING");
|
||||
break;
|
||||
|
||||
case CMS_ERROR_CODES.SELECTED_PROCESS:
|
||||
ManageError(ERROR_LEVEL.WARNING, libraryError.localizationKey);
|
||||
break;
|
||||
|
||||
case CMS_ERROR_CODES.INTERNAL_ERROR:
|
||||
ManageException(ERROR_LEVEL.FATAL, libraryError.exception);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void RestoreConnection()
|
||||
{
|
||||
if (reconnectionIsRunning == false)
|
||||
{ // Set thread as running state
|
||||
reconnectionIsRunning = true;
|
||||
|
||||
// Start reconnection thread
|
||||
ConnThread = new Thread(() =>
|
||||
TryNcConnection()
|
||||
);
|
||||
|
||||
ConnThread.Start();
|
||||
}
|
||||
}
|
||||
|
||||
public static void StartCMSClient()
|
||||
{
|
||||
//Setup the Path Variable
|
||||
@@ -1629,5 +1633,5 @@ public static class ThreadsFunctions
|
||||
}
|
||||
}
|
||||
|
||||
#endregion SupportFunctions
|
||||
#endregion Public Methods
|
||||
}
|
||||
Reference in New Issue
Block a user