Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Помогите сделать более читабельную UML (диаграмму состояний)  (Прочитано 19022 раз)
0 Пользователей и 1 Гость смотрят эту тему.
pokk
Помогающий

ru
Offline Offline

« : 15-10-2014 03:46 » 

Здравствуйте, уже долго время бьюсь над читаемостью диаграммы кучу раз перерисовывал, но не как не могу добиться что бы она была сходу понятна и отображала полностью логику написанного кода.

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

В общем из всего этого нарисовал такую диаграмму.


Прицеп передачи такой после перехода в состояние FILE_SEND   размер файла  дробится на более мелкие порции(send_len) и эти порции считывается из ПЗУ  после , то что считали отправляем в буфер. Для этого смотрим сколько свободного места в буфере (freeSize) если свободного места в буфер мало (send_len>freeSize), то дробим считанную порцию по freeSize  и отправляем по кускам в буфер. Если буфер совсем заполненный то ожидаем его очищения.

В этой диаграмме мне не нравится то, что логику деления размера файла на несколько порций как-то не хорошо видно и при этом она всё загромождает.
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #1 : 15-10-2014 08:51 » 

Думаю, что нагляднее было бы изобразить как автомат: узлы показывают состояние, а на стрелках писать действия, лучше в виде одиночного имени.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
pokk
Помогающий

ru
Offline Offline

« Ответ #2 : 15-10-2014 09:11 » 

Цитата
Думаю, что нагляднее было бы изобразить как автомат: узлы показывают состояние, а на стрелках писать действия...
А на этой диаграмме не так ? Я именно так и рисовал её.
Цитата
.....лучше в виде одиночного имени.
имеете ввиду заменить весь текст на  X1/Z1   X2/Z2 ?
Я уже думал об этом, но ещё у меня в планах этот автомат (FILE_SEND) отобразить на общей схеме, а не просто нарисовать квадратик с надписью FILE_SEND.
По этому если всё заменить на X1/Z1  на общей схеме без кучи описаний (X1,X2) не разберёшься.   
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #3 : 15-10-2014 10:42 » 

У тебя в узлах написаны и действия в том числе.

Я имею в виду на "инициализация", "чтение файла" и т.п. Детали, что на что ты поделил и какой счетчик инкрементировал, вторичны, ведь эти действия могут быть выделены в отдельную процедуру, которую ты можешь вызвать из разных действий.

Например, алгоритм простейшей игры с 1/2 кнопками.

Состояния:
1. входное;
2. выходное;
3. в игре;
4. вне игры.

События + действия:
1. "Включение игры". Восстановление состояния. Преинициализация.
2. "Останов игры". Сохранение состояния.
3. "Кнопка Start". Изменение состояния. Постинициализация.
4. "Кнопка Play". Изменение состояния. По условию "завершение игры": выдача призов и преинициализация.

Переходы:
1 -(Включение игры)-> [3 | 4]
3 -(Останов игры)-> 2
4 -(Останов игры)-> 2
3 -(Кнопка Start)-> 4
3 -(Кнопка Play)-> [3 | 4]

Что поместить в пре-, а что в пост-инициализацию, для понимания алгоритма не существенно, скрыто в реализации и не мешает читать.

UPD: 1. входное и 2. выходное - суть есть одно и тоже, но здесь удобнее их разнести.

P.S.: я считаю, что UML-неUML - пофиг. Главное, чтобы было понятно - ради этого и делается.
« Последнее редактирование: 15-10-2014 10:45 от RXL » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
pokk
Помогающий

ru
Offline Offline

« Ответ #4 : 16-10-2014 01:13 » 

Цитата
У тебя в узлах написаны и действия в том числе.
INIT_FILE_SEND и SEND_INIT ? Без них  много переходов в одно состояние выходит как-то так =(


Цитата
P.S.: я считаю, что UML-неUML - пофиг. Главное, чтобы было понятно - ради этого и делается.
Аналогично пока я разбираюсь чисто для себя что бы в случае как-нибудь затыка можно было быстро набросать алгоритм того что пытаюсь сделать . Ну и в идеале вообще с него и начинать, но это не как не выходит. 

P.S.: Что с предварительным просмотром случилось ?
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #5 : 16-10-2014 06:56 » 

По прежнему, глядя на схему ни черта не понятно. Жаль
Кстати, а зачем все uppercase? Это тоже мешает. Я не видел твой код и что такое «SEND_COMPLETED[SEND_LEN!=0]/END_INIT()» мне совершенно не понятно. Для чего тогда схема, какую цель она преследует? Что такое «WAIT CLEAR BUF»? Это разве состояние? Не обижайся, но это полный бред.
На самом деле для задачи «приемо-передача через сокет» у тебя внутри должно быть одно состояние с набором событий и действий и два внешних (вход и выход).
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
pokk
Помогающий

ru
Offline Offline

« Ответ #6 : 16-10-2014 08:03 » 

Цитата
Я не видел твой код и что такое «SEND_COMPLETED[SEND_LEN!=0]/END_INIT()» мне совершенно не понятно
Что-то я забыл написать что функция SEND, READ выполнена виде конечного автомата. По этому я имел ввиду что автомат SEND завершил передачу порции, но ещё есть данные которые надо передавать они загружаются функцией END_INIT
Цитата
Не обижайся, но это полный бред.
Да мне этот бред и самому не нравится. но лучше не выходит.
Цитата
Что такое «WAIT CLEAR BUF»? Это разве состояние?
Это физический буфер и в этом состоянии я ожидаю пока он станет пустой что бы можно было дальше  в него данные записывать.
Этого ещё в коде нету.
 
Вот ниже код программы тут я ещё планирую переделать пару моментов.
Если смотреть на первую диаграмму, то  SEND_FSM  это  два состояния SEND  и  SEND_INIT.  Собственно автомат  send_test_MAIN  записывает данные в буфер.
Код:
switch(mode_web2){
//----------------------------------------------------------------
// case FIND_PAGE...............................
//----------------------------------------------------------------
    case WEB_FILE_SEND:{
            if(file_len>0){
                    MAX_LEN=7000;
                    if (file_len >= MAX_LEN){file_send =MAX_LEN;}
                    else{file_send = file_len;}
mode_web2=WEB_READ_PAGE;
                  break;
            }
            content_file=0;
            mode_web2=WEB_DISCONNECT;
            break;
    }
//----------------------------------------------------------------
    case WEB_READ_PAGE:{
            status=flash_read_FSM(content_file,buf_read,7000);
            if(status==COMPLETED){
                    content_send=buf_read;
                    mode_web2=WEB_SEND_PAGE;
                    file_send_temp=file_send;
            }
            break;
    }
//----------------------------------------------------------------
    case WEB_SEND_PAGE:{
                status=SEND_FSM(&file_send,&content_send);
                if(status==COMPLETED){
                        mode_web2=WEB_FILE_SEND;
                        content_file=content_file+file_send_temp;
                        file_len=file_len-file_send_temp;
                }
                break;
        }
//----------------------------------------------------------------
    case WEB_DISCONNECT:{
            disconnect_FSM(0,&temp);
            if(temp==0){
                    mode_web2=WEB_FIND_PAGE;
                    mode_web=WEB_START;
            }
            break;
    }
}



Код:
unsigned char SEND_FSM(unsigned int *file_len,unsigned char** content){
        static unsigned char mode_send_page=WEB_INIT_SEND_PAGE;
        unsigned char status=PAUSE;
        unsigned char exit=PAUSE;
        switch(mode_send_page){
            case WEB_INIT_SEND_PAGE:{
                    //------------------------------------------------------------------
                    maxlen=getSn_TX_FSR(0);
                    if(*file_len>0){
                            if (*file_len >= maxlen){send_len =maxlen;}
                            else{send_len = *file_len;}
                            flag_send_WEB=0;
                            mode_send_page=WEB_SEND_BUF; // Запуск передачи
                    }else{
                            exit=COMPLETED;
                    }
                    
                    break;
            }
            case WEB_SEND_BUF:{
                    //------------------case2 send data------------------------------------------------
                    status=send_test_MAIN(0, *content, send_len,0);
                    if(status==COMPLETED){
                            mode_send_page=WEB_INIT_SEND_PAGE;
                            *content += send_len; // устанавливаем место где закончили передавать
                            *file_len -= send_len;
                    }
                    break;
            }
            break;
        }
        return exit;
}

« Последнее редактирование: 16-10-2014 08:06 от pokk » Записан
pokk
Помогающий

ru
Offline Offline

« Ответ #7 : 17-10-2014 06:59 » 

Вот ещё один вариант диаграммы сделал уже должно быть лучше. Есть какие-нибудь замечания ?     
« Последнее редактирование: 17-10-2014 07:01 от pokk » Записан
pokk
Помогающий

ru
Offline Offline

« Ответ #8 : 27-11-2014 04:29 » 

Здравствуйте, опять я со своей блок схемой.

Делаю программный 1-wire протокол на таймере. Сделал вложенными автоматами т.е есть  управляющая программа в которая задаёт порядок вызовов функций формирования reset, запись данных и чтение выглядит это примерно так:
Код:
switch(manager_t2){
                //-------------reset----------------------
case MANAG_CRASH:{ SEND_ERRORR(); break;}
                        case MANAG_0:{                // отправляем команду reset
                            //T2_POWER_ON
                            status_1wire=reset_1wire();
                            if(status_1wire==ERROR_1WIRE0){
                                // Произошла авария замыкание 0    
                                manager_t2=MANAG_CRASH;
                                break;
                            }
                            if(status_1wire==ERROR_1WIRE1){
                                // Произошла авария замыкание 1                            
                                manager_t2=MANAG_CRASH;
                                break;                                
                            }                            
                            if(status_1wire==COMPLEATED){
                                // Сигнал reset успешно сформирован
                                #if __DEBUG_1WIRE__
                                    DEBUGER("RESET=COMPLEATED",0,0,MESEGER);
                                #endif                                        
                                manager_t2++;
                            }                            
                            break;          //(вместо break сделать переход goto)  (mode_t=RESET; будет указывать что надо продолжать reset)
                        }
                //-------------write 0C----------------------        
                        case MANAG_1:{
                            if(Write_data_1wire(0x0C)){
                                manager_t2++;
                                #if __DEBUG_1WIRE__
                                    DEBUGER("WRITE_0С=COMPLEATED",0,0,MESEGER);
                                #endif                                          
                            }
                            break;
                        }
                //-------------write 00----------------------        
                        case MANAG_2:{
                            if(Write_data_1wire(0x00)){
                                manager_t2++;
                            }
                            break;
                        }
}
 
Функции  reset_1wire   так
Код:
#define PAUSE 0         // Автомат в процессе работы
#define COMPLEATED 1    // Автомат выполнился
#define ERROR_1WIRE0 2  // Ошибка замыкание 0
#define ERROR_1WIRE1 3  // Ошибка замыкание 1

unsigned char reset_1wire(void) {
    static unsigned char mode_reset=RESET_0;
        switch(mode_reset){
//            case INIT_1wire:{
//            //T2_POWER_ON;
//            }
            case RESET_0:{
                    TCCR1B=0x01; //10 000 000        
                    TCNT1=(0xFFFF-4800); //+34
                    T2_OUT
                    T1_OUT
                    mode_reset=RESET_1;
                    break;
            }
            case RESET_1:{    
                    TCNT1=(0xFFFF-700); //+34
                    T2_IN
                    T1_IN
                    mode_reset=RESET_2;
                    break;
            }            
            case RESET_2:{        
            
                    TCNT1=(0xFFFF-4300); //+34
                    mode_reset=RESET_3;  
                    if(T2==1){
                        mode_reset=RESET_0;                    
                        return ERROR_1WIRE1; // Авария замыкание 1
                    }
                    if(T1==1){
                        mode_reset=RESET_0;                    
                        return ERROR_1WIRE1; // Авария замыкание 1
                    }                                                
                    break;
            }
            case RESET_3:{    
                    TCNT1=(0xFFFF-200); //+34
                    if(T2==0){
                        mode_reset=RESET_0;
                        return ERROR_1WIRE0; // Авария замыкание 0                          
                    }
                    if(T1==0){
                        mode_reset=RESET_0;                    
                        return ERROR_1WIRE0; // Авария замыкание 0                                                      
                    }
                    //mode_reset=INIT_1wire;
                    mode_reset=RESET_0;
                    return COMPLEATED;                                                        
                    break;
         }
//            case RESET_EXIT:{
//            
//                break;
//            }
}
return PAUSE;
}


Диаграмма управляющей программы

Диаграмма функции reset

Подскажите как правильно на UML диаграмме отобразить выход из функции(return COMPLEATED,PAUSE,ERROR_1WIRE0).
Как отобразить то что состояние изменилось после COMPLEATED или ERROR_1WIRE0 (произошла переинициализация)  и одновременно был выдан параметр



« Последнее редактирование: 27-11-2014 04:31 от pokk » Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #9 : 27-11-2014 04:43 » 

слишком много UML теории, как по мне. Теория, это, конечно, хорошо, но на практике удобнее применить потоки. Здесь, как я понимаю, устройство не поддерживает многопоточность, но это не мешает её организовать вручную

В итоге: имеется буфер передачи, буфер приёма, объект потока чтения, объект потока отправки.

Тактируем объекты потоков из основной программы, и алгоритм становится быть разделён на три чётких, более простых:

1) основная программа - тактовый генератор
2) поток чтения (читает новые данные, добавляет в буфер чтения)
3) поток отправки (глядит содержимое буфера отправки, извлекает из него данные и отправляет их - какими нужно кусочками)


Если устройство поддерживает аппаратную многопоточность, не забывать про синхронизацию доступа к общим данным потоков
« Последнее редактирование: 27-11-2014 04:46 от Алексей++ » Записан

pokk
Помогающий

ru
Offline Offline

« Ответ #10 : 27-11-2014 06:37 » 

Да вы правы многопоточность здесь не поддерживается.
Вроде как уже уже не раз мне советовали с потоками разобраться, но всё не как не могу до них добраться.
Как примерно выглядит поток отправки и как его тактировать ?
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #11 : 27-11-2014 07:02 » 

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

Код:
struct s_buffer
{
//реализация буфера
};

//общие ресурсы
s_buffer l_send_buffer;
s_buffer l_receive_buffer;

//"поток" отправки
void func_sender()
{
if(l_send_buffer.size())
{
s_buffer part;

//вытащить из начала буфера кусок данных длиной N
l_send_buffer.getPartFromBegin(&part,N);

//отправить
//part->
}
}

//"поток" приёма
void func_receiver()
{
s_buffer part;

//прочитать входящие данные
//->part

//дописать в приёмный буфер
l_receive_buffer.append(&part);
}

int main()
{
//основной цикл
while(1)
{
//что-то делается
//добавление данных в l_send_buffer
//чтение данных из l_receive_buffer
//...

func_sender();

//что-то делается
//добавление данных в l_send_buffer
//чтение данных из l_receive_buffer
//...

func_receiver();

//что-то делается
//добавление данных в l_send_buffer
//чтение данных из l_receive_buffer
//...
}
}

можно ещё расставить приоритеты, если нужно (ввести контроль отдаваемых функциям тактов, ввести досрочный корректный выход из функций по неким флагам , выставленным в прерываниях и т.д.)
Записан

pokk
Помогающий

ru
Offline Offline

« Ответ #12 : 27-11-2014 07:58 » 

Кстати то что я ранее написал это было для термодатчика.

А как быть с задержками?  к примеру в буфере на отправку было 3 байта их надо отправить с разной задержкой через 15-450микросекунд и что бы остальная часть программы не подписала на это время?

Что-то я запутался и совсем не представляю как к моему случаю применить потоки. ведь там данные разные надо отправлять то импульс reset сформировать  то данные отправить (сформировать разные)  или  вы имели ввиду  что func_sender только на отправку данных ?

 

Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #13 : 27-11-2014 08:07 » 

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

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

По деталям меня можно не спрашивать, это всё лишь схема )
Записан

Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #14 : 27-11-2014 21:21 » 

Подскажите как правильно на UML диаграмме отобразить выход из функции

Посмотрите для начала классику. Если останутся вопросы, попробуем обсудить.
Записан

Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
pokk
Помогающий

ru
Offline Offline

« Ответ #15 : 28-11-2014 06:27 » 

Алексей++, Не много вроде прояснилось но для 1-wire  надо завести таймер для формирование квантов около 1мкс, при этом основная программа почти не сможет работать. по этому я отказался от такого подхода.

Dale,Вопросы есть.
Много где видел такие схемы. С переходом сanсel всё понятно он идёт из всех 3х состояний. А вот переход переход  New ставит меня в ступор.
Что запустится текущее состояние или с начало начнётся всё?
А на счёт изначального вопроса что-то нечего не нашёл разве что чёрная окружность внутри белой(финальное состояние) у меня изначально так и было, но когда начал рисовать диаграмму сразу косяк нашёл что после вывода ошибок у меня переинициализации не было после того как его добил  финальное состояние исчезло.

« Последнее редактирование: 28-11-2014 06:32 от pokk » Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #16 : 28-11-2014 06:57 » 

pokk, там не нужен аппаратный таймер, вообще говоря, для функционирования такой схемы.

Но было бы отлично, если есть некий счётчик тактов, прошедших, скажем, с момента запуска чипа - на этой основе можно делать "пассивные" таймеры, которые не нужно всё время тактировать.  Если же такого счётчика нет, то да, проблема с точностью интервалов будет
Записан

Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #17 : 30-11-2014 00:30 » 

Dale,Вопросы есть.
...
А вот переход переход  New ставит меня в ступор.
Что запустится текущее состояние или с начало начнётся всё?

Все в ваших руках.

Можно запустить с любого из подсостояний. Зависит от того, какую нотацию используете. Если UML 2.x, то там введено понятие Entry Point, через него можно перейти в любое подсостояние составного состояния. В версии 1.x допускался просто переход внутрь составного состояния, но такая нотация менее строга и более сумбурна.

Можно запустить с начального псевдосостояния. Для этого нужно войти в составное состояние, как сейчас на вашем рисунке (переход new).

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

Так что как захотите, так и будет работать. Только, не в обиду, настоятельный совет: изучите инструмент, которым пользуетесь. Гадать в программировании неуместно, нужно знать. Все это очень подробно разжевано с массой примеров в Bruce Powel Douglass, Real Time UML: Advances in The UML for Real-Time Systems, 3.4 Behavior and the Single Object. Еще у Самека есть кое-что по теме.

А на счёт изначального вопроса что-то нечего не нашёл

А я и самого-то изначального вопроса не нашел. В первом посте - только перечень фактов, вопросов нет.
Записан

Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
pokk
Помогающий

ru
Offline Offline

« Ответ #18 : 01-12-2014 04:04 » 

Цитата
Наконец, можно оснастить субмашину "мелкой" либо "глубокой" памятью, тогда она будет запоминать прошлое состояние и возвращаться к нему, а не перестартовать каждый раз с самого начала.
А разве функция reset_1wire реализована не так ?  в переменной  mode_reset запоминается состояние и при выходе входе из функции оно сохраняется.
Это и есть протопоток?

Так как необходимо запускать функцию reset_1wire несколько раз(естественно после прохода всех состояний),для этого требовалось перезагрузить её вот с этим у меня были не большие проблемы. Я сделал что после прохода всех состояний (завершения выполнения функции)  mode_reset  сбрасывалось в начальное состояние. Было всё хорошо и удобно, но когда потребовалось перезагрузить автомат(в случае зависания) то не получилось получить доступ к локальной глобальной переменной по этому пришлось сделать просто глобальными.


Я вот почитывал "Гома Х. - UML Проектирование систем реального времени, распределенных и параллельных приложений (Объектно-ориентированное программирование) - 2011"


Записан
Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #19 : 01-12-2014 07:09 » new

А разве функция reset_1wire реализована не так ?

Я говорю только про диаграмму состояний. Текст функции не читал, т.к. там write-only code, для чтения компилятором, а не человеком.

в переменной  mode_reset запоминается состояние и при выходе входе из функции оно сохраняется.
Это и есть протопоток?

Весьма отдаленное сходство с протопотоком есть, но не более. В протопотоках используется "машина Даффа", а не явные case, поэтому поток управления более нагляден.

не получилось получить доступ к локальной глобальной переменной

Такие реально бывают?
Записан

Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines