Package dics :: Module tc08
[hide private]
[frames] | no frames]

Source Code for Module dics.tc08

  1  #!/usr/bin/env python 
  2   
  3  import datetime 
  4  import logging 
  5  import os 
  6  import re 
  7  import shm 
  8  import signal 
  9  import struct 
 10  import time 
 11  import xmlrpclib 
 12   
13 -class tc08:
14 """ 15 Class providing an python interface to a Pico Technology Ltd. 16 TC-08 temperature logger using the tc08lnx Linux driver 17 from Pico Technology. 18 19 Requires tc08lnx to be installed in /usr/local/bin. Also requires the 20 non-standard python module shm (System V shared memory). 21 """ 22 # Format of the C struct used in the shared memory. 23 __format = "ii8ll" 24
25 - def __init__(self, channels, channel_names, portname, \ 26 units='C', interval=60, name='tc08', id=42, master=True):
27 """ 28 Arguments: 29 30 name: A string with which to label this instance. Used 31 for logging purposes 32 33 channels: Type of thermocouple connected to each channel, 34 encoded as a string of eight characters, one per 35 channel. Available types are B, E, J, K, R, 36 S, T and X (microvolts). For unused channels enter 37 '-'. 38 39 channel_names: String containing a comma seperated list of 40 labels for the channels. 41 42 portname: String containing the device name of the serial port 43 that the TC-08 is connected to, e.g. '/dev/ttyS0' 44 45 units: Specifies the units, 'C' for celsius, 'K' for Kelvin. 46 47 interval: Interval, in seconds, at which entries are added to the log 48 49 name: String to indentify the particlar instance for logging 50 purposes. 51 52 id: Unique ID number used to generate the shared memory key. 53 54 master: True is this instance is the 'master', and should start 55 the tc08lnx driver. False if this is a slave instance, 56 which should just connect to a pre-existing shared memory 57 area and log file. 58 """ 59 # Begin logging 60 self.logger = logging.getLogger(name) 61 # Store the channel config string 62 self.__channels = channels 63 # Store the list of channel names 64 self.__channel_names = channel_names.split(',') 65 # Also create a dictionary for reverse lookups of channel numbers 66 self.__channel_dict = dict(zip(self.channel_names, \ 67 range(1, len(self.channel_names)+1))) 68 # Determine what units to use. 69 if units == 'C' or 'c': 70 self.__units = 'C' 71 elif units == 'K' or 'k': 72 self.__units = 'K' 73 else: 74 self.logger.warning("Invalid units '%s' given! Using Celsius." \ 75 % units) 76 self.__units = 'C' 77 # Generate a key for the shared memory 78 self.__id = int(id) 79 key = shm.ftok("/usr/local/bin/tc08lnx", self.__id) 80 # Construct a unique file name for the temperature log based on the 81 # date and the name of this tc08 instance. 82 date = datetime.date.fromtimestamp(time.time()) 83 filename = '%s_%s.log' % (date.strftime('%Y%m%d'), name) 84 if os.path.exists(filename): 85 i = 1 86 while os.path.exists(filename + '-%i' % i): 87 i += 1 88 filename += '-%i' % i 89 # Add the relevant config info to the log file as initial comment line 90 try: 91 logfile = file(filename, 'w') 92 logfile.write("# %s %s %s\n" % (name, channels, channel_names)) 93 logfile.close 94 except IOError: 95 self.logger.critical("Couldn't access log file %s for writing!" % \ 96 filename) 97 98 # Start the driver running 99 self.logger.info('Connecting to TC-08 on port %s...' % portname) 100 self.__pid = os.spawnlp(os.P_NOWAIT, "/usr/local/bin/tc08lnx", \ 101 "tc08lnx", \ 102 "-u", "%s" % units, \ 103 "-t", channels, \ 104 "-k", "%i" % key, \ 105 "-o", "%s" % filename, \ 106 "-s", "%i" % int(interval), \ 107 portname) 108 self.__filename = filename 109 # Check the state of the tc08lnx process. Should be either 110 # running or sleeping. If the tc08 isn't connected to the port 111 # specified, or some other problem, it'll be a zombie by now. 112 ps_fd = os.popen('ps -p %i -o state=' % self.__pid) 113 state = ps_fd.read(1) 114 ps_fd.close() 115 if state == 'Z' or state == 'X' or state == '': 116 # tc08lnx is a zombie, or dead, or non-existant. 117 # Noisily log this terrible event 118 self.logger.critical('Driver failure for TC-08 on port %s!' \ 119 % portname) 120 121 # Get the id of the shared memory area 122 # This sometimes fails for mysterious reasons. 123 try: 124 id = shm.getshmid(key) 125 except KeyError: 126 # Noisily log this disaster. 127 self.logger.critical('Shm key failure for TC-08 on port %s!'\ 128 % portname) 129 130 # Create a shared memory object 131 try: 132 self.__memory = shm.memory(id) 133 except shm.error: 134 # Noisily log this disaster. 135 self.logger.critical('Shm error for TC-08 on port %s!'\ 136 % portname) 137 138 # Store the size of the shared memory object 139 self.__size = self.__memory.size 140 # Attach the mem to address space of this process 141 self.__memory.attach()
142
143 - def __del__(self):
144 """ 145 Ensure the tc08lnx driver gets killed. 146 """ 147 self.logger.debug('__del__() called') 148 os.kill(self.__pid, signal.SIGINT)
149
150 - def getChannels(self):
151 """ 152 Returns the channel config string. 153 """ 154 self.logger.debug('getChannels() called.') 155 return self.__channels
156
157 - def getColdJunction(self):
158 """ 159 Returns the current cold junction temperature. 160 """ 161 self.logger.debug('getColdJunction() called.') 162 cjt = struct.unpack(tc08.__format, \ 163 self.__memory.read(self.__size))[10]\ 164 /100.0 165 if self.__units == 'K' or self.__units == 'k': 166 cjt += 273.15 167 return cjt
168 169
170 - def getID(self):
171 """ 172 Returns the unique ID used to identify the shared memory block. 173 """ 174 self.logger.debug('getID() called') 175 return self.__id
176 177
178 - def getName(self, number):
179 """ 180 Returns the channel label for a given channel number 181 """ 182 self.logger.debug('getName(%i) called.' % number) 183 return self.__channel_names[number-1]
184
185 - def getNames(self):
186 """ 187 Returns the list of channel labels 188 """ 189 self.logger.debug('getNames() called.') 190 return self.__channel_names
191
192 - def getNumber(self, name):
193 """ 194 Returns the channel number corresponding to a given label 195 """ 196 self.logger.debug('getNumber("%s") called.' % name) 197 return self.channel_dict[name]
198
199 - def getTemps(self):
200 """ 201 Returns the current temperatures as an list. 202 """ 203 self.logger.debug('getTemps() called.') 204 num_temps = len(self.channel_names) 205 return [t/100.0 for t in \ 206 struct.unpack(tc08.__format, \ 207 self.__memory.read(self.__size))[2:2+num_temps]]
208
209 - def getUnits(self):
210 """ 211 Returns the temperature units in use, either 'C' for celsius or 212 'K' for kelvin. 213 """
214
215 - def isActive(self):
216 self.logger.debug('isActive() called') 217 if struct.unpack(tc08.__format, self.__memory.read(self.__size))[1]: 218 return True 219 else: 220 return False
221
222 - def isInitialised(self):
223 self.logger.debug('isInitialised() called') 224 if struct.unpack(tc08.__format, self.__memory.read(self.__size))[0]: 225 return True 226 else: 227 return False
228