Getting started =============== This sections intends to be a crash course of **pysig** library, describing basic usage scenarios. For more information please refer to the rest of documentation. It will provide you all the necessary details to understand all the features supported by **pysig**. The basics ********** This example will show how you can create a simple router for managing the flow of events between a listener and a sender. First, lets create a sender. :: import sig # create router object router = sig.Router() # create a sender sender = router.addSender("my_sender", [ "my_event_1", "my_event_2"]) It's simple enough to understand, that we have created a sender object called **my_sender** that may trigger two events in the near future. Let's trigger first event, to see how this is done. :: # create a trigger for my_event_1 sig_ev1 = sender.getSignal("my_event_1") # trigger this event data = { "info1" : "something", "info2" : 2 } # do trigger sig_ev1.trigger(data) For the sake of simplicity, we can write this in only one line: :: router.getSender("my_sender").getSignal("my_event_1").trigger({"info1":"somedata", "info2":2}) Now, what we have done, is to trigger an event with additional data attached, even though no listeners are registered to it. That is no problem for **pysig**, but is somehow useless. Lets define our callback function. :: # this is your callback function def listen_to_events(info, data): event = info.get("event") sender = info.get("sender") print "Received event '%s' from sender '%s' having data '%s'" % (event, sender, data) Now, let's register it to the sender. :: # let's register our listener callback router.addListener(listen_to_events, "my_sender", "my_event_1") It's done, we are listening to the first event of our sender. You may notice, we used the router object for registering the listener. We may also use the sender object with the same effect. :: sender.addListener(listen_to_events, "my_event1") The router object simplifies the registration. But be aware, when using the router, the sender and event parameter are purely optional. That is for a very good reason, the listener may register to **all** sender events or even **all** router events, using special **broadcast** events that will be described later on in this documentation. **Note!** Please note that **sender_identifier** and **event_identifier** are the exact same *objects* passed by the caller to the **Router.addSender** function and not just strings. The only restriction regarding these parameters is that they need to be JSON-serializable and *hashable*. Otherwise said, they can be **str** objects as well as **int**, **float** objects. For example, this code is perfectly valid for **pysig**, but it also may be confusing: :: [...] sender = router.addSender( 20, [ 1, 2.0, "2.0"]) sig_ev1 = sender.getSignal(1) sig_ev2 = sender.getSignal(2.0) sig_ev3 = sender.getSignal("2.0") [...] You can also use dictionaries as long as they are encoded: :: import json [...] sender_name = { "id" : 123, "source" : "http://www.something.com" } sender_name = json.dumps(sender_name) sender = router.addSender(senderName, [1,2,3] ) [...] Example ------- Lets see how it looks, in the proper order. :: import sig # this is your callback function def listen_to_events(info, data): event = info.get("event") sender = info.get("sender") print "Received event '%s' from sender '%s' having data '%s'" % (event, sender, data) # create router router = sig.Router() # create sender sender = router.addSender("my_sender", ["my_event_1", "my_event_2"]) sig_ev1 = sender.getSignal("my_event_1") # register listener router.addListener(listen_to_events, "my_sender", "my_event_1") # trigger first event sig_ev1.trigger({"info1":"something", "info2":2}) The output will be, of course: :: Received event 'my_event_1' from sender 'my_sender' having data '{'info1': 'something', 'info2': 2}' Network ******* The above example is useful for dispatching events *intra-process*, otherwise said, inside the same application. For distributing these events over a network, we have to use the server and client implementation of **pysig**. Carrier ------- The communication between a **pysig** server and its client are assured by the **Carrier** class. This class defines the abstraction layer necessary for **pysig** to communicate. We can implement your own carrier, by deriving from this class. Currently **pysig** supports only built-in **TCP Client** and **TCP Server** carriers. Server ------ In order to receive commands remotely, you need to use instead of the **Router** class the **ServerRouter** class. The difference of this two, is that the second one requires a carrier for instantiation. Client ------ In order to send commands remotely, you need to use the **ClientRouter** class instead of the **Router** class. Also, instead of ``addSender``, ``addListener``, ``removeSender``, ``removeListener`` methods, you are supposed to use the corresponding ``addRemoteSender``, ``addRemoteListener``, ``removeRemoteSender`` and ``removeRemoteListener`` methods. The first set of methods will register senders and listeners only locally, as explained later in this documentation. Examples -------- In the root of **pysig** repository we can find the ``examples`` folder. In this folder we can find several ready to use examples, that shows network functionality support. We will enumerate some of them. ``generic_server.py`` ^^^^^^^^^^^^^^^^^^^^^ This example starts a basic **pysig** router server that also registers a sender which periodically fires a given event. The name of the sender and the event fired can be customized the the caller from **stdin**. :: Log level (default: info): Server bind (default: 0.0.0.0): Server port (default: 3000): Sender (default: timer): Event (default: tick): Event data (default: None):server data Timer delay (default: 5): [sig][ 36][ info][ info]-[TCP_SRV] Starting to listen on 0.0.0.0:3000 [sig][ 79][ ][ info]-[timer] Triggering event tick with data 'server data' ``generic_client_sender.py`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This example shows how to create a router client that connects to a given server. It uses the client to register a custom sender that triggers an event periodically. The address of router, the name of the sender and the name of the event are read from **stdin**. :: Server IP (default: 127.0.0.1): Server port (default: 3000): Sender (default: timer_client): Event (default: tick): Event data (default: None):client data Timer delay (default: 5): [sig][ 36][ info][ info]-[TCP_CL] Connecting to 127.0.0.1:3000 [sig][ 36][ info][ info]-[TCP_CL] Started receive thread for 127.0.0.1:3000 [sig][ 60][ ][ info]-[timer_client] Triggering event tick with data 'client data' ``generic_client_listener.py`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This example shows how to create a router client that connects to a given server. It uses the client to register a listener to a specific event, a **broadcast** event or a **channel** event. It reads the address of the router from **stding** and also the name of the sender and event. The output below, shows how we ran the example and instructed it to listen for the *tick* event issued by the server called *timer*. :: Server IP (default: 127.0.0.1): Server port (default: 3000): Sender (default: None):timer Event (default: None):tick [sig][ 36][ info][ info]-[TCP_CL] Connecting to 127.0.0.1:3000 [sig][ 36][ info][ info]-[TCP_CL] Started receive thread for 127.0.0.1:3000 [sig][ 41][ listener][ info]-[timer] Event tick received with data '"test"' We can also instruct it to listen to the *tick* event issued by the ``generic_client_sender.py`` as long as this example runs. :: Server IP (default: 127.0.0.1): Server port (default: 3000): Sender (default: None):timer_client Event (default: None):tick [sig][ 36][ info][ info]-[TCP_CL] Connecting to 127.0.0.1:3000 [sig][ 36][ info][ info]-[TCP_CL] Started receive thread for 127.0.0.1:3000 [sig][ 41][ listener][ info]-[timer_client] Event tick received with data '"client data"' When ``generic_client_sender.py`` is restarted, the example should output the following: :: [sig][ 34][ listener_connect][ info]-Sender timer_client is now disconnected [sig][ 34][ listener_connect][ info]-Sender timer_client is now connected That is because the ``generic_client_listener.py`` automatically registers for the built-in events **sig.EVENT_CONNECT** and **sig.EVENT_DISCONNECT**, for the indicated sender as this code shows: :: client.addRemoteListener(listener_connect, sender, sig.EVENT_CONNECT) client.addRemoteListener(listener_connect, sender, sig.EVENT_DISCONNECT) client.addRemoteListener(listener, sender, event) **Note!** Using the examples, mentioned above, you can easily experiment the concept of **broadcast** events, **broadcast** senders and **channel** events, by simply modiifying the parameters passed from **stdin**. ``alert_listener.py`` ^^^^^^^^^^^^^^^^^^^^^ This example implements a listener connected remotely to a **pysig** server, to a list of events from a plural of senders, that triggers a Desktop notification for **Linux** users, whenever an event was triggered. ``web_events_logger.py`` ^^^^^^^^^^^^^^^^^^^^^^^^ This example implements a web application that logs all the received events. You need to install ``web.py`` library in order to make it work. You can install ``web.py`` using pip: :: pip install web.py Just like ``alert_listener.py`` this example implements a listener connected remotely to a **pysig** server. From **stdin** you can configure it to listen to a list of specific events, from a list of specific senders or to the **broadcast** event of specific senders or to **channel** events. After is configured you can modifiy the list of subscriptions by using a RESTful API. The log of events are printed out either in **HTML** or **PLAIN** text format. The following is an example input of a possible configuration of ``web_events_logger.py`` example, instructing it to: * connect to the **pysig** server at *192.168.9.20:3000* * limit the number of events to 200 entries, after which the least-recent entry will be deleted * listen to all events from *ifttt* sender (registering to iffttt **broadcast** event) * listen to the specific *ir* event from *keros* sender * listen to the *#notify* channel from **all** senders :: Server IP (default: 127.0.0.1):192.168.9.20 Server port (default: 3000): Queue limit (default: 20):200 Sender 1:ifttt Event 1:location Event 2:weather Event 3: Sender 2:keros Event 1:cap Event 2:ir Event 3: Sender 3: After configuring the web application via **stdin** the following output should be seen: :: [sig][ 36][ info][ info]-[TCP_CL] Connecting to 192.168.9.20:3000 [sig][ 36][ info][ info]-[TCP_CL] Started receive thread for 192.168.9.20:3000 [sig][ 79][perform_registration][ info]-Registering to ifttt:None [sig][ 90][perform_registration][ info]-done [sig][ 79][perform_registration][ info]-Registering to keros:ir [sig][ 90][perform_registration][ info]-done [sig][ 79][perform_registration][ info]-Registering to None:#notify [sig][ 90][perform_registration][ info]-done http://0.0.0.0:8080/ The RESTful API that you can use with this web application is: ``/`` The index page will return the recorded log entries. An optional parameter is the **fmt** parameter that can be passed as **plain** or **html**. Default is **html**. Another set of optional arguments is the **sender** and **event** parameter, that once passed to the GET request it will filter out the log for the given sender and/or event. Ex: ``http://localhost:8080/?fmt=plain`` ``/regs`` or ``/registrations`` Returns the list of subscriptions this application is currently registered at. Same as for index ``/`` page, the optional **fmt** parameter is used to select format. Ex: ``http://localhost:8080/regs?fmt=plain`` ``/clear`` or ``/flush`` Resets the list of logged events. Ex: ``http://localhost:8080/clear`` ``/config`` or ``/cfg`` Receives the optional argument **limit** that is used to modify the limit of events that logs and then outputs a **JSON** containing the current configuration. Ex: ``http://localhost:8080/config?limit=100`` ``/add`` Registers to the given subscription and receives a **sender** and **event** parameter. If you omit one of them, it will be defaulted to **None**. Ex: ``http://localhost:8080/add?sender=timer&event=tick`` ``/remove`` or ``/rem`` Removes a certain registration by using the given **sender** and **event** parameters. If you omit one of them, it will be defaulted to **None**. Ex: ``http://localhost:8080/remove?sender=timer&event=tick``