/ lib / python / qtvcp / qt_istat.py
qt_istat.py
  1  import os
  2  import linuxcnc
  3  import collections
  4  
  5  # Set up logging
  6  import logger
  7  log = logger.getLogger(__name__)
  8  # Set the log level for this module
  9  log.setLevel(logger.INFO) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL
 10  
 11  try:
 12      LINUXCNCVERSION = os.environ['LINUXCNCVERSION']
 13  except:
 14      LINUXCNCVERSION = 'UNAVAILABLE'
 15  
 16  INIPATH = os.environ.get('INI_FILE_NAME', '/dev/null')
 17  
 18  HOME = os.environ.get('EMC2_HOME', '/usr')
 19  if HOME is not None:
 20      IMAGEDIR = os.path.join(HOME, "share","qtvcp","images")
 21  else:
 22      IMAGEDIR = None
 23  
 24  class _IStat(object):
 25      def __init__(self):
 26          # only initialize once for all instances
 27          if self.__class__._instanceNum >=1:
 28              return
 29          self.__class__._instanceNum += 1
 30          self.LINUXCNC_IS_RUNNING = bool(INIPATH != '/dev/null')
 31          if not self.LINUXCNC_IS_RUNNING:
 32              # Reset the log level for this module
 33              # Linuxcnc isn't running so we expect INI errors
 34              log.setLevel(logger.CRITICAL)
 35          self.LINUXCNC_VERSION = LINUXCNCVERSION
 36          self.inifile = linuxcnc.ini(INIPATH)
 37          self.MDI_HISTORY_PATH = '~/.axis_mdi_history'
 38          self.QTVCP_LOG_HISTORY_PATH = '~/qtvcp.log'
 39          self.MACHINE_LOG_HISTORY_PATH = '~/.machine_log_history'
 40          self.PREFERENCE_PATH = '~/.Preferences'
 41          self.SUB_PATH = None
 42          self.IMAGE_PATH = IMAGEDIR
 43          self.LIB_PATH = os.path.join(HOME, "share","qtvcp")
 44  
 45          self.MACHINE_IS_LATHE = False
 46          self.MACHINE_IS_METRIC = False
 47          self.MACHINE_UNIT_CONVERSION = 1
 48          self.MACHINE_UNIT_CONVERSION_9 = [1]*9
 49          self.AVAILABLE_AXES = ['X','Y','Z']
 50          self.AVAILABLE_JOINTS = [0,1,2]
 51          self.GET_NAME_FROM_JOINT = {0:'X',1:'Y',2:'Z'}
 52          self.GET_JOG_FROM_NAME = {'X':0,'Y':1,'Z':2}
 53          self.NO_HOME_REQUIRED = False
 54          self.JOG_INCREMENTS = None
 55          self.ANGULAR_INCREMENTS = None
 56  
 57          self.MAX_LINEAR_VELOCITY = 60
 58          self.DEFAULT_LINEAR_VELOCITY = 15.0
 59  
 60          self.DEFAULT_SPINDLE_SPEED = 200
 61          self.MAX_SPINDLE_SPEED = 2500
 62          self.MAX_FEED_OVERRIDE = 1.5
 63          self.MAX_SPINDLE_OVERRIDE = 1.5
 64          self.MIN_SPINDLE_OVERRIDE = 0.5
 65  
 66          self.update()
 67  
 68      def update(self):
 69          self.MDI_HISTORY_PATH = self.inifile.find('DISPLAY', 'MDI_HISTORY_FILE') or '~/.axis_mdi_history'
 70          self.QTVCP_LOG_HISTORY_PATH = self.inifile.find('DISPLAY', 'LOG_FILE') or '~/qtvcp.log'
 71          self.MACHINE_LOG_HISTORY_PATH = self.inifile.find('DISPLAY', 'MACHINE_LOG_PATH') or '~/.machine_log_history'
 72          self.PREFERENCE_PATH = self.inifile.find("DISPLAY","PREFERENCE_FILE_PATH") or None
 73          self.SUB_PATH = (self.inifile.find("RS274NGC", "SUBROUTINE_PATH")) or None
 74          if self.SUB_PATH is not None:
 75              for mpath in (self.SUB_PATH.split(':')):
 76                  if 'macro' in mpath:
 77                      path = mpath
 78                      break
 79              self.MACRO_PATH = mpath or None
 80          else:
 81              self.MACRO_PATH = None
 82          self.MACHINE_IS_LATHE = bool(self.inifile.find("DISPLAY", "LATHE"))
 83          extensions = self.inifile.findall("FILTER", "PROGRAM_EXTENSION")
 84          self.PROGRAM_FILTERS = ([e.split(None, 1) for e in extensions]) or None
 85          self.PARAMETER_FILE = (self.inifile.find("RS274NGC", "PARAMETER_FILE")) or None
 86          try:
 87              # check the ini file if UNITS are set to mm"
 88              # first check the global settings
 89              units=self.inifile.find("TRAJ","LINEAR_UNITS")
 90              if units==None:
 91                  # else then the X axis units
 92                  units=self.inifile.find("AXIS_0","UNITS")
 93          except:
 94              units = "inch"
 95          # set up the conversion arrays based on what units we discovered
 96          if units=="mm" or units=="metric" or units == "1.0":
 97              self.MACHINE_IS_METRIC = True
 98              self.MACHINE_UNIT_CONVERSION = 1.0/25.4
 99              self.MACHINE_UNIT_CONVERSION_9 = [1.0/25.4]*3+[1]*3+[1.0/25.4]*3
100              log.debug('Machine is METRIC based. unit Conversion constant={}'.format(self.MACHINE_UNIT_CONVERSION ))
101          else:
102              self.MACHINE_IS_METRIC = False
103              self.MACHINE_UNIT_CONVERSION = 25.4
104              self.MACHINE_UNIT_CONVERSION_9 = [25.4]*3+[1]*3+[25.4]*3
105              log.debug('Machine is IMPERIAL based. unit Conversion constant={}'.format(self.MACHINE_UNIT_CONVERSION ))
106  
107          axes = self.inifile.find("TRAJ", "COORDINATES")
108          if axes is not None: # i.e. LCNC is running, not just in Qt Desinger
109              axes = axes.replace(" ", "")
110              log.debug('TRAJ COORDINATES: {}'.format(axes))
111              self.AVAILABLE_AXES = []
112              self.GET_NAME_FROM_JOINT = {}
113              self.AVAILABLE_JOINTS = []
114              self.GET_JOG_FROM_NAME = {}
115              temp = []
116              for num, letter in enumerate(axes):
117                  temp.append(letter)
118  
119                  # list of available axes
120                  if letter not in self.AVAILABLE_AXES:
121                      self.AVAILABLE_AXES.append(letter.upper())
122  
123                  # map of axis designation from joint number
124                  # This allows calling joints x2 or y2 etc
125                  count = collections.Counter(temp)
126                  if count[letter]>1: c = letter+str(count[letter])
127                  else: c = letter
128                  self.GET_NAME_FROM_JOINT[num] = c
129  
130                  # map of axis designation to joint-to-jog when in axis mode.
131                  # so then you can jog either joint of an axis to move the axis
132                  if count[letter]>1:
133                      self.GET_JOG_FROM_NAME[c] = self.GET_JOG_FROM_NAME[letter]
134                  else:
135                      self.GET_JOG_FROM_NAME[c] = num
136  
137                  # list of availble joint numbers
138                  self.AVAILABLE_JOINTS.append(num)
139  
140                  # AXIS sanity check
141                  av = self.inifile.find('AXIS_%s'% letter.upper(), 'MAX_VELOCITY') or None
142                  aa = self.inifile.find('AXIS_%s'% letter.upper(), 'MAX_ACCELERATION') or None
143                  if av is None or aa is None:
144                      log.critical('MISSING [AXIS_{}] MAX VeLOCITY or MAX ACCELERATION entry in INI file.'.format(letter.upper()))
145          self.NO_HOME_REQUIRED = int(self.inifile.find("TRAJ", "NO_FORCE_HOMING") or 0)
146  
147          # home all check
148          self.HOME_ALL_FLAG = 1
149          # set Home All Flage only if ALL joints specify a HOME_SEQUENCE
150          jointcount = len(self.AVAILABLE_JOINTS)
151          self.JOINTSEQUENCELIST = {}
152          for j in range(jointcount):
153              seq = self.inifile.find("JOINT_"+str(j), "HOME_SEQUENCE")
154              if seq is None:
155                  seq = -1
156                  self.HOME_ALL_FLAG = 0
157              self.JOINTSEQUENCELIST[j] = seq
158          # joint sequence/type
159          self.JOINT_TYPE = [None] * jointcount
160          self.JOINT_SEQUENCE = [None] * jointcount
161          for j in range(jointcount):
162              section = "JOINT_%d" % j
163              self.JOINT_TYPE[j] = self.inifile.find(section, "TYPE") or "LINEAR"
164              self.JOINT_SEQUENCE[j]  = self.inifile.find(section, "HOME_SEQUENCE") or ""
165  
166          # jogging increments
167          increments = self.inifile.find("DISPLAY", "INCREMENTS")
168          if increments:
169              if "," in increments:
170                  self.JOG_INCREMENTS = [i.strip() for i in increments.split(",")]
171              else:
172                  self.JOG_INCREMENTS = increments.split()
173              if not "continuous" in increments:
174                  self.JOG_INCREMENTS.insert(0, "Continuous")
175          else:
176              if self.MACHINE_IS_METRIC:
177                  self.JOG_INCREMENTS = ["Continuous",".001 mm",".01 mm",".1 mm","1 mm"]
178              else:
179                  self.JOG_INCREMENTS = ["Continuous",".0001 in",".001 in",".01 in",".1 in"]
180  
181          # angular jogging increments
182          increments = self.inifile.find("DISPLAY", "ANGULAR_INCREMENTS")
183          if increments:
184              if "," in increments:
185                  self.ANGULAR_INCREMENTS = [i.strip() for i in increments.split(",")]
186              else:
187                  self.ANGULAR_INCREMENTS = increments.split()
188              if not "continuous" in increments:
189                  self.ANGULAR_INCREMENTS.insert(0, "Continuous")
190          else:
191              self.ANGULAR_INCREMENTS = ["Continuous","1","45","180","360"]
192          temp = self.inifile.find("TRAJ", "COORDINATES")
193          if temp:
194              self.TRAJ_COORDINATES = temp.lower().replace(" ","")
195          else:
196              self.TRAJ_COORDINATES = None
197          self.JOINT_COUNT = int(self.inifile.find("KINS","JOINTS")or 0)
198          self.DEFAULT_LINEAR_JOG_VEL = float(self.get_error_safe_setting("DISPLAY","DEFAULT_LINEAR_VELOCITY", 1)) * 60
199          self.MIN_LINEAR_JOG_VEL = float(self.get_error_safe_setting("DISPLAY","MIN_LINEAR_VELOCITY",1)) * 60
200          self.MAX_LINEAR_JOG_VEL = float(self.get_error_safe_setting("DISPLAY","MAX_LINEAR_VELOCITY",5)) * 60
201          self.DEFAULT_ANGULAR_JOG_VEL = float(self.get_error_safe_setting("DISPLAY","DEFAULT_ANGULAR_VELOCITY",6)) * 60
202          self.MIN_ANGULAR_JOG_VEL = float(self.get_error_safe_setting("DISPLAY","MIN_ANGULAR_VELOCITY",1)) * 60
203          self.MAX_ANGULAR_JOG_VEL = float(self.get_error_safe_setting("DISPLAY","MAX_ANGULAR_VELOCITY",60)) * 60
204          self.DEFAULT_SPINDLE_SPEED = int(self.get_error_safe_setting("DISPLAY","DEFAULT_SPINDLE_SPEED",200))
205          self.MAX_SPINDLE_SPEED = int(self.get_error_safe_setting("DISPLAY","MAX_SPINDLE_SPEED",2500))
206          self.MAX_SPINDLE_OVERRIDE = float(self.get_error_safe_setting("DISPLAY","MAX_SPINDLE_OVERRIDE",1)) * 100
207          self.MIN_SPINDLE_OVERRIDE = float(self.get_error_safe_setting("DISPLAY","MIN_SPINDLE_OVERRIDE",0.5)) * 100
208          self.MAX_FEED_OVERRIDE = float(self.get_error_safe_setting("DISPLAY","MAX_FEED_OVERRIDE",1.5)) * 100
209          self.MAX_TRAJ_VELOCITY = float(self.get_error_safe_setting("TRAJ","MAX_LINEAR_VELOCITY",
210                                      self.get_error_safe_setting("AXIS_X","MAX_VELOCITY", 5) )) * 60
211  
212          # user message dialog system
213          self.USRMESS_BOLDTEXT = self.inifile.findall("DISPLAY", "MESSAGE_BOLDTEXT")
214          self.USRMESS_TEXT = self.inifile.findall("DISPLAY", "MESSAGE_TEXT")
215          self.USRMESS_TYPE = self.inifile.findall("DISPLAY", "MESSAGE_TYPE")
216          self.USRMESS_PINNAME = self.inifile.findall("DISPLAY", "MESSAGE_PINNAME")
217          self.USRMESS_DETAILS = self.inifile.findall("DISPLAY", "MESSAGE_DETAILS")
218          self.USRMESS_ICON = self.inifile.findall("DISPLAY", "MESSAGE_ICON")
219          if len(self.USRMESS_TEXT) != len(self.USRMESS_TYPE):
220              log.warning('Invalid message configuration (missing text or type) in INI File [DISPLAY] section ')
221          if len(self.USRMESS_TEXT) != len(self.USRMESS_PINNAME):
222              log.warning('Invalid message configuration (missing pinname) in INI File [DISPLAY] section')
223          if len(self.USRMESS_TEXT) != len(self.USRMESS_BOLDTEXT):
224              log.warning('Invalid message configuration (missing boldtext) in INI File [DISPLAY] sectioN')
225          if len(self.USRMESS_TEXT) != len(self.USRMESS_DETAILS):
226              log.warning('Invalid message configuration (missing details) in INI File [DISPLAY] sectioN')
227          try:
228              self.ZIPPED_USRMESS = zip(self.USRMESS_BOLDTEXT,self.USRMESS_TEXT,self.USRMESS_DETAILS,self.USRMESS_TYPE,self.USRMESS_PINNAME)
229          except:
230              self.ZIPPED_USRMESS = None
231  
232          # XEmbed tabs
233          # AXIS panel style:
234          self.GLADEVCP = (self.inifile.find("DISPLAY", "GLADEVCP")) or None
235          
236          # tab style for qtvcp tab style is used everty where
237          self.TAB_NAMES = (self.inifile.findall("DISPLAY", "EMBED_TAB_NAME")) or None
238          self.TAB_LOCATIONS = (self.inifile.findall("DISPLAY", "EMBED_TAB_LOCATION")) or []
239          self.TAB_CMDS   = (self.inifile.findall("DISPLAY", "EMBED_TAB_COMMAND")) or None
240          if self.TAB_NAMES is not None and len(self.TAB_NAMES) != len(self.TAB_CMDS):
241              log.critical('Embeded tab configuration -invalaid number of TAB_NAMES vrs TAB_CMDs')
242          if self.TAB_NAMES is not None and len(self.TAB_LOCATIONS) != len(self.TAB_NAMES):
243              log.warning('Embeded tab configuration -invalaid number of TAB_NAMES vrs TAB_LOCATION - guessng default.')
244              for num,i in enumerate(self.TAB_NAMES):
245                  try:
246                      if self.TAB_LOCATIONS[num]:
247                          continue
248                  except:
249                      self.TAB_LOCATIONS.append("default")
250          try:
251              self.ZIPPED_TABS = zip(self.TAB_NAMES,self.TAB_LOCATIONS,self.TAB_CMDS)
252          except:
253              self.ZIPPED_TABS = None
254  
255          self.MDI_COMMAND_LIST = (self.inifile.findall("MDI_COMMAND_LIST", "MDI_COMMAND")) or None
256          self.TOOL_FILE_PATH = self.get_error_safe_setting("EMCIO", "TOOL_TABLE")
257          self.POSTGUI_HALFILE_PATH = (self.inifile.find("HAL", "POSTGUI_HALFILE")) or None
258  
259      ###################
260      # helper functions
261      ###################
262  
263      def get_error_safe_setting(self, heading, detail, default=None):
264          result = self.inifile.find(heading, detail)
265          if result:
266              return result
267          else:
268              log.warning('INI Parsing Error, No {} Entry in {}, Using: {}'.format(detail, heading, default))
269              return default
270  
271      def convert_machine_to_metric(self, data):
272          if self.MACHINE_IS_METRIC:
273              return data
274          else:
275              return data * 25.4
276  
277      def convert_machine_to_imperial(self, data):
278          if self.MACHINE_IS_METRIC:
279              return data * (1/25.4)
280          else:
281              return data
282  
283      def convert_metric_to_machine(self, data):
284          if self.MACHINE_IS_METRIC:
285              return data
286          else:
287              return data * (1/25.4)
288  
289      def convert_imperial_to_machine(self, data):
290          if self.MACHINE_IS_METRIC:
291              return data * 25.4
292          else:
293              return data 
294  
295      def convert_9_metric_to_machine(self,v):
296          if self.MACHINE_IS_METRIC:
297              return v
298          else:
299              c = [1.0/25.4]*3+[1]*3+[1.0/25.4]*3
300              return map(lambda x,y: x*y, v, c)
301  
302      def convert_9_imperial_to_machine(self,v):
303          if self.MACHINE_IS_METRIC:
304              c = [25.4]*3+[1]*3+[25.4]*3
305              return map(lambda x,y: x*y, v, c)
306          else:
307              return v
308  
309      def convert_units(self, data):
310          return data * self.MACHINE_UNIT_CONVERSION
311  
312      def convert_units_9(self,v):
313          c = self.MACHINE_UNIT_CONVERSION_9
314          return map(lambda x,y: x*y, v, c)
315  
316      # This finds the filter program's initilizing
317      # program eg python for .py from INI
318      def get_filter_program(self, fname):
319          ext = os.path.splitext(fname)[1]
320          if ext:
321              return self.inifile.find("FILTER", ext[1:])
322          else:
323              return None
324  
325      # get filter extensions in QT format
326      def get_qt_filter_extensions(self,):
327          all_extensions = [("G code (*.ngc)")]
328          try:
329              for k, v in self.PROGRAM_FILTERS:
330                  k = k.replace('.',' *.')
331                  k = k.replace(',',' ')
332                  all_extensions.append( ( ';;%s (%s)'%(v,k)) )
333              all_extensions.append((';;All (*)'))
334              temp =''
335              for i in all_extensions:
336                  temp = '%s %s'%(temp ,i)
337              return temp
338          except:
339              return ('All (*)')
340  
341