123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336 |
- '''*****************************************************************************
- AToMPM - A Tool for Multi-Paradigm Modelling
- Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
- This file is part of AToMPM.
- AToMPM is free software: you can redistribute it and/or modify it under the
- terms of the GNU Lesser General Public License as published by the Free Software
- Foundation, either version 3 of the License, or (at your option) any later
- version.
- AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public License along
- with AToMPM. If not, see <http://www.gnu.org/licenses/>.
- *****************************************************************************'''
- import copy, subprocess, traceback, os
- from pytcore.core.himesis import HConstants as HC
- from utils import Utilities as utils
- '''
- implements the DesignerAPI functions used in pattern/attribute constraints
- and actions
- NOTE: the current username is required (and remembered) to enable file I/O
- within his own directory... the current backend aswid is required (and
- remembered) to enable httpreqs to backend from rule code
- NOTE: cloned from mmmk.js constraint/action/accessor DesignerAPI... same
- comments and thoughts apply... noteworthy differences are
- . instead of atompm ids, API functions now take __pLabels as node
- identifiers
- . 'sys_*()' functions enable system calls and file i/o
- . 'session_*()' functions provide a clean and efficient way to
- remember information across rules
- . the 'isConnectionType()' function '''
- class DesignerAPI :
- def __init__(self,username,aswid,mtwid) :
- self._username = username
- self._aswid = aswid
- self._mtwid = mtwid
-
- def _aswPrintReq(self,msg):
- utils.httpReq( 'PUT',
- '127.0.0.1:8124',
- '/GET/console?wid='+self._aswid,
- {'text':msg})
-
- def _printToDevCon(self,msg):
- self._aswPrintReq(msg)
- '''
- configure this instance of DesignerAPI
- 1. set the Himesis graph that will be used to satisfy getAttr/setAttr/...
- function calls... this graph represents a (partially) matched LHS, NAC
- or RHS
- 2. set the type of the calling function... legal values are
- patternCondition
- patternAction
- attrCondition
- attrAction
- 3. set the pattern-label -> graph-index mapping
- 4. set reference to exception holder
- 5. set the __pLabel of the owner of the calling function, if any
- 6. set the attribute that owns the calling function, if any
- 7. set reference to journal (to write changes to), if any
- 8. extend the pattern-label -> graph-index map w/ $atompmId -> graph-index
- mappings... this enables access to all graph nodes (not just matched
- ones, that are incidently the only ones w/ associated __pLabels)
-
- TBI: self._pl2gi should be given a name that reflects step 8. and errors
- pertaining to missing/incorrect pLabel parameters should be updated
- to account for fact that specified pLabels may not be pLabels (i.e.,
- they could be of the form '$atompmId:*' '''
- def configure(self,graph,type,pl2gi,ex,pLabel=None,attr=None,journal=None) :
- self._graph = graph
- self._type = type
- self._pl2gi = pl2gi
- self._ex = ex
- self._pLabel = pLabel
- self._attr = attr
- self._journal = journal
- matched = self._pl2gi.values()
- for v in self._graph.vs :
- if v.index not in matched :
- self._pl2gi['$atompmId:'+str(v['$atompmId'])] = v.index
-
-
- '''
- wrapper around raise()... to be properly reported to the client,
- exceptions can not simply be raised... this would just cause them to be
- captured by their ExecutionContext who might return a generic error (not
- the one it caught)... instead, we store exceptions, if any, in self._ex
- for the caller to do with them what he wants... '''
- def __raise(self,msg) :
- self._ex['$err'] = msg
- raise RuntimeError(msg)
- # ***************************** API functions ***************************** #
- def _getAttr(self,attr=None,pLabel=None) :
- if pLabel == None :
- if self._type.startswith('pattern') :
- self.__raise(\
- 'getAttr() requires a __pLabel in pattern conditions/actions')
- pLabel = self._pLabel
- elif not self._type.startswith('pattern') :
- self.__raise(\
- 'getAttr() only accepts a __pLabel in pattern conditions/actions')
- elif pLabel not in self._pl2gi :
- self.__raise(\
- 'invalid getAttr() __pLabel :: '+str(pLabel)+' (either no node '+\
- 'with that __pLabel exists, or none is matched yet)')
- if attr == None :
- if not self._type.startswith('attr') :
- self.__raise(\
- 'getAttr() can only be called without parameters'+\
- 'in attribute conditions/actions')
- attr = self._attr
- n = self._graph.vs[self._pl2gi[pLabel]]
- if attr not in n.attribute_names() :
- self.__raise('invalid getAttr() attribute :: '+str(attr))
- return copy.deepcopy(n[attr])
-
-
- def _getAttrNames(self,pLabel=None):
- if pLabel == None :
- if self._type.startswith('pattern') :
- self.__raise(\
- 'getAttrNames() requires a __pLabel in pattern conditions/actions')
- pLabel = self._pLabel
- elif not self._type.startswith('pattern') :
- self.__raise(\
- 'getAttrNames() only accepts a __pLabel in pattern conditions/actions')
- elif pLabel not in self._pl2gi :
- self.__raise(\
- 'invalid getAttr() __pLabel :: '+str(pLabel)+' (either no node '+\
- 'with that __pLabel exists, or none is matched yet)')
- n = self._graph.vs[self._pl2gi[pLabel]]
- return n.attribute_names()
- def _hasAttr(self,attr=None,pLabel=None) :
- if pLabel == None :
- if self._type.startswith('pattern') :
- self.__raise(\
- 'hasAttr() requires a __pLabel in pattern conditions/actions')
- pLabel = self._pLabel
- elif not self._type.startswith('pattern') :
- self.__raise(\
- 'hasAttr() only accepts a __pLabel in pattern conditions/actions')
- elif pLabel not in self._pl2gi :
- self.__raise(\
- 'invalid hasAttr() __pLabel :: '+str(pLabel)+' (either no node '+\
- 'with that __pLabel exists, or none is matched yet)')
- if attr == None :
- self.__raise(\
- 'hasAttr() can not be called without an attribute parameter')
- n = self._graph.vs[self._pl2gi[pLabel]]
- return attr in n.attribute_names()
- def _setAttr(self,attr,val,pLabel) :
- if self._type != 'patternAction' :
- self.__raise('setAttr() can only be used within RHS actions')
- elif pLabel == None :
- self.__raise('setAttr() requires a valid __pLabel')
- elif pLabel not in self._pl2gi :
- self.__raise(\
- 'invalid setAttr() __pLabel :: '+str(pLabel)+' (either no node '+\
- 'with that __pLabel exists, or none is matched yet)')
- n = self._graph.vs[self._pl2gi[pLabel]]
- if attr not in n.attribute_names() :
- self.__raise('invalid setAttr() attribute :: '+attr)
- oldVal = n[attr]
- if oldVal != val :
- n[attr] = val
- self._journal.append(
- {'op':'CHATTR',
- 'guid':n[HC.GUID],
- 'attr':attr,
- 'old_val':oldVal,
- 'new_val':val})
- n[HC.MT_DIRTY] = True
- def _getAllNodes(self,fulltypes=None) :
- if not self._type.startswith('pattern') :
- self.__raise(\
- 'getAllNodes() can only be used in pattern conditions/actions')
- elif fulltypes != None and fulltypes.__class__ != [].__class__ :
- self.__raise('invalid getAllNodes() fulltypes array :: '+fulltypes)
- pLabels = []
- for pLabel in self._pl2gi :
- n = self._graph.vs[self._pl2gi[pLabel]]
- if fulltypes == None or n[HC.FULLTYPE] in fulltypes :
- pLabels.append(pLabel)
- return pLabels
- def _getNeighbors(self,dir,type,pLabel) :
- if not self._type.startswith('pattern') :
- self.__raise(\
- 'getNeighbors() can only be used in pattern conditions/actions')
- elif pLabel == None :
- self.__raise('getNeighbors() requires a valid __pLabel')
- elif pLabel not in self._pl2gi :
- self.__raise(\
- 'invalid getNeighbors() __pLabel :: '+str(pLabel)+' (no node '+\
- 'with that __pLabel exists)')
- pLabels = set()
- if len(self._graph.es) > 0 :
- gi2pl = dict((v, k) for (k, v) in self._pl2gi.items())
- idx = self._pl2gi[pLabel]
- for e in self._graph.get_edgelist() :
- if e[0] == idx and \
- (dir == '>' or dir == '*' or dir == "out") and \
- (type == '*' or self._graph.vs[e[1]][HC.FULLTYPE] == type) :
- pLabels.add(gi2pl[e[1]])
- elif e[1] == idx and \
- (dir == '<' or dir == '*' or dir == "in") and \
- (type == '*' or self._graph.vs[e[0]][HC.FULLTYPE] == type) :
- pLabels.add(gi2pl[e[0]])
- return list(pLabels)
- def _isConnectionType(self,pLabel) :
- if not self._type.startswith('pattern') :
- self.__raise(\
- 'isConnectionType() can only be used in pattern conditions/actions')
- elif pLabel == None :
- self.__raise('isConnectionType() requires a valid __pLabel')
- elif pLabel not in self._pl2gi :
- self.__raise(\
- 'invalid isConnectionType() __pLabel :: '+str(pLabel)+' (no node '+\
- 'with that __pLabel exists)')
- return self._graph.vs[self._pl2gi[pLabel]][HC.CONNECTOR_TYPE]
- def _session_get(self,key) :
- if key in self._graph.session :
- return self._graph.session[key]
- def _session_put(self,key,val) :
- if not self._type.endswith('Action') :
- self.__raise(\
- 'session_put() can only be used in attribute and pattern actions')
- self._graph.session[key] = val
- return val
- def _pauseTransformation(self):
- self._httpReq("PUT", '127.0.0.1:8125', '/execmode?wid='+self._mtwid, {'mode':'pause'})
-
- def _stopTransformation(self):
- self._httpReq("PUT", '127.0.0.1:8125', '/execmode?wid='+self._mtwid, {'mode':'stop'})
-
- def _resumeTransformation(self):
- self._httpReq("PUT", '127.0.0.1:8125', '/execmode?wid='+self._mtwid, {'mode':'play'})
- def _httpReq(self,method,host,uri,data) :
- if host == None :
- return utils.httpReq(
- method,
- '127.0.0.1:8124',
- uri+'?wid='+self._aswid,
- data)
- else :
- return utils.httpReq(method,host,uri,data)
-
- def _print(self,str) :
- print(str)
- def _sys_call(self,args) :
- try :
- return subprocess.call(args)
- except OSError as ex :
- self.__raise('system call crashed on :: '+ex.strerror)
- def _sys_mkdir(self,path) :
- try :
- return os.makedirs('./users/'+self._username+'/'+path)
- except OSError as ex :
- if ex.errno != 17 :
- #ignore directory already exists error
- self.__raise('directory creation failed :: '+ex.strerror)
- def _sys_readf(self,path) :
- f = open('./users/'+self._username+'/'+path,'r')
- contents = f.read()
- f.close()
- return contents
- def _sys_writef(self,path,content,append=True) :
- if append :
- f = open('./users/'+self._username+'/'+path,'a')
- else :
- f = open('./users/'+self._username+'/'+path,'w')
- f.write(content)
- f.close()
|