From 37d8a6b48a724e0861919f7561b7a371d59cba7a Mon Sep 17 00:00:00 2001 From: Scratchcat1 Date: Thu, 29 Mar 2018 15:27:16 +0100 Subject: [PATCH] Add files via upload --- AATC_AStar.py | 137 +++++++------ AATC_Client.py | 10 +- AATC_Client_Text_UI.py | 139 +++++--------- AATC_Config.py | 7 +- AATC_Coordinate.py | 16 +- AATC_Create_Graph.py | 8 +- AATC_Crypto.py | 58 +++--- AATC_CryptoBeta.py | 22 +-- AATC_DB.py | 213 +++++++++------------ AATC_Drone.py | 20 +- AATC_Drone_Logic.py | 26 +-- AATC_GPIO.py | 34 ++-- AATC_Monitor.py | 4 +- AATC_Monitor_Viewer.py | 227 ++++++++++------------ AATC_NoFlyZoneGrapher.py | 24 +-- AATC_Server_002.py | 404 +++++++++++++++++---------------------- AATC_Server_Starter.py | 125 +----------- AATC_Weather.py | 14 +- 18 files changed, 637 insertions(+), 851 deletions(-) diff --git a/AATC_AStar.py b/AATC_AStar.py index 5a5b1e0..6b40947 100644 --- a/AATC_AStar.py +++ b/AATC_AStar.py @@ -43,23 +43,23 @@ def __init__(self,BlockSize = 500,FolderName = "GraphFolder",GraphFileName = "Gr self._ABSlots = ABSlots - def Size(self,xSize,ySize,zSize): + def Size(self,xSize,ySize,zSize): #Sets size of graph grid self._xSize = xSize self._ySize = ySize self._zSize = zSize - def Get_Size(self): + def Get_Size(self): #Gets size of graph grid return self._xSize, self._ySize, self._zSize - def Get_Count(self): + def Get_Count(self): #Get the count values for each dimension return self._xCount, self._yCount, self._zCount - def add_node(self,node): + def add_node(self,node): #Add a node object to the graph self._Nodes[node.Get_NodeID()] = node - def Add_Edges(self,xRange,yRange,zRange): + def Add_Edges(self,xRange,yRange,zRange): #Add the edges to the graph print("Adding edges...") self._xCount = int(xRange/self._xSize) self._yCount = int(yRange/self._ySize) @@ -71,10 +71,10 @@ def Add_Edges(self,xRange,yRange,zRange): print("zCount:",self._zCount) - for node in self._Nodes.values(): + for node in self._Nodes.values(): #Calculate edges for each node friends = self.CalculateNeighbours(node.Get_NodeID(),self._xCount,self._yCount,self._zCount) for friend in friends: - node.add_friend(friend) + node.add_friend(friend) #Add edges to nodes. def CalculateNeighbours(self,NodeID,xCount,yCount,zCount): zlow,zhigh,ylow,yhigh,xlow,xhigh = False,False,False,False,False,False @@ -94,6 +94,8 @@ def CalculateNeighbours(self,NodeID,xCount,yCount,zCount): if (NodeID-1) // (zCount*yCount) != (xCount-1): xhigh = True + + #Code below finds the nodeIDs of each of the nodes neighbours by detecting if the node is not on an edge of the area for the graph. if zlow: friends.append(NodeID-1) if ylow: @@ -158,19 +160,19 @@ def CalculateNeighbours(self,NodeID,xCount,yCount,zCount): ################################### - def MapHash(self,value,div): + def MapHash(self,value,div): #Get the hash of the value passed return int(value//div) - def Node_Cache_Hash(self,Key): + def Node_Cache_Hash(self,Key): #Find the hash for the node cache value. return int(int(hashlib.md5(str(Key).encode('utf8')).hexdigest()[:8],16)%self._Node_Cache_BlockSize) #Generates integer hash of key then int div by BlockSize ################################## - def Build_Node_Cache(self): + def Build_Node_Cache(self): #Precalculates the node_cache values self._Node_Cache = {} for node in self._Nodes.values(): - x = node.Get_Coords().Get_X() + 0.25*self._xSize #Prevents floating point rounding errors - y = node.Get_Coords().Get_Y() + 0.25*self._ySize + x = node.Get_Coords().Get_X() + 0.25*self._xSize #Prevents floating point rounding errors + y = node.Get_Coords().Get_Y() + 0.25*self._ySize #Otherwise integer division may map a node on top of another (x+1)//y = x//y in edge cases if x z = node.Get_Coords().Get_Z() + 0.25*self._zSize mx,my,mz = self.MapHash(x,self._xSize),self.MapHash(y,self._ySize),self.MapHash(z,self._zSize) @@ -186,15 +188,15 @@ def Save_Node_Cache(self): Sets[r][Key] = self._Node_Cache[Key] #Adds the item to the set print("Saving Node Cache. Sets:",len(Sets)) - for Letter in self.GetFolderNames(): + for Letter in self.GetFolderNames(): #Writes to all copies as this subroutine will only be run on graph generation for Set in Sets: - filename = os.path.join(self._cwd,self._FolderName,Letter,self._BlockFileName+"NC"+str(Set)+self._BlockFileSuffix) + filename = os.path.join(self._cwd,self._FolderName,Letter,self._BlockFileName+"NC"+str(Set)+self._BlockFileSuffix) #Generates the path to the file data = Sets[Set] with open(filename,"wb") as file: - pickle.dump(data,file,protocol = pickle.HIGHEST_PROTOCOL) + pickle.dump(data,file,protocol = pickle.HIGHEST_PROTOCOL) #Dumps the set using pickle. - def Get_Node_Cache(self,x,y,z): + def Get_Node_Cache(self,x,y,z): #Load nodeID for a given coordinate using the key Key = (x,y,z) if Key not in self._Node_Cache: NCBlockID = self.Node_Cache_Hash(Key) @@ -214,7 +216,7 @@ def Get_Node_Cache(self,x,y,z): raise ValueError("Node_Cache Key requested is not in the NCBlockID checked. Check BlockSize or regenerate blockfiles.") - def Direct_NodeID(self,x,y,z): + def Direct_NodeID(self,x,y,z): #Access NodeID without first mapping the coordinates return self.Get_Node_Cache(x,y,z) def All_NodeIDs(self,StartValue = 1, MaxValue = None): @@ -227,12 +229,12 @@ def All_NodeIDs(self,StartValue = 1, MaxValue = None): return NodeIDList - def Find_NodeID(self,x,y,z): + def Find_NodeID(self,x,y,z): #Find the NodeID a coordinate is for mx,my,mz = self.MapHash(x,self._xSize),self.MapHash(y,self._ySize),self.MapHash(z,self._zSize) NodeID = self.Get_Node_Cache(mx,my,mz) return NodeID - def Obj_Find_NodeID(self,Obj): + def Obj_Find_NodeID(self,Obj): x,y,z = Obj.Get_Coords().Get_X(),Obj.Get_Coords().Get_Y(),Obj.Get_Coords().Get_Z() NodeID = self.Find_NodeID(x,y,z) return NodeID @@ -240,7 +242,7 @@ def Obj_Find_NodeID(self,Obj): ############################# - def SaveGraph(self,AutoNodeSave = True,AutoNodeCacheSave = True): + def SaveGraph(self,AutoNodeSave = True,AutoNodeCacheSave = True): #Save the graph to the disk. print("Saving graph...") for Letter in self.GetFolderNames(): os.makedirs(os.path.join(os.getcwd(),self._FolderName,Letter),exist_ok = True) @@ -260,12 +262,12 @@ def SaveGraph(self,AutoNodeSave = True,AutoNodeCacheSave = True): except Exception as e: print("Error occured while saving graph file ",e) - def ImportGraph(self): + def ImportGraph(self): #Loads graph properties print("Importing graph") ABSlot = self._ABSlot try: - filename = os.path.join(os.getcwd(),self._FolderName,"A",self._GraphFileName+self._GraphFileSuffix) #MUST ALWAYS HAVE ATLEAST THE FOLDER "A" in order to load the configuration - with open(filename,"rb") as file: + filename = os.path.join(os.getcwd(),self._FolderName,"A",self._GraphFileName+self._GraphFileSuffix) #MUST ALWAYS HAVE ATLEAST THE FOLDER "A" in order to load the configuration + with open(filename,"rb") as file: #The graph at this point does not know how many copies there are ImportFile = pickle.load(file) self.__dict__.update(ImportFile.__dict__) @@ -279,7 +281,8 @@ def ImportGraph(self): ################ def Hash(self,Value): return int(Value//self._BlockSize) - def GetNode(self,NodeID): + + def GetNode(self,NodeID): #Takes a nodeID, finds the hash, finds the file with the node ( a block) and loads this block. if NodeID not in self._Nodes: BlockID = self.Hash(NodeID) try: @@ -297,9 +300,9 @@ def GetNode(self,NodeID): return self._Nodes[NodeID] else: #Raises error if cannot get node - raise ValueError("NodeID requested is not in the BlockID checked. Check BlockSize or regenerate blockfiles. NodeID: "+str(NodeID)) + raise ValueError("NodeID requested is not in the BlockID checked. Check BlockSize or regenerate blockfiles. NodeID: "+str(NodeID)) #If an error occured ( node was not loaded). This is normally the case if the graph has not been generated correctly. - def SaveNodes(self,FolderNames = None): + def SaveNodes(self,FolderNames = None): #Saves the current graph into the folders defined by the set FolderNames, or all if FolderNames = None. if FolderNames == None: FolderNames = self.GetFolderNames() Sets = {} @@ -317,38 +320,38 @@ def SaveNodes(self,FolderNames = None): filename = os.path.join(self._cwd,self._FolderName,Letter,self._BlockFileName+"N"+str(Set)+self._BlockFileSuffix) data = Sets[Set] with open(filename,"wb") as file: - pickle.dump(data,file,protocol = pickle.HIGHEST_PROTOCOL) + pickle.dump(data,file,protocol = pickle.HIGHEST_PROTOCOL) #Writes the block to the file using pickle. def EvictNode(self,NodeID): #Removes a node from the Nodes dict if NodeID in self._Nodes: self._Nodes.pop(NodeID) - return True + return True #True if node was in memory, false if not else: return False - def FlushGraph(self): + def FlushGraph(self): #Empty the graph to reduce memory usage self._Nodes = {} self._Node_Cache = {} - def Get_Nodes(self): + def Get_Nodes(self): #Get all nodes currently in memory return self._Nodes ####################################### - def GetFolderNames(self): + def GetFolderNames(self): #Generates the set of foldernames the current graph will use names = [] for x in range(self._ABSlots): names.append(chr(65+x)) return names - def CurrentFolderName(self): - char = chr( int(65+ ((time.time()+self._ABInterval*self._ABSlot)//self._ABInterval)%self._ABSlots)) + def CurrentFolderName(self): #Finds the current folder the graph should save in. + char = chr( int(65+ ((time.time()+self._ABInterval*self._ABSlot)//self._ABInterval)%self._ABSlots)) return char -class Node: +class Node: #Stores the attributes of a node in object form def __init__(self,NodeID,Cost,Coords): self._NodeID = NodeID self._Friends = [] @@ -370,13 +373,20 @@ def Get_Cost(self): def Set_Cost(self,cost): self._Cost = cost -def EstimateDistance(Node,Target,xSize,ySize,zSize): +def EstimateDistance(Node,Target,xSize,ySize,zSize): #Estimates the distance to the target node A* HEURISTIC Node_Coords = Node.Get_Coords() - Target_Coords = Target.Get_Coords() + Target_Coords = Target.Get_Coords() #USES THE TOTAL DISTANCE IN EACH DIMENSION TO REDUCE SEARCH TIME COMPARED TO EUCLIDIAN DISTANCE return (abs(Node_Coords.Get_X()-Target_Coords.Get_X())/xSize+abs(Node_Coords.Get_Y()-Target_Coords.Get_Y())/ySize+abs(Node_Coords.Get_Z()-Target_Coords.Get_Z())/zSize) - #return math.sqrt((Node_Coords.Get_X()-Target_Coords.Get_X()/xSize)**2+(Node_Coords.Get_Y()-Target_Coords.Get_Y()/ySize)**2 + (Node_Coords.Get_Z()-Target_Coords.Get_Z()/zSize)**2)*0.9 +## return math.sqrt((Node_Coords.Get_X()-Target_Coords.Get_X()/xSize)**2+(Node_Coords.Get_Y()-Target_Coords.Get_Y()/ySize)**2 + (Node_Coords.Get_Z()-Target_Coords.Get_Z()/zSize)**2)*0.9 -def AStarPQ(graph,start,target): # Set all g to node_count + 1 +def AStarPQ(graph,start,target): #The priority queue version of the A* algorithm + """ + Priority queue version of the A* algorithm. + This replaces the task of finding the minimum of f using min with a priority queue + This improves the performance as the size of the search increases + However as this is less memory efficient (must keep a priority queue of all open nodes ) it is not the default option used but is offered as an alternative. + + """ StartTime = time.time() xSize,ySize,zSize = graph.Get_Size() @@ -389,23 +399,23 @@ def AStarPQ(graph,start,target): # Set all g to node_count + 1 g[start] = 0 f[start] = EstimateDistance(graph.GetNode(start),graph.GetNode(target),xSize,ySize,zSize) - fp.put((EstimateDistance(graph.GetNode(start),graph.GetNode(target),xSize,ySize,zSize),start)) + fp.put((EstimateDistance(graph.GetNode(start),graph.GetNode(target),xSize,ySize,zSize),start)) #Sets the start node to the current best node Found = False while len(OpenSet) != 0: - current = fp.pop()[1] + current = fp.pop()[1] #Find best node - if current == target: + if current == target: #If the node has been found, break Found = True break - OpenSet.pop(current) + OpenSet.pop(current) ClosedSet[current] = 1 - for NodeID in graph.GetNode(current).Get_Friends(): - if NodeID in ClosedSet: + for NodeID in graph.GetNode(current).Get_Friends(): #For each neighbour on the graph + if NodeID in ClosedSet: #This node has no better route, ignore continue - if NodeID not in OpenSet: + if NodeID not in OpenSet: #Add to openset if not yet in it. This only loads the nodes into the g,f,fp sets now in order to reduce memeory usage OpenSet[NodeID] = 1 g[NodeID] = math.inf f[NodeID] = math.inf @@ -413,14 +423,14 @@ def AStarPQ(graph,start,target): # Set all g to node_count + 1 NewNode = graph.GetNode(NodeID) tScore = g[current] + NewNode.Get_Cost() - if tScore >= g[NodeID]: + if tScore >= g[NodeID]: #If does not offer a better path continue continue cameFrom[NodeID] = current g[NodeID] = tScore - fp.remove((f[NodeID],NodeID)) - fTemp = g[NodeID] + EstimateDistance(NewNode,graph.GetNode(target),xSize,ySize,zSize) + fp.remove((f[NodeID],NodeID)) #Remove from priority queue + fTemp = g[NodeID] + EstimateDistance(NewNode,graph.GetNode(target),xSize,ySize,zSize) f[NodeID] = fTemp - fp.put((fTemp,NodeID)) + fp.put((fTemp,NodeID)) #Readd to priority queue f.pop(current) #These values will not be refered to again since the current NodeID has been moved to the closed set . This therefore reduces memory usage very slightly g.pop(current) @@ -450,28 +460,28 @@ def AStar2(graph,start,target): # Set all g to node_count + 1 f[start] = EstimateDistance(graph.GetNode(start),graph.GetNode(target),xSize,ySize,zSize) Found = False while len(OpenSet) != 0: - current = min(f,key = lambda n:f[n]) #Faster (143 vs 114 ms) and doesnt require OpenList to be made + current = min(f,key = lambda n:f[n]) #Faster (143 vs 114 ms) and doesnt require OpenList to be made.Find best node if current == target: Found = True break - OpenSet.pop(current) + OpenSet.pop(current) ClosedSet[current] = 1 - for NodeID in graph.GetNode(current).Get_Friends(): + for NodeID in graph.GetNode(current).Get_Friends(): #For each neighboyur if NodeID in ClosedSet: continue - if NodeID not in OpenSet: + if NodeID not in OpenSet: #Add to openset if not yet in it. This only loads the nodes into the g,f sets now in order to reduce memeory usage OpenSet[NodeID] = 1 g[NodeID] = math.inf f[NodeID] = math.inf NewNode = graph.GetNode(NodeID) tScore = g[current] + NewNode.Get_Cost() - if tScore >= g[NodeID]: + if tScore >= g[NodeID]: #If not a better path contine, otherwise update. continue cameFrom[NodeID] = current g[NodeID] = tScore @@ -491,7 +501,7 @@ def AStar2(graph,start,target): # Set all g to node_count + 1 return None -def FindPath(cameFrom,current): +def FindPath(cameFrom,current): #Retraces the path from the end node to the start node. path = [current] while current in cameFrom: current = cameFrom[current] @@ -499,7 +509,18 @@ def FindPath(cameFrom,current): return path[::-1] -def Benchmark(FLUSH = 100,MAXNODE = 80000): + + + + + + +############################################## + +# Below are random benchmarks used for improvement of the search algorithms + + +def Benchmark(FLUSH = 100,MAXNODE = 80000): #Benchmark with test of removing nodes from memory every FLUSH steps graph = DynoGraph() graph.ImportGraph() @@ -519,7 +540,7 @@ def Benchmark(FLUSH = 100,MAXNODE = 80000): -def MultiBenchmark(num_proc = 4,FLUSH=100,MAXNODE=80000): +def MultiBenchmark(num_proc = 4,FLUSH=100,MAXNODE=80000): #Benchmark but with multiple processes to simulate server import multiprocessing procs = [] for x in range(num_proc): @@ -528,7 +549,7 @@ def MultiBenchmark(num_proc = 4,FLUSH=100,MAXNODE=80000): procs.append(p) -def CAStarBenchmark(Random = False): +def CAStarBenchmark(Random = False): #Test of the CAStar module. Not part of NEA graph = DynoGraph() graph.ImportGraph() if Random: diff --git a/AATC_Client.py b/AATC_Client.py index 586cb4b..fbedef6 100644 --- a/AATC_Client.py +++ b/AATC_Client.py @@ -35,7 +35,7 @@ #Create Socket #Create Connection -def Connect(remote_ip,PORT): +def Connect(remote_ip,PORT): #Connects to the server s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.connect((remote_ip, PORT)) @@ -51,7 +51,7 @@ def Connect(remote_ip,PORT): ## return Sucess,Message -class UserInterface: +class UserInterface: #Used to interface with the server def __init__(self,Connection): self._con = Connection self._Crypto = AATC_Crypto.Crypter(self._con) @@ -237,11 +237,11 @@ def Exit(self): ############################################## ############################################## - def Send(self,Code,data): + def Send(self,Code,data): #encrypt and send data to server Info = self._Crypto.Encrypt(codecs.encode(str((Code,data)))) self._con.sendall(Info) - def Recv(self): #Returns tuple of Sucess,Message,Data of which data may just be useless for that function + def Recv(self): #receive and decrypt data from server try: data = self._Crypto.Decrypt(recvall.recvall(self._con)) data = ast.literal_eval(codecs.decode(data)) @@ -253,7 +253,7 @@ def Recv(self): #Returns tuple of Sucess,Message,Data of which data may just return (False,"Conversion/Transfer Error"+str(e),[]) -def CreateUserInterface(IP = "192.168.0.19",Port = 8000): +def CreateUserInterface(IP = "192.168.0.19",Port = 8000): #Create user interface to server soc = Connect(IP,Port) U = UserInterface(soc) return U diff --git a/AATC_Client_Text_UI.py b/AATC_Client_Text_UI.py index 07706d2..9fa3eff 100644 --- a/AATC_Client_Text_UI.py +++ b/AATC_Client_Text_UI.py @@ -42,15 +42,49 @@ -1:"Exit" - } + } #Maps command numbers onto the strings of permissions -class UserTextUI: +class UserTextUI: #Interface with server via text interface def __init__(self,UserInterface,MenuOptions): self._UserInterface = UserInterface self._MenuOptions = MenuOptions - - def Main_Loop(self): + self._Commands = { + "Login" : self.Login, + "GetNoFlyZones" : self.GetNoFlyZones, + "AddNoFlyZone" : self.AddNoFlyZone, + "RemoveNoFlyZone" : self.RemoveNoFlyZone, + "ModifyNoFlyZoneLevel" : self.ModifyNoFlyZoneLevel, + "AddDrone" : self.AddDrone, + "RemoveDrone" : self.RemoveDrone, + "GetDroneID" : self.GetDroneID, + "GetDroneCredentials" : self.GetDroneCredentials, + "SetDroneCredentials" : self.SetDroneCredentials, + "CheckDroneOwnership" : self.CheckDroneOwnership, + "GetDroneInfo" : self.GetDroneInfo, + "GetDronesUser" : self.GetDronesUser, + "GetDronesAll" : self.GetDronesAll, + "GetUserID" : self.GetUserID, + "GetUsername" : self.GetUsername, + "AddUser" : self.AddUser, + "SetFlightVisibility" : self.SetFlightVisibility, + "SetAccountType" : self.SetAccountType, + "UserChangePassword" : self.UserChangePassword, + "GetFlightsUser" : self.GetFlightsUser, + "GetFlightsAll" : self.GetFlightsAll, + "AddFlight" : self.AddFlight, + "RemoveFlight" : self.RemoveFlight, + "GetFlightWaypointsUser" : self.GetFlightWaypointsUser, + "GetFlightWaypointsAll" : self.GetFlightWaypointsAll, + "GetMonitorID" : self.GetMonitorID, + "GetMonitorName" : self.GetMonitorName, + "AddMonitorPermission" : self.AddMonitorPermission, + "RemoveMonitorPermission" : self.RemoveMonitorPermission, + "ModifyMonitorPermissionDate" : self.ModifyMonitorPermissionDate, + "GetMonitorPermissionUser" : self.GetMonitorPermissionUser, + "Exit" : self.Call_Exit} + + def Main_Loop(self): #Loop until exit self._Exit = False while not self._Exit: try: @@ -61,13 +95,13 @@ def Main_Loop(self): except Exception as e: print("Error occured in Client Text UI",e) - def PrintMainMenu(self): + def PrintMainMenu(self): #Display the menu neatly print("\n"*2) print("AATC Client Main Menu") for x in self._MenuOptions.items(): print("{0:>3} : {1}".format(str(x[0]),x[1])) - def GetMenuChoice(self): + def GetMenuChoice(self): MenuChoice = -99 while MenuChoice not in self._MenuOptions: #will exit once valid menu option is chosen try: @@ -76,89 +110,16 @@ def GetMenuChoice(self): print("Integers only") return MenuChoice - def EvaluateChoice(self,MenuChoice): - Command = self._MenuOptions[MenuChoice] #Gets full, easier to work with string - - if Command == "Login": - self.Login() - - elif Command == "GetNoFlyZones": - self.GetNoFlyZones() - elif Command == "AddNoFlyZone": - self.AddNoFlyZone() - elif Command == "RemoveNoFlyZone": - self.RemoveNoFlyZone() - elif Command == "ModifyNoFlyZoneLevel": - self.ModifyNoFlyZoneLevel() - - elif Command == "AddDrone": - self.AddDrone() - elif Command == "RemoveDrone": - self.RemoveDrone() - elif Command == "GetDroneID": - self.GetDroneID() - elif Command == "GetDroneCredentials": - self.GetDroneCredentials() - elif Command == "SetDroneCredentials": - self.SetDroneCredentials() - elif Command == "CheckDroneOwnership": - self.CheckDroneOwnership() - elif Command == "GetDroneInfo": - self.GetDroneInfo() - elif Command == "GetDronesUser": - self.GetDronesUser() - elif Command == "GetDronesAll": - self.GetDronesAll() - - elif Command == "GetUserID": - self.GetUserID() - elif Command == "GetUsername": - self.GetUsername() - elif Command == "AddUser": - self.AddUser() - elif Command == "SetFlightVisibility": - self.SetFlightVisibility() - elif Command == "SetAccountType": - self.SetAccountType() - elif Command == "UserChangePassword": - self.UserChangePassword() - - elif Command == "GetFlightsUser": - self.GetFlightsUser() - elif Command == "GetFlightsAll": - self.GetFlightsAll() - elif Command == "AddFlight": - self.AddFlight() - elif Command == "RemoveFlight": - self.RemoveFlight() - - elif Command == "GetFlightWaypointsUser": - self.GetFlightWaypointsUser() - elif Command == "GetFlightWaypointsAll": - self.GetFlightWaypointsAll() + def EvaluateChoice(self,MenuChoice): #Execute the correct command. + Command = self._Commands.get(self._MenuOptions[MenuChoice],None) #Gets full, easier to work with string - elif Command == "GetMonitorID": - self.GetMonitorID() - elif Command == "GetMonitorName": - self.GetMonitorName() - - elif Command == "AddMonitorPermission": - self.AddMonitorPermission() - elif Command == "RemoveMonitorPermission": - self.RemoveMonitorPermission() - elif Command == "ModifyMonitorPermissionDate": - self.ModifyMonitorPermissionDate() - elif Command == "GetMonitorPermissionUser": - self.GetMonitorPermissionUser() - - - - elif Command == "Exit": - self.Call_Exit() + if Command != None: + Command() else: print("Please correctly register method to EvaluateChoice method") + - def DisplayResults(self,Sucess,Message,Data = None): + def DisplayResults(self,Sucess,Message,Data = None): #Neatly display the results print("Sucess >>",Sucess) print("Message >>",Message) if Data not in [None,[]]: @@ -308,7 +269,7 @@ def AddFlight(self): HighPoints = [] point = "" print("Enter Coordinates one by one, enter 'Done' once complete") - while point != "Done": + while point != "Done": #Obtain all the main points on the flight till the user enters done point = input("Enter Coordinate in form (x,y,z) >>") if point != "Done": HighPoints.append(point) @@ -369,7 +330,7 @@ def GetMonitorPermissionUser(self): ################################################# - def Call_Exit(self): + def Call_Exit(self): #Exit communication from the server and exit print("Exiting..") try: Sucess,Message = self._UserInterface.Exit() @@ -388,10 +349,10 @@ def Call_Exit(self): while not Exit: try: print("Connecting to server...") - U = AATC_Client.CreateUserInterface(IP = "127.0.0.1") + U = AATC_Client.CreateUserInterface(IP = "127.0.0.1") #Connect to server TextUI = UserTextUI(U,MenuOptions) - TextUI.Main_Loop() + TextUI.Main_Loop() #Run main loop Choice = input("Exit? (Y/N) >>").upper() if Choice == "Y": diff --git a/AATC_Config.py b/AATC_Config.py index 6404266..1b30939 100644 --- a/AATC_Config.py +++ b/AATC_Config.py @@ -7,7 +7,7 @@ SERVER_PRIVATE_KEY = b"0\x82\x02]\x02\x01\x00\x02\x81\x81\x00\xb4N\xe2,D\x10\t.\x07UkD\x0b\x05u\x95n\r4\xcd\xa0A\x07\x8e\x93\xc3$ZP\x1e\x82\xce\x908a\x88A[\x84L\xcbk\xf9\xf1\xd5\xb6\x18\xcf\x11\x0eo\x9e\x9b\x83\xff\x87z\xd0\xa5\xf5gDR\xd7\xb9\x13\xc7\xb3\xedAV\x96\x15\x0e\xe9i\xf7\n\x93\n\xf2-\xd4\xe9\x90\x8f\xaa\xa4y\x9b\x91\x1f\x0c\x93\xc6\xf3\xc0\x1e\xfd\xccl\xa32\xde`\xa9\x9bH$Q\x94.\xf0s\x12\x87\x8bS\x1cs\r\xa1O\xb8\xca\xa46y\x02\x03\x01\x00\x01\x02\x81\x80N,}F\xef\xe3{\xf7f\xda\x93\xfd\x16B1\xae\xad\xde/\xec\x8e\xf4b\xd0`$\x15\x18\xca\xef\xcf\x8a\xbb`\x96x\xba\xcdp\xd8\xd2\xc1g\xc6\x7f\xff\xc9U\xcdqR\xd7\x93YqjM\xc74\x8c\xe7N\xea\x13>14\xfb\xce \xab'\xcdg\x9c\\C(d\x8b\xc9\x7f\xc2\xc9\xd6\x18t\x1cS\xa1\xa6\x82\x94\x80\xce3\xba\x81\x0fU\x043\xf2\xa6\x96}\x98,q\xce\xaeH\x97\xe1\xdd3e~\xa5\xd7\x06Q\xf8\xc0\x92\xb1q\x19\x02A\x00\xc1\xbd\xaa\xa20\xe1W~#\x10r\xeeG\x19\x83@\x94\xe1\x8c\xe1D\xe84f\x00\xee\xc9k/\xec\xa3\x01N\x1e\xd7\x00\xc50\xc3l\xcd\xb5\xbe\xa9\x91&\x05W[(<\t\x9bALL\xf3%\x03<\xd3\x17&\x0f\x02A\x00\xee@'\xcc\xb6C\x1e5f\xda\xb6\xf5\x11\xcci\xc2Xo\xd4\xb1\xd0!\x1b\xb5F]\x83\x10\xa6\xf1d<,\x82\xd8\xce\xaf~1\xb9\x07:\xb9\xc5\x10r\xb7\x1f,\x1c\x11k\xf2\xc1\t\xe2\xb5\xbb\x11{\xf47\xa2\xf7\x02A\x00\x95\xe2\xb4v\x126\xe3\xc7t=/\x8ddx:p\xe6=\xb1\x0e>\x8f\x1e\xaa\xa3\xa2\x195\xd1\xb7\xd3L\x192\x06\xc8S\xc6,\n\xab\x03&dm$\xeb\x10\xf07*k\x8c/rf=\xdd\xae\xa9\x89\xed\xe8\xc5\x02A\x00\xc73\x16\xd3\xbfT0\xce\xbfw\x80\xdf8\x89k\xa7\x95\x9f\xb2\xfb\x14\xa4\x89\xf1/\xab\x01\xe9\x8b[j\xce0\xfd\x19\xde\xfd\xb6\nF\xc4\xb7\xa2\x19\x15\x1e\x84\xe3hR\xf3r\t\x1e\xc3p-8\x02o\\\n\xa4\x93\x02@=\x1cp*k\xbb\x8fL\xc7\xc0\xf4/xY6NJ\x1e/\x04\xb6y\x8e\x13`pL\x8e\xfbl\xads2\x00\x1a\xfb\\n\xda\xe2 \xe3\x9a\x18', 'NotBefore': 1, 'Name': '0', 'PublicKey': b'0\x81\x9f0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x81\x8d\x000\x81\x89\x02\x81\x81\x00\xc2\x0cmp\xbaN\x86\xb9\x05!*\x8d\xd2\xa1\x95\x16}\xa9gALok\x9d\xa7\xf6\xa9\r\x80\xae\x82\x90*N\x071}\x0f\xd5\xe7F0\xe4\x03\xaal\xb6b\xdc\xbc\xea\x087\xd3\xce\x92Pl~\x01\x8d\xc2\xb9\tY\x9c\xdf\xa9\xbe\xa7F!\xfdG_Wj\x7f\xf2P\xad\xa9\x87?\xc5\xdd.\x0c\xa0+!\xf4\xdcv\x8d\xe05S\xa3\x15TL\xa7\x10\x888h\x9a\x0f\xebU\n\x8c\xec\xc7\xa3E\x98\xe6h\xc6yM\xe1PGRm\x02\x03\x01\x00\x01', 'Issuer': 'IssuerA'}, {'NotAfter': 10000000000, 'Signature': b"\xba\xe2Q\xf4A\xe1\xdc\xad\xbb\xaf\xcdtvAD\xae\xad\xad\x86\x12\xf5e\xe4\x8d\rT\xce\xf7\xe0R\x0eF\xd58\xa6w\x8c\xd0JL\xde\xa7n\xf1j\xa7H\x96\xba\xa5\x1a\x17H\xe7\x02\x80_6x\x0b\xec]\x80\xde'\xcc\x15\xf6\tF\xab1\xb56ga\xd3\x17*\x0c8\xc9k?\x12\x00%\x1b\x14D~\x89\xf2\xcb\x0cP\xf6\x1dy\xbf\x9e\xbd\x96\n\x8a\xfc\x974z\x1a\xaaV\x0c\x06\x86\xbeNv\xeeYHl\x00\xf6\xc3\xffy\x98", 'NotBefore': 1, 'Name': '1', 'PublicKey': b'0\x81\x9f0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x81\x8d\x000\x81\x89\x02\x81\x81\x00\xb2}\t\xe1\xd3\x95J\x0ba\xc6~K\xbeT\xdcu\xba\x8d\x94\xd1s\xf0\x8bn\xfe*\x9e(B\x9f<3\xdd\xd1\nvz]P\x95\xb9\xf6Oa\xc0\x9e\xfcn\xa8\xa8I\x81Y\xc6k\xbe\xe1\x03L\xae\xec\xe7\xb0e\x90\x9b\xef\xc9-\x92\xce\xce\x06:t\xbc \xa4\x9eEgw\x0c>\x84\xf2i]\x0e\xb2\xb8\x7f\x10\xc8\xed\xcf4\xaa\x0fvD\xd4\x026\xf3\x86Dd\xa5\x11\x897\x9c\x9e/\xe6J\xb1.\x10\xdb\xa9o5R{\xb1\xcb\x02\x03\x01\x00\x01', 'Issuer': '0'}, {'NotAfter': 10000000000, 'Signature': b"+\x8e\xf0\xad\xfe&\x9avY\xaaP\xcdE\x86a\x84\xf8\xd2\x97\xba\xa5}N;\xa0\x14-\x8e\xad\xe2Y\xcf@\xab\x17\xfdO\xca\xe8H:\xfc}I\xfd\xc4\xe1\x95\xd4\x07\xe2\x8c\xb0Z\x10\xa2\xa2\x1b\xb4?\x0c8\xb2\xbd\x92X)\xa2\xdf\x9b\xa8F\x1b\x88|\\\xe6\xa6'7$\xa9\x8b[\xe3\xc3\xbc\xe2j2\x82\x8c:\xf0;\xd8l\xcd*\xd1\x08C\xfe\x1b\xa5\x00\xebW\x9f\xdf\xc4a\xd8\xe3\x01v\xb6a\x14\xd3\n*\xf4)W}7\x0c", 'NotBefore': 1, 'Name': 'localhostsssss', 'PublicKey': b'0\x81\x9f0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x81\x8d\x000\x81\x89\x02\x81\x81\x00\xb4N\xe2,D\x10\t.\x07UkD\x0b\x05u\x95n\r4\xcd\xa0A\x07\x8e\x93\xc3$ZP\x1e\x82\xce\x908a\x88A[\x84L\xcbk\xf9\xf1\xd5\xb6\x18\xcf\x11\x0eo\x9e\x9b\x83\xff\x87z\xd0\xa5\xf5gDR\xd7\xb9\x13\xc7\xb3\xedAV\x96\x15\x0e\xe9i\xf7\n\x93\n\xf2-\xd4\xe9\x90\x8f\xaa\xa4y\x9b\x91\x1f\x0c\x93\xc6\xf3\xc0\x1e\xfd\xccl\xa32\xde`\xa9\x9bH$Q\x94.\xf0s\x12\x87\x8bS\x1cs\r\xa1O\xb8\xca\xa46y\x02\x03\x01\x00\x01', 'Issuer': '1'}] -#Minor setting to use given AES and IV. Will not removed even if it is a security issue, preshared keys is the simple counter against shor's algorithm (cannot have a fibre optic cable to a drone for quantum cryptography). +#Minor setting to use given AES and IV. Will not removed even if it is a security issue, preshared keys could be the simple counter against public key cryptograph being broken though vunrable to threft. SET_ENCRYPTION_KEYS_ENABLE = False SET_AES_KEY = b"00000000000000000000000000000000" SET_IV_KEY = b"00000000000000000000000000000000" @@ -46,3 +46,8 @@ ENABLE_FINE_SPEED_ESTIMATION = False #Allows fine grained estimation of drone speed due to wind. Noticable slowdown when creating flight due to many requests which may be limited by OWM depending on contract OWM_SLEEP_TIME = 1/60*60*4 # How long the add flight program should sleep between requests to reduce the chance of overusing requests. 1/REQUESTS_PER_MINUTE*60*NUMBER_OF_CONCURRENT_SEARCHES should ensure that the program never hits the limit of OWM. OWM_API_KEY = '5b943c4857a45d75ef7ee9b301666fa8' #Contains the API key for the OWM + + + + +BOT_TOKEN = "472230564:AAEHTSJ446LE_BO_hQ8B4PeVmUTrB8gRsEA" #API token for the telegram bot diff --git a/AATC_Coordinate.py b/AATC_Coordinate.py index 182bfbf..1e23cb5 100644 --- a/AATC_Coordinate.py +++ b/AATC_Coordinate.py @@ -1,6 +1,6 @@ import math -class Coordinate: +class Coordinate: #Stores the coordinate data in a simpler to use object format. def __init__(self,x,y,z=0,xSize=0,ySize=0,zSize=0): self._x = x self._y = y @@ -38,19 +38,19 @@ def Set_ZSize(self,z): self._zSize = z - def Print(self): + def Print(self): #neatly display coordinate information print("Coordinate:") print("X: {:<8} xSize:{:<8}".format(round(self._x,7),self._xSize)) print("Y: {:<8} ySize:{:<8}".format(round(self._y,7),self._ySize)) print("Z: {:<8} zSize:{:<8}".format(round(self._z,7),self._zSize)) - def __str__(self): + def __str__(self): #Dump to string. Size not included as size is not ever needed as a string format. return str((round(self._x,8),round(self._y,8),round(self._z,8))) def getTuple(self): return (self._x,self._y,self._z) - def copy(self): + def copy(self): #duplicates coordinate object. return Coordinate(self._x,self._y,self._z,self._xSize,self._ySize,self._zSize) @@ -63,7 +63,7 @@ def AddCoords(Coord,VectorCoord): #Simulates the drone moving Coord.Set_Z(z) return Coord -def CalculateVector(Coords,TargetCoords,Speed): +def CalculateVector(Coords,TargetCoords,Speed): #Calculates speed vector in coordinate format dx = TargetCoords.Get_X()- Coords.Get_X() dy = TargetCoords.Get_Y()- Coords.Get_Y() dz = TargetCoords.Get_Z()- Coords.Get_Z() @@ -89,7 +89,7 @@ def toDegree(x): return 180*x/math.pi -def AtWaypoint(Coords,WaypointCoords,xSize,ySize,zSize): +def AtWaypoint(Coords,WaypointCoords,xSize,ySize,zSize): #Detects if the coordiante is within certain boundaries of a waypoint x,y,z = False,False,False if abs(Coords.Get_X()-WaypointCoords.Get_X()) <= xSize: x = True @@ -100,7 +100,7 @@ def AtWaypoint(Coords,WaypointCoords,xSize,ySize,zSize): return all([x,y,z]) -def DeltaCoordToMetres(aCoord,bCoord): +def DeltaCoordToMetres(aCoord,bCoord): #Converts distance between coordinates to metres #Formula for dx and dy from : https://stackoverflow.com/questions/3024404/transform-longitude-latitude-into-meters dx = abs(aCoord.Get_X() - bCoord.Get_X()) dy = abs(aCoord.Get_Y() - bCoord.Get_Y()) # in degrees @@ -117,7 +117,7 @@ def DeltaCoordToMetres(aCoord,bCoord): return Distance -def GetBearing(Coord1,Coord2): +def GetBearing(Coord1,Coord2): #Finds bearing between two coordinates dy = Coord2.Get_Y()-Coord1.Get_Y() dx = math.cos(math.pi/180 * Coord1.Get_Y())*(Coord2.Get_X()-Coord1.Get_X()) angle = (360+90-(math.atan2(dy,dx)*180/math.pi))%360 diff --git a/AATC_Create_Graph.py b/AATC_Create_Graph.py index 13417a3..caf5fad 100644 --- a/AATC_Create_Graph.py +++ b/AATC_Create_Graph.py @@ -1,7 +1,7 @@ #Create graph module import decimal,AATC_AStar,AATC_Coordinate -def drange(start,end,step): +def drange(start,end,step): #Generator function to produce each point for the graph step = decimal.Decimal(step) r = decimal.Decimal(start) while r < end: @@ -10,7 +10,7 @@ def drange(start,end,step): -def CreationDialogue(): +def CreationDialogue(): #Obtain parameters for graph and generate graph print("2 million nodes ~ 357MB") xStart =float(input("Enter start x Coord")) yStart = float(input("Enter start y Coord")) @@ -31,7 +31,7 @@ def CreationDialogue(): for x in drange(xStart,xEnd,xInterval): for y in drange(yStart,yEnd,yInterval): for z in drange(zStart,zEnd,zInterval): - Coord = AATC_Coordinate.Coordinate(x,y,z) + Coord = AATC_Coordinate.Coordinate(x,y,z) #Add nodes to graph for each point on the 3D grid node = AATC_AStar.Node(nodeID,1,Coord) graph.add_node(node) nodeID +=1 @@ -41,7 +41,7 @@ def CreationDialogue(): zRange = zEnd - zStart graph.Add_Edges(xRange,yRange,zRange) graph.Build_Node_Cache() - graph.SaveGraph() + graph.SaveGraph() #Create edges and cache and save graph #return graph diff --git a/AATC_Crypto.py b/AATC_Crypto.py index 926578c..7476b0a 100644 --- a/AATC_Crypto.py +++ b/AATC_Crypto.py @@ -40,17 +40,17 @@ def GenerateKey(self,key_size = AATC_Config.DEFAULT_RSA_KEYSIZE): - def ClientGenerateKey(self,RSA_KeySize,AES_KeySize= AATC_Config.DEFAULT_AES_KEYSIZE): + def ClientGenerateKey(self,RSA_KeySize,AES_KeySize= AATC_Config.DEFAULT_AES_KEYSIZE): #Start the relevant key exchange system - if AATC_Config.SET_ENCRYPTION_KEYS_ENABLE: #Allows preshared encryption keys to be used + if AATC_Config.SET_ENCRYPTION_KEYS_ENABLE: #Allows preset encryption keys to be used self.SetEncryptionKeys(AATC_Config.SET_AES_KEY, AATC_Config.SET_IV_KEY) - elif AATC_Config.ENCRYPTION_USE_PRESHARED_KEYS: + elif AATC_Config.ENCRYPTION_USE_PRESHARED_KEYS: #Uses preshared certificates self.ClientPreSharedKeys(RSA_KeySize,AES_KeySize) else: - self.ClientExchangeKeys(RSA_KeySize,AES_KeySize) + self.ClientExchangeKeys(RSA_KeySize,AES_KeySize) #Exchange AES keys using RSA self.Send(("Exit",())) @@ -60,44 +60,44 @@ def ClientGenerateKey(self,RSA_KeySize,AES_KeySize= AATC_Config.DEFAULT_AES_KEYS - def ClientPreSharedKeys(self,RSA_KeySize,AES_KeySize): + def ClientPreSharedKeys(self,RSA_KeySize,AES_KeySize): #Swap keys using certificate authentication self.Send(("GetServerCertificateChain",())) Sucess,Message,CertificateChain = self.SplitData(self.Recv()) if not Sucess: raise Exception("Server did not respond to command") if AES_KeySize not in AATC_Config.ALLOWED_AES_KEYSIZES: - raise Exception("AES key size not in ALLOWED_AES_KEYSIZES. Change keysize to an allowed value") + raise Exception("AES key size not in ALLOWED_AES_KEYSIZES. Change keysize to an allowed value") #Only accept allowed key sizes AESKey,IV = GenerateKeys(AES_KeySize) - PublicKey = AATC_CryptoBeta.VerifyCertificates(CertificateChain,AATC_Config.ROOT_CERTIFICATES,self._con) + PublicKey = AATC_CryptoBeta.VerifyCertificates(CertificateChain,AATC_Config.ROOT_CERTIFICATES,self._con) #Verify the certificate chain is valid. - if PublicKey: + if PublicKey: #If the chain is valid PKO = PKCS1_OAEP.new(RSA.import_key(PublicKey)) EncryptedAESKey = PKO.encrypt(AESKey) EncryptedIV = PKO.encrypt(IV) self.SetEncryptionKeys(AESKey,IV) - self.Send(("SetKey",(EncryptedAESKey,EncryptedIV))) + self.Send(("SetKey",(EncryptedAESKey,EncryptedIV))) #Swap keys encrypted using server public key Sucess,Message,Data = self.SplitData(self.Recv()) if not Sucess: - raise Exception("Server rejected setting AES_Keys"+Message) + raise Exception("Server rejected setting AES_Keys"+Message) else: print("Certificate Chain is not valid") if AATC_Config.AUTO_GENERATE_FALLBACK: - self.ClientExchangeKeys(RSA_KeySize,AES_KeySize) + self.ClientExchangeKeys(RSA_KeySize,AES_KeySize) #Fall back on key exchange if allowed else: - raise Exception("Certificate Chain is not valid. Exception raised") + raise Exception("Certificate Chain is not valid. Exception raised") #Raise exception if not allowed - def ClientExchangeKeys(self,RSA_KeySize,AES_KeySize): + def ClientExchangeKeys(self,RSA_KeySize,AES_KeySize): #Exchange the keys using RSA encryption RSAKey = RSA.generate(RSA_KeySize) privateKey = RSAKey.exportKey("DER") - publicKey = RSAKey.publickey().exportKey("DER") + publicKey = RSAKey.publickey().exportKey("DER") #Get the RAS keys. self.Send(("GenerateKey",(publicKey,AES_KeySize))) Sucess,Message,data = self.SplitData(self.Recv()) @@ -105,7 +105,7 @@ def ClientExchangeKeys(self,RSA_KeySize,AES_KeySize): RSAPrivateKey = RSA.import_key(privateKey) RSAPrivateObject = PKCS1_OAEP.new(RSAPrivateKey) - AESKey = RSAPrivateObject.decrypt(data[0]) + AESKey = RSAPrivateObject.decrypt(data[0]) #Decrypt the recieved keys IV = RSAPrivateObject.decrypt(data[1]) if Sucess == False: @@ -116,17 +116,19 @@ def ClientExchangeKeys(self,RSA_KeySize,AES_KeySize): ################################################################ - def ServerGenerateKey(self): + def ServerGenerateKey(self): #Server select the correct mode. if AATC_Config.SET_ENCRYPTION_KEYS_ENABLE: - self.SetEncryptionKeys(AATC_Config.SET_AES_KEY, AATC_Config.SET_IV_KEY) + self.SetEncryptionKeys(AATC_Config.SET_AES_KEY, AATC_Config.SET_IV_KEY) #Use preset keys if enabled self._Exit = False - while not self._Exit: + while not self._Exit: #Start a server type loop (responds to commands from client) data = self.Recv() Command, Arguments = data[0],data[1] + + #Respond to relevant command if Command == "GenerateKey": - Sucess,Message,Data = self.ServerGenerateKeys(Arguments) + Sucess,Message,Data = self.ServerGenerateKeys(Arguments) elif Command == "GetServerCertificateChain": Sucess,Message,Data = self.GetServerCertificateChain(Arguments) @@ -150,7 +152,7 @@ def ServerGenerateKey(self): - def ServerGenerateKeys(self,Arguments): + def ServerGenerateKeys(self,Arguments): #Generate keys and encrypt with the provided RSA key publicKey,AES_KeySize = Arguments[0],Arguments[1] if AES_KeySize not in AATC_Config.ALLOWED_AES_KEYSIZES: @@ -161,20 +163,20 @@ def ServerGenerateKeys(self,Arguments): PublicKeyObject = PKCS1_OAEP.new( RSA.import_key(publicKey)) EncryptedAESKey = PublicKeyObject.encrypt(AESKey) - EncryptedIV = PublicKeyObject.encrypt(IV) + EncryptedIV = PublicKeyObject.encrypt(IV) #Encrypt AES keys self.SetEncryptionKeys(AESKey,IV) - return True,"Instated encryption keys",[EncryptedAESKey,EncryptedIV] + return True,"Instated encryption keys",[EncryptedAESKey,EncryptedIV] #Return values to be sent - def GetServerCertificateChain(self,Arguments = None): + def GetServerCertificateChain(self,Arguments = None): #Respond to request to get certificate chain. return True,"Server Certificate Chain",AATC_Config.SERVER_CERTIFICATE_CHAIN - def ServerSetKey(self,Arguments): + def ServerSetKey(self,Arguments): #Set provided keys encrypted with public key of server PKO = PKCS1_OAEP.new(RSA.import_key(AATC_Config.SERVER_PRIVATE_KEY)) AESKey,IV = Arguments[0],Arguments[1] - AESKey,IV = PKO.decrypt(AESKey),PKO.decrypt(IV) + AESKey,IV = PKO.decrypt(AESKey),PKO.decrypt(IV) #Decrypt keys - if len(AESKey) in AATC_Config.ALLOWED_AES_KEYSIZES: + if len(AESKey) in AATC_Config.ALLOWED_AES_KEYSIZES: self.SetEncryptionKeys(AESKey,IV) return True,"Keys set",[] else: @@ -188,7 +190,7 @@ def ServerSetKey(self,Arguments): ############################################### - def SetEncryptionKeys(self,AESKey,IV): + def SetEncryptionKeys(self,AESKey,IV): #Set the encryption keys and AES encryption objects self._AESKey = AESKey self._IV = IV self._EncryptAES = AES.new(self._AESKey,AES.MODE_GCM,self._IV) #Two seperate instances to encrypt and decrypt as non ECB AES is a stream cipher @@ -222,7 +224,7 @@ def SplitData(self,data): -def GenerateKeys(AES_KeySize): +def GenerateKeys(AES_KeySize): #Creates random keys using source which is cryptographically random AESKey = os.urandom(AES_KeySize) # Here to allow regeneration of AES key while still in loop if required. IV = os.urandom(AES_KeySize) return AESKey,IV diff --git a/AATC_CryptoBeta.py b/AATC_CryptoBeta.py index 50fa568..1a08812 100644 --- a/AATC_CryptoBeta.py +++ b/AATC_CryptoBeta.py @@ -3,7 +3,7 @@ from Crypto.Hash import SHA256 from Crypto.Signature import PKCS1_PSS -def GenerateCertificate(Name,Issuer,NotBefore,NotAfter,PublicKey,IssuerPrivateKey): +def GenerateCertificate(Name,Issuer,NotBefore,NotAfter,PublicKey,IssuerPrivateKey): #Create a certificate signed with issuer private key Certificate = {} Certificate["Name"] = Name Certificate["Issuer"] = Issuer @@ -19,7 +19,7 @@ def GenerateCertificate(Name,Issuer,NotBefore,NotAfter,PublicKey,IssuerPrivateKe Certificate["Signature"] = Signature return Certificate -def VerifyCertificates(CertificateChain,RootCertificates,con = False, Reverse = False): +def VerifyCertificates(CertificateChain,RootCertificates,con = False, Reverse = False): #Verify chain of certificates CertificateChain = copy.deepcopy(CertificateChain) #To prevent loss of chain when verifing. if len(CertificateChain) > AATC_Config.MAX_CERTIFICATE_CHAIN_LENGTH: return False @@ -35,10 +35,10 @@ def VerifyCertificates(CertificateChain,RootCertificates,con = False, Reverse = break if Valid and con: - Valid = VerifyBaseAddress(CertificateChain[len(CertificateChain)-1],con) + Valid = VerifyBaseAddress(CertificateChain[len(CertificateChain)-1],con) #Validates address of connectyion if connection is provided - if Valid and ValidateCertificate(BaseCertificate,RootCert["PublicKey"]): + if Valid and ValidateCertificate(BaseCertificate,RootCert["PublicKey"]): #Validates certificate chain. CurrentPublicKey = BaseCertificate["PublicKey"] for Certificate in CertificateChain: if not ValidateCertificate(Certificate,CurrentPublicKey): @@ -48,12 +48,12 @@ def VerifyCertificates(CertificateChain,RootCertificates,con = False, Reverse = else: return False print("Valid Certificate Chain ") - return CurrentPublicKey + return CurrentPublicKey #Only returns key if valid. -def ValidateCertificate(Certificate,IssuerPublicKey): +def ValidateCertificate(Certificate,IssuerPublicKey): #Validate a single certificate , ensuring within time limits and is correctly signed if not( Certificate["NotBefore"] <= time.time() and Certificate["NotAfter"] >= time.time()): return False key = RSA.import_key(IssuerPublicKey) @@ -63,14 +63,14 @@ def ValidateCertificate(Certificate,IssuerPublicKey): return Valid_Signature -def GetSignatureSource(Certificate): +def GetSignatureSource(Certificate): #Obtains the string to be hashed for the signature SignatureSource = codecs.encode(str(Certificate["Name"]) + str(Certificate["Issuer"]) + str(Certificate["NotBefore"]) + str(Certificate["NotAfter"]) + str(hashlib.sha256(Certificate["PublicKey"]).hexdigest())) return SignatureSource - +#Test Certificates and keys RootCertificates = [{'Signature': b'qV\x98n\xa8\x0c\xe1\xde\xc6\xf8\xbdK\xd4>\xf3\xdf\xb1\xc2\x02\x06z\xbf\x83#\xa2\xd5)>&\xdf\xf1\xbcm5$\x15wB\x84/L&\x1aA\xa2=\xae\xe2\x92\xab\xed\x1bP\x02c)5IT\xc1\xcft\xf1gb\x11\xd0\xa1p\xd3\xcei\xbe\xb8\xb3t\x06\xbf/\xd6$i\xc2\xfbO\xb2\x94]\x02\xa1\xf9\xfe\xec9\xd12!\x91\xea\xcb\x13\x0b\x7f\xd7\xfc\xac\xb8F\xe5\xd3\x7f\xee$\xc7?#\x07\x01\x8f\xf9\xb1\xaex\x87\x07Z#\x9e', 'NotBefore': 1, 'Name': 'IssuerA', 'PublicKey': b'0\x81\x9f0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x81\x8d\x000\x81\x89\x02\x81\x81\x00\xcddI\xe9\xcbV\xc2\x931\n+\xc5\xef\xcc\xeft\xf8\x01\xe8s\x12\xe2\r)\xf8\x81\xb9\x1e\xcc\x06!n\xff\xd0=\xf1\x92\xa6\xd0\xff\x91~s\xf9pY\xde\xc2\xe4\x8e`\xd2\x02\xe8\xf2r\xa7=\xdbFK\x9a\x02b\x1a\xcdP\x07\x92>GU\x1a?\x89\x96\x89\x99\xf3b\xe5<\xfdJY\xd2\xc4]W\xe9j\x87\xb4\x82\xe0\xa8c\xa7\x87\xe2(\xb0\xa6\xc2M>\xab\xb5r\xd8\xdf\x10\x1a\x07\xd6\xc0\t\xb0\xa3Ng\x1aD%\xab\xb52\xad\x02\x03\x01\x00\x01', 'NotAfter': 10000000000000, 'Issuer': 'IssuerA'}, ] @@ -84,7 +84,7 @@ def GetSignatureSource(Certificate): # CERTIFICATE CHAIN MUST RUN FROM ROOT CERTIFIATE TO SERVER -def CreateTestChain(length,RSA_KeySize,PRIVATE_KEY,hostname="localhost"): +def CreateTestChain(length,RSA_KeySize,PRIVATE_KEY,hostname="localhost"): #Generates a chain used to test the program as well as the time taken to generate large chains. chain = [] Issuer = "IssuerA" for x in range(length): @@ -103,14 +103,14 @@ def CreateTestChain(length,RSA_KeySize,PRIVATE_KEY,hostname="localhost"): return chain,pk -def VerifyBaseAddress(EndCertificate,con): +def VerifyBaseAddress(EndCertificate,con): #Verify if connection is to the correct server. A server could register the certificate with the server domain name so that the client can ensure they have the correct certificate. address = con.getpeername()[0] try: domain = socket.gethostbyaddr(address)[0] except: domain = None - if EndCertificate["Name"] not in [address,domain]: + if EndCertificate["Name"] not in [address,domain]: #If it cannot link the certificate name to the domain or address then alert user print("[WARNING] The certificate name of the server could not be matched with the looked up domain name or address of the server") if AATC_Config.ALLOW_FAILED_DOMAIN_LOOKUP: print("Continuing anyway. Set ALLOW_FAILED_DOMAIN_LOOKUP to False to disable this behaviour") diff --git a/AATC_DB.py b/AATC_DB.py index c1c6da9..6985a74 100644 --- a/AATC_DB.py +++ b/AATC_DB.py @@ -26,30 +26,28 @@ class DBConnection: This is the Database connection object for AATC. This provides a DB Connection of other objects using a simple API The General form is : Sucess,Message,*Data* = DatabaseConnection . Request( *Arguments* ) - + + General behaviour: A Get...() Function which returns values from a table, the Message will contain a string version of the headers in a list eg '["DroneID","DroneName","OtherColumnName"] A Delete/Remove/Add/Set (Things which dont return specific data) will return Sucess,Message !!!THE UserID IS VITAL FOR MOST TRANSACTIONS WHICH REQUIRE A PERMISSION!!! This is to prevent users interfereing with others. Many functions will require a UserID. This is a unique number for a Username and can be obtained by DBConnection.GetUserID('Username') - In the future it may be possible to store the UserID in this object Table_Headers("TableName") will get the headers of that table - Reset() will drop all tables and create them again! THIS CAN TAKE A LONG TIME AND IS PROBABLY NOT THREADSAFE. WILL CRASH OTHER USERS! NOT ACCESSABLE AT ALL RIGHT NOW - - This object is designed more towards security and reliability than performance, therefore more checks occur which can impeed performance + Reset() will drop all tables and create them again! THIS CAN TAKE A LONG TIME AND IS PROBABLY NOT THREADSAFE. WILL CRASH OTHER USERS! """ def __init__(self): print("Initializing database connection ") - self._db_con = sql.connect("localhost","AATC_Server","password","AATC_Database") + self._db_con = sql.connect("localhost","AATC_Server","password","AATC_Database") #Connects to mysql server self._cur = self._db_con.cursor() self._cur_header = self._db_con.cursor() def Exit(self): self._db_con.close() - def Table_Headers(self,TableName): + def Table_Headers(self,TableName): #Function get the column names for a table in order to allow the client to identify what each column is for. self._cur_header.execute("SHOW COLUMNS FROM "+ TableName) # Cannot use placeholders when referencing the table name , syntax error result = self._cur_header.fetchall() Headers = [] @@ -63,14 +61,14 @@ def AddDrone(self,UserID,DroneName,DronePassword,DroneType,DroneSpeed,DroneRange if self._cur.fetchall() == (): self._cur.execute("INSERT INTO Drone(UserID,DroneName,DroneType,DroneSpeed,DroneRange,DroneWeight,FlightsFlown,LastCoords,LastBattery) VALUES(%s,%s,%s,%s,%s,%s,0,'(0,0,0)',0)",(UserID,DroneName,DroneType,DroneSpeed,DroneRange,DroneWeight)) _,_,DroneID = self.GetDroneID(UserID,DroneName) - DroneID = DroneID[0][0] #Sets up password in seperate table for maintainance security + DroneID = DroneID[0][0] #Sets up password in seperate table for maintainance security( if SELECT * were used the password would be disclosed in plaintext self._cur.execute("INSERT INTO DroneCredentials(DroneID,DronePassword) VALUES(%s,%s)",(DroneID,DronePassword)) self._db_con.commit() return True,"Added Drone" else: return False,"This drone already exists for you" - def RemoveDrone(self,UserID,DroneID): + def RemoveDrone(self,UserID,DroneID): #Delete a drone self._cur.execute("SELECT 1 FROM Drone WHERE UserID = %s AND DroneID = %s",(UserID,DroneID)) #If drone belongs to user if self._cur.fetchall() != (): self._cur.execute("DELETE FROM Drone WHERE DroneID = %s",(DroneID,)) @@ -81,30 +79,30 @@ def RemoveDrone(self,UserID,DroneID): return False,"This drone does not exist or you do not have permission to delete this drone" def DroneCheckCredentials(self,DroneID,DronePassword): - self._cur.execute("SELECT DroneID FROM DroneCredentials WHERE DroneID = %s AND DronePassword = %s",(DroneID,DronePassword)) + self._cur.execute("SELECT DroneID FROM DroneCredentials WHERE DroneID = %s AND DronePassword = %s",(DroneID,DronePassword)) #Search if login is correct DroneIDFetch = self._cur.fetchall() if DroneIDFetch != (): return True,"Correct Drone Credentials",DroneIDFetch[0][0] else: return False,"Incorrect Drone Credentials",-1 - def DroneGetDroneInfo(self,DroneID): + def DroneGetDroneInfo(self,DroneID): #Function to allow drone to get information about itself self._cur.execute("SELECT * FROM Drone WHERE DroneID = %s",(DroneID,)) return True,str(self.Table_Headers("Drone")),self._cur.fetchall() - def GetDroneID(self,UserID,DroneName): + def GetDroneID(self,UserID,DroneName): #Gets the drone ID for a given drone name and userID self._cur.execute("SELECT DroneID FROM Drone WHERE UserID = %s AND DroneName = %s",(UserID,DroneName)) return True,"['DroneID']",self._cur.fetchall() - def GetDroneCredentials(self,UserID,DroneID): + def GetDroneCredentials(self,UserID,DroneID): #Obtains the drone's credentials self._cur.execute("SELECT DroneCredentials.* FROM Drone,DroneCredentials WHERE Drone.UserID = %s AND Drone.DroneID = DroneCredentials.DroneID AND DroneCredentials.DroneID = %s",(UserID,DroneID)) return True,str(self.Table_Headers("DroneCredentials")),self._cur.fetchall() - def SetDroneCredentials(self,UserID,DroneID,DronePassword): - self._cur.execute("SELECT 1 FROM Drone WHERE Drone.UserID = %s AND Drone.DroneID = %s",(UserID,DroneID)) + def SetDroneCredentials(self,UserID,DroneID,DronePassword): #Alters the drones credentials + self._cur.execute("SELECT 1 FROM Drone WHERE Drone.UserID = %s AND Drone.DroneID = %s",(UserID,DroneID)) #Checks the user owns the drone if self._cur.fetchall() != (): self._cur.execute("UPDATE DroneCredentials SET DronePassword = %s WHERE DroneID = %s",(DronePassword,DroneID)) self._db_con.commit() @@ -112,23 +110,23 @@ def SetDroneCredentials(self,UserID,DroneID,DronePassword): else: return False,"This drone does not exist or you do not have permission to change it's credentials" - def CheckDroneOwnership(self,UserID,DroneID): + def CheckDroneOwnership(self,UserID,DroneID): #Checks if a user owns the drone self._cur.execute("SELECT 1 FROM Drone WHERE UserID = %s AND DroneID = %s",(UserID,DroneID)) return True,"['DroneOwnership']",self._cur.fetchall() - def GetDroneInfo(self,UserID,DroneID): + def GetDroneInfo(self,UserID,DroneID): #Get the information about a single drone self._cur.execute("SELECT DISTINCT Drone.* FROM Drone,User WHERE Drone.DroneID = %s AND (Drone.UserID = %s OR (Drone.UserID = User.UserID AND User.PublicVisibleFlights = 1))",(DroneID,UserID)) return True,str(self.Table_Headers("Drone")),self._cur.fetchall() - def GetDronesUser(self,UserID): + def GetDronesUser(self,UserID): #Get all user drones self._cur.execute("SELECT * FROM Drone WHERE UserID = %s",(UserID,)) self._db_con.commit() return True,str(self.Table_Headers("Drone")),self._cur.fetchall() - def GetDronesAll(self): + def GetDronesAll(self): #Get all publicly visible drones self._cur.execute("SELECT Drone.* FROM User,Drone WHERE User.UserID = Drone.UserID AND User.PublicVisibleFlights = 1") return True,str(self.Table_Headers("Drone")),self._cur.fetchall() - def UpdateDroneStatus(self,DroneID,LastCoords,LastBattery): + def UpdateDroneStatus(self,DroneID,LastCoords,LastBattery): #Update the latest information about the drone self._cur.execute("UPDATE Drone SET LastCoords = %s,LastBattery = %s WHERE DroneID = %s",(str(LastCoords),int(LastBattery),DroneID)) self._db_con.commit() return True,"Updated Drone Status" @@ -142,8 +140,8 @@ def GetUsername(self,UserID): self._cur.execute("SELECT Username FROM User WHERE UserID = %s",(UserID,)) return True,"['Username']",self._cur.fetchall() - def AddUser(self,Username,Password): - self._cur.execute("SELECT 1 FROM User WHERE Username = %s",(Username,)) + def AddUser(self,Username,Password): #Add a new user + self._cur.execute("SELECT 1 FROM User WHERE Username = %s",(Username,)) #Checks the user does not already exist if self._cur.fetchall() == (): self._cur.execute("INSERT INTO User(Username,Password,PublicVisibleFlights,PermissionAdder,ZoneCreatorPermission,ZoneRemoverPermission,ZoneModifierPermission) VALUES(%s,%s,0,0,0,0,0)",(Username,Hash(Password,Username))) self._db_con.commit() @@ -151,35 +149,36 @@ def AddUser(self,Username,Password): else: return False,"User already exists" - def CheckCredentials(self,Username,Password): - self._cur.execute("SELECT UserID FROM User WHERE Username = %s AND Password = %s",(Username,Hash(Password,Username))) + def CheckCredentials(self,Username,Password): #Check as user's login is correct + self._cur.execute("SELECT UserID FROM User WHERE Username = %s AND Password = %s",(Username,Hash(Password,Username))) #Checks the password and username are correct UserIDFetch = self._cur.fetchall() if UserIDFetch != (): - return True,"Correct Credentials",UserIDFetch[0][0] + return True,"Correct Credentials",UserIDFetch[0][0] #Return the UserID else: return False,"Incorrect Credentials",-1 - def SetFlightVisibility(self,UserID,Value): - if Value in [0,1]: + def SetFlightVisibility(self,UserID,Value): #Alter the flight visiblilty for a user + if Value in [0,1]: #Ensures this can only be 0,1 , could be expanded to other values self._cur.execute("UPDATE User SET PublicVisibleFlights = %s WHERE UserID = %s",(Value,UserID)) self._db_con.commit() return True,"Changed PublicVisibleFlights Value" else: return False,"Invalid PublicVisibleFlights Value" - def SetAccountType(self,UserID,Permission,Value): + + def SetAccountType(self,UserID,Permission,Value): #Alter a permission for the user options = ["ZoneCreatorPermission","ZoneRemoverPermission","ZoneModifierPermission"] - if Permission not in options: + if Permission not in options: #Checks only these options are allowed to prevent SQL injection return False,"This setting does not exist. Options are :"+str(options) self._cur.execute("UPDATE User SET "+Permission+" =%s WHERE UserID = %s AND PermissionAdder > 2",(Value,UserID)) #The string concatation is safe as only strings which are found exactly in options can be inserted and so are safe strings, cannot contain any other commands - self._db_con.commit() + self._db_con.commit() #Above checks permission adder so only users with PermissionAdder > 2 can change their permissions - prevents all users obtaining these permisssions return True,"Set AccountType Value" - def UserChangePassword(self,UserID,OldPassword,NewPassword): - self._cur.execute("SELECT Username from User WHERE UserID = ?",(UserID,)) + def UserChangePassword(self,UserID,OldPassword,NewPassword): #Change the user's password + self._cur.execute("SELECT Username from User WHERE UserID = ?",(UserID,)) # Find the username for the salt Username = self._cur.fetchall()[0][0] - self._cur.execute("SELECT 1 FROM User WHERE UserID = %s and Password = %s",(UserID,Hash(OldPassword,Username))) + self._cur.execute("SELECT 1 FROM User WHERE UserID = %s and Password = %s",(UserID,Hash(OldPassword,Username))) #Check old password is correct if self._cur.fetchall() != (): - self._cur.execute("UPDATE User SET Password = %s WHERE UserID = %s",(Hash(NewPassword,Username),UserID)) + self._cur.execute("UPDATE User SET Password = %s WHERE UserID = %s",(Hash(NewPassword,Username),UserID)) #update password self._db_con.commit() return True,"Changed password" else: @@ -187,17 +186,17 @@ def UserChangePassword(self,UserID,OldPassword,NewPassword): ##################### FLIGHT ############################## - def GetFlightsUser(self,UserID): + def GetFlightsUser(self,UserID): #Get all flights belonging to the user self._cur.execute("SELECT Flight.* FROM Flight,Drone WHERE Flight.DroneID = Drone.DroneID AND Drone.UserID = %s",(UserID,)) return True,str(self.Table_Headers("Flight")),self._cur.fetchall() - def GetFlightsAll(self): + def GetFlightsAll(self): #Get all publicly visible flights self._cur.execute("SELECT Flight.* FROM Flight,Drone,User WHERE Flight.DroneID = Drone.DroneID AND Drone.UserID = User.UserID AND User.PublicVisibleFlights = 1") return True,str(self.Table_Headers("Flight")),self._cur.fetchall() def AddFlight(self,UserID,DroneID,StartCoords,EndCoords,StartTime,ETA,EndTime,Distance,XOffset,YOffset,ZOffset): - self._cur.execute("SELECT 1 FROM User,Drone WHERE Drone.DroneID = %s AND Drone.UserID = %s",(DroneID,UserID)) + self._cur.execute("SELECT 1 FROM User,Drone WHERE Drone.DroneID = %s AND Drone.UserID = %s",(DroneID,UserID)) #Check user owns drone if self._cur.fetchall() !=(): self._cur.execute("INSERT INTO Flight(DroneID,StartCoords,EndCoords,StartTime,ETA,EndTime,Distance,XOffset,YOffset,ZOffset,Completed) VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,0)",(DroneID,str(StartCoords),str(EndCoords),int(StartTime),int(ETA),int(EndTime),int(Distance),XOffset,YOffset,ZOffset)) self._db_con.commit() @@ -206,8 +205,8 @@ def AddFlight(self,UserID,DroneID,StartCoords,EndCoords,StartTime,ETA,EndTime,Di else: return False,"You do not have permission to launch this drone",-1 - def RemoveFlight(self,UserID,FlightID): - self._cur.execute("SELECT 1 FROM Flight,Drone,User WHERE Flight.FlightID = %s AND Flight.DroneID = Drone.DroneID AND Drone.UserID = %s",(FlightID,UserID)) #If User owns this drone + def RemoveFlight(self,UserID,FlightID): #Delete a flight + self._cur.execute("SELECT 1 FROM Flight,Drone,User WHERE Flight.FlightID = %s AND Flight.DroneID = Drone.DroneID AND Drone.UserID = %s",(FlightID,UserID)) #If User owns this drone/flight if self._cur.fetchall() != (): self._cur.execute("DELETE FROM Flight WHERE FlightID = %s",(FlightID,)) self._db_con.commit() @@ -215,9 +214,9 @@ def RemoveFlight(self,UserID,FlightID): else: return False,"You do not have permission to remove this flight" - def CheckForFlight(self,DroneID,MaxLookAheadTime): + def CheckForFlight(self,DroneID,MaxLookAheadTime): #Checks for a nearby (time wise) flight for the drone InvalidateDelay = 1800 - self._cur.execute("SELECT FlightID FROM Flight WHERE DroneID = %s AND StartTime < (%s+%s) AND StartTime > (%s-%s) AND Completed = 0 ORDER BY StartTime ASC LIMIT 1",(DroneID,GetTime(),MaxLookAheadTime, GetTime(),InvalidateDelay)) + self._cur.execute("SELECT FlightID FROM Flight WHERE DroneID = %s AND StartTime < (%s+%s) AND StartTime > (%s-%s) AND Completed = 0 ORDER BY StartTime ASC LIMIT 1",(DroneID,GetTime(),MaxLookAheadTime, GetTime(),InvalidateDelay)) #Checks if a flight is available in the allowed range FlightIDFetch = self._cur.fetchall() self._db_con.commit() if FlightIDFetch != (): @@ -225,16 +224,16 @@ def CheckForFlight(self,DroneID,MaxLookAheadTime): else: return False,"Flight is not available",[] - def GetFlight(self,DroneID,FlightID): - self._cur.execute("SELECT * FROM Flight WHERE DroneID = %s AND FlightID = %s",(DroneID,FlightID)) + def GetFlight(self,DroneID,FlightID): #Get details about a given flight + self._cur.execute("SELECT * FROM Flight WHERE DroneID = %s AND FlightID = %s",(DroneID,FlightID)) #Checks the drone can access the flight FlightFetch = self._cur.fetchall() if FlightFetch != (): return True,str(self.Table_Headers("Flight")),FlightFetch else: return False,"Flight not obtained, Flight may not exist or you may not have permission",[] - def MarkFlightComplete(self,DroneID,FlightID,Code): - self._cur.execute("SELECT 1 FROM Flight WHERE DroneID = %s AND FlightID = %s",(DroneID,FlightID)) + def MarkFlightComplete(self,DroneID,FlightID,Code): #Mark a flight as complete using a given code. + self._cur.execute("SELECT 1 FROM Flight WHERE DroneID = %s AND FlightID = %s",(DroneID,FlightID)) #Checks drone can access the flight if self._cur.fetchall() != (): self._cur.execute("UPDATE Flight SET Completed = %s,EndTime = %s WHERE FlightID = %s",(Code,GetTime(),FlightID)) self._cur.execute("UPDATE Drone SET FlightsFlown = FlightsFlown +1 WHERE DroneID = %s",(DroneID,)) @@ -243,30 +242,30 @@ def MarkFlightComplete(self,DroneID,FlightID,Code): else: return False,"You do not have permission to mark this flight complete" - def GetCompletedFlightIDs(self,EndTimeThreshold): - self._cur.execute("SELECT FlightID FROM Flight WHERE (Completed > 0 AND (EndTime + %s) < %s) OR (EndTime+ %s) < %s",(EndTimeThreshold,GetTime(),EndTimeThreshold*3,GetTime())) + def GetCompletedFlightIDs(self,EndTimeThreshold): #Obtain the FlightIDs of all flights which have been completed + self._cur.execute("SELECT FlightID FROM Flight WHERE (Completed > 0 AND (EndTime + %s) < %s) OR (EndTime+ %s) < %s",(EndTimeThreshold,GetTime(),EndTimeThreshold*3,GetTime())) return True,"['FlightID']",self._cur.fetchall() - def CleanFlights(self,FlightIDList): + def CleanFlights(self,FlightIDList): #Delete all flights in the FlightID list self._cur.executemany("DELETE FROM Flight WHERE FlightID = %s",FlightIDList) self._db_con.commit() return True,"Deleted completed flights above threshold" #---------------------------- FLIGHT WAYPOINT------------------------------------ - def GetFlightWaypoints(self,DroneID,FlightID): + def GetFlightWaypoints(self,DroneID,FlightID): #Get all the flight waypoints for a flight self._cur.execute("SELECT FlightWaypoints.* FROM Flight,FlightWaypoints WHERE Flight.DroneID = %s AND Flight.FlightID = FlightWaypoints.FlightID AND FlightWaypoints.FlightID = %s",(DroneID,FlightID)) return True,str(self.Table_Headers("FlightWaypoints")),self._cur.fetchall() - def GetFlightWaypointsUser(self,UserID): + def GetFlightWaypointsUser(self,UserID): #Get all the flightt waypoints for a user self._cur.execute("SELECT FlightWaypoints.* FROM FlightWaypoints,Flight,Drone WHERE FlightWaypoints.FlightID = Flight.FlightID AND Flight.DroneID = Drone.DroneID AND Drone.UserID = %s ORDER BY FlightWaypoints.FlightID, FlightWaypoints.WaypointNumber",(UserID,)) return True,str(self.Table_Headers("FlightWaypoints")),self._cur.fetchall() - def GetFlightWaypointsAll(self): + def GetFlightWaypointsAll(self): #Get all publicly visible flight waypoints. self._cur.execute("SELECT FlightWaypoints.* FROM FlightWaypoints,Flight,Drone,User WHERE FlightWaypoints.FlightID = Flight.FlightID AND Flight.DroneID = Drone.DroneID AND Drone.UserID = User.UserID AND User.PublicVisibleFlights = 1 ORDER BY FlightWaypoints.FlightID,FlightWaypoints.WaypointNumber") return True,str(self.Table_Headers("FlightWaypoints")),self._cur.fetchall() - def AddWaypoint(self,UserID,FlightID,WaypointNumber,Coords,ETA,BlockTime=0): - self._cur.execute("SELECT 1 FROM User,Flight,Drone WHERE User.UserID = %s AND User.UserID = Drone.UserID AND Drone.DroneID = Flight.DroneID AND Flight.FlightID = %s",(UserID,FlightID)) + def AddWaypoint(self,UserID,FlightID,WaypointNumber,Coords,ETA,BlockTime=0): #Add a single new waypoint + self._cur.execute("SELECT 1 FROM User,Flight,Drone WHERE User.UserID = %s AND User.UserID = Drone.UserID AND Drone.DroneID = Flight.DroneID AND Flight.FlightID = %s",(UserID,FlightID)) #Check the client can add this waypoint if self._cur.fetchall() !=(): self._cur.execute("INSERT INTO FlightWaypoints(FlightID,WaypointNumber,Coords,ETA,BlockTime) VALUES(%s,%s,%s,%s,%s)",(FlightID,WaypointNumber,str(Coords),int(ETA),BlockTime)) self._db_con.commit() @@ -274,8 +273,8 @@ def AddWaypoint(self,UserID,FlightID,WaypointNumber,Coords,ETA,BlockTime=0): else: return False,"You do not have permission to add a waypoint for this flight" - def AddWaypoints(self,UserID,FlightID,Waypoints): - self._cur.execute("SELECT 1 FROM User,Flight,Drone WHERE User.UserID = %s AND User.UserID = Drone.UserID AND Drone.DroneID = Flight.DroneID AND Flight.FlightID = %s",(UserID,FlightID)) + def AddWaypoints(self,UserID,FlightID,Waypoints): #Add a number of new waypoints in a batch to reduce overhead + self._cur.execute("SELECT 1 FROM User,Flight,Drone WHERE User.UserID = %s AND User.UserID = Drone.UserID AND Drone.DroneID = Flight.DroneID AND Flight.FlightID = %s",(UserID,FlightID)) #Checks user can add flight if self._cur.fetchall() !=(): self._cur.executemany("INSERT INTO FlightWaypoints(FlightID,WaypointNumber,Coords,ETA,BlockTime) VALUES(%s,%s,%s,%s,%s)",Waypoints) self._db_con.commit() @@ -283,8 +282,8 @@ def AddWaypoints(self,UserID,FlightID,Waypoints): else: return False,"You do not have permission to add a waypoint for this flight" - def RemoveFlightWaypoints(self,UserID,FlightID): - self._cur.execute("SELECT 1 FROM User,Flight,Drone WHERE User.UserID = %s AND User.UserID = Drone.UserID AND Drone.DroneID = Flight.DroneID AND Flight.FlightID = %s",(UserID,FlightID)) + def RemoveFlightWaypoints(self,UserID,FlightID): #Remove all waypoints for a given flight + self._cur.execute("SELECT 1 FROM User,Flight,Drone WHERE User.UserID = %s AND User.UserID = Drone.UserID AND Drone.DroneID = Flight.DroneID AND Flight.FlightID = %s",(UserID,FlightID)) #Check user can delete waypoint if self._cur.fetchall() !=(): self._cur.execute("DELETE FROM FlightWaypoints WHERE FlightID = %s",(FlightID,)) self._db_con.commit() @@ -293,33 +292,33 @@ def RemoveFlightWaypoints(self,UserID,FlightID): return False,"You do not have permission to delete these waypoints" - def CleanCompletedFlightWaypoints(self,FlightID): #Server only command + def CleanCompletedFlightWaypoints(self,FlightID): #Server only command, deletes all flight waypoints for the given flight self._cur.execute("DELETE FROM FlightWaypoints WHERE FlightID = %s",(FlightID,)) self._db_con.commit() return True,"Deleted waypoints" ########################### MONITOR ################################## def AddMonitor(self,MonitorName,MonitorPassword): - self._cur.execute("SELECT 1 FROM Monitor WHERE MonitorName = %s",(MonitorName,)) + self._cur.execute("SELECT 1 FROM Monitor WHERE MonitorName = %s",(MonitorName,)) #Checks monitordoes not exist if self._cur.fetchall() == (): - self._cur.execute("INSERT INTO Monitor(MonitorName,MonitorPassword,AccountType) VALUES(%s,%s,'Default')",(MonitorName,Hash(MonitorPassword,MonitorName))) + self._cur.execute("INSERT INTO Monitor(MonitorName,MonitorPassword) VALUES(%s,%s)",(MonitorName,Hash(MonitorPassword,MonitorName))) self._db_con.commit() return True,"Added Monitor" else: return False,"Monitor already exists" - def MonitorCheckCredentials(self,MonitorName,MonitorPassword): - self._cur.execute("SELECT MonitorID FROM Monitor WHERE MonitorName = %s AND MonitorPassword = %s",(MonitorName,Hash(MonitorPassword,MonitorName))) + def MonitorCheckCredentials(self,MonitorName,MonitorPassword): #Checks if the login for a monitor is correct + self._cur.execute("SELECT MonitorID FROM Monitor WHERE MonitorName = %s AND MonitorPassword = %s",(MonitorName,Hash(MonitorPassword,MonitorName))) #Checks monitor credentials MonitorIDFetch = self._cur.fetchall() if MonitorIDFetch != (): return True,"Correct Credentials",MonitorIDFetch[0][0] else: return False,"Incorrect Credentials",-1 - def MonitorChangePassword(self,MonitorID,OldPassword,NewPassword): - self._cur.execute("SELECT MonitorName FROM Monitor WHERE MonitorID = ?",(MonitorID,)) + def MonitorChangePassword(self,MonitorID,OldPassword,NewPassword): #Change the password for a monitor + self._cur.execute("SELECT MonitorName FROM Monitor WHERE MonitorID = ?",(MonitorID,)) #Obtains monitor name MonitorName = self._cur.fetchall()[0][0] - self._cur.execute("SELECT 1 FROM Monitor WHERE MonitorID = %s AND MonitorPassword = %s",(MonitorID,Hash(OldPassword,MonitorName))) + self._cur.execute("SELECT 1 FROM Monitor WHERE MonitorID = %s AND MonitorPassword = %s",(MonitorID,Hash(OldPassword,MonitorName))) #Ensures old password is correct if self._cur.fetchall() != (): self._cur.execute("UPDATE Monitor SET MonitorPassword = %s WHERE MonitorID = %s",(Hash(NewPassword,MonitorName),MonitorID)) self._db_con.commit() @@ -327,17 +326,17 @@ def MonitorChangePassword(self,MonitorID,OldPassword,NewPassword): else: return False, "Incorrect old password" - def GetMonitorDrones(self,MonitorID): + def GetMonitorDrones(self,MonitorID): #Get all the monitors for a drone from monitor permissions self._cur.execute("SELECT Drone.* FROM Drone,MonitorPermission WHERE Drone.UserID = MonitorPermission.UserID AND MonitorPermission.MonitorID = %s",(MonitorID,)) self._db_con.commit() return True,str(self.Table_Headers("Drone")),self._cur.fetchall() - def GetMonitorFlights(self,MonitorID): + def GetMonitorFlights(self,MonitorID): #Get all flights for a monitor via monitor permissions self._cur.execute("SELECT Flight.* FROM Flight,Drone,MonitorPermission WHERE Flight.DroneID = Drone.DroneID AND Drone.UserID = MonitorPermission.UserID and MonitorPermission.MonitorID = %s",(MonitorID,)) self._db_con.commit() return True,str(self.Table_Headers("Flight")),self._cur.fetchall() - def GetMonitorFlightWaypoints(self,MonitorID): + def GetMonitorFlightWaypoints(self,MonitorID): #Get all flight waypoints for a monitor via monitor permissions self._cur.execute("SELECT FlightWaypoints.* FROM FlightWaypoints,Flight,Drone,MonitorPermission WHERE FlightWaypoints.FlightID = Flight.FlightID AND Flight.DroneID = Drone.DroneID AND Drone.UserID = MonitorPermission.UserID and MonitorPermission.MonitorID = %s",(MonitorID,)) self._db_con.commit() return True,str(self.Table_Headers("FlightWaypoints")),self._cur.fetchall() @@ -352,8 +351,8 @@ def GetMonitorName(self,MonitorID): return True,"['MonitorName']",self._cur.fetchall() #-------------------------- MONITOR PERMISSION ------------------------------------ - def AddMonitorPermission(self,UserID,MonitorID,ExpiryDate): - self._cur.execute("SELECT 1 FROM MonitorPermission WHERE UserID = %s AND MonitorID = %s",(UserID,MonitorID)) + def AddMonitorPermission(self,UserID,MonitorID,ExpiryDate): #Add a monitor permission + self._cur.execute("SELECT 1 FROM MonitorPermission WHERE UserID = %s AND MonitorID = %s",(UserID,MonitorID)) # Checks permission does not already exist if self._cur.fetchall() ==(): self._cur.execute("INSERT INTO MonitorPermission(MonitorID,UserID,LastAccessed,ExpiryDate) VALUES (%s,%s,%s,%s)",(MonitorID,UserID,0,ExpiryDate)) self._db_con.commit() @@ -361,45 +360,48 @@ def AddMonitorPermission(self,UserID,MonitorID,ExpiryDate): else: return False,"MonitorPermission already exists" - def RemoveMonitorPermission(self,UserID,MonitorID): + def RemoveMonitorPermission(self,UserID,MonitorID): #remove a monitor permission self._cur.execute("DELETE FROM MonitorPermission WHERE UserID = %s AND MonitorID = %s",(UserID,MonitorID)) self._db_con.commit() return True,"Deleted MonitorPermission" - def ModifyMonitorPermissionDate(self,UserID,MonitorID,NewDate): + def ModifyMonitorPermissionDate(self,UserID,MonitorID,NewDate): #Modify a monitor permission self._cur.execute("UPDATE MonitorPermission SET ExpiryDate = %s WHERE UserID = %s AND MonitorID = %s",(NewDate,UserID,MonitorID)) self._db_con.commit() return True,"Updated Date for MonitorPermission" - def GetMonitorPermissionUser(self,UserID): + + def GetMonitorPermissionUser(self,UserID): #Get all permissions for a user self._cur.execute("SELECT * FROM MonitorPermission WHERE UserID = %s",(UserID,)) Headers = self.Table_Headers("MonitorPermission") return True,str(Headers),self._cur.fetchall() - def GetMonitorPermissionMonitor(self,MonitorID): + + def GetMonitorPermissionMonitor(self,MonitorID): #Get all the permissions for a monitor self._cur.execute("SELECT * FROM MonitorPermission WHERE MonitorID = %s",(MonitorID,)) Headers = self.Table_Headers("MonitorPermission") return True,str(Headers),self._cur.fetchall() - def UpdateMonitorPermissionLastAccessed(self,UserID,MonitorID,NewDate = None): + + def UpdateMonitorPermissionLastAccessed(self,UserID,MonitorID,NewDate = None): #Update when the permission was last accessed. Not used due to performance loss and not being particularly useful if NewDate == None: NewDate = GetTime() self._cur.execute("UPDATE MonitorPermission SET LastAccessed = %s WHERE MonitorID = %s AND UserID = %s",(NewDate,MonitorID,UserID)) self._db_con.commit() return True,"Updated LastAccessed" - def CleanMonitorPermissions(self): + def CleanMonitorPermissions(self): #Remove old monitor permissions after expiry self._cur.execute("DELETE FROM MonitorPermission WHERE ExpiryDate < %s",(GetTime(),)) self._db_con.commit() return True,"Cleaned Monitor Permission" ######################## No Fly Zone ############################################### - def AddNoFlyZone(self,Coord1,Coord2,Level,UserID): + def AddNoFlyZone(self,Coord1,Coord2,Level,UserID): #Add a no fly zone if not CoordLessThanOrEqual(Coord1,Coord2): #If Coord2 is closer to origin in every point than Coord1 It is an invalid coordinate Message = "Invalid Coordinates. Coordinate 2:"+str(Coord2)+" is closer in some dimentions to the origin than Coordinate 1"+str(Coord1) return False,Message else: if type(Level) is not int or Level <= 0: return False, "Invalid Level. Must be an integer and > 0" - self._cur.execute("SELECT 1 FROM User WHERE UserID = %s AND ZoneCreatorPermission =1",(UserID,)) + self._cur.execute("SELECT 1 FROM User WHERE UserID = %s AND ZoneCreatorPermission =1",(UserID,)) #Checks user has permission to add a NoFlyZone if self._cur.fetchall() != (): self._cur.execute("INSERT INTO NoFlyZone(StartCoord,EndCoord,Level,OwnerUserID) VALUES(%s,%s,%s,%s)",(str(Coord1),str(Coord2),Level,UserID)) self._db_con.commit() @@ -417,7 +419,7 @@ def RemoveNoFlyZone(self,UserID,ZoneID): return False,"You do not own this NoFlyZone or you do not have the ZoneRemoverPermission" #Replace String permission with max no fly zone level permission - def ModifyNoFlyZoneLevel(self,UserID,ZoneID,Level): + def ModifyNoFlyZoneLevel(self,UserID,ZoneID,Level): #Modify a no fly zone level. if type(Level) is not int or Level <=0: return False,"Invalid Level. Must be an integer and > 0" self._cur.execute("SELECT 1 FROM NoFlyZone,User WHERE (NoFlyZone.ZoneID = %s AND NoFlyZone.OwnerUserID = %s ) OR (User.UserID = %s AND User.ZoneModifierPermission = 1)",(ZoneID,UserID,UserID)) # Gets 1 if (Zone with ZoneID has UserID as owner) OR (User with UserID has ZoneModifier Permission) @@ -429,7 +431,7 @@ def ModifyNoFlyZoneLevel(self,UserID,ZoneID,Level): else: return False,"You do not own this NoFlyZone or you do not have the ZoneModifier Permission" #Replace String permission with max no fly zone level permission - def GetNoFlyZones(self): + def GetNoFlyZones(self): #Get all no fly zones self._cur.execute("SELECT * FROM NoFlyZone") Headers = self.Table_Headers("NoFlyZone") #Gets Headers of table to be sent as message return True,str(Headers),self._cur.fetchall() @@ -437,7 +439,7 @@ def GetNoFlyZones(self): ########################################################################### ################## InputStack ################################# - def Bot_addValue(self,value,chat_id): + def Bot_addValue(self,value,chat_id): #Add an input value to the input stack for a chat id self._cur.execute("SELECT MAX(stack_pos) FROM InputStack WHERE chat_id = %s",(chat_id,)) result = self._cur.fetchall() if not result[0][0] == None: @@ -447,29 +449,29 @@ def Bot_addValue(self,value,chat_id): self._cur.execute("INSERT INTO InputStack VALUES(%s,%s,%s)",(chat_id,stack_pos,value)) self._db_con.commit() - def Bot_getCommand(self,chat_id): + def Bot_getCommand(self,chat_id): #Get the command on the stack for a given chat id self._cur.execute("SELECT value FROM InputStack WHERE chat_id = %s AND stack_pos = 0",(chat_id,)) result = self._cur.fetchall() return result[0][0] - def Bot_getStack(self,chat_id): + def Bot_getStack(self,chat_id): #Get the entire stack ordered backwards self._cur.execute("SELECT value FROM InputStack WHERE chat_id = %s ORDER BY stack_pos ASC",(chat_id,)) result = self._cur.fetchall() return result - def Bot_getStackSize(self,chat_id): + def Bot_getStackSize(self,chat_id): #Get the size of the stack self._cur.execute("SELECT COUNT(1) FROM InputStack WHERE chat_id = %s",(chat_id,)) result = self._cur.fetchall() return result[0][0] - def Bot_flushStack(self,chat_id): + def Bot_flushStack(self,chat_id): #Cancel the entire stack self._cur.execute("DELETE FROM InputStack WHERE chat_id = %s",(chat_id,)) self._db_con.commit() ################################################################## ####################### Sessions ########################## - def Bot_SetUserID(self,chat_id,UserID): + def Bot_SetUserID(self,chat_id,UserID): #Set the userID a chat is logged in as self._cur.execute("SELECT 1 FROM Sessions WHERE chat_id = %s",(chat_id,)) if len(self._cur.fetchall()) == 0: self._cur.execute("INSERT INTO Sessions VALUES(%s,%s)",(chat_id,UserID)) @@ -477,7 +479,7 @@ def Bot_SetUserID(self,chat_id,UserID): self._cur.execute("UPDATE Sessions SET UserID = %s WHERE chat_id = %s",(UserID,chat_id)) self._db_con.commit() - def Bot_GetUserID(self,chat_id): + def Bot_GetUserID(self,chat_id): #Get the userId the chat is logged in as self._cur.execute("SELECT UserID FROM Sessions WHERE chat_id = %s",(chat_id,)) result = self._cur.fetchall() if len(result) == 0: @@ -487,19 +489,19 @@ def Bot_GetUserID(self,chat_id): ########################################################################## - def ResetDatabase(self): + def ResetDatabase(self): #Resets the tables self._cur.execute("SET FOREIGN_KEY_CHECKS = 0") #Otherwise dropping tables will raise errors. TABLES = ["User","Drone","Monitor","MonitorPermission","Flight","FlightWaypoints","NoFlyZone","DroneCredentials","InputStack","Sessions"] for item in TABLES: # Drops all tables self._cur.execute("DROP TABLE IF EXISTS {0}".format(item)) self._cur.execute("CREATE TABLE User(UserID INTEGER PRIMARY KEY AUTO_INCREMENT, Username TEXT,Password TEXT, PublicVisibleFlights INT, PermissionAdder INT , ZoneCreatorPermission INT, ZoneRemoverPermission INT,ZoneModifierPermission INT)") - self._cur.execute("CREATE TABLE Drone(DroneID INTEGER PRIMARY KEY AUTO_INCREMENT, UserID INT, DroneName TEXT, DroneType TEXT, DroneSpeed INT, DroneRange INT, DroneWeight REAL, FlightsFlown INT, LastCoords TEXT, LastBattery REAL)") + self._cur.execute("CREATE TABLE Drone(DroneID INTEGER PRIMARY KEY AUTO_INCREMENT, UserID INT, DroneName TEXT, DroneType TEXT, DroneSpeed INT, DroneRange INT, DroneWeight REAL, FlightsFlown INT, LastCoords TEXT, LastBattery REAL,FOREIGN KEY(UserID) REFERENCES User(UserID))") self._cur.execute("CREATE TABLE Monitor(MonitorID INTEGER PRIMARY KEY AUTO_INCREMENT, MonitorName TEXT, MonitorPassword TEXT)") - self._cur.execute("CREATE TABLE MonitorPermission(MonitorID INT ,UserID INT, LastAccessed TEXT, ExpiryDate TEXT,PRIMARY KEY(MonitorID,UserID),FOREIGN KEY(MonitorID) REFERENCES Monitor(MonitorID))") - self._cur.execute("CREATE TABLE Flight(FlightID INTEGER PRIMARY KEY AUTO_INCREMENT, DroneID INT, StartCoords TEXT, EndCoords TEXT, StartTime REAL, ETA REAL, EndTime REAL, Distance REAL,XOffset REAL , YOffset REAL , ZOffset REAL,Completed INT)") - self._cur.execute("CREATE TABLE FlightWaypoints(FlightID INT, WaypointNumber INT, Coords TEXT, ETA REAL, BlockTime INT ,PRIMARY KEY(FlightID,WaypointNumber))") - self._cur.execute("CREATE TABLE NoFlyZone(ZoneID INTEGER PRIMARY KEY AUTO_INCREMENT, StartCoord TEXT, EndCoord TEXT, Level INT, OwnerUserID INT)") + self._cur.execute("CREATE TABLE MonitorPermission(MonitorID INT ,UserID INT, LastAccessed TEXT, ExpiryDate TEXT,PRIMARY KEY(MonitorID,UserID),FOREIGN KEY(MonitorID) REFERENCES Monitor(MonitorID),FOREIGN KEY(UserID) REFERENCES User(UserID))") + self._cur.execute("CREATE TABLE Flight(FlightID INTEGER PRIMARY KEY AUTO_INCREMENT, DroneID INT, StartCoords TEXT, EndCoords TEXT, StartTime REAL, ETA REAL, EndTime REAL, Distance REAL,XOffset REAL , YOffset REAL , ZOffset REAL,Completed INT,FOREIGN KEY(DroneID) REFERENCES Drone(DroneID))") + self._cur.execute("CREATE TABLE FlightWaypoints(FlightID INT, WaypointNumber INT, Coords TEXT, ETA REAL, BlockTime INT ,PRIMARY KEY(FlightID,WaypointNumber),FOREIGN KEY(FlightID) REFERENCES Flight(FlightID))") + self._cur.execute("CREATE TABLE NoFlyZone(ZoneID INTEGER PRIMARY KEY AUTO_INCREMENT, StartCoord TEXT, EndCoord TEXT, Level INT, OwnerUserID INT,FOREIGN KEY(OwnerUserID) REFERENCES User(UserID))") self._cur.execute("CREATE TABLE DroneCredentials(DroneID INTEGER PRIMARY KEY AUTO_INCREMENT ,DronePassword TEXT)") self._cur.execute("CREATE TABLE InputStack(chat_id INT , stack_pos INT, value TEXT, PRIMARY KEY(chat_id,stack_pos))") @@ -509,28 +511,3 @@ def ResetDatabase(self): self._db_con.commit() - -#Pre Primary/Forigen key thing -##self._cur.execute("CREATE TABLE User(UserID INTEGER PRIMARY KEY AUTO_INCREMENT, Username TEXT,Password TEXT, PublicVisibleFlights INT, PermissionAdder INT , ZoneCreatorPermission INT, ZoneRemoverPermission INT,ZoneModifierPermission INT)") -##self._cur.execute("CREATE TABLE Drone(DroneID INTEGER PRIMARY KEY AUTO_INCREMENT, UserID INT, DroneName TEXT, DroneType TEXT, DroneSpeed INT, DroneRange INT, DroneWeight REAL, FlightsFlown INT, LastCoords TEXT, LastBattery REAL)") -##self._cur.execute("CREATE TABLE Monitor(MonitorID INTEGER PRIMARY KEY AUTO_INCREMENT, MonitorName TEXT, MonitorPassword TEXT)") -##self._cur.execute("CREATE TABLE MonitorPermission(MonitorID INT ,UserID INT, LastAccessed TEXT, ExpiryDate TEXT,PRIMARY KEY(MonitorID,UserID),FOREIGN KEY(MonitorID) REFERENCES Monitor(MonitorID))") -##self._cur.execute("CREATE TABLE Flight(FlightID INTEGER PRIMARY KEY AUTO_INCREMENT, DroneID INT, StartCoords TEXT, EndCoords TEXT, StartTime REAL, ETA REAL, EndTime REAL, Distance REAL,XOffset REAL , YOffset REAL , ZOffset REAL,Completed INT)") -##self._cur.execute("CREATE TABLE FlightWaypoints(FlightID INT, WaypointNumber INT, Coords TEXT, ETA REAL, BlockTime INT)") -##self._cur.execute("CREATE TABLE NoFlyZone(ZoneID INTEGER PRIMARY KEY AUTO_INCREMENT, StartCoord TEXT, EndCoord TEXT, Level INT, OwnerUserID INT)") -##self._cur.execute("CREATE TABLE DroneCredentials(DroneID INTEGER PRIMARY KEY AUTO_INCREMENT ,DronePassword TEXT)") -## -##self._cur.execute("CREATE TABLE InputStack(chat_id INT , stack_pos INT, value TEXT)") -##self._cur.execute("CREATE TABLE Sessions(chat_id INT PRIMARY KEY, UserID INT)") - - - -#Code of stuff with foriegn keys, not used as causes trouble and has no advantage -####self._cur.execute("CREATE TABLE User(UserID INTEGER PRIMARY KEY AUTO_INCREMENT, Username TEXT,Password TEXT, PublicVisibleFlights INT, AccountType TEXT)") -####self._cur.execute("CREATE TABLE Drone(DroneID INTEGER PRIMARY KEY AUTO_INCREMENT, UserID INT, DroneName TEXT, DroneType TEXT, DroneSpeed INT, DroneRange INT, DroneWeight REAL, FlightsFlown INT, LastCoords TEXT, LastBattery REAL,FOREIGN KEY(UserID) REFERENCES User(UserID))") -####self._cur.execute("CREATE TABLE Monitor(MonitorID INTEGER PRIMARY KEY AUTO_INCREMENT, MonitorName TEXT, MonitorPassword TEXT,AccountType TEXT)") -####self._cur.execute("CREATE TABLE MonitorPermission(MonitorID INT ,UserID INT, LastAccessed TEXT, ExpiryDate TEXT,PRIMARY KEY(MonitorID,UserID),FOREIGN KEY(MonitorID) REFERENCES Monitor(MonitorID),FOREIGN KEY(UserID) REFERENCES User(UserID))") -####self._cur.execute("CREATE TABLE Flight(FlightID INTEGER PRIMARY KEY AUTO_INCREMENT, DroneID INT, StartCoords TEXT, EndCoords TEXT, StartTime REAL, ETA REAL, EndTime REAL, Distance REAL,XOffset REAL , YOffset REAL , ZOffset REAL,Completed INT,FOREIGN KEY(DroneID) REFERENCES Drone(DroneID))") -####self._cur.execute("CREATE TABLE FlightWaypoints(FlightID INT, WaypointNumber INT, Coords TEXT, ETA REAL, BlockTime INT ,FOREIGN KEY(FlightID) REFERENCES Flight(FlightID))") -####self._cur.execute("CREATE TABLE NoFlyZone(ZoneID INTEGER PRIMARY KEY AUTO_INCREMENT, StartCoord TEXT, EndCoord TEXT, Level INT, OwnerUserID INT,FOREIGN KEY(OwnerUserID) REFERENCES User(UserID))") -####self._cur.execute("CREATE TABLE DroneCredentials(DroneID INTEGER PRIMARY KEY AUTO_INCREMENT ,DronePassword TEXT,FOREIGN KEY(DroneID) REFERENCES Drone(DroneID))") diff --git a/AATC_Drone.py b/AATC_Drone.py index 60e3890..8f747fa 100644 --- a/AATC_Drone.py +++ b/AATC_Drone.py @@ -95,7 +95,7 @@ def Exit(self): -class Flight: +class Flight: #Stores details about a flight def __init__(self,FlightID,DroneID,StartCoord,EndCoord,StartTime,ETA,Distance): self._FlightID = FlightID self._DroneID = DroneID @@ -126,7 +126,7 @@ def Get_ETA(self): def Get_Distance(self): return self._Distance -class Waypoint: +class Waypoint: #Stores details about a waypoint def __init__(self,FlightID,WaypointNumber,Coord,ETA): self._FlightID = FlightID self._WaypointNumber = WaypointNumber @@ -148,12 +148,12 @@ def Get_ETA(self): -def ConvertCoordString(CoordString): +def ConvertCoordString(CoordString): #Converts a string coordinate to a coordinate object CoordTuple = ast.literal_eval(CoordString) Coord = AATC_Coordinate.Coordinate(CoordTuple[0],CoordTuple[1],CoordTuple[2],0,0,0) return Coord -def GetFlightObject(Message,Data): +def GetFlightObject(Message,Data): #Generates the flight object Data = Data[0] # as Data = [(Stuff,stuff,even more stuff)] Columns = ast.literal_eval(Message) FlightIDIndex = Columns.index("FlightID") @@ -179,7 +179,7 @@ def GetFlightObject(Message,Data): FlightObj = Flight(FlightID,DroneID,StartCoord,EndCoord,StartTime,ETA,Distance) return FlightObj -def GetWaypointObjects(Message,Data): +def GetWaypointObjects(Message,Data): #Generates the waypoint objects WaypointList = [] Columns = ast.literal_eval(Message) @@ -207,10 +207,10 @@ def GetWaypointObjects(Message,Data): ## WaypointList.append(item[1]) return WaypointList -def MakeDroneInfo(DroneMessage,DroneData): +def MakeDroneInfo(DroneMessage,DroneData): return DroneInformation(DroneMessage,DroneData) -class DroneInformation: +class DroneInformation: #Stores information about the drone to be simulated def __init__(self,Message,DroneInfo): DroneInfo = DroneInfo[0] Message = ast.literal_eval(Message) @@ -225,7 +225,7 @@ def __init__(self,Message,DroneInfo): self._DroneType = ColumnValue["DroneType"] self._DroneSpeed = ColumnValue["DroneSpeed"] self._DroneRange = ColumnValue["DroneRange"] - self._DroneWeight = ColumnValue["DroneWeight"] + self._DroneWeight = ColumnValue["DroneWeight"] #Imports the drone information from the string def Get_DroneID(self): return self._DroneID @@ -245,7 +245,7 @@ def Get_DroneWeight(self): -def Connect(remote_ip,PORT): +def Connect(remote_ip,PORT): #Connects to the server try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) @@ -261,7 +261,7 @@ def Connect(remote_ip,PORT): sys.exit() -def CreateDroneInterface(IP = "127.0.0.1",Port = 8002): +def CreateDroneInterface(IP = "127.0.0.1",Port = 8002): #Creates a drone interface connection to the server soc = Connect(IP,Port) D = DroneInterface(soc) return D diff --git a/AATC_Drone_Logic.py b/AATC_Drone_Logic.py index b0d2b31..882e2c9 100644 --- a/AATC_Drone_Logic.py +++ b/AATC_Drone_Logic.py @@ -1,7 +1,7 @@ import AATC_Drone,threading,queue,time,AATC_GPIO,random, AATC_Config import AATC_Coordinate -class DroneLogicSystem: +class DroneLogicSystem: #deals with the communication with the server def __init__(self,DroneID,DronePassword,FlightQueue,StatusQueue,GPIO_Queue,Sleep_Time = 30): self._DroneID = DroneID self._DronePassword = DronePassword @@ -15,7 +15,7 @@ def Main(self): InFlight = False while not Exit: try: - self._D = AATC_Drone.CreateDroneInterface(IP = "127.0.0.1") + self._D = AATC_Drone.CreateDroneInterface(IP = "127.0.0.1") #Connect and log in LoginSucess,Message = self._D.Login(self._DroneID,self._DronePassword) if LoginSucess: @@ -107,7 +107,7 @@ def GetAllFlightInfo(D,FlightID): #Gets all drone flight information and pack -def SimulateMovement(Coord,VectorCoord,Sleep_Time = 0.1): +def SimulateMovement(Coord,VectorCoord,Sleep_Time = 0.1): #Simulates movement by waiting. 1/Sleep_Time = Speedup factor Coord = AATC_Coordinate.AddCoords(Coord,VectorCoord) time.sleep(Sleep_Time) return Coord @@ -121,7 +121,7 @@ def PutStatus(StatusQueue,Coords,Battery,MarkComplete = None,EmptyOverride = Fal StatusQueue.put(data) -def DecrementBattery(DroneInfo,CoordA,CoordB,Battery): +def DecrementBattery(DroneInfo,CoordA,CoordB,Battery): #Decrease battery based on range and distance travelled distance = AATC_Coordinate.DeltaCoordToMetres(CoordA,CoordB) decAmount = (distance/DroneInfo.Get_DroneRange())*100*(1+random.randint(-1,1)*0.1) * AATC_Config.DRONE_BATTERY_DRAIN_MULT Battery -= decAmount @@ -130,25 +130,25 @@ def DecrementBattery(DroneInfo,CoordA,CoordB,Battery): def DroneHardware(FlightQueue,StatusQueue): Battery = AATC_Config.DEFAULT_DRONE_BATTERY_VALUE Coords = AATC_Coordinate.Coordinate(*AATC_Config.DEFAULT_DRONE_COORDINATE) - xSize,ySize,zSize = AATC_Config.DEFAULT_DRONE_ATWAYPOINT_SIZES + xSize,ySize,zSize = AATC_Config.DEFAULT_DRONE_ATWAYPOINT_SIZES #Set default settings for simulation while True: time.sleep(1) - PutStatus(StatusQueue,Coords,Battery) + PutStatus(StatusQueue,Coords,Battery) #Updates status if not FlightQueue.empty(): data = FlightQueue.get() - DroneInfo,Flight,Waypoints = data[1][0],data[1][1],data[1][2] + DroneInfo,Flight,Waypoints = data[1][0],data[1][1],data[1][2] #Obtain flight information AllWaypoints = [Flight.Get_StartCoord()]+Waypoints+[Flight.Get_EndCoord()] - for number,point in enumerate(AllWaypoints): - while not AATC_Coordinate.AtWaypoint(Coords,point.Get_Coord(),xSize,ySize,zSize): + for number,point in enumerate(AllWaypoints): #loop through eachwaypoint + while not AATC_Coordinate.AtWaypoint(Coords,point.Get_Coord(),xSize,ySize,zSize): #Repeat until reached waypoint LastCoord = Coords.copy() VectorCoord = AATC_Coordinate.CalculateVector(Coords,point.Get_Coord(),DroneInfo.Get_DroneSpeed()) - Coords = SimulateMovement(Coords,VectorCoord) + Coords = SimulateMovement(Coords,VectorCoord) #Move drone Battery = DecrementBattery(DroneInfo,Coords,LastCoord,Battery) - PutStatus(StatusQueue,Coords,Battery) + PutStatus(StatusQueue,Coords,Battery) #Update status if number == 0: print("Reached Start Coordinate") @@ -209,7 +209,7 @@ def DroneHardware(FlightQueue,StatusQueue): -def Startup(DroneID,DronePassword): +def Startup(DroneID,DronePassword): ##Starts the program FlightQueue = queue.Queue() StatusQueue = queue.Queue() @@ -223,4 +223,4 @@ def Startup(DroneID,DronePassword): LogicThread = threading.Thread(target = droneLogicObject.Main) HardwareThread.start() - LogicThread.start() + LogicThread.start() #Starts the threads. diff --git a/AATC_GPIO.py b/AATC_GPIO.py index baafe12..0263ab5 100644 --- a/AATC_GPIO.py +++ b/AATC_GPIO.py @@ -31,7 +31,7 @@ def GPIO_Wait_Switch(pin,wait_time = 1, SWITCH_MODE= 1, Indicator_Pin = False): pass -def GPIO_Thread(Thread_Name,GPIO_Queue): +def GPIO_Thread(Thread_Name,GPIO_Queue): #Generic thread for executing functions Exit = False Function = BlankFunction FuncArgs = () @@ -60,7 +60,7 @@ def GPIO_Thread(Thread_Name,GPIO_Queue): -class Thread_Handle: +class Thread_Handle: #Acts as storage for thread name, pointer and communications queue def __init__(self,Thread_Name,ThreadPointer,Queue): self._Thread_Name = Thread_Name self._ThreadPointer = ThreadPointer @@ -79,7 +79,7 @@ def Get_Queue(self): -class Thread_Controller: +class Thread_Controller: #Handle usage of sub processes automatically using this object def __init__(self,Command_Queue,Name = ""): print("Creating Thread Controller",Name) self._Name ="TC"+ Name + " >" @@ -93,13 +93,13 @@ def Create_Thread(self,Thread_Name,TargetCommand = GPIO_Thread,TargetArgs = (),P if Process: Thread_Queue = multiprocessing.Queue() threadPointer = multiprocessing.Process(target = TargetCommand,args = (Thread_Name,Thread_Queue)+TargetArgs) - else: + else: #Create the secondary thread/process depending on selected type Thread_Queue = queue.Queue() threadPointer = threading.Thread(target = TargetCommand,args = (Thread_Name,Thread_Queue)+TargetArgs) self._Threads[Thread_Name] = Thread_Handle(Thread_Name,threadPointer,Thread_Queue) threadPointer.start() - def Close_Thread(self,Thread_Name): + def Close_Thread(self,Thread_Name): #Close a thread of this name ClosingThreadHandle = self._Threads.pop(Thread_Name) Queue = ClosingThreadHandle.Get_Queue() Queue.put(("Exit",())) @@ -107,7 +107,7 @@ def Close_Thread(self,Thread_Name): return ClosingThreadHandle #Returns Thread_Handle of thread - def PassData(self,Thread_Name,Data): + def PassData(self,Thread_Name,Data): #Pass data to the subprocess via the queue Queue = self._Threads[Thread_Name].Get_Queue() Queue.put(Data) @@ -115,10 +115,10 @@ def Main(self): Exit = False while not Exit: try: - Request = self._Command_Queue.get() #(Thread_Name/Controller command,"Command",Args) + Request = self._Command_Queue.get() #(Thread_Name/Controller,"Command",Args) self._Command_Queue.task_done() - if Request[0] == "Controller": + if Request[0] == "Controller": #If this is a command to be processed by the controller Command,Args = Request[1],Request[2] if Command == "Create_Thread": #In total form ("Controller","Create_Thread",(ThreadName,[TargetFunction,TargetArguments])) self.Create_Thread(*Args) @@ -128,12 +128,12 @@ def Main(self): self.Close_Thread(*Args) elif Command == "Exit": #Shutdown everything self.Reset(*Args) - self._Exit = True + Exit = True elif Command == "Reset": #Shutdown all threads, not controller self.Reset(*Args) else: - self.PassData(Request[0],Request[1:]) + self.PassData(Request[0],Request[1:]) #Pass the data to the subprocess excluding the Target process name. except Exception as e: @@ -141,7 +141,7 @@ def Main(self): print(self._Name,"Shutting down") - def Reset(self,Wait_Join = False): + def Reset(self,Wait_Join = False): #Close all processes , optionally waiting for them to join. print(self._Name,"Reseting GPIO Threading Controller...") Thread_Names = list(self._Threads.keys()) ThreadHandles = [] @@ -164,7 +164,7 @@ def Reset(self,Wait_Join = False): -def Create_Controller(process = False, Name = ""): +def Create_Controller(process = False, Name = ""): #Create a thread controller and return the command queue if process: q = multiprocessing.Queue() else: @@ -203,11 +203,11 @@ def Command(Thread_Name,arg1,arg2...): # Example functions -def BlankFunction(Thread_Name): +def BlankFunction(Thread_Name): #Just sleep repeatedly time.sleep(0.2) return True -def DisplayName(Thread_Name,Sleep_Time): +def DisplayName(Thread_Name,Sleep_Time): #Repeatedly display the threads name print("Message from Thread",Thread_Name,". Sleeping for time",Sleep_Time) time.sleep(Sleep_Time) return True @@ -228,7 +228,7 @@ def BlinkTest(Thread_Name,pin,frequency,cycles=1,repeat = False): #prints demon ##################General GPIO functions -def Blink(Thread_Name,pin,frequency,cycles = 1,repeat= False): +def Blink(Thread_Name,pin,frequency,cycles = 1,repeat= False): #Blinks an LED pauseTime = 1/(frequency*2) GPIO.setmode(GPIO.BOARD) GPIO.setup(pin,GPIO.OUT) @@ -252,7 +252,7 @@ def Blink(Thread_Name,pin,frequency,cycles = 1,repeat= False): -def Pattern(Thread_Name, Pattern ,ReferenceTime=1,repeat = True): +def Pattern(Thread_Name, Pattern ,ReferenceTime=1,repeat = True): #Executes a pattern of LED events pins = set() for item in Pattern: pins.add(item[0]) @@ -278,7 +278,7 @@ def Pattern(Thread_Name, Pattern ,ReferenceTime=1,repeat = True): GPIO.cleanup(*pins) return repeat -def PatternGenerator(PinSet=[11,13,21],StateSet = [0,1] ,Length = 50 ,MinTime = 0.1 ,MaxTime = 1 , RoundingTime = 2): +def PatternGenerator(PinSet=[11,13,21],StateSet = [0,1] ,Length = 50 ,MinTime = 0.1 ,MaxTime = 1 , RoundingTime = 2): #Generate a random pattern Pattern = [] for x in range(Length): Pattern.append(( diff --git a/AATC_Monitor.py b/AATC_Monitor.py index 0551591..4274183 100644 --- a/AATC_Monitor.py +++ b/AATC_Monitor.py @@ -129,7 +129,7 @@ def Exit(self): -def Connect(remote_ip,PORT): +def Connect(remote_ip,PORT): #Connect to server try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) @@ -146,7 +146,7 @@ def Connect(remote_ip,PORT): -def CreateMonitorInterface(IP = "192.168.0.19",Port = 8001): +def CreateMonitorInterface(IP = "192.168.0.19",Port = 8001): #Create interface to server soc = Connect(IP,Port) M = MonitorInterface(soc) return M diff --git a/AATC_Monitor_Viewer.py b/AATC_Monitor_Viewer.py index b61b754..85e62a2 100644 --- a/AATC_Monitor_Viewer.py +++ b/AATC_Monitor_Viewer.py @@ -60,8 +60,6 @@ def GetZoom(self): def SetZoom(self,Zoom): self._CameraZoom = Zoom - def GetCameraCoords(self): - return self._CameraCoord def IncrementCameraCoordX(self,Amount): self._CameraCoord.Set_X( Amount+self._CameraCoord.Get_X()) @@ -89,7 +87,7 @@ def Get_Coord(self): return self._CameraCoord def Draw(self): - CameraEndX= self._CameraCoord.Get_X() + self._CameraCoord.Get_XSize() #Moved to new variablt to reduce recalculation + CameraEndX= self._CameraCoord.Get_X() + self._CameraCoord.Get_XSize() #Moved to new variablt to reduce recalculation/ function call overhead CameraEndY = self._CameraCoord.Get_Y() + self._CameraCoord.Get_YSize() CameraX = self._CameraCoord.Get_X() CameraY = self._CameraCoord.Get_Y() @@ -100,7 +98,7 @@ def Draw(self): Object = DrawObject["Object"] Object_Coords = Object.Get_Coords() x = Object_Coords.Get_X() - y = Object_Coords.Get_Y() + y = Object_Coords.Get_Y() #Extract variables first to reduce overhead of function calls later xSize = Object_Coords.Get_XSize() ySize = Object_Coords.Get_YSize() if ((x < CameraEndX) and ((x+xSize) > CameraX)) and \ @@ -115,8 +113,8 @@ def Draw(self): height = MaxLimit(height,self._ypixel) font_size = int(100*width/self._xpixel) # Would benifit from being cythonised image = Object.Make_Image(width,height) - self._gameDisplay.blit(image,(PosX,PosY)) - if font_size > 5: + self._gameDisplay.blit(image,(PosX,PosY)) #Draw object to screen + if font_size > 5: #If fond it large enoguht to be useful then display font = (None, font_size) self._gameDisplay.blit(Object.Make_Text(font) ,(PosX+width,PosY)) @@ -125,43 +123,30 @@ def Draw(self): -class Monitor_Sprite(pygame.sprite.Sprite): +class Monitor_Sprite(pygame.sprite.Sprite): #Stores details about a sprite def __init__(self,CoordObject,Type = "",Text = "",Colour = (255,255,255)): self._Coords = CoordObject self._Type = Type self._Text = Text self._Colour = Colour self._image = pygame.Surface((1,1)) - #self.Make_Image(2,2) # No longer needed, is similar to that above. def Make_Image(self,width,height): if self._image.get_size() != (width,height): #If new image does not match with previous self._image = GetImage(width,height,self._Colour) return self._image -## self.image = pygame.Surface((width,height)).convert() -## self.image.fill(self.Colour) - def Make_Text(self,font): - text = str((self._Coords.Get_X(),self._Coords.Get_Y(),self._Coords.Get_Z())) +" " +self._Text + " "+ self._Type + + def Make_Text(self,font): #Generate image with text in + text = str(self._Coords) +" " +self._Text + " "+ self._Type return GetText(text,font,False,self._Colour) def Get_Coords(self): return self._Coords -## -## def Make_CoordsText(self,font): -## self.CoordsText = GetText(str((self.Coords.x,self.Coords.y,self.Coords.z)),font,False,self.Colour) -## #self.CoordsText = font.render(str((self.Coords.x,self.Coords.y,self.Coords.z)),False,self.Colour) -## -## def Make_Text(self,font): -## self.DrawnText = GetText(self.Text,font,False,self.Colour) -## #self.DrawnText = font.render(self.Text,False,self.Colour) -## -## def Make_Type(self,font): -## self.DrawnType = GetText(self.Type,font,False,self.Colour) -## #self.DrawnType = font.render(self.Type,False,self.Colour) - - -def MakeDroneSprites(Message,RawDroneList): + + + +def MakeDroneSprites(Message,RawDroneList): #Generate drone sprite objects DroneList = [] Columns = ast.literal_eval(Message) CoordIndex = Columns.index("LastCoords") @@ -169,7 +154,7 @@ def MakeDroneSprites(Message,RawDroneList): UserIDIndex = Columns.index("UserID") DroneNameIndex = Columns.index("DroneName") BatteryIndex = Columns.index("LastBattery") - for Drone in RawDroneList: + for Drone in RawDroneList: #For each message extract information and add to sprite LastCoords = ast.literal_eval(Drone[CoordIndex]) Coord = AATC_Coordinate.Coordinate(LastCoords[0],LastCoords[1],LastCoords[2],0.001,0.001,0.00001) Text = "D:"+str(Drone[DroneIDIndex]) +" U:"+str(Drone[UserIDIndex]) +" N:"+str(Drone[DroneNameIndex])+" B:"+str(Drone[BatteryIndex]) @@ -178,7 +163,7 @@ def MakeDroneSprites(Message,RawDroneList): DroneList.append(Sprite) return DroneList -def MakeFlightSprites(Message,RawFlightList): +def MakeFlightSprites(Message,RawFlightList):#Generate flight sprite objects FlightList = [] Columns = ast.literal_eval(Message) FlightIDIndex = Columns.index("FlightID") @@ -189,7 +174,7 @@ def MakeFlightSprites(Message,RawFlightList): ETAIndex = Columns.index("ETA") EndTimeIndex = Columns.index("EndTime") DistanceIndex = Columns.index("Distance") - for Flight in RawFlightList: + for Flight in RawFlightList: #For each message extract information and add to sprite. Start and end coordinates as different sprites #Start Sprite Coords = ast.literal_eval(Flight[StartCoordsIndex]) Coord = AATC_Coordinate.Coordinate(Coords[0],Coords[1],Coords[2],0.001,0.001,0.0001) @@ -206,13 +191,13 @@ def MakeFlightSprites(Message,RawFlightList): return FlightList -def MakeWaypointSprites(Message,RawWaypointList): +def MakeWaypointSprites(Message,RawWaypointList): #Generate flight sprite objects WaypointList = [] Columns = ast.literal_eval(Message) CoordIndex = Columns.index("Coords") WaypointNumberIndex = Columns.index("WaypointNumber") FlightIDIndex = Columns.index("FlightID") - for Waypoint in RawWaypointList: + for Waypoint in RawWaypointList: #For each message extract information and add to sprite Coords = ast.literal_eval(Waypoint[CoordIndex]) Coord = AATC_Coordinate.Coordinate(Coords[0],Coords[1],Coords[2],0.001,0.001,0.00001) Text = "F:"+ str(Waypoint[FlightIDIndex]) +" W:"+ str(Waypoint[WaypointNumberIndex]) @@ -220,14 +205,14 @@ def MakeWaypointSprites(Message,RawWaypointList): WaypointList.append(Monitor_Sprite(Coord,"Waypoint",Text,Colour)) return WaypointList -def MakeZoneSprites(Message,RawZoneList): +def MakeZoneSprites(Message,RawZoneList): #Generate zone sprite objects ZoneList = [] Columns = ast.literal_eval(Message) StartCoordIndex = Columns.index("StartCoord") EndCoordIndex = Columns.index("EndCoord") LevelIndex = Columns.index("Level") ZoneIDIndex = Columns.index("ZoneID") - for Zone in RawZoneList: + for Zone in RawZoneList: #For each message extract information and add to sprite StartCoords = ast.literal_eval(Zone[StartCoordIndex]) EndCoords = ast.literal_eval(Zone[EndCoordIndex]) Coord = AATC_Coordinate.Coordinate(StartCoords[0],StartCoords[1],StartCoords[2],EndCoords[0]-StartCoords[0],EndCoords[1]-StartCoords[1],EndCoords[2]-StartCoords[2]) @@ -236,9 +221,13 @@ def MakeZoneSprites(Message,RawZoneList): ZoneList.append(Monitor_Sprite(Coord,"NoFlyZone",Text,Colour)) return ZoneList -def MakeSprites(M): - print("Refreshing data") +def MakeSprites(M): #Obtain information about each entity and form sprites + print("Refreshing data") SpriteList = [] + Sucess,Message,NoFlyZones = M.GetNoFlyZones() #Lowest priority object + if Sucess: #If request did not fail + SpriteList += MakeZoneSprites(Message,NoFlyZones) + Sucess,Message,DronesAll = M.GetDronesAll() if Sucess: SpriteList += MakeDroneSprites(Message,DronesAll) @@ -259,14 +248,10 @@ def MakeSprites(M): if Sucess: SpriteList += MakeWaypointSprites(Message,WaypointsAll) - Sucess,Message,WaypointsMonitor = M.GetMonitorFlightWaypoints() + Sucess,Message,WaypointsMonitor = M.GetMonitorFlightWaypoints() #Highest priority object if Sucess: SpriteList += MakeWaypointSprites(Message,WaypointsMonitor) - Sucess,Message,NoFlyZones = M.GetNoFlyZones() - if Sucess: - SpriteList += MakeZoneSprites(Message,NoFlyZones) - #Sprites are created in this function to simplify code in case of an error(False) print("Refreshed data. Sprites :",len(SpriteList)) return SpriteList #All sprites which were sucessfully created @@ -288,90 +273,90 @@ def GetTimeWarp(self): +if __name__ == "__main__": + xpixel = 800 + ypixel = 550 + Refresh_Delay = 60 + clock = pygame.time.Clock() + pressed = pygame.key.get_pressed + Exit = "N" + + + + while Exit != "Y": + try: + M = AATC_Monitor.CreateMonitorInterface(IP = "127.0.0.1",Port = 8001) + print(M.Login("Zini","")) + #Sucess,Message,Data = M.GetCoordDetails() + #MinCoords,MaxCoords,StepCoords = Data[0],Data[1],Data[2] + MinCoords,MaxCoords = AATC_Coordinate.Coordinate(0,0,0),AATC_Coordinate.Coordinate(1,1,50) + MonCamera = Camera(xpixel,ypixel,MinCoords,MaxCoords) + while True: + MonCamera.ResetDrawObject() + Sprites = MakeSprites(M) + #font = (None, 30) + for sprite in Sprites: + MonCamera.AddDrawObject(sprite,False) + + + TimeWarp = TimeWarper() + Last_Refresh_Time = time.time() + refresh = False + while not refresh: + MonCamera.CameraWipe() + TimeWarpFactor = TimeWarp.GetTimeWarp() #Time warp to ensure constant movement speed + + if time.time() >= (Last_Refresh_Time + Refresh_Delay): #Check if time to refresh + refresh = True + for event in pygame.event.get(): + if event.type == pygame.QUIT: #Quit + print("Monitor exit was called") + pygame.quit() + sys.exit() + + elif event.type == pygame.MOUSEBUTTONDOWN: + print("Camera details") #Mainly for debugging + print(MonCamera.GetZoom()) + print(MonCamera.Get_Coord().Get_X()) + print(MonCamera.Get_Coord().Get_Y()) + print(MonCamera.Get_Coord().Get_XSize()) + print(MonCamera.Get_Coord().Get_YSize()) + print(len(MonCamera.Get_DrawObjects())) + print("FPS:"+str(clock.get_fps())) +## elif event.type == pygame.KEYDOWN: +## pass + + + CameraCoord = MonCamera.Get_Coord() + if pressed()[pygame.K_w]: #Shift camera + MonCamera.IncrementCameraCoordY(-0.01*CameraCoord.Get_YSize()*TimeWarpFactor) #Move ? axis by 1/100 of the axis size by time warp factor + if pressed()[pygame.K_s]: + MonCamera.IncrementCameraCoordY(0.01*CameraCoord.Get_YSize()*TimeWarpFactor) + if pressed()[pygame.K_a]: + MonCamera.IncrementCameraCoordX(-0.01*CameraCoord.Get_XSize()*TimeWarpFactor) + if pressed()[pygame.K_d]: + MonCamera.IncrementCameraCoordX(0.01*CameraCoord.Get_XSize()*TimeWarpFactor) + + if pressed()[pygame.K_q]:#Zoom out + MonCamera.SetZoom(MonCamera.GetZoom()*0.99**TimeWarpFactor) + if pressed()[pygame.K_e]:#Zoom in + MonCamera.SetZoom(MonCamera.GetZoom()*1.01**TimeWarpFactor) + + if pressed()[pygame.K_SPACE]: + refresh = True -xpixel = 800 -ypixel = 550 -Refresh_Delay = 60 -clock = pygame.time.Clock() -pressed = pygame.key.get_pressed -Exit = "N" - - - -while Exit != "Y": - try: - M = AATC_Monitor.CreateMonitorInterface(IP = "127.0.0.1",Port = 8001) - print(M.Login("Zini","")) - #Sucess,Message,Data = M.GetCoordDetails() - #MinCoords,MaxCoords,StepCoords = Data[0],Data[1],Data[2] - MinCoords,MaxCoords = AATC_Coordinate.Coordinate(0,0,0),AATC_Coordinate.Coordinate(1,1,50) - MonCamera = Camera(xpixel,ypixel,MinCoords,MaxCoords) - while True: - MonCamera.ResetDrawObject() - Sprites = MakeSprites(M) - #font = (None, 30) - for sprite in Sprites: - MonCamera.AddDrawObject(sprite,False) - - - TimeWarp = TimeWarper() - Last_Refresh_Time = time.time() - refresh = False - while not refresh: - MonCamera.CameraWipe() - TimeWarpFactor = TimeWarp.GetTimeWarp() - - if time.time() >= (Last_Refresh_Time + Refresh_Delay): - refresh = True - for event in pygame.event.get(): - if event.type == pygame.QUIT: - print("Monitor exit was called") - pygame.quit() - sys.exit() - elif event.type == pygame.MOUSEBUTTONDOWN: - print("Camera details") #Mainly for debugging - print(MonCamera.GetZoom()) - print(MonCamera.Get_Coord().Get_X()) - print(MonCamera.Get_Coord().Get_Y()) - print(MonCamera.Get_Coord().Get_XSize()) - print(MonCamera.Get_Coord().Get_YSize()) - print(len(MonCamera.Get_DrawObjects())) - print("FPS:"+str(clock.get_fps())) - elif event.type == pygame.KEYDOWN: - pass - - - CameraCoord = MonCamera.Get_Coord() - if pressed()[pygame.K_w]: #Shift camera - MonCamera.IncrementCameraCoordY(-0.01*CameraCoord.Get_XSize()*TimeWarpFactor) #/ as Greater zoom means need more fidelety - if pressed()[pygame.K_s]: - MonCamera.IncrementCameraCoordY(0.01*CameraCoord.Get_XSize()*TimeWarpFactor) - if pressed()[pygame.K_a]: - MonCamera.IncrementCameraCoordX(-0.01*CameraCoord.Get_XSize()*TimeWarpFactor) - if pressed()[pygame.K_d]: - MonCamera.IncrementCameraCoordX(0.01*CameraCoord.Get_XSize()*TimeWarpFactor) - - if pressed()[pygame.K_q]:#Zoom out - MonCamera.SetZoom(MonCamera.GetZoom()*0.99**TimeWarpFactor) - if pressed()[pygame.K_e]:#Zoom in - MonCamera.SetZoom(MonCamera.GetZoom()*1.01**TimeWarpFactor) - - if pressed()[pygame.K_SPACE]:#Zoom in - refresh = True - + MonCamera.UpdateCameraSize() + MonCamera.Draw() #Draw images + pygame.display.flip() #Update display + clock.tick(60) #Keep to 60 fps - MonCamera.UpdateCameraSize() - MonCamera.Draw() - pygame.display.flip() - clock.tick(60) - - - except Exception as e: - print(e) - print("An error occured, restarting main loop") - Exit = input("Exit? Y/N").upper() + + except Exception as e: + print(e) + print("An error occured, restarting main loop") + Exit = input("Exit? Y/N").upper() diff --git a/AATC_NoFlyZoneGrapher.py b/AATC_NoFlyZoneGrapher.py index 155a60a..f24b019 100644 --- a/AATC_NoFlyZoneGrapher.py +++ b/AATC_NoFlyZoneGrapher.py @@ -1,6 +1,6 @@ import AATC_DB,AATC_AStar,ast,time,AATC_Config,AATC_Coordinate -def NoFlyZoneGrapher_Launch(Thread_Name,Thread_Queue,Interval = 36000): +def NoFlyZoneGrapher_Launch(Thread_Name,Thread_Queue,Interval = 36000): #Launch no fly zone grapher NFZG = NoFlyZoneGrapher(Thread_Name,Thread_Queue,Interval) NFZG.Main_Loop() @@ -29,7 +29,7 @@ def __init__(self,Thread_Name,Thread_Queue,Interval = 36000): - def Main_Loop(self): + def Main_Loop(self): #Repeatedly loop until told to exit. self._Exit = False while not self._Exit: try: @@ -55,10 +55,10 @@ def Force_Write(self): #Cycles through all slots and writes current state. for Slot in range(len(graph.GetFolderNames())): self.Make_Values(NoFlyZoneData,ABSlot = Slot) - def Mod(self,Coords): + def Mod(self,Coords): #Find modulus tuple of coordinate return int(Coords.Get_X()//self._xSize),int(Coords.Get_Y()//self._ySize),int(Coords.Get_Z()//self._zSize) - def GetNoFlyZones(self): + def GetNoFlyZones(self): #Extract the details of each no fly zone into a dictionary for ease of use _,Columns,Data = self._DB.GetNoFlyZones() Columns = ast.literal_eval(Columns) @@ -80,7 +80,7 @@ def GetNoFlyZones(self): return ProcessedData - def Make_Values(self,NoFlyZoneData,ABSlot = 1): + def Make_Values(self,NoFlyZoneData,ABSlot = 1): #Write values to the graph on disk graph = AATC_AStar.DynoGraph(ABSlot = ABSlot) graph.ImportGraph() Values = {} @@ -96,24 +96,24 @@ def Make_Values(self,NoFlyZoneData,ABSlot = 1): NodeID = graph.Direct_NodeID(x,y,z) #Gets NodeID for that area if NodeID in Values: - v = max([Zone["Level"],Values[NodeID]]) + v = max([Zone["Level"],Values[NodeID]]) #Update node to have the maximum cost of all no fly zones for that location. else: - v = Zone["Level"] + v = Zone["Level"] #If the node has not yet been modified it will not yet be in the Values dictionary. Values[NodeID] = v #This gets the maximum cost for that node -## ######MODIFY TO BE OK -## graph.Node_Cache = {} #Reduces memory usage. + + graph.FlushGraph() #Reduces memory usage by removing the Node_Caches print("[NoFlyZoneGrapher] Length of Values:",len(Values)) - for NodeID in graph.All_NodeIDs(): #CHECK THIS. Only using those involved with a new no fly zone may cause issues if a no fly zone was removed. Maybe should be set to all node IDs. + for NodeID in graph.All_NodeIDs(): node = graph.GetNode(NodeID) - if node.Get_NodeID() in Values: + if node.Get_NodeID() in Values: #Alter cost of node. node.Set_Cost( Values[node.Get_NodeID()]) Values.pop(node.Get_NodeID())# Reduce memory usage by evicting Node values which have been added already else: - node.Set_Cost(1) + node.Set_Cost(1) #Reset cost to 1 if not covered by no fly zone if NodeID % AATC_Config.NOFLYZONEGRAPHER_FLUSH_GRAPH_NUMBER == 0: #Every N nodes the program will save the current nodes and remove them from memory try: graph.SaveNodes([graph.CurrentFolderName()]) #Ensures any loaded nodes are saved. As all nodes in a block are loaded , entire blocks are saved thus no nodes are lost. diff --git a/AATC_Server_002.py b/AATC_Server_002.py index aac6c27..ca0f616 100644 --- a/AATC_Server_002.py +++ b/AATC_Server_002.py @@ -1,6 +1,6 @@ -import codecs,ast,socket,recvall,os,math,random,time,pickle -import AATC_AStar,AATC_DB, AATC_Crypto,AATC_Config,AATC_Weather -from AATC_Coordinate import * +import codecs,ast,socket,recvall,random,time +import AATC_AStar,AATC_DB, AATC_Crypto,AATC_Config,AATC_Weather,AATC_Coordinate +##from AATC_Coordinate import * def GetTime(): return int(time.time()) @@ -25,13 +25,14 @@ class ClientConnection: """ def __init__(self,Thread_Name,Thread_Queue,Connection): self._DB = AATC_DB.DBConnection() + self.Create_Commands() self._Thread_Name = Thread_Name self._Thread_Queue = Thread_Queue self._con = Connection self._Crypto = AATC_Crypto.Crypter(self._con, mode = "SERVER" ) self._ClientID = -1 #Used to identify if has logged in yet - def Connection_Loop(self): + def Connection_Loop(self): #Loop until exit. Checks and executes commands each loop. """ Keeps looping in request for Client, Recived data in format (CommandString,(Arg1,Arg2...)) @@ -68,9 +69,9 @@ def Connection_Loop(self): self._con.close() print("Process is exiting") - def Send(self,data): + def Send(self,data): #Encrypt and send self._con.sendall(self._Crypto.Encrypt(codecs.encode(str(data)))) - def Recv(self): + def Recv(self): #Receive and decrypt try: data = self._Crypto.Decrypt(recvall.recvall(self._con)) data = ast.literal_eval(codecs.decode(data)) @@ -78,105 +79,74 @@ def Recv(self): except Exception as e: return ("",())#Never references a command - def Exit(self,Arguments = None): + def Exit(self,Arguments = None): #Exit the program #self._DB.db_con.close() + self._ExitLoop = True print("Process for ",self._Thread_Name,":",self._ClientID," is exiting..") return True,"Server process is exiting. Ok to disconnect",[] -class UserConnection(ClientConnection): - - def ProcessCommand(self,Command,Arguments): - if self._ClientID == -1: - if Command == "Login": - Sucess,Message,Data = self.Login(Arguments) - elif Command == "AddUser": # If adding a new user, one must create it first, then log in seperatly - Sucess,Message,Data = self.AddUser(Arguments) - elif Command == "Exit": - Sucess,Message,Data = self.Exit(Arguments) - Exit = True - else: - Sucess,Message,Data = False,"Command does not exist",[] - - else: - if Command == "GetNoFlyZones": - Sucess,Message,Data = self.GetNoFlyZones(Arguments) - elif Command == "AddNoFlyZone": - Sucess,Message,Data = self.AddNoFlyZone(Arguments) - elif Command == "RemoveNoFlyZone": - Sucess,Message,Data = self.RemoveNoFlyZone(Arguments) - elif Command == "ModifyNoFlyZoneLevel": - Sucess,Message,Data = self.ModifyNoFlyZoneLevel(Arguments) - - elif Command == "AddDrone": - Sucess,Message,Data = self.AddDrone(Arguments) - elif Command == "RemoveDrone": - Sucess,Message,Data = self.RemoveDrone(Arguments) - elif Command == "GetDroneID": - Sucess,Message,Data = self.GetDroneID(Arguments) - elif Command == "GetDroneCredentials": - Sucess,Message,Data = self.GetDroneCredentials(Arguments) - elif Command == "SetDroneCredentials": - Sucess,Message,Data = self.SetDroneCredentials(Arguments) - elif Command == "CheckDroneOwnership": - Sucess,Message,Data = self.CheckDroneOwnership(Arguments) - elif Command == "GetDroneInfo": - Sucess,Message,Data = self.GetDroneInfo(Arguments) - elif Command == "GetDronesUser": - Sucess,Message,Data = self.GetDronesUser(Arguments) - elif Command == "GetDronesAll": - Sucess,Message,Data = self.GetDronesAll(Arguments) - - elif Command == "Login": #Can then change UserID without restarting, UserID is changed as well as components on client side - Sucess,Message,Data = self.Login(Arguments) - elif Command == "GetUserID": - Sucess,Message,Data = self.GetUserID(Arguments) - elif Command == "GetUsername": - Sucess,Message,Data = self.GetUsername(Arguments) - elif Command == "SetFlightVisibility": - Sucess,Message,Data = self.SetFlightVisibility(Arguments) - elif Command == "SetAccountType": - Sucess,Message,Data = self.SetAccountType(Arguments) - elif Command == "UserChangePassword": - Sucess,Message,Data = self.UserChangePassword(Arguments) - - elif Command == "GetFlightsUser": - Sucess,Message,Data = self.GetFlightsUser(Arguments) - elif Command == "GetFlightsAll": - Sucess,Message,Data = self.GetFlightsAll(Arguments) - elif Command == "AddFlight": - Sucess,Message,Data = self.AddFlight(Arguments) - elif Command == "RemoveFlight": - Sucess,Message,Data = self.RemoveFlight(Arguments) +class UserConnection(ClientConnection): #Used to interface with the user clients. + def Create_Commands(self): + self._Non_Authenticated_Commands = { + "Login":self.Login, + "AddUser":self.AddUser, + "Exit":self.Exit } + self._Authenticated_Commands = { + "GetNoFlyZones":self.GetNoFlyZones, + "AddNoFlyZone":self.AddNoFlyZone, + "RemoveNoFlyZone":self.RemoveNoFlyZone, + "ModifyNoFlyZoneLevel":self.ModifyNoFlyZoneLevel, + + "AddDrone":self.AddDrone, + "RemoveDrone":self.RemoveDrone, + "GetDroneID":self.GetDroneID, + "GetDroneCredentials":self.GetDroneCredentials, + "SetDroneCredentials":self.SetDroneCredentials, + "CheckDroneOwnership":self.CheckDroneOwnership, + "GetDroneInfo":self.GetDroneInfo, + "GetDronesUser":self.GetDronesUser, + "GetDronesAll":self.GetDronesAll, + + "Login":self.Login, + "GetUserID":self.GetUserID, + "GetUsername":self.GetUsername, + "SetFlightVisibility":self.SetFlightVisibility, + "SetAccountType":self.SetAccountType, + "UserChangePassword":self.UserChangePassword, + + + "GetFlightsUser":self.GetFlightsUser, + "GetFlightsAll":self.GetFlightsAll, + "AddFlight":self.AddFlight, + "RemoveFlight":self.RemoveFlight, - elif Command == "GetFlightWaypointsUser": - Sucess,Message,Data = self.GetFlightWaypointsUser(Arguments) - elif Command == "GetFlightWaypointsAll": - Sucess,Message,Data = self.GetFlightWaypointsAll(Arguments) + "GetFlightWaypointsUser":self.GetFlightWaypointsUser, + "GetFlightWaypointsAll":self.GetFlightWaypointsAll, - elif Command == "GetMonitorID": - Sucess,Message,Data = self.GetMonitorID(Arguments) - elif Command == "GetMonitorName": - Sucess,Message,Data = self.GetMonitorName(Arguments) + "GetMonitorID":self.GetMonitorID, + "GetMonitorName":self.GetMonitorName, - elif Command == "AddMonitorPermission": - Sucess,Message,Data = self.AddMonitorPermission(Arguments) - elif Command == "RemoveMonitorPermission": - Sucess,Message,Data = self.RemoveMonitorPermission(Arguments) - elif Command == "ModifyMonitorPermissionDate": - Sucess,Message,Data = self.ModifyMonitorPermissionDate(Arguments) - elif Command == "GetMonitorPermissionUser": - Sucess,Message,Data = self.GetMonitorPermissionUser(Arguments) - - elif Command == "Exit": - Sucess,Message,Data = self.Exit(Arguments) - self._ExitLoop = True - #Else if command doesnt exist send back Failure - else: - Sucess,Message,Data = False,"Command does not exist",[] - print(self._Thread_Name,self._ClientID," tried to use unregistered command") + "AddMonitorPermission":self.AddMonitorPermission, + "RemoveMonitorPermission":self.RemoveMonitorPermission, + "ModifyMonitorPermissionData":self.ModifyMonitorPermissionDate, + "GetMonitorPermissionUser":self.GetMonitorPermissionUser, + + "Exit":self.Exit } + + def ProcessCommand(self,Command,Arguments): #Executes a command with arguments depending on login state + if self._ClientID == -1: + Function = self._Non_Authenticated_Commands.get(Command) + else: + Function = self._Authenticated_Commands.get(Command) + if Function != None: + Sucess,Message,Data = Function(Arguments) + else: + Sucess,Message,Data = False,"Command does not exist",[] + print(self._Thread_Name,self._ClientID," tried to use unregistered command") return Sucess,Message,Data + def Login(self,Arguments): Username,Password = Arguments[0],Arguments[1] @@ -301,7 +271,7 @@ def GetFlightsAll(self,Arguments = None): Sucess,Message,Data = self._DB.GetFlightsAll() return Sucess,Message,Data - def AddFlight(self,Arguments): + def AddFlight(self,Arguments): #Finds the path for a flight and adds it to the database DroneID,HighPoints,StartTime = Arguments[0],Arguments[1],Arguments[2] if StartTime < GetTime(): if StartTime <= 2678400: #Allow starting of flights up to 1 month ahead. No need to enter very large numbers for no reason. @@ -372,7 +342,7 @@ def AddFlight(self,Arguments): TotalDistance = 0 for x in range(len(CoordList)): if x != 0: #If not first Coord add the difference in distance to time etc - Distance = DeltaCoordToMetres(CoordList[x]["Coords"],CoordList[x-1]["Coords"]) #Gets distance in metres + Distance = AATC_Coordinate.DeltaCoordToMetres(CoordList[x]["Coords"],CoordList[x-1]["Coords"]) #Gets distance in metres TotalDistance += Distance if AATC_Config.ENABLE_FINE_SPEED_ESTIMATION: @@ -412,13 +382,14 @@ def AddFlight(self,Arguments): - #Stuff I still need to do - #Eg add to tempoary PrebookFlight Table - #User pathfinding to translate to Waypoints,Flight and remove from table - return Sucess,Message,[] +## #Stuff I still need to do +## #Eg add to tempoary PrebookFlight Table +## #User pathfinding to translate to Waypoints,Flight and remove from table +## return Sucess,Message,[] def RemoveFlight(self,Arguments): FlightID = Arguments[0] + Sucess,Message = self._DB.RemoveFlightWaypoints(self._ClientID,FlightID) Sucess,Message = self._DB.RemoveFlight(self._ClientID,FlightID) return Sucess,Message,[] @@ -481,18 +452,19 @@ def GetMonitorPermissionUser(self,Arguments = None): -class BotConnection(UserConnection): +class BotConnection(UserConnection): #Interfaces with the bot def __init__(self,UserID,chat_id,packet,OutputQueue): self._ClientID = UserID - self.chat_id = chat_id - self.OutputQueue = OutputQueue + self._chat_id = chat_id + self._OutputQueue = OutputQueue + self.Create_Commands() self._DB = AATC_DB.DBConnection() self._Thread_Name = "[BOT FOR UID "+str(self._ClientID)+"]" Command, Arguments = packet[0],packet[1] self.Main(Command,Arguments) - def Main(self,Command,Arguments): + def Main(self,Command,Arguments): #Executes the single command passed to the routine try: Sucess,Message,Data = self.ProcessCommand(Command,Arguments) @@ -508,117 +480,98 @@ def Main(self,Command,Arguments): - def Send(self,data): + def Send(self,data): #Sends data back to the main bot thread Sucess,Message,Data = data[0],data[1],data[2] data = str(Sucess) +"\n" +str(Message)+ "\n" + str(Data) - self.OutputQueue.put((data,self.chat_id)) + self._OutputQueue.put((data,self._chat_id)) - def Login(self,Arguments): + def Login(self,Arguments): #Sets the new UserID for the given chat_id in the session table Username,Password = Arguments[0],Arguments[1] Sucess,Message,UserID = self._DB.CheckCredentials(Username,Password) self._DB.Bot_SetUserID(self.chat_id,UserID) return Sucess,Message,[] +## +##class WebConnection(UserConnection): # Not part of project as such +## def __init__(self,UserID): +## self._ClientID = UserID +## self._DB = AATC_DB.DBConnection() +## self._Thread_Name = "[WEB FOR UID "+str(self._ClientID)+"]" +## +## +## +## def Main(self,packet): +## Command, Arguments = packet[0],packet[1] +## try: +## Sucess,Message,Data = self.ProcessCommand(Command,Arguments) +## +## except Exception as e: +## Sucess,Message,Data = False,"An Error occured"+str(e),[] +## print("Error occured with UserID:",str(self._ClientID),". Error :",str(e),". Sending failure message") +## +## +## self._DB.Exit() +## return Sucess,Message,Data +## +## def Login(self,Arguments): +## Username,Password = Arguments[0],Arguments[1] +## Sucess,Message,UserID = self._DB.CheckCredentials(Username,Password) +## return Sucess,Message,UserID +## +## +## -class WebConnection(UserConnection): - def __init__(self,UserID): - self._ClientID = UserID - self._DB = AATC_DB.DBConnection() - self._Thread_Name = "[WEB FOR UID "+str(self._ClientID)+"]" - - - def Main(self,packet): - Command, Arguments = packet[0],packet[1] - try: - Sucess,Message,Data = self.ProcessCommand(Command,Arguments) - - except Exception as e: - Sucess,Message,Data = False,"An Error occured"+str(e),[] - print("Error occured with UserID:",str(self._ClientID),". Error :",str(e),". Sending failure message") - self._DB.Exit() - return Sucess,Message,Data - - def Login(self,Arguments): - Username,Password = Arguments[0],Arguments[1] - Sucess,Message,UserID = self._DB.CheckCredentials(Username,Password) - return Sucess,Message,UserID - + +class MonitorConnection(ClientConnection): #Interfaces with a monitor + def Create_Commands(self): + self._Non_Authenticated_Commands = { + "Login":self.Login, + "AddMonitor":self.AddMonitor, + "Exit":self.Exit } + self._Authenticated_Commands = { + "Login":self.Login, + "MonitorChangePassword":self.MonitorChangePassword, + + "GetNoFlyZones":self.GetNoFlyZones, + "GetDronesAll":self.GetDronesAll, + "GetUserID":self.GetUserID, + "GetUsername":self.GetUsername, + "GetMonitorDrones":self.GetMonitorDrones, + "GetMonitorFlights":self.GetMonitorFlights, + "GetMonitorFlightWaypoints":self.GetMonitorFlightWaypoints, + "GetMonitorID":self.GetMonitorID, + "GetMonitorName":self.GetMonitorName, + "RemoveMonitorPermission":self.RemoveMonitorPermission, + "GetMonitorPermissionMonitor":self.GetMonitorPermissionMonitor, - + "GetFlightsAll":self.GetFlightsAll, + "GetFlightWaypointsAll":self.GetFlightWaypointsAll, -class MonitorConnection(ClientConnection): - def ProcessCommand(self,Command,Arguments): + "Exit":self.Exit} + + + def ProcessCommand(self,Command,Arguments): if self._ClientID == -1: - if Command == "Login": - Sucess,Message,Data = self.Login(Arguments) - elif Command == "AddMonitor": # If adding a new Monitor, one must create it first, then log in seperatly - Sucess,Message,Data = self.AddMonitor(Arguments) - elif Command == "Exit": - Sucess,Message,Data = self.Exit(Arguments) - Exit = True - else: - Sucess,Message,Data = False,"Command does not exist",[] - + Function = self._Non_Authenticated_Commands.get(Command) else: - if Command == "Login": - Sucess,Message,Data = self.Login(Arguments) - elif Command == "MonitorChangePassword": - Sucess,Message,Data = self.MonitorChangePassword(Arguments) - - elif Command == "GetNoFlyZones": - Sucess,Message,Data = self.GetNoFlyZones(Arguments) - - elif Command == "GetDronesAll": - Sucess,Message,Data = self.GetDronesAll(Arguments) - - elif Command == "GetUserID": - Sucess,Message,Data = self.GetUserID(Arguments) - elif Command == "GetUsername": - Sucess,Message,Data = self.GetUsername(Arguments) - - elif Command == "GetMonitorDrones": - Sucess,Message,Data = self.GetMonitorDrones(Arguments) - elif Command == "GetMonitorFlights": - Sucess,Message,Data = self.GetMonitorFlights(Arguments) - elif Command == "GetMonitorFlightWaypoints": - Sucess,Message,Data = self.GetMonitorFlightWaypoints(Arguments) - - elif Command == "GetMonitorID": - Sucess,Message,Data = self.GetMonitorID(Arguments) - elif Command == "GetMonitorName": - Sucess,Message,Data = self.GetMonitorName(Arguments) - - elif Command == "RemoveMonitorPermission": - Sucess,Message,Data = self.RemoveMonitorPermission(Arguments) - elif Command == "GetMonitorPermissionMonitor": - Sucess,Message,Data = self.GetMonitorPermissionMonitor(Arguments) - - elif Command == "GetFlightsAll": - Sucess,Message,Data = self.GetFlightsAll(Arguments) - - elif Command == "GetFlightWaypointsAll": - Sucess,Message,Data = self.GetFlightWaypointsAll(Arguments) - - elif Command == "Exit": - Sucess,Message,Data = self.Exit(Arguments) - self._ExitLoop = True - - #Else if command doesnt exist send back Failure - else: - Sucess,Message,Data = False,"Command does not exist",[] - print(self._Thread_Name,self._ClientID," tried to use unregistered command") + Function = self._Authenticated_Commands.get(Command) + if Function != None: + Sucess,Message,Data = Function(Arguments) + else: + Sucess,Message,Data = False,"Command does not exist",[] + print(self._Thread_Name,self._ClientID," tried to use unregistered command") return Sucess,Message,Data ################################ @@ -712,45 +665,34 @@ def GetFlightWaypointsAll(self,Arguments = None): -class DroneConnection(ClientConnection): +class DroneConnection(ClientConnection): #Interfaces with drones + def Create_Commands(self): + self._Non_Authenticated_Commands = { + "Login":self.Login, + "Exit":self.Exit } + self._Authenticated_Commands = { + "UpdateDroneStatus":self.UpdateDroneStatus, + "DroneGetDroneInfo":self.DroneGetDroneInfo, + "CheckForFlight":self.CheckForFlight, + "GetFlight":self.GetFlight, + "GetFlightWaypoints":self.GetFlightWaypoints, + "MarkFlightComplete":self.MarkFlightComplete, + "Exit":self.Exit } - def ProcessCommand(self,Command,Arguments): + def ProcessCommand(self,Command,Arguments): #Executes a command with arguments depending on login state if self._ClientID == -1: - if Command == "Login": - Sucess,Message,Data = self.Login(Arguments) - elif Command == "Exit": - Sucess,Message,Data = self.Exit(Arguments) - Exit = True - else: - Sucess,Message,Data = False,"Command does not exist",[] - - + Function = self._Non_Authenticated_Commands.get(Command) else: - if Command == "UpdateDroneStatus": - Sucess,Message,Data = self.UpdateDroneStatus(Arguments) - - elif Command == "DroneGetDroneInfo": - Sucess,Message,Data = self.DroneGetDroneInfo(Arguments) - - elif Command == "CheckForFlight": - Sucess,Message,Data = self.CheckForFlight(Arguments) - elif Command == "GetFlight": - Sucess,Message,Data = self.GetFlight(Arguments) - elif Command == "GetFlightWaypoints": - Sucess,Message,Data = self.GetFlightWaypoints(Arguments) - elif Command == "MarkFlightComplete": - Sucess,Message,Data = self.MarkFlightComplete(Arguments) - - elif Command == "Exit": - Sucess,Message,Data = self.Exit(Arguments) - self._ExitLoop = True - - #Else if command doesnt exist send back Failure - else: - Sucess,Message,Data = False,"Command does not exist",[] - print(self._Thread_Name,self._ClientID," tried to use unregistered command") + Function = self._Authenticated_Commands.get(Command) + if Function != None: + Sucess,Message,Data = Function(Arguments) + else: + Sucess,Message,Data = False,"Command does not exist",[] + print(self._Thread_Name,self._ClientID," tried to use unregistered command") return Sucess,Message,Data + + def Login(self,Arguments): DroneID,DronePassword = Arguments[0],Arguments[1] Sucess,Message,self._ClientID = self._DB.DroneCheckCredentials(DroneID,DronePassword) @@ -759,7 +701,7 @@ def Login(self,Arguments): ######################################################## def UpdateDroneStatus(self,Arguments): - LastCoords,LastBattery = Coordinate(*Arguments[0]),Arguments[1] + LastCoords,LastBattery = AATC_Coordinate.Coordinate(*Arguments[0]),Arguments[1] Sucess,Message = self._DB.UpdateDroneStatus(self._ClientID,LastCoords,LastBattery) return Sucess,Message,[] @@ -798,20 +740,20 @@ def MarkFlightComplete(self,Arguments): -def Cleaner(Thread_Name,Thread_Queue,Interval = 36000,EndTimeThreshold = 72000): +def Cleaner(Thread_Name,Thread_Queue,Interval = 36000,EndTimeThreshold = 72000): #Ensures the database does not contain old or unneeded data by removing old items. Exit = False while not Exit: try: DB = AATC_DB.DBConnection() while not Exit: print("Cleaner starting cleaning") - DB.CleanMonitorPermissions() + DB.CleanMonitorPermissions() #Remove expired permissions Sucess,Message,FlightIDs = DB.GetCompletedFlightIDs(EndTimeThreshold) - DB.CleanFlights(FlightIDs) - for WrappedID in FlightIDs: #Wrapped as will be in for FlightIDs = [[a,],[b,],[c,]] where letters mean flightIDs DB.CleanCompletedFlightWaypoints(WrappedID[0]) + + DB.CleanFlights(FlightIDs) #Remove old flights print("Cleaner completed cleaning. Sleeping..") time.sleep(Interval) @@ -823,6 +765,8 @@ def Cleaner(Thread_Name,Thread_Queue,Interval = 36000,EndTimeThreshold = 72000): except Exception as e: print("Error in Cleaner",e) + time.sleep(60) + DB.Exit() diff --git a/AATC_Server_Starter.py b/AATC_Server_Starter.py index 392b557..f042b68 100644 --- a/AATC_Server_Starter.py +++ b/AATC_Server_Starter.py @@ -4,115 +4,6 @@ from flask_app import Flask_Test_App -def UserProcessSpawner(): - while True: - try: - HOST = '' - PORT = 8000 - - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - print( 'Socket created') - s.bind((HOST, PORT)) - - print( 'Socket bind complete') - s.listen(10) - print( 'Socket now listening') - - - while True: - try: - conn, addr = s.accept() - print( '\nConnected with ' + addr[0] + ':' + str(addr[1])+ "Type:User") - UserProcess = multiprocessing.Process(target = MakeUserConnection,args = (conn,)) - UserProcess.start() - except Exception as e: - print("Error creating User connection",str(e)) - except Exception as e: - print("Error in UserProcessSpawner",str(e)) - -def MakeUserConnection(Thread_Name,Thread_Queue,conn): - try: - UConn = AATC_Server.UserConnection(Thread_Name,Thread_Queue,conn) - UConn.Connection_Loop() - except Exception as e: - print("Serious error in UserConnection",e) - -##################################################### - -def MonitorProcessSpawner(): - while True: - try: - HOST = '' - PORT = 8001 - - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - print( 'Socket created') - - s.bind((HOST, PORT)) - print( 'Socket bind complete') - s.listen(10) - print( 'Socket now listening') - - while True: - try: - conn, addr = s.accept() - print( '\nConnected with ' + addr[0] + ':' + str(addr[1]) + "Type:Monitor") - MonitorProcess = multiprocessing.Process(target = MakeMonitorConnection,args = (conn,)) - MonitorProcess.start() - except Exception as e: - print("Error creating Monitor connection",str(e)) - except Exception as e: - print("Error in MonitorProcessSpawner",str(e)) - -def MakeMonitorConnection(Thread_Name,Thread_Queue,conn): - try: - MConn = AATC_Server.MonitorConnection(Thread_Name,Thread_Queue,conn) - MConn.Connection_Loop() - except Exception as e: - print("Serious error in MonitorConnection",e) - - - -############################################################ - -def DroneProcessSpawner(): - while True: - try: - HOST = '' - PORT = 8002 - - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - print( 'Socket created') - s.bind((HOST, PORT)) - - - print( 'Socket bind complete') - s.listen(10) - print( 'Socket now listening') - - - while True: - try: - conn, addr = s.accept() - print( '\nConnected with ' + addr[0] + ':' + str(addr[1])+ "Type:Drone") - DroneProcess = multiprocessing.Process(target = MakeDroneConnection, args = (conn,)) - DroneProcess.start() - except Exception as e: - print("Error creating Drone connection",str(e)) - except Exception as e: - print("Error in DroneProcessSpawner",str(e)) - -def MakeDroneConnection(Thread_Name,Thread_Queue,conn): - try: - DConn = AATC_Server.DroneConnection(Thread_Name,Thread_Queue,conn) - DConn.Connection_Loop() - except Exception as e: - print("Serious error in DroneConnection",e) - - ########################################################## #####################This section is part of the flask webserver component of the AATC program, not part of the main project. @@ -124,9 +15,9 @@ def StartFlaskServer(Thread_Name,Thread_Queue): ########################################################## -def ProcessSpawner(Name,Communications_Queue,Port,Type,Target): +def ProcessSpawner(Name,Communications_Queue,Port,Type,Target): #Spawn processes for each connection Exit = False - Spawner_Control_Queue = AATC_GPIO.Create_Controller() + Spawner_Control_Queue = AATC_GPIO.Create_Controller() #Create a controller for the sub processes ID_Counter = 1 DisplayName = "["+str(Name)+":"+str(Type)+"]" while not Exit: @@ -137,7 +28,7 @@ def ProcessSpawner(Name,Communications_Queue,Port,Type,Target): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) print(DisplayName,'Socket created') - s.bind((HOST, PORT)) + s.bind((HOST, PORT)) #Bind to the port print(DisplayName, 'Socket bind complete') @@ -147,10 +38,10 @@ def ProcessSpawner(Name,Communications_Queue,Port,Type,Target): while not Exit: try: - conn, addr = s.accept() + conn, addr = s.accept() #Accept connections print(DisplayName, ' Connected with' , addr[0] , ':' , str(addr[1]), "Type:",Type) Thread_Name = Type+str(ID_Counter) - Spawner_Control_Queue.put(("Controller","Create_Process",(Thread_Name,Target,(conn,)))) + Spawner_Control_Queue.put(("Controller","Create_Process",(Thread_Name,Target,(conn,)))) #Create the sub process for the connection ID_Counter +=1 except Exception as e: print("Error creating" ,Type,"connection",str(e)) @@ -169,7 +60,7 @@ def ProcessSpawner(Name,Communications_Queue,Port,Type,Target): Spawner_Control_Queue.put(("Controller","Exit",(True,))) -def StartProcesses(Control_Queue): +def StartProcesses(Control_Queue): #Start each of the main sub processes. Control_Queue.put(("Controller","Create_Process",("USpawner",ProcessSpawner,(8000,"User",MakeUserConnection)))) Control_Queue.put(("Controller","Create_Process",("MSpawner",ProcessSpawner,(8001,"Monitor",MakeMonitorConnection)))) Control_Queue.put(("Controller","Create_Process",("DSpawner",ProcessSpawner,(8002,"Drone",MakeDroneConnection)))) @@ -183,7 +74,7 @@ def StartProcesses(Control_Queue): print("[StartProcesses] All processes started") -if __name__ == "__main__": +if __name__ == "__main__": #Starts the server print("Server is starting") @@ -200,7 +91,7 @@ def StartProcesses(Control_Queue): print("Killing all Server processes....") print("This may take time, sleeping processes will be killed when resuming from sleep") - Control_Queue.put(("Controller","Exit",(True,))) + Control_Queue.put(("Controller","Exit",(True,))) #Tells the sub processes to quit print("Main process is now exiting...") sys.exit() diff --git a/AATC_Weather.py b/AATC_Weather.py index e083d99..a26e8e6 100644 --- a/AATC_Weather.py +++ b/AATC_Weather.py @@ -1,13 +1,13 @@ -import pyowm, AATC_Coordinate, math,time +import pyowm, AATC_Coordinate, math,time,AATC_Config -def Get_OWM(): - owm = pyowm.OWM('5b943c4857a45d75ef7ee9b301666fa8') +##def Get_OWM(): +## owm = pyowm.OWM('5b943c4857a45d75ef7ee9b301666fa8') class OWM_Control: def __init__(self): - self._owm = pyowm.OWM('5b943c4857a45d75ef7ee9b301666fa8') + self._owm = pyowm.OWM(AATC_Config.OWM_API_KEY) - def Get_Ajusted_Speed(self,CoordA,CoordB,Speed,At_Time = time.time()): + def Get_Adjusted_Speed(self,CoordA,CoordB,Speed,At_Time = time.time()): #Calculate the estimated speed if wind is present (lower speed when moving into wind) try: forecast = self._owm.daily_forecast_at_coords(CoordA.Get_Y(),CoordA.Get_X()) @@ -19,9 +19,9 @@ def Get_Ajusted_Speed(self,CoordA,CoordB,Speed,At_Time = time.time()): Vx = Speed*math.sin(AATC_Coordinate.toRadian(bearing))+ wind["speed"]*math.sin(AATC_Coordinate.toRadian(wind_bearing)) Vy = Speed*math.cos(AATC_Coordinate.toRadian(bearing))+ wind["speed"]*math.cos(AATC_Coordinate.toRadian(wind_bearing)) V = math.sqrt(Vx**2+Vy**2) - if V < 0: #To prevent estimating negative speeds + if V <= 0: #To prevent estimating negative speeds V = 1 return V except: - return Speed + return Speed #If failure to connect return normal speed.