# ebb_serial.py
# Serial connection utilities for EiBotBoard
# https://github.com/evil-mad/plotink
# 
# Intended to provide some common interfaces that can be used by 
# EggBot, WaterColorBot, AxiDraw, and similar machines.
#
# Version 0.3, Dated June 28, 2016.
#
# Thanks to Shel Michaels for bug fixes and helpful suggestions. 
#
# The MIT License (MIT)
# 
# Copyright (c) 2016 Evil Mad Scientist Laboratories
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

import serial
import inkex
import gettext

def version():
	return "0.3"	# Version number for this document

def findPort():	
	#Find a single EiBotBoard connected to a USB port.
	try:
		from serial.tools.list_ports import comports
	except ImportError:
		comports = None		
		return None
	if comports:
		comPortsList = list(comports())
		EBBport = None
		for port in comPortsList:
			if port[1].startswith("USB-SERIAL CH340"):
				EBBport = port[0] 	#Success; EBB found by name match.
				break	#stop searching-- we are done.
		if EBBport is None:
			for port in comPortsList:
				if port[2].startswith("USB VID:PID=04D8:FD92"):
					EBBport = port[0] #Success; EBB found by VID/PID match.
					break	#stop searching-- we are done.				
		return EBBport

def testPort( comPort ):
	'''
	Return a SerialPort object
	for the first port with an EBB (EiBotBoard; EggBot controller board).
	YOU are responsible for closing this serial port!
	'''		
	if comPort is not None:
		try:
			serialPort = serial.Serial( comPort, timeout=3.0 ) # 1 second timeout!
			serialPort.write( 'v\r' )
			strVersion = serialPort.readline()			
			if strVersion and strVersion.startswith( 'EBB' ):
				return serialPort
							
			serialPort.write( 'v\r' ) 
			strVersion = serialPort.readline()
			if strVersion and strVersion.startswith( 'EBB' ):
				return serialPort					
			serialPort.close()
		except serial.SerialException:
			pass
		return None
	else:
		return None

def openPort():
	foundPort = findPort()
	serialPort = testPort( foundPort )
	if serialPort:
		return serialPort
	return None

def closePort(comPort):
	if comPort is not None:
		try:
			comPort.close()
		except serial.SerialException:
			pass

def query( comPort, cmd ):
	if (comPort is not None) and (cmd is not None):
		response = ''
		try:
			comPort.write( cmd )
			response = comPort.readline()
			nRetryCount = 0
			while ( len(response) == 0 ) and ( nRetryCount < 100 ):
				# get new response to replace null response if necessary
				response = comPort.readline()
				nRetryCount += 1
			if cmd.strip().lower() not in ["v","i","a", "mr","pi","qm"]:
				#Most queries return an "OK" after the data requested.
				#We skip this for those few queries that do not return an extra line.
				unused_response = comPort.readline() #read in extra blank/OK line
				nRetryCount = 0
				while ( len(unused_response) == 0 ) and ( nRetryCount < 100 ):
					# get new response to replace null response if necessary
					unused_response = comPort.readline()
					nRetryCount += 1
		except:
			inkex.errormsg( gettext.gettext( "Error reading serial data." ) )
		return response
	else:
		return None

def command( comPort, cmd ):
	if (comPort is not None) and (cmd is not None):
		try:
			comPort.write( cmd )
			response = comPort.readline()
			nRetryCount = 0
			while ( len(response) == 0 ) and ( nRetryCount < 100 ):
				# get new response to replace null response if necessary
				response = comPort.readline()
				nRetryCount += 1
				inkex.errormsg("Retry" + str(nRetryCount))
			if response.strip().startswith("OK"):
				pass  # 	inkex.errormsg( 'OK after command: ' + cmd ) #Debug option: indicate which command.
			else:
				if ( response != '' ):
					inkex.errormsg( 'Error: Unexpected response from EBB.') 
					inkex.errormsg( '   Command: ' + cmd.strip() )
					inkex.errormsg( '   Response: ' + str( response.strip() ) )
				else:
					inkex.errormsg( 'EBB Serial Timeout after command: ' + cmd )

# 		except Exception,e: 
# 			inkex.errormsg(  str(e))	#For debugging: one may wish to display the error.
		except:
			inkex.errormsg( 'Failed after command: ' + cmd )
			pass 