{- Mobile Chatroom. The conversation history is encapsulated in a mobile agent that can migrate on demand. There are two classes of agents: the chat agent, which migrates from site to site, and summoners agents, which are static (one per site) and used to call the chat agent. The chat agent carries the history log of all messages that have been typed in by users. The program uses graphics, runs on 1 to n machines. Specify your machines in config file, the first machine should be used to compile and execute chatroom.pi. Add or remove calls of function spawn in chatroom.pi to match the number of machines you have (currently 2). Modify the string parameter of display in the invocations of spawn in chatroom.pi if it will not display where you wanted (currently machine's local screen will be used). Usage: start np on remote machines, type np chatroom.pi on a local machine (i.e. the first one in config file), press a mouse button over the small icon in the left-bottom corner of the screen. Note: if IP address of some machine appears in config more than once (with different port numbers) then you may need to execute np on that machine with an option -p to specify which port number shall be used. -} import "Nstd/Map" import "Graphics/Graphics" program hosts : [Site Site Site Site] = ( val [s1 s2 s3 s4] = hosts val Xevents = (cons #Event [Poll>[]] (list.make #Event 1 [Button_down>[]])) new moveOn :^[Agent Site String] new done :^[] agent ChatAgent = ( type Entry = [String String] new history:^(List Entry) run history!(nil) {- def prlog ([h:String m:String] l:(List Entry)) : [] = if (null l) then [] else ((pr h); (pr ": "); (prNL m); (prlog (car l)(cdr l))) -} def drawlog (x:Int y:Int n:Int [h:String m:String] l:(List Entry)) : [] = if (null l) then ((moveto x y); (draw_string h); (draw_string ": "); (draw_string m)) else if (<< n 10) then ((moveto x y); (draw_string h); (draw_string ": "); (draw_string m); (drawlog x (- y 10) (+ n 1) (car l)(cdr l))) else [] new lock:^[] run lock![] def err s:String = print!(+$ "Error:" s) def main (log:(List Entry) sitename:String display:String) : (List Entry) = ( (open_graph (+$ display " 400x300+100-50") err); (set_color magenta); (moveto 200 280); (draw_string "A chat room in Nomadic Pict"); (set_color blue); (moveto 20 265); (draw_string "Hello!"); (moveto 20 250); (draw_string "A chat room agent migrated"); (moveto 20 235); (draw_string (+$ "to your machine called " sitename)); if (null log) then ((set_color red); (moveto 20 220); (draw_string "Your comment ? "); val message=(echo_keys) (close_graph); (cons #Entry [sitename message] log)) else (val [lasthost lastmsg] = (car log) {- last entry -} (moveto 20 220); (draw_string (+$ "I have a message from " lasthost)); (moveto 20 205); (draw_string (+$ "=> " lastmsg)); (moveto 20 185); if (null (cdr log)) then ((set_color red); (draw_string "Your comment ? "); val message=(echo_keys) (close_graph); (cons #Entry [sitename message] log)) else ((set_color black); (draw_string "A history log is below"); {- (prNL "A history log: "); (prlog (car log)(cdr log)); -} (drawlog 20 170 0 (car log)(cdr log)); (set_color red); (moveto 20 55); (draw_string "Your comment ? "); val message=(echo_keys) (close_graph); (cons #Entry [sitename message] log))) ) moveOn ?* [su:Agent s:Site display:String] = lock?_ = ( migrate to s (sound 10000 200); run print!"A chat room agent arrived." history?log = (val updatedlog = (main log (sys.addr_of_site s) display) ( history!updatedlog | done @ su![] | lock![])) ) ) def spawn [s:Site display:String] = ( agent Summoner = ( def err s:String = print!(+$ "Error:" s) def open_window () : Status = ( (open_graph (+$ display " 100x50+0-0") err); (moveto 30 30); (set_color red); (draw_string "DAEMON"); (moveto 10 15); (set_color black); (draw_string "Click mouse"); (wait_next_event Xevents) ) def main status:Status = if status.button then ( print!"A mouse button pressed." | moveOn @ ChatAgent ! [Summoner s display] | ((close_graph); done?_ = main!(open_window))) else main!(wait_next_event Xevents) migrate to s run print!"Summoner installed." val status = (open_window) main!status ) in () ) ( spawn![s1 ""] | spawn![s2 ""] {- | spawn![s2 "hainault:0.0"] | spawn![s3 "sinapsi:0.0"] | spawn![s4 "vesicle:0.0"] -} ) ) {-********************************************************************* * Copyright (c) 1998-2001 Pawel T. Wojciechowski * * Central Server * *********************************************************************-} {Agent} = Agent {Site} = Site new register : ^[Agent Site] new migrating : ^Agent new migrated : ^Site new message : ^[#X Agent !X X] new dack : ^[] new deliver : ^[#X !X X] new ack : ^[] new currentloc : ^Site {toplevel P prog_param}[Agent Agent Site ] = ( val s1 = (sys.get_site 0) val s2 = (sys.get_site 1) val s3 = (sys.get_site 2) val s4 = (sys.get_site 3) agent a = (agent D = (def eq (a:Agent b:Agent) : Bool = (sys.== #Agent a b) new lock : ^(Map Agent Site) (lock!(map.make eq) | register?*[a s]= lock?m= ( lock!(map.add m a s) | ack![]) | migrating?*a= lock?m= switch (map.lookup m a) of (Found> s:Site -> ( ack![] | migrated?s' = ( lock!(map.add m a s') | ack![])) NotFound> _:[] -> ()) | message?*[#X a:Agent c:!X v:X]= lock?m= switch (map.lookup m a) of (Found> s:Site -> ( deliver![c v] | dack?_ = lock!m) NotFound> _:[] -> ()) )) in val SD = s1 ( deliver?*[#X c:!X v:X] = ( dack![] | c!v ) | register![a s1] | ack?_ = (currentloc!s1 | (val !prog_param = [s1 s2 s3 s4] val par = [a D SD] { P }par))) ) in ()) {- Create a new agent -} { agent b=P in Q } par = currentloc?s= (val [a D SD] = par agent B = (val !b = B ( deliver?*[#X c:!X v:X] = ( dack![] | c!v ) | register![B s] | ack?_= iflocal ack![] then ( currentloc!s | (val par = [B D SD] { P }par)) else () )) in val !b = B ack?_= ( currentloc!s | { Q }par)) {- Migrate to a site -} { migrate to s P }par = ( val [a D SD] = par currentloc?_= ( migrating!a | ack?_ = ( migrate to s ( migrated!s | ack?_ = ( currentloc!s | { P }par) ))) ) {- Location-independent output to agent -} { c@b!v }par = (val [a D SD] = par message![b c v])