====================
Agent communications
====================

Using templates
---------------

Templates is the method used by SPADE to dispatch received messages to the behaviour that is waiting for that message.
When adding a behaviour you can set a template for that behaviour, which allows the agent to deliver a message received
by the agent to that registered behaviour. A ``Template`` instance has the same attributes of a ``Message`` and all the
attributes defined in the template must be equal in the message for this to match.

The attributes that can be set in a template are:

* **to**: the jid string of the receiver of the message.
* **sender** the jid string of the sender of the message.
* **body**: the body of the message.
* **thread**: the thread id of the conversation.
* **metadata**: a (key, value) dictionary of strings to define metadata of the message. This is useful, for example, to include `FIPA <http://www.fipa.org>`_ attributes like *ontology*, *performative*, *language*, etc.

An example of template matching::

    template = Template()
    template.sender = "sender1@host"
    template.to = "recv1@host"
    template.body = "Hello World"
    template.thread = "thread-id"
    template.metadata = {"performative": "query"}

    message = Message()
    message.sender = "sender1@host"
    message.to = "recv1@host"
    message.body = "Hello World"
    message.thread = "thread-id"
    message.set_metadata("performative", "query")

    assert template.match(message)

Templates also support boolean operators to create more complex templates. Bitwise operators (&, |, ^ and ~) may be used
to combine simpler templates.

* **&**: Does a boolean AND between templates.
* **|**: Does a boolean OR between templates.
* **^**: Does a boolean XOR between templates.
* **~**: Returns the complement of the template.

Some examples of these operators::

    t1 = Template()
    t1.sender = "sender1@host"
    t2 = Template()
    t2.to = "recv1@host"
    t2.metadata = {"performative": "query"}


    m = Message()
    m.sender = "sender1@host"
    m.to = "recv1@host"
    m.metadata = {"performative": "query"}

    # And AND operator
    assert (t1 & t2).match(m)

    t3 = Template()
    t3.sender = "not_valid_sender@host"

    # A NOT complement operator
    assert (~t3).match(m)


Sending and Receiving Messages
------------------------------

As you know, messages are the basis of every MAS. They are the centre of the whole "computing as interaction" paradigm
in which MAS are based. So it is very important to understand which facilities are present in SPADE to work with
agent messages.

First and foremost, threre is a ``Message`` class. This class is ``spade.message.Message`` and you can instantiate it to
create new messages to work with. The class provides a method to introduce metadata into messages, this is useful for
using the fields present in standard FIPA-ACL Messages. When a message is ready to be sent, it can be passed on to the
send() method of the behaviour, which will trigger the internal communication process to actually send it to its
destination. Note that the send function is an async coroutine, so it needs to be called with an ``await`` statement.

.. warning:: Remember to change the example's jids and passwords by your own accounts. These accounts do not exist
    and are only for demonstration purposes.

Here is a self-explaining example::

    import spade
    from spade.agent import Agent
    from spade.behaviour import OneShotBehaviour
    from spade.message import Message
    from spade.template import Template


    class SenderAgent(Agent):
        class InformBehav(OneShotBehaviour):
            async def run(self):
                print("InformBehav running")
                msg = Message(to="receiver@your_xmpp_server")     # Instantiate the message
                msg.set_metadata("performative", "inform")  # Set the "inform" FIPA performative
                msg.set_metadata("ontology", "myOntology")  # Set the ontology of the message content
                msg.set_metadata("language", "OWL-S")       # Set the language of the message content
                msg.body = "Hello World"                    # Set the message content

                await self.send(msg)
                print("Message sent!")

                # set exit_code for the behaviour
                self.exit_code = "Job Finished!"

                # stop agent from behaviour
                await self.agent.stop()

        async def setup(self):
            print("SenderAgent started")
            self.b = self.InformBehav()
            self.add_behaviour(self.b)


    async def main():
        senderagent = SenderAgent("sender@your_xmpp_server", "sender_password")
        await senderagent.start(auto_register=True)
        print("Sender started")

        await spade.wait_until_finished(receiveragent)
        print("Agents finished")


    if __name__ == "__main__":
        spade.run(main())



This code would output::

    $ python sender.py
    SenderAgent started
    InformBehav running
    Message sent!
    Agent finished with exit code: Job Finished!



Ok, we have sent a message but now we need someone to receive that message. Show me the code::

    import spade
    from spade.agent import Agent
    from spade.behaviour import OneShotBehaviour
    from spade.message import Message
    from spade.template import Template


    class SenderAgent(Agent):
        class InformBehav(OneShotBehaviour):
            async def run(self):
                print("InformBehav running")
                msg = Message(to="receiver@your_xmpp_server")     # Instantiate the message
                msg.set_metadata("performative", "inform")  # Set the "inform" FIPA performative
                msg.body = "Hello World"                    # Set the message content

                await self.send(msg)
                print("Message sent!")

                # stop agent from behaviour
                await self.agent.stop()

        async def setup(self):
            print("SenderAgent started")
            b = self.InformBehav()
            self.add_behaviour(b)

    class ReceiverAgent(Agent):
        class RecvBehav(OneShotBehaviour):
            async def run(self):
                print("RecvBehav running")

                msg = await self.receive(timeout=10) # wait for a message for 10 seconds
                if msg:
                    print("Message received with content: {}".format(msg.body))
                else:
                    print("Did not received any message after 10 seconds")

                # stop agent from behaviour
                await self.agent.stop()

        async def setup(self):
            print("ReceiverAgent started")
            b = self.RecvBehav()
            template = Template()
            template.set_metadata("performative", "inform")
            self.add_behaviour(b, template)



    async def main():
        receiveragent = ReceiverAgent("receiver@your_xmpp_server", "receiver_password")
        await receiveragent.start(auto_register=True)
        print("Receiver started")

        senderagent = SenderAgent("sender@your_xmpp_server", "sender_password")
        await senderagent.start(auto_register=True)
        print("Sender started")

        await spade.wait_until_finished(receiveragent)
        print("Agents finished")


    if __name__ == "__main__":
        spade.run(main())


.. note:: It's important to remember that the send and receive functions are **coroutines**, so they **always**
    must be called with the ``await`` statement.

In this example you can see how the ``RecvBehav`` behaviour receives the message because the template includes a
*performative* with the value **inform** in the metadata and the sent message does also include that metadata, so the
message and the template match.

The code below would output::

    $ python send_and_recv.py
    ReceiverAgent started
    Receiver started
    RecvBehav running
    SenderAgent started
    Sender started
    InformBehav running
    Message sent!
    Message received with content: Hello World
    Agents finished

    Process finished with exit code 0

