Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Caché, Ensemble, DeepSee, MiniM, IRIS, GT.M Новый топик    Ответить
 Интерактивный вызов класс-методов Cache из Вэббраузера (без "Таймаута")  [new]
MyasnikovIA
Member

Откуда: Новосибирск
Сообщений: 65
Доброго времени суток.

Решил поделиться с коллегами наболевшим. Долгое время раздражала отсутствие интерактивности при работе в Cache' через вэб технологию. Что это значит:
Допустим нам надо выполнить очень большой SQL запрос, поместить результат в EXEL (xml) файл. , и по окончанию закачать его на сторону клиента, причем самое гадкое в том, что клиент очень желает видеть процесс создания этого файла. А еще веселье в том, что у клиента очень шаловливые ручки, и он любит обновлять страницу, а то и вовсе закроет браузер. При повторной загрузке он конечно же опять нажмет на кнопку старта, и все начнется снова в новом процессе. Мне кажется данная проблема знакома многим.

Стандартный вызов #server(..Test(Arg1,arg2))# тут явно не поможет.

Для решения таких задач разработал класс HTML.RunJob https://github.com/MyasnikovIA/HTML.RunJob

Опять же приветствуется конструктивная критика, что можно улучшить. Буду очень признателен.

Пример применения (есть на ginhab):
Class User.TestRunJob Extends %CSP.Page
{

ClassMethod OnPage() As %Status
{
     ;  <script language="JavaScript" type="text/javascript" src="HTML.RunJob.cls"></script>
     ;  <script language="JavaScript" type="text/javascript" src="%25ZHTML.RunJob.cls"></script> 
      
    &html< 
      <script language="JavaScript" type="text/javascript" src="%25ZHTML.RunJob.cls"></script>
      <button onclick='runProcess()'>Запустить процесс</button>
      <button onclick='KillProcess()'>Уничтожить процесс</button>
      <br>
      <br>
      <br>Уничтожить процесс: <button onclick='alert(callJob("..KillProc2",this.innerHTML))' id='KillProc'>---</button>
      <br>
      <br>
      <div id='info'>FrmCache</div>
      <br><progress id='progress' value="0" max="100" style='width:100%'></progress>
      <br>
      <div id='FrmCache'>FrmCache</div>
      <br>
      <br>
      <div id='demo'>eee</div>
      <br>
      <script type="text/javascript" >
      
      // Синхронный запуск класс метода
      // alert(callJob("..Test2"));

      // Синхронный запуск класс метода(Полный путь к классметоду)
      alert(callJob("User.TestRunJob.Test2"));
     
      /// функция срабатывает перед вызовом класс метода
      beginFun=function(){
          
      } 
      
      /// функция обработки запроса состояния работы процесса
      ProgressFun=function(arg){
         document.getElementById("demo").innerHTML='Wor progress:'+JSON.stringify(arg);
         if (arg.js!=undefined){  eval(arg.js);    }
         if (arg.UserJob!=undefined){  document.getElementById("KillProc").innerHTML = arg.UserJob;    }
      }
     
      /// функция срабатывает по окончанию работы процесса
      CalBackFun=function(arg){
          // document.getElementById("FrmCache").innerHTML =JSON.stringify(arg);
          document.getElementById("demo").innerHTML='';
          if (''+arg=={}){     alert('Работа закончена')    }
          document.getElementById("progress").value=0;
      }
     
      
      var JobID="MYJoB_3";
      
      /// Параметры обработки запроса
      var param=[beginFun,ProgressFun,CalBackFun,JobID,1000];
      /// запуск процесса повторно после перезагрузки страницы
      refJob(param);
      
      runProcess=function(){
         var res=callJob("..Test",param);
         document.getElementById("demo").innerHTML=res;
      }
      
      KillProcess=function(){
          killJob(JobID) ;
      }
      
     </script>
    >
    w ##class(%SYS.System).InstanceGUID()
    Quit $$$OK
}

ClassMethod KillProc2(JobProc)
{
    w $zu(4,JobProc)
}

ClassMethod Test2()
{
    w $ZDT($h)
}

///Процесс который будет работать  параллельно и очень долго  
ClassMethod Test()
{
    
    ; %runjob - имя глобала , через который происходит обмен Web морды и Cache` процесса
    s count=1000
    for a=1:1:count {   
       h 1
       s @%runjob@("info")= " Счетчик внутри процесса "_a
       s val=100\count*a
       s jsCode=""
       s jsCode=jsCode_" document.getElementById(""progress"").max = "_count_"; "
       s jsCode=jsCode_" document.getElementById(""progress"").value = "_a_"; "
       s jsCode=jsCode_" document.getElementById(""FrmCache"").innerHTML ="""_$ZDT($h)_" - "_a_"  "_val_"%"" "
       ; s @%runjob@("eval")=jsCode
       s @%runjob@("js")=jsCode
       s @%runjob@("UserJob")=$JOB
    }
    w $zdt($h)
    s @%runjob@("OK")=1 ; сообщаем Web морде о том, что процесс закончен корректно
    q
}

}
5 дек 17, 17:22    [21008275]     Ответить | Цитировать Сообщить модератору
 Re: Интерактивный вызов класс-методов Cache из Вэббраузера (без "Таймаута")  [new]
Блок А.Н.
Member

Откуда: Новосибирск
Сообщений: 3752
MyasnikovIA,

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

Из своих подобных изобретений могу назвать многоуровневые статусы задач. Т.е. процесс может устанавливать статус не всей задачи целиком, а только одного определенного уровня. При установке определенного уровня статуса вышестоящие не затрагиваются, а нижестоящие затираются. Показалось очень удобным, применял в своей библиотеке формирования *.ODS файлов.
6 дек 17, 00:56    [21009407]     Ответить | Цитировать Сообщить модератору
 Re: Интерактивный вызов класс-методов Cache из Вэббраузера (без "Таймаута")  [new]
MyasnikovIA
Member

Откуда: Новосибирск
Сообщений: 65
Блок А.Н.,

Из сообщения я понял, что есть способ проще и легче. Только не понял какой. Можно пример, или ссылку где можно изучить эту технологию :)
А огород написан по той причине, что не знал иного способа. Хотел бы прокачать свой уровень, за счет коллег :)
6 дек 17, 04:20    [21009493]     Ответить | Цитировать Сообщить модератору
 Re: Интерактивный вызов класс-методов Cache из Вэббраузера (без "Таймаута")  [new]
krvsa
Member

Откуда: г Волжский
Сообщений: 13045
MyasnikovIA, помимо кащейских
#server(..Test(Arg1,arg2))#
// и
#call(..Test(Arg1,arg2))#

Есть и "стандартные" методы общения клиента с сервером... Например все тот же ajax/.

Против повторных запусков используется простая блокировка "сигнальных" узлов/глобалов...

Остается организовать прогресс на сервере и отображение его на клиенте.
6 дек 17, 09:03    [21009608]     Ответить | Цитировать Сообщить модератору
 Re: Интерактивный вызов класс-методов Cache из Вэббраузера (без "Таймаута")  [new]
Блок А.Н.
Member

Откуда: Новосибирск
Сообщений: 3752
MyasnikovIA,

Ну, собственно, krvsa все сказал, только я бы на #call не особо надеяться, как способ асинхронного вызова.
Не возражаете, если на псевдокоде?

Веб страница:
<Кнопка запуска>
<Область отображения прогресса>
...
</Область отображения прогресса>
<script language=javascript>
ЗапуститьОтчет(){...}
ПроверитьСостояниеПрогресса(){...}
ПроверитьРазрешениеЗапускаСВыбраннымиПараметрами(){...}
</script>

/// Класс отчета. Большинства методов можно выносить в абстрактный родительский класс
Class Report
{
/// Запустить отчет асинхронно. Вернет 1, если запущено
ClassMethhod RunAsync(params) as %Boolean
{
ret:'..CheckStartPermission(params) 0
job PrepareData(params)
ret 1
}
/// Проверить разрешение старта с выбранными параметрами
Classmethod CheckStartPermission(params) as %Boolean
{
s permisson=..Lock(params)
d ..Unlock(params)
ret permission
}
ClassMethod Lock(params)
{
lock ^report(params):0
ret $test
}
ClassMethod Unlock(params)
{
lock -^report(params)
}
ClassMethod PrepareData(params)
{
s lock=..Lock(params)
ret:'lock
/// Установить статус процесса

for
{
///Обновить статус процесса
....
}
d ..Unlock(params)
}
}

Все, в общем, просто, и все вы это знаете
6 дек 17, 10:57    [21009961]     Ответить | Цитировать Сообщить модератору
 Re: Интерактивный вызов класс-методов Cache из Вэббраузера (без "Таймаута")  [new]
MyasnikovIA
Member

Откуда: Новосибирск
Сообщений: 65
krvsa,

Как раз это и реализовано в HTML.RunJob
Запускается процесс, при этом инициируется глобал с именем идентификатора "MYJoB_3" и глобальна и переменная с именем %runjob
Все значения записанные в глобал @%runjob@(“Arg1”)=”Val” в процессе опроса состояния вернутся как JSON объект {…,”Arg1”:”Val”} и будут помещены в качестве входного аргумента JS функции “ ProgressFun=function(arg){ } “ – из примера

Таким образом организован обмен информацией из запущенного процесса и браузером.

Если глобал будет уничтожен, или в нем появится ветка с именем “OK”=1 , тогда опрос состояния процесса прекратится и запустится JS функция CalBackFun=function(arg){ }

Что касается огорода, то основной код был взят из JS “/csp/broker/cspxmlhttp.js“ ответная часть на сервере cache’ тоже взята из системного класса %CSP.Broker.cls . все было переработано и объединено в одном классе(для удобства развертывания).
6 дек 17, 11:22    [21010114]     Ответить | Цитировать Сообщить модератору
 Re: Интерактивный вызов класс-методов Cache из Вэббраузера (без "Таймаута")  [new]
krvsa
Member

Откуда: г Волжский
Сообщений: 13045
MyasnikovIA
Как раз это и реализовано в HTML.RunJob

Дело твое...
Просто я согласен с этим высказыванием
Блок А.Н.
Что-то все-таки это слишком большой огород для такой задачи.
6 дек 17, 11:49    [21010288]     Ответить | Цитировать Сообщить модератору
 Re: Интерактивный вызов класс-методов Cache из Вэббраузера (без "Таймаута")  [new]
MyasnikovIA
Member

Откуда: Новосибирск
Сообщений: 65
В JS появляются следующие функции :
callJob – функция запуска процесса
refJob - функция восстановления процесса
killJob - функция уничтожения процесса
и если библиотека подключается в cache классе, то библиотека cspbroker.js не будет подключена, в связи с чем я переопределяю js функцию cspCallHttpServerMethod
она вызывается в тех случаях когда в JS коде встречается конструкция #call( … )#

При запуске callJob в первом параметре передается имя классметода в полном или коротком представлении ( "..Test2" или "User.TestRunJob.Test2" )

Если вторым аргументом подается массив простой аргумент (строка , число) тогда работает как обыкновенный синхронный запрос, аналогично работе #server(…)#.

Если на вход вторым аргументом подается массив объектов в виде:
var param=[beginFun,ProgressFun,CalBackFun,”JobID”,1000];
 beginFun(){}         – js функция срабатывает сразу же после запуска cache метода
ProgressFun(arg){}  – js функция срабатывает при опросе состояния процесса  
CalBackFun(arg){}   – js  функция срабатывает после окончания работы cache  класс-метода, или если процесс уничтожен
”JobID”        –  идентификатор процесса по которому производится опрос состояния
1000            –   интервал опроса состояния процесса
тогда класс метод запускается в параллельном потоке. При этом функция ProgressFun повторно вызывается через 1000 мсек. (время задается в параметре четвертым аргументом) .

Все остальные аргументы помещаются как входящие аргументы cache класс-метода
6 дек 17, 11:54    [21010313]     Ответить | Цитировать Сообщить модератору
 Re: Интерактивный вызов класс-методов Cache из Вэббраузера (без "Таймаута")  [new]
MyasnikovIA
Member

Откуда: Новосибирск
Сообщений: 65
все это после подключения в HTML коде класса
<script language="JavaScript" type="text/javascript" src="HTML.RunJob.cls"></script>
6 дек 17, 11:55    [21010314]     Ответить | Цитировать Сообщить модератору
 Re: Интерактивный вызов класс-методов Cache из Вэббраузера (без "Таймаута")  [new]
MyasnikovIA
Member

Откуда: Новосибирск
Сообщений: 65
krvsa,

Спасибо за мнение. Это на самом деле важно.
6 дек 17, 11:56    [21010323]     Ответить | Цитировать Сообщить модератору
 Re: Интерактивный вызов класс-методов Cache из Вэббраузера (без "Таймаута")  [new]
Sheonn
Member

Откуда:
Сообщений: 16
MyasnikovIA,
Т.е. в CSP это использовать нельзя из-за переопределения cspCallHttpServerMethod?
6 дек 17, 19:18    [21012558]     Ответить | Цитировать Сообщить модератору
 Re: Интерактивный вызов класс-методов Cache из Вэббраузера (без "Таймаута")  [new]
MyasnikovIA
Member

Откуда: Новосибирск
Сообщений: 65
Sheonn,

Использовать можно
в классе HTML.RunJob присутствует следующая конструкция (в районе строки 592 -+ 5 строк)
     if (typeof cspCallHttpServerMethod !== 'function') {
         cspCallHttpServerMethod=callJob;
     }


Благодаря этой конструкции, если функция cspCallHttpServerMethod не определена, тогда она определяется.

Если определить в CSP внутри тэга HEAD
<HEAD> 
   <script language="JavaScript" type="text/javascript" src="HTML.RunJob.cls"></script>
</HEAD> 

Перед выполнением Cache добавит пере закрывающим тэгом "</HEAD>" библиотеки "cspxmlhttp.js" и "cspbroker.js"
и получится конструкция
<HEAD> 
<script language="JavaScript" type="text/javascript" src="HTML.RunJob.cls"></script>
<script language="JavaScript" type="text/javascript" src="/csp/broker/cspxmlhttp.js"></script><script language="JavaScript" type="text/javascript" src="/csp/broker/cspbroker.js"></script></HEAD>



В связи с этим cspCallHttpServerMetho будет сначала создан в HTML.RunJob.cls а затем переопределен в cspxmlhttp.js
и все будет работать по старому.

callJob – функция запуска процесса
refJob - функция восстановления процесса
killJob - функция уничтожения процесса

Никто не запрещает закомментировать HTML.RunJob условие перед инициализацией , и оставить
cspCallHttpServerMethod=callJob;

Но надо помнить, что подключать HTML.RunJob надо будет после закрывающего тэга </head> иначе библиотека "cspxmlhttp.js" опять переопределит её.

Это может понадобится, в тех случаях, когда есть желание использовать HTML.RunJob и есть необходимость скрыть от пользователя имя вызываемого класс метода при применении конструкции:
 #call(..Test([beginFun,ProgressFun,CalBackFun,”JobID”,1000],arg1,arg2) )#
7 дек 17, 04:51    [21013359]     Ответить | Цитировать Сообщить модератору
Все форумы / Caché, Ensemble, DeepSee, MiniM, IRIS, GT.M Ответить