# camron blackburn 1.10.20
# updates for swabcasting, zach fredin ~april 2020
# gcode cheat sheet: https://www.hartwiginc.com/wp-content/uploads/2015/02/G-Code-List_Okuma.pdf
import argparse

class GcodeMarking:
    ''' class for generating gcode file '''
    def __init__(self, gfile):
        self.gfile = gfile

    def writeln(self, string):
        gfile.write(string + "\n")
        return

    def setup_gfile(self, power, feed):
        self.writeln(";---- Declar and Set Variables -----")
        self.writeln("DVAR $POWER")
        self.writeln("DVAR $FEED")
        self.writeln("")

        self.writeln("$POWER=%s" % power)
        self.writeln("$FEED=%s" % feed)
        self.writeln("")

        # set absolute position
        self.writeln("G90")
        # roughing cycle ?? what does it mean to the oxford ?
        self.writeln("G71")
        # zeroing position
        self.writeln("G92 X0 Y0")
        self.writeln("G108")
        self.writeln("")

        self.writeln(";Laser cutting")
        self.writeln(";tool.diameter = 0.045")
        self.writeln("")

        self.writeln('FARCALL "ATTENUATOR.PGM" s$POWER')
        self.writeln('MSGCLEAR -1')
        self.writeln('MSGDISPLAY 1, "program Started"')
        self.writeln("")

        return

    def raster_square(self, xpos, ypos, dim, fill, speed_var):
        ''' xpos, ypos (float, mm): x and y position of upper left corner for square
         dim (float, mm): dimensions of square
         fill (float, mm): fill interval, distance between line marking
         '''
        self.writeln("")
        self.writeln("G0 X%s Y%s" % (xpos, ypos))
        self.writeln("BEAMON")

        xmax = xpos + dim
        num_passes = int(dim / fill)
        curr_y = ypos

        # sawtooth pattern fill
        for i in range(0, num_passes*2+1):
            if i % 2 == 0:
                self.writeln("G1 X%s F$%s" % (xmax, speed_var))
            else:
                self.writeln("G1 X%s Y%s F$%s" % (xpos, round(curr_y-fill, 3), speed_var))
                curr_y = curr_y-fill

        self.writeln("BEAMOFF")
        return

    def outline_circle(self, reps, xpos, ypos, radius):
        ''' reps: number of times around each circle
            xpos, ypos (float, mm): x and y position of center point of circle
            radius (float, mm): radius of circle
        '''
        self.writeln("")
        self.writeln("G0 X%s Y%s" % (xpos, ypos))
        self.writeln("BEAMON")
        self.writeln("REPEAT %s" % (reps))
        self.writeln("G2 I%s F$FEED" % radius)
        self.writeln("ENDRPT")
        self.writeln("BEAMOFF")
        return

    def outline_circle_array(self, radius, spacing, reps, qty_x, qty_y):
        ''' creates a rectilinear array of outline circles starting at the
            current X,Y location and proceding X+/Y+.
            radius (float, mm): radius of each circle
            spacing (float, mm): center-to-center spacing between circles
            reps: number of times around each circle
            qty_x, qty_y (float, mm): number of circles in each direction
        '''
        for i in range(1, qty_x):
            for j in range(1, qty_y):
                self.outline_circle(reps, i * spacing, j * spacing, radius)
        return

    def horiz_line(self, xpos, ypos, length, dreps):
        ''' creates a horizontal line starting at at a given X,Y location and
            proceding X+
            xpos, ypos (float, mm): x and y position of the left endpoint
            length (float, mm): length of line
            dreps: number of double passes (back-and-forth)
        '''
        self.writeln("")
        self.writeln("G0 X%s Y%s" % (xpos, ypos))
        self.writeln("BEAMON")
        self.writeln("REPEAT %s" % (dreps))
        self.writeln("G1 X%s F$FEED" % (length))
        self.writeln("G1 X0 F$FEED")
        self.writeln("ENDRPT")
        self.writeln("BEAMOFF")
        return

    def horiz_line_array(self, length, spacing, dreps, oreps, qty):
        ''' creates an array of horizontal lines starting at the current X,Y
            location and proceding X+/Y+.
            length (float, mm): length of each horizontal line
            spacing (float, mm): vertical distance between each line
            dreps: number of double passes (back-and-forth)
            oreps: number of overall program repetitions
            qty: number of lines
        '''
        self.writeln("")
        self.writeln("REPEAT %s" % (oreps))
        for i in range(0, qty):
            self.horiz_line(0, i * spacing, length, dreps)
        self.writeln("ENDRPT")
        return

    def set_gfile_var(self, name, val):
        self.writeln("$%s=%s" % (name, val))
        return

    def def_array_variables(self):
        self.writeln(";---- Declare and Set Variables -----")
        for i in range(ARRAY_DIM):
            self.writeln("DVAR $POWER%s" % i)
            self.writeln("DVAR $SPEED%s" % i)
        self.writeln("")

    def square_array(self, dim, fill, min_power, max_power, min_speed, max_speed):
        ''' includes setup commands/etc'''
        power_step = (max_power - min_power) / ARRAY_DIM
        speed_step = (max_speed - min_speed) / ARRAY_DIM

        self.def_array_variables()

        # set power values
        self.writeln("")
        self.set_gfile_var("POWER0", min_power)
        curr_power = min_power
        for i in range(1, ARRAY_DIM):
            curr_power += power_step
            self.set_gfile_var("POWER%s" % i, curr_power)
        self.writeln("")

        # set speed values
        self.writeln("")
        self.set_gfile_var("SPEED0", min_speed)
        curr_speed = min_speed
        for i in range(1, ARRAY_DIM):
            curr_speed += speed_step
            self.set_gfile_var("SPEED%s" % i, curr_speed)
        self.writeln("")

        # set absolute position
        self.writeln("G90")
        # roughing cycle ?? what does it mean to the oxford ?
        self.writeln("G71")
        # zeroing position
        self.writeln("G92 X0 Y0")
        self.writeln("G108")
        self.writeln("")

        self.writeln(";Laser cutting")
        self.writeln(";tool.diameter = 0.045")
        self.writeln('MSGDISPLAY 1, "Program Starting"')
        self.writeln("")
        self.writeln("")

        curr_xpos = 0
        curr_ypos = 0
        for i in range(ARRAY_DIM):
            for j in range(ARRAY_DIM):
                self.writeln('FARCALL "ATTENUATOR.PGM" s$POWER%s' % j)
                self.writeln("MSGCLEAR -1")
                self.writeln('MSGDISPLAY 1, "starting square at x %s y %s with SPEED%s POWER%s"' % (curr_xpos, curr_ypos, i, j))
                self.raster_square(curr_xpos, curr_ypos, dim, fill, "SPEED%s" % i)
                curr_xpos = curr_xpos + dim + 1
            curr_ypos = curr_ypos - dim - 1
            curr_xpos = 0

        return

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="generate g-code for laser circles")
    parser.add_argument("name", help="gcode file name")
    parser.add_argument("--power", help="laser power setting")
    parser.add_argument("--feed", help="laser feed")
    parser.add_argument("--spacing", help="circle spacing")
    parser.add_argument("--radius", help="circle radius")
    parser.add_argument("--qty_x", help="number of circles in X direction")
    parser.add_argument("--qty_y", help="number of circles in Y direction")
    args = parser.parse_args()

    ''' various combinations tried and commented out... '''

    gfile = open(args.name, "w")
    marking = GcodeMarking(gfile)
    '''marking.square_array(4, float(args.fill), 0.5, 4, 20, 60)'''
    marking.setup_gfile(float(args.power), float(args.feed))
    '''marking.outline_circle_array(float(args.radius), float(args.spacing), int(args.qty_x), int(args.qty_y))'''
    '''marking.outline_circle_array(float(0.05), float(0.05), int(40), int(50), int(50))'''
    '''marking.outline_circle_array(float(0.025), float(0.05), int(40), int(50), int(50))'''
    marking.horiz_line_array(float(2), float(0.05), int(5), int(5), int(20))
    gfile.close()