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
PDF
