Commit 3108bc83 authored by Maxime Perrotin's avatar Maxime Perrotin

MSC Core API

parents
install:
@echo Installing the Python MSC libraray...
@python setup.py install --record install.record
.PHONY: install
API for manipulating and editing MSC files
(Message Sequence Charts)
Part of the TASTE project
(c) European Space Agency
http://taste.tuxfamily.org
Contact: maxime.perrotin@esa.int
#******************************************************************************
#
# TASTE Msc Diagram Editor
# http://taste.tuxfamily.org/
#
# This file is part of TASTE Msc Editor.
#
# TASTE Msc Diagram Editor is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# TASTE Msc Diagram Editor is distributed in the hope that it will be
# useful,(but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with TASTE Msc Diagram Editor. If not, see
# <http://www.gnu.org/licenses/>.
#
# Author: Angel Esquinas <aesquina@datsi.fi.upm.es>
#
# Copyright (c) 2012 European Space Agency
#
#******************************************************************************
u"""MsCore contains all the principals class to manage "Message Sequence \
Chart" files and elements.
.. moduleauthor:: Angel Esquinas Fernandez <aesquina@datsi.fi.upm.es>
"""
from mscelement import MscElement
from mscevent import MscEvent
from mscinstance import MscInstance
from basicmsc import BasicMsc
from mscmessage import MscMessage
from mscdocument import MscDocument
from mscvisitor import MscVisitor
from msctimer import MscTimer
from mscinstancekind import MscInstanceKind
from mscmessagedecl import MscMessageDecl
import mscregexp as MscRegExp
__version__ = 1.0
__all__ = ['MscElement', 'MscEvent', 'MscTimer', 'MscInstanceKind',
'MscMessageDecl', 'MscRegExp', 'MscInstance', 'MscMessage',
'BasicMsc', 'MscDocument', 'MscVisitor']
This diff is collapsed.
#******************************************************************************
#
# TASTE Msc Diagram Editor
# http://taste.tuxfamily.org/
#
# This file is part of TASTE Msc Editor.
#
# TASTE Msc Diagram Editor is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# TASTE Msc Diagram Editor is distributed in the hope that it will be
# useful,(but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with TASTE Msc Diagram Editor. If not, see
# <http://www.gnu.org/licenses/>.
#
# Author: Angel Esquinas <aesquina@datsi.fi.upm.es>
#
# Copyright (c) 2012 European Space Agency
#
#******************************************************************************
'''
Created on Jun 11, 2012
@author: angel
'''
class Virtuality():
Virtual, Redefined, Finalized = range(3)
#******************************************************************************
#
# TASTE Msc Diagram Editor
# http://taste.tuxfamily.org/
#
# This file is part of TASTE Msc Editor.
#
# TASTE Msc Diagram Editor is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# TASTE Msc Diagram Editor is distributed in the hope that it will be
# useful,(but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with TASTE Msc Diagram Editor. If not, see
# <http://www.gnu.org/licenses/>.
#
# Author: Angel Esquinas <aesquina@datsi.fi.upm.es>
#
# Copyright (c) 2012 European Space Agency
#
#******************************************************************************
from mscio import *
\ No newline at end of file
import logging
from msccore.mscvisitor import MscVisitor
logger = logging.getLogger(__name__)
class CoreToText(MscVisitor):
u'''
Export MscCore Document to Msc Textual File according ITU T.Z120
Recommendation (02 / 2011)
'''
INDENT = u" "
def __init__(self, path):
#Open the file in write mode
self._file = open(path, 'w')
def write(self):
self._file.write(self._result)
self._file.close()
def _declInstances(self, instances):
u""" Create decl line """
self.instDeclDict = {}
for i in instances:
name = i.getName()
# Create line of text
if name == u"":
try:
text = u"{}".format(i.instanceKind().kindString())
except:
logger.error("Instance with no name and no kind")
else:
text = u"{}".format(name)
if i.instanceKind():
# If instanceKind is declared it must not be empty
text += u": {}".format(i.instanceKind().kindString())
if not (text in self.instDeclDict):
self.instDeclDict[text] = text
# Create text of all declarations
instDecl = u""
for i in self.instDeclDict.values():
instDecl += u"{}inst {};\n".format(self.INDENT, i)
return instDecl
def _declMessages(self, msgDecl):
u""" Return text for all messages declared with parameters """
# The messages when parameters must be declared within msc document,
# because of this only read msgdecl from msc document.
msgs = u""
for m in msgDecl:
text = m.textual()
text = "{ind}msg {msg};\n".format(ind=self.INDENT,
msg=text)
msgs += text
return msgs
def visitorMscDocument(self, element):
instancesUsed = []
mscs = u''
for m in element.mscs():
m.accept(self)
text = self.obtainResult()
text = self.indent(text)
mscs = mscs + text
# Ask for instances inside msc
instancesUsed.extend(m.instances())
# Data definition clause
dataDef = u""
lang = element.language()
if lang != None:
dataDef += u"{ind}language {lang};\n".format(ind=self.INDENT,
lang=lang)
data = element.dataDefinition()
if data != None:
dataDef += u"{ind}data {data};\n".format(ind=self.INDENT,
data=data)
declInstances = self._declInstances(instancesUsed)
declMsgs = self._declMessages(element.messageDecl())
head = 'mscdocument {0};\n'.format(element.getName())
head += dataDef
head += declInstances
head += declMsgs
end = 'endmscdocument;\n'
self._result = head + mscs + end
def visitorBMSC(self, element):
instances = u""
events = u""
self.msgTS = {} # To know if exist messages with the same name
self.instance = element
for e in element.events():
e.element.accept(self)
text = self.obtainResult()
text = u"gate {0}".format(text)
text = self.indent(text)
events = events + text
for i in element.instances():
i.accept(self)
text = self.obtainResult()
text = self.indent(text)
instances = instances + text
head = u'msc {0}{1}\n'.format(element.getName(), self.end(element))
end = u'endmsc;\n'
self._result = head + events + instances + end
def visitorMscInstance(self, element):
if element.instanceKind() == None:
head = u"{} : instance {}\n".format(element.getName(),
self.end(element))
else:
# If instance have kind, then is posible that have name
if element.getName() != u"":
head = (u"{name} : instance {kind}{end}\n"
.format(name=element.getName(),
kind=element.instanceKind().kindString(),
end=self.end(element))
)
else:
head = (u"instance {kind}{end}\n"
.format(kind=element.instanceKind().kindString(),
end=self.end(element))
)
end = u"endinstance;\n"
self.instance = element
events = u''
for e in element.events():
e.element.accept(self)
text = self.obtainResult()
text = self.indent(text)
events = events + text
self._result = head + events + end
def visitorMscMessage(self, element):
# Annotate in symbol table (sender, receiver, messageName) : objects
# If other object is in list then a identifier is used
key = element.instanceSender(), element.instanceReceiver(), element.getName()
if not key in self.msgTS:
# Insert the element in the TS with key 'key'
self.msgTS[key] = [element]
l = self.msgTS[key]
# Check if element in list of objects
if l.count(element) == 0:
l.append(element)
msgInstName = None
index = l.index(element)
if index > 0: # If more than one, then use message inst name
msgInstName = u"{0}".format(index)
if element.instanceSender() == self.instance:
receiverName = element.instanceReceiver().getName()
# print element.instanceReceiver()
# print element.parent()
if element.instanceReceiver() == element.parent(): # ENV
receiverName = u'env'
self._result = u'out {0} to {1}{2}\n'.format(element.textual(msgInstName),
receiverName,
self.end(element))
else:
senderName = element.instanceSender().getName()
if element.instanceSender() == element.parent():
senderName = u'env'
self._result = u'in {0} from {1}{2}\n'.format(element.textual(msgInstName),
senderName,
self.end(element))
def visitorMscTimer(self, element):
u""" Export MscTimer to text """
ttype = element.timerType()
if ttype == element.StartTimer:
timer = u"starttimer"
elif ttype == element.StopTimer:
timer = u"stoptimer"
elif ttype == element.TimeOut:
timer = u"timeout"
self._result = timer + " {}{}\n".format(element.getName(),
self.end(element))
def end(self, element):
u"""
Return "end" token of element.
If element have comment return "comment<comment>;"
"""
comment = element.comment()
if comment != u"":
end = " comment '{}';".format(comment)
else:
end = u";"
return end
def indent(self, text):
lines = text.split(u'\n')
text = u''
# print lines
for i in lines[:len(lines) - 1]:
text = text + u'{}{}\n'.format(self.INDENT, i)
return text
\ No newline at end of file
#******************************************************************************
#
# TASTE Msc Diagram Editor
# http://taste.tuxfamily.org/
#
# This file is part of TASTE Msc Editor.
#
# TASTE Msc Diagram Editor is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# TASTE Msc Diagram Editor is distributed in the hope that it will be
# useful,(but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with TASTE Msc Diagram Editor. If not, see
# <http://www.gnu.org/licenses/>.
#
# Author: Angel Esquinas <aesquina@datsi.fi.upm.es>
#
# Copyright (c) 2012 European Space Agency
#
#******************************************************************************
'''
Created on May 7, 2012
@author: angel
'''
import logging
logger = logging.getLogger(__name__)
from mscparser import MscParser
from msccoretotext import CoreToText
def importMSC(path):
u"""
Read Msc File and return MscDocument object contains data
of file read
"""
logger.info("Start to importing msc from file '%s'", path)
fileRead = open(path)
parser = MscParser()
document = parser.parse(fileRead.read())
logger.info("Import finished")
return document
def exportMSC(document, path):
u"""
Write MscDocument data in a text file.
"""
parser = CoreToText(path)
document.accept(parser)
parser.write()
#******************************************************************************
#
# TASTE Msc Diagram Editor
# http://taste.tuxfamily.org/
#
# This file is part of TASTE Msc Editor.
#
# TASTE Msc Diagram Editor is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# TASTE Msc Diagram Editor is distributed in the hope that it will be
# useful,(but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with TASTE Msc Diagram Editor. If not, see
# <http://www.gnu.org/licenses/>.
#
# Author: Angel Esquinas <aesquina@datsi.fi.upm.es>
#
# Copyright (c) 2012 European Space Agency
#
#******************************************************************************
'''
Created on Apr 11, 2012
@author: angel
'''
from msccore import MscElement
class Msc(MscElement):
'''
classdocs
'''
def __init__(self, name, parent=None):
'''
Constructor
'''
super(Msc, self).__init__(name, parent, self.TYPE)
'''
Created on Apr 27, 2012
@author: angel
'''
from mscitemmodel import *
'''
Created on Apr 27, 2012
@author: angel
'''
from PySide.QtCore import QModelIndex, Qt
from PySide.QtGui import QStandardItemModel, QStandardItem, QIcon
class MscItemModel(QStandardItemModel):
'''
First Version: Show the BMSC and the list of instances
'''
def __init__(self, mscDocument, parent=None):
'''
Constructor
'''
super(MscItemModel, self).__init__(parent)
self._mscDocument = mscDocument
self._instances = []
self.createIcons()
# TODO: Change this
# When Data changed regenerate the model
# self._mscDocument.elementAddedSignal.connect(self.createModel)
self.createModel()
def createModel(self):
print "CREATE MODEL"
self.clear()
parentItem = self.invisibleRootItem()
for i in self._mscDocument.mscs():
print i.getName()
item = self.newMscItem(i.getName())
parentItem.appendRow(item)
def newMscItem(self, name):
# TODO: Add distinct type for high msc
return QStandardItem(QIcon(self._icons['bmsc']), name)
def createIcons(self):
self._icons = dict()
self._icons['bmsc'] = QIcon(":/instance")
print self._icons
class MscItem(QStandardItem):
def __init__(self, *args, **kwargs):
super(MscItem, self).__init__(*args, **kwargs)
This diff is collapsed.
#******************************************************************************
#
# TASTE Msc Diagram Editor
# http://taste.tuxfamily.org/
#
# This file is part of TASTE Msc Editor.
#
# TASTE Msc Diagram Editor is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# TASTE Msc Diagram Editor is distributed in the hope that it will be
# useful,(but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with TASTE Msc Diagram Editor. If not, see
# <http://www.gnu.org/licenses/>.
#
# Author: Angel Esquinas <aesquina@datsi.fi.upm.es>
#
# Copyright (c) 2012 European Space Agency
#
#******************************************************************************
"""
.. This docstring is intended to use with sphinx.
The :class:`msccore.MscElement` class is the base class of almost all msc \
elements. This class is intended to be subclassed.
It is a subclass of :class:`QtCore.QObject`, so it can use \
:class:`QtCore.Signal`. The :class:`msccore.MscElement` declares two \
:class:`~QtCore.Signal`: :attr:`MscElement.dataHasChanged` to notify when \
internal data structures or attributes of elements have been modified, and \
:attr:`~MscElement.nameChanged` to notify when the element's name has been
changed.
The :class:`MscElement` has the attribute :attr:`MscElement.Name` to hold \
the name of the element. To modify the name of the element use the method
:meth:`MscElement.setName` and to read the name of the element use
:meth:`MscElement.name`.
"""
from PySide.QtCore import QObject, Signal
from PySide.QtCore import QRegExp
import mscregexp
class MscElement(QObject):
'''
Constructs a :class:`MscElement` with the `name` and the given `parent`.
:param name: default empty string, Name of the element.
:type name: unicode
:param parent: default None
:type parent: QObject
'''
# String to print type of object
TYPE = 'MscElement'
# Signal that must be emitted when data of the element has changed.
# dataChangedSignal = Signal()
dataHasChanged = Signal()
"""
:attr:`dataHasChanged` notify when internal properties of the
:class:`MscElement` instance have changed.
This signal is emitted within :meth:`dataChanged` method. When
:attr:`dataHasChanged` is emitted, :attr:`contentsChanged` should also be
emitted, because if the internal properties of `MscElement` are modified
this implies that the contents of the object have also changed.
.. seealso::
:attr:`MscElement.contentsChanged`, :meth:`MscElement.dataChanged`
"""
nameChanged = Signal(unicode)
""":param unicode: The new name.
:type unicode: unicode"""
contentsChanged = Signal()
u"""
:attr:`contentsChanged` signal is emitted when changes in the its
child objects or changes in itself exist.
When data is changed in an element, because of new objects or other change,
this signal must be emitted. The difference with :attr:`dataHasChanged`
signal is that dataHasChanged is only emitted when the data is changed
in the object itself, not if it's changed in any of its child elements.
.. seealso::
:meth:`MscElement.dataChanged`, :attr:`MscElement.dataHasChanged`
"""
deleted = Signal(QObject)
u"""
:attr:`deleted` signal is emitted before the :class:`MscElement` object \
is deleted.
This signal is emitted within :meth:`MscElement.delete` method.
.. seealso::
:meth:`MscElement.delete`
"""
def __init__(self, name='', parent=None, elemType=TYPE):
'''
construct
'''
super(MscElement, self).__init__(parent)
self._name = ''
self._NameRegExp = QRegExp('(' + mscregexp.Name + ')')
self.elemType = elemType
self.setName(name)
self._comment = u""
def setNameRegExp(self, reg=None):
u"""
Set :class:`QtCore.QRegExp` `reg` as the validator to allow the change
of `name` property using :meth:`MscElement.setName`.
:param reg: default `None`, :class:Pyside.QtCore.RegExp
"""
self._NameRegExp = reg
# Name
def getName(self):
return self.name()
def name(self):
u'''
Return the name of the element
:rtype: unicode
'''
return self._name
def setName(self, name):
'''
Set the name of the element.
:param unicode name: Name of the element.
Each :class:`MscElement` object should have a different name.
The `name` property is changed with the `name` content if the
:data:`msccore.mscregexp.NameRegExpr` property is not set or in case
it is, the `name` is partially or fully accepted.
By default, this property contains an empty string
'''
# isCorrect = self.filterName(name)
if self._NameRegExp.indexIn(name) > -1:
accepted = self._NameRegExp.cap(1)
self._name = accepted
self.nameChanged.emit(self._name)
self.dataChanged()
def setExpression(self, expr):
u"""
Sets or changes internal properties based on `expr`.
:param unicode expr: Expression to obtain the data.
If this function is needed it has to be subclassed.
*Example:*
"process <<in>> extern" could be separated into 3 expressions,
(process, <<in>>, extern) to set class properties.
By default, this function calls :meth:`MscElement.setName` with `expr`
as argument.
"""
return self.setName(expr)
# Comment
def setComment(self, comment=u""):
u"""
Set the comment of the object.
:param commment: unicode
This property holds the comment of the object.
By default, this property contains an empty string
.. note:: After the property has changed this method calls \
:meth:`MscElement.dataChanged` emitting the \
:attr:`~MscElement.dataHasChanged` Signal.
"""
self._comment = comment
self.dataChanged()
def comment(self):
u"""
Return the comment.
:rtype: unicode
This property holds the comment of the object.
By default, this property contains an empty string
"""
return self._comment
def dataChanged(self):
'''
This function must be called when internal properties of the object
have changed. It emits both the "dataChanged" signal
and 'contentsChanged' signal.
'''
self.dataHasChanged.emit()