Вверх ↑
Ответов: 5446
Рейтинг: 323
#1: 2006-10-21 18:30:09 ЛС | профиль | цитата


Урок 5. Черновик. Версия 0.9.0 . Помещается для обсуждения!
Исправления:
21.10.2006: порядок чтения точек (ошибку нашёл Tad)
21.10.2006: модифицировано описание GetData (идея подсказана Вячеславом)
22.10.2006: поправлен пример 5.2.2 - убрана лишняя кнопка (в свете замечания о порядке чтения точек);
22.10.2006: начата 3 часть
22.10.2006: модифицировано описание Hub (автор - Tad)
23.10.2006: большАя правка, смена концепции. Спасибо всем, кто высказывал свои пожелания и преложения!
4.11.2006: Вроде придумал, как доделать 3 часть. Обсуждение приветствуется!



[size=+1]Урок 5. Потоки и иже с ними
Этот урок посвящён одному из фундаментальных понятий HiAsm: потоку, и методам работы с ним.
Уровень: Начальный
Необходимые навыки: Основы работы в среде (добавление компонентов, изменение свойств компонент, создание связей, редактирование формы и схемы), копирование схем из форума, знание основных элементов интерфейса Windows (кнопки, надписи, флажки (CheckBox), переключатели (RadioButton)

Часть 1. Введение. Типы потоков. "Синий" поток. Hub

Что же такое "поток"? Заглянем в !Глоссарий!:

Поток (связь) - линия, соединяющая две точки компонент (или компонента). Так же существует понятие Поток-Данные, означающее, что вместе с потоком от одного компонента другому были переданы какие-то данные. В этом случае компонент может "извлечь данные из потока" и использовать их для своей работы.


Не очень-то понятно, не так ли? Давайте представим, что программа на HiAsm - это транспортная система.

Потоки - это железные (синие) и автомобильные (красные) дороги.

Компоненты при таком подходе - это станции, но не простые: функционирование станции сводится отправлению других (своих персональных) составов и
автомобилей по маршрутам, определяемым связями элемента, подключенными справа и сверху. Но
только в ответ на пришедший транспорт слева или снизу. Приходящий поезд (или автомобиль) ожидает на станции, куда он прибыл, окончания всех транспортных операций на станции, заказ на которые он привез. После чего обязательно возвращается назад тем же путем, которым прибыл, с сообщением о выполнении сего ответственного задания.

Данные, которые передаются в потоке - это груз, перевозимым составом. Обычный состав может перевозить только 1 единицу груза: «ничего» (Null) или одно целое число или одно действительное число или одну строку (вне зависимости от длины) или один массив (вне зависимости от длины) и т.п.

Очевидно, что поезд не может проехать по автомобильной дороге, а автомобиль --- по железной...

Рассмотрим с помощью этих аналогий простейший пример (схема 5.1.1):


Add(Button,693842,28,35)
{
Left=25
Top=35
Caption="Кнопка"
Data=String(Привет, Мир!)
link(onClick,12405783:doText,[])
}
Add(Label,12405783,112,35)
{
Left=90
Top=40
Width=195
}

Здесь имеются 2 станции – отправитель Button (Кнопка) и получатель Надпись (Label). Имеется и груз – это содержимое поля Data кнопки. При нажатии на кнопку формируется состав (происходит событие onClick), везущий строку «Привет, Мир!», взятую из свойства Data. Так как "рельсы" (связь) есть, то состав отправляется по ним. По прибытии на станцию Label на путь doText состав отгружает копию товара (вызывает метод с данными из потока), в ответ на появление товара на платформе doLabel компонент Label меняет свою надпись (свойство Caption) на привезённую. Так как дальше ехать некуда, то состав возвращается на станцию отправления тем же путём, но без остановок на промежуточных станциях.

Построим более сложную схему, в которой надпись будет зависеть от того, какую кнопку нажал пользователь (схема 5.1.2)


Add(Button,12494952,91,56)
{
Left=10
Top=5
Caption="Первая"
Data=String(Надпись номер один)
link(onClick,3810058:doEvent1,[(152,62)(152,83)])
}
Add(Button,13680843,91,105)
{
Left=10
Top=35
Caption="Вторая"
Data=String(Надпись номер два)
link(onClick,3810058:doEvent2,[(152,111)(152,90)])
}
Add(Label,14429757,245,77)
{
Left=75
Top=20
Width=270
}
Add(Hub,3810058,182,77)
{
InCount=2
OutCount=1
link(onEvent1,14429757:doText,[])
}

Здесь появился новый элемент - Hub.
Hub - это узловая станция у которой может быть несколько режимов работы:

1. Один входящий путь -> несколько выходящих
При прибытии на такую станцию с поезда снимается груз (данные).
По выходящим путям, в порядке очередности (правые точки сверху-вниз), отправляются поезда с копией груза (данными). Каждый следующий поезд отправляется только по возвращении предыдщего.

2. Несколько входящих -> один выходящий
Станция может быть "зарезервирована", это значит, что она примет поезд толко с определённой входной ветки, и пока поезд с этой ветки не будет обслужен, другие входные ветки блокируются
(не обслуживаются). Снятие резерва происходит по возращении поезда, запросившего резервирование, на станцию отправления

3. Несколько входящих -> несколько выходящих
Комбинация 1. и 2.: поезда по очереди прибывают на станцию, откуда по очереди отправляются поезда по выходным веткам.

При отправлении поезда со станции "диспетчер" резервирует все узловые станции на пути следования поезда. При этом никакой поезд не может тронуться в путь до тех пор, пока хотя бы одна станция на пути его следования заблокирована (для него). По возвращении поезда на станцию отправления все зарезервированные для него станции освобождаются.

Конец 1 части 5 урока
Часть 2. "Красный" поток. GetData, Memory. Очерёдность потоков.

Рассмотрим теперь вертикальные потоки (красные) на примере следующеё схемы (схема 5.2.1):


Add(Button,693842,154,196)
{
Left=25
Top=35
Caption="Кнопка"
link(onClick,12405783:doText,[])
}
Add(Label,12405783,238,196)
{
Left=90
Top=40
Width=195
link(Text,1932628:Value,[])
}
Add(Memory,1932628,238,119)
{
Default=String(Превед, Миръ!)
}

Отличий этой схемы от предыдущей несколько:

  • Добавлен компонент Memory, в котором лежит строка «Превед, Миръ!»
  • Нижняя точка Data компонента Memory соединена с верхней точкой Text компонента Label
  • Содержимое свойства Data компонента Button установлено в NULL. Смысл этого действия будет ясен позднее

В этой схеме появился новый для нас компонент Memory. Этот компонент можно рассматривать как «склад» на одну «привозную» единицу товара (Value) и одну фиксированную единицу (Default, значение по умолчанию). У этой станции 2 входных пути: doValue и doClear, и одни выходной onData. Поезд, прибывающий на путь doValue, заменяет содержимое склада Value на привезённый товар; а поезд, прибывающий на путь doClear, - на (копию) товара со склада Default. В обоих случаях с пути onData отправляется поезд с копией нового содержимого "привозного" склада. Кроме того, этот склад имеет погрузочную зону для автомобилей – нижняя точка Value, и поэтому может служить как зона переноса груза из сотава в грузовик.

Что же изменилось в функционировании схемы? Теперь при нажатии кнопки в путь отправляется пустой состав. По прибытии на путь doText станции Label пустого состава станция отправляет со «стоянки» (верхней точки) Label "грузовик" по имеющейся автомобильной дороге (красному потоку). По прибытии на станцию Memory в погрузочную зону Value он получает копию товара со склада Value и возвращается с этим товаром обратно на станцию Label. Далее с разгруженным товаром поступают точно так же, как если бы его привёз железнодорожный состав.

Аналогом Hub-а для автодорог является компонент GetData, который можно рассматривать как дополнительные погрузочные зоны для грузовиков без расширения склада. Напомню, что при "погрузке" товар со склада не исчезает Автомобили так же, как и поезда, не могут начинать движение до тех пор, пока хотя бы одна развилка на пути их следования закрыта.
(тут нужен пример, но придумать его не могу!)

Для реализации "GetData наоборот" используется компонент GetIndexData (схема 5.2.2):


Add(Button,12494952,231,154)
{
Left=75
Top=5
Caption="Первая"
Data=Integer(0)
link(onClick,3810058:doEvent1,[(271,160)(271,181)])
}
Add(Button,13680843,231,203)
{
Left=135
Top=5
Caption="Вторая"
Data=Integer(1)
link(onClick,3810058:doEvent2,[(271,209)(271,188)])
}
Add(Label,14429757,343,231)
{
Left=5
Top=30
Width=185
link(Text,8509863:Var,[])
}
Add(Hub,3810058,280,175)
{
InCount=2
link(onEvent1,8509863:doIndex,[(334,181)(334,181)])
link(onEvent2,14429757:doText,[(327,188)(327,237)])
}
Add(Memory,13356512,322,126)
{
Default=String(Надпись номер один)
}
Add(Memory,14669312,371,126)
{
Default=String(Надпись номер 2)
}
Add(GetIndexData,8509863,343,175)
{
link(Data1,13356512:Value,[(349,167)(328,167)])
link(Data2,14669312:Value,[(356,167)(377,167)])
}
Add(Label,10628788,238,105)
{
Left=5
Top=10
Caption="Выбрать"
}

Как работает GetIndexData?

  • По прибытии состава на платформу doIndex соединяет верхнюю точку, номер которой "привёз" состав, с нижней точкой; если состав привёз неверный индекс: меньше 0 или больше, чем число верхних точек - 1), то закрывает проезд: любой "грузовик", прибывший на эту станцию, немедленно отправляется назад "пустым" (везущим Null). До первого переключения проезд через компонент является закрытым.
  • По прибытии "грузовик" на нижнюю точку Var, если проезд открыт, грузовик направляется на соответствующий путь, как если бы никакой "развилки" не было (блокировка-разблокировка при этом всё равно выполняется)

Внимательный читатель может задать вопрос: а что произойдёт, если поезд в схеме 5.2.1 (или 5.2.2) не будет пустым? В таких случаях нужно руководствоваться нехитрым правилом приоритетов:

  • Наивысшим приоритетом обладают данные, доставленные грузовиком по автодороге
  • Если грузовик ничего не доставил (NULL) или вообще не дороги (не подключена точка), то используются данные, привезённые по ЖД (переданные при вызове метода)
  • Наконец, если и по ЖД ничего не было привезено, используются данные из соответствующего свойства "по умолчанию" элемента, если оно есть

Замечу, что этот порядок используется тогда, когда нет других указаний относительно порядка чтения данных. Примеры обратного будут рассмотрены в следующих частях. Здесь же замечу, что свойство Caption компонента Label не является свойством "по умолчанию" (в качестве значения свойства "по умолчанию" используется пустая строка).

Конец части 2 урока 5
Часть 3. DoData, IndexToChannel, ChannelToIndex
(в разработке)
В прошлой части мы затронули один из элементов, связывающих красные и синие потоки -- Memory. Но это не единственный такой компонент.

Поставим такую задачу: пусть по нажатии на кнопку "Первая" текст надписи меняется на "Это раз", на кнопку "Вторая" -- на "Это два", а по щелчку по надписи -- на "клац". Как же это реализовать? Можно воспользоваться жульническим методом (схема 5.3.1) :

Add(Button,12494952,217,154)
{
Left=75
Top=5
Caption="Первая"
Data=String(Это раз)
link(onClick,3810058:doEvent1,[(264,160)(264,181)])
}
Add(Button,13680843,217,203)
{
Left=135
Top=5
Caption="Вторая"
Data=String(Это два)
link(onClick,3810058:doEvent2,[(264,209)(264,188)])
}
Add(Label,14429757,336,175)
{
Left=5
Top=30
Width=185
link(onClick,2522918:doClear,[])
}
Add(Hub,3810058,280,175)
{
InCount=3
OutCount=1
link(onEvent1,14429757:doText,[(327,181)(327,181)])
}
Add(Memory,2522918,399,168)
{
Default=String(Клац!)
link(onData,3810058:doEvent3,[(441,174)(441,158)(270,158)(270,195)])
}
но это некрасиво.

А теперь рассмотрим корректный метод решения поставленной задачи (схема 5.3.2):

Add(Button,12494952,217,154)
{
Left=75
Top=5
Caption="Первая"
Data=String(Это раз)
link(onClick,3810058:doEvent1,[(264,160)(264,181)])
}
Add(Button,13680843,217,196)
{
Left=135
Top=5
Caption="Вторая"
Data=String(Это два)
link(onClick,3810058:doEvent2,[(264,202)(264,188)])
}
Add(Label,14429757,336,175)
{
Left=5
Top=30
Width=185
link(onClick,7251959:doData,[])
}
Add(Hub,3810058,280,175)
{
InCount=3
OutCount=1
link(onEvent1,14429757:doText,[(327,181)(327,181)])
}
Add(DoData,7251959,385,175)
{
Data=String(Клац!)
link(onEventData,3810058:doEvent3,[(427,181)(427,165)(270,165)(270,195)])
}

В этом примере мы видим новый элемент - DoData. DoData - это второй тип станции-связки между ЖД и автодорогами, позволяющий груз с автодороги погрузить на ЖД. При этом, если автодороги нет, или грузовик вернулся пустым (NULL), то на поезд погружается "значение по умолчанию" из свойства Data. В нашем примере реализуется последняя возможность - дороги нет, поэтому состав получает строку "Клац!", которую благополучно доставляет на станцию Label. Ещё раз напомню, что станции в нашей транспортной системе не сквозные, прибытие поезда на станцию вызывает отправление уже другого поезда .

Рассмотрим, наконец, дав элемента, очень похожих на Hub - это ChannelToIndex и IndexToChannel на следующем примере (схема 5.3.3):

Add(RadioButton,522844,238,98)
{
Left=5
Top=10
Width=30
Caption="0"
link(onSelect,6224976:doWork1,[(289,104)(289,139)])
}
Add(RadioButton,4209694,238,140)
{
Left=40
Top=10
Width=35
Caption="1"
link(onSelect,6224976:doWork2,[])
}
Add(RadioButton,14433966,238,182)
{
Left=75
Top=10
Width=35
Caption="2"
link(onSelect,6224976:doWork3,[(289,188)(289,153)])
}
Add(ChanelToIndex,6224976,308,133)
{
Count=3
link(onIndex,4600533:doText,[])
}
Add(Label,750749,301,84)
{
Top=35
Height=17
Caption="Опция №"
}
Add(Edit,4600533,413,133)
{
Left=55
Top=30
Text=""
}

Чем же отличается ChanbelToIndex от Hub? Отвечу: по прибытии состава на один из входных путей с выходного пути отправляется состав не копией входных данных, а с номером пути (отсчитываемым сверху вниз, номер верхнего пути - 0). ЩЗамечу, что вхходные данные можно получить с точки Data.

Расширим пример, добавив обратную связь:

Add(RadioButton,522844,168,56)
{
Left=5
Top=10
Width=30
Caption="0"
link(onSelect,6224976:doWork1,[(219,62)(219,97)])
}
Add(RadioButton,4209694,168,98)
{
Left=40
Top=10
Width=35
Caption="1"
link(onSelect,6224976:doWork2,[])
}
Add(RadioButton,14433966,168,140)
{
Left=75
Top=10
Width=35
Caption="2"
link(onSelect,6224976:doWork3,[(219,146)(219,111)])
}
Add(ChanelToIndex,6224976,238,91)
{
Count=3
link(onIndex,4600533:doText,[])
}
Add(Label,750749,231,42)
{
Top=35
Height=17
Caption="Опция №"
}
Add(Edit,4600533,343,91)
{
Left=55
Top=30
Text=""
}
Add(IndexToChanel,9921775,343,189)
{
Count=3
Data=Integer(1)
Point(Index)
link(onEvent1,522844:doSelect,[(410,195)(410,140)(140,140)(140,62)])
link(onEvent2,4209694:doSelect,[(400,202)(400,159)(150,159)(150,104)])
link(Index,4600533:Text,[(349,156)(349,156)])
link(onEvent3,14433966:doSelect,[(390,209)(390,180)(160,180)(160,146)])
}
Add(Button,9379850,273,189)
{
Left=5
Top=55
Width=100
Caption="Выбрать"
link(onClick,9921775:doEvent,[])
}

IndexToChannel работает следующим образом: он отправляет данные с точки Data (или из свойства Data) по пути, указанному во входящем потоке (или с помощью точки Index).

Откуда взялась точка "Index" у IndexToChannel? Очень просто: с закладки "Точки"!


Наконец, финальный штрих - "протаскивание" данных через ChannelToIndex

Add(RadioButton,522844,168,56)
{
Left=5
Top=10
Width=30
Caption="0"
link(onSelect,6224976:doWork1,[(219,62)(219,97)])
}
Add(RadioButton,4209694,168,98)
{
Left=40
Top=10
Width=35
Caption="1"
link(onSelect,6224976:doWork2,[])
}
Add(RadioButton,14433966,168,140)
{
Left=75
Top=10
Width=35
Caption="2"
link(onSelect,6224976:doWork3,[(219,146)(219,111)])
}
Add(ChanelToIndex,6224976,238,91)
{
Count=3
Point(Data)
link(onIndex,15299371:doEvent1,[])
}
Add(Label,750749,231,42)
{
Top=35
Height=17
Caption="Опция №"
}
Add(Edit,4600533,343,91)
{
Left=55
Top=30
Text=""
}
Add(IndexToChanel,9921775,357,189)
{
Count=3
Data=Integer(1)
Point(Index)
link(onEvent1,522844:doSelect,[(410,195)(410,140)(140,140)(140,62)])
link(onEvent2,4209694:doSelect,[(400,202)(400,159)(150,159)(150,104)])
link(onEvent3,14433966:doSelect,[(397,209)(397,180)(160,180)(160,146)])
link(Index,4600533:Text,[(363,156)(349,156)])
}
Add(Button,9379850,273,189)
{
Left=5
Top=55
Width=100
Caption="Выбрать"
link(onClick,9921775:doEvent,[])
}
Add(Edit,2873425,343,245)
{
Left=55
Top=55
link(Str,6224976:Data,[(349,184)(244,184)])
}
Add(Label,1437291,287,35)
{
Top=60
Caption="Данные"
}
Add(Hub,15299371,287,91)
{
link(onEvent1,4600533:doText,[])
link(onEvent2,2873425:doText,[(334,104)(334,251)])
}

На этом 5 урок закончен, всем кто слушал и комментировал - спасибо.



[size=-2]------ Добавлено в 18:28
galkov писал(а):

Да вот еще.
Мы иногда уговариваем машиниста захватить кое-чего обратно. Мало кто знает правда [Image]
Типа возвращается машинист и говорит: "Мне надо было дальше ехать, но я не поехал. Давай ты съездишь - вот маршрут и данные".

И едут. Причем напрямую, мимо предыдущей ветки. Которая остается свободной для других паровозиков.
Смысл делать такие фокусы имеет смысл, когда в элементе этот вызов последний и делать больше ничего не надо...


Э-э... А мона примерчик? Если это что-то простое, то можно будет поправить текст урока

[size=-2]------ Добавлено в 18:30
Про "автомобиль" я понимаю, это когда обращение к массиву идёт (прочитал в справке). А паровоз?

карма: 1

0