# gpio_ablib.py 
#
# Python functions collection to easily manage the I/O lines and 
# Daisy modules with the following Acme Systems boards:
# ARIA G25 (http://www.acmesystems.it/aria) 
#
# (C) 2021 Sergio Tanzilli <tanzilli@acmesystems.it>
# (C) 2021 Acme Systems srl (http://www.acmesystems.it)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

import os.path
import time
import fcntl
import struct
import thread
import threading
import select

legacy_id=False

# Connectors pin assignments
# 'pin name', 'kernel id'  # pin description

aria_north = {
	'2'  :  96,
	'3'  :  97,
	'4'  :  98,
	'5'  :  99,
	'6'  : 100,
	'7'  : 101,
	'8'  : 102,
	'9' :  103,
	'10' : 104,
	'11' : 105,
	'12' : 106,
	'13' : 107,
	'14' : 108,
	'15' : 109,
	'16' : 110,
	'17' : 111,
	'18' : 112,
	'19' : 113,
	'20' : 114,
	'21' : 115,
	'22' : 116,
	'23' : 117,
}

aria_east = {
	'2'  : 118,
	'3'  : 119,
	'4'  : 120,
	'5'  : 121,
	'6'  : 122,
	'7'  : 123,
	'8'  : 124,
	'9' :  125,
	'10' : 126,
	'11' : 127,
}

aria_south = {
	'2'  :  53,
	'3'  :  52,
	'4'  :  51,
	'5'  :  50,
	'6'  :  49,
	'7'  :  48,
	'8'  :  47,
	'9' :   46,
	'10' :  45,
	'11' :  44,
	'12' :  43,
	'13' :  42,
	'14' :  41,
	'15' :  40,
	'16' :  39,
	'17' :  38,
	'18' :  37,
	'19' :  36,
	'20' :  35,
	'21' :  34,
	'22' :  33,
	'23' :  32,
}

aria_west = {
	'9' :   54,
	'10' :  55,
	'11' :  56,
	'12' :  57,
	'13' :  58,
	'14' :  59,
	'15' :  60,
	'16' :  61,
	'17' :  62,
	'18' :  63,
	'20' :  75,
	'21' :  76,
	'22' :  77,
	'23' :  78,
}

# Kernel IDs descriptors for each connector
connectors = {
	'N'   :  aria_north,
	'E'   :  aria_east,
	'S'   :  aria_south,
	'W'   :  aria_west,
}

#New pin assigment table
pin2kid = {
	'N2'  :  96,
	'N3'  :  97,
	'N4'  :  98,
	'N5'  :  99,
	'N6'  : 100,
	'N7'  : 101,
	'N8'  : 102,
	'N9' :  103,
	'N10' : 104,
	'N11' : 105,
	'N12' : 106,
	'N13' : 107,
	'N14' : 108,
	'N15' : 109,
	'N16' : 110,
	'N17' : 111,
	'N18' : 112,
	'N19' : 113,
	'N20' : 114,
	'N21' : 115,
	'N22' : 116,
	'N23' : 117,
	'E2'  : 118,
	'E3'  : 119,
	'E4'  : 120,
	'E5'  : 121,
	'E6'  : 122,
	'E7'  : 123,
	'E8'  : 124,
	'E9' :  125,
	'E10' : 126,
	'E11' : 127,
	'S2'  :  53,
	'S3'  :  52,
	'S4'  :  51,
	'S5'  :  50,
	'S6'  :  49,
	'S7'  :  48,
	'S8'  :  47,
	'S9' :   46,
	'S10' :  45,
	'S11' :  44,
	'S12' :  43,
	'S13' :  42,
	'S14' :  41,
	'S15' :  40,
	'S16' :  39,
	'S17' :  38,
	'S18' :  37,
	'S19' :  36,
	'S20' :  35,
	'S21' :  34,
	'S22' :  33,
	'S23' :  32,
	'W9' :   54,
	'W10' :  55,
	'W11' :  56,
	'W12' :  57,
	'W13' :  58,
	'W14' :  59,
	'W15' :  60,
	'W16' :  61,
	'W17' :  62,
	'W18' :  63,
	'W20' :  75,
	'W21' :  76,
	'W22' :  77,
	'W23' :  78,
}

pinmode = {
	"OUTPUT" : "low",
	"LOW" : "low",
	"HIGH" : "high",
	"INPUT" : "in",
}

pinlevel = {
	"HIGH" : 1,
	"LOW"  : 0,
}

def get_gpio_path(kernel_id):
	kernel_id=kernel_id-32	
	
	iopath="/sys/class/gpio/pio" 
	if kernel_id>=0 and kernel_id<=31:
		iopath="%sA%d" % (iopath,kernel_id-0)
	if kernel_id>=32 and kernel_id<=63:
		iopath="%sB%d" % (iopath,kernel_id-32)
	if kernel_id>=64 and kernel_id<=95:
		iopath="%sC%d" % (iopath,kernel_id-64)
			
	return iopath		
			

def get_kernel_id(connector_name,pin_number):
	return connectors[connector_name][pin_number]

def export(kernel_id):
	iopath=get_gpio_path(kernel_id)
	if not os.path.exists(iopath): 
		f = open('/sys/class/gpio/export','w')
		f.write(str(kernel_id-32))
		f.close()

def unexport(kernel_id):
	iopath=get_gpio_path(kernel_id)
	if os.path.exists(iopath): 
		f = open('/sys/class/gpio/unexport','w')
		f.write(str(kernel_id-32))
		f.close()

def direction(kernel_id,direct):
	iopath=get_gpio_path(kernel_id)
	if os.path.exists(iopath): 
		f = open(iopath + '/direction','w')
		f.write(direct)
		f.close()

def set_value(kernel_id,value):
	iopath=get_gpio_path(kernel_id)
	if os.path.exists(iopath): 
		f = open(iopath + '/value','w')
		f.write(str(value))
		f.close()

def get_value(kernel_id):
	if kernel_id<>-1:
		iopath=get_gpio_path(kernel_id)
		if os.path.exists(iopath): 
			f = open(iopath + '/value','r')
			a=f.read()
			f.close()
			return int(a)

def set_edge(kernel_id,value):
	iopath=get_gpio_path(kernel_id)
	if os.path.exists(iopath): 
		if value in ('none', 'rising', 'falling', 'both'):
		    f = open(iopath + '/edge','w')
		    f.write(value)
		    f.close()

class Pin():
	"""
	AriaG25 pins related class
	"""
	kernel_id=None
	fd=None

	def __init__(self,pin,mode):
		self.kernel_id=pin2kid[pin]
		export(self.kernel_id)
		direction(self.kernel_id,pinmode[mode])

		iopath=get_gpio_path(self.kernel_id)
		if os.path.exists(iopath): 
			self.fd = open(iopath + '/value','r')

	def digitalWrite(self,level):
		set_value(self.kernel_id,pinlevel[level])

	def high(self):
		set_value(self.kernel_id,1)
		
	def low(self):
		set_value(self.kernel_id,0)

	def on(self):
		set_value(self.kernel_id,1)
		
	def off(self):
		set_value(self.kernel_id,0)

	def set_value(self,value):
		return set_value(self.kernel_id,value)

	def get_value(self):
		return get_value(self.kernel_id)

	get = get_value

	def wait_edge(self,fd,callback,debouncingtime):
		debouncingtime=debouncingtime/1000.0 # converto in millisecondi
		timestampprec=time.time()
		counter=0
		po = select.epoll()
		po.register(fd,select.EPOLLET)
		while True:
			events = po.poll()
			timestamp=time.time()
			if (timestamp-timestampprec>debouncingtime) and counter>0:
				callback()
			counter=counter+1
			timestampprec=timestamp

	def set_edge(self,value,callback,debouncingtime=0):
		if self.fd!=None:
			set_edge(self.kernel_id,value)
			thread.start_new_thread(self.wait_edge,(self.fd,callback,debouncingtime))
			return
		else:		
			thread.exit()

