Archiving Data Files from Device Cloud

From Digi Developer

Jump to: navigation, search

Contents

Archiving Data Files from Device Cloud

When experimenting with XBee sensors, solar power and other fun technologies, it may be useful to archive your old data files on your own server in raw XML form. For example, someone testing use of solar power for a remote sensor could archive data for a full year, then run a report comparing the solar panel voltage verse battery voltage verse temperature verse month of the year.

Using Device Cloud presentations such as idigi_db (included in DIA) or idigi_upload (on this page Dia_Event_Uploader), you'll have a circular collection of N files which eventually overwrite themselves. For example, if you upload every hour and have at most 25 files, then your Device Cloud account will hold only 1 days worth of data.

Running by CRON

The Python script below can be run by a task scheduler every few hours to copy the data into a local archive. For example CRON can be used on a Unix/Linux server.

Assuming the user which runs this job runs at low permission level, you should create a shell script which runs this python script, saving the output into a log location. User ownership and permissions can be tricky, but with care it will work. For example, you can archive the files to a SAMBA share drive under a special SAMBA user.

Python Code

This Python application runs under Windows or Linux - it is not designed to run on a Digi gateway.

ZIP Archive

Here is the Python code as a ZIP file: Media:Archive.zip

Full Code

If you wish to browse the file for ideas, it is included inline below

# Archive.py - download the XML files from the Device Cloud server
 
import os
import sys
import time
import urllib, urllib2
from urllib2 import URLError
 
# put your idigi account info here
IDIGI_URL = "http://developer.idigi.com"
USERNAME = "my_nama"
PASSWORD = "my_passa"
 
# your archive builds below this - if you use CRON to run this script,
# then you'll want the full path name
ARCHIVEBASE = '/home/idigi/archive'
 
# archive files are named with the modified-date from the Device Cloud storage
# in a form 'YYYYMMDD_HHMM' such as '20100812_1045'
 
# set to True to append the seconds, False to truncate them
ADD_SECS = False
 
# set True to force the seconds to be '00', ignored if ADD_SECS = False
ZERO_SECS = True
 
           # tuple of device id and storage directory tag name
DEV_LIST = [ ("00010000-00000000-03560210-13650987", 'x3_aio'),
             ("00000000-00000000-00409DFF-FF36EE2C", 'demo_solar'),
             ]
 
# In this example, the archive will store files as as:
#   /home/idigi/archive/x3_aio/x3_aio_20100812_1045.xml
#   /home/idigi/archive/x3_aio/x3_aio_20100812_1100.xml
#   /home/idigi/archive/demo_solar/demo_solar_20100812_1045.xml
#   /home/idigi/archive/demo_solar/demo_solar_20100812_1100.xml
# and so on
 
def log_into_idigi(username, password, idigiurl):
    '''Open the HTTP socket with login to idigi.com'''
    pwdManager = urllib2.HTTPPasswordMgrWithDefaultRealm()
    pwdManager.add_password(None, idigiurl, username, password)
    authHandler = urllib2.HTTPBasicAuthHandler(pwdManager)
    opener = urllib2.build_opener(authHandler)
    urllib2.install_opener(opener)
    return
 
def get_file_list(idigiurl, devid):
    '''Fetch a list of files in this device storage, which
    are returned as a list of tuples such as:
     [('data02.xml', '20100812_1045'),
      ('data03.xml', '20100812_1100')]
    These represent the file name in idigi and modified time
    '''
    dataurl = "%s/ws/data/~/%s/" % (idigiurl, devid)
 
    try:
        f = urllib2.urlopen(dataurl)
        data = f.read()
        # print data
        f.close()
 
    except URLError, e:
        print e.code
        print e.read()
        f.close()
        sys.exit(1)
 
    data = data.split('<')
    lst = []
    for ln in data:
        x = parse_fileinfo( ln)
        if x is not None:
            lst.append(x)
 
    return lst
 
def parse_fileinfo( ln):
    '''Return a tuple of file name and string of modified date,
    such as ('data02.xml', '20100812_1045')
    '''
 
    # you could expand this to use SAX or another XML parser, but
    # since we just extract 2 pieces of info per line, this brute
    # force method works and is likely faster
 
    if ln.startswith('exist:resource'):
        # print 'ln:%s' % ln
        x = ln.find('name=')
        if x >= 0:
            st = ln[x+6:].split('"')
            name = st[0]
            x = ln.find('modified=')
            if x >= 0:
                st = ln[x+10:].split('"')
                date = st[0]
                date = date[0:4] + date[5:7] + date[8:10] + '_' + \
                       date[11:13] + date[14:16]
                if ADD_SECS:
                    # then user wants the seconds in the name
                    if ZERO_SECS:
                        # then just treat as seconds
                        date += '00'
                    else: # else save actual
                        date += date[17:19]
                return (name, date)
 
    return None
 
def get_one_file(idigiurl, devid, filnam):
    '''Download one file as a string'''
    dataurl = "%s/ws/data/~/%s/%s" % (idigiurl, devid, filnam)
 
    try:
        f = urllib2.urlopen(dataurl)
        data = f.read()
        f.close()
    except URLError, e:
        print e.code
        print e.read()
        f.close()
        sys.exit(1)
 
    return data
 
def main():
 
    for dev_id, dev_nam in DEV_LIST:
        # verify the archive sub-directory exists
        arch = os.path.join(ARCHIVEBASE, dev_nam)
        if not os.path.exists(arch):
            print "Creating archive dir <%s>" % arch
            os.mkdir(arch)
        else:
            print "Device <%s> archive dir <%s> exists" % (dev_id,arch)
 
        # get the file list for this device        
        log_into_idigi(USERNAME, PASSWORD, IDIGI_URL)
        files = get_file_list(IDIGI_URL, dev_id)
        # print files                
 
        for fil_nam, fil_tim in files:
            save_file = os.path.join(arch, dev_nam + '_' + fil_tim + '.xml')
 
            if os.path.exists(save_file):
                print "Archive file <%s> already exists, skip download" % arch
 
            else:
                data = get_one_file(IDIGI_URL, dev_id, fil_nam)
                print data
                try:
                    print 'Saving file %s ...' % save_file,
                    f = open(save_file,'wb')
                    f.write(data)
                    f.close
                    print 'done.'
 
                except URLError, e:
                    print e.code
                    print e.read()
                    f.close()
 
if __name__ == '__main__':
    main()
Personal tools
Wiki Editing