Communication between Integrated Clients Objects#

As shown in Sending and Receiving Tables and Images over SAMP, the SAMPIntegratedClient class can be used to communicate with other SAMP-enabled tools such as TOPCAT, SAO DS9, or Aladin Desktop.

In this section, we look at how we can set up two SAMPIntegratedClient instances and communicate between them.

First, start up a SAMP hub as described in Starting and Stopping a SAMP Hub Server.

Next, we create two clients and connect them to the hub:

>>> from astropy import samp
>>> client1 = samp.SAMPIntegratedClient(name="Client 1", description="Test Client 1",
...                                     metadata = {"client1.version":"0.01"})
>>> client2 = samp.SAMPIntegratedClient(name="Client 2", description="Test Client 2",
...                                     metadata = {"client2.version":"0.25"})
>>> client1.connect()
>>> client2.connect()

We now define functions to call when receiving a notification, call or response:

>>> def test_receive_notification(private_key, sender_id, mtype, params, extra):
...     print("Notification:", private_key, sender_id, mtype, params, extra)

>>> def test_receive_call(private_key, sender_id, msg_id, mtype, params, extra):
...     print("Call:", private_key, sender_id, msg_id, mtype, params, extra)
...     client1.ereply(msg_id, samp.SAMP_STATUS_OK, result = {"txt": "printed"})

>>> def test_receive_response(private_key, sender_id, msg_id, response):
...     print("Response:", private_key, sender_id, msg_id, response)

We subscribe client 1 to "samp.app.*" and bind it to the related functions:

>>> client1.bind_receive_notification("samp.app.*", test_receive_notification)
>>> client1.bind_receive_call("samp.app.*", test_receive_call)

We now bind message tags received by client 2 to suitable functions:

>>> client2.bind_receive_response("my-dummy-print", test_receive_response)
>>> client2.bind_receive_response("my-dummy-print-specific", test_receive_response)

We are now ready to test out the clients and callback functions. Client 2 notifies all clients using the “samp.app.echo” message type via the hub:

>>> client2.enotify_all("samp.app.echo", txt="Hello world!")
['cli#2']
Notification: 0d7f4500225981c104a197c7666a8e4e cli#2 samp.app.echo {'txt':
'Hello world!'} {'host': 'antigone.lambrate.inaf.it', 'user': 'unknown'}

We can also find a dictionary that specifies which clients would currently receive samp.app.echo messages:

>>> print(client2.get_subscribed_clients("samp.app.echo"))
{'cli#2': {}}

Client 2 calls all clients with the "samp.app.echo" message type using "my-dummy-print" as a message-tag:

>>> print(client2.call_all("my-dummy-print",
...                        {"samp.mtype": "samp.app.echo",
...                         "samp.params": {"txt": "Hello world!"}}))
{'cli#1': 'msg#1;;cli#hub;;cli#2;;my-dummy-print'}
Call: 8c8eb53178cb95e168ab17ec4eac2353 cli#2
msg#1;;cli#hub;;cli#2;;my-dummy-print samp.app.echo {'txt': 'Hello world!'}
{'host': 'antigone.lambrate.inaf.it', 'user': 'unknown'}
Response: d0a28636321948ccff45edaf40888c54 cli#1 my-dummy-print
{'samp.status': 'samp.ok', 'samp.result': {'txt': 'printed'}}

Client 2 then calls client 1 using the "samp.app.echo" message type, tagging the message as "my-dummy-print-specific":

>>> try:
...     print(client2.call(client1.get_public_id(),
...                        "my-dummy-print-specific",
...                        {"samp.mtype": "samp.app.echo",
...                         "samp.params": {"txt": "Hello client 1!"}}))
... except samp.SAMPProxyError as e:
...     print("Error ({0}): {1}".format(e.faultCode, e.faultString))
msg#2;;cli#hub;;cli#2;;my-dummy-print-specific
Call: 8c8eb53178cb95e168ab17ec4eac2353 cli#2
msg#2;;cli#hub;;cli#2;;my-dummy-print-specific samp.app.echo {'txt': 'Hello
Cli 1!'} {'host': 'antigone.lambrate.inaf.it', 'user': 'unknown'}
Response: d0a28636321948ccff45edaf40888c54 cli#1 my-dummy-print-specific
{'samp.status': 'samp.ok', 'samp.result': {'txt': 'printed'}}

We can now define a function called to test synchronous calls:

>>> def test_receive_sync_call(private_key, sender_id, msg_id, mtype, params, extra):
...     import time
...     print("SYNC Call:", sender_id, msg_id, mtype, params, extra)
...     time.sleep(2)
...     client1.reply(msg_id, {"samp.status": samp.SAMP_STATUS_OK,
...                            "samp.result": {"txt": "printed sync"}})

We now bind the samp.test message type to test_receive_sync_call:

>>> client1.bind_receive_call("samp.test", test_receive_sync_call)
>>> try:
...     # Sync call
...     print(client2.call_and_wait(client1.get_public_id(),
...                                 {"samp.mtype": "samp.test",
...                                  "samp.params": {"txt": "Hello SYNCRO client 1!"}},
...                                  "10"))
... except samp.SAMPProxyError as e:
...     # If timeout expires than a SAMPProxyError is returned
...     print("Error ({0}): {1}".format(e.faultCode, e.faultString))
SYNC Call: cli#2 msg#3;;cli#hub;;cli#2;;sampy::sync::call samp.test {'txt':
'Hello SYNCRO Cli 1!'} {'host': 'antigone.lambrate.inaf.it', 'user':
'unknown'}
{'samp.status': 'samp.ok', 'samp.result': {'txt': 'printed sync'}}

Finally, we disconnect the clients from the hub at the end:

>>> client1.disconnect()
>>> client2.disconnect()