pytecplot 1.5.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pytecplot-1.5.2.dist-info/LICENSE.html +77 -0
- pytecplot-1.5.2.dist-info/METADATA +508 -0
- pytecplot-1.5.2.dist-info/RECORD +97 -0
- pytecplot-1.5.2.dist-info/WHEEL +5 -0
- pytecplot-1.5.2.dist-info/top_level.txt +1 -0
- tecplot/__init__.py +94 -0
- tecplot/annotation/__init__.py +7 -0
- tecplot/annotation/annotation.py +223 -0
- tecplot/annotation/geometry.py +369 -0
- tecplot/annotation/georeference.py +33 -0
- tecplot/annotation/image.py +185 -0
- tecplot/annotation/iter.py +105 -0
- tecplot/annotation/polyline.py +381 -0
- tecplot/annotation/text.py +744 -0
- tecplot/constant.py +401 -0
- tecplot/data/__init__.py +34 -0
- tecplot/data/array.py +645 -0
- tecplot/data/create.py +1 -0
- tecplot/data/dataset.py +1809 -0
- tecplot/data/extract.py +544 -0
- tecplot/data/face_neighbors.py +494 -0
- tecplot/data/facemap.py +814 -0
- tecplot/data/load.py +2075 -0
- tecplot/data/nodemap.py +577 -0
- tecplot/data/operate.py +928 -0
- tecplot/data/query.py +334 -0
- tecplot/data/save.py +343 -0
- tecplot/data/variable.py +280 -0
- tecplot/data/zone.py +841 -0
- tecplot/exception.py +201 -0
- tecplot/export/__init__.py +13 -0
- tecplot/export/animation.py +521 -0
- tecplot/export/export_setup.py +318 -0
- tecplot/export/image.py +577 -0
- tecplot/export/print_setup.py +218 -0
- tecplot/export/time_animation.py +607 -0
- tecplot/extension/__init__.py +14 -0
- tecplot/extension/ipython.py +68 -0
- tecplot/layout/__init__.py +20 -0
- tecplot/layout/frame.py +1285 -0
- tecplot/layout/layout.py +488 -0
- tecplot/layout/page.py +541 -0
- tecplot/legend/__init__.py +4 -0
- tecplot/legend/contour_legend.py +516 -0
- tecplot/legend/legend.py +141 -0
- tecplot/legend/line_legend.py +66 -0
- tecplot/legend/rgb_coloring_legend.py +291 -0
- tecplot/legend/scatter_legend.py +136 -0
- tecplot/macro.py +339 -0
- tecplot/plot/__init__.py +62 -0
- tecplot/plot/axes.py +881 -0
- tecplot/plot/axis.py +1350 -0
- tecplot/plot/blanking.py +584 -0
- tecplot/plot/contour.py +1302 -0
- tecplot/plot/effects.py +162 -0
- tecplot/plot/fieldmap.py +2159 -0
- tecplot/plot/grid.py +758 -0
- tecplot/plot/isosurface.py +1167 -0
- tecplot/plot/labels.py +338 -0
- tecplot/plot/linemap.py +1905 -0
- tecplot/plot/linking.py +489 -0
- tecplot/plot/plot.py +1791 -0
- tecplot/plot/rgb_coloring.py +281 -0
- tecplot/plot/scatter.py +396 -0
- tecplot/plot/slice.py +1544 -0
- tecplot/plot/streamtrace.py +1949 -0
- tecplot/plot/symbol.py +567 -0
- tecplot/plot/ticks.py +666 -0
- tecplot/plot/title.py +380 -0
- tecplot/plot/vector.py +679 -0
- tecplot/plot/view.py +868 -0
- tecplot/session/__init__.py +112 -0
- tecplot/session/aux_data.py +276 -0
- tecplot/session/config.py +92 -0
- tecplot/session/session.py +425 -0
- tecplot/session/state_changed.py +225 -0
- tecplot/session/style.py +504 -0
- tecplot/tecutil/__init__.py +16 -0
- tecplot/tecutil/arglist.py +230 -0
- tecplot/tecutil/captured_output.py +177 -0
- tecplot/tecutil/constant.py +1312 -0
- tecplot/tecutil/index_set.py +118 -0
- tecplot/tecutil/lock.py +36 -0
- tecplot/tecutil/message_pb2.py +39 -0
- tecplot/tecutil/preamble.py +54 -0
- tecplot/tecutil/stringlist.py +114 -0
- tecplot/tecutil/sv.py +1662 -0
- tecplot/tecutil/tecutil.py +12402 -0
- tecplot/tecutil/tecutil_client.py +627 -0
- tecplot/tecutil/tecutil_connector.py +937 -0
- tecplot/tecutil/tecutil_rpc.py +6845 -0
- tecplot/tecutil/util.py +332 -0
- tecplot/text/__init__.py +3 -0
- tecplot/text/font.py +224 -0
- tecplot/text/label_format.py +374 -0
- tecplot/text/textbox.py +106 -0
- tecplot/version.py +41 -0
|
@@ -0,0 +1,937 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import calendar
|
|
3
|
+
import contextlib
|
|
4
|
+
import ctypes
|
|
5
|
+
import logging
|
|
6
|
+
import platform
|
|
7
|
+
import re
|
|
8
|
+
import struct
|
|
9
|
+
import sys
|
|
10
|
+
import time
|
|
11
|
+
|
|
12
|
+
from ctypes import (c_char, c_char_p, c_int, c_int32, c_size_t, c_void_p,
|
|
13
|
+
POINTER, pointer, c_bool, cast, byref, util)
|
|
14
|
+
from collections import Counter, namedtuple
|
|
15
|
+
from datetime import date
|
|
16
|
+
from enum import Enum
|
|
17
|
+
from locale import setlocale, LC_NUMERIC
|
|
18
|
+
from os import path
|
|
19
|
+
from subprocess import Popen, PIPE
|
|
20
|
+
from textwrap import dedent
|
|
21
|
+
from warnings import warn
|
|
22
|
+
|
|
23
|
+
from .captured_output import captured_output
|
|
24
|
+
from ..constant import *
|
|
25
|
+
from ..exception import *
|
|
26
|
+
from .tecutil import TecUtil
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
log = logging.getLogger(__name__)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ManagerStartReturnCode(Enum):
|
|
33
|
+
Ok = 0
|
|
34
|
+
HomeDirectoryNotSpecified = 1
|
|
35
|
+
LicenseFileNotFound = 2
|
|
36
|
+
LicenseIsInvalid = 3
|
|
37
|
+
LicenseExpired = 4
|
|
38
|
+
InternalInitializationError = 5
|
|
39
|
+
EngineUninitialized = 6
|
|
40
|
+
LicenseFileContainsPermanent = 7
|
|
41
|
+
|
|
42
|
+
def to_string(self, tecutil_connector):
|
|
43
|
+
return tecutil_connector.handle \
|
|
44
|
+
.ManagerStartReturnCodeString(self.value).value.decode()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def find_file(filenames, searchpaths):
|
|
48
|
+
if isinstance(searchpaths, str):
|
|
49
|
+
searchpaths = searchpaths.split(os.pathsep)
|
|
50
|
+
for filename in filenames:
|
|
51
|
+
for searchpath in searchpaths:
|
|
52
|
+
fpath = path.join(searchpath, filename)
|
|
53
|
+
if path.exists(fpath):
|
|
54
|
+
return fpath
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
SDKVersion = namedtuple('SDKVersion', ['MajorVersion', 'MinorVersion',
|
|
58
|
+
'MajorRevision', 'MinorRevision'])
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class allocated_c_char_p(c_char_p):
|
|
62
|
+
def __del__(self):
|
|
63
|
+
libc = ctypes.CDLL('msvcrt' if platform.system() == 'Windows' else None)
|
|
64
|
+
libc.free(self)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class TecUtilConnector(object):
|
|
68
|
+
|
|
69
|
+
Message = namedtuple('Message', ['level', 'message'])
|
|
70
|
+
|
|
71
|
+
def __init__(self):
|
|
72
|
+
if __debug__:
|
|
73
|
+
# ensure log messages get out to console
|
|
74
|
+
logging.basicConfig()
|
|
75
|
+
|
|
76
|
+
self.handle = None
|
|
77
|
+
self.tecutil_handle = None
|
|
78
|
+
self.libbatch_path = None
|
|
79
|
+
self.started = None
|
|
80
|
+
self.stopped = None
|
|
81
|
+
self._last_message = None
|
|
82
|
+
self._load_library_error = None
|
|
83
|
+
|
|
84
|
+
self.client = None
|
|
85
|
+
|
|
86
|
+
self.suspended = False
|
|
87
|
+
self.state_changes = {}
|
|
88
|
+
|
|
89
|
+
if __debug__:
|
|
90
|
+
self._tecutil_call_count = Counter()
|
|
91
|
+
self._style_call_count = {'GET': Counter(), 'SET': Counter()}
|
|
92
|
+
|
|
93
|
+
if ctypes.sizeof(c_void_p) != 8:
|
|
94
|
+
msg = '64-bit Python is required to use PyTecplot'
|
|
95
|
+
raise TecplotLibraryNotLoadedError(msg)
|
|
96
|
+
|
|
97
|
+
def bind_local_objects(self):
|
|
98
|
+
log.debug('binding local objects')
|
|
99
|
+
import tecplot
|
|
100
|
+
tecplot.tecutil.ArgList = tecplot.tecutil.LocalArgList
|
|
101
|
+
tecplot.tecutil.IndexSet._bind_tecutil()
|
|
102
|
+
tecplot.tecutil.StringList._bind_tecutil()
|
|
103
|
+
|
|
104
|
+
def bind_remote_objects(self):
|
|
105
|
+
import tecplot
|
|
106
|
+
tecplot.tecutil.ArgList = tecplot.tecutil.RemoteArgList
|
|
107
|
+
tecplot.tecutil.IndexSet._bind_tecutil()
|
|
108
|
+
tecplot.tecutil.StringList._bind_tecutil()
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def connected(self):
|
|
112
|
+
return self.client is not None and self.client.connected
|
|
113
|
+
|
|
114
|
+
def connect(self, host='localhost', port=7600, timeout=10, quiet=False):
|
|
115
|
+
try:
|
|
116
|
+
from tecplot.tecutil.tecutil_client import TecUtilClient
|
|
117
|
+
self.client = TecUtilClient()
|
|
118
|
+
self.client.connect(host, port, timeout, quiet=quiet)
|
|
119
|
+
except (ImportError, TecplotSystemError, TecplotInvalidMessage) as err_pb:
|
|
120
|
+
self.client = None
|
|
121
|
+
if isinstance(err_pb, (ImportError, TecplotSystemError)):
|
|
122
|
+
# no protobuf installed
|
|
123
|
+
msg = 'required Python module: protobuf'
|
|
124
|
+
raise ImportError(msg)
|
|
125
|
+
else:
|
|
126
|
+
# protobuf installed, both not working
|
|
127
|
+
msg = 'invalid message received on port {}'.format(port)
|
|
128
|
+
raise TecplotInvalidMessage(msg + '\n' + str(err_pb))
|
|
129
|
+
|
|
130
|
+
self.update_sdk_version()
|
|
131
|
+
|
|
132
|
+
if self.sdk_version_info < (2020, 1):
|
|
133
|
+
msg = 'PyTecplot requires Tecplot 360 version 2020 R1 or later'
|
|
134
|
+
raise TecplotOutOfDateEngineError((2020, 1), msg)
|
|
135
|
+
|
|
136
|
+
self.bind_local_objects()
|
|
137
|
+
self.client.patch_tecutil()
|
|
138
|
+
|
|
139
|
+
def disconnect(self):
|
|
140
|
+
self.bind_remote_objects()
|
|
141
|
+
self.client.disconnect()
|
|
142
|
+
self.update_sdk_version()
|
|
143
|
+
log.info('Disconnected from Tecplot 360 instance.')
|
|
144
|
+
|
|
145
|
+
@property
|
|
146
|
+
def tecsdkhome(self):
|
|
147
|
+
if not hasattr(self, '_tecsdkhome'):
|
|
148
|
+
if self.connected:
|
|
149
|
+
self._tecsdkhome = _tecutil.TecplotGetHomeDirectory()
|
|
150
|
+
else:
|
|
151
|
+
try:
|
|
152
|
+
self._tecsdkhome = os.environ['TECSDKHOME']
|
|
153
|
+
if not path.isdir(self._tecsdkhome):
|
|
154
|
+
raise OSError
|
|
155
|
+
except (KeyError, OSError):
|
|
156
|
+
if self.libbatch_path is not None:
|
|
157
|
+
log.debug('reading tecutilbatch path to get SDK home')
|
|
158
|
+
tecutilbatch_dir = path.dirname(self.libbatch_path)
|
|
159
|
+
self._tecsdkhome = path.dirname(tecutilbatch_dir)
|
|
160
|
+
else:
|
|
161
|
+
self._tecsdkhome = ''
|
|
162
|
+
return self._tecsdkhome
|
|
163
|
+
|
|
164
|
+
def init_local_library(self):
|
|
165
|
+
setlocale(LC_NUMERIC, 'C')
|
|
166
|
+
os.environ['LC_NUMERIC'] = 'C'
|
|
167
|
+
|
|
168
|
+
load = {'Darwin': self.load_darwin,
|
|
169
|
+
'Linux': self.load_linux,
|
|
170
|
+
'Windows': self.load_windows}
|
|
171
|
+
|
|
172
|
+
log.debug('Attempting to load Tecplot batch library')
|
|
173
|
+
load[platform.system()]()
|
|
174
|
+
msg = 'Successfully loaded Tecplot batch library: {}'
|
|
175
|
+
log.info(msg.format(self.libbatch_path))
|
|
176
|
+
|
|
177
|
+
self.handle.AcquireLicense.restype = c_bool
|
|
178
|
+
self.handle.GetTUAssertErrorMessage.restype = c_char_p
|
|
179
|
+
self.handle.LicenseExpirationDate.restype = c_char_p
|
|
180
|
+
self.handle.LicenseInfo.restype = c_char_p
|
|
181
|
+
self.handle.LicenseIsRoaming.restype = c_bool
|
|
182
|
+
self.handle.LicenseIsValid.restype = c_bool
|
|
183
|
+
self.handle.LicenseStartRoaming.argtypes = (c_int,)
|
|
184
|
+
self.handle.LicenseStartRoaming.restype = c_bool
|
|
185
|
+
self.handle.LicenseStopRoaming.argtypes = (c_bool,)
|
|
186
|
+
self.handle.LicenseStopRoaming.restype = c_bool
|
|
187
|
+
self.handle.ReleaseLicense.restype = c_bool
|
|
188
|
+
try:
|
|
189
|
+
self.handle.TranslateMacroToPython.restype = POINTER(c_char)
|
|
190
|
+
self.handle.TranslateMacroWithRawDataToPython.restype = POINTER(c_char)
|
|
191
|
+
except:
|
|
192
|
+
log.debug('engine does not support macro translations')
|
|
193
|
+
|
|
194
|
+
_tecutil.configure_tecutil_argtypes()
|
|
195
|
+
self.bind_remote_objects()
|
|
196
|
+
self.update_sdk_version()
|
|
197
|
+
|
|
198
|
+
if self.sdk_version_info < (2020, 1):
|
|
199
|
+
msg = 'PyTecplot requires Tecplot 360 version 2020 R1 or later'
|
|
200
|
+
raise TecplotOutOfDateEngineError((2020, 1), msg)
|
|
201
|
+
|
|
202
|
+
self.handle.Start.argtypes = (c_int, POINTER(c_char_p),)
|
|
203
|
+
self.handle.ManagerStartReturnCodeString.argtypes = (c_int,)
|
|
204
|
+
self.handle.ManagerStartReturnCodeString.restype = allocated_c_char_p
|
|
205
|
+
self.handle.Start.restype = ManagerStartReturnCode
|
|
206
|
+
|
|
207
|
+
self.patch_tecutil()
|
|
208
|
+
|
|
209
|
+
def patch_tecutil(self):
|
|
210
|
+
'''Modify TecUtil interface according to historical changes in SDK.
|
|
211
|
+
|
|
212
|
+
2021.2
|
|
213
|
+
Facemap and FaceNeighbor connections now use zone unique IDs
|
|
214
|
+
(64-bit integers) instead of indices (32-bit integers).
|
|
215
|
+
Methods affected:
|
|
216
|
+
* Facemap.set_boundary_connections()
|
|
217
|
+
* FaceNeighbors.add_neighbors()
|
|
218
|
+
|
|
219
|
+
see also: TecUtilConnector.patch_tecutil()
|
|
220
|
+
'''
|
|
221
|
+
if self.sdk_version_info < (2021, 2):
|
|
222
|
+
for fn in (
|
|
223
|
+
_tecutil.handle.tecUtilDataFaceMapAssignBConns,
|
|
224
|
+
_tecutil.handle.tecUtilDataFaceMapAssignBConns64,
|
|
225
|
+
_tecutil.handle.tecUtilDataFaceNbrAssignByRef,
|
|
226
|
+
_tecutil.handle.tecUtilDataFaceNbrAssignByRef64,
|
|
227
|
+
):
|
|
228
|
+
fn.argtypes = fn.argtypes[:-1] + [POINTER(c_int32)]
|
|
229
|
+
|
|
230
|
+
def load_linux(self):
|
|
231
|
+
|
|
232
|
+
def _syslibpath():
|
|
233
|
+
ret = []
|
|
234
|
+
try:
|
|
235
|
+
log.debug('acquiring system library search path using ldconfig')
|
|
236
|
+
cmd = 'ldconfig -v -N'
|
|
237
|
+
proc = Popen(cmd, shell=True,
|
|
238
|
+
executable=os.environ.get('SHELL', '/bin/bash'),
|
|
239
|
+
env=os.environ, stdout=PIPE, stderr=PIPE)
|
|
240
|
+
ptrn = re.compile(r'^\t')
|
|
241
|
+
for line in proc.communicate()[0][:-1].decode().split('\n'):
|
|
242
|
+
if not ptrn.match(line):
|
|
243
|
+
d = line.split(':')[0]
|
|
244
|
+
if path.exists(d) and path.isdir(d):
|
|
245
|
+
ret.append(d)
|
|
246
|
+
except:
|
|
247
|
+
log.debug('diagnostic command failed: ' + cmd)
|
|
248
|
+
return ret
|
|
249
|
+
|
|
250
|
+
def _missinglibs(lib):
|
|
251
|
+
ret = []
|
|
252
|
+
try:
|
|
253
|
+
log.debug('looking for missing libs using ldd (clean env)')
|
|
254
|
+
cmd = 'ldd ' + lib
|
|
255
|
+
proc = Popen(cmd, shell=True,
|
|
256
|
+
executable=os.environ.get('SHELL', '/bin/bash'),
|
|
257
|
+
#env=os.environ,
|
|
258
|
+
stdout=PIPE, stderr=PIPE)
|
|
259
|
+
out, err = proc.communicate()
|
|
260
|
+
if 'command not found' not in err.decode():
|
|
261
|
+
if len(out.decode().split('\n')) < 3:
|
|
262
|
+
log.debug(out.decode())
|
|
263
|
+
for line in out.decode().split('\n'):
|
|
264
|
+
if line.endswith('not found'):
|
|
265
|
+
ret.append(line.split()[0])
|
|
266
|
+
except:
|
|
267
|
+
log.debug('diagnostic command failed: ' + cmd)
|
|
268
|
+
return ret
|
|
269
|
+
|
|
270
|
+
def _find_library(libnames):
|
|
271
|
+
libpath = os.environ.get('LD_LIBRARY_PATH', None)
|
|
272
|
+
for libname in libnames:
|
|
273
|
+
ret = None
|
|
274
|
+
if libpath is not None:
|
|
275
|
+
ret = find_file([libname], libpath)
|
|
276
|
+
if ret is None:
|
|
277
|
+
ret = find_file([libname], _syslibpath())
|
|
278
|
+
if ret is not None:
|
|
279
|
+
return ret
|
|
280
|
+
|
|
281
|
+
def _dl_abspath(libname):
|
|
282
|
+
"""Returns the absolute path of the loaded dynamic library"""
|
|
283
|
+
log.debug('using libc to get path to tecutilbatch library')
|
|
284
|
+
|
|
285
|
+
class dl_phdr_info(ctypes.Structure):
|
|
286
|
+
_fields_ = [
|
|
287
|
+
('padding0', c_void_p), # ignore it
|
|
288
|
+
('dlpi_name', c_char_p)]
|
|
289
|
+
callback_t = ctypes.CFUNCTYPE(c_int, POINTER(dl_phdr_info),
|
|
290
|
+
POINTER(c_size_t), c_char_p)
|
|
291
|
+
_dl_abspath.lib_abspath = None
|
|
292
|
+
|
|
293
|
+
def callback(info, size, data):
|
|
294
|
+
if data in info.contents.dlpi_name:
|
|
295
|
+
_dl_abspath.lib_abspath = \
|
|
296
|
+
info.contents.dlpi_name.decode()
|
|
297
|
+
return 0
|
|
298
|
+
|
|
299
|
+
libc = ctypes.CDLL(None)
|
|
300
|
+
libc.dl_iterate_phdr.argtypes = [callback_t, c_char_p]
|
|
301
|
+
libc.dl_iterate_phdr.restype = c_int
|
|
302
|
+
libc.dl_iterate_phdr(callback_t(callback), libname.encode())
|
|
303
|
+
return _dl_abspath.lib_abspath
|
|
304
|
+
|
|
305
|
+
libfmt = 'lib{name}.so'
|
|
306
|
+
libbatch = libfmt.format(name='tecutilbatch')
|
|
307
|
+
libchecked = libfmt.format(name='tecutilchecked')
|
|
308
|
+
libnames = [libbatch, libchecked]
|
|
309
|
+
|
|
310
|
+
cdllexcept = {}
|
|
311
|
+
try:
|
|
312
|
+
self.handle = ctypes.cdll.LoadLibrary(libbatch)
|
|
313
|
+
self.tecutil_handle = ctypes.cdll.LoadLibrary(libchecked)
|
|
314
|
+
if self.handle is None or self.tecutil_handle is None:
|
|
315
|
+
raise OSError
|
|
316
|
+
else:
|
|
317
|
+
self.libbatch_path = _dl_abspath(libbatch)
|
|
318
|
+
return
|
|
319
|
+
except (OSError, TypeError) as e:
|
|
320
|
+
exc_type, exc_obj, exc_tb = sys.exc_info()
|
|
321
|
+
fname = path.split(exc_tb.tb_frame.f_code.co_filename)[1]
|
|
322
|
+
info = [str(x) for x in [e, exc_type,
|
|
323
|
+
'{}:{}'.format(fname, exc_tb.tb_lineno)]]
|
|
324
|
+
cdllexcept[libbatch] = '\n'.join(info)
|
|
325
|
+
|
|
326
|
+
if self.handle is None:
|
|
327
|
+
# fall-back to tecinterprocess
|
|
328
|
+
try:
|
|
329
|
+
libinterproc = libfmt.format(name='tecinterprocess')
|
|
330
|
+
self.handle = ctypes.cdll.LoadLibrary(libinterproc)
|
|
331
|
+
self.tecutil_handle = self.handle
|
|
332
|
+
if self.handle is None:
|
|
333
|
+
raise OSError
|
|
334
|
+
else:
|
|
335
|
+
self.libbatch_path = _dl_abspath(libinterproc)
|
|
336
|
+
return
|
|
337
|
+
except (OSError, TypeError) as e:
|
|
338
|
+
exc_type, exc_obj, exc_tb = sys.exc_info()
|
|
339
|
+
fname = path.split(exc_tb.tb_frame.f_code.co_filename)[1]
|
|
340
|
+
info = [str(x) for x in [e, exc_type,
|
|
341
|
+
'{}:{}'.format(fname, exc_tb.tb_lineno)]]
|
|
342
|
+
cdllexcept[libinterproc] = '\n'.join(info)
|
|
343
|
+
|
|
344
|
+
msg = dedent('''\
|
|
345
|
+
The Tecplot batch library or one of its dependencies could not be
|
|
346
|
+
found. This is usually the result of not setting the environment
|
|
347
|
+
path LD_LIBRARY_PATH to the directory containing the tecplot
|
|
348
|
+
executables.
|
|
349
|
+
|
|
350
|
+
LD_LIBRARY_PATH={libpath}
|
|
351
|
+
|
|
352
|
+
Library file name(s):
|
|
353
|
+
{libnames}
|
|
354
|
+
''').format(libpath=os.environ.get('LD_LIBRARY_PATH', None),
|
|
355
|
+
libnames='\n '.join(libnames))
|
|
356
|
+
|
|
357
|
+
self.libbatch_path = _find_library([libbatch])
|
|
358
|
+
if self.libbatch_path is None:
|
|
359
|
+
raise TecplotLibraryNotFoundError(msg)
|
|
360
|
+
|
|
361
|
+
msg += dedent('''
|
|
362
|
+
Found batch library file:
|
|
363
|
+
{foundlib}
|
|
364
|
+
|
|
365
|
+
Your Tecplot 360 EX may be out of date. Please install the latest
|
|
366
|
+
version of 360 EX which can be obtained here:
|
|
367
|
+
https://www.tecplot.com/downloads
|
|
368
|
+
''').format(foundlib=self.libbatch_path)
|
|
369
|
+
|
|
370
|
+
missinglibs = _missinglibs(self.libbatch_path)
|
|
371
|
+
if len(missinglibs):
|
|
372
|
+
msg += dedent('''
|
|
373
|
+
Missing libraries:
|
|
374
|
+
{missinglibs}
|
|
375
|
+
''').format(missinglibs='\n '.join(missinglibs))
|
|
376
|
+
|
|
377
|
+
lib = path.basename(self.libbatch_path)
|
|
378
|
+
msg += dedent('''
|
|
379
|
+
Python ctypes exception caught while trying to load library:
|
|
380
|
+
{cdllexcept}
|
|
381
|
+
''').format(cdllexcept='\n '.join(cdllexcept[lib].split('\n')))
|
|
382
|
+
|
|
383
|
+
raise TecplotLibraryNotLoadedError(msg)
|
|
384
|
+
|
|
385
|
+
def load_darwin(self):
|
|
386
|
+
|
|
387
|
+
def _missinglibs(lib):
|
|
388
|
+
ret = []
|
|
389
|
+
try:
|
|
390
|
+
log.debug('looking for missing libs using otool')
|
|
391
|
+
cmd = 'otool -L ' + lib
|
|
392
|
+
proc = Popen(cmd, shell=True,
|
|
393
|
+
executable=os.environ.get('SHELL', '/bin/bash'),
|
|
394
|
+
env=os.environ, stdout=PIPE, stderr=PIPE)
|
|
395
|
+
out, err = proc.communicate()
|
|
396
|
+
if 'command not found' not in err.decode():
|
|
397
|
+
for line in out.decode().split('\n'):
|
|
398
|
+
if line.endswith('not found'):
|
|
399
|
+
ret.append(line.split()[0])
|
|
400
|
+
except:
|
|
401
|
+
log.debug('diagnostic command failed: ' + cmd)
|
|
402
|
+
return ret
|
|
403
|
+
|
|
404
|
+
def _find_library(libnames):
|
|
405
|
+
for libname in libnames:
|
|
406
|
+
ret = util.find_library(libname)
|
|
407
|
+
if ret is not None:
|
|
408
|
+
return ret
|
|
409
|
+
|
|
410
|
+
libfmt = 'lib{name}.so'
|
|
411
|
+
libbatch = libfmt.format(name='tecutilbatch')
|
|
412
|
+
libchecked = libfmt.format(name='tecutilchecked')
|
|
413
|
+
libnames = [libbatch, libchecked]
|
|
414
|
+
|
|
415
|
+
cdllexcept = {}
|
|
416
|
+
self.libbatch_path = _find_library([libbatch])
|
|
417
|
+
try:
|
|
418
|
+
self.handle = ctypes.cdll.LoadLibrary(libbatch)
|
|
419
|
+
self.tecutil_handle = ctypes.cdll.LoadLibrary(libchecked)
|
|
420
|
+
if self.handle is None or self.tecutil_handle is None:
|
|
421
|
+
raise OSError
|
|
422
|
+
else:
|
|
423
|
+
return
|
|
424
|
+
except (OSError, TypeError) as e:
|
|
425
|
+
exc_type, exc_obj, exc_tb = sys.exc_info()
|
|
426
|
+
fname = path.split(exc_tb.tb_frame.f_code.co_filename)[1]
|
|
427
|
+
info = [str(x) for x in [e, exc_type, fname, exc_tb.tb_lineno]]
|
|
428
|
+
cdllexcept[libbatch] = '\n'.join(info)
|
|
429
|
+
|
|
430
|
+
if self.handle is None:
|
|
431
|
+
# fall-back to tecinterprocess
|
|
432
|
+
try:
|
|
433
|
+
libinterproc = libfmt.format(name='tecinterprocess')
|
|
434
|
+
self.libbatch_path = _find_library([libinterproc])
|
|
435
|
+
self.handle = ctypes.cdll.LoadLibrary(libinterproc)
|
|
436
|
+
self.tecutil_handle = self.handle
|
|
437
|
+
if self.handle is None:
|
|
438
|
+
raise OSError
|
|
439
|
+
else:
|
|
440
|
+
return
|
|
441
|
+
except (OSError, TypeError) as e:
|
|
442
|
+
exc_type, exc_obj, exc_tb = sys.exc_info()
|
|
443
|
+
fname = path.split(exc_tb.tb_frame.f_code.co_filename)[1]
|
|
444
|
+
info = [str(x) for x in [e, exc_type,
|
|
445
|
+
'{}:{}'.format(fname, exc_tb.tb_lineno)]]
|
|
446
|
+
cdllexcept[libinterproc] = '\n'.join(info)
|
|
447
|
+
|
|
448
|
+
msg = dedent('''\
|
|
449
|
+
The Tecplot batch library or one of its dependencies could not be
|
|
450
|
+
found. This is usually the result of not setting the environment
|
|
451
|
+
path DYLD_LIBRARY_PATH to the directory containing the tecplot
|
|
452
|
+
executables.
|
|
453
|
+
|
|
454
|
+
DYLD_LIBRARY_PATH={libpath}
|
|
455
|
+
|
|
456
|
+
Batch library file name(s):
|
|
457
|
+
{libnames}
|
|
458
|
+
''').format(libpath=os.environ.get('DYLD_LIBRARY_PATH', None),
|
|
459
|
+
libnames='\n '.join(libnames))
|
|
460
|
+
|
|
461
|
+
if self.libbatch_path is None:
|
|
462
|
+
raise TecplotLibraryNotFoundError(msg)
|
|
463
|
+
|
|
464
|
+
msg += dedent('''
|
|
465
|
+
Found batch library file:
|
|
466
|
+
{foundlib}
|
|
467
|
+
|
|
468
|
+
Your Tecplot 360 EX may be out of date. Please install the latest
|
|
469
|
+
version of 360 EX which can be obtained here:
|
|
470
|
+
https://www.tecplot.com/downloads
|
|
471
|
+
''').format(foundlib=self.libbatch_path)
|
|
472
|
+
|
|
473
|
+
missinglibs = _missinglibs(self.libbatch_path)
|
|
474
|
+
if len(missinglibs):
|
|
475
|
+
msg += dedent('''
|
|
476
|
+
Missing libraries:
|
|
477
|
+
{missinglibs}
|
|
478
|
+
''').format(missinglibs='\n '.join(missinglibs))
|
|
479
|
+
|
|
480
|
+
lib = path.basename(self.libbatch_path)
|
|
481
|
+
msg += dedent('''
|
|
482
|
+
Python ctypes exception caught while trying to load library:
|
|
483
|
+
{cdllexcept}
|
|
484
|
+
''').format(cdllexcept='\n '.join(cdllexcept[lib].split('\n')))
|
|
485
|
+
|
|
486
|
+
raise TecplotLibraryNotLoadedError(msg)
|
|
487
|
+
|
|
488
|
+
def load_windows(self):
|
|
489
|
+
|
|
490
|
+
def _load_library(libbatch, libchecked=None):
|
|
491
|
+
try:
|
|
492
|
+
self.handle = ctypes.cdll.LoadLibrary(libbatch)
|
|
493
|
+
if self.handle is None:
|
|
494
|
+
return False, 'Failed to load: {}'.format(libbatch)
|
|
495
|
+
if libchecked:
|
|
496
|
+
self.tecutil_handle = ctypes.cdll.LoadLibrary(libchecked)
|
|
497
|
+
if self.tecutil_handle is None:
|
|
498
|
+
return False, 'Failed to load: {}'.format(libchecked)
|
|
499
|
+
else:
|
|
500
|
+
self.tecutil_handle = self.handle
|
|
501
|
+
if not os.path.isabs(libbatch):
|
|
502
|
+
libbatch = util.find_library(os.path.splitext(libbatch)[0])
|
|
503
|
+
return True, libbatch
|
|
504
|
+
except OSError as e:
|
|
505
|
+
exc_type, exc_obj, exc_tb = sys.exc_info()
|
|
506
|
+
fname = path.split(exc_tb.tb_frame.f_code.co_filename)[1]
|
|
507
|
+
info = [str(x) for x in [e, exc_type, fname,
|
|
508
|
+
exc_tb.tb_lineno]]
|
|
509
|
+
return False, '\n'.join(info)
|
|
510
|
+
|
|
511
|
+
@contextlib.contextmanager
|
|
512
|
+
def _patch_path(libdir):
|
|
513
|
+
if libdir is None:
|
|
514
|
+
if sys.version_info >= (3, 8):
|
|
515
|
+
for p in filter(None, os.environ['PATH'].split(os.pathsep)):
|
|
516
|
+
os.add_dll_directory(p)
|
|
517
|
+
yield
|
|
518
|
+
else:
|
|
519
|
+
original_path = os.environ['PATH']
|
|
520
|
+
try:
|
|
521
|
+
if libdir and sys.version_info >= (3, 8):
|
|
522
|
+
os.add_dll_directory(libdir)
|
|
523
|
+
os.environ['PATH'] = libdir + os.pathsep + os.environ['PATH']
|
|
524
|
+
yield
|
|
525
|
+
finally:
|
|
526
|
+
os.environ['PATH'] = original_path
|
|
527
|
+
|
|
528
|
+
cdllexcept = []
|
|
529
|
+
for libs in (('tecutilbatch.dll', 'tecutilchecked.dll'),
|
|
530
|
+
('tecinterprocess.dll',),
|
|
531
|
+
('tecutilbatchd.dll', 'tecutilcheckedd.dll')):
|
|
532
|
+
|
|
533
|
+
# convert libs to absolute paths if possible
|
|
534
|
+
libdir = None
|
|
535
|
+
for d in os.environ['PATH'].split(os.pathsep):
|
|
536
|
+
abslibs = [os.path.join(d, lib) for lib in libs]
|
|
537
|
+
if all(os.path.exists(f) for f in abslibs):
|
|
538
|
+
libs = abslibs
|
|
539
|
+
libdir = d
|
|
540
|
+
break
|
|
541
|
+
|
|
542
|
+
with _patch_path(libdir):
|
|
543
|
+
success, msg = _load_library(*libs)
|
|
544
|
+
if success:
|
|
545
|
+
self.libbatch_path = msg
|
|
546
|
+
return
|
|
547
|
+
else:
|
|
548
|
+
cdllexcept.append(msg)
|
|
549
|
+
|
|
550
|
+
msg = dedent('''\
|
|
551
|
+
The Tecplot batch library or one of its dependencies could not be
|
|
552
|
+
found. This is usually the result of not setting the environment
|
|
553
|
+
path PATH to the directory containing the tecplot executables.
|
|
554
|
+
Alternatively, your Tecplot 360 EX may be out of date. Please
|
|
555
|
+
install the latest version of 360 EX which can be obtained here:
|
|
556
|
+
https://www.tecplot.com/downloads
|
|
557
|
+
|
|
558
|
+
Batch library file names:
|
|
559
|
+
tecutilbatch.dll
|
|
560
|
+
tecutilchecked.dll
|
|
561
|
+
|
|
562
|
+
PATH={libpath}
|
|
563
|
+
|
|
564
|
+
Found batch library file:
|
|
565
|
+
{foundlib}
|
|
566
|
+
''').format(libpath=os.environ.get('PATH', None),
|
|
567
|
+
foundlib=self.libbatch_path)
|
|
568
|
+
|
|
569
|
+
if cdllexcept:
|
|
570
|
+
info = '\n '.join(cdllexcept[0].split('\n'))
|
|
571
|
+
# Error 127 == Missing dll symbol.
|
|
572
|
+
if 'Error 127' in info:
|
|
573
|
+
info += ('\n***NOTE: If your script uses PyQt, ' +
|
|
574
|
+
'you must import tecplot before importing PyQt.\n')
|
|
575
|
+
msg += dedent('''
|
|
576
|
+
Python ctypes exception caught while trying to load library:
|
|
577
|
+
{cdllexcept}
|
|
578
|
+
''').format(cdllexcept=info)
|
|
579
|
+
|
|
580
|
+
if self.libbatch_path is None:
|
|
581
|
+
raise TecplotLibraryNotFoundError(msg)
|
|
582
|
+
|
|
583
|
+
raise TecplotLibraryNotLoadedError(msg)
|
|
584
|
+
|
|
585
|
+
def start(self):
|
|
586
|
+
if self.started:
|
|
587
|
+
return
|
|
588
|
+
|
|
589
|
+
self._license_check_count = 0
|
|
590
|
+
|
|
591
|
+
if self.handle is None:
|
|
592
|
+
try:
|
|
593
|
+
self.init_local_library()
|
|
594
|
+
except KeyError as e:
|
|
595
|
+
self._load_library_error = TecplotLibraryNotFoundError, str(e)
|
|
596
|
+
except AttributeError as e:
|
|
597
|
+
log.error(e)
|
|
598
|
+
self._load_library_error = TecplotOutOfDateEngineError, (2020, 1, 0)
|
|
599
|
+
except Exception as e:
|
|
600
|
+
log.debug(e)
|
|
601
|
+
self._load_library_error = type(e), str(e)
|
|
602
|
+
|
|
603
|
+
if self.handle is None or self.libbatch_path is None:
|
|
604
|
+
if self._load_library_error is not None:
|
|
605
|
+
Err, msg = self._load_library_error
|
|
606
|
+
raise Err(msg)
|
|
607
|
+
else:
|
|
608
|
+
raise TecplotLibraryNotLoadedError
|
|
609
|
+
|
|
610
|
+
if self.stopped:
|
|
611
|
+
msg = 'PyTecplot cannot be restarted after it has been stopped.'
|
|
612
|
+
raise TecplotLibraryNotLoadedError(msg)
|
|
613
|
+
|
|
614
|
+
log.debug('Attempting to start Tecplot engine')
|
|
615
|
+
msg = 'tecutilbatch path: {0} [{1}]'
|
|
616
|
+
log.debug(msg.format(self.libbatch_path,
|
|
617
|
+
time.ctime(os.stat(self.libbatch_path).st_mtime)))
|
|
618
|
+
|
|
619
|
+
log.debug('SDK home: "{}"'.format(self.tecsdkhome))
|
|
620
|
+
|
|
621
|
+
# Suppress the batch log file from the SDK, can be reenabled
|
|
622
|
+
# by setting the BATCHLOGFILE environment variable.
|
|
623
|
+
if 'BATCHLOGFILE' not in os.environ:
|
|
624
|
+
os.putenv('BATCHLOGFILE', os.devnull)
|
|
625
|
+
|
|
626
|
+
with captured_output(log.debug):
|
|
627
|
+
result = None
|
|
628
|
+
try:
|
|
629
|
+
# The Linux version of the SDK, 2020 and newer, passes SDK
|
|
630
|
+
# configuration command line arguments required to ensure
|
|
631
|
+
# that the SDK is started with a configuration that matches
|
|
632
|
+
# the libraries loaded as prepared by the tec360-env script.
|
|
633
|
+
# Examples are: --disable-FBOs, --osmesa, and --mesa.
|
|
634
|
+
args = [sys.executable, '-h', self.tecsdkhome]
|
|
635
|
+
args.extend(os.environ.get('TECPLOT_SDK_CONFIG','').split())
|
|
636
|
+
log.debug('args: '+' '.join(args))
|
|
637
|
+
|
|
638
|
+
argc = len(args)
|
|
639
|
+
argv = (c_char_p * len(args))(*[a.encode() for a in args])
|
|
640
|
+
result = self.handle.Start(argc, argv)
|
|
641
|
+
except Exception:
|
|
642
|
+
msg = 'Error initializing Tecplot engine. Your Tecplot 360 ' + \
|
|
643
|
+
'installation may be out of date.'
|
|
644
|
+
raise TecplotInitializationError(msg)
|
|
645
|
+
|
|
646
|
+
if self.update_last_message():
|
|
647
|
+
self.log_last_message()
|
|
648
|
+
if self.last_message.level >= log.getEffectiveLevel():
|
|
649
|
+
raise TecplotInitializationError(self.last_message.message)
|
|
650
|
+
|
|
651
|
+
# Many error codes are license related, but not all of the them.
|
|
652
|
+
# We don't want to imply that there was a license error if the problem
|
|
653
|
+
# is not license related. For example, if batch.log is not writable,
|
|
654
|
+
# the return code will be "InternalInitializationError".
|
|
655
|
+
# In that case it would be misleading to throw a license error.
|
|
656
|
+
|
|
657
|
+
# Therefore, we are careful to throw a license error only
|
|
658
|
+
# if the error code is license related, and
|
|
659
|
+
# otherwise throw a TecplotSystemError.
|
|
660
|
+
if result != ManagerStartReturnCode.Ok:
|
|
661
|
+
errmsg = result.to_string(self)
|
|
662
|
+
if result in [ManagerStartReturnCode.LicenseExpired,
|
|
663
|
+
ManagerStartReturnCode.LicenseIsInvalid,
|
|
664
|
+
ManagerStartReturnCode.LicenseFileNotFound,
|
|
665
|
+
ManagerStartReturnCode.LicenseFileContainsPermanent]:
|
|
666
|
+
raise TecplotLicenseError(errmsg)
|
|
667
|
+
elif result != ManagerStartReturnCode.Ok:
|
|
668
|
+
raise TecplotSystemError(errmsg)
|
|
669
|
+
|
|
670
|
+
log.info('Tecplot engine started - license acquired')
|
|
671
|
+
info = self.handle.LicenseInfo().decode()
|
|
672
|
+
log.info('License information:\n ' + info.replace('\n', '\n '))
|
|
673
|
+
self.started = True
|
|
674
|
+
|
|
675
|
+
expire = self.license_expiration
|
|
676
|
+
if isinstance(expire, date):
|
|
677
|
+
days_left = (expire - date.today()).days
|
|
678
|
+
if days_left < 31:
|
|
679
|
+
if self.license_is_roaming:
|
|
680
|
+
warning_msg = '''
|
|
681
|
+
Your roaming license will expire in **{} days**.
|
|
682
|
+
'''
|
|
683
|
+
else:
|
|
684
|
+
warning_msg = '''
|
|
685
|
+
Your Tecplot software maintenance subscription
|
|
686
|
+
(TecPLUS) will expire in **{} days**, after which you
|
|
687
|
+
will no longer be able to use PyTecplot. Contact
|
|
688
|
+
sales@tecplot.com to renew your TecPLUS subscription.
|
|
689
|
+
'''
|
|
690
|
+
warning_msg = dedent(warning_msg).format(days_left)
|
|
691
|
+
warn(warning_msg)
|
|
692
|
+
|
|
693
|
+
if self.update_last_message():
|
|
694
|
+
# at this point, the engine has started successfully,
|
|
695
|
+
# but there was an error message.
|
|
696
|
+
# Perhaps an addon did not load correctly.
|
|
697
|
+
lvl = self.last_message.level
|
|
698
|
+
msg = self.last_message.message
|
|
699
|
+
if msg:
|
|
700
|
+
if lvl > logging.WARNING: # downgrade message to warning
|
|
701
|
+
lvl = logging.WARNING
|
|
702
|
+
log.log(lvl, msg)
|
|
703
|
+
|
|
704
|
+
def stop(self):
|
|
705
|
+
if self.stopped:
|
|
706
|
+
return
|
|
707
|
+
elif self.started and not self.connected:
|
|
708
|
+
# On macOS and prior to SDK v2022.1, calling stop from pytecplot would
|
|
709
|
+
# cause a crash on exit for certain edge cases.
|
|
710
|
+
if platform.system() == 'Darwin' and self.sdk_version_info < (2022, 1):
|
|
711
|
+
self.release_license()
|
|
712
|
+
else:
|
|
713
|
+
with captured_output(log.debug):
|
|
714
|
+
self.handle.Stop()
|
|
715
|
+
log.info('Tecplot engine stopped - license released')
|
|
716
|
+
self.started = False
|
|
717
|
+
self.stopped = True
|
|
718
|
+
|
|
719
|
+
if __debug__:
|
|
720
|
+
if log.isEnabledFor(logging.DEBUG):
|
|
721
|
+
fmt = '{: >8d} {}'
|
|
722
|
+
call_data = (('TecUtil', self._tecutil_call_count),
|
|
723
|
+
('Get style', self._style_call_count['GET']),
|
|
724
|
+
('Set style', self._style_call_count['SET']))
|
|
725
|
+
for call_type, calls in call_data:
|
|
726
|
+
if calls:
|
|
727
|
+
data = sorted(calls.most_common(),
|
|
728
|
+
key=lambda x: (-x[1], x[0]))
|
|
729
|
+
msg = '\n'.join(fmt.format(n, f) for f, n in data)
|
|
730
|
+
log.debug(call_type + ' call counts:\n' + msg)
|
|
731
|
+
|
|
732
|
+
@property
|
|
733
|
+
def license_is_valid(self):
|
|
734
|
+
if not self.started:
|
|
735
|
+
return False
|
|
736
|
+
lcc = self._license_check_count
|
|
737
|
+
self._license_check_count = 0 if lcc == 10000 else lcc + 1
|
|
738
|
+
return lcc != 0 or self.handle.LicenseIsValid()
|
|
739
|
+
|
|
740
|
+
def acquire_license(self):
|
|
741
|
+
if self.connected:
|
|
742
|
+
msg = dedent('''\
|
|
743
|
+
PyTecplot is connected to a running
|
|
744
|
+
instance of Tecplot 360.
|
|
745
|
+
Disconnect before attempting to proceed in batch-mode.''')
|
|
746
|
+
raise TecplotLogicError(msg)
|
|
747
|
+
if not self.started:
|
|
748
|
+
self.start()
|
|
749
|
+
if not self.license_is_valid:
|
|
750
|
+
if not self.handle.AcquireLicense():
|
|
751
|
+
raise TecplotLicenseError('Could not acquire a valid license.')
|
|
752
|
+
|
|
753
|
+
def release_license(self):
|
|
754
|
+
if not self.connected and self.license_is_valid:
|
|
755
|
+
self.handle.ReleaseLicense()
|
|
756
|
+
|
|
757
|
+
@property
|
|
758
|
+
def license_expiration(self):
|
|
759
|
+
self.acquire_license()
|
|
760
|
+
expiration_date = self.handle.LicenseExpirationDate().decode()
|
|
761
|
+
try:
|
|
762
|
+
y,m,d = [int(x) for x in expiration_date.split('-')]
|
|
763
|
+
return date(year=y, month=m, day=d)
|
|
764
|
+
except:
|
|
765
|
+
return expiration_date
|
|
766
|
+
|
|
767
|
+
def start_roaming(self, days):
|
|
768
|
+
self.acquire_license()
|
|
769
|
+
if not self.handle.LicenseStartRoaming(int(days)):
|
|
770
|
+
msg = getattr(self.update_last_message(), 'message', None)
|
|
771
|
+
raise TecplotLicenseError(msg)
|
|
772
|
+
expiration_date = self.license_expiration
|
|
773
|
+
if isinstance(expiration_date, date):
|
|
774
|
+
days_left = (expiration_date - date.today()).days
|
|
775
|
+
msg = dedent('''
|
|
776
|
+
You have successfully checked out a roaming license of
|
|
777
|
+
Tecplot. This will be valid for {} days until
|
|
778
|
+
midnight of {}.'''.format(days_left, expiration_date))
|
|
779
|
+
log.critical(msg)
|
|
780
|
+
|
|
781
|
+
@property
|
|
782
|
+
def license_is_roaming(self):
|
|
783
|
+
self.acquire_license()
|
|
784
|
+
return self.handle.LicenseIsRoaming()
|
|
785
|
+
|
|
786
|
+
def stop_roaming(self, force=False):
|
|
787
|
+
try:
|
|
788
|
+
self.acquire_license()
|
|
789
|
+
if not self.handle.LicenseStopRoaming(False):
|
|
790
|
+
raise TecplotLicenseError
|
|
791
|
+
msg = 'Your Tecplot roaming license has been checked in.'
|
|
792
|
+
log.critical('\n' + msg)
|
|
793
|
+
except TecplotError:
|
|
794
|
+
if force:
|
|
795
|
+
if not self.handle.LicenseStopRoaming(force):
|
|
796
|
+
raise TecplotLicenseError
|
|
797
|
+
msg = 'The local Tecplot roaming license has been cleared.'
|
|
798
|
+
log.critical('\n' + msg)
|
|
799
|
+
else:
|
|
800
|
+
try:
|
|
801
|
+
msg = getattr(self.update_last_message(), 'message', '')
|
|
802
|
+
except:
|
|
803
|
+
msg = ''
|
|
804
|
+
msg += dedent(r'''
|
|
805
|
+
You may try this command to
|
|
806
|
+
clear the local roaming license:
|
|
807
|
+
tecplot.session.stop_roaming(force=True)''')
|
|
808
|
+
raise TecplotLicenseError(msg)
|
|
809
|
+
|
|
810
|
+
@property
|
|
811
|
+
def last_message(self):
|
|
812
|
+
return self._last_message
|
|
813
|
+
|
|
814
|
+
def clear_last_message(self):
|
|
815
|
+
self._last_message = None
|
|
816
|
+
|
|
817
|
+
def log_last_message(self):
|
|
818
|
+
if self.last_message:
|
|
819
|
+
if self.last_message.message:
|
|
820
|
+
log.log(self.last_message.level, self.last_message.message)
|
|
821
|
+
|
|
822
|
+
def update_last_message(self):
|
|
823
|
+
def _cleanup_msg(msg):
|
|
824
|
+
msg = msg.decode('utf-8').splitlines()
|
|
825
|
+
return os.linesep.join([s for s in msg if s.strip()])
|
|
826
|
+
last_message = None
|
|
827
|
+
if self.handle is None:
|
|
828
|
+
return last_message
|
|
829
|
+
msg = self.handle.GetTUAssertErrorMessage()
|
|
830
|
+
if msg:
|
|
831
|
+
last_message = TecUtilConnector.Message(
|
|
832
|
+
level=logging.CRITICAL,
|
|
833
|
+
message=_cleanup_msg(msg))
|
|
834
|
+
self.handle.ClearErrorMessage()
|
|
835
|
+
elif __debug__:
|
|
836
|
+
log_level = {
|
|
837
|
+
MessageBoxType.Error: logging.ERROR,
|
|
838
|
+
MessageBoxType.Warning: logging.WARNING,
|
|
839
|
+
MessageBoxType.Information: logging.INFO}
|
|
840
|
+
msg_ptr = self.tecutil_handle.tecUtilLastErrorMessage()
|
|
841
|
+
if msg_ptr:
|
|
842
|
+
msg_char_p = cast(msg_ptr, c_char_p)
|
|
843
|
+
msg = msg_char_p.value
|
|
844
|
+
if msg:
|
|
845
|
+
self.tecutil_handle.tecUtilStringDealloc(msg_char_p)
|
|
846
|
+
# get message type/level
|
|
847
|
+
mbox_val = self.tecutil_handle.tecUtilLastErrorMessageType()
|
|
848
|
+
mbox_type = MessageBoxType(mbox_val)
|
|
849
|
+
last_message = TecUtilConnector.Message(
|
|
850
|
+
level=log_level.get(mbox_type, logging.WARNING),
|
|
851
|
+
message=_cleanup_msg(msg))
|
|
852
|
+
# clean up
|
|
853
|
+
self.tecutil_handle.tecUtilParentLockStart(False)
|
|
854
|
+
try:
|
|
855
|
+
self.tecutil_handle.tecUtilLastErrorMessageClear()
|
|
856
|
+
finally:
|
|
857
|
+
self.tecutil_handle.tecUtilParentLockFinish()
|
|
858
|
+
if last_message:
|
|
859
|
+
self._last_message = last_message
|
|
860
|
+
return last_message
|
|
861
|
+
|
|
862
|
+
@property
|
|
863
|
+
def sdk_version_info(self):
|
|
864
|
+
if not hasattr(self, '_sdk_version_info'):
|
|
865
|
+
try:
|
|
866
|
+
if self.connected:
|
|
867
|
+
self._sdk_version_info = SDKVersion(*self.client.sdk_version_info)
|
|
868
|
+
else:
|
|
869
|
+
self._sdk_version_info = SDKVersion(
|
|
870
|
+
self.tecutil_handle.tecUtilTecplotGetMajorVersion(),
|
|
871
|
+
self.tecutil_handle.tecUtilTecplotGetMinorVersion(),
|
|
872
|
+
self.tecutil_handle.tecUtilTecplotGetMajorRevision(),
|
|
873
|
+
self.tecutil_handle.tecUtilTecplotGetMinorRevision())
|
|
874
|
+
except AttributeError:
|
|
875
|
+
self._sdk_version_info = SDKVersion(0, 0, 0, 0)
|
|
876
|
+
return self._sdk_version_info
|
|
877
|
+
|
|
878
|
+
@property
|
|
879
|
+
def sdk_version(self):
|
|
880
|
+
version_info = self.sdk_version_info
|
|
881
|
+
if version_info == SDKVersion(0, 0, 0, 0):
|
|
882
|
+
return 'unknown'
|
|
883
|
+
else:
|
|
884
|
+
return '{}.{}-{}-{}'.format(*version_info)
|
|
885
|
+
|
|
886
|
+
def update_sdk_version(self):
|
|
887
|
+
import tecplot
|
|
888
|
+
try:
|
|
889
|
+
del self._sdk_version_info
|
|
890
|
+
except AttributeError:
|
|
891
|
+
pass
|
|
892
|
+
tecplot.version.sdk_version_info = self.sdk_version_info
|
|
893
|
+
tecplot.version.sdk_version = self.sdk_version
|
|
894
|
+
tecplot.sdk_version_info = tecplot.version.sdk_version_info
|
|
895
|
+
tecplot.sdk_version = tecplot.version.sdk_version
|
|
896
|
+
|
|
897
|
+
def macro_record_start(self, filename):
|
|
898
|
+
self.start()
|
|
899
|
+
if not self.handle.MacroRecordStart(filename.encode()):
|
|
900
|
+
lastmsg = self.update_last_message()
|
|
901
|
+
if lastmsg:
|
|
902
|
+
if lastmsg.level < logging.ERROR:
|
|
903
|
+
if __debug__:
|
|
904
|
+
self.log_last_message()
|
|
905
|
+
pass
|
|
906
|
+
else:
|
|
907
|
+
raise TecplotSystemError(lastmsg.message)
|
|
908
|
+
else:
|
|
909
|
+
raise TecplotRuntimeError('failed to start macro recording')
|
|
910
|
+
|
|
911
|
+
def macro_record_end(self):
|
|
912
|
+
self.handle.MacroRecordEnd()
|
|
913
|
+
|
|
914
|
+
def translate_macro_to_python(self, command):
|
|
915
|
+
self.start()
|
|
916
|
+
|
|
917
|
+
command = command.strip()
|
|
918
|
+
rawdata = bool(re.search('RAWDATA', command, flags=re.I))
|
|
919
|
+
translate = {
|
|
920
|
+
True: self.handle.TranslateMacroWithRawDataToPython,
|
|
921
|
+
False: self.handle.TranslateMacroToPython,
|
|
922
|
+
}[rawdata]
|
|
923
|
+
|
|
924
|
+
cptr = translate(command.encode('utf-8'))
|
|
925
|
+
cstr = cast(cptr, c_char_p)
|
|
926
|
+
try:
|
|
927
|
+
return cstr.value.decode('utf-8').strip()
|
|
928
|
+
except:
|
|
929
|
+
if "'''" in command:
|
|
930
|
+
command = command.replace("'", r"\'")
|
|
931
|
+
return "tp.macro.execute_command('''" + command + "''')"
|
|
932
|
+
finally:
|
|
933
|
+
self.tecutil_handle.tecUtilStringDealloc(cstr)
|
|
934
|
+
|
|
935
|
+
|
|
936
|
+
_tecutil_connector = TecUtilConnector()
|
|
937
|
+
_tecutil = TecUtil(_tecutil_connector)
|