#!/usr/bin/env python

'''

# File Name : uarm_core.py
# Author : Joey Song
# Version : V1.0
# Date : 6 Jan, 2016
# Modified Date : 6 Jan, 2016
# Description : This documents is for uarm ROS Library and ROS package
# Copyright(C) 2016 uArm Team. All right reserved.

'''


# All libraries needed to import
# Import system library
import sys
import time
import rospy

# Import uarm for python library
#from UArmForPython.uarm_python import Uarm
import pyuarm

# Import messages type
from std_msgs.msg import String
from std_msgs.msg import Float32
from std_msgs.msg import UInt8
from std_msgs.msg import Int32
from uarm.msg import Angles
from uarm.msg import Coords
from uarm.msg import CoordsWithTime
from uarm.msg import CoordsWithTS4

# Read current Coords function
def readCurrentCoords():
	cc = uarm.get_position()
	print 'Current location is x: %2.2fcm, y: %2.2fcm, z: %2.2fcm.' %(float(cc[0]), float(cc[1]), float(cc[2]))

	rospy.set_param('current_x', cc[0])
	rospy.set_param('current_y', float(cc[1]))
	rospy.set_param('current_z', float(cc[2]))

# Read current Angles function
def readCurrentAngles():
	ra = {}
	ra['s1'] = uarm.get_servo_angle(0)
	ra['s2'] = uarm.get_servo_angle(1)
	ra['s3'] = uarm.get_servo_angle(2)
	ra['s4'] = uarm.get_servo_angle(3)

	print 'Four Servo Angles: %2.2f, %2.2f, %2.2f and %2.2f degrees.' %(ra['s1'], ra['s2'],ra['s3'],ra['s4'])

	rospy.set_param('servo_1',ra['s1'])
	rospy.set_param('servo_2',ra['s2'])
	rospy.set_param('servo_3',ra['s3'])
	rospy.set_param('servo_4',ra['s4'])

# Read stopper function
def readStopperStatus():
	val = uarm.get_tip_sensor()
	if val == True:
		print 'Stopper is actived'
		rospy.set_param('stopper_status','HIGH')
	else:
		print 'Stopper is not actived'
		rospy.set_param('stopper_status','LOW')


# Write single angle
def writeSingleAngle(num, angle):
	# limits of each servo
	s3_max = 170 - uarm.get_servo_angle(1)
	if num == 1:
		if angle > 160:
			angle = 160
		elif angle < 20:
			angle = 20
	elif num == 2:
		if angle > 140:
			angle = 140
		elif angle < 0:
			angle = 0
	elif num == 3:
		if angle > s3_max:
			angle = s3_max
		elif angle < 0:
			angle = 0
	elif num == 4:
		if angle > 180:
			angle = 180
		elif angle < 0:
			angle = 0
	else:
		print "Input incorrect: wrong servo number"

	uarm.set_servo_angle(num-1, angle)


# Write coordinate: x y z and speed
def writeCoordinate(coordinate):
	x = float(coordinate[1])
	y = float(coordinate[2])
	z = float(coordinate[3])
	if len(coordinate) == 4:
		speed = 5

	elif len(coordinate) == 5:
		speed = float(coordinate[4])

	else:
		print "Input incorrect: wrong number of arguments"

	if y > 0:
		y = -y

	uarm.set_position(x, y, z, speed)


# Main connect function
def connectFcn():

	global failed_number
	global connectionStatus
	connectionStatus = 0
	global uarm
	global controlFcnLoop
	global listenerFcn
	controlFcnLoop = True
	listenerFcn = True

	if len(sys.argv)<2:
		# 1 means no input argument
		failed_number = 20
		controlFcnLoop = False
		listenerFcn = False
		print 'Input Incorrect'
		sys.exit(0)
		return 1

	if sys.argv[1] == 'connect':
		print '======================================================='
		print 'Connecting ...'
		print '======================================================='

		if len(sys.argv) == 2:
			failed_number = 21
			uarm = pyuarm.get_uarm()
			uarm.connect()
			connectionStatus = 1

			print 'Connected'
			return 21

		elif len(sys.argv) == 3:

			failed_number = 22

			if sys.argv[2] == 'l':
				failed_number = 23
				uarm = pyuarm.get_uarm()
				uarm.connect()
				controlFcnLoop = False
				print 'Connected'
			else:
				connectionStatus = 1
				print 'Connected'
			return 22

		# followed by l - directly listen to nodes
		elif len(sys.argv) == 4:
			failed_number = 23
			uarm =  pyuarm.get_uarm()
			uarm.connect()

			connectionStatus = 1
			print 'Connected'

			if sys.argv[3] == 'l':
				print 'Directly to listen mode'
				controlFcnLoop = False
				return 25
			return 23

		else:
			# 2 means input argument is wrong
			return 24
	#if sys.argv[1] == 'd'


# Main control function
def controlFcn():

	while controlFcnLoop:
		commands = raw_input("Input Commands (Input h to see all commands): ")

		if commands == 'h':
			print ' '
			print 'Control mode'
			print '=================================================================='
			print 'h                  - show help and list all commands'
			print 'cc                 - current coordinates'
			print 'ra                 - read angles'
			print 'wa a1 a2 a3 a4     - write angles'
			print '                     for example: wa 120 80 40 40'
			print 'wsa servo a        - write single angle'
			print '                     for example: wsa 1 50'
			print 'mt x y z           - move to a point with speed(defaut: 5mm/s)'
			print '                     for example: mt 0 12 12'
			print 'mt x y z speed     - move to a point with speed(mm/s)'
			print '                     for example: mt 0 12 12 10'
			print 'pp 1/0             - pump on/off'
			print 'ss                 - status of stopper'
			print 'at                 - attach uarm'
			print 'de                 - detach uarm'
			print 'l                  - begin listener mode'
			print 'e                  - exit'
			print '=================================================================='
			print ' '

		elif commands == 'l':
			print 'Exit: Break the control fuction loop'
			break;

		elif commands == 'e':
			print 'Detach all servos and exit the program'
			uarm.set_servo_detach()
			sys.exit(0)

		elif len(commands) == 0:
			print 'len is 0'

		else:
			commands_split = commands.split()

			# Detach
			if commands_split[0] == 'detach'or commands_split[0] == 'de':
				if len(commands_split) == 1:
					uarm.set_servo_detach()
				else:
					print 'no other commands should be input'
				pass

			# Attach
			if commands_split[0] == 'attach'or commands_split[0] == 'at':
				if len(commands_split) == 1:
					uarm.set_servo_attach()
				else:
					print 'no other commands should be input'
				pass

			# Stopper Status
			elif commands_split[0] == 'stopperStatus'or commands_split[0] == 'ss':
				if len(commands_split) == 1:
					readStopperStatus()
				else:
					print 'no other commands should be input'
				pass

			# Pump Control
			elif commands_split[0] == 'pump' or commands_split[0] == 'pp':
				if len(commands_split) == 1:
					print 'Status should be input'
				elif len(commands_split) >2:
					print 'Too many inputs'
				else:
					if commands_split[1] == '1' or commands_split[1].lower() == 'high' or commands_split[1].lower() == 'on':
						uarm.set_pump(1)
					elif commands_split[1] == '0' or commands_split[1].lower() == 'low'or commands_split[1].lower() == 'off':
						uarm.set_pump(0)
					else:
						print 'Incorrect inputs, should input 1 / 0 / HIGH / LOW / ON / OFF'
				pass

			# Current Coordinates
			elif commands_split[0] == 'currentCoords' or commands_split[0] == 'cc':
				if len(commands_split) == 1:
					readCurrentCoords()
				else:
					print 'no other commands should be input'
				pass

			# Write Single Angles
			elif commands_split[0] == 'writeSingleAngles' or commands_split[0] == 'wsa':
				if len(commands_split) == 3:
					writeSingleAngle(int(commands_split[1]), float(commands_split[2]))
				else:
					print 'Input incorrect: Wrong number of arguments'
				pass

			# Write Angles
			elif commands_split[0] == 'wa':
				if len(commands_split) == 5:
					writeSingleAngle(1, float(commands_split[1]))
					writeSingleAngle(2, float(commands_split[2]))
					writeSingleAngle(3, float(commands_split[3]))
					writeSingleAngle(4, float(commands_split[4]))
				else:
					print 'Input incorrect: Wrong number of arguments'
				pass

			# Current Angles
			elif commands_split[0] == 'readAngles' or commands_split[0] == 'ra':
				if len(commands_split) == 1:
					 readCurrentAngles()
				else:
					print 'no other commands should be input'
				pass

			# Move Tos
			elif commands_split[0] == 'moveTo' or commands_split[0] == 'mt':
				if len(commands_split) == 4 or len(commands_split) == 5:
					writeCoordinate(commands_split)
				else:
					print 'Input incorrect: Wrong number of arguments'
			else:
				pass


# pump control function once received data from topic
def pumpCallack(data):

	data_input = data.data

	if data_input == 0:
		uarm.pump_control(0)
		print 'Pump: Off'
	elif data_input == 1:
		uarm.pump_control(1)
		print 'Pump: On'
	else:
		pass


# pump str control function once received data from topic
def pumpStrCallack(data):

	data_input = data.data
	print data_input

	if data_input.lower() == 'low' or data_input.lower() == 'off':
		uarm.pump_control(0)
		print 'Pump: Off'
	elif data_input.lower() == 'on' or data_input.lower() == 'high':
		uarm.pump_control(1)
		print 'Pump: On'
	else:
		pass


# angles control function once received data from topic
def writeAnglesCallback(servos):

	servo = {}
	servo['s1'] = servos.servo_1
	servo['s2'] = servos.servo_2
	servo['s3'] = servos.servo_3
	servo['s4'] = servos.servo_4
	for i in servo:
		if servo[i]>180: servo[i] = 180
		if servo[i]<0: servo[i] = 0

	uarm.set_servo_angle(0, servo['s1'])
	uarm.set_servo_angle(1, servo['s2'])
	uarm.set_servo_angle(2, servo['s3'])
	uarm.set_servo_angle(3, servo['s4'])

	print 'Movement: Moved Once'


# attach or detach uarm function once received data from topic
def attchCallback(attachStatus):
	data_input = attachStatus.data

	if data_input.lower() == 'attach' :
		uarm.attach_all_servos()
		print 'uArm: Attach'
	elif data_input.lower() == 'detach':
		uarm.detach_all_servos()
		print 'uArm: Detach'
	else:
		pass


# move to function once received data from topic
def moveToCallback(coords):
	x = coords.x
	y = coords.y
	if y>0:
		y = -y
	z = coords.z
	uarm.set_position(x, y, z, 2)
	print 'Movement: Moved Once'


# moveto functions once received data from topic
def moveToTimeCallback(coordsAndT):
	x = coordsAndT.x
	y = coordsAndT.y
	if y>0:
		y = -y
	z = coordsAndT.z
	time = coordsAndT.time
	if time == 0:
		uarm.set_position(x, y, z, 2)
	else:
		uarm.set_position(x, y, z, 2)

	print 'Movement: Moved Once'
	pass


# moveto functions once received data from topic
def moveToTimeAndS4Callback(coordsAndTS4):

	x = coordsAndTS4.x
	y = coordsAndTS4.y
	if y>0:
		y = -y
	z = coordsAndTS4.z
	time = coordsAndTS4.time
	s4 = coordsAndTS4.servo_4
	if s4 > 180: s4 = 180
	if s4 <0 : s4 =0
	uarm.set_position(x, y, z, s4, 0, time, 0, 0)
	print 'Movement: Moved Once'
	pass


# print current coords once received data from topic
def currentCoordsCallback(times):

	Times = times.data
	if Times == 1:
		readCurrentCoords()
	elif Times < 0:
		pass
	else:
		for i in range(0,Times):
			readCurrentCoords()
			time.sleep(1)


# print current angles once received data from topic
def readAnglesCallback(times):

	Times = times.data
	if Times == 1:
		readCurrentAngles()
	elif Times < 0:
		pass
	else:
		for i in range(0,Times):
			readCurrentAngles()
			time.sleep(1)


# print stoppers status once received data from topic
def stopperStatusCallback(times):
	Times = times.data
	if Times == 1:
		readStopperStatus()
	elif Times < 0:
		pass
	else:
		for i in range(0,Times):
			readStopperStatus()
			time.sleep(1)


# monitor mode for listening to all topics
def listener():
	print ' '
	print 'Begin monitor mode - listening to all fucntional topics'
	print '======================================================='
	print '         Use rqt_graph to check the connection         '
	print '======================================================='

	rospy.init_node('uarm_core',anonymous=True)

	rospy.Subscriber("uarm_status",String, attchCallback)
	rospy.Subscriber("pump_control",UInt8, pumpCallack)
	rospy.Subscriber("pump_str_control",String, pumpStrCallack)

	rospy.Subscriber("read_coords",Int32, currentCoordsCallback)
	rospy.Subscriber("read_angles",Int32, readAnglesCallback)
	rospy.Subscriber("stopper_status",Int32, stopperStatusCallback)

	rospy.Subscriber("write_angles",Angles, writeAnglesCallback)
	rospy.Subscriber("move_to",Coords, moveToCallback)
	rospy.Subscriber("move_to_time",CoordsWithTime, moveToTimeCallback)
	rospy.Subscriber("move_to_time_s4",CoordsWithTS4, moveToTimeAndS4Callback)

	rospy.spin()
	pass


# show eroors
def processFailedNum(failed_number):

	if failed_number > 20 and failed_number < 26:
		print 'ERROR: Input Connection Address Is Incorrect'
	if failed_number == 20:
		print 'uArm: Please Connect uArm first '


if __name__ == '__main__':

	try:
		# Connect uarm first
		return_value = connectFcn()

		# Control uarm through commands
		controlFcn()

		# Monitor mode
		if listenerFcn == True:
			listener()

	except:
		processFailedNum(failed_number)
		print 'ERROR: Execution Failed'
		pass

	finally:
		print 'DONE: Program Stopped'
		pass