From Digi Developer
DIA Release 2.0.x (May 2012)
Note: this version is not released yet.
One of the largest changes is the introduction of two different XBee manager devices, so your YML file should now use either of these:
Although ZigBee users should migrate to call the ZigBeeDeviceManager explicitly, YML files including the old XBeeDeviceManager will run the ZigBeeDeviceManager instead.
DigiMesh Sleep Design
The DIA DigiMeshDeviceManager assumes 1 of 2 designs:
- Non-sleeping, so all nodes run as SM=0
- Sleeping, with the gateway acting as the preferred Sleep Coordinator. Nodes will be set to:
- Gateway/Sleep-Coordinator: SM=7, SO=0x05
- Non-Sleeping Node: SM=7, SO=0x02
- Sleeping Node: SM=8, SO=0x02
Support for sleep-coordinator 'Nomination' and the asynchronous/pin-sleep modes of DigiMesh are not yet supported.
DigiMesh YML Changes
Here is an YML usage for the DigiMeshDeviceManager:
- name: xbman driver: devices.xbee.xbee_device_manager.digimesh_device_manager:DigiMeshDeviceManager settings: dh_dl_force: True dh_dl_refresh_min: 300 sleep_time: 9000 wake_time: 1000 set_if: True
The dh_dl_force and dh_dl_refresh_min settings apply to both ZigBee and DigiMesh systems and will be explained in another section of this document.
The three DigiMesh specific settings are:
- sleep_time is an integer which defaults to 2000 (2 seconds), which means the sleep coordinator instructs all nodes wake up every 2 seconds. Set this to 0 to disable sleeping.
- wake_time is an integer which defaults to 2000 (2 seconds), which means the sleep coordinator instructs all nodes to stay awake for 2 seconds. If sleep_time = 0, this setting is ignored.
- set_if is a boolean which defines if DIA will adjust the IR/IF settings in remote nodes. It defaults to False, which assumes the user manually handles data messages. If set to True, then DIA makes a best-effort to map individual driver 'sample_rate' settings to XBee IF intervals.
For example, if a XBee DM AIO adapter has a sample_rate_ms setting of 60000 (once a minute). The default DigiMeshDeviceManager settings of sleep_time = 2 seconds and wake_time = 2 seconds means a sleep-cycle of 4 seconds total. So DIA will set the XBee DM AIO adapter with IR=0xFFFF and IF=0x0F, which means the AIO adapter will send in an IO data sample when it wakes within the 15th sleep-cycle (ie: 60-sec / 4-sec). DIA rounds down if the rates do not factor cleanly.
Internal Driver Changes to Support DigiMesh
Users wishing their custom driver code to run under DigiMesh will need to make some changes to their drivers.
First, drivers must not directly set SM, SO, or IR - they must submit the required settings using the XBee Manager. For example:
xbee_sleep_cfg = self._xbee_manager.get_sleep_block( self._extended_address, sleep=False, sleep_rate_ms=sample_rate, awake_time_ms=0)
The XBee Manager (ZigBee or DigiMesh) uses the 'sleep' parameter to select the correct SM/SO settings. The 'sleep_rate_ms' and 'awake_time_ms' parameters are used to set the setting SN/SP/ST for ZigBee and IR/IF for DigiMesh.
For clearer examples, look at the DIA drivers for the Digi Adapters.
Second, your driver should no longer set the DH or DL settings directly. The support for these have been moved to the XBee Manager since the values required may be different for different XBee technology.
Destination Address (DH/DL) Support
In this release of DIA, drivers should no longer directly set the DH/DL of XBee nodes. This function has moved to the XBeeDeviceManager classes to support different behavior required by different Xbee technologies.
Two new settings have been added which can be used with both ZigBee and DigiMesh systems:
- dh_dl_force is a string and can be:
- None or False, which means do NOT change DH/DL in any node
- True, which means do what is most appropriate for the XBee technology. For ZigBee, this forces the default aggregator address of '00:00:00:00:00:00:00:00!' into all DH/DL. For DigiMesh, this forces the gateway's SH/SL address into all DH/DL.
- Coordinator, which means use the gateway or coordinator SH/SL address for all nodes DH/DL
- The string of an exact address such as '00:13:a2:00:40:32:d9:51!' which is to be used.
- dh_dl_refresh_min is a string which defines when the DH/DL setting is affected.
- None, which means never change DH/DL at all
- Once, which means DH/DL will be set when nodes are configured, and also broadcast once.
- Config, which means DH/DL will only be set when nodes are configured, and not broadcast.
- A time with tag such as '5 min' or '1 day' which defines a repeated broadcast interval. if no tag is applied, then a number is assumed to be in minutes.
Rapid Reboot Detection
Many Digi gateways include an option to reboot if an auto-started Python script exits. This is a wise precaution against an expected error occurring months after reboot, but can make the unit unreachable by Device Cloud if the error occurs repeatedly at the start of the script.
For example, if the main Python script has a simple typo or a ZIP file was accidentally truncated during download, then the auto-started Python script may exit instantly, forcing a reboot within seconds of the last reboot and startup. A gateway locked in such a reboot-cycle will never be connected to Device Cloud long enough to allow fixing the problem.
This release of DIA addresses this within the main dia.py file. By default this feature is disabled, so users wishing this protection must manually create (or seed) a text file named nospin.txt in the python area of the gateway. The file name is case-sensitive, so prevent allowing Windows to rename your file Nospin.txt!
The file can be empty or contain a line of text. Once active, the file will be rewritten by dia.py upon every reboot with a timestamp, and if dia.py detects that the last 10 reboots have occurred in less than 20 minutes, then dia.py will sleep for 10 minutes before trying to start.
If you enable this feature on a gateway without time service (so it always boots as 1-Jan-1970), then after 10 reboots the gateway will always delay starting for 10 minutes. This may be undesirable, but not as undesirable as becoming unreachable from Device Cloud.
Users writing their own auto-start code should consider copying this same function to their own applications.
Other Internal Driver Changes
Older DIA drivers tended to make very poor use of Python or object oriented paradigms. For example, much of the code within most DIA Xbee drivers is duplicated in all of the peers - literally cut-and-pasted. This is a clear violation of Object Programming concepts which assume that if all derived classes will need the same code fragment, then it should be handled by the base class, not repeated within all derived classes.
Besides the previous tracer-levels of ('debug', 'info', 'warning', 'error', 'critical'), two new lower levels are defined BELOW debug.
These should be used for simple debug statements showing a routine was called. This was added because some programmers pepper too many 'debug' statements such as:
def calculate_average(self, a, b): # this should now be self._tracer.calls() instead self._tracer.debug("calculate_average")
These tend to pollute the debug trace with considerable low-grade information. Defining a new sub-debug level named 'calls' allows the user to enable/disable these simple, low-grade debug lines upon demand.
For example, these are used by the DigiMesh manager to announce all of the mesh-wake and mesh-sleep messages received from the gateway XBee. This information is generally used only when debugging the xbee manager, or sleep/performance problems with drivers.
- tracer lines now return a boolean value, True if active
This should be used in situations where the tracer parameters are costly to format. In the example below, the parameter is a time-expensive operation - a STRING conversion of a list of hundreds of integers. Python must evaluate all parameters whether the tracer level is true or not, which can cost a huge time penalty even when tracing is not enabled. Using an if-then statement greatly reduces the performance hit.
if self._tracer.debug(): # my_list is converted to a string ONLY when debug is True self._tracer.debug("list data:%s", str(my_list))
The DeviceBase now creates three variables which can be used by derived classes:
- self._name = name as passed in by def __init__(self, name, core_services, settings, properties)
- self._core = core_services as passed in by def __init__(self, name, core_services, settings, properties)
- self._tracer = get_tracer(name)
In past DIA versions, most derived class duplicated the effort to create and manage these as self.__name, self.__core, and self.__tracer. These cause no harm, but waste resources and prevent derived classes from being created from derived classes. When porting drivers to the new DIA, you are encouraged to use DeviceBase's copies of these variables.
The DeviceBase also now has a setting named 'trace', which defaults to , which means use global trace level in self._tracer. It can hold any valid tracer-level for the Tracer module, such as 'debug', 'info', and so on. This is used when a user has for example 20 devices, but wants only 1 of them to output tracer.debug lines, and the other 19 to output tracer.info lines.
The XBeeBase now creates and manages two variables which can be used by derived classes:
- self._xbee_manager, which is set by the code:
# Fetch the XBee Manager name from the Settings Manager: dm = self._core.get_service("device_driver_manager") self._xbee_manager = dm.instance_get( SettingsBase.get_setting(self, "xbee_device_manager"))
- self._extended_address, which is set by the code:
# Get the extended address of the device: self._extended_address = SettingsBase.get_setting(self, "extended_address")
This also means the XBeeBase class manages the settings named "xbee_device_manager" and "extended_address". Derived classes should attempt to create or manage these settings. When porting drivers to the new DIA, you are encouraged to use XBeeBase's copies of these variables and settings.
The XBeeBase class registers a callback named self.running_indication() which derived classes can overload as desired. The default base class routine looks like this:
def running_indication(self): """ Indicate that we have completed config and are running """ self._tracer.info("Configuration is Complete. Running indication.") return