# Two stage example Virtual Machine file
# moves get set in Main
# usb port needs to be set in initInterfaces
# Nadya Peek Dec 2014

# ------IMPORTS-------
from pygestalt import nodes
from pygestalt import interfaces
from pygestalt import machines
from pygestalt import functions
from pygestalt.machines import elements
from pygestalt.machines import kinematics
from pygestalt.machines import state
from pygestalt.utilities import notice
from pygestalt.publish import rpc     # remote procedure call dispatcher
import time
import io
import csv



# ------VIRTUAL MACHINE------
class virtualMachine(machines.virtualMachine):

    """
    Need to insert the /dev/tty.usbserial.XXXXXX as per your machine
    """

    def initInterfaces(self):
        if self.providedInterface:
            self.fabnet = self.providedInterface
        # providedInterface is defined in the virtualMachine class.
        else:
            self.fabnet = interfaces.gestaltInterface(
                            'FABNET',
                            interfaces.serialInterface(
                                baudRate=115200,
                                interfaceType='ftdi',
                                portName='/dev/ttyUSB0')
                        )

    def initControllers(self):
        self.xAxisNode = nodes.networkedGestaltNode(
            'X Axis', self.fabnet, filename='086-005a.py',
            persistence=self.persistence)
        self.yAxisNode = nodes.networkedGestaltNode(
            'Y Axis', self.fabnet, filename='086-005a.py',
            persistence=self.persistence)
        self.zAxisNode = nodes.networkedGestaltNode(
            'Z Axis', self.fabnet, filename='086-005a.py',
            persistence=self.persistence)

        # register the nodes
        self.xyzNode = nodes.compoundNode(
            self.xAxisNode, self.yAxisNode, self.zAxisNode)

    def initCoordinates(self):
        self.position = state.coordinate(['mm', 'mm', 'mm'])

    def initKinematics(self):
        self.xAxis = elements.elementChain.forward(
            [elements.microstep.forward(4),
             elements.stepper.forward(1.8),
             elements.leadscrew.forward(8),
             elements.invert.forward(True)])
        self.yAxis = elements.elementChain.forward(
            [elements.microstep.forward(4),
             elements.stepper.forward(1.8),
             elements.leadscrew.forward(8),
             elements.invert.forward(True)])
        self.zAxis = elements.elementChain.forward(
            [elements.microstep.forward(4),
             elements.stepper.forward(1.8),
             elements.leadscrew.forward(8),
             elements.invert.forward(True)])
        # direct drive on all axes
        self.stageKinematics = kinematics.direct(3)

    def initFunctions(self):
        self.move = functions.move(virtualMachine=self,
                                   virtualNode=self.xyzNode,
                                   axes=[self.xAxis, self.yAxis, self.zAxis],
                                   kinematics=self.stageKinematics,
                                   machinePosition=self.position,
                                   planner='null')
        # an incremental wrapper for the move function
        self.jog = functions.jog(self.move)
        pass

    def initLast(self):
        """
        self.machineControl.setMotorCurrents(aCurrent = 0.8,
                                             bCurrent = 0.8, cCurrent = 0.8)
        self.xNode.setVelocityRequest(0)   # clear velocity on nodes.
        Eventually this will be put in the motion planner on initialization
        to match state.
        """
        pass

    def publish(self):
        # self.publisher.addNodes(self.machineControl)
        pass

    def getPosition(self):
        return {'position': self.position.future()}

    def setPosition(self, position=[None]):
        self.position.future.set(position)

    def setSpindleSpeed(self, speedFraction):
        # self.machineControl.pwmRequest(speedFraction)
        pass

    def enableMotorsRequest(self):
        # return self.enableRequest()
        pass

    def disableMotorsRequest(self):
        # return self.disableRequest()
        pass

	def loadSVG():
		moves = []
		fname = raw_input("Enter filename: ")
		if len(fname) == 0:
			fname = "plot.csv"
		try:
			fh = open(fname)
		except:
			print "Error: File (", fname, ") cannot be opened."
			exit()
		plot = csv.reader(fh)
		for ele in plot:
			x = round(float(ele[0]), 1)
			y = round(float(ele[1]), 1)
			z = round(float(ele[0]), 1)
			move = (x,y,z)
			print move
			moves.append(move)
		fh.close()
		print ("Reading done!")	
		
# ------IF RUN DIRECTLY FROM TERMINAL------
if __name__ == '__main__':

    # The persistence file remembers the node you set.
    # It'll generate the first time you run the
    # file. If you are hooking up a new node, delete the previous persistence
    # file.
    stages = virtualMachine(persistenceFile="test.vmp")

    # You can load a new program onto the nodes if you are so inclined.
    # This is currently set to
    # the path to the 086-005 repository on Nadya's machine.
    # stages.xyNode.loadProgram('../../../086-005/086-005a.hex')

    # This is a widget for setting the potentiometer to set the motor
    # current limit on the nodes.
    # The A4982 has max 2A of current, running the widget will
    # interactively help you set.
    # stages.xyNode.setMotorCurrent(0.7)

    # This is for how fast the system will run
    stages.xyzNode.setVelocityRequest(1)

    # Some random moves to test with

    # print calibration instructions
    print "Type <w|s> to move xAxis fwd/backward"
    print "Type <a|d> to move yAxis up/down"
    print "Type <t|g> to move zAxis up/down"
    print "Type <q> when done with calibration"

    x = y = z = 0

    while True:
        ch = raw_input("x[w,s], y[a,d], z[t,g], q :")
        if ch == 'q':
            break
        elif ch == 'w':
            stages.jog([-2, 0, 0])
        elif ch == 's':
            stages.jog([2, 0, 0])
        elif ch == 'a':
            stages.jog([0, -2, 0])
        elif ch == 'd':
            stages.jog([0, 2, 0])
        elif ch == 't':
            stages.jog([0, 0, -2])
        elif ch == 'g':
            stages.jog([0, 0, 2])
        else:
            pass
    stages.initCoordinates()
    stages.initFunctions()
	
    #loadSVG()#used 4 spaces instead of tab to indent.

    # draw a square 2D, lower z then increase z
    # moves = [[0, 0, 20], [0, 45, 0], [35, 45, 0],
    #          [35, 10, 0], [0, 10, 0],
    #          [0, 0, 0], [0, 0, -20]]

	# Move!
    for move in moves:
        stages.move(move, 0)
        status1 = stages.xAxisNode.spinStatusRequest()
        status2 = stages.yAxisNode.spinStatusRequest()
        # This checks to see if the move is done.
        while status1['stepsRemaining'] > 0 or status2['stepsRemaining'] > 0 or status3['stepsRemaining'] > 0:
            time.sleep(0.001)
            status1 = stages.xAxisNode.spinStatusRequest()
            status2 = stages.yAxisNode.spinStatusRequest()
			status3 = stages.zAxisNode.spinStatusRequest()
	stages.xyzNode.disableMotorsRequest()

   
