Материал этой лекции будет излагаться на примере - упрощенном фрагменте телекоммуникационной системы из области мобильной телефонии. Данная система реализует стык пользовательского интерфейса мобильного телефона (клавиатурный ввод и дисплейная индикация) и верхнего уровня интерфейса телефона с наземной сетью.
Эта система, без сомнения, является реактивной. Она
Этим примером хотелось продемонстрировать, что в случае реактивных систем целесообразно создавать UML-спецификации с последующей автоматической генерацией конечного кода приложения. Как уже отмечалось выше, генерация целевого кода по UML-диаграммам желательна, но часто наталкивается на препятствия. Таким препятствием является отсутствие емких визуальных абстракций - одновременно и наглядных, и имеющих полноценную
Успешными средствами моделирования реактивных систем являются
На рис. 7.1 представлена диаграмма последовательностей, на которой обозначены главные действующие лица примера и основной сценарий их взаимодействия. Сразу после включения телефонной трубки (пользователь нажал нужную кнопку на аппарате) клавиатура (Keyboard), с помощью сообщения Switch-on, создает компоненту UserDriver, которая, в свою очередь, создает компоненту Main. После этого компонента UserDriver, с помощью сообщения DPINInput, посылает дисплею команду отобразить приглашение на ввод PIN. Введенный пользователем PIN пересылается с клавиатуры компоненте UserDriver, а та переправляет его компоненте Main, где происходит его проверка. Если PIN правильный, то компонента Main начинает искать сеть, посылая оповещение об этом
компоненте UserDriver (сообщение SearchForPLMN ). Последняя, в свою очередь, командует дисплею отобразить картинку поиска сети (сообщение DSearchPLMN ). Когда сеть найдена, Main посылает UserDriver сообщение HomePLMN, и та, после его получения, предлагает дисплею отобразить картинку-сообщение, что полный сервис телефона доступен пользователю (сообщение DHomePLMN ). Компонента Main переходит в состояние Idle и готова обслуживать запросы пользователя телефона и входящие запросы из сети.
(рис 7.1) Главный сценарий примера
Из четырех сущностей, представленных на диаграмме с рис. 7.1, нас интересуют только две - UserDriver и Main. Оставшиеся не являются программным UserDriver и Main представлены на рис. 7.12.
(рис 7.2) Компоненты Main и UserDriver
В нашем примере поведение компоненты Main определяется с помощью диаграммы конечных автоматов. Иллюстративный вариант этой спецификации представлен на рис. 7.3. Данная диаграмма создана для того, чтобы функциональность компоненты Main стала понятной в первом приближении: имена состояний и события обозначены по-русски, нет описания действий в переходах и т. д. Формальная спецификация поведения компоненты Main будет представлена ниже (нетерпеливые могут посмотреть уже сейчас на рис. 7.12). UML позволяет создавать такие промежуточные, эскизные спецификации и хранить их наравне с основными.
(рис 7.3) Иллюстративное описание поведения компоненты Main
Прокомментируем рис. 7.3. При включении трубки компонента Main переходит в состояние "Ожидание PIN". Если PIN введен верно, то далее компонента Main оказывается в состоянии "Поиск сети" и ищет свою сеть - ту, в которой абонент зарегистрирован и информация о которой хранится в его трубке. Если своя сеть найдена, то происходит переход в состояние "Ожидание запроса". В этом состоянии компонента Main способна обрабатывать запросы на установку соединения - как исходящего, от абонента трубки, так и входящего, поступившего из сети. Но эта функциональность уже отсутствует в нашем примере, дабы не усложнять его чрезмерно.
Это был самый "хороший" сценарий, в результате исполнения которого телефонная трубка включилась и готова обслуживать абонента. Теперь рассмотрим самый "плохой" сценарий, когда телефон есть, сеть есть, а позвонить никуда, кроме как в милицию, нельзя (гм, что может быть хуже…). В состоянии "Ожидание PIN" компонента Main может принять три попытки ввода неверного PIN. Если вторая или третья попытка окажется удачной, то дальнейшие события разворачиваются так, как описано выше. Если же и в третьей попытке был введен неверный PIN, то происходит переход в состояние "Ожидание ". В этом состоянии принимается десять попыток ввести верный и после десятой попытки происходит переход в состояние "Блокирована". Если компонента Main находится в этом состоянии, то трубка может позволить абоненту сделать только экстренный выз
ов (милиция, скорая помощь), а также выключить трубку. Теперь
рассмотрим различные "боковые ветки", спецификации которых посвящен остаток диаграммы.
В состоянии "Ожидание , и после этого он снова получит возможность вводить PIN.
Main каждые 0,5 секунд будет проверять, не появилась ли своя Main будет продолжать поиск сети.В состояниях "Ожидание PIN", "Ожидание Main обеспечивает абоненту обработку экстренного вызова и возвращается обратно в то состояние, в котором ее прервали. Наконец, в любом состоянии компонента Main способна обработать сообщение о выключении трубки, посланное пользователем с клавиатуры через компоненту UserDriver, и завершить свою работу.
Компоненты UserDriver и Main реализуют интерфейсы UD и Main соответственно, которые подсоединяются к ним через порты. Чтобы корректно функционировать, каждая из этих компонент нуждается в интерфейсе, определяемом другой компонентой. Все это можно видеть на рис. 7.2.
Спецификации интерфейсов UD и Main представлены на рис. 7.4. В этих интерфейсах есть только сообщения. Напомним, что компонента реализует такой интерфейс, когда умеет обрабатывать сообщения, обозначенные в ее интерфейсе.
(рис 7.4) Описание интерфейсов UD и Main
И, наконец, определим необходимые внутренние операции и атрибуты компоненты Main (см. рис. 7.5). Данные свойства компоненты, разумеется, являются private, т. е. не видны "наружу".
(рис 7.5) Внутренние свойства компоненты Main
Выше было представлено целых три диаграммы (рис. 7.2, рис. 7.4, рис. 7.5), где описываются одни и те же сущности - компоненты Main и UserDriver, а также их интерфейсы. Конечно, это же самое можно было сделать, используя одну-единственную диаграмму, но получилось бы громоздко и существенно менее понятно.
Базовой конструкцией конечного автомата является состояние (state) - стабильный "отрезок жизни" компоненты, когда она готова к обработке событий и делает это в зависимости от предыдущей истории поведения.
"Стабильность" состояния понимается в одном из следующих смыслов:
UML .Важно не только то, что компонента очередной раз перешла в стабильное положение, даже если сама стабильность по качеству одна и та же - например, простое ожидание. Существенно также то, что было до этого момента, т. е. история поведения компоненты. Пусть, например, компонента Main ожидает события срабатывания PLMNSearch и VPLMN. Закроем глаза на то, что она при этом выполняет различную "фоновую" деятельность. Реакция компоненты на сообщение timeout в этих состояниях будет разной (см. рис. 7.12).
Каждый миг жизни неповторим. В том числе, во многом, и для сложной программной компоненты - иначе, например, тестировщики так бы не мучались, воспроизводя ошибки системы. Однако в случае реактивных систем линии жизни программных компонент из бесконечных или очень длинных делаются относительно небольшими. Различные состояния компоненты факторизуют ее поведение, отбрасывая несущественные отличия и "склеивая", отождествляя, разные отрезки жизни, делая множество состояний из бесконечного конечным и обозримым. Например, пользователь мобильной трубки ввел правильный и тогда он снова начинает вводить PIN. Компонента Main "не помнит", что все идет по второму кругу. Или по третьему, и так далее. Для нее все происходит так, как будто трубку только что включили.
В UML 2.0 у состояния возможны следующие атрибуты:
Примеры имен состояний можно увидеть на рис. 7.6 - PLMNSearch (поиск мобильной трубкой своей сети) и WaitingForPIN (ожидание мобильной станцией ввода пользователем PIN ). Если генерировать по диаграмме конечных автоматов программный код, то в диаграммах лучше использовать англоязычные идентификаторы, допустимые в целевом языке программирования.
(рис 7.6) Примеры состояний
PLMNSearch запускается таймер T, ограничивающий максимальное пребывание компоненты в этом состоянии временным интервалом 0.5 секунд (именно на этот временной интервал таймер T установлен, как будет рассказано ниже, при подробном обсуждении поведения компоненты Main ).
PLMNSearch происходит остановка таймера T, так как этот
PLNMSearch запускается операция PLMN_Search(), которая осуществляет поиск своей сети для данной мобильной трубки.
WaitingForPIN, после ввода владельцем трубки PIN и при условии, что этот PIN неверен и число сделанных попыток не превосходит трех, компонента остается в этом же состоянии. Если бы произошел выход и повторный вход в состояние WatingForPIN, то счетчик попыток (переменная n ) был бы обнулен.
Важной конструкцией конечного автомата является
Конструкция конечного автомата, которая определяет переход компоненты из одного состояния в другое в связи с возникновением определенного события, так и называется -
(рис 7.7) Пример перехода
После того как компонента Main, находясь в состоянии WaitingForPIN, получила сообщение PIN, она переходит в состояние PLMNSearch (поиск сети), совершая в переходе единственное действие - посылку сообщения Search_for_PLMN компоненте UserDriver, чтобы та могла показать соответствующую картинку на дисплее телефона.
Переход невозможно прервать, и если уж он запустился, то все действия, определенные в нем, должны отработать, прежде чем компонента сможет отреагировать на какое-либо следующее событие в системе. После окончания перехода компонента оказывается в новом состоянии: обработка текущего события, вызвавшего переход, считается завершенной и компонента может реагировать на следующие события.
Выход компоненты из состояния может зависеть не только от события, но и от значения PLMNSearch выполняется не просто после получения PIN, а только после его проверки и в случае, когда он правильный. Вызов функции CheckPIN(), возвращающей булевское значение, выступает здесь в роли
(рис 7.8) Пример охраняющего условия
Действия в переходе не специфицируются в UML - согласно стандарту это некоторая последовательность выражений (скорее всего, на языке реализации). Тем не менее будем отличать следующие виды действий:
Последний вид действия авторы UML все-таки "вытянули" на модельный уровень, назвав эту конструкцию выбор (choice). Пример показан на рис. 7.9.
(рис 7.9) Пример конструкции выбор
После того, как пользователь мобильного телефона ввел PIN, возможно три ситуации: (i) PIN введен правильно; (ii) PIN введен неправильно и число попыток не превышает трех; (iii) PIN введен неправильно и число попыток равно трем. Первая и третья ситуация специфицированы с применением конструкции выбора, а вторая определена с помощью
С помощью конструкции "выбор" можно создавать сложные переходы.
При моделировании систем реального времени очень важной является конструкция
У
Событие "таймер Т истек" (получение компонентой сообщения timeout с именем истекшего таймера Т ) означает, что с момента старта Т времени прошло ровно столько, на какое он был установлен. Обработчики события timeout должны быть описаны в поведении компоненты.
На рис. 7.10 приводится пример работы с таймером. При входе в состояние VPLMN (мобильной трубке доступен ограниченный сервис некоторой чужой станции, так как своя не найдена) таймер T стартует (установлен на временной интервал в 0,5 секунд он был когда-то раньше). Пробыв в этом состоянии положенное время (то есть те самые 0,5 секунд), трубка снова начинает поиск своей сети - а вдруг абонент, передвигаясь, снова оказался в зоне ее доступа? При выходе из данного состояния таймер останавливается - это сделано для обработки ситуаций, когда выход из данного состояния происходит не по timeout, а по другому событию. Будем считать, что остановка истекшего таймера также является корректной - при этом ничего не происходит, но такая возможность позволяет уменьшить сложность спецификации.
(рис 7.10) Пример работы с таймером
UML и взят из языка SDL. Вместо него в UML есть операции со временем, но их, по-моему, удобнее использовать на диаграммах последовательностей. SDL и использовал в примере, можно выразить с помощью extention-механизма UML, специально созданного для подобных расширений языка.Рассмотрим один интересный вид состояния, который не вошел в UML, но был в языке SDL -
Эти состояния применяются для того, чтобы в конечном автомате компактно определить одинаковые реакции компоненты на ряд событий в нескольких разных состояниях. Используется символ состояния, внутри которого перечисляются все состояния, которые таким образом объединяются. Далее для них определяются общие события и переходы. Если вместо имени состояния вводится символ "*", то это означает, что определяется набор общих переходов для всех состояний данной компоненты.
На рис. 7.11, а показано, что компонента Main в состояниях WaitingForPIN, WaitingForPUK, ServiceBlocked, VPLMN, Idle одинаково реагирует на запрос по обработке экстренного вызова. При получении сообщения EmergencyCall она переходит в состояние ECProcessing, где происходит обработка этого вызова. После этого происходит возврат в исходное состояние. Этот возврат - не вполне обычный переход, так как, фактически, ECProcessing. Реализация всего этого в программном коде будет представлена ниже.
На рис. 7.11, б представлен переход из группового состояния-звездочки: в любом состоянии компонента Main должна обработать сообщение о выключении трубки (пользователь нажал на кнопку "выключить").
(рис 7.11) Переход из группового состояния
UML.
На рис. 7.12 представлен формальный вариант спецификации поведения компоненты Main, по которому осуществляется генерация исполняемого кода.
(рис 7.12) Полная спецификация поведения компоненты Main
Эта диаграмма получается из диаграммы, представленной на рис. 7.3, путем выполнения следующих действий:
PLMN (Public Land Mobile T ;СВ конце этого раздела представлен код на языке С, который автоматически сгенерирован по диаграмме с рис. 7.12. Сделаю несколько оговорок относительно этого кода.
Main и UD, представленных на рис. 7.4. Очевидно, что они должны быть доступны и в С-файле, который содержит сгенерированный код для компоненты UserDriver. Поэтому содержимое этих секций должно находиться в отдельных h-файлах, которые присоединяются к нужным с-файлам с помощью include-директивы языка C.UserDriver. Также требуется реализовать и многочисленные процедуры поддержки, в частности, SendMessage() для посылки сообщений, GetEvent() для получения очередного события, таймерные операции. И, наконец, я не дописал пример, не реализовав процедуры CheckPin(), CheckPUK() и некоторые другие.Однако все эти "недостатки" делают сгенерированный код более компактным и удобным в иллюстративных целях.
Первые секции представленного ниже файла определяют различные данные и вспомогательные процедуры. Эта информация делится на несколько групп.
Main и UD, внутренние данные и процедуры компоненты Main (см. рис. 7.5) - раздел "//logical procedures and data structures"."//internal types, data and procedures". Сюда относятся, кроме уже отмеченных выше процедур SendMessage() и GetEvent(), также следующие процедуры: MainStateMachne() - осуществляет запуск сгенерированного конечного автомата; EventProcessing() - содержит обработку событий конечного автомата.Конечный автомат компоненты Main работает как цикл, запускающий обработчик события (процедуру EventProcessing() ), когда компонента Main "понимает", что произошло некоторое событие, предназначенное ей для обработки. Цикл останавливается, если процедура EventProcessing() возвращает значение false. Это означает, что произошедшее событие - это получение компонентой сообщения SwitchOff, (команда выключить трубку).
По-хорошему, у компоненты должна быть еще очередь событий, в которую некий диспетчер помещает информацию о тех событиях в системе, которые ей предназначаются. А сама компонента, когда готова обрабатывать следующее событие, обращается в эту очередь. Работа с очередью скрыта в операции GetEvent(), а соответствующие структуры данных я не стал показывать, чтобы не
Теперь о процедуре EventProcessing(). Обработчик событий конечного автомата реализуется как оператор switch по состояниям компоненты Main. Каждая его ветка соответствует определенному состоянию. Внутри этих веток происходит ветвление по возможным событиям. Это - почти всегда еще один оператор switch по значениям обрабатываемых в данном состоянии сообщений. Исключением является обработка события "найдена своя станция" в соcтоянии PLMNSearch. Здесь событием является значение true переменной HomePLMN, а не присланное извне сообщение. Аналогичный способ обработки такого же типа события можно увидеть в состоянии ECProcessing.
Далее для некоторых состояний ( WaitingForPIN, WaitingForPUK ) ветки оператора switch начинаются с проверки значения логической переменной nextstate, и в случае, когда ее значение true, выполняются действия по входу для этих состояний. Это нужно, поскольку false.
В реализации stop при выходе из состояний PLMNSearch и VPLMN вызывается даже в тех случаях, когда переход из состояния происходит по событию timeout. Это заложено еще в модель (см. рис. 7.12), и генератор лишь повторил эту некорректность. Но, создавая такую модель, я понимал, что делаю, надеясь на то, что процедура stop умеет останавливать таймер, если он истек.
//Main interface
typedef enum ToMain {PIN, EmergencyCall,TimeoutT,PUK, SwitchOn, SwitchOff};
//UD interface
typedef enum ToUserDriver{ToPin, CallProcessing, VPLMN, HomePLMN, Blocked, ToPUK, Search_for_PLMN};
// timer definition
typedef int timer;
void start(timer);
void set (timer, float);
void stop (timer);
// logical procedures and data structures
int n;
int k;
bool V_PLMN;
bool Home_PLMN;
timer T;
bool CallProcessingFinished;
bool CheckPIN();
bool CheckPUK();
void PLMN_Search();
void EC_Processing();
//internal types, data and procedures
typedef enum State {WatingForPIN,WatingForPUK, PLMNSearch, ECProcessing, VPLMN, Idle, ServiceBlocked};
ToMain input_message;
State state, prevstate;
bool nextstate = true;
void SendMessage (ToUserDriver);
bool GetEvent();
void MainStateMachine();
bool EventProcessing ();
void MainStateMachine()
{
//State Machine Initialization
bool finish = false;
// Transition from Start
SendMessage(ToPin);
set (T, 0.5);
state = WatingForPIN;
//State Machne Run
while (!finish)
if (GetEvent()) finish = EventProcessing();
}
bool EventProcessing (){
bool f = false;
switch (state){
case WatingForPIN:
if (nextstate) n = 0;
nextstate = true;
switch (input_message) {
case PIN:
if (CheckPIN()) {SendMessage (Search_for_PLMN);state = PLMNSearch;}
else if (n==3) {SendMessage(ToPUK); state = WatingForPUK;}
else {n++; nextstate = false;}
break;
case EmergencyCall: SendMessage(CallProcessing);
prevstate = state; state = ECProcessing; break;
case SwitchOff: f = true; break;}
break;
case WatingForPUK:
if (nextstate)k = 0;
nextstate = true;
switch (input_message){
case PUK:
if (CheckPUK()) state = WatingForPIN;
else if (k< 10){k++; nextstate = false;}
else if (k == 10) {SendMessage(Blocked); state = ServiceBlocked;}
break;
case EmergencyCall: SendMessage(CallProcessing);
prevstate = state; state = ECProcessing; break;
case SwitchOff: f = true; break;}
break;
case PLMNSearch:
nextstate = true;
start(T);
PLMN_Search();
if (Home_PLMN) {SendMessage (HomePLMN); stop(T); state = Idle; break;}
switch (input_message){
case TimeoutT: stop(T);
if (V_PLMN) {SendMessage(VPLMN); state =VPLMN;}
else state=PLMNSearch;
break;
case EmergencyCall: stop(T); SendMessage(CallProcessing);
prevstate = state; state = ECProcessing; break;
case SwitchOff: stop(T);f = true; break;}
break;
case VPLMN:
nextstate = true;
start(T);
switch (input_message){
case TimeoutT: stop(T); state=PLMNSearch; break;
case EmergencyCall: stop(T); SendMessage(CallProcessing);
prevstate = state; state = ECProcessing; break;
case SwitchOff: stop(T); f = true; break;}
break;
case ECProcessing:
nextstate = true;
CallProcessingFinished = false;
EC_Processing();
if (CallProcessingFinished) state = prevstate;
else if (input_message == SwitchOff) f = true;
break;
case Idle:
nextstate = true;
switch (input_message){
case EmergencyCall: SendMessage(CallProcessing);
prevstate = state; state = ECProcessing; break;
case SwitchOff: f = true; break;}
break;
case ServiceBlocked:
nextstate = true;
switch (input_message){
case EmergencyCall: SendMessage(CallProcessing);
prevstate = state; state = ECProcessing; break;
case SwitchOff: f = true; break;}
break;
}
return f;
}
GSM, который является предметной областью для нашего примера.UML, возможность создавать постепенные спецификации, подобно представленным на рис. 7.2, рис. 7.4, рис. 7.5? Можно ли использовать такой подход в реальных промышленных проектах, или он применим только при создании учебников?UML состояние, которое обозначает выполнение сложным алгоритмом определенной работы, игнорируя свойство прерываемости?UML?UML?UML?Main код не будет работать?switch не по состояниям, а по событиям. Когда, на ваш взгляд, такая реализации оправдана?Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.