Я создал объект, который используется для управления испытательным оборудованием (осциллографом), который общается с помощью библиотеки Visa. Этот объект (объект области видимости) работает нормально, но есть один метод, который я создал, чтобы запросить область для усреднения формы сигнала, выполнение которого занимает некоторое время (около секунды или около того), и блокирует выполнение пользовательского интерфейса во время получения данные.
Чтобы обойти эту проблему, я сначала попытался создать объект задачи и использовать этот объект задачи для выполнения функции, которая запрашивает область действия для данных; но я обнаружил, что что-то в самом объекте драйвера Visa, по-видимому, все еще выполняется в главном потоке (и, следовательно, замедляет мой пользовательский интерфейс).
Затем я сделал еще один тест и создал новый поток, и этот поток вызвал функцию. Внутри этой функции я инициализировал объект области действия, установил рабочие параметры, а затем вызвал долгосрочную функцию. На этот раз мой пользовательский интерфейс работал как обычно, без замедлений.
Итак, похоже, мне нужно на самом деле инициализировать объект области внутри нового потока, чтобы он действительно работал асинхронно. Но теперь у меня есть новый вызов. Мне нужно получить доступ к свойствам и методам объектов из основного потока, чтобы настроить вещи, запросить информацию о состоянии и т. Д. Есть ли чистый способ эффективно читать и записывать свойства и методы класса из другого потока? Или есть какие-нибудь библиотеки, чтобы сделать это проще?
Моя текущая идея - создать класс-оболочку для объекта области, а затем этот класс-оболочку инициализировать объект области в новом потоке. Но я не уверен в лучшем способе эффективного доступа к членам объекта. Или еще лучше, есть ли лучший подход к этой проблеме?
РЕДАКТИРОВАТЬ: Ниже приведена дополнительная информация и код для тестовой программы, которую я написал. Пользовательский интерфейс представляет собой простую форму с кнопками Acquire и Connect и двумя метками (одна для отображения измерений, а другая показывает число, которое увеличивается при нажатии кнопки «Нажать»:
Вот код созданного мною объекта Scope:
using System;
using Ivi.Scope.Interop;
using Tektronix.Tkdpo2k3k4k.Interop;
using System.Diagnostics;
namespace Test_App
{
public class DPO4034
{
#region [NOTES] Installing TekVisa Drivers for DPO4034
/*
1. Download and install the TekVisa Connectivity Software from here:
https://www.tek.com/oscilloscope/tds7054-software/tekvisa-connectivity-software-v411
2. Check under Start -> All Programs -> TekVisa and see if the "Open Choice Installation Manager" shortcut works.If not, then update all shortcuts to point to the correct base folder for the TekVISA files, which is "C:\Program Files\IVI Foundation\VISA\".
3. Download the DPO4000 series IVI driver from here:
https://www.tek.com/oscilloscope/dpo4054-software/dpo2000-dpo3000-dpo4000-ivi-driver
4. After running the unzip utility, open the unzipped folder and goto x64 -> Shared Components, and run the IviCleanupUtility_2.0.0.exe utility to make sure no shared IVI components exist.
5. Run the IviSharedComponents64_2.1.1.exe file to install shared components.
6. Go up one folder and open the IVI Driver Folder and run the Tkdpo2k3k4k-x64.msi installer to install the scope IVI driver.
7. In the VS project, add references to the following COM components:
• IviDriverLib
• IviScopeLib
• Tkdpo2k3k4kLib
8. Right Click on each of the three references in the Solution Explorer and select Properties in the menu. When the properties window appears, set the "Embed Interop Types" property to False.
*/
#endregion
#region Class Variables
Tkdpo2k3k4kClass driver; // IVI Driver representing the DPO4034
IIviScope scope; // IVI Scope object representing the DPO4034
#endregion
#region Class Constructors
public DPO4034()
{
this.driver = new Tkdpo2k3k4kClass();
this.scope = (IIviScope)driver;
}
~DPO4034()
{
this.Disconnect();
}
#endregion
#region Public Properties
/// <summary>
/// Returns true if the scope is connected (initialized)
/// </summary>
public bool Connected
{
get
{
return this.driver.IIviDriver_Initialized;
}
}
#endregion
#region Public Methods
/// <summary>
/// Initializes the connection to the scope
/// <paramref name="reset"/>Resets the scope after connecting if set to true</param>
/// </summary>
/// <returns>True if the function succeeds</returns>
public bool Connect(bool reset = false)
{
try
{
if (!this.Connected)
{
this.Disconnect();
}
this.driver.Initialize("TCPIP::10.10.0.200::INSTR", true, reset, "Simulate=false, DriverSetup= Model=DPO4034");
return true;
}
catch (Exception ex)
{
PrintError(ex, "Connect");
return false;
}
}
/// <summary>
/// Closes the connection to the scope
/// </summary>
/// <returns>True if the function succeeds</returns>
public bool Disconnect()
{
try
{
if (this.Connected)
{
this.driver.Close();
}
return true;
}
catch (Exception ex)
{
PrintError(ex, "Disconnect");
return false;
}
}
/// <summary>
/// Reads the average value of the waveform on the selected channel
/// </summary>
/// <param name="channel">1-4 for channels 1 to 4</param>
/// <returns>The measured average value</returns>
public double ReadWaveformAverage(int channel)
{
if (this.Connected)
{
try
{
double value = 0;
this.scope.Measurements.Item["CH" + channel.ToString()].FetchWaveformMeasurement(IviScopeMeasurementEnum.IviScopeMeasurementVoltageAverage, ref value);
return value;
}
catch (Exception ex)
{
PrintError(ex, "ReadWaveformAverage");
return 0;
}
}
else
{
PrintError("Oscilloscope not connected", "ReadWaveformAverage");
return 0;
}
}
#endregion
#region Private Methods
/// <summary>
/// Prints an error message to the debug console
/// </summary>
/// <param name="err">Error object</param>
/// <param name="source">Source of the error</param>
private void PrintError(Exception err, string source = "") //, bool showMessageBox = false)
{
Debug.Print($"Error: {err.Message}");
Debug.Print($"Source: {source}");
}
/// <summary>
/// Prints an error message to the debug console
/// </summary>
/// <param name="err">Error object</param>
/// <param name="source">Source of the error</param>
private void PrintError(string error, string source = "")
{
Debug.Print($"Error: {error}");
Debug.Print($"Source: {source}");
}
#endregion
}
}
Вот код для версии формы, которая использует асинхронную функцию и задачи для непосредственного вызова функций получения:
using System;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Test_App
{
public partial class Form1 : Form
{
byte number = 0;
public Form1()
{
InitializeComponent();
}
private void cmdAcquire_Click(object sender, EventArgs e)
{
takeMeasurements();
}
async void takeMeasurements()
{
try
{
// Create new instance of the scope object and connect to it
DPO4034 Scope = new DPO4034();
Scope.Connect();
// Update status
PrintStatus(Scope.Connected ? "Connected" : "Error");
// Loop continuously and print the samples to the status label
while (Scope.Connected)
{
double inputVoltage = await Task.Run(() => Scope.ReadWaveformAverage(1));
double inputCurrent = await Task.Run(() => Scope.ReadWaveformAverage(2));
double outputVoltage = await Task.Run(() => Scope.ReadWaveformAverage(3));
PrintStatus($"CH1: {inputVoltage}\n" +
$"CH2: {inputCurrent}\n" +
$"CH3: {outputVoltage}\n");
}
}
catch (Exception)
{
PrintStatus("Error");
}
}
private void cmdIncrement(object sender, EventArgs e)
{
// This is just something for me to make the interface do to see
// how responsive it is
lblNumber.Text = number.ToString();
number++;
}
// Prints status text to the label on the form
private void PrintStatus(string text)
{
Status.Text = text;
}
}
}
и вот код для версии формы, которая использует отдельный поток для запуска объекта области действия:
using System;
using System.Threading;
using System.Windows.Forms;
namespace Test_App
{
public partial class Form1 : Form
{
Thread t;
byte number = 0;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
t = new Thread(new ThreadStart(takeMeasurements));
}
private void cmdAcquire_Click(object sender, EventArgs e)
{
t.Start();
}
// Function to create scope object and take acquisitions
void takeMeasurements()
{
try
{
// Create new instance of the scope object and connect to it
DPO4034 Scope = new DPO4034();
Scope.Connect();
// Update status
PrintStatus(Scope.Connected ? "Connected" : "Error");
// Loop continuously and print the samples to the status label
while (Scope.Connected)
{
double inputVoltage = Scope.ReadWaveformAverage(1);
double inputCurrent = Scope.ReadWaveformAverage(2);
double outputVoltage = Scope.ReadWaveformAverage(3);
PrintStatus($"CH1: {inputVoltage}\n" +
$"CH2: {inputCurrent}\n" +
$"CH3: {outputVoltage}\n");
}
}
catch (Exception)
{
PrintStatus("Error");
}
}
private void cmdIncrement(object sender, EventArgs e)
{
// This is just something for me to make the interface do to see
// how responsive it is
lblNumber.Text = number.ToString();
number++;
}
// Prints status text to the label on the form
private void PrintStatus(string text)
{
if (!this.IsDisposed)
{
this.BeginInvoke((MethodInvoker)delegate
{
Status.Text = text;
});
}
else
{
t.Abort();
}
}
}
}
Надеюсь, это даст больше понимания того, чего я пытаюсь достичь. Спасибо всем за ваши комментарии, и я с нетерпением жду ваших отзывов.
EDIT2: Просто для большей ясности, метод, который я предпочел бы использовать (если это возможно), это метод, использующий задачи. В текущей программе объект Scope инициализируется в верхней части формы в главном потоке и доступен для нескольких объектов в программе.