Blame view

instruments/TPG261.py 10.9 KB
91efd0ebc   mer0m   Add files via upload
1
2
3
4
5
6
7
  from abstract_instrument import abstract_instrument
  import serial
  
  #==============================================================================
  
  ALL_VAL_TYPE = ['PRE']
  ALL_CHANNELS = ['1']
9058343c5   bmarechal   some minor fixes
8
  ADDRESS = "/dev/ttyS0"
91efd0ebc   mer0m   Add files via upload
9
10
11
12
  
  #==============================================================================
  
  class TPG261(abstract_instrument):
9058343c5   bmarechal   some minor fixes
13
14
      def __init__(self, channels, vtypes, address):
          self.address = address
bb6b08041   bmarechal   Add configure method
15
16
          self.channels = channels
          self.vtypes = vtypes
91efd0ebc   mer0m   Add files via upload
17
18
19
20
21
  
      def model(self):
          return "PfeifferTPG261"
  
      def connect(self):
9058343c5   bmarechal   some minor fixes
22
23
          print('Connecting to device @%s...' %(self.address))
          self.TPG = MaxiGauge(self.address)
bb6b08041   bmarechal   Add configure method
24
25
26
27
28
          print('  --> Ok')
          print(self.model())
          self.configure()
  
      def configure(self):
b383042b0   bmarechal   -
29
          pass
91efd0ebc   mer0m   Add files via upload
30
31
  
      def getValue(self):
b383042b0   bmarechal   -
32
          self.read()
91efd0ebc   mer0m   Add files via upload
33
34
35
36
          return "%s
  "%self.ps[0].pressure
  
      def read(self):
b383042b0   bmarechal   -
37
          self.ps = self.TPG.pressures()
91efd0ebc   mer0m   Add files via upload
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
  
      def disconnect(self):
          self.TPG.disconnect()
  
      def send(self, command):
          pass
  
  
  
  
  # from Philipp Klaus, philipp.l.klaus AT web.de PfeifferVacuum.py
  
  class MaxiGauge (object):
      def __init__(self, serialPort, baud=9600, debug=False):
          self.debug=debug
          try:
              self.connection = serial.Serial(serialPort, baudrate=baud, timeout=0.2)
          except serial.serialutil.SerialException as se:
              raise MaxiGaugeError(se)
          #self.send(C['ETX']) ### We might reset the connection first, but it doesn't really matter:
  
      def checkDevice(self):
          message = "The Display Contrast is currently set to %d (out of 20).
  " % self.displayContrast()
          message += "Keys since MaxiGauge was switched on: %s (out of 1,2,3,4,5).
  " % ", ".join( map (str, self.pressedKeys()) )
          return message
  
      def pressedKeys(self):
          keys = int(self.send('TKB',1)[0])
          pressedKeys = []
          for i in [4,3,2,1,0]: # It's got 5 keys
              if keys/2**i == 1:
                  pressedKeys.append(i+1)
                  keys = keys%2**i
          pressedKeys.reverse()
          return pressedKeys
  
      def displayContrast(self,newContrast=-1):
          if newContrast == -1: return int(self.send('DCC',1)[0])
          else: return int(self.send('DCC,%d' % (newContrast,) ,1)[0])
  
      def pressures(self):
          return [self.pressure(i+1) for i in range(1)]
  
      def pressure(self, sensor):
          if sensor < 1 or sensor >6: raise MaxiGaugeError('Sensor can only be between 1 and 6. You choose ' + str(sensor))
          reading = self.send('PR%d' % sensor, 1)  ## reading will have the form x,x.xxxEsx <CR><LF> (see p.88)
          try:
              r = reading[0].split(',')
              status = int(r[0])
              pressure = float(r[-1])
          except:
              raise MaxiGaugeError("Problem interpreting the returned line:
  %s" % reading)
          return PressureReading(sensor, status, pressure)
  
      def debugMessage(self, message):
          if self.debug: print(repr(message))
  
      def send(self, mnemonic, numEnquiries = 0):
          self.connection.flushInput()
          self.write(mnemonic+LINE_TERMINATION)
          #if mnemonic != C['ETX']: self.read()
          #self.read()
          self.getACQorNAK()
          response = []
          for i in range(numEnquiries):
              self.enquire()
              response.append(self.read())
          return response
  
      def write(self,what):
          self.debugMessage(what)
          self.connection.write(what)
  
      def enquire(self):
          self.write(C['ENQ'])
  
      def read(self):
          data = ""
          while True:
              x = self.connection.read()
              self.debugMessage(x)
              data += x
              if len(data)>1 and data[-2:]==LINE_TERMINATION:
                  break
          return data[:-len(LINE_TERMINATION)]
  
      def getACQorNAK(self):
          returncode = self.connection.readline()
          self.debugMessage(returncode)
          ## The following is usually expected but our MaxiGauge controller sometimes forgets this parameter... That seems to be a bug with the DCC command.
          #if len(returncode)<3: raise MaxiGaugeError('Only received a line termination from MaxiGauge. Was expecting ACQ or NAK.')
          if len(returncode)<3: self.debugMessage('Only received a line termination from MaxiGauge. Was expecting ACQ or NAK.')
          if len(returncode)>2 and returncode[-3] == C['NAK']:
              self.enquire()
              returnedError = self.read()
              error = str(returnedError).split(',' , 1)
              print repr(error)
              errmsg = { 'System Error': ERR_CODES[0][int(error[0])] , 'Gauge Error': ERR_CODES[1][int(error[1])] }
              raise MaxiGaugeNAK(errmsg)
          #if len(returncode)>2 and returncode[-3] != C['ACQ']: raise MaxiGaugeError('Expecting ACQ or NAK from MaxiGauge but neither were sent.')
          if len(returncode)>2 and returncode[-3] != C['ACQ']: self.debugMessage('Expecting ACQ or NAK from MaxiGauge but neither were sent.')
          # if no exception raised so far, the interface is just fine:
          return returncode[:-(len(LINE_TERMINATION)+1)]
  
      def disconnect(self):
          #self.send(C['ETX'])
          if hasattr(self, 'connection') and self.connection: self.connection.close()
  
      def __del__(self):
          self.disconnect()
  
  class PressureReading(object):
      def __init__(self, id, status, pressure):
          if int(id) not in range(1,7): raise MaxiGaugeError('Pressure Gauge ID must be between 1-6')
          self.id = int(id)
          if int(status) not in PRESSURE_READING_STATUS.keys(): raise MaxiGaugeError('The Pressure Status must be in the range %s' % PRESSURE_READING_STATUS.keys())
          self.status = int(status)
          self.pressure = float(pressure)
  
      def statusMsg(self):
          return PRESSURE_READING_STATUS[self.status]
  
      def __repr__(self):
          return "Gauge #%d: Status %d (%s), Pressure: %f mbar
  " % (self.id, self.status, self.statusMsg(), self.pressure)
  
  
  ### ------ now we define the exceptions that could occur ------
  
  class MaxiGaugeError(Exception):
      pass
  
  class MaxiGaugeNAK(MaxiGaugeError):
      pass
  
  ### ------- Control Symbols as defined on p. 81 of the english
  ###        manual for the Pfeiffer Vacuum TPG256A  -----------
  C = {
    'ETX': "\x03", # End of Text (Ctrl-C)   Reset the interface
    'CR':  "\x0D", # Carriage Return        Go to the beginning of line
    'LF':  "\x0A", # Line Feed              Advance by one line
    'ENQ': "\x05", # Enquiry                Request for data transmission
    'ACQ': "\x06", # Acknowledge            Positive report signal
    'NAK': "\x15", # Negative Acknowledge   Negative report signal
    'ESC': "\x1b", # Escape
  }
  
  LINE_TERMINATION=C['CR']+C['LF'] # CR, LF and CRLF are all possible (p.82)
  
  ### Mnemonics as defined on p. 85
  M = [
    'BAU', # Baud rate                           Baud rate                                    95
    'CAx', # Calibration factor Sensor x         Calibration factor sensor x (1 ... 6)        92
    'CID', # Measurement point names             Measurement point names                      88
    'DCB', # Display control Bargraph            Bargraph                                     89
    'DCC', # Display control Contrast            Display control contrast                     90
    'DCD', # Display control Digits              Display digits                               88
    'DCS', # Display control Screensave          Display control screensave                   90
    'DGS', # Degas                               Degas                                        93
    'ERR', # Error Status                        Error status                                 97
    'FIL', # Filter time constant                Filter time constant                         92
    'FSR', # Full scale range of linear sensors  Full scale range of linear sensors           93
    'LOC', # Parameter setup lock                Parameter setup lock                         91
    'NAD', # Node (device) address for RS485     Node (device) address for RS485              96
    'OFC', # Offset correction                   Offset correction                            93
    'OFC', # Offset correction                   Offset correction                            93
    'PNR', # Program number                      Program number                               98
    'PRx', # Status, Pressure sensor x (1 ... 6) Status, Pressure sensor x (1 ... 6)          88
    'PUC', # Underrange Ctrl                     Underrange control                           91
    'RSX', # Interface                           Interface                                    94
    'SAV', # Save default                        Save default                                 94
    'SCx', # Sensor control                      Sensor control                               87
    'SEN', # Sensor on/off                       Sensor on/off                                86
    'SPx', # Set Point Control Source for Relay xThreshold value setting, Allocation          90
    'SPS', # Set Point Status A,B,C,D,E,F        Set point status                             91
    'TAI', # Test program A/D Identify           Test A/D converter identification inputs    100
    'TAS', # Test program A/D Sensor             Test A/D converter measurement value inputs 100
    'TDI', # Display test                        Display test                                 98
    'TEE', # EEPROM test                         EEPROM test                                 100
    'TEP', # EPROM test                          EPROM test                                   99
    'TID', # Sensor identification               Sensor identification                       101
    'TKB', # Keyboard test                       Keyboard test                                99
    'TRA', # RAM test                            RAM test                                     99
    'UNI', # Unit of measurement (Display)       Unit of measurement (pressure)               89
    'WDT', # Watchdog and System Error Control   Watchdog and system error control           101
  ]
  
  
  ### Error codes as defined on p. 97
  ERR_CODES = [
    {
          0: 'No error',
          1: 'Watchdog has responded',
          2: 'Task fail error',
          4: 'IDCX idle error',
          8: 'Stack overflow error',
         16: 'EPROM error',
         32: 'RAM error',
         64: 'EEPROM error',
        128: 'Key error',
       4096: 'Syntax error',
       8192: 'Inadmissible parameter',
      16384: 'No hardware',
      32768: 'Fatal error'
    } ,
    {
          0: 'No error',
          1: 'Sensor 1: Measurement error',
          2: 'Sensor 2: Measurement error',
          4: 'Sensor 3: Measurement error',
          8: 'Sensor 4: Measurement error',
         16: 'Sensor 5: Measurement error',
         32: 'Sensor 6: Measurement error',
        512: 'Sensor 1: Identification error',
       1024: 'Sensor 2: Identification error',
       2048: 'Sensor 3: Identification error',
       4096: 'Sensor 4: Identification error',
       8192: 'Sensor 5: Identification error',
      16384: 'Sensor 6: Identification error',
    }
  ]
  
  ### pressure status as defined on p.88
  PRESSURE_READING_STATUS = {
    0: 'Measurement data okay',
    1: 'Underrange',
    2: 'Overrange',
    3: 'Sensor error',
    4: 'Sensor off',
    5: 'No sensor',
    6: 'Identification error'
  }