О разработке интерактивных приложений под ОС IBM i (aka AS/400)
Здравствуйте, уважаемые читатели. Меня зовут Владимир Лебедев, я работаю в Альфа-Банке и занимаюсь тем, что пытаюсь максимально упростить жизнь разработчиков АБС Equation, занимающихся разработкой приложений под операционную систему IBM i.
Сразу скажу, что литературный слог — точно не мой конек. Я больше про исследования и скрупулезное изучение технических возможностей, про поиск инженерных решений. Однако у нас в сообществе разработчиков IBMi в банке принято, что по итогам года выбираются законченные реализации, которыми мы делимся с читателями и за пределами Альфа-Банка. В этом году в их число попала и моя работа.
Также скажу, что пытливый читатель не найдет в статье сногсшибательных прорывов и идей, которые кардинально меняют мир вокруг. Скорее, работу можно рискнуть сравнить с процессом доказательства теоремы Ферма. Известно, что Пьер Ферма еще в 1637 году сформулировал свою великую теорему. Почти четыре столетия ученые пытались расколоть этот орешек. Но удалось это сделать только в 1994 году Эндрю Уайлсу, а в 2016 году этот гениальный норвежец получил за дело своей жизни Абелевскую премию. Доказательство теоремы Ферма не несет за собой исключительной практической ценности или стремления к славе и успеху, но в процессе решения задачи были найдены интересные идеи, выросли целые поколения ученых.
Мой скромный (но, похвалю себя =), честный и кропотливый) труд — он не про решения во фронтовом программном обеспечении Альфа-Банка. Хотя, надо признать, что мои коллеги здесь крайне преуспели, и банк занимает свое заслуженное место в рейтингах. Мой труд про исследования, которые являются неотъемлемой историей любого профессионального сообщества, стремящегося к саморазвитию на всех уровнях.
Итак, начнем. ОС IBM i и ее предшественницы — aka AS/400 — известны тем, что все ее интерактивные приложения до сих пор работают через так называемый зеленый экран (green screen или GS). Выглядит это примерно так:
Пример Green Screen-а
Если вы разработчик под IBMi, то, несомненно, согласитесь, что разработка такого рода приложений вызывает неудобства (а, если прямо сказать, то большую головную боль и внутреннее раздражение). Даже для продвинутых разработчиков (особенно для них) привыкших к разработке современных пользовательских интерфейсов на базе веб-технологий.
Надо признать, что при всем неудобстве интерактивные приложения с консольным GS до сих пор очень нужны многим зрелым, многолетним предприятиям. И Альфа-Банк не исключение. Такую «зеленую» картинку можно наблюдать на мониторе большинства сотрудников бэк-офиса. Green Screen для IBM i — это как SSH и SHELL для Unix-а. К слову, SSH и SHELL есть в IBM i. Но здесь вы попадаете в особый runtime, именуемый PASE, со своими правилами игры.
Важно отметить, что платформа IBMi поддерживает 2 базовых программных модели — уже устаревшую OPM (Original Program Model) и активно развиваемую на данный момент ILE (Integrated Language Environment). Если кратко, то под программной моделью здесь понимается совокупность решений операционной системы по квотированию ресурсов, возможностям кросс-языковых реализаций и т.д. Но при всех преимуществах новой ILE-концепции эффективность рекомендуемых IBM-ом способов разработки Green Screens оставляет желать лучшего.
Так как же разрабатываются интерактивные приложения IBM i?
Я уже упомянул, что разработка интерактивных приложений под IBM i с использованием традиционных подходов — занятие не самое увлекательное. А уж детальный рассказ про эту разработку вызовет зевоту или бурное негодование среди видавших виды. Однако, я не могу опустить это описание, так как это важно для общего понимания проблематики. Насколько подробно следить за мыслью — решайте сами.
Объекты, типы, подтипы
Стоит обратить внимание, что IBMi является одной из самых ранних объектных операционных систем. В отличие от традиционных Unix/Windows, здесь нет файлов в обычном понимании. Система оперирует специализированными объектами разных по назначению типов — файлы (БД и интерфейсы I/O), программы (исполняемый код), профайлы (учетные записи) и т.д. Внутри этих объектов прячутся как сами данные (согласно назначению), так и все необходимые метаданные, позволяющие правильно интерпретировать объект и правильно им воспользоваться.
Некоторые типы объектов имеют подтипы. Например, объект типа *FILE, подтипа PF — т.н. физический файл — инкапсулирует внутри себя все необходимое для работы с ним, как с таблицей БД. А тип *FILE c подтипом LF (логический файл) реализует индекс таблицы.
Обратим внимание на важный для описываемой истории подтип объекта *FILE — дисплейный файл (DSPF). Его предназначение как раз в описании пользовательского интерфейса (терминальный green screen). Интерактивное взаимодействие программы и пользователя выполняется именно через этот объект.
Форматы записи
Основное содержимое дисплейного файла — форматы записи. Каждый такой формат записи определяет шаблон внешнего вида пользовательского экрана или его части. Т.е. типизирует поля (например, только для вывода или ввода/вывода, текст или число), фиксирует их размерность и расположение на экране.
Поле — это наименьшая единица данных, которая распознается и обрабатывается системой управления данными. Форматы записи в дисплейных файлах определяются с помощью спецификации описания данных (DDS).
Описание файла описывает данные на трех разных уровнях:
- Уровень поля
- Уровень записи
- Уровень файла
Описание уровня поля позволяет дать системе подробные характеристики поля, такие как расположение на экране, допустимый тип данных, представление поля при операциях ввода и вывода, тип поля, возможность редактирования, выравнивание данных в поле и прочее.
Запись — это упорядоченный набор из одного или нескольких полей. Описание уровня записи позволяет вам сказать системе, как выглядит конкретная запись или ее формат записи. Запись является минимальной единицей обмена данными между системой и прикладной программой, независимо от наличия полей.
Описание уровня файла — это описание, которое применяется к файлу в целом. Для файла дисплея вы можете указать используемые форматы записи, дисплейную станцию, набор графических символов.
Использование дисплейных файлов
Формирование исходного файла дисплея осуществляется с помощью кодирования DDS.
Пример дисплейного файла в редакторе SEU
Да, печально, но дисплейный файл имеет только позиционную форму записи. Других форм на 2020-й год нет. Однако, давайте все же пройдем не самый увлекательный путь, которым ходили и в прошлом веке, чтобы двигаться дальше и искать пути решения.
Пример примером, давайте рассмотрим задачку.
Создадим дисплейный файл для некоторой программы WINDEMOR:
- программа принимает на вход два параметра
- если второй аргумент равен «A», выводит окно с сообщением «Внештатная ситуация №1»
- если второй аргумент равен «B», то выводит окно с сообщением «Внештатная ситуация №2»
- при любом другом аргументе выводит «Ошибка вызова».
- Окно можно закрыть клавишей F3. Нажатие любых других клавиш приводят к сообщению «Функциональная клавиша недопустима».
- В верхней рамке, окно должно содержать базовую информацию о модуле среды АБС Equation:
<ИМЯ ПРОГРАММЫ> <ВЕРСИЯ> <ТЕКУЩАЯ БИЗНЕС-ДАТА>
Пример окна с выводом сообщения
A*----------------------------------------------------------------
A* System Name - PUB400
A*
A* File Name - WINDEMOD
A* File Description - Демонстрация работы с окном
A* File Type - Display
A*L Level - 100
A*
A* Creation Date - 2020/02/25
A* Last Amendment Date - 2020/02/25
A*----------------------------------------------------------------
A* Change Control
A* ~~~~~~~~~~~~~~
A* Date Initials Amendment
A* ~~~~ ~~~~~~~~ ~~~~~~~~~
A* 2020/02/25 USRXXX Создано
A*
A*----------------------------------------------------------------
A* Purpose
A* ~~~~~~
A* Демонстрация работы с экраном
A*----------------------------------------------------------------
A* Indicators
A* ~~~~~~~~~~
A* Number Description
A* ~~~~~~ ~~~~~~~~~~~
A* 01-04 Attribute control indicators (message to display)
A* 10-13 External account number indicators
A* 71 Modify data tag
A* 73 Errors to display
A* 74 Warnings to display
A* 76 Protect branch
A* 83 Load message subfile
A* 90 Keyboard key pressed
A* 91 Rollup
A* 92 Rolldown
A* 93 Help key pressed
A* 94 Home key pressed
A* 95 Clear key pressed
A*----------------------------------------------------------------
A DSPSIZ(24 80 *DS3)
A REF(*LIBL/EQFREF)
A INDARA
A PRINT
A VLDCMDKEY(90)
A HOME(94)
A CLEAR(95)
A ALTHELP
A HELP
A CF02
A CF03
A CF04
A CF05
A CF06
A CF09
A CF10
A CF11
A CF12
A CF13
A CF14
A CF15
A CF16
A CF17
A CF18
A CF19
A CF20
A CF21
A CF22
A CF23
A CF24
A*----------------------------------------------------------------
A R HEADER
A WINDOW(7 10 7 59)
A BLINK
A LOCK
A KAPPG7 7A O 1 1COLOR(BLU)
A KAPLVL R O 1 9REFFLD(FPL)
A COLOR(BLU)
A ZLTITL R O 1 13REFFLD(ONM)
A DSPATR(HI)
A ZUDATE R O 1 49REFFLD(DAZ)
A*
A*----------------------------------------------------------------
A R WINDEMODA
A WINDOW(HEADER)
A CHANGE(84)
A RTNCSRLOC(&ZHRFMT &ZHFLD)
A BLINK
A INVITE
A OVERLAY
A PUTOVR
A RMVWDW
A*
A ZHFLD 10A H
A ZHRFMT 10A H
A 01N02 3 15'Внештатная ситуация №1'
A DSPATR(HI)
A N01N02 3 17'Внештатная ситуация №2'
A DSPATR(HI)
A N01 02 3 24'Ошибка вызова'
A DSPATR(HI)
A 6 1'F3=Выход'
A COLOR(BLU)
A*
A*----------------------------------------------------------------
A R OPTION
A CHANGE(84)
A BLINK
A LOCK
A OVERLAY
A PUTOVR
A N83 ERASE(MSGSFL)
A WINDOW(HEADER)
A ZLCMD 58A O 6 1COLOR(BLU)
A OVRDTA
A*
A*----------------------------------------------------------------
A R MSGSFL SFL SFLMSGRCD(06)
A FLDKEY SFLMSGKEY
A FLDPGM SFLPGMQ
A*
A*
A R MSGCTL SFLCTL(MSGSFL)
A WINDOW(HEADER)
A BLINK
A LOCK
A OVERLAY
A PUTOVR
A SFLDSP
A SFLINZ
A 83 SFLEND
A SFLSIZ(0020)
A SFLPAG(0001)
A FLDPGM SFLPGMQ(10)
A*
A*----------------------------------------------------------------
A R CLEAN
A ASSUME
A 24 2' '
Функции-расширения
В операционной системе существуют функции-расширения стандартного API для С/С++ или встроенные в языки инструкции, которые позволяют открывать дисплейные файлы и взаимодействовать с ними.
Важно отметить, что строки «R HEADER», «R WINDEMODA», «R OPTION» и другие определяют то, что называется записью дисплейного файла, или по-другому Record-форматом. Этот формат определяет структуру буфера, через который будет происходить обмен данными между эмулятором терминала пользователя и программой, которая работает с «дисплейником».
Если программе необходимо интерактивное взаимодействие с терминалом пользователя, то она должна записать в Record-формат, что хочет вывести в поля экрана (если программа ничего не хочет выводить на экран, ей все равно нужно записать пустые бланковые поля). После этого необходимо произвести чтение Record-формата, при этом управление передается операционной системе, которая выводит пользователю статическое содержимое (например, фраза «Внештатная ситуация №1» в позицию 3/15»).
Под спойлером можете посмотреть код программы на языке IBM RPG, который демонстрирует работу с данным окном и реализует логику приложения.
/TITLE Демонстрация работы с окном
*---------------------------------------------------------------------
* System Name - PUB400
*
* Program Name - WINDEMOR
* Program Title - Демонстрация работы с окном
*
*L Level - 100
*
* Creation Date - 25FEB20
* Last Amendment Date - 25FEB20
*---------------------------------------------------------------------
*P Purpose
*P ~~~~~~~
*P Демонстрация работы с окном
*P
*---------------------------------------------------------------------
*H Change Control
*H ~~~~~~~~~~~~~~
*H Project Programmer Date Level
*H ~~~~~~~ ~~~~~~~~~~ ~~~~ ~~~~~
*H WINDEMO USERXXX 25FEB20 100
*H Создано
*---------------------------------------------------------------------
/COPY EQCPYLESRC,HSPEC
H DFTNAME(WINDEMOR) WINDEMO
*
*---------------------------------------------------------------------
* FILES
*---------------------------------------------------------------------
FWINDEMOD CF E WORKSTN INFSR(*PSSR) USROPN
*
*---------------------------------------------------------------------
* DATA STRUCTURES
*---------------------------------------------------------------------
*
D @EM S 37 DIM(20)
D #Check1 S 1A
*---------------------------------------------------------------------
* PRE-INITIALISED DATA STRUCTURES
*---------------------------------------------------------------------
*
* KAPROG data structure
D KAPROG DS
D MISYS 1 6 INZ('MISYS ')
D KAPLVL 8 10 INZ('100')
D KAPPGM 12 17
D KAPPG7 12 18
*
/COPY EQCPYLESRC,DSJOBE_A
/COPY EQCPYLESRC,DSSYSE
*
/COPY EQCPYLESRC,DSEPMS
/COPY EQCPYLESRC,DPGMDS Data structure for progra
/COPY EQCPYLESRC,DMSGDS_A DS for dump message *
*---------------------------------------------------------------------
* NAMED CONSTANTS
*---------------------------------------------------------------------
/COPY EQCPYLESRC,STDCNST Standard program constant
*---------------------------------------------------------------------
/COPY EQCPYLESRC,STDFLDS
/COPY EQCPYLESRC,CMRINZS
/COPY EQCPYLESRC,CMRMAIN
/COPY EQCPYLESRC,CMRABRT
/COPY EQCPYLESRC,CMRPSSR
?*---------------------------------------------------------------------
?* C SPEC START
?*---------------------------------------------------------------------
C CSpecStart TAG
/COPY EQCPYLESRC,MAIN
*
*---------------------------------------------------------------------
* PARAMETER AND KEY LISTS
*---------------------------------------------------------------------
*
* Parameter lists
C *ENTRY PLIST
C PARM @Exit 1
C PARM @Chk1 1 № сообщ. для вывода
*=====================================================================
* MAINLINE
*=====================================================================
C MOVE #NO @Exit
C If %PARMS = 2
C Eval #Check1 = @Chk1
C Else
C Eval #Check1 = *Blanks
C EndIf
*
* Если #Check1 = 'A' => первое сообщение, #Check2 = 'B' => второе сообщение
* иначе - сообщение об ошибке вызова
*
C If #Check1 <> 'A' And #Check1 <> 'B'
C* если неверный параметр, устанавливаем флаг завершения
C MOVE #YES @Exit
C EndIf
C*
C MOVE #NO #EOP 1
*
C IF not %OPEN(WINDEMOD)
C OPEN WINDEMOD
C ENDIF
C Z-ADD *ZERO S3
C EXSR Z000
*
C DOW #EOP = #NO
C EXSR B000
C ENDDO
*
* Return to caller
C EXSR C000
*
C RETURN
*=====================================================================
* SUBROUTINES
*=====================================================================
*---------------------------------------------------------------------
C *INZSR BEGSR
/COPY EQCPYLESRC,INZSR
*---------------------------------------------------------------------
*
*S Program initialization
*
*---------------------------------------------------------------------
*
C *Dtaara Define DAJOBCTLE DSJOBE
C *Dtaara Define DASYSCTL DSSYSE
C IN *DTAARA
*
C MOVEL(P) @PGMID @PGM
C MOVEL(P) @PGMID FLDPGM
C MOVEL(P) @PGMID KAPPG7
*
C MOVE $ZDATE ZUDATE
*
C ENDSR
/EJECT
*---------------------------------------------------------------------
C B000 BEGSR
*---------------------------------------------------------------------
*
C READ WINDEMODA
*
C Z-ADD *ZERO S3
*
C MOVE *BLANKS #CMDKY 2
C MOVE '0' #TAGNO 1
*
C *IN90 CASEQ *ON B100
C END
C #TAGNO CABEQ '2' B000XT
*
*
B001 C IF #EOP = #NO
C EXSR Z000
E001 C ENDIF
*
C B000XT ENDSR
/EJECT
*---------------------------------------------------------------------
C B100 BEGSR
*---------------------------------------------------------------------
*
B001 C IF *INKC = *ON
C MOVEL(P) '03' #CMDKY
C MOVE #YES #EOP
C MOVE '2' #TAGNO
E001 C ENDIF
*
*
C MOVEL(P) 'KSM0807' DSEPMS
C EXSR USA10R
*
C ENDSR
/EJECT
*---------------------------------------------------------------------
C Z000 BEGSR
*---------------------------------------------------------------------
*
* Write header format
C WRITE HEADER
*
C Z-ADD *ZERO S3
*
C MOVE #YES @CLRQ
C CALLB 'UTM14C'
C PARM @PGM 10
C PARM @EM
C PARM S3 2 0
C PARM @CLRQ 1
*
* Write detail format
S001 C SELECT
W001 C WHEN #Check1 = 'A'
C MOVE *ON *IN01
C MOVE *OFF *IN02
W001 C WHEN #Check1 = 'B'
C MOVE *OFF *IN01
C MOVE *OFF *IN02
B001 C OTHER
C MOVE *OFF *IN01
C MOVE *ON *IN02
E001 C ENDSL
C WRITE WINDEMODA
*
C ENDSR
/EJECT
*---------------------------------------------------------------------
C C000 BEGSR
*---------------------------------------------------------------------
*
B001 C IF %OPEN(WINDEMOD)
C CLOSE WINDEMOD
E001 C ENDIF
*
C RETURN
*
C ENDSR
/EJECT
*---------------------------------------------------------------------
C USA10R BEGSR
*---------------------------------------------------------------------
*
C MOVEL(P) @ERM @MSGID
C MOVEL(P) @PMALL @MSGDA
*
C CALLB 'UTM02C'
C PARM @MSGID 7
C PARM @MSGDA 30
C PARM @MSGDT 132
C PARM @MSGSV 2 0
*
B001 C IF S3 < 20
C ADD 1 S3
C EVAL @EM(S3) = DSEPMS
E001 C ENDIF
*
C MOVE *BLANKS DSEPMS
*
C ENDSR
?*
/COPY EQCPYLESRC,PSSR_B
*
*---------------------------------------------------------------------
* COMPILE TIME ARRAY DATA
*---------------------------------------------------------------------
*
Итоги по DDS и недостатки
DDS представляет широкие возможности по работе с экраном дисплея и взаимодействия с прикладной программой, реализуя возможности протокола обмена данными терминала 5250 для IBM i. Несмотря на то, что кодирование файлов дисплея является фактически стандартом для взаимодействия с экраном, этот способ, на мой взгляд, имеет и ряд недостатков:
- Статическое определение атрибутов экрана (полей и записей) и, как следствие, невозможность динамического переопределения представления экрана.
- DDS является позиционным языком, то есть атрибуты и ключевые слова вводятся в определенных позициях (как на перфокарте).
- Сложность визуального восприятия и понимания исходных файлов.
- DDS довольно сложен и непривычен для изучения, содержит большое количество ключевых слов.
- DDS непривычен для большинства программистов, не знакомых с таким стилем кодирования.
- Сложность создания нетривиальных экранных конструкций и реализации манипуляций с экраном, не предусмотренных DDS.
Итак, мы с вами прошлись по головной боли. Посмотрели каких «милых позиционных монстров» разработчикам приходится, скрепя сердце, создавать для реализации даже простейших задач, при этом понимая, что к созданным изваяниям придется еще не раз возвращаться и вносить изменения по новым требованиям к задаче. Настало время осознать всю тяжесть темного прошлого и посмотреть в светлое будущее.
Альтернатива стандарту
Есть ли альтернатива DDS? Безусловно, есть. Внимание привлек интерфейс (API) менеджера динамического экрана (Dynamic Screen Manager — DSM), реализующий протокол потока данных терминала 5250 для IBM i.
Давайте поподробнее и подотошнее познакомимся с ним. Далее разберемся, какие же выгоды можно извлечь по сравнению с DDS. Читать именно этот пункт подробно или нет — зависит от того, собираетесь ли вы использовать результаты или захотите погрузиться в реализацию.
DSM API
DSM API представляет собой набор экранных интерфейсов ввода/вывода, которые обеспечивают возможности динамического создания и управления экранами для языков программирования ILE. Поскольку DSM-интерфейсы являются связанными, они доступны только для ILE-программ.
DSM API обеспечивает альтернативу существующему способу определения представления экрана вне программы посредством кодирования в DDS или UIM. Вместо этого программисты могут использовать в своих программах последовательность обращений к DSM для динамического определения и управления представлением экрана. В отличие от статических методов, интерфейс DSM обеспечивает приложениям, нуждающимся в более динамичном управлении экраном, необходимую гибкость.
Поддержка, предоставляемая DSM, варьируется от низкоуровневых интерфейсов для непосредственных манипуляций с экраном до работы с окнами.
DSM API состоит из следующих функциональных групп:
- Низкоуровневые сервисы (Low-level services), обеспечивают непосредственный интерфейс к командам потока данных 5250 для выполнения базовых операций экранного ввода/вывода и состоят из следующих функциональных групп:
- Screen Manipulation and Query APIs, для осуществления запросов и манипулирования состоянием экрана.
- Buffer Manipulation and Query APIs, для создания, опроса и манипулирования буферами ввода и команд, использующимися для взаимодействия с экраном.
- Screen Input APIs, для чтения данных полей и другой информации с экрана.
- Screen Output APIs, для определения полей и записи данных на экран.
- Window services, оконные сервисы, используемые для создания, удаления, перемещения и изменения размеров окон.
- Session services, сервисы сессии, обеспечивающие общий интерфейс прокрутки, который может использоваться для создания, опроса и манипулирования сессиями, для выполнения операций ввода и вывода с сессиями.
Атрибуты
Казалось бы, простейшая и для понимания, и для применения вещь. Однако здесь тоже кроются подводные камни. Многое зависит не только от правильной интерпретации имеющейся документации, но и от накопленного опыта хождения по схожим граблям.
В общем случае вывод информации на экран не отличается от других подобных устройств. Информация выводится блоками из одного или более символов цветом предшествующего ей атрибута. Атрибут определяет цвет всех выводимых за ним символов, пока не встретится следующий атрибут, и так далее. При отсутствии атрибутов применяется атрибут зеленого цвета. Каждый атрибут занимает одну позицию и на экране выглядит как пустая позиция (пробел).
Например:
X — символ
R — атрибут красного цвета
G — атрибут зеленого цвета
B — атрибут синего цвета
На экране это может выглядеть так:
Ввод информации осуществляется с помощью полей ввода, создаваемых и обслуживаемых терминалом. Количество полей ввода ограничено, допускается одновременное существование не более 256 полей. Характеристиками полей (цветом лидирующего атрибута, длиной, типом и способом ввода данных, выравниванием и т.д.) можно управлять. Имеется набор клавиш перемещения внутри полей и между полями, а также набор клавиш завершения редактирования. Поля могут быть простыми и композитными, т.е. состоящими из нескольких простых полей и ведущими себя как одно целое. Поля предваряются лидирующим атрибутом, определяющим цвет поля, и завершаются атрибутом конца поля (всегда «зеленый на черном»).
Так выглядят на экране поля ввода:
Окна
Если вы разработчик и собираетесь «залезть» в DSM, то при работе с DSM важно понимать структуру и порядок формирования окон, а также порядок вывода и отображения информации на экране. Невнимание к этому аспекту может привести к удивительным или даже весьма комичным ошибкам, которые легко устраняются, когда педантично все разложишь по полочкам.
Окном является прямоугольная область экрана, не выходящая за его пределы. Положение и размеры окна определяются координатами левого верхнего угла окна и количеством строк и столбцов окна. Окно может иметь или не иметь рамку. В наличии могут быть не все элементы, составляющие окно.
Окно может не иметь рамки, но если она есть, то возможно определение атрибутов рамки. Атрибуты рамки одинаковы и определяют ее цвет. Лидирующий атрибут окна определяет цвет символов, выводимых в окно. Правый атрибут завершения — это не жестко определенный атрибут, а признак необходимости расчета завершающего атрибута и место для его вывода на экран.
Пример структуры окна
Окно с атрибутами
Так выглядит окно, созданное программой с учетом представленной структуры. Зеленое окно с синей рамкой на белом экране. Для наглядности выбраны инверсные цвета. Хочу напомнить, что символы имеют цвет, определяемый предваряющим их атрибутом. Цвет символов сохраняется, пока не встретится следующий атрибут. На этой картинке правый атрибут завершения окна во всех строках имеет значение «белый инверсный». Таким образом восстанавливается цвет экрана.
Наличие атрибутов обеспечивает правильное закрашивание рамки и информации в окне. Правильные цвета всех окон на экране обеспечивает программа управления окнами (терминал).
В наличии могут быть не все элементы, составляющие окно. Каждый элемент, относящийся к окну занимает одну позицию экрана, как лидирующий или завершающий атрибут, или две позиции экрана, как рамка или атрибуты рамки. В случае наличия всех элементов окна его ширина не будет превышать ширины экрана минус 6 позиций, а высота — высоты экрана минус 2 позиции.
Пример структуры окна без рамки и ее атрибутов
Окно без атрибутов
Терминал 5250 и DSM
Терминал 5250 имеет блочно-ориентированную организацию потока данных, а атрибуты на экране располагаются отдельно от символов информации. К тому же терминал сам исполняет функцию менеджера окон, отслеживает их взаимное расположение, перемещение и обеспечивает правильное отображение элементов окон в соответствии с их иерархией.
DSM в явном виде поддерживает все необходимые операции с экраном, хотя и довольно специфичен и непривычен в применении, что объясняется особенностями потока данных 5250. Первоначально была предпринята попытка максимально использовать возможности DSM в плане, касающемся оконных конструкций. Но это оказалось не лучшим вариантом, поскольку система управления окнами, восстановления содержимого окон и экрана была не самой удобной и не обеспечивала необходимых требований к визуальному восприятию, иногда передаче подлежал излишне большой объем информации, что в конце концов сказывалось на удобстве использования и быстроте программы. В то же время попытка реализации экранных окон своими силами показала неплохие результаты в плане быстродействия и удобства работы. В этом направлении и было принято решение двигаться далее.
Безусловно, базовые вопросы взаимодействия с терминалом 5250 никуда не делись и лежат в основе нашей разработки. Основными особенностями работы при этом является блочно-ориентированное взаимодействие с терминалом и наличие высоких функциональных возможностей последнего. Можно сказать, что терминал выполняет роль полноценного менеджера экрана, а DSM — обертки над протоколом обмена данными, скрывающей детали его реализации
ASDU для 5250. Решение или приговор?
Так получилось, что к моменту исследования у меня был достаточно богатый положительный опыт использования библиотеки CURSES. Этот опыт был для проекта Enterprise-уровня. Я взял на себя смелость посмотреть, насколько прежние мысли ложатся на требования, возможности и ограничения, предъявляемые со стороны DSM.
Кроме того, библиотека CURSES в UNIX-системах фактически является стандартом текстового ввода/вывода, и было принято решение попытаться использовать имеющийся опыт для написания функционала ввода/вывода для терминала 5250.
Снова погрузимся в теорию и терминологию. А куда без нее?
Элементы
Элементы, составляющие окно в нашей системе, не отличаются от аналогичных в терминале. При этом возможно создание следующих экранных конструкций:
- WIN — Прямоугольная область экрана, не превышающая его размеров, в которую возможен вывод данных, и с которой могут быть связаны поля ввода терминала. Фактически окно отражает область памяти, соответствующую его координатам на экране.
- SUBWIN — Прямоугольная область экрана, не выходящая за пределы окна (WIN) и разделяющая с ним одну и ту же область памяти. Использование SUBWIN бывает очень удобно при осуществлении сегментации окна (WIN).
- PAD — Область памяти, не имеющая привязки к окну или экрану, и способная отличаться от них по размерам в любую сторону. Синхронизация PAD и экрана может осуществляться отдельной командой. В остальном поведении PAD аналогичен окну WIN.
- SUBPAD — Структура, которая разделяет одну и ту же область памяти с PAD и не может превышать его по размерам. Аналогична SUBWIN, но для PAD.
Структуры
И если окна (WIN, SUBWIN) проецируются в содержащую их структуру целиком, то проекция PADа зависит от соотношения его размеров и внешней структуры. В случае, например, когда PAD больше экрана, на экран проецируется только часть PADа.
Основные конструкции
На базе структур созданы предопределенные конструкции:
- STDSCR — Имитирует всю область экрана, лежащую на самом нижнем уровне иерархии окон.
- NEWSCR — Виртуальный экран, содержащий в себе содержимое всех окон и STDSCR, для которых была выполнена операция обновления. Порядок обновления окон при выполнении этой операции будет определять содержимое экрана NEWSCR.
- CURSCR — Виртуальный экран, предположительно соответствующий физическому экрану терминала после выполнения операции обновления.
- STDSCR доступен для всех оконных операций, в то время, как использование NEWSCR и CURSCR носит, скорее, служебный характер.
Операции с окнами
Определены следующие операции с окнами:
- создание, удаление, изменение размеров, перемещение, обновление.
- копирование областей окон.
- определение областей прокрутки, прокрутка содержимого окна.
- вывод данных в окно и получение данных из окна.
- удаление и вставка строк в окно.
- получение информации о курсоре и управление положением курсора в окне.
- получение характеристик окна и управление ими.
Особенности ввода данных
Ввод данных обеспечивается терминалом. Это связано с тем, что эта функциональность реализована терминалом, и подавляющая часть клавиш недоступна для иного использования, кроме как для ввода информации в поля ввода терминала. Информация, введенная в поля ввода, становится доступной только по завершении редактирования. Таким образом идеология вывода информации в значительной степени базируется на идеях библиотеки CURSES, в то время как ввод обеспечивается исключительно терминалом. Эта ситуация порождает некоторые особенности синхронизации данных между прикладной программой и терминалом, к тому же переносит акцент в организации управления экраном и вывода данных в прикладную задачу.
Поля ввода на экране терминала создаются с помощью API DSM и инициализируются значениями с виртуального экрана.
Данные, введенные в поля ввода с клавиатуры, не отображаются ни в одном окне, а, следовательно, и в виртуальном экране программы. Поэтому необходимо прочитать введенную информацию, учесть ее изменения в программе, модифицировать переменные и отобразить ее в виртуальном экране программы, чтобы его содержимое совпало с экраном терминала.
Фигуры с выделенным красным цветом контуром отображают синхронизированные состояния терминала и виртуального экрана.
Создание FRAME
Для учета иерархии окон и более удобного управления ими была создана структура FRAME.
В своем составе FRAME имеет окна WIN и SUBWIN и, возможно, PAD. Окна разделяют одну и ту же область памяти, но SUBWIN меньше на величину рамки и сопутствующих элементов. Фактически это изолированная область вывода. Фреймы же поддерживают иерархию окон и позволяют обновлять их в нужном порядке, что обеспечивает целостность результирующего экрана. Кроме того, осуществляется надзор за атрибутами всей выводимой на экран информации с учетом иерархии окон. Каждый фрейм характеризуется наличием или отсутствием рамки, ее формой, строки заголовка и сноски, атрибутами (цветами элементов). В дополнение к операциям с окнами, фрейм позволяет работать с иерархией окон, перемещать окна в ней, делать их невидимыми и наоборот.
Вывод информации в окна осуществляется с некоторой оптимизацией, с отметкой измененных символов, чтобы в дальнейшем уменьшить объем передаваемых данных.
Вывод информации в эти оконные конструкции не приводит к изменению состояния экрана до вызова команды синхронизации содержимого окна и виртуального экрана и команды обновления физического экрана терминала. Фактически работа строится на предположении, что виртуальный и физический экраны соответствуют друг другу, и на мерах по обеспечению этого.
Поля ввода, управляемые терминалом, несколько выбиваются из этого порядка. Во-первых, поля ввода способны принимать данные не только с клавиатуры, но и с экрана. Это позволяет инициализировать их нужными значениями и изменять их содержимое при необходимости. Во-вторых, необходимо обеспечить синхронизацию полей ввода с соответствующими оконными структурами. Иначе возникает достаточно труднонаходимая неоднозначность. В связи с тем, что об окнах ASDU терминалу ничего не известно, а поля ввода создаются по определенным правилам и