Lotus Notes / Domino – прекрасная платформа для создания мощных корпоративных
информационных систем, ориентированных на групповую работу с электронными
документами. В своей работе над комплексной медицинской информационной системой
"Кондопога" мы на основе тщательного анализа средств разработки и
имеющихся на рынке СУБД выбрали Lotus Notes / Domino в качестве основы всей
системы. Разработка осуществляется с 1999 года, за это время мы постепенно
перешли с версии 4.6 на версию R 5, а затем – на R 6. В данный момент идет
тестирование R 6.5 на совместимость с существующим ПО.
Lotus Notes / Domino полностью отвечает ключевым требования к созданию медицинской
информационной системы по надежности, безопасности, отказоустойчивости и
масштабированию. Работа пользователя в этой среде в максимальной степени
приближена к привычной работе с документами – фактически, бумага и авторучка
у медицинских сотрудников заменена на компьютер. Формы электронных документов
могут быть разработаны по точной аналогии с их бумажными аналогами (при необходимости),
а стандартные средства для работы с документами (создание, редактирование,
печать, отправка по e - mail, электронная цифровая подпись и т.д.) требуют
от пользователя минимального объема обучения.
Однако, как и в любой информационной технологии, имеется ряд недостатков,
с которыми приходится мириться и искать пути их преодоления. Основной их
недостатков Lotus Notes / Domino для применения в медицинской сфере – это
слабая поддержка таблиц в электронных документах. На практике даже с точки
зрения пользователя встроенные в клиентское программное обеспечение Lotus
Notes средства для работы с таблицами значительно уступают аналогичным инструментам
в Microsoft Office. А с точки зрения инструментария разработчика средства
для управления таблицами тем более являются малоэффективными. Некоторые изменения
в этом направлении были сделаны в версии R 6 Domino, однако и они являются
недостаточными. Фактически, в Lotus Notes таблица, как средство отображения,
управления и хранения информации, отсутствует как класс. Но это и понятно
– ведь Lotus Notes – это, прежде всего, объектно-ориентированная СУБД, предназначенная
для групповой работы над документами.
Вместе с тем в нашей работе поддержка табличного формата хранения информации
является неотъемлемой функцией системы. Некоторые документы (лист назначений,
например) и некоторые приложения (бухгалтерия, аптека, склад, автоматизация
службы питания и т.д.) несравненно более эффективно работаю под управлением
реляционной СУБД, чем в среде Lotus Notes / Domino. Все это породило необходимость
совместного использования Lotus Notes / Domino и реляционной СУБД, в качестве
которой был выбран Microsoft SQL Server [1]. В качестве средства разработки
в Lotus Notes / Domino используется специальное программное обеспечение Lotus
Designer, позволяющее создавать мультиплатформенные приложения на Visual
Basic -подобном языке Lotus Script, @-формулах или Java Script. Это мощное
приложение позволяет за очень небольшое время разрабатывать необходимые программы
как для выполнения в среде Lotus Notes, так и для работы в обычном браузере
Internet. Однако для создания приложения для реляционной СУБД его возможностей
явно недостаточно. Поэтому в качестве дополнительного инструментария мы используем
Borland Delphi (в настоящее время – версию 6.0).
Одним из серьезных препятствий на использовании Delphi является задача совместного
доступа как к информации в реляционной базе данных, так и для доступа к базам
данных Lotus Notes / Domino. Для решения этой задачи имеется несколько подходов:
- Использование компонентов сторонних производителей (http://www.torry.net/index.htm, http://www.geocities.com/SiliconValley/Peaks/8307/)
- Использование приложения Lotus Notes SQL (http://www-10.lotus.com/ldd)
- Разработка собственных компонентов, используя Notes API (http://www.notesnet.ru/)
- Доступ к ресурсам Lotus Notes посредством OLE.
Первый подход подразумевает использование компонентов для доступа к Lotus
Notes. При этом программы могут обращаться к базам данных и документам,
используя инкапсулированные в эти компоненты свойства и методы. Мы апробировали
имевшиеся в свое время предложения и не нашли решения, полностью удовлетворяющего
наши требования. Использование компонентов вносило нестабильность в создаваемые
приложения, которые нам не удавалось быстро локализовать и исправить, т.к.
либо с разработчиками не возможно было наладить контакт, либо тех.поддержка
требовала дополнительных финансовых затрат и времени.
Второй подход также, к сожалению, не отвечал требованиям. При этом Notes
SQL фактически эмулирует обращение к базе данных Lotus Notes, как к обычной
реляционной таблице. Тестирование различных версий Notes SQL показала нестабильность
этого программного обеспечения. Особенно ярко недостатки Notes SQL проявлялись
при обработке больших объемов информации – в случайные моменты работы программы
возникали неустранимые ошибки, которые приводили к полном прекращению работы
программ.
Третий подход является более предпочтительным, однако и от него мы со временем
отошли в силу его трудоемкости, большой сложно написания программы, массы
низкоуровневого кода и высоких требований к знанию внутренний архитектуры
Lotus Notes.
Первое время доступ к Lotus Notes посредством OLE казался нам неприемлемым
вариантом с точки зрения скорости работы. Однако наш 5-летний опыт работы
доказал высокую устойчивость программ на основе этого подхода и вполне приемлемую
скорость обработки информации.
Далее мы на примерах покажем, как написать приложение в среде Borland Delphi
для баз данных Lotus Notes.
1. Инициализация сессии.
Основной принцип в написании программ состоит в использовании встроенных
классов Lotus Notes в коде программ. Для этого в первую очередь необходимо
инициализировать сессию связи с Lotus Notes. Для этого требуется, чтобы
клиентское программное обеспечение Lotus Notes было инсталлировано на каждом
компьютере, использующим программу и подключено к одному или нескольким серверам
Domino.
Создадим новое приложение. В разделе uses главного окна приложения укажем
ComOBJ – это библиотека, позволяющая вызывать и обращаться к OLE -объектам
[4].
В разделе public объявим переменные, общие для всего приложения:
public
{ Public declarations }
MySession : OLEVariant; // текущая сессия Lotus Notes
MyLNDataBase : OLEVariant; // база данных Lotus Notes...
Теперь необходимо написать обработчик события OnCreate главной формы приложения,
в котором мы должны создать объект NotesSession, чтобы, используя его в
дальнейшем, иметь возможность в рамках одного приложения обращаться сразу
к нескольким базам данных, серверам, документам, представлениям и т.д. Обработчик
должен иметь следующий вид:
procedure TfmMain.FormCreate(Sender: TObject);
begin
MySession:= createOLEObject('Notes.Notessession');
if varisempty(MySession) then
begin
ShowMessage('Не могу создать сессию с сервером Lotus Notes');
Exit;
end;
end;
Следует иметь в виду, что в рамках одного приложения следует только один раз
инициализировать объект Notessession, т.к. каждая последующая инициализация
будет закрывать предыдущую сессию, а все объекты, созданные на основе этой
сессии, потеряют свою актуальность и их обработка будет невозможна.
2. Доступ к базе данных
После того, как мы инициализировали сессию связи с Lotus Notes, мы можем
обращаться к любым серверам Lotus Domino и базам данных на них. Принципиально
получить доступ к БД Lotus Notes можно 2 способами – либо обратиться к текущей
базе данных, открытой в Lotus Notes, либо вызвать соответствующий метод NotesSession
и открыть любую другую БД [3]. Последний случая является наиболее востребованным,
поэтому рассмотрим его:
procedure TMyButtomClick(Sender: TObject);
var MyServer: string;
begin
// Необходимо вычислить имя сервера,
// на котором находится необходимая нам БД
MyServer:=...
// Теперь открываем БД – например, откроем адресную книгу сервера
MyLNDataBase:=MySession.GetDataBase(MyServer, ‘names.nsf’);
end;
Мы в своей работе используем реестр Windows, в котором храним имя сервера
Domino по умолчанию, к которому подключаются приложения. Для упрощения разработки
и администрирования программ мы использую функцию GetDefaultServerName, которая
работает по следующему алгоритму:
- При вызове функции она проверяет наличие определенного ключа в реестре
Windows.
- Если этот ключ отсутствует или он содержит пустое значение, функция обращается
к файлу notes.ini и считывает из него значение параметра почтового сервера
Notes. Имя сервера записывается как значение ключа Windows
- Если ключ в реестре Windows имеется и не содержит пустое значение, функция
считывает его и возвращает в качестве ответа.
Таким образом, при первом старте приложения оно самостоятельно выполняет настройку
реестра Windows на подключение к текущему серверу Domino. Если в дальнейшем
требуется перенаправить работу приложения на другой сервер, то либо пользователь
при помощи встроенных в приложение визуальных средств, либо администратор сети
вручную меняют значение ключа. Т.о. со следующего запуска начинает обращаться
к другому серверу – обеспечивает поддержка мультисерверной конфигурации информационной
системы.
3. Работа с базой данных
Из программы, написанной в Borland Delphi, доступны практически все свойства
и методы, предусмотренные разработчиками Lotus Notes / Domino. В том числе
Вы можете осуществлять навигацию по представлениям, осуществлять поиск документов
в базе данных, в том числе и гипертекстовый поиск и т.д. Особенностей по работе
с базой данных вследствие использования Delphi мы не обнаружили. Поэтому в
качестве примера приведем фрагмент кода, осуществляющий последовательный перебор
и считывание документов из коллекции документов NotesDocumentCollection базы
данных адресной книги сервера.
procedure TfmMainWindow.BitBtn1Click(Sender: TObject);
var DocumCount: longint; // количество документов в коллекции
i : longint; // шаг цикла
B1: OLEVariant; // переменная для объекта NotesDatabase
BodyQuery: ansistring;
C1: OLEVariant; // переменная для объекта NotesDocumentCollection
D1: OLEVariant; // переменная для объекта NotesDocument
begin
DocumCount:=0;
// Получаем доступ к БД.
B1:= MySession.GetDatabase(GetDefaultServerName,'names.nsf');
BodyQuery:='Form = "Person"';
// Для поиска используем специальную функцию LNSearch
C1:=LNSearch(MySession,B1,’Пример запроса’,BodyQuery);
DocumCount:=C1.Count;
if DocumCount=0 then Exit; // искомые документы не найдены
D1:=C1.GetFirstDocument;
for i:=1 to DocumCount do
begin.... здесь осуществляется обработка документа
D1:=C1.GetNextDocument(D1);
end;
end;
В этом примере программа обращается к текущему серверу и открывает на нем
базу данных адресной книги. Затем, используя специально разработанную функцию
LNSearch, производит поиск документов в базе данных. Если не найдено ни одного
документа, то работа процедуры завершается. Если какие-то документы найдены,
то они последовательно обрабатываются в цикле. Применение специальной функции
LNSearch обусловлено тем, что стандартный метод Search в классе NotesDatabase, кроме формулы для поискового запроса, требует передать дату самого старого
документа, который этот запрос сможет вернуть в качестве результата. При этом
дата должна быть передана не в качестве переменной типа TDate или TDateTime, а в качестве OLEVariant -переменной, созданной как объект класса NotesDataTime.
function LNSearch(LNSession, LNDataBase: OLEVAriant;
Logo: string;query: string):OLEVariant;
var r1:WideString;
r2: OLEVariant;
r3: Smallint;
C1: OleVariant;
begin
r1:=query;
r2:=LNSession.CreateDateTime('01.01.1990'); // здесь может быть любая дата
r3:=0;
C1:=LNDataBase.SEARCH(r1,r2,r3);
Result:=C1;
end;
Отметим, что по нашим наблюдениям, при написании программ в Borland Delphi
следует стремиться использовать навигацию по представлениям вместо использования
метода search. При этом скорость обработки одной и той же коллекции документов,
полученной из представления, примерно на 40% выше, чем при обработке документов,
полученных поиском в базе данных.
4. Работа с документами
В отличие от работы с базами данных, обработка документов Lotus Notes имеет
массу подводных камней. При этом в программах приходится выполнять много одинаковых
операций, которые целесообразно выделять в отдельные функции. Вначале рассмотрим
стандартную функцию считывания текстового значения поля из документа.
function LNGetFieldStrValue(Document: OLEVariant;
FieldName: string;
DefaultValue: string): AnsiString;
var SendValue, RetValue: OLEVariant;
TmpS: Ansistring;
MyPos: integer;
begin
TmpS:=' ';
if FieldName<>'' then
begin
SendValue:=FieldName;
if not varisempty(Document) then
begin
Try
RetValue:=Document.HasItem(FieldName);
except
begin
RetValue:=false;
end; // do
end; // Try
if RetValue then
begin
RetValue :=Document.GetFirstItem(SendValue);
try
TmpS:=RetValue.Text;
except
TmpS:=DefaultValue;
end;
end else TmpS:=DefaultValue;
end else TmpS:=DefaultValue; // varisempty chek
end else TmpS:=DefaultValue;
if TmpS='' then TmpS:=DefaultValue;
Result :=tmpS;
end;
Эта простая функция позволяет значительно упростить написание программ, особенно
в случае, когда документ содержит большое количество полей, значения которых
необходимо считать и обработать. Необходимо отметить, что очень часто в полях
документов Lotus Notes хранится несколько значений или значения записаны с
символами, препятствующими корректной работе со строками в Borland Delphi.
Мы в таком случае используем перегруженную версию представленной функции, которая
может возвращать «очищенную» строку или определенную подстроку [4].
По аналогии с представленным примером разработаны и версии для числовых полей,
а также полей с датой или временем, что представляет особую трудность при написании
программ. В таком случае, кроме стандартных проверок на корректность документа,
наличия в нем указанного поля, функция может проверить тип возвращаемого значения
или выполнить необходимые преобразования. Например, возможна конвертация строкового
значения в число или смена символа разделителя разрядов на основании текущих
настроек операционной системы.
Несколько усложненный пример – это считывание значения поля в профайле. Как
известно, Lotus Notes, кроме стандартных документов, позволяет поддерживать
хранение информации в т.н. профайлах – документах, к которым можно обратиться
по имени формы и, как дополнение, по имени текущего пользователя. Для чтения
текстового значения из профайла рассмотрим следующую функцию:
function LNGetProfileField(MySession, MyDBName: OLEVariant;
MyServerName, MyProfileName, MyUserName, MyFieldName: string):string;
var D1: OLEVariant;
tmpS: AnsiString;
begin
if MyServerName='' then
tmpS:= GetDefaultServerName else tmpS:=MyServerName;
if varisempty(MyDBName) then
begin
ShowMessage('Фатальная ошибка! Переданный объект
<База данных> пуст. Продолжение невозможно!');
Exit;
end;
D1:=MyDBName.GetProfileDocument( MyProfileName, MyUserName);
if varisempty(D1) then
begin
ShowMessage ('Ошибка при получении профайла '+MyProfileName+'
из базе данных '+MyServerName+
' / '+MyDBName.Name+'. Продолжение невозможно!');
Exit;
end;
tmpS:=LNGetFieldStrValue(D1,MyFieldName,'',False);
Result :=tmpS;
end;
Как видно из примера, эта функция использует стандартную функцию LNGetFieldStrValue, представленную ранее, но перед этим выполняет ряд дополнительных проверок
и операций.
5. Выводы
Мы используем представленную технологию в практической разработке медицинской
информационной системы "Кондопога" вот уже в течение 5 лет. За это время многократно
убедились в прекрасной устойчивости и приемлемой скорости работы программ,
написанных на Borland Delphi для баз данных Lotus Notes / Domino. Фактически
мы убедились, что способны создавать программы на Borland Delphi, которые
используют весь арсенал встроенных в Lotus Notes классов [3].
Отметим, что со временем в арсенале программиста накапливается самая разнообразная
масса готовых функций и процедур, которые целесообразно аккумулировать либо
в виде подключаемых библиотек, либо в виде отдельных модулей (pas -файлов).
При этом, по нашим наблюдениям, время на разработку новой программы можно сократить
в несколько раз именно за счет использования готовых и отлаженных приложений.
А это позволяет снизить стоимость разработки и повысить устойчивость приложений,
что является уже не столько инструментарием разработчика, сколько экономическим
стимулом.
Постепенно нами была накоплена целая библиотека класса middleware, которая реализует
практически весь необходимый функционал для написания программ в Borland Delphi
для среды Lotus Notes. Это позволило разработать нашу информационную систему
таким образом, что взаимные недостатки реляционных и объектно-ориентированных
баз данных фактически полностью компенсируются взаимными достоинствами. Поэтому
пользователи ИС "Кондопога" одинаково комфортно используют и возможности совместной
работы над электронными документами Lotus Notes и встроенные в базы данных Domino
приложения, предоставляющие расширенные возможности работы с таблицами реляционных
баз данных, мгновенное построение диаграмм на основе данных из документов Lotus
Notes и т.д.
Литература:
- Гусев А.В., Дмитриев А.Г. Microsoft SQL Server против MySQL в медицинских
информационных системах. http://citforum.ru/ database/ articles/ msmysql/
- Гусев А.В., член-корр. РАМН Дуданов И.П., Романов Ф.А., Дмитриев А.Г. Особенности
в проектировании и практической разработке медицинской информационной системы. http://www.citforum.ru/ consulting/ articles/ med/
- Линд Дебби, Керн Стив. Lotus Notes и Domino R5. Энциклопедия пользователя:
Пер. с англ. /Дебби Линд, Стив Керн. – К.: Издательство «ДиаСофт», 2000. –
656 с.
- Кэнту М. Delphi 6 для профессионалов. – СПб.: Питер, 2002. – 1088 с.
Об авторах:
Гусев Александр Владимирович – к.т.н., ст. инженер-программист вычислительного
центра ОАО "Кондопога"
Дмитриев Александр Геннадьевич – инженер-программист вычислительного центра
ОАО "Кондопога"
Тихонов Станислав Игоревич – инженер-программист вычислительного центра ОАО