Exemplo n.º 1
0
 def __init__(self, parent):
     super(FloorplanPanel, self).__init__(parent)
     self.parent = parent
     self.img_path = parent.img_path
     self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
     self.Bind(wx.EVT_LEFT_UP, self.onClick)
     self.Bind(wx.EVT_PAINT, self.on_paint)
     self.survey_points = []
     self.data_filename = '%s.json' % self.parent.survey_title
     if os.path.exists(self.data_filename):
         self._load_file(self.data_filename)
     self.collector = Collector(self.parent.interface, self.parent.server)
     self.parent.SetStatusText("Ready.")
Exemplo n.º 2
0
 def __init__(self, parent):
     super(FloorplanPanel, self).__init__(parent)
     self.parent = parent
     self.img_path = parent.img_path
     self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
     self.Bind(wx.EVT_LEFT_UP, self.onLeftUp)
     self.Bind(wx.EVT_LEFT_DOWN, self.onLeftDown)
     self.Bind(wx.EVT_MOTION, self.onMotion)
     self.Bind(wx.EVT_RIGHT_UP, self.onRightClick)
     self.Bind(wx.EVT_PAINT, self.on_paint)
     self.survey_points = []
     self._moving_point = None
     self._moving_x = None
     self._moving_y = None
     self.scale_x = 1.0
     self.scale_y = 1.0
     self.data_filename = '%s.json' % self.parent.survey_title
     if os.path.exists(self.data_filename):
         self._load_file(self.data_filename)
     self._duration = self.parent.duration
     self.collector = Collector(self.parent.server, self._duration,
                                self.parent.scanner)
     self.parent.SetStatusText("Ready.")
Exemplo n.º 3
0
class FloorplanPanel(wx.Panel):
    def __init__(self, parent):
        super(FloorplanPanel, self).__init__(parent)
        self.parent = parent
        self.img_path = parent.img_path
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
        self.Bind(wx.EVT_LEFT_UP, self.onClick)
        self.Bind(wx.EVT_PAINT, self.on_paint)
        self.survey_points = []
        self.data_filename = '%s.json' % self.parent.survey_title
        if os.path.exists(self.data_filename):
            self._load_file(self.data_filename)
        self.collector = Collector(self.parent.interface, self.parent.server)
        self.parent.SetStatusText("Ready.")

    def _load_file(self, fpath):
        with open(fpath, 'r') as fh:
            raw = fh.read()
        data = json.loads(raw)
        for point in data:
            p = SurveyPoint(self, point['x'], point['y'])
            p.set_result(point['result'])
            p.set_is_finished()
            self.survey_points.append(p)

    def OnEraseBackground(self, evt):
        """Add a picture to the background"""
        dc = evt.GetDC()
        if not dc:
            dc = wx.ClientDC(self)
            rect = self.GetUpdateRegion().GetBox()
            dc.SetClippingRect(rect)
        dc.Clear()
        bmp = wx.Bitmap(self.img_path)
        dc.DrawBitmap(bmp, 0, 0)

    def onClick(self, event):
        pos = event.GetPosition()
        self.parent.SetStatusText('Got click at: %s' % pos)
        self.survey_points.append(SurveyPoint(self, pos[0], pos[1]))
        self.Refresh()
        res = {}
        count = 0
        for protoname, udp in {'tcp': False, 'udp': True}.items():
            for suffix, reverse in {'': False, '-reverse': True}.items():
                if udp and reverse:
                    logger.warning('Skipping reverse UDP; always fails')
                    continue
                count += 1
                tmp = self.run_iperf(count, udp, reverse)
                if tmp is None:
                    # bail out; abort this survey point
                    del self.survey_points[-1]
                    self.parent.SetStatusText('Aborted; ready to retry...')
                    self.Refresh()
                    return
                # else success
                res['%s%s' % (protoname, suffix)] = {
                    x: getattr(tmp, x, None)
                    for x in RESULT_FIELDS
                }
        self.parent.SetStatusText('Running iwconfig...')
        self.Refresh()
        res['iwconfig'] = self.collector.run_iwconfig()
        self.parent.SetStatusText('Running iwscan...')
        self.Refresh()
        res['iwscan'] = self.collector.run_iwscan()
        self.survey_points[-1].set_result(res)
        self.survey_points[-1].set_is_finished()
        self.parent.SetStatusText('Saving to: %s' % self.data_filename)
        self.Refresh()
        res = json.loads([x.as_dict for x in self.survey_points],
                         cls=SafeEncoder)
        # iwlib returns the 'stats' block items 'level' and 'noise' as dBm values.
        # These values are stored in a 8bit encoded integer. Python deals with the encoding
        # of these integers as though they were normal ints. This code corrects that.
        # The encoding for the 8bit binary number is between -192 and 63 in iwlib.
        for pindex, point in enumerate(res):
            iwconfig = point['result']['iwconfig']['stats']
            iwconfig['level'] = iwconfig['level'] - 256 if iwconfig[
                'level'] > 63 else iwconfig['level']
            iwconfig['noise'] = iwconfig['noise'] - 256 if iwconfig[
                'noise'] > 63 else iwconfig['noise']

            iwscan = point['result']['iwscan']
            for index, item in enumerate(iwscan):
                level = item['stats']['level'] - 256 if item['stats'][
                    'level'] > 63 else item['stats']['level']
                noise = item['stats']['noise'] - 256 if item['stats'][
                    'noise'] > 63 else item['stats']['noise']
                iwscan[index]['stats']['level'] = level
                iwscan[index]['stats']['noise'] = noise
            res[pindex]['result']['iwconfig'] = iwconfig
            res[pindex]['result']['iwscan'] = iwscan

        with open(self.data_filename, 'w') as fh:
            fh.write(json.dumps(res))
        self.parent.SetStatusText('Saved to %s; ready...' % self.data_filename)
        self.Refresh()

    def warn(self, message, caption='Warning!'):
        dlg = wx.MessageDialog(self.parent, message, caption,
                               wx.OK | wx.ICON_WARNING)
        dlg.ShowModal()
        dlg.Destroy()

    def YesNo(self, question, caption='Yes or no?'):
        dlg = wx.MessageDialog(self.parent, question, caption,
                               wx.YES_NO | wx.ICON_QUESTION)
        result = dlg.ShowModal() == wx.ID_YES
        dlg.Destroy()
        return result

    def run_iperf(self, count, udp, reverse):
        self.parent.SetStatusText('Running iperf %d/3 (udp=%s, reverse=%s)' %
                                  (count, udp, reverse))
        self.Refresh()
        tmp = self.collector.run_iperf(udp, reverse)
        if tmp.error is None:
            return tmp
        # else this is an error
        if tmp.error.startswith('unable to connect to server'):
            self.warn('ERROR: Unable to connect to iperf server. Aborting.')
            return None
        if self.YesNo('iperf error: %s. Retry?' % tmp.error):
            self.Refresh()
            return self.run_iperf(count, udp, reverse)
        # else bail out
        return tmp

    def on_paint(self, event=None):
        dc = wx.ClientDC(self)
        for p in self.survey_points:
            p.draw(dc)
Exemplo n.º 4
0
class FloorplanPanel(wx.Panel):
    def __init__(self, parent):
        super(FloorplanPanel, self).__init__(parent)
        self.parent = parent
        self.img_path = parent.img_path
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
        self.Bind(wx.EVT_LEFT_UP, self.onLeftUp)
        self.Bind(wx.EVT_LEFT_DOWN, self.onLeftDown)
        self.Bind(wx.EVT_MOTION, self.onMotion)
        self.Bind(wx.EVT_RIGHT_UP, self.onRightClick)
        self.Bind(wx.EVT_PAINT, self.on_paint)
        self.survey_points = []
        self._moving_point = None
        self._moving_x = None
        self._moving_y = None
        self.data_filename = '%s.json' % self.parent.survey_title
        if os.path.exists(self.data_filename):
            self._load_file(self.data_filename)
        self.collector = Collector(self.parent.interface, self.parent.server)
        self.parent.SetStatusText("Ready.")

    def _load_file(self, fpath):
        with open(fpath, 'r') as fh:
            raw = fh.read()
        data = json.loads(raw)
        for point in data:
            p = SurveyPoint(self, point['x'], point['y'])
            p.set_result(point['result'])
            p.set_is_finished()
            self.survey_points.append(p)

    def OnEraseBackground(self, evt):
        """Add a picture to the background"""
        dc = evt.GetDC()
        if not dc:
            dc = wx.ClientDC(self)
            rect = self.GetUpdateRegion().GetBox()
            dc.SetClippingRect(rect)
        dc.Clear()
        bmp = wx.Bitmap(self.img_path)
        dc.DrawBitmap(bmp, 0, 0)

    def onRightClick(self, event):
        x, y = event.GetPosition()
        point = None
        for p in self.survey_points:
            # important to iterate the whole list, so we find the most recent
            if p.includes_point(x, y):
                point = p
        if point is None:
            self.parent.SetStatusText(f"No survey point found at ({x}, {y})")
            self.Refresh()
            return
        # ok, we have a point to remove
        point.draw(wx.ClientDC(self), color='blue')
        res = self.YesNo(f'Remove point at ({x}, {y}) shown in blue?')
        if not res:
            self.parent.SetStatusText('Not removing point.')
            self.Refresh()
            return
        self.survey_points.remove(point)
        self.parent.SetStatusText(f'Removed point at ({x}, {y})')
        self.Refresh()
        self._write_json()

    def onLeftDown(self, event):
        x, y = event.GetPosition()
        point = None
        for p in self.survey_points:
            # important to iterate the whole list, so we find the most recent
            if p.includes_point(x, y):
                point = p
        if point is None:
            self.parent.SetStatusText(f"No survey point found at ({x}, {y})")
            self.Refresh()
            return
        self._moving_point = point
        self._moving_x = point.x
        self._moving_y = point.y
        point.draw(wx.ClientDC(self), color='blue')

    def onLeftUp(self, event):
        if self._moving_point is None:
            self._do_measurement(event.GetPosition())
            return
        x, y = event.GetPosition()
        oldx = self._moving_point.x
        oldy = self._moving_point.y
        self._moving_point.x = x
        self._moving_point.y = y
        self._moving_point.draw(wx.ClientDC(self), color='red')
        res = self.YesNo(
            f'Move point from blue ({oldx}, {oldy}) to red ({x}, {y})?')
        if not res:
            self._moving_point.x = self._moving_x
            self._moving_point.y = self._moving_y
        self._moving_point = None
        self._moving_x = None
        self._moving_y = None
        self.Refresh()
        self._write_json()

    def onMotion(self, event):
        if self._moving_point is None:
            return
        x, y = event.GetPosition()
        dc = wx.ClientDC(self)
        self._moving_point.erase(dc)
        self._moving_point.x = x
        self._moving_point.y = y
        self._moving_point.draw(dc, color='red')

    def _check_bssid(self, data):
        if self.parent.bssid is None:
            return True
        bssid = data['Access Point'].decode().lower()
        if bssid == self.parent.bssid:
            return True
        msg = f'ERROR: Expected BSSID {self.parent.bssid} but found ' \
              f'BSSID {iwc["Access Point"]}'
        self.parent.SetStatusText(msg)
        self.Refresh()
        self.warn(msg)
        return False

    def _do_measurement(self, pos):
        self.parent.SetStatusText('Got click at: %s' % pos)
        self.survey_points.append(SurveyPoint(self, pos[0], pos[1]))
        self.Refresh()
        res = {}
        count = 0
        iwc = self.collector.run_iwconfig()
        if not self._check_bssid(iwc):
            return
        for protoname, udp in {'tcp': False, 'udp': True}.items():
            for suffix, reverse in {'': False, '-reverse': True}.items():
                if udp and reverse:
                    logger.warning('Skipping reverse UDP; always fails')
                    continue
                count += 1
                tmp = self.run_iperf(count, udp, reverse)
                if tmp is None:
                    # bail out; abort this survey point
                    del self.survey_points[-1]
                    self.parent.SetStatusText('Aborted; ready to retry...')
                    self.Refresh()
                    return
                # else success
                res['%s%s' % (protoname, suffix)] = {
                    x: getattr(tmp, x, None)
                    for x in RESULT_FIELDS
                }
        self.parent.SetStatusText('Running iwconfig...')
        self.Refresh()
        res['iwconfig'] = self.collector.run_iwconfig()
        if not self._check_bssid(res['iwconfig']):
            del self.survey_points[-1]
            return
        self.Refresh()
        if self.parent.scan:
            self.parent.SetStatusText('Running iwscan...')
            self.Refresh()
            res['iwscan'] = self.collector.run_iwscan()
        self.survey_points[-1].set_result(res)
        self.survey_points[-1].set_is_finished()
        self.parent.SetStatusText('Saving to: %s' % self.data_filename)
        self.Refresh()
        self._write_json()
        self._ding()

    def _ding(self):
        if self.parent.ding_path is None:
            return
        subprocess.call([self.parent.ding_command, self.parent.ding_path])

    def _write_json(self):
        res = json.dumps([x.as_dict for x in self.survey_points],
                         cls=SafeEncoder)
        with open(self.data_filename, 'w') as fh:
            fh.write(res)
        self.parent.SetStatusText('Saved to %s; ready...' % self.data_filename)
        self.Refresh()

    def warn(self, message, caption='Warning!'):
        dlg = wx.MessageDialog(self.parent, message, caption,
                               wx.OK | wx.ICON_WARNING)
        dlg.ShowModal()
        dlg.Destroy()

    def YesNo(self, question, caption='Yes or no?'):
        dlg = wx.MessageDialog(self.parent, question, caption,
                               wx.YES_NO | wx.ICON_QUESTION)
        result = dlg.ShowModal() == wx.ID_YES
        dlg.Destroy()
        return result

    def run_iperf(self, count, udp, reverse):
        self.parent.SetStatusText('Running iperf %d/3 (udp=%s, reverse=%s)' %
                                  (count, udp, reverse))
        self.Refresh()
        tmp = self.collector.run_iperf(udp, reverse)
        if tmp.error is None:
            return tmp
        # else this is an error
        if tmp.error.startswith('unable to connect to server'):
            self.warn('ERROR: Unable to connect to iperf server. Aborting.')
            return None
        if self.YesNo('iperf error: %s. Retry?' % tmp.error):
            self.Refresh()
            return self.run_iperf(count, udp, reverse)
        # else bail out
        return tmp

    def on_paint(self, event=None):
        dc = wx.ClientDC(self)
        for p in self.survey_points:
            p.draw(dc)
Exemplo n.º 5
0
class FloorplanPanel(wx.Panel):
    def __init__(self, parent):
        super(FloorplanPanel, self).__init__(parent)
        self.parent = parent
        self.img_path = parent.img_path
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
        self.Bind(wx.EVT_LEFT_UP, self.onLeftUp)
        self.Bind(wx.EVT_LEFT_DOWN, self.onLeftDown)
        self.Bind(wx.EVT_MOTION, self.onMotion)
        self.Bind(wx.EVT_RIGHT_UP, self.onRightClick)
        self.Bind(wx.EVT_PAINT, self.on_paint)
        self.survey_points = []
        self._moving_point = None
        self._moving_x = None
        self._moving_y = None
        self.scale_x = 1.0
        self.scale_y = 1.0
        self.data_filename = '%s.json' % self.parent.survey_title
        if os.path.exists(self.data_filename):
            self._load_file(self.data_filename)
        self._duration = self.parent.duration
        self.collector = Collector(self.parent.server, self._duration,
                                   self.parent.scanner)
        self.parent.SetStatusText("Ready.")

    def _load_file(self, fpath):
        with open(fpath, 'r') as fh:
            raw = fh.read()
        data = json.loads(raw)
        if 'survey_points' not in data:
            logger.error('Trying to load incompatible JSON file')
            exit(1)
        for point in data['survey_points']:
            p = SurveyPoint(self, point['x'], point['y'])
            p.set_result(point['result'])
            p.set_is_finished()
            self.survey_points.append(p)

    def OnEraseBackground(self, evt):
        """Add a picture to the background"""
        dc = evt.GetDC()
        if not dc:
            dc = wx.ClientDC(self)
            rect = self.GetUpdateRegion().GetBox()
            dc.SetClippingRect(rect)
        dc.Clear()

        # Get window size
        W, H = self.GetSize()

        # Load floorplan
        bmp = wx.Bitmap(self.img_path)
        image = wx.Bitmap.ConvertToImage(bmp)

        # Store scaling factors for pixel corrections
        self.scale_x = image.GetWidth() / W
        self.scale_y = image.GetHeight() / H

        # Scale image to window size
        logger.debug("Scaling image to {} x {}".format(W, H))
        image = image.Scale(W, H, wx.IMAGE_QUALITY_HIGH)

        # Draw image
        scaled_bmp = wx.Bitmap(image)
        dc.DrawBitmap(scaled_bmp, 0, 0)

    # Get X and Y coordinated scaled to ABSOLUTE coordinates of the floorplan
    def get_xy(self, event):
        X, Y = event.GetPosition()
        W, H = self.GetSize()
        x = int(X * self.scale_x)
        y = int(Y * self.scale_y)
        return [x, y]

    def onRightClick(self, event):
        x, y = self.get_xy(event)
        point = None
        for p in self.survey_points:
            # important to iterate the whole list, so we find the most recent
            if p.includes_point(x, y):
                point = p
        if point is None:
            self.parent.SetStatusText(f"No survey point found at ({x}, {y})")
            self.Refresh()
            return
        # ok, we have a point to remove
        point.draw(wx.ClientDC(self), color='blue')
        res = self.YesNo(f'Remove point at ({x}, {y}) shown in blue?')
        if not res:
            self.parent.SetStatusText('Not removing point.')
            self.Refresh()
            return
        self.survey_points.remove(point)
        self.parent.SetStatusText(f'Removed point at ({x}, {y})')
        self.Refresh()
        self._write_json()

    def onLeftDown(self, event):
        x, y = self.get_xy(event)
        point = None
        for p in self.survey_points:
            # important to iterate the whole list, so we find the most recent
            if p.includes_point(x, y):
                point = p
        if point is None:
            self.parent.SetStatusText(f"No survey point found at ({x}, {y})")
            self.Refresh()
            return
        self._moving_point = point
        self._moving_x = point.x
        self._moving_y = point.y
        point.draw(wx.ClientDC(self), color='lightblue')

    def onLeftUp(self, event):
        x, y = pos = self.get_xy(event)
        if self._moving_point is None:
            self._do_measurement(pos)
            return
        oldx = self._moving_point.x
        oldy = self._moving_point.y
        self._moving_point.x = x
        self._moving_point.y = y
        self._moving_point.draw(wx.ClientDC(self), color='lightblue')
        res = self.YesNo(f'Move point from ({oldx}, {oldy}) to ({x}, {y})?')
        if not res:
            self._moving_point.x = self._moving_x
            self._moving_point.y = self._moving_y
        self._moving_point = None
        self._moving_x = None
        self._moving_y = None
        self.Refresh()
        self._write_json()

    def onMotion(self, event):
        if self._moving_point is None:
            return
        x, y = pos = self.get_xy(event)
        dc = wx.ClientDC(self)
        self._moving_point.erase(dc)
        self._moving_point.x = x
        self._moving_point.y = y
        self._moving_point.draw(dc, color='lightblue')

    def _check_bssid(self):
        # Return early if BSSID is not to be verified
        if self.parent.bssid is None:
            return True
        # Get BSSID from link
        bssid = self.collector.scanner.get_current_bssid()
        # Compare BSSID, exit early on match
        if bssid == self.parent.bssid:
            return True
        # Error logging
        logger.error('Expected BSSID %s but found BSSID %s from kernel',
                     self.parent.bssid, bssid)
        msg = f'ERROR: Expected BSSID {self.parent.bssid} but found ' \
              f'BSSID {bssid}'
        self.parent.SetStatusText(msg)
        self.Refresh()
        self.warn(msg)
        return False

    def _abort(self, reason):
        self.survey_points[-1].set_is_failed()
        self.parent.SetStatusText('Aborted: {}'.format(reason))
        self.Refresh()

    def _do_measurement(self, pos):
        self.parent.SetStatusText('Starting survey...')
        # Add new survey point
        self.survey_points.append(SurveyPoint(self, pos[0], pos[1]))
        # Delete failed survey points
        self.survey_points = [p for p in self.survey_points if not p.is_failed]
        self.Refresh()
        res = {}
        count = 0
        # Number of steps in total (for the progress computation)
        steps = 5
        # Check if we are connected to an AP, all the
        # rest doesn't any sense otherwise
        if not self.collector.check_associated():
            self._abort("Not connected to an access point")
            return
        # Check BSSID
        if not self._check_bssid():
            self._abort("BSSID check failed")
            return

        # Skip iperf test if empty server string was given
        if self.collector._iperf_server is not None:
            for protoname, udp in {'tcp': False, 'udp': True}.items():
                for suffix, reverse in {'': False, '-reverse': True}.items():
                    # Update progress mark
                    self.survey_points[-1].set_progress(count, steps)
                    count += 1

                    # Check if we're still connected to the same AP
                    if not self._check_bssid():
                        self._abort("BSSID check failed")
                        return

                    # Start iperf test
                    tmp = self.run_iperf(count, udp, reverse)
                    if tmp is None:
                        # bail out; abort this survey point
                        self._abort("iperf test failed")
                        return
                    # else success
                    res['%s%s' % (protoname, suffix)] = {
                        x: getattr(tmp, x, None)
                        for x in RESULT_FIELDS
                    }

        # Check if we're still connected to the same AP
        if not self._check_bssid():
            self._abort("BSSID check failed")
            return

        # Get all signal metrics from nl
        self.parent.SetStatusText(
            'Getting signal metrics (Quality, signal strength, etc.)...')
        self.Refresh()
        data = self.collector.scanner.get_iface_data()
        # Merge dicts
        res = {**res, **data}
        self.survey_points[-1].set_progress(4, steps)

        # Scan APs in the neighborhood
        if self.parent.scan:
            self.parent.SetStatusText(
                'Scanning all access points within reach...')
            self.Refresh()
            res['scan_results'] = self.collector.scan_all_access_points()
        self.survey_points[-1].set_progress(5, steps)

        # Save results and mark survey point as complete
        self.survey_points[-1].set_result(res)
        self.survey_points[-1].set_is_finished()
        self.parent.SetStatusText('Saving to: %s' % self.data_filename)
        self.Refresh()
        self._write_json()
        self._ding()

    def _ding(self):
        if self.parent.ding_path is None:
            return
        subprocess.call([self.parent.ding_command, self.parent.ding_path])

    def _write_json(self):
        # Only store finished survey points
        survey_points = [
            p.as_dict for p in self.survey_points if p.is_finished
        ]

        res = json.dumps(
            {
                'img_path': self.img_path,
                'survey_points': survey_points
            },
            cls=SafeEncoder,
            indent=2)
        with open(self.data_filename, 'w') as fh:
            fh.write(res)
        self.parent.SetStatusText('Saved to %s; ready...' % self.data_filename)
        self.Refresh()

    def warn(self, message, caption='Warning!'):
        dlg = wx.MessageDialog(self.parent, message, caption,
                               wx.OK | wx.ICON_WARNING)
        dlg.ShowModal()
        dlg.Destroy()

    def YesNo(self, question, caption='Yes or no?'):
        dlg = wx.MessageDialog(self.parent, question, caption,
                               wx.YES_NO | wx.ICON_QUESTION)
        result = dlg.ShowModal() == wx.ID_YES
        dlg.Destroy()
        return result

    def run_iperf(self, count, udp, reverse):
        proto = "UDP" if udp else "TCP"
        # iperf3 default direction is uploading to the server
        direction = "Download" if reverse else "Upload"
        self.parent.SetStatusText(
            'Running iperf %d/4: %s (%s) - takes %i seconds' %
            (count, direction, proto, self._duration))
        self.Refresh()
        tmp = self.collector.run_iperf(udp, reverse)
        if tmp.error is None:
            return tmp
        # else this is an error
        if tmp.error.startswith('unable to connect to server'):
            self.warn(
                'ERROR: Unable to connect to iperf server at {}. Aborting.'.
                format(self.collector._iperf_server))
            return None
        if self.YesNo('iperf error: %s. Retry?' % tmp.error):
            self.Refresh()
            return self.run_iperf(count, udp, reverse)
        # else bail out
        return tmp

    def on_paint(self, event=None):
        dc = wx.ClientDC(self)
        for p in self.survey_points:
            p.draw(dc)
Exemplo n.º 6
0
class FloorplanPanel(wx.Panel):
    def __init__(self, parent):
        super(FloorplanPanel, self).__init__(parent)
        self.parent = parent
        self.img_path = parent.img_path
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
        self.Bind(wx.EVT_LEFT_UP, self.onClick)
        self.Bind(wx.EVT_PAINT, self.on_paint)
        self.survey_points = []
        self.data_filename = '%s.json' % self.parent.survey_title
        if os.path.exists(self.data_filename):
            self._load_file(self.data_filename)
        self.collector = Collector(self.parent.interface, self.parent.server)
        self.parent.SetStatusText("Ready.")

    def _load_file(self, fpath):
        with open(fpath, 'r') as fh:
            raw = fh.read()
        data = json.loads(raw)
        for point in data:
            p = SurveyPoint(self, point['x'], point['y'])
            p.set_result(point['result'])
            p.set_is_finished()
            self.survey_points.append(p)

    def OnEraseBackground(self, evt):
        """Add a picture to the background"""
        dc = evt.GetDC()
        if not dc:
            dc = wx.ClientDC(self)
            rect = self.GetUpdateRegion().GetBox()
            dc.SetClippingRect(rect)
        dc.Clear()
        bmp = wx.Bitmap(self.img_path)
        window_size = self.GetSize()
        image = wx.Bitmap.ConvertToImage(bmp)
        image = image.Scale(window_size[0], window_size[1],
                            wx.IMAGE_QUALITY_HIGH)
        bmp = wx.Bitmap(image)
        dc.DrawBitmap(bmp, 0, 0)

    def onClick(self, event):
        pos = event.GetPosition()
        self.parent.SetStatusText('Got click at: %s' % pos)
        window_size = self.GetSize()
        relposx = (pos[0] / window_size[0])
        relposy = (pos[1] / window_size[1])
        self.survey_points.append(SurveyPoint(self, relposx, relposy))

        self.Refresh()
        res = {}
        if self.collector._iperf_server != '':
            count = 0
            for protoname, udp in {'tcp': False, 'udp': True}.items():
                for suffix, reverse in {'': False, '-reverse': True}.items():
                    if udp and reverse:
                        logger.warning('Skipping reverse UDP; always fails')
                        continue
                    count += 1
                    tmp = self.run_iperf(count, udp, reverse)
                    if tmp is None:
                        # bail out; abort this survey point
                        del self.survey_points[-1]
                        self.parent.SetStatusText('Aborted; ready to retry...')
                        self.Refresh()
                        return
                    # else success
                    res['%s%s' % (protoname, suffix)] = {
                        x: getattr(tmp, x, None)
                        for x in RESULT_FIELDS
                    }
        else:
            logger.warning("No Iperf server set. Skipping Iperf scan.")
        self.parent.SetStatusText('Running iwconfig...')
        self.Refresh()
        res['iwconfig'] = self.collector.run_iwconfig()
        self.parent.SetStatusText('Running iwscan...')
        self.Refresh()
        res['iwscan'] = self.collector.run_iwscan()
        self.survey_points[-1].set_result(res)
        self.survey_points[-1].set_is_finished()
        self.parent.SetStatusText('Saving to: %s' % self.data_filename)
        self.Refresh()
        res = json.dumps([x.as_dict for x in self.survey_points],
                         cls=SafeEncoder)
        with open(self.data_filename, 'w') as fh:
            fh.write(res)
        self.parent.SetStatusText('Saved to %s; ready...' % self.data_filename)
        self.Refresh()

    def warn(self, message, caption='Warning!'):
        dlg = wx.MessageDialog(self.parent, message, caption,
                               wx.OK | wx.ICON_WARNING)
        dlg.ShowModal()
        dlg.Destroy()

    def YesNo(self, question, caption='Yes or no?'):
        dlg = wx.MessageDialog(self.parent, question, caption,
                               wx.YES_NO | wx.ICON_QUESTION)
        result = dlg.ShowModal() == wx.ID_YES
        dlg.Destroy()
        return result

    def run_iperf(self, count, udp, reverse):
        self.parent.SetStatusText('Running iperf %d/3 (udp=%s, reverse=%s)' %
                                  (count, udp, reverse))
        self.Refresh()
        tmp = self.collector.run_iperf(udp, reverse)
        if tmp.error is None:
            return tmp
        # else this is an error
        if tmp.error.startswith('unable to connect to server'):
            self.warn('ERROR: Unable to connect to iperf server. Aborting.')
            return None
        if self.YesNo('iperf error: %s. Retry?' % tmp.error):
            self.Refresh()
            return self.run_iperf(count, udp, reverse)
        # else bail out
        return tmp

    def on_paint(self, event=None):
        dc = wx.ClientDC(self)
        for p in self.survey_points:
            p.draw(dc)
Exemplo n.º 7
0
 def run(self, ifname, server):
     if os.geteuid() != 0:
         raise RuntimeError('ERROR: This script must be run as root/sudo.')
     c = Collector(ifname, server)
     print(c.run())