#!/usr/bin/env python3 # # This is stepconf, a graphical configuration editor for LinuxCNC # Copyright 2007 Jeff Epler # stepconf 1.1 revamped by Chris Morley 2014 # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # This builds the HAL files from the collected data. # import os import time import shutil class HAL: def __init__(self,app): # access to: self.d = app.d # collected data global SIG SIG = app._p # private data (signal names) self.a = app # The parent, stepconf def write_halfile(self, base): inputs = self.a.build_input_set() outputs = self.a.build_output_set() filename = os.path.join(base, self.d.machinename + ".hal") # make a backup copy if hal file exists if os.path.exists(filename): shutil.copy2(filename, filename.replace(".hal", "_" + str(int(time.time())) + ".hal")) file = open(filename, "w") print(_("# Generated by stepconf 1.1 at %s") % time.asctime(), file=file) print(_("# If you make changes to this file, they will be"), file=file) print(_("# overwritten when you run stepconf again"), file=file) print("loadrt [KINS]KINEMATICS", file=file) # qtplasmac requires 3 spindles if self.d.select_qtplasmac: print("loadrt [EMCMOT]EMCMOT base_period_nsec=[EMCMOT]BASE_PERIOD servo_period_nsec=[EMCMOT]SERVO_PERIOD num_joints=[KINS]JOINTS num_spindles=[TRAJ]SPINDLES", file=file) else: print("loadrt [EMCMOT]EMCMOT base_period_nsec=[EMCMOT]BASE_PERIOD servo_period_nsec=[EMCMOT]SERVO_PERIOD num_joints=[KINS]JOINTS", file=file) port3name=port2name=port2dir=port3dir="" if self.d.number_pports>2: port3name = ' '+self.d.ioaddr3 if self.d.pp3_direction: # Input option port3dir =" in" else: port3dir =" out" if self.d.number_pports>1: port2name = ' '+self.d.ioaddr2 if self.d.pp2_direction: # Input option port2dir =" in" else: port2dir =" out" if not self.d.sim_hardware: print("loadrt hal_parport cfg=\"%s out%s%s%s%s\"" % (self.d.ioaddr, port2name, port2dir, port3name, port3dir), file=file) else: name='parport.0' if self.d.number_pports>1: name='parport.0,parport.1' print("loadrt sim_parport names=%s"%name, file=file) if self.a.doublestep(): print("setp parport.0.reset-time %d" % self.d.steptime, file=file) if not self.d.select_qtplasmac: encoder = SIG.PHA in inputs counter = SIG.PHB not in inputs probe = SIG.PROBE in inputs pwm = SIG.PWM in outputs pump = SIG.PUMP in outputs else: encoder, counter, probe, pwm, pump = False, False, False, False, False, limits_homes = SIG.ALL_LIMIT_HOME in inputs stepgens = '0' for axis in range(1, len(self.d.axislist)): stepgens += ",0" print("loadrt stepgen step_type={}".format(stepgens), file=file) if encoder: print("loadrt encoder num_chan=1", file=file) if self.d.pyvcphaltype == 1 and self.d.pyvcpconnect == 1: if encoder: print("loadrt abs count=1", file=file) print("loadrt scale count=1", file=file) print("loadrt lowpass count=1", file=file) if self.d.usespindleatspeed: print("loadrt near", file=file) if pump: print("loadrt charge_pump", file=file) print("net estop-out charge-pump.enable iocontrol.0.user-enable-out", file=file) print("net charge-pump <= charge-pump.out", file=file) if limits_homes: print("loadrt lut5", file=file) if pwm: print("loadrt pwmgen output_type=1", file=file) if self.d.classicladder: print("loadrt classicladder_rt numPhysInputs=%d numPhysOutputs=%d numS32in=%d numS32out=%d numFloatIn=%d numFloatOut=%d" % (self.d.digitsin , self.d.digitsout , self.d.s32in, self.d.s32out, self.d.floatsin, self.d.floatsout), file=file) if self.d.select_qtplasmac: print("loadrt plasmac", file=file) print(file=file) print("addf parport.0.read base-thread", file=file) if self.d.number_pports > 1: print("addf parport.1.read base-thread", file=file) if self.d.number_pports > 2: print("addf parport.2.read base-thread", file=file) if self.d.sim_hardware: print("source sim_hardware.hal", file=file) if encoder: print("addf sim-encoder.make-pulses base-thread", file=file) print("addf stepgen.make-pulses base-thread", file=file) if encoder: print("addf encoder.update-counters base-thread", file=file) if pump: print("addf charge-pump base-thread", file=file) if pwm: print("addf pwmgen.make-pulses base-thread", file=file) print("addf parport.0.write base-thread", file=file) if self.a.doublestep(): print("addf parport.0.reset base-thread", file=file) if self.d.number_pports > 1: print("addf parport.1.write base-thread", file=file) if self.d.number_pports > 2: print("addf parport.2.write base-thread", file=file) print(file=file) print("addf stepgen.capture-position servo-thread", file=file) if self.d.sim_hardware: print("addf sim-hardware.update servo-thread", file=file) if encoder: print("addf sim-encoder.update-speed servo-thread", file=file) if encoder: print("addf encoder.capture-position servo-thread", file=file) print("addf motion-command-handler servo-thread", file=file) print("addf motion-controller servo-thread", file=file) if self.d.classicladder: print("addf classicladder.0.refresh servo-thread", file=file) if self.d.select_qtplasmac: print("addf plasmac servo-thread", file=file) print("addf stepgen.update-freq servo-thread", file=file) if limits_homes: print("addf lut5.0 servo-thread", file=file) if pwm: print("addf pwmgen.update servo-thread", file=file) if self.d.pyvcphaltype == 1 and self.d.pyvcpconnect == 1: if encoder: print("addf abs.0 servo-thread", file=file) print("addf scale.0 servo-thread", file=file) print("addf lowpass.0 servo-thread", file=file) if self.d.usespindleatspeed: print("addf near.0 servo-thread", file=file) if pwm: x1 = self.d.spindlepwm1 x2 = self.d.spindlepwm2 y1 = self.d.spindlespeed1 y2 = self.d.spindlespeed2 scale = (y2-y1) / (x2-x1) offset = x1 - y1 / scale print(file=file) print("net spindle-cmd-rpm => pwmgen.0.value", file=file) print("net spindle-on <= spindle.0.on => pwmgen.0.enable", file=file) print("net spindle-pwm <= pwmgen.0.pwm", file=file) print("setp pwmgen.0.pwm-freq %s" % self.d.spindlecarrier, file=file) print("setp pwmgen.0.scale %s" % scale, file=file) print("setp pwmgen.0.offset %s" % offset, file=file) print("setp pwmgen.0.dither-pwm true", file=file) print("net spindle-cmd-rpm <= spindle.0.speed-out", file=file) print("net spindle-cmd-rpm-abs <= spindle.0.speed-out-abs", file=file) print("net spindle-cmd-rps <= spindle.0.speed-out-rps", file=file) print("net spindle-cmd-rps-abs <= spindle.0.speed-out-rps-abs", file=file) print("net spindle-at-speed => spindle.0.at-speed", file=file) if SIG.ON in outputs and not pwm: print("net spindle-on <= spindle.0.on", file=file) if SIG.CW in outputs: print("net spindle-cw <= spindle.0.forward", file=file) if SIG.CCW in outputs: print("net spindle-ccw <= spindle.0.reverse", file=file) if SIG.BRAKE in outputs: print("net spindle-brake <= spindle.0.brake", file=file) if SIG.MIST in outputs: print("net coolant-mist <= iocontrol.0.coolant-mist", file=file) if SIG.FLOOD in outputs: print("net coolant-flood <= iocontrol.0.coolant-flood", file=file) # do the qtplasmac connections and preferences file if self.d.select_qtplasmac: self.qtplasmac_connections(file, base) prefsfile = os.path.join(base, self.d.machinename + ".prefs") self.qtplasmac_prefs(prefsfile) if encoder: print(file=file) if SIG.PHB not in inputs: print("setp encoder.0.position-scale %f"\ % self.d.spindlecpr, file=file) print("setp encoder.0.counter-mode 1", file=file) else: print("setp encoder.0.position-scale %f" \ % ( 4.0 * int(self.d.spindlecpr)), file=file) print("net spindle-position encoder.0.position => spindle.0.revs", file=file) print("net spindle-velocity-feedback-rps encoder.0.velocity => spindle.0.speed-in", file=file) print("net spindle-index-enable encoder.0.index-enable <=> spindle.0.index-enable", file=file) print("net spindle-phase-a encoder.0.phase-A", file=file) print("net spindle-phase-b encoder.0.phase-B", file=file) print("net spindle-index encoder.0.phase-Z", file=file) if probe: print(file=file) print("net probe-in => motion.probe-input", file=file) for i in range(4): dout = "dout-%02d" % i if dout in outputs: comment = "" if self.d.select_qtplasmac and i > 0: comment = "#qtplasmac uses this digital output: " print("%snet %s <= motion.digital-out-%02d" % (comment, dout, i), file=file) for i in range(4): din = "din-%02d" % i if din in inputs: comment = "" if self.d.select_qtplasmac and i > 0: comment = "#qtplasmac uses this digital input: " print("%snet %s => motion.digital-in-%02d" % (comment, din, i), file=file) self.tandemsigs = {} if self.d.tandemjoints: for j in self.d.tandemjoints: self.tandemsigs["{}step".format(j)] = 1 self.tandemsigs["{}dir".format(j)] = 1 print(file=file) self.outputlist = [] for o in (1,2,3,4,5,6,7,8,9,14,16,17): self.connect_output(file, o) if self.d.number_pports>1: if self.d.pp2_direction:# Input option pinlist = (1,14,16,17) else: pinlist = (1,2,3,4,5,6,7,8,9,14,16,17) print(file=file) for i in pinlist: self.connect_output(file, i, 1) print(file=file) self.inputlist = [] for i in (10,11,12,13,15): self.connect_input(file, i) if self.d.number_pports>1: if self.d.pp2_direction: # Input option pinlist = (2,3,4,5,6,7,8,9,10,11,12,13,15) else: pinlist = (10,11,12,13,15) print(file=file) for i in pinlist: self.connect_input(file, i, 1) print(file=file) if limits_homes: print("setp lut5.0.function 0x10000", file=file) print("net all-limit-home => lut5.0.in-4", file=file) print("net all-limit <= lut5.0.out", file=file) for j in self.d.axislist: print("net {0}homing <= joint.{1}.homing => lut5.0.in-{1}".format(j, self.d.axislist.index(j)), file=file) # connect the joints for j in self.d.axislist: self.connect_joint(file, self.d.axislist.index(j), j) print(file=file) # wacky estop handling for qtplasmac with sim hardware if self.d.select_qtplasmac and self.d.sim_hardware: print("\n# ---QTPLASMAC SIM ESTOP HANDLING---", file=file) print("loadrt or2 names=estop_or", file=file) print("loadrt not names=estop_not", file=file) print("addf estop_or servo-thread", file=file) print("addf estop_not servo-thread", file=file) print("net sim:estop-raw estop_or.out estop_not.in", file=file) print("net sim:estop-out estop_not.out iocontrol.0.emc-enable-in", file=file) else: # standard estop handling print("net estop-out <= iocontrol.0.user-enable-out", file=file) if self.d.classicladder and self.d.ladderhaltype == 1 and self.d.ladderconnect: # external estop program print(file=file) print(_("# **** Setup for external estop ladder program -START ****"), file=file) print(file=file) print("net estop-out => classicladder.0.in-00", file=file) print("net estop-ext => classicladder.0.in-01", file=file) print("net estop-strobe classicladder.0.in-02 <= iocontrol.0.user-request-enable", file=file) print("net estop-outcl classicladder.0.out-00 => iocontrol.0.emc-enable-in", file=file) print(file=file) print(_("# **** Setup for external estop ladder program -END ****"), file=file) elif SIG.ESTOP_IN in inputs: print("net estop-ext => iocontrol.0.emc-enable-in", file=file) else: print("net estop-out => iocontrol.0.emc-enable-in", file=file) print(file=file) if self.d.manualtoolchange: if not self.d.select_qtdragon: print("loadusr -W hal_manualtoolchange", file=file) print("net tool-change iocontrol.0.tool-change => hal_manualtoolchange.change", file=file) print("net tool-changed iocontrol.0.tool-changed <= hal_manualtoolchange.changed", file=file) print("net tool-number iocontrol.0.tool-prep-number => hal_manualtoolchange.number", file=file) else: print("net tool-change <= iocontrol.0.tool-change", file=file) print("net tool-changed => iocontrol.0.tool-changed", file=file) print("net tool-number <= iocontrol.0.tool-prep-number", file=file) qt = os.path.join(base, "qtvcp_postgui.hal") if not os.path.exists(qt): f1 = open(qt, "w") print("net tool-change => hal_manualtoolchange.change", file=f1) print("net tool-changed <= hal_manualtoolchange.changed", file=f1) print("net tool-number => hal_manualtoolchange.number", file=f1) f1.close() else: print("net tool-number <= iocontrol.0.tool-prep-number", file=file) print("net tool-change-loopback iocontrol.0.tool-change => iocontrol.0.tool-changed", file=file) print("net tool-prepare-loopback iocontrol.0.tool-prepare => iocontrol.0.tool-prepared", file=file) if self.d.classicladder: print(file=file) if self.d.modbus: print(_("# Load Classicladder with Modbus master included (GUI must run for Modbus)"), file=file) print("loadusr classicladder --modmaster custom.clp", file=file) else: print(_("# Load Classicladder without GUI (can reload LADDER GUI in AXIS GUI"), file=file) print("loadusr classicladder --nogui custom.clp", file=file) if self.d.pyvcp: vcp = os.path.join(base, "custompanel.xml") if not os.path.exists(vcp): f1 = open(vcp, "w") print("", file=f1) print("", file=f1) print("", file=f1) print("", file=f1) # Same as from pncconf # the jump list allows multiple hal files to be loaded postgui # this simplifies the problem of overwriting the users custom HAL code # when they change pyvcp sample options # if the user picked existing pyvcp option and the postgui_call_list is present # don't overwrite it. otherwise write the file. # qtplasmac doesn't use call list if not self.d.select_qtplasmac: calllist_filename = os.path.join(base, "postgui_call_list.hal") f1 = open(calllist_filename, "w") print(_("# These files are loaded post GUI, in the order they appear"), file=f1) print(_("# Generated by stepconf 1.1 at %s") % time.asctime(), file=f1) print(_("# If you make changes to this file, they will be"), file=f1) print(_("# overwritten when you run stepconf again"), file=f1) print(file=f1) if self.d.pyvcp and not self.d.select_qtplasmac: print("source pyvcp_options.hal", file=f1) if self.d.manualtoolchange and self.d.select_qtdragon: print("source qtvcp_postgui.hal", file=f1) print("source custom_postgui.hal", file=f1) f1.close() # qtplasmac doesn't use pyvcp_options if not self.d.select_qtplasmac: pyfilename = os.path.join(base, "pyvcp_options.hal") f1 = open(pyfilename, "w") # If the user asked for pyvcp sample panel add the HAL commands too if self.d.pyvcp and self.d.pyvcphaltype == 1 and self.d.pyvcpconnect: # spindle speed/tool # display print(_("# These files are loaded post GUI, in the order they appear"), file=f1) print(_("# Generated by stepconf 1.1 at %s") % time.asctime(), file=f1) print(_("# If you make changes to this file, they will be"), file=f1) print(_("# overwritten when you run stepconf again"), file=f1) print(_("# **** Setup of spindle speed display using pyvcp -START ****"), file=f1) if encoder: print(_("# **** Use ACTUAL spindle velocity from spindle encoder"), file=f1) print(_("# **** spindle-velocity-feedback-rps bounces around so we filter it with lowpass"), file=f1) print(_("# **** spindle-velocity-feedback-rps is signed so we use absolute component to remove sign"), file=f1) print(_("# **** ACTUAL velocity is in RPS not RPM so we scale it."), file=f1) print(file=f1) print(("setp scale.0.gain 60"), file=f1) print(("setp lowpass.0.gain %f")% self.d.spindlefiltergain, file=f1) print(("net spindle-velocity-feedback-rps => lowpass.0.in"), file=f1) print(("net spindle-fb-filtered-rps lowpass.0.out => abs.0.in"), file=f1) print(("net spindle-fb-filtered-abs-rps abs.0.out => scale.0.in"), file=f1) print(("net spindle-fb-filtered-abs-rpm scale.0.out => pyvcp.spindle-speed"), file=f1) print(file=f1) print(_("# **** set up spindle at speed indicator ****"), file=f1) if self.d.usespindleatspeed: print(file=f1) print(("net spindle-cmd-rps-abs => near.0.in1"), file=f1) print(("net spindle-velocity-feedback-rps => near.0.in2"), file=f1) print(("net spindle-at-speed <= near.0.out"), file=f1) print(("setp near.0.scale %f")% self.d.spindlenearscale, file=f1) else: print(("# **** force spindle at speed indicator true because we chose no feedback ****"), file=f1) print(file=f1) print(("sets spindle-at-speed true"), file=f1) print(("net spindle-at-speed => pyvcp.spindle-at-speed-led"), file=f1) else: print(_("# **** Use COMMANDED spindle velocity from LinuxCNC because no spindle encoder was specified"), file=f1) print(file=f1) print(("net spindle-cmd-rpm-abs => pyvcp.spindle-speed"), file=f1) print(file=f1) print(("# **** force spindle at speed indicator true because we have no feedback ****"), file=f1) print(file=f1) print(("net spindle-at-speed => pyvcp.spindle-at-speed-led"), file=f1) print(("sets spindle-at-speed true"), file=f1) f1.close() else: print(_("# These files are loaded post GUI, in the order they appear"), file=f1) print(_("# Generated by stepconf 1.1 at %s") % time.asctime(), file=f1) print(_("# If you make changes to this file, they will be"), file=f1) print(_("# overwritten when you run stepconf again"), file=f1) print(("sets spindle-at-speed true"), file=f1) f1.close() # stepconf adds custom.hal and custom_postgui.hal file if one is not present if self.d.customhal or self.d.classicladder or self.d.halui: for i in ("custom","custom_postgui"): custom = os.path.join(base, i+".hal") if not os.path.exists(custom): f1 = open(custom, "w") print(_("# Include your %s HAL commands here")%i, file=f1) print(_("# This file will not be overwritten when you run stepconf again"), file=f1) print(file=f1) f1.close() file.close() self.sim_hardware_halfile(base) self.d.add_md5sum(filename) #****************** # HELPER FUNCTIONS #****************** def connect_joint(self, file, num, let): axnum = "xyzabcuvw".index(let[0]) lat = self.d.latency print(file=file) print("setp stepgen.%d.position-scale [JOINT_%d]SCALE" % (num, num), file=file) print("setp stepgen.%d.steplen 1" % num, file=file) if self.a.doublestep(): print("setp stepgen.%d.stepspace 0" % num, file=file) else: print("setp stepgen.%d.stepspace 1" % num, file=file) print("setp stepgen.%d.dirhold %d" % (num, self.d.dirhold + lat), file=file) print("setp stepgen.%d.dirsetup %d" % (num, self.d.dirsetup + lat), file=file) print("setp stepgen.%d.maxaccel [JOINT_%d]STEPGEN_MAXACCEL" % (num, num), file=file) print("net %spos-cmd joint.%d.motor-pos-cmd => stepgen.%d.position-cmd" % (let, num, num), file=file) print("net %spos-fb stepgen.%d.position-fb => joint.%d.motor-pos-fb" % (let, num, num), file=file) print("net %sstep <= stepgen.%d.step" % (let, num), file=file) print("net %sdir <= stepgen.%d.dir" % (let, num), file=file) print("net %senable joint.%d.amp-enable-out => stepgen.%d.enable" % (let, num, num), file=file) homesig = self.a.home_sig(let) if homesig: print("net %s => joint.%d.home-sw-in" % (homesig, num), file=file) min_limsig = self.min_lim_sig(let) if min_limsig: print("net %s => joint.%d.neg-lim-sw-in" % (min_limsig, num), file=file) max_limsig = self.max_lim_sig(let) if max_limsig: print("net %s => joint.%d.pos-lim-sw-in" % (max_limsig, num), file=file) def sim_hardware_halfile(self,base): custom = os.path.join(base, "sim_hardware.hal") if self.d.sim_hardware: f1 = open(custom, "w") print(_("# This file sets up simulated limits/home/spindle encoder hardware."), file=f1) print(_("# This is a generated file do not edit."), file=f1) print(file=f1) inputs = self.a.build_input_set() if SIG.PHA in inputs and not self.d.select_qtplasmac: print("loadrt sim_encoder names=sim-encoder", file=f1) print("setp sim-encoder.ppr %d"%int(self.d.spindlecpr), file=f1) print("setp sim-encoder.scale 1", file=f1) print(file=f1) print("net spindle-cmd-rps sim-encoder.speed", file=f1) print("net fake-spindle-phase-a sim-encoder.phase-A", file=f1) print("net fake-spindle-phase-b sim-encoder.phase-B", file=f1) print("net fake-spindle-index sim-encoder.phase-Z", file=f1) print(file=f1) print("loadrt sim_axis_hardware names=sim-hardware", file=f1) print(file=f1) for j in self.d.axislist: print("net {:16}joint.{}.pos-fb sim-hardware.{}current-pos".format(j.upper() + "joint-pos-fb", self.d.axislist.index(j), j.upper()), file=f1) print(file=f1) for j in self.d.axislist: if j[0] == 'a': limit = 20000 else: limit = 1000 print("setp sim-hardware.{:14}{}".format(j.upper() + "maxsw-upper", limit), file=f1) print("setp sim-hardware.{:14}[JOINT_{}]MAX_LIMIT".format(j.upper() + "maxsw-lower", self.d.axislist.index(j)), file=f1) print("setp sim-hardware.{:14}[JOINT_{}]MIN_LIMIT".format(j.upper() + "minsw-upper", self.d.axislist.index(j)), file=f1) print("setp sim-hardware.{:14}-{}".format(j.upper() + "minsw-lower", limit), file=f1) print("setp sim-hardware.{:14}[JOINT_{}]HOME_OFFSET".format(j.upper() + "homesw-pos", self.d.axislist.index(j)), file=f1) if self.d.units: # change sim home switch hysteresis for metric configs print("setp sim-hardware.{:14}{}".format(j.upper() + "homesw-hyst", 0.6), file=f1) print(file=f1) for port in range(0,self.d.number_pports): if port==0 or not self.d.pp2_direction: # output option pinlist = (10,11,12,13,15) else: pinlist = (2,3,4,5,6,7,8,9,10,11,12,13,15) self.inputlist = [] for i in pinlist: self.connect_input(f1, i, port, True) print(file=f1) if port==0 or not self.d.pp2_direction: # output option pinlist = (1,2,3,4,5,6,7,8,9,14,16,17) else: pinlist = (1,14,16,17) self.tandemsigs = {} if self.d.tandemjoints: for j in self.d.tandemjoints: self.tandemsigs["{}step".format(j)] = 1 self.tandemsigs["{}dir".format(j)] = 1 self.outputlist = [] for o in pinlist: self.connect_output(f1, o, port, True) print(file=f1) print("net fake-all-home sim-hardware.homesw-all", file=f1) print("net fake-all-limit sim-hardware.limitsw-all", file=f1) print("net fake-all-limit-home sim-hardware.limitsw-homesw-all", file=f1) print(file=f1) for j in ["x","x2","y","y2","z","a","u","v"]: print("net {:21}sim-hardware.{}bothsw-out".format("fake-both-" + j, j.upper()), file=f1) print("net {:21}sim-hardware.{}maxsw-out".format("fake-max-" + j, j.upper()), file=f1) print("net {:21}sim-hardware.{}minsw-out".format("fake-min-" + j, j.upper()), file=f1) print(file=f1) for j in ["x","x2","y","y2","z","a","u","v"]: print("net {:21}sim-hardware.{}homesw-out".format("fake-home-" + j, j.upper()), file=f1) print(file=f1) for j in ["x","x2","y","y2","z","a","u","v"]: print("net {:21}sim-hardware.{}bothsw-homesw-out".format("fake-both-home-" + j, j.upper()), file=f1) print("net {:21}sim-hardware.{}maxsw-homesw-out".format("fake-max-home-" + j, j.upper()), file=f1) print("net {:21}sim-hardware.{}minsw-homesw-out".format("fake-min-home-" + j, j.upper()), file=f1) # if self.d.sim_hardware: print(file=f1) for j in self.d.axislist: if SIG.ALL_LIMIT_HOME in inputs: print("net {}homing => sim-hardware.{}homing".format(j, j.upper()), file=f1) else: print("net {}homing joint.{}.homing => sim-hardware.{}homing".format(j, self.d.axislist.index(j), j.upper()), file=f1) f1.close() else: if os.path.exists(custom): os.remove(custom) def connect_input(self, file, num, port=0, fake=False): ending='' if port == 0: p = self.d['pin%d' % num] i = self.d['pin%dinv' % num] else: p = self.d['pp2_pin%d_in' % num] i = self.d['pp2_pin%d_in_inv' % num] if p == SIG.UNUSED_INPUT: return if p not in self.inputlist: self.inputlist.append(p) else: if not fake: print("duplicate input \"%s\" will not be connected to \"parport.%d.pin-%02d-in%s\"" % \ (SIG.human_input_names[SIG.hal_input_names.index(p)], port, num, ending)) return if fake: p='fake-'+p ending='-fake' p ='{0:<20}'.format(p) else: p ='{0:<15}'.format(p) # comment out inputs for a qtplasmac sim comment = "" if self.d.select_qtplasmac: if p.strip()[-6:] in ["din-01", "din-02", "din-03"]: comment = "#qtplasmac uses this digital input: " elif "spindle-" in p: comment = "#qtplasmac doesn't use a spindle: " elif "probe-" in p: comment = "#qtplasmac doesn't use a probe: " elif self.d.sim_hardware and "plasmac:" in p: comment = "#qtplasmac disabled for sim mode: " if i and not fake: print("%snet %s <= parport.%d.pin-%02d-in-not%s" \ % (comment, p, port, num, ending), file=file) else: print("%snet %s <= parport.%d.pin-%02d-in%s" \ % (comment, p, port, num, ending), file=file) def connect_output(self, file, num, port=0, fake=False): ending='' if port == 0: p = self.d['pin%d' % num] i = self.d['pin%dinv' % num] else: p = self.d['pp2_pin%d' % num] i = self.d['pp2_pin%dinv' % num] if p == SIG.UNUSED_OUTPUT: return if p not in self.outputlist: self.outputlist.append(p) else: if not fake: print("duplicate output \"%s\" will not be connected to \"parport.%d.pin-%02d-out%s\"" % \ (SIG.human_output_names[SIG.hal_output_names.index(p)], port, num, ending)) return if fake: signame ='fake-'+p ending='-fake' signame ='{0:<20}'.format(signame) else: signame ='{0:<15}'.format(p) if i and not fake: print("setp parport.%d.pin-%02d-out-invert%s 1" %(port, num, ending), file=file) # check for a tandem joint sig = signame.strip().replace("fake-", "") if sig in self.tandemsigs: value = self.tandemsigs[sig] if value == 2: signame = signame.replace('{} '.format(sig), '{}{}{}'.format(sig[0], value, sig[1:])) self.tandemsigs[sig] = value + 1 # comment out inputs for a qtplasmac sim comment = "" if self.d.select_qtplasmac: if signame.strip()[-7:] in ["dout-01", "dout-02", "dout-03"]: comment = "#qtplasmac uses this digital output: " elif "spindle-" in signame: comment = "#qtplasmac doesn't use a spindle: " elif self.d.sim_hardware and "plasmac:" in signame: comment = "#qtplasmac disabled for sim mode: " print("%snet %s => parport.%d.pin-%02d-out%s" % (comment, signame, port, num, ending), file=file) if self.a.doublestep() and not fake: if p in (SIG.XSTEP, SIG.YSTEP, SIG.ZSTEP, SIG.ASTEP, SIG.USTEP, SIG.VSTEP, SIG.X2STEP, SIG.Y2STEP): print("setp parport.0.pin-%02d-out-reset%s 1" % (num,ending), file=file) def min_lim_sig(self, axis): inputs = self.a.build_input_set() thisaxisminlimits = set((SIG.ALL_LIMIT, SIG.ALL_LIMIT_HOME, "min-" + axis, "min-home-" + axis, "both-" + axis, "both-home-" + axis)) for i in inputs: if i in thisaxisminlimits: if i==SIG.ALL_LIMIT_HOME: # ALL_LIMIT is reused here as filtered signal return SIG.ALL_LIMIT else: return i def max_lim_sig(self, axis): inputs = self.a.build_input_set() thisaxismaxlimits = set((SIG.ALL_LIMIT, SIG.ALL_LIMIT_HOME, "max-" + axis, "max-home-" + axis, "both-" + axis, "both-home-" + axis)) for i in inputs: if i in thisaxismaxlimits: if i==SIG.ALL_LIMIT_HOME: # ALL_LIMIT is reused here as filtered signal return SIG.ALL_LIMIT else: return i # qtplasmac hal connections def qtplasmac_connections(self, file, base): print("\n# ---PLASMA INPUT DEBOUNCE---", file=file) print("loadrt dbounce names=db_breakaway,db_float,db_ohmic,db_arc-ok", file=file) print("addf db_float servo-thread", file=file) print("addf db_ohmic servo-thread", file=file) print("addf db_breakaway servo-thread", file=file) print("addf db_arc-ok servo-thread", file=file) print("\n# ---JOINT ASSOCIATED WITH THE Z AXIS---", file=file) jnum = 2 if not self.d.sim_hardware: print("net plasmac:axis-position joint.{:d}.pos-fb => plasmac.axis-z-position".format(jnum), file=file) else: print("net Zjoint-pos-fb => plasmac.axis-z-position", file=file) comment = "" if self.d.sim_hardware: comment = "#qtplasmac disabled for sim mode: " print("\n# ---PLASMA INPUTS---", file=file) print("# ---all modes---", file=file) print("{}net plasmac:float-switch => db_float.in".format(comment), file=file) print("{}net plasmac:breakaway => db_breakaway.in".format(comment), file=file) print("{}net plasmac:ohmic-probe => db_ohmic.in".format(comment), file=file) print("net plasmac:ohmic-sense-in => plasmac.ohmic-sense-in", file=file) print("# ---modes 0 & 1", file=file) print("{}net plasmac:arc-voltage-in => plasmac.arc-voltage-in".format(comment), file=file) print("# ---modes 1 & 2", file=file) print("{}net plasmac:arc-ok-in => db_arc-ok.in".format(comment), file=file) print("# ---mode 2", file=file) print("{}net plasmac:move-up => plasmac.move-up".format(comment), file=file) print("{}net plasmac:move-down => plasmac.move-down".format(comment), file=file) print("\n# ---PLASMA OUTPUTS---", file=file) print("# ---all modes---", file=file) print("net plasmac:ohmic-enable <= plasmac.ohmic-enable", file=file) print("net plasmac:scribe-arm <= plasmac.scribe-arm", file=file) print("net plasmac:scribe-on <= plasmac.scribe-on", file=file) # if encoder required for thcad if self.d.thcadenc: print("\n# ---ARC VOLTAGE ENCODER---", file=file) print("{}loadrt encoder num_chan=1".format(comment), file=file) print("{}addf encoder.update-counters base-thread".format(comment), file=file) print("{}addf encoder.capture-position servo-thread".format(comment), file=file) print("{}setp encoder.0.counter-mode 1".format(comment), file=file) print("{}setp encoder.0.position-scale 1".format(comment), file=file) print("{}net plasmac:arc-voltage-raw encoder.0.phase-A".format(comment), file=file) print("{}net plasmac:arc-voltage-in encoder.0.velocity".format(comment), file=file) # if ohmic sensing contact if self.d.ohmiccontact: print("\n{}net plasmac:ohmic-probe <= plasmac.ohmic-sense-out".format(comment), file=file) # add custom.hal if not existing chfilename = os.path.join(base, "custom.hal") if os.path.exists(chfilename): # else make a file with new values chfilename = os.path.join(base, "custom.hal.new_values") f1 = open(chfilename, "w") print(_("# Include your custom HAL commands here"), file=f1) print(_("# This file will not be overwritten when you run stepconf again"), file=f1) print("\n# for the float and ohmic inputs each increment in delay is", file=f1) print("# is a 0.001mm (0.00004\") increase in any probed height result", file=f1) print("setp db_float.delay 5", file=f1) if self.d.thcadenc: print("# set to zero if using internal ohmic sensing", file=f1) print("setp db_ohmic.delay 0", file=f1) else: print("setp db_ohmic.delay 5", file=f1) print("setp db_breakaway.delay 5", file=f1) print("setp db_arc-ok.delay 5", file=f1) # if ohmic sensing contact if self.d.ohmiccontact: print("\n# ---OHMIC SENSE CONTACT DEBOUNCE---", file=f1) print("setp plasmac.ohmic-sense-off-delay 3", file=f1) print("setp plasmac.ohmic-sense-on-delay 3", file=f1) # hints for fine tuning print(_("\n\n\n########################################"), file=f1) print(_("# The following variables are available for fine tuning some parameters."), file=f1) print(_("# To use any of these, uncomment the required setp line and set an appropriate value.\n"), file=f1) print(_("# Dampen excessive noise on the arc voltage input"), file=f1) print(_("# default = 0 (volts)"), file=f1) print("#setp plasmac.lowpass-frequency 0\n", file=f1) print(_("# The time delay from losing the arc ok signal until QtPlasmaC reacts to the arc loss."), file=f1) print(_("# default = 0.0 (seconds)"), file=f1) print("#setp plasmac.arc-lost-delay 0.0\n", file=f1) print(_("# For mode 0 Arc-OK only, the number of consecutive readings within the threshold that are required to set the Arc-OK signal."), file=f1) print(_("# default = 6"), file=f1) print("#setp plasmac.arc-ok-counts 6\n", file=f1) print(_("# For mode 0 Arc-OK only, the maximum voltage deviation that is allowed for a valid voltage to set the Arc OK signal."), file=f1) print(_("# default = 10 (volts)"), file=f1) print("#setp plasmac.arc-ok-threshold 10\n", file=f1) print(_("# The voltage above and below 0V that will display as 0V. Prevents small fluctuations from flickering the voltage display."), file=f1) print(_("# default = 0 (volts)"), file=f1) print("#setp plasmac.zero-window 0\n", file=f1) print(_("# The distance (in millimeters) away from the Z MAX_LIMIT that QtPlasmaC will allow the Z axis to travel while under machine control."), file=f1) print(_("# default = 5 (mm)"), file=f1) print("#setp plasmac.max-offset 5\n", file=f1) print(_("# The required number of consecutive times that the threshold has been exceeded before applying the void lock to the THC."), file=f1) print(_("# default = 2"), file=f1) print("#setp plasmac.kerf-error-max 2\n", file=f1) f1.close() # add custom_postgui.hal if not existing custom = os.path.join(base, "custom_postgui.hal") if not os.path.exists(custom): f1 = open(custom, "w") print(_("# Include your custom_postgui HAL commands here"), file=f1) print(_("# This file will not be overwritten when you run stepconf again"), file=f1) print(file=f1) f1.close() self.d.qtplasmacvscale = 1 self.d.qtplasmacvoffset = 0 # if using thcad for arc voltage and not a sim config if self.d.thcadenc & 1 and not self.d.sim_hardware: if self.d.voltsrdiv < 150: dratio = self.d.voltsrdiv else: dratio = (self.d.voltsrdiv + 100000) / 100000 if self.d.voltsmodel.startswith("2"): if "(W1 down)" in self.d.voltsmodel: thcadvolts = 5 else: thcadvolts = 10 else: thcadvolts = int(self.d.voltsmodel) self.d.qtplasmacvscale = dratio / (((self.d.voltsfullf - self.d.voltszerof) * 1000) / int(self.d.voltsfjumper) / thcadvolts) self.d.qtplasmacvoffset = self.d.voltszerof * 1000 / int(self.d.voltsfjumper) # qtplasmac has a shutdown.hal sdfilename = os.path.join(base, "shutdown.hal") f1 = open(sdfilename, "w") print("# Include your shutdown HAL commands here", file=f1) print("# This file will not be overwritten when you run stepconf again", file=f1) f1.close() # if a sim config if self.d.sim_hardware: # make a sim_postgui.hal file spfilename = os.path.join(base, "sim_postgui.hal") f1 = open(spfilename, "w") print("# QTPLASMAC SIMULATOR PANEL", file=f1) print("\n# load the simulated torch", file=f1) print("loadusr -Wn sim-torch sim-torch", file=f1) print("\n# load the sim GUI", file=f1) print("loadusr -Wn qtplasmac_sim qtvcp qtplasmac_sim.ui", file=f1) print("\n# connect to existing plasmac connections", file=f1) print("net plasmac:torch-on => qtplasmac_sim.torch_on sim-torch.start", file=f1) print("net plasmac:cut-volts => sim-torch.voltage-in", file=f1) print("\n# create new sim connections", file=f1) print("net sim:arc-ok qtplasmac_sim.arc_ok => db_arc-ok.in", file=f1) print("net sim:arc-voltage-in sim-torch.voltage-out => plasmac.arc-voltage-in", file=f1) print("net sim:arc_voltage_offset qtplasmac_sim.arc_voltage_offset-f => sim-torch.offset-in", file=f1) print("net sim:breakaway qtplasmac_sim.sensor_breakaway => db_breakaway.in", file=f1) print("net sim:float qtplasmac_sim.sensor_float => db_float.in", file=f1) print("net sim:move-down qtplasmac_sim.move_down => plasmac.move-down", file=f1) print("net sim:move-up qtplasmac_sim.move_up => plasmac.move-up", file=f1) print("net sim:ohmic qtplasmac_sim.sensor_ohmic => db_ohmic.in", file=f1) f1.close() def qtplasmac_prefs(self, prefsfile): def putPrefs(prefs, file, section, option, value): if prefs.has_section(section): prefs.set(section, option, str(value)) prefs.write(open(prefsfile, "w")) else: prefs.add_section(section) prefs.set(section, option, str(value)) prefs.write(open(prefsfile, "w")) from configparser import RawConfigParser prefs = RawConfigParser() prefs.optionxform = str putPrefs(prefs, prefsfile, 'PLASMA_PARAMETERS', 'Arc Voltage Offset', '{:.3f}'.format(self.d.qtplasmacvoffset)) putPrefs(prefs, prefsfile, 'PLASMA_PARAMETERS', 'Arc Voltage Scale', '{:.6f}'.format(self.d.qtplasmacvscale)) putPrefs(prefs, prefsfile, 'GUI_OPTIONS', 'Mode', self.d.qtplasmacmode) putPrefs(prefs, prefsfile, 'GUI_OPTIONS', 'Estop type', self.d.qtplasmacestop) dro = 'top' if self.d.qtplasmacdro else 'bottom' putPrefs(prefs, prefsfile, 'GUI_OPTIONS', 'DRO position', dro) putPrefs(prefs, prefsfile, 'GUI_OPTIONS', 'Flash error', self.d.qtplasmacerror) putPrefs(prefs, prefsfile, 'GUI_OPTIONS', 'Hide run', self.d.qtplasmacstart) putPrefs(prefs, prefsfile, 'GUI_OPTIONS', 'Hide pause', self.d.qtplasmacpause) putPrefs(prefs, prefsfile, 'GUI_OPTIONS', 'Hide abort', self.d.qtplasmacstop) for ub in range(1, 21): putPrefs(prefs, prefsfile, 'BUTTONS', '{} Name'.format(ub), self.d.qtplasmac_bnames[ub-1]) putPrefs(prefs, prefsfile, 'BUTTONS', '{} Code'.format(ub), self.d.qtplasmac_bcodes[ub-1]) putPrefs(prefs, prefsfile, 'POWERMAX', 'Port', self.d.qtplasmacpmx) # Boiler code def __getitem__(self, item): return getattr(self, item) def __setitem__(self, item, value): return setattr(self, item, value)