Показано с 1 по 4 из 4

Тема: ГиперТерминал в СП310

  1. #1
    Пользователь Аватар для petera
    Регистрация
    06.05.2011
    Адрес
    Минск
    Сообщений
    3,341

    По умолчанию ГиперТерминал в СП310

    Обсуждение возможности управления ШД из гипертерминала Windows http://www.owen.ru/forum/showthread.php?t=26553 сподвигло меня на этот проект.
    Там я предложил некий гибрид гипертерминала и OSM Programmer http://www.owen.ru/forum/showthread....l=1#post244671
    Только решение было неполным. Там нет самого главного - макроса на си, который должен выполнять обмен символами с контроллером. В конечном варианте мнемоники команд должны передаваться в макрос, а он в свою очередь должен отправлять их в порт панели по одному символу и далее принимать из порта по одному символу контроллера и складывать полученные символы(байты) в регистры, которые будут отображаться в окне терминала на экране панели.
    Т.к. я в Си "чайник", то решил потренироваться и сделать на Си не достающий макрос. Но потом подумал, а почему бы не попробовать сделать в панели настоящий терминал с возможностями как, например, у гипертерминала Windows,с полноразмерной клавиатурой, с переключением раскладки клавиш с русской на английскую и на оборот, поддержкой клавиш Shift, Ctrl, Caps Lock, Tab, Backspace.
    Вот, что у меня получилось. На этот раз никаких ГМ
    В проекте один экран
    Захват-1.png
    четыре окна, на каждом своя раскладка клавиш
    Захват-2.png Захват-3.png

    Захват-4.png Захват-5.png
    и один макрос на Си
    Захват-8.png

    Запущенный терминал будет выглядеть так
    Захват-9.png

    Скрытый текст:

    В эмуляторе макросы на Си не работают, а снять видео работающей панели мне нечем, по этому не могу показать работу терминала "в деле".
    В проекте для связи с удаленным терминалом используется порт PLC, скорость 57600(можно изменить в настройках проекта).
    Работу терминала можно протестировать если подключить панель к СОМ порту компьютера (можно и через переходник RS232/USB) простым трехпроводным кабелем
    2 <------> 3
    3 <------> 2
    5 <------> 5
    И запустить программу HyperTerminal. Настройки СОМ порта следует сделать такими
    Захват-10.png

    Важно согласовать параметры приема и передачи
    Например так
    Захват-6.png <----> Захват-7.png

    Назначение переключателя "DEL как / "BS пробел BS" аналогично этим настройкам HyperTerminal
    Захват-11.png

    Для начала сеанса связи поднять трубки кнопкой на экране панели и кнопкой на панели инструментов HyperTerminal.

    Посмотреть как работает вывод символов в окно терминала можно даже и без физического подключения к удаленному терминалу, ни каких сообщений о потере связи и тормозов в реакции панели из-за такого сообщения нет.
    Вложения Вложения
    Последний раз редактировалось petera; 05.05.2017 в 14:13.
    26 лет деятельности в области промышленной безопасности

  2. #2
    Пользователь Аватар для petera
    Регистрация
    06.05.2011
    Адрес
    Минск
    Сообщений
    3,341

    По умолчанию

    Т.к. комментарии на русском языке в макросах не поддерживаются, а по английски я только через translate.google, то выкладываю текст макроса для гипертерминала отдельно
    Код:
    /*
                                                    Макрос "ТЕРМИНАЛ"
    
                                              (с) petera для форума ОВЕН
    
    
    */
    
    // Used PSW Adr ---------------------
    #define cCountAdr       256
    #define lCountAdr       257
    #define MarkAdr         258
    #define SetupAdr        259
    #define KbdStatusAdr    260
    #define inAdr           300                 // (DWord) !!!
    #define bufAdr          500                 // buffer uses (lTotal x cTotal) Regs.!!!
    //-----------------------------------------------------------------------------------
    #define KbdStatus   PSW[KbdStatusAdr]       /* В битах этог регистра - состояние управляющих кнопок клавиатуры (Shift, Ctrl, Repead),
                                                   индикаторов "Прием", "Передача" и бит разрешения работы макроса */
    #define Setup       PSW[SetupAdr]           // Настройки режимов приема и передачи
    #define cCount      PSW[cCountAdr]          // Номер текущей позиции в строке
    #define lCount      PSW[lCountAdr]          // Номер текущей строки
                                                /* Курсор - мигающий символ '_', указывает знакоместо в окне терминала,
                                                   в которое будет вводится очередной символ. Координаты курсора однозначно
                                                   определяются перемнными cCount и lCount */
    #define savMark     PSW[MarkAdr]            /* Сохраненный код символа, который скрыт курсором
                                                 т.к. курсор может перемещаться на экране не только вперед, но и вверх,
                                                 вниз и назад, то перед перемещением курсора в другую позицию необходимо
                                                 сохранять код символа в том знакоместе, в котором окажется курсор после перемещения.
                                                 И при последующем перемещании курсора возвращать сохраненный символ в знакоместо
                                                 ранее занимаемое курсором */
    #define Input       PSW[inAdr]              // Буфер для приема символов с клавиатуры
    #define dwInput     *(DWORD*)(PSW + inAdr)  // -- // -- в формате DWord
    #define buffer      PSW[bufAdr]             /*Начальный адрес области регистров PSW из которых сделано текстовое окно терминала
                                                 для окна терминала будет занято (lTotal x cTotal) регистров PSW */
    
    //---Макро для чтения бит регистра Setup---------------------------------------------------------
    #define GetSetupStatus(bitno) ((Setup) & (1 << (bitno)))
    //---------------------------- назначение бит регистра Setup
    #define CRLFreceive     0   //При вводе с клавиатуры дополнять символ CR символом LF(перевод строки)
    #define CRLFsend        1   //Дополнять символ CR, полученный от удаленного терминала, символом LF(перевод строки)    
    #define BS_BSsend       3   //Передавать символ "backspace"(BS) как комбинацию из "BS"+"пробел"+"BS"
    #define Echo            4   /*Включить режим "Эхо". В режиме "Эхо" все денные,принятые от удаленного терминала
                                   тут же отправляются обратно, после чего обрабатываются и выводятся на экран */  
    #define Visible         5   /*"Показывать введенные символы". Символы, вводимые с клавиатуры, отправляются удаленному
                                   терминалу и также отображаются в окне. Если Visible=FALSE, то символы, вводимые с клавиатуры,
                                   отправляются удаленному терминалу, но на экран не выводятся.*/
    
    //---Макро для работы с битами регистра KbdStatus-----------------------------------------------
    #define ReversBit(reg, bitno) ((reg) ^= (1 << (bitno)))
    #define GetKbdStatus(bitno)   ((KbdStatus) & (1 << (bitno)))
    #define ResetKbdStatus(bitno) ((KbdStatus) &= ( ~ (1 << ((bitno)))))
    #define SetKbdStatus(bitno)   ((KbdStatus) |= ( 1 << ((bitno))))
    //---------------------------- назначение бит регистра KbdStatus
    #define Shift           0       // Нажата кнопка Shift
    #define Ctrl            1       // Нажата кнопка Ctrl
    #define Repead          2       // Нажата кнопка Rep - повторение символов при вводе
    #define RunEnable       3       // Работа приема и передачи разешена 
    #define receiver        4       // Состояние индикаторп "Прием"
    #define sender          5       // Состояние индикатора "Передача"
    
    //------------------------------------------------------------------
    const int lTotal = 18;          // Количество строк в окне терминала
    const int cTotal = 80;          // Количество символов в одной строке
    const int tabs = 8;             // Кратность (в символах) позиций табуляции
    
    BYTE byReceive[3] = {0x00};     /* Буфер приема для СОМ порта
                                       буфер приема имеет и второе назначение. Т.к. все, что пришло в буфер приема
                                       от удаленного терминала безусловно должно быть выведено на экран.
                                       Чтобы использовать этот же фрагмент кода вывода на экран и для вывода символов,
                                       вводимых с клавиатуры, буфер приема будет использован и при вводе данных с клавиатуры */
    BYTE bySend[3] = {0x00};        // Буфер передачи для СОМ порта
    BYTE *pBuff;                    // Указатель на адрес одного символа в памяти регистров, отображаемых в окне терминала
    int i, count, tmp;
    BOOL flagSend, flagVisible;     // флаги - требуется вывод в СОМ порт, требуется вывод на экран
    
    //--------------------------------------------------------------------
    pBuff = (BYTE *)&buffer + lCount * cTotal + cCount; //Установить указатель в текущую позицию курсора
    //--------------------------------
    
    if ((lCount == 0) && (cCount == 0) && (savMark == 0)) { //Clr Screen.
                                                //Если перед вызовом макроса обнулить счетчики
                                                // строк и символов и записать в регистр savMark значение 0
        memset(&buffer, ' ', cTotal * lTotal);  // то во все байты всех регистров отображаемых в окне терминала 
        savMark = ' ';                          // будут записаны коды пробела - произойдет "очистка" окна терминала
    }
    //-------------------------------
    if (!GetKbdStatus(RunEnable)) {             //Если работа макроса запрещена, то
        ResetKbdStatus(Repead);                 //"отжать" кнопку "rep"
        ResetKbdStatus(sender);                 //Выключить индикатор "Передача"
        ResetKbdStatus(receiver);               //Выключить индикатор "Прием"
        dwInput = 0;                            //Очистить буфер клавиатуры
        *pBuff = savMark;                       //Восстановить символ, скрытый курсором
        return;                                 //Завершить работу макроса
    }
    //-----------------------------
    flagSend = GetSetupStatus(Echo);            //Получить состояние настроек "Эхо" и "Показывать введенные символы"
    flagVisible = GetSetupStatus(Visible);
    
    Enter(PLC);                                 //Открыть порт PLC
    count = Receive(PLC, byReceive, 3, 100, 12);/*Читать 3 байта, TimeOut=100мс, TimeOutBytes=12мс(что это за зверь я не знаю)
                                                  значения тайм-аутов подбирал методом "научного тыка" на скорости 57600.
                                                  возможно, что при уменьшении скорости до 9600 TimeOut немного увеличить.
                                                  Макрос с TimeOut=100 будет работать и на скорости 115200,если в гипертерминале Windows
                                                  не зажимать клавиши до автоповтора, т.к. в редких случаях единичные символы
                                                  при приеме могут "проглатываться". Передедача из панели на 115200 без нареканий.*/  
    if (count == NULL) {                        //Если ничего не пришло, то проверить наличие символов в буфере клавиатуры
        if (dwInput == 0) {                     //Если и в буфере клавиатуры пусто
            flagSend = flagVisible = FALSE;     //то ничего не отправлять и ничего не выводить на экран
            count = 0;
        }
        else {                                  //А если кто-то нажимал кнопки клавиатуры, то
            SetKbdStatus(sender);               //Включить индикатор "Передача"
            ResetKbdStatus(receiver);           //Выключить индикатор "Прием""
            memcpy(&byReceive, &Input, 3);      /*Копировать три байта из буфера клавиатуры в буфер передачи
                                                  последующие манипуляции будем проводить с содержимым буфера передачи
                                                  его же в дальнейшем будем отправлять через СОМ порт и выводить на экран */
            //Проверить состояние управляющих кнопок 
            if (GetKbdStatus(Shift)) {                                       //Если нажата кнопка Shift
                if (((byReceive[0] >= 0x41) && (byReceive[0] <= 0x5A)) ||    //и введен код заглавной латинской буквы
                    ((byReceive[0] >= 0x61) && (byReceive[0] <= 0x7A)) ||    // или строчной латинской буквы
                     (byReceive[0] >= 0xC0))  ReversBit(byReceive[0], 5);    // или любой русской буквы, то поменять заглавную на строчную или наоборот
                else if ((byReceive[0] == 0xA8) ||                           // если введен код Ё или ё
                         (byReceive[0] == 0xB8)) ReversBit(byReceive[0], 4); // то и их поменять местами            
            }
            if (GetKbdStatus(Ctrl)) {      //Если нажата кнопка Ctrl, то заменить ЛАТИНСКИЕ БУКВЫ на управляющие коды
                if (((byReceive[0] >= 0x41) && (byReceive[0] <= 0x5A)) ||
                    ((byReceive[0] >= 0x61) && (byReceive[0] <= 0x7A)))  byReceive[0] = byReceive[0] & 0x1F;
                else  byReceive[0] = 0;    // Если это были не буквы, то ничего не отправлять и не выводить
            }
            //Возможно нужно изменить способ передачи символов CR(ctrl+M /Enter) или backspace - (ctrl+H /Delete)
            if (GetSetupStatus(CRLFsend) && (byReceive[0] == 0x0D)) byReceive[1] = 0x0A;    //Если нужно, то к CR добавить LF
            else if (GetSetupStatus(BS_BSsend) && (byReceive[0] == 0x08)) {                 //Если нужно, то к BS 
                byReceive[1] = 0x20;                                                        //добавить ' '
                byReceive[2] = 0x08;                                                        //и ещё один BS
            }
            count = strlen(byReceive);          //Возможно длина посылки теперь будет другой
            memcpy(&bySend, &byReceive, count); //Копировать то, что на вводили с клавиатуры в буфер передачи  
            flagSend = TRUE;                    //Разрешить передачу удаленному терминалу
                if (! GetKbdStatus(Repead)) {   //Если кнопка повтора не нажата, то 
                    dwInput = 0;                //очистить буфер клавиатуры
                    ResetKbdStatus(Ctrl);       //"отжать" кнопку Ctrl
                    ResetKbdStatus(Shift);      //"отжать" кнопку Shift 
                }                               /*А если кнопка повтора нажата, то код введенного символа и модификаторы Ctrl и Shift
                                                 запомнятся и будут заново отправлены в следующем цикле выполнения макроса */
            }
    }
    //Сюда попали, минуя обработку буфера клавиатуры, если буфер чтения СОМ порта не пустой
    else { 
        ResetKbdStatus(sender);                 //Выключить индикатор "Передача"
        SetKbdStatus(receiver);                 //Включить индикатор "прием"
        flagVisible = TRUE;                     //Выводить на экран содержимое буфера приема
        memcpy(&bySend, &byReceive, count);     //Копировать в буфер передачи из буфера приема все принятые символы
                                                //в неизменном виде на случай необходимости Эха удаленному терминалу
        if (GetSetupStatus(CRLFreceive) && (byReceive[0] == 0x0D)) {//Если нужно, то в буфере приема к полученным CR добавить LF
            byReceive[Min(2, count)] = 0x0A;
            count = Min(3, count+1);
        }
    }
    //------------------------------------
    // В эту точку можем попасть или после приема данных от удаленного терминала или после ввода символов с клавиатуры
    // и всегда если буфер приема из СОМ порта был пуст и буфер клавиатуры тоже пуст
    if ((count != NULL) && flagSend) {  //отправка данных удаленному терминалу необходима если есть, что отправлять и 
                                        //если, например включен флаг Эхо
        Send(PLC, bySend, count);       //отправка даанных удаленному терминалу
    }
    Leave(PLC);                         //Закрыть СОМ порт   
    //------------------------------------
    //Эта часть программы - вывод информации на экран
    if ((count == 3) && (byReceive[0] == 0x1B) && flagVisible) {    //Если в буфере приема три символа и код первого символа 0x1B
                                                                    // то в буфере Escape последовательность
            *pBuff = savMark;                               //восстановить в текущем знакоместе символ скрытый курсором 
            tmp = MAKEWORD(byReceive[2], byReceive[1]);     //Собрать два оставшихся в буфере символа "в кучку"
            switch (tmp) {
                case 0x5B41:    //Up                        //Это кнопка стрелка вверх
                    if (lCount != 0) {                      //Пока не достигнем верха окна терминала
                        lCount--;                           //переместим курсор в ту же позицию вышележащей строки
                        pBuff = pBuff - cTotal;             //пересчитать адрес нового положения курсора
                    }
                    break;
                case 0x5B42:    //Down                      //Это кнопка стрелка вниз
                    if (lCount != lTotal -1) {              //Пока не достигнем низа окна терминала
                        lCount++;                           //переместим курсор в ту же позицию нижележащей строки
                        pBuff = pBuff + cTotal;             //пересчитать адрес нового положения курсора
                    }
                    break;
                case 0x5B44:    //Left                      //Это кнопка стрелка влево
                    if (cCount != 0) {                      //Пока не достигнем левого края окна терминала
                        cCount--;                           //переместим курсор на одну позицию влево в той же строке
                        pBuff --;                           //пересчитать адрес нового положения курсора
                    }
                    break;
                case 0x5B43:    //Right                     //Это кнопка стрелка вправо
                    if (cCount != cTotal - 1) {             //Пока не достигнем правого края окна терминала
                        cCount++;                           //переместим курсор на одну позицию вправо в той же строке
                        pBuff++;                            //пересчитать адрес нового положения курсора
                    }
                    break;
                case 0x5B48:    //Home                      //Это кнопка Домой, т.е. 
                    lCount = 0;                             //переместить курсор в левый верхний угол окна терминала
                    cCount = 0;
                    pBuff = (BYTE *)&buffer;                //пересчитать адрес нового положения курсора (указатель на начало буфера)
                    break;
                case 0x5B4B:    //End                       //Кнопка End
                    *pBuff = ' ';                           //В позиции курсора записать пробел
                    memset(pBuff, ' ', cTotal - cCount);    //Удалить все символы от текущего положения курсора до донца строки
                    break;
            }
            savMark = *pBuff;                               //Сохранить символ в знакоместе экрана, на которое теперь будет указывать курсор
    }
    else if ((count != NULL) && flagVisible) {              //А если в буфере не оказалось Escape последовательность, а он не пустой
        for (i=0; i<count; i++) {                           //то будем выводить по очереди все символы, которые обнаружим в буфере
            *pBuff = savMark;                               //восстановить в текущем знакоместе символ скрытый курсором 
            switch (byReceive[i]) {                         //кроме печатных символов в буфере могут находится управляющие символы
                                                            //некоторые из управляющих символов используются для управления выводом
                case 0x07:                                  //Bell - (ctrl+G). Удаленный терминал может прислать звуковой сигнал
                    Beep();                                 //и если звук в панели не запрещен (PFW[2] == 0), то услышим короткий Бип
                    break;
                case 0x8:                                   //backspace - (ctrl+H /Delete)
                    if (cCount != 0) {                      //если курсор не в начале строки
                        cCount--;                           //то прееместить его на одно знакоместо влево
                        pBuff--;                            //пересчитать адрес нового положения курсора
                    }
                    break;
                case 0x9:                                   //Tab - (ctrl+I) Табуляция - перемещение курсора вправо до ближайшей позиции кратной "tabs"
                    if (cCount < (cTotal - tabs)) {         //Если до конца строки осталось болше чем значение "tabs"
                        tmp = tabs - cCount % tabs;         //то подсчитать сколько символов от текущего положения курсора до до ближайшей позиции кратной "tabs"
                        cCount = cCount + tmp;              //переместить курсор в новую позицию
                        pBuff = pBuff + tmp;                //пересчитать адрес нового положения курсора
                    }
                    else {                                  //если до конца строки символов осталось меньше чем значение "tabs"
                        tmp = cTotal -1 - cCount;           //то подсчитать сколько их осталось
                        cCount = cTotal-1;                  //и новое положение курсора, в этом случае, однозначно д.б. в конце строки
                        pBuff = pBuff + tmp;                //пересчитать адрес нового положения курсора
                    }
                    break;
                case 0xA:                                   //LF - line feed (ctrl+J). Перевод строки
                    lCount++;                               //переместим курсор в ту же позицию нижележащей строки
                    pBuff = pBuff + cTotal;                 //пересчитать адрес нового положения курсора
                    break;
                case 0xD:                                   //CR - carriage return (ctrl+M /Enter). Возврат каретки
                    cCount = 0;                             //переместим курсор в начало текущей строки
                    pBuff = (BYTE *)&buffer + lCount * cTotal;//пересчитать адрес нового положения курсора
                    break;
    //следующие три управляющих кода пока не используются
                case 0x03:        //ETX - end of text (ctrl+C) 
                case 0x04:        //EOT - end of transmission (ctrl+D)
                case 0x1A:        //ctrl+Z (SUB -substitute)
    //А теперь выводим печатные смволы
                default:
                    *pBuff = byReceive[i];                  //очередной символ из буфера приема поместить по адресу курсора
                    pBuff++;                                //декремент указателя адреса
                    cCount++;                               //переместить курсор в следующую позицицию строки
                    break;
            }
            savMark = *pBuff;                               //Сохранить символ в знакоместе экрана, на которое теперь будет указывать курсор
        }
    }
    //----------------------------------------------------------------------
    if (cCount >= cTotal) {                                 //если текущее(расчетное) положение курсора оказалось больше чем символов в одной строке
        cCount =0;                                          //то курсор переместить в начало следующей строки
        lCount++;
    }
    if (lCount >= lTotal) {                                      //если после этого нмер текущей строки стал больше чем есть строк в окне терминала
        memmove(&buffer, &buffer+cTotal/2, ((lTotal-1)*cTotal)); //по переписать все содержимое строк начиная со второй и по последнюю в начало буфера окна
        lCount=lTotal-1;                                         //теперь номер текущей строки будет на 1 меньше, чем общее количество строк
        memset(&buffer+(lTotal-1) * cTotal/2, ' ', cTotal);      //освободившуюся последнюю строку очистить (заполнить пробелами)
        savMark = ' ';                                           //естественно новый смвол скрытый курсором будет - пробел
    }
    //---------------------------------------------------------------------
    //Мигающий курсор
    if (GetPSBStatus(4)) *pBuff = '_';           //0,5 сек показывать курсор '_'   
    else                 *pBuff = savMark;       //0,5 сек показывать символ, скрытый курсором
    //-----------------------------------------
    Последний раз редактировалось petera; 05.05.2017 в 15:16.
    26 лет деятельности в области промышленной безопасности

  3. #3
    Пользователь Аватар для petera
    Регистрация
    06.05.2011
    Адрес
    Минск
    Сообщений
    3,341

    По умолчанию

    Открыл для себя "волшебное слово" - static
    Переменные, определенные в функциях, называются локальными переменными. Локальные переменные создаются при входе в функцию и уничтожаются при выходе из нее. Поэтому локальные переменные не могут содержать значения между вызовами функций. Единственным исключением из этого правила являются переменные, объявленные со спецификатором static. Он заставляет компилятор воспринимать данную переменную как глобальную, но область видимости по-прежнему ограничена функцией.
    Это позволило исключить из макроса три регистра PSW256, PSW257 и PSW258, которые использовались для хранения значений переменных между вызовами макроса.
    Теперь регистры PSW используются только для передачи аргументов в макрос.
    Изменения в коде выделены красным цветом.
    Код:
    /*
                                                    Макрос "ТЕРМИНАЛ"
    
                                              (с) petera для форума ОВЕН
    
     */
    
    // Used PSW Adr ---------------------
    //#define cCountAdr       256              Эти регистры 
    //#define lCountAdr       257              больше
    //#define MarkAdr         258              ненужны !!!
    #define SetupAdr        259
    #define KbdStatusAdr    260
    #define inAdr           300                 // (DWord) !!!
    #define bufAdr          500                 // buffer uses (lTotal x cTotal) Regs.!!!
    //-----------------------------------------------------------------------------------
    #define KbdStatus   PSW[KbdStatusAdr]       /* В битах этог регистра - состояние управляющих кнопок клавиатуры (Shift, Ctrl, Repead),
                                                   индикаторов "Прием", "Передача" и бит разрешения работы макроса */
    #define Setup       PSW[SetupAdr]           // Настройки режимов приема и передачи
    //#define cCount      PSW[cCountAdr]        Эти регистры
    //#define lCount      PSW[lCountAdr]        больше ненужы
    //#define savMark     PSW[MarkAdr]          ненужны !!!  
    #define Input       PSW[inAdr]              // Буфер для приема символов с клавиатуры
    #define dwInput     *(DWORD*)(PSW + inAdr)  // -- // -- в формате DWord
    #define buffer      PSW[bufAdr]             /*Начальный адрес области регистров PSW из которых сделано текстовое окно терминала
                                                 для окна терминала будет занято (lTotal x cTotal) регистров PSW */
    
    //---Макро для чтения бит регистра Setup---------------------------------------------------------
    #define GetSetupStatus(bitno) ((Setup) & (1 << (bitno)))
    //---------------------------- назначение бит регистра Setup
    #define CRLFreceive     0   //При вводе с клавиатуры дополнять символ CR символом LF(перевод строки)
    #define CRLFsend        1   //Дополнять символ CR, полученный от удаленного терминала, символом LF(перевод строки)    
    #define BS_BSsend       3   //Передавать символ "backspace"(BS) как комбинацию из "BS"+"пробел"+"BS"
    #define Echo            4   /*Включить режим "Эхо". В режиме "Эхо" все денные,принятые от удаленного терминала
                                   тут же отправляются обратно, после чего обрабатываются и выводятся на экран */  
    #define Visible         5   /*"Показывать введенные символы". Символы, вводимые с клавиатуры, отправляются удаленному
                                   терминалу и также отображаются в окне. Если Visible=FALSE, то символы, вводимые с клавиатуры,
                                   отправляются удаленному терминалу, но на экран не выводятся.*/
    //---Макро для инверсии бит -------------------------------------------------------------------
    #define ReversPSB(PSB_No)     PSB[(PSB_No) / 8] ^= (1 << ((PSB_No) % 8)) /* В дополнение к существующим SetPSB(PSB_No) и ResetPSB(PSB_No)
                                                                              этот макро выполняет инверсию заданного PSB
        Пример:
        ReversPSB(256);                                                      */
    
    #define ReversBit(reg, bitno) ((reg) ^= (1 << (bitno)))                  //
    //---Макро для работы с битами регистра KbdStatus-----------------------------------------------
    #define GetKbdStatus(bitno)   ((KbdStatus) & (1 << (bitno)))
    #define ResetKbdStatus(bitno) ((KbdStatus) &= ( ~ (1 << ((bitno)))))
    #define SetKbdStatus(bitno)   ((KbdStatus) |= ( 1 << ((bitno))))
    //---------------------------- назначение бит регистра KbdStatus
    #define Shift           0       // Нажата кнопка Shift
    #define Ctrl            1       // Нажата кнопка Ctrl
    #define Repead          2       // Нажата кнопка Rep - повторение символов при вводе
    #define RunEnable       3       // Работа приема и передачи разешена 
    #define receiver        4       // Состояние индикатора "Прием"
    #define sender          5       // Состояние индикатора "Передача"
    #define ClrScreen       6       // Команда "Очистить экран"
    
    //------------------------------------------------------------------
    const int lTotal = 18;          // Количество строк в окне терминала
    const int cTotal = 80;          // Количество символов в одной строке
    const int tabs = 8;             // Кратность (в символах) позиций табуляции
    
    static int cCount, lCount;      // Номер текущей позиции в строке и Номер текущей строки
    static char savMark;            /* Сохраненный код символа, который скрыт курсором
                                       т.к. курсор может перемещаться на экране не только вперед, но и вверх,
                                       вниз и назад, то перед перемещением курсора в другую позицию необходимо
                                       сохранять код символа в том знакоместе, в котором окажется курсор после перемещения.
                                       И при последующем перемещании курсора возвращать сохраненный символ в знакоместо
                                       ранее занимаемое курсором */
                                    /* Курсор - мигающий символ '_', указывает знакоместо в окне терминала,
                                       в которое будет вводится очередной символ. Координаты курсора однозначно
                                       определяются переменными cCount и lCount */
    static BOOL init;               // Флаг инициализации(устанавливается после первого вызова макроса)
    
    BYTE byReceive[3] = {0x00};     /* Буфер приема для СОМ порта
                                       буфер приема имеет и второе назначение. Т.к. все, что пришло в буфер приема
                                       от удаленного терминала безусловно должно быть выведено на экран.
                                       Чтобы использовать этот же фрагмент кода вывода на экран и для вывода символов,
                                       вводимых с клавиатуры, буфер приема будет использован и при вводе данных с клавиатуры */
    BYTE bySend[3] = {0x00};        // Буфер передачи для СОМ порта
    BYTE *pBuff;                    // Указатель адреса символа в памяти регистров, отображаемых в окне терминала
    int i, count, tmp;
    BOOL flagSend, flagVisible;     // флаги - требуется вывод в СОМ порт, требуется вывод на экран
    
    //--------------------------------------------------------------------
    //pBuff = (BYTE *)&buffer + lCount * cTotal + cCount; //Установить указатель в текущую позицию курсора
    //--------------------------------
    
    //if ((lCount == 0) && (cCount == 0) && (savMark == 0)) { //Clr Screen.
    if (GetKbdStatus(ClrScreen) || !init) {	//Clr Screen
        ResetKbdStatus(ClrScreen);              //Сбросить бит команды
        init = TRUE;                                   
        memset(&buffer, ' ', cTotal * lTotal);  //Во все байты всех регистров отображаемых в окне терминала
        savMark = ' ';                          //будут записаны коды пробела - произойдет "очистка" окна терминала
        lCount = cCount = 0;                    //курсор в правый верхний угол окна терминала
        pBuff = (BYTE *)&buffer;                //Установить указатель на начало буфера окна
    
    }
    else pBuff = (BYTE *)&buffer + lCount * cTotal + cCount; //Установить указатель в текущую позицию курсора
    
    //-------------------------------
    if (!GetKbdStatus(RunEnable)) {             //Если работа макроса запрещена, то
        ResetKbdStatus(Repead);                 //"отжать" кнопку "rep"
        ResetKbdStatus(sender);                 //Выключить индикатор "Передача"
        ResetKbdStatus(receiver);               //Выключить индикатор "Прием"
        dwInput = 0;                            //Очистить буфер клавиатуры
        *pBuff = savMark;                       //Восстановить символ, скрытый курсором
        return;                                 //Завершить работу макроса
    }
    //-----------------------------
    flagSend = GetSetupStatus(Echo);            //Получить состояние настроек "Эхо" и "Показывать введенные символы"
    flagVisible = GetSetupStatus(Visible);
    
    Enter(PLC);                                 //Открыть порт PLC
    count = Receive(PLC, byReceive, 3, 100, 12);/*Читать 3 байта, TimeOut=100мс, TimeOutBytes=12мс(что это за зверь я не знаю)
                                                  значения тайм-аутов подбирал методом "научного тыка" на скорости 57600.
                                                  возможно, что при уменьшении скорости до 9600 TimeOut немного увеличить.
                                                  Макрос с TimeOut=100 будет работать и на скорости 115200,если в гипертерминале Windows
                                                  не зажимать клавиши до автоповтора, т.к. в редких случаях единичные символы
                                                  при приеме могут "проглатываться". Передедача из панели на 115200 без нареканий.*/  
    if (count == NULL) {                        //Если ничего не пришло, то проверить наличие символов в буфере клавиатуры
        if (dwInput == 0) {                     //Если и в буфере клавиатуры пусто
            flagSend = flagVisible = FALSE;     //то ничего не отправлять и ничего не выводить на экран
            count = 0;
        }
        else {                                  //А если кто-то нажимал кнопки клавиатуры, то
            SetKbdStatus(sender);               //Включить индикатор "Передача"
            ResetKbdStatus(receiver);           //Выключить индикатор "Прием""
            memcpy(&byReceive, &Input, 3);      /*Копировать три байта из буфера клавиатуры в буфер передачи
                                                  последующие манипуляции будем проводить с содержимым буфера передачи
                                                  его же в дальнейшем будем отправлять через СОМ порт и выводить на экран */
            //Проверить состояние управляющих кнопок 
            if (GetKbdStatus(Shift)) {                                       //Если нажата кнопка Shift
                if (((byReceive[0] >= 0x41) && (byReceive[0] <= 0x5A)) ||    //и введен код заглавной латинской буквы
                    ((byReceive[0] >= 0x61) && (byReceive[0] <= 0x7A)) ||    // или строчной латинской буквы
                     (byReceive[0] >= 0xC0))  ReversBit(byReceive[0], 5);    // или любой русской буквы, то поменять заглавную на строчную или наоборот
                else if ((byReceive[0] == 0xA8) ||                           // если введен код Ё или ё
                         (byReceive[0] == 0xB8)) ReversBit(byReceive[0], 4); // то и их поменять местами            
            }
            if (GetKbdStatus(Ctrl)) {      //Если нажата кнопка Ctrl, то заменить ЛАТИНСКИЕ БУКВЫ на управляющие коды
                if (((byReceive[0] >= 0x41) && (byReceive[0] <= 0x5A)) ||
                    ((byReceive[0] >= 0x61) && (byReceive[0] <= 0x7A)))  byReceive[0] = byReceive[0] & 0x1F;
                else  byReceive[0] = 0;    // Если это были не буквы, то ничего не отправлять и не выводить
            }
            //Возможно нужно изменить способ передачи символов CR(ctrl+M /Enter) или backspace - (ctrl+H /Delete)
            if (GetSetupStatus(CRLFsend) && (byReceive[0] == 0x0D)) byReceive[1] = 0x0A;    //Если нужно, то к CR добавить LF
            else if (GetSetupStatus(BS_BSsend) && (byReceive[0] == 0x08)) {                 //Если нужно, то к BS 
                byReceive[1] = 0x20;                                                        //добавить ' '
                byReceive[2] = 0x08;                                                        //и ещё один BS
            }
            count = strlen(byReceive);          //Возможно длина посылки теперь будет другой
            memcpy(&bySend, &byReceive, count); //Копировать то, что на вводили с клавиатуры в буфер передачи  
            flagSend = TRUE;                    //Разрешить передачу удаленному терминалу
                if (! GetKbdStatus(Repead)) {   //Если кнопка повтора не нажата, то 
                    dwInput = 0;                //очистить буфер клавиатуры
                    ResetKbdStatus(Ctrl);       //"отжать" кнопку Ctrl
                    ResetKbdStatus(Shift);      //"отжать" кнопку Shift 
                }                               /*А если кнопка повтора нажата, то код введенного символа и модификаторы Ctrl и Shift
                                                 запомнятся и будут заново отправлены в следующем цикле выполнения макроса */
            }
    }
    //Сюда попали, минуя обработку буфера клавиатуры, если буфер чтения СОМ порта не пустой
    else { 
        ResetKbdStatus(sender);                 //Выключить индикатор "Передача"
        SetKbdStatus(receiver);                 //Включить индикатор "прием"
        flagVisible = TRUE;                     //Выводить на экран содержимое буфера приема
        memcpy(&bySend, &byReceive, count);     //Копировать в буфер передачи из буфера приема все принятые символы
                                                //в неизменном виде на случай необходимости Эха удаленному терминалу
        if (GetSetupStatus(CRLFreceive) && (byReceive[0] == 0x0D)) {//Если нужно, то в буфере приема к полученным CR добавить LF
            byReceive[Min(2, count)] = 0x0A;
            count = Min(3, count+1);
        }
    }
    //------------------------------------
    // В эту точку можем попасть или после приема данных от удаленного терминала или после ввода символов с клавиатуры
    // и всегда если буфер приема из СОМ порта был пуст и буфер клавиатуры тоже пуст
    if ((count != NULL) && flagSend) {  //отправка данных удаленному терминалу необходима если есть, что отправлять и 
                                        //если, например включен флаг Эхо
        Send(PLC, bySend, count);       //отправка даанных удаленному терминалу
    }
    Leave(PLC);                         //Закрыть СОМ порт   
    //------------------------------------
    //Эта часть программы - вывод информации на экран
    if ((count == 3) && (byReceive[0] == 0x1B) && flagVisible) {    //Если в буфере приема три символа и код первого символа 0x1B
                                                                    // то в буфере Escape последовательность
            *pBuff = savMark;                               //восстановить в текущем знакоместе символ скрытый курсором 
            tmp = MAKEWORD(byReceive[2], byReceive[1]);     //Собрать два оставшихся в буфере символа "в кучку"
            switch (tmp) {
                case 0x5B41:    //Up                        //Это кнопка стрелка вверх
                    if (lCount != 0) {                      //Пока не достигнем верха окна терминала
                        lCount--;                           //переместим курсор в ту же позицию вышележащей строки
                        pBuff = pBuff - cTotal;             //пересчитать адрес нового положения курсора
                    }
                    break;
                case 0x5B42:    //Down                      //Это кнопка стрелка вниз
                    if (lCount != lTotal -1) {              //Пока не достигнем низа окна терминала
                        lCount++;                           //переместим курсор в ту же позицию нижележащей строки
                        pBuff = pBuff + cTotal;             //пересчитать адрес нового положения курсора
                    }
                    break;
                case 0x5B44:    //Left                      //Это кнопка стрелка влево
                    if (cCount != 0) {                      //Пока не достигнем левого края окна терминала
                        cCount--;                           //переместим курсор на одну позицию влево в той же строке
                        pBuff --;                           //пересчитать адрес нового положения курсора
                    }
                    break;
                case 0x5B43:    //Right                     //Это кнопка стрелка вправо
                    if (cCount != cTotal - 1) {             //Пока не достигнем правого края окна терминала
                        cCount++;                           //переместим курсор на одну позицию вправо в той же строке
                        pBuff++;                            //пересчитать адрес нового положения курсора
                    }
                    break;
                case 0x5B48:    //Home                      //Это кнопка Домой, т.е. 
                    lCount = 0;                             //переместить курсор в левый верхний угол окна терминала
                    cCount = 0;
                    pBuff = (BYTE *)&buffer;                //пересчитать адрес нового положения курсора (указатель на начало буфера)
                    break;
                case 0x5B4B:    //End                       //Кнопка End
                    *pBuff = ' ';                           //В позиции курсора записать пробел
                    memset(pBuff, ' ', cTotal - cCount);    //Удалить все символы от текущего положения курсора до донца строки
                    break;
            }
            savMark = *pBuff;                               //Сохранить символ в знакоместе экрана, на которое теперь будет указывать курсор
    }
    else if ((count != NULL) && flagVisible) {              //А если в буфере не оказалось Escape последовательность, а он не пустой
        for (i=0; i<count; i++) {                           //то будем выводить по очереди все символы, которые обнаружим в буфере
            *pBuff = savMark;                               //восстановить в текущем знакоместе символ скрытый курсором 
            switch (byReceive[i]) {                         //кроме печатных символов в буфере могут находится управляющие символы
                                                            //некоторые из управляющих символов используются для управления выводом
                case 0x07:                                  //Bell - (ctrl+G). Удаленный терминал может прислать звуковой сигнал
                    Beep();                                 //и если звук в панели не запрещен (PFW[2] == 0), то услышим короткий Бип
                    break;
                case 0x8:                                   //backspace - (ctrl+H /Delete)
                    if (cCount != 0) {                      //если курсор не в начале строки
                        cCount--;                           //то прееместить его на одно знакоместо влево
                        pBuff--;                            //пересчитать адрес нового положения курсора
                    }
                    break;
                case 0x9:                                   //Tab - (ctrl+I) Табуляция - перемещение курсора вправо до ближайшей позиции кратной "tabs"
                    if (cCount < (cTotal - tabs)) {         //Если до конца строки осталось болше чем значение "tabs"
                        tmp = tabs - cCount % tabs;         //то подсчитать сколько символов от текущего положения курсора до до ближайшей позиции кратной "tabs"
                        cCount = cCount + tmp;              //переместить курсор в новую позицию
                        pBuff = pBuff + tmp;                //пересчитать адрес нового положения курсора
                    }
                    else {                                  //если до конца строки символов осталось меньше чем значение "tabs"
                        tmp = cTotal -1 - cCount;           //то подсчитать сколько их осталось
                        cCount = cTotal-1;                  //и новое положение курсора, в этом случае, однозначно д.б. в конце строки
                        pBuff = pBuff + tmp;                //пересчитать адрес нового положения курсора
                    }
                    break;
                case 0xA:                                   //LF - line feed (ctrl+J). Перевод строки
                    lCount++;                               //переместим курсор в ту же позицию нижележащей строки
                    pBuff = pBuff + cTotal;                 //пересчитать адрес нового положения курсора
                    break;
                case 0xD:                                   //CR - carriage return (ctrl+M /Enter). Возврат каретки
                    cCount = 0;                             //переместим курсор в начало текущей строки
                    pBuff = (BYTE *)&buffer + lCount * cTotal;//пересчитать адрес нового положения курсора
                    break;
    //следующие три управляющих кода пока не используются
                case 0x03:        //ETX - end of text (ctrl+C) 
                case 0x04:        //EOT - end of transmission (ctrl+D)
                case 0x1A:        //ctrl+Z (SUB -substitute)
    //А теперь выводим печатные символы
                default:
                    *pBuff = byReceive[i];                  //очередной символ из буфера приема поместить по адресу курсора
                    pBuff++;                                //декремент указателя адреса
                    cCount++;                               //переместить курсор в следующую позицицию строки
                    break;
            }
            savMark = *pBuff;                               //Сохранить символ в знакоместе экрана, на которое теперь будет указывать курсор
        }
    }
    //----------------------------------------------------------------------
    if (cCount >= cTotal) {                                 //если текущее(расчетное) положение курсора оказалось больше чем символов в одной строке
        cCount =0;                                          //то курсор переместить в начало следующей строки
        lCount++;
    }
    if (lCount >= lTotal) {                                      //если после этого нмер текущей строки стал больше чем есть строк в окне терминала
        memmove(&buffer, &buffer+cTotal/2, ((lTotal-1)*cTotal)); //по переписать все содержимое строк начиная со второй и по последнюю в начало буфера окна
        lCount=lTotal-1;                                         //теперь номер текущей строки будет на 1 меньше, чем общее количество строк
        memset(&buffer+(lTotal-1) * cTotal/2, ' ', cTotal);      //освободившуюся последнюю строку очистить (заполнить пробелами)
        savMark = ' ';                                           //естественно новый смвол скрытый курсором будет - пробел
    }
    //---------------------------------------------------------------------
    //Мигающий курсор
    if (GetPSBStatus(4)) *pBuff = '_';           //0,5 сек показывать курсор '_'   
    else                 *pBuff = savMark;       //0,5 сек показывать символ, скрытый курсором
    //-----------------------------------------
    Команда на очистку окна теперь передается через 6 бит регистра KbdStatus(PSW260). И кнопка "Clr" теперь обычный переключатель.
    Захват-1.png

    ЗЫ.
    Режим выполнения макроса, как и в предыдущем проекте, должен быть "Параллельный" !!!
    Захват-2.png
    Вложения Вложения
    Последний раз редактировалось petera; 09.05.2017 в 03:59.
    26 лет деятельности в области промышленной безопасности

  4. #4

    По умолчанию

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

Похожие темы

  1. СП310 WEB Визуализация
    от Dmitry-357 в разделе Панели оператора (HMI)
    Ответов: 1
    Последнее сообщение: 22.03.2017, 21:04
  2. Картинки для СП310
    от Игорь-КИП в разделе Панели оператора (HMI)
    Ответов: 0
    Последнее сообщение: 16.03.2017, 19:54
  3. пр200+сп310
    от zamnarzanom в разделе Программируемые реле
    Ответов: 5
    Последнее сообщение: 10.10.2016, 09:13
  4. Плк 110 + 2 сп310
    от Kostennikov в разделе Помощь Разработчикам
    Ответов: 4
    Последнее сообщение: 12.07.2016, 12:59

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •