Automate Tasks with Flexible Robot Workers
trigram15-Jan-2011/16:30:33+1:00
Alors, voilà, j'ai trouvé un script sur reboltutorial.com qui m'intéresse pour ce que je souhaite faire.
Maintenant,, ce n'est peut-être pas la bonne approche... mais bon...

http://reboltutorial.com/blog/automate-tasks/

But: réaliser un système de robots qui simulent des utilisateurs se connectant à un site Web (téléchargement de la page et de tous les éléments associés comme le fait un navigateur Web), l'objectif étant de reprendre des logs d'un serveur Web pour avoir un scénario réaliste. Evidemment, il y a après le problème si il y a une notion de session, de connexion à compte... dans un premier temps connexion aux pages statiques en GET dans les logs.

Du coup, je me suis dis que l'exemple de rebtut pourrait éventuellement me servir de base.

J'ai donc fait ceci :

%robot.r

REBOL []

;this ROBOT will serve as prototype to create other Robots
ROBOT: make object! [

  stock-symbols: [] ; property to store the list of stock-symbols

  ;task1 function
  download: func [stock-symbol] [
      url: to-url rejoin [http://www.google.com/finance/historical?q= stock-symbol "&output=csv"]
      target-file: to-rebol-file rejoin [stock-symbol ".csv"]
      write/lines target-file read/lines url
      append stock-symbols stock-symbol
  ]

  ;task2 function
  list: func [][
      foreach symbol stock-symbols [print symbol]
  ]

  ;core engine
  run: func [task-list][
      stock-symbols: [] ; reset the stocks list
      do bind task-list 'self ; this is the hard part to understand see explanation below
  ]

]


%worker1.r
REBOL []

do %robot.r
print "Worker1 started !"
tasks-list1: [
  download "GOOG"
  download "YHOO"
  download "MSFT"
  list
]
Worker1: make ROBOT []
Worker1/run tasks-list1
ask "Worker1 has finished !"


%worker2.r
REBOL []

do %robot.r
print "Worker2 started !"
tasks-list2: [
  download "AAPL"
  download "ORCL"
  list
]
Worker2: make ROBOT[]
Worker2/run tasks-list2
ask "Worker2 has finished !"


%workers.r
REBOL []

workers-list: [
  %worker1.r
  %worker2.r
]
foreach worker workers-list [
  launch worker
]
ask "All workers started."


C'est un bon début... maintenant, je veux pimenter un petit les choses... enfin reboliser à savoir :
- avoir une communication entre le workers.r qui lance les workers worker1.r et worker2.r pour que ces derniers soient silencieux mais renvoi le fait que leur travail est terminé
- avoir un serveur qui puissent lancer des workers et évidemment sur des machines différentes (avoir une liste du type [%worker1.r 192.168.1.10 %worker2.r 192.168.1.11]
comme mon objectif final est de simuler des utilisateurs sur un site Web c'est mieux de pouvoir le faire depuis plusieurs machine.
Je pense que REBOL peut le faire et est doué pour cela, non ?

Nico
trigram15-Jan-2011/17:03:02+1:00
Evidemment, à terme, il faudrait une application graphique qui permette de voir l'avancement des workers, de pouvoir cliquer sur un worker et voir précisément où il en est...
trigram15-Jan-2011/23:45:31+1:00
Pour la partie téléchargement de la page et de ses éléments...

http://www.rebol.org/view-script.r?script=download-page.r

Pour l'instant, cela ne gère pas :
- la notion de cache avec rafraîchissement comme un browser web
- les éléments autre que CSS, Javascript et image (ne télécharge pas le Flash par exemple)
- ne parallélise pas le téléchargement comme doit très certainement le faire un browser web classique

Pour l'aspect parallélisation, il faudrait peut-être mettre en place un système de micro serveur qui télécharge un type de ressource spécifique.

A étudier.
trigram16-Jan-2011/16:31:32+1:00
Je suis en train de faire un serveur maitre %master-worker-server.r qui communique avec des %worker-server.r pour pouvoir lancer la simulation depuis plusieurs machines pour être plus réaliste.

Après, je dis qu'avec notre R2 + Cheyenne + sql-protocol on pourrait aussi passer par ce trio de choc...
ldci17-Jan-2011/9:20:41+1:00
Salut
Peut être qu'une façon de faire est d'utiliser un "bus logiciel" comme celui que j'avais montré à la Devcon 07 à Paris. Le principe est d'utiliser la fonction awake associée à un port TCP. Cette technique permettrait un parallélisme pour tes robots.
Voici le résumé de la devcon
In many of experiments done in the lab we have to record sensors that generate electrical signals to measure biophysical phenomena, such as temperature, force or brain electrophysiological activity. Some commonly used sensors are strain gauges, thermocouples, thermistors, angular encoders, linear encoders, electrodes … To measure signals from these various transducers, we have first to convert them into a form that an A/N device can accept for recording. But we also have to analyze « on line » the sampled data in order to modify different parameters of the subject’s environment according to the behavioral reactions that are analyzed by the recording of sensors. This implies the use of a multitasking development. Unfortunately Rebol doesn’t offer such opportunities for multitasking experiments. This is the reason why we have developed Rencontre. Rencontre is a middleware elaborated for the collaboration between interactive applications developed with different languages and toolkits and running on different operating systems. Since Rebol is a messaging language using internet protocols it was very easy to conceived Rencontre as a model of communication between active “agents” which are dynamically stored in a list: agents connect to the bus, send messages and leave the bus without interfering with the other agents. Another characteristic of this middleware is the very simple exchange data protocol between agents we adopted. Each agent, whatever the operating system or the language used by the developer, must able to send and receive a text message (a string of chars) including only information about the name of the agent and the content of the message organized as follows “Application Name: Message”. Moreover each agent is responsible of the data treatment send by the others agents. If the agent has to process the messages sent by another agent, works is done locally. On the other case, the message is purely ignored.
Whatever the language used, the functioning process is similar. A supervisor creates the bus by opening a TCPIP port. Each agent must first verify that the bus is created and then connect to the bus. In other words agents give appointments to each other on the bus. When connected agents can send messages that are collected and then broadcasted by the supervisor to each connected agent. When receiving a message, each agent can locally process or ignore the process.
trigram17-Jan-2011/16:50:42+1:00
@ldci

Je ne trouves pas fonction awake... désolé je suis débutant...

S'agit-il de cette fonction ?

http://www.rebol.com/docs/words/wdispatch.html
ldci18-Jan-2011/10:38:18+1:00
@trigram

Je crois que c'est Bouba qui avait documenté ce point


Voici les principes généraux :
----------------------------

- A un port, on associe une fonction "awake" (c'est un pointeur de code)
- On ajoute le ports à la liste des port en attente (system/ports/wait-list)
- On set le mode "no-wait" qui doit être false sinon les fonctions "awake" sont appelés continuellement
- On appelle la fonction wait [] pour que les fonctions "awake" soient appelées lorsqu'un événement arrive sur les ports
- Lors de la fermeture d'un port, il faut l'enlever de la liste des ports en attente
- Toutes les fonctions "awake" doivent retourner FALSE ou NONE, sinon le wait [] se termine
- Dans le cas d'un port en attente de connection,
- la fonction "awake" est appelée à chaque nouvelle connection
- Dans le cas des autres ports,
- la fonction "awake" est appelée chaque fois que des données sont arrivées
- lorsque le port est fermé à l'autre bout de la ligne

Avantages de la méthode
-----------------------
- Il n'est pas nécessaire de programmer une boucle d'attente sur les ports
- On n'est pas en conflit avec "view" qui utilise aussi wait (voir le source de do-events)
- Il est moins nécessaire de programmer le traitement des erreurs
- La fermeture d'un port à l'autre bout de la ligne est bien traité (on a pas de read qui attendent jusqu'au timeout)

Voici le code


Rebol [
   title: "Rencontre: Port awake Utils"
]

Error1: "Rebol bus unreachable !"

; ----------------------------------------------
; Ajout d'un port ‡ la liste des port en attente
; ----------------------------------------------

append-waiting-port: func [
   port [port!]
   :awake [any-function!]
][
   set-modes port [no-wait: false]
   ;any function can be used when we get an awake event
   port/awake: :awake
   insert tail system/ports/wait-list port
   ;port
]

; -------------------
; Fermeture d'un port
; -------------------

close-waiting-port: func [
   port [port!]
][
   remove find system/ports/wait-list port
   close port
]



et l'exemple du serveur: tu passes Connection-Handler


#! /usr/bin/rebol -qs
Rebol [
   title: "Rencontre"
   Author: "François Jouen"
]
context [
do %awake.r

host_address: system/network/host-address
Default-Port: 2010
connection-list: []
active: false
ccount: 0
app_name: ""
app_message: ""


; ---------------------
; Connection Handler for the Server
; ---------------------

Connection-Handler: func [listener [port!] /local port]
   [
   port: first listener
   append-waiting-port port Message-handler
   append connection-list port
   ccount: ccount + 1
   cc/text: to-string ccount
   append clear commes/text join "Connection from " port/host
   show [commes cc]
   false
]


; ------------------
; Server Message Handler
; ------------------

Message-Handler: func [ port [port!] /local data connection]
   [
   ; nothing to process: exit
   if not value? in port/state 'inBuffer [return false]

   clear data: make string! 257
   if zero? read-io port data 256 [
      close-waiting-port port
      remove connection-list find port
      return false
   ]
   ; parse messages from clients and get app name and message
   tmp: parse/all data ":"
   app_name: first tmp
   app_message: second tmp
   ; some app wants to quit the bus
   if found? find app_message "Quit" [
      s: join port/host [" ["app_name"]" " disconnected"]
      ccount: ccount - 1
      cc/text: to-string ccount
      append clear commes/text s]
      cport/text: port/host
      cname/text: app_name
      console/text: app_message
      
      show [commes cc cport cname console]
   
   
   ; now broadcast message data for all connected clients   
   foreach connection connection-list [
      if error? try [
      smsg: join port/host [":" app_name ":" app_message]
      insert connection smsg] []
      
   ]
   false
]

; ------------------
; Start Server
; ------------------

Start-Server: does [
   if not active [
      port: make port! join to-url "tcp://:" pf/text
      append-waiting-port port connection-handler
      open/direct port
      aled/colors: [0.255.0 0.255.0]
      append clear commes/text join "Port " [ port/port-id " is waiting for connection " newline]
      show [commes aled]
      active: true
   ]
]


; ---------------
; Quit Application
;-------------------

Quit-Requested: does [
   if (confirm/with "Really Quit ?" ["Yes" "No"]) [
      if active [close port ]
      quit]
]


ServerWin: layout/offset [
   across
   space 5x5
   at 5x5
   txt 70 "Server" left
   info 100 to-string host_address
   text "Port"
   pf: field 70 to-string Default-Port
   aled: led 24X24
   btn 70 "Start" [Start-Server]
   btn 70 "Quit" [Quit-Requested]
   at 80X35 commes: info 390
   at 5X70 text 70 "Agents"
   cc: info 50 to-string ccount Center
   cport: info 100 cname: info 230
   at 80x100 console: info 390
] 10x50

view/new ServerWin


insert-event-func [
      either all [event/type = 'close event/face = ServerWin][quit-requested][event]
]

do-events
; for the waiting ports
wait []
]
trigram18-Jan-2011/18:00:43+1:00
OK, merci.
Je suis en train d'étudier le tout.

J'ai commencé à faire un client pour simplifier...

REBOL [
title: "Client Rencontre"
]

context [

Connect-Port: none
Server: "localhost"
Server-Port: 2010

Show-Log: funct [msg] [
  commes/text msg
  show [commes]
]

Connect-Server: does [
  Show-Log "Connecting to Server..."
  if error? try [
    Connect-Port: open rejoin [tcp:// Server ":" Server-Port]
    aled/colors: [0.255.0 0.255.0]
    Show-Log "Connected to Server."    
  ][
    aled/colors: [255.0.0 255.0.0]
    Show-Log "Failed to connect to Server !"
  ]
  show [aled commes]
]

Disconnect-Server: does [
  if error? try [
    Show-Log "Disconnecting to Server..."
    close Connect-Port
    Show-Log "Disconnected to Server."
  ][
    aled/colors: [255.0.0 255.0.0]
    Show-Log "Failed to disconnect to Server"
  ]
  show [aled commes]
]

Send-Message: funct [msg] [
  Show-Log join "Try to send message : " msg
  aled2/colors: [0.0.0 0.0.0]
  show [aled2]
  if error? try [
    insert Connect-Port msg
    aled2/colors: [0.255.0 0.255.0] 
    Show-Log "Message sent !"
  ][
    aled2/colors: [255.0.0 255.0.0]
    Show-Log "Failed to send message !"
  ]
  show [aled2 commes]
]

ClientWin: layout/offset [
   across
   space 5x5 
   txt 70 "Client" left 
   text "Port" 
   pf: field 70 to-string Server-Port
   btn 70 "Connect" [Connect-Server]
   aled: led 24X24
   btn 70 "Disconnect" [Disconnect-Server]
   return
   commes: info 390 "..."
   return
   text "Application"
   msg-app: field "AppTest"
   return
   text "Message"
   msg: field
   aled2: led 24X24
   btn 70 "Send" [Send-Message rejoin [msg-app/text ":" msg/text]]
   return
   btn 70 "Quit" [quit]
] 10x50

view/new ClientWin

do-events
]
trigram18-Jan-2011/18:01:32+1:00
Comme je ne maîtrise pas VID, il y a des trucs qui ne marche pas.

C'est un début, le squelette.
trigram19-Jan-2011/0:20:30+1:00
A moins qu'il n'y avait un exemple de client avec émission et réception de message du Bus...
ldci19-Jan-2011/9:42:18+1:00
@trigram
Oups. Désolé, j'avais oublié la copie des clients
En voici un premier qui génère des valeurs aléatoires et les envoie au superviseur

#! /usr/bin/rebol -qs
Rebol [
   title: "Rencontre: Acquisition"
    Author: "François Jouen"
]

context [
host_address: system/network/host-address
Default_Port: 2010
Application_Name: "Acquisition"
Connected: false
acqoff: false
do %awake.r

; ------------------
; Agent Message Handler
; ------------------

Agent-message-handler: func [
   port [port!]
   /local data
][
   if not value? in port/state 'inBuffer [return false]
   clear data: make string! 257
   if zero? read-io port data 256 [
      close-waiting-port port
      return false
   ]
   
   ; nothing to process for this app which is just a data sender
   false
]

Connect: does [
      if not connected [
      if error? try [
         port: make port! join to-url "tcp://" [svr/text":" pf/text]
         append-waiting-port port Agent-message-handler
         open/direct port
         connected: true
         append clear messages/text "Rebol Bus reached"
         aled/colors: [0.255.0 0.255.0] show [aled Messages]]
      [Alert Error1 connected: false]   
      ]
]
Disconnect: does [
   if connected [insert port join Application_Name [" :Quit"]
   connected: false
   aled/colors: [255.0.0 255.0.0]
   show [Messages aled]]
]
Send_Message: does [
if connected [
   until [
      val: random 250
      insert port join Application_Name [":" to-string val ]
      wait 0.05
   acqoff]
   ]            
]



view/new layout/offset [
      origin 0x0
      across
      space 2x5
      at 5x5 label 100 "Server" svr: field 100 to-string host_address
      btn 80 "Join the bus" [Connect]
      at 5x30 label 100 "Port" pf: field 100 to-string Default_Port
      btn 80 "Quit the bus" [ Disconnect]      
      at 5x55 btn 100 "Start Acquisition" [ acqoff: false Send_Message]
      btn 100 "Stop Acquisition" [acqoff: true]
      btn 80 "Quit" [quit]
      at 5x80 messages: info 200 aled: led 80X24      
]50x50




do-events]


et un second qui récupère les valeurs et les affiche

#! /usr/bin/rebol -qs
Rebol [
   title: "Rencontre: Visualisation"
   Author: "François Jouen"
]

context [
host_address: system/network/host-address
;host_address: 90.3.112.204
Default_Port: 2010
Application_Name: "Visualisation"
Connected: false
x: y: 0
col: 0.255.0
plot: copy [pen col line]
do %awake.r

; ------------------
; Agent Message Handler
; ------------------

Agent-message-handler: func [
   port [port!]
   /local data
][
   
   if not value? in port/state 'inBuffer [return false]
   
   clear data: make string! 257
   if zero? read-io port data 256 [
      close-waiting-port port
      return false
   ]
    if connected [
      ; data parsing to get information
      
      temp: parse/all data ":"
      saddress: first temp
      ;get message sender
      msender: second temp
      ; OK we have to process these data
      if found? find msender "Acquisition" [
         tval: third temp
         messages/text: tval show messages
         ; for visualisation
         if error? try [y: to-integer tval] [y: 250]
         Show_Image]
   ]   
   false
]

Connect: does [
      if not connected [
      if error? try [
         port: make port! join to-url "tcp://" [svr/text":" pf/text]
         append-waiting-port port Agent-message-handler
         open/direct port
         connected: true
         messages/text: "Rebol Bus reached"
         aled/colors: [0.255.0 0.255.0]
         show [aled Messages]]
      [Alert Error1 connected: false]   
      ]
]
Disconnect: does [
   if connected [insert port join Application_Name " :Quit"
   connected: false
   messages/text: "Leaving Rebol Bus"
   aled/colors: [255.0.0 255.0.0]
   show [aled Messages]]
]

Clear_Screen: does [
   plot: copy [pen col line]
   x: 0
   append clear visu/effect reduce ['grid 10x10 82.82.92 ]
   append visu/effect reduce ['draw plot]
   show visu
]

Show_Image: does [
      if x > 315 [Clear_Screen]
      if x = 0 [append visu/effect reduce ['draw plot]]
      append plot as-pair x y
      wait 0 ; for mac osx !
      show visu
      x: x + 1
      
]

view/new layout/offset [
      origin 0x0
      across
      space 2x5
      at 5x5 label 100 "Server" svr: field 100 to-string host_address
      btn 80 "Join the bus" [Connect]
      at 5x30 label 100 "Port" pf: field 100 to-string Default_Port
      btn 80 "Quit the bus" [ Disconnect]   
      at 5x55 messages: info 200 aled: led 80X24
      at 0x80 visu: box 315x285 black
      at 0x365 btn 315 "Quit" [Quit]
      do [append clear visu/effect reduce ['grid 10x10 82.82.92 ]]
]550x50




do-events]

Ce code fonctionne sans pb et tu peux t'en inspirer pour tes robots.
Bon code et à plus
jocko19-Jan-2011/10:26:26+1:00
Bonjour, François,

Je viens de tester ton code: joli travail (comme toujours !), et bel exemple de la puissance de rebol dans ces applications réparties.

A +

Jo
trigram19-Jan-2011/14:31:41+1:00
Rencontre en action...

ldci19-Jan-2011/21:45:42+1:00
@jocko: merci c'est un plaisir de recevoir des fleurs venant d'un grand reboler comme toi.
En fait j'avais développé ce code pour la Devcon de 2007 pour montrer qu'on pouvait pallier l'absence de multithreading en Rebol. Depuis, je me sers de ce bus tous les jours pour gérer des expérimentations qui nécessitent un traitement réparti entre machines hétérogènes sur un réseau. Je m'en sers par exemple pour transférer des images issues de la Webcam d'une machine pour effectuer un traitement sur une autre machine

@trigram: cette version est une démo, mais l'intérêt est que tu peux ajouter autant d'agents que tu veux sans ralentir l'ensemble du processus. Autre point intéressant, tes agents peuvent être programmés avec un autre langage et être localisés sur n'importe quelle machine de ton réseau et ce indépendamment de l'OS.
PS: comment as tu fait pour mettre une copie d'image sur le site?
A+
trigram19-Jan-2011/22:02:56+1:00
Facile, tu mets l'url vers ton image.
En l'occurence, j'ai mis l'image sur Picassa et ensuite j'ai mis le liens directement dans le message.
trigram19-Jan-2011/22:09:14+1:00
Mais j'aurai du utiliser le Free Hosting de GreG...
Ooouuuppppsss !!! :(
Promis le prochain, j'utilise digicamsoft.
trigram19-Jan-2011/22:18:06+1:00
C'est une demo mais un bonne base.
Cela pourrait faire l'objet d'un bel article.
On peut imaginer des usages tels que :
- chat
- travail collaboratif : tableur collaboratif
- avoir un client qui se charge de répercuter les infos sur Twitter par exemple ou publier en RSS

???

Quelles sont les perfs et la charge que peut supporter ce bus logiciel ?
ldci20-Jan-2011/10:25:39+1:00
Pour les performances et la charge, je n'ai pas vraiment testé. En fait j'ai créé une dizaine d'agents qui se connectent et se déconnectent sans qu'il y ait de ralentissement du processus général.
Le broadcasting des messages ne pose aucun problème de charge, car les messages sont diffusés simultanément à tous les agents. Après les pbs sont les temps de transfert sur le réseau, et le travail que fait chaque agent qui peut l'amener à laisser passer quelques messages.
Un exemple, dans une recherche, j'ai une machine qui envoie les images vidéos issues d'une webcam à une autre machine qui assure le traitement des images. Si je passe par le réseau WIFI de l'université, j'ai des ralentissements et des blocages de l'interface cliente. Solution: liaison directe des deux machines avec un cable et un réseau Adhoc et là pas de problèmes. Comme toujours avec Rebol, il faut choisir une solution optimale sans forcément que cette solution soit la plus optimale d'un point de vue algorithmique (cf. les commentaires de DocKimbel sur la mémoire).
De toutes les façons ce sera à tester, mais pour l'instant ça répond à mes besoins spécifiques.
A +
GreG20-Jan-2011/11:17:29+1:00
@trigram:
Pour mettre tes images en ligne, tu peux aussi essayer flickr, jalbum, smugmug, darqroom, zenfolio, photobucket, shutterfly, facebook, twitpic, imageshack, mypicturetown, pbase, ....
J'arrete là, ce n'est pas exhaustif

Login required to Post.


Powered by RebelBB and REBOL 2.7.8.4.2