Coverage for larch/epics/xspress3.py: 23%

224 statements  

« prev     ^ index     » next       coverage.py v7.6.0, created at 2024-10-16 21:04 +0000

1#!/usr/bin/python 

2import sys 

3import os 

4import time 

5from configparser import ConfigParser 

6 

7from epics import Device, caget, caput, poll 

8from epics.devices.mca import MCA, ROI 

9from .ad_mca import ADMCA, ADMCAROI 

10 

11MAX_ROIS = 48 

12 

13class ADFileMixin(object): 

14 """mixin class for Xspress3""" 

15 def filePut(self, attr, value, **kws): 

16 return self.put("%s%s" % (self.filesaver, attr), value, **kws) 

17 

18 def fileGet(self, attr, **kws): 

19 return self.get("%s%s" % (self.filesaver, attr), **kws) 

20 

21 def setFilePath(self, pathname): 

22 fullpath = os.path.join(self.fileroot, pathname) 

23 return self.filePut('FilePath', fullpath) 

24 

25 def setFileTemplate(self,fmt): 

26 return self.filePut('FileTemplate', fmt) 

27 

28 def setFileWriteMode(self,mode): 

29 return self.filePut('FileWriteMode', mode) 

30 

31 def setFileName(self,fname): 

32 return self.filePut('FileName', fname) 

33 

34 def nextFileNumber(self): 

35 self.setFileNumber(1+self.fileGet('FileNumber')) 

36 

37 def setFileNumber(self, fnum=None): 

38 if fnum is None: 

39 self.filePut('AutoIncrement', 1) 

40 else: 

41 self.filePut('AutoIncrement', 0) 

42 return self.filePut('FileNumber',fnum) 

43 

44 def getLastFileName(self): 

45 return self.fileGet('FullFileName_RBV',as_string=True) 

46 

47 def FileCaptureOn(self): 

48 return self.filePut('Capture', 1) 

49 

50 def FileCaptureOff(self): 

51 return self.filePut('Capture', 0) 

52 

53 def setFileNumCapture(self,n): 

54 return self.filePut('NumCapture', n) 

55 

56 def FileWriteComplete(self): 

57 return (0==self.fileGet('WriteFile_RBV') ) 

58 

59 def getFileTemplate(self): 

60 return self.fileGet('FileTemplate_RBV',as_string=True) 

61 

62 def getFileName(self): 

63 return self.fileGet('FileName_RBV',as_string=True) 

64 

65 def getFileNumber(self): 

66 return self.fileGet('FileNumber_RBV') 

67 

68 def getFilePath(self): 

69 return self.fileGet('FilePath_RBV',as_string=True) 

70 

71 def getFileNameByIndex(self,index): 

72 return self.getFileTemplate() % (self.getFilePath(), self.getFileName(), index) 

73 

74class Xspress3BaseMixin(object): 

75 """xspress3 mixin -- triggers, acquire, etc""" 

76 def useExternalTrigger(self): 

77 self.TriggerMode = 3 

78 

79 def useInternalTrigger(self): 

80 self.TriggerMode = 1 

81 

82 def setTriggerMode(self, mode): 

83 self.TriggerMode = mode 

84 

85 def start(self, capture=True): 

86 time.sleep(.05) 

87 if capture: 

88 self.FileCaptureOn() 

89 self.Acquire = 1 

90 

91 def stop(self): 

92 self.Acquire = 0 

93 self.FileCaptureOff() 

94 

95 def get_rois(self): 

96 return [m.get_rois() for m in self.mcas] 

97 

98class Xspress3(Device, ADFileMixin, Xspress3BaseMixin): 

99 """Epics Xspress3.20 interface (with areaDetector2)""" 

100 

101 det_attrs = ('NumImages', 'NumImages_RBV', 'Acquire', 'Acquire_RBV', 

102 'ArrayCounter_RBV', 'ERASE', 'UPDATE', 'AcquireTime', 

103 'TriggerMode', 'StatusMessage_RBV', 'DetectorState_RBV') 

104 

105 _nonpvs = ('_prefix', '_pvs', '_delim', 'filesaver', 'fileroot', 

106 'pathattrs', '_nonpvs', 'nmca', 'mcas') 

107 

108 pathattrs = ('FilePath', 'FileTemplate', 'FileName', 'FileNumber', 

109 'Capture', 'NumCapture') 

110 

111 def __init__(self, prefix, nmca=4, filesaver='HDF1:', 

112 fileroot='/home/xspress3/cars5/Data'): 

113 if not prefix.endswith(':'): 

114 prefix = "%s:" % prefix 

115 self.nmca = nmca 

116 attrs = [] 

117 attrs.extend(['%s%s' % (filesaver,p) for p in self.pathattrs]) 

118 

119 self.filesaver = filesaver 

120 self.fileroot = fileroot 

121 self._prefix = prefix 

122 self.mcas = [] 

123 for i in range(nmca): 

124 imca = i+1 

125 dprefix = "%sdet1:" % prefix 

126 rprefix = "%sMCA%iROI" % (prefix, imca) 

127 data_pv = "%sMCA%i:ArrayData" % (prefix, imca) 

128 mca = ADMCA(dprefix, data_pv=data_pv, roi_prefix=rprefix) 

129 self.mcas.append(mca) 

130 

131 Device.__init__(self, prefix, attrs=attrs, delim='') 

132 for attr in self.det_attrs: 

133 self.add_pv("%sdet1:%s" % (prefix, attr), attr) 

134 for i in range(nmca): 

135 imca = i+1 

136 for j in range(10): 

137 isca = j+1 

138 attr="C%iSCA%i"% (imca, isca) 

139 

140 time.sleep(0.05) 

141 

142 def TimeSeriesCaptureOn(self, npts=None): 

143 """ turns on a Time Series Capture""" 

144 for imca in range(len(self.mcas)): 

145 if npts is not None: 

146 self._pvs["MCA%iTSNumPoints" % (imca+1)].put(npts) 

147 self._pvs["SCA%iTSNumPoints" % (imca+1)].put(npts) 

148 time.sleep(0.025) 

149 for imca in range(len(self.mcas)): 

150 self._pvs["MCA%iTSControl" % (imca+1)].put(0) 

151 self._pvs["SCA%iTSControl" % (imca+1)].put(0) 

152 

153 def TimeSeriesCaptureOff(self): 

154 """ turns off a Time Series Capture""" 

155 for imca in range(len(self.mcas)): 

156 self._pvs["MCA%iTSControl" % (imca+1)].put(2) 

157 self._pvs["SCA%iTSControl" % (imca+1)].put(2) 

158 

159 def roi_calib_info(self): 

160 buff = ['[rois]'] 

161 add = buff.append 

162 rois = self.mcas[0].get_rois() 

163 for iroi, roi in enumerate(rois): 

164 name = roi.Name 

165 hi = roi.MinX + roi.SizeX 

166 if len(name.strip()) > 0 and hi > 0: 

167 dbuff = [] 

168 for m in range(self.nmca): 

169 dbuff.extend([roi.MinX, roi.MinX+roi.SizeX]) 

170 dbuff = ' '.join([str(i) for i in dbuff]) 

171 add("ROI%2.2i = %s | %s" % (iroi, name, dbuff)) 

172 

173 add('[calibration]') 

174 add("OFFSET = %s " % (' '.join(["0.000 "] * self.nmca))) 

175 add("SLOPE = %s " % (' '.join(["0.010 "] * self.nmca))) 

176 add("QUAD = %s " % (' '.join(["0.000 "] * self.nmca))) 

177 add('[dxp]') 

178 return buff 

179 

180 def restore_rois(self, roifile): 

181 """restore ROI setting from ROI.dat file""" 

182 cp = ConfigParser() 

183 cp.read(roifile) 

184 roidat = [] 

185 iroi = 0 

186 for a in cp.options('rois'): 

187 if a.lower().startswith('roi'): 

188 name, dat = cp.get('rois', a).split('|') 

189 lims = [int(i) for i in dat.split()] 

190 lo, hi = lims[0], lims[1] 

191 roidat.append((name.strip(), lo, hi)) 

192 

193 for mca in self.mcas: 

194 mca.set_rois(roidat) 

195 

196class Xspress310(Device, ADFileMixin, Xspress3BaseMixin): 

197 """Epics Xspress3.10 interface (older version)""" 

198 attrs = ('NumImages', 'NumImages_RBV', 

199 'Acquire', 'Acquire_RBV', 

200 'ArrayCounter_RBV', 

201 'ERASE', 'UPDATE', 'AcquireTime', 

202 'TriggerMode', 'StatusMessage_RBV', 

203 'DetectorState_RBV') 

204 

205 _nonpvs = ('_prefix', '_pvs', '_delim', 'filesaver', 

206 'fileroot', 'pathattrs', '_nonpvs', '_save_rois', 

207 'nmca', 'dxps', 'mcas') 

208 

209 pathattrs = ('FilePath', 'FileTemplate', 

210 'FileName', 'FileNumber', 

211 'Capture', 'NumCapture') 

212 

213 def __init__(self, prefix, nmca=4, filesaver='HDF5:', 

214 fileroot='/home/xspress3/cars5/Data'): 

215 self.nmca = nmca 

216 attrs = list(self.attrs) 

217 attrs.extend(['%s%s' % (filesaver,p) for p in self.pathattrs]) 

218 

219 self.filesaver = filesaver 

220 self.fileroot = fileroot 

221 self._prefix = prefix 

222 self._save_rois = [] 

223 self.mcas = [MCA(prefix, mca=i+1) for i in range(nmca)] 

224 

225 Device.__init__(self, prefix, attrs=attrs, delim='') 

226 time.sleep(0.1) 

227 

228 

229 def select_rois_to_save(self, roilist): 

230 """copy rois from MCA record to arrays to be saved 

231 by XSPress3""" 

232 roilist = list(roilist) 

233 if len(roilist) < 4: roilist.append((50, 4050)) 

234 pref = self._prefix 

235 self._save_rois = [] 

236 for iroi, roiname in enumerate(roilist): 

237 label = roiname 

238 if isinstance(roiname, tuple): 

239 lo, hi = roiname 

240 label = '[%i:%i]' % (lo, hi) 

241 else: 

242 rname = roiname.lower().strip() 

243 lo, hi = 50, 4050 

244 for ix in range(MAX_ROIS): 

245 nm = caget('%smca1.R%iNM' % (pref, ix)) 

246 if nm.lower().strip() == rname: 

247 lo = caget('%smca1.R%iLO' % (pref, ix)) 

248 hi = caget('%smca1.R%iHI' % (pref, ix)) 

249 break 

250 self._save_rois.append(label) 

251 for imca in range(1, self.nmca+1): 

252 pv_lo = "%sC%i_MCA_ROI%i_LLM" % (pref, imca, iroi+1) 

253 pv_hi = "%sC%i_MCA_ROI%i_HLM" % (pref, imca, iroi+1) 

254 caput(pv_hi, hi) 

255 caput(pv_lo, lo) 

256 

257 def roi_calib_info(self): 

258 buff = ['[rois]'] 

259 add = buff.append 

260 rois = self.get_rois() 

261 for iroi in range(len(rois[0])): 

262 name = rois[0][iroi].NM 

263 hi = rois[0][iroi].HI 

264 if len(name.strip()) > 0 and hi > 0: 

265 dbuff = [] 

266 for m in range(self.nmca): 

267 dbuff.extend([rois[m][iroi].LO, rois[m][iroi].HI]) 

268 dbuff = ' '.join([str(i) for i in dbuff]) 

269 add("ROI%2.2i = %s | %s" % (iroi, name, dbuff)) 

270 

271 add('[calibration]') 

272 add("OFFSET = %s " % (' '.join(["0.000 "] * self.nmca))) 

273 add("SLOPE = %s " % (' '.join(["0.010 "] * self.nmca))) 

274 add("QUAD = %s " % (' '.join(["0.000 "] * self.nmca))) 

275 add('[dxp]') 

276 return buff 

277 

278 def restore_rois(self, roifile): 

279 """restore ROI setting from ROI.dat file""" 

280 cp = ConfigParser() 

281 cp.read(roifile) 

282 rois = [] 

283 self.mcas[0].clear_rois() 

284 prefix = self.mcas[0]._prefix 

285 if prefix.endswith('.'): 

286 prefix = prefix[:-1] 

287 iroi = 0 

288 for a in cp.options('rois'): 

289 if a.lower().startswith('roi'): 

290 name, dat = cp.get('rois', a).split('|') 

291 lims = [int(i) for i in dat.split()] 

292 lo, hi = lims[0], lims[1] 

293 # print('ROI ', name, lo, hi) 

294 roi = ROI(prefix=prefix, roi=iroi) 

295 roi.LO = lo 

296 roi.HI = hi 

297 roi.NM = name.strip() 

298 rois.append(roi) 

299 iroi += 1 

300 

301 poll(0.050, 1.0) 

302 self.mcas[0].set_rois(rois) 

303 cal0 = self.mcas[0].get_calib() 

304 for mca in self.mcas[1:]: 

305 mca.set_rois(rois, calib=cal0)