Reputation: 51
Is it possible to pass information between Netlogo models without using LevelSpace or Hubnet? I'm trying to output variables from one model and into a second model. The reason I don't want to use LevelSpace for this is because I'm using the xw (xtraWidget) extension as a frontend UI for my models and LevelSpace does not allow the xw extension to activate when models open. Hubnet is for LAN operations and these models will be on a server online.
Upvotes: 0
Views: 119
Reputation: 12580
Do all of the models involved use xw? If only one does, you could have the model be the LevelSpace parent.
If more than one does, probably the simplest way to do this is by communicating by writing to and reading from a file. The safest version of this is to have the sender wait for a file to not exist, open the file, write to it, and closes the file. The receiver, similarly, waits for the file to exist, opens the file, reads from it, then closes the file and deletes it, indicating to the sender that it has finished reading. This should avoid most weird timing issues and, for instance, Windows locking open files and the like, but it does require the two models stay in lock step, as they will block on sending and receiving. This would look like so:
Sender:
to send-data [ filename data ]
while [ file-exists? filename ] [] ; Could add a `wait` so this doesn't eat as much cpu while waiting
file-open filename
file-write data
file-close
end
Receiver:
to-report receive-data [ filename ]
while [ not file-exists? filename ] [] ; Could also add a `wait` here
file-open "filename"
let data file-read
file-close
file-delete filename
report data
end
Example usage:
Sender:
observer> send-data "foo" 123 send-data "foo" 456
Receiver:
observer> show receive-data "foo" show receive-data "foo"
observer: 123
observer: 456
Another way to do this (and probably much better) would be to use the Python extension to perform inter-process communication via the multiprocessing
library. If you're interested in this method, add a comment, and I can write up some example code. Here are the related docs to get you started: https://docs.python.org/3/library/multiprocessing.html#using-a-remote-manager
Update with Python solution:
The idea here is to start a multiprocessing server that the models then connect to. One model will start the server, then both models will create clients that connect to that server. They will then pass data via a queue in that server.
First, we setup python and import the libraries we care about. This will be run on both models.
to setup-python
py:setup py:python3
(py:run
"from multiprocessing.managers import BaseManager"
"from multiprocessing import Process"
"from queue import Queue")
end
We start the server (in just one of the models) like so:
to start-server
(py:run
"def start_server():"
" class QueueManager(BaseManager): pass"
" queue = Queue()"
" QueueManager.register('get_queue', callable=lambda:queue)"
" m = QueueManager(address=('', 50000), authkey=b'turtles')"
" s = m.get_server()"
" s.serve_forever()"
"proc = Process(target=start_server) "
"proc.start()")
end
We then start the client in both models like so:
to start-client
(py:run
"class QueueManager(BaseManager): pass"
"QueueManager.register('get_queue')"
"m = QueueManager(address=('', 50000), authkey=b'turtles')"
"m.connect()"
"queue = m.get_queue()")
end
We can then send and receive data with the following:
to send-data [ data ]
py:set "data" data
(py:run "queue.put(data)")
end
to-report receive-data
report (py:runresult "queue.get(block=False)")
end
Note that I set receive-data
to not wait for data; it will just error if there's no data waiting. If you do block, it's pretty easy to freeze NetLogo (and then you have to go and kill the underlying python process manually). You'll probably want to adjust that behavior. See https://docs.python.org/3/library/queue.html#queue.Queue.get for various options, or just throw a carefully
around the whole thing and report a default value when you get an error.
Putting it all together, on one model we have something like:
observer> setup-python
observer> start-server
observer> start-client
observer> send-data 123
observer> send-data 456
and in the other model we have:
observer> setup-python
observer> start-client
observer> show receive-data
observer: 123
observer> show receive-data
observer: 456
A few notes:
start-client
and start-server
should be run exactly once after setup-python
. If you want to call them again, you need to call setup-python
again to shutdown the python process. You might get something like [Errno 98] Address already in use
when restarting a client after restarting the server; just wait a sec for the old server to die and try again.halt
(and does sometimes), but not always.Hope that helps!
Upvotes: 1