From c1392c1b9cff01e2b7ba8fb7a6c70f12f0494d60 Mon Sep 17 00:00:00 2001 From: uvos Date: Sun, 5 Feb 2023 18:45:48 +0100 Subject: [PATCH] inial commit --- 3040T.hal | 222 +++++++++++++++++++++++++++++++++++++ 3040T.ini | 191 ++++++++++++++++++++++++++++++++ M100 | 6 + M101 | 4 + M102 | 32 ++++++ M103 | 24 ++++ M104 | 12 ++ UVOSBannerLCNC.gif | Bin 0 -> 16939 bytes compensation.py | 232 +++++++++++++++++++++++++++++++++++++++ custom_postgui.hal | 55 ++++++++++ gcodeUtils/3dscann.ngc | 77 +++++++++++++ gcodeUtils/AreaMill.ngc | 29 +++++ gcodeUtils/XCut.ngc | 33 ++++++ gcodeUtils/YCut.ngc | 29 +++++ gcodeUtils/scann.ngc | 36 ++++++ gcodeUtils/scannSync.ngc | 34 ++++++ microscopeTuchoff.ngc | 0 rectangle_probe.ngc | 73 ++++++++++++ testpannel.hal | 1 + tool-change.ngc | 96 ++++++++++++++++ tool-job-begin.ngc | 12 ++ tool.tbl | 15 +++ touchoff-z-to-bead.ngc | 38 +++++++ 23 files changed, 1251 insertions(+) create mode 100644 3040T.hal create mode 100644 3040T.ini create mode 100755 M100 create mode 100755 M101 create mode 100755 M102 create mode 100755 M103 create mode 100755 M104 create mode 100644 UVOSBannerLCNC.gif create mode 100644 compensation.py create mode 100644 custom_postgui.hal create mode 100644 gcodeUtils/3dscann.ngc create mode 100644 gcodeUtils/AreaMill.ngc create mode 100644 gcodeUtils/XCut.ngc create mode 100644 gcodeUtils/YCut.ngc create mode 100644 gcodeUtils/scann.ngc create mode 100644 gcodeUtils/scannSync.ngc create mode 100644 microscopeTuchoff.ngc create mode 100644 rectangle_probe.ngc create mode 100644 testpannel.hal create mode 100644 tool-change.ngc create mode 100644 tool-job-begin.ngc create mode 100644 tool.tbl create mode 100644 touchoff-z-to-bead.ngc diff --git a/3040T.hal b/3040T.hal new file mode 100644 index 0000000..51537c2 --- /dev/null +++ b/3040T.hal @@ -0,0 +1,222 @@ +#Real Time Modules +loadrt [KINS]KINEMATICS +loadrt [EMCMOT]EMCMOT base_period_nsec=[EMCMOT]BASE_PERIOD servo_period_nsec=[EMCMOT]SERVO_PERIOD num_joints=[KINS]JOINTS +loadrt hal_parport cfg="0 out" +setp parport.0.reset-time 5000 +loadrt stepgen step_type=0,0,0,0 ctrl_type=p,p,p,p +loadrt ilowpass count=1 +loadrt timedelay count=2 +loadrt not count=1 +loadrt or2 count=3 +loadrt xor2 count=2 +loadrt mux2 count=1 +loadrt and2 count=1 + +addf parport.0.read base-thread +addf stepgen.make-pulses base-thread +addf parport.0.write base-thread +addf parport.0.reset base-thread + +addf stepgen.capture-position servo-thread +addf motion-command-handler servo-thread +addf motion-controller servo-thread +addf stepgen.update-freq servo-thread + +#Pin Assignments + +net probe-in => motion.probe-input + +net estop-out => parport.0.pin-01-out +net xstep => parport.0.pin-02-out +setp parport.0.pin-02-out-reset 1 +net xdir => parport.0.pin-03-out +net ystep => parport.0.pin-04-out +setp parport.0.pin-04-out-reset 1 +net ydir => parport.0.pin-05-out +net zstep => parport.0.pin-06-out +setp parport.0.pin-06-out-reset 1 +setp parport.0.pin-07-out-invert 1 +net zdir => parport.0.pin-07-out +net astep => parport.0.pin-16-out +net adir => parport.0.pin-17-out + +net estop-ext <= parport.0.pin-10-in +net min-home-x <= parport.0.pin-11-in-not +net min-home-y <= parport.0.pin-12-in-not +net max-home-z <= parport.0.pin-13-in-not +net probe-in <= parport.0.pin-15-in-not + +#X-Axis +setp stepgen.0.position-scale [JOINT_0]SCALE +setp stepgen.0.steplen 1 +setp stepgen.0.stepspace 0 +setp stepgen.0.dirhold 36000 +setp stepgen.0.dirsetup 36000 +setp stepgen.0.maxaccel [JOINT_0]STEPGEN_MAXACCEL +net xpos-cmd joint.0.motor-pos-cmd => stepgen.0.position-cmd +net xpos-fb stepgen.0.position-fb => joint.0.motor-pos-fb +net xstep <= stepgen.0.step +net xdir <= stepgen.0.dir +net xenable joint.0.amp-enable-out => stepgen.0.enable +net min-home-x => joint.0.home-sw-in +net min-home-x => joint.0.neg-lim-sw-in + +#Y-Axis +setp stepgen.1.position-scale [JOINT_1]SCALE +setp stepgen.1.steplen 1 +setp stepgen.1.stepspace 0 +setp stepgen.1.dirhold 36000 +setp stepgen.1.dirsetup 36000 +setp stepgen.1.maxaccel [JOINT_1]STEPGEN_MAXACCEL +net ypos-cmd joint.1.motor-pos-cmd => stepgen.1.position-cmd +net ypos-fb stepgen.1.position-fb => joint.1.motor-pos-fb +net ystep <= stepgen.1.step +net ydir <= stepgen.1.dir +net yenable joint.1.amp-enable-out => stepgen.1.enable +net min-home-y => joint.1.home-sw-in +net min-home-y => joint.1.neg-lim-sw-in + +#Z-Axis +setp stepgen.2.position-scale [JOINT_2]SCALE +setp stepgen.2.steplen 1 +setp stepgen.2.stepspace 0 +setp stepgen.2.dirhold 36000 +setp stepgen.2.dirsetup 36000 +setp stepgen.2.maxaccel [JOINT_2]STEPGEN_MAXACCEL +net zpos-cmd joint.2.motor-pos-cmd => stepgen.2.position-cmd +net zpos-fb stepgen.2.position-fb => joint.2.motor-pos-fb +net zstep <= stepgen.2.step +net zdir <= stepgen.2.dir +net zenable joint.2.amp-enable-out => stepgen.2.enable +net max-home-z => joint.2.home-sw-in +net max-home-z => joint.2.pos-lim-sw-in + +#A-Axis +setp stepgen.3.position-scale [JOINT_3]SCALE +setp stepgen.3.steplen 1 +setp stepgen.3.stepspace 0 +setp stepgen.3.dirhold 36000 +setp stepgen.3.dirsetup 36000 +setp stepgen.3.maxaccel [JOINT_3]STEPGEN_MAXACCEL +net astep <= stepgen.3.step +net adir <= stepgen.3.dir +net aenable joint.3.amp-enable-out => stepgen.3.enable +#net apos-cmd joint.3.motor-pos-cmd => stepgen.3.position-cmd +#net apos-fb stepgen.3.position-fb => joint.3.motor-pos-fb + +net estop-out <= iocontrol.0.user-enable-out +net estop-ext => iocontrol.0.emc-enable-in + +#Toolchanger +loadusr -W hal_manualtoolchange +net tool-change iocontrol.0.tool-change => hal_manualtoolchange.change +net tool-changed iocontrol.0.tool-changed <= hal_manualtoolchange.changed +net tool-number iocontrol.0.tool-prep-number => hal_manualtoolchange.number +net tool-prepare-loopback iocontrol.0.tool-prepare => iocontrol.0.tool-prepared + +#CNCExtension +loadusr -Wn cncextension ./cncextension + +#Amp enable +net amp-enable halui.machine.is-on => cncextension.wr0 + +#Spindle Control + +addf mux2.0 servo-thread +net spindle-select => mux2.0.sel +sets spindle-select 1 +net spindle-cmd-rpm <= spindle.0.speed-out +net spindle-cmd-rpm => mux2.0.in0 +setp mux2.0.in1 0 +net spindle-axis-rpm mux2.0.out => stepgen.3.velocity-cmd + +addf and2.0 servo-thread +net spindle-fwd-and spindle.0.forward => and2.0.in0 +net spindle-select => and2.0.in1 +net spindle-main-fwd <= and2.0.out +net spindle-main-fwd => cncextension.ch0 +net spindle-main-fwd => cncextension.opt + +addf timedelay.0 servo-thread +setp timedelay.0.on-delay 10 +setp timedelay.0.off-delay 0 +net spindle-main-fwd => timedelay.0.in +net spindle-on-timer <= timedelay.0.out + +addf timedelay.1 servo-thread +setp timedelay.1.on-delay 10 +setp timedelay.1.off-delay 0 +net spindle-main-break spindle.0.brake => timedelay.1.in +net spindle-off-timer <= timedelay.1.out + +addf or2.0 servo-thread +net spindle-on-timer => or2.0.in0 +net spindle-off-timer => or2.0.in1 +net spindle-main-inhibit <= or2.0.out + +addf or2.1 servo-thread +addf not.0 servo-thread +net spindle-select => not.0.in +net spindle-select-neg <= not.0.out +net spindle-select-neg => or2.1.in0 +net spindle-main-inhibit => or2.1.in1 +net spindle-inhibit <= or2.1.out +net spindle-inhibit => spindle.0.at-speed + +net spindle-position <= stepgen.3.position-fb +net spindle-position => spindle.0.revs +net spindle-axis-rpm => spindle.0.speed-in +net spindle-index-enable encoder.3.index-enable <=> spindle.0.index-enable + +#MPG +setp joint.0.jog-vel-mode 0 +setp axis.x.jog-vel-mode 0 + +setp joint.1.jog-vel-mode 0 +setp axis.y.jog-vel-mode 0 + +setp joint.2.jog-vel-mode 0 +setp axis.z.jog-vel-mode 0 + +setp joint.0.jog-scale 0.0001 +setp axis.x.jog-scale 0.0001 + +setp joint.1.jog-scale 0.0001 +setp axis.y.jog-scale 0.0001 + +setp joint.2.jog-scale 0.00005 +setp axis.z.jog-scale 0.00005 + +net x_enable cncextension.xEnable => axis.x.jog-enable +net y_enable cncextension.yEnable => axis.y.jog-enable +net z_enable cncextension.zEnable => axis.z.jog-enable +net raw-encoder-counts <= cncextension.encoder + +addf ilowpass.0 servo-thread +setp ilowpass.0.scale 1024 +setp ilowpass.0.gain 0.01 +net raw-encoder-counts => ilowpass.0.in +net encoder-counts <= ilowpass.0.out + +net encoder-counts => axis.x.jog-counts +net encoder-counts => axis.y.jog-counts +net encoder-counts => axis.z.jog-counts + +#Collisionavoid +loadusr -Wn collisionavoid ./collisionavoid + +net xpos-abs-cmd joint.0.pos-cmd => collisionavoid.xaxis +net ypos-abs-cmd joint.1.pos-cmd => collisionavoid.yaxis +net zpos-abs-cmd joint.2.pos-cmd => collisionavoid.zaxis +net collision-stop collisionavoid.stop => halui.estop.activate + +#Autolevel +loadusr -Wn compensation python2 compensation.py probe-results.txt cubic +net xpos-abs-cmd => compensation.x-pos +net ypos-abs-cmd => compensation.y-pos +net zpos-abs-cmd => compensation.z-pos +net eoffset-enable compensation.enable-out => axis.z.eoffset-enable +net eoffset-scale compensation.scale => axis.z.eoffset-scale +net eoffset-counts compensation.counts => axis.z.eoffset-counts +net eoffset-clear compensation.clear => axis.z.eoffset-clear + diff --git a/3040T.ini b/3040T.ini new file mode 100644 index 0000000..772a387 --- /dev/null +++ b/3040T.ini @@ -0,0 +1,191 @@ +# This config file was created 2021-03-12 16:42:08.823352 by the update_ini script +# The original config files may be found in the /home/philipp/linuxcnc/configs/3040T/3040T.old directory + +[EMC] +# The version string for this INI file. +VERSION = 1.1 + +MACHINE = 3040T +DEBUG = 0 + +[DISPLAY] +DISPLAY = axis +EDITOR = gedit +POSITION_OFFSET = RELATIVE +POSITION_FEEDBACK = ACTUAL +ARCDIVISION = 64 +GRIDS = 10mm 20mm 50mm 100mm +MAX_FEED_OVERRIDE = 2 +MIN_SPINDLE_OVERRIDE = 0.5 +MAX_SPINDLE_OVERRIDE = 1.2 +DEFAULT_LINEAR_VELOCITY = 6.00 +MIN_LINEAR_VELOCITY = 0 +MAX_LINEAR_VELOCITY = 55 +DEFAULT_ANGULAR_VELOCITY = 40 +MAX_ANGULAR_VELOCITY = 250 +INTRO_GRAPHIC = ./UVOSBannerLCNC.gif +INTRO_TIME = 5 +INCREMENTS = 5mm 1mm .5mm .1mm .05mm .01mm .005mm +PYVCP=pannel.xml +OPEN_FILE ="" +PROGRAM_PREFIX=/home/philipp/linuxcnc/nc_files + + +[FILTER] +PROGRAM_EXTENSION = .png,.gif,.jpg Greyscale Depth Image +PROGRAM_EXTENSION = .py Python Script +png = image-to-gcode +gif = image-to-gcode +jpg = image-to-gcode +py = python + +[RS274NGC] +PARAMETER_FILE = linuxcnc.var +SUBROUTINE_PATH = /home/philipp/linuxcnc/configs/3040T +USER_M_PATH = /home/philipp/linuxcnc/configs/3040T +RS274NGC_STARTUP_CODE =G64 P0.1 s2400 +REMAP=M6 modalgroup=6 ngc=tool-change +REMAP=M400 modalgroup=10 ngc=touchoff-z-to-bead +REMAP=M600 modalgroup=6 ngc=tool-job-begin +FEATURES=12 + +[EMCMOT] + +EMCMOT = motmod +COMM_TIMEOUT = 1.0 +BASE_PERIOD = 35000 +SERVO_PERIOD = 1000000 + +[TASK] +TASK = milltask +CYCLE_TIME = 0.010 + +[HAL] +HALUI=halui +HALFILE = 3040T.hal +POSTGUI_HALFILE = custom_postgui.hal + +[HALUI] +MDI_COMMAND = M600 +MDI_COMMAND = G0 X0 Y0 +MDI_COMMAND = G53 G0 X0 Y0 Z0 +MDI_COMMAND = G53 g0 z0 +MDI_COMMAND = G10 L20 p0 x0 y0 z0 +MDI_COMMAND = G10 L20 p0 x36.575 y-38.364 +MDI_COMMAND = G10 L2 p0 x261.3 y286.9 +MDI_COMMAND = M400 +MDI_COMMAND = o call + +[TRAJ] + +COORDINATES = XYZA +LINEAR_UNITS = mm +ANGULAR_UNITS = degree +DEFAULT_LINEAR_VELOCITY = 6.00 +MAX_LINEAR_VELOCITY = 60.00 + +[EMCIO] +EMCIO = io +CYCLE_TIME = 0.100 +TOOL_TABLE = tool.tbl +TOOL_CHANGE_AT_G30 = 0 + + +[KINS] +KINEMATICS = trivkins coordinates=XYZA +JOINTS = 4 + +[AXIS_X] +MIN_LIMIT = -0.001 +MAX_LIMIT = 290.0 +MAX_VELOCITY = 55 +MAX_ACCELERATION = 1500.0 + +[JOINT_0] +TYPE = LINEAR +HOME = 0.0 +MAX_VELOCITY = 55 +MAX_ACCELERATION = 1500.0 +STEPGEN_MAXACCEL = 1875.0 +SCALE = -400.0 +FERROR = 1 +MIN_FERROR = .25 +MIN_FERROR = 10.25 +MIN_LIMIT = -0.001 +MAX_LIMIT = 290.0 +HOME_OFFSET = -1.000000 +HOME_SEARCH_VEL = -20.000000 +HOME_LATCH_VEL = -1.250000 +HOME_IGNORE_LIMITS = YES +HOME_SEQUENCE = 1 + +[AXIS_Y] +MIN_LIMIT = -0.001 +MAX_LIMIT = 395.0 +MAX_VELOCITY = 55 +MAX_ACCELERATION = 800.0 + +[JOINT_1] +TYPE = LINEAR +HOME = 0.0 +MAX_VELOCITY = 55 +MAX_ACCELERATION = 800.0 +STEPGEN_MAXACCEL = 1250.0 +SCALE = -400.0 +FERROR = 1 +MIN_FERROR = .25 +MIN_LIMIT = -0.001 +MAX_LIMIT = 395.0 +BACKLASH = 0.01 +HOME_OFFSET = -1.000000 +HOME_SEARCH_VEL = -20.000000 +HOME_LATCH_VEL = -1.250000 +HOME_IGNORE_LIMITS = YES +HOME_SEQUENCE = 1 + +[AXIS_Z] +MIN_LIMIT = -75 +MAX_LIMIT = 0.001 +MAX_VELOCITY = 50 +MAX_ACCELERATION = 2000.0 +OFFSET_AV_RATIO = 0.2 + +[JOINT_2] +TYPE = LINEAR +HOME = 0.0 +MAX_VELOCITY = 50 +MAX_ACCELERATION = 2000.0 +STEPGEN_MAXACCEL = 2500.0 +SCALE = -400.0 +FERROR = 1 +MIN_FERROR = .25 +MIN_LIMIT = -75 +MAX_LIMIT = 0.001 +HOME_OFFSET = 1.000000 +HOME_SEARCH_VEL = 30.000000 +HOME_LATCH_VEL = 1.250000 +HOME_IGNORE_LIMITS = YES +HOME_SEQUENCE = 0 + +[AXIS_A] +MIN_LIMIT = -99999 +MAX_LIMIT = 99999 +MAX_VELOCITY = 3.33 +MAX_ACCELERATION = 2000.0 + +[JOINT_3] +TYPE = ANGULAR +HOME = 0.0 +MAX_VELOCITY = 3.33 +MAX_ACCELERATION = 2000.0 +STEPGEN_MAXACCEL = 2500.0 +#SCALE = 4.6296 #old scale maybe accurate for absolute positon mode? +SCALE = 27.7776 +FERROR = 1 +MIN_FERROR = .25 +MIN_LIMIT = -99999 +MAX_LIMIT = 99999 +SEARCH_VEL = 0 +LATCH_VEL = 0 +USE_INDEX = NO +HOME_SEQUENCE = 0 diff --git a/M100 b/M100 new file mode 100755 index 0000000..a69417c --- /dev/null +++ b/M100 @@ -0,0 +1,6 @@ +#!/bin/bash + +cd ~/linuxcnc/scannout/default + +LAST=$(find . -maxdepth 1 -name "*.jpg" -type f -printf "%p\n" | sed -e s/.\\/out-//g -e s/.jpg//g | sort -g | tail -1) +cnccapture -D /dev/v4l/by-id/usb-Etron_Technology__Inc._XWJ200-video-index0 out-$(($LAST + 1)).jpg diff --git a/M101 b/M101 new file mode 100755 index 0000000..449b01f --- /dev/null +++ b/M101 @@ -0,0 +1,4 @@ +#!/bin/bash + +rm ~/linuxcnc/scannout/default/* 2> /dev/null +exit 0 diff --git a/M102 b/M102 new file mode 100755 index 0000000..8b28657 --- /dev/null +++ b/M102 @@ -0,0 +1,32 @@ +#!/bin/bash + +cd ~/linuxcnc/scannout/multiscan + +if [ -p /tmp/CNCCaptureCtl ] +then + echo exit > /tmp/CNCCaptureCtl + sleep 2 +fi + +LAST=$(find . -maxdepth 1 -type d -printf "%p\n" | sed -e s/.\\///g | sort -g | tail -1) + +re='^[0-9]+$' +if ! [[ $LAST =~ $re ]] ; then + LAST=0 +fi + +mkdir $(($LAST+1)) + +cnccapture -d -D /dev/v4l/by-id/usb-Etron_Technology__Inc._XWJ200-video-index0 ~/linuxcnc/scannout/multiscan/$(($LAST+1))/ & + +sleep 3 + +for i in {1..600} +do + if [ -f /tmp/CNCCaptureRdy ]; then + break + else + sleep 0.1 + fi +done + diff --git a/M103 b/M103 new file mode 100755 index 0000000..ac99358 --- /dev/null +++ b/M103 @@ -0,0 +1,24 @@ +#!/bin/bash + +ctlPipe=/tmp/CNCCaptureCtl + +if [[ -p $ctlPipe ]] +then + echo snap > "$ctlPipe" + sleep 0.5 + + for i in {1..600} + do + sleep 0.1 + if [ -f /tmp/CNCCaptureRdy ]; then + echo "redy" + break + fi + done + + exit 0 +else + echo "no ctlPipe" + exit 1 +fi + diff --git a/M104 b/M104 new file mode 100755 index 0000000..82e2644 --- /dev/null +++ b/M104 @@ -0,0 +1,12 @@ +#!/bin/bash + +if [ -p /tmp/CNCCaptureCtl ] +then + echo exit > /tmp/CNCCaptureCtl + sleep 1 + exit 0 +else + exit 1 +fi + + diff --git a/UVOSBannerLCNC.gif b/UVOSBannerLCNC.gif new file mode 100644 index 0000000000000000000000000000000000000000..0820529e6ee71508ce6f92c62ad7f27ee5761cae GIT binary patch literal 16939 zcmWh!by(Bi7yhh^7^6mylxBpKh$E%M(MUN$S{$X=D^j{k9H2-$L?i?pDInrdRK#Fl zAu0kEUt|9K-M{bi-1D4s&wJi;?tAw;JDOXB(!c}2yT8D{1p={5eNijWn~o=6;)MLH8nMLb#)C5ja|ETX=-X}X=!O| zYwPIf=<4d~>FMd~>l+vt5Q#)XLqj7YBV%J@6BCo&yLX$KnwpuJnVXwiSXfwETJG7i z$I8lT@7}%E*4F#>?IV#$Ha0f4wzhV5cJ}u64h{~Ej*k2H?{{)?a&~rhadB~Vb#-%d zJ8CcvmzTGP(njP!@|PC!^0yYB90t65*Zm86%`d79UT)B6B`>F7Z(>FAAj`d(S(GA zW58#H6I8XhLT+wuUS3{)ettngL1AHG zQBhHGadAmWNoi?mSy@?md3i-eMP+4WRaI4Wb#+ZmO>J%M#ful~>gq0Cy2NI)>+9+9?9?;jW#xOMB+;NalU(9rPk@a@~Txm@nZ$jIpE=-AlU z`1tt5#Kh#}AFTX71j-J3Bjj@7}$+xw-rI@AG)P`T6+=4<0;x_;6ui z;nAZh?V%H0ZL_)M*H zchkKZ^W^pC)7@9^Um`Kl3NAOA=Np|$w1a1Ev^=~@Zgjcm($o5=&A&VL#Y|7zlWUZ* z90k{#?N7U-AGQYHz1i`sm-=evqHAyGi(6@5)?eK1y|#Rt2Ei!0^>J3lvcz>lX8XEc z-6>Rct#j+Y{^o9_aoo$<{_c18*>)Ec4-DLR|FGGsE##i;RXT(tN~k<=>*l9teaRm> zFS2_-FLRj~C6OTd)~l%!ox^iMBaC;vM%PR3LqEar#qPM}xuIJpJ__RnCisKHga3V5 zdsuLSJ3RF3$L6cMmk$1=|M|7^<-_v*+qeJz1>l-ryq)FPL~R#_Dx6yvG8HiYR6Y5vO~_)H{?)mEQaBUIm(T>;S1()8S~=M44ESz z(Vt?9JB_Q#taHg0V+seB&%`nU!?xHHEj&E!=u!#Hnq zT*A{tcR~VtaQ>gozJ|)_ei3JsrDTo|u4E{k(^QdGug+S{IoyYY=NdkUewAlYuKXq6 z8g3CGYc3sgS=Pm9da}gR(fN&>Us#oTMN*o@{>rfYL;I_av#T@|j}H77S)24AW>?** zji*;H(V;HU^(?hk&BlDk>Zzp??If+Hs@Ns1<~r-`sph7-oK}Ha+d#l{8|Q)LhmMN1 z>Nt(8AFVqYxyIHXITNxM+OJTNmf z+OhWNoFcmC;t9dWPwXrPeAF1^kv=B6Uw3O<@^HMvr0lWhiBd|ZHMghKFAX-#==6R} z%Cjg|PA)KkoSeL8@wxqVu{A{R4bLGiJLSP)#(R?mk4r6YtK)}JQI7+bv!m)lAmHO{ zhHPoQNP4q_jLOm2z#0{)HB;A#Jg?-oniq}^+Cx`bkDa?v#z#h(kDnwb4#fs>6s7y8 z)IYFhW2H9C{r1F6Y@~PCy;zTQ8k4Ql&)}R6?Fqo=NHW4C1O?`x&tW+5E@FG=sGQ=2 zN{ZoYbx}LTU;a~dW(_#!W2f}Pp5~5kUjKNsAaeJwy27L;_DtN2ltH zeVSno1_ZrQypxTS`oNlYS$X3-Wv4K#pJD2Dh3kvz^j^YdHWJl8#h%z>E+nP_;*5wf zrMt2{>eCL`znnCeawt3?c1q3GBZpJzpcf(wEdzH$#r&Wv+au+3D;NKq{0(lo-Z-`JA{b6$$ z6jySQR*TY+cJcU5PQG5_s~c`MPp`^vTweEcl)k$-DY$SL^lEKgDA01W7%Ya?Y;pD6 zB!<}LUg3@}iq|(QYbkc;OwVe4tDT;xbNq5OR9=89%7~p*J0jlVtR7(2yRdL&#G`gg zHR{n^wtGiMa$C^ZT~?4oq#L_i+sg(PAQnCM&-rd%IwrXW$HejmUZ%3fwUsr!oMz_U zb5e|-uZ8`X2uXSKVQBNH!D+cT57~S$s`{uiB!>-~&p&>0)>u!{S!(a24HKbowwMg3 zckf2JN3Ly;hR6LKHO&@}Ue|9Lzs|_HH@kGyz8#cRYOi+xvt@Abj7z7|?V0D@7CG(3 zo&@WwZN4{iv~dOAJ|skd&PE81g?{v5*RN9+UozYSp9j1Ne^q_D3won< zjUnkjXH*_I@~hTceu)X0OrG1=UYx-Jig>#JZouOt zRb=7eauW(vgFr{Qkk)XOd|swCy+QiknnNRhzAz&Nt#RPJ%fSFx<(x^AA2-ImoBxoV zKyMD@GCXRQV9oA5m=Nx|@2#IOVpdOkxB@fa4hh~j*CQ0k{Sfl28qO6l6^?qUfV-JtCXd1vq~SE& zw{mn=R;4eQPnykbsi*yZMG&=^5>%14i)((r);!oh^s%ulry9IR_!o zKtgN)Nv5SeAEUttTmXuv0bn4NiwB@8S-+5P|Jh$bfSj3}20L*W645_D*9rg!F#p@A zoM02YmJJ}eJCl2cw-B~D0JjqmU7(->iFR_Ysfc~ZRWb{(SK1(C?q;o^O#8TC5hAT! zcNl=P?}5-n;AZa&6$LDVi?XJ&3~Zp5&LByAxqZ1i2rdyr5~lJ$M%n#>KXwEiL~_8Y zSo#kKe{&p5fc=k+Ka9;^F10V-+^fKEou03vJv zL8d{}C=ho(>=+T5MM0h+!<{%_Su9A13S^%+jkSUNH+jqspzbe(h!l#W3L#zq%md3% zMp}82V6Fg2!UNMOTfq=MII!Cg2-i~he<-?fY1;J(?`YhvN1zMp*uWG z9S;#gfk@H-+~U8E1!_ltN~O{N9*1%(#ARtv4}hV9hjCR6_mIT|;s7j=?6&(6jMyx$ zMnTvZo8V0m)I#t+LjX+$85F{Esb&RSq)4gn?#S?jbQ@YY>UuG%mXE5W!d3WyB~k)T z0I6bO7s;5L6ig2Z(*(Waq?%DM6>7N!iX~>dNFvI!AYy&u!kZv-5{$t?6>vxo zQdy?HHqsK#OAh4p5>z9Aj-x=t00}7`*prVKAP9ByBgdU`Co7A(IYn~Lm>V?ABmomc zfcW6^pkkoCg_(bDX6jP_3q#QUO#z+h#1MJ~kph8SsQLGd9S9F#u7%=K6gZU{8n1>( z%pM>Wt|jRw(_4xz2$rQi`F8U=l4;Dmu@;u))V0jSja+P252$bkSNSO7WnI3z1-URe|_&_2U4HwR&DE({aK4m+E~G#^c8Txe!aLX)>1p<`Ge&g1Xh^em zu`cms9_@DOGfol zFqEb8Zc1*VR&7#t(|~_%WLo(^4o0RGGu(>7w_;2w#j<=z>d%VDi(pF|5Saiw*XoHg zhTw~Uz1YSgXG(A(YaeYCwkw z9kFNt&~BSJ8499{ii!WPCPKSbqP3~NwM}NJ?e==xEjDHnKuJ@Ib@&y(rKk@7P`6MV zwh8v#Y_F4q37-^07D6bL=H_peCd!bwP584?>qLSe+kkk%a0~DzAQB+qU;8_oEo^q2 zQ5D>rI_jP}E~=_f=lR5?eDRQ}Z8DpJt|M0IhC&KB7dT5rpgpxQORfFYZHTs9)UDiG zn?01fn7iEk`8B|l+Wt$5YLC5WDF$-ow=Xw0T=Id+Bm(AG_*}A=`a_s89ejj+J-0eE znGd%pEbU8yq*9SNSS`m>zE}Hw3rYWw1y#C<%qAgEaN!gZjAi~0q2Q@RWF84s!_lqT ztZtkki>m8g!J=ncaU?4GCcyEjX}Xn#xy8qDu|lJ~TO*s8TQm+u?&ctQaFBzU;Gh^5 znaZ2BkMYpL9C0ufTr1bh6Ij9yVTYtc0aGkondzqS3T9*j4x`q^MVL4p#{;gs*1Q_4o2Cc%h207;z?uLtA+STPN)Y2rdV6CnW?t-~y)Ol_q$_5WHSe9PB6kl5)dEF@+$bP{50f%q041 zpS_&;3R%KKkl6tDw-}fRPA8+!2aP=OoO^p6?Lx$9ZlXtIMx{<+xD-qn78Laf3oZoZ zQ-nrHkGww#<&Z%d7GRQ97YH$~nI&#iIeuQ-Fo663uMFs7;iqZ>c5TDLDbF-*080Xb z(BVD#8?HtWm*c}vA2|4a{YjN4D*YeVLi3&~B?2*Qw66W!%VPi4>-x=Plu9DdOnC~s z+tx=CDxrWM3yZ-iusQCn5t7gd0Mqynu;47k#N{~vk1gncGtjG5ZSO~f9;y}tvB3j* z0eWlCa2yA`olO&%PkVyg@QHAy&CKFbQ^*8u8+Q{vYtm762{y zBUVN0d3UdE1}Vg#b}1+WHpJy|j!-QHtbqdW$BzH?{YPNZm`_3e*!enj!@y0xoK5Ax zP_LgFT(k{G;K1#ivv2K2SP{gn2*_;ildaQ}1r($s_NtB{;KfEajy=>KUcJhX*bYNS z061SFray)w7#YYNAtQvbI1?^rhA)&rDaMc>KRYrdenDJ=#i2Clla_IvBQQJSs+1U@ z3BZS*^M?=AXpMlP*%R+ins^e#9mq(8|G}?SS~(Q>tIF1RHkvAU!Ouf~Q8`kyDJI23 z_jBI`-OA-+kzFD_o+HKg=p9)S65(81ehsl(oA(e3I z-Cc&D09xZjyLt+#kysCSkhCHaCIwT-d@912_fl&FILk&S&aEEwJ<>r&FWnNm0JIOH z+tI&zMgjD^H4Z|8)BX_+3fPIe^!orbRBOl@=Wy7f2nC1Gyq4!w!T`bRU0eTZ z7~*AhXwXMk!7vU@faURn6gEursK_8LV7>xwprMo8-?fmB_&r9qQkLcGp%|OLmY>fA4fwC*y6&3EV+ZQKq4glte6^AWpKz%3jz;ggLi@W#{qTa zj=Bi)M_rGqBGQ+wC<#?+{BIhzvPJ34PaA7f0E?Q2;BUnI3slP(VZa@ap!^fdC?A z#NaD1;zDWj?F^b;^#-`M8Nv$IXUx~6uLTdLeH&eKRV4;f2169JvG6dIYh>uqqj{T!%7 zGPhFxpxrR-mE%Xb>(trFY~x)bLf3R8<6|P$ctQ55;_9BIC+1acZI%S7m5mG9KCQkA zivB#l&Y??2zr7P(DkpMf&j(9MeLzy?F77=qfGb#D0pX3~R%c`Er+|5Fgl4wU4b2Ca zy*mksT$%B51Mh3GFmXpWQJy+~P$uwue1N4n^oF1rm{ILd`LuekEgGjN%R5_4eH|;= zw$+Z-&iIyNs421U*5bC$(Y5lyRE*KWU#ZnV2?1zE}E6dPkiZoHlfM?xf^{A|a!*EjS(ZdYH<$N9Tu zgZD&g6|g5l=&*ACu&N^xf6ffNlFoYzX_}_g2~FaCBe-2II@iXN0I9;}=_YV0ZWYi~ zSQ;5Y6a~a)BP>6VF1&2L?$Rcbt|J)j78)nzkH1;vjAcXpg;d1wqWvPb93)!o^F=1r zVgUUlnfrH?(sLck4>?thTt(u=B8TtN=$?3wf|Cvi$Ab>LAKjG4bMAt(HAl0gYc0Ql zjPq>0v~>Lmw0pXze&vl>OgJ?dqd8fs7H`~G;i5W^y*D##pm|&RLlXT^VG(a(RCbV> zkHM7kk*-oH&&5+H*ukv-CZ;L&l9Bk6dGqP$gwGRZ%LP#Uh)MPEf^#;~^HF`XadL-B zjP{C@{yH_ za&|i2<1~DrlAU)bqr_pWX>j|G2EUlpbiqH2;(dTRN6{hu};L<}m))@5JzY8x0v# z-FL&k=D<_+NIx^PFhn|8k`cSqIcJ*%w;p5X5La{kg?`7wTGADtRZ-s`Vk-r(Jmv*7( zU|YIsuE8!#3kFhqyjjG`Cgp=yj^xspDhxUFTvF_d@m5~BEPEwh5a=uant)K{H$z#p zc@Vu9r8HZZWNHr6E0#7N@!&~GrHmgOCkWlK5MkXRy+U?ZSp1_ntn zA*Vvc@;SS`g+tNZBg&HPG$@8P3HC%nuz_Y7hCw4bsz|%Pf)i2}v-K$7kD}mBtV92y z4<{~A?A2R2w2eO`sT+kd(nHY~0{Np-F+0xU33QOQPj747d7uJE@v|Uw63b#Q%F5B) zmaf^(AkIkMH<%Nb?pvJDQClw-qcLQ9gI?`}Sdt@6##$Z`;bO!Va;sE_tLy1CdJtjG zP=SWL*S=)j!cJGJ{cJ3rHoE^^%7G%IB#cl*Hl&(>$a|k%92Ko~0ZI@cY@Cb4atp_C z{D6KKXGNM`5~Y(^bRlSsfjg*7QZ8LN7;Mzj;+{HbY_caWmL1yc1JfHO@yN9I9uVdb z4$TH0lYB$bd!TvHY9jn``i+i=#Zhk$22EwLzSF5)LH!|-p)F&BHQFtqDFZ^8;X6;u zX^!jTem$Hpm6A@`oROk+3DE)`x7?#TMqUC5Z`;0-FXsZWcDF)w>DMYncQZttPPT%O z_2@Z?50V+1!#sNmDje_YBx3eJwetjQrv1W|l2i39s<{@0Vt2i4@WW2VX$^JK#MeYY)BVj8Q+0PvPei_4QU*8`0 z1gjJ{gB{r5bJrYUy1xNSVWO>!lm#CwsHn&MK9G&z0SKR6MIzU_A4twxc~u)Vh~@Kb zd;2Ru4uIWUUWf4!MiGoV#g&@J9y?ifFFwj|Oji2#HwtrrUMzu`1|TDwEy|t+XBc<`>Y~xM3VqM{xR#?}zg0j%am9Jo zjPaiCL`ORib3zC~UoZE%83b&~?9pwMoy{0}8g=;evsWQ<&j3<_IU;;hec6~>)=ckb zHp|m`4V?jnzH1!WUTqF|SEbdwIZctoT+`u<8V?$fWl9J%^|k&_-EEfs5HW7CI)#%%Z# zgovi@)_y%+PzkznQu1P!!G|^z>BT*$L0NPuV_ZBBJViIDy#C&J&?Mq&5ucuGXtOt) zY00K*d=Zu2WI8zM@BM9dPY#N7ss{?`ls?ND&Al4^1>St^`$#|y2~Xs}1*h$STzi*z z+hdyuoS2h86xQkIBUY^AC4S9i93P?f9?bMX=d(svvrQJ zbs4ljMz}*IJ11f@6G;evr+ulV)8adZnT%^HSSLpmAlU~V_R7)jyQcS51k?=LZ9aAg z>O|BWzD9)T8#}#eW~z{AhW)_qEU1v`BtCb_k!|-`3?Se$a{7?ADaC2dumVh;xDzXl zp8@DPQwQynpqXV6(}^6{WOAlMAF0ZU1nvW!cwVd43_5wE7{M{PNx~-Hh>&hp24K8pBl6=}61PJsdEaQVPjpIP)&F=Z@@8gYMU4M&yFSxmgba@0}1U zi{!KJGejW%jIc^XLe|~>%dWS6-%BE9Lic7S7a~0QH&(w6f!Oq-?&;z^4Ee>oIu?NJ zn)7L>i;n*+j|%$tdOHQ3`gdpzC>ym1-elQrG(rrTCv>K1W>nU4=)sy(P$vp>)xI9X z%ZmD4x+`1yQX+igYXRU7@=Qa3J6s{^2)@054BLxHAt6%Ph&d*C`5{dSm~A&={AX8& zBBH&2JEK{R0tyH>F(>Ze5%dcQNdrrtomr}9nvgvlE$I6wtSfQ{ey~^>_7xi_z#a)# zK8ky+`yiB(VO{AWwMBC#Ba+h|ESqF%?M9?Dd+Hp>OyMBNJeuCwJ?eivchC&--ZJ4& z)2&q`b}i%c7g0%y*ZJT3jm?>X3J}BeEOqun%QB`3&&RPILm`syn^T-R0a%@xi zY?|txO5Yxa<$nbd{!E;p!zXa)krCcI6JYG7FgLx+&C{4??8`s|hRf!|@6SCVc0)%^ zs*z;eL6GF{*MzXX3^nAuAdQ*il&SU2*Zlw@QNb_iNak@0;;74$0yz*Yw@i6z+E4}% zH=lUzWu#BM|CdBJiS{ycU>Z3+%qXOrtUWqqhtYB?gD-+J&pKr}Ja)>0+HBT=`A`bY zG4Gqqos)1|(xhQMBicXnfUsYp&!OY~Pmf0D8w^vD>$B%78cN;B(#)Cgda#cJOBxTTD>cT>?2 znW3!ic~YxU5V4s~nZTK>Wu&exlpHwJ6a4Jv*~}9=2s{44eqlYK9ePpn6BPn@VF>qc z`mWdf#2Y%JJ;nP&q}T8EIPdekUa~s_pc~MFm@s{d9fqe@8P=S&e`@gFl_jS_mNQp> z!vlOgv0?&L{h>ai-c`~9k2p?vl1dJ|nVR|QVsIL7`FtO%ZsLW44(R;*7li0rhM~aY z>$0zg7z)kLR{>W-3G@g-BA2v-3;^<}(Fx2@cC~Cxny^Z7pMMIqY4XPgjr+ z7C~2sXJzb(=6cn$QYcS*CYFEaWy39%zHD7wSlhQwmVv&#o z<+I~MtM1m!d@Gs~X@t@9a_J{#C%^l7e=|@p)=DoR{=-#ckW}0$s=Kf zjIyru6FaZckgr4a--dN&p4!PIkz9|6G~uS?qogJXY=5c$Xu5@Yj~gOzS$?0_X!;~( z!tkSG@F*1kkV}J@5{~#77mZ9vgYjVZw09=@D<_*+E@alwBWVnc6-IcV@`)`qc_gwA z^66fMxf8<1H2RcN^l47!X>R6u3N!zBw4^On)>Y9d+H_bRZJtS~G*@2GrWb-TU) z<1;PvL_#F16LEDI(L9MbyND=gj%{rK3_5tlUtZ99iUk0dj)Vs3RD%Cb6bVf+Oh_M& zxig=*Lx`FXGk<1T-NijrM#PLXXcKgha8E4Z&B;!d3x~CzT;|WbYe|o`-0!$xx zcl0Nnb4PcRA&icr$);159LDQ6<#IbL6On}B;TTsA)Mb%THV^V3XGp@U-}z=l?yx$8 zWWY$obkoOAbBMby|9LF~EOCs22L46piz36S2Pe|3$r+Z_2f&S;ICLdrH$S4s9rAnb zSRXvxjLp=e`7|Vg4ld58nNnA8vRt_W7HLWvQVI4UX3X!6{}|dBNlEuIl?7uFpXM@W zHmIWS5a)If?vx3!f<~}EeS5BPMHMW0rwF}iEB!=r7s*1mh^EQeD!Tdk*W~A=w_5|T zOiLm}DMUtM3Oc8-T;sT{c8BHCM?W0{7*C~ZB4We(-hA|cN2kHGN95r6%nkS#;^)kB zo0;d+AjYCiggVrh$0+_EQ8a-;wR2B<_26OuQ6g-1n;CKfsm^ zRrUL-=9lEggIe=GdCAc27hx{P5?m4W=VBZEIIqvdfKqswWTn*e*dOQl@MODhhXZJ+ zvb88v#@a*?N)!Ce*;-FfNRR3*hVvf^f2i58xWloCKdhCZ9w8@cf%u_&3vq_dfYxJ)8qtespui80=h< z_;>Li32oz?($HO`XG|6ir$Y8F|FHi2W0S?Ss08i2E&o?U-J9PJD<(j1vyR+nxh+EX zX02%V)Alu|hZnwb{jn=%k(K)wB}7&F%z>ODq!vn_EIOYFGyBiN_GH=NWDgUtDsWUl zq+csPp)tu&(MUI?Cw{u|SO@q}y&;@dT6zc` zZqc-JxfSj?)i1b8fJ8fiTNC~b1`yr*P8T8IUqeny#{PN`_Fp*yyzo6A$_B{|NXm~g z)R80J>I}2Amvzfq`<~$U_=80>WF<*Z7i_|6!m0H0rw*k3cJG5KO%&lNtPm_b`p62q zWhbI9!-xJDwDagW=S8{wp5)8@O(B%aGom#Z&iutskO#Do@_9c(r2=O02Wj3i3yX zX?aNO_%l#rL#>mrh z7u;yz+#j$;=|9oyWT}zE_u0h)@D;Hhou~vwJh<1eY2EDmfQ8<}-sI04FK*Fr&g)AP zff1iW)EvoaW?Y7|gl2}gYUJQzPRac8L-bmrMRz1R74Bi5Tq^eT+>0t=mWEcxc(JSD zh(_S9I@+qU#Dh=AYeSH{kxP>WBa zsFhueieAI{Y$kSCP5Hn@@3rY&mpF_}gtdDG7kLKzE%ew(7CtEZj(o|g(VQB1B!xMc zJZE7t0Lh145vHQ{?a5ac7K%CbQ>!`F17L~mS7~`I)}m6x5*Brm0zh(n?zqI(p%{OO zuUa?pl5TF;SSRzr#KKCUjxDl%!i{B^r1}t2)brUUzIH zhb~pKW8`K)6Ts!OH5997jYpgex&IW4qau0lo4ehu9uSatM02#Il)%En`@3I?8d(=jJWQI z9Aptj!0GQIXb_1M3RL#OW`i4X;Nz(403l#+u7|%;qd>@bpm_v1p~y)pSp0CKduUG- z#0WGQD9mOke;ics-Pe&jZ2ha)g2EKLy3rB4+@?Rm!tG)G(=Z9?vH?Jn1Het#ARI6v zl*NPomo#lD5E~YgZK47yX_tt>m8=geYz=?l;RKg5J0}079EInb(QdU0=gk#|K}zgDGIO zi&5vkeF(&XtDrmH*(gUmSS6DR)gTE*61@vY#cO#`ge8_CHuxYE=Vhb3cJ-{>NB%pj zR~fqYK*64vQFU|BF3B+0KsHrjJ_K*)Rl6$D>;%(^w%HxX28-ulSxUc&S@zLj$pKBc zQPvhFh-fU`PgB`$b#w?LciT_-XBd&iBtPeJCBKsqduHC|8;+_fZWrFM`no0R&l-U% zP+sjP^1)j2VRFe57@{d<=IpkyD(0V-b=zp4#vhMDx;NOijJi*QSc*Cj9WAb6>Fi>S zWfYJ3oYmEYoXR7@a7qAcbuL{BY@DN+*e z7%TxS1r8q~!==G(xeR$c2-8Bov-ggXkl)1{ooP3?qN^2`hwO9D!6YMW#VoryC)~~C)ATJ((%Ld9wqSP}LH6MZ2J7 zf}CM|&Lc~XnzGN1vfpalf3kh9vHv>diG=Enm}GAKrSYsw&fvc_q3;-6>tC{hi+*?-3b}uBXLo z(nAG-7$5>o%ppg+j$o|2^}Ogy3)gRE9L@g}uV>{QI4dKanjf}(CcG!iOHLN0mrc+_i11b19JWPNnL+;_1#oztcD&)NE?=jKdaZk{aq?4x&%6_dYcDVOJm8lAtWN(4rj^XPAAID!r8i&W3hGuOq@ zN$E#YS8S!q`Ct*=qt0ELc1NC&5vpW^HQ7)=_`zn5PoeD%R)G9aDjnFNnJ)J=A&%9VIKH-S{BnV{?jl?h0QhdrZ>b^kl9T@Q?Sybi55+O+*7F z+x@%ao(bNX8~?g`e*?D7nlB9Jm(MPz4l-4K z({2j0F<=x(7l1F&YQOLh+APF;{l8VRaP0bg~i0+lVgEFqJg86_;OGUqI(dW2biJR~$8gp9(fI zuSe7eNBaS$_#4}VPhj$yN@EjJWypDt2&eo54KC7xhV@qRPg|D5K@@M0o;Ww? zZ|nzz01%!F(j`Qwo^tiXxu_Gv5B+0(fj&TZ77)IC_0$8B@QtgdZ(bE1zj|ioo$w-8`1!lD ztF&_)yUxF*ofkxjtZ_wzH0j$kx@@$lS~EkxS=1z&DF9funpn3r7Mz|Q(u_sgU?ZBb zYB-fQc`pSt+2tF{HhOf5de!js6Lsgg4kyqXq9e>ggaB@kTv(F~0!h7&&W{8YMAD^x zuSr3*q=jOn)96K~Mx@o=OVdCl8T67IIzE?PT0DX;w2>*L%T$iY#JAW`;7&!(*9Ym9 zqoAru8~G`^{4BkC4y3@ND-6C@cn(sWjZu6}SNsU7+W;wT+9*LED*d||P+I~LTrUDv z#@Z@NFqC!Il?h-KHHM1)x(X4jYQj)8A62yitC4KgQd*sB?Q8nRYi3!sfN<3x*%(;h zl4Rhf^0Q~o?C1A_r_N^G_|LbV=-{QxoZihewt|?D>`a`-OvrY-y_vg%#&$_{e9h-YRHUBv&JxR0o)LLe7=5O|$!J{EX>h$cowFe_lTJHI^hI(pz z%6a2Wmh>j)Ev@CfY|j7QTmX(KOc*IlvXO>_Xxb*(wy4I!dElR&fE`eLw zQtR~^>kYD_$|mcLwyi2|u~!1Ln_^?Hx-it-7|zY``&qF)WT%_acX{0Ws`=6w;XsIN z&8Al1bLFy&dGeCG?^D@XM!L+-?bd6Kb{tPgm!gg-#m+Q(%#>{ge z7UugeXB|iy8tYY%y?4)Fu8!bOW;`(YU@ZxVNUYISns0mudaRp!1i($-=9>wcy~msX zd1vfnSzo^9JN1hX-UOcc6nJYOuACd4I}DyOa6_x%l`hM*LKc?sWOb>8ALL>o#`>;+44VvkyM9(W3;s zc7t~7+;+SQX!jf z>h}`FYc9=tgYLNT+H<2PB;jpBLdL1KciZ!Yd7Z!BPzLHcA8M7~@)Vib%o&`E9ts#4 zT3^e5(D~`5KKJd$hLmo}PyJHxwatL{W3mPn8lNW^`SvECD^KZuZH=kP*ZnrBb@6)q z-HrF(lRnQrj^BBy`` zgZ9f2gYI=SO@YlejcYqNoqErPk3OF{`eLcWJep%ca#^GmA(+qjOF>N%jHTMm)dyU3 zH}SRm2aI1QYld_40jF1FL^xCDu>3`l&%|NF;I%C|Z1{p;9oO|?SpVbdhmRX=p9FCm z>-r<#6H2t>B@qT&ky~4*Vh~53Mn%Wt&+Arq#?%z+);4U-gc;OryubVNz1;a@@&yKs z)z>b+G*k>9kVThu=lIyYuc!Bn6@`jt2j^2~62VNz;+b}u!qm6cb5!+?xW!6(@+hcOj*yrztkLWSqsQ13fsa-aeY9GHjx5L*>KJ`Rg_`ogI_2}S8 zn#A=~)eLE8NG{)b6ddm7;hA;xX|*1wNiQ<$#`C$OF9aFi_y5o{SC_tEA-$^g()zW$ z8F3|U+2iLCP4`{6ckA2wUy=@rOO%wx;nG3|aAqZ-uCpG_Ve+YBN`|;Qow#tDg2$a+ zKiY0Pe&M=*O9@Qf%(sydy0)cw?RZqi`Lo96$?bTi*`b5iri;zKM;geD#m^2W*N^rD z{fOUdFuM0uYRKp0{{_qeGyZ<}^QguN(DYg;NbNw^)WxxC@BuJwA$Hsy)iXhx0Z$Xw zM0P|=I0Q#pB`?#Bcxu5$9q`SJSI|`ft z3cNcCq=3B7yS%G_z2Ex^l?e) zmoeK%5i4_$%dU?yv61(@7Wk-Wgu2iVJ<%7v(H}k1C%w`yJ<~V6(?31bN4?ZfJ=Ise z)n7fMJ=b@=*MB|OhrQU3J=vGN*`Gbyr@h**Pd(eWz1zP%+{eA#&pqAOz1`nE z-sipE?>*o5z2E;m;0M0o4?f`+zTqD};wQf1FFxZpzT-bWWzU5y&=4Za< NZ$9UDzSjo?06Sc>4Z{Ec literal 0 HcmV?d00001 diff --git a/compensation.py b/compensation.py new file mode 100644 index 0000000..1438840 --- /dev/null +++ b/compensation.py @@ -0,0 +1,232 @@ +#!/usr/bin/env python2 +"""Copyright (C) 2020 Scott Alford, scottalford75@gmail.com + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +""" + +update = 0.025 # this is how often the z external offset value is updated based on current x & y position +safeOffset = 20 + +import sys +import os.path, time +import numpy as np +from scipy.interpolate import griddata +from enum import Enum, unique + +import linuxcnc + +@unique +class States(Enum): + START = 1 + IDLE = 2 + LOADMAP = 3 + RUNNING = 4 + RESET = 5 + STOP = 6 + + +class Compensation : + def __init__(self) : + self.comp = {} + if len(sys.argv)<2: + print "ERROR! No input file name specified!" + sys.exit() + + self.filename = sys.argv[1] + self.method = sys.argv[2] + + # default to cubic if not specified + if self.method == "" : self.methond = "cubic" + + + def loadMap(self) : + # data coordinates and values + self.data = np.loadtxt(self.filename, dtype=float, delimiter=" ", usecols=(0, 1, 2)) + self.x_data = np.around(self.data[:,0],1) + self.y_data = np.around(self.data[:,1],1) + self.z_data = self.data[:,2] + + # get the x and y, min and max values from the data + self.xMin = int(np.min(self.x_data)) + self.xMax = int(np.max(self.x_data)) + self.yMin = int(np.min(self.y_data)) + self.yMax = int(np.max(self.y_data)) + + print " xMin = ", self.xMin + print " xMax = ", self.xMax + print " yMin = ", self.yMin + print " yMax = ", self.yMax + + # target grid to interpolate to, 1 grid per mm + self.xSteps = (self.xMax-self.xMin)+1 + self.ySteps = (self.yMax-self.yMin)+1 + self.x = np.linspace(self.xMin, self.xMax, self.xSteps) + self.y = np.linspace(self.yMin, self.yMax, self.ySteps) + self.xi,self.yi = np.meshgrid(self.x,self.y) + print " xSteps = ", self.xSteps + print " ySteps = ", self.ySteps + print " x = ", self.x + + # interpolate, zi has all the offset values but need to be transposed + self.zi = griddata((self.x_data,self.y_data),self.z_data,(self.xi,self.yi),method=self.method) + self.zi = np.transpose(self.zi) + + + def compensate(self) : + # get our nearest integer position + self.xpos = int(round(self.h['x-pos'])) + self.ypos = int(round(self.h['y-pos'])) + + zo = safeOffset + + if self.xpos >= self.xMin and self.xpos <= self.xMax and self.ypos >= self.yMin and self.ypos <= self.yMax : + self.Xn = self.xpos - self.xMin + self.Yn = self.ypos - self.yMin + + # get the nearest compensation offset and convert to counts (s32) with a scale (float) + # Requested offset == counts * scale + zo = self.zi[self.Xn,self.Yn] + if np.isnan(zo) : + zo = safeOffset + + self.scale = 0.001 + compensation = int(zo / self.scale) + return compensation + + + def run(self) : + import hal, time + + self.h = hal.component("compensation") + self.h.newpin("enable-in", hal.HAL_BIT, hal.HAL_IN) + self.h.newpin("enable-out", hal.HAL_BIT, hal.HAL_OUT) + self.h.newpin("scale", hal.HAL_FLOAT, hal.HAL_IN) + self.h.newpin("counts", hal.HAL_S32, hal.HAL_OUT) + self.h.newpin("clear", hal.HAL_BIT, hal.HAL_IN) + self.h.newpin("x-pos", hal.HAL_FLOAT, hal.HAL_IN) + self.h.newpin("y-pos", hal.HAL_FLOAT, hal.HAL_IN) + self.h.newpin("z-pos", hal.HAL_FLOAT, hal.HAL_IN) + self.h.newpin("fade-height", hal.HAL_FLOAT, hal.HAL_IN) + self.h.ready() + + s = linuxcnc.stat() + + currentState = States.START + prevState = States.STOP + + try: + while True: + time.sleep(update) + + # get linuxcnc task_state status for machine on / off transitions + s.poll() + + if currentState == States.START : + if currentState != prevState : + print("\nCompensation entering START state") + prevState = currentState + + # do start-up tasks + print(" %s last modified: %s" % (self.filename, time.ctime(os.path.getmtime(self.filename)))) + + prevMapTime = 0 + + self.h["counts"] = 0 + + # transition to IDLE state + currentState = States.IDLE + + elif currentState == States.IDLE : + if currentState != prevState : + print("\nCompensation entering IDLE state") + prevState = currentState + + # stay in IDLE state until compensation is enabled + if self.h["enable-in"] : + currentState = States.LOADMAP + + elif currentState == States.LOADMAP : + if currentState != prevState : + print("\nCompensation entering LOADMAP state") + prevState = currentState + + mapTime = os.path.getmtime(self.filename) + + if mapTime != prevMapTime: + self.loadMap() + print(" Compensation map loaded") + prevMapTime = mapTime + + # transition to RUNNING state + currentState = States.RUNNING + + elif currentState == States.RUNNING : + if currentState != prevState : + print("\nCompensation entering RUNNING state") + prevState = currentState + + if self.h["enable-in"] : + # enable external offsets + self.h["enable-out"] = 1 + + fadeHeight = self.h["fade-height"] + zPos = self.h["z-pos"] + + if fadeHeight == 0 : + compScale = 1 + elif zPos < fadeHeight: + compScale = (fadeHeight - zPos)/fadeHeight + if compScale > 1 : + compScale = 1 + else : + compScale = 0 + + if s.task_state == linuxcnc.STATE_ON : + # get the compensation if machine power is on, else set to 0 + # otherwise we loose compensation eoffset if machine power is cycled + # when copensation is enable + compensation = self.compensate() + self.h["counts"] = compensation * compScale + self.h["scale"] = self.scale + else : + self.h["counts"] = 0 + + else : + # transition to RESET state + currentState = States.RESET + + elif currentState == States.RESET : + if currentState != prevState : + print("\nCompensation entering RESET state") + prevState = currentState + + # reset the eoffsets counts register so we don't accumulate + self.h["counts"] = 0 + # toggle the clear output + self.h["clear"] = 1; + time.sleep(0.1) + self.h["clear"] = 0; + + # disable external offsets + #self.h["enable-out"] = 0 + + # transition to IDLE state + currentState = States.IDLE + + except KeyboardInterrupt: + raise SystemExit + +comp = Compensation() +comp.run() diff --git a/custom_postgui.hal b/custom_postgui.hal new file mode 100644 index 0000000..6705080 --- /dev/null +++ b/custom_postgui.hal @@ -0,0 +1,55 @@ +# Include your customized HAL commands here +# The commands in this file are run after the AXIS GUI (including PyVCP panel) starts + +net mpg pyvcp.mpg-chkbtn => cncextension.enableMpg + +#spindle +net spindle-select <= pyvcp.spindle-select.Milling + +# Vacuume and coolant +addf xor2.0 servo-thread +net sigVacuumeBtn pyvcp.vacuume-chkbtn => xor2.0.in0 +net sigIoctlFlood iocontrol.0.coolant-flood => xor2.0.in1 +net vacuume <= xor2.0.out +net vacuume => cncextension.wr1 + +addf xor2.1 servo-thread +net sigAirBtn pyvcp.air-chkbtn => xor2.1.in0 +net sigIoctlAir iocontrol.0.coolant-mist => xor2.1.in1 +net air <= xor2.1.out +net air => cncextension.wr2 + +#alcohol +net alcohol pyvcp.alcohol-scale-i => cncextension.pwm +net alcoholPrime pyvcp.prime-chkbtn => cncextension.pwmPrime + +net spindle-inhibit => pyvcp.feed-inhibit-led + +addf or2.2 servo-thread +net touchoffall-btn pyvcp.touchoffall-btn => or2.2.in0 +net button1 cncextension.button2 => or2.2.in1 + +net m600 halui.mdi-command-00 <= pyvcp.m600-btn +net g0x0y0 halui.mdi-command-01 <= pyvcp.x0y0-btn +net g53g0x0y0z0 halui.mdi-command-02 <= pyvcp.g53g0x0y0z0-btn +net g53g0z0 halui.mdi-command-03 <= pyvcp.g53g0z0-btn +net g10l20x0y0z0 halui.mdi-command-04 <= or2.2.out +net g10l20x20y20 halui.mdi-command-05 <= pyvcp.microscope-btn +net g53g10l20pin halui.mdi-command-06 <= pyvcp.touchoffpin-btn +net touchofzpin halui.mdi-command-07 <= pyvcp.touchofz-btn + +#Collisionavoid + +net collision-stop => pyvcp.collision-led +net chuck pyvcp.chuck-chkbtn => collisionavoid.chuck +setp pyvcp.chuck-chkbtn.changepin 1 + +#Autolevel +net levelpin pyvcp.level-chkbtn => compensation.enable-in +net runalprobe halui.mdi-command-08 <= pyvcp.alprobe-btn + +#Autolevel spinbox display +net alprsx pyvcp.alprsx => pyvcp.alprsxDisp +net alprsy pyvcp.alprsy => pyvcp.alprsyDisp +net alintervl pyvcp.alintervl => pyvcp.alintervlDisp +net alheight pyvcp.alheight => pyvcp.alheightDisp diff --git a/gcodeUtils/3dscann.ngc b/gcodeUtils/3dscann.ngc new file mode 100644 index 0000000..af92f8e --- /dev/null +++ b/gcodeUtils/3dscann.ngc @@ -0,0 +1,77 @@ +% +(Workpice Settings) +# = 10 +# = 10 +# = 20 + +(Imaging Settings) +# = 5 +# = 5 +# = 5 + +(Milling Settings) +# = 2000 +# = 8 +# = 1 + + +# = 0 + +g90 +G64 P0.05 + +O010 while [[-1*#] LT #] + + (MAKE IMAGES) + #1 = 0 + #2 = 0 + g0 z[#+#] + g0 x0y0 + M102 + + + O100 while [#1 LT [#]] + #2 = 0 + g0 x0 y[#1] + M103 + O101 while [#2 LT [#]] + #2 = [#2+#] + g0 x[#2] + M103 + O102 if [ #1 EQ 0 ] + # = [#+1] + O102 endif + O101 endwhile + #1 = [#1+#] + # = [#+1] + O100 endwhile + M104 + + (AREA MILL) + G0 X0.0 Y0.0 + M3 + G4 P10 + G1 F[#] Z# + + #1 = 0 + O110 while [#1 LT #] + x0 y[#1] + G1 z# + x[#] + #1 = [#1+#] + G0 z[#+1] + O110 endwhile + + O111 if [ #1 NE # ] + x0 y# + G1 z# + x[#] + G0 z[#+1] + O111 endif + + m5 + G4 P12 + + #=[#-#] +O010 endwhile +% diff --git a/gcodeUtils/AreaMill.ngc b/gcodeUtils/AreaMill.ngc new file mode 100644 index 0000000..9c5993b --- /dev/null +++ b/gcodeUtils/AreaMill.ngc @@ -0,0 +1,29 @@ +% +# = 2500 +# = 75 +# = 90 +# = 3 + +G91 G28 Z0 +M3 +G0 G90 X0.0 Y0.0 S2400 +G0 z5 + + +#1 = 0 +O100 while [#1 LT #] + G1 F[#] z0 + x[#] + #1 = [#1+#] + G0 z5 + x0 y[#1] +O100 endwhile + +g1 z0 +F[#] x[#] +G0 z5 +x0 y0 + +G91 G28 Z0 +G90 +% diff --git a/gcodeUtils/XCut.ngc b/gcodeUtils/XCut.ngc new file mode 100644 index 0000000..a00dcfb --- /dev/null +++ b/gcodeUtils/XCut.ngc @@ -0,0 +1,33 @@ +% +# = 2000 +# = 110 +# = 3 +# =1 + +(G91 G28 Z0) +G91 G28 Z0 +G90 +M3 s24000 +G00 G90 X0.0 Y0.0 +G0 z5 +G1 F[#] + + +#1 = 0 +O100 while [#1 gt 0-[#-# ]] + #1 = [#1-#] + G1 F[#] z[#1] + x[#] + G0 z5 + x0 +O100 endwhile + +G1 F[#] +z[0-#] +x[#] +G0 z5 +x0 +G91 G28 Z0 +G90 +M2 +% diff --git a/gcodeUtils/YCut.ngc b/gcodeUtils/YCut.ngc new file mode 100644 index 0000000..1570169 --- /dev/null +++ b/gcodeUtils/YCut.ngc @@ -0,0 +1,29 @@ +% +# = 2000 +# = 110 +# = 3 +# =1 + +G91 G28 Z0 +G00 G90 X0.0 Y0.0 S0 +G0 z5 +G1 F[#] + + +#1 = 0 +O100 while [#1 gt 0-[#-#]] + #1 = [#1-#] + G1 F[#] z[#1] + Y[#] + G0 z5 + Y0 +O100 endwhile + +G1 F[#] +z[0-#] +Y[#] +G0 z5 +Y0 +G91 G28 Z0 +G90 +% diff --git a/gcodeUtils/scann.ngc b/gcodeUtils/scann.ngc new file mode 100644 index 0000000..23b9b7d --- /dev/null +++ b/gcodeUtils/scann.ngc @@ -0,0 +1,36 @@ +% +# = 50 +# = 50 +# = 5 +# = 5 + +# = 0 +# = 0 + +#1 = 0 +#2 = 0 + +M102 +g90 g0 x0y0 + +O100 while [#1 LT [#]] + #2 = 0 + g0 x0 y[#1] + M103 + O101 while [#2 LT [#]] + #2 = [#2+#] + g0 x[#2] + M103 + O102 if [ #1 EQ 0 ] + # = [#+1] + O102 endif + O101 endwhile + #1 = [#1+#] + # = [#+1] +O100 endwhile + +M104 + +(DEBUG, # by #) + +% diff --git a/gcodeUtils/scannSync.ngc b/gcodeUtils/scannSync.ngc new file mode 100644 index 0000000..08e4376 --- /dev/null +++ b/gcodeUtils/scannSync.ngc @@ -0,0 +1,34 @@ +% +# = 50 +# = 50 +# = 5 +# = 5 + +# = 0 +# = 0 + +#1 = 0 +#2 = 0 + +M101 +g90 g0 x0y0 + +O100 while [#1 LT [#]] + #2 = 0 + g0 x0 y[#1] + M100 + O101 while [#2 LT [#]] + #2 = [#2+#] + g0 x[#2] + M100 + O102 if [ #1 EQ 0 ] + # = [#+1] + O102 endif + O101 endwhile + #1 = [#1+#] + # = [#+1] +O100 endwhile + +(DEBUG, # by #) + +% diff --git a/microscopeTuchoff.ngc b/microscopeTuchoff.ngc new file mode 100644 index 0000000..e69de29 diff --git a/rectangle_probe.ngc b/rectangle_probe.ngc new file mode 100644 index 0000000..a67ef41 --- /dev/null +++ b/rectangle_probe.ngc @@ -0,0 +1,73 @@ +( Rectangular area probe ) +o sub +# = 0 +# = #<_hal[pyvcp.alprsx]> +# = #<_hal[pyvcp.alintervl]> +# = 0 +# = #<_hal[pyvcp.alprsy]> +# = #<_hal[pyvcp.alintervl]> +# = #<_hal[pyvcp.alheight]> +# = 0 +# = 50 + +# = [fix [abs[# - #]/# + 1]] +# = [fix [abs[# - #]/# + 1]] + +O1 if[[#*#-#] lt #] + # = [# + 1] +O1 endif +O2 if[[#*#-#] lt #] + # = [# + 1] +O2 endif + +# = [# * #] + +(debug, Probing # * # = # points) +(LOGOPEN,probe-results.txt) + +# = 0 +# = 0 +G53 G0 Z0 +G0 X0 Y0 +G30.1 +G0Z# +F# + +O12 while [# lt #] + # = 0 + # = [#+#*#] +O13 if [# gt #] + # = # +O13 endif + G0 Y# +O14 while [# lt #] + # = 0 +O15 if [[#/2] - fix[#/2] eq 0] + # = [# + # * #] +O15 else + # = [# + # * [# - # - 1]] +O15 endif +O16 if [# gt #] + # = # +O16 endif + # = 1 + G0 x# + G38.2Z# (probe until contact, toward work, with error) + G0z# + # = [#5061+#5181] + # = [#5062+#5182] + # = #5063 + (LOG,# # #) + # = [# + 1] +O14 endwhile + G0Z# + # = [# + 1] +O12 endwhile + +(LOG,# Finished: total points = #) +(LOGCLOSE) +(debug, Finished: see probe-results.txt) + +G0Z# +G0X#Y# +o endsub diff --git a/testpannel.hal b/testpannel.hal new file mode 100644 index 0000000..b18a081 --- /dev/null +++ b/testpannel.hal @@ -0,0 +1 @@ +loadusr pyvcp ./pannel.xml diff --git a/tool-change.ngc b/tool-change.ngc new file mode 100644 index 0000000..33cd7cd --- /dev/null +++ b/tool-change.ngc @@ -0,0 +1,96 @@ +O SUB + +#<_UseInches> = 0 ( set to 1 to use inches here, or 0 to use millimeters; should match units on tool.tbl dimensions ) +#<_TravelZ> = 0 ( machine Z coordinate for travelling, typically near max Z to avoid ever hitting the work ) +#<_TravelFeed> = 3000 ( feedrate used for general Z moves when avoiding G0 ) +#<_ProbeX> = 9.5 ( machine X coordinate of switch/touch-off plate ) +#<_ProbeY> = 24.5 ( machine Y coordinate of switch/touch-off plate ) +#<_ProbeFastZ> = -20 ( machine Z coord to move to before starting probe, longest tool should not touch switch at this Z ) +#<_ProbeMinZ> = -70 ( machine Z coord to stop probe, shortest tool must touch switch at this Z, must be > min Z ) +#<_ProbeRetract> = 0.2 ( small distance to retract before approaching switch/touch-off plate second time ) +#<_ProbeFastFeed> = 3000 ( feed rate for moving to _ProbeFastZ ) +#<_ProbeFeed1> = 750 ( feed rate for touching switch/touch-off plate first time ) +#<_ProbeFeed2> = 3 ( feed rate for touching switch/touch-off plate second time ) +#<_ToolChangeX> = 100 ( machine X coordinate to pause at for manual tool changing ) +#<_ToolChangeY> = 100 ( machine Y coordinate to pause at for manual tool changing ) +#<_MistOnDuringProbe> = 0 ( set to 1 for mist, or 2 for coolant, or 0 for nothing during probing, to clear switch of swarf ) + + + +O100 IF [ EXISTS[#<_ToolDidReferance>] EQ 0 ] + #<_ToolDidReferance> = 0 +O100 ENDIF + +O101 IF [ EXISTS[#<_IgnoreFirstTool>] EQ 0 ] + #<_IgnoreFirstTool> = 0 +O101 endif + +O102 IF [ #<_IgnoreFirstTool> EQ 0 ] + #<_IgnoreFirstTool> = 1 +O102 ELSE + M49 (disable spindle speed and feed rate override controls) + O105 IF [ #<_ToolDidReferance> EQ 0 ] + G49 ( clear tool length compensation prior to saving state if this is first time ) + O105 ENDIF +(AXIS,hide) + M6 ( do the normal M6 stuff ) + M70 ( save current modal state ) + + M9 ( turn off coolant, will be restored on return if it was on ) + M5 ( turn off spindle, cannot be on during the probe ) + G[21 - #<_UseInches>] ( use inches or millimeters as required here, units will be restored on return ) + G30.1 ( save current position in #5181-#5183... ) + G49 ( clear tool length compensation ) + G90 ( use absolute positioning here ) + G94 ( use feedrate in units/min ) + G40 ( turn cutter radius compensation off here ) + + G53 G1 F[#<_TravelFeed>] Z[#<_TravelZ>] ( go to high travel level on Z ) + + O200 IF [ #<_ToolDidReferance> EQ 0 ] + G53 G0 X[#<_ProbeX>] Y[#<_ProbeY>] ( to probe switch ) + G53 G1 F[#<_ProbeFastFeed>] Z[#<_ProbeFastZ>]( move tool closer to switch -- we shouldn't hit it ) + G54 G1 F[#<_ProbeFeed1>] G91 ( use relative positioning ) + O103 IF [ #<_MistOnDuringProbe> EQ 1 OR #<_MistOnDuringProbe> EQ 2 ] + M[7 + #<_MistOnDuringProbe> - 1] ( turn on mist/coolant ) + O103 ENDIF + G38.2 Z[#<_ProbeMinZ> - #<_ProbeFastZ>] F[#<_ProbeFeed1>] ( trip switch slowly ) + G0 Z[#<_ProbeRetract>] ( go up slightly ) + G38.2 Z[#<_ProbeRetract>*-1.25] F[#<_ProbeFeed2>] ( trip switch very slowly ) + M9 ( turn off mist ) + G90 ( use absolute positioning ) + #<_ToolZRef> = #5063 ( save trip point ) + #<_ToolZLast> = #<_ToolZRef> ( save last tool Z position ) + G53 G1 F[#<_TravelFeed>] Z[#<_TravelZ>] ( return to safe level ) + #<_ToolDidReferance> = 1 ( we have been in this section to set reference value already ) + G53 G0 X[#<_ToolChangeX>] Y[#<_ToolChangeY>] ( nice place for changing tool ) + (MSG, SwitchTool ) + M0 ( pause execution ) + O200 ENDIF + + G53 G0 X[#<_ProbeX>] Y[#<_ProbeY>] ( to high place directly over switch ) + G53 G1 F[#<_ProbeFastFeed>] Z[#<_ProbeFastZ>]( move tool closer to switch -- we shouldn't hit it ) + G54 G1 F[#<_ProbeFeed1>] G91 ( use relative positioning ) + O104 IF [ #<_MistOnDuringProbe> EQ 1 OR #<_MistOnDuringProbe> EQ 2 ] + M[7 + #<_MistOnDuringProbe> - 1] ( turn on mist/coolant ) + O104 ENDIF + G38.2 Z[#<_ProbeMinZ> - #<_ProbeFastZ>] F[#<_ProbeFeed1>] ( trip switch slowly ) + G0 Z[#<_ProbeRetract>] ( go up slightly ) + G38.2 Z[#<_ProbeRetract>*-1.25] F[#<_ProbeFeed2>] ( trip switch very slowly ) + M9 ( turn off mist ) + G90 ( use absolute positioning ) + #<_ToolZ> = #5063 ( save new tool length ) + G43.1 Z[#<_ToolZ> - #<_ToolZRef>] ( set new tool length Z offset, we do this now to show operator even though it has to be set again after M72 ) + #<_ToolZLast> = #<_ToolZ> ( save last tool length ) + G53 G0 Z[#<_TravelZ>] ( return to safe level ) + G53 G0 X[#5181] Y[#5182] ( return to saved position ) + G53 G0 Z[#5183] + M72 ( restore modal state ) + M3 + G43.1 Z[#<_ToolZ> - #<_ToolZRef>] ( set new tool length Z offset ) + M48 (enable spindle speed and feed rate override controls) + (AXIS,show) +O102 ENDIF + +O ENDSUB +M2 diff --git a/tool-job-begin.ngc b/tool-job-begin.ngc new file mode 100644 index 0000000..ca1255e --- /dev/null +++ b/tool-job-begin.ngc @@ -0,0 +1,12 @@ +O SUB +( Filename: tool-job-begin.ngc ) +( LinuxCNC Manual Tool-Change Subroutines for Milling Machines version 1.1: subroutine 2/2 ) +( Intended to be run as a remapped M600 command. Used to indicate that the next tool change, M6, is the first tool of a job. ) + +#<_ToolDidFirst> = 0 ( new job, we haven't yet called for the first time ) +#<_IgnoreFirstTool> = 0 +#<_ToolDidReferance> = 0 +G49 ( clear tool height adjustment ) + +O ENDSUB +M2 diff --git a/tool.tbl b/tool.tbl new file mode 100644 index 0000000..adf759b --- /dev/null +++ b/tool.tbl @@ -0,0 +1,15 @@ +T1 P1 D2 Z0 +T2 P2 D2 Z0 +T3 P3 D2 Z0 +T4 P4 D2 Z0 +T5 P5 D2 Z0 +T6 P6 D2 Z0 +T7 P7 D2 Z0 +T8 P8 D2 Z0 +T9 P9 D2 Z0 +T10 P10 D2 Z0 +T11 P11 D2 Z0 +T12 P12 D2 Z0 +T13 P13 D2 Z0 +T14 P14 D2 Z0 + diff --git a/touchoff-z-to-bead.ngc b/touchoff-z-to-bead.ngc new file mode 100644 index 0000000..283c1a1 --- /dev/null +++ b/touchoff-z-to-bead.ngc @@ -0,0 +1,38 @@ +o sub +#<_TravelZ> = 0 ( machine Z coordinate for travelling, typically near max Z to avoid ever hitting the work ) +#<_ProbeX> = 9.5 ( machine X coordinate of switch/touch-off plate ) +#<_ProbeY> = 24.5 ( machine Y coordinate of switch/touch-off plate ) +#<_ProbeFastZ> = -20 ( machine Z coord to move to before starting probe, longest tool should not touch switch at this Z ) +#<_ProbeMinZ> = -70 ( machine Z coord to stop probe, shortest tool must touch switch at this Z, must be > min Z ) +#<_ProbeRetract> = 0.2 ( small distance to retract before approaching switch/touch-off plate second time ) +#<_ProbeFastFeed> = 3000 ( feed rate for moving to _ProbeFastZ ) +#<_ProbeFeed1> = 750 ( feed rate for touching switch/touch-off plate first time ) +#<_ProbeFeed2> = 3 ( feed rate for touching switch/touch-off plate second time ) + +#<_ProbeToBeadZ> = 13.1 + +m73 +G30.1 ( save current position in #5181-#5183... ) +G49 ( clear tool length compensation ) +G90 ( use absolute positioning here ) +G94 ( use feedrate in units/min ) +G40 ( turn cutter radius compensation off here ) +G54 (use coordinate system 1) +M49 (disable spindle speed and feed rate override controls) +G53 G0 Z[#<_TravelZ>] ( go to high travel level on Z ) +G53 G0 X[#<_ProbeX>] Y[#<_ProbeY>] ( to probe switch ) +G53 G1 F[#<_ProbeFastFeed>] Z[#<_ProbeFastZ>] ( move tool closer to switch -- we shouldn't hit it ) +G54 G1 F[#<_ProbeFeed1>] G91 ( use relative positioning ) + +G38.2 Z[#<_ProbeMinZ> - #<_ProbeFastZ>] F[#<_ProbeFeed1>] ( trip switch slowly ) +G0 Z[#<_ProbeRetract>] ( go up slightly ) +G38.2 Z[#<_ProbeRetract>*-1.25] F[#<_ProbeFeed2>] ( trip switch very slowly ) +G90 ( use absolute positioning ) +G10 L20 p0 z[#<_ProbeToBeadZ>] +(DEBUG, #5063) +G53 G0 Z[#<_TravelZ>] ( return to safe level ) +G53 G0 X[#5181] Y[#5182] +G53 G0 Z[#5183] +M48 (enable spindle speed and feed rate override controls) +o endsub +M2