English Version - www.oocities.org/vhpcg
VHP Computing Group
ТехнологииПродукцияМатериалыСпециалистыПартнёрыЗапись в Гостевую КнигуПросмотр Гостевой Книги

3 Сентября, 2001
Владимир Трухин
ведущий инженер-программист
ОАО "Воткинская ГЭС"
Fax: +7 (34241) 63297
E-mail:
vlt@gesvt.permenergo.ru
vlt@votges.ru

Дистанционная выгрузка приложений

Проблема

Приложения создаются для пользователей и запускаются пользователями. Будучи запущенным пользователем, приложение может работать с утра и до вечера. Пользователь в это время может беззаботно уйти обедать или совещаться в смежный отдел.

Но вот незадача, именно в это самое время Вам, как администратору системы или как разработчику, необходимо изменить структуры таблиц или выполнить срочные работы на сервере. Как быть с работающим приложением? Сбросить соединение пользователя или отключить сервер, несмотря на работающее приложение? Это может привести к потере данных. Дождаться появления пользователя? Но от этого могут пострадать другие клиенты, ожидающие завершения работ. Сбегать самому и выгрузить программу? Хорошая идея, если для этого не надо преодолевать десятки миль. Извечный вопрос: «Что делать?».

Решение

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

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

Всё это было бы очень хорошо, если бы не следующие моменты:

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

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

·     Приложения не должны внезапно исчезать с экрана.

·     Пользователь должен получить сообщение о причинах выгрузки приложения или предполагаемом времени возобновления работы.

·     Пользователь должен иметь отсрочку выгрузки приложения, чтобы успеть произвести ручную выгрузку приложения так, как он это делает обычно. При этом он должен видеть, сколько времени осталось до автоматического завершения.

Для реализации этих требований средство автоматической выгрузки приложения должно обладать следующими возможностями:

·     Объект класса автоматической выгрузки приложения должен быть невидимым до получения команды выгрузки приложения.

·       Объект класса должен реагировать на два события:

1.  Появление в каталоге файла, являющегося командой выгрузки этого приложения.

2.  Появление в каталоге файла, являющегося командой выгрузки всех приложений.

·     Объект класса должен хранить ссылку на процедуру завершения приложения, имитирующую действия пользователя по завершению приложения.

·     При получении команды выгрузки приложения объект класса должен стать видимым.

·     Командный файл должен содержать текст сообщения, которое считывается объектом класса и показывается пользователю.

·     На экземпляре класса должен располагаться индикатор прогрессии, показывающий время, оставшееся до полного завершения приложения.

·     По истечении времени ожидания действий пользователя, экземпляр класса должен запустить процедуру завершения приложения.

·     Экземпляр класса должен работать как в приложении с главным окном, так и в приложении без главного окна.

Индикатор прогрессии

В качестве индикатора прогрессии можно использовать любой имеющийся класс индикатора процесса или элемент ActiveX. Предположим, что у нас нет ни того, ни другого. Создать индикатор процесса совсем не трудно. Достаточно использовать контейнер, как базовый класс, и разместить в нём объекты изображающие индикатор. Это могут быть объекты класса SHAPE. Первый будет показывать общую длину процесса, другой текущее значение процесса. 

Определение такого класса может выглядеть следующим образом:

DEFINE CLASS progressbar AS container

** Длина полосы индикатора
Width = 100
** Высота полосы индикатора
Height = 20
BackStyle = 0
BorderWidth = 0

** Максимальное значение переменной процесса
MaxValue = (this.Width)

** Текущее значение переменной процесса
CurrentValue = 0
Name = "progressbar"

** Объект, показывающий общую длину процесса
ADD OBJECT border AS shape WITH ;
** Разместить на всей площади контейнера
Top = 0, ;
Left = 0, ;
Height = (this.parent.Height), ;
Width = (this.parent.Width), ;
Name = "Border"

ADD OBJECT bar AS shape WITH ;
** Разместить по высоте контейнера с нулевой длиной
Top = 0, ;
Left = 0, ;
Height = (this.parent.Height), ;
Width = 0, ;
BorderStyle = 0, ;
BorderWidth = 0, ;
Curvature = 0, ;
FillStyle = 0, ;
FillColor = RGB(0,0,255), ;
Name = "Bar"

PROCEDURE currentvalue_assign
    ** При изменении текущего значения параметра
    ** необходимо перерисовать объект текущего значения
    LPARAMETERS vNewVal
    ** Анализ значения принятого параметра
    do case
        case m.vNewVal<0
            THIS.CurrentValue = 0
        case m.vNewVal>0 and m.vNewVal<=THIS.MaxValue
            THIS.CurrentValue = m.vNewVal
        case m.vNewVal>THIS.MaxValue
            THIS.CurrentValue = THIS.MaxValue
    endcase
    перерисовка объекта
    THIS.Bar.Width=int(THIS.Width*(THIS.CurrentValue/THIS.MaxValue))
ENDPROC

PROCEDURE Init
    ** Настройка объектов на размер контейнера
    this.Bar.Height=this.Height
    this.Bar.Width=0
    this.Border.Height=this.Height
    this.Border.Width=this.Width
ENDPROC

ENDDEFINE

Окно оповещения пользователя

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

После появления этого окна на экране, должен запуститься индикатор процесса. Когда индикатор достигнет своего граничного значения, должна запуститься процедура завершения приложения.

Примерный код для этого класса:

DEFINE CLASS alarmwindow AS form
Height = 218
Width = 298
Desktop = .T.
DoCreate = .T.
AutoCenter = .T.
BorderStyle = 2
Caption = "Remote Shutdown"
TitleBar = 1
AlwaysOnTop = .T.
BackColor = RGB(255,255,128)
MaxValue = (this.Progress.Width)
CurrentValue = 0
lightFlash = 0
Name = "alarmwindow"
ParentRef = .F.

    ADD OBJECT label1 AS label WITH ;
    FontBold = .F., ;
    FontSize = 9, ;
    WordWrap = .T., ;
    Alignment = 2, ;
    BackStyle = 0, ;
    Caption = "Attantion! “+;
              “This application will have been shutdowned. “+;
              “Save data and exit!", ;
    Height = 33, ;
    Left = 26, ;
    Top = 5, ;
    Width = 265, ;
    Name = "Label1"

** Изображение включенного индикатора
ADD OBJECT lighton AS image WITH ;
    Picture = "red.bmp", ;
    BackStyle = 0, ;
    Height = 12, ;
    Left = 11, ;
    Top = 8, ;
    Width = 12, ;
    Name = "LightOn"

** Изображение отключенного индикатора
ADD OBJECT lightoff AS image WITH ;
    Picture = "grey.bmp", ;
    BackStyle = 0, ;
    Enabled = .T., ;
    Height = 12, ;
    Left = 11, ;
    Top = 8, ;
    Visible = .F., ;
    Width = 12, ;
    Name = "LightOff"

** Таймер времени ожидания пользователя
ADD OBJECT timer AS timer WITH ;
    Top = 11, ;
    Left = 270, ;
    Height = 23, ;
    Width = 23, ;
    Enabled = .F., ;
    Interval = 500, ;
    Name = "Timer"

** Объект индикатора процесса
ADD OBJECT progress AS progressbar WITH ;
         Top = 45, ;
         Left = 6, ;
         Width = 285, ;
         Height = 20, ;
         Name = "Progress", ;
         Border.DefHeight = "", ;
         Border.DefWidth = "", ;
         Border.BackStyle = 0, ;
         Border.Name = "Border", ;
         Bar.DefHeight = "", ;
         Bar.Name = "Bar"

     ADD OBJECT inform AS editbox WITH ;
         FontBold = .F., ;
         Alignment = 0, ;
         BackStyle = 1, ;
         BorderStyle = 1, ;
         Enabled = .F., ;
         Height = 138, ;
         Left = 6, ;
         ReadOnly = .F., ;
         Top = 78, ;
         Width = 288, ;
         DisabledBackColor = RGB(255,255,128), ;
         DisabledForeColor = RGB(0,0,0), ;
         Name = "Inform"

     PROCEDURE maxvalue_access
          RETURN THIS.Progress.MaxValue
     ENDPROC

     PROCEDURE maxvalue_assign
         LPARAMETERS vNewVal 
         THIS.Progress.MaxValue = m.vNewVal
     ENDPROC

     PROCEDURE currentvalue_access
         RETURN THIS.Progress.CurrentValue
     ENDPROC

     PROCEDURE currentvalue_assign
         LPARAMETERS vNewVal
         THIS.Progress.CurrentValue = m.vNewVal
     ENDPROC

     PROCEDURE start
         this.Timer.Enabled=.T.
     ENDPROC

     PROCEDURE getcaption
         if type('gcVersion')='C'
             return allt(gcVersion)
         else
             return 'Attention'
         endif
     ENDPROC

     PROCEDURE Init
         LPARAMETERS lnTimeOut
         if type('lnTimeOut')!='N'
             this.MaxValue=lnTimeOut
         endif
     ENDPROC

     PROCEDURE timer.Timer
         this.parent.LightOn.Visible=!this.parent.LightOn.Visible
         this.parent.LightOff.Visible=!this.parent.LightOff.Visible
         local lnLightFlash
         lnLightFlash=this.parent.LightFlash
         this.parent.LightFlash=this.parent.LightFlash+1
         this.parent.CurrentValue=this.parent.CurrentValue+this.Interval
         if this.parent.CurrentValue>=this.parent.MaxValue
             local loShutDown
             loShutDown=this.parent.ParentRef
             loShutDown.TimeOut()
             this.parent.Release()
         endif
     ENDPROC

     PROCEDURE inform.Init
         this.Value='The application will have been unload to update it!'     ENDPROC

ENDDEFINE

Окно оповещения пользователя в приложении на базе формы верхнего уровня

Определим подкласс для класса окна оповещения пользователя. Цель этого подкласса – работа в приложении на базе формы верхнего уровня без главного окна Visual FoxPro.

DEFINE CLASS alarmwindowtlf AS alarmwindow

     Desktop = .F.
     ShowWindow = 1
     Name = "alarmwindowtlf"
     Label1.Name = "Label1"
     LightOn.Height = 12
     LightOn.Width = 12
     LightOn.Name = "LightOn"
     LightOff.Height = 12
     LightOff.Width = 12
     LightOff.Name = "LightOff"
     Timer.Name = "Timer"
     PROGRESS.Border.DefHeight = ""
     PROGRESS.Border.DefWidth = ""
     PROGRESS.Border.Name = "Border"
     PROGRESS.Bar.DefHeight = ""
     PROGRESS.Bar.Name = "Bar"
     PROGRESS.Name = "PROGRESS"

ENDDEFINE

Класс дистанционной выгрузки приложения

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

DEFINE CLASS powershutdownms AS timer

     Height = 23
     Width = 23
     Enabled = .F.

** Интервал поиска файла (по умолчанию)
     Interval = 60000

** Имя файла для выгрузки всех приложений
systemshutdownfile = "ShutDown.txt"

** Имя файла для выгрузки конкретного приложения
     taskshutdownfile = NULL

** Время ожидания действий пользователя
     shutdownwaittime = 180000

** Имя процедуры завершения приложения
     shutdownhandler = NULL

     PROTECTED alarmmess
     alarmmess = NULL

     Name = "powershutdownms"

     PROTECTED inform

** Запуск окна оповещения пользователя
     PROCEDURE startalarm
         local loAlarm
         this.AlarmMess=createobject('AlarmWindow')
         loAlarm=this.AlarmMess
         loAlarm.Inform.Value=this.Inform
         loAlarm.MaxValue=this.ShutDownWaitTime
         loAlarm.ParentRef=this
         loAlarm.Show()
         loAlarm.Start()
     ENDPROC

** Обработка события завершения ожидания пользователя
     PROCEDURE timeout
         if !isnull(this.AlarmMess)
             local loAlarmMess
             loAlarmMess=this.AlarmMess
             if !isnull(this.ShutDownHandler)
                 if type('this.ShutDownHandler')='C'
                     local lcShutDownHandler
                     lcShutDownHandler=this.ShutDownHandler
                     do &lcShutDownHandler
                 else
                     this.ShutDown()
                 endif
             else
                 this.ShutDown()
             endif
         endif
     ENDPROC

** Завершение приложения (по умолчанию)
     PROCEDURE shutdown
         quit
     ENDPROC

** Поиск командного файла
     PROCEDURE Timer
         local llShutDown
         llShutDown=.F.
         if file(this.SystemShutDownFile)
             this.Inform=filetostr(this.SystemShutDownFile)
             llShutDown=.T.
         else
             if !isnull(this.TaskShutDownFile)
                 if type('this.TaskShutDownFile')='C'
                     if file(this.TaskShutDownFile)
                         this.Inform=filetostr(this.TaskShutDownFile)
                         llShutDown=.T.
                     endif
                 endif
             endif
         endif
         if llShutDown
             this.Enabled=.F.
             this.StartAlarm()
         endif
     ENDPROC

     PROCEDURE Init
         this.Inform=''
     ENDPROC

ENDDEFINE

Подкласс класса дистанционной выгрузки приложения для работы без главного окна VFP

Этот подкласс будет отличаться от своего родителя только кодом метода запуска окна оповещения пользователя – StartAlarm.

DEFINE CLASS powershutdowntlf AS powershutdownms

     Name = "powershutdowntlf"

     PROCEDURE startalarm
         local loAlarm
         this.AlarmMess=createobject('AlarmWindowTLF')
         loAlarm=this.AlarmMess
         loAlarm.Inform.Value=this.Inform
         loAlarm.MaxValue=this.ShutDownWaitTime
         loAlarm.ParentRef=this
         loAlarm.Show()
         loAlarm.Start()
     ENDPROC

ENDDEFINE

Заключение

Разместив код для создания объекта этого класса в MAIN-модуле приложения, мы обеспечим дистанционную выгрузку приложения и развяжем себе руки.

Пример такого кода может выглядеть следующим образом:

** MAIN
PROCEDURE MAIN
PUBLIC goPSD
SET CLASSLIB TO SHUTDOWN
goPSD=createobject(‘PowerShutDownMS’)
** Интервал для поиска командного файла = 3 минут
goPSD.Interval=3*60*1000
** Время ожидания действий пользователя = 5 минут
goPSD.ShutDownWaitTime=5*60*1000
** Процедура завершения
goPSD.ShutDownHandler=’Finish’
goPSD.Enabled=.T.
** …
** код инициализации приложения
** …

ENDPROC

PROCEDURE Finish
** код завершения приложения
ENDPROC

 Демонстрационный пример и текст статьи в формате MS Word (zip-файл,65KB)


Главная страница | Технологии | Продукция | Материалы | Специалисты | Партнёры
Запись в Гостевую Книгу | Просмотр Гостевой Книги