tagreader 6.0.2__tar.gz → 6.0.4__tar.gz
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.
- {tagreader-6.0.2 → tagreader-6.0.4}/PKG-INFO +10 -17
- {tagreader-6.0.2 → tagreader-6.0.4}/README.md +3 -11
- {tagreader-6.0.2 → tagreader-6.0.4}/pyproject.toml +5 -5
- {tagreader-6.0.2 → tagreader-6.0.4}/tagreader/clients.py +2 -80
- {tagreader-6.0.2 → tagreader-6.0.4}/tagreader/utils.py +61 -74
- {tagreader-6.0.2 → tagreader-6.0.4}/tagreader/web_handlers.py +11 -37
- {tagreader-6.0.2 → tagreader-6.0.4}/LICENSE +0 -0
- {tagreader-6.0.2 → tagreader-6.0.4}/tagreader/__init__.py +0 -0
- {tagreader-6.0.2 → tagreader-6.0.4}/tagreader/__version__.py +0 -0
- {tagreader-6.0.2 → tagreader-6.0.4}/tagreader/cache.py +0 -0
- {tagreader-6.0.2 → tagreader-6.0.4}/tagreader/logger.py +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: tagreader
|
|
3
|
-
Version: 6.0.
|
|
3
|
+
Version: 6.0.4
|
|
4
4
|
Summary: Tagreader is a Python package for reading trend data from the OSIsoft PI and Aspen Infoplus.21 IMS systems.
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: Aspen InfoPlus.21,OSIsoft PI
|
|
7
7
|
Author: Einar S. Idsø
|
|
8
8
|
Author-email: eiids@equinor.com
|
|
9
|
-
Requires-Python: >=3.
|
|
9
|
+
Requires-Python: >=3.9.2,<4.0.0
|
|
10
10
|
Classifier: Development Status :: 5 - Production/Stable
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
12
|
Classifier: Intended Audience :: Science/Research
|
|
@@ -16,22 +16,23 @@ Classifier: Operating System :: MacOS
|
|
|
16
16
|
Classifier: Operating System :: Microsoft :: Windows
|
|
17
17
|
Classifier: Operating System :: POSIX :: Linux
|
|
18
18
|
Classifier: Programming Language :: Python :: 3
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
20
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
21
19
|
Classifier: Programming Language :: Python :: 3.10
|
|
22
20
|
Classifier: Programming Language :: Python :: 3.11
|
|
23
21
|
Classifier: Programming Language :: Python :: 3.12
|
|
24
22
|
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
25
25
|
Classifier: Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator
|
|
26
26
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
27
27
|
Classifier: Topic :: Utilities
|
|
28
28
|
Provides-Extra: notebooks
|
|
29
|
-
Requires-Dist: certifi (>=2024.12.14,<
|
|
29
|
+
Requires-Dist: certifi (>=2024.12.14,<2026.0.0)
|
|
30
30
|
Requires-Dist: diskcache (>=5.6.1,<6.0.0)
|
|
31
31
|
Requires-Dist: matplotlib (>=3.7.5,<4.0.0) ; extra == "notebooks"
|
|
32
|
-
Requires-Dist: msal-bearer (>=
|
|
32
|
+
Requires-Dist: msal-bearer (>=1.3.0,<2.0.0)
|
|
33
33
|
Requires-Dist: notebook (>=7.2.2,<8.0.0) ; extra == "notebooks"
|
|
34
34
|
Requires-Dist: pandas (>=1)
|
|
35
|
+
Requires-Dist: platformdirs (>=4.3.7,<5.0.0)
|
|
35
36
|
Requires-Dist: pycryptodome (>=3.20.0,<4.0.0)
|
|
36
37
|
Requires-Dist: requests (>=2,<3)
|
|
37
38
|
Requires-Dist: requests-kerberos (>=0,<1)
|
|
@@ -48,25 +49,17 @@ Description-Content-Type: text/markdown
|
|
|
48
49
|
[](https://pepy.tech/project/tagreader)
|
|
49
50
|
|
|
50
51
|
Tagreader is a Python package for reading timeseries data from the OSIsoft PI and Aspen Infoplus.21
|
|
51
|
-
Information
|
|
52
|
-
as possible to the backend historians.
|
|
52
|
+
Information Management Systems (IMS). It is intended to be easy to use, and present as similar interfaces
|
|
53
|
+
as possible to the backend plant historians.
|
|
53
54
|
|
|
54
55
|
## Installation
|
|
55
56
|
You can install tagreader directly into your project from pypi by using pip
|
|
56
|
-
or another package manager.
|
|
57
|
+
or another package manager. Supports Python version 3.9.2 and above.
|
|
57
58
|
|
|
58
59
|
```shell
|
|
59
60
|
pip install tagreader
|
|
60
61
|
```
|
|
61
62
|
|
|
62
|
-
The following are required and will be installed:
|
|
63
|
-
|
|
64
|
-
* pandas
|
|
65
|
-
* requests
|
|
66
|
-
* requests-kerberos
|
|
67
|
-
* certifi
|
|
68
|
-
* diskcache
|
|
69
|
-
|
|
70
63
|
## Usage
|
|
71
64
|
Tagreader is easy to use for both Equinor internal IMS services, and non-internal usage. For non-internal usage
|
|
72
65
|
you simply need to provide the corresponding IMS service URLs and IMSType.
|
|
@@ -6,25 +6,17 @@
|
|
|
6
6
|
[](https://pepy.tech/project/tagreader)
|
|
7
7
|
|
|
8
8
|
Tagreader is a Python package for reading timeseries data from the OSIsoft PI and Aspen Infoplus.21
|
|
9
|
-
Information
|
|
10
|
-
as possible to the backend historians.
|
|
9
|
+
Information Management Systems (IMS). It is intended to be easy to use, and present as similar interfaces
|
|
10
|
+
as possible to the backend plant historians.
|
|
11
11
|
|
|
12
12
|
## Installation
|
|
13
13
|
You can install tagreader directly into your project from pypi by using pip
|
|
14
|
-
or another package manager.
|
|
14
|
+
or another package manager. Supports Python version 3.9.2 and above.
|
|
15
15
|
|
|
16
16
|
```shell
|
|
17
17
|
pip install tagreader
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
The following are required and will be installed:
|
|
21
|
-
|
|
22
|
-
* pandas
|
|
23
|
-
* requests
|
|
24
|
-
* requests-kerberos
|
|
25
|
-
* certifi
|
|
26
|
-
* diskcache
|
|
27
|
-
|
|
28
20
|
## Usage
|
|
29
21
|
Tagreader is easy to use for both Equinor internal IMS services, and non-internal usage. For non-internal usage
|
|
30
22
|
you simply need to provide the corresponding IMS service URLs and IMSType.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "tagreader"
|
|
3
|
-
version = "6.0.
|
|
3
|
+
version = "6.0.4"
|
|
4
4
|
description = "Tagreader is a Python package for reading trend data from the OSIsoft PI and Aspen Infoplus.21 IMS systems."
|
|
5
5
|
authors = ["Einar S. Idsø <eiids@equinor.com>", "Morten Dæhli Aslesen <masl@equinor.com"]
|
|
6
6
|
license = "MIT"
|
|
@@ -28,18 +28,18 @@ classifiers=[
|
|
|
28
28
|
]
|
|
29
29
|
|
|
30
30
|
[tool.poetry.dependencies]
|
|
31
|
-
python = "^3.
|
|
31
|
+
python = "^3.9.2"
|
|
32
32
|
pandas = ">=1"
|
|
33
|
-
certifi = "
|
|
33
|
+
certifi = ">=2024.12.14,<2026.0.0"
|
|
34
34
|
requests = "^2"
|
|
35
35
|
requests-kerberos = "^0"
|
|
36
|
-
msal-bearer = "
|
|
36
|
+
msal-bearer = "^1.3.0"
|
|
37
37
|
notebook = { version = "^7.2.2", optional = true }
|
|
38
38
|
matplotlib = { version = "^3.7.5", optional = true }
|
|
39
39
|
diskcache = "^5.6.1"
|
|
40
40
|
pycryptodome = "^3.20.0"
|
|
41
41
|
requests-ntlm = ">=1.1,<=2.0"
|
|
42
|
-
|
|
42
|
+
platformdirs = "^4.3.7"
|
|
43
43
|
|
|
44
44
|
[tool.poetry.group.dev.dependencies]
|
|
45
45
|
pre-commit = "^3"
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
from datetime import datetime, timedelta, tzinfo
|
|
1
|
+
from datetime import datetime, timedelta, timezone, tzinfo
|
|
3
2
|
from itertools import groupby
|
|
4
3
|
from operator import itemgetter
|
|
5
4
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
@@ -16,9 +15,6 @@ from tagreader.utils import (
|
|
|
16
15
|
ReaderType,
|
|
17
16
|
convert_to_pydatetime,
|
|
18
17
|
ensure_datetime_with_tz,
|
|
19
|
-
find_registry_key,
|
|
20
|
-
find_registry_key_from_name,
|
|
21
|
-
is_windows,
|
|
22
18
|
)
|
|
23
19
|
from tagreader.web_handlers import (
|
|
24
20
|
AspenHandlerWeb,
|
|
@@ -119,80 +115,6 @@ def get_next_timeslice(
|
|
|
119
115
|
return start, calc_end
|
|
120
116
|
|
|
121
117
|
|
|
122
|
-
def get_server_address_aspen(datasource: str) -> Optional[Tuple[str, int]]:
|
|
123
|
-
"""Data sources are listed under
|
|
124
|
-
HKEY_CURRENT_USER\\Software\\AspenTech\\ADSA\\Caches\\AspenADSA\\username.
|
|
125
|
-
For each data source there are multiple keys with Host entries. We start by
|
|
126
|
-
identifying the correct key to use by locating the UUID for Aspen SQLplus
|
|
127
|
-
services located under Aspen SQLplus service component. Then we find the
|
|
128
|
-
host and port based on the path above and the UUID.
|
|
129
|
-
"""
|
|
130
|
-
|
|
131
|
-
if not is_windows():
|
|
132
|
-
return None
|
|
133
|
-
import winreg
|
|
134
|
-
|
|
135
|
-
regkey_clsid = winreg.OpenKey(
|
|
136
|
-
winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Classes\Wow6432Node\CLSID"
|
|
137
|
-
)
|
|
138
|
-
regkey, _ = find_registry_key_from_name(
|
|
139
|
-
regkey_clsid, "Aspen SQLplus service component"
|
|
140
|
-
)
|
|
141
|
-
regkey_implemented_categories = winreg.OpenKeyEx(regkey, "Implemented Categories")
|
|
142
|
-
|
|
143
|
-
_, aspen_uuid = find_registry_key_from_name(
|
|
144
|
-
regkey_implemented_categories, "Aspen SQLplus services"
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
reg_adsa = winreg.OpenKey(
|
|
148
|
-
winreg.HKEY_CURRENT_USER,
|
|
149
|
-
r"Software\AspenTech\ADSA\Caches\AspenADSA\\" + os.getlogin(),
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
try:
|
|
153
|
-
reg_site_key = winreg.OpenKey(reg_adsa, datasource + "\\" + aspen_uuid)
|
|
154
|
-
host = winreg.QueryValueEx(reg_site_key, "Host")[0]
|
|
155
|
-
port = int(winreg.QueryValueEx(reg_site_key, "Port")[0])
|
|
156
|
-
return host, port
|
|
157
|
-
except FileNotFoundError:
|
|
158
|
-
return None
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
def get_server_address_pi(datasource: str) -> Optional[Tuple[str, int]]:
|
|
162
|
-
"""
|
|
163
|
-
PI data sources are listed under
|
|
164
|
-
HKEY_LOCAL_MACHINE\\Software\\Wow6432Node\\PISystem\\PI-SDK\\x.x\\ServerHandles or
|
|
165
|
-
\\Software\\PISystem\\PI-SDK\\x.x\\ServerHandles.
|
|
166
|
-
|
|
167
|
-
:param datasource: Name of data source
|
|
168
|
-
:return: host, port
|
|
169
|
-
:type: tuple(string, int)
|
|
170
|
-
"""
|
|
171
|
-
|
|
172
|
-
if not is_windows():
|
|
173
|
-
return None
|
|
174
|
-
import winreg
|
|
175
|
-
|
|
176
|
-
try:
|
|
177
|
-
reg_key = winreg.OpenKey(
|
|
178
|
-
winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Wow6432Node\PISystem\PI-SDK"
|
|
179
|
-
)
|
|
180
|
-
reg_key_handles = find_registry_key(reg_key, "ServerHandles")
|
|
181
|
-
reg_site_key = find_registry_key(reg_key_handles, datasource)
|
|
182
|
-
if reg_site_key is None:
|
|
183
|
-
reg_key = winreg.OpenKey(
|
|
184
|
-
winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\PISystem\PI-SDK"
|
|
185
|
-
)
|
|
186
|
-
reg_key_handles = find_registry_key(reg_key, "ServerHandles")
|
|
187
|
-
reg_site_key = find_registry_key(reg_key_handles, datasource)
|
|
188
|
-
if reg_site_key is not None:
|
|
189
|
-
host = winreg.QueryValueEx(reg_site_key, "path")[0]
|
|
190
|
-
port = int(winreg.QueryValueEx(reg_site_key, "port")[0])
|
|
191
|
-
return host, port
|
|
192
|
-
except FileNotFoundError:
|
|
193
|
-
return None
|
|
194
|
-
|
|
195
|
-
|
|
196
118
|
def get_handler(
|
|
197
119
|
imstype: Optional[IMSType],
|
|
198
120
|
datasource: str,
|
|
@@ -557,7 +479,7 @@ class IMSClient:
|
|
|
557
479
|
except ValueError:
|
|
558
480
|
start = convert_to_pydatetime(start)
|
|
559
481
|
if end is None:
|
|
560
|
-
end = datetime.
|
|
482
|
+
end = datetime.now(timezone.utc)
|
|
561
483
|
elif isinstance(end, (str, pd.Timestamp)):
|
|
562
484
|
end = convert_to_pydatetime(end)
|
|
563
485
|
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import enum
|
|
2
|
+
import hashlib
|
|
2
3
|
import logging
|
|
3
4
|
import platform
|
|
4
|
-
import
|
|
5
|
+
import ssl
|
|
5
6
|
from datetime import datetime, tzinfo
|
|
6
7
|
from enum import Enum
|
|
8
|
+
from pathlib import Path
|
|
7
9
|
from typing import Union
|
|
8
10
|
|
|
11
|
+
import certifi
|
|
9
12
|
import pandas as pd
|
|
10
13
|
import pytz
|
|
14
|
+
import requests
|
|
15
|
+
from platformdirs import user_data_dir
|
|
11
16
|
|
|
12
17
|
from tagreader.logger import logger
|
|
13
18
|
|
|
@@ -27,47 +32,11 @@ def is_linux() -> bool:
|
|
|
27
32
|
if is_windows():
|
|
28
33
|
import winreg
|
|
29
34
|
|
|
30
|
-
|
|
31
35
|
if is_mac():
|
|
32
36
|
import socket
|
|
33
37
|
import subprocess
|
|
34
38
|
|
|
35
39
|
|
|
36
|
-
def find_registry_key(base_key, search_key_name: str):
|
|
37
|
-
search_key_name = search_key_name.lower()
|
|
38
|
-
if base_key is not None:
|
|
39
|
-
num_keys, _, _ = winreg.QueryInfoKey(base_key)
|
|
40
|
-
for i in range(0, num_keys):
|
|
41
|
-
key_name = winreg.EnumKey(base_key, i)
|
|
42
|
-
if key_name.lower() == search_key_name:
|
|
43
|
-
return winreg.OpenKey(base_key, key_name)
|
|
44
|
-
else:
|
|
45
|
-
key = find_registry_key(
|
|
46
|
-
winreg.OpenKey(base_key, key_name), search_key_name
|
|
47
|
-
)
|
|
48
|
-
if key is not None:
|
|
49
|
-
return key
|
|
50
|
-
return None
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def find_registry_key_from_name(base_key, search_key_name: str):
|
|
54
|
-
search_key_name = search_key_name.lower()
|
|
55
|
-
num_keys, _, _ = winreg.QueryInfoKey(base_key)
|
|
56
|
-
key = key_string = None
|
|
57
|
-
for i in range(0, num_keys):
|
|
58
|
-
try:
|
|
59
|
-
key_string = winreg.EnumKey(base_key, i)
|
|
60
|
-
key = winreg.OpenKey(base_key, key_string)
|
|
61
|
-
_, num_vals, _ = winreg.QueryInfoKey(key)
|
|
62
|
-
if num_vals > 0:
|
|
63
|
-
(_, key_name, _) = winreg.EnumValue(key, 0)
|
|
64
|
-
if str(key_name).lower() == search_key_name:
|
|
65
|
-
break
|
|
66
|
-
except Exception as err:
|
|
67
|
-
logging.error("{}: {}".format(i, err))
|
|
68
|
-
return key, key_string
|
|
69
|
-
|
|
70
|
-
|
|
71
40
|
def convert_to_pydatetime(date_stamp: Union[datetime, str, pd.Timestamp]) -> datetime:
|
|
72
41
|
if isinstance(date_stamp, datetime):
|
|
73
42
|
return date_stamp
|
|
@@ -138,64 +107,82 @@ def add_equinor_root_certificate() -> bool:
|
|
|
138
107
|
running on Equinor host.
|
|
139
108
|
|
|
140
109
|
NB! This needs to be repeated after updating the cacert module.
|
|
141
|
-
NB! Will not download certificates.
|
|
142
110
|
|
|
143
111
|
Returns:
|
|
144
112
|
bool: True if function completes successfully
|
|
145
113
|
"""
|
|
146
|
-
|
|
147
|
-
import ssl
|
|
114
|
+
certificate = find_local_equinor_root_certificate()
|
|
148
115
|
|
|
149
|
-
|
|
116
|
+
# If certificate is not found locally, we download it from the Equinor server
|
|
117
|
+
if certificate == "":
|
|
118
|
+
logger.debug(
|
|
119
|
+
"Unable to locate Equinor Root CA certificate on this host. Downloading from Equinor server."
|
|
120
|
+
)
|
|
121
|
+
response = requests.get("http://pki.equinor.com/aia/ecpr.crt")
|
|
150
122
|
|
|
151
|
-
|
|
152
|
-
|
|
123
|
+
if response.status_code != 200:
|
|
124
|
+
logger.error(
|
|
125
|
+
"Unable to find Equinor Root CA certificate locally and on Equinor server."
|
|
126
|
+
)
|
|
127
|
+
return False
|
|
153
128
|
|
|
154
|
-
|
|
155
|
-
der = None
|
|
129
|
+
certificate = response.text.replace("\r", "")
|
|
156
130
|
|
|
157
|
-
|
|
131
|
+
# Write result to user data so we can read the cert from there next time
|
|
132
|
+
filepath = Path(user_data_dir("tagreader")) / "equinor_root_ca.crt"
|
|
133
|
+
try:
|
|
134
|
+
filepath.parent.mkdir(parents=True, exist_ok=True)
|
|
135
|
+
filepath.write_text(certificate)
|
|
136
|
+
logger.debug("Equinor Root CA certificate written to cache")
|
|
137
|
+
except Exception as e:
|
|
138
|
+
logger.debug(f"Failed to write Equinor Root CA certificate to cache: {e}")
|
|
139
|
+
|
|
140
|
+
if certificate in certifi.contents():
|
|
141
|
+
logger.debug("Equinor Root Certificate already exists in certifi store")
|
|
158
142
|
return True
|
|
159
|
-
|
|
160
|
-
|
|
143
|
+
|
|
144
|
+
ca_file = certifi.where()
|
|
145
|
+
with open(ca_file, "ab") as f:
|
|
146
|
+
f.write(bytes(certificate, "ascii"))
|
|
147
|
+
logger.debug("Equinor Root Certificate added to certifi store")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def find_local_equinor_root_certificate() -> str:
|
|
151
|
+
equinor_root_pem_hash = "5A206332CE73CED1D44C8A99C4C43B7CEE03DF5F"
|
|
152
|
+
ca_search = "Equinor Root CA"
|
|
153
|
+
|
|
154
|
+
if is_windows():
|
|
155
|
+
logger.debug("Checking for Equinor Root CA in Windows certificate store")
|
|
161
156
|
for cert in ssl.enum_certificates("CA"):
|
|
162
|
-
|
|
157
|
+
found_cert = cert[0]
|
|
163
158
|
# deepcode ignore InsecureHash: <Only hashes to compare with known hash>
|
|
164
|
-
if hashlib.sha1(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
break
|
|
159
|
+
if hashlib.sha1(found_cert).hexdigest().upper() == equinor_root_pem_hash:
|
|
160
|
+
return ssl.DER_cert_to_PEM_cert(found_cert)
|
|
161
|
+
|
|
168
162
|
elif is_mac():
|
|
163
|
+
logger.debug("Checking for Equinor Root CA in MacOS certificate store")
|
|
169
164
|
macos_ca_certs = subprocess.run(
|
|
170
165
|
["security", "find-certificate", "-a", "-c", ca_search, "-Z"],
|
|
171
166
|
stdout=subprocess.PIPE,
|
|
172
167
|
).stdout
|
|
173
168
|
|
|
174
|
-
if
|
|
169
|
+
if equinor_root_pem_hash in str(macos_ca_certs).upper():
|
|
175
170
|
c = get_macos_equinor_certificates()
|
|
176
171
|
for cert in c:
|
|
177
172
|
# deepcode ignore InsecureHash: <Only hashes to compare with known hash>
|
|
178
|
-
if hashlib.sha1(cert).hexdigest().upper() ==
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
if
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
else:
|
|
190
|
-
logger.debug("Writing certificate to certifi store.")
|
|
191
|
-
ca_file = certifi.where()
|
|
192
|
-
with open(ca_file, "ab") as f:
|
|
193
|
-
f.write(bytes(pem, "ascii"))
|
|
194
|
-
logger.debug("Completed")
|
|
195
|
-
else:
|
|
196
|
-
warnings.warn("Unable to locate root certificate on this host.")
|
|
173
|
+
if hashlib.sha1(cert).hexdigest().upper() == equinor_root_pem_hash:
|
|
174
|
+
return ssl.DER_cert_to_PEM_cert(cert)
|
|
175
|
+
|
|
176
|
+
# If the certificate is not found in the local cert store, look in the tagreader cache
|
|
177
|
+
filepath = Path(user_data_dir("tagreader")) / "equinor_root_ca.crt"
|
|
178
|
+
|
|
179
|
+
try:
|
|
180
|
+
if filepath.exists():
|
|
181
|
+
return filepath.read_text()
|
|
182
|
+
except Exception as e:
|
|
183
|
+
logger.debug(f"Failed to read Equinor Root CA certificate from cache: {e}")
|
|
197
184
|
|
|
198
|
-
return
|
|
185
|
+
return ""
|
|
199
186
|
|
|
200
187
|
|
|
201
188
|
def get_macos_equinor_certificates():
|
|
@@ -19,7 +19,7 @@ from urllib3.exceptions import InsecureRequestWarning
|
|
|
19
19
|
|
|
20
20
|
from tagreader.cache import BucketCache, SmartCache
|
|
21
21
|
from tagreader.logger import logger
|
|
22
|
-
from tagreader.utils import
|
|
22
|
+
from tagreader.utils import ReaderType, is_mac, is_windows, urljoin
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
class MD4:
|
|
@@ -72,7 +72,7 @@ def get_auth_aspen(use_internal: bool = True):
|
|
|
72
72
|
if use_internal:
|
|
73
73
|
return HTTPKerberosAuth(mutual_authentication=OPTIONAL)
|
|
74
74
|
|
|
75
|
-
from msal_bearer
|
|
75
|
+
from msal_bearer import BearerAuth
|
|
76
76
|
|
|
77
77
|
tenantID = "3aa4a235-b6e2-48d5-9195-7fcf05b459b0"
|
|
78
78
|
clientID = "7adaaa99-897f-428c-8a5f-4053db565b32"
|
|
@@ -258,10 +258,6 @@ class AspenHandlerWeb(BaseHandlerWeb):
|
|
|
258
258
|
self._max_rows = options.get("max_rows", 100000)
|
|
259
259
|
self._connection_string = "" # Used for raw SQL queries
|
|
260
260
|
|
|
261
|
-
@staticmethod
|
|
262
|
-
def generate_connection_string(host, *_, **__):
|
|
263
|
-
raise NotImplementedError
|
|
264
|
-
|
|
265
261
|
@staticmethod
|
|
266
262
|
def generate_search_query(
|
|
267
263
|
tag: Optional[str],
|
|
@@ -669,25 +665,6 @@ class AspenHandlerWeb(BaseHandlerWeb):
|
|
|
669
665
|
connection_string += f"<![CDATA[{query}]]></SQL>"
|
|
670
666
|
return connection_string
|
|
671
667
|
|
|
672
|
-
def initialize_connection_string(
|
|
673
|
-
self,
|
|
674
|
-
host: Optional[str] = None,
|
|
675
|
-
port: int = 10014,
|
|
676
|
-
connection_string: Optional[str] = None,
|
|
677
|
-
):
|
|
678
|
-
if connection_string:
|
|
679
|
-
self._connection_string = connection_string
|
|
680
|
-
else:
|
|
681
|
-
if host is None:
|
|
682
|
-
from tagreader.clients import get_server_address_aspen
|
|
683
|
-
|
|
684
|
-
host = get_server_address_aspen(self.datasource)
|
|
685
|
-
self._connection_string = (
|
|
686
|
-
f"DRIVER=AspenTech SQLPlus;HOST={host};"
|
|
687
|
-
f"PORT={port};CHARINT=N;CHARFLOAT=N;"
|
|
688
|
-
"CHARTIME=N;CONVERTERRORS=N"
|
|
689
|
-
)
|
|
690
|
-
|
|
691
668
|
def query_sql(self, query: str, parse: bool = True) -> Union[str, pd.DataFrame]:
|
|
692
669
|
url = urljoin(self.base_url, "SQL")
|
|
693
670
|
if self._connection_string is None:
|
|
@@ -712,16 +689,17 @@ class AspenHandlerWeb(BaseHandlerWeb):
|
|
|
712
689
|
parsed_dict = res.json()["data"][0]
|
|
713
690
|
|
|
714
691
|
cols = []
|
|
715
|
-
|
|
716
|
-
|
|
692
|
+
if "cols" in parsed_dict.keys():
|
|
693
|
+
for i in parsed_dict["cols"]:
|
|
694
|
+
cols.append(i["n"])
|
|
717
695
|
|
|
718
696
|
rows = []
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
697
|
+
if "rows" in parsed_dict.keys():
|
|
698
|
+
for i in parsed_dict["rows"]:
|
|
699
|
+
element = []
|
|
700
|
+
for j in i["fld"]:
|
|
701
|
+
element.append(j["v"])
|
|
702
|
+
rows.append(element)
|
|
725
703
|
return pd.DataFrame(data=rows, columns=cols)
|
|
726
704
|
return res.text
|
|
727
705
|
|
|
@@ -754,10 +732,6 @@ class PIHandlerWeb(BaseHandlerWeb):
|
|
|
754
732
|
def _time_to_UTC_string(time: datetime) -> str:
|
|
755
733
|
return time.astimezone(pytz.UTC).strftime("%d-%b-%y %H:%M:%S")
|
|
756
734
|
|
|
757
|
-
@staticmethod
|
|
758
|
-
def generate_connection_string(host, *_, **__):
|
|
759
|
-
raise NotImplementedError
|
|
760
|
-
|
|
761
735
|
@staticmethod
|
|
762
736
|
def escape(s: str) -> str:
|
|
763
737
|
# https://techsupport.osisoft.com/Documentation/PI-Web-API/help/topics/search-queries.html
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|