Commit ae300714 authored by Maxime Perrotin's avatar Maxime Perrotin
Browse files

Introduce undo command for the msc handler

parent d1ab49e9
......@@ -354,6 +354,7 @@ def gui():
{'dll': Signal(unicode, type, type)})
encoder_backend.send_via_dll = DLLHandler()
encoder_backend.send_via_dll.dll.connect(sdl.send_tc)
editor.msc.connect(sdl.add_to_msc)
else:
editor.msc.connect(msc.addToMsc)
......
......@@ -5,7 +5,8 @@ from time import strftime
from multiprocessing import Process, Pipe
from PySide.QtGui import(QFileDialog, QGraphicsView, QDockWidget, QMessageBox,
QErrorMessage, QTableWidget, QTableWidgetItem)
QErrorMessage, QTableWidget, QTableWidgetItem,
QUndoCommand)
from PySide.QtCore import Slot, Qt, QFile, QTimer
from PySide.QtUiTools import QUiLoader
......@@ -13,7 +14,6 @@ from mscStreamingScene import MscStreamingScene
import resources
CUR_DIR = os.path.dirname(os.path.realpath(__file__))
#SCENARIO_DIALOG_FILE = os.path.join(CUR_DIR, 'logging.ui')
SCENARIO_DIALOG_FILE = ':/logging.ui'
# Convenient type for handling MSC messages
......@@ -21,6 +21,71 @@ SCENARIO_DIALOG_FILE = ':/logging.ui'
MSG = type('MSG', (), {'direction': '', 'label': ''})
class AddToMsc(QUndoCommand):
''' Undo command to add something to the MSC (message, timer, box) '''
def __init__(self, handler, direction, message):
''' Init: prepare the message '''
super(AddToMsc, self).__init__()
self.handler = handler
self.message = message
self.increase_factor = 30
self.txt = MSG()
self.txt.direction = direction
# Add a counter (unique Id) incremented for each message
# (eg. out MsgName,1(Param);) - to respect MSC syntax
split = message.strip().split('(', 1)
if len(split) > 1:
self.txt.label = (',%d(' % handler.cnt_id).join(split)
else:
self.txt.label = split[0] + ',%d' % handler.cnt_id
# Create the graphical item
if direction in ('in', 'out'):
if direction == 'out':
start_item = handler.gui_instance
end_item = handler.taste_instance
elif direction == 'in':
start_item = handler.taste_instance
end_item = handler.gui_instance
self.item = handler.msc_scene.addMessage(
start_item, end_item, handler.next_y, label=message)
elif direction == 'set':
self.item = handler.msc_scene.addSetTimer(handler.taste_instance,
handler.next_y,
message)
elif direction == 'reset':
self.item = handler.msc_scene.addResetTimer(handler.taste_instance,
handler.next_y,
message)
elif direction == 'timeout':
self.item = handler.msc_scene.addTimeout(handler.taste_instance,
handler.next_y,
message)
elif direction == 'condition':
# Condition: single word with no space
self.item = handler.msc_scene.addCondition(handler.taste_instance,
y=handler.next_y,
label=condition)
self.increase_factor = 40
self.item.hide()
# This is how to add a comment on the graph:
# comment = handler.msc_scene.addComment(msg)
#comment.setCommentText("Hello!")
def undo(self):
''' Undo: delete the item from the MSC '''
self.handler.cnt_id -= 1
self.handler.msg.pop()
self.handler.next_y -= self.increase_factor
self.item.hide()
def redo(self):
''' Redo: add the item to the MSC '''
self.handler.cnt_id += 1
self.handler.msg.append(self.txt)
self.handler.next_y += self.increase_factor
self.item.show()
class mscHandler(object):
'''
Class managing MSC operations
......@@ -108,50 +173,63 @@ inst {fv};'''.format(fv=fv_name)]
else:
self.dock.hide()
@Slot(unicode, unicode)
def addToMsc(self, direction, message):
@Slot()
def addToMsc(self, direction, message, undo_stack=None):
''' Add a message to the MSC '''
if not self.running:
self.log.debug("MSC not running")
return
self.msc_started = True
msg = MSG()
msg.direction = direction
# Add a counter (unique Id) incremented for each message
# (eg. out MsgName,1(Param);) - to respect MSC syntax
split = message.strip().split('(', 1)
if len(split) > 1:
msg.label = (',%d(' % self.cnt_id).join(split)
else:
msg.label = split[0] + ',%d' % self.cnt_id
self.cnt_id += 1
self.msg.append(msg)
# Draw the message on the MSC Scene
if direction in ('in', 'out'):
if direction == 'out':
start_item = self.gui_instance
end_item = self.taste_instance
elif direction == 'in':
start_item = self.taste_instance
end_item = self.gui_instance
msg = self.msc_scene.addMessage(
start_item, end_item, self.next_y, label=message)
elif direction == 'set':
self.msc_scene.addSetTimer(self.taste_instance,
self.next_y,
message)
elif direction == 'reset':
self.msc_scene.addResetTimer(self.taste_instance,
self.next_y,
message)
elif direction == 'timeout':
self.msc_scene.addTimeout(self.taste_instance,
self.next_y,
message)
# This is how to add a comment on the graph:
# comment = self.msc_scene.addComment(msg)
#comment.setCommentText("Hello!")
self.next_y += 30
undo_cmd = AddToMsc(self, direction, message)
if undo_stack:
undo_stack.push(undo_cmd)
# TODO: check that the redo() is called anyway
# msg = MSG()
# msg.direction = direction
# # Add a counter (unique Id) incremented for each message
# # (eg. out MsgName,1(Param);) - to respect MSC syntax
# split = message.strip().split('(', 1)
# if len(split) > 1:
# msg.label = (',%d(' % self.cnt_id).join(split)
# else:
# msg.label = split[0] + ',%d' % self.cnt_id
# self.cnt_id += 1
# self.msg.append(msg)
# # Draw the message on the MSC Scene
# if direction in ('in', 'out'):
# if direction == 'out':
# start_item = self.gui_instance
# end_item = self.taste_instance
# elif direction == 'in':
# start_item = self.taste_instance
# end_item = self.gui_instance
# msg = self.msc_scene.addMessage(
# start_item, end_item, self.next_y, label=message)
# self.next_y += 30
# elif direction == 'set':
# msg = self.msc_scene.addSetTimer(self.taste_instance,
# self.next_y,
# message)
# self.next_y += 30
# elif direction == 'reset':
# msg = self.msc_scene.addResetTimer(self.taste_instance,
# self.next_y,
# message)
# self.next_y += 30
# elif direction == 'timeout':
# msg = self.msc_scene.addTimeout(self.taste_instance,
# self.next_y,
# message)
# self.next_y += 30
# elif direction == 'condition':
# # Condition: single word with no space
# condbox = self.msc_scene.addCondition(self.taste_instance,
# y=self.next_y,
# label=condition)
# self.next_y += 40
# # This is how to add a comment on the graph:
# # comment = self.msc_scene.addComment(msg)
# #comment.setCommentText("Hello!")
@Slot(unicode)
def addCondition(self, condition):
......
......@@ -34,6 +34,7 @@ from mscgraphics import MscGraphInstance
from mscgraphics import MscGraphBasicMSCScene
from mscgraphics import MscGraphMessage
from mscgraphics import MscGraphCondition
from mscgraphics import MscGraphTimer
from collections import deque
from msccore import mscregexp
......
......@@ -77,7 +77,7 @@ class sdlHandler(QObject):
Class managing SDL models
'''
# signal sent whenever a paramless TM or TC event happens
msc = Signal(unicode, unicode)
msc = Signal(unicode, unicode, QUndoStack)
# Signal sent when the state of the SDL model changes
state_change = Signal(unicode)
allowed_messages = Signal(list)
......@@ -226,16 +226,11 @@ class sdlHandler(QObject):
self.on_event()
def on_event(self, tc_name=None, param=None):
''' Process signals indicating that a PI was called in the shared lib:
update global state, manage undo/redo, manage active buttons
This function does not trigger any undoable action, thus can
be called without restriction at any time, by anybody '''
if not ASN1:
# The dataview must have been loaded to create ASN.1 native types
return
''' Update the list of global states and GUI after a TC has been sent
This function does not trigger any undoable action '''
complete_state = []
# Read in the DLL the list of internal variables
for var, (sort, _) in self.proc.variables.viewitems():
# get internal variables, translate them to swig, and print them
typename = sort.ReferencedTypeName.replace('-', '_')
get_size = getattr(self.dll, "{}_size".format(var))
get_size.restype = ctypes.c_uint
......@@ -258,9 +253,9 @@ class sdlHandler(QObject):
self.asn1_editor.updateVariable(as_pyside,
root=self.tree_items[var])
complete_state.append(asn1_instance) # not gser
# Add the SDL state to the current state
# Add the SDL state to the new global state
complete_state.append(self.current_sdl_state)
# And save the current state in a graph
# And save this new state in the graph, if it was not there yet
new_hash = hash(frozenset(complete_state))
self.set_of_states[new_hash] = complete_state
# Find the list of allowed TC based on the current state
......@@ -273,9 +268,11 @@ class sdlHandler(QObject):
# Enable/disable the parameterless TC buttons accordingly
for tc, button in self.buttons.viewitems():
if tc in self.proc.timers:
# Ignore timers, they are handled differently
# Ignore timers, they are handled differently, below
continue
button.setEnabled(tc in allowed_tc)
if tc_name in self.timers:
self.buttons[name].setEnabled(False)
# Emit the list of allowed TC for the GUI to update other buttons
self.allowed_messages.emit(allowed_tc)
if tc_name:
......@@ -304,52 +301,47 @@ class sdlHandler(QObject):
dock.show()
return dock
@Slot()
def add_to_msc(self, direction, msg):
''' Create an undo command and display message on the MSC diagram '''
self.msc.emit(direction, msg, self.undo_stack)
@Slot()
def send_tc(self, name, tc_func_ptr, param=None):
''' Send a TC - Used either locally (parameterless TCs) or via
a signal sent by the B-mapper-generated backends '''
if name in self.timers:
self.buttons[name].setEnabled(False)
self.msc.emit('timeout', name)
else:
self.add_to_msc('timeout', name)
elif not param:
msg = '{tc}{arg}'.format(tc=name,
arg='({})'.format(param.GSER()) if param else '')
self.msc.emit('out', msg)
self.add_to_msc('out', msg)
with undo.UndoMacro(self.undo_stack, 'Send TC'):
undo_cmd = SendTC(self, tc_func_ptr, param)
self.undo_stack.push(undo_cmd)
self.check_state()
# if param:
# # Cast the SWIG type (ASN.1 Native format) to a ctypes pointer
# try:
# swig_ptr = int(param._ptr)
# except TypeError:
# # when swig uses a proxy class, pointer is in _ptr.this
# swig_ptr = int(param._ptr.this)
# param_ptr = ctypes.cast(swig_ptr, ctypes.POINTER(ctypes.c_uint32))
# tc_func_ptr(param_ptr)
# else:
# tc_func_ptr()
# self.check_state()
# Update windows, highlight state, enable/disable buttons, etc.
self.on_event(tc_name=name, param=param.GSER() if param else None)
self.current_hash = self.on_event(tc_name=name,
param=param.GSER() if param
else None)
def receive_tm(self, tm_name):
''' Callback function when a paramless TM is received '''
self.msc.emit('in', tm_name)
self.add_to_msc('in', tm_name)
self.log_area.addItem('Received event "{}"'.format(tm_name))
def set_timer(self, name, duration):
''' Callback function when the SDL model sets a timer '''
self.msc.emit('set', 'SET_{}({})'.format(name, duration))
self.add_to_msc('set', 'SET_{}_{}'.format(name, duration))
self.log_area.addItem('Received event "SET_{}({})"'
.format(name, duration))
self.buttons[name].setEnabled(True)
def reset_timer(self, name):
''' Callback function when the SDL model resets a timer '''
self.msc.emit('reset', 'RESET_{}'.format(name))
self.add_to_msc('reset', 'RESET_{}'.format(name))
self.log_area.addItem('Received event "RESET_{}"'
.format(name))
self.buttons[name].setEnabled(False)
......
Supports Markdown
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