[Tutor] newbie bragging about code

Bob Gailer bgailer at alum.rpi.edu
Thu Nov 13 15:42:47 EST 2003


At 05:05 PM 11/12/2003, Eur van Andel wrote:

>Hi
>
>I sent the prog hereunder to my father with comments. Maybe these comments 
>will
>help others or draw better comments.

I notice you are not using classes, and that's OK. I will show one way to 
use classes in the hopes of giving you a "leg up" on OOP.

One indication of a need for classes is when you are doing the same thing 
twice (creating files in this case). Consider:

class File:
   def __init__(self, kind):
     self.fileName = strftime("%d-%m-%Y_%H-%M." + kind, localtime())
     self.file = file(self.fileName, 'w')
   def write(self.txt):
     self.file.write(txt)

# everything that's common to the 2 file types is encapsulated in one class 
definition. Then you proceed to create 2 instances of File, each dedicated 
to a unique purpose:
...
logfile = File('log')
logfile.write('temperatures and pump duty cycles\n')
logfile.write('from six fans and pump controller\n')
if raw_output:
    rawfile = File('raw')

I factored out the initial writing to the log file to keep the class 
definition simple.
There are numerous other options here, including creating subclasses for 
the 2 types, and using __call__ instead of write (in which case you'd write 
to the file thus: logfile('temperatures and pump duty cycles\n')

>The program sends data to and from a string of PIC microcontrollers that
>measure 4 temperatures of a water to air heat exchanger. These messages leave
>the PC as hex strings like ;0123FAFAFAFACC where ; is the delimiter, 01 the
>address, 23 the command, FA the four data bytes and CC the checksum. They get
>cnverted by a PIC converter board to pulse duration code msgs on a two signal
>wire bus with hardware node costs of $2 (two RJ 6/6 amp connectors and 
>74HC14).
>The bus carries power too. I need to connect several 1000's of these later.
>
>The PIC controllers (16F819) have a bootloader that allows reprogramming 
>of the
>application program in flash memory.  The program hereunder deals just with
>receiving and sending temperatures.
>
>
>Output first:
>
> >/home/eur # cd xwisp/
> >/home/eur/xwisp # python almeria4.py
> >Run
>all PICs run (from bootloader to app)
>
> >1 0x4E4D4E4E 2 0x4B464A49 3 0x4C4D4F4E 4 0x4C4A4B4C
> >5 0x53534F53 6 0x4B4D544F 7 0x53515152 8 0x4F4E4E54
>raw-output, nice debugging. Only data in recvd msgs shown
>
> >
> >1 15.6 15.4 15.6 15.6 2 15.0 14.0 14.8 14.6 3 15.2 15.4 15.8 15.6
> >4 15.2 14.8 15.0 15.2 5 16.6 16.6 15.8 16.6 6 15.0 15.4 16.8 15.8
> >7 16.6 16.2 16.2 16.4 8 15.8 15.6 15.6 16.8 15.6 15.4 15.7 15.7
>8 PICs with board number and 4 temps each. last four are average temps 
>that get
>send to first PIC (which has no sensors, it controls the water flow)
>
> >
> >
> >1 0x4E4D4E4E 2 0x4B464A49 3 0x4C4D4F4E 4 0x4C4A4B4C
> >5 0x52535053 6 0x4B4D544F 7 0x53515152 8 0x4F4E4E54
> >
> >1 15.6 15.4 15.6 15.6 2 15.0 14.0 14.8 14.6 3 15.2 15.4 15.8 15.6
> >4 15.2 14.8 15.0 15.2 5 16.4 16.6 16.0 16.6 6 15.0 15.4 16.8 15.8
> >7 16.6 16.2 16.2 16.4 8 15.8 15.6 15.6 16.8 15.6 15.4 15.8 15.7
>Quite cold in here. Netherlands in winter, Pythoneer to cheap to turn on
>heating after office hours.
> >
> >
> >1 0x4D4D4E4E 2 0x4B464A4A 3 0x4C4D4F4E 4 0x4C4A4B4C
> >5 0x52534E53 6 0x4C4D544F 7 0x53515151 8 0x4E4D4D54
> >
> >1 15.4 15.4 15.6 15.6 2 15.0 14.0 14.8 14.8 3 15.2 15.4 15.8 15.6
> >4 15.2 14.8 15.0 15.2 5 16.4 16.6 15.6 16.6 6 15.2 15.4 16.8 15.8
> >7 16.6 16.2 16.2 16.2 8 15.6 15.4 15.4 16.8 15.6 15.4 15.7 15.7
> >
>
>
>Code follows, inline bragging after that:
>
>
>
>####################################################################
>#
>#       program almeria3.py
>#       makes logfile
>#       polls boards for temperatures
>#       writes temperatures to screen
>#       records temperatures in logfile
>#       makes new logfile every midnight
>#     can output raw packets
>#
>#       to do: config file, separate data routine for msgs
>#       fault-tolerant: no output (or the lack of output)
>#       may NOT crash the program
>#       re-boot boards if necessary
>#     send average temps to pump board
>#
>###################################################################
>
>from time import strftime, localtime, sleep
>import random, fpformat
>import sys
>from xwisp_fwx3 import fwx
>import string
>
>def make_logfile():
>    logfile = open(strftime("%d-%m-%Y_%H-%M.log", localtime()),'w')
>    #logfile = open(strftime("%d_%H%M.log", localtime()),'w') # MSDOS
>    logfile.write('temperatures and pump duty cycles\n')
>    logfile.write('from six fans and pump controller\n')
>    return(logfile)
>
>def make_rawfile():
>    rawfile = open(strftime("%d-%m-%Y_%H-%M.raw", localtime()),'w')
>    #logfile = open(strftime("%d_%H%M.log", localtime()),'w') # MSDOS
>    logfile.write('raw msgs from fwx boards\n')
>    return(rawfile)
>
>def data_send_X(B3,B2,B1, B0):        # makes integer that is a 8-char in hex
>     data = '%02X' % B3 + '%02X' % B2 + '%02X' % B1 + '%02X' % B0
>     data = long(data, 16)
>     #print '%08X' % data
>     return data
>
>
>def send_temps_to_pumpcontr(Tai, Tao, Twl, Twh):
>    Response = f.Send_Receive(75, data_send_X(Tai*5, Tao*5, Twl*5, Twh*5), 
> 1) #
>pump_controller has address 1
>    #print Response
>
>############################ START OF PROGRAM #######################
>
>
>number_of_boards = 8            # should be in config file
>raw_output = True
>
>T = [ [None] * 5 for i in range(1,number_of_boards+2) ] # array of
>[0..num][0..4]
>
>f = fwx( '/dev/cuaa0' )
>Errors = 0
>f.CMD_RUN()
>
>date = localtime()[2]
>
>logfile = make_logfile()
>if raw_output:
>    rawfile = make_rawfile()
>
>
># $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ START OF MAIN LOOP $$$$$$$$$$$$$$
>
>while True:
>    minutes, seconds =  localtime()[4], localtime()[5]
>
>    if date != localtime()[2]:   # a new day has dawned, open a new logfile
>       logfile = make_logfile()
>       date = localtime()[2]
>
>    if minutes % 1 == 0:
>       if seconds % 10 == 0:
>          if raw_output:
>               rawfile.write(strftime('"%d-%m-%Y %H:%M:%S" ', localtime()))
>
>          for n in range(1, number_of_boards + 1):
>             Response = f.Send_Receive(33, Address = n)
>             if Response == None:
>                if raw_output:
>                   print '%1d None      ' % n,
>                   rawfile.write('%1d None ')
>                   if n % 4 == 0:   # after 4 raw packets
>                      print '\n',
>                Response = 825307441L
>             else:
>                if raw_output:
>                   print '%1d 0x%08X' % (n,Response),
>                   rawfile.write('%1d 0x%08X ' % (n, Response))
>                   if n % 4 == 0:   # after 4 raw packets
>                      print '\n',
>
>             D = '%08X' % Response
>             T[n][1] = int(D[0:2],16)/5.0        # T air in
>             T[n][2] = int(D[2:4],16)/5.0        # T air out
>             T[n][3] = int(D[4:6],16)/5.0        # T water high
>             T[n][4] = int(D[6:8],16)/5.0        # T water low
>             # end of looping over boards
>
>          if raw_output:
>             print '\n',
>             rawfile.write('\n')
>
>          logfile.write(strftime('"%d-%m-%Y %H:%M:%S" ', localtime()))
>          Tai = Tao = Twl = Twh = 0
>          print Tai, Tao, Twl, Twh
>          for n in range(1, number_of_boards+1):
>             print '%1d' % n,
>             logfile.write('%1d' % n)
>             if n > 1 and n < number_of_boards:  # 1 = pump, last= control 
> group
>                Tai = Tai + T[n][1]
>                Tao = Tao + T[n][2]
>                Twl = Twl + T[n][3]
>                Twh = Twh + T[n][4]
>
>             for i in range(1,5):
>                print '%4.1f' % T[n][i],
>                logfile.write('%4.1f' % T[n][i])
>             if n % 3 == 0:  # after three temperature quads
>                 print '\n',
>
>          Tai = Tai / (number_of_boards - 2) # 1 pump board, 1 control group
>board
>          Tao = Tao / (number_of_boards - 2)
>          Twl = Twl / (number_of_boards - 2)
>          Twh = Twh / (number_of_boards - 2)
>
>          print '%4.1f %4.1f %4.1f %4.1f' % (Tai, Tao, Twl, Twh)
>
>          print '\n'
>
>          logfile.write('\n')
>          logfile.flush()
>
>          send_temps_to_pumpcontr(Tai, Tao, Twl, Twh)
>
>          sleep(2)
>#   if seconds < 55:
>#      sleep(55 - seconds)
>
>
>
>
>
>
>
> >####################################################################
> >#
> >#      program almeria3.py
> >#      makes logfile
> >#      polls boards for temperatures
> >#      writes temperatures to screen
> >#      records temperatures in logfile
> >#      makes new logfile every midnight
> >#     can output raw packets
> >#
> >#      to do: config file, separate data routine for msgs
>needs work
>
> >#      fault-tolerant: no output (or the lack of output)
> >#      may NOT crash the program
>works.
>
> >#      re-boot boards if necessary
>needs work
>
> >#     send average temps to pump board
>works
>
> >#
> >###################################################################
> >
> >from time import strftime, localtime, sleep
> >import random, fpformat
> >import sys
>several libs, but I'll have to check if still needed
>
> >from xwisp_fwx3 import fwx
>wouter's msg routines
>
> >import string
> >
> >def make_logfile():
>def: define function
>(): no arguments
>
> >   logfile = open(strftime("%d-%m-%Y_%H-%M.log", localtime()),'w')
>12-11-2003_23-59.log, open for writing, the 'w'
>
>
> >   #logfile = open(strftime("%d_%H%M.log", localtime()),'w') # MSDOS
> >   logfile.write('temperatures and pump duty cycles\n')
> >   logfile.write('from six fans and pump controller\n')
> >   return(logfile)
>so function returns logfile object. Call function with
>
>logfile = make_logfile()
>
>after that, you can do things like:
>
>logfile.close()
>logfile.flush()
>
>
> >
> >def make_rawfile():
> >   rawfile = open(strftime("%d-%m-%Y_%H-%M.raw", localtime()),'w')
> >   #logfile = open(strftime("%d_%H%M.log", localtime()),'w') # MSDOS
>12_23-59.log, 8-char MSDOS compatible file name. This code snippet was
>developed under DOS.
>
> >   logfile.write('raw msgs from fwx boards\n')
> >   return(rawfile)
> >
> >def data_send_X(B3,B2,B1, B0):        # makes integer that is a 8-char 
> in hex
> >    data = '%02X' % B3 + '%02X' % B2 + '%02X' % B1 + '%02X' % B0
>
>'02X' % 255 means: write 255 as hexadecimal (X) string ('..'),  two chars wide
>(2) with leading zeroes (0). Yes, Python is powerful. I took 3 days to
>understand this.
>
> >    data = long(data, 16)
> >    #print '%08X' % data
>debugging :-)
>
> >    return data
>as 8-char hex string from 4 integers. If integers are 199498535 or other
>useless number >255, hex string will always be 8 char wide. Very robust.
>
> >
> >
> >def send_temps_to_pumpcontr(Tai, Tao, Twl, Twh):
> >   Response = f.Send_Receive(75, data_send_X(Tai*5, Tao*5, Twl*5, 
> Twh*5), 1) # pump_controller has address 1
> >   #print Response
>remember: internal byte representation of temps in PIC is degree celsius * 5
>
>75 is bad: needs to be replaced by cmd_set_av_temps constant.
>
> >
> >############################ START OF PROGRAM #######################
> >
> >
> >number_of_boards = 8           # should be in config file
>indeed.
>
> >raw_output = True
>idem, in config file
>
> >
> >T = [ [None] * 5 for i in range(1,number_of_boards+2) ] # array of 
> [0..num][0..4]
>clumsy array of stupid newbie that wants arrays like T[1..n][1..4] instead of
>Python way T[0..n-1][0..3]. Array should be replaced with nice set of fourfold
>tuples.
>
> >
> >f = fwx( '/dev/cuaa0' )
>FreeBSD serial port 1
>
> >Errors = 0
> >f.CMD_RUN()
>run boards
> >
> >date = localtime()[2]
> >
> >logfile = make_logfile()
> >if raw_output:
> >   rawfile = make_rawfile()
> >
> >
> ># $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ START OF MAIN LOOP $$$$$$$$$$$$$$
> >
> >while True:
> >   minutes, seconds =  localtime()[4], localtime()[5]
>powerful Python tuple (data set) value setting.
>
>a,b,c,d = 1,2,3,4
>
>is normal Python. Typically T = [a,b,c,d] should be used
>
> >
> >   if date != localtime()[2]:  # a new day has dawned, open a new logfile
> >      logfile = make_logfile()
> >      date = localtime()[2]
> >
> >   if minutes % 1 == 0:
> >      if seconds % 10 == 0:
>for fast response
>
>Note that Python has no begin...end, only indentation. One space off and the
>compilers hurls abuse at you, rightly so. Even one tab and 8 spaces on
>different lines are not accepted. ":" is "then"
>
> >         if raw_output:
> >             rawfile.write(strftime('"%d-%m-%Y %H:%M:%S" ', localtime()))
>Writing time to raw log, to match the raw packets later to the
>temperatures.
>
> >
> >         for n in range(1, number_of_boards + 1):
>stupid newbie counting from 1 to n. Python way is
>
>for n in range(number_of_boards)
>
>n will be 0...8
>
> >            Response = f.Send_Receive(33, Address = n)
>get 4 temperatures
>
> >            if Response == None:
> >               if raw_output:
> >                  print '%1d None      ' % n,
> >                  rawfile.write('%1d None ')
> >                  if n % 4 == 0:   # after 4 raw packets
> >                     print '\n',
> >               Response = 825307441L
>is 0x31313131 is 49 49 49 49 is 4 times 9.8*5
>which will thoroughly confuse pump_controller. Bad.
>
> >            else:
> >               if raw_output:
> >                  print '%1d 0x%08X' % (n,Response),
>print n as 1 digit integer, Response as 0xXXXXXXXX 8-digit hex number
>trailing comma means no newline after print
>
>print '\n'
>
>with no trailing comma means TWO newlines
>
> >                  rawfile.write('%1d 0x%08X ' % (n, Response))
>note middle and trailing space in format string: chunk.write() will not spaced
>numbers like print does
>
> >                  if n % 4 == 0:   # after 4 raw packets
> >                     print '\n',
>to the screen, not to the file
> >
> >            D = '%08X' % Response
>D now 8 (hex) char long string
>
> >            T[n][1] = int(D[0:2],16)/5.0       # T air in
>T[n][1] is integer of first two chars of string, with base number 16, divided
>by 5. 5.0 is written so Python will understand T[n][1] is a float
>
> >            T[n][2] = int(D[2:4],16)/5.0       # T air out
> >            T[n][3] = int(D[4:6],16)/5.0       # T water high
> >            T[n][4] = int(D[6:8],16)/5.0       # T water low
> >            # end of looping over boards
> >
> >         if raw_output:
> >            print '\n',
> >            rawfile.write('\n')
>Now a newline to the raw file
>
> >
> >         logfile.write(strftime('"%d-%m-%Y %H:%M:%S" ', localtime()))
> >         Tai = Tao = Twl = Twh = 0
>resetting the values for averaging
>
> >         print Tai, Tao, Twl, Twh
>debugging
>
> >         for n in range(1, number_of_boards+1):
>clueless newbie loops again over boards, sigh
>
> >            print '%1d' % n,
> >            logfile.write('%1d' % n)
>
> >            if n > 1 and n < number_of_boards:  # 1 = pump, last= 
> control group
>first board is pump_contr, last board is control_group, both should not be 
>used
>for calculating average
>
> >               Tai = Tai + T[n][1]
> >               Tao = Tao + T[n][2]
> >               Twl = Twl + T[n][3]
> >               Twh = Twh + T[n][4]
>Real Pythoneers would combine these statements:
>
>             T[n][1] = int(D[0:2],16)/5.0        # T air in
>             Tai = Tai + T[n][1]
>
>to
>
>             Tai = Tai + T[n][1] = int(D[0:2],16)/5.0    # T air in
>
>in a single loop
> >
> >            for i in range(1,5):
> >               print '%4.1f' % T[n][i],
> >              logfile.write('%4.1f' % T[n][i])
>no space, bad
>
> >            if n % 3 == 0:  # after three temperature quads
> >                print '\n',
>print board number and temperatures in line of three
> >
> >         Tai = Tai / (number_of_boards - 2) # 1 pump board, 1 control 
> group board
> >         Tao = Tao / (number_of_boards - 2)
> >         Twl = Twl / (number_of_boards - 2)
> >         Twh = Twh / (number_of_boards - 2)
>averaging
> >
> >         print '%4.1f %4.1f %4.1f %4.1f' % (Tai, Tao, Twl, Twh)
>debugging
> >
> >         print '\n'
> >
> >         logfile.write('\n')
> >         logfile.flush()
>flush data stream to logfile so no data loss when power failure
> >
> >         send_temps_to_pumpcontr(Tai, Tao, Twl, Twh)
>ha! This took me a full day to get this single statement working!
>
> >
> >         sleep(2)
>so not 6 times in the same second. Yes, Python is that fast.
>
> >#   if seconds < 55:
> >#      sleep(55 - seconds)
>To minimize system load?
>
>
>--
>Ir. E.E. van Andel, Fine Wire Heat Exchangers, Fiwihex B.V. www.fiwihex.com
>Wierdensestraat 74, NL-7604 BK  Almelo, The Netherlands   eur at fiwihex.nl
>phone +31-546-491106  fax +31-546-491107  mobile +31-653-286573
>
>_______________________________________________
>Tutor maillist  -  Tutor at python.org
>http://mail.python.org/mailman/listinfo/tutor
>
>
>
>---
>Incoming mail is certified Virus Free.
>Checked by AVG anti-virus system (http://www.grisoft.com).
>Version: 6.0.538 / Virus Database: 333 - Release Date: 11/10/2003

Bob Gailer
bgailer at alum.rpi.edu
303 442 2625
-------------- next part --------------

---
Outgoing mail is certified Virus Free.
Checked by AVG anti-virus system (http://www.grisoft.com).
Version: 6.0.538 / Virus Database: 333 - Release Date: 11/10/2003


More information about the Tutor mailing list