Commit 51d877fe authored by Maxime Perrotin's avatar Maxime Perrotin

Ada backend - added support for systems without signals

parent b60cc84d
......@@ -156,7 +156,7 @@ def _process(process):
# Add the declaration of the runTransition procedure
process_level_decl.append('procedure runTransition(Id: Integer);')
process_level_decl.append('procedure state_start;')
#process_level_decl.append('procedure state_start;')
#process_level_decl.append('pragma export(C, start, "{}_start");'
# .format(process_name))
......@@ -170,6 +170,7 @@ def _process(process):
asn1_modules = '\n'.join(['with {dv};\nuse {dv};'.format(
dv=dv.replace('-', '_'))
for dv in process.asn1Modules])
asn1_modules += '\nwith adaasn1rtl;\nuse adaasn1rtl;'
except TypeError:
asn1_modules = '-- No ASN.1 data types are used in this model'
taste_template = ['''\
......@@ -180,9 +181,6 @@ use System.IO;
{dataview}
with adaasn1rtl;
use adaasn1rtl;
with Interfaces;
use Interfaces;
......@@ -196,7 +194,7 @@ package body {process_name} is'''.format(process_name=process_name,
{dataview}
package {process_name} is'''.format(process_name=process_name,
dataview=asn1_modules)]
dataview=asn1_modules)]
# Generate the the code of the procedures
inner_procedures_code = []
......@@ -209,11 +207,11 @@ package {process_name} is'''.format(process_name=process_name,
taste_template.extend(process_level_decl)
# Generate the code for the start procedure
taste_template.extend([
'procedure state_start is',
'begin',
'null;',
'end state_start;', ''])
#taste_template.extend([
# 'procedure state_start is',
# 'begin',
# 'null;',
# 'end state_start;', ''])
# Add the code of the procedures definitions
taste_template.extend(inner_procedures_code)
......@@ -335,6 +333,12 @@ package {process_name} is'''.format(process_name=process_name,
taste_template.append('procedure runTransition(Id: Integer) is')
taste_template.append('trId : Integer := Id;')
# If the process has no input, output, procedures, or timers, then Ada
# will not compile the body - generate a pragma to fix this
if not process.timers and not process.procedures \
and not process.input_signals and not process.output_signals:
ads_template.append('pragma elaborate_body;')
# Transform inner labels to floating labels
Helper.inner_labels_to_floating(process)
......
......@@ -72,7 +72,7 @@ def inner_labels_to_floating(process):
def flatten(process, sep='_'):
''' Flatten the nested states:
''' In-place update of the AST: flatten a model with nested states
Rename inner states, procedures, etc. and move them to process level
'''
def update_terminator(context, term, process):
......
......@@ -21,6 +21,8 @@
import logging
import math
import re
from itertools import chain
from PySide import QtGui, QtCore
try:
......@@ -241,31 +243,54 @@ def preprocess_nodes(my_graph, bounding_rect, dpi):
# while Qt uses the top-left corner, so we must translate all y-coord.
bb_height = bounding_rect[3]
nodes = []
attrs = []
for each in my_graph.subgraphs():
# Transform cluster nodes into nodes (attributes differ)
llx, lly, urx, ury = each.graph_attr['bb'].split(',')
wid = float(urx) - float(llx)
hei = float(ury) - float(lly)
attr = {}
attr.update(pos='{0},{1}'.format(llx, lly),
width=wid,
height=hei,
shape=each.graph_attr['shape'],
label=each.graph_attr['label'],
style=each.graph_attr['style'],
name='cluster_'+each.graph_attr['label'],
kind='cluster')
attrs.append(attr)
for node in my_graph.nodes_iter():
if not node.attr:
continue
node.attr.update(name=node.name)
attrs.append(node.attr)
for node in attrs:
new_node = {}
# Get the node shape attribute - default is record
new_node['shape'] = node.attr.get('shape') or 'record'
new_node['shape'] = node.get('shape') or 'record'
# node main name
new_node['name'] = str(node)
new_node['name'] = node['name']
try:
# node complete label (can contain several compartments)
properties = (
node.attr['label'][1:-2].split('|')[1].replace('\\', '\n'))
new_node['properties'] = properties # node.attr.get('label')
node['label'][1:-2].split('|')[1].replace('\\', '\n'))
new_node['properties'] = properties
except IndexError:
pass
# transform width and height from inches to pixels
new_node['width'] = float(node.attr.get('width')) * RENDER_DPI['X']
new_node['height'] = float(node.attr.get('height')) * RENDER_DPI['Y']
if node.get('kind', '') == 'cluster':
new_node['width'] = node['width']
new_node['height'] = node['height']
else:
new_node['width'] = float(node['width']) * RENDER_DPI['X']
new_node['height'] = float(node['height']) * RENDER_DPI['Y']
# get the position of the CENTER of the node
center_pos = [float(val)
for val in node.attr.get('pos').split(',')]
center_pos = [float(val) for val in node['pos'].split(',')]
# apply dpi-conversion from 72 to 96
center_pos[0] *= (RENDER_DPI['X'] / dpi)
# translate y-coord from bottom-left to top-left corner
center_pos[1] = (
bb_height - center_pos[1]) * (RENDER_DPI['Y'] / dpi)
center_pos[1] = (bb_height - center_pos[1]) * (RENDER_DPI['Y'] / dpi)
new_node['pos'] = [center_pos[0] - (new_node['width'] / 2.0),
center_pos[1] - (new_node['height'] / 2.0)]
nodes.append(new_node)
......@@ -297,31 +322,34 @@ def update(scene):
center_x = center_pos.x() * (dpi / RENDER_DPI['X'])
center_y = (bb_height - center_pos.y()) * (dpi / RENDER_DPI['Y'])
pos = unicode('{x},{y}'.format(x=int(center_x), y=int(center_y)))
#print node['name'], node['height'], node['height'] / RENDER_DPI['Y']
#pos = unicode('{x},{y}'.format(x=int(center_x), y=int(center_y)))
pos = unicode('{x},{y}'.format(x=float(center_x), y=float(center_y)))
if node['shape'] in (Point, Diamond):
graph.add_node(node['name'], pos=pos, shape=lookup[node['shape']],
fixedsize='true', width=node['width'] / RENDER_DPI['X'],
height=node['height'] / RENDER_DPI['Y'])
height=node['height'] / RENDER_DPI['Y'], pin=True)
else:
graph.add_node(node['name'], pos=pos, shape=lookup[node['shape']])
graph.add_node(node['name'], pos=pos, pin=True,
shape=lookup[node['shape']])
# Keep edges from the previous graph
for edge in EDGES:
graph.add_edge(edge, label=edge.attr.get('label') or '')
#print graph.to_string()
if nodes:
#before = scene.itemsBoundingRect().center()
before = scene.itemsBoundingRect().center()
before_pos = graph.get_node(nodes[0]['name']).attr['pos']
render_statechart(scene, graph, keep_pos=True)
#delta = scene.itemsBoundingRect().center() - before
after_pos = graph.get_node(nodes[0]['name']).attr['pos']
#print before_pos,after_pos
delta = scene.itemsBoundingRect().center() - before
# graphviz translates the graph to pos (0, 0) -> move it back
# to the exact graphical position where the user clicked
#for item in scene.items():
# if isinstance(item, genericSymbols.Symbol):
# item.setPos(item.pos() - delta)
for item in scene.visible_symb:
item.setPos(item.pos() - delta)
def render_statechart(scene, graph=None, keep_pos=False):
......@@ -337,8 +365,10 @@ def render_statechart(scene, graph=None, keep_pos=False):
graph.graph_attr.update(dpi='72.0')
EDGES[:] = graph.edges()
scene.clear()
G_SYMBOLS.clear()
for each in scene.visible_symb:
each.setVisible(False)
#scene.clear()
#G_SYMBOLS.clear()
# Compute all the coordinates (self-modifying function)
# Force the fontsize of the nodes to be 12, as in OpenGEODE
......@@ -364,19 +394,110 @@ def render_statechart(scene, graph=None, keep_pos=False):
nodes = preprocess_nodes(graph, bounding_rect, dot_dpi)
node_symbols = []
for node in nodes:
#print node
shape = node.get('shape')
try:
node_symbol = lookup[shape](node, graph)
G_SYMBOLS.add(node_symbol)
node_symbols.append(node_symbol)
scene.addItem(node_symbol)
except KeyError:
raise TypeError('Statechart - unsupported shape: ' + shape)
G_SYMBOLS.add(node_symbol)
node_symbols.append(node_symbol)
scene.addItem(node_symbol)
edges = preprocess_edges(graph, node_symbols, bounding_rect, dot_dpi)
for edge in edges:
Edge(edge, graph)
def create_dot_graph(root_ast):
''' Return a dot.AGraph item, from an ogAST.Process or child entry '''
graph = dotgraph.AGraph(strict=False, directed=True)
diamond = 0
for state in root_ast.mapping.viewkeys():
# create a new node for each state (including nested states)
if state.endswith('START'):
graph.add_node(state, label='', shape='point',
fixedsize='true', width=10.0 / 72.0)
else:
#print 'adding', state
graph.add_node(state, label=state, shape='record', style='rounded')
for each in root_ast.composite_states:
# this will have to be recursive
subnodes = (name for name in graph.iternodes()
if name.startswith(each.statename.lower() + '_'))
graph.add_subgraph(subnodes, name='cluster_'+each.statename.lower(),
label=each.statename.lower(),
style='rounded', shape='record')
for state, inputs in root_ast.mapping.viewitems():
# Add edges
transitions = \
inputs if not state.endswith('START') \
else [root_ast.transitions[inputs]]
#[root_ast.content.start]
for trans in transitions:
source = state
# transition label - there can be several inputs
try:
# Keep only message name, remove params and newlines
# (newlines are not supported by graphviz)
label, = re.match(r'([^(]+)', trans.inputString).groups()
label = label.strip().replace('\n', ' ')
except AttributeError:
# START transition may have no inputString
label = ''
def find_terminators(trans):
''' Recursively find all NEXTSTATES '''
next_states = [term for term in trans.terminators
if term.kind == 'next_state']
joins = [term for term in trans.terminators
if term.kind == 'join']
for join in joins:
# JOIN - Find corresponding label
try:
corr_label, = [lab for lab in
root_ast.content.floating_labels +
root_ast.labels if
lab.inputString.lower() ==
join.inputString.lower()]
except ValueError:
LOG.error('Missing label: ' + join.inputString)
else:
# Don't recurse forever in case of livelock
if corr_label.inputString != trans.inputString:
next_states.extend(find_terminators(corr_label))
return set(next_states)
# Determine the list of terminators in this transition
next_states = find_terminators(trans)
if len(next_states) > 1:
# more than one terminator - add intermediate node
graph.add_node(str(diamond),
shape='diamond',
fixedsize='true',
width=15.0 / 72.0,
height=15.0 / 72.0, label='')
graph.add_edge(source, str(diamond), label=label)
source = str(diamond)
label = ''
diamond += 1
for term in next_states:
if term.inputString.strip() == '-':
target = state
else:
target = term.inputString.lower()
LOG.debug('Edge from ' + source + ' to ' +
term.inputString + ' label: ' + label)
for each in root_ast.composite_states:
# check with deeper nesting
if each.statename.lower() == target.lower():
target = 'cluster_' + target
break
graph.add_edge(source, target, label=label)
#print graph.to_string()
return graph
if __name__ == '__main__':
render_statechart(None)
......@@ -7,6 +7,6 @@
defined language, and generate Ada code from the models.
"""
__version__ = "0.991"
__version__ = "0.992"
from opengeode import opengeode
......@@ -2,7 +2,7 @@
# Resource object code
#
# Created: Sat May 17 07:26:45 2014
# Created: Mon May 19 19:58:31 2014
# by: The Resource Compiler for PySide (Qt v4.8.6)
#
# WARNING! All changes made in this file will be lost!
......@@ -216,15 +216,15 @@ def get_interfaces(ast, process_name):
all_signals.extend(signals_in_system(system))
process_ref = find_process_declaration(system, process_name)
if process_ref:
# Go to the block where the process is defined
process_parent = process_ref.parent
break
else:
if isinstance(ast, ogAST.Block):
process_ref = ast
process_parent = ast
else:
raise TypeError('Process ' + process_name +
' is defined but not not declared in a system')
# Go to the block where the process is defined
process_parent = process_ref.parent
# Find in and out signals names using the signalroutes
for signalroute in process_parent.signalroutes:
for route in signalroute['routes']:
......@@ -1770,12 +1770,11 @@ def process_definition(root, parent=None, context=None):
process.processName = child.text
try:
# Retrieve process interface (PI/RI)
async_signals, procedures = get_interfaces(
parent, child.text)
async_signals, procedures = get_interfaces(parent, child.text)
process.input_signals.extend([sig for sig in async_signals
if sig['direction'] == 'in'])
if sig['direction'] == 'in'])
process.output_signals.extend([sig for sig in async_signals
if sig['direction'] == 'out'])
if sig['direction'] == 'out'])
process.procedures.extend(procedures)
except AttributeError as err:
# No interface because process is defined standalone
......@@ -2833,6 +2832,7 @@ def pr_file(root):
for mod in ast.asn1Modules:
ast.asn1_constants.extend(DV.exportedVariables[mod])
except (ImportError, NameError):
# Can happen if DataView.py is not there
LOG.info('USE Clause did not contain ASN.1 filename')
for child in systems:
LOG.debug('found SYSTEM')
......
......@@ -68,13 +68,14 @@ import Renderer
import Clipboard
import Statechart
import Lander
import Helper
# Try importing graphviz for the SDL to Statechart converter
# This is optional, as graphviz installation can not be easily
# automated on some platforms by opengeode installation scripts.
try:
import pygraphviz as dot
import pygraphviz
graphviz = True
except ImportError:
graphviz = False
......@@ -86,7 +87,7 @@ except ImportError:
pass
__all__ = ['opengeode']
__version__ = '0.991'
__version__ = '0.992'
if hasattr(sys, 'frozen'):
# Detect if we are running on Windows (py2exe-generated)
......@@ -695,7 +696,6 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
def sdl_to_statechart(self):
''' Create a graphviz representation of the SDL model '''
graph = dot.AGraph(strict=False, directed=True)
pr_raw = self.get_pr_string()
pr_data = str('\n'.join(pr_raw))
ast, _, _ = ogParser.parse_pr(string=pr_data)
......@@ -703,76 +703,10 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
process_ast, = ast.processes
except ValueError:
LOG.error('No statechart to render')
return
diamond = 0
for state, inputs in process_ast.mapping.viewitems():
# create a new node for each state
if state == 'START':
graph.add_node(state, shape='point',
fixedsize='true', width=10.0 / 72.0)
else:
graph.add_node(state, shape='record', style='rounded')
# Add edges
transitions = inputs if state != 'START' else [
process_ast.content.start]
for trans in transitions:
source = state
# transition label - there can be several inputs
try:
# Keep only message name, remove params and newlines
# (newlines are not supported by graphviz)
label, = re.match(r'([^(]+)',
trans.inputString).groups()
label = label.strip().replace('\n', ' ')
except AttributeError:
# START transition has no inputString
label = ''
def find_terminators(trans):
''' Recursively find all NEXTSTATES '''
next_states = [term for term in trans.terminators
if term.kind == 'next_state']
joins = [term for term in trans.terminators
if term.kind == 'join']
for join in joins:
# JOIN - Find corresponding label
try:
corr_label, = [lab for lab in
process_ast.content.floating_labels +
process_ast.labels if
lab.inputString.lower() ==
join.inputString.lower()]
except ValueError:
LOG.error('Missing label: ' + join.inputString)
else:
# Don't recurse forever in case of livelock
if corr_label.inputString != trans.inputString:
next_states.extend(
find_terminators(corr_label))
return set(next_states)
# Determine the list of terminators in this transition
next_states = find_terminators(trans)
if len(next_states) > 1:
# more than one terminator - add intermediate node
graph.add_node(str(diamond), shape='diamond',
fixedsize='true',
width=15.0 / 72.0, height=15.0 / 72.0, label='')
graph.add_edge(source, str(diamond), label=label)
source = str(diamond)
label = ''
diamond += 1
for term in next_states:
if term.inputString.strip() == '-':
target = state
else:
target = term.inputString.lower()
LOG.debug('Edge from ' + source + ' to ' +
term.inputString + ' label: ' + label)
graph.add_edge(source, target, label=label)
#print graph.to_string()
return graph
return None
# Flatten nested states
Helper.flatten(process_ast)
return Statechart.create_dot_graph(process_ast)
def export_branch_to_picture(self, symbol, filename, doc_format):
......@@ -927,8 +861,8 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
elif self.mode == 'wait_placement':
try:
parent = self.can_insert(
event.scenePos(), self.button_selected)
parent = \
self.can_insert(event.scenePos(), self.button_selected)
except TypeError as err:
self.messages_window.addItem(str(err))
else:
......@@ -1157,6 +1091,9 @@ class SDL_View(QtGui.QGraphicsView, object):
self.check_model()
elif event.key() == Qt.Key_F5:
self.refresh()
# Refresh statechart
if graphviz:
Statechart.update(self.scene())
elif event.matches(QtGui.QKeySequence.Open):
self.open_diagram()
elif event.matches(QtGui.QKeySequence.New):
......@@ -1180,9 +1117,6 @@ class SDL_View(QtGui.QGraphicsView, object):
def refresh(self):
''' Refresh the complete view '''
self.scene().refresh()
# Refresh statechart
if graphviz:
Statechart.update(self.scene())
self.setSceneRect(self.scene().sceneRect())
self.viewport().update()
......@@ -1755,6 +1689,7 @@ class OG_MainWindow(QtGui.QMainWindow, object):
graph = scene.sdl_to_statechart()
try:
Statechart.render_statechart(self.statechart_scene, graph)
self.statechart_view.refresh()
except (IOError, TypeError) as err:
LOG.debug(str(err))
elif key_event.key() == Qt.Key_Colon:
......
# $ANTLR 3.1.3 Mar 17, 2009 19:23:44 sdl92.g 2014-05-17 07:26:48
# $ANTLR 3.1.3 Mar 17, 2009 19:23:44 sdl92.g 2014-05-19 19:58:33
import sys
from antlr3 import *
......
# $ANTLR 3.1.3 Mar 17, 2009 19:23:44 sdl92.g 2014-05-17 07:26:46
# $ANTLR 3.1.3 Mar 17, 2009 19:23:44 sdl92.g 2014-05-19 19:58:32
import sys
from antlr3 import *
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment