The diagram below shows the overall structure of the application.
Sensors such as Monnit Wireless Sensors post data across the Internet to the BMON Django application located on the web server. The sensor readings are stored in a SQLite database dedicated to those readings. The Django application utilizes a separate database to store other objects needed by the BMON system, such as building information, sensor descriptions, alert conditions, etc. This database is the standard Django database that is generated from the Models in the Django application.
Details on Django Server Application¶
This section presents some further details on the Django Server
application. In Django vocabulary, the BMON Django Project consists of
bmsapp application. Thus, the important code files for the
application are found in the
bmon/bmsapp directory. Here is a list
of the directories contained in the
The following sections describe some of the key modules used by the application.
views.py Module (root bmsapp directory)¶
Some of the important functions in the
views.py module are:
For Storing Sensor Readings
store_reading(): Stores one sensor reading in the sensor reading database.
store_readings(): Stores multiple sensor readings in the sensor reading database.
For Providing Content to the Browser Application
reports(): Returns the main Reports page which is used to configure and view all of the charts and reports provided by the BMON application. The Django template used to create the page is the reports.html template.
get_report_results(): The Reports page in the browser is a Single Page Application. When the User makes a change to the input controls on the page that affects the report or chart, this function is called by the browser to request the new report or chart content. More explanation of the data format returned is provided later in this document.
bldg_list(): Returns a list of buildings to display to the user for selection.
chart_sensor_list(): Returns a list of charts/reports and a list of sensors that are appropriate for the current building selected by the user.
Sensor Reading Database¶
All interactions with the Sensor Reading Database occur through the
BMSdata class located in the bmsdata.py module. The class
contains methods for storage of sensor readings, retrieval of sensor
readings, and database backup operations. For an installed BMON system,
the first sensor reading database operation will cause the creation of
the SQLite database, which will be stored in the
bmon/bmsapp/readingdb/data directory. When the
BMSdata.backup_db() method is called, the backup files of the SQLite
database are placed in the
backup files older than a certain number of days (a method parameter)
are deleted when a new backup file is stored.
To use a different database technology, the only part of the BMON
application that would need to be changed is the
as all access to the database occurs through that module.
Each report or chart type provided by BMON is mapped to a Python class
located in the
bmon/bmsapp/reports directory that generates the
report or chart. In the basechart.py file, you can see how
each single building chart is mapped to a particular class that is used to render the chart:
# These are the possible chart types currently implemented, in the order they will be # presented to the User. BLDG_CHART_TYPES = [ BldgChartType(0, 'Dashboard', 'dashboard.Dashboard'), BldgChartType(1, 'Current Sensor Values', 'currentvalues.CurrentValues'), BldgChartType(2, 'Plot Sensor Values over Time', 'timeseries.TimeSeries'), BldgChartType(3, 'Hourly Profile of a Sensor', 'hourlyprofile.HourlyProfile'), BldgChartType(4, 'Histogram of a Sensor', 'histogram.Histogram'), BldgChartType(5, 'Sensor X vs Y Scatter Plot', 'xyplot.XYplot'), BldgChartType(6, 'Download Sensor Data to Excel', 'exportdata.ExportData') ]
The last constructor parameter for BldgChartType gives the class that is
used to create the chart. For example, the Histogram chart is created by
Histogram class located in the
For charts/reports that present data from multiple buildings, the
mapping from chart type to class occurs in the models.py
file, in the definition of the
class MultiBuildingChart(models.Model): ''' One particular chart that utilizes data from a group of buildings ''' # descriptive title of the Chart title = models.CharField(max_length=60, unique=True) MULTI_CHART_CHOICES = ( ('currentvalues_multi.CurrentValuesMulti', 'Current Sensor Values'), ('normalizedbyddbyft2.NormalizedByDDbyFt2', 'Energy / Degree-Day / ft2'), ('normalizedbyft2.NormalizedByFt2', 'Energy / ft2'), ) ... more code
You can see that the Multi-building Current Sensor Values report is
produced by the
class. If you wish to create an additional type of multi-building
report, you need to add a new choice in
MULTI_CHART_CHOICES and then
create the class that the new report is mapped to in the
These chart classes all must have a
result() method that returns the
report/chart content. This report/chart content is used to fill out the
id="results" HTML div element on the Reports page in the browser.
The return value from the
result() function is generally a Python
dictionary with two keys: an
html key and an
objects key. The
value of the
html key is the HTML that is inserted into the
results div on the browser page. The value of the
objects key is
a list of two-tuples, one two-tuple for each object that the browser
needs to create. Objects that the BMON client app knows how to create
are Plotly charts and Dashboards. The fist element of the two-tuple is
the object type that the browser should create (
dashboard), and the second element is a configuration dictionary for
that particular object.
Transforms and Calculated Fields¶
calculated-fields allow for new sensor readings to be created from mathematical combinations of other readings or from acquisition from the Internet. The general code for creating calculated fields is in this module. The specific calculated field functions are currently found in the calcfuncs01.py module, although other modules could be created to hold specific calculated field functions. A Cron job runs main_cron.py which in turn runs calc_readings.py to control the process of creating calculated fields.
Main Cron Job¶
BMON utilizes the Linux Cron utility to run a script that performs a number of tasks that are repeated at equal time intervals. (A better design may have been to start a separate thread in BMON to perform this repetitive task.) As shown in the How to Install BMON on a Web Server document, the Cron job entry looks like the following (the directory paths are dependent on your specific install):
*/5 * * * * ~/webapps/bmon_django/bmon/manage.py runscript main_cron > /dev/null 2>&1
This cron job:
- creates calculated reading values and stores Internet weather data in the reading database every half hour,
- checks for active Alert Conditions every five minutes,
- creates a daily status line in the log file indicating how many sensor readings were stored in the database during the past day (viewable by browsing to
- creates a backup of the main Django database every day, and
- creates a backup of the reading database every three days.
The Cron job executes the main_cron.py script by using the Django Extensions runscript feature. This runscript command allows the scripts to operate within the Django context, having full access to Django models and the settings file, for example.
Details on the Client Web Browser Application¶
For the Alaska Housing Finance Corporation installation of the BMON application, there are three main BMON pages displayed for the data-viewing user (not the Admin user):
- A Map page that displays a Google map with clickable dots at the locations of the buildings containing sensors.
- The main Charts/Reports page of the application where the user can view and analyze the sensor data.
- A Training page with some video tutorials and report information.
The choice of main pages is somewhat configurable through the Django settings file. Here is the section of the settings file that allows configuration and selection of the Default page that will show when a user browses to the base URL for the application:
# Information about the Navigation links that appear at the top of each web page. # First item in tuple is Text that will be shown for the link. # Second item is the name of the template that will be rendered to produce the page. # 'reports' is a special name that will cause the main reports/charts page to be # rendered. For other names in this position, there must be a corresponding # [template name].html file present in the templates/bmsapp directory. The custom # template cannot match any of the URLs listed in urls.py. # The third item (optional) is True if this item should be the default index page for # the application. BMSAPP_NAV_LINKS = ( ('Map', 'map'), ('Data Charts and Reports', 'reports', True), ('Training Videos and Project Reports', 'training_anthc'), )
The main Charts/Reports page is the core of the application. It functions as a Single Page Application. With the initial loading of that page, all of the static HTML of the page and all of HTML Input elements are downloaded from the server. As the user changes inputs, AJAX calls are made to the server to update the contents of various selection inputs (e.g. update the sensor selection dropdown due to a new building being selected) and to update the final chart or report results.
User Interface Configuration based on Chart/Report Type¶
As discussed before, each type of chart or report has Python class on the server that is responsible for generation of the chart/report. Each one of those classes also contains some configuration properties that control:
- which HTML input elements are visible in the browser client for that chart type,
- whether multiple sensors can be selected for this chart type,
- whether the chart or report should be automatically refreshed when inputs change,
- whether an automatic timed refresh of the chart or report should occur every 10 minutes.
BaseChart class from which all chart/report classes inherit,
you can see the class properties that control these elements of the
browser interface described above:
class BaseChart(object): """Base class for all of the chart classes. """ # Constants to override, if needed for the specific chart being created. # These constants affect configuration of the browser user interface that # will be used for this particular chart. # This is a comma-separated list of the client HTML controls that need to be # visible for this chart. CTRLS = 'time_period, refresh' # 1 if the Sensor selection control should allow for selecting more than one # sensor. MULTI_SENSOR = 0 # 1 if the chart should automatically recalculate and refresh when the user # changes inputs. AUTO_RECALC = 1 # 1 if the chart should automatically refresh every 10 minutes even without # changes in user inputs TIMED_REFRESH = 0
Updating the Main Chart/Report Content¶
As the user changes inputs in the browser application, the main chart or
report requires updating. When an update is required, the browser makes
a request to the server
reports/results/ URL, passing all of the
user’s inputs to the server. The server responds with JSON content
created from the
result() method for the particular chart type being
viewed. This content was described above in the Report/Chart Creation
section and consists of an HTML string and list of objects for the
browser to create. Here is the CoffeeScript code in
that requests that content from the server, inserts the HTML in the
results div and then creates the requested browser objects (Plotly
charts and Dashboards.)
If an object is a ‘dashboard’, it is created by the CoffeeScript code in the dashboard.coffee file.