#!/usr/bin/env python

import rospy
import rospkg
import rosparam
import yaml
import dynamic_reconfigure.client
import os
import argparse

import time
from std_msgs.msg import Float32MultiArray, Int32MultiArray, Bool, Int16, String, Int8, UInt16, UInt8
from jmoab_autopilot_ros.msg import GoalWaypoints
from sensor_msgs.msg import NavSatFix
from geometry_msgs.msg import TwistWithCovarianceStamped
import numpy as np
from simple_pid import PID

class GPS_NAV(object):

	def __init__(self, param_file, mission_file, NS):

		################################ Init/Pub/Sub #####################################
		rospy.init_node("gps_waypoints_autopilot_node", anonymous=True)

		################################ ROS Parameters #####################################
		sv_node = "gps_waypoints_server_node"

		# https://answers.ros.org/question/169866/load-yaml-with-code/
		# load yaml file to rosparam server without running server on python
		f = open(param_file, 'r')
		yamlfile = yaml.load(f)
		rosparam.upload_params("/", yamlfile)

		## get parameter from rosparam server that we just loaded above
		self.max_start_str = rosparam.get_param(sv_node+"/max_start_str")
		self.min_start_str = rosparam.get_param(sv_node+"/min_start_str")
		self.str_adj = rosparam.get_param(sv_node+"/str_adj")
		self.skid_adj = rosparam.get_param(sv_node+"/skid_adj")
		self.str_mid = rosparam.get_param(sv_node+"/str_mid")
		self.max_start_thr = rosparam.get_param(sv_node+"/max_start_thr")
		self.thr_adj = rosparam.get_param(sv_node+"/thr_adj")
		self.thr_slowest = rosparam.get_param(sv_node+"/thr_slowest")
		self.thr_mid = rosparam.get_param(sv_node+"/thr_mid")
		self.goal_dist_thresh = rosparam.get_param(sv_node+"/goal_dist_thresh")
		self.goal_ang_thresh = rosparam.get_param(sv_node+"/goal_ang_thresh")
		self.x_track_error_start = rosparam.get_param(sv_node+"/x_track_error_start")
		self.x_track_repose_dist = rosparam.get_param(sv_node+"/x_track_repose_dist")
		self.pid_hdg_out_thresh = rosparam.get_param(sv_node+"/pid_hdg_out_thresh")
		self.pid_x_out_thresh = rosparam.get_param(sv_node+"/pid_x_out_thresh")
		self.hdg_p = rosparam.get_param(sv_node+"/hdg_p")
		self.hdg_i = rosparam.get_param(sv_node+"/hdg_i")
		self.hdg_d = rosparam.get_param(sv_node+"/hdg_d")
		self.cross_p = rosparam.get_param(sv_node+"/cross_p")
		self.cross_i = rosparam.get_param(sv_node+"/cross_i")
		self.cross_d = rosparam.get_param(sv_node+"/cross_d")
		self.vel_p = rosparam.get_param(sv_node+"/vel_p")
		self.vel_i = rosparam.get_param(sv_node+"/vel_i")
		self.vel_d = rosparam.get_param(sv_node+"/vel_d")
		self.use_heartbeat = rosparam.get_param(sv_node+"/use_heartbeat")

		########################### GPS Parameters ##########################
		self.lat = 0.0
		self.lon = 0.0
		self.hdg = 0.0

		self.lat1 = 0.0
		self.lon1 = 0.0

		self.lat2 = 0.0
		self.lon2 = 0.0

		self.gps_abs_vel = 0.0

		self.use_gps1 = False
		self.use_gps2 = False

		mission_name = "mission.txt"
		# self.mission_path = os.path.join(jmoab_autopilot_ros_path, "waypoints", mission_name)
		self.mission_path = mission_file

		self.lat_target_list = []
		self.lon_target_list = []
		self.speed_target_list = []
		self.delay_target_list = []
		self.sbus_throttle_target_list = []
		self.relay_target_list = []
		self.total_points = 0
		self.target_wp = 0

		self.got_new_wps = False

		self.hb_timestamp = time.time()
		self.hb_timeout = 10.0
		self.hb_period = 0.0

		########################### PID Parameters ##########################
		## PID heading steering control
		self.setpoint_diff_hdg = 0.0

		self.max_err = 100.0	#10.0	#2.0

		self.pid_hdg = PID(self.hdg_p, self.hdg_i, self.hdg_d, setpoint=self.setpoint_diff_hdg)
		self.pid_hdg.tunings = (self.hdg_p, self.hdg_i, self.hdg_d)
		self.pid_hdg.sample_time = 0.001
		self.pid_hdg.output_limits = (-self.max_err, self.max_err)

		## PID cross track error steering control
		self.setpoint_x = 0.0

		self.max_x_err = 100.0	#2.0

		self.pid_x = PID(self.cross_p, self.cross_i, self.cross_d, setpoint=self.setpoint_x)
		self.pid_x.tunings = (self.cross_p, self.cross_i, self.cross_d)
		self.pid_x.sample_time = 0.001
		self.pid_x.output_limits = (-self.max_x_err, self.max_x_err)

		self.pid_vel = PID(self.vel_p, self.vel_i, self.vel_d, setpoint=1.0)
		self.pid_vel.tunings = (self.vel_p, self.vel_i, self.vel_d)
		self.pid_vel.sample_time = 0.001
		self.max_vel = 3.0
		self.pid_vel.output_limits = (0.0, self.max_vel)

		self.enable_update = True
		################################ Dynamic Reconfigure #####################################
		print("Wait for server node...")
		try:
			## This we will get live data from rqt_reconfigure
			self.client = dynamic_reconfigure.client.Client(sv_node, timeout=2, config_callback=self.param_callback)
			print("Open rqt_reconfigure to tune paramters realtime")
		except rospy.ROSException:
			print("Server node is not alive, load parameters from GpsWaypoints.yaml file")
			pass

		########################### Robot Parameters ##########################
		self.ROBOT_MODE = "MANUAL"
		#self.sbus_throttle_mid = 1024
		#self.sbus_steering_mid = 969	#1024
		self.cart_mode = 0
		self.completed_flag = False
		self.hdg_calib_flag = False

		self.sbus_max = 1680
		self.vel_min = 0.2
		self.vel_max = 2.2

		self.obst_flag = False

		self.update_local_param()

		########################### Pub/Sub Topics ##########################

		rospy.Subscriber(self.namespace_attaching(NS, "/ublox/fix"), NavSatFix, self.gps_callback)
		rospy.Subscriber(self.namespace_attaching(NS, "/ublox2/fix"), NavSatFix, self.gps2_callback)
		rospy.Subscriber(self.namespace_attaching(NS, "/jmoab_compass"), Float32MultiArray, self.compass_callback)
		rospy.Subscriber(self.namespace_attaching(NS, "/atcart_mode"), Int8, self.atcart_mode_callback)
		# rospy.Subscriber("/hdg_calib_flag", Bool, self.hdg_calib_flag_callback)
		rospy.Subscriber(self.namespace_attaching(NS, "/heartbeat"), Bool, self.heartbeat_callback)
		rospy.Subscriber(self.namespace_attaching(NS, "/goal_waypoints"), GoalWaypoints, self.goal_wp_callback)
		rospy.Subscriber(self.namespace_attaching(NS, "/request_store_waypoints"), Bool, self.request_wp_callback)
		rospy.Subscriber(self.namespace_attaching(NS, "/obstacle_detection"), UInt8, self.obst_detect_callback)
		rospy.Subscriber(self.namespace_attaching(NS, "/ublox/fix_velocity"), TwistWithCovarianceStamped, self.gps_vel_callback)

		self.sbus_cmd_pub = rospy.Publisher(self.namespace_attaching(NS, "/sbus_cmd"), Int32MultiArray, queue_size=10)
		self.sbus_cmd = Int32MultiArray()
		self.reply_wp_pub = rospy.Publisher(self.namespace_attaching(NS, "/reply_goal_waypoints"), GoalWaypoints, queue_size=10)
		self.reply_wp_msg = GoalWaypoints()
		self.arrived_wp_pub = rospy.Publisher(self.namespace_attaching(NS, "/arrived_waypoints"), UInt16, queue_size=10)
		self.arrived_wp_msg = UInt16()
		self.atcart_mode_cmd_pub = rospy.Publisher(self.namespace_attaching(NS, "/atcart_mode_cmd"), Int8, queue_size=10)
		self.atcart_mode_cmd_msg = Int8()
		self.jmoab_relay1_pub = rospy.Publisher(self.namespace_attaching(NS, "/jmoab_relay1"), Bool, queue_size=10)
		self.jmoab_relay1_msg = Bool()
		self.mission_completed_pub = rospy.Publisher(self.namespace_attaching(NS, "/mission_completed"), Bool, queue_size=10)
		self.mission_completed_msg = Bool()

		########################### Start ##########################
		self.get_target_points()

		time.sleep(1)
		self.loop()
		rospy.spin()

	def namespace_attaching(self, NS, topic_name):
		if NS is None:
			return topic_name
		else:
			if NS.startswith("/"):
				topic_name = NS + topic_name
			else:
				topic_name = "/" + NS + topic_name
			return topic_name

	def get_target_points(self):

		## Reset all previos data
		self.lat_target_list = []
		self.lon_target_list = []
		self.speed_target_list = []
		self.delay_target_list = []
		self.sbus_throttle_target_list = []
		self.relay_target_list = []

		if os.path.exists(self.mission_path):
			print("Extract lat/lon from mission.txt")
			file = open(self.mission_path, 'r')
			lines = file.read().splitlines()

			for line in lines:
				if len(line)<40:
					pass
				else:
					line_list = line.split('\t')
					self.speed_target_list.append(float(line_list[4]))
					self.delay_target_list.append(float(line_list[5]))
					self.relay_target_list.append(int(float(line_list[6])))
					self.lat_target_list.append(float(line_list[8]))
					self.lon_target_list.append(float(line_list[9]))

			file.close()

			self.total_points = len(self.lat_target_list)

			for speed in self.speed_target_list:
				self.sbus_throttle_target_list.append(self.vel2sbus(speed))

			print("lat_target_list", self.lat_target_list)
			print("lon_target_list", self.lon_target_list)
			print("speed_target_list", self.speed_target_list)
			print("delay_target_list", self.delay_target_list)
			print("sbus_throttle_target_list", self.sbus_throttle_target_list)
			print("relay_target_list", self.relay_target_list)

	def generate_mission_file(self, current_wp, coord_frame, command, param1, param2, param3, param4, lat_list, lon_list, alt_list, autocontinue):

		mission_file = open(self.mission_path, 'w+')
		mission_file.write("QGC WPL 110\n")

		for i in range(len(lat_list)):
			mission_file.write('{:d}'.format(i))
			mission_file.write("\t")
			mission_file.write("{:d}".format(current_wp[i]))
			mission_file.write("\t")
			mission_file.write("{:d}".format(coord_frame[i]))
			mission_file.write("\t")
			mission_file.write("{:d}".format(command[i]))
			mission_file.write("\t")
			mission_file.write("{:.6f}".format(param1[i]))
			mission_file.write("\t")
			mission_file.write("{:.6f}".format(param2[i]))
			mission_file.write("\t")
			mission_file.write("{:.6f}".format(param3[i]))
			mission_file.write("\t")
			mission_file.write("{:.6f}".format(param4[i]))
			mission_file.write("\t")
			mission_file.write('{:.16f}'.format(lat_list[i]))
			mission_file.write("\t")
			mission_file.write('{:.16f}'.format(lon_list[i]))
			mission_file.write("\t")
			mission_file.write("{:.2f}".format(alt_list[i]))
			mission_file.write("\t")
			mission_file.write("{:d}".format(autocontinue[i]))
			if i != (len(lat_list)-1):
				mission_file.write("\n")

		mission_file.close()

	def heartbeat_callback(self, msg):

		if msg.data == True:

			self.hb_timestamp = time.time()

	def param_callback(self, config):

		print("Got new parameters")
		# rospy.loginfo("Config set to {int_param}, {double_param}, {str_param}, {bool_param}".format(**config))
		# rospy.loginfo("Config set to {p}, {i}, {d}".format(**config))
		self.max_start_str = config["max_start_str"]
		self.min_start_str = config["min_start_str"]
		self.str_adj = config["str_adj"]
		self.skid_adj = config["skid_adj"]
		self.str_mid = config["str_mid"]
		self.max_start_thr = config["max_start_thr"]
		self.thr_adj = config["thr_adj"]
		self.thr_slowest = config["thr_slowest"]
		self.thr_mid = config["thr_mid"]
		self.goal_dist_thresh = config["goal_dist_thresh"]
		self.goal_ang_thresh = config["goal_ang_thresh"]
		self.x_track_error_start = config["x_track_error_start"]
		self.x_track_repose_dist = config["x_track_repose_dist"]
		self.pid_hdg_out_thresh = config["pid_hdg_out_thresh"]
		self.pid_x_out_thresh = config["pid_x_out_thresh"]
		self.hdg_p = config["hdg_p"]
		self.hdg_i = config["hdg_i"]
		self.hdg_d = config["hdg_d"]
		self.cross_p = config["cross_p"]
		self.cross_i = config["cross_i"]
		self.cross_d = config["cross_d"]
		self.vel_p = config["vel_p"]
		self.vel_i = config["vel_i"]
		self.vel_d = config["vel_d"]
		self.use_heartbeat = config["use_heartbeat"]
		self.pid_hdg.tunings = (self.hdg_p, self.hdg_i, self.hdg_d)
		self.pid_x.tunings = (self.cross_p, self.cross_i, self.cross_d)
		self.pid_vel.tunings = (self.vel_p, self.vel_i, self.vel_d)

		self.update_local_param()

	def update_local_param(self):
		self.sbus_throttle_mid = self.thr_mid #1024
		self.sbus_steering_mid = self.str_mid #969
		# Throttle
		self.sbus_throttle_const = self.max_start_thr + self.thr_adj

		# PIVOT Turning
		self.sbus_skidding_right = self.max_start_str + self.skid_adj
		self.sbus_skidding_left = self.min_start_str - self.skid_adj

		# Cross track steering
		self.sbus_steering_max = self.max_start_str + self.str_adj  # steer to right
		self.sbus_steering_min = self.min_start_str - self.str_adj  # steer to left

	def goal_wp_callback(self, msg):
		print("New goal_waypoints come")
		self.total_wps = len(msg.lat.data)
		current_wp_list = [0]*self.total_wps
		coord_frame_list = [3]*self.total_wps
		command_list = [16]*self.total_wps
		param1_list = msg.speed.data	# speed list
		param2_list = msg.delay.data	# delay list
		param3_list = msg.relay.data # relay list
		param4_list = [0]*self.total_wps
		lat_list = msg.lat.data
		lon_list = msg.lon.data
		alt_list = [10]*self.total_wps
		autocon_list = [1]*self.total_wps

		self.got_new_wps = True

		self.generate_mission_file(current_wp_list, coord_frame_list, command_list, \
			param1_list, param2_list, param3_list, param4_list, \
			lat_list, lon_list, alt_list, autocon_list)

		self.reply_wp_pub.publish(msg)

	def request_wp_callback(self,msg):

		print("Request waypoints")
		if msg.data == True:
			self.reply_wp_msg.lat.data = self.lat_target_list
			self.reply_wp_msg.lon.data = self.lon_target_list
			self.reply_wp_msg.speed.data = self.speed_target_list
			self.reply_wp_msg.delay.data = self.delay_target_list
			self.reply_wp_msg.relay.data = self.relay_target_list

			self.reply_wp_pub.publish(self.reply_wp_msg)

	def gps_callback(self, msg):

		self.lat1 = msg.latitude
		self.lon1 = msg.longitude

		self.use_gps1 = True

	def gps2_callback(self, msg):

		self.lat2 = msg.latitude
		self.lon2 = msg.longitude

		self.use_gps2 = True

	def compass_callback(self, msg):

		self.hdg = msg.data[2]

	def atcart_mode_callback(self, msg):
		self.cart_mode = msg.data

		# if self.cart_mode != 2:
		# 	self.completed_flag = False
	# def hdg_calib_flag_callback(self, msg):
	# 	self.hdg_calib_flag = msg.data

	# 	if msg.data == True:
	# 		print("=============== got calib flag =================")

	def gps_vel_callback(self, msg):
		vel_x = msg.twist.twist.linear.x
		vel_y = msg.twist.twist.linear.y
		self.gps_abs_vel = np.sqrt(vel_x**2 + vel_y**2)

	def obst_detect_callback(self, msg):
		if msg.data == 2:
			self.obst_flag = True
			# print("WARNING: Obstacle on front or back!!")
		else:
			self.obst_flag = False

	def vel2sbus(self, vel):

		if vel < 0.05:
			sbus = 1024
		elif 0.05 <= vel < self.vel_min:
			sbus = self.thr_slowest
		elif (self.vel_min <= vel <= self.vel_max):
			sbus = self.map_with_limit(vel, self.vel_min, self.vel_max, self.thr_slowest, self.sbus_max)
		elif vel > self.vel_max:
			sbus = self.sbus_max

		return int(sbus)

	def get_distance(self, lat1, lon1, lat2, lon2):

		R = 6371.0*1000.0
		lat_start = np.radians(lat1)
		lon_start = np.radians(lon1)
		lat_end = np.radians(lat2)
		lon_end = np.radians(lon2)
		dLat = lat_end - lat_start
		dLon = lon_end - lon_start

		a = np.sin(dLat/2.0)*np.sin(dLat/2.0) + np.cos(lat_start)*np.cos(lat_end)*np.sin(dLon/2.0)*np.sin(dLon/2.0)
		c = 2.0*np.arctan2(np.sqrt(a),np.sqrt(1-a))

		d = c*R

		return d

	def get_bearing(self, lat1, lon1, lat2, lon2):

		lat_start = np.radians(lat1)
		lon_start = np.radians(lon1)
		lat_end = np.radians(lat2)
		lon_end = np.radians(lon2)
		dLat = lat_end - lat_start
		dLon = lon_end - lon_start

		y = np.sin(dLon)*np.cos(lat_end)
		x = np.cos(lat_start)*np.sin(lat_end) - np.sin(lat_start)*np.cos(lat_end)*np.cos(dLon)
		bearing = np.degrees(np.arctan2(y,x))

		return bearing

	def ConvertTo180Range(self, deg):

		deg = self.ConvertTo360Range(deg)
		if deg > 180.0:
			deg = -(180.0 - (deg%180.0))

		return deg

	def ConvertTo360Range(self, deg):

		# if deg < 0.0:
		deg = deg%360.0

		return deg

	def find_smallest_diff_ang(self, goal, cur):

		## goal is in 180ranges, we need to convert to 360ranges first

		diff_ang1 = abs(self.ConvertTo360Range(goal) - cur)

		if diff_ang1 > 180.0:

			diff_ang = 180.0 - (diff_ang1%180.0)
		else:
			diff_ang = diff_ang1

		## check closet direction
		compare1 = self.ConvertTo360Range(self.ConvertTo360Range(goal) - self.ConvertTo360Range(cur + diff_ang))
		compare2 = self.ConvertTo180Range(goal - self.ConvertTo180Range(cur + diff_ang))
		# print(compare1, compare2)
		if (abs(compare1) < 0.5) or (compare1 == 360.0) or (abs(compare2) < 0.5) or (compare2 == 360.0):
			sign = 1.0 # clockwise count from current hdg to target
		else:
			sign = -1.0 # counter-clockwise count from current hdg to target

		return diff_ang, sign

	def map_with_limit(self, val, in_min, in_max, out_min, out_max):

		# out = ((val - in_min) * ((out_max - out_min) / (in_max - in_min))) + out_min
		## in_min must be the minimum input
		## in_max must be the maximum input

		## out_min is supposed to map with in_min value
		## out_max is sipposed to map with in_max value
		## out_min can be less/more than out_max, doesn't matter

		m = (out_max - out_min)/(in_max - in_min)
		out = m*(val - in_min) + out_min

		if out_min > out_max:
			if out > out_min:
				out = out_min
			elif out < out_max:
				out = out_max
			else:
				pass
		elif out_max > out_min:
			if out > out_max:
				out = out_max
			elif out < out_min:
				out = out_min
			else:
				pass
		else:
			pass

		# print(m, val, in_min, in_max, out_min, out_max)

		return out

	def control_relay(self, flag):

		for i in range(10):
			self.jmoab_relay1_msg.data = flag
			self.jmoab_relay1_pub.publish(self.jmoab_relay1_msg)

	def mission_is_completed(self, completed_flag):
		self.mission_completed_msg.data = completed_flag
		self.mission_completed_pub.publish(self.mission_completed_msg)

	def loop(self):

		rate = rospy.Rate(30)
		print("Start GPS Waypoints Navigation")

		## it must enter get_goal_ang_once first
		get_goal_ang_once = True
		turning_flag = False
		get_goal_dist_once = False
		throttle_flag = False
		end_flag = False
		cur_dist = 0.0
		goal_dist = 0.0

		## for skid to DIR heading
		get_goal_turn_ang_once = True
		skidding_flag = False
		reset_flag = False
		hdg_diff = 0.0

		from_hdg_diff = True
		from_x_track = True

		## for checking cart mode
		cart_mode_check_lock = False
		from_step = 0
		pause_flag = False
		from_obst_flag_detect = False
		## for heartbeat
		hb_trig_once = True

		while not rospy.is_shutdown():

			## average front/back gps to get somewhere in middle of the cart
			if self.use_gps1 and self.use_gps2:
				self.lat = (self.lat1 + self.lat2)/2.0
				self.lon = (self.lon1 + self.lon2)/2.0
			else:
				self.lat = self.lat1
				self.lon = self.lon1

			# if (not self.hdg_calib_flag):
			###########################################
			### Robot will start once got auto_mode ###
			###########################################
			if (self.cart_mode == 2) and (not self.completed_flag) and (not self.obst_flag):

				######################################################
				### In case of pause, these condition handle flags ###
				######################################################
				if (from_step == 1) and cart_mode_check_lock:
					get_goal_ang_once = True
					cart_mode_check_lock = False
					print("Resume step1")
				elif (from_step == 2) and cart_mode_check_lock:
					turning_flag = True
					cart_mode_check_lock = False
					print("Resume step2")
				elif (from_step == 3) and cart_mode_check_lock:
					get_goal_dist_once = True
					cart_mode_check_lock = False
					print("Resume step3")
				elif (from_step == 4) and cart_mode_check_lock:
					throttle_flag = True
					self.pid_hdg.auto_mode = True
					self.pid_x.auto_mode = True
					cart_mode_check_lock = False
					self.pid_vel.auto_mode = True
					print("Resume step4")
				elif (from_step == 5) and cart_mode_check_lock:
					end_flag = True
					cart_mode_check_lock = False
					print("Resume step5")
				elif from_obst_flag_detect:
					self.control_relay(self.relay_target_list[self.target_wp])
					self.pid_vel.auto_mode = True
					from_obst_flag_detect = False

				#######################################
				##### Step 1 Get how much to turn #####
				##### only one time               #####
				#######################################
				if get_goal_ang_once:
					goal_ang = self.get_bearing(self.lat, self.lon, self.lat_target_list[self.target_wp], self.lon_target_list[self.target_wp])
					## goal_ang is in 180ranges, hdg is in 360ranges
					diff_ang, sign = self.find_smallest_diff_ang(goal_ang, self.hdg)
					goal_hdg = self.hdg + (diff_ang*sign)

					get_goal_ang_once = False
					turning_flag = True
					end_flag = False
					print("step: 1 | goal_ang: {:.2f} | diff_ang: {:.2f} | sign: {:.1f} | hdg: {:.2f}".format(goal_ang, diff_ang, sign, self.hdg))
					step = 1

					self.pid_vel.auto_mode = False
					self.pid_vel.setpoint = self.speed_target_list[self.target_wp]

					self.control_relay(self.relay_target_list[self.target_wp])

				##########################################################
				##### Step 2 turning until less than angle threshold #####
				##### continuously                                   #####
				##########################################################
				if goal_hdg < 0.0:
					self.hdg = self.ConvertTo180Range(self.hdg)
				if goal_hdg > 360.0:
					goal_hdg = self.ConvertTo360Range(goal_hdg)

				if turning_flag and (abs(goal_hdg - self.hdg) > self.goal_ang_thresh) and (get_goal_ang_once==False):
					if sign == 1.0:
						sbus_steering = self.sbus_skidding_right # turn right
						sbus_throttle = self.sbus_throttle_mid
					elif sign == -1.0:
						sbus_steering = self.sbus_skidding_left # turn left
						sbus_throttle = self.sbus_throttle_mid
					step = 2

				elif (throttle_flag == False) and (turning_flag == True):
					print("finish turning")
					turning_flag = False
					get_goal_dist_once = True
					sbus_steering = self.sbus_steering_mid
					sbus_throttle = self.sbus_throttle_mid

				##############################################
				##### Step 3 Get how much distance to go #####
				##### only one time                      #####
				##############################################
				if get_goal_dist_once:
					goal_dist = self.get_distance(self.lat, self.lon, self.lat_target_list[self.target_wp], self.lon_target_list[self.target_wp])
					start_lat = self.lat
					start_lon = self.lon
					cur_dist = 0.0
					get_goal_dist_once = False
					throttle_flag = True
					end_flag = False

					step = 3
					print("step: 3 | hdg: {:.2f}".format(self.hdg))
					reset_tic = time.time()

					self.pid_vel.auto_mode = True

				#############################################################
				##### Step 4 go straight until less than dist threshold #####
				##### continuously                                      #####
				#############################################################
				if throttle_flag and (abs(goal_dist - cur_dist) > self.goal_dist_thresh) :

					## A is point of previous point
					## in case of just start there is no -1 element in target_list so we use previous lat/lon
					if (self.target_wp == 0):
						A_lat = start_lat
						A_lon = start_lon
					else:
						A_lat = self.lat_target_list[self.target_wp-1]
						A_lon = self.lon_target_list[self.target_wp-1]

					## reset_flag tells if the cart went out of the route, so it will re-target heading with current pose
					## if no reset_flag, it will just calculate angle to goal/bot with self.target_wp-1 and self.target_wp
					if reset_flag == True:
						ang_A_to_goal = self.get_bearing(start_lat, start_lon, self.lat_target_list[self.target_wp], self.lon_target_list[self.target_wp])
						ang_A_to_bot = self.get_bearing(start_lat, start_lon, self.lat, self.lon)
					else:
						ang_A_to_goal = self.get_bearing(A_lat, A_lon, self.lat_target_list[self.target_wp], self.lon_target_list[self.target_wp])
						ang_A_to_bot = self.get_bearing(A_lat, A_lon, self.lat, self.lon)

					if (ang_A_to_goal > 90.0) or (ang_A_to_goal < -90.0):
						ang_A_to_bot = self.ConvertTo360Range(ang_A_to_bot)
						ang_A_to_goal = self.ConvertTo360Range(ang_A_to_goal)

					#########################################################################################
					############################### cross-track-error calculation ###########################
					#########################################################################################

					##############################################################################################################################
					## "a" is a distance from target_wp-1 to bot, but if there is reset_flag, we use current pose of bot instead of target_wp-1 ##
					## "c" is a distance from target_wp-1 to target_wp                                                                          ##
					## "b" is a distance from bot to target_wp                                                                                ##
					## "k" is a distance projection of bot's pose back to route plane                                                           ##
					## "x" is a distance from bot to route (cross-track-distance)                                                               ##
					##############################################################################################################################
					if reset_flag == True:
						# previous point and current pose
						a = self.get_distance(start_lat, start_lon, self.lat, self.lon)
						# previous point and goal point
						c = self.get_distance(start_lat, start_lon, self.lat_target_list[self.target_wp], self.lon_target_list[self.target_wp])
					else:
						# previous point and current pose
						a = self.get_distance(A_lat, A_lon, self.lat, self.lon)
						# previous point and goal point
						c = self.get_distance(A_lat, A_lon, self.lat_target_list[self.target_wp], self.lon_target_list[self.target_wp])

					# current pose and goal point
					b = self.get_distance(self.lat, self.lon, self.lat_target_list[self.target_wp], self.lon_target_list[self.target_wp])

					k = (a**2 - b**2 + c**2)/(2*c)
					x = np.sqrt(a**2 - k**2)
					if np.isnan(x):
						x = 0.0
					# print("a: {:.2f} | b: {:.2f} | c: {:.2f} | k: {:.2f} | x: {:.2f}".format(a,b,c,k,x))

					##########################################################
					## To find the robot is on which side of the route      ##
					## both ang_A_to_goal/bot are in 180ranges,             ##
					## so if the path c is pointing to 3rd or 4th quadrant, ##
					## then convert ang_A_to_goal/bot to 360ranges          ##
					##########################################################
					if ang_A_to_bot < ang_A_to_goal:
						x_side = -1.0	# on the left side of route
					else:
						x_side = 1.0	# on the right side of route

					x_track_error = x*x_side

					#############################################################################################
					############################### bearing hdg compare calculation #############################
					#############################################################################################

					#########################
					## use cross track PID ##
					#########################
					if (abs(x_track_error) > self.x_track_error_start):

						## Check one time that it's from hdg_diff PID, then enable x_tracK PID and disable hdg_diff PID
						if from_hdg_diff:
							self.pid_hdg.auto_mode = False
							self.pid_x.auto_mode = True
							from_x_track = True
							from_hdg_diff = False

						output_pid_x = self.pid_x(x_track_error)
						if x_track_error < -self.pid_x_out_thresh: #if output_pid_x > self.pid_x_out_thresh:
							sbus_steering = int(self.map_with_limit(output_pid_x, 0.0, self.max_x_err, self.max_start_str, self.sbus_steering_max))
						elif x_track_error > self.pid_x_out_thresh:	#elif output_pid_x < -self.pid_x_out_thresh:
							sbus_steering = int(self.map_with_limit(output_pid_x, -self.max_x_err, 0.0, self.sbus_steering_min, self.min_start_str))
						else:
							sbus_steering = self.sbus_steering_mid

						# sbus_throttle = self.sbus_throttle_const

						pid_mode = "x_error"
						OUTPUT_PID = output_pid_x

					#################
					## use hdg PID ##
					#################
					else:

						## Check one time that it's from x_tracK PID, then enable hdg_diff PID and disable x_tracK PID
						if from_x_track:
							self.pid_hdg.auto_mode = True
							self.pid_x.auto_mode = False
							from_x_track = False
							from_hdg_diff = True

						## hdg might got converted to 180ranges somewhere, but we need 360ranges to use with find_smallest_diff_ang function
						self.hdg = self.ConvertTo360Range(self.hdg)
						## using find_smallest_diff_ang function to compare between goal bearing (180ranges) and current heading (360ranges)
						hdg_diff, diff_sign = self.find_smallest_diff_ang(ang_A_to_goal, self.hdg)
						hdg_diff = self.ConvertTo180Range(hdg_diff*(-diff_sign))
						output_pid = self.pid_hdg(hdg_diff)

						if hdg_diff < -self.pid_hdg_out_thresh:	#if output_pid > self.pid_hdg_out_thresh:
							sbus_steering = int(self.map_with_limit(output_pid, 0.0, self.max_err, self.max_start_str, self.sbus_steering_max))
						elif hdg_diff > self.pid_hdg_out_thresh:	#elif output_pid < -self.pid_hdg_out_thresh:
							sbus_steering = int(self.map_with_limit(output_pid, -self.max_err, 0.0, self.sbus_steering_min, self.min_start_str))
						else:
							sbus_steering = self.sbus_steering_mid

						pid_mode = "hdg_diff"
						OUTPUT_PID = output_pid

					###########################################################################
					## if very close distance, use very low speed, ignore user speed command ##
					###########################################################################
					output_pid_vel = self.pid_vel(self.gps_abs_vel)
					if goal_dist < 2.0:
						sbus_throttle = self.thr_slowest+20
					else:
						if abs(goal_dist - cur_dist) < 2.0:
							sbus_throttle = self.thr_slowest+20
						else:
							if self.speed_target_list[self.target_wp] != 0.0:
								# sbus_throttle = self.sbus_throttle_target_list[self.target_wp] #self.sbus_throttle_const
								sbus_throttle = int(self.map_with_limit(output_pid_vel, 0, self.max_vel, 1024, 1680))
							else:
								sbus_throttle = self.sbus_throttle_const

							# print("output_pid_vel: {:.2f} | thr_pid: {:d}".format(output_pid_vel, sbus_throttle_pid))

					###############################################################
					### Resest target heading again if cross track error is big ###
					###############################################################
					reset_timeout = time.time() - reset_tic
					if (abs(x_track_error) > self.x_track_repose_dist) and (reset_timeout > 2.0):
						throttle_flag = False
						get_goal_ang_once = True
						reset_flag = True
						print("Reset target heading")
						sbus_steering = self.sbus_steering_mid
						sbus_throttle = self.sbus_throttle_mid

						self.pid_x.auto_mode = False
						self.from_hdg_diff = True

					####################################
					### Print out all necessary info ###
					####################################
					print("PID: {:} | tg_wp: {:d} | x: {:.2f} | rst_T/O: {:.2f} | rst_flg: {:} | AngAtoBot: {:.2f} | hdg: {:.2f} | AngAtoGoal: {:.2f} | hdgDff: {:.2f} | outPID: {:.2f} | goalDist: {:.2f} curDist: {:.2f} | str: {:d} | thr: {:d} | vel: {:.2f} | vel_sp: {:.2f} | outPIDV: {:.2f}".format(\
						pid_mode, self.target_wp, x_track_error, reset_timeout, reset_flag, ang_A_to_bot, self.hdg, ang_A_to_goal, hdg_diff, OUTPUT_PID, goal_dist, cur_dist, sbus_steering, sbus_throttle, self.gps_abs_vel, self.speed_target_list[self.target_wp], output_pid_vel))

					## update cur_dist with current lat/lon compare to start lat/lon
					cur_dist = self.get_distance(start_lat, start_lon, self.lat, self.lon)
					step = 4

				elif (throttle_flag == True) and (turning_flag == False):
					throttle_flag = False
					get_goal_ang_once = True
					end_flag = True
					reset_flag = False
					print("finish throttle")
					sbus_steering = self.sbus_steering_mid
					sbus_throttle = self.sbus_throttle_mid

				#####################################################################################
				##### Step 5 check if turning and throttle are completed, then increment the wp #####
				#####################################################################################

				if (throttle_flag == False) and (turning_flag == False) and (end_flag == True):
					sbus_steering = self.sbus_steering_mid
					sbus_throttle = self.sbus_throttle_mid
					self.sbus_cmd.data = [int(sbus_steering), int(sbus_throttle)]
					self.sbus_cmd_pub.publish(self.sbus_cmd)
					print("Delay as {:.2f} seconds".format(self.delay_target_list[self.target_wp]))
					time.sleep(self.delay_target_list[self.target_wp])
					self.target_wp += 1
					self.pid_vel.auto_mode = False

				######################################################################
				##### Step 6 check every time that target_wp is completed or not #####
				######################################################################
				if self.target_wp == len(self.lat_target_list):
					self.target_wp = 0
					# self.ROBOT_MODE = "MANUAL"
					self.completed_flag = True
					sbus_steering = self.sbus_steering_mid
					sbus_throttle = self.sbus_throttle_mid
					print("Mission completed")
					print("Switch from auto to manual, and back to auto again to restart mission.")

				self.sbus_cmd.data = [int(sbus_steering), int(sbus_throttle)]

				if (step != 4) and (not cart_mode_check_lock):
					print("step: {:d} | target_wp: {:d} | goal_hdg: {:.3f} | hdg: {:.3f} | goal_dist: {:.3f} | cur_dist: {:.3f}".format(\
						step, self.target_wp, goal_hdg, self.hdg, goal_dist, cur_dist))

				pause_flag = True

				self.mission_is_completed(False)

			#########################################################
			### This is pause on manual or hold mode by Tx switch ###
			#########################################################
			elif (self.cart_mode != 2) and (pause_flag):
				if get_goal_ang_once:
					get_goal_ang_once = False
					from_step = 1
					print("Stop at step1")
				elif turning_flag:
					turning_flag = False
					from_step = 2
					print("Stop at step2")
				elif get_goal_dist_once:
					get_goal_dist_once = False
					from_step = 3
					print("Stop at step3")
				elif throttle_flag:
					throttle_flag = False
					from_step = 4
					self.pid_hdg.auto_mode = False
					self.pid_x.auto_mode = False
					self.pid_vel.auto_mode = False
					print("Stop at step4")
				elif end_flag:
					end_flag = False
					from_step = 5
					print("Stop at step5")

				cart_mode_check_lock = True

				if (self.got_new_wps):
					print("Reset lat/lon mission during mission")
					self.get_target_points()
					self.target_wp = 0
					self.got_new_wps = False

				self.mission_is_completed(False)

			#########################################
			### This is already completed mission ###
			#########################################
			elif (self.cart_mode == 2) and self.completed_flag:
				pause_flag = False
				self.mission_is_completed(True)
				pass

			########################################################################
			### Set back to manual or hold once, disable completed_flag to False ###
			########################################################################
			elif (self.cart_mode != 2) and self.completed_flag:
				self.completed_flag = False
				pause_flag = False

				if (self.got_new_wps):
					print("Completed previous mission, load new mission")
					self.get_target_points()
					self.target_wp = 0
					self.got_new_wps = False

				self.mission_is_completed(True)

			#########################################################################
			### Firstly start, maybe in manual/hold mode, completed_flag is False ###
			#########################################################################
			else:

				if (self.got_new_wps):
					print("Reset lat/lon mission before start")

					self.get_target_points()
					self.target_wp = 0
					self.got_new_wps = False

				if (self.obst_flag):
					## Trig only one time
					if from_obst_flag_detect == False:
						self.control_relay(False)
						self.pid_vel.auto_mode = False
						print("Detected obstacle....")
					from_obst_flag_detect = True

				self.sbus_cmd.data = [self.sbus_steering_mid, self.sbus_throttle_mid]

				self.mission_is_completed(False)

			self.sbus_cmd_pub.publish(self.sbus_cmd)

			##########################
			### heartbeat checking ###
			##########################
			self.hb_period = (time.time() - self.hb_timestamp)
			if self.use_heartbeat and (self.hb_period > self.hb_timeout):
				if (int(self.hb_period)%self.hb_timeout == 0) or hb_trig_once:
					print("Hearbeat timeout.. switch cart mode to hold")
					self.atcart_mode_cmd_msg.data = 0
					self.atcart_mode_cmd_pub.publish(self.atcart_mode_cmd_msg)
					hb_trig_once = False

			#############################################################
			### continuously send arrived wp                          ###
			### target_wp starts from 0, so we can send this directly ###
			#############################################################
			self.arrived_wp_msg.data = self.target_wp
			self.arrived_wp_pub.publish(self.arrived_wp_msg)

			rate.sleep()

if __name__ == "__main__":

	parser = argparse.ArgumentParser(description='GPS Waypoints Server node')
	parser.add_argument('--param_file',
						help="A file path of GpsWaypoints.yaml, default is the one in jmoab_autopilot_ros/cfg/")
	parser.add_argument('--mission_file',
						help="A file path of mission.txt, default is the one in jmoab_autopilot_ros/waypoints/")
	parser.add_argument('--ns',
						help="a namespace in front of topics")

	args = parser.parse_args(rospy.myargv()[1:])
	print(args.param_file)
	param_file = args.param_file
	mission_file = args.mission_file
	ns = args.ns

	if param_file is None:
		print("Use jmoab_autopilot_ros/cfg/GpsWaypoints.yaml")
		rospack = rospkg.RosPack()
		jmoab_autopilot_ros_path = rospack.get_path("jmoab_autopilot_ros")
		yaml_name = "GpsWaypoints.yaml"
		yaml_path = os.path.join(jmoab_autopilot_ros_path, "cfg", yaml_name)
	else:
		yaml_path = param_file

	if mission_file is None:
		print("Use jmoab_autopilot_ros/waypoints/mission.txt")
		rospack = rospkg.RosPack()
		jmoab_autopilot_ros_path = rospack.get_path("jmoab_autopilot_ros")
		mission_name = "mission.txt"
		mission_path = os.path.join(jmoab_autopilot_ros_path, "waypoints", mission_name)
	else:
		mission_path = mission_file

	if ns is not None:
		print("Use namespace as {:}".format(ns))
	else:
		print("No namespace, using default")

	a = GPS_NAV(yaml_path, mission_path, ns)


from django.http import Http404
from django.conf import settings
from django.core.cache import cache
from rest_framework import  permissions
from api.secure.models import role, project_user_role, role_permission
from api.project.models import project

class api_permission(permissions.BasePermission):
    '''
    A implement class that match site client permission to socpes.
    '''
    def has_permission(self, request, view):
        """
        Return `True` if permission is granted, `False` otherwise.

        All we need to do here is that:
        We need get a set of permission for current user, if it's super user, just
        return True.

        For PRIVATE project, only project members can view (GET method), instead of
        Anonymous, so we should check if current user is a team member before next
        step.

        All permissions will cached by role name.
        """

        # super user has all permissions.
        if request.user.is_superuser:
            return True

        project_is_private = False
        perm_parm_dict = {'owner_username':'', 'project_name':''}
        perm_parm_dict.update(request.parser_context['kwargs'])

        owner_username = perm_parm_dict['owner_username']
        project_name = perm_parm_dict['project_name']
        perm_list = []

        all_perms = cache.get(settings.PERMISSIONS_CACHED_KEY)
        if not all_perms:
            all_role = role.objects.all()
            all_perms = {}
            for r in all_role:
                p_list = []
                rp_ins_list = role_permission.objects.filter(role=r)
                for rp in rp_ins_list:
                    p_dict = {}
                    p_dict['codename'] = rp.permission.endpoint.codename
                    p_dict['GPPD'] = rp.permission.GPPD
                    p_list.append(p_dict)
                all_perms[r.name] = p_list
            # default expire time is 1 day, which confied in settings.py
            cache.set(settings.PERMISSIONS_CACHED_KEY, all_perms)

        if project_name and owner_username:
            proj_ins = self.__get_project_instance(owner_username, project_name)
            project_is_private = self.__check_project_private(proj_ins)
            # check if project is PRIVATE
            if project_is_private and not self.__is_team_member(proj_ins, request.user):
                # check if current user is team member.
                return False

            pur_ins_list = project_user_role.objects.filter(project=proj_ins, user=request.user)
            for pur in pur_ins_list:
                perm_list.extend(all_perms[pur.role.name])
            # all users has anonymous' permission
            perm_list.extend(all_perms[settings.ANONYMOUS_ROLE_NAME])

        if not len(perm_list):
            perm_list = all_perms[settings.ANONYMOUS_ROLE_NAME]

        # get view namespace
        view_namespace = self.__get_fullname(view)
        GPPD = settings.GPPD[request.method.upper()]
        return self.__has_permission_on_view(GPPD, view_namespace, perm_list)

    def has_object_permission(self, request, view, obj):
        """
        Return `True` if permission is granted, `False` otherwise.
        """
        return True

    def __has_permission_on_view(self, method, view_namespace, permission_list):
        '''
        Return `True` is at list one row was found, `False` otherwise.
        '''
        #raise BaseException(method, view_namespace)
        for p in permission_list:
            if p['codename'] == view_namespace and p['GPPD'] == method:
                return True

        return False

    def __get_fullname(self, o):
        '''
        Return full namespace of object.
        '''
        return o.__module__ + "." + o.__class__.__name__

    def __get_project_instance(self, owner_username, project_name):
        '''
        Get project instance.
        '''
        proj_ins = None
        try:
            proj_ins = project.objects.get_by_natural_key(owner_username, project_name)
        except: raise Http404
        else:
            return proj_ins

    def __check_project_private(self, project_instance):
        '''
        Check if project is private.

        If one of or both arguments not provide, raise HTTP 404 error.
        '''
        return project_instance.is_private

    def __is_team_member(self, project_instance,  username):
        '''
        Check if a user is one of team member of the givn project.
        '''
        username_list = project_instance.objects.get_team_members_username()

        if isinstance(username_list, (tuple, list)):
            return username_list.count(username)
        else:
            raise False

        return True

    def __is_owner(self, project_instance, username):
        '''
        Check if a user is the owner of the givn project.
        '''
        if project_instance.user.username == username:
            return True
        else:
            return False


from discord.ext import commands
from discord import Embed, utils
from dotenv import dotenv_values

import os
import asyncio
from hashlib import md5
from datetime import datetime
import locale
import random

from utils.utils import read_json_file, create_json_file

class Bet(commands.Cog):
    def __init__(self, bot):
        self.bot = bot

        env = dotenv_values()
        self.storage_path = env.get("STORAGE_PATH")
        self.check_storage()

        self.bet_channel = int(env.get('BET_CHANNEL_ID'))

        self.choices = {
            'binary': {
                'postive': 'ðŸ”µ',
                'negative': 'ðŸ”´',
            },
        }
        self.default_bet_type = 'binary'

    @commands.Cog.listener()
    async def on_ready(self):
        stored_bets = read_json_file(self.storage_path)
        ctx = self.bot.get_channel(int(self.bet_channel))

        pins = await ctx.pins()
        for message in pins:
            await message.unpin()

        for author_id, author in stored_bets.items():
            for bet_hash, bet in author['bets'].items():
                if bet['status']:
                    message = await ctx.send(embed=self.format_output_create(False, bet_hash, author_id, bet['content']))
                    for choice_emoji in self.choices[bet['bet_type']].values():
                        await message.add_reaction(choice_emoji)

                    await message.pin()

    @commands.command()
    async def create(self, ctx, *, content: str):
        """Permet de crÃ©er une prÃ©diction avec son contenu"""
        self.check_correct_channel(ctx)

        stored_bets = read_json_file(self.storage_path)
        author = str(ctx.author.id)

        if author not in stored_bets:
            stored_bets[author] = self.init_author(ctx.author)

        content = content.strip()
        bet_type = content.split(' ')[0]
        if bet_type in self.choices:
            content = content[len(bet_type)-1:]
        else:
            bet_type = self.default_bet_type

        bet_hash = md5(content.encode('utf-8')).hexdigest()
        existing_bet = bet_hash in stored_bets[author]['bets']

        if existing_bet:
            await ctx.send(self.format_output_create(existing_bet))
        else:
            current_timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

            stored_bets[author]['bets'][bet_hash] = self.init_bet(content, current_timestamp, bet_type)
            create_json_file(self.storage_path, stored_bets)

            message = await ctx.send(embed=self.format_output_create(existing_bet, bet_hash, author, content))

            for choice_emoji in self.choices[bet_type].values():
                await message.add_reaction(choice_emoji)

            await message.pin()

    @commands.command()
    async def delete(self, ctx, bet_hash):
        """Permet de supprimer une prÃ©diction avec ID"""
        self.check_correct_channel(ctx)

        stored_bets = read_json_file(self.storage_path)
        author = str(ctx.author.id)

        possible_deletion = author in stored_bets and bet_hash in stored_bets[author]['bets']

        if possible_deletion:
            stored_bets[author]['bets'].pop(bet_hash)
            create_json_file(self.storage_path, stored_bets)

            pins = await ctx.pins()
            for message in pins:
                if len(message.embeds) > 0:
                    embed = message.embeds[0]
                    author_embed = embed.footer.text.split(' ')[-1]
                    bet_hash_embed = embed.footer.text.split(' ')[0]
                    if bet_hash == bet_hash_embed and author == author_embed:
                        await message.unpin()

        await ctx.send(self.format_output_delete(possible_deletion, bet_hash))

    @commands.command()
    async def close(self, ctx, bet_hash):
        """Permet de terminer une prÃ©diction grÃ¢ce Ã  son ID et d'afficher le rÃ©sultat"""
        self.check_correct_channel(ctx)

        stored_bets = read_json_file(self.storage_path)
        author = str(ctx.author.id)

        possible_closing = author in stored_bets and bet_hash in stored_bets[author]['bets']

        data = []
        if possible_closing:
            stored_bets[author]['bets'][bet_hash]['status'] = False
            create_json_file(self.storage_path, stored_bets)

            author = stored_bets[author]
            bet = author['bets'][bet_hash]
            timestamp = datetime.strptime(bet['timestamp'], '%Y-%m-%d %H:%M:%S')

            emojis = self.choices[bet['bet_type']]
            choices = []
            for emoji in emojis:
                if len(bet['choices'][emoji]) > 0:
                    choice_data = {}
                    users = [await self.bot.fetch_user(user) for user in bet['choices'][emoji]]
                    choice_data['users'] = [user.name for user in users]
                    choice_data['choice_emoji'] = emojis[emoji]
                    choice_data['choice_value'] = emoji
                    choices.append(choice_data)

            data.append({
                'name': author['infos']['name'],
                'avatar': author['infos']['avatar'],
                'bets': [{
                    'timestamp': timestamp.strftime("%Y-%m-%d"),
                    'content': bet['content'],
                    'choices': choices,
                }]
            })

            await ctx.send(embed=self.format_output_show(data)[0])

            author = str(ctx.author.id)
            pins = await ctx.pins()
            for message in pins:
                if len(message.embeds) > 0:
                    embed = message.embeds[0]
                    author_embed = embed.footer.text.split(' ')[-1]
                    bet_hash_embed = embed.footer.text.split(' ')[0]
                    if bet_hash == bet_hash_embed and author == author_embed:
                        await message.unpin()
        else:

            await ctx.send(self.format_output_close(possible_closing, bet_hash))

    @commands.command()
    async def open(self, ctx, bet_hash):
        """Permet de re-dÃ©marrer une prÃ©diction grÃ¢ce Ã  son ID et de donner l'accÃ¨s au vote"""
        self.check_correct_channel(ctx)

        stored_bets = read_json_file(self.storage_path)
        author = str(ctx.author.id)

        possible_opening = author in stored_bets and bet_hash in stored_bets[author]['bets']

        if possible_opening:
            stored_bets[author]['bets'][bet_hash]['status'] = True
            create_json_file(self.storage_path, stored_bets)

            bet = stored_bets[author]['bets'][bet_hash]
            message = await ctx.send(embed=self.format_output_create(False, bet_hash, author, bet['content']))
            for choice_emoji in self.choices[bet['bet_type']].values():
                await message.add_reaction(choice_emoji)

            await message.pin()

        else:
            await ctx.send(self.format_output_open(possible_opening, bet_hash))

    @commands.command()
    async def show(self, ctx):
        """Permet d'afficher les rÃ©sultats de toutes LES prÃ©dictions en cours et terminÃ©es"""
        self.check_correct_channel(ctx)

        stored_bets = read_json_file(self.storage_path)

        data = []
        for author in stored_bets.values():
            author_data = {}

            author_data['name'] = author['infos']['name']
            author_data['avatar'] = author['infos']['avatar']
            author_data['bets'] = []
            for bet in author['bets'].values():
                bet_data = {}

                timestamp = datetime.strptime(bet['timestamp'], '%Y-%m-%d %H:%M:%S')
                bet_data['timestamp'] = timestamp.strftime("%Y-%m-%d")
                bet_data['content'] = bet['content']
                bet_data['choices'] = []

                emojis = self.choices[bet['bet_type']]
                for emoji in emojis:
                    if len(bet['choices'][emoji]) > 0:
                        choice_data = {}
                        users = [await self.bot.fetch_user(user) for user in bet['choices'][emoji]]
                        choice_data['users'] = [user.name for user in users]
                        choice_data['choice_emoji'] = emojis[emoji]
                        choice_data['choice_value'] = emoji

                        bet_data['choices'].append(choice_data)

                author_data['bets'].append(bet_data)

            data.append(author_data)

        for embed in self.format_output_show(data):
            await ctx.send(embed=embed)

    @commands.command()
    async def list(self, ctx):
        """Permet de lister toutes TES prÃ©dictions avec leur ID et statut"""
        self.check_correct_channel(ctx)

        stored_bets = read_json_file(self.storage_path)
        author = str(ctx.author.id)
        author_name = ctx.author.name
        author_picture = ctx.author.avatar.url

        bets = {}
        if author in stored_bets and len(stored_bets[author]['bets']) > 0:
            bets = {bet_hash: bet['content'] for bet_hash, bet in stored_bets[author]['bets'].items()}

        await ctx.send(embed=self.format_output_list(author_name, author_picture, bets))

    @commands.Cog.listener()
    async def on_reaction_add(self, reaction, user):
        if user.bot:
            return

        if reaction.message.author.bot:

            users = [user async for user in reaction.users()]
            intented_message = False
            for user_reacted in users:
                if user_reacted.bot:
                    intented_message = True
                    break

            if intented_message:
                stored_bets = read_json_file(self.storage_path)
                embed = reaction.message.embeds[0]
                reacting_user = str(user.id)
                author = embed.footer.text.split(' ')[-1]
                bet_hash = embed.footer.text.split(' ')[0]
                bet = stored_bets[author]['bets'][bet_hash]

                if bet['status']:
                    emoji = str(reaction.emoji)
                    emojis = {value: key for key, value in self.choices[bet['bet_type']].items()}

                    if emoji in emojis:
                        # ADD new choice
                        if reacting_user not in bet['choices'][emojis[emoji]]:
                            bet['choices'][emojis[emoji]].append(reacting_user)
                        emojis.pop(emoji)

                        # DEL old possible choice
                        for other_emoji in emojis.values():
                            if reacting_user in bet['choices'][other_emoji]:
                                bet['choices'][other_emoji].remove(reacting_user)
                                break

                        create_json_file(self.storage_path, stored_bets)

    def check_correct_channel(self, ctx):
        if ctx.channel.id != self.bet_channel:
            raise commands.CheckFailure("Channel isn't valid!")

    def check_storage(self):
        if not os.path.exists(self.storage_path):
            create_json_file(self.storage_path, {})

    def init_author(self, member):
        return {
            'bets': {},
            'infos': {
                'avatar': member.avatar.url,
                'name': member.name,
            },
        }

    def init_bet(self, content: str, timestamp: str, bet_type: str = 'binary'):
        return {
                'content': content,
                'timestamp': timestamp,
                'status': True,
                'bet_type': bet_type,
                'choices': {choice: [] for choice in self.choices[bet_type].keys()}
            }

    def format_output_create(self, exisiting_bet: bool, bet_hash: str = '', author: str = '', content: str = ''):
        if exisiting_bet:
            return 'prÃ©diction dÃ©jÃ  mÃ©morisÃ©e'
        else:
            embed = Embed(title=f':game_die: NOUVELLE PRÃ‰DICTION :sparkles:', description= f':crystal_ball: -{content}')
            embed.set_footer(text=f"{bet_hash} crÃ©Ã©e par {author}")
            return embed

    def format_output_show(self, data: list):
        embeds = []

        for author in data:
            title = 'PrÃ©diction'
            if len(author['bets']) > 1:
                title += 's'

            high = random.randint(200, 255)
            mid = random.randint(100, 199)
            low = random.randint(0, 99)
            r, g, b = random.sample([high, mid, low], 3)
            hex_color = int("{:02x}{:02x}{:02x}".format(r, g, b), 16)

            embed = Embed(title=title, color=hex_color)

            embed.set_author(name=author['name'], icon_url=author['avatar'])

            for bet in author['bets']:
                timestamp = bet['timestamp']
                prediction = bet['content']
                embed.add_field(name=f'[{timestamp}] - {prediction}', value='', inline=False)

                for choice in bet['choices']:
                    users = ('\n').join(choice['users'])
                    value =f"```\n{users}\n```"
                    name = f' {choice["choice_emoji"]} {choice["choice_value"]}'
                    embed.add_field(name=name, value=value, inline=True)

            embeds.append(embed)

        return embeds

    def format_output_list(self, author_name: str, author_picture: str, bets: dict,):
        embed = Embed()
        embed.set_author(name=author_name, icon_url=author_picture)

        for bet_hash, bet in bets.items():
            embed.add_field(name=bet_hash, value=bet, inline=False)

        if len(bets.keys()) == 0:
            embed.add_field(name="Pas de prÃ©dictions trouvÃ©es", value="", inline=False)

        return embed

    def format_output_delete(self, deletion_return: bool, bet_hash: str):
        if deletion_return:
            return f'prÃ©diction {bet_hash} supprimÃ©e'
        else:
            return f"aucune prÃ©diction correspondate Ã  l'utilisateur prÃ©diction ou ID de la prÃ©diction incorrect"

    def format_output_close(self, closing_return: bool, bet_hash: str):
        if closing_return:
            return f'prÃ©diction {bet_hash} fermÃ©e'
        else:
            return f"aucune prÃ©diction correspondate Ã  l'utilisateur prÃ©diction ou ID de la prÃ©diction incorrect"

    def format_output_open(self, opening_return: bool, bet_hash: str):
        if opening_return:
            return f'prÃ©diction {bet_hash} ouverte'
        else:
            return f"aucune prÃ©diction correspondate Ã  l'utilisateur prÃ©diction ou ID de la prÃ©diction incorrect"

async def setup(bot):
    await bot.add_cog(Bet(bot))


from bs4 import BeautifulSoup
from selenium import webdriver
import time
def get_follower(url="https://twitter.com/KMbappe") :
    # try:
    #init webdriver
    options = webdriver.ChromeOptions()
    options.add_argument("headless")
    driver = webdriver.Chrome(options=options)
    time.sleep(1)
    driver.get(url)
    time.sleep(1)

    # page_source checking
    # file = open("output.txt","w",encoding="utf-8")
    # file.write(driver.page_source)
    # file.close()

    #scrape page_source using beautifulsoup
    bs = BeautifulSoup(driver.page_source,"lxml")
    get_tag = bs.find("a",href=lambda  href:href and "follower" in href)
    # print(get_tag)
    follower = get_tag.find_all("span")[1].string
    print(follower)
    driver.quit()
    # except :
    #     print("Something gone wrong")
    return follower

if __name__ == "__main__":
    inp = str(input("Please enter : ").strip())
    if "https://twitter.com/" not in inp :
        get_follower()
    else :
        get_follower(inp)


class connect:
    def __init__(self, access=None, secret=None):
        self.region='us-east-2'
        self.ac = access
        self.se = secret
        self.myaddress='52.14.29.127'
        self.key='dec15a'
        self.MAX_SPOT_BID='1.0'
        self.security_groups = ['default']

        import time
        import boto
        import boto.ec2
        from boto.ec2.blockdevicemapping import BlockDeviceType, BlockDeviceMapping
        self.ec2_conn = boto.ec2.connect_to_region(self.region, aws_access_key_id=self.ac, aws_secret_access_key=self.se)

        shell_script = """
        #!/bin/bash -ex
        yum install -y docker mysql git python-pip
        sudo amazon-linux-extras install -y docker
        pip install aws-ec2-assign-elastic-ip
        aws-ec2-assign-elastic-ip --access-key {}, --secret-key {}  --valid-ips {}
        service docker start
        docker run -d -p 8887:8888 -v /tmp:/tmp shantanuo/notebook

        """
        self.my_user_data=shell_script.format(self.ac,self.se, self.myaddress).encode()

        self.dev_sda1 = BlockDeviceType()
        self.bdm = BlockDeviceMapping()
        self.dev_sda1.size = 500
        self.bdm['/dev/xvda'] = self.dev_sda1

    def startEc2Spot(self, ami, instance_type):
        aid = {'image_id': ami, 'instance_type': instance_type, 'user_data': self.my_user_data , 'key_name': self.key,
              'security_groups': self.security_groups, 'block_device_map' : self.bdm, 'price': self.MAX_SPOT_BID}
        daid=dict(aid)
        rid=self.ec2_conn.request_spot_instances(**daid)

        time.sleep(180)
        job_sir_id = rid[0].id # spot instance request = sir, job_ is the relevant aws item for this job
        reqs = self.ec2_conn.get_all_spot_instance_requests()
        for sir in reqs:
            if sir.id == job_sir_id:
                job_instance_id = sir.instance_id
                print ("job instance id: " + str(job_instance_id))

                if self.myaddress:
                    self.ec2_conn.associate_address(job_instance_id, self.myaddress)
                    print ('ssh -i ' + self.key+ '.pem ec2-user@'+self.myaddress)
                else:
                    address = self.ec2_conn.allocate_address()
                    self.ec2_conn.associate_address(job_instance_id, address.public_ip)
                    print ('ssh -i ' + self.key+ '.pem ec2-user@'+str(address.public_ip))

    def startEc2(self, ami, instance_type):
        aid = { 'image_id': ami, 'instance_type': instance_type, 'user_data': self.my_user_data ,'key_name': self.key,
                 'security_groups': self.security_groups, 'block_device_map' : self.bdm}
        daid=dict(aid)
        rid=self.ec2_conn.run_instances(**daid)

        time.sleep(120)
        iid=self.ec2_conn.get_all_instances(filters={'reservation-id': rid.id})[0].instances[0]
        if self.myaddress:
            self.ec2_conn.associate_address(iid.id, self.myaddress)
            print ('ssh -i ' + self.key+ '.pem ec2-user@'+self.myaddress)
        else:
            address = self.ec2_conn.allocate_address()
            self.ec2_conn.associate_address(iid.id, address.public_ip)
            print ('ssh -i ' + self.key+ '.pem ec2-user@'+str(address.public_ip))

    def showEc2(self):
        mylist=[]
        info=self.ec2_conn.get_only_instances()
        for reservation in info:
            mylist.append( (reservation , reservation.launch_time, reservation.instance_type,
                            reservation.image_id,reservation.state, reservation.ip_address))
        import pandas as pd
        col_name=['instance_id', 'launch_time', 'instance_type', 'image_id', 'state', 'ip_address']
        try:
            df = pd.DataFrame(mylist, columns=col_name)
            return df
        except ValueError:
            pass

    def deleteEc2(self,  instance_id_to_delete, mylist=None):
        mylist=[]
        mylist.append(instance_id_to_delete)
        print (mylist)
        self.ec2_conn.terminate_instances(instance_ids=mylist)

    def deleteAllEc2(self):
        mylistDel=[]
        info=self.ec2_conn.get_only_instances()
        for reservation in info:
            mylistDel.append(reservation.id)

        print ("this does not actually delete the ec2 instances. Run the following command on your own risk :)\n")
        print ("import boto")
        print ("ec2_conn = boto.connect_ec2(aws_access_key_id='%s', aws_secret_access_key='%s')" % (self.ac, self.se))
        print ("ec2_conn.terminate_instances(instance_ids=%s)" % (mylistDel))

    def showImages(self):
        mylist=[]
        i_list=self.ec2_conn.get_all_images(owners=['self'])
        for i in i_list:
            mylist.append((i.id, i.is_public, i.name, i.description,i.region))
        import pandas as pd
        col_name=['image_id', 'is_public', 'name', 'description', 'region']
        df = pd.DataFrame(mylist, columns=col_name)
        return df

    """
#x = connect('xxx', 'xxx')
#x.startEc2Spot('ami-009d6802948d06e52', 'm3.large')
#x.showEc2()
#x.deleteAllEc2()
#x.showImages()
    """


from jinja2 import Environment, FileSystemLoader

## Necessaire pour les dates en français
import datetime
import locale
locale.setlocale(locale.LC_ALL, 'fr_FR.UTF-8')

TMPL_DIR = "tmpl"
fichier = "index.jinja"

templateLoader = FileSystemLoader(searchpath=TMPL_DIR)
templateEnv = Environment(loader=templateLoader)
template = templateEnv.get_template(fichier)

data={
        'time_stamp':datetime.datetime.now().strftime("%x %X")
    }

print( template.render( data ) )


import os

import time
import numpy as np
import pandas as pd
from sklearn.metrics import accuracy_score

from language_clf import LangClf
import pdb

# --- Tirando referências -----

path = r'../Resources/bibles/'
stems_path = r'../Resources/stems/stems.csv'
new_dir = r'../Resources/texts/'

def load_data(training_split=.8, number_documents=100):
    """
     It loads the data according to a given threshold. The threshold will tell us the number of books to be loaded.
     Here the train, test, and stems are loaded.
    :param number_documents: The total number of documents to be retrieved
    :param training_split: How you want to split them
    :return: the training set, testing set and stem dictionary
    """

    x_text, y_text = [], []

    stems_dic = pd.read_csv(stems_path, encoding='utf-8').dropna()
    X_train = []
    y_train = []

    X_test = []
    y_test = []

    for filename in os.listdir(new_dir):
        file = open(new_dir + filename, encoding='utf-8')
        text = [line.strip('\n') for line in file.readlines()]

        text = text[:number_documents]
        train_size = int(len(text) * training_split)
        X_train.append(text[:train_size])
        y_train.append(filename.split('.')[0])
        X_test.append(text[train_size:])
        y_test.append(filename.split('.')[0])

    return stems_dic, np.array(X_train), np.array(y_train), np.array(X_test), np.array(y_test)

def classify(threshold=4):
    """
    Makes the classification of all languages according to a threshold
    :return: two vectors with the text label, predicted, similarity of all predictions
    """
    start = time.time()  # initial time

    stems_dic, X_train, y_train, X_test, y_test = load_data(.1)  # Loads the data according to the established
    print('Train shape: ', X_train.shape)
    print('X_test shape: ', X_test.shape)
    # threshold in
    # terms of number of books
    clf = LangClf(stems_dic)
    print('Fitting the clf...')

    clf.fit(X_train, y_train)  # Fits the Classifier with the training and test set
    print('Testing...')
    y_pred = clf.test(X_test)

    time_taken = (time.time() - start)

    accuracy = accuracy_score(y_test, y_pred)

    print("Threshold: ", threshold)
    print("Accuracy: ", accuracy)
    print("Final time: {:.5f}min".format(time_taken / 60))
    clf.run_som()

    print()
    print('-' * 40)
    print()
    # clf.get_test_plot()
    return {"Threshold": threshold,
            "Accuracy": accuracy,
            "Time": time_taken / 60}

def run_experiment():
    for i in range(1, 2):
        classify(i)

def main():
    run_experiment()

if __name__ == "__main__":
    main()


import tensorflow as tf
import numpy as np
import tensorflow_datasets as tfds
from matplotlib import pyplot as plt
import string
from PIL import Image

# 标签数据 对应 字符
# 0-9
# 10-35 A~Z
# 36-61 a~z
label = list(string.digits + string.ascii_uppercase + string.ascii_lowercase)
# 记录已经遍历的数量
records = [0 for x in label]
# 存储文件夹
path = 'D:\\Temp\\emnist\\'

# 加载数据
(train, test), ds_info = tfds.load(
    'emnist',
    split=['train', 'test'],
    data_dir='./tensorflow_datasets',
    shuffle_files=True,  #
    as_supervised=True,
    with_info=True,
)

# emnist数据集天生旋转，翻转每张图片数据
def normalize_img(image, label):
    """Normalizes images: `uint8` -> `float32`."""
    # image = tf.transpose(image, perm=[1, 0, 2])
    # return tf.cast(image, tf.float32) / 255., label
    # perm=[1,0,2]表示只翻转第一第二维度
    return tf.transpose(image, perm=[1, 0, 2]), label

# 开始翻转图片数据
train = train.map(normalize_img, num_parallel_calls=tf.data.AUTOTUNE)
test = test.map(normalize_img, num_parallel_calls=tf.data.AUTOTUNE)

# 将图片数据转为numpy
train_numpy = np.vstack(tfds.as_numpy(train))
test_numpy = np.vstack(tfds.as_numpy(test))

# 保存图片
for p in train_numpy:
    image_array = np.asarray(p[0])
    image_array = image_array.reshape(28, 28)
    image = Image.fromarray(image_array.astype('uint8'))
    lab = p[1]
    directory = label[lab]
    if lab >= 36:
        directory += directory
    path_train = path + '\\train\\' + directory + '\\' + label[lab] + '-' + str(records[lab]) + '.png'
    image.save(path_train)
    records[lab] += 1

for p in test_numpy:
    image_array = np.asarray(p[0])
    image_array = image_array.reshape(28, 28)
    image = Image.fromarray(image_array.astype('uint8'))
    lab = p[1]
    directory = label[lab]
    if lab >= 36:
        directory += directory
    path_train = path + '\\test\\' + directory + '\\' + label[lab] + '-' + str(records[lab]) + '.png'
    image.save(path_train)
    records[lab] += 1


import numpy as np
import matplotlib.pyplot as plt

def sinusGenerator(amplitude, frequency, time, theta):
    t = np.arange(0,time,0.1)
    y = amplitude * np.sin(2*frequency*t + np.deg2rad(theta))
    return t,y

t1, y1 = sinusGenerator(1,1,4,0)
t2, y2 = sinusGenerator(1,1,4,90)

# 1st Type
# plt.plot(t1,y1, label="sin(0)")
# plt.plot(t2,y2, label="sin(90)")
# plt.legend()

# 2nd Type
# plt.plot(t1,y1, label="sin(0)")
# plt.plot(t2,y2, label="sin(90)")
# plt.legend(loc="upper center")

# 3rd Type
# plt.plot(t1,y1, label="sin(0)")
# plt.plot(t2,y2, label="sin(90)")
# plt.legend(loc="upper center", bbox_to_anchor=(0.5,-0.05))

# 4th Type
plt.figure(1)
ax = plt.subplot(111)
plt.plot(t1,y1, "r--", label="sin(0)" )
plt.plot(t2,y2, "b--", label="sin(90)")
box = ax.get_position()
# shrink to 0.75
ax.set_position([box.x0, box.y0, box.width*0.75, box.height])
plt.legend(loc="upper center", bbox_to_anchor=(1.2,1))
plt.xlabel("Time(second)")
plt.ylabel("Amplitude(cm)")

#show plot
plt.show()


import pygame

class Graphics(object):
    def __init__(self, element):
        self.color = (0, 0, 0)
        self.elem = element
    def draw(self, screen, x, y, w, h):
        from Spike import Spike
        #print("drawing at: ", x, y, w, h, self.elem)
        """draws a rectangle representation of the element defined in the
        constructor
        """
        if isinstance(self.elem, Spike):
            print(y)
        pygame.draw.rect(screen,self.color,[int(x),int(y),w,h],2)


import torch
import torch.nn as nn
from torch.autograd import Variable
import torch.nn.functional as F
import requests
import numpy as np
import sklearn

class GRU_with_Conv1D_Model (nn.Module):
    def __init__(self, total_tag, embeding_size, gru_hidden,gru_layer, filter_num_1, filter_size_1, filter_num_2, filter_size_2, output_size, max_word):
        super(GRU_with_Conv1D_Model, self).__init__()
        self.total_tag = total_tag
        self.max_word = max_word
        self.embeding_size = embeding_size
        self.gru_hidden = gru_hidden
        self.gru_layer = gru_layer
        self.filter_num_1 = filter_num_1
        self.filter_size_1 = filter_size_1
        self.filter_num_2 = filter_num_2
        self.filter_size_2 = filter_size_2
        self.output_size = output_size
        self.Embeding = nn.Embedding(self.total_tag, self.embeding_size)
        self.GRU = nn.GRU(self.embeding_size , self.gru_hidden, self.gru_layer, bidirectional = True)
        self.Conv1d_layer1 = nn.Sequential(nn.Conv1d(in_channels=self.gru_hidden*2,
                                                            out_channels=self.filter_num_1,
                                                            kernel_size=self.filter_size_1),
                                                         #nn.BatchNorm1d (self.filter_size_1),
                                                         nn.ReLU())

        self.Conv1d_layer2 = nn.Sequential(nn.Conv1d(in_channels=self.filter_num_1,
                                                            out_channels=self.filter_num_2,
                                                            kernel_size=self.filter_size_2),
                                                         #nn.BatchNorm1d (self.filter_size_2),
                                                         nn.ReLU())
        # batch size is  set to 1 , output_size is set to 2
        L = (self.max_word- self.filter_size_1+ 1)- self.filter_size_2+ 1
        self.linear = nn.Linear( self.filter_num_2 * L, self.output_size)

    def forward(self, input_data):
        embed_res = self.Embeding (input_data)
        if len(input_data) < self.max_word:
            for c in range (self.max_word - len(input_data)):
                embed_res = torch.cat((embed_res, torch.zeros(1, self.embeding_size)),dim = 0 )
        print (' Embedding :::: ' , embed_res.size())
        # let the input dimension be the (L , N , M) and the get the output with the dimension (L , N , H)
        gru_res, _ = self.GRU(embed_res.unsqueeze(1))
        print (' GRU :::: ' , gru_res.size())
        # let the input dimension be the (N , Cin , L) and then the output with the dimension (N, Cout , Lout)
        conv_res1 = self.Conv1d_layer1 (gru_res.permute(1,2,0))
        print (' conv_res1 :::: ' , conv_res1.size())
        conv_res2 = self.Conv1d_layer2 (conv_res1)
        print ('conv_res2 :::: ' , conv_res2.size())
        linear_res = self.linear(conv_res2.view(-1))
        final_res = F.sigmoid(linear_res)
        return final_res

if __name__ == '__main__':
    # Testing the code
    model_v3 = GRU_with_Conv1D_Model(total_tag= 16, embeding_size=8, gru_hidden= 6 , gru_layer=1, filter_num_1=5, filter_size_1=2, filter_num_2 = 5, filter_size_2 = 2,output_size = 7, max_word = 10)
    test_vec = torch.LongTensor([1,2,3,4,5,6,7])
    model_v3 (test_vec)
    print ('V3_Model_with_conv_and_gru ::: \n', model_v3 )


from enum import Enum, auto
import re
from typing import List, Optional, Any, Tuple, Dict
import xml.etree.ElementTree as XML

from errors import ErrorCodes, handle_error

escapes=re.compile(r"\\[0-9]{3}")

class InstructionKey(Enum):
  MOVE = auto()#

  CREATEFRAME = auto()#
  PUSHFRAME = auto()#
  POPFRAME = auto()#

  DEFVAR = auto()#
  CALL = auto()#
  RETURN = auto()#
  PUSHS = auto()#
  POPS = auto()#

  ADD = auto()#
  SUB = auto()#
  MUL = auto()#
  DIV = auto()#
  IDIV = auto()#

  LT = auto()#
  GT = auto()#
  EQ = auto()#

  AND = auto()#
  OR = auto()#
  NOT = auto()#

  INT2CHAR = auto()#
  STRI2INT = auto()#
  INT2FLOAT = auto()#
  FLOAT2INT = auto()#

  READ = auto()#
  WRITE = auto()#
  CONCAT = auto()#
  STRLEN = auto()#
  GETCHAR = auto()#
  SETCHAR = auto()#
  TYPE = auto()#
  JUMP = auto()#

  JUMPIFEQ = auto()#
  JUMPIFNEQ = auto()#

  EXIT = auto()#
  LABEL = auto()#

  DPRINT = auto()#
  BREAK = auto()

  # Stack operations
  CLEARS = auto()#

  ADDS = auto()#
  SUBS = auto()#
  MULS = auto()#
  DIVS = auto()#
  IDIVS = auto()#

  LTS = auto()#
  GTS = auto()#
  EQS = auto()#

  ANDS = auto()#
  ORS = auto()#
  NOTS = auto()#

  INT2CHARS = auto()#
  STRI2INTS = auto()#
  INT2FLOATS = auto()#
  FLOAT2INTS = auto()#

  JUMPIFEQS = auto()#
  JUMPIFNEQS = auto()#

STRING_TO_INSTRUCTION = {
  "MOVE": InstructionKey.MOVE,
  "CREATEFRAME": InstructionKey.CREATEFRAME,
  "PUSHFRAME": InstructionKey.PUSHFRAME,
  "POPFRAME": InstructionKey.POPFRAME,
  "DEFVAR": InstructionKey.DEFVAR,
  "CALL": InstructionKey.CALL,
  "RETURN": InstructionKey.RETURN,
  "PUSHS": InstructionKey.PUSHS,
  "POPS": InstructionKey.POPS,
  "ADD": InstructionKey.ADD,
  "SUB": InstructionKey.SUB,
  "MUL": InstructionKey.MUL,
  "DIV": InstructionKey.DIV,
  "IDIV": InstructionKey.IDIV,
  "LT": InstructionKey.LT,
  "GT": InstructionKey.GT,
  "EQ": InstructionKey.EQ,
  "AND": InstructionKey.AND,
  "OR": InstructionKey.OR,
  "NOT": InstructionKey.NOT,
  "INT2CHAR": InstructionKey.INT2CHAR,
  "STRI2INT": InstructionKey.STRI2INT,
  "INT2FLOAT": InstructionKey.INT2FLOAT,
  "FLOAT2INT": InstructionKey.FLOAT2INT,
  "READ": InstructionKey.READ,
  "WRITE": InstructionKey.WRITE,
  "CONCAT": InstructionKey.CONCAT,
  "STRLEN": InstructionKey.STRLEN,
  "GETCHAR": InstructionKey.GETCHAR,
  "SETCHAR": InstructionKey.SETCHAR,
  "TYPE": InstructionKey.TYPE,
  "LABEL": InstructionKey.LABEL,
  "JUMP": InstructionKey.JUMP,
  "JUMPIFEQ": InstructionKey.JUMPIFEQ,
  "EXIT": InstructionKey.EXIT,
  "DPRINT": InstructionKey.DPRINT,
  "BREAK": InstructionKey.BREAK,

  # Stack operations
  "CLEARS": InstructionKey.CLEARS,

  "ADDS": InstructionKey.ADDS,
  "SUBS": InstructionKey.SUBS,
  "MULS": InstructionKey.MULS,
  "DIVS": InstructionKey.DIVS,
  "IDIVS": InstructionKey.IDIVS,

  "LTS": InstructionKey.LTS,
  "GTS": InstructionKey.GTS,
  "EQS": InstructionKey.EQS,

  "ANDS": InstructionKey.ANDS,
  "ORS": InstructionKey.ORS,
  "NOTS": InstructionKey.NOTS,

  "INT2CHARS": InstructionKey.INT2CHARS,
  "STRI2INTS": InstructionKey.STRI2INTS,
  "INT2FLOATS": InstructionKey.INT2FLOATS,
  "FLOAT2INTS": InstructionKey.FLOAT2INTS,

  "JUMPIFEQS": InstructionKey.JUMPIFEQS,
  "JUMPIFNEQS": InstructionKey.JUMPIFNEQS
}

class ArgumentTypeKey(Enum):
  TYPE = auto()
  LABEL = auto()
  VAR = auto()
  INT = auto()
  FLOAT = auto()
  BOOL = auto()
  STRING = auto()
  NIL = auto()

STRING_TO_ARGUMENT_TYPE = {
  "type": ArgumentTypeKey.TYPE,
  "label": ArgumentTypeKey.LABEL,
  "var": ArgumentTypeKey.VAR,
  "int": ArgumentTypeKey.INT,
  "float": ArgumentTypeKey.FLOAT,
  "bool": ArgumentTypeKey.BOOL,
  "string": ArgumentTypeKey.STRING,
  "nil": ArgumentTypeKey.NIL
}

class FrameTypeKey(Enum):
  GLOBAL = auto()
  LOCAL = auto()
  TEMPORARY = auto()

STRING_TO_FRAME_TYPE = {
  "GF": FrameTypeKey.GLOBAL,
  "LF": FrameTypeKey.LOCAL,
  "TF": FrameTypeKey.TEMPORARY
}

def dec2char(matchobj):
  dec = matchobj.group(0)
  dec = int(dec[1:])
  return chr(dec)

class Argument:
  def __init__(self, t:ArgumentTypeKey, value:str, idx:int):
    self.idx = idx
    self.type = t
    if value is None:
      value = ""

    if self.type == ArgumentTypeKey.NIL and value == "nil":
      self.value = None
    elif self.type == ArgumentTypeKey.INT:
      try:
        self.value = int(value)
      except:
        handle_error(ErrorCodes.XML_BAD_STRUCTURE, f"Failed to convert value '{value}' to int")
    elif self.type == ArgumentTypeKey.FLOAT:
      try:
        self.value = float(value)
      except:
        try:
          self.value = float.fromhex(value)
        except:
          handle_error(ErrorCodes.XML_BAD_STRUCTURE, f"Failed to convert value '{value}' to float")
    elif self.type == ArgumentTypeKey.BOOL and (value == "true" or value == "false"):
      if value == "true":
        self.value = True
      else:
        self.value = False
    elif self.type == ArgumentTypeKey.STRING:
      self.value = escapes.sub(dec2char, value)
    elif self.type == ArgumentTypeKey.LABEL:
      self.value = value
    elif self.type == ArgumentTypeKey.TYPE:
      if value not in ("float", "int", "bool", "string", "nil"):
        handle_error(ErrorCodes.XML_BAD_STRUCTURE, f"Argument type can't have value '{value}'")
      self.value = STRING_TO_ARGUMENT_TYPE[value]
    elif self.type == ArgumentTypeKey.VAR and len(value) != 0:
      amp_pos = value.find("@")
      if amp_pos == -1:
        handle_error(ErrorCodes.XML_BAD_STRUCTURE, f"Can't parse string '{value}' as variable type")

      if value[:amp_pos] not in STRING_TO_FRAME_TYPE.keys():
        handle_error(ErrorCodes.XML_BAD_STRUCTURE, f"Value '{value[:amp_pos]}' is not valid identificator of frame")

      frame_type = STRING_TO_FRAME_TYPE[value[:amp_pos]]

      variable_name = value[amp_pos + 1:]
      if not variable_name:
        handle_error(ErrorCodes.XML_BAD_STRUCTURE, f"String '{value}' missing variable name for parsing as variable")

      self.value = (frame_type, variable_name)
    else:
      handle_error(ErrorCodes.XML_BAD_STRUCTURE, f"Type '{self.type}' and value '{value}' is not valid combination of values")

  def __repr__(self):
    return f"Argument(Type: {self.type}, Value: '{self.value}')"

class Instruction:
  def __init__(self, instruction: InstructionKey, order:int, arguments: Optional[List[Argument]] = None):
    self.order = order
    self.instruction = instruction
    self.arguments = arguments if arguments else []

  @classmethod
  def from_element(cls, element: XML.Element):
    if "opcode" not in element.keys() or "order" not in element.keys():
      handle_error(ErrorCodes.XML_BAD_STRUCTURE, "Missing 'opcode' or 'order' instruction attribute")

    instruction_name = element.attrib["opcode"].upper()
    if instruction_name not in STRING_TO_INSTRUCTION.keys():
      handle_error(ErrorCodes.XML_BAD_STRUCTURE, f"'{instruction_name}' is not valid instruction")

    argument_name_list = [arg.tag for arg in element]
    if argument_name_list.count("arg1") > 1 or argument_name_list.count("arg2") > 1 or argument_name_list.count("arg3") > 1:
      handle_error(ErrorCodes.XML_BAD_STRUCTURE, f"'{instruction_name}' have multiple arguments with same name")

    order = element.attrib["order"]
    if not order.isdecimal():
      handle_error(ErrorCodes.XML_BAD_STRUCTURE, f"'{instruction_name}' have invalid order attribute")

    arguments = []
    used_argument_indexes = []
    for argument in element:
      if argument.tag not in ("arg1", "arg2", "arg3"):
        handle_error(ErrorCodes.XML_BAD_STRUCTURE, f"'{argument.tag}' in instruction '{instruction_name}' is not valid argument of instruction")

      if "type" not in argument.keys():
        handle_error(ErrorCodes.XML_BAD_STRUCTURE, f"'{argument.tag}' in instruction '{instruction_name}' don't have type attribute")

      type_string = argument.attrib["type"]
      if type_string not in STRING_TO_ARGUMENT_TYPE:
        handle_error(ErrorCodes.INTERN, f"'{type_string}' is not valid type")

      idx = int(argument.tag[3:])
      if idx in used_argument_indexes:
        handle_error(ErrorCodes.XML_BAD_STRUCTURE, f"'{argument.tag}' in instruction '{instruction_name}' is already present")

      used_argument_indexes.append(idx)
      arguments.append(Argument(STRING_TO_ARGUMENT_TYPE[type_string], argument.text, idx))

    # Check if there are no arg3 without arg2 or arg1 or arg2 without arg1
    if (3 in used_argument_indexes and (2 not in used_argument_indexes or 1 not in used_argument_indexes)) or \
      (2 in used_argument_indexes and 1 not in used_argument_indexes):
      handle_error(ErrorCodes.XML_BAD_STRUCTURE, f"'{instruction_name}' have invalid argument indexes")

    arguments.sort(key=lambda x: x.idx)
    return cls(STRING_TO_INSTRUCTION[instruction_name], int(order), arguments)

  def __repr__(self):
    arguments_string = (", ".join([str(arg) for arg in self.arguments])) if self.arguments else ""
    return f"Instruction({self.order}, {self.instruction}, [{arguments_string}])"

class VariableTypeKey(Enum):
  INT = auto()
  FLOAT = auto()
  BOOL = auto()
  STRING = auto()
  NIL = auto()

ArgumentTypeToVariableType = {
  ArgumentTypeKey.INT: VariableTypeKey.INT,
  ArgumentTypeKey.FLOAT: VariableTypeKey.FLOAT,
  ArgumentTypeKey.BOOL: VariableTypeKey.BOOL,
  ArgumentTypeKey.STRING: VariableTypeKey.STRING,
  ArgumentTypeKey.NIL: VariableTypeKey.NIL
}

class Variable:
  def __init__(self, label:str):
    self.__label = label

    self.__type:Optional[VariableTypeKey] = None
    self.__value = None

  def is_initialized(self):
    return self.__type is not None

  def set_value(self, value):
    if isinstance(value, bool):
      self.__type = VariableTypeKey.BOOL
    elif isinstance(value, int):
      self.__type = VariableTypeKey.INT
    elif isinstance(value, float):
      self.__type = VariableTypeKey.FLOAT
    elif isinstance(value, str):
      self.__type = VariableTypeKey.STRING
    elif value is None:
      self.__type = VariableTypeKey.NIL
    else:
      handle_error(ErrorCodes.INTERN, f"Invalid datatype '{type(value)}' passed to variable")
    self.__value = value

  def get_label(self):
    return self.__label

  def get_value(self) -> Tuple[VariableTypeKey, Any]:
    return self.__type, self.__value

  def __repr__(self):
    return f"[{self.__label}:{self.__type}='{self.__value}']"

class Frame:
  def __init__(self, frame_type: FrameTypeKey):
    self.type = frame_type # only for debug

    self.variable_storage:Dict[str, Variable] = {}

  def __get_by_name(self, label:str):
    if not self.__variable_exist(label): return None
    return self.variable_storage.get(label)

  def __variable_exist(self, label:str) -> bool:
    if label in self.variable_storage.keys(): return True
    return False

  def create_variable(self, label:str):
    if self.__variable_exist(label):
      handle_error(ErrorCodes.SEMANTIC_ERROR, f"Variable with name '{label}' already exists in frame of type '{self.type}'")
    self.variable_storage[label] = Variable(label)

  def set_value(self, label:str, value):
    variable = self.__get_by_name(label)
    if variable is None:
      handle_error(ErrorCodes.VARIABLE_DONT_EXIST, f"Variable with name '{label}' doesn't exists in frame of type '{self.type}'")

    variable.set_value(value)

  def get_value(self, label:str) -> Tuple[VariableTypeKey, Any]:
    variable = self.__get_by_name(label)
    if variable is None:
      handle_error(ErrorCodes.VARIABLE_DONT_EXIST, f"Variable with name '{label}' doesn't exists in frame of type '{self.type}'")

    return variable.get_value()

  def get_number_of_initialized_variables(self):
    cnt = 0
    for var in self.variable_storage.values():
      if var.is_initialized():
        cnt += 1

    return cnt

  def __repr__(self):
    variables = "\n\t".join([str(var) for var in self.variable_storage])
    return f"Frame({self.type}:\n\t{variables})"


import os
import sys
import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException
from PIL import Image

chrome_options = Options()
delay = 5 # seconds

def get_warranty_HTML(file):
  global driver
  chrome_options.add_argument("--headless")
  chrome_options.add_argument("--log-level=3")
  driver = webdriver.Chrome('C:/TEMP/chromedriver.exe', options=chrome_options)
  driver.get('https://support.hpe.com/hpsc/wc/public/find')
  if os.name == "posix":
    os.system ("clear")
  elif os.name == ("ce" or "nt" or "dos"):
    os.system ("cls")

  captchadef()

  count_lines = len(open(file).readlines())

  if count_lines < 10:
    check_n(count_lines, file, driver)

  elif count_lines <= 20:
    driver.find_element_by_xpath('//*[@id="wcFormDataItem"]/span/a').click()
    check_n(count_lines, file, driver)
  else:
    print('No more of 20 serials')
    quit()

def check_n(count_lines, file, driver):
  n = 0
  try:
    WebDriverWait(driver, delay).until(EC.presence_of_element_located((By.ID, 'serialNumber%s' %(n))))
    print ("Checking Serials, please wait...!")
  except TimeoutException:
    print ("Loading took too much time!")
  while n < count_lines:
    for line in open(file):
      driver.find_element_by_id('serialNumber%s' %(n)).send_keys(line.strip())
      n= n + 1

  try:
    driver.find_element_by_name('submitButton').click()
  except:
    print('Submit Button not found, exit...')
    quit()

  captchadef()

  try_number = 0
  while True:
    try:
      driver.find_element_by_xpath('//*[@id="nonIntroBlock"]/div')
      n = 0
      remo = 1
      while n < count_lines:
        try:
          product= 'productNumber'+ str(n)
          driver.find_element_by_id(product)
          for element in driver.find_elements_by_xpath('//*[@id="serialNumber%s"]' %(str(n))):
            print ('\n', element.get_attribute('value'))
          if try_number >= 2:
            remocheck = ('/html/body/div[1]/div/div[3]/div/table/tbody/tr/td[3]/table/tbody/tr[2]/td/div/form/table/tbody/tr[%s]/td[4]/a' %(str(remo)))
            print('\nRemoving bad serials with P/N')
            driver.find_element_by_xpath(remocheck).click()
            paso = 1
            break
          product_n = input('Need insert Product number for this serial: ')
          driver.find_element_by_id(product).clear()
          driver.find_element_by_id(product).send_keys(product_n)
        except:
          pass
        n = n + 1
        remo = remo + 2
      print('Trying...')
      driver.find_element_by_name('submitButton').click()
      try_number = try_number + 1
      paso = 1
    except:
      paso = 1
      break

  captchadef()

  if paso == 1:
    f = open("checked.txt","a")
    for line in open(file):
      print('Warranty checked for '+ line.strip())
      f.write('\nWarranty checked for '+ line.strip() +('\n'))
      print('-----------------------------------------------')
      f.write('----------------------------------------------- \n')
      for element in driver.find_elements_by_xpath('//*[@id="product_description_%s"]/h3/b' %(line.strip())):
        print (element.text)
        f.write(element.text +('\n'))
      for element in driver.find_elements_by_xpath('//*[@id="generate_table_%s"]/div' %(line.strip())):
        print (element.text)
        f.write(element.text +('\n'))
      for element in driver.find_elements_by_xpath('//*[@id="generate_table_%s"]/table/tbody' %(line.strip())):
        print (element.text)
        f.write(element.text)
        print('\n')
        f.write('\n')
    f.close()
    input('All SN checked press ENTER to finish.')
  driver.close()

def captchadef():
  global driver
  # Time to waiting is page is ready 'delay'
  try:
    WebDriverWait(driver, delay).until(EC.presence_of_element_located((By.ID, 'captchaChars')))
  except TimeoutException:
    print ("Loading took too much time or not captcha needed")
    return()
  try:
    z = 0
    if z > 1:
      print('Captcha fail, try again...')
    while driver.find_element_by_id('captchaChars'):
      element = driver.find_element_by_xpath('//*[@id="captchaImg"]')
      location = element.location
      size = element.size
      time.sleep(2)
      driver.save_screenshot('pageimage.png')
      # crop image
      x = location['x']
      y = location['y']
      width = location['x']+size['width']
      height = location['y']+size['height']
      im = Image.open('pageImage.png')
      im = im.crop((int(x), int(y), int(width), int(height)))
      im.save('captcha.png')
      img=Image.open('captcha.png')
      img.show()
      capt_re = input('Please insert captcha: ')
      driver.find_element_by_id('captchaChars').send_keys(capt_re)
      driver.find_element_by_id('captchaSubmitBtn').click()
      os.system('TASKKILL /F /IM Microsoft.Photos.exe 2>NUL') #for windows 10
  except:
    print('\nNo more captcha needed')
  return()

def main(argv):
  if len(argv) == os.path.isfile('*.txt'):
    print('\nChecking for %s ... \n' % (argv[1]))
    get_warranty_HTML(argv[1])
  else:
    while True:
      argv = input('Please insert your txt file (q): ')

      if os.path.isfile(argv):
        print('\nChecking for %s ... \n' % (argv))
        get_warranty_HTML(argv)
      if argv == 'q':
        quit()

if __name__ == "__main__":
    main(sys.argv)


import logging
from functools import partial

from pyqtgraph.Qt import QtWidgets
from pyqtgraph import SpinBox

class DP832Widget(QtWidgets.QWidget):
    """A Qt widget for controlling the Rigol DP832 power supply."""
    def __init__(self, driver):
        """
        Args:
            driver: The dp832 driver.
        """
        super().__init__()

        self.driver = driver

        # top level layout
        layout = QtWidgets.QGridLayout()

        channels = 3
        for ch in range(1, channels+1):
            layout_row = 0

            # label
            layout.addWidget(QtWidgets.QLabel(f'Ch {ch}'), layout_row, ch)
            layout_row += 1

            # for on/off buttons
            def toggle(checked, ch, state):
                self.driver.toggle_output(ch=ch, state=state)

            # button to turn the channel on
            self.on_button = QtWidgets.QPushButton('On')
            self.on_button.clicked.connect(partial(toggle, ch=ch, state=True))
            layout.addWidget(self.on_button, layout_row, ch)
            layout_row += 1

            # button to turn the channel off
            self.off_button = QtWidgets.QPushButton('Off')
            self.off_button.clicked.connect(partial(toggle, ch=ch, state=False))
            layout.addWidget(self.off_button, layout_row, ch)
            layout_row += 1

            # voltage spinbox
            volts_layout = QtWidgets.QHBoxLayout()
            volts_layout.addWidget(QtWidgets.QLabel('Volts'))
            volts_spinbox = SpinBox(value=self.driver.get_voltage(ch), bounds=(0, 30), siPrefix=False)
            def set_volts(spinbox, ch):
                self.driver.set_voltage(ch, spinbox.value(), confirm=False)
            volts_spinbox.sigValueChanged.connect(partial(set_volts, ch=ch))
            volts_layout.addWidget(volts_spinbox)
            layout.addLayout(volts_layout, layout_row, ch)
            layout_row += 1

            # current spinbox
            amps_layout = QtWidgets.QHBoxLayout()
            amps_layout.addWidget(QtWidgets.QLabel('Amps'))
            amps_spinbox = SpinBox(value=self.driver.get_current(ch), bounds=(0, 3), siPrefix=False)
            def set_amps(spinbox, ch):
                self.driver.set_current(ch, spinbox.value(), confirm=False)
            amps_spinbox.sigValueChanged.connect(partial(set_amps, ch=ch))
            amps_layout.addWidget(amps_spinbox)
            layout.addLayout(amps_layout, layout_row, ch)
            layout_row += 1

        # take up any additional space in the final row with padding
        layout.setRowStretch(layout_row, 1)

        self.setLayout(layout)