def run(self): """ query for current location """ # ignore signals being used by main program signal.signal(signal.SIGINT,signal.SIG_IGN) signal.signal(signal.SIGTERM,signal.SIG_IGN) # get and transmit device details ret = self._devicedetails() if ret: self._icomms.put(('gpsd',isots(),GPS_GPSD,self._dd)) # if fixed location, send one frontline trace otherwise poll over device if self._fixed: self._icomms.put(('gpsd',isots(),GPS_FLT,self._defFLT)) else: while True: # block on our poll time (this will cause serial device to fill up if self._cI.poll(self._poll) and self._cI.recv() == POISON: break while self._gpsd.waiting(): # at each iteration make sure we did not get a poison pill if self._cI.poll() and self._cI.recv() == POISON: break # get the next input from the device rpt = self._gpsd.next() if rpt['class'] != 'TPV': continue # try to read in all data try: if rpt['epx'] > self._qpx or rpt['epy'] > self._qpy: continue else: flt = {'fix':rpt['mode'], 'coord':self._m.toMGRS(rpt['lat'],rpt['lon']), 'epy':rpt['epy'], 'epx':rpt['epx'], 'alt':rpt['alt'] if 'alt' in rpt else float("nan"), 'dir':rpt['track'] if 'track' in rpt else float("nan"), 'spd':rpt['spd'] if 'spd' in rpt else float("nan"), 'dop':{'xdop':'{0:.3}'.format(self._gpsd.xdop), 'ydop':'{0:.3}'.format(self._gpsd.ydop), 'pdop':'{0:.3}'.format(self._gpsd.pdop)}} self._icomms.put(('gpsd',isots(),GPS_FLT,flt)) break except (KeyError,AttributeError): # a KeyError means not all values are present, an # AttributeError means not all dop values are present pass except: # blanket exception quit device read loop break # tell collator we're down, notify Iyri if ret: self._icomms.put(('gpsd',isots(),GPS_GPSD,None)) self._cI.send((IYRI_DONE,'gpsd','','')) self._cI.close() if self._gpsd: self._gpsd.close()
def run(self): """ execute frame process loop """ # ignore signals used by main program & add signal for stop signal.signal(signal.SIGINT, signal.SIG_IGN) # CTRL-C and kill -INT stop signal.signal(signal.SIGTERM, signal.SIG_IGN) # kill -TERM stop # local variables sid = None # current session id rdos = {} # (deprecated) radio map hwaddr->True stop = False # stop flag # notify collator we're up self._icomms.put((self.cs, isots(), THRESH_THRESH, self.pid)) while not stop: try: # do not check task queue until sid has been set ins = [self._cC, self._tasks._reader] if sid else [self._cC] (rs, _, _) = select.select(ins, [], []) except select.error as e: # hide (4,'Interupted system call') errors if e[0] == 4: continue rs = [] # get each message for r in rs: try: # get & process data if r == self._cC: (tkn, ts, d) = self._cC.recv() else: (tkn, ts, d) = self._tasks.get(0.5) if tkn == POISON: stop = True elif tkn == COL_SID: sid = d[0] elif tkn == COL_RDO: rdos[d[0]] = True elif tkn == COL_FRAME: self._processframe(sid, ts, d) except Exception as e: # blanket exception, catch all and notify collator msg = "{0} {1}\n{2}".format(type(e).__name__, e, tb()) self._icomms.put( (self.cs, isots(), THRESH_WARN, (msg, None))) # notify collector we're down if self._curs: self._curs.close() if self._conn: self._conn.close() self._icomms.put((self.cs, isots(), THRESH_THRESH, None))
def _newthresher(self, q, l, b, m): """ creates a new thresher :param q: task queue for threshers :param l: STA lock :param b: buffer :param m: smap dict :returns tuple t = (thresher process id,send connection): """ # NOTE: send the sid last so the thresher will not attempt to process # any frames until it has all radio(s) data r, s = mp.Pipe(False) t = Thresher(self._icomms, q, r, l, b, self._dbstr) t.start() for mac in m: ts = isots() s.send((COL_RDO, ts, [mac])) if self._sid: s.send((COL_SID, isots(), [self._sid])) return t.pid, s
def _processframe(self, sid, ts, d): """ inserts frame into db :param sid: session id :param ts: timestamp of frame :param d: data """ # (1) pull frame off the buffer & notify Collator src = idx = b = None try: src, idx, b = d # hwaddr,index,nBytes f = self._buff[(idx * DIM_N):(idx * DIM_N) + b].tobytes() self._icomms.put((self.cs, isots(), THRESH_DQ, (None, idx))) except ValueError: self._icomms.put((self.cs, isots(), THRESH_ERR, 'Bad Data')) return except Exception as e: tr = tb() te = type(e).__name__ if not src: msg = "Failed extracting data: {0}->{1}\n{2}".format(te, e, tr) self._icomms.put((self.cs, isots(), THRESH_ERR, (msg, None))) else: msg = "Failed extracting frame: {0}->{1}\n{2}".format( te, e, tr) self._icomms.put((self.cs, isots(), THRESH_ERR, (msg, idx))) return # (2) parse the frame fid = None # set an invalid frame id dR = {} # make an empty rtap dict dM = mpdu.MPDU() # & an empty mpdu dict try: lF = len(f) dR = rtap.parse(f) dM = mpdu.parse(f[dR['sz']:], rtap.flags_get(dR['flags'], 'fcs')) vs = (sid, ts, lF, dR['sz'], dM.offset, [(dR['sz'] + dM.offset), (lF - dM.stripped)], dR['flags'], int('a-mpdu' in dR['present']), rtap.flags_get(dR['flags'], 'fcs')) except rtap.RadiotapException as e: # parsing failed @ radiotap, set empty vals & alert Collator msg = "Parsing failed at radiotap: {0} in buffer[{1}]".format( e, idx) self._icomms.put((self.cs, isots(), THRESH_WARN, (msg, None))) vs = (sid, ts, lF, 0, 0, [0, lF], 0, 0, 0) dR['err'] = e except mpdu.MPDUException as e: # mpdu failed - initialize empty mpdu vals & alert Collator msg = "Parsing failed at mpdu: {0} in buffer[{1}]".format(e, idx) self._icomms.put((self.cs, isots(), THRESH_WARN, (msg, None))) vs = (sid, ts, lF, dR['sz'], 0, [dR['sz'], lF], dR['flags'], int('a-mpdu' in dR['present']), rtap.flags_get(dR['flags'], 'fcs')) except Exception as e: # blanket error msg = "Parsing Error. {0} {1}\n{2}".format( type(e).__name__, e, tb()) self._icomms.put((self.cs, isots(), THRESH_WARN, (msg, idx))) return # (3) store the frame # NOTE: individual helper functions will commit database inserts/updates # but allow exceptions to fall through as db errors will be caught in this # try & rollbacks will be executed here try: # insert the frame and get frame's id self._next = 'frame' sql = """ insert into frame (sid,ts,bytes,bRTAP,bMPDU,data,flags,ampdu,fcs) values (%s,%s,%s,%s,%s,%s,%s,%s,%s) returning frame_id; """ self._curs.execute(sql, vs) fid = self._curs.fetchone()[0] self._conn.commit() # write raw bytes self._next = 'frame_raw' sql = "insert into frame_raw (fid,raw) values (%s,%s);" self._curs.execute(sql, (fid, psql.Binary(f))) self._conn.commit() # insert radiotap and/or malformed if we had any issues self._next = 'radiotap' if not 'err' in dR: self._insertrtap(fid, src, dR) else: sql = """ insert into malformed (fid,location,reason) values (%s,%s,%s); """ self._curs.execute(sql, (fid, 'radiotap', dR['err'])) self._conn.commit() # insert mpdu and malfored (if it exists) for err in dM.error: sql = """ insert into malformed (fid,location,reason) values (%s,%s,%s); """ self._curs.execute(sql, (fid, err[0], err[1])) self._conn.commit() # notify collator, but we want to continue attempting to store # as much as possible so set src and index to None msg = "Frame {0}. Bad MPDU {1}".format(fid, err) self._icomms.put((self.cs, isots(), THRESH_WARN, (msg, None))) # (a) store frame details in db if not dM.isempty: self._next = 'mpdu' self._insertmpdu(fid, dM) # (b) extract self._next = 'extract' # pull unique addresses of stas from mpdu into the addr dict # having the form: <hwaddr>->{'loc:[<i..n>], where i=1..4 # 'id':<sta_id>} # NOTE: insertsta modifies the addrs dict in place assigning ids # to each nonbroadcast address # sta addresses # Fct To From Addr1 Addr2 Addr3 Addr4 # IBSS/Intra 0 0 RA=DA TA=SA BSSID N/A # From AP 0 1 RA=DA TA=BSSID SA N/A # To AP 1 0 RA=BSSID TA=SA DA N/A # Wireless DS 1 1 RA=BSSID TA=BSSID DA=WDS SA=WDS addrs = {} for i, a in enumerate(['addr1', 'addr2', 'addr3', 'addr4']): if not a in dM: break if dM[a] in addrs: addrs[dM[a]]['loc'].append(i + 1) else: addrs[dM[a]] = {'loc': [i + 1], 'id': None} # insert the sta, sta_activity self._insertsta(fid, sid, ts, addrs) self._insertsta_activity(sid, ts, addrs) # further process mgmt frames? if dM.type == mpdu.FT_MGMT: self._next = 'mgmt' self._processmgmt(fid, sid, ts, addrs, dM) # notify collator, we're done self._icomms.put((self.cs, isots(), THRESH_DONE, (None, idx))) except psql.OperationalError as e: # unrecoverable # NOTE: for now we do not exit on unrecoverable error but allow # collator to send the poison pill self._conn.rollback() args = (fid, self._next, e.pgcode, e.pgerror) msg = "<PSQL> Frame {0} failed at {1}. {2}->{3}.".format(*args) self._icomms.put((self.cs, isots(), THRESH_ERR, (msg, idx))) except psql.Error as e: # rollback to avoid db errors self._conn.rollback() args = (fid, self._next, e.pgcode, e.pgerror) msg = "<PSQL> Frame {0} failed at {1}. {2}->{3}.".format(*args) self._icomms.put((self.cs, isots(), THRESH_WARN, (msg, idx))) except Exception as e: self._conn.commit() # commit anything that may be pending args = (fid, self._next, type(e).__name__, e, tb()) msg = "<UNK> Frame {0} failed at {1}. {2}->{3}\n{4}.".format(*args) self._icomms.put((self.cs, isots(), THRESH_WARN, (msg, idx)))
def run(self): """ run execution loop """ # ignore signals being used by main program signal.signal(signal.SIGINT, signal.SIG_IGN) signal.signal(signal.SIGTERM, signal.SIG_IGN) # start tuner stuner = TUNE_PAUSE if self._paused else TUNE_SCAN try: qT = mp.Queue() tuner = Tuner(qT, self._cI, self._rdo, self._scan, self._ds, self._chi, stuner) tuner.start() self._icomms.put((self._rdo.vnic, isots(), RDO_RADIO, self.radio)) except Exception as e: self._cI.send((IYRI_ERR, self._role, type(e).__name__, e)) self._icomms.put((self._rdo.vnic, isots(), RDO_FAIL, e)) else: msg = "tuner-{0} up".format(tuner.pid) self._cI.send((IYRI_INFO, self._role, 'Tuner', msg)) # execute sniffing loop # RadioController will check for any notifications from the Tuner, a tuple # t = (event,timestamp,message) where message is another tuple of the form # m = (cmdid,description) such that cmdid != -1 if this command comes from # an external source. If there are no notifications from Tuner, check if # any frames are queued on the socket and read them in to the buffer. # If we're paused, read into a null buffer ix = 0 # index of current frame m = DIM_M # save some space with n = DIM_N # these err = False # error state, do nothing until we get POISON pill while True: try: # pull any notification from Tuner & process ev, ts, msg = qT.get_nowait() if ev == POISON: break elif ev == CMD_ERR: self._cI.send((CMD_ERR, self._role, msg[0], msg[1])) elif ev == RDO_FAIL: self._cI.send((IYRI_ERR, self._role, 'Tuner', msg[1])) self._icomms.put((self._rdo.vnic, ts, RDO_FAIL, msg[1])) elif ev == RDO_STATE: self._cI.send((CMD_ACK, self._role, msg[0], msg[1])) elif ev == RDO_HOLD: stuner = TUNE_HOLD self._icomms.put((self._rdo.vnic, ts, RDO_HOLD, msg[1])) if msg[0] > -1: self._cI.send((CMD_ACK, self._role, msg[0], msg[1])) elif ev == RDO_SCAN: stuner = TUNE_SCAN self._icomms.put((self._rdo.vnic, ts, RDO_SCAN, msg[1])) if msg[0] > -1: self._cI.send((CMD_ACK, self._role, msg[0], msg[1])) elif ev == RDO_LISTEN: stuner = TUNE_LISTEN self._icomms.put((self._rdo.vnic, ts, RDO_LISTEN, msg[1])) if msg[0] > -1: self._cI.send((CMD_ACK, self._role, msg[0], msg[1])) elif ev == RDO_PAUSE: stuner = TUNE_PAUSE self._icomms.put((self._rdo.vnic, ts, RDO_PAUSE, ' ')) if msg[0] > -1: self._cI.send((CMD_ACK, self._role, msg[0], msg[1])) except Empty: # no notices from Tuner if err: continue # error state, wait till iyri sends poison pill try: # if paused, pull any frame off and ignore otherwise pass on if stuner == TUNE_PAUSE: _ = self._s.recv(n) else: l = self._s.recv_into( self._buffer[(ix * n):(ix * n) + n], n) self._icomms.put( (self._rdo.vnic, isots(), RDO_FRAME, (ix, l))) ix = (ix + 1) % m except socket.timeout: pass except socket.error as e: err = True self._cI.send((IYRI_ERR, self._role, 'Socket', e)) self._icomms.put((self._rdo.vnic, isots(), RDO_FAIL, e)) except Exception as e: err = True self._cI.send((IYRI_ERR, self._role, type(e).__name__, e)) self._icomms.put((self._rdo.vnic, isots(), RDO_FAIL, e)) # wait on Tuner to finish while mp.active_children(): sleep(0.5) # notify Iyri and Collator we are down & shut down self._cI.send((IYRI_DONE, self._role, 'done', 'done')) self._icomms.put((self._rdo.vnic, isots(), RDO_RADIO, None)) if not self._shutdown(): try: self._cI.send( (IYRI_WARN, self._role, 'Shutdown', "Incomplete reset")) except (EOFError, IOError): pass
def run(self): """ switch channels based on associted dwell times """ # ignore signals being used by main program signal.signal(signal.SIGINT, signal.SIG_IGN) signal.signal(signal.SIGTERM, signal.SIG_IGN) # starting paused or scanning? if self._state == TUNE_PAUSE: self._qR.put((RDO_PAUSE, isots(), [-1, ' '])) else: self._qR.put((RDO_SCAN, isots(), [-1, self._chs])) # execution loop - wait on the internal connection for each channels # dwell time. IOT avoid a state where we would continue to scan the same # channel, i.e. 'state' commands, use a remaining time counter remaining = 0 # remaining dwell time counter dwell = self._ds[0] # save original dwell time while True: # set the poll timeout to remaining if we need to finish this scan # or to None if we are in a static state if self._state in [TUNE_PAUSE, TUNE_HOLD, TUNE_LISTEN]: to = None else: to = self._ds[self._i] if not remaining else remaining ts1 = time() # get timestamp if self._cI.poll( to): # we hold on the iyri connection during poll time tkn = self._cI.recv() ts = time() # tokens will have 2 flavor's POISON and 'cmd:cmdid:params' # where params is empty (for POISON) or a '-' separated list if tkn == POISON: # for a POISON, quit and notify the RadioController self._qR.put((POISON, ts2iso(ts), [-1, ' '])) break # calculate time remaining for this channel if not remaining: remaining = self._ds[self._i] - (ts - ts1) else: remaining -= (ts - ts1) # parse the requested command cmd, cid, ps = tkn.split(':') # force into 3 components cid = int(cid) # & convert id to int if cmd == 'state': self._qR.put((RDO_STATE, ts2iso(ts), [cid, self.meta])) elif cmd == 'scan': if self._state != TUNE_SCAN: self._state = TUNE_SCAN self._qR.put((RDO_SCAN, ts2iso(ts), [cid, self._chs])) else: self._qR.put( (CMD_ERR, ts2iso(ts), [cid, "redundant cmd"])) elif cmd == 'txpwr': err = "txpwr not currently supported" self._qR.put((CMD_ERR, ts2iso(ts), [cid, err])) elif cmd == 'spoof': err = "spoof not currently supported" self._qR.put((CMD_ERR, ts2iso(ts), [cid, err])) elif cmd == 'hold': if self._state != TUNE_HOLD: self._state = TUNE_HOLD self._qR.put( (RDO_HOLD, ts2iso(ts), [cid, self.channel])) else: self._qR.put( (CMD_ERR, ts2iso(ts), [cid, "redundant cmd"])) elif cmd == 'pause': if self._state != TUNE_PAUSE: self._state = TUNE_PAUSE self._qR.put( (RDO_PAUSE, ts2iso(ts), [cid, self.channel])) else: self._qR.put( (CMD_ERR, ts2iso(ts), [cid, "redundant cmd"])) elif cmd == 'listen': if self._state != TUNE_LISTEN: try: ch, chw = ps.split('-') if chw == 'None': chw = None self._rdo.setch(ch, chw) self._state = TUNE_LISTEN details = "{0}:{1}".format(ch, chw) self._qR.put( (RDO_LISTEN, ts2iso(ts), [cid, details])) except ValueError: err = "invalid param format" self._qR.put((CMD_ERR, ts2iso(ts), [cid, err])) except radio.RadioException as e: self._qR.put((CMD_ERR, ts2iso(ts), [cid, str(e)])) else: self._qR.put( (CMD_ERR, ts2iso(ts), [cid, "redundant cmd"])) else: err = "invalid command {0}".format(cmd) self._qR.put((CMD_ERR, ts2iso(ts), [cid, err])) else: try: # no token, go to next channel, reset remaining self._i = (self._i + 1) % len(self._chs) self._rdo.setch(self._chs[self._i][0], self._chs[self._i][1]) remaining = 0 # reset remaining timeout except radio.RadioException as e: self._qR.put((RDO_FAIL, isots(), [-1, e])) except Exception as e: # blanket exception self._qR.put((RDO_FAIL, isots(), [-1, e]))
def run(self): """ run execution loop """ # ignore signals being used by main program signal.signal(signal.SIGINT, signal.SIG_IGN) signal.signal(signal.SIGTERM, signal.SIG_IGN) # function local variables mint = mp.cpu_count() # min/initial # of threshers maxt = self._ps['max'] # maximum # of threshers lSta = mp.Lock() # sta lock qT = mp.Queue() # thresher task queue rmap = {} # maps callsigns (vnic) to hwaddr fmap = {} # maps src (hwaddr) to frames smap = {} # maps src (hwaddr) to sensor params threshers = {} # pid->connection tst = 0 # timestamp of last thresher creation stop = False # poison pill has been received poisoned = False # poison pill has been sent (to threshers) df = rf = 0 # number dropped frames & read frames # create our frame buffer m = DIM_M * 5 # set max # of frames n = DIM_N # and keep n as is _ix = 0 # current 'pointer' cb = memoryview(mp.Array('B', m * n, lock=False)) # send sensor up notification, platform details & create threshers try: self._submitsensor(isots(), True) for _ in xrange(mint): try: (pid, send) = self._newthresher(qT, lSta, cb, smap) threshers[pid] = send except RuntimeError as e: self._cI.send((IYRI_WARN, 'collator', 'Thresher', e)) tst = time() self._submitplatform() except psql.Error as e: if e.pgcode == '23P01': err = "Last session closed incorrectly" else: err = "{0}->{1}".format(e.pgcode, e.pgerror) self._conn.rollback() self._cI.send((IYRI_ERR, 'collator', 'Thresher', err)) # notify iyri of startup parameters msg = "{0} threshers (min={1},max={2}) & a {3} x {4} internal buffer" msg = msg.format(format(len(threshers)), mint, maxt, m, n) self._cI.send((IYRI_INFO, 'collator', 'Startup', msg)) # execution loop - NOTE: if submitsensor failed, there will be no # threshers and we will immediately fall through the exection loop ins = [self._cI, self._icomms._reader] while mp.active_children(): # if we have stop, send out the poison pills once all frames have been # processed (or at least pulled of the buffer) if stop: if not [i for i in fmap if fmap[i]] and not poisoned: msg = "{0} outstanding frames".format(len(fmap)) for pid in threshers: try: threshers[pid].send((POISON, None, None)) except Exception as e: msg = "Error stopping Thresher-{0}: {1}".format( pid, e) self._cI.send( (IYRI_WARN, 'collator', 'Thresher', e)) poisoned = True # continue with processing try: # block longer if we havent gotten poison pill (rs, _, _) = select.select(ins, [], [], 0.5 if stop else 5) # convert tkns from iyri to icomms format # NOTE: all Thresh events (exluding THRESH_THRESH) will return # data as a tuple t = (string message,buffer index) where any # individual item in the tuple could be None for r in rs: if r == self._cI: (cs, ts, ev, d) = ('', '', self._cI.recv(), '') else: (cs, ts, ev, d) = self._icomms.get_nowait() if ev == POISON: stop = True elif ev == THRESH_THRESH: # thresher process up/down message if d is not None: # thresher up msg = "{0} up".format(cs) self._cI.send( (IYRI_INFO, 'collator', 'Thresher', msg)) else: # thresher down pid = int(cs.split('-')[1]) threshers[pid].close() del threshers[pid] msg = "{0} down".format(cs) self._cI.send( (IYRI_INFO, 'collator', 'Thresher', msg)) elif ev == THRESH_DQ: # frame has been pulled off buffer (_, idx) = d fmap[idx] = 0 elif ev == THRESH_WARN or ev == THRESH_ERR or ev == THRESH_DONE: # frame processing finished msg, idx = d try: del fmap[idx] except: pass # handle individual events below if ev == THRESH_WARN: self._cI.send( (IYRI_WARN, 'collator', 'Thresher', msg)) elif ev == THRESH_ERR: pid = int(cs.split('-')[1]) msg = "{0}->{1}".format(cs, msg[0]) self._cI.send( (IYRI_WARN, 'collator', 'Thresher', msg)) threshers[pid].send((POISON, None, None)) # make a new thresher (to replace this one) try: (pid, send) = self._newthresher(qT, lSta, cb, smap) threshers[pid] = send except RuntimeError as e: self._cI.send( (IYRI_WARN, 'Collator', Thresher, e)) # determine workload percentage if stop: continue if len(fmap) / (m * 1.0) <= 0.1 and len(threshers) > mint: msg = "Workload falling below 10%, removing a threseher" self._cI.send((IYRI_INFO, 'collator', 'load', msg)) pid = threshers.keys()[0] threshers[pid].send((POISON, None, None)) elif ev == GPS_GPSD: # gpsd up/down self._submitgpsd(ts, d) if d: self._cI.send( (IYRI_INFO, 'collator', 'GPSD', 'initiated')) elif ev == GPS_FLT: self._submitflt(ts, d) elif ev == RDO_RADIO: # up/down message from radio(s). if d is not None: # radio up (mac, role) = d['mac'], d['role'] # shouldn't happen but make sure: assert (cs not in rmap) # reinitialization assert (role in ['abad', 'shama']) # role is valid # notify iyri radio is up & add to maps msg = "{0}:{1} initiated".format(role, cs) self._cI.send( (IYRI_INFO, 'collator', 'Radio', msg)) rmap[cs] = mac smap[mac] = {'cb': None} if role == 'abad': smap[mac]['cb'] = self._ab else: smap[mac]['cb'] = self._sb # update threshers for pid in threshers: threshers[pid].send((COL_RDO, ts, [mac])) # submit radio & antenna(s) self._submitradio(ts, mac, d) for i in xrange(d['nA']): self._submitantenna( ts, { 'mac': mac, 'idx': i, 'type': d['type'][i], 'gain': d['gain'][i], 'loss': d['loss'][i], 'x': d['x'][i], 'y': d['y'][i], 'z': d['z'][i] }) else: # radio down msg = "{0} shutdown".format(cs) self._cI.send( (IYRI_INFO, 'collator', 'Radio', msg)) self._submitradio(ts, rmap[cs], None) # delete our maps (only delete tasks if its empty) del smap[rmap[cs]] del rmap[cs] elif ev == RDO_FAIL: msg = "Deleting radio {0}: {1}".format(cs, d) self._cI.send((IYRI_ERR, 'collator', 'Radio', msg)) self._submitradioevent(ts, [rmap[cs], 'fail', d]) # delete our maps del smap[rmap[cs]] del rmap[cs] elif RDO_SCAN <= ev <= RDO_STATE: if ev == RDO_SCAN: # need to format the channel list d = ','.join( ["{0}:{1}".format(c, w) for (c, w) in d]) self._cI.send((IYRI_INFO, cs, RDO_DESC[ev], d)) self._submitradioevent(ts, [rmap[cs], RDO_DESC[ev], d]) elif ev == RDO_FRAME: # get the index,length of the frame & src hwaddr (i, l) = d hw = rmap[cs] # about to overwrite a frame? if _ix in fmap and fmap[_ix] == 1: df += 1 print 'dropping' continue # move to our buffer, put on threshers queue & update index cb[(_ix * n):(_ix * n) + l] = smap[hw]['cb'][(i * n):(i * n) + l] fmap[_ix] = 1 qT.put((COL_FRAME, ts, (hw, _ix, l))) _ix = (_ix + 1) % m rf += 1 # if the buffer reaches 25% capacity, add a thresher # NOTE: consider all outstanding frames even those that # have been pulled of the buffer but are still being # processed) if len(fmap) / (m * 1.0) > 0.25 and len(threshers) < maxt: # create at most mint new threshers w/out adding # more than allowed. Give time for previously # created threshers to start working tsnow = time() if tsnow - tst < 3: continue x = min(mint, maxt - len(threshers)) msg = "Buffer at 25%, adding {0} threshers".format( x) self._cI.send((IYRI_INFO, 'collator', 'load', msg)) for _ in xrange(x): try: (pid, send) = self._newthresher( qT, lSta, cb, smap) threshers[pid] = send except RuntimeError as e: self._cI.send( (IYRI_WARN, 'Collator', Thresher, e)) tst = time() else: # unidentified event type, notify iyri msg = "unknown event. {0}->{1}".format(ev, cs) self._cI.send((IYRI_WARN, 'collator', 'icomms', msg)) except Empty: continue except (EOFError, IOError): pass except psql.OperationalError as e: # unrecoverable rmsg = "{0}: {1}".format(e.pgcode, e.pgerror) self._cI.send((IYRI_ERR, 'collator', 'DB', rmsg)) except psql.Error as e: # possible incorrect semantics/syntax self._conn.rollback() rmsg = "{0}: {1}".format(e.pgcode, e.pgerror) self._cI.send((IYRI_WARN, 'collator', 'SQL', rmsg)) except IndexError as e: rmsg = "Bad index {0} in event {1}".format(e, ev) self._cI.send((IYRI_ERR, 'collator', "Index", rmsg)) except KeyError as e: rmsg = "Bad key {0} in event {1}".format(e, ev) self._cI.send((IYRI_ERR, 'collator', 'Key', rmsg)) except select.error as e: # hide (4,'Interupted system call') errors if e[0] != 4: self._cI.send((IYRI_WARN, 'collator', 'select', e)) except Exception as e: # handle catchall error self._cI.send((IYRI_WARN, 'collator', type(e).__name__, e)) # clean up & shut down try: try: # notify Nidus of sensor - check if radio(s), gpsd closed out for cs in rmap: self._submitradio(isots(), rmap[cs], None) if self._gid: self._submitgpsd(isots(), None) if self._sid: self._submitsensor(isots(), False) except psql.Error: self._conn.rollback() self._cI.send( (IYRI_WARN, 'collator', 'shutdown', "DB is corrupted")) if self._curs: self._curs.close() if self._conn: self._conn.close() if rf or df: msg = "{0} read frames, {1} dropped frames".format(rf, df) self._cI.send((IYRI_DONE, 'collator', 'Complete', msg)) self._cI.close() except (EOFError, IOError): pass except Exception as e: if self._cI and not self._cI.closed: msg = "Failed to clean up: {0}".format(e) self._cI.send((IYRI_WARN, 'collator', 'shutdown', msg))