def handle(self, *args, **options): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((TCP_IP, TCP_PORT)) #s.connect((TCP_IP, TCP_PORT)) #sending the auth message, receiving the response and sending an ack message key = authentication(s) CPRSession.objects.all().delete() cprkey = CPRSession(key=key,time=datetime.now()) cprkey.save() #mounting the XML response to server seckey_msg = "<?xml version=\"1.0\" encoding=\"ASCII\"?><Package><Header Version=\"1.0\" Id=\"2\" /><Data SessionId=\""+key+"\" /></Package>" close_msg = "<?xml version=\"1.0\" encoding=\"ASCII\"?>\n<Package>\n <Header Version=\"1.0\" Id=\"99\" />\n <Data SessionId=\""+key+"\" />\n</Package>" self.stdout.write('>> Starting main loop.\n') #sending the response to the server, and awaiting the outbox message s.send(ack_msg) s.send(seckey_msg) data = s.recv(BUFFER_SIZE) s.send(ack_msg) #listening all information given by CPR. # >> ==================================================== +-------------------------------+-\ # >> ==================================================== | MAIN EXTRACTOR LOOP | > # >> ==================================================== +-------------------------------+-/ self.stdout.write('>> Starting main loop.\n') while 1: # if there is messages to read in the socket if ([s],[],[]) == select.select([s],[],[],0): # reads and inform the acknowledgement of the message inbox = s.recv(BUFFER_SIZE) s.send(ack_msg) try: # >> ==================================================== +-------------------------------+-\ # >> ==================================================== | PARSING XML | > # >> ==================================================== +-------------------------------+-/ # parse the received xml and turns it into a dict self.stdout.write("================================\n"+inbox+"\n================================\n") xml = ElementTree.fromstring(inbox.strip("")) xmldict = XmlDictConfig(xml) # checks if it's a tracking table if xmldict['Header']['Id'] == '198': try: # >> ==================================================== +-------------------------------+-\ # >> ==================================================== | DATABASE LOOKUP | > # >> ==================================================== +-------------------------------+-/ # tries to pick the equipment and the date of the tracking table e = Equipment.objects.get(serial=xmldict['TCA']['SerialNumber']) v = Vehicle.objects.get(equipment=e) sys = lowestDepth(e.system.all()) searchdate = datetime.strptime(xmldict['Event']['EventDateTime'], "%Y/%m/%d %H:%M:%S") try: # tries to pick the tracking table with the same equipment and eventdate t = Tracking.objects.get(Q(equipment=e) & Q(eventdate=searchdate)) except ObjectDoesNotExist: # probably the try statement above will raise this exception, so the script shall create the tracking t = Tracking(equipment=e, eventdate=searchdate, msgtype=xmldict['Datagram']['MsgType']) t.save() # >> ==================================================== +-------------------------------+-\ # >> ==================================================== | CUSTOMFIELD AND DATA MERGING | > # >> ==================================================== +-------------------------------+-/ #iterates over the input dicts and saves the information for each matching custom field in the database table for k_type,d_type in xmldict.items(): if type(d_type).__name__ == 'dict': for k_tag,d_tag in d_type.items(): try: #find the customfield to put the tracking data under, then create and save it # TODO: if needed, optimize this database lookup doing one big search out of the for statement # TODO: and iterate over the list c = CustomField.objects.get(Q(type=k_type)&Q(tag=k_tag)) tdata = TrackingData(tracking=t,type=c,value=d_tag) tdata.save() except ObjectDoesNotExist: pass # >> ================================================= +----------------------------------+-\ # >> ================================================= | GEOCODING AND MORE DATA ADDITION | > # >> ================================================= +----------------------------------+-/ #reverse geocoding in the background geocodeinfo = ReverseGeocode(str(xmldict['GPS']['Lat']),str(xmldict['GPS']['Long'])) #get the custom fields for the right things geocodefields = CustomField.objects.filter(type='Geocode') geodict = {} for field in geocodefields: geodict[field.tag] = field #saving the tracking datas to the tracking TrackingData(tracking=t, type=geodict['Address'],value=geocodeinfo[1]).save() TrackingData(tracking=t, type=geodict['City'],value=geocodeinfo[2]).save() TrackingData(tracking=t, type=geodict['State'],value=geocodeinfo[3]).save() TrackingData(tracking=t, type=geodict['PostalCode'],value=geocodeinfo[4]).save() #saving the vehicle tracking data field = CustomField.objects.get(tag="Vehicle") TrackingData(tracking=t,type=field,value=v.id).save() #saving the system tracking data field = CustomField.objects.get(tag="System") TrackingData(tracking=t,type=field,value=sys.id).save() # print the success message self.stdout.write('>> The tracking table sent on '+str(searchdate)+' for the equipment '+ xmldict['TCA']['SerialNumber'] +' has been saved successfully.\n') # >> ==================================================== +-------------------------------+-\ # >> ==================================================== | ALERTS AND DATA COMPARISON | > # >> ==================================================== +-------------------------------+-/ # Here is the main alert handler. First, queries the database looking if there's some alert that matches the # received tracking. After that, for each alert in the result of the query, alert in each way available. #queries the vehicle in the database vehicle = v #(has been done before) #if the last alert sent for the vehicle is not null if vehicle.last_alert_date is not None: #calculate the difference between the last alert and a possibly new one total_seconds = (searchdate - vehicle.last_alert_date).days * 24 * 60 * 60 + (searchdate - vehicle.last_alert_date).seconds #check if there's enough time between the last alert sent and a possibly new one if total_seconds > vehicle.threshold_time*60: self.stdout.write('>> Alert threshold reached.\n') vehicle.last_alert_date = searchdate vehicle.save() #pick the alert records to check alerts = Alert.objects.filter(Q(vehicle=vehicle) & Q(time_end__gte=searchdate) & Q(time_start__lte=searchdate) & Q(active=True)) #iterates over the inputs and checks if it is needed to send the alert for k_type,d_type in dict(xmldict['Input'].items() + xmldict['LinearInput'].items()).items(): try: #dont look for GPS information now (will be done for the geofences) c = CustomField.objects.get(Q(tag=k_type)& ~Q(type='GPS')) #function that returns true if the alert shall be sent, and false if not. for alert in alerts: if AlertComparison(self,alert,c,d_type): #function that sends in the proper ways the alerts AlertSender(self,alert,vehicle,searchdate,geocodeinfo) else: pass # self.stdout.write('>> Nao entrou no Alert Comparison.\n') except ObjectDoesNotExist: # self.stdout.write('>> Entrou no except "objectdoesnotexist".\n') #exception thrown for the inputs and linear inputs that didn't match #any field in the database pass # >> ==================================================== +-------------------------------+-\ # >> ==================================================== | GEOFENCE COMPARISONS | > # >> ==================================================== +-------------------------------+-/ #check if the position is inside (or outside) some geofence alert geoalerts = alerts.filter(trigger__custom_field__tag='GeoFence') for alert in geoalerts: if GeofenceComparison(self,alert,xmldict["GPS"]["Lat"], xmldict["GPS"]["Long"]): AlertSender(self,alert,vehicle,searchdate) else: #if the vehicle never had thrown alerts, give him a last alert date vehicle.last_alert_date = searchdate vehicle.save() #exceptions thrown if the equipment does not exist. # TODO: create the equipments that isn't in the equipments database table except ObjectDoesNotExist: pass except KeyError: pass #if the message is a command status message elif xmldict['Header']['Id'] == '106': # TODO: update the status for the given command print xmldict e = Vehicle.objects.get(equipment__serial=xmldict['Data']['Serial']) c = ItrackCommand.objects.filter(equipment=e) c = c.get(state=u'0') self.stdout.write(str(c)+","+xmldict['Data']['Status']+ "\n") if xmldict['Data']['Status'] == '3': #message was sent to the GPRS network #as we only have one command per time in the command table, change the command status self.stdout.write('here!\n') c.state = u"1" c.time_received = datetime.strptime(xmldict['Header']['TimeStamp'], "%Y/%m/%d %H:%M:%S") c.save() #except ParseError: #TODO : parse the things when two tracking tables comes to the inbox - this case is kind of rare, #TODO : but makes the extractor crash when the 'except ParseError' is turned on. except: pass
def run(self): # Have our thread serve "forever": #System and Vehicle custom fields systemField = CustomField.objects.get(tag="System") vehicleField = CustomField.objects.get(tag="Vehicle") root_system = System.objects.get(parent=None) while True: try: if ((self.stopped() and clientPool.qsize() == 0) or (self.stopped() and threading.active_count() > 2)): self.setStatus("Thread stopped.") break # Get a client out of the queue try: client = clientPool.get(True,1) except Queue.Empty: client = None # Check if we actually have an actual client in the client variable: if client != None: status_display[0]+=1 inbox = client[0].recv(8192) self.setStatus('Processing data from '+client[1][0]) client[0].close() # file = codecs.open("./") datadict = json.loads(inbox) if datadict['Type'] == 'MTC State Tracking': serial_data = datadict['serial'] sendstate_data = datadict['sendstate'] date_data = datadict['date'] e = Equipment.objects.get(Q(serial=serial_data)) searchdate = datetime.strptime( date_data, "%Y-%m-%d %H:%M:%S") t = Tracking(equipment=e, eventdate=searchdate, msgtype="TRACKING") t.save() e.lasttrack_update = t.pk e.save() TrackingData(tracking=t,type=CustomField.objects.get(id=67), value=sendstate_data).save() elif datadict['Type'] == 'Tracking': # tries to pick the equipment and the date of the tracking table try: #first, check if the equipment exists type_id = datadict['Identification']['EquipType'] e = Equipment.objects.get( Q(serial=datadict['Identification']['Serial']) ) try: #print(e.name) #second, if the vehicle exists, for that equipment, # insert the tracking head on the tracking table vehicle = Vehicle.objects.get(equipment=e) sys = lowestDepth(e.system.all()) try: searchdate = datetime.strptime( datadict['Identification']['Date'], "%Y/%m/%d %H:%M:%S") except ValueError: searchdate = datetime.strptime( datadict['Identification']['Date'], "%Y-%m-%d %H:%M:%S") self.setStatus('Tracking at: '+str(searchdate)+' from equip: '+ str(e)) #create the tracking t = Tracking( equipment=e, eventdate=searchdate, msgtype="TRACKING") t.save() e.lasttrack_data = t.pk e.save() # mounting the list of data received io = {} try: if datadict.has_key("Input") and datadict['Input'] != None: io['Input'] = datadict['Input'].copy() except Exception as err: print(inbox) print("#2",err) pass try: io['LinearInput'] = datadict['LinearInput'].copy() except Exception as err: print(inbox) print("#3",err) pass has_output = False try: if datadict.has_key("Output") and datadict['Output'] != None: io['Output'] = datadict['Output'].copy() has_output = True except Exception as err: print(inbox) print("#4",err) pass try: io['GPS'] = datadict['GPS'].copy() except Exception as err: print(inbox) print("#5",err) pass try: if has_output : for x0 in xrange(1,8): ttype = 53 + x0 TrackingData(tracking=t,type=CustomField.objects.get(id=ttype), value=io['Output']['Output' + str(x0)]).save() except Exception as err: print(inbox) print("#6",err) pass #filtering that list, leaving only the registered #custom fields for the equipment type io_filtered = {} for cf in equipTypeDict[int(type_id)]: for k,v in io.items(): if not io_filtered.has_key(k): #pre-populating to avoid KeyError #io_filtered[k] = {} pass if (cf.tag in v.keys() and cf.type == k): #mounting the dict, associating the custom #field with the value in the tracking io_filtered[cf] = v[cf.tag] #inserting the tracking datas under the tracking head for k_cf,v in io_filtered.items(): TrackingData( tracking=t, type=k_cf, value=v ).save() cflist = [x[0] for x in io_filtered.items()] for cf in equipTypeDict[int(type_id)]: if cf not in cflist: TrackingData( tracking=t, type=cf, value="OFF" ).save() try: #reverse geocoding in the background _lat = float(datadict['GPS']['Lat']) _lon = float(datadict['GPS']['Long']) geocodeinfo = ReverseGeocode(_lat,_lon) status_display[1] += 1 #saving the acquired geocode information TrackingData( tracking=t, type=geoDict['Address'], value=geocodeinfo[1]).save() TrackingData( tracking=t, type=geoDict['City'], value=geocodeinfo[2] ).save() TrackingData( tracking=t, type=geoDict['State'], value=geocodeinfo[3] ).save() TrackingData( tracking=t, type=geoDict['PostalCode'], value=geocodeinfo[4] ).save() self.setStatus('Reverse geocode finished. ') # and adding extra vehicle and system custom fields TrackingData( tracking=t, type=vehicleField, value=vehicle.id).save() TrackingData( tracking=t, type=systemField,value=sys.id).save() #queries the vehicle in the database #if the last alert sent for the vehicle is not null except Exception as err: # for th in threading.enumerate(): # if isinstance(th,OutputThread): # th.stop() print(inbox) print("#7",err) pass if vehicle.last_alert_date is not None: total_seconds = ( (searchdate - vehicle.last_alert_date).days * 24 * 60 * 60 + (searchdate - vehicle.last_alert_date).seconds ) # check if there's enough time between the last alert # sent and a possibly new one if total_seconds > vehicle.threshold_time*60: vehicle.last_alert_date = searchdate vehicle.save() #pick the alert records to check alerts = Alert.objects.filter( Q(vehicle=vehicle) & Q(time_end__gte=searchdate) & Q(time_start__lte=searchdate) & Q(active=True) ) geoalerts = alerts.filter( trigger__custom_field__tag='GeoFence' "Waiting to process data." ) # iterates over the inputs and checks if it # is needed to send the alert for k,v in io_filtered.items(): if k.type in ['Input','LinearInput']: for alert in alerts: if AlertComparison(self,alert,k,v): self.setStatus('Found alert to send.') AlertSender(self,alert,vehicle, searchdate,geocodeinfo) #checking the geofence alerts for alert in geoalerts: if GeofenceComparison( self,alert, io["GPS"]["Lat"], io["GPS"]["Long"] ): self.setStatus('Found geofence alert to send.') AlertSender(self,alert,vehicle,searchdate) else: # if the vehicle never had thrown alerts, # give him a last alert date vehicle.last_alert_date = searchdate vehicle.save() self.setStatus("Waiting to process data.") except ObjectDoesNotExist: self.setStatus("Equipment without vehicle. Dropping received data.") pass except ObjectDoesNotExist: self.setStatus('Equipment not found on the database.'+ 'Creating and inserting under the root system') try: eq = Equipment( name = datadict['Identification']['Serial'], serial = datadict['Identification']['Serial'], type = equipTypeIndex[type_id], available = True ) eq.save() eq.system.add(root_system) except IntegrityError: pass except KeyError: self.setStatus('Equip Type "'+str(type_id) + '" not '+ 'recognized. Dropping recived data.') except KeyError: self.setStatus('Equip Type "'+str(type_id) + '" not '+ 'recognized. Dropping recived data.') elif datadict['Type'] == 'Command': pass elif datadict['Type'] == 'CarMeter': try: serial_data = datadict['Identification']['Serial'] e = Equipment.objects.get(Q(serial=serial_data)) try: searchdate = datetime.strptime( datadict['Identification']['Date'],"%Y/%m/%d %H:%M:%S") except ValueError: searchdate = datetime.strptime( datadict['Identification']['Date'],"%Y-%m-%d %H:%M:%S") t = Tracking(equipment=e, eventdate=searchdate, msgtype="CARMETER") t.save() e.lastdriver = t.pk e.save() #datadict['Identification']['CardId'] TrackingData(tracking=t,type=CustomField.objects.get(id=68), value=datadict['Identification']['CardId']).save() except Exception as err: print(err.args) client[0].close() else: self.setStatus("Waiting to process data.") except Exception as err: print("#1",err) pass