Coverage for pyilper/pilscope.py: 80%
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
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
1#!/usr/bin/python3
2# -*- coding: utf-8 -*-
3# pyILPER 1.2.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#
26from PySide6 import QtWidgets
27import datetime
28from .pilconfig import PILCONFIG
29from .pilwidgets import cls_tabtermgeneric, T_BOOLEAN, T_STRING,O_DEFAULT
30from .pildevbase import cls_pildevbase
32#
33# Scope tab object classes ----------------------------------------------------
34#
35# Changelog
36#
37# 21.11.2015 jsi
38# - introduced show IDY frames option in scope tab
39# 28.04.2016 jsi
40# - enable tabscope to log inbound, outbound and both traffic
41# 29.04.2016 jsi
42# - log scope unbuffered
43# 01.08.2017 jsi
44# - refactoring: tab classes moved to this file
45# 03.09.2017 jsi
46# - register pildevice is now method of comobject
47# 14.09.2017 jsi
48# - refactoring
49# 22.09.2017 jsi
50# - get actual number of columns with the get_cols method
51# 23.09.2017 jsi
52# - output hex code option added
53# - fixed bug in form feed condition determination in out_scope
54# 25.09.2017 jsi
55# - extended options to display frames (mnemonic only, hex only, mnemonic and hex)
56# 04.10.2017 jsi
57# - display mode of scopes not initialized properly on program start
58# 16.01.2018 jsi
59# - adapted to cls_tabtermgeneric, implemented new cascading config menu
60# 13.08.2018 jsi
61# - log file tagging as proposed by Sylvain Cote
62# 14.08.2018 jsi
63# - added "**" to tag to find tags better in the logfile
64# 16.01.2018 jsi
65# - send int instead of char to terminal
67LOG_INBOUND=0
68LOG_OUTBOUND=1
69LOG_BOTH=2
70DISPLAY_MNEMONIC=0
71DISPLAY_HEX=1
72DISPLAY_BOTH=2
73log_mode= ["Inbound", "Outbound", "Both"]
74display_mode= ["Mnemonic","Hex","Both"]
76class cls_tabscope(cls_tabtermgeneric):
78 def __init__(self,parent,name):
79 super().__init__(parent,name)
80 self.scope_charpos=0
81#
82# init local config parameters
83#
84 self.showIdy= PILCONFIG.get(self.name,"showidy",False)
85 self.displayMode= PILCONFIG.get(self.name,"displaymode",DISPLAY_MNEMONIC)
86 self.logMode=PILCONFIG.get(self.name,"logmode",LOG_INBOUND)
87#
88# add logging
89#
90 self.add_logging()
91#
92# add tag button
93#
94 self.tagButton=QtWidgets.QPushButton("Tag Logfile")
95 self.add_controlwidget(self.tagButton)
96 self.tagButton.setEnabled(False)
97 self.tagButton.clicked.connect(self.do_tagbutton)
98#
99# add scope config options to cascading menu
100#
101 self.cBut.add_option("Show IDY frames","showidy",T_BOOLEAN,[True,False])
102 self.cBut.add_option("Display Mode","displaymode",T_STRING,display_mode)
103 self.cBut.add_option("Log mode","logmode",T_STRING,log_mode)
104#
105# create HP-IL devices and let the GUI object know them
106#
107 self.pildevice= cls_pilscope(True,self)
108 self.pildevice2= cls_pilscope(False,self)
109 self.guiobject.set_pildevice(self.pildevice)
111 self.cBut.config_changed_signal.connect(self.do_tabconfig_changed)
112#
113# handle changes of tab configuration
114#
115 def do_tabconfig_changed(self):
116 param= self.cBut.get_changed_option_name()
117#
118# change local config parameters
119#
120 if param=="showidy":
121 self.showIdy= PILCONFIG.get(self.name,"showidy")
122 self.pildevice.set_show_idy(self.showIdy)
123 self.pildevice2.set_show_idy(self.showIdy)
124 elif param=="displaymode":
125 self.displayMode= PILCONFIG.get(self.name,"displaymode")
126 self.pildevice.set_displayMode(self.displayMode)
127 self.pildevice2.set_displayMode(self.displayMode)
128 elif param=="logmode":
129 self.logMode= PILCONFIG.get(self.name,"logmode")
130 self.pildevice.setlocked(True)
131 self.pildevice.setactive(PILCONFIG.get(self.name,"active") and not (self.logMode == LOG_OUTBOUND))
132 self.pildevice.setlocked(False)
133 self.pildevice2.setlocked(True)
134 self.pildevice2.setactive(PILCONFIG.get(self.name,"active") and not (self.logMode == LOG_INBOUND))
135 self.pildevice2.setlocked(False)
136 super().do_tabconfig_changed()
138 def enable(self):
139 super().enable()
140 self.parent.commthread.register(self.pildevice,self.name)
141 self.pildevice.setactive(PILCONFIG.get(self.name,"active") and not (self.logMode == LOG_OUTBOUND))
142 self.pildevice.set_show_idy(self.showIdy)
143 self.pildevice.set_displayMode(self.displayMode)
144 if self.logging:
145 self.tagButton.setEnabled(True)
147 def post_enable(self):
148 self.parent.commthread.register(self.pildevice2,self.name)
149 self.pildevice2.setactive(PILCONFIG.get(self.name,"active") and not (self.logMode== LOG_INBOUND))
150 self.pildevice2.set_show_idy(self.showIdy)
151 self.pildevice2.set_displayMode(self.displayMode)
153 def disable(self):
154 super().disable()
156 def do_cbActive(self):
157 self.active= self.cbActive.isChecked()
158 PILCONFIG.put(self.name,"active",self.active)
159 self.pildevice.setlocked(True)
160 self.pildevice.setactive(PILCONFIG.get(self.name,"active") and not (self.logMode == LOG_OUTBOUND))
161 self.pildevice.setlocked(False)
162 self.pildevice2.setlocked(True)
163 self.pildevice2.setactive(PILCONFIG.get(self.name,"active") and not (self.logMode == LOG_INBOUND))
164 self.pildevice2.setlocked(False)
166 try:
167 self.toggle_active()
168 except AttributeError:
169 pass
170 return
171#
172# set tag button active/inactive
173#
174 def do_cbLogging(self):
175 super().do_cbLogging()
176 if self.logging:
177 self.tagButton.setEnabled(True)
178 else:
179 self.tagButton.setEnabled(False)
180#
181# exec tag button, because we may write it asynchronous pause the thread
182#
183 def do_tagbutton(self):
184 text,okPressed=QtWidgets.QInputDialog.getText(self,"Tag Logfile","Logfile tag",QtWidgets.QLineEdit.Normal,"")
185 if okPressed and text != "":
186 if self.parent.commthread is not None:
187 if self.parent.commthread.isRunning():
188 self.parent.commthread.halt()
189#
190# all errors that might occur during log write are handled from the
191# cbLogging methods, we do not need to catch errors
192#
193 self.cbLogging.logWrite("\n")
194 self.cbLogging.logWrite(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
195 self.cbLogging.logWrite(" ** ")
196 self.cbLogging.logWrite(text)
197 self.cbLogging.logWrite("\n")
198 if self.parent.commthread is not None:
199 self.parent.commthread.resume()
200#
201#
202# forward character to the terminal frontend widget and do logging
203#
204 def out_scope(self,s):
205 l=len(s)
206 if self.scope_charpos+l>=self.guiobject.get_cols() :
207 self.guiobject.out_terminal(0x0D)
208 self.guiobject.out_terminal(0x0A)
209 self.cbLogging.logWrite("\n")
210 self.cbLogging.logFlush()
211 self.scope_charpos=0
212 for i in range(0,len(s)):
213 self.guiobject.out_terminal(ord(s[i]))
214 self.cbLogging.logWrite(s)
215 self.scope_charpos+=l
216#
217# HP-IL scope class -----------------------------------------------------------
218#
219# Changelog
220# 06.10.2015 jsi:
221# - class statement syntax update
222# 21.11.2015 jsi:
223# - removed SSRQ/CSRQ approach
224# - introduced show IDY frames option
225# 29.11.2015 jsi:
226# - introduced device lock
227# 07.02.2016 jsi
228# - refactored and merged new Ildev base class of Christoph Giesselink
229# 28.04.2016 jsi:
230# - introduced inbound parameter, if True use uppercase letters if False use loweercase
231# 09.08.2017 jsi:
232# - register_callback_output implemented (from base class)
233# 14.09.2017 jsi
234# - refactoring
236class cls_pilscope(cls_pildevbase):
238 def __init__ (self, inbound,parent):
239 super().__init__()
240 self.__inbound__= inbound
241 self.__mnemo__= ["DAB", "DSR", "END", "ESR", "CMD", "RDY", "IDY", "ISR"]
242 self.__scmd0__= ["NUL", "GTL", "???", "???", "SDC", "PPD", "???", "???",
243 "GET", "???", "???", "???", "???", "???", "???", "ELN",
244 "NOP", "LLO", "???", "???", "DCL", "PPU", "???", "???",
245 "EAR", "???", "???", "???", "???", "???", "???", "???" ]
246 self.__scmd9__= ["IFC", "???", "REN", "NRE", "???", "???", "???", "???",
247 "???", "???", "AAU", "LPD", "???", "???", "???", "???"]
248 self.__show_idy__= False
249 self.__displayMode__= DISPLAY_MNEMONIC
250 self.__parent__= parent
252#
253# public -------
254#
256 def set_show_idy(self,flag):
257 self.__show_idy__= flag
259 def set_displayMode(self,flag):
260 self.__displayMode__= flag
261#
262# public (overloaded) -------
263#
264# convert frame to readable text and call the parent method out_scope
265#
266 def process (self,frame):
267 if not self.__isactive__:
268 return(frame)
269#
270# ignore IDY frames
271#
272 if ((frame & 0x700) == 0x600) and not self.__show_idy__:
273 return (frame)
275 n= frame & 255
276 s= "{:3s} {:02X}".format(self.__mnemo__[frame // 256], n)
278#
279# CMD
280#
281 if (frame & 0x700) == 0x400 :
282 t= n // 32
284 if t == 0:
285 s= self.__scmd0__[n & 31 ]
286 elif t == 1:
287 if (n & 31) == 31:
288 s="UNL"
289 else:
290 s= "LAD {:02X}".format( n & 31)
291 elif t == 2:
292 if (n & 31) == 31:
293 s="UNT"
294 else:
295 s= "TAD {:02X}".format( n & 31)
296 elif t == 3:
297 s= "SAD {:02X}".format( n & 31)
298 elif t == 4:
299 if (n & 31) < 16:
300 s= "PPE {:02X}".format( n & 31)
301 else:
302 s= self.__scmd9__[n & 15]
303 elif t == 5:
304 s= "DDL {:02X}".format( n & 31)
305 elif t == 6:
306 s= "DDT {:02X}".format( n & 31)
307 if s[0] =="?":
308 s="CMD {:02x}".format(n)
309 else:
310#
311# RDY
312#
313 if (frame & 0x700) == 0x500:
314 if n < 128:
315 if n == 0:
316 s= "RFC"
317 elif n == 64:
318 s= "ETO"
319 elif n == 65:
320 s= "ETE"
321 elif n == 66:
322 s= "NRD"
323 elif n == 96:
324 s= "SDA"
325 elif n == 97:
326 s= "SST"
327 elif n== 98:
328 s = "SDI"
329 elif n == 99:
330 s = "SAI"
331 elif n == 100:
332 s = "TCT"
333 else:
334 s = "RDY {:2X}".format(n)
335 else:
336 t= n // 32
337 if t == 4:
338 s = "AAD {:2X}".format(n & 31)
339 elif t == 5:
340 s = "AEP {:2X}".format(n & 31)
341 elif t == 6:
342 s = "AES {:2X}".format(n & 31)
343 elif t == 7:
344 s = "AMP {:2X}".format(n & 31)
345 else:
346 s = "RDY {:2X}".format(n)
348#
349# inbound frames are lowercase, outbound frames are uppercase
350#
351 if self.__displayMode__== DISPLAY_MNEMONIC:
352 s="{:6s} ".format(s)
353 elif self.__displayMode__== DISPLAY_HEX:
354 s="{:03X} ".format(frame)
355 elif self.__displayMode__== DISPLAY_BOTH:
356 s="{:6s} ({:03X}) ".format(s,frame)
357 if not self.__inbound__:
358 s= s.lower()
359 self.__access_lock__.acquire()
360 locked= self.__islocked__
361 self.__access_lock__.release()
362 if not locked:
363 self.__parent__.out_scope(s)
364 return (frame)