Coverage for pyilper/pildevbase.py: 87%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

207 statements  

1#!/usr/bin/python3 

2# -*- coding: utf-8 -*- 

3# pyILPER 1.6.1 for Linux 

4# 

5# An emulator for virtual HP-IL devices for the PIL-Box 

6# derived from ILPER 1.4.5 for Windows 

7# Copyright (c) 2008-2013 Jean-Francois Garnier 

8# C++ version (c) 2013 Christoph Gießelink 

9# Python Version (c) 2015 Joachim Siebold 

10# 

11# This program is free software; you can redistribute it and/or 

12# modify it under the terms of the GNU General Public License 

13# as published by the Free Software Foundation; either version 2 

14# of the License, or (at your option) any later version. 

15# 

16# This program is distributed in the hope that it will be useful, 

17# but WITHOUT ANY WARRANTY; without even the implied warranty of 

18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

19# GNU General Public License for more details. 

20# 

21# You should have received a copy of the GNU General Public License 

22# along with this program; if not, write to the Free Software 

23# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 

24# 

25# HP-IL virtual device base object class --------------------------------------- 

26# 

27# Derived fom IldevBase.cpp from Christoph Gießelink 

28# 

29# Changelog 

30# 

31# 16.02.2016 jsi: 

32# - merged new Ildev base class of Christoph Gießelink 

33# 01.03.2016 cg: 

34# - removed EOT response from __outdata__ to implement "no response" feature 

35# 05.03.2016 jsi: 

36# - wrong variable name __ptdsi__ corrected 

37# - improved getstatus, use accesss lock and return ilstate as text 

38# 02.08.2017 jsi: 

39# - class variable for length of status information introduced, alter 

40# statement to clear service request bit to handle multiple byte status data 

41# changed the order of status bytes output (least significant byte first) 

42# - reset service request bit, if DCL odr SDC 

43# 07.08.2017 jsi: 

44# - register functions for callbacks removed 

45# 10.08.2017 jsi 

46# - check if self.__did__ != "" and not != None 

47# 05.10.2017 jsi: 

48# - reset device address if device was reactivated an an HP-IL addressing 

49# operation happened in the meantime 

50# 18.12.2017 jsi: 

51# - check whether the sent and the incoming data byte are identical if a 

52# virtual device is talker. Send ETE if not. 

53# - fix a bug in handling the SRQ bit in DOE frames 

54# Both changes are courtesy of Christoph Gießelink 

55# 16.01.2018 jsi: 

56# - adapted to new local config parameters for color scheme and terminal width 

57 

58 

59import threading 

60 

61class cls_pildevbase: 

62 

63 def __init__(self): 

64 

65 self.__aid__ = 0x00 # accessory id  

66 self.__defaddr__ = 0 # default address alter AAU 

67 self.__did__ = "" # device id 

68 self.__status__ = 0 # HP-IL status 

69 self.__addr__ = 0 # HP-IL primary address (by TAD,LAD) 

70 # bits 0-5=AAD or AEP, bit 7=1 means 

71 # auto address taken 

72 self.__addr2nd__ = 0 # HP-IL secondary address (by SAD) 

73 # bits 0-5=AES, bit 7 means auto addr taken 

74 self.__ilstate__ =0 # state machine flag 

75 # bit 7, bit 6, bit5, bit 4 

76 # 0 0 0 0 idle 

77 # 0 0 1 0 addressed listener in 

78 # second. add. mode 

79 # 0 0 0 1 addressed talker in 

80 # second. add. mode 

81 # 1 0 0 0 addressed listener 

82 # 0 1 0 0 addressed talker  

83 # bit 0 or bit 1 set active takler 

84 # bit 1: SDA, SDI 

85 # bit 0: SST, SDI, SAI, NRD 

86 self.__talker_frame__=0 # frame sent as talker 

87 self.__ptsdi__ = 0 # output pointer for device id 

88 self.__status_len__=1 # length of device status in bytes 

89 self.__ptssi__ = 0 # output pointer for hp-il status 

90 self.__isactive__= False # device active in loop 

91 self.__addr_framecounter__=0 # framecounter when device got an address 

92 self.__threadobject__=None # reference to the thread object 

93 self.__status_lock__= threading.Lock() 

94 self.__islocked__= False 

95 self.__access_lock__= threading.Lock() 

96# 

97# --- public functions --- 

98# 

99# set device active/inactive. If the device becomes active check if an AAU, 

100# AAD, AEP or AES happened in the mean time. In this case reset the device 

101# address 

102# 

103 def setactive(self, active): 

104 if not self.__isactive__ and active: 

105 if self.__addr_framecounter__ != self.__threadobject__.get_addr_framecounter(): 

106 self.__addr__=0 

107 self.__addr2nd__=0 

108 self.__isactive__= active 

109# 

110# set object reference to thread object 

111# 

112 def setThreadObject(self,obj): 

113 self.__threadobject__= obj 

114# 

115# set local and update globel addr_framecounter 

116# 

117 def update_addr_framecounter(self): 

118 self.__addr_framecounter__= self.__threadobject__.get_framecounter() 

119 self.__threadobject__.update_addr_framecounter(self.__addr_framecounter__) 

120# 

121# return device status 

122# 

123 def getstatus(self): 

124 self.__access_lock__.acquire() 

125 status="idle" 

126 if self.__ilstate__ & 0x03: 

127 status="act. talker" 

128 else: 

129 if self.__ilstate__ & 0xA0: 

130 status="addr. listener" 

131 elif self.__ilstate__ & 0x50: 

132 status="addr. talker" 

133 

134 ret= [self.__isactive__, self.__did__, self.__aid__, self.__addr__, self.__addr2nd__, status] 

135 self.__access_lock__.release() 

136 return ret 

137 

138# 

139# lock device, all output is disabled 

140# 

141 def setlocked(self,locked): 

142 self.__access_lock__.acquire() 

143 self.__islocked__= locked 

144 self.__access_lock__.release() 

145 

146# 

147# Process device 

148# 

149 def process(self,frame): 

150 

151# 

152# if device is not active, return 

153# 

154 if not self.__isactive__: 

155 return(frame) 

156# 

157# process frames 

158# 

159 if (frame & 0x400) == 0: 

160 frame= self.__do_doe__(frame) 

161 elif (frame & 0x700) == 0x400: 

162 frame= self.__do_cmd__(frame) 

163 elif (frame & 0x700) == 0x500: 

164 frame= self.__do_rdy__(frame) 

165# 

166# set service request bit if data available status bit set 

167# 

168 if self.__getstatus__() & 0x40: 

169 if (frame & 0x700) == 0x000: # data 00x xxxx xxxx -> 001 xxxx xxxx 

170 frame= frame | 0x100 

171 if (frame & 0x700) == 0x200: # end 01x xxxx xxxx -> 011 xxxx xxxx 

172 frame= frame | 0x100 

173 if (frame & 0x700) == 0x600: # idy 11x xxxx xxxx -> 111 xxxx xxxx 

174 frame= frame | 0x100 

175 return(frame) 

176 

177# 

178# --- private --- 

179# 

180# 

181# get status byte from device thread safe 

182# 

183 def __getstatus__(self): 

184 self.__status_lock__.acquire() 

185 status= self.__status__ 

186 self.__status_lock__.release() 

187 return(status) 

188# 

189# set status byte of device thread safe 

190# 

191 def __setstatus__(self,status): 

192 self.__status_lock__.acquire() 

193 self.__status__= status 

194 self.__status_lock__.release() 

195# 

196# output data stub 

197# 

198 def __outdata__(self,frame): 

199 return frame 

200 

201# 

202# input data stub 

203# 

204 def __indata__(self,frame): 

205 return 

206 

207# 

208# device clear stub 

209# 

210 def __clear_device__(self): 

211 # reset service request bit 

212 s= self.__getstatus__() 

213 s &= ~ 0x40 

214 self.__setstatus__(s) 

215 return 

216# 

217# stub for extended sad commands 

218# 

219 def __cmd_sad_ext__(self,frame): 

220 return frame 

221 

222# 

223# stub for extended commands 

224# 

225 def __cmd_ext__(self,frame): 

226 return frame 

227 

228# 

229# manage HPIL data frames, returns the returned frame 

230# 

231 def __do_doe__(self,frame): 

232 talker_error= False 

233 

234 if (self.__ilstate__ & 0xC0) == 0x40: # addressed talker? 

235 

236 if (self.__ilstate__ & 0x03) != 0: # active talker? 

237 

238# compare last talker frame with actual frame without SRQ bit 

239 talker_error = ((frame & 0x6FF) != (self.__talker_frame__ &0x6FF)) 

240 

241# data (SDA) status (SST) or accessory ID (SDI) 

242 if (not talker_error) and (self.__ilstate__ & 0x02 !=0): # talker 

243 

244# save current SRQ bit 

245 SrqBit= frame & 0x100 

246 

247# status (SST) or accessory ID (SDI) 

248 if (self.__ilstate__ & 0x01) != 0: 

249 

250# 0x43: active talker (multibyte status) 

251 if self.__ptssi__ > 0: # SST 

252 self.__ptssi__= self.__ptssi__-1 

253 if self.__ptssi__ > 0: 

254 frame= (self.__getstatus__() >> (( self.__status_len__-self.__ptssi__ ) * 8)) & 0xFF 

255 if self.__ptsdi__ > 0: # SDI 

256 if self.__ptsdi__ == len(self.__did__): 

257 frame=0 

258 self.__ptsdi__=0 

259 else: # SDI 

260 frame= ord(self.__did__ [self.__ptsdi__]) 

261 self.__ptsdi__= self.__ptsdi__+1 

262 if self.__ptssi__ == 0 and self.__ptsdi__ == 0 : 

263# EOT for SST and SDI 

264 frame= 0x540 

265 else: # 0x42 active talker (data) 

266 frame=self.__outdata__(frame) # SDA 

267# a set SRQ bit doesn't matter on ready class frames 

268 frame |= SrqBit 

269 else: # 0x41 active talker (single byte status) 

270 frame= 0x540 # end of SAI or NRD or talker error 

271 

272 if frame == 0x540: # EOT 

273# check for error and set ETE frame 

274 if talker_error: 

275 frame= 0x541 # ETE 

276 self.__ilstate__ &= ~ 0x03 # delete active talker 

277 self.__talker_frame__= frame 

278 

279 if (self.__ilstate__ & 0xC0)==0x80: # listener 

280 self.__indata__(frame) 

281 return(frame) 

282# 

283# manage HP-IL command frames 

284# 

285 def __do_cmd__(self,frame): 

286 

287 n= frame & 0xff 

288 t= n >> 5 

289 if t == 0: 

290 if n == 4: # SDC 

291 if (self.__ilstate__ & 0x80) != 0: # listener 

292 self.__clear_device__() 

293 elif n == 20: # DCL 

294 self.__clear_device__() 

295 elif t == 1: # LAD 

296 n= n & 31 

297 if n == 31: # UNL, if not talker then go to idle state 

298 if (self.__ilstate__ & 0xA0) != 0: # not talker ? 

299 self.__ilstate__ &= 0x50 # idle 

300 else: # if MLA go to listen state 

301 if (self.__ilstate__ &0x80) ==0 and n == (self.__addr__ & 31): 

302 if (self.__addr2nd__ & 0x80) == 0: 

303 self.__ilstate__ = 0x80 # set addressed listener 

304 else: 

305 self.__ilstate__ = 0x20 # set addressed listener second. add mode 

306 elif t == 2: # TAD  

307 n= n & 31 

308 if n == (self.__addr__ & 31): 

309 if (self.__addr2nd__ & 0x80) == 0: # if MTA go to talker state 

310 self.__ilstate__= 0x40 # set addressed talker 

311 else: 

312 self.__ilstate__= 0x10 # set addressed talker, second. add. mode 

313 else: 

314 if ( self.__ilstate__ & 0x50) != 0: # addressed talker? 

315 self.__ilstate__ &= 0xA0 # idle 

316 elif t == 3: # SAD 

317 if (self.__ilstate__ & 0x30) !=0: # addressed talker or listener 2nd addr mode? 

318 n = n & 31 

319 if n == (self.__addr2nd__ & 31): 

320 self.__ilstate__<<=2 # switch to addressed listener/talker 

321 else: 

322 self.__ilstate__=0 # idle 

323 else: 

324 frame= self.__cmd_sad_ext__(frame) 

325 elif t == 4: 

326 n= n & 31 

327 if n == 16: # IFC 

328 self.__ilstate__= 0x0 # idle 

329 elif n == 26: # AAU 

330 self.update_addr_framecounter() 

331 self.__addr__= self.__defaddr__ 

332 self.__addr2nd__= 0 

333 else: 

334 frame= self.__cmd_ext__(frame) 

335 return(frame) 

336 

337# 

338# HP-IL ready frames 

339# 

340 def __do_rdy__(self,frame): 

341 

342 n= frame & 0xFF 

343 if n <= 127: 

344 if ( self.__ilstate__ & 0x40) !=0: # SOT, addressed talker? 

345 if n == 66: #NRD 

346 self.__ptsdi__ = 0 

347 self.__ptssi__ = 0 

348 self.__ilstate__ = 0x41 # abort transfer, clear SDA/SDI 

349 

350 elif n == 96: # SDA 

351 frame=self.__outdata__(frame) 

352 if frame != 0x560: # not sda received data 

353 self.__ilstate__= 0x42 # active talker, SDA/SDI 

354 self.__talker_frame__= frame # last talker frame 

355 elif n == 97: # SST 

356 # reset service request bit 

357 s= self.__getstatus__() 

358 s &= ~ 0x40 

359 self.__setstatus__(s) 

360 # update IL status and return no. of status bytes 

361 self.__ptssi__ = self.__status_len__ 

362 if self.__ptssi__ > 0: # response to status request 

363 frame = (self.__getstatus__() >> ((self.__status_len__-self.__ptssi__) * 8)) & 0xFF 

364 self.__ilstate__= 0x43 # active talker 

365 self.__talker_frame__= frame # last talker frame 

366 elif n == 98: # SDI 

367 if self.__did__ != "": 

368 frame= ord(self.__did__[0]) 

369 self.__ptsdi__ = 1 # other 2 

370 self.__ilstate__= 0x43 # active talker, SDA/SDI, SST/SDI/SAI 

371 self.__talker_frame__= frame 

372 elif n == 99: # SAI 

373 frame= self.__aid__ & 0xFF 

374 self.__ilstate__= 0x41 # active talker, SST/SDI/SAI 

375 self.__talker_frame__= frame 

376 else: 

377 if n < 0x80 +31: # AAD 

378 if ((self.__addr__ & 0x80) == 0 and self.__addr2nd__ ==0): 

379 # AAD, if not already an assigned address, take it 

380 self.update_addr_framecounter() 

381 self.__addr__ = n 

382 frame=frame+1 

383 elif (n >= 0xA0 and n < 0xA0 + 31): # AEP 

384 # AEP, if not already an assigned address and got an AES frame, 

385 if ((self.__addr__ & 0x80) == 0 and (self.__addr2nd__ & 0x80) != 0): 

386 # take it 

387 self.update_addr_framecounter() 

388 self.__addr__= n & 0x9F 

389 elif (n >= 0xC0 and n < 0xC0 +31): # AES 

390 if (self.__addr__ & 0x80) == 0: 

391 self.update_addr_framecounter() 

392 self.__addr2nd__= n & 0x9F 

393 frame=frame + 1 

394 return (frame)