Проект делаю на Unity 2018.3.10. Скрипты на С#.
Имеется самодельное устройство подключенное к USB и определяемое ОС как подключенное в COM-порт. Устройство шлёт ключевую последовательность из 3 байт, а затем набор данных. Поскольку в компиляторе Unity у класса SerialPort не реализован event
на принятие данных, пришлось в ручную создавать отдельный поток и читать данные приходящие из буфера. Порт для подключения брал из диспетчера устройств и задавал вручную.
Решив автоматизировать процесс начал подключаться к каждому порту и в течении одной секунды ожидать ключевой последовательности. Если последовательность приходила, то добавлять порт в список устройств, если не было то пропускать, и в любом случае я закрывал подключение. На выходе этого метода имел список портов к которым подключены мои устройства.
Весь этот процесс сильно тормозит UI, потому что я по очереди подключаюсь ко всем портам, и для каждого создаю поток чтения данных, а поток работает в течении секунды, а я ожидаю завершения работы всех потоков. В общем это долго!
Недавно познакомился с Task
, async
и await
, но не разобрался ещё до конца.
Думается что Task
-и мне подойдут больше, чем отдельный несколько потоков, да ещё и запускающихся последовательно.
Прошу помочь, т.е. подсказать как реализовать асинхронное подключение сразу к нескольким портам, определить есть на этом порту моё устройство или нету и в конце вернуть список портов с устройствами.
По сути вопрос: Как одновременно запустить несколько задач внутри которых будет выполняться мой метод подключения и проверки, а затем вернуть результат?
Сейчас выглядит это примерно так:
SerialThread
- класс который содержит в себе метод с циклом while(true){}
в котором происходит чтение байт из буфера порта.
Connect(out sT, out T, name)
- метод для инициализации и запуска потока.
private List<string> GetPortListWithDevices() {
var spNames = new List<string>(SerialPort.GetPortNames());
if (spNames.Count == 0) return spNames;
var timer = new System.Timers.Timer(1000);
try {
timer.Elapsed += delegate(object sender, ElapsedEventArgs args) {
((System.Timers.Timer) sender).Enabled = false;
};
var result = new List<string>();
foreach (var name in spNames) {
SerialThread sT = null;
Thread T = null;
try {
Connect(out sT, out T, name);
timer.Enabled = true;
timer.Start();
while (timer.Enabled) {
// Ждём.....
}
if (sT.isDevice)
result.Add(name);
else Debug.LogWarning($"SP ({name}) is not Digitizer");
}
catch (Exception e) {
Debug.LogWarning($"SP ({name}) not added to list");
}
finally {
Disconnect(ref sT, ref T);
}
}
return result;
}
catch (Exception e) {
Debug.LogException(e);
return null;
}
}
UPD: метод Connect()
private static bool Connect(out SerialThread sT, out Thread T, string portName, int baudRate = 38400) {
try {
sT = new SerialThread(portName, baudRate, _reconnectionDelay, maxUnreadMessages);
T = new Thread(sT.RunForever) {Name = portName};
T.Start();
return true;
}
catch (Exception e) {
Debug.LogException(e);
sT = null;
T = null;
return false;
}
}
UPD: Не совсем понятно, чего мы ожидаем от метода RunForever класса SerialThread, но я предположил что он выглядит так:
public bool IsDevice { get; set; }
public string PortName { get; set; }
public void RunForever(string portName)
{
while(/*true*/)
{
IsDevice = true;
}
}
Тогда код будет примерно таким:
private async Task<SerialThread> ConnectAsync(string portName, int baudRate = 38400)
{
var serialThread = new SerialThread(portName, baudRate, _reconnectionDelay, maxUnreadMessages);
await Task.Run(() => serialThread.RunForever(portName));
return serialThread;
}
Обратится можно так
private async Task<IEnumerable<string>> GetPortListWithDevicesAsync()
{
var spNames = new List<string>(SerialPort.GetPortNames());
return spNames.AsParallel()
.Select(async x => await ConnectAsync(x, baudRate: 123))
.Select(x => x.Result)
.Where(x => x.IsDevice)
.Select(x=>x.PortName)
.ToArray();
}
Или так:
private IEnumerable<string> GetPortListWithDevices()
{
var spNames = new List<string>(SerialPort.GetPortNames());
var tasks = spNames.Select(x => ConnectAsync(x, baudRate: 123)).ToArray();
Task.WaitAll(tasks);
return tasks.Select(x => x.Result)
.Where(x => x.IsDevice)
.Select(x => x.PortName);
}
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Есть два объекта, рисуемые на форме (на пример, круг и треугольник) и необходимо при движение (движение происходит при нажатии стрелок на клавиатуре)...
Всем доброго дня! Сразу суть вопроса - запрос в коде не отрабатывается верно - не приходят данные, проблема только с полем WWG_NAME, остальные...
В автотесте Есть адрес: https://flowtimeqapnmsoftlabs
Хотелось бы узнать, как можно заполнить элементы матрицы, используя некоторую функцию, которая генерирует числа в заданном диапазонеВ C++ данный...