Transport Python programmer's guide

Purpose of this guide

This guide introduces the Python programming language by showing how to create and run a simple Python program. It describes how to load and run Python programs onto Digi Transport devices, either through the command-line or Web user interfaces. It reviews Python modules, particularly those modules with Digi-specific behavior. This guide describes how to run the executable programs and describes program files.

What Is Python?

Python is a dynamic, object-oriented language that can be used for developing a wide range of software applications, from simple programs to more complex embedded applications. It includes extensive libraries and works well with other languages. A true open-source language, Python runs on a wide range of operating systems, such as Windows, Linux/Unix, Mac OS X, OS/2, Amiga, Palm Handhelds, and Nokia mobile phones. Python has also been ported to Java and .NET virtual machines.

For more information on the Python Programming Language, go to http://www.Python.org/ and click the Documentation link.

The Transports use Python version 2.6.1.

Running Python

How to run a Python program on a Digi Transport router. Firstly check your firmware version on the router, we recommend using firmware version 5090 or later. Start by checking your router has Python in its firmware by following these simple steps:

Step 1: Using either a telnet or serial connection (default login/password = username/password) to the router issue the following commands

  pycfg 0 stderr2stdout on
  Python

Step 2: At the prompt now type the following command:

  help()

The following should then be displayed:

  Welcome to Python 2.6!  This is the online help utility.

If this is your first time using Python, you should definitely check out the tutorial on the Internet at http://docs.Python.org/tutorial/.

Step 3: Type the following to exit the Python interpreter exit() Now you can upload your Python script to the router via FTP. For testing purposes you can simply run the script by using the following command:

  Python filename.py

For the router to start the script automatically on Powerup / Reboot issue the following commands:

  cmd 0 autocmd "Python filename.py"
  config 0 save

First Python script

Lets start with a hello world program. Firstly create a text file with the following text inside:

  print "Hello World!"

Save this file with a file name of myfirst.py Now FTP the myfirst.py text file onto the router and issue the following command: Python myfirst.py

The router should produce the following output:

  OK
  Hello World!

Miscellaneous items

Your Python code can detect when it is running on a Transport product by importing the SYS module, then testing the sys.platform variable like this:

  if sys.platform == 'digiSarOS':
     print "Running on Digi Transport"

File names on the Transport are limited to 8 characters or less. There is also a 3 character limit on file type extensions. Example:

  myfile.py  - is valid (6 characters and 2 character file type extension)
  myLongFileName.py - is not valid
  myfile.superlongextension - is not valid (6 characters are ok but we have more than 3 characters on the filetype extension)

Also, please note that the Digi ESP, which is used as a code development tool, is not aware of this limitation. That means the programmer must make sure to use short filenames on their projects in the ESP.

Python examples can be generated on the Digi ESP code development tool (Digi ESP -> file -> new -> digi Python application sample project). This tool can be downloaded from the Digi website.

RCI is a remote protocol used for configuring the Transport from an application. RCI is defined on the Digi website in the RCI command reference Guide.

Device ID can be determined via the 'ati5' command.

Other example scripts

WR44 - bus demo, Python script

Below is the complete script we are using in our Bus Demo, this script collects the status of the two Digital Inputs and the current GPS co-ordinates. The script will only send data when there is a change in either the GPS co-ordinates or one of the Digital Inputs alters state. The file has been separated into individual modules for ease of explanation however you can download the complete script here bus-demo.py The required libraries import sarcli import time import socket Modules The first module converts the NMEA GPS info to lat / long co-ordinates:

def Lat_Long(raw_gps):
        gps_array = []
        gps_array = (raw_gps.split(','))

        if gps_array[1] == '':
                gps_array[1] = "4791.75429"
                gps_array[2] = "N"
        if gps_array[3] == '':
                gps_array[3] = "01208.62562"
                gps_array[4] = "E"

        lat_raw = gps_array[1]
        long_raw = gps_array[3]

        lat_dd = lat_raw[0:2]
        long_dd = long_raw[0:3]
        lat_mm = lat_raw[2:]
        long_mm = long_raw[3:]
        lat_dd = int(lat_dd)
        lat_mm = float(lat_mm)
        lat = (lat_mm / 60) + lat_dd
        lat = round(lat,3)

        long_dd = int(long_dd)
        long_mm = float(long_mm)
        long = (long_mm / 60) + long_dd
        long = round(long,3)

        if gps_array[2] == "S":
                lat = '-' + str(lat)
        else:
                lat = str(lat)

        if gps_array[4] == "W":
                long = '-' + str(long)
        else:
                long = str(long)
        return [lat,long]

This module creates a TCP Socket to the WR44 itself and collect the GPS info:

def getSocket_Data(gpsHOST, gpsPORT):

  data = 'None'

  try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((gpsHOST, gpsPORT))
        print 'Getting current GPS co-ordinates'
        data = s.recv(50)
        return data

  except:

        print 'Error - Cannot Connect to ', gpsHOST
        return data

This module create a TCP Socket to the web server and sends the Data:

def sendSocket_Data(HOST, PORT, data):

  try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((HOST, PORT))
        print 'Successfully sent new status and GPS to', HOST
        s.send (data)

  except:
        print 'Error - sending new data to', HOST

This module issues a gpio command to the Transport command line interface to collect the status of the Digital Inputs:

def Get_Cli():
        clidata = ""
        cli = sarcli.open()
        cli.write("gpio")
        while True:
                tmpdata = cli.read(-1)
                if not tmpdata:
                        break
                clidata += tmpdata
        cli.close()
        return clidata

Checks the individual status of the gpio line:

def gpio_check(str, pin):
        if (pin + " : OFF") in str:
                return "off"

        return "on"

Writes the individual status of the 3 gpio lines into the variables:

def gpio(gpio_str):

        idx = gpio_str.find("Output:")

        input  = gpio_str[:idx-1]
        output = gpio_str[idx:]

        in_status     = gpio_check(input, "in")
        io_in_status  = gpio_check(input, "inout")
        io_out_status = gpio_check(output, "inout")

        return in_status, io_in_status, io_out_status

Define constants and Arrays

gpsHOST = '127.0.0.1'     # The remote host
gpsPORT = 2000            # The same port as used by the server
latlong = []              # Lat Long array
oldlatlong = []           # the old lat and long array
iostatus = []             # io status array
panic_status = "off"      # set the initial panic status to off
ign_status = "off"        # set the initial alarm status to off
HOST = 'gromit.mobilemonkey.co.uk'    # The remote web server hostname
PORT = 9999              # This is the port the web server is listening on


oldlatlong.append(50.000000003)
oldlatlong.append(-1.990900000)

The main program loop

            # Main
print "To stop, type \"Python kill\""

while True:
        clidata = Get_Cli()
        iostatus = gpio(clidata)
        changed = False
        if iostatus[0] == "off":
                if panic_status == "off":
                        panic_status = "on"
                        changed = True
        else:
                if panic_status == "on":
                        panic_status = "off"
                        changed = True

        if iostatus [1] == "on":
                if ign_status == "off":
                        ign_status = "on"
                        changed = True
        else:
                if ign_status == "on":
                        ign_status = "off"
                        changed = True



        rawgps = getSocket_Data(gpsHOST, gpsPORT)
#       print 'new data line' , rawgps
        latlong = Lat_Long(rawgps)
        if latlong[0] != oldlatlong[0]:
#               send the gps data and rewrite oldlatlong
                changed = True
                oldlatlong[0] = latlong[0]
        if latlong[1] != oldlatlong[1]:
#               send the gps data and rewrite oldlatlong
                changed = True
                oldlatlong[1] = latlong[1]
        if changed == True:
                status = panic_status + ',' + ign_status
                data = status + ','+ str(latlong[0]) + ',' + str(latlong[1])
                print data
                sendSocket_Data(HOST, PORT, data)
        time.sleep(1)

Timed event, Python script

This script waits until a specific time of the day and then completes a task. The script has been separated into sections for ease of explanation however you can download the complete script here time-evn.py

import sarcli
import time

#Modules
#The module issues commands to the command line:

def cli(command):
           cli =  sarcli.open()
           cli.write(command)
           cli.close()


#Define constants and Arrays

event_time = "13:22"
running = True


#The main program loop

while running:

        # gets the current time ie "13:01"
        current_time = time.strftime ("%H:%M", time.localtime())

        if current_time == event_time:

                #  It is time to do something..

                print " I'm Doing something "
                command = 'setevent "time_evn.py: Its time to do something"'
                cli(command)

                time.sleep(61) # Sleep for 61 seconds so that we do not do the event again.

        else:
                print current_time
                time.sleep(50) # Its not time go to sleep for 50 seconds.

Below is a telemetry card control Python script

Below is the complete script for controlling the telemetry card, this script waits for an SMS message which contains either a "Camera on" or "Camera off". After receiving the command it will process it changing the state of the relay on the Telemetry board and the reply back to the sender with a "camera now on /off" message. Download the complete script here

Note This script is offered as an example and its reliability can not be guaranteed, the router must have Firmware version 5100 or later to run. smsctrl.py

            #The required libraries

import threading
import sarcli
import os
import sys
import time

'''
Threads
The first thread runs continuously and checks the eventlog for 'SMS Received:' Messages:
'''

class eventlog (threading.Thread):
    def run (self):

        # Constants
        running = True
          # This is what we are looking for in the eventlog.txt
        string_match = "SMS Received:"
        filename = "eventlog.txt"

        while running:
          # Try to open the eventlog.txt file
          try:
            file = open(filename, 'r')

          except:
           # Output to the eventlog if there is a problem opening the eventlog.txt
           cli =  sarcli.open()
           cli.write('setevent "smsctrl:error opening file"')
           cli.close()

          # Check the eventlog.txt file for the string_match value.
          for line in file:
                if string_match in line:
                  line = line.strip('\r\n')
                  command_str = "basic 0 nv " + '"' + line + '"'
                  cli = sarcli.open()
                  cli.write(command_str)
                  cli.close()
                  break

        file.close()
        time.sleep(10)

# This is thread that turns on the Relay waits 30secs then turns it off:

class DelayOnOff (threading.Thread):
    def run (self):

        answer = SetRelay("on")
        ReplySms(answer, cmd_array[1])
        # Wait 30 seconds
        time.sleep(30)
        answer = SetRelay("off")
        ReplySms(answer, cmd_array[1])

#This module takes the eventlog item and separates the command and phone number:

def GetEvent(output_string):
  event_array = []
  command_array = []

  event_array = (output_string.split(','))
  command= str(event_array[2])
  command_array=(command.split(':'))
  command_array[1] = str(command_array[1]).lstrip() 
  command_array[2] = str(command_array[2]).lstrip()
  return command_array

#This module alters the State of the Relay on the Telemetry board and returns the relays status:

def SetRelay(state):
  clir = sarcli.open()
  if state == "on":
        try:
           clir.write("anaconda -y 1")
           clir.write('setevent "Smsctrl:Camera now on"')
           answer= "on"
        except:
           clir.write('setevent "smsctrl:error setting relay"')
           answer= "error"

  elif state == "off":
        try:
           clir.write("anaconda -y 0")
           clir.write('setevent "Smsctrl:Camera now off"')
           answer= "off"
        except:
           clir.write('setevent "smsctrl:error setting relay"')
           answer= "error"
  clir.close()
  return answer


#This module sends back an SMS reply to the originators phone number with the status:

def ReplySms(answer, phonenum):
  clir = sarcli.open()
  if answer == "on":
            sms = 'sendsms ' + phonenum + ' "Camera now on"'

  elif answer == "off":
            sms = 'sendsms ' + phonenum + ' "Camera now off"'
  else:
            sms = 'sendsms ' + phonenum + ' " Error setting relay"'

  try:
    clir.write(sms)
  except:
    clir.write('setevent "smsctrl:error sending sms"')
    clir.close()


#Define constants and Arrays

# Constants
running = True

#Variables
completed_command = "null"
cmd_array = []

#The main program loop


# Main
print "To stop, type \"Python kill\""

# Start eventlog Thread
eventlog().start()

while running:

        cli = sarcli.open()
        try:
           cli.write("basic 0 nv")
           tmpdata = cli.read(-1)
        except:
          print "error writing command: ", tmpdata
          errormsg = 'setevent ' + '"' + tmpdata + '"'
          cli.write("errormsg")
        cli.close()
        if completed_command != tmpdata:
                print "new event: ", tmpdata
                cmd_array = GetEvent(tmpdata)
                if cmd_array[2] == "Camera on":
                        answer = SetRelay("on")
                        ReplySms(answer, cmd_array[1])
                        completed_command = tmpdata
                elif cmd_array[2] == "Camera off":
                        answer = SetRelay("off")
                        ReplySms(answer, cmd_array[1])
                        completed_command = tmpdata
                elif cmd_array[2] == "Camera on 30":
                        DelayOnOff().start()
                        completed_command = tmpdata
                else:
                        time.sleep(5)
        else:
                time.sleep(5)

Wake on Lan, Python script

This script sends out a Wake on Lan packet to a specific host. The script has been separated into sections for ease of explanation however you can download the complete script here wol.py.

            #The required libraries

import socket
import struct

#Modules
#The module sends out Wake On LAN packets

def wake_on_lan(macaddress):

    """ Switches on remote computers using WOL. """

    # Check macaddress format and try to compensate.

    if len(macaddress) == 12:
        pass

    elif len(macaddress) == 12 + 5:

        sep = macaddress[2]
        macaddress = macaddress.replace(sep, '')

   else:
        raise ValueError('Incorrect MAC address format')

    # Pad the synchronization stream.
    data = ''.join(['FFFFFFFFFFFF', macaddress * 20])
    send_data = ''

    # Split up the hex values and pack.

    for i in range(0, len(data), 2):

        send_data = ''.join([send_data,

                             struct.pack('B', int(data[i: i + 2], 16))])

    # Broadcast it to the LAN.

    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    # sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    sock.sendto(send_data, ('192.168.1.255', 9))

#The main program loop

if __name__ == '__main__':

    # Use macaddresses with any seperators.
    wake_on_lan('00:40:63:FC:0C:75')
    wake_on_lan('00-40-63-FC-0C-75')

    # or without any seperators.
    wake_on_lan('004063FC0C75')

Supported Python modules

    A
        abc
        aifc
        anydbm
        array
        ast
        asynchat
        asyncore
        atexit
        audiodev
    B
        BaseHTTPServer
        Bastion
        base64
        bdb
        binascii
        binhex
        bisect
    C
        CGIHTTPServer
        ConfigParser
        Cookie
        cPickle
        cProfile
        cStringIO
        calendar
        cgi
        cgitb
        chunk
        cmath
        cmd
        code
        codecs
        codeop
        collections
        colorsys
        commands
        compileall
        contextlib
        cookielib
        copy
        copy_reg
        csv
    D
        DocXMLRPCServer
        datetime
        dbhash
        decimal
        difflib
        digihw
        digiwdog
        digiweb
        dircache
        dis
        doctest
        dumbdbm
        dummy_thread
        dummy_threading
    E
        email
        encodings
        errno
        exceptions
    F
        filecmp
        fileinput
        fnmatch
        formatter
        fpformat
        fractions
        ftplib
        functools
        future_builtins
    G
        gc
        genericpath
        getopt
        getpass
        gettext
        glob
        gzip
    H
        HTMLParser
        hashlib
        heapq
        hmac
        htmlentitydefs
        htmllib
        httplib
    I
        ihooks
        imaplib
        imghdr
        imp
        imputil
        inspect
        io
        itertools
    K
        keyword
    L
        linecache
        locale
        logging
    M
        MimeWriter
        macpath
        macurl2path
        mailbox
        mailcap
        markupbase
        marshal
        math
        md5
        mhlib
        mime
        mimetools
        mimetypes
        mimify
        modulefinder
        multifile
        mutex
    N
        netrc
        new
        nntplib
        ntpath
        nturl2path
        numbers
    O
        opcode
        operator
        optparse
        os
        os2emxpath
    P
        parser
        pdb
        pickle
        pickletools
        pipes
        pkgutil
        platform
        plistlib
        popen2
        poplib
        posix
        posixfile
        posixpath
        pprint
        profile
        pstats
        pty
        py_compile
        pyclbr
        pydoc
        pydoc_topics
        pyexpat
    Q
        Queue
        quopri
    R
        random
        re
        repr
        rexec
        rfc822
        rlcompleter
        robotparser
        runpy
    S
        SimpleHTTPServer
        SimpleXMLRPCServer
        SocketServer
        StringIO
        sarcli
        sarutils
        sched
        select
        sets
        sgmllib
        sha
        shelve
        shlex
        shutil
        site
        smtpd
        smtplib
        sndhdr
        socket
        sre        
        sre_compile
        sre_constants
        sre_parse
        ssl
        stat
        statvfs
        string
        stringold
        stringprep
        strop
        struct
        subprocess
        sunau
        sunaudio
        symbol
        symtable
        sys
   T
        tabnanny
        tarfile
        telnetlib
        tempfile
        termios
        textwrap
        this
        thread
        threading
        time
        timeit
        toaiff
        token
        tokenize
        trace
        traceback
        tty
        types
   U
        UserDict
        UserList
        UserString
        unittest
        urllib
        urllib2
        urlparse
        user
        uu
        uuid
    W
        warnings
        wave
        weakref
        webbrowser
        whichdb
        X
        xdrlib
        xmllib
        xmlrpclib
    Z
        zipfile
        zipimport
        zlib

References

Portions of this document are reproduced from documents by Matt jameson & Jon Lyons: extranet.jrp2.com/~jpowell/gromit.mobilemonkey.co.uk/trans-Python.html