PageRenderTime 31ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/mdsobjects/python/compound.py

https://gitlab.com/MDSplus/mdsplus
Python | 474 lines | 390 code | 30 blank | 54 comment | 53 complexity | b62a7d1b932b22855c7aa76e4c4af998 MD5 | raw file
  1. if '__package__' not in globals() or __package__ is None or len(__package__)==0:
  2. def _mimport(name,level):
  3. return __import__(name,globals())
  4. else:
  5. def _mimport(name,level):
  6. return __import__(name,globals(),{},[],level)
  7. _data=_mimport('mdsdata',1)
  8. _dtypes=_mimport('_mdsdtypes',1)
  9. class Compound(_data.Data):
  10. def __init__(self,*args, **params):
  11. """MDSplus compound data.
  12. """
  13. if self.__class__.__name__=='Compound':
  14. raise TypeError("Cannot create instances of class Compound")
  15. if 'args' in params:
  16. args=params['args']
  17. if 'params' in params:
  18. params=params['params']
  19. if not hasattr(self,'_dtype'):
  20. self._dtype=_dtypes.fromName('DTYPE_'+self.__class__.__name__.upper())
  21. if 'opcode' in params:
  22. self._opcode=params['opcode']
  23. try:
  24. self._argOffset=self.argOffset
  25. except:
  26. self._argOffset=len(self.fields)
  27. if isinstance(args,tuple):
  28. if len(args) > 0:
  29. if isinstance(args[0],tuple):
  30. args=args[0]
  31. self.args=args
  32. for keyword in params:
  33. if keyword in self.fields:
  34. super(type(self),self).__setitem__(self._fields[keyword],params[keyword])
  35. def __hasBadTreeReferences__(self,tree):
  36. for arg in self.args:
  37. if isinstance(arg,_data.Data) and arg.__hasBadTreeReferences__(tree):
  38. return True
  39. return False
  40. def __fixTreeReferences__(self,tree):
  41. from copy import deepcopy
  42. ans = deepcopy(self)
  43. newargs=list(ans.args)
  44. for idx in range(len(newargs)):
  45. if isinstance(newargs[idx],_data.Data) and newargs[idx].__hasBadTreeReferences__(tree):
  46. newargs[idx]=newargs[idx].__fixTreeReferences__(tree)
  47. ans.args=tuple(newargs)
  48. return ans
  49. def __getattr__(self,name,*args):
  50. if name in self.__dict__:
  51. return self.__dict__[name]
  52. if name == '_opcode_name':
  53. return 'undefined'
  54. if name == '_fields':
  55. return dict()
  56. if name == 'args':
  57. try:
  58. return self.__dict__[name]
  59. except:
  60. return None
  61. if name == '_opcode':
  62. return None
  63. if name == '_argOffset':
  64. return 0
  65. if name == 'opcode':
  66. return self._opcode
  67. if name == 'dtype':
  68. return self._dtype
  69. if name == self._opcode_name:
  70. return self._opcode
  71. if name in self._fields:
  72. try:
  73. return self.args[self._fields[name]]
  74. except:
  75. return None
  76. raise AttributeError('No such attribute '+str(name))
  77. def __getitem__(self,num):
  78. try:
  79. return self.args[num]
  80. except:
  81. return None
  82. def __setattr__(self,name,value):
  83. if name == 'opcode':
  84. self._opcode=value
  85. if name == 'args':
  86. if isinstance(value,tuple):
  87. self.__dict__[name]=value
  88. return
  89. else:
  90. raise TypeError('args attribute must be a tuple')
  91. if name in self._fields:
  92. tmp=list(self.args)
  93. while len(tmp) <= self._fields[name]:
  94. tmp.append(None)
  95. tmp[self._fields[name]]=value
  96. self.args=tuple(tmp)
  97. return
  98. if name == self._opcode_name:
  99. self._opcode=value
  100. return
  101. super(Compound,self).__setattr__(name,value)
  102. def __setitem__(self,num,value):
  103. if isinstance(num,slice):
  104. indices=num.indices(num.start+len(value))
  105. idx=0
  106. for i in range(indices[0],indices[1],indices[2]):
  107. self.__setitem__(i,value[idx])
  108. idx=idx+1
  109. else:
  110. try:
  111. tmp=list(self.args)
  112. except:
  113. tmp=list()
  114. while len(tmp) <= num:
  115. tmp.append(None)
  116. tmp[num]=value
  117. self.args=tuple(tmp)
  118. return
  119. # def decompile(self):
  120. # arglist=list()
  121. # for arg in self.args:
  122. # arglist.append(makeData(arg).decompile())
  123. # return 'Build_'+self.__class__.__name__+'('+','.join(arglist)+')'
  124. def getArgumentAt(self,idx):
  125. """Return argument at index idx (indexes start at 0)
  126. @rtype: Data,None
  127. """
  128. return Compound.__getitem__(self,idx+self._argOffset)
  129. def getArguments(self):
  130. """Return arguments
  131. @rtype: Data,None
  132. """
  133. return Compound.__getitem__(self,slice(self._argOffset,None))
  134. def getDescAt(self,idx):
  135. """Return descriptor with index idx (first descriptor is 0)
  136. @rtype: Data
  137. """
  138. return Compound.__getitem__(self,idx)
  139. def getDescs(self):
  140. """Return descriptors or None if no descriptors
  141. @rtype: tuple,None
  142. """
  143. return self.args
  144. def getNumDescs(self):
  145. """Return number of descriptors
  146. @rtype: int
  147. """
  148. try:
  149. return len(self.args)
  150. except:
  151. return 0
  152. def setArgumentAt(self,idx,value):
  153. """Set argument at index idx (indexes start at 0)"""
  154. return super(type(self),self).__setitem__(idx+self._argOffset,value)
  155. def setArguments(self,args):
  156. """Set arguments
  157. @type args: tuple
  158. """
  159. return super(type(self),self).__setitem__(slice(self._argOffset,None),args)
  160. def setDescAt(self,n,value):
  161. """Set descriptor at index idx (indexes start at 0)"""
  162. return super(type(self),self).__setitem__(n,value)
  163. def setDescs(self,args):
  164. """Set descriptors
  165. @type args: tuple
  166. """
  167. self.args=args
  168. class MetaClass(type):
  169. def __new__(meta,classname,bases,classDict):
  170. if len(classDict)==0:
  171. classDict=dict(bases[0].__dict__)
  172. newClassDict=classDict
  173. newClassDict['_fields']=dict()
  174. idx=0
  175. if 'fields' in classDict:
  176. for f in classDict['fields']:
  177. name=f[0:1].upper()+f[1:]
  178. exec ("def get"+name+"(self): return self.__getattr__('"+f+"')")
  179. exec ("newClassDict['get'+name]=get"+name)
  180. newClassDict['get'+name].__doc__='Get the '+f+' field\n@rtype: Data'
  181. exec ('def set'+name+'(self,value): return self.__setattr__("'+f+'",value)')
  182. exec ("newClassDict['set'+name]=set"+name)
  183. newClassDict['set'+name].__doc__='Set the '+f+' field\n@type value: Data\n@rtype: None'
  184. exec ("newClassDict['_dtype']=_dtypes.DTYPE_"+classname.upper())
  185. newClassDict['_fields'][f]=idx
  186. idx=idx+1
  187. c=type.__new__(meta,classname,bases,newClassDict)
  188. else:
  189. c=type.__new__(meta,classname,bases,classDict)
  190. return c
  191. class _Action(Compound):
  192. """
  193. An Action is used for describing an operation to be performed by an
  194. MDSplus action server. Actions are typically dispatched using the
  195. mdstcl DISPATCH command
  196. """
  197. fields=('dispatch','task','errorLog','completionMessage','performance')
  198. Action=MetaClass('Action',(_Action,),{})
  199. class _Call(Compound):
  200. """
  201. A Call is used to call routines in shared libraries.
  202. """
  203. fields=('image','routine')
  204. _opcode_name='retType'
  205. Call=MetaClass('Call',(_Call,),{})
  206. class _Conglom(Compound):
  207. """A Conglom is used at the head of an MDSplus conglomerate. A conglomerate is a set of tree nodes used
  208. to define a device such as a piece of data acquisition hardware. A conglomerate is associated with some
  209. external code providing various methods which can be performed on the device. The Conglom class contains
  210. information used for locating the external code.
  211. """
  212. fields=('image','model','name','qualifiers')
  213. Conglom=MetaClass('Conglom',(_Conglom,),{})
  214. class _Dependency(Compound):
  215. """A Dependency object is used to describe action dependencies. This is a legacy class and may not be recognized by
  216. some dispatching systems
  217. """
  218. fields=('arg1','arg2')
  219. Dependency=MetaClass('Dependency',(_Dependency,),{})
  220. class _Dimension(Compound):
  221. """A dimension object is used to describe a signal dimension, typically a time axis. It provides a compact description
  222. of the timing information of measurements recorded by devices such as transient recorders. It associates a Window
  223. object with an axis. The axis is generally a range with possibly no start or end but simply a delta. The Window
  224. object is then used to bracket the axis to resolve the appropriate timestamps.
  225. """
  226. fields=('window','axis')
  227. Dimension=MetaClass('Dimension',(_Dimension,),{})
  228. class _Dispatch(Compound):
  229. """A Dispatch object is used to describe when an where an action should be dispatched to an MDSplus action server.
  230. """
  231. fields=('ident','phase','when','completion')
  232. def __init__(self,*args,**kwargs):
  233. if 'dispatch_type' not in kwargs:
  234. kwargs['dispatch_type']=2
  235. kwargs['opcode']=kwargs['dispatch_type']
  236. super(_Dispatch,self).__init__(args=args,params=kwargs)
  237. if self.completion is None:
  238. self.completion = None
  239. Dispatch=MetaClass('Dispatch',(_Dispatch,),{})
  240. class _Function(Compound):
  241. """A Function object is used to reference builtin MDSplus functions. For example the expression 1+2
  242. is represented in as Function instance created by Function(opcode='ADD',args=(1,2))
  243. """
  244. fields=tuple()
  245. def __init__(self,opcode,args):
  246. """Create a compiled MDSplus function reference.
  247. Number of arguments allowed depends on the opcode supplied.
  248. """
  249. # from _opcodes.opcodes import find_opcode
  250. super(Function,self).__init__(args=args,opcode=opcode)
  251. # opc=find_opcode(self._opcode)
  252. # if opc:
  253. # opc.check_args(args)
  254. # self._opcode=opc.number
  255. # else:
  256. # raise Exception("Invalid opcode - "+str(self._opcode))
  257. # self.__dict__['opc']=opc
  258. # def setOpcode(self,opcode):
  259. # """Set opcode
  260. # @param opcode: either a string or a index number of the builtin operation
  261. # @type opcode: str,int
  262. # """
  263. # from _opcodes.opcodes import find_opcode
  264. # opc=find_opcode(opcode)
  265. # if not opc:
  266. # raise Exception("Invalid opcode - "+str(opcode))
  267. # self.opcode=opcode
  268. # self.__dict__['opc']=opc
  269. #
  270. # The following code can be used if we want to implement TDI opcodes in python code using the opcodes.py module.
  271. # If it is commened out, it will default to using TdiEvaluate to perform the evaluation.
  272. #
  273. # def evaluate(self):
  274. # """Returns the answer when the function is evaluated."""
  275. # return self.opc.evaluate(self.args)
  276. #
  277. # def decompile(self):
  278. # return self.opc.str(self.args)
  279. # def __str__(self):
  280. # """Returns the string representation of the function."""
  281. # return self.opc.str(self.args)
  282. #
  283. #def compile_function(name,*args):
  284. #
  285. # opcode=find_opcode(name)
  286. # if opcode:
  287. # if opcode.class_of:
  288. # return opcode.class_of(*args)
  289. # else:
  290. # return opc(opcode.number,*args)
  291. # else:
  292. # return opc('BUILD_CALL',None,*args)
  293. Function=MetaClass('Function',(_Function,),{})
  294. class _Method(Compound):
  295. """A Method object is used to describe an operation to be performed on an MDSplus conglomerate/device
  296. """
  297. fields=('timeout','method','object')
  298. Method=MetaClass('Method',(_Method,),{})
  299. class _Procedure(Compound):
  300. """A Procedure is a deprecated object
  301. """
  302. fields=('timeout','language','procedure')
  303. Procedure=MetaClass('Procedure',(_Procedure,),{})
  304. class _Program(Compound):
  305. """A Program is a deprecated object"""
  306. fields=('timeout','program')
  307. Program=MetaClass('Program',(_Program,),{})
  308. class _Range(Compound):
  309. """A Range describes a ramp. When used as an axis in a Dimension object along with a Window object it can be
  310. used to describe a clock. In this context it is possible to have missing begin and ending values or even have the
  311. begin, ending, and delta fields specified as arrays to indicate a multi-speed clock.
  312. """
  313. fields=('begin','ending','delta')
  314. def decompile(self):
  315. parts=list()
  316. for arg in self.args:
  317. parts.append(_data.makeData(arg).decompile())
  318. return ' : '.join(parts)
  319. Range=MetaClass('Range',(_Range,),{})
  320. class _Routine(Compound):
  321. """A Routine is a deprecated object"""
  322. fields=('timeout','image','routine')
  323. Routine=MetaClass('Routine',(_Routine,),{})
  324. class _Signal(Compound):
  325. """A Signal is used to describe a measurement, usually time dependent, and associated the data with its independent
  326. axis (Dimensions). When Signals are indexed using s[idx], the index is resolved using the dimension of the signal
  327. """
  328. fields=('value','raw')
  329. def _getDims(self):
  330. return self.getArguments()
  331. dims=property(_getDims)
  332. """The dimensions of the signal"""
  333. # def decompile(self):
  334. # arglist=list()
  335. # for arg in self.args:
  336. # arglist.append(makeData(arg).decompile())
  337. # return 'Build_Signal('+','.join(arglist)+')'
  338. def dim_of(self,idx=0):
  339. """Return the signals dimension
  340. @rtype: Data
  341. """
  342. if idx < len(self.dims):
  343. return self.dims[idx]
  344. else:
  345. return _data.makeData(None)
  346. def __getitem__(self,idx):
  347. """Subscripting <==> signal[subscript]. Uses the dimension information for subscripting
  348. @param idx: index or Range used for subscripting the signal based on the signals dimensions
  349. @type idx: Data
  350. @rtype: Signal
  351. """
  352. if isinstance(idx,slice):
  353. idx = Range(idx.start,idx.stop,idx.step)
  354. return _data.Data.execute('$[$]',self,idx)
  355. def getDimensionAt(self,idx=0):
  356. """Return the dimension of the signal
  357. @param idx: The index of the desired dimension. Indexes start at 0. 0=default
  358. @type idx: int
  359. @rtype: Data
  360. """
  361. try:
  362. return self.dims[idx]
  363. except:
  364. return None
  365. def getDimensions(self):
  366. """Return all the dimensions of the signal
  367. @rtype: tuple
  368. """
  369. return self.dims
  370. def setDimensionAt(self,idx,value):
  371. """Set the dimension
  372. @param idx: The index into the dimensions of the signal.
  373. @rtype: None
  374. """
  375. return self.setArgumentAt(idx,value)
  376. def setDimensions(self,value):
  377. """Set all the dimensions of a signal
  378. @param value: The dimensions
  379. @type value: tuple
  380. @rtype: None
  381. """
  382. return self.setArguments(value)
  383. Signal=MetaClass('Signal',(_Signal,),{})
  384. class _Window(Compound):
  385. """A Window object can be used to construct a Dimension object. It brackets the axis information stored in the
  386. Dimension to construct the independent axis of a signal.
  387. """
  388. fields=('startIdx','endIdx','timeAt0')
  389. # def decompile(self):
  390. # return 'Build_Window('+makeData(self.startIdx).decompile()+','+makeData(self.endIdx).decompile()+','+makeData(self.timeAt0)+')'
  391. Window=MetaClass('Window',(_Window,),{})
  392. class _Opaque(Compound):
  393. """An Opaque object containing a binary uint8 array and a string identifying the type.
  394. """
  395. fields=('data','otype')
  396. # def decompile(self):
  397. # return 'Build_Opaque('+makeData(self.data).decompile()+','+makeData(self.otype).decompile()+')'
  398. def getImage(self):
  399. import Image
  400. from StringIO import StringIO
  401. return Image.open(StringIO(_data.makeData(self.getData()).data().data))
  402. def fromFile(filename,typestring):
  403. """Read a file and return an Opaque object
  404. @param filename: Name of file to read in
  405. @type filename: str
  406. @param typestring: String to denote the type of file being stored
  407. @type typestring: str
  408. @rtype: Opaque instance
  409. """
  410. import numpy as _N
  411. f = open(filename,'rb')
  412. try:
  413. opq=Opaque(_data.makeData(_N.fromstring(f.read(),dtype="uint8")),typestring)
  414. finally:
  415. f.close()
  416. return opq
  417. fromFile=staticmethod(fromFile)
  418. Opaque=MetaClass('Opaque',(_Opaque,),{})