#!/usr/bin/env python
# -*- coding: utf-8 -*-
# for command line parsing
import getopt, sys
import socket 
import threading
import struct
import GPIO as gpio
import pcf8591 as adc
import smbus
import time
import RPi.GPIO as GPIO
import dht11
from time import sleep
import snap7
from snap7.util import *
import struct
from snap7.snap7types import *
# --------------------------- 显示屏
from lib import LCD_2inch4
from PIL import Image,ImageDraw,ImageFont
from screen import initText,drawText,showText, getInformation
import logging
import spidev as SPI
import os

# ----------------------- plc
# Area表示内存区。取值0x84:D区  0x83：M区  0x82：Q区  0x81：I区  0x1C:C区（16Bit）  0x1D：T区（16Bit）
# ！d 1位    !f 4位 
global PLC_address_M
global PLC_address_Q
global PLC_address_I
global PLC_address_C
global PLC_address_T
global start_address_M0
global start_address_M1
global start_address_M2
global start_address_M3
global start_address_M4
global start_address_M5
global start_address_M6
global start_address_M12
global size
PLC_address_V = 0x84
PLC_address_M = 0x83   # 电梯楼层地址  M 区地址
PLC_address_Q = 0x82  # 电梯状态   Q 区地址
PLC_address_I = 0x81
PLC_address_C = 0x1C
PLC_address_T = 0x1D

start_address_M0 = 0   # 0开门  1关门   3上升  4下降  5上升P  6下降P  7停止
start_address_M1 = 1   # 2二楼上升  3三楼上升  4四楼上升  5五楼上升  6六楼下降
start_address_M2 = 2   # 1一楼上升  2二楼下降  3三楼下降  4四楼下降  5五楼下降 
start_address_M3 = 3   # 6上限报警  7下限报警
start_address_M4 = 4   # 0平层标志位  1一楼平层 2二楼平层 3三楼平层 4四楼平层 5五楼平层 6六楼平层
start_address_M5 = 5	  # 1一楼按钮P  ...
start_address_M6 = 6   # 1电梯在一楼  2电梯在二楼  3电梯在三楼 ...
start_address_M12 = 12  # 0 非运动状态 1上升标志位 2下降标志位  3减速标志位  6报警  7轿门关 
size = 1

start_address_staus_1 = 0
start_address_staus_2 = 1
start_address_staus_3 = 2
# ----------------------------------------------------------plc
plc = snap7.client.Client()
plc.set_connection_type(3)
plc.connect("192.168.2.1",0,1)
print("CON STATUS: ", plc.get_connected())
# ----------------------------------------------------------
# ------------------------树莓派

bus = smbus.SMBus(0) # 查询寄存器地址sudo i2cdetect -y 0
TRIG = 17
ECHO = 22
port = 7500
pin_1 = 26
pin_2 = 23
Pin_3 = 24
pin_4 = 25
global Temp_sensor
global BuzzerPin
Temp_sensor=21  # 温度湿度gpio
BuzzerPin = 20 # buzzer 控制器
options, remainder = getopt.gnu_getopt(sys.argv[1:], 'p:h', ['port=', 'help'])

for opt, arg in options:
	if opt in ('-p', '--port'):
		port = int(arg)
	elif opt in ('-h', '--help'):
		print arg
		print "Transducer interface module (TIM) daemon"
		print "-TBD-"
		exit() # TODO: is it a good practice

# TODO: hardware auto config
# - I2C
# - GPIO

ip = "0.0.0.0"

# listen_num 5 = # TODO:

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

def command_handler(sock, addr, TRIG, ECHO): # TODO: function name tbd
	#LCD_Screen()
	print("Source: %s:%s" % addr)
	msg_rx = sock.recv(2048)
	id, val = struct.unpack('>ii', msg_rx)

	if (id == 10):  # 读取seTemperature
		print("read temp")
		val = ReadTemperature_humidity(1)  
		pass
	elif (id == 11):  # 读取seLight
		print("read light")
		val = read(1, I2CAddress0)
		pass
	elif (id == 12): # 测量seHumidity
		print("read humd")
		val = ReadTemperature_humidity(2)
		pass
	elif (id == 13):  # 读取seDistence
		print("read distence")
		val = ReadDistenceHC(TRIG, ECHO)
		pass
	elif (id == 14): # 读取seAcceleration_x
		print("read acceleration_x")
		val = ReadAcc(I2CAddress1)
		val = val[0]
		pass
	elif (id == 15):
		print("read acceleration_y")
		val = ReadAcc(I2CAddress1)
		val = val[1]
		pass
	elif (id == 16):
		print("read acceleration_z")
		val = ReadAcc(I2CAddress1)
		val = val[2]
		pass
	elif(id == 17): # 读取seHS
		print("read HS sensor")
		val = read(3, I2CAddress0)
		pass
	elif(id == 18): # 
		print("read voltage")
		val = 5 # V
		pass
	elif (id == 19):
		print("read current")
		val = 51  # mA
		pass
	elif (id == 110):
		print("read power")
		val = 0.25
		pass
	elif(id == 111): # 读取presure
		val = 10
		pass
	elif(id == 112): # sensor free
		val = 1
		pass
	elif(id == 113): # sensor free
		val = 1
		pass
	elif (id == 20): # 执行 acLamp
		print("write lamp %d" % val)
		if val:
			gpio.write_gpio_pin('relay0', True)
		else:
			gpio.write_gpio_pin('relay0', False)
	elif (id == 21): # 执行 acHunidifier
		print("write lamp %d" % val)
		if val:
			gpio.write_gpio_pin('relay1', True)
		else:
			gpio.write_gpio_pin('relay1', False)
	elif (id == 22): # 执行 acbuzzer
		print("write lamp %d" % val)
		if val:
			buzzer(val)
		else:
			buzzer(val)
	elif (id == 23): # 执行 acLEDScreen
		print("write lamp %d" % val)
		if val:
			gpio.write_gpio_pin('relay3', True)
		else:
			gpio.write_gpio_pin('relay3', False)
	elif (id == 24): # 执行 acLEDScreen
		print("write lamp %d" % val)
	elif (id == 25): # 执行 acLEDScreen
		print("write lamp %d" % val)
	elif(id == 30): # get floor
		value = read_plc(PLC_address_M, start_address_M6, size)
		val = pow(value)
		pass
	elif(id == 31): # get state of floor
		val = ReadMemory(plc, PLC_address_M, 0, 0, S7WLByte)
		#val = pow(value)
		pass
	elif(id == 32): # 灯状态
		val = 1
		pass
	elif(id == 33): # get state of lamp
		val = ReadMemory(plc, PLC_address_V, 7300, 4, S7WLDWord)
		val = str(val)
		pass
	elif(id == 34): # get free
		val = 1
		pass
	elif(id == 35): # get free
		val = 1
		pass
	elif(id == 36): # get free
		val = 1
		pass
	elif(id == 37): # get free
		val = 1
		pass
	elif(id == 38): # get free
		val = 1
		pass
	elif(id == 39): # get free
		val = 1
		pass
	elif(id == 40): # set floor
		if val:
			WriteMemory(plc, PLC_address_M, start_address_M0, 0, S7WLBit, True)
		pass
	elif(id == 41): # set state of door
		if val:
			WriteMemory(plc, PLC_address_M, start_address_M5, val, S7WLBit, True)
		pass
	elif(id == 42): # 向下冲底
		if val == 1:
			WriteMemory(plc, PLC_address_M, start_address_M12, 2, S7WLBit, True)
		elif val == 0:
			WriteMemory(plc, PLC_address_M, start_address_M12, 2, S7WLBit, False)
		pass
	elif(id == 43): # 向上冲顶
		if val ==1:
			WriteMemory(plc, PLC_address_M, start_address_M12, 1, S7WLBit, True)
		elif val == 0:
			WriteMemory(plc, PLC_address_M, start_address_M12, 1, S7WLBit, False)
		pass
	elif(id == 44): # set free
		val = 1
		pass
	elif(id == 45): # set free
		val = 1
		pass
	elif(id == 46): # set free
		val = 1
		pass
	elif(id == 47): # set free
		val = 1
		pass
	elif(id == 48): # set free
		val = 1
		pass
	print("id:%d\t val:%d" % (id, val))
	msg_tx = struct.pack('>i', val)
	sock.send(msg_tx)
	sock.close()

def LCD_Screen():
	RST = 27
	DC= 25
	BL = 18
	bus = 0
	device = 0 
	logging.basicConfig(level= logging.DEBUG)
	draw, image, disp,Font = initText()
	flag_LCD = True
	value_floor_number = read_plc(PLC_address_M, start_address_M6, size)
	value_floor_number = pow(value_floor_number)
	value_floor_temperature = ReadTemperature_humidity(1)
	value_floor_humidity = ReadTemperature_humidity(1)
	draw, image, disp = drawText(u"当前楼层：",20,40,Font[0],draw,image,disp)
	draw, image, disp = drawText(str(value_floor_number),160,40,Font[0],draw,image,disp)
	draw, image, disp = drawText(u"当前温度：",20,80,Font[0],draw,image,disp)
	draw, image, disp = drawText(str(value_floor_temperature),160,80,Font[0],draw,image,disp)
	draw, image, disp = drawText(u"当前湿度：",20,120,Font[0],draw,image,disp)
	draw, image, disp = drawText(str(value_floor_humidity),160,120,Font[0],draw,image,disp)
	while(flag_LCD):
		text, X, Y= getInformation()
		for i in range(len(X)):
			draw, image, disp = drawText(text[i],X[i],Y[i],Font[0],draw,image,disp)
		showText(draw, image, disp)
		flag_LCD = False

def WriteMemory(self, PLC_address, byte, bit, datatype, value):   # plc例如M2.0  ： byte 为2    bit 为0      datatype类型选择S7WLBit  value（True or False）
	result = self.read_area(PLC_address, 0, byte, datatype)
	if datatype  == S7WLBit:
		set_bool(result, 0, bit, value)
	if datatype == S7WLByte or datatype == S7WLWord:
		set_int(result, 0, value)
	if datatype == S7WLReal:
		set_real(result, 0, value)
	if datatype == S7WLWord:
		set_dword(result, 0, value)
	self.write_area(PLC_address, 0, byte, result)


def ReadMemory(self, PLC_address,byte,bit,datatype):
	if PLC_address == 0x84:
		result=self.read_area(PLC_address,0,byte,datatype)
	  	if datatype==S7WLBit:  #这里的datatype应该可以写数字（参照上面写入output），或者像这里直接写类型
	      		return get_bool(result,0,bit)
	  	if datatype==S7WLByte or datatype==S7WLWord:
	      		return int(get_int(result,0))/256
	  	if datatype==S7WLReal:        #elif更好
	      		return get_real(result,0)
	  	if datatype==S7WLDWord:
	      		return get_dword(result,0)
	else:
		result=self.read_area(PLC_address,1,byte,datatype)
	  	if datatype==S7WLBit:  #这里的datatype应该可以写数字（参照上面写入output），或者像这里直接写类型
	      		return get_bool(result,0,bit)
	  	if datatype==S7WLByte or datatype==S7WLWord:
	      		return int(get_int(result,0))/256
	  	if datatype==S7WLReal:        #elif更好
	      		return get_real(result,0)
	  	if datatype==S7WLDWord:
	      		return get_dword(result,0)

def ReadTemperature_humidity(flag):
	#define GPIO 14 as DHT11 data pin
	
	# Main program block
	GPIO.setwarnings(False)
	GPIO.setmode(GPIO.BCM)       # Use BCM GPIO numbers
	# Initialise display
	#  lcd_init()
	instance = dht11.DHT11(pin = Temp_sensor)
	#get DHT11 sensor value
	result = instance.read()
	#print"Temperature = ",result.temperature,"C"," Humidity = ",result.humidity,"%"
	if (flag == 1):
		return result.temperature
	else:
		return result.humidity


def ReadAcc(I2CAddress): # 读取加速度信息
	data_acc_x = bus.read_byte_data(I2CAddress, 0x04)
	data_acc_y = bus.read_byte_data(I2CAddress, 0x06)
	data_acc_z = bus.read_byte_data(I2CAddress, 0x08)
	acc_x = data_acc_x / 16
	acc_y = data_acc_y / 16
	acc_z = data_acc_z / 16
	acc = [acc_x, acc_y, acc_z]
	return acc 


def ReadDistenceHC(TRIG, ECHO):  # 测距

	GPIO.setmode(GPIO.BCM)
	GPIO.setup(TRIG, GPIO.OUT)
	GPIO.setup(ECHO, GPIO.IN)
	GPIO.output(TRIG, True)
	time.sleep(0.001)
	GPIO.output(TRIG, False)
	while GPIO.input(ECHO) == 0:
		pass
	pulse_start = time.time()

	while GPIO.input(ECHO) == 1:
		pass
	pulse_end = time.time()

	puluse_duration = pulse_end - pulse_start
	distence  = puluse_duration * 343 / 2 * 100
	distence = round(distence, 2)
	print("distence: {}cm".format(distence))
	GPIO.cleanup()
	return distence 


def ReadDistenceNoopLoop():
	ser = serial.Serial("/dev/ttyAMA0", 115200) # 串口操作， 加速度计
	count = ser.inWaiting()
	if count >= 32:
    		dat = ser.read(count)
			#print(dat)
	        for i in range(16):
	        	if dat[i] == 0x57 and dat[i+1] == 0xff and dat[i+2] == 0 :
	        		if dat[i+12]+dat[i+13]*255==0 :
	        			print("out of range!")
	        		else:
	        			z=dat[i+11]
	        			print("status = ")
	        			
	        			p = dat[i+12]+dat[i+13]*255
	        			print("strength = ")
	        			
	        			q = dat[i+8]+dat[i+9]*255
	        			print("distence = ")

def buzzer(flag):
		GPIO.setmode(GPIO.BCM)       # Numbers GPIOs by physical location
		GPIO.setup(BuzzerPin, GPIO.OUT)
		GPIO.output(BuzzerPin, GPIO.HIGH)
		if (flag == 1):
					GPIO.output(BuzzerPin, GPIO.LOW) # 低电平响
		else:
	    			GPIO.output(BuzzerPin, GPIO.HIGH)


## delay? twice?
def read(chn, address): #channel   # 读取PCF寄存器数据
    if chn == 0:
        bus.write_byte(address,0x40)   #发送一个控制字节到设备
    if chn == 1:
        bus.write_byte(address,0x41)
    if chn == 2:
        bus.write_byte(address,0x42)
    if chn == 3:
        bus.write_byte(address,0x43)
    bus.read_byte(address)             #从设备读取单个字节，而不指定设备寄存器。
    return bus.read_byte(address)      #返回某通道输入的模拟值A/D转换后的数字值

def write(val):
    temp = val  # 将字符串值移动到temp
    temp = int(temp) # 将字符串改为整数类型
    # print temp to see on terminal else comment out
    bus.write_byte_data(address, 0x40, temp) 
    #写入字节数据，将数字值转化成模拟值从AOUT输出

def Ac_Setup(address):
    bus.write_byte_data(address, 0x22)
    bus.write_byte_data(address, 0x20)
    bus.write_byte_data(address, 0x00)
    bus.write_byte_data(address, 0x05)

def write_plc(address, start, data, dbnumber = 0):
	value = plc.read_area(address, dbnumber, start, data)
	snap7.util.set_bool(value, dbnumber, start, 1)
	plc.write_area(address, dbnumber, start, value)


def read_plc(address, start, data, dbnumber = 0):
	if address ==0x83:
		value = plc.read_area(address, dbnumber, start, data)
		data = struct.unpack('!b', value)
		struct.calcsize('!b')
		#print(data[0])
	elif address == 0x81:
		pass
	return int(data[0])

def pow(num):
	flag = 0
	while True:
		a = num / 2
		num = a
		flag = flag + 1
		if a == 1:
			number = flag
			flag = 0
			break
	return number

def main():
	global I2CAddress0
	global I2CAddress1
	I2CAddress0 = 0x48  # PCF8592地址
	I2CAddress1 = 0x23  # 加速度计地址
	####
	gpio.init_gpio_subsystem()
	#LCD_Screen()
	gpio.register_gpio_pin('relay0', pin_1, gpio.READ_WRITE)
	gpio.register_gpio_pin('relay1', pin_2, gpio.READ_WRITE)
	gpio.register_gpio_pin('relay2', Pin_3, gpio.READ_WRITE)
	gpio.register_gpio_pin('relay3', pin_4, gpio.READ_WRITE)
	val = gpio.read_gpio_pin('relay0')
	#print(val)
	####
	s.bind((ip, port))
	print("Listenning to: %s:%d" % (ip, port))
	s.listen(5) # TODO:
	connection_cnt = 0

	while True:  
		sock, addr = s.accept() 
		connection_cnt += 1
		print("Count: %d" % connection_cnt)
		thread = threading.Thread(target = command_handler, args = (sock, addr, TRIG, ECHO))
		thread.start()


if __name__ == '__main__':
	main()

