Exemple #1
0
class PlotPanel(object):
    def __init__(self, parent, frame):
        self.parent = parent  # we are drawing on our parent, so dc comes from this
        self.frame = frame  # the frame owns any controls we might need to update

        parent.SetDropTarget(TextDropTarget(
            self))  # calls self.OnDropText when drag and drop complete

        self.width = 800
        self.height = 200
        self.margin = min(self.height / 10, 20)
        self.font = wx.Font(self.margin / 2, wx.DEFAULT, wx.FONTSTYLE_NORMAL,
                            wx.FONTWEIGHT_NORMAL)
        self.pixmap = wx.EmptyBitmap(self.width, self.height)
        self.plot_size = self.width
        self.max = -1e32
        self.min = 1e32
        self.plot_interval = 200
        self.plots = {}
        self.auto_scale = True
        self.offset = 0.0
        self.scale = 1.0
        self.x_axis = None

        messages_xml_map.parse_messages()

        self.ivy_interface = IvyMessagesInterface(_IVY_APPNAME)

        # start the timer
        self.timer = wx.FutureCall(self.plot_interval, self.OnTimer)

    def SetPlotInterval(self, value):
        self.plot_interval = value
        self.timer.Restart(self.plot_interval)
        self.timer = wx.FutureCall(self.plot_interval, self.OnTimer)

    def SetAutoScale(self, value):
        self.auto_scale = value

    def SetMin(self, value):
        self.min = value

    def SetMax(self, value):
        self.max = value

    def Pause(self, pause):
        if pause:
            self.timer.Stop()
        else:
            self.timer = wx.FutureCall(self.plot_interval, self.OnTimer)

    def ResetScale(self):
        self.max = -1e32
        self.min = 1e32

    def OnClose(self):
        self.timer.Stop()
        try:
            IvyStop()
        except IvyIllegalStateError as e:
            print(e)

    def OnErase(self, event):
        pass

    def ShowMessagePicker(self, parent):
        frame = messagepicker.MessagePicker(parent, self.BindCurve,
                                            self.ivy_interface)
        frame.Show()

    def OnDropText(self, data):
        [ac_id, category, message, field,
         scale] = data.encode('ASCII').split(':')
        self.BindCurve(int(ac_id), message, field, scale=float(scale))

    def OnIvyMsg(self, agent, *larg):
        # print(larg[0])
        data = larg[0].split(' ')
        ac_id = int(data[0])
        message = data[1]

        if ac_id not in self.plots:
            return

        if message not in self.plots[ac_id]:
            return

        for field in self.plots[ac_id][message]:
            plot = self.plots[ac_id][message][field]
            ix = messages_xml_map.message_dictionary["telemetry"][
                message].index(field)
            point = float(data[ix + 2])

            if self.x_axis is None or self.x_axis.id != plot.id:
                if self.auto_scale:
                    scaled_point = (point + plot.offset) * plot.scale
                    self.max = max(self.max, scaled_point)
                    self.min = min(self.min, scaled_point)

            if self.x_axis is not None:
                plot.index = self.x_axis.index
            plot.AddPoint(point, self.x_axis)

    def BindCurve(self,
                  ac_id,
                  message,
                  field,
                  color=None,
                  use_as_x=False,
                  scale=1.0):
        # -- add this telemetry to our list of things to plot ...
        message_string = _IVY_STRING % (ac_id, message)
        # print('Binding to %s' % message_string)

        if ac_id not in self.plots:
            self.plots[ac_id] = {}

        if message not in self.plots[ac_id]:
            self.plots[ac_id][message] = {}

        if field in self.plots[ac_id][message]:
            self.plots[ac_id][message][field].color = wx.Color(
                random.randint(0, 255), random.randint(0, 255),
                random.randint(0, 255))
            return

        ivy_id = self.ivy_interface.bind_raw(self.OnIvyMsg,
                                             str(message_string))
        title = '%i:%s:%s' % (ac_id, message, field)
        self.plots[ac_id][message][field] = PlotData(ivy_id, title,
                                                     self.plot_size, color,
                                                     scale)
        self.frame.AddCurve(ivy_id, title, use_as_x)
        if use_as_x:
            self.x_axis = self.plots[ac_id][message][field]

    def CalcMinMax(self, plot):
        if not self.auto_scale: return
        for x in plot.data:
            self.max = max(self.max, x)
            self.min = min(self.min, x)
            self.frame.SetMinMax(self.min, self.max)

    def FindPlotName(self, ivy_id):
        for ac_id in self.plots:
            for msg in self.plots[ac_id]:
                for field in self.plots[ac_id][msg]:
                    if self.plots[ac_id][msg][field].id == ivy_id:
                        return (ac_id, msg, field)
        return (None, None, None)

    def FindPlot(self, ivy_id):
        (ac_id, msg, field) = self.FindPlotName(ivy_id)
        if ac_id is None:
            return None

        return self.plots[ac_id][msg][field]

    def RemovePlot(self, ivy_id):
        (ac_id, msg, field) = self.FindPlotName(ivy_id)
        if ac_id is None:
            return

        if (self.x_axis is not None) and (self.x_axis.id == ivy_id):
            self.x_axis = None

        self.ivy_interface.unbind(ivy_id)
        del self.plots[ac_id][msg][field]
        if len(self.plots[ac_id][msg]) == 0:
            del self.plots[ac_id][msg]

    def OffsetPlot(self, ivy_id, offset):
        plot = self.FindPlot(ivy_id)
        if plot is None:
            return

        plot.SetOffset(offset)
        print('panel value: %.2f' % value)
        CalcMinMax(plot)

    def ScalePlot(self, ivy_id, offset):
        plot = self.FindPlot(ivy_id)
        if plot is None:
            return

        plot.SetScale(offset)
        CalcMinMax(plot)

    def SetRealTime(self, ivy_id, value):
        plot = self.FindPlot(ivy_id)
        if plot is None:
            return

        plot.SetRealTime(value)

    def SetXAxis(self, ivy_id):
        plot = self.FindPlot(ivy_id)
        if plot is None:
            return

        self.x_axis = plot

    def ClearXAxis(self):
        self.x_axis = None

    def OnSize(self, size):
        (width, height) = size
        if self.width == width and self.height == height:
            return

        self.pixmap = wx.EmptyBitmap(width, height)
        self.width = width
        self.height = height
        self.plot_size = width
        self.margin = min(self.height / 10, 20)
        self.font = wx.Font(self.margin / 2, wx.DEFAULT, wx.FONTSTYLE_NORMAL,
                            wx.FONTWEIGHT_NORMAL)

    def OnTimer(self):
        self.timer.Restart(self.plot_interval)
        self.frame.SetMinMax(self.min, self.max)
        self.DrawFrame()

    def DrawFrame(self):
        dc = wx.ClientDC(self.parent)
        bdc = wx.BufferedDC(dc, self.pixmap)
        bdc.SetBackground(wx.Brush("White"))
        bdc.Clear()

        self.DrawBackground(bdc, self.width, self.height)

        title_y = 2
        for ac_id in self.plots:
            for message in self.plots[ac_id]:
                for field in self.plots[ac_id][message]:
                    plot = self.plots[ac_id][message][field]
                    if (self.x_axis is not None) and (self.x_axis.id
                                                      == plot.id):
                        continue
                    title_height = plot.DrawTitle(bdc, 2, self.width, title_y)
                    plot.DrawCurve(bdc, self.width, self.height, self.margin,
                                   self.max, self.min, self.x_axis)

                    title_y += title_height + 2

    def DrawBackground(self, dc, width, height):

        # Time Graduations
        dc.SetFont(self.font)

        if self.x_axis is None:
            t = self.plot_interval * width
            t1 = "0.0s"
            t2 = "-%.1fs" % (t / 2000.0)
            t3 = "-%.1fs" % (t / 1000.0)
        else:
            x_max = self.x_axis.x_max
            x_min = self.x_axis.x_min
            t1 = "%.2f" % x_max
            t2 = "%.2f" % (x_min + (x_max - x_min) / 2.0)
            t3 = "%.2f" % x_min

        (w, h) = dc.GetTextExtent(t1)
        dc.DrawText(t1, width - w, height - h)
        # (w,h) = dc.GetTextExtent(t2)             #save time since h will be the same
        dc.DrawText(t2, width / 2, height - h)
        # (w,h) = dc.GetTextExtent(t3)             #save time since h will be the same
        dc.DrawText(t3, 0, height - h)

        # Y graduations
        if self.max == -1e32:
            return

        (_min_, _max_) = (self.min, self.max)
        if _max_ < _min_:  # prevent divide by zero or inversion
            (_min_, _max_) = (-1, 1)
        if _max_ == _min_:
            (_min_, _max_) = (_max_ - 0.5, _max_ + 0.5)

        delta = _max_ - _min_
        dy = (height - self.margin * 2) / delta
        scale = math.log10(delta)
        d = math.pow(10.0, math.floor(scale))
        u = d
        if delta < 2 * d:
            u = d / 5
        elif delta < 5 * d:
            u = d / 2
        tick_min = _min_ - math.fmod(_min_, u)
        for i in range(int(delta / u) + 1):
            tick = tick_min + float(i) * u
            s = str(tick)
            (w, h) = dc.GetTextExtent(s)
            y = height - self.margin - int((tick - _min_) * dy) - h / 2
            dc.DrawText(s, 0, y)
Exemple #2
0
class MessageInterface:

    """
    MessageInterface

    This is an abstraction layer used to simplify the use of paparazzi
    message interface, especially for time measurement and casting of
    message payload data (which sometimes stays in ascii)

    """

    def prettify_message(msg):
        """
        Sometimes IvyMessageInterface does not cast data to their binary types.
        This function cast all fields to their binary types. (supposed to be
        done by PprzMessage.payload_to_binay but does not seem to be working)

        It also measure reception time.
        """
        timestamp = time.time()
        fieldValues = []
        for fieldValue, fieldType in zip(msg.fieldvalues, msg.fieldtypes):
            if "int" in fieldType:
                castType = int
            elif "float" in fieldType:
                castType = float
            elif "string" in fieldType:
                castType = str
            elif "char" in fieldType:
                castType = int
            else:
                # Could not indentify type, leave field as is
                fieldValues.append(fieldValue)

            # Checking if is a list
            if '[' in fieldType:
                fieldValues.append([castType(value) for value in fieldValue])
            else:
                fieldValues.append(castType(fieldValue))
        msg.set_values(fieldValues)
        msg.timestamp = timestamp
        return msg


    def parse_pprz_msg(msg):
        """
        Alias to IvyMessageInterface.parse_pprz_msg, but with prettify_message
        called at the end to ensure all data are in binary format.
        """
        class Catcher:
            """
            This is a type specifically to catch result from
            IvyMessageInterface.parse_pprz_msg which only outputs result via a
            callback.
            """
            def set_message(self, aircraftId, message):
                self.message    = message
                self.aircraftId = str(aircraftId)
        catcher = Catcher()
        IvyMessagesInterface.parse_pprz_msg(catcher.set_message, msg)
        return [MessageInterface.prettify_message(catcher.message),
                catcher.aircraftId]


    def __init__(self, ivyBusAddress=None):
        
        if ivyBusAddress is None:
            ivyBusAddress = os.getenv('IVY_BUS')
            if ivyBusAddress is None:
                ivyBusAddress == ""
        self.messageInterface = IvyMessagesInterface(ivy_bus=ivyBusAddress)


    def bind(self, callback, ivyRegex):
        return self.messageInterface.subscribe(
            lambda sender, msg: callback(MessageInterface.prettify_message(msg)),
            ivyRegex)


    def bind_raw(self, callback, ivyRegex):
        return self.messageInterface.bind_raw(callback, ivyRegex)


    def unbind(self, bindId):
        self.messageInterface.unbind(bindId)

    
    def send(self, msg):
        return self.messageInterface.send(msg)
Exemple #3
0
class PlotPanel(object):
    def __init__(self, parent, frame):
        self.parent = parent  # we are drawing on our parent, so dc comes from this
        self.frame = frame  # the frame owns any controls we might need to update

        parent.SetDropTarget(TextDropTarget(self))  # calls self.OnDropText when drag and drop complete

        self.width = 800
        self.height = 200
        self.margin = min(self.height / 10, 20)
        self.font = wx.Font(self.margin / 2, wx.DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)
        self.pixmap = wx.EmptyBitmap(self.width, self.height)
        self.plot_size = self.width
        self.max = -1e32
        self.min = 1e32
        self.plot_interval = 200
        self.plots = {}
        self.auto_scale = True
        self.offset = 0.0
        self.scale = 1.0
        self.x_axis = None

        messages_xml_map.parse_messages()

        self.ivy_interface = IvyMessagesInterface(_IVY_APPNAME)

        # start the timer
        self.timer = wx.FutureCall(self.plot_interval, self.OnTimer)

    def SetPlotInterval(self, value):
        self.plot_interval = value
        self.timer.Restart(self.plot_interval)
        self.timer = wx.FutureCall(self.plot_interval, self.OnTimer)

    def SetAutoScale(self, value):
        self.auto_scale = value

    def SetMin(self, value):
        self.min = value

    def SetMax(self, value):
        self.max = value

    def Pause(self, pause):
        if pause:
            self.timer.Stop()
        else:
            self.timer = wx.FutureCall(self.plot_interval, self.OnTimer)

    def ResetScale(self):
        self.max = -1e32
        self.min = 1e32

    def OnClose(self):
        self.timer.Stop()
        try:
            IvyStop()
        except IvyIllegalStateError as e:
            print(e)

    def OnErase(self, event):
        pass

    def ShowMessagePicker(self, parent):
        frame = messagepicker.MessagePicker(parent, self.BindCurve, self.ivy_interface)
        frame.Show()

    def OnDropText(self, data):
        [ac_id, category, message, field, scale] = data.encode('ASCII').split(':')
        self.BindCurve(int(ac_id), message, field, scale=float(scale))

    def OnIvyMsg(self, agent, *larg):
        # print(larg[0])
        data = larg[0].split(' ')
        ac_id = int(data[0])
        message = data[1]

        if ac_id not in self.plots:
            return

        if message not in self.plots[ac_id]:
            return

        for field in self.plots[ac_id][message]:
            plot = self.plots[ac_id][message][field]
            ix = messages_xml_map.message_dictionary["telemetry"][message].index(field)
            point = float(data[ix + 2])

            if self.x_axis is None or self.x_axis.id != plot.id:
                if self.auto_scale:
                    scaled_point = (point + plot.offset) * plot.scale
                    self.max = max(self.max, scaled_point)
                    self.min = min(self.min, scaled_point)

            if self.x_axis is not None:
                plot.index = self.x_axis.index
            plot.AddPoint(point, self.x_axis)

    def BindCurve(self, ac_id, message, field, color=None, use_as_x=False, scale=1.0):
        # -- add this telemetry to our list of things to plot ...
        message_string = _IVY_STRING % (ac_id, message)
        # print('Binding to %s' % message_string)

        if ac_id not in self.plots:
            self.plots[ac_id] = {}

        if message not in self.plots[ac_id]:
            self.plots[ac_id][message] = {}

        if field in self.plots[ac_id][message]:
            self.plots[ac_id][message][field].color = wx.Color(random.randint(0, 255), random.randint(0, 255),
                                                               random.randint(0, 255))
            return

        ivy_id = self.ivy_interface.bind_raw(self.OnIvyMsg, str(message_string))
        title = '%i:%s:%s' % (ac_id, message, field)
        self.plots[ac_id][message][field] = PlotData(ivy_id, title, self.plot_size, color, scale)
        self.frame.AddCurve(ivy_id, title, use_as_x)
        if use_as_x:
            self.x_axis = self.plots[ac_id][message][field]

    def CalcMinMax(self, plot):
        if not self.auto_scale: return
        for x in plot.data:
            self.max = max(self.max, x)
            self.min = min(self.min, x)
            self.frame.SetMinMax(self.min, self.max)

    def FindPlotName(self, ivy_id):
        for ac_id in self.plots:
            for msg in self.plots[ac_id]:
                for field in self.plots[ac_id][msg]:
                    if self.plots[ac_id][msg][field].id == ivy_id:
                        return (ac_id, msg, field)
        return (None, None, None)

    def FindPlot(self, ivy_id):
        (ac_id, msg, field) = self.FindPlotName(ivy_id)
        if ac_id is None:
            return None

        return self.plots[ac_id][msg][field]

    def RemovePlot(self, ivy_id):
        (ac_id, msg, field) = self.FindPlotName(ivy_id)
        if ac_id is None:
            return

        if (self.x_axis is not None) and (self.x_axis.id == ivy_id):
            self.x_axis = None

        self.ivy_interface.unbind(ivy_id)
        del self.plots[ac_id][msg][field]
        if len(self.plots[ac_id][msg]) == 0:
            del self.plots[ac_id][msg]

    def OffsetPlot(self, ivy_id, offset):
        plot = self.FindPlot(ivy_id)
        if plot is None:
            return

        plot.SetOffset(offset)
        print('panel value: %.2f' % value)
        CalcMinMax(plot)

    def ScalePlot(self, ivy_id, offset):
        plot = self.FindPlot(ivy_id)
        if plot is None:
            return

        plot.SetScale(offset)
        CalcMinMax(plot)

    def SetRealTime(self, ivy_id, value):
        plot = self.FindPlot(ivy_id)
        if plot is None:
            return

        plot.SetRealTime(value)

    def SetXAxis(self, ivy_id):
        plot = self.FindPlot(ivy_id)
        if plot is None:
            return

        self.x_axis = plot

    def ClearXAxis(self):
        self.x_axis = None

    def OnSize(self, size):
        (width, height) = size
        if self.width == width and self.height == height:
            return

        self.pixmap = wx.EmptyBitmap(width, height)
        self.width = width
        self.height = height
        self.plot_size = width
        self.margin = min(self.height / 10, 20)
        self.font = wx.Font(self.margin / 2, wx.DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)

    def OnTimer(self):
        self.timer.Restart(self.plot_interval)
        self.frame.SetMinMax(self.min, self.max)
        self.DrawFrame()

    def DrawFrame(self):
        dc = wx.ClientDC(self.parent)
        bdc = wx.BufferedDC(dc, self.pixmap)
        bdc.SetBackground(wx.Brush("White"))
        bdc.Clear()

        self.DrawBackground(bdc, self.width, self.height)

        title_y = 2
        for ac_id in self.plots:
            for message in self.plots[ac_id]:
                for field in self.plots[ac_id][message]:
                    plot = self.plots[ac_id][message][field]
                    if (self.x_axis is not None) and (self.x_axis.id == plot.id):
                        continue
                    title_height = plot.DrawTitle(bdc, 2, self.width, title_y)
                    plot.DrawCurve(bdc, self.width, self.height, self.margin, self.max, self.min, self.x_axis)

                    title_y += title_height + 2

    def DrawBackground(self, dc, width, height):

        # Time Graduations
        dc.SetFont(self.font)

        if self.x_axis is None:
            t = self.plot_interval * width
            t1 = "0.0s"
            t2 = "-%.1fs" % (t / 2000.0)
            t3 = "-%.1fs" % (t / 1000.0)
        else:
            x_max = self.x_axis.x_max
            x_min = self.x_axis.x_min
            t1 = "%.2f" % x_max
            t2 = "%.2f" % (x_min + (x_max - x_min) / 2.0)
            t3 = "%.2f" % x_min

        (w, h) = dc.GetTextExtent(t1)
        dc.DrawText(t1, width - w, height - h)
        # (w,h) = dc.GetTextExtent(t2)             #save time since h will be the same
        dc.DrawText(t2, width / 2, height - h)
        # (w,h) = dc.GetTextExtent(t3)             #save time since h will be the same
        dc.DrawText(t3, 0, height - h)

        # Y graduations
        if self.max == -1e32:
            return

        (_min_, _max_) = (self.min, self.max)
        if _max_ < _min_:  # prevent divide by zero or inversion
            (_min_, _max_) = (-1, 1)
        if _max_ == _min_:
            (_min_, _max_) = (_max_ - 0.5, _max_ + 0.5)

        delta = _max_ - _min_
        dy = (height - self.margin * 2) / delta
        scale = math.log10(delta)
        d = math.pow(10.0, math.floor(scale))
        u = d
        if delta < 2 * d:
            u = d / 5
        elif delta < 5 * d:
            u = d / 2
        tick_min = _min_ - math.fmod(_min_, u)
        for i in range(int(delta / u) + 1):
            tick = tick_min + float(i) * u
            s = str(tick)
            (w, h) = dc.GetTextExtent(s)
            y = height - self.margin - int((tick - _min_) * dy) - h / 2
            dc.DrawText(s, 0, y)