dapi.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. '''This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
  2. Copyright 2011 by the AToMPM team and licensed under the LGPL
  3. See COPYING.lesser and README.md in the root of this project for full details'''
  4. import copy, subprocess, traceback, os
  5. from pytcore.core.himesis import HConstants as HC
  6. from utils import Utilities as utils
  7. '''
  8. implements the DesignerAPI functions used in pattern/attribute constraints
  9. and actions
  10. NOTE: the current username is required (and remembered) to enable file I/O
  11. within his own directory... the current backend aswid is required (and
  12. remembered) to enable httpreqs to backend from rule code
  13. NOTE: cloned from mmmk.js constraint/action/accessor DesignerAPI... same
  14. comments and thoughts apply... noteworthy differences are
  15. . instead of atompm ids, API functions now take __pLabels as node
  16. identifiers
  17. . 'sys_*()' functions enable system calls and file i/o
  18. . 'session_*()' functions provide a clean and efficient way to
  19. remember information across rules
  20. . the 'isConnectionType()' function '''
  21. class DesignerAPI :
  22. def __init__(self,username,aswid,mtwid) :
  23. self._username = username
  24. self._aswid = aswid
  25. self._mtwid = mtwid
  26. def _aswPrintReq(self,msg):
  27. utils.httpReq( 'PUT',
  28. '127.0.0.1:8124',
  29. '/GET/console?wid='+self._aswid,
  30. {'text':msg})
  31. def _printToDevCon(self,msg):
  32. self._aswPrintReq(msg)
  33. '''
  34. configure this instance of DesignerAPI
  35. 1. set the Himesis graph that will be used to satisfy getAttr/setAttr/...
  36. function calls... this graph represents a (partially) matched LHS, NAC
  37. or RHS
  38. 2. set the type of the calling function... legal values are
  39. patternCondition
  40. patternAction
  41. attrCondition
  42. attrAction
  43. 3. set the pattern-label -> graph-index mapping
  44. 4. set reference to exception holder
  45. 5. set the __pLabel of the owner of the calling function, if any
  46. 6. set the attribute that owns the calling function, if any
  47. 7. set reference to journal (to write changes to), if any
  48. 8. extend the pattern-label -> graph-index map w/ $atompmId -> graph-index
  49. mappings... this enables access to all graph nodes (not just matched
  50. ones, that are incidently the only ones w/ associated __pLabels)
  51. TBI: self._pl2gi should be given a name that reflects step 8. and errors
  52. pertaining to missing/incorrect pLabel parameters should be updated
  53. to account for fact that specified pLabels may not be pLabels (i.e.,
  54. they could be of the form '$atompmId:*' '''
  55. def configure(self,graph,type,pl2gi,ex,pLabel=None,attr=None,journal=None) :
  56. self._graph = graph
  57. self._type = type
  58. self._pl2gi = pl2gi
  59. self._ex = ex
  60. self._pLabel = pLabel
  61. self._attr = attr
  62. self._journal = journal
  63. matched = self._pl2gi.values()
  64. for v in self._graph.vs :
  65. if v.index not in matched :
  66. self._pl2gi['$atompmId:'+str(v['$atompmId'])] = v.index
  67. '''
  68. wrapper around raise()... to be properly reported to the client,
  69. exceptions can not simply be raised... this would just cause them to be
  70. captured by their ExecutionContext who might return a generic error (not
  71. the one it caught)... instead, we store exceptions, if any, in self._ex
  72. for the caller to do with them what he wants... '''
  73. def __raise(self,msg) :
  74. self._ex['$err'] = msg
  75. print("Error: " + str(msg))
  76. raise RuntimeError(msg)
  77. # ***************************** API functions ***************************** #
  78. def _getAttr(self,attr=None,pLabel=None) :
  79. if pLabel == None :
  80. if self._type.startswith('pattern') :
  81. self.__raise(\
  82. 'getAttr() requires a __pLabel in pattern conditions/actions')
  83. pLabel = self._pLabel
  84. elif not self._type.startswith('pattern') :
  85. self.__raise(\
  86. 'getAttr() only accepts a __pLabel in pattern conditions/actions')
  87. elif pLabel not in self._pl2gi :
  88. self.__raise(\
  89. 'invalid getAttr() __pLabel :: '+str(pLabel)+' (either no node '+\
  90. 'with that __pLabel exists, or none is matched yet)')
  91. if attr == None :
  92. if not self._type.startswith('attr') :
  93. self.__raise(\
  94. 'getAttr() can only be called without parameters'+\
  95. 'in attribute conditions/actions')
  96. attr = self._attr
  97. n = self._graph.vs[self._pl2gi[pLabel]]
  98. if attr not in n.attribute_names() :
  99. self.__raise('invalid getAttr() attribute :: '+str(attr))
  100. return copy.deepcopy(n[attr])
  101. def _getAttrNames(self,pLabel=None):
  102. if pLabel == None :
  103. if self._type.startswith('pattern') :
  104. self.__raise(\
  105. 'getAttrNames() requires a __pLabel in pattern conditions/actions')
  106. pLabel = self._pLabel
  107. elif not self._type.startswith('pattern') :
  108. self.__raise(\
  109. 'getAttrNames() only accepts a __pLabel in pattern conditions/actions')
  110. elif pLabel not in self._pl2gi :
  111. self.__raise(\
  112. 'invalid getAttr() __pLabel :: '+str(pLabel)+' (either no node '+\
  113. 'with that __pLabel exists, or none is matched yet)')
  114. n = self._graph.vs[self._pl2gi[pLabel]]
  115. return n.attribute_names()
  116. def _hasAttr(self,attr=None,pLabel=None) :
  117. if pLabel == None :
  118. if self._type.startswith('pattern') :
  119. self.__raise(\
  120. 'hasAttr() requires a __pLabel in pattern conditions/actions')
  121. pLabel = self._pLabel
  122. elif not self._type.startswith('pattern') :
  123. self.__raise(\
  124. 'hasAttr() only accepts a __pLabel in pattern conditions/actions')
  125. elif pLabel not in self._pl2gi :
  126. self.__raise(\
  127. 'invalid hasAttr() __pLabel :: '+str(pLabel)+' (either no node '+\
  128. 'with that __pLabel exists, or none is matched yet)')
  129. if attr == None :
  130. self.__raise(\
  131. 'hasAttr() can not be called without an attribute parameter')
  132. n = self._graph.vs[self._pl2gi[pLabel]]
  133. return attr in n.attribute_names()
  134. def _setAttr(self,attr,val,pLabel) :
  135. if self._type != 'patternAction' :
  136. self.__raise('setAttr() can only be used within RHS actions')
  137. elif pLabel == None :
  138. self.__raise('setAttr() requires a valid __pLabel')
  139. elif pLabel not in self._pl2gi :
  140. self.__raise(\
  141. 'invalid setAttr() __pLabel :: '+str(pLabel)+' (either no node '+\
  142. 'with that __pLabel exists, or none is matched yet)')
  143. n = self._graph.vs[self._pl2gi[pLabel]]
  144. if attr not in n.attribute_names() :
  145. self.__raise('invalid setAttr() attribute :: '+attr)
  146. oldVal = n[attr]
  147. if oldVal != val :
  148. n[attr] = val
  149. self._journal.append(
  150. {'op':'CHATTR',
  151. 'guid':n[HC.GUID],
  152. 'attr':attr,
  153. 'old_val':oldVal,
  154. 'new_val':val})
  155. n[HC.MT_DIRTY] = True
  156. def _getAllNodes(self,fulltypes=None) :
  157. if not self._type.startswith('pattern') :
  158. self.__raise(\
  159. 'getAllNodes() can only be used in pattern conditions/actions')
  160. elif fulltypes != None and fulltypes.__class__ != [].__class__ :
  161. self.__raise('invalid getAllNodes() fulltypes array :: '+fulltypes)
  162. pLabels = []
  163. for pLabel in self._pl2gi :
  164. n = self._graph.vs[self._pl2gi[pLabel]]
  165. if fulltypes == None or n[HC.FULLTYPE] in fulltypes :
  166. pLabels.append(pLabel)
  167. return pLabels
  168. def _getNodesFromLabels(self, pLabels):
  169. nodes = [self._graph.vs[self._pl2gi[pLabel]] for pLabel in pLabels]
  170. return nodes
  171. def _getNeighbors(self,dir,type,pLabel) :
  172. if not self._type.startswith('pattern') :
  173. self.__raise(\
  174. 'getNeighbors() can only be used in pattern conditions/actions')
  175. elif pLabel == None :
  176. self.__raise('getNeighbors() requires a valid __pLabel')
  177. elif pLabel not in self._pl2gi :
  178. self.__raise(\
  179. 'invalid getNeighbors() __pLabel :: '+str(pLabel)+' (no node '+\
  180. 'with that __pLabel exists)')
  181. pLabels = set()
  182. if len(self._graph.es) > 0 :
  183. gi2pl = dict((v, k) for (k, v) in self._pl2gi.items())
  184. idx = self._pl2gi[pLabel]
  185. for e in self._graph.get_edgelist() :
  186. if e[0] == idx and \
  187. (dir == '>' or dir == '*' or dir == "out") and \
  188. (type == '*' or self._graph.vs[e[1]][HC.FULLTYPE] == type) :
  189. pLabels.add(gi2pl[e[1]])
  190. elif e[1] == idx and \
  191. (dir == '<' or dir == '*' or dir == "in") and \
  192. (type == '*' or self._graph.vs[e[0]][HC.FULLTYPE] == type) :
  193. pLabels.add(gi2pl[e[0]])
  194. return list(pLabels)
  195. def _isConnectionType(self,pLabel) :
  196. if not self._type.startswith('pattern') :
  197. self.__raise(\
  198. 'isConnectionType() can only be used in pattern conditions/actions')
  199. elif pLabel == None :
  200. self.__raise('isConnectionType() requires a valid __pLabel')
  201. elif pLabel not in self._pl2gi :
  202. self.__raise(\
  203. 'invalid isConnectionType() __pLabel :: '+str(pLabel)+' (no node '+\
  204. 'with that __pLabel exists)')
  205. return self._graph.vs[self._pl2gi[pLabel]][HC.CONNECTOR_TYPE]
  206. def _session_get(self,key) :
  207. if key in self._graph.session :
  208. return self._graph.session[key]
  209. def _session_put(self,key,val) :
  210. if not self._type.endswith('Action') :
  211. self.__raise(\
  212. 'session_put() can only be used in attribute and pattern actions')
  213. self._graph.session[key] = val
  214. return val
  215. def _pauseTransformation(self):
  216. self._httpReq("PUT", '127.0.0.1:8125', '/execmode?wid='+self._mtwid, {'mode':'pause'})
  217. def _stopTransformation(self):
  218. self._httpReq("PUT", '127.0.0.1:8125', '/execmode?wid='+self._mtwid, {'mode':'stop'})
  219. def _resumeTransformation(self):
  220. self._httpReq("PUT", '127.0.0.1:8125', '/execmode?wid='+self._mtwid, {'mode':'play'})
  221. def _httpReq(self,method,host,uri,data) :
  222. if host == None :
  223. return utils.httpReq(
  224. method,
  225. '127.0.0.1:8124',
  226. uri+'?wid='+self._aswid,
  227. data)
  228. else :
  229. return utils.httpReq(method,host,uri,data)
  230. def _print(self,str) :
  231. print(str)
  232. def _sys_call(self,args) :
  233. try :
  234. return subprocess.call(args)
  235. except OSError as ex :
  236. self.__raise('system call crashed on :: '+ex.strerror)
  237. def _sys_mkdir(self,path) :
  238. try :
  239. return os.makedirs('./users/'+self._username+'/'+path)
  240. except OSError as ex :
  241. if ex.errno != 17 :
  242. #ignore directory already exists error
  243. self.__raise('directory creation failed :: '+ex.strerror)
  244. def _sys_readf(self,path) :
  245. f = open('./users/'+self._username+'/'+path,'r')
  246. contents = f.read()
  247. f.close()
  248. return contents
  249. def _sys_writef(self,path,content,append=True) :
  250. if append :
  251. f = open('./users/'+self._username+'/'+path,'a')
  252. else :
  253. f = open('./users/'+self._username+'/'+path,'w')
  254. f.write(content)
  255. f.close()