Changes between Version 9 and Version 10 of Tutorial


Ignore:
Timestamp:
Aug 22, 2013, 11:34:46 AM (11 years ago)
Author:
Frédéric
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Tutorial

    v9 v10  
    2929Functional Block-s can be deployed as one or several Device-s. In '''pKNyX''', each Device runs as a process. All Device-s can run on the same machine, or spread over a set of machines. Like in real KNX world.
    3030
    31 == Create the device ==
    32 
    33 As said, '''pKNyX''' main entry is a Device, which will run as a process. As any good framework, '''pKNyX''' helps in starting writing code by auto-generating some usefull files, in a specific hierarchy, to implement this Device in a correct way.
    34 
    35 The tool to do that is '''{{{pknyx-admin.py}}}'''. This a global tool to manage devices (create/check/start). Let's see how to use it to create a fresh device:
     31== Create device structure ==
     32
     33As any good framework, '''pKNyX''' helps in starting writing code by auto-generating some usefull files, in a specific hierarchy, to implement a Device in a correct way.
     34
     35The tool to do that is '''{{{pknyx-admin.py}}}'''. This a global tool to manage devices (create/check/run). Let's see how to use it to create a fresh device:
    3636
    3737{{{
    3838$ pknyx-admin.py createdevice timer
    39 create 'timer' from template...
    40 done
     39enerate 'timer' structure from template...
     40'timer' dir created
     41'timer/admin.py' file generated
     42'timer/timer' dir created
     43'timer/timer/__init__.py' file generated
     44'timer/timer/config.py' file generated
     45'timer/timer/device.py' file generated
     46'timer/timer/fb' dir created
     47'timer/timer/fb/__init__.py' file generated
     48'timer/timer/fb/timerFB.py' file generated
     49'timer/timer/plugins' dir created
     50'timer/timer/plugins/__init__.py' file generated
     51'timer' structure done
    4152}}}
    4253
     
    4556{{{
    4657$ tree timer
    47 timer
     58timer/
    4859├── admin.py
    4960└── timer
    5061    ├── config.py
    5162    ├── device.py
    52     └── __init__.py
    53 
    54 1 directory, 4 files
    55 }}}
    56 
    57 The top-level dir {{{timer}}} can be renamed as you want. It contains a script called '''{{{admin.py}}}'''; this script is in fact the {{{pknyx-admin.py}}} one, with a pre-defined env var pointing on our timer structure, in order to manage it. It is possible to use the global {{{pknyx-admin.py}}} script, but it would need to define $PKNX_DEVICE_PATH var and make it point to the second {{{timer}}} dir, so that the python interpreter can correctly import our files. So, for now, let's use the {{{admin.py}}} script.
    58 
    59 The '''{{{config.py}}}''' file contains a few pre-defined constants:
     63    ├── fb
     64    │   ├── __init__.py
     65    │   └── timerFB.py
     66    ├── __init__.py
     67    └── plugins
     68        └── __init__.py
     69
     703 directories, 7 files
     71}}}
     72
     73The top-level dir {{{timer}}} can be renamed as you want. All further references are from this dir.
     74
     75This dir contains a script called '''{{{admin.py}}}'''; this script is in fact the {{{pknyx-admin.py}}} one, with a pre-defined env var pointing on our timer structure, in order to manage it. It is possible to use the global {{{pknyx-admin.py}}} script, but it would need to define $PKNX_DEVICE_PATH var and make it point to the second {{{timer}}} dir, so that the python interpreter can correctly import our files. So, for now, let's use the {{{admin.py}}} script.
     76
     77The '''{{{timer/config.py}}}''' file contains a few pre-defined constants:
    6078
    6179{{{
     
    7492This is where we will add new configs values for our Device, if needed.
    7593
    76 The last file is '''{{{device.py}}}'''; this is the most important one, where we will implement our Device code:
    77 
    78 {{{
    79 """# -*- coding: utf-8 -*-
    80 
    81 from pknyx.api import Device, FunctionalBlock
     94The next file, '''{{{timer/device.py}}}''', contains a Device example:
     95
     96{{{
     97#!python
     98# -*- coding: utf-8 -*-
     99
     100from pknyx.api import Device
     101
     102from fb.timerFB import TimerFB
     103
     104
     105class Timer(Device):
     106    FB_01 = dict(cls=TimerFB, name="timer_fb", desc="timer fb")
     107
     108    LNK_01 = dict(fb="timer_fb", dp="dp_01", gad="1/1/1")
     109
     110    DESC = "Timer"
     111
     112
     113DEVICE = Timer
     114}}}
     115
     116Last generated file is '''{{{timer/fb/timerFB.py}}}'''; it contains a Functional Block example:
     117
     118{{{
     119#!python
     120# -*- coding: utf-8 -*-
     121
     122from pknyx.api import FunctionalBlock
    82123from pknyx.api import logger, schedule, notify
    83124
    84125
    85 class FB(FunctionalBlock):
     126class TimerFB(FunctionalBlock):
    86127    DP_01 = dict(name="dp_01", access="output", dptId="1.001", default="Off")
    87128
    88129    GO_01 = dict(dp="dp_01", flags="CWT", priority="low")
    89130
    90     DESC = "FB"
    91 
    92 
    93 class Timer(Device):
    94     FB_01 = dict(cls=FB, name="fb_01", desc="fb 01")
    95 
    96     LNK_01 = dict(fb="fb_01", dp="dp_01", gad="1/1/1")
    97 
    98     DESC = "Timer"
    99 
    100 
    101 DEVICE = Timer
    102 }}}
    103 
    104 As you see, it already contains a dummy Functional Block, and a dummy Device, in order to show how things work.
    105 
    106 == The Timer example ==
     131    DESC = "Timer FB"
     132}}}
     133
     134== Timer implementation ==
    107135
    108136Ok, lets's start to implement our simple timer example. This timer monitors the state of a light, and switches it off automatically after a delay.
    109137
    110 Let's modify the {{{device.py}}} according to this:
    111 
    112 {{{
    113 #!python
    114 from pknyx.api import Device, FunctionalBlock
     138First, modify the {{{timerFB.py}}} file according to this:
     139
     140{{{
     141#!python
     142# -*- coding: utf-8 -*-
     143
     144from pknyx.api import FunctionalBlock
    115145from pknyx.api import logger, schedule, notify
    116146
     
    154184            logger.info("%s: delay changed; restart timer" % self._name)
    155185            self._timer = delay
     186}}}
     187
     188Then, modify {{{device.py}}} like this:
     189
     190{{{
     191#!python
     192# -*- coding: utf-8 -*-
     193
     194from pknyx.api import Device
     195
     196from fb.timerFB import TimerFB
    156197
    157198
    158199class Timer(Device):
    159     FB_01 = dict(cls=TimerFB, name="timerfb", desc="timer fb")
     200    FB_01 = dict(cls=TimerFB, name="timer_fb", desc="timer fb")
    160201
    161202    LNK_01 = dict(fb="timerfb", dp="cmd", gad="1/1/1")
     
    163204    LNK_03 = dict(fb="timerfb", dp="delay", gad="1/3/1")
    164205
    165     DESC = "Timer device"
     206    DESC = "Timer"
    166207
    167208
     
    175216{{{
    176217$ ./admin.py checkdevice
    177 MainThread::AdminUtility._checkRunDevice(): logger level is 'info'
    178 MainThread::AdminUtility._checkRunDevice(): config path is './timer'
    179 MainThread::AdminUtility._checkRunDevice(): device name is 'timer'
    180 MainThread::AdminUtility._checkRunDevice(): device individual address is '1.1.1'
    181 no error found
     218Logger level is 'info'
     219Config path is './timer'
     220Device name is 'timer'
     221Device Individual Address is '1.1.1'
     222No error found
    182223$
    183224}}}
     
    187228{{{
    188229$ ./admin.py rundevice
    189 MainThread::AdminUtility._checkRunDevice(): logger level is 'info'
    190 MainThread::AdminUtility._checkRunDevice(): config path is './timer'
    191 MainThread::AdminUtility._checkRunDevice(): device name is 'timer'
    192 MainThread::AdminUtility._checkRunDevice(): device individual address is '1.1.1'
    193 MainThread::AdminUtility._runDevice(): detach is 'False'
    194 MainThread::Scheduler started
    195 MainThread::Stack running
     230Logger level is 'info'
     231Config path is './timer'
     232Device name is 'timer'
     233Device Individual Address is '1.1.1'
     234Detaching is 'False'
     235Scheduler started
     236Stack running
    196237}}}
    197238
     
    199240
    200241{{{
    201 LinkLayer::timerfb: start timer for 10s
    202 
    203 Thread-15::timerfb: timer expired; switch off
     242timerfb: start timer for 10s
     243
     244timerfb: timer expired; switch off
    204245}}}
    205246
     
    210251'''Important note: to avoid internal loops, a device drops all telegrams sent by itself. So, if you want 2 virtual devices to communicate, you must ensure that they don't use the same source address, like in a real installation. Keep this in mind ; if you don't see telegrams you are expecting, the problem may be there.'''
    211252
    212 So, lets deep inside this example to explain how things work. First, we import some python objects:
    213 
    214 {{{
    215 #!python
    216 from pknyx.api import Device, FunctionalBlock
     253So, lets deep inside this example to explain how things work, starting with the {{{timer/timer/gb/timerFB.py}}} module.
     254
     255First, we import some python objects:
     256
     257{{{
     258#!python
     259from pknyx.api import FunctionalBlock
    217260from pknyx.api import logger, schedule, notify
    218261}}}
    219262
    220 '''{{{Device}}}''' and '''{{{FunctionalBlock}}}''' are classes; '''{{{logger}}}''', '''{{{schedule}}}''', '''{{{notify}}}''' are instances.
     263'''{{{FunctionalBlock}}}''' is a class; '''{{{logger}}}''', '''{{{schedule}}}''', '''{{{notify}}}''' are instances.
    221264
    222265The {{{logger}}} object is a global logger service, which allows you to output all informations you want, at different levels. '''pKNyX''' makes a big usage of that logger. By default, the template set the logger level to '''{{{info}}}''', but you can set it to other levels. See the documentation.
     
    307350I think you got the point ;o)
    308351
    309 Now we need to implement our Device:
     352Now we need to implement our Device, in {{{timer/timer/device.py}}}:
     353
     354{{{
     355#!python
     356# -*- coding: utf-8 -*-
     357
     358from pknyx.api import Device
     359
     360from fb.timerFB import TimerFB
     361}}}
     362
     363A few imports; most important one is, of course, our custom Functional Block.
     364
     365Then, we implement our Device:
    310366
    311367{{{
    312368#!python
    313369class Timer(Device):
    314     FB_01 = dict(cls=TimerFB, name="timerfb", desc="timer fb")
    315 
    316     LNK_01 = dict(fb="timerfb", dp="cmd", gad="1/1/1")
    317     LNK_02 = dict(fb="timerfb", dp="state", gad="1/2/1")
    318     LNK_03 = dict(fb="timerfb", dp="delay", gad="1/3/1")
    319 
    320     DESC = "Timer device"
    321 }}}
    322 
    323 As you see, it must inherits the {{{Device}}} class. Like for the Function Block, things are defined through class attributes; '''pKNyX''', like python, tries to limit the number of paradigms.
     370    FB_01 = dict(cls=TimerFB, name="timer_fb", desc="timer fb")
     371
     372    LNK_01 = dict(fb="timer_fb", dp="cmd", gad="1/1/1")
     373    LNK_02 = dict(fb="timer_fb", dp="state", gad="1/2/1")
     374    LNK_03 = dict(fb="timer_fb", dp="delay", gad="1/3/1")
     375
     376    DESC = "Timer"
     377}}}
     378
     379As you see, it inherits the {{{Device}}} class. Like for the Function Block, things are defined through class attributes; '''pKNyX''', like python, tries to limit the number of paradigms.
    324380
    325381We first need to tell which Function Block(-s) our device will use. This is done by creating a dict, which name must start with '''{{{FB_}}}'''. Here, we only have one Functional Block.