Software Design MSC Editor

From TASTE
Jump to: navigation, search

Overview

Versions

  • 1- The authoritative are the grafical scene class, It is the model of data.
  • 2- In the future we have to achieve that the data will be out the scene, and the scene only be entrust to paint the element, but it have not the control of the data.

Parser

Must be parsed ITU T.120 Recomendation

The parser is developed with PLY(Python Lex-Yacc). PLY is an implementation of lex and yacc parsing tools for Python. We can obtain PLY from PLY Home Page

This part describes the whole development process. At the begining there is description of parsing process.

Parsing

When the parser starts to read the input, it expect to read the document head and then descriptions of MSCs.

  • When parser read the head of document creates a Msc Document Data class. And It is the new scope and the root.
  • Then the parser expects MSC description, this starts with the name. Then, the parser creates a bMSC data class and put it like new scope.
  • When bMSc is created the parser expects starts of instances. When it has read the beginning of instance (I1 : instance) it creates an instance with the name, add the instance to bMSC in the scope.

Problems With Grammar Deffinition ITU T Z.120 (02/2011)

I think that the latest version of ITU-T Z.120 recomendation for Message Sequence chart is not correctly define. I have problems to define the events inside instance declaration. The ITU-T define:

<msc body> :== <msc statement> | <instance decl statement>
<msc statement> = <text definition> | <event definition>
<event definition> :== <instance name> : <instance event list>
<instance event list> :== <instance head statement> <instance event>* {<instance end statement> | ...}
<instance decl statement> :== <instance head statement> <instance event>* {<instance end statement | ...}
<instance head statement> :== 'instance' [<instance kind>] ... <end>
<instance end statement> :== 'endinstancd' <end>

where:

*<instance kind> is name of class
*<end> is a optional comment followed for ';'

with this grammar is not allowed obtain "Event oriented textual syntax" examples showed in the same recommendation. This are one of the examples:

...
Initiator: instance process ISAP_Manager_Ini;
Responder: instance;
all: condition when Disconnected;
Initiator: in ICONreq from env;
           out ICON to Responder;
Responder: in ICON from Initiator;
           out ICONind to env;
           condition wait;
           endinstance;
Initiator: action 'setting_counter';
           starttimer T (5);
           condition wait;
           timeout T;
           out IDISind to env;
           condition disconnected;
           endinstance;
endmsc;

And other mistake is describe events of no name instances. It is possible using the rule <instance decl statement>. Example:

instance;
    out msg_1 to inst_2;
    in msg_2 from inst_3;
endinstance

For this reasons I decide to develop the parser using this grammar:

<>

Lexer

Keywords admited

  • from
  • in
  • instance
  • msc
  • mscdocument
  • out
  • to

Parser

The MscParser is a class that defined a Python Lex-Yacc (PLY) parser for MSC Grammar.To use this class is necessary to have installed PLY package.

When the MscParser object is created, you can pass all the options that is allowed to yacc.yacc method in PLY. The one options that can't be passed are "tabmodule=".

The parser generate a mscparsetab.py module the first time that instance of class is created, It is for efficiency. To prevent MscParser from generating any kind of parser table file, use: "write_tables=0" as parameter in the object constructor.

To parser a file you must call "parse" method with the file path as argument. This function return a MscDocument object. It is a root node AST Tree of mscCore objects.

Use the parser

Example of use:

import sys
import mscParser
if __name__ == '__main__':
    fileRead = open(sys.argv[1])
    parser = mscParser.MscParser()
    ast = parser.parse(fileRead.read())
    #Print the Msc Objects Tree
    ast.show()

Design

The Msc Data are stored in hierarchical tree of Msc Core Objects.

Msc Core Items

Msc Core Items are the principal data objects. All the libraries (export to msc, import from textual file..) and almost widgets created for the application works using msc core data objects.

mscCore is the principal package of classes. All elements of this package subclass MscElement, it is a subclass of QOBject and allow Signals. One of the most important signals are 'dataHasChanged' and 'deleted'.

dataHasChanged signal must be emmited when data included into the subclassing class is changed. This allow other class, like MscGraphItems associated, knows that internal data from msc element has changed and it can be updated readind the new data.

'deleted' signal is emmited before the object will be deleted, and allow to update the internal structures of other objects that contain references to this object, and remove these and disconnect the signal associated before the element will be destroyed.

MSC Graphical Items

Are wrappers of Msc Core Items. Each Msc Graphical Item has asociation 1 to 1 of Msc Core Objects. The Msc Graphical Items are only a graphical wrapper of Msc Core Objects, in this way the Msc core Items can be showed. This class inherited from QGraphicsItem so it can be showed in standard QGraphicsView.

MscGraphItem Class

The class MscGraphItem is the base class for all of elements that can be drawed in the Taste Msc Editor. It is a subclass of QGraphicObject, but it can be showed in standard QGraphicsScene.

The MscGraphItem are developed to have a 1 to 1 relation with MscCore objects, It provide a methods to set and get the MscCore data object associated with the graphical object. The objective of this design is make MscGraphItems like wrappers of MscCore Object Data. This methods are:

  • mscData()
  • setMscData()

In this way when data (no graphical: name, kind, order) of MscGraphItem is changed inside the scene using keyboard and mouse the pourpuse is to make the change into MscCoreClass associated and when change is produced, the MscCore class emit DataChange signal and then graphical object receive the signal and read the data of associated object and update showed.

MscGraphItem reimplement boundingRect(), contextMenu() functions.

It has a flag "resizable" than can be set with setResizable(value) function. When the class is set as resizable and it is selected then four grabbers are drawed in the corners around the element. This grabbers allow change the size of the element.

To know the correct position of corners and bounding rect of the MscGraphItem subclass variables x, y, width and height must be set correctly in all moment. This variables x,y represent the local coordinates of the paint start, and width and height represents the width and height of rect than contains all the elements paints.

To have new MscGraphItem only need to reimplement the paint() function. If the object is resizable then resizeItem() must be override.

Create new MSC Graphical Item

This is a example of creation new graphical element based in MscGraphItem. In this case the new element is the timer graphics.

The first is to create new class, we will named "MscTimer", in first this is not resizable but we do not implement resizeItem() function.

With this code you have a graphical object for Timer, and is correct implementetion of MscGraphItem:

 
  class MscGraphTimer(MscGraphItem):
    '''
    Class that draws a Timer simbol of MSC Standard
    '''
    TimerWidth = 15
    TimerLine = 40

    def __init__(self, data=None, parent=None):
        """
        Initialize
        """
        super(MscGraphTimer, self).__init__(parent)

        # Create Path
        self.createPath()
        self.setFlag(super(MscGraphTimer, self).ItemIsMovable, True)
        
    

    #**************************************************************************
    # Paint Functions
    #**************************************************************************
    def updateBounding(self):
        u"""
        Update internal drawing coordinates
        """
        self.prepareGeometryChange()
        rect = self.path.boundingRect()
        self.drawOriginX = rect.x()
        self.drawOriginY = rect.y()
        self.width = rect.width()
        self.height = rect.height()

    def createPath(self):
        u"""
        Create the path of Timer
        """
        # To rotate 180
        matrix = QMatrix()
        matrix.rotate(180)
        # Triangle of timer
        triangle = QPainterPath()
        triangle.lineTo(-self.TimerWidth / 2, -self.TimerWidth / 2)
        triangle.lineTo(self.TimerWidth / 2, -self.TimerWidth / 2)
        triangle.closeSubpath()

        # Second triangle, rotate 180 the first triangle
        triangle2 = QPainterPath(matrix.map(triangle))

        self.path = QPainterPath()
        self.path.lineTo(self.TimerLine, 0)
        triangle.translate(self.TimerLine, 0)
        triangle2.translate(self.TimerLine, 0)
        self.path.addPath(triangle)
        self.path.addPath(triangle2)

        # Set correct values of x, y, width and height
        self.updateBounding()

    def paint(self, painter, option, widget):
        painter.setPen(self.pen)
        painter.drawPath(self.path)

Now, we must add funcionality to the class. One important graphical funcionality is than it only can move in Y axis and it can't be positioning out of his parent. For this pourpose the parent must be a MscGraphical class that admit events, because of if offer UpperLimitInstance(), BottomLimitInstance() functions that return the limits betwen events can be.