import os import time class INI: def __init__(self,app): # access to: self.d = app.d # collected data global SIG SIG = app._p # private data (signals names) global _PD _PD = app._p # private data self.a = app # The parent, pncconf def write_inifile(self, base): filename = os.path.join(base, self.d.machinename + ".ini") file = open(filename, "w") print(_("# Generated by PNCconf at %s") % time.asctime(), file=file) print(_("# Using LinuxCNC version: %s") % self.d.linuxcnc_version, file=file) print(_("# If you make changes to this file, they will be"), file=file) print(_("# overwritten when you run PNCconf again"), file=file) print(file=file) print("[EMC]", file=file) print("MACHINE = %s" % self.d.machinename, file=file) print("DEBUG = 0", file=file) # the joints_axes conversion script named 'update_ini' # will try to update for joints_axes if no VERSION is set print("VERSION = 1.1", file=file) print(file=file) print("[DISPLAY]", file=file) if self.d.frontend == _PD._AXIS: print("DISPLAY = axis", file=file) elif self.d.frontend == _PD._TKLINUXCNC: print("DISPLAY = tklinuxcnc", file=file) elif self.d.frontend == _PD._GMOCCAPY: print("DISPLAY = gmoccapy", file=file) elif self.d.frontend == _PD._TOUCHY: print("DISPLAY = touchy", file=file) elif self.d.frontend == _PD._QTDRAGON: print("DISPLAY = qtvcp qtdragon", file=file) print("PREFERENCE_FILE_PATH = WORKINGFOLDER/qtdragon.pref", file=file) # qtplasmac has multiple screens elif self.d.frontend == _PD._QTPLASMAC: if self.d.qtplasmacscreen == 2: screen = "qtplasmac_9x16" elif self.d.qtplasmacscreen == 1: screen = "qtplasmac_4x3" else: screen = "qtplasmac" print("DISPLAY = qtvcp {}".format(screen), file=file) if self.d.gladevcp: theme = self.d.gladevcptheme if theme == "Follow System Theme":theme = "" else: theme = " -t "+theme if self.d.frontend in(_PD._AXIS, _PD._TOUCHY): if self.d.centerembededgvcp: print("EMBED_TAB_NAME = GladeVCP", file=file) print("EMBED_TAB_COMMAND = halcmd loadusr -Wn gladevcp gladevcp -c gladevcp%s -H gvcp_call_list.hal -x {XID} gvcp-panel.ui"%(theme), file=file) elif self.d.sideembededgvcp: print("GLADEVCP =%s -H gvcp_call_list.hal gvcp-panel.ui"%(theme), file=file) elif self.d.frontend == _PD._GMOCCAPY: if self.d.centerembededgvcp: print("EMBED_TAB_NAME = Center_panel", file=file) print("EMBED_TAB_LOCATION = ntb_preview", file=file) elif self.d.sideembededgvcp: print("EMBED_TAB_NAME = Right Panel", file=file) print("EMBED_TAB_LOCATION = box_right", file=file) else: print("EMBED_TAB_NAME = User Panel", file=file) print("EMBED_TAB_LOCATION = ntb_user_tabs", file=file) print("EMBED_TAB_COMMAND = gladevcp -c gladevcp %s -H gvcp_call_list.hal -x {XID} gvcp-panel.ui"%(theme), file=file) if self.d.position_offset == 1: temp ="RELATIVE" else: temp = "MACHINE" print("POSITION_OFFSET = %s"% temp, file=file) if self.d.position_feedback == 1: temp ="ACTUAL" else: temp = "COMMANDED" print("POSITION_FEEDBACK = %s"% temp, file=file) print("MAX_FEED_OVERRIDE = %f"% self.d.max_feed_override, file=file) if self.d.frontend == _PD._QTDRAGON: print("MAX_SPINDLE_0_OVERRIDE = %f"% self.d.max_spindle_override, file=file) print("MIN_SPINDLE_0_OVERRIDE = %f"% self.d.min_spindle_override, file=file) print("DEFAULT_SPINDLE_0_SPEED = 500", file=file) print("MIN_SPINDLE_0_SPEED = 100", file=file) print("MAX_SPINDLE_0_SPEED = 2500", file=file) # qtplasmac doesn't use spindle override elif self.d.frontend != _PD._QTPLASMAC: print("MAX_SPINDLE_OVERRIDE = %f"% self.d.max_spindle_override, file=file) print("MIN_SPINDLE_OVERRIDE = %f"% self.d.min_spindle_override, file=file) print("INTRO_GRAPHIC = linuxcnc.gif", file=file) print("INTRO_TIME = 5", file=file) print("PROGRAM_PREFIX = %s" % \ os.path.expanduser("~/linuxcnc/nc_files"), file=file) if self.d.pyvcp: print("PYVCP = pyvcp-panel.xml", file=file) # these are for AXIS GUI and QtPlasmaC if self.d.units == _PD._METRIC: if self.d.frontend == _PD._QTPLASMAC: print("INCREMENTS = %s"% self.d.increments_metric_qtplasmac, file=file) else: print("INCREMENTS = %s"% self.d.increments_metric, file=file) else: if self.d.frontend == _PD._QTPLASMAC: print("INCREMENTS = %s"% self.d.increments_imperial_qtplasmac, file=file) else: print("INCREMENTS = %s"% self.d.increments_imperial, file=file) if self.d.axes == 2: print("LATHE = 1", file=file) print("POSITION_FEEDBACK = %s"% temp, file=file) print("DEFAULT_LINEAR_VELOCITY = %f"% self.d.default_linear_velocity, file=file) print("MAX_LINEAR_VELOCITY = %f"% self.d.max_linear_velocity, file=file) print("MIN_LINEAR_VELOCITY = %f"% self.d.min_linear_velocity, file=file) print("DEFAULT_ANGULAR_VELOCITY = %f"% self.d.default_angular_velocity, file=file) print("MAX_ANGULAR_VELOCITY = %f"% self.d.max_angular_velocity, file=file) print("MIN_ANGULAR_VELOCITY = %f"% self.d.min_angular_velocity, file=file) # qtplasmac has internal editor if self.d.frontend != _PD._QTPLASMAC: print("EDITOR = %s"% self.d.editor, file=file) print("GEOMETRY = %s"% self.d.geometry, file=file) print("CYCLE_TIME = 100", file=file) # set up MDI macro buttons if self.d.frontend == _PD._QTDRAGON: print(file=file) print("[MDI_COMMAND_LIST]", file=file) print("MDI_COMMAND = G0 Z0;X0 Y0", file=file) print("MDI_COMMAND = G53 G0 Z0;G53 G0 X0 Y0", file=file) print(file=file) print("[FILTER]", file=file) # qtplasmac has a different filter section if self.d.frontend == _PD._QTPLASMAC: print("PROGRAM_EXTENSION = .ngc,.nc,.tap GCode File (*.ngc, *.nc, *.tap)", file=file) print("ngc = qtplasmac_gcode", file=file) print("nc = qtplasmac_gcode", file=file) print("tap = qtplasmac_gcode", file=file) else: print("PROGRAM_EXTENSION = .png,.gif,.jpg Greyscale Depth Image", file=file) print("PROGRAM_EXTENSION = .py Python Script", file=file) print("png = image-to-gcode", file=file) print("gif = image-to-gcode", file=file) print("jpg = image-to-gcode", file=file) print("py = python", file=file) print(file=file) print("[TASK]", file=file) print("TASK = milltask", file=file) print("CYCLE_TIME = 0.010", file=file) print(file=file) print("[RS274NGC]", file=file) print("PARAMETER_FILE = linuxcnc.var", file=file) # qtplasmac has extra rs274ngc variables if self.d.frontend == _PD._QTPLASMAC: code = 21 if self.d.units == _PD._METRIC else 20 print("RS274NGC_STARTUP_CODE = G{} G40 G49 G80 G90 G92.1 G94 G97 M52P1".format(code), file=file) print("SUBROUTINE_PATH = ./:../../nc_files", file=file) print("USER_M_PATH = ./:../../nc_files", file=file) print("", file=file) else: if self.d.units == _PD._METRIC: unit = 21 p = .025 else: unit = 20 p =.001 print ("RS274NGC_STARTUP_CODE = G{} G40 G90 G94 G97 G64 P{}".format(unit,p), file=file) #base_period = self.d.ideal_period() print(file=file) print("[EMCMOT]", file=file) print("EMCMOT = motmod", file=file) print("COMM_TIMEOUT = 1.0", file=file) #print("BASE_PERIOD = %d" % self.d.baseperiod, file=file) print("SERVO_PERIOD = %d" % self.d.servoperiod, file=file) print(file=file) print("[HMOT]", file=file) if not self.d.useinisubstitution: print(_("# **** This is for info only ****"), file=file) print("CARD0=hm2_%s.0"% self.d.mesa0_currentfirmwaredata[_PD._BOARDNAME], file=file) if self.d.number_mesa == 2: for boardnum in range(0,int(self.d.number_mesa)): if boardnum == 1 and (self.d.mesa0_currentfirmwaredata[_PD._BOARDNAME] == self.d.mesa1_currentfirmwaredata[_PD._BOARDNAME]): halnum = 1 else: halnum = 0 print(file, "CARD1=hm2_%s.%d"% (self.d.mesa1_currentfirmwaredata[_PD._BOARDNAME], halnum), file=file) if self.d._substitution_list: print(_("# These are to ease setting custom component's parameters in a custom HAL file"), file=file) print(file=file) for i,temp in enumerate(self.d._substitution_list): a,b = self.d._substitution_list[i] if a =="": print(file=file) else: print("%s=%s"%(a,b), file=file) print(file=file) print("[HAL]", file=file) print("HALUI = halui", file=file) print("HALFILE = %s.hal" % self.d.machinename, file=file) # qtplasmac requires the qtplasmac_comp file to connect the plasmac component if self.d.frontend == _PD._QTPLASMAC: print("HALFILE = qtplasmac_comp.hal", file=file) print("HALFILE = custom.hal", file=file) if self.d.pyvcp and self.d.pyvcphaltype == 1 and self.d.pyvcpconnect: print("POSTGUI_HALFILE = pyvcp_options.hal", file=file) if self.d.serial_vfd: if self.d.gs2_vfd: print("POSTGUI_HALFILE = gs2_vfd.hal", file=file) if self.d.mitsub_vfd: print("POSTGUI_HALFILE = mitsub_vfd.hal", file=file) if self.d.toolchangeprompt: if self.d.frontend == _PD._QTDRAGON: print("POSTGUI_HALFILE = qtvcp_postgui.hal", file=file) elif self.d.frontend == _PD._GMOCCAPY: print("POSTGUI_HALFILE = gmoccapy_postgui.hal", file=file) print("POSTGUI_HALFILE = custom_postgui.hal", file=file) print("SHUTDOWN = shutdown.hal", file=file) print(file=file) print("[HALUI]", file=file) if self.d.halui == True: for i in range(0,15): cmd =self.d["halui_cmd" + str(i)] if cmd =="": break print("MDI_COMMAND = %s"% cmd, file=file) # Build axis/joints info # add X axis num_joints = 1; coords = "X"; tandemflag = False tandemjoint = self.a.tandem_check('x') print('tandem:',tandemjoint) if tandemjoint: #add tandem to X tandemflag = True num_joints += 1 coords += 'X' if self.d.axes in (0,1): # xyz or xyyza # add Y axis num_joints += 1 coords += 'Y' tandemjoint = self.a.tandem_check('y') if tandemjoint: #add tandem to Y tandemflag = True num_joints += 1 coords += 'Y' # add Z axis num_joints += 1 coords += 'Z' tandemjoint = self.a.tandem_check('z') if tandemjoint: tandemflag = True num_joints += 1 coords += 'Z' if self.d.axes == 1: # for xyza # add A axis num_joints += 1 coords += 'A' tandemjoint = self.a.tandem_check('a') if tandemjoint: # add tandem to A tandemflag = True num_joints += 1 coords += 'A' if not self.d.axes in (0,1,2): print("___________________unknown self.d.axes",self.d.axes) return print(file=file) print("[KINS]", file=file) # trivial kinematics: no. of joints == no.of axes) # with trivkins, axes do not have to be consecutive print("JOINTS = %d"%num_joints, file=file) if tandemflag and self.d.frontend != _PD._QTPLASMAC: print("KINEMATICS = trivkins coordinates=%s kinstype=BOTH"%coords.replace(" ",""), file=file) else: print("KINEMATICS = trivkins coordinates=%s"%coords.replace(" ",""), file=file) print(file=file) print("[TRAJ]", file=file) # qtplasmac requires 3 spindles if self.d.frontend == _PD._QTPLASMAC: print("SPINDLES = 3", file=file) print("COORDINATES = ",coords, file=file) if self.d.axes == 1: print("MAX_ANGULAR_VELOCITY = %.2f" % self.d.amaxvel, file=file) defvel = min(60, self.d.amaxvel/10.) print("DEFAULT_ANGULAR_VELOCITY = %.2f" % defvel, file=file) if self.d.units == _PD._METRIC: print("LINEAR_UNITS = mm", file=file) else: print("LINEAR_UNITS = inch", file=file) print("ANGULAR_UNITS = degree", file=file) if self.d.axes == 2: maxvel = max(self.d.xmaxvel, self.d.zmaxvel) else: maxvel = max(self.d.xmaxvel, self.d.ymaxvel, self.d.zmaxvel) hypotvel = (self.d.xmaxvel**2 + self.d.ymaxvel**2 + self.d.zmaxvel**2) **.5 defvel = min(maxvel, max(.1, maxvel/10.)) print("DEFAULT_LINEAR_VELOCITY = %.2f" % defvel, file=file) print("MAX_LINEAR_VELOCITY = %.2f" % maxvel, file=file) if self.d.restore_joint_position: print("POSITION_FILE = position.txt", file=file) if not self.d.require_homing: print("NO_FORCE_HOMING = 1", file=file) print(file=file) print("[EMCIO]", file=file) print("EMCIO = io", file=file) print("CYCLE_TIME = 0.100", file=file) print("TOOL_TABLE = tool.tbl", file=file) # qtplasmac doesn't require these if self.d.frontend != _PD._QTPLASMAC: if self.d.allow_spindle_on_toolchange: print("TOOL_CHANGE_WITH_SPINDLE_ON = 1", file=file) if self.d.raise_z_on_toolchange: print("TOOL_CHANGE_QUILL_UP = 1", file=file) if self.d.random_toolchanger: print("RANDOM_TOOLCHANGER = 1", file=file) all_homes = bool(self.a.home_sig("x") and self.a.home_sig("z")) if self.d.axes in (0,1): all_homes = bool(all_homes and self.a.home_sig("y")) # A axis usually doesn't have home switches #if self.d.axes == 1: all_homes = all_homes and self.a.home_sig("a") ############################################################## # build axis/joint info ############################################################## # self.d.axes: # 0 = xyz # 1 = xz # 2 = xyza # todo: simplify hardcoding for trivkins sequential joint no.s jnum = 0 # Always X AXIS self.write_one_axis(file, 'x') tandemjoint = self.a.tandem_check('x') self.write_one_joint(file, 0, "x", "LINEAR", all_homes, tandemjoint) if tandemjoint: jnum += 1 self.write_one_joint(file, jnum, "x", "LINEAR", all_homes, True, ismain = False) jnum += 1 print("#******************************************", file=file) # Maybe add Y AXIS if self.d.axes in(0,1): # xyz or xyza self.write_one_axis(file, 'y') tandemjoint = self.a.tandem_check('y') self.write_one_joint(file, jnum, "y", "LINEAR", all_homes, tandemjoint) if tandemjoint: jnum += 1 self.write_one_joint(file, jnum, "y", "LINEAR", all_homes, True, ismain = False) jnum += 1 print("#******************************************", file=file) # Always add Z AXIS self.write_one_axis(file, 'z') tandemjoint = self.a.tandem_check('z') self.write_one_joint(file, jnum, "z", "LINEAR", all_homes, tandemjoint) if tandemjoint: jnum += 1 self.write_one_joint(file, jnum, "z", "LINEAR", all_homes, True, ismain = False) jnum += 1 print("#******************************************", file=file) # Maybe add A AXIS if self.d.axes == 1: # xyza self.write_one_axis(file, 'a') tandemjoint = self.a.tandem_check('a') self.write_one_joint(file, jnum, "a", "ANGULAR", all_homes, tandemjoint) if tandemjoint: jnum += 1 self.write_one_joint(file, jnum, "a", "ANGULAR", all_homes, True, ismain = False) jnum += 1 print("#******************************************", file=file) # usually add SPINDLE except for qtplasmac if self.d.include_spindle and self.d.frontend != _PD._QTPLASMAC: self.write_one_joint(file, 9, "s", "null", all_homes, False) file.close() self.d.add_md5sum(filename) def write_one_joint(self, file, num, letter, type, all_homes, tandemflag, ismain = True): def get(s): return self.d[letter + s] pwmgen = self.a.pwmgen_sig(letter) tppwmgen = self.a.tppwmgen_sig(letter) stepgen = self.a.stepgen_sig(letter) encoder = self.a.encoder_sig(letter) resolver = self.a.resolver_sig(letter) potoutput = self.a.potoutput_sig(letter) closedloop = False if stepgen and (encoder or resolver): closedloop = True if (encoder or resolver) and (pwmgen or tppwmgen) : closedloop = True if closedloop and letter == "s": closedloop = False #print("INI ",letter + " is closedloop? "+ str(closedloop),encoder,pwmgen,tppwmgen,stepgen) print(file=file) if letter == 's': print("[SPINDLE_%d]" % 0, file=file) else: print("[JOINT_%d]" % num, file=file) print("TYPE = %s" % type, file=file) print("HOME = %s" % get("homepos"), file=file) print("FERROR = %s"% get("maxferror"), file=file) print("MIN_FERROR = %s" % get("minferror"), file=file) if not letter == "s" or (letter == "s" and stepgen): print("MAX_VELOCITY = %s" % get("maxvel"), file=file) print("MAX_ACCELERATION = %s" % get("maxacc"), file=file) if stepgen: print(_("# The values below should be 25% larger than MAX_VELOCITY and MAX_ACCELERATION"), file=file) print(_("# If using BACKLASH compensation STEPGEN_MAXACCEL should be 100% larger."), file=file) if get("usecomp") or get("usebacklash"): factor = 2.0 else: factor = 1.25 print("STEPGEN_MAXVEL = %.2f" % (float(get("maxvel")) * factor), file=file) print("STEPGEN_MAXACCEL = %.2f" % (float(get("maxacc")) * factor), file=file) print("P = %s" % get("P"), file=file) print("I = %s" % get("I"), file=file) print("D = %s" % get("D"), file=file) print("FF0 = %s" % get("FF0"), file=file) print("FF1 = %s" % get("FF1"), file=file) print("FF2 = %s" % get("FF2"), file=file) print("BIAS = %s"% get("bias"), file=file) print("DEADBAND = %s"% get("deadband"), file=file) print("MAX_OUTPUT = %s" % get("maxoutput"), file=file) if encoder or resolver: if get("invertencoder"): temp = -1 else: temp = 1 if encoder: print("ENCODER_SCALE = %s" % (get("encoderscale") * temp), file=file) else: print("RESOLVER_SCALE = %s" % (get("encoderscale") * temp), file=file) if pwmgen or potoutput: if get("invertmotor"): temp = -1 else: temp = 1 if letter == 's': if self.d.susenegativevoltage: min_voltage_factor = -1 else: min_voltage_factor = 0 # dont output negative voltages max_voltage_factor = 10.0/self.d.soutputmaxvoltage # voltagelimit if self.d.suseoutputrange2: scale = 1 # we scale the multiple ranges in the HAL file else: scale = self.d.soutputscale # we scale to the max RPM print("OUTPUT_SCALE = %s"% ( int(max_voltage_factor * scale * temp) ), file=file) print("OUTPUT_MIN_LIMIT = %s"% ( int(min_voltage_factor * scale) ), file=file) print("OUTPUT_MAX_LIMIT = %s"% ( int(scale) ), file=file) print(file=file) else: print("OUTPUT_SCALE = %s" % (get("outputscale") * temp), file=file) pwmpinname = self.a.make_pinname(pwmgen) if (pwmgen and "analog" in pwmpinname) or potoutput: print("OUTPUT_MIN_LIMIT = %s"% (get("outputminlimit")), file=file) print("OUTPUT_MAX_LIMIT = %s"% (get("outputmaxlimit")), file=file) if stepgen: print(_("# these are in nanoseconds"), file=file) print("DIRSETUP = %d"% int(get("dirsetup")), file=file) print("DIRHOLD = %d"% int(get("dirhold")), file=file) print("STEPLEN = %d"% int(get("steptime")), file=file) print("STEPSPACE = %d"% int(get("stepspace")), file=file) if get("invertmotor"): temp = -1 else: temp = 1 print("STEP_SCALE = %s"% (get("stepscale") * temp), file=file) if letter == 's':return if get("usecomp"): print("COMP_FILE = %s" % get("compfilename"), file=file) print("COMP_FILE_TYPE = %s" % get("comptype"), file=file) if get("usebacklash"): print("BACKLASH = %s" % get("backlash"), file=file) minlim, maxlim = self.find_limits(letter) print("MIN_LIMIT = %s" % minlim, file=file) print("MAX_LIMIT = %s" % maxlim, file=file) thisaxishome = set(("all-limit-home", "all-home", "home-" + letter, "min-home-" + letter, "max-home-" + letter, "both-home-" + letter)) ignore = set(("min-home-" + letter, "max-home-" + letter, "both-home-" + letter, "all-limit-home")) share = set(("all-limit-home", "all-home")) homes = False for i in thisaxishome: if self.a.findsignal(i): homes = True # set homing speeds and directions # search direction : True = positive direction # latch direction : True = opposite direction if homes: searchvel = abs(get("homesearchvel")) latchvel = abs(get("homelatchvel")) #print(get("searchdir")) if get("searchdir") == 0: searchvel = -searchvel if get("latchdir") == 0: latchvel = -latchvel else: if get("latchdir") == 1: latchvel = -latchvel if ismain: print("HOME_OFFSET = %f" % get("homesw"), file=file) else: print("HOME_OFFSET = %f" % get("hometandemsw"), file=file) print("HOME_SEARCH_VEL = %f" % searchvel, file=file) print("HOME_LATCH_VEL = %f" % latchvel, file=file) print("HOME_FINAL_VEL = %f" % get("homefinalvel"), file=file) if get("usehomeindex"):useindex = "YES" else: useindex = "NO" print("HOME_USE_INDEX = %s" % useindex, file=file) for i in ignore: if self.a.findsignal(i): print("HOME_IGNORE_LIMITS = YES", file=file) break for i in share: if self.a.findsignal(i): print("HOME_IS_SHARED = 1", file=file) break else: print("HOME_OFFSET = %s" % get("homepos"), file=file) # if user doesn't request manual individual homing, add the sequence number: if not self.d.individual_homing: seqnum = int(get("homesequence")) # if a tandem joint we wish to finish the home sequence together if tandemflag: wait ='-' else: wait = '' print("HOME_SEQUENCE = %s%d" % (wait,seqnum), file=file) def write_one_axis(self, file, letter): # For KINEMATICS_IDENTITY: # use axis MIN,MAX values identical corresponding joint values def get(s): return self.d[letter + s] minlim, maxlim = self.find_limits(letter) if not letter == "s": axis_letter = letter.upper() print(file=file) print("#******************************************", file=file) print("[AXIS_%s]" % axis_letter, file=file) # qtplasmac requires double vel & acc to use eoffsets correctly if self.d.frontend == _PD._QTPLASMAC: print(_("# MAX_VEL & MAX_ACC need to be twice the corresponding joint value"), file=file) print("MAX_VELOCITY = %s" % (get("maxvel") * 2), file=file) print("MAX_ACCELERATION = %s" % (get("maxacc") * 2), file=file) print("OFFSET_AV_RATIO = 0.5", file=file) else: print("MAX_VELOCITY = %s" % get("maxvel"), file=file) print("MAX_ACCELERATION = %s" % get("maxacc"), file=file) print("MIN_LIMIT = %s" % minlim, file=file) print("MAX_LIMIT = %s" % maxlim, file=file) # linuxcnc doesn't like having home right on an end of travel, # so extend the travel limit by up to .001in or .01mm def find_limits(self, letter): def get(s): return self.d[letter + s] minlim = -abs(get("minlim")) maxlim = get("maxlim") home = get("homepos") extend = 0.01 if self.d.units == _PD._METRIC else 0.001 if minlim == home: minlim = minlim - extend elif maxlim == home: maxlim = maxlim + extend return (minlim, maxlim) # BOILER CODE def __getitem__(self, item): return getattr(self, item) def __setitem__(self, item, value): return setattr(self, item, value)