Опубликован: 04.12.2007 | Уровень: специалист | Доступ: платный | ВУЗ: Санкт-Петербургский государственный университет
Лекция 7:

Визуальное моделирование систем реального времени, часть II

< Лекция 6 || Лекция 7: 1234 || Лекция 8 >

Генерация кода

В конце этого раздела представлен код на языке С, который автоматически сгенерирован по диаграмме с рис. 7.12. Сделаю несколько оговорок относительно этого кода.

  1. Я представил результаты генерации в одном файле, хотя на самом деле файлов должно быть намного больше. Вот, например, секции, помеченные примечаниями "//Main interface" и "//UD interface". Они описывают сгенерированный код для UML-интерфейсов Main и UD, представленных на рис. 7.4. Очевидно, что они должны быть доступны и в С-файле, который содержит сгенерированный код для компоненты UserDriver. Поэтому содержимое этих секций должно находиться в отдельных h-файлах, которые присоединяются к нужным с-файлам с помощью include-директивы языка C.
  2. Нужно честно признать, что представленный ниже код работать не будет. И не только потому, что его нужно дополнить сгенерированным кодом для компоненты UserDriver. Также требуется реализовать и многочисленные процедуры поддержки, в частности, SendMessage() для посылки сообщений, GetEvent() для получения очередного события, таймерные операции. И, наконец, я не дописал пример, не реализовав процедуры CheckPin(), CheckPUK() и некоторые другие.

Однако все эти "недостатки" делают сгенерированный код более компактным и удобным в иллюстративных целях.

Первые секции представленного ниже файла определяют различные данные и вспомогательные процедуры. Эта информация делится на несколько групп.

  1. То, что генерируется по UML-моделям: интерфейсы Main и UD, внутренние данные и процедуры компоненты Main (см. рис. 7.5) - раздел "//logical procedures and data structures".
  2. Вспомогательные данные и процедуры, которые генерируются независимо от содержимого исходной UML-спецификацией и представляют собой инфраструктуру сгенерированного кода. Описание этих сущностей помечено комментарием "//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;
}
Листинг 7.1.

Контрольные вопросы

  1. Опишите тот фрагмент стандарта GSM, который является предметной областью для нашего примера.
  2. Как вам кажется, есть ли в CASE-средствах, реализующих UML, возможность создавать постепенные спецификации, подобно представленным на рис. 7.2, рис. 7.4, рис. 7.5? Можно ли использовать такой подход в реальных промышленных проектах, или он применим только при создании учебников?
  3. Охарактеризуйте главные аспекты состояния - стабильность, зависимость от истории, факторизация, возможность реакции на события извне компоненты.
  4. Что значит, что состояние факторизует поведение компоненты?
  5. Как можно определить в UML состояние, которое обозначает выполнение сложным алгоритмом определенной работы, игнорируя свойство прерываемости?
  6. Что такое деятельность по входу/выходу? Как она может быть промоделирована другими конструкциями конечного автомата UML?
  7. Почему деятельность по входу/выходу не может быть длительной?
  8. Что такое деятельность в состоянии? Прерываема ли она внешними событиями?
  9. Что такое внутренний переход?
  10. Какие, по вашему мнению, могут возникнуть проблемы при использовании групповых состояний, определенных в этой лекции?
  11. Как выразить групповые состояния в терминах диаграмм конечных автоматов UML?
  12. Какие виды событий вы можете назвать?
  13. Что в реактивных системах может быть источником событий?
  14. Расскажите, чем переход отличается от состояния.
  15. Что такое охраняющее условие? Опишите, как оно работает. Приведите свой пример.
  16. Перечислите виды возможных действий в переходах. Присутствуют ли они в метамодели UML?
  17. Для чего используется конструкция "выбор"?
  18. Расскажите о конструкции "таймер".
  19. Почему сгенерированный для компоненты Main код не будет работать?
  20. Перепишите сгенерированный код, сделав первый switch не по состояниям, а по событиям. Когда, на ваш взгляд, такая реализации оправдана?
< Лекция 6 || Лекция 7: 1234 || Лекция 8 >
Ольга Зырянова
Ольга Зырянова

Здравствуйте, не могу найти ссылку на скачивание курса  «Визуальное моделирование: теория и практика»

 

Номер платежа 6400454020565

Анна Митюрёва
Анна Митюрёва

http://www.intuit.ru/studies/courses/1041/218/info

С мобильного приложения доступ есть, а через сайт не отображается. Печально =(

Ярославй Грива
Ярославй Грива
Россия, г. Санкт-Петербург
Игорь Лука
Игорь Лука
Молдова, Республика, Кишинев