/* Copyright or (C) or Copr. GET / ENST, Telecom-Paris, Ludovic Apvrille * * ludovic.apvrille AT enst.fr * * This software is a computer program whose purpose is to allow the * edition of TURTLE analysis, design and deployment diagrams, to * allow the generation of RT-LOTOS or Java code from this diagram, * and at last to allow the analysis of formal validation traces * obtained from external tools, e.g. RTL from LAAS-CNRS and CADP * from INRIA Rhone-Alpes. * * This software is governed by the CeCILL license under French law and * abiding by the rules of distribution of free software. You can use, * modify and/ or redistribute the software under the terms of the CeCILL * license as circulated by CEA, CNRS and INRIA at the following URL * "http://www.cecill.info". * * As a counterpart to the access to the source code and rights to copy, * modify and redistribute granted by the license, users are provided only * with a limited warranty and the software's author, the holder of the * economic rights, and the successive licensors have only limited * liability. * * In this respect, the user's attention is drawn to the risks associated * with loading, using, modifying and/or developing or reproducing the * software by the user in light of its specific status of free software, * that may mean that it is complicated to manipulate, and that also * therefore means that it is reserved for developers and experienced * professionals having in-depth computer knowledge. Users are therefore * encouraged to load and test the software's suitability as regards their * requirements in conditions enabling the security of their systems and/or * data to be ensured and, more generally, to use and operate it in the * same conditions as regards security. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL license and that you accept its terms. */ package avatartranslator.tosysmlv2; import avatartranslator.*; import static avatartranslator.AvatarTransition.*; import static avatartranslator.tosysmlv2.Avatar2SysMLNames.*; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.ArrayList; import java.util.function.BiConsumer; /** * Class Avatar2SysMLV2 * Creation: 20/06/2024 * * @author Sophie Coudert * @version 0.1 20/06/2024 */ public class Avatar2SysML { /** the source Avatar Specification */ private AvatarSpecification avspec; /** the buffer in which theSysML Model is written (returned at the end of the generation process) */ private StringBuffer avsysml; /** signals concerned by AvatarQueryOnSignal (collected at beginning because require some specific handling)*/ private HashSet<AvatarSignal> fifoSet = new HashSet<AvatarSignal>(); /** infos about methods and signals collected while generation declarations and used while generating state machines */ private HashMap<AvatarMethod, MethodInfo> methodMap = new HashMap<AvatarMethod, MethodInfo>(); /** path from top level to the block (block sequence) being currently handled */ private ArrayList<String> blockChain = new ArrayList<String>(); /** SysML code to make inner blocks visible at top level. Contains one declaration per inner block */ private StringBuffer blockLinks; /** the standard indentation step string */ private final String indentStep = " "; /** size of the standard indentation step */ private final int indentStepSize = 4; /** the current indentation string */ private String indentation; /** (fixed) header for any Avatar SysML model instance */ private final String header = "package AvatarInstance {\n" + indentStep + "private import AvatarGeneral::*;\n" + indentStep + "private import AvatarBlockTypes::*;\n" + indentStep + "private import AvatarCommunication::*;\n" + indentStep + "private import AvatarTransitionServer::*;\n"; /** the single constructor */ public Avatar2SysML(AvatarSpecification _avspec) { avspec = _avspec; } /** the generator. Builds the SysML model of the specification provided to the constructor */ public StringBuffer avatar2SysML() { fifoSet.clear(); methodMap.clear(); blockChain.clear(); indentation = ""; blockLinks = new StringBuffer(indentStep + "// Block Shortcut Links $$$$$$$$$$$$\n"); avsysml = new StringBuffer(header); indent(1); searchCountSignals();; dataTypes2SysML(); communications2SysML(); blocks2SysML(); avsysml.append(blockLinks); avsysml.append("}\n"); return avsysml; } /** increase indentation */ private void indent(int n) { for (int i= 0; i < n; i++) indentation += indentStep; } /** decrease indentation */ private void unindent(int n) { indentation = indentation.substring(n * indentStepSize); } /** infos collected while generating declarations of methods and signals, to be used later. * * Used when generating state machine description, for example when handling ActionsOnSignal. */ static private class MethodInfo { private String name; // method name private String msgtype; // null for methods that are not signals private ArrayList<String> fieldNames; // names of parameters private ArrayList<AvatarType> fieldTypes; // int, bool or undefined private ArrayList<AvatarDataType> dataTypes; // datatype for "undefined" fieldType, otherwise null. private AvatarType returnType; // int, bool or undefined private AvatarDataType returnDataType; // datatype if returnType is "undefined", otherwise null. /** for void methods (without return type) */ MethodInfo(String _name) { name = _name; msgtype = null; fieldNames = new ArrayList<String>(); fieldTypes = new ArrayList<AvatarType>(); dataTypes = new ArrayList<AvatarDataType>(); returnType = null; returnDataType = null; } /** for non-void methods (with return type) */ MethodInfo(String _name, AvatarType _returnType, AvatarDataType dt) { name = _name; msgtype = null; fieldNames = new ArrayList<String>(); fieldTypes = new ArrayList<AvatarType>(); dataTypes = new ArrayList<AvatarDataType>(); returnType = _returnType; if (_returnType != AvatarType.UNDEFINED) returnDataType = null; else returnDataType = dt; } /** for signals */ MethodInfo(String _name, String _msgtype) { name = _name; msgtype = _msgtype; fieldNames = new ArrayList<String>(); fieldTypes = new ArrayList<AvatarType>(); dataTypes = new ArrayList<AvatarDataType>(); returnType = null; returnDataType = null; } /** add parameter at the end. * @param _dt datatype, only relevant if _type is UNDEFINED */ public void addField(String _fieldName, AvatarType _type, AvatarDataType _dt){ if (_type == AvatarType.UNDEFINED) dataTypes.add(_dt); else if (!(_type == AvatarType.BOOLEAN || _type == AvatarType.INTEGER)) return; // unknown type... TO CHANGE ? ... else dataTypes.add(null); fieldNames.add(_fieldName); fieldTypes.add(_type); } /** number of parameters */ public int getArity() { return fieldNames.size(); } /** SysML name */ public String getName(){ return name; } /** SysML name for message type describing profile */ public String getMessageType(){ return msgtype; } /** method Avatar return type (null if none)*/ public AvatarType getReturnType(){ return returnType; } /** method Avatar return datatype (null if no return type or return primitive type)*/ public AvatarDataType getReturnDataType(){ return returnDataType; } /** ith parameter SysML name */ public String getFieldName(int i){ return fieldNames.get(i); } /** ith parameter Avatar type */ public AvatarType getFieldType(int i){ return fieldTypes.get(i); } /** ith parameter Avatar datatype (null if primitive type)*/ public AvatarDataType getDataType(int i){ return dataTypes.get(i); } } /** * Memorized while naming states, to be reused for generating states */ static private class StateInfo { /** SysML name */ private String name; /** Sysml name of the state that is technically added before this (if exists) */ private String prename = ""; /** for states that havn't technically added state before them * @param _name SysML name */ StateInfo(String _name) { name = _name; } /** for states that have a technically added state before them * @param _name SysML name of the state * @param _prename SysML name of the technically added state before */ StateInfo(String _name, String _prename) { name = _name; prename = _prename; } /** SysML name of the state */ public String getName(){ return name; } /** SysML name of the technically added state before */ public String getPreName(){ return prename; } } /** Collect (in fifoSet) all signals that are used in an AvatarQueryOnSignal element of the specification. * * (they require some specific handling while generating...) */ private void searchCountSignals() { for(AvatarBlock block: avspec.getListOfBlocks()) { for(AvatarStateMachineElement sme : block.getStateMachine().getListOfElements()) { if(sme instanceof AvatarQueryOnSignal) fifoSet.add(((AvatarQueryOnSignal) sme).getSignal()); } } } /** update path blockChain when entering the block-handling procedure for a a block / sub-block * @param _block: the block /sub-block to handle */ private void chainBlock(String _block) { blockChain.add(_block); } /** update path blockChain when exiting the block-handling procedure for a a block / sub-block */ private void unchainBlock() { if(blockChain.size() > 0) blockChain.remove(blockChain.size() - 1); } /** generate SysML code to make a block visible at top level through its name (and not its path). * * Links block name to block path (updates blockLinks which is added to avsysml at the end of * the generation process). Consistent because all blocks have different names. */ private void blocklink() { int size = blockChain.size(); if (size < 2) return; StringBuffer chain = new StringBuffer(); for (int i=0; i < size-1; i++){ chain.append(blockChain.get(i) + "."); } chain.append(blockChain.get(size - 1)); blockLinks.append( indentStep + "part " + blockChain.get(size - 1) + " : '#AvatarBlock' :> " + chain.toString() + " = " + chain.toString() + ";\n" ); } /** generates SysML datatype declarations and put them at the current position (end) of avsysml */ private void dataTypes2SysML(){ if (avspec.getDataTypes() == null || avspec.getDataTypes().size() == 0) return; avsysml.append("\n" + indentation + "// DATATYPES $$$$$$$$$$$$$$$$$$$$$$$$\n"); for(AvatarDataType dt : avspec.getDataTypes()){ // put Header avsysml.append("\n" + indentation + "attribute def " + dataTypeSysMLname(dt.getName()) + " :> '#AvatarDataType'"); // handle fields int nbFields = dt.attributeNb(); if (nbFields == 0) avsysml.append(";\n"); else { // put fields. // Note: SysML has no default values thus default values are explicitely set avsysml.append(" { \n"); indent(1); for (int i = 0; i < nbFields; i++) { AvatarAttribute aa = dt.getAttribute(i); avsysml.append( indentation + "attribute " + fieldSysMLname(aa.getName()) + " : " ); if (aa.getType() == AvatarType.BOOLEAN) { avsysml.append("Boolean"); if (aa.hasInitialValue()) avsysml.append(" := " + aa.getInitialValue().trim() + ";\n"); else avsysml.append(" default := " + AvatarType.BOOLEAN.getDefaultInitialValue().trim() + ";\n"); } else if (aa.getType() == AvatarType.INTEGER) { avsysml.append("Integer"); if (aa.hasInitialValue()) avsysml.append(" := " + aa.getInitialValue().trim() + ";\n"); else avsysml.append(" default := " + AvatarType.INTEGER.getDefaultInitialValue().trim() + ";\n"); } else avsysml.append(aa.getDataType().getName() + ";\n"); // datatypes don't have default values } unindent(1); avsysml.append(indentation + "}\n"); } } } /** generates SysML declarations of communication structures and put them at the current position (end) of avsysml. * * Communication structures are * <ul> * <li> Relations, corresponding to Avatar relations </li> * <li> Channels declarations (a channel is an inSignal/outSignal connection inside a relation) </li> * <li> Binding of block signals to channels, to complete channel's characterization </li> * <li> in/out Messages (structures) declarations, linking them to channels. This providing signals with profiles </li> * </ul> */ private void communications2SysML(){ int nbfiforelNumber = 0; int bfiforelNumber = 0; int syncrelNumber = 0; if (avspec.getRelations() == null || avspec.getRelations().size() == 0) return; avsysml.append("\n" + indentation + "// COMMUNICATIONS $$$$$$$$$$$$$$$$$$$$$$$$\n"); for(AvatarRelation ar: avspec.getRelations()) { // declare some parameters depending on relation type ============== int relationType; // SYNC, FIFO or BFIFO int relationNumber; // to distinguish relations with same characteristics String relationConstructor; // #Sync_Rel, #Bfifo_Rel, #NBfifo_Rel (c.f. Avatar SysML library) String relationParameters; // for the call of SysML constructors of relation parts String channelConstructor; // #Sync, #Bfifo, #NBfifo (c.f. Avatar SysML library) // compute parameters depending on relation type ============== // if (ar.isAMS()) ... TODO String blk1 = ar.getBlock1().getName(); String blk2 = ar.getBlock2().getName(); String blk1SysMLname = blockSysMLname(blk1); String blk2SysMLname = blockSysMLname(blk2); relationParameters = "('@block1' = " + blk1SysMLname + ", '@block2' = " + blk2SysMLname; if (! ar.isAsynchronous()) { // sync relation relationType = SYNC; relationNumber = syncrelNumber++; relationConstructor = "'#Sync_Rel'"; channelConstructor = "'#Sync'"; if(ar.isPrivate()) relationParameters += ", '@private'=true)"; // default is false in Avatar SysML library } else { // fifo relation if(ar.isPrivate()) // default: false in Avatar SysML library if(ar.isLossy()) // default: false in Avatar SysML library if(ar.getSizeOfFIFO() > 1) // default size: 1 in Avatar SysML library relationParameters += ", '@private'=true, '@lossy'=true, '@max_size'=" + ar.getSizeOfFIFO() + ")"; else relationParameters += ", '@private'=true, '@lossy'=true)"; else if(ar.getSizeOfFIFO() > 1) // default size: 1 in Avatar SysML library relationParameters += ", '@private'=true, '@max_size'=" + ar.getSizeOfFIFO() + ")"; else relationParameters += ", '@private'=true)"; else if(ar.isLossy()) // default: false in Avatar SysML library if(ar.getSizeOfFIFO() > 1) // default size: 1 in Avatar SysML library relationParameters += ", '@lossy'=true, '@max_size'=" + ar.getSizeOfFIFO() + ")"; else relationParameters += ", '@lossy'=true)"; else if(ar.getSizeOfFIFO() > 1) // default size: 1 in Avatar SysML library relationParameters += ", '@max_size'=" + ar.getSizeOfFIFO() + ")"; if (ar.isBlocking()) { // blocking fifo relation relationType = BFIFO; relationNumber = bfiforelNumber++; relationConstructor = "'#Bfifo_Rel'"; channelConstructor = "'#Bfifo'"; } else { // non blocking fifo relation relationType = NBFIFO; relationNumber = nbfiforelNumber++; relationConstructor = "'#NBfifo_Rel'"; channelConstructor = "'#NBfifo'"; } } // general generation (using parameters above) : put declaration to avsysml ============== String relationSYSMLname = relationSysMLname(blk1, blk2, relationType, relationNumber); // relation declaration -------------------- avsysml.append("\n" + indentation + "// Relation " + relationSYSMLname + "=============\n"); avsysml.append(indentation + "part " + relationSYSMLname + ": " + relationConstructor); if(relationParameters != null) avsysml.append(" = " + relationConstructor + relationParameters); avsysml.append(";\n"); // Channels and associated Messages declarations ---------------- int relationSize = ar.getSignals1().size(); for(int i=0; i<relationSize; i++) { // iterate on signal connections AvatarSignal sig1 = ar.getSignals1().get(i); AvatarSignal sig2 = ar.getSignals2().get(i); boolean out2in = (sig1.getInOut() == AvatarSignal.OUT); String channelSYSMLname = channelSysMLname(channelName(blk1, blk2, sig1.getName(), sig2.getName(), out2in), relationType); String sig1SYSMLname = signalSysMLname(sig1.getName()); String sig2SYSMLname = signalSysMLname(sig2.getName()); String message1SYSMLname = messageSysMLname(blk1,sig1.getName()); String message2SYSMLname = messageSysMLname(blk2,sig2.getName()); MethodInfo sig1Info = new MethodInfo(sig1SYSMLname, message1SYSMLname); // to collect info MethodInfo sig2Info = new MethodInfo(sig2SYSMLname, message2SYSMLname); // to collect info // Channel declaration ......................... avsysml.append("\n" + indentation + "// Channel " + channelSYSMLname + "-------------\n"); avsysml.append(indentation + "part " + channelSYSMLname + " : " + channelConstructor + " = " + channelConstructor + "('@relation' = " + relationSYSMLname + ");\n"); // Binding block signals to the channel ................ if (out2in) { // depends on direction. symetric. avsysml.append(indentation + "binding : '#OutSignalBinding' bind " + blk1SysMLname + "." + sig1SYSMLname + " = " + channelSYSMLname + ";\n"); avsysml.append(indentation + "binding : '#InSignalBinding' bind " + blk2SysMLname + "." + sig2SYSMLname + " = " + channelSYSMLname + ";\n"); } else { avsysml.append(indentation + "binding : '#InSignalBinding' bind " + blk1SysMLname + "." + sig1SYSMLname + " = " + channelSYSMLname + ";\n"); avsysml.append(indentation + "binding : '#OutSignalBinding' bind " + blk2SysMLname + "." + sig2SYSMLname + " = " + channelSYSMLname + ";\n"); } // Message declarations ......................... // one Message per direction, parallel build in two buffers msg1Buf and msg2Buf // put declaration headers StringBuffer msg1Buf = new StringBuffer(indentation + "\n// Message of signal " + blk1SysMLname + "." + sig1SYSMLname + "............\n"); StringBuffer msg2Buf = new StringBuffer(indentation + "\n// Message of signal " + blk2SysMLname + "." + sig2SYSMLname + "............\n"); if (out2in) { msg1Buf.append(indentation + "part def " + message1SYSMLname + " :> '#OutMessage', " + message2SYSMLname + " {\n"); msg2Buf.append(indentation + "part def " + message2SYSMLname + " :> '#InMessage' {\n" + indentation + indentStep + "private part '@channel' : '#Channel' = " + channelSYSMLname + ";\n"); } else { msg1Buf.append(indentation + "part def " + message1SYSMLname + " :> '#InMessage' {\n" + indentation + indentStep + "private part '@channel' : '#Channel' = " + channelSYSMLname + ";\n"); msg2Buf.append(indentation + "part def " + message2SYSMLname + " :> '#OutMessage', " + message1SYSMLname + " {\n"); } indent(1); // handle message fields. in-message fields redefine out-message fields int nbFields = sig1.getListOfOriginalAttributes().size(); // parameters for handling a field AvatarType type; String attributeType; // SysML name associated to type String fieldName1; String fieldName2; int j=0; while (j < nbFields ) { // browse channels' fielsd AvatarAttribute aa1 = sig1.getListOfOriginalAttributes().get(j); // compute parameters for handling this field // compute field type parameters AvatarDataType dataType = null; if (aa1.isDataType()) { dataType = aa1.getDataType(); attributeType = dataTypeSysMLname(dataType.getName()) + ";\n"; type = AvatarType.UNDEFINED; } else if (aa1.isBool()) { attributeType = "Boolean;\n"; type = AvatarType.BOOLEAN; } else if (aa1.isInt()) { attributeType = "Integer;\n"; type = AvatarType.INTEGER; } else { attributeType = "Integer;\n"; type = AvatarType.INTEGER; } // TODO : error: SHOULD NOT BE TIMER // compute field names fieldName1 = fieldSysMLname(aa1.getName()); fieldName2 = fieldSysMLname(sig2.getListOfOriginalAttributes().get(j).getName()); // put field declaration if (out2in) { // depends on direction. symetric. msg1Buf.append(indentation + "attribute " + fieldName1 + " redefines " + fieldName2 + ";\n"); msg2Buf.append(indentation + "attribute " + fieldName2 + " : " + attributeType); } else { msg1Buf.append(indentation + "attribute " + fieldName1 + " : " + attributeType); msg2Buf.append(indentation + "attribute " + fieldName2 + " redefines " + fieldName1 + ";\n"); } // collect info to be used later sig1Info.addField(fieldName1, type, dataType); sig2Info.addField(fieldName2, type, dataType); j++; } unindent(1); msg1Buf.append(indentation + "}\n"); msg2Buf.append(indentation + "}\n"); // put declarations to avsysml if (out2in) { // order only relevant for the aesthetics avsysml.append(msg2Buf); avsysml.append(msg1Buf); } else { avsysml.append(msg1Buf); avsysml.append(msg2Buf); } // memorize collected info methodMap.put(sig1,sig1Info); methodMap.put(sig2,sig2Info); } } } /** generates SysML declarations for block methods. * To be called while generating block declarations. Directly put generated text in avsysml. */ private void method2SysML(AvatarMethod am){ MethodInfo methodInfo; // to collect info // compute return type (null for void methods).................... String returnType; // SysML name of return type, or null List<AvatarAttribute> returns = am.getListOfOriginalReturnAttributes(); // size must be <= 1 if(returns.size() == 0) { // void method returnType = null; methodInfo = new MethodInfo(methodSysMLname(am.getName())); } else { if (returns.get(0).isDataType()) { AvatarDataType returnDataType = returns.get(0).getDataType(); returnType = dataTypeSysMLname(returns.get(0).getDataType().getName()); methodInfo = new MethodInfo(methodSysMLname(am.getName()), AvatarType.UNDEFINED, returnDataType); } else if (returns.get(0).isBool()) { returnType = "Boolean"; methodInfo = new MethodInfo(methodSysMLname(am.getName()), AvatarType.BOOLEAN, null); } else if (returns.get(0).isInt()) { returnType = "Integer"; methodInfo = new MethodInfo(methodSysMLname(am.getName()), AvatarType.INTEGER, null); } else { // ERROR : TIMER, should not happen returnType = "Integer"; methodInfo = new MethodInfo(methodSysMLname(am.getName()), AvatarType.INTEGER, null); } } // put header (different for void and non-void methods)..................... if (returnType == null) avsysml.append(indentation + "action " + methodSysMLname(am.getName()) + ": '#AvatarVoidMethod'"); else avsysml.append(indentation + "calc " + methodSysMLname(am.getName()) + ": '#AvatarCalcMethod'"); // put parameters and return type as fields ....................... // collect corresponding info int nbFields = am.getListOfOriginalAttributes().size(); if (nbFields == 0 && returnType == null) avsysml.append(";\n"); else { avsysml.append(" {\n"); indent(1); int j = 0; // iterate on method parameters, put corresponding fields while (j < nbFields) { AvatarAttribute aa = am.getListOfOriginalAttributes().get(j); if (aa.isDataType()) { avsysml.append(indentation + "attribute " + fieldSysMLname(aa.getName()) + " : " + dataTypeSysMLname(aa.getDataType().getName()) + ";\n"); methodInfo.addField(fieldSysMLname(aa.getName()), AvatarType.UNDEFINED, aa.getDataType()); } else if (aa.isInt()) { avsysml.append(indentation + "attribute " + fieldSysMLname(aa.getName()) + " : Integer;\n"); methodInfo.addField(fieldSysMLname(aa.getName()), AvatarType.INTEGER, null); } else if (aa.isBool()) { avsysml.append(indentation + "attribute " + fieldSysMLname(aa.getName()) + " : Boolean;\n"); methodInfo.addField(fieldSysMLname(aa.getName()), AvatarType.BOOLEAN, null); } else { // ERROR should not happen avsysml.append(indentation + "attribute " + fieldSysMLname(aa.getName()) + " : Integer;\n"); methodInfo.addField(fieldSysMLname(aa.getName()), AvatarType.INTEGER, null); } j++; } // put return field for non-void methods if (returnType != null) avsysml.append(indentation + "return : " + returnType + ";\n"); unindent(1); avsysml.append(indentation + "}\n"); } methodMap.put(am, methodInfo); // memorize collected info } /** generates SysML declarations for block signals. * To be called while generating block declarations. Directly put generated text in avsysml. Signals used in AvatarQueryOnSignal elements must * be typed as fifos (other ones are not constrained). Thus, fifoSet must have been computed before calling this method. */ private void signal2SysML(AvatarSignal as){ avsysml.append(indentation + "part " + methodMap.get(as).getName() + " : " + (fifoSet.contains(as) ? "'#Fifo'" : "'#Channel'") + ";\n"); } /** generates SysML declarations for all blocks: iterate on specification block list */ private void blocks2SysML(){ avsysml.append("\n" + indentation + "// BLOCKS $$$$$$$$$$$$$$$$$$$$$$$$\n"); for(AvatarBlock block: avspec.getListOfBlocks()) if (block.getFather() == null) block2SysML(block); } /** generates SysML declarations for a blocks and direct access to inner blocks through their name (not their full path) */ private void block2SysML(AvatarBlock block){ String blockSYSMLname = blockSysMLname(block.getName()); // to memorize timers while iterating on attributes (delayed declarations) ArrayList<AvatarAttribute> timerList = new ArrayList<AvatarAttribute>(); chainBlock(blockSYSMLname); blocklink(); // to directly access inner blocks through their (short) name // put block header -------------------- avsysml.append("\n" + indentation + "// Block " + blockSYSMLname + "=============\n"); avsysml.append(indentation + "part " + blockSYSMLname + " : '#AvatarBlock' {\n"); indent(1); // put attribute declarations ---------------------------- // no default value in SysML, thus default values are set explicitly // timer attributes are memorized to be declared later if (block.getAttributes() != null && block.getAttributes().size() != 0) { avsysml.append("\n" + indentation + "// Attributes ---------------------\n"); int j = 0; int nbAttributes = block.getOriginalAttributes().size(); while(j < nbAttributes) { // iterate on block's attributes AvatarAttribute aa = block.getOriginalAttributes().get(j); if (aa.isDataType()) { avsysml.append(indentation + "attribute " + attributeSysMLname(aa.getName()) + " : " + dataTypeSysMLname(aa.getDataType().getName()) + ";\n"); } else if (aa.isInt()) { avsysml.append(indentation + "attribute " + attributeSysMLname(aa.getName()) + " : Integer"); if (aa.hasInitialValue()) avsysml.append(" := " + aa.getInitialValue().trim()); else avsysml.append(" default := " + AvatarType.INTEGER.getDefaultInitialValue().trim()); avsysml.append(";\n"); } else if (aa.isBool()) { avsysml.append(indentation + "attribute " + attributeSysMLname(aa.getName()) + " : Boolean"); if (aa.hasInitialValue()) avsysml.append(" := " + aa.getInitialValue().trim()); else avsysml.append(" default := " + AvatarType.BOOLEAN.getDefaultInitialValue().trim()); avsysml.append(";\n"); } else if (aa.isTimer()) { timerList.add(aa); } else {} // TODO : should not happen. to remove if not possible j++; } } // put constant declarations -------------------- if (block.getConstants() != null && block.getConstants().size() != 0) { avsysml.append("\n" + indentation + "// Constants ---------------------\n"); int j = 0; int nbConstants = block.getConstants().size(); while(j < nbConstants) { // iterate on block's constants AvatarAttribute aa = block.getOriginalAttributes().get(j); if (aa.isDataType()) { avsysml.append(indentation + "readolny attribute " + attributeSysMLname(aa.getName()) + " : " + dataTypeSysMLname(aa.getDataType().getName()) + ";\n"); } else if (aa.isInt()) { avsysml.append(indentation + "readolny attribute " + attributeSysMLname(aa.getName()) + " : Integer"); if (aa.hasInitialValue()) avsysml.append(" := " + aa.getInitialValue().trim()); else avsysml.append(" default := " + AvatarType.INTEGER.getDefaultInitialValue().trim()); avsysml.append(";\n"); } else if (aa.isBool()) { avsysml.append(indentation + "readolny attribute " + attributeSysMLname(aa.getName()) + " : Boolean"); if (aa.hasInitialValue()) avsysml.append(" := " + aa.getInitialValue().trim()); else avsysml.append(" default := " + AvatarType.BOOLEAN.getDefaultInitialValue().trim()); avsysml.append(";\n"); } else {} // TODO ? error should not happen (timers arn't constants...) j++; } } // put method declarations -------------------- if (block.getMethods() != null && block.getMethods().size() != 0) { avsysml.append("\n" + indentation + "// Methods ---------------------\n"); for (AvatarMethod am : block.getMethods()) method2SysML(am); } // put signal declarations -------------------- if (block.getSignals() != null && block.getSignals().size() != 0) { avsysml.append("\n" + indentation + "// Signals ---------------------\n"); for (AvatarSignal as : block.getSignals()) signal2SysML(as); } // put timer declarations ------------------- if (timerList != null && timerList.size() != 0) { avsysml.append("\n" + indentation + "// Timers ---------------------\n"); for (AvatarAttribute timer : timerList) avsysml.append(indentation + "part " + timerBlockSysMLname(timer.getName()) + ": '#AvatarTimer' = '#AvatarTimer'();\n"); } // put state-machine description -------------------- avsysml.append("\n" + indentation + "// state-machine -------------------\n"); statemachine2SysML(block.getStateMachine()); // sub-blocks ------------------- List<AvatarBlock> subBlockList = new ArrayList<AvatarBlock>(); for(AvatarBlock blk: avspec.getListOfBlocks()) { if (blk.getFather() == block) subBlockList.add(blk); } if (subBlockList.size() != 0) { avsysml.append("\n" + indentation + "// Sub-Blocks øøøøøøøøøøøøøøøøøøøøøøø\n"); for (AvatarBlock blk : subBlockList) block2SysML(blk); } unindent(1); avsysml.append(indentation + "}\n"); unchainBlock(); } /** generates state information for each state machine element of the specification and technically * added states (before some communication elements). Put this information in a map which is returned. */ private HashMap<AvatarStateMachineElement, StateInfo> computeStateInfo(AvatarStateMachine asm) { // the map to fill and return HashMap<AvatarStateMachineElement, StateInfo> stateMap = new HashMap<AvatarStateMachineElement, StateInfo>(); // associate names to future SysML states......................... // number to identify states in an unambigous way int randomNumber = 0; int countNumber = 0; int sendNumber = 0; int receiveNumber = 0; int setNumber = 0; int resetNumber = 0; int expireNumber = 0; // produces and memorize stateinfo structure (couple name, prename) for all state machine element for (AvatarStateMachineElement asme : asm.getListOfElements()) { StateInfo stateinfo; // states without communication hav just a name. if (asme instanceof AvatarStartState) stateinfo = new StateInfo(startStateSysMLname()); else if (asme instanceof AvatarStopState) stateinfo = new StateInfo(stopStateSysMLname()); else if (asme instanceof AvatarState) stateinfo = new StateInfo(standardStateSysMLname(asme.getName())); else if (asme instanceof AvatarQueryOnSignal) stateinfo = new StateInfo(countStateSysMLname(((AvatarQueryOnSignal) asme).getSignal().getName(), countNumber++)); else if (asme instanceof AvatarRandom) stateinfo = new StateInfo(randomStateSysMLname(randomNumber++)); // next cases rely on communication and thus, are subject to the introduction of an additional "preceding" state. else if (asme instanceof AvatarActionOnSignal) { if (((AvatarActionOnSignal) asme).isSending()) stateinfo = new StateInfo( sendStateSysMLname(((AvatarActionOnSignal) asme).getSignal().getName(), sendNumber), presendStateSysMLname(((AvatarActionOnSignal) asme).getSignal().getName(), sendNumber++)); else stateinfo = new StateInfo(receiveStateSysMLname(((AvatarActionOnSignal) asme).getSignal().getName(), receiveNumber), prereceiveStateSysMLname(((AvatarActionOnSignal) asme).getSignal().getName(), receiveNumber++)); } else if (asme instanceof AvatarSetTimer) { stateinfo = new StateInfo(setTimerStateSysMLname(((AvatarTimerOperator) asme).getTimer().getName(), setNumber), presetTimerStateSysMLname(((AvatarTimerOperator) asme).getTimer().getName(), setNumber++)); } else if (asme instanceof AvatarResetTimer) { stateinfo = new StateInfo(resetTimerStateSysMLname(((AvatarTimerOperator) asme).getTimer().getName(), resetNumber), preresetTimerStateSysMLname(((AvatarTimerOperator) asme).getTimer().getName(), resetNumber++)); } else if (asme instanceof AvatarExpireTimer) { stateinfo = new StateInfo(expireTimerStateSysMLname(((AvatarTimerOperator) asme).getTimer().getName(), expireNumber), preexpireTimerStateSysMLname(((AvatarTimerOperator) asme).getTimer().getName(), expireNumber++)); } else continue; // TODO ? curious states "AvatarEmpty" not handled stateMap.put(asme, stateinfo); } return stateMap; } /** generates SysML description of state machine */ private void statemachine2SysML(AvatarStateMachine asm) { HashMap<AvatarStateMachineElement, StateInfo> stateMap = computeStateInfo(asm); // put state-machine header to avsysml............................. avsysml.append(indentation + "exhibit state '@statemachine' : '#AvatarStateMachine' {\n"); indent(1); // put SysML states descriptions with associated transition descriptions......... stateMap.forEach(new State2SysML(stateMap)); unindent(1); avsysml.append(indentation + "}\n"); } /** buffer to build SysML declarations of outgoing transitions from one state(filled by method transitionsAndRequests) */ private StringBuffer sysMLtransitions = new StringBuffer(); /** buffer to build in-a-state requests associated to the outgoing transitions of this state (filled by method transitionsAndRequests) */ private StringBuffer sysMLrequests = new StringBuffer(); /** communication (and timer) states that require an added state before (as they have incomming transitions with action, from the currently * handled state) (filled by method transitionsAndRequests) */ private List<AvatarStateMachineElement> requirePreCom = new ArrayList<AvatarStateMachineElement>(); /** to iterate on the state machine elements of one block. An iteration puts a state declaration in avsysml, followed by the declarations of * its outgoing transitions. If the needed technically added pre-communication are detected while handling these transitions. The declarations * of these added states are added after the transition declarations, followed by their own single outgoing transition. */ private class State2SysML implements BiConsumer<AvatarStateMachineElement, StateInfo> { /** provides all states with name information */ HashMap<AvatarStateMachineElement, StateInfo> stateMap; /** false untill stopState has been encountered (as it can be encountered several times but generated only once) */ Boolean stopStateDone; public State2SysML(HashMap<AvatarStateMachineElement, StateInfo> _stateMap) { stateMap = _stateMap; stopStateDone = false; } /** puts a state declaration in avsysml, followed by the declarations of * its outgoing transitions. If the needed technically added pre-communication are detected while handling these transitions. The declarations * of these added states are added after the transition declarations, followed by their own single outgoing transition. */ public void accept(AvatarStateMachineElement asme, StateInfo info) { // compute transition declarations and request descriptions for the outgoing transitions of the state // identify states that require an added pre-communication state indent(1); transitionsAndRequests(info.getName(), asme.getNexts(), ((asme instanceof AvatarState) ? "'@pool'" : "'@request'"), stateMap); unindent(1); String requests = sysMLrequests.toString(); // to put after state declaration // put state Descriptions ................................ // depending on state type if(asme instanceof AvatarState){ avsysml.append("\n" + indentation + "state " + info.getName() + " : '#AvatarStandardState'"); if (requests.equals("")) avsysml.append(";\n"); else avsysml.append(" = '#AvatarStandardState'(\n" + requests + "\n" + indentation + ");\n"); } else if(asme instanceof AvatarRandom){ // specific: assign a random value to a variable avsysml.append("\n" + indentation + "state " + info.getName() + " : '#AvatarRandomState' = '#AvatarRandomState'(\n"); if (!requests.equals("")) avsysml.append(requests + ",\n"); // put request description (none means default request) indent(1); avsysml.append(indentation + "'@state_action' = '#Assignment'(\n"); indent(1); // the variable to be assigned avsysml.append(indentation + "'@target' = " + attributeSysMLname(((AvatarRandom)asme).getVariable()) + ",\n"); // the random value to assign avsysml.append(indentation + "'@value' = '#bound_random'(" + expr2SysML(((AvatarRandom)asme).getMinValue().replaceAll("__",".")) + ", " + expr2SysML(((AvatarRandom)asme).getMaxValue().replaceAll("__",".")) +")\n"); unindent(1); avsysml.append(indentation + ")\n"); unindent(1); avsysml.append(indentation + ");\n"); } else if(asme instanceof AvatarQueryOnSignal){ // specific: assign the content size of a signal to a variable avsysml.append("\n" + indentation + "state " + info.getName() + " : '#AvatarCountState' = '#AvatarCountState'(\n"); if (!requests.equals("")) avsysml.append(requests + ",\n"); // put request description (none means default request) indent(1); avsysml.append(indentation + "'@state_action' = '#Assignment'(\n"); indent(1); // the variable to assign avsysml.append(indentation + "'@target' = " + attributeSysMLname(((AvatarQueryOnSignal)asme).getAttribute().getName()) + ",\n"); // the size of the content of the concerned signal avsysml.append(indentation + "'@value' = " + methodMap.get(((AvatarQueryOnSignal)asme).getSignal()).getName() + ".'@amount'()\n"); unindent(1); avsysml.append(indentation + ")\n"); unindent(1); avsysml.append(indentation + ");\n"); } else if(asme instanceof AvatarActionOnSignal){ if(((AvatarActionOnSignal)asme).isSending()) { avsysml.append("\n" + indentation + "state " + info.getName() + " : '#AvatarSendState'"); if (requests.equals("")) // default request avsysml.append(";\n"); else // put requests description avsysml.append(" = '#AvatarSendState'(\n" + requests + "\n" + indentation + ");\n"); } else { avsysml.append("\n" + indentation + "state " + info.getName() + " : '#AvatarReceiveState'"); if (requests.equals("")) // default request avsysml.append(";\n"); else avsysml.append(" = '#AvatarReceiveState'(\n" + requests + "\n" + indentation + ");\n"); } } else if(asme instanceof AvatarStartState){ avsysml.append("\n" + indentation + "entry action " + info.getName() + " :'#AvatarStartState'"); if (requests.equals("")) // default request avsysml.append(";\n"); else // put requests description avsysml.append(" = '#AvatarStartState'(\n" + requests + "\n" + indentation + ");\n"); } else if(asme instanceof AvatarStopState){ // specific mandatory empty request list (implicite) if(!stopStateDone) avsysml.append("\n" + indentation + "exit action " + info.getName() + " :'#AvatarStopState';\n"); stopStateDone = true; } else if(asme instanceof AvatarSetTimer){ avsysml.append("\n" + indentation + "state " + info.getName() + " : '#AvatarSetTimerState'"); if (requests.equals("")) // default request avsysml.append(";\n"); else // put requests description avsysml.append(" = '#AvatarSetTimerState'(\n" + requests + "\n" + indentation + ");\n"); } else if(asme instanceof AvatarResetTimer){ avsysml.append("\n" + indentation + "state " + info.getName() + " : '#AvatarResetTimerState'"); if (requests.equals("")) // default request avsysml.append(";\n"); else // put requests description avsysml.append(" = '#AvatarResetTimerState'(\n" + requests + "\n" + indentation + ");\n"); } else if(asme instanceof AvatarExpireTimer){ avsysml.append("\n" + indentation + "state " + info.getName() + " : '#AvatarExpireTimerState'"); if (requests.equals("")) // default request avsysml.append(";\n"); else // put requests description avsysml.append(" = '#AvatarExpireTimerState'(\n" + requests + "\n" + indentation + ");\n"); } // put transition declarations ................................................. avsysml.append(sysMLtransitions); // put pre-communication states to add and their single outgoing transition for (AvatarStateMachineElement aos: requirePreCom) { // the name of an added states is obtained by StateInfo.getPreName() // the target of its outgoing transition is the communication state itself (StateInfo.getName()) // endTransition adds distribution law information at the end of the transition declarations StateInfo stateinfo = stateMap.get(aos); if (aos instanceof AvatarActionOnSignal) { MethodInfo signalinfo = methodMap.get(((AvatarActionOnSignal)aos).getSignal()); if (((AvatarActionOnSignal)aos).isSending()) { // put pre-communication State avsysml.append("\n" + indentation + "state " + stateinfo.getPreName() + " : '#AvatarPreSendState' = '#AvatarPreSendState' (\n"); indent(1); avsysml.append(indentation + "'@request' = {\n"); indent(1); // sending request from pre-send state to send state avsysml.append(sendRequest2SysML(1, "0", "0", signalinfo, ((AvatarActionOnSignal)aos).getOriginalValues()) + "\n"); unindent(1); avsysml.append(indentation + "}\n"); unindent(1); avsysml.append(indentation + ");\n"); // transition from pre-send state to send state avsysml.append("\n" + indentation + "transition : '#AvatarTransition' first " + stateinfo.getPreName() + " then " + stateinfo.getName() + endTransition(0,"","", 1)); } else { // put pre-communication State avsysml.append("\n" + indentation + "state " + stateinfo.getPreName() + " : '#AvatarPreReceiveState' = '#AvatarPreReceiveState' (\n"); indent(1); avsysml.append(indentation + "'@request' = {\n"); indent(1); // receiving request from pre-receive state to receive state avsysml.append(receiveRequest2SysML(1, "0", "0", signalinfo.getName()) + "\n"); unindent(1); avsysml.append(indentation + "}\n"); unindent(1); avsysml.append(indentation + ");\n"); // transition from pre-receive state to receive state avsysml.append("\n" + indentation + "transition : '#AvatarTransition' first " + stateinfo.getPreName() + "\n"); indent(1); // updating target variables with received values String doAction = receiveActions2SysML(signalinfo, ((AvatarActionOnSignal)aos).getOriginalValues()); if (doAction.length() == 0) // receive simple signal (without carried value) avsysml.append(indentation + "then " + stateinfo.getName() + endTransition(0,"","", 1)); else { // put updating action avsysml.append(doAction); avsysml.append(" then " + stateinfo.getName() + endTransition(0,"","", 1)); } unindent(1); } } else if (aos instanceof AvatarSetTimer) { // a special kind of send request // put pre-communication State avsysml.append("\n" + indentation + "state " + stateinfo.getPreName() + " : '#AvatarPreSendState' = '#AvatarPreSendState' (\n"); indent(1); avsysml.append(indentation + "'@request' = {\n"); indent(1); // put the specific sending request, carrying a value avsysml.append(setTimerRequest2SysML(1, "0", "0", timerBlockSysMLname(((AvatarSetTimer) aos).getTimer().getName()) , ((AvatarSetTimer)aos).getTimerValue()) + " }\n"); unindent(2); avsysml.append(indentation + ");\n"); // put the associated transition avsysml.append("\n" + indentation + "transition : '#AvatarTransition' first " + stateinfo.getPreName() + " then " + stateinfo.getName() + endTransition(0,"","", 1)); } else if (aos instanceof AvatarResetTimer) { // a special kind of send request // put pre-communication State avsysml.append("\n" + indentation + "state " + stateinfo.getPreName() + " : '#AvatarPreSendState' = '#AvatarPreSendState' (\n"); indent(1); avsysml.append(indentation + "'@request' = {\n"); indent(1); // put the specific sending request, carrying no value avsysml.append(resetTimerRequest2SysML(1, "0", "0", timerBlockSysMLname(((AvatarResetTimer) aos).getTimer().getName())) + " }\n"); unindent(2); avsysml.append(indentation + ");\n"); // put the associated transition avsysml.append("\n" + indentation + "transition : '#AvatarTransition' first " + stateinfo.getPreName() + " then " + stateinfo.getName() + endTransition(0,"","", 1)); } else if (aos instanceof AvatarExpireTimer) { // a special kind of send request // put pre-communication State avsysml.append("\n" + indentation + "state " + stateinfo.getPreName() + " : '#AvatarPreReceiveState' = '#AvatarPreReceiveState' (\n"); indent(1); avsysml.append(indentation + "'@request' = {\n"); indent(1); // put the specific receiving request, carrying no value avsysml.append(expireTimerRequest2SysML(1, "0", "0", timerBlockSysMLname(((AvatarExpireTimer) aos).getTimer().getName())) + " }\n"); unindent(2); avsysml.append(indentation + ");\n"); // put the associated transition avsysml.append("\n" + indentation + "transition : '#AvatarTransition' first " + stateinfo.getPreName() + endTransition(0,"","", 1)); indent(1); avsysml.append(indentation + "then " + stateinfo.getName() + ";\n"); unindent(1); } } } } /** distribution law information, to put at the end of associated the transition declarations */ private String endTransition(int delayDistributionLaw, String delayExtra1, String delayExtra2, double probability){ if (delayDistributionLaw == DELAY_UNIFORM_LAW && probability == 1) return ";\n"; // nothing because default StringBuffer result = new StringBuffer(" {\n"); indent(1); // put distribution law result.append(indentation + "attribute '@delayDistributionLaw' : String = \"" + DISTRIBUTION_LAWS[delayDistributionLaw] + "\";\n"); // put distribution law first parameter (if exists) if (NB_OF_EXTRA_ATTRIBUTES[delayDistributionLaw] > 0) result.append(indentation + "attribute '" + LABELS_OF_EXTRA_ATTRIBUTES_1[delayDistributionLaw] + "' : String = \"" + delayExtra1 + "\";\n"); // put distribution law second parameter (if exists) if (NB_OF_EXTRA_ATTRIBUTES[delayDistributionLaw] > 1) result.append(indentation + "attribute '" + LABELS_OF_EXTRA_ATTRIBUTES_2[delayDistributionLaw] + "' : String = \"" + delayExtra2 + "\";\n"); // put probability (if different from the default one) if (probability != DEFAULT_PROBABILITY) result.append(indentation + "attribute '@weight' : Real = " + probability + ";\n"); unindent(1); result.append(indentation + "}\n"); return result.toString(); } /** computes transition declarations and request descriptions for the outgoing transitions of the state. Put the corresponding * texts in the sysMLtransitions and sysMLrequests StringBuffers. While doing this, identify communication states that require * an added pre-communication state. * @param srcName name of the source state * @param nexts outgoing transitions from the source state * @param poolName "pool" or "request" depending on the number of allowed outgoing transitions * @param stateMap to find state names */ private void transitionsAndRequests(String srcName, List<AvatarStateMachineElement> nexts, String poolName, HashMap<AvatarStateMachineElement, StateInfo> stateMap) { // initialization requirePreCom.clear(); sysMLtransitions.delete(0, sysMLtransitions.length()); sysMLrequests.delete(0, sysMLrequests.length()); int nb = nexts.size(); if (nb == 0) { // no transition sysMLrequests.append(indentation + poolName + " = { null }\n"); return; } if (nb == 1) { // no parenthesis required indent(1); // handle the single transition transitionAndRequest(srcName, (AvatarTransition)nexts.get(0), 0, stateMap); unindent(1); if(sysMLrequests.toString().trim().equals("'#immediate_request'")) { // remove because it is the default sysMLrequests.delete(0, sysMLrequests.length()); return; } // wrap without parenthesis sysMLrequests.insert(0, indentation + poolName + " = {\n"); sysMLrequests.append("\n" + indentation + "}"); return; } // more than one request -> list between parenthesis indent(1); // put list for(int i=0; i<nb; i++){ transitionAndRequest(srcName, (AvatarTransition)nexts.get(i), i+1, stateMap); if(i != nb-1) sysMLrequests.append(",\n"); else sysMLrequests.append("\n"); } unindent(1); // wrap with parenthesis sysMLrequests.insert(0, indentation + poolName + " = {(\n"); sysMLrequests.append(indentation + ")}"); } /** remove wrapping brackets of a guard (if exist)*/ private String clean_guard(String _guard) { int open = _guard.indexOf('['); int close = _guard.lastIndexOf(']'); if (open != -1 && close != -1 && close > open) return _guard.substring(open+1, close); else return _guard; } /** add a transition declaration in sysMLtransitions and the associated request description in sysMLrequests. Ii the the * transition has actions and the transition's target is a communication, then this target is identified as requiring an * added pre-communication state (added to requirePreCom). In the produced SysML model, transitions are ordered with indexes * beginning at 1. * * @param srcName source of the transition * @param at the transition to handle * @param index index of the transition, or 0 if transition is alone (real index is 1) * @param stateMap to make state names available */ private void transitionAndRequest(String srcName, AvatarTransition at, int index, HashMap<AvatarStateMachineElement, StateInfo> stateMap){ int transindex = ((index == 0) ? 1 : index); // true index // identify cases and instantiate case specific parameters for the parametrized handling that follows ................ // declare parameters boolean guarded = !at.hasNonDeterministicGuard(); AvatarStateMachineElement target = at.getNext(0); String tgtName; // SysML name of transition's target (in SysML model, may differ from Avatar model, due to added states) int requestType = 0; // 0:trivial, 1:Send, 2:Receive, 3:SetTimer, 4: ResetTimer, 5:ExpireTimer // computes parameters if((at.getActions()!=null && at.getActions().size()!=0) && (target instanceof AvatarActionOnSignal || target instanceof AvatarTimerOperator)) { // added pre-communication state required before target (in avatar) which is communication. Type: trivial. requirePreCom.add(target); tgtName = stateMap.get(target).getPreName(); // transition's target is the added state } else { tgtName = stateMap.get(target).getName(); // transition's target is the same as in Avatar if(target instanceof AvatarActionOnSignal){ // communication if (((AvatarActionOnSignal)target).isSending()) requestType = 1; // Send else requestType = 2; // Receive } else if (target instanceof AvatarSetTimer) requestType = 3; // SetTimer else if (target instanceof AvatarResetTimer) requestType = 4; // ResetTimer else if (target instanceof AvatarExpireTimer) requestType = 5; // ExpireTimer } // put transition's request respecting parameters..................................... if (guarded) { sysMLrequests.append(indentation + "if " + expr2SysML(clean_guard((at).getOriginalGuard())) + " ?\n"); indent(1); } // delay don't depend on parameters String minDelay = ( at.getOriginalMinDelay().length()==0 ? "0" : expr2SysML(at.getOriginalMinDelay()) ); String maxDelay = ( at.getOriginalMaxDelay().length()==0 ? "0" : expr2SysML(at.getOriginalMaxDelay()) ); // call the type specific compute-request method and put request if(requestType == 0) // Trivial sysMLrequests.append(trivialRequest2SysML(transindex, minDelay, maxDelay)); else if (requestType == 1) // Send sysMLrequests.append(sendRequest2SysML(transindex, minDelay, maxDelay, methodMap.get(((AvatarActionOnSignal)target).getSignal()), ((AvatarActionOnSignal)target).getOriginalValues())); else if (requestType == 2) // Receive sysMLrequests.append(receiveRequest2SysML(transindex, minDelay, maxDelay, methodMap.get(((AvatarActionOnSignal)target).getSignal()).getName())); else if (requestType == 3) // Set sysMLrequests.append(setTimerRequest2SysML(transindex, minDelay, maxDelay, timerBlockSysMLname(((AvatarTimerOperator) target).getTimer().getName()), ((AvatarSetTimer) target).getTimerValue())); else if (requestType == 4) // Reset sysMLrequests.append(resetTimerRequest2SysML(transindex, minDelay, maxDelay, timerBlockSysMLname(((AvatarTimerOperator) target).getTimer().getName()))); else // Expire sysMLrequests.append(expireTimerRequest2SysML(transindex, minDelay, maxDelay, timerBlockSysMLname(((AvatarTimerOperator) target).getTimer().getName()))); if(guarded) { unindent(1); sysMLrequests.append("\n" + indentation + "else '#nok_request'(" + transindex + ")"); } // put transition's declaration ........................................ unindent(2); // to reach indent level of request declaration // compute transition'actions String doAction; // the SysML action part of the transition indent(1); if(requestType == 2) // compute receive specific actions (variable update). No other transition actions thanks to added states. doAction = receiveActions2SysML(methodMap.get(((AvatarActionOnSignal)target).getSignal()), ((AvatarActionOnSignal)target).getOriginalValues()); else // computes actions from the Avatar transition's action list doAction = transitionActions2SysML(at); unindent(1); // put transition declaration // header sysMLtransitions.append("\n" + indentation + "transition : '#AvatarTransition' first " + srcName); // index if(index > 0) // not default thus explicit index sysMLtransitions.append(" if '@index' == " + index + "\n"); else // default thus implicit index sysMLtransitions.append("\n"); // actions indent(1); if (doAction == null || doAction.length() == 0) // no action, put target and distribution law information sysMLtransitions.append(indentation + "then " + tgtName + endTransition(at.getDelayDistributionLaw(), at.getDelayExtra1(), at.getDelayExtra2(), at.getProbability())); else { // put actions, target and distribution law information sysMLtransitions.append(doAction); sysMLtransitions.append(" then " + tgtName + endTransition(at.getDelayDistributionLaw(),at.getDelayExtra1(),at.getDelayExtra2(), at.getProbability())); } indent(1); } /** computes request to non-communication state * * @param index request index * @param min minimal delay. not null and not empty (must be set to "0" in these cases) * @param max maximal delay. not null and not empty (must be set to "0" in these cases) * @return text of the relevant SysML call to the relevant SysML constructor */ private String trivialRequest2SysML(int index, String min, String max) { if (max.equals("0")) // no delay or simple delay if (min.equals("0")) // no delay if (index == 1) // default thus implicit return indentation + "'#immediate_request'"; else // explicit return indentation + "'#TrivialRequest'('@index' = " + index + ")"; else // simple delay if (index == 1) // default thus implicit return indentation + "'#TrivialRequest'('@delay' = " + min + ")"; else // explicit return indentation + "'#TrivialRequest'('@index' = " + index + ", '@delay' = " + min + ")"; else if (max.trim().equals(min.trim())) // simple delay (equal bounds) if(index == 1) // default thus implicit return indentation + "'#TrivialRequest'('@delay' = " + min + ")"; else // explicit return indentation + "'#TrivialRequest'('@index' = " + index + ", '@delay' = " + min + ")"; else // range delay if(index == 1) // default thus implicit return indentation + "'#TrivialRequest'('@delay' = '#bound_random'(" + min + ", " + max + "))"; else // explicit return indentation + "'#TrivialRequest'('@index' = " + index + ", '@delay' = '#bound_random'(" + min + ", " + max + "))"; } /** computes request to send state * * @param index request index * @param min minimal delay. not null and not empty (must be set to "0" in these cases) * @param max maximal delay. not null and not empty (must be set to "0" in these cases) * @param signalInfo name and profile information about the concerned signal * @param values strings denoting send parameter values * @return text of the relevant SysML call to the relevant SysML constructor */ private String sendRequest2SysML(int index, String min, String max, MethodInfo signalInfo, List<String> values) { // request Header (SysML constructor) StringBuffer result = new StringBuffer(indentation + "'#SendRequest'(\n"); indent(1); // index if (index != 1) // not default thus explicit result.append(indentation + "'@index' = " + index + ",\n"); // signal (equivalent to channel in SysML) result.append(indentation + "'@channel'= " + signalInfo.getName() + ",\n"); // delay if (max.equals("0")) { // no delay or simple delay if (!min.equals("0")) // simple delay result.append(indentation + "'@delay' = " + min + ",\n"); } else if (max.trim().equals(min.trim())) // simple delay (equal bounds) result.append(indentation + "'@delay' = " + min + ",\n"); else // range delay result.append(indentation + "'@delay' = '#bound_random'(" + min + ", " + max + "),\n"); // payload int nbFields = signalInfo.getArity(); if (nbFields == 0) // simple SysML constructor call result.append(indentation + "'@payload' = " + signalInfo.getMessageType() + "()\n"); else { // SysML constructor call with parameters result.append(indentation + "'@payload' = " + signalInfo.getMessageType() + "(\n"); indent(1); int j = 0; while (j < nbFields) { // iterate on parameters result.append(indentation + expr2SysML(values.get(j)) + ",\n"); j++; } result.replace(result.length()-2, result.length(), " )\n"); unindent(1); } unindent(1); result.append(indentation + ")"); return result.toString(); } /** computes request to set-timer state * * @param index request index * @param min minimal delay. not null and not empty (must be set to "0" in these cases) * @param max maximal delay. not null and not empty (must be set to "0" in these cases) * @param timer name of the concerned timer * @param value string denoting the duration until timer expiration (countdown) * @return text of the relevant SysML call to the relevant SysML constructor */ private String setTimerRequest2SysML(int index, String min, String max, String timer, String value) { // request Header (SysML constructor) StringBuffer result = new StringBuffer(indentation + "'#AvatarSetTimerRequest'(\n"); indent(1); // index if (index != 1) // not default thus explicit result.append(indentation + "'@index' = " + index + ",\n"); // setting a timer is sending a message on a dedicated "@set" channel of the timer block result.append(indentation + "'@channel'= " + timer + ".'@set'" + ",\n"); // delay if (max.equals("0")) { // no delay or simple delay if (!min.equals("0")) // simple delay result.append(indentation + "'@delay' = " + min + ",\n"); } else if (max.equals(min)) // simple delay (equal bounds) result.append(indentation + "'@delay' = " + min + ",\n"); else // range delay result.append(indentation + "'@delay' = '#bound_random'(" + min + ", " + max + "),\n"); // payload result.append(indentation + "'@payload' = '#TimerSetMsg'(" + value + ")\n"); unindent(1); result.append(indentation + ")"); return result.toString(); } /** computes request to reset-timer state * * @param index request index * @param min minimal delay. not null and not empty (must be set to "0" in these cases) * @param max maximal delay. not null and not empty (must be set to "0" in these cases) * @param timer name of the concerned timer * @return text of the relevant SysML call to the relevant SysML constructor */ private String resetTimerRequest2SysML(int index, String min, String max, String timer) { // request Header (SysML constructor) StringBuffer result = new StringBuffer(indentation + "'#AvatarResetTimerRequest'(\n"); indent(1); //index if (index != 1) // not default thus explicit result.append(indentation + "'@index' = " + index + ",\n"); // resetting a timer is sending a message on a dedicated "@reset" channel of the timer block result.append(indentation + "'@channel'= " + timer + ".'@reset'" + ",\n"); // delay if (max.equals("0")) { // no delay or simple delay if (!min.equals("0")) // simple delay result.append(indentation + "'@delay' = " + min + ",\n"); } else if (max.equals(min)) // simple delay (equal bounds) result.append(indentation + "'@delay' = " + min + ",\n"); else // range delay result.append(indentation + "'@delay' = '#bound_random'(" + min + ", " + max + "),\n"); // payload (simple message without value) result.append(indentation + "'@payload' = '#TimerResetMsg'()\n"); unindent(1); result.append(indentation + ")"); return result.toString(); } /** computes request to send state * * @param index request index * @param min minimal delay. not null and not empty (must be set to "0" in these cases) * @param max maximal delay. not null and not empty (must be set to "0" in these cases) * @param signalName name of the concerned signal * @return text of the relevant SysML call to the relevant SysML constructor */ private String receiveRequest2SysML(int index, String min, String max, String signalName) { // request Header (SysML constructor) StringBuffer result = new StringBuffer(indentation + "'#ReceiveRequest'(\n"); indent(1); // index if (index != 1) // not default thus explicit result.append(indentation + "'@index' = " + index + ",\n"); // signal (equivalent to channel in SysML) result.append(indentation + "'@channel'= " + signalName); // delay if (max.equals("0")) // no delay or simple delay if (!min.equals("0")) // simple delay result.append(",\n" + indentation + "'@delay' = " + min + "\n"); else // no delay result.append("\n"); else if (max.equals(min)) // simple delay (equal bounds) result.append(indentation + "'@delay' = " + min + ",\n"); else // range delay result.append(",\n" + indentation + "'@delay' = '#bound_random'(" + min + ", " + max + ")\n"); unindent(1); result.append(indentation + ")"); return result.toString(); } private String expireTimerRequest2SysML(int index, String min, String max, String chname) { // request Header (SysML constructor) StringBuffer result = new StringBuffer(indentation + "'#AvatarExpireTimerRequest'(\n"); indent(1); // index if (index != 1) // not default thus explicit result.append(indentation + "'@index' = " + index + ",\n"); // timer expiration is detected by receiving a message on a dedicated "@expire" channel of the timer block result.append(indentation + "'@channel'= " + chname + ".'@expire'"); // delay if (max.equals("0")) // no delay or simple delay if (!min.equals("0")) // simple delay result.append(",\n" + indentation + "'@delay' = " + min + "\n"); else // no delay result.append("\n"); else if (max.equals(min)) // simple delay (equal bounds) result.append(indentation + "'@delay' = " + min + ",\n"); else // range delay result.append(",\n" + indentation + "'@delay' = '#bound_random'(" + min + ", " + max + ")\n"); unindent(1); result.append(indentation + ")"); return result.toString(); } /** computes the action part of the SysML transition associated to an Avatar transition */ String transitionActions2SysML(AvatarTransition at) { StringBuffer result; int size = at.getNbOfAction(); if (size == 0) return null; // header result = new StringBuffer(indentation + "do action : '#TransitionAction' {\n" + indentation + indentStep + "first start;\n"); indent(1); for(int i = 0; i < size; i++) { // iterate on actions of the Avatar transition String ac = at.getOriginalAction(i); int eq = ac.indexOf("="); if(eq != -1) { // assignment action String lh = leftHandSysMLname(ac.substring(0,eq).trim()); // lefthand String rh = expr2SysML(ac.substring(eq+1).trim()); // righthand result.append(indentation + "then assign " + lh + ":= " + rh + ";\n"); } else { // method call result.append(indentation + "then action = " + expr2SysML(ac) + ";\n"); } } result.append(indentation + "then done;\n"); unindent(1); result.append(indentation + "}"); return result.toString(); } /** computes the action part of the SysML transition associated to an Avatar transition leading to a receive state. Due to the introduction of * pre-communication states (when required), there are no other actions than the updating of variables w.r.t. the received message */ String receiveActions2SysML(MethodInfo signalInfo, List<String> values) { if (values == null || values.size() == 0) return ""; // simple signal without value StringBuffer result = new StringBuffer(indentation + "do action : '#ReceiveAction' {\n"); indent(1); // put access to the received message result.append(indentation + "item '@msg' : " + signalInfo.getMessageType() + " = '@payload' as " + signalInfo.getMessageType() + ";\n" + indentation + "first start;\n"); int nbFields = signalInfo.getArity(); int j = 0; while (j < nbFields) { // iterate on message fields result.append(indentation + "then assign " + leftHandSysMLname(values.get(j)) + " := '@msg'." + signalInfo.getFieldName(j) + ";\n"); j++; } result.append(indentation + "then done;\n"); unindent(1); result.append(indentation + "}"); return result.toString(); } }