Записки программиста, обо всем и ни о чем. Но, наверное, больше профессионального.

2012-06-22

Javascript FSM


Поскольку мне не подошла готовая библиотека, реализующая подход FSM, я решил делать свой вариант реализации. Простой, ничего лишнего, без блекджека-и-шлюх. В моем варианте реакция на событие определяется однозначно через текущее состояние и само событие. Фактически, строка «currentState_eventName» дает ссылку на функцию для исполнения.

Законченный пример (без HTML кода):
$(document).ready(onDocReady());

function onDocReady() {
 try {
  $(document).stopTime('doWork');
  $(document).everyTime(999, 'doWork', onTimerWork);
 } catch(ex) {
  console.log('Error in function onDocReady: ' + ex.description, true);
 }
} // function onDocReady()


function onTimerWork(i) { // work cycle
 try {
  // app ready?
  if (VSClient.isBusy) return;
  if (VSClient.currentState == '') { 
   VSClient.configure();
   return;
  }

  VSClient.procEvent('timer');

 } catch(ex) {
  $(document).stopTime('doWork');
  VSClient.procEvent('error');
  log('Error in function onTimerWork: ' + ex.description, true);
 }
} // function onTimerWork(i)


function onSelectFile() { // onClick button 'SelectFile...'
 try {
  VSClient.procEvent('selectFile');
 } catch (ex) {
  log('Error in function onSelectFile: ' + ex.description, true);
  VSClient.procEvent('error');
 }
 return true;
} // function onSelectFile()


VSClient = { // Object with app data
 sl: '' // Silverlight object
 , isBusy: false
 , fsmTable: [
  { from: '', event: 'configure', to: 'ready', action: 'configure' }
  , { from: 'ready', event: 'selectFile', to: 'wait4FileName', action: 'selectFile' }
  , { from: 'wait4FileName', event: 'timer', to: 'haveFileName', action: 'getFileName' }
  , { from: '*', event: 'error', to: 'error', action: 'stopOnError' }
 ]
 , currentState: ''
 , fsmArray: {}

 , configure: function() { // on page loaded, init app data
  log('VSClient.configure');
  this.isBusy = false;
  this.sl = $('#silverlightObject')[0].content.vcuSL;
  this.currentState = 'ready';
  for(var n = 0; n < this.fsmTable.length; n++) {
   var itm = this.fsmTable[n];
   this.fsmArray[itm.from + '_' + itm.event] = itm;
  }
  try {
   var res = this.sl.procMessage('test', '');
  } catch (ex) {
   this.sl = '';
   this.currentState = '';
  }
  log('VSClient curr.state: ' + this.currentState);
 } // configure: function(){}
}; // VSClient


VSClient.procEvent = function(evt) {
 var llog = log;
 if (evt == 'timer') llog = function(){;}
 var ind = this.currentState + '_' + evt;
 var itm = this.fsmArray[ind] || this.fsmArray['*_' + evt] || '';
 if (itm == '') {
  llog('VSClient.procEvent, no such transition: ' + ind);
  return;
 }
 llog('VSClient.procEvent, tableIndex: ' + ind);
 this.currentState = itm.to;
 llog('VSClient curr.state: ' + this.currentState);
 if (itm.action == '') {}
 else {
  this[itm.action].call(this);
 }
} // VSClient.procEvent = function(evt)


// { from: 'ready', event: 'selectFile', to: 'wait4FileName', action: this.selectFile }
VSClient.selectFile = function() { // fsm action
 log('VSClient.selectFile');
 var res = this.sl.procMessage('selectFile', '');
 if(res == '' || res.indexOf('fail') == 0) {
  // error
  log('VSClient.selectFile, error: ' + res, true);
 } else {
  // wait for SL, user must pick the file
  $('#inpFileName').val('');
 }
} // VSClient.selectFile = function()


// { from: 'wait4FileName', event: 'timer', to: 'haveFileName', action: this.getFileName }
VSClient.getFileName = function() { // fsm action
// check if user pick the file
 var res = VSClient.sl.procMessage('selectFile', 'getName');
 if(res == '') {
  // file not selected
  this.currentState = 'wait4FileName';
  return;
 }
 log('VSClient.getFileName, res: [' + res + ']');
 log('VSClient curr.state: ' + this.currentState);
 $('#inpFileName').val(res);
} // VSClient.getFileName = function()


// { from: '*', event: 'error', to: 'error', action: this.stopOnError }
VSClient.stopOnError = function() { // fsm action
 log('VSClient.stopOnError');
} // VSClient.stopOnError = function()

По сравнению с тем, что было (нагромождение if-else), как небо и земля. Теперь код понятнее изрядно.


Вообще, это я все терзаю задачу «resumable http chunked file upload». Просто эталонная задача для прокачки скиллов. Вот, к примеру, на хабре попалось:

Если вы когда-либо загружали видеофайл на сайт, то знаете это чувство когда загрузилось 90% и вы случайно обновляете страницу.
В этом учебном руководстве я покажу, как сделать видео загрузчик для сайта, который может возобновить прерванную загрузку, и генерировать обложку после завершения.
...
Чтобы сделать этот загрузчик, сервер должен отслеживать процесс загрузки, что бы была возможность восстановить его при обрыве. Чтобы выполнить эту задачу... с помощью Node.js...
мы будем использовать Socket.io...


Это перевод вот этой статьи. Ужасный перевод, но суть уловить можно. Как совершенно верно заметили в каментах:

Стрёмный перевод.
Но есть два полезных момента в статье: из неё вы узнаете, что Socket.io (посредством WebSocket) умеет передавать двоичные данные, и что файл на клиенте можно разбивать на куски с помощью slice api.

Пойду, попробую решить задачу возобновляемой отгрузки файлов на связке HTML5 FileAPI + Node.js + Socket.io
Надо же проверить, где грабли лежат. А вдруг многогигабайтные файлы невозможно обработать? Или — а нахрена тут Node.js, что, без него нельзя?

Комментариев нет:

Отправить комментарий

Архив блога

Ярлыки

linux (241) python (191) citation (186) web-develop (170) gov.ru (159) video (124) бытовуха (115) sysadm (100) GIS (97) Zope(Plone) (88) бурчалки (84) Book (83) programming (82) грабли (77) Fun (76) development (73) windsurfing (72) Microsoft (64) hiload (62) internet provider (57) opensource (57) security (57) опыт (55) movie (52) Wisdom (51) ML (47) driving (45) hardware (45) language (45) money (42) JS (41) curse (40) bigdata (39) DBMS (38) ArcGIS (34) history (31) PDA (30) howto (30) holyday (29) Google (27) Oracle (27) tourism (27) virtbox (27) health (26) vacation (24) AI (23) Autodesk (23) SQL (23) humor (23) Java (22) knowledge (22) translate (20) CSS (19) cheatsheet (19) hack (19) Apache (16) Manager (15) web-browser (15) Никонов (15) Klaipeda (14) functional programming (14) happiness (14) music (14) todo (14) PHP (13) course (13) scala (13) weapon (13) HTTP. Apache (12) SSH (12) frameworks (12) hero (12) im (12) settings (12) HTML (11) SciTE (11) USA (11) crypto (11) game (11) map (11) HTTPD (9) ODF (9) Photo (9) купи/продай (9) benchmark (8) documentation (8) 3D (7) CS (7) DNS (7) NoSQL (7) cloud (7) django (7) gun (7) matroska (7) telephony (7) Microsoft Office (6) VCS (6) bluetooth (6) pidgin (6) proxy (6) Donald Knuth (5) ETL (5) NVIDIA (5) Palanga (5) REST (5) bash (5) flash (5) keyboard (5) price (5) samba (5) CGI (4) LISP (4) RoR (4) cache (4) car (4) display (4) holywar (4) nginx (4) pistol (4) spark (4) xml (4) Лебедев (4) IDE (3) IE8 (3) J2EE (3) NTFS (3) RDP (3) holiday (3) mount (3) Гоблин (3) кухня (3) урюк (3) AMQP (2) ERP (2) IE7 (2) NAS (2) Naudoc (2) PDF (2) address (2) air (2) british (2) coffee (2) fitness (2) font (2) ftp (2) fuckup (2) messaging (2) notify (2) sharepoint (2) ssl/tls (2) stardict (2) tests (2) tunnel (2) udev (2) APT (1) CRUD (1) Canyonlands (1) Cyprus (1) DVDShrink (1) Jabber (1) K9Copy (1) Matlab (1) Portugal (1) VBA (1) WD My Book (1) autoit (1) bike (1) cannabis (1) chat (1) concurrent (1) dbf (1) ext4 (1) idioten (1) join (1) krusader (1) license (1) life (1) migration (1) mindmap (1) navitel (1) pneumatic weapon (1) quiz (1) regexp (1) robot (1) science (1) serialization (1) spatial (1) tie (1) vim (1) Науру (1) крысы (1) налоги (1) пианино (1)