Tasking2 Library
-
Eugen Graf
- guru
- Сообщения: 6502
- Зарегистрирован: 13 ноя 2007, 02:20
- Награды: 4
- Версия LabVIEW: 2009
- Откуда: Saarbrücken
- Контактная информация:
Tasking2 Library
Представляю вашему вниманию библиотеку и структуру организации программ (проектов), которую я использую во всех своих последних проектах.
Приложеный к этой теме архив нужно распаковать и положить папку в user.lib для того, чтобы она отображалась в палитре приборов.
Структура библиотеки думаю понятна, как обычно Open, Read, Write, Close. Так же в библиотеке имеется темплейт с коментариями, который сильно облегчает работу с этой библиотекой.
И так к делу
1. Создаём проект и главный виртуальный прибор Main.vi. В палитре ищем вышеназваный темплейт и располагаем его на блок-диаграмме.
2. Создаём контрол Enum-Typedef в котором мы перечислим все параллельные потоки, средством общения которых являются очереди (Queue).
3. Предположим у нас в проекте будет три параллельных потока-цикла While, один из которых будет находится в Main.vi и два в подприборах SubVI 1 и SubVI 2.
4. Подменяем номер таска на только что созданый нами контрол. Убираем коментарий.
5. Создаём ещё один контрол Enum-Typedef, в котором мы перечислим все глобальные команды (в данном примере это одна команда exit) и подменим им нумерик для глобальных команд. Убираем коментарий.
О глобальных командах. В этой библиотеке я разделил команды на локальные (у каждого потока свой список локальных команд) и глобальные, на которые должны реагировать сразу все потоки. В дальнейшем вы поймёте, что это удобно.
6. Создаём ещё один контрол Enum-Typedef для локальных команд главного цикла находящегося в Main.vi. Заносим в список две команды Data from SubVI 1 и Data from SubVI 2. Подменяем им нумерик константу как показано на скрине. Убираем коментарий.
7. По принципу описаному в пунктах 5 и 6 подменяем номер таска и Enum с глобальными командами для SubVI 1 и SubVI 2, так же создаём списки локальных команд для этих подприборов.
8. При этом важно указать правильное название таска (смотрим скрин)
9. В Main.vi добавляем ещё один дополнительный цикл для обработки ивентов (нажатия на кнопки и прочее).
10. Добавляем в Main.vi следующее:
a. Tasking Open в самом начале при старте
b. Tasking Close в самом конце, если все параллельные циклы окончили свою работу
c. Подсоединяем все потоки так, чтобы они работали параллельно.
11. Добавляем кнопку и ивент exit. Используем Write All из библиотеки и глобальную команду exit, указав при этом что это глобальная команда с помощью флэга.
12. По идее уже всё должно работать. Теперь переходим к локальным командам для подприборов. Добавляем кнопку и ивент для запроса данных из подприбора SubVI 1.
13. Здесь главное не перепутать кому предназначается команда и взять тот список локальных команд, который принадлежит именно этому получателю.
14. Спрограммируем реакцию подприбора SubVI 1 на получение команды get data из Main.vi. Предположим мы будем генерировать случайное число и отправлять это число в Main.vi.
15. В нижнем цикле Main.vi, отвечающем за представление данных на лицевой панели (верхний мы используем для обработки ивентов) мы должны как то интерпретировать полученные данные. Это сделает Variant To Data из палитры Cluster->Variant. Тип данных мы знаем - это double.
16. Такую же процедуру проделаем с подприбором SubVI 2, но на этот раз возьмём строковой тип данных.
17 И в Main.vi так же указываем строковой тип данных.
Таким образом у нас получается организованная комуникация между параллельными потоками. О других видах средств общения (Notifier и User Event), поддерживаемых этой библиотекой я расскажу позднее, но принцип точно такой же что и с очередями.
Жду ваших отзывов
Приложеный к этой теме архив нужно распаковать и положить папку в user.lib для того, чтобы она отображалась в палитре приборов.
Структура библиотеки думаю понятна, как обычно Open, Read, Write, Close. Так же в библиотеке имеется темплейт с коментариями, который сильно облегчает работу с этой библиотекой.
И так к делу
1. Создаём проект и главный виртуальный прибор Main.vi. В палитре ищем вышеназваный темплейт и располагаем его на блок-диаграмме.
2. Создаём контрол Enum-Typedef в котором мы перечислим все параллельные потоки, средством общения которых являются очереди (Queue).
3. Предположим у нас в проекте будет три параллельных потока-цикла While, один из которых будет находится в Main.vi и два в подприборах SubVI 1 и SubVI 2.
4. Подменяем номер таска на только что созданый нами контрол. Убираем коментарий.
5. Создаём ещё один контрол Enum-Typedef, в котором мы перечислим все глобальные команды (в данном примере это одна команда exit) и подменим им нумерик для глобальных команд. Убираем коментарий.
О глобальных командах. В этой библиотеке я разделил команды на локальные (у каждого потока свой список локальных команд) и глобальные, на которые должны реагировать сразу все потоки. В дальнейшем вы поймёте, что это удобно.
6. Создаём ещё один контрол Enum-Typedef для локальных команд главного цикла находящегося в Main.vi. Заносим в список две команды Data from SubVI 1 и Data from SubVI 2. Подменяем им нумерик константу как показано на скрине. Убираем коментарий.
7. По принципу описаному в пунктах 5 и 6 подменяем номер таска и Enum с глобальными командами для SubVI 1 и SubVI 2, так же создаём списки локальных команд для этих подприборов.
8. При этом важно указать правильное название таска (смотрим скрин)
9. В Main.vi добавляем ещё один дополнительный цикл для обработки ивентов (нажатия на кнопки и прочее).
10. Добавляем в Main.vi следующее:
a. Tasking Open в самом начале при старте
b. Tasking Close в самом конце, если все параллельные циклы окончили свою работу
c. Подсоединяем все потоки так, чтобы они работали параллельно.
11. Добавляем кнопку и ивент exit. Используем Write All из библиотеки и глобальную команду exit, указав при этом что это глобальная команда с помощью флэга.
12. По идее уже всё должно работать. Теперь переходим к локальным командам для подприборов. Добавляем кнопку и ивент для запроса данных из подприбора SubVI 1.
13. Здесь главное не перепутать кому предназначается команда и взять тот список локальных команд, который принадлежит именно этому получателю.
14. Спрограммируем реакцию подприбора SubVI 1 на получение команды get data из Main.vi. Предположим мы будем генерировать случайное число и отправлять это число в Main.vi.
15. В нижнем цикле Main.vi, отвечающем за представление данных на лицевой панели (верхний мы используем для обработки ивентов) мы должны как то интерпретировать полученные данные. Это сделает Variant To Data из палитры Cluster->Variant. Тип данных мы знаем - это double.
16. Такую же процедуру проделаем с подприбором SubVI 2, но на этот раз возьмём строковой тип данных.
17 И в Main.vi так же указываем строковой тип данных.
Таким образом у нас получается организованная комуникация между параллельными потоками. О других видах средств общения (Notifier и User Event), поддерживаемых этой библиотекой я расскажу позднее, но принцип точно такой же что и с очередями.
Жду ваших отзывов
- Вложения
-
- Tasking2.zip
- LV 8.2
- (130.42 КБ) 650 скачиваний
-
- Tasking2 Example.zip
- LV 8.6
- (160.93 КБ) 602 скачивания
-
Eugen Graf
- guru
- Сообщения: 6502
- Зарегистрирован: 13 ноя 2007, 02:20
- Награды: 4
- Версия LabVIEW: 2009
- Откуда: Saarbrücken
- Контактная информация:
Re: Tasking2 Library
Ах да, совсем забыл. После того как вы распакуете архив и положите библиотеку в user.lib нужно во первых перестартонуть , а так же в Tools->Advanced->Edit Palette Set выбрать Place VI Content для темплейта. Тогда блок диаграмму темплейта можно будет сразу переносить в блок диаграмму прибора (как в случае с циклами и структурами).
-
Eugen Graf
- guru
- Сообщения: 6502
- Зарегистрирован: 13 ноя 2007, 02:20
- Награды: 4
- Версия LabVIEW: 2009
- Откуда: Saarbrücken
- Контактная информация:
Re: Tasking2 Library
И ещё наверное будет интересно знать, что команды можно отправлять не только в другие потоки, но и самому себе. Получается Queued State Machine, состояниями которой можно управлять как извне так и изнутри.
-
Eugen Graf
- guru
- Сообщения: 6502
- Зарегистрирован: 13 ноя 2007, 02:20
- Награды: 4
- Версия LabVIEW: 2009
- Откуда: Saarbrücken
- Контактная информация:
-
FireFly
- expert
- Сообщения: 1321
- Зарегистрирован: 25 апр 2009, 08:58
- Награды: 2
- Версия LabVIEW: 2014
- Откуда: Санкт-Петербург
- Поблагодарили: 1 раз
Re: Tasking2 Library
Ну лично для меня слишком унифицировано. Лично для меня удобнее сделать кластер всех очередей, для каждой очереди создать цикл, для каждого класса создать методы отправки эвента в очереди всех классов. Да это много однообразной работы, которая один в один повторяется из проекта в проект, но именно это помогает мне хорошо осознать то что я делаю, и работа с проектом идёт лучше.
Хотя, возможно, я так думаю пока проекты небольшие, а когда масштабы вырастут - воспользуюсь твоим способом.
Хотя, возможно, я так думаю пока проекты небольшие, а когда масштабы вырастут - воспользуюсь твоим способом.
Иногда лучше молчать и слыть идиотом, чем заговорить и развеять все сомнения.
-
Eugen Graf
- guru
- Сообщения: 6502
- Зарегистрирован: 13 ноя 2007, 02:20
- Награды: 4
- Версия LabVIEW: 2009
- Откуда: Saarbrücken
- Контактная информация:
Re: Tasking2 Library
Ну то же самое в этой библиотеке и сделано. Только ещё имеется удобный темплейт. Думаю многим сэкономит время да и приучит к однотипниму упорядоченному программированию.
По поводу унификации:
http://www.automationlabs.ru/forum/show ... 7#post6187
Идея зародилась уже аж:
12.04.2007, 00:51
По поводу унификации:
http://www.automationlabs.ru/forum/show ... 7#post6187
Идея зародилась уже аж:
12.04.2007, 00:51
Re: Tasking2 Library
eg
Не мог бы ты показать, как с помощью библиотеки организовать непрерывный сбор данных? Есть предположение, что нужно добавить цикл, из которого постоянно будет посылаться команда получить данные в другой цикл. Или есть более красивое решение?
Не мог бы ты показать, как с помощью библиотеки организовать непрерывный сбор данных? Есть предположение, что нужно добавить цикл, из которого постоянно будет посылаться команда получить данные в другой цикл. Или есть более красивое решение?
-
Eugen Graf
- guru
- Сообщения: 6502
- Зарегистрирован: 13 ноя 2007, 02:20
- Награды: 4
- Версия LabVIEW: 2009
- Откуда: Saarbrücken
- Контактная информация:
Re: Tasking2 Library
Да, я делаю по другому. Из какого то цикла посылается команда "старт" и в цикле сбора занным запускается внутренний цикл. Остановить этот внутренний цикл можно какой нибудь другой командой, например "стоп". Но вопрос в том как прочитать команду "стоп"? Для этого в библиотеке имеется Queue Status, который нужно поместить во внутренний цикл сбора данных. И заканчивать этот внутренний цикл по флэгу New Element Available.
Пример на картинках позже выложу.
Пример на картинках позже выложу.
-
Eugen Graf
- guru
- Сообщения: 6502
- Зарегистрирован: 13 ноя 2007, 02:20
- Награды: 4
- Версия LabVIEW: 2009
- Откуда: Saarbrücken
- Контактная информация:
Re: Tasking2 Library
И так, в SubVI2 добавлены две команды старт и стоп. По команде старт запускается внутренний цикл, который непрерывно собирает данные и отправляет их в Main.
По любой команде внутренний цикл прерывается и выходит во внешний, где и считывается команда стоп и SubVI2 ничего не делает и ждёт другую команду.
Ну а в Main мы просто добавили две кнопки, нажатие на которые отправляет команды в SubVI2. Так же в Main в нижнем цикле добавлен новый кейс, который показывает данные из внутреннего цикла сбора данных SubVI2.
По любой команде внутренний цикл прерывается и выходит во внешний, где и считывается команда стоп и SubVI2 ничего не делает и ждёт другую команду.
Ну а в Main мы просто добавили две кнопки, нажатие на которые отправляет команды в SubVI2. Так же в Main в нижнем цикле добавлен новый кейс, который показывает данные из внутреннего цикла сбора данных SubVI2.
- Вложения
-
- Tasking2 Example2.zip
- (165.14 КБ) 446 скачиваний
Re: Tasking2 Library
Спасибо. Библиотека понравилась. Попробую взять на вооружение. Ещё бы RT FIFO добавить, но там нужно указывать размер очереди при создании.
-
Eugen Graf
- guru
- Сообщения: 6502
- Зарегистрирован: 13 ноя 2007, 02:20
- Награды: 4
- Версия LabVIEW: 2009
- Откуда: Saarbrücken
- Контактная информация:
Re: Tasking2 Library
Добавь, код открытый. У меня RT нет.
Библиотека сама по себе особой ценности не представляет, а вот идея такого стиля программирования очень распространена. Например вот:
http://expressionflow.com/2007/10/01/la ... hitecture/
В принципе существуют два способа организации программирования более сложных проектов в , это Queued State Machine и Functional Global Variables. Оба принципа используют параллельные циклы, но различные способы обмена данными между ними.
Я всего лишь постарался внести ясность в первый из них и успешно пользуюсь своей библиотекой.
Лично я так до конца и не понял как избежать Race Conditions во втором случае, но знаю, что это возможно, перенеся большую часть логики именно в FGV.
Библиотека сама по себе особой ценности не представляет, а вот идея такого стиля программирования очень распространена. Например вот:
http://expressionflow.com/2007/10/01/la ... hitecture/
В принципе существуют два способа организации программирования более сложных проектов в , это Queued State Machine и Functional Global Variables. Оба принципа используют параллельные циклы, но различные способы обмена данными между ними.
Я всего лишь постарался внести ясность в первый из них и успешно пользуюсь своей библиотекой.
Лично я так до конца и не понял как избежать Race Conditions во втором случае, но знаю, что это возможно, перенеся большую часть логики именно в FGV.
-
- interested
- Сообщения: 2
- Зарегистрирован: 27 июл 2010, 15:44
- Версия LabVIEW: 2009
- Контактная информация:
Re: Tasking2 Library
Спасибо за библиотеку. Возникло несколько вопросов. Используете ли вы подобный подход при работе с подключениями по TCP/IP, когда требуется прослушивать входящие сообщения используя связку create listener и wait on listener? Тут как и в случае с вложенным циклом возникает проблема останова потока, которую не получается решить тем же способом, так как Wait On Listener "висит" сам в себе при подаче таймаута "-1". На ум приходят два варинта: использование таймаутов или закрытие ссылки на listener извне. Первый не устравает, потому что хочется работать по событиям. А во втором не удается локализовать работу с сетью в одном потоке.
Еще пара технических вопросов. У меня стоит версия LabVIEW 2009 и некоторые VI, например Tasking2 Read Queue.vi выдают ошибку: Reentrant VIs can only have dynamic dispatch terminals if they share clones between instances. Т.е. приходится отказываться либо от хранения каких либо данных внутри VI, либо жертвовать переопределением функций в случае наследования(хотя обе возможности в контретной библиотеке как я вижу не используются).
Так же интересно почему функции записи Tasking2 Write ... .vi не оформлены как reentrant. Выходит, что независимые потоки могут блокировать выполнение друг друга в случае одновременной записи даже в разные очереди.
Еще пара технических вопросов. У меня стоит версия LabVIEW 2009 и некоторые VI, например Tasking2 Read Queue.vi выдают ошибку: Reentrant VIs can only have dynamic dispatch terminals if they share clones between instances. Т.е. приходится отказываться либо от хранения каких либо данных внутри VI, либо жертвовать переопределением функций в случае наследования(хотя обе возможности в контретной библиотеке как я вижу не используются).
Так же интересно почему функции записи Tasking2 Write ... .vi не оформлены как reentrant. Выходит, что независимые потоки могут блокировать выполнение друг друга в случае одновременной записи даже в разные очереди.
-
Eugen Graf
- guru
- Сообщения: 6502
- Зарегистрирован: 13 ноя 2007, 02:20
- Награды: 4
- Версия LabVIEW: 2009
- Откуда: Saarbrücken
- Контактная информация:
Re: Tasking2 Library
Спасибо за фидбэк!Sleepyhead писал(а):Спасибо за библиотеку.
Да, использую.Sleepyhead писал(а):Возникло несколько вопросов. Используете ли вы подобный подход при работе с подключениями по TCP/IP, когда требуется прослушивать входящие сообщения используя связку create listener и wait on listener?
Использую таймауты, так же как и в случае с VISA Read.Sleepyhead писал(а):Тут как и в случае с вложенным циклом возникает проблема останова потока, которую не получается решить тем же способом, так как Wait On Listener "висит" сам в себе при подаче таймаута "-1". На ум приходят два варинта: использование таймаутов или закрытие ссылки на listener извне. Первый не устравает, потому что хочется работать по событиям. А во втором не удается локализовать работу с сетью в одном потоке.
Не думаю, что наследование пригодится, сделай их статичными. Хотя если добавить хранение каких либо данных внутрь, зачем? Ну а если что то нужно хранить, то я бы использовал кластер самого класса Tasking2.Sleepyhead писал(а):Еще пара технических вопросов. У меня стоит версия LabVIEW 2009 и некоторые VI, например Tasking2 Read Queue.vi выдают ошибку: Reentrant VIs can only have dynamic dispatch terminals if they share clones between instances. Т.е. приходится отказываться либо от хранения каких либо данных внутри VI, либо жертвовать переопределением функций в случае наследования(хотя обе возможности в контретной библиотеке как я вижу не используются).
Не важно, т.к. размер очереди бесконечный, Write Queue зависать не собирается, то есть блокировка может произойти максимум на пару микро- милисекунд. Но если количество данных конечно огромное, то скорее стоит сделать их так же реинтрантными, думаю не помешает. Может быть я просто забыл об этом.Sleepyhead писал(а):Так же интересно почему функции записи Tasking2 Write ... .vi не оформлены как reentrant. Выходит, что независимые потоки могут блокировать выполнение друг друга в случае одновременной записи даже в разные очереди.
-
- interested
- Сообщения: 2
- Зарегистрирован: 27 июл 2010, 15:44
- Версия LabVIEW: 2009
- Контактная информация:
Re: Tasking2 Library
Т.е. во вложенном цикле происходит ожидание нового подключения в случае TCP/IP или распознаных данных/команды в случе VISA и дальнейшая обработка, а при таймауте новая итерация цикла?eg писал(а): Использую таймауты, так же как и в случае с VISA Read.
Интересна дальнейшая судьба TCP/IP соединения. После подключения кого либо, напрашивается создание динамического потока через VI Server, который будет ожидать команд из сети, но как быть с отправкой данных в сеть из других потоков? Обычно я передавал connection ID основному управляющему циклу, а он хранил ее и передавал кому надо через глобальную переменную(неприятное решение, сделанное на скорую руку). Или же предпологается наличие заранее созданного потока, в котором регистрируются новые соединения, а он в свою очередь занимается мониторингом команд и отправкой данных в сеть?
-
Eugen Graf
- guru
- Сообщения: 6502
- Зарегистрирован: 13 ноя 2007, 02:20
- Награды: 4
- Версия LabVIEW: 2009
- Откуда: Saarbrücken
- Контактная информация:
Re: Tasking2 Library
Вопрос понятен. Для обратной коммуникации в созданный поток передаётся класс Tasking, а для коммуникации с созданным потоком из "главного" создаётся массив очередей. Для каждого созданного потока в массив добавляется новая очередь.
Дополнительно сообщаю, что библиотека Tasking статична, поэтому в случае динамического, неограниченного и заранее неизвестного количества TCP соединений, я бы лучше использовал другой, более динамичный pattern, к примеру Observer или Publish Subscribe, что в принципе одно и то же.
Обязательно посмотри проект "Chat", там всё понятно.
Дополнительно сообщаю, что библиотека Tasking статична, поэтому в случае динамического, неограниченного и заранее неизвестного количества TCP соединений, я бы лучше использовал другой, более динамичный pattern, к примеру Observer или Publish Subscribe, что в принципе одно и то же.
Обязательно посмотри проект "Chat", там всё понятно.