HelloWord V3

Goal

For this last example, we will create a more sophisticated server : it will

  • calculate the average temperature
  • report its state via a led
  • use a finish state machine (fsm)

We’ll also see how to start it as a service at boot.

The bus

Components and thread are defined the same way we did in the previous tutorial

The bus is now based on a fsm (official site for more documentation)

At first, we must define state and transitions :

states = [
   'booting',
   'sleeping',
   'reporting',
   'ringing',
]
"""The tutorial states :
"""

transitions = [
    { 'trigger': 'wakeup',
        'source': '*',
        'dest': 'reporting',
    },
    { 'trigger': 'report',
        'source': '*',
        'dest': 'reporting',
    },
    { 'trigger': 'sleep',
        'source': '*',
        'dest': 'sleeping',
    },
    { 'trigger': 'ring',
        'source': 'reporting',
        'dest': 'ringing',
    },
]

And start / stop the fsm when starting/stopping the bus :

def start(self, mqttc, trigger_thread_reload_cb=None):
    """Start the bus
    """
    for bus in self.buses:
        self.buses[bus].start(mqttc, trigger_thread_reload_cb=None)
    JNTBus.start(self, mqttc, trigger_thread_reload_cb)
    self._tutorial_statemachine =  Machine(self,
        states=self.states,
        transitions=self.transitions,
        initial='booting')

def stop(self):
    """Stop the bus
    """
    self.stop_check()
    self.sleep()
    for bus in self.buses:
        self.buses[bus].stop()
    JNTBus.stop(self)

We should now define actions when entering states :

def on_enter_reporting(self):
    """
    """
    logger.debug("[%s] - on_enter_reporting", self.__class__.__name__)
    self.bus_acquire()
    try:
        self.nodeman.find_value('led', 'blink').data = 'heartbeat'
        self.nodeman.add_polls(self.polled_sensors, slow_start=True, overwrite=False)
    except:
        logger.exception("[%s] - Error in on_enter_reporting", self.__class__.__name__)
    finally:
        self.bus_release()
    self.on_check()

def on_enter_ringing(self):
    """
    """
    logger.debug("[%s] - on_enter_ringing", self.__class__.__name__)
    self.bus_acquire()
    try:
        self.nodeman.find_value('led', 'blink').data = 'warning'
    except:
        logger.exception("[%s] - Error in on_enter_ringing", self.__class__.__name__)
    finally:
        self.bus_release()

...

The finish state machine

The diagram :

_images/fsm_bus_3.png

Create a server

We will now create a server script, this will allow to start our server on startup:

$ vim src/scripts/jnt_tutorial
import logging
logger = logging.getLogger(__name__)
import sys, os, re, shutil, datetime
from janitoo.runner import Runner, jnt_parse_args
from janitoo_raspberry.server import PiServer

class MyPiServer(Runner):

    def __init__(self):
        Runner.__init__(self)
        self.server = None
        #print self.options

    def app_run(self):
        self.server = PiServer(self.options)
        self.server.start()
        self.server.run()

    def app_shutdown(self):
        self.server.stop()
        self.server = None

daemon_runner = MyPiServer()
#This ensures that the logger file handle does not get closed during daemonization
#daemon_runner.daemon_context.files_preserve=[handler.stream]
daemon_runner.do_action()

Open the init script and update the provides, descriptions and NAME:

$ vim src/scripts/jnt_tutorial.init
### BEGIN INIT INFO
# Provides:             jnt_tutorial
# Required-Start:       $remote_fs $syslog $network
# Required-Stop:        $remote_fs $syslog $network
# Default-Start:        2 3 4 5
# Default-Stop:         0 1 6
# Short-Description:    Janitoo tutorial
# Description:          Janitoo tutorial appliance server
#
### END INIT INFO

NAME="jnt_tutorial"

And copy it to the right directory :

$ sudo cp src/scripts/jnt_tutorial.init /etc/init.d/jnt_tutorial

Spy it

Open a new shell and launch

$ jnt_spy

This will launch a spyer for the mqtt protocol

Go to the first terminal and copy the config file to the config directory. The filename must be the same as the one using for the service name (jnt_tutorial) :

$ cd /opt/janitoo/etc
$ cp /opt/janitoo/src/janitoo_tutorial/tests/data/helloworldv3.conf jnt_tutorial.conf

Open the configuration file and update the needed parts :

$ vim /opt/janitoo/etc/jnt_tutorial.conf
[system]
service = jnt_tutorial
log_dir = /opt/janitoo/log
home_dir = /opt/janitoo/home
pid_dir = /opt/janitoo/run
conf_dir = /opt/janitoo/etc
broker_ip = 127.0.0.1
broker_port = 1883
broker_keepalive = 60
heartbeat_timeout = 10
heartbeat_count = 3
slow_start = 0.5

...

[handler_file]
class = FileHandler
level = DEBUG
formatter = generic
args = ('/opt/janitoo/log/jnt_tutorial.log', 'w')

There are many options startup like slow_start (a dedicated options for slow machines to sleep between startup steps). Look at source for list :(.

You can now starts the service :

$ sudo service jnt_tutorial start

You can look at the protocol during startup on the spyer terminal.

You can also look at logs. In a new terminal :

$ tail -n 100 -f /opt/janitoo/log/jnt_tutorial.log

Its time to query ther server. Go to the first terminal and query the network :

$ jnt_query network

You should receive the list of nodes availables on your server :

hadd       uuid                 name                      location                  product_type
hadd       uuid                 name                      location                  product_type
0225/0000  tutorial3            Hello world               Rapsberry                 Default product type
0225/0002  tutorial3__temperature Temperature               Onewire                   Temperature sensor
0225/0004  tutorial3__led       Led                       GPIO                      Software
0225/0003  tutorial3__cpu       CPU                       Hostsensor                Software component
0225/0001  tutorial3__ambiance  Ambiance 1                DHT                       Temperature/humidity sensor

You can also query a node :

$ jnt_query node --hadd 0225/0000

Check the config values :

$ jnt_query node --hadd 0225/0000 --vuuid request_info_configs
hadd       node_uuid                 uuid                           idx  data                      units      type  genre cmdclass help
0225/0004  tutorial3__led            switch_poll                    0    300                       seconds    4     3     112      The poll delay of the value
0225/0004  tutorial3__led            blink_poll                     0    300                       seconds    4     3     112      The poll delay of the value
0225/0004  tutorial3__led            location                       0    GPIO                      None       8     3     112      The location of the node
0225/0004  tutorial3__led            pin                            0    1                         None       4     3     112      The pin number on the board
0225/0004  tutorial3__led            name                           0    Led                       None       8     3     112      The name of the node
0225/0001  tutorial3__ambiance       temperature_poll               0    300                       seconds    4     3     112      The poll delay of the value
0225/0001  tutorial3__ambiance       name                           0    Ambiance 1                None       8     3     112      The name of the node
0225/0001  tutorial3__ambiance       pin                            0    6                         None       4     3     112      The pin number on the board
0225/0001  tutorial3__ambiance       humidity_poll                  0    300                       seconds    4     3     112      The poll delay of the value
0225/0001  tutorial3__ambiance       location                       0    DHT                       None       8     3     112      The location of the node
0225/0001  tutorial3__ambiance       sensor                         0    11                        None       4     3     112      The sensor type : 11,22,2302
0225/0000  tutorial3                 tutorial3_temperature_poll     0    300                       seconds    4     3     112      The poll delay of the value
0225/0000  tutorial3                 tutorial3_temperature_critical 0    50                        None       4     3     112      The critical temperature. If 2 of the 3 temperature sensors are up to this value, a security notification is sent.
0225/0000  tutorial3                 location                       0    Rapsberry                 None       8     3     112      The location of the node
0225/0000  tutorial3                 name                           0    Hello world               None       8     3     112      The name of the node
0225/0000  tutorial3                 tutorial3_timer_delay          0    45                        None       4     3     112      The delay between 2 checks
0225/0003  tutorial3__cpu            frequency_poll                 0    30                        seconds    4     3     112      The poll delay of the value
0225/0003  tutorial3__cpu            temperature_poll               0    30                        seconds    4     3     112      The poll delay of the value
0225/0003  tutorial3__cpu            voltage_poll                   0    30                        seconds    4     3     112      The poll delay of the value
0225/0003  tutorial3__cpu            location                       0    Hostsensor                None       8     3     112      The location of the node
0225/0003  tutorial3__cpu            name                           0    CPU                       None       8     3     112      The name of the node
0225/0002  tutorial3__temperature    temperature_poll               0    300                       seconds    4     3     112      The poll delay of the value
0225/0002  tutorial3__temperature    location                       0    Onewire                   None       8     3     112      The location of the node
0225/0002  tutorial3__temperature    hexadd                         0    28-00000463b745           None       8     3     112      The hexadecimal address of the DS18B20
0225/0002  tutorial3__temperature    name                           0    Temperature               None       8     3     112      The name of the node

Get the user values :

$ jnt_query node --hadd 0225/0000 --vuuid request_info_users
hadd       node_uuid                 uuid                           idx  data                      units      type  genre cmdclass help
0225/0001  tutorial3__ambiance       temperature                    0    19.0                      °C         3     2     49       The temperature
0225/0001  tutorial3__ambiance       humidity                       0    24.0                      %          3     2     49       The humidity
0225/0000  tutorial3                 tutorial3_temperature          0    None                      °C         3     2     49       The average temperature of tutorial.
0225/0000  tutorial3                 tutorial3_state                0    sleeping                  None       8     2     49       The state of the machine.
0225/0000  tutorial3                 tutorial3_change               0    None                      None       8     2     0        Change the state of the machine.
0225/0003  tutorial3__cpu            frequency                      0    1000                      MHz        3     2     49       The frequency of the CPU
0225/0003  tutorial3__cpu            voltage                        0    1.35                      V          3     2     49       The voltage of the CPU
0225/0003  tutorial3__cpu            temperature                    0    37.9                      °C         3     2     49       The temperature of the CPU
0225/0002  tutorial3__temperature    temperature                    0    19.5                      °C         3     2     49       The temperature

Start it at boot

To start your server on boot, use :

$ sudo update-rc.d jnt_tutorial defaults

You can also stop, restart, kill, ... your server using :

$ sudo service jnt_tutorial
Usage: /etc/init.d/jnt_tutorial {start|stop|restart|reload|status|kill}

Performances

$ nice top
PID   USER      PR  NI  VIRT  RES  SHR S  %CPU %MEM    TIME+  COMMAND
24126 root      20   0 59352  13m 4292 S   5,9  2,7   0:38.28 /usr/bin/python /usr/local/bin/jnt_tutorial -c /opt/janitoo/src/janitoo_tutorial/tests/data/helloworldv