stdglue.py
  1  # stdglue - canned prolog and epilog functions for the remappable builtin codes (T,M6,M61,S,F)
  2  #
  3  # we dont use argspec to avoid the generic error message of the argspec prolog and give more
  4  # concise ones here
  5  
  6  # cycle_prolog,cycle_epilog: generic code-independent support glue for oword sub cycles
  7  #
  8  # these are provided as starting point - for more concise error message you would better
  9  # write a prolog specific for the code
 10  #
 11  # Usage:
 12  #REMAP=G84.3  modalgroup=1 argspec=xyzqp prolog=cycle_prolog ngc=g843 epilog=cycle_epilog
 13  
 14  import emccanon 
 15  from interpreter import *
 16  throw_exceptions = 1
 17  
 18  # REMAP=S   prolog=setspeed_prolog  ngc=setspeed epilog=setspeed_epilog
 19  # exposed parameter: #<speed>
 20  
 21  def setspeed_prolog(self,**words):
 22      try:
 23          c = self.blocks[self.remap_level]
 24          if not c.s_flag:
 25              self.set_errormsg("S requires a value") 
 26              return INTERP_ERROR
 27          self.params["speed"] = c.s_number
 28      except Exception,e:
 29          self.set_errormsg("S/setspeed_prolog: %s)" % (e))
 30          return INTERP_ERROR
 31      return INTERP_OK
 32  
 33  def setspeed_epilog(self,**words):
 34      try:
 35          if not self.value_returned:
 36              r = self.blocks[self.remap_level].executing_remap
 37              self.set_errormsg("the %s remap procedure %s did not return a value"
 38                               % (r.name,r.remap_ngc if r.remap_ngc else r.remap_py))
 39              return INTERP_ERROR
 40          if self.return_value < -TOLERANCE_EQUAL: # 'less than 0 within interp's precision'
 41              self.set_errormsg("S: remap procedure returned %f" % (self.return_value)) 
 42              return INTERP_ERROR
 43          if self.blocks[self.remap_level].builtin_used:
 44              pass
 45              #print "---------- S builtin recursion, nothing to do"
 46          else:
 47              self.speed = self.params["speed"]
 48              emccanon.enqueue_SET_SPINDLE_SPEED(self.speed)
 49          return INTERP_OK
 50      except Exception,e:
 51          self.set_errormsg("S/setspeed_epilog: %s)" % (e))
 52          return INTERP_ERROR
 53      return INTERP_OK    
 54  
 55  # REMAP=F   prolog=setfeed_prolog  ngc=setfeed epilog=setfeed_epilog
 56  # exposed parameter: #<feed>
 57  
 58  def setfeed_prolog(self,**words):
 59      try:
 60          c = self.blocks[self.remap_level]
 61          if not c.f_flag:
 62              self.set_errormsg("F requires a value") 
 63              return INTERP_ERROR
 64          self.params["feed"] = c.f_number
 65      except Exception,e:
 66          self.set_errormsg("F/setfeed_prolog: %s)" % (e))
 67          return INTERP_ERROR
 68      return INTERP_OK    
 69  
 70  def setfeed_epilog(self,**words):
 71      try:
 72          if not self.value_returned:
 73              r = self.blocks[self.remap_level].executing_remap
 74              self.set_errormsg("the %s remap procedure %s did not return a value"
 75                               % (r.name,r.remap_ngc if r.remap_ngc else r.remap_py))
 76              return INTERP_ERROR
 77          if self.blocks[self.remap_level].builtin_used:
 78              pass
 79              #print "---------- F builtin recursion, nothing to do"
 80          else:
 81              self.feed_rate = self.params["feed"]
 82              emccanon.enqueue_SET_FEED_RATE(self.feed_rate)
 83          return INTERP_OK
 84      except Exception,e:
 85          self.set_errormsg("F/setfeed_epilog: %s)" % (e))
 86          return INTERP_ERROR
 87      return INTERP_OK    
 88  
 89  # REMAP=T   prolog=prepare_prolog ngc=prepare epilog=prepare_epilog
 90  # exposed parameters: #<tool> #<pocket>
 91  
 92  def prepare_prolog(self,**words):
 93      try:
 94          cblock = self.blocks[self.remap_level]
 95          if not cblock.t_flag:
 96              self.set_errormsg("T requires a tool number")
 97              return INTERP_ERROR
 98          tool  = cblock.t_number
 99          if tool:
100              (status, pocket) = self.find_tool_pocket(tool)
101              if status != INTERP_OK:
102                  self.set_errormsg("T%d: pocket not found" % (tool))
103                  return status
104          else:
105              pocket = -1 # this is a T0 - tool unload
106          self.params["tool"] = tool
107          self.params["pocket"] = pocket
108          return INTERP_OK
109      except Exception, e:
110          self.set_errormsg("T%d/prepare_prolog: %s" % (int(words['t']), e))
111          return INTERP_ERROR
112  
113  def prepare_epilog(self, **words):
114      try:
115          if not self.value_returned:
116              r = self.blocks[self.remap_level].executing_remap
117              self.set_errormsg("the %s remap procedure %s did not return a value"
118                               % (r.name,r.remap_ngc if r.remap_ngc else r.remap_py))
119              return INTERP_ERROR
120          if self.blocks[self.remap_level].builtin_used:
121              #print "---------- T builtin recursion, nothing to do"
122              return INTERP_OK
123          else:
124              if self.return_value > 0:
125                  self.selected_tool = int(self.params["tool"])
126                  self.selected_pocket = int(self.params["pocket"])
127                  emccanon.SELECT_POCKET(self.selected_pocket, self.selected_tool)
128                  return INTERP_OK
129              else:
130                  self.set_errormsg("T%d: aborted (return code %.1f)" % (int(self.params["tool"]),self.return_value))
131                  return INTERP_ERROR
132      except Exception, e:
133          self.set_errormsg("T%d/prepare_epilog: %s" % (tool,e))
134          return INTERP_ERROR       
135  
136  # REMAP=M6  modalgroup=6 prolog=change_prolog ngc=change epilog=change_epilog
137  # exposed parameters:
138  #    #<tool_in_spindle>
139  #    #<selected_tool>
140  #    #<current_pocket>
141  #    #<selected_pocket>
142  
143  def change_prolog(self, **words):
144      try:
145          # this is relevant only when using iocontrol-v2.
146          if self.params[5600] > 0.0:
147              if self.params[5601] < 0.0:
148                  self.set_errormsg("Toolchanger hard fault %d" % (int(self.params[5601])))
149                  return INTERP_ERROR
150              print "change_prolog: Toolchanger soft fault %d" % int(self.params[5601])
151  
152          if self.selected_pocket < 0:
153              self.set_errormsg("M6: no tool prepared")
154              return INTERP_ERROR
155          if self.cutter_comp_side:
156              self.set_errormsg("Cannot change tools with cutter radius compensation on")
157              return INTERP_ERROR
158          self.params["tool_in_spindle"] = self.current_tool
159          self.params["selected_tool"] = self.selected_tool
160          self.params["current_pocket"] = self.current_pocket # this is probably nonsense
161          self.params["selected_pocket"] = self.selected_pocket
162          return INTERP_OK
163      except Exception, e:
164          self.set_errormsg("M6/change_prolog: %s" % (e))
165          return INTERP_ERROR
166  
167  def change_epilog(self, **words):
168      try:
169          if not self.value_returned:
170              r = self.blocks[self.remap_level].executing_remap
171              self.set_errormsg("the %s remap procedure %s did not return a value"
172                               % (r.name,r.remap_ngc if r.remap_ngc else r.remap_py))
173              yield INTERP_ERROR
174          # this is relevant only when using iocontrol-v2.
175          if self.params[5600] > 0.0:
176              if self.params[5601] < 0.0:
177                  self.set_errormsg("Toolchanger hard fault %d" % (int(self.params[5601])))
178                  yield INTERP_ERROR
179              print "change_epilog: Toolchanger soft fault %d" % int(self.params[5601])
180  
181          if self.blocks[self.remap_level].builtin_used:
182              #print "---------- M6 builtin recursion, nothing to do"
183              yield INTERP_OK
184          else:
185              if self.return_value > 0.0:
186                  # commit change
187                  self.selected_pocket =  int(self.params["selected_pocket"])
188                  emccanon.CHANGE_TOOL(self.selected_pocket)
189                  self.current_pocket = self.selected_pocket
190                  self.selected_pocket = -1
191                  self.selected_tool = -1
192                  # cause a sync()
193                  self.set_tool_parameters()
194                  self.toolchange_flag = True
195                  yield INTERP_EXECUTE_FINISH
196              else:
197                  self.set_errormsg("M6 aborted (return code %.1f)" % (self.return_value))
198                  yield INTERP_ERROR
199      except Exception, e:
200          self.set_errormsg("M6/change_epilog: %s" % (e))
201          yield INTERP_ERROR
202  
203  # REMAP=M61  modalgroup=6 prolog=settool_prolog ngc=settool epilog=settool_epilog
204  # exposed parameters: #<tool> #<pocket>
205  
206  def settool_prolog(self,**words):
207      try:
208          c = self.blocks[self.remap_level]
209          if not c.q_flag:
210              self.set_errormsg("M61 requires a Q parameter") 
211              return INTERP_ERROR
212          tool = int(c.q_number)
213          if tool < -TOLERANCE_EQUAL: # 'less than 0 within interp's precision'
214              self.set_errormsg("M61: Q value < 0") 
215              return INTERP_ERROR
216          (status,pocket) = self.find_tool_pocket(tool)
217          if status != INTERP_OK:
218              self.set_errormsg("M61 failed: requested tool %d not in table" % (tool))
219              return status
220          self.params["tool"] = tool
221          self.params["pocket"] = pocket
222          return INTERP_OK
223      except Exception,e:
224          self.set_errormsg("M61/settool_prolog: %s)" % (e))
225          return INTERP_ERROR
226  
227  def settool_epilog(self,**words):
228      try:
229          if not self.value_returned:
230              r = self.blocks[self.remap_level].executing_remap
231              self.set_errormsg("the %s remap procedure %s did not return a value"
232                               % (r.name,r.remap_ngc if r.remap_ngc else r.remap_py))
233              return INTERP_ERROR
234  
235          if self.blocks[self.remap_level].builtin_used:
236              #print "---------- M61 builtin recursion, nothing to do"
237              return INTERP_OK
238          else:
239              if self.return_value > 0.0:
240                  self.current_tool = int(self.params["tool"])
241                  self.current_pocket = int(self.params["pocket"])
242                  emccanon.CHANGE_TOOL_NUMBER(self.current_pocket)
243                  # cause a sync()
244                  self.tool_change_flag = True
245                  self.set_tool_parameters()
246              else:
247                  self.set_errormsg("M61 aborted (return code %.1f)" % (self.return_value))
248                  return INTERP_ERROR
249      except Exception,e:
250          self.set_errormsg("M61/settool_epilog: %s)" % (e))
251          return INTERP_ERROR
252  
253  # educational alternative: M61 remapped to an all-Python handler
254  # demo - this really does the same thing as the builtin (non-remapped) M61
255  #
256  # REMAP=M61 modalgroup=6 python=set_tool_number
257  
258  def set_tool_number(self, **words):
259      try:
260          c = self.blocks[self.remap_level]
261          if c.q_flag:
262              toolno = int(c.q_number)
263          else:
264              self.set_errormsg("M61 requires a Q parameter")
265              return status 
266          (status,pocket) = self.find_tool_pocket(toolno)
267          if status != INTERP_OK:
268              self.set_errormsg("M61 failed: requested tool %d not in table" % (toolno))
269              return status
270          if words['q'] > -TOLERANCE_EQUAL: # 'greater equal 0 within interp's precision'
271              self.current_pocket = pocket
272              self.current_tool = toolno
273              emccanon.CHANGE_TOOL_NUMBER(pocket)
274              # cause a sync()
275              self.tool_change_flag = True
276              self.set_tool_parameters()
277              return INTERP_OK
278          else:
279              self.set_errormsg("M61 failed: Q=%4" % (toolno))
280              return INTERP_ERROR
281      except Exception, e:
282          self.set_errormsg("M61/set_tool_number: %s" % (e))
283          return INTERP_ERROR
284  
285  _uvw = ("u","v","w","a","b","c")
286  _xyz = ("x","y","z","a","b","c")
287  # given a plane, return  sticky words, incompatible axis words and plane name
288  # sticky[0] is also the movement axis
289  _compat = {
290      emccanon.CANON_PLANE_XY : (("z","r"),_uvw,"XY"),
291      emccanon.CANON_PLANE_YZ : (("x","r"),_uvw,"YZ"),
292      emccanon.CANON_PLANE_XZ : (("y","r"),_uvw,"XZ"),
293      emccanon.CANON_PLANE_UV : (("w","r"),_xyz,"UV"),
294      emccanon.CANON_PLANE_VW : (("u","r"),_xyz,"VW"),
295      emccanon.CANON_PLANE_UW : (("v","r"),_xyz,"UW")}           
296  
297  # extract and pass parameters from current block, merged with extra parameters on a continuation line
298  # keep tjose parameters across invocations
299  # export the parameters into the oword procedure
300  def cycle_prolog(self,**words):
301      # self.sticky_params is assumed to have been initialized by the
302      # init_stgdlue() method below
303      global _compat
304      try:    
305          # determine whether this is the first or a subsequent call
306          c = self.blocks[self.remap_level]
307          r = c.executing_remap
308          if c.g_modes[1] == r.motion_code:
309              # first call - clear the sticky dict
310              self.sticky_params[r.name] = dict()
311  
312          self.params["motion_code"] = c.g_modes[1]
313  
314          (sw,incompat,plane_name) =_compat[self.plane]
315          for (word,value) in words.items():
316              # inject current parameters
317              self.params[word] = value
318              # record sticky words
319              if word in sw:
320                  if self.debugmask & 0x00080000: print "%s: record sticky %s = %.4f" % (r.name,word,value)
321                  self.sticky_params[r.name][word] = value
322              if word in incompat:
323                  return "%s: Cannot put a %s in a canned cycle in the %s plane" % (r.name, word.upper(), plane_name)
324  
325          # inject sticky parameters which were not in words:
326          for (key,value) in self.sticky_params[r.name].items():
327              if not key in words:
328                  if self.debugmask & 0x00080000: print "%s: inject sticky %s = %.4f" % (r.name,key,value)
329                  self.params[key] = value
330  
331          if not "r" in self.sticky_params[r.name]:
332              return "%s: cycle requires R word" % (r.name)
333          else:
334              if self.sticky_params[r.name] <= 0.0:
335                  return "%s: R word must be > 0 if used (%.4f)" % (r.name, words["r"])
336  
337          if "l" in words:
338              # checked in interpreter during block parsing
339              # if l <= 0 or l not near an int
340              self.params["l"] = words["l"]
341              
342          if "p" in words:
343              p = words["p"]
344              if p < 0.0:
345                  return "%s: P word must be >= 0 if used (%.4f)" % (r.name, p)
346              self.params["p"] = p
347  
348          if self.feed_rate == 0.0:
349              return "%s: feed rate must be > 0" % (r.name)
350          if self.feed_mode == INVERSE_TIME:
351              return "%s: Cannot use inverse time feed with canned cycles" % (r.name)
352          if self.cutter_comp_side:
353              return "%s: Cannot use canned cycles with cutter compensation on" % (r.name)
354          return INTERP_OK
355  
356      except Exception, e:
357          raise
358          return "cycle_prolog failed: %s" % (e)
359  
360  # make sure the next line has the same motion code, unless overriden by a
361  # new G code
362  def cycle_epilog(self,**words):
363      try:
364          c = self.blocks[self.remap_level]
365          self.motion_mode = c.executing_remap.motion_code # retain the current motion mode
366          return INTERP_OK
367      except Exception, e:
368          return "cycle_epilog failed: %s" % (e)
369  
370  # this should be called from TOPLEVEL __init__()
371  def init_stdglue(self):
372      self.sticky_params = dict()