iplotDataAccess 1.3.1.post1__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.
Files changed (33) hide show
  1. iplotdataaccess-1.3.1.post1/MANIFEST.in +1 -0
  2. iplotdataaccess-1.3.1.post1/PKG-INFO +79 -0
  3. iplotdataaccess-1.3.1.post1/README.md +51 -0
  4. iplotdataaccess-1.3.1.post1/iplotDataAccess/__init__.py +3 -0
  5. iplotdataaccess-1.3.1.post1/iplotDataAccess/_version.py +21 -0
  6. iplotdataaccess-1.3.1.post1/iplotDataAccess/appDataAccess.py +26 -0
  7. iplotdataaccess-1.3.1.post1/iplotDataAccess/csvAccess.py +191 -0
  8. iplotdataaccess-1.3.1.post1/iplotDataAccess/dataAccess.py +195 -0
  9. iplotdataaccess-1.3.1.post1/iplotDataAccess/dataCommon.py +130 -0
  10. iplotdataaccess-1.3.1.post1/iplotDataAccess/dataHandling/exportData/__init__.py +0 -0
  11. iplotdataaccess-1.3.1.post1/iplotDataAccess/dataHandling/exportData/exportData.py +259 -0
  12. iplotdataaccess-1.3.1.post1/iplotDataAccess/dataSource.py +187 -0
  13. iplotdataaccess-1.3.1.post1/iplotDataAccess/dataSourceConfig.py +0 -0
  14. iplotdataaccess-1.3.1.post1/iplotDataAccess/data_sources.cfg +14 -0
  15. iplotdataaccess-1.3.1.post1/iplotDataAccess/imasDBMaster.py +193 -0
  16. iplotdataaccess-1.3.1.post1/iplotDataAccess/imasUtils.py +269 -0
  17. iplotdataaccess-1.3.1.post1/iplotDataAccess/imaspyAccess.py +536 -0
  18. iplotdataaccess-1.3.1.post1/iplotDataAccess/nestedDatatype.py +163 -0
  19. iplotdataaccess-1.3.1.post1/iplotDataAccess/realTimeStreamer.py +283 -0
  20. iplotdataaccess-1.3.1.post1/iplotDataAccess/tests/csv/ITER/MCTB-TEST/data_111.csv +12 -0
  21. iplotdataaccess-1.3.1.post1/iplotDataAccess/tests/test_imas.py +750 -0
  22. iplotdataaccess-1.3.1.post1/iplotDataAccess/tests/test_uda.py +92 -0
  23. iplotdataaccess-1.3.1.post1/iplotDataAccess/tests/test_zstreamer.py +137 -0
  24. iplotdataaccess-1.3.1.post1/iplotDataAccess/udaAccess.py +732 -0
  25. iplotdataaccess-1.3.1.post1/iplotDataAccess.egg-info/PKG-INFO +79 -0
  26. iplotdataaccess-1.3.1.post1/iplotDataAccess.egg-info/SOURCES.txt +32 -0
  27. iplotdataaccess-1.3.1.post1/iplotDataAccess.egg-info/dependency_links.txt +1 -0
  28. iplotdataaccess-1.3.1.post1/iplotDataAccess.egg-info/requires.txt +13 -0
  29. iplotdataaccess-1.3.1.post1/iplotDataAccess.egg-info/top_level.txt +1 -0
  30. iplotdataaccess-1.3.1.post1/pyproject.toml +62 -0
  31. iplotdataaccess-1.3.1.post1/setup.cfg +12 -0
  32. iplotdataaccess-1.3.1.post1/setup.py +7 -0
  33. iplotdataaccess-1.3.1.post1/versioneer.py +2296 -0
@@ -0,0 +1 @@
1
+ include iplotDataAccess/tests/csv/ITER/MCTB-TEST/data_111.csv
@@ -0,0 +1,79 @@
1
+ Metadata-Version: 2.4
2
+ Name: iplotDataAccess
3
+ Version: 1.3.1.post1
4
+ Summary: Data access for applications using IDSs or CBS
5
+ Author-email: ITER Organization <jscabanilla@minsait.com>
6
+ Maintainer-email: Jhon Steeven Cabanilla Alvarado <jscabanilla@minsait.com>, Pablo Martin Villares <pmartin@minsait.com>, Mario Gomez Ballesteros <mgomezb@minsait.com>
7
+ License: MIT
8
+ Project-URL: Homepage, https://github.com/iplot-viz/iplotdataaccess
9
+ Project-URL: Repository, https://github.com/iplot-viz/iplotdataaccess.git
10
+ Project-URL: Issues, https://github.com/iplot-viz/iplotdataaccess/issues
11
+ Keywords: IDS,CBS,Interface Data Structures,Control Breakdown Structure
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Requires-Python: >=3.9
16
+ Description-Content-Type: text/markdown
17
+ Requires-Dist: cachetools>=4.2.0
18
+ Requires-Dist: iplotLogging>=1.2.0
19
+ Requires-Dist: requests>=2.25.1
20
+ Requires-Dist: sseclient-py>=1.7
21
+ Requires-Dist: pyarrow>=16.1.0
22
+ Requires-Dist: h5py>=3.11.0
23
+ Requires-Dist: pandas>=1.2.0
24
+ Provides-Extra: imaspy
25
+ Requires-Dist: PyYAML>=6.0.1; extra == "imaspy"
26
+ Provides-Extra: test
27
+ Requires-Dist: pytest>=6.0; extra == "test"
28
+
29
+ # Data Access Library
30
+
31
+ Data access for applications represented using Interface Data Structures (IDSs) or Control Breakdown Structure (CBS).
32
+
33
+ ## Requirements
34
+
35
+ 1. **python <= 3.11**
36
+ 2. **Dependencies**: Managed
37
+ via [pyproject.toml](https://github.com/iplot-viz/iplotdataaccess/blob/develop/pyproject.toml).
38
+
39
+ ## Installation
40
+
41
+ Install the package from PyPi:
42
+
43
+ ```bash
44
+ pip install iplotDataAccess
45
+ ```
46
+
47
+ ### Installation with optional dependencies
48
+
49
+ You can also install optional dependencies (e.g. imaspy and test):
50
+
51
+ ```bash
52
+ pip install "iplotDataAccess[imaspy, test]"
53
+ ```
54
+
55
+ #### List of optional dependencies
56
+
57
+ - ```imaspy```: installs required packages.
58
+ - ```test```: installs required packages to run the tests with pytest.
59
+
60
+ ### Usage Example
61
+
62
+ ```bash
63
+ from iplotDataAccess.dataAccess import DataAccess
64
+
65
+ # Create a DataAccess object
66
+ da = DataAccess()
67
+
68
+ # Example: load data from CODAC UDA source
69
+ data_obj = da.get_data("codacuda", varname="IC_ICH1_FAFB_MEAS/phase", tsS="2018-11-21T09:30:00", tsE="2018-11-21T09:45:00", nbp=-1)
70
+ print(data_obj.xdata, data_obj.ydata)
71
+ ```
72
+
73
+ ### Contributing
74
+
75
+ 1. Fork it!
76
+ 2. Create your feature branch: ```git checkout -b my-new-feature ```
77
+ 3. Commit your changes: ```git commit -am 'Add some feature' ```
78
+ 4. Push to the branch:```git push origin my-new-feature ```
79
+ 5. Submit a pull request
@@ -0,0 +1,51 @@
1
+ # Data Access Library
2
+
3
+ Data access for applications represented using Interface Data Structures (IDSs) or Control Breakdown Structure (CBS).
4
+
5
+ ## Requirements
6
+
7
+ 1. **python <= 3.11**
8
+ 2. **Dependencies**: Managed
9
+ via [pyproject.toml](https://github.com/iplot-viz/iplotdataaccess/blob/develop/pyproject.toml).
10
+
11
+ ## Installation
12
+
13
+ Install the package from PyPi:
14
+
15
+ ```bash
16
+ pip install iplotDataAccess
17
+ ```
18
+
19
+ ### Installation with optional dependencies
20
+
21
+ You can also install optional dependencies (e.g. imaspy and test):
22
+
23
+ ```bash
24
+ pip install "iplotDataAccess[imaspy, test]"
25
+ ```
26
+
27
+ #### List of optional dependencies
28
+
29
+ - ```imaspy```: installs required packages.
30
+ - ```test```: installs required packages to run the tests with pytest.
31
+
32
+ ### Usage Example
33
+
34
+ ```bash
35
+ from iplotDataAccess.dataAccess import DataAccess
36
+
37
+ # Create a DataAccess object
38
+ da = DataAccess()
39
+
40
+ # Example: load data from CODAC UDA source
41
+ data_obj = da.get_data("codacuda", varname="IC_ICH1_FAFB_MEAS/phase", tsS="2018-11-21T09:30:00", tsE="2018-11-21T09:45:00", nbp=-1)
42
+ print(data_obj.xdata, data_obj.ydata)
43
+ ```
44
+
45
+ ### Contributing
46
+
47
+ 1. Fork it!
48
+ 2. Create your feature branch: ```git checkout -b my-new-feature ```
49
+ 3. Commit your changes: ```git commit -am 'Add some feature' ```
50
+ 4. Push to the branch:```git push origin my-new-feature ```
51
+ 5. Submit a pull request
@@ -0,0 +1,3 @@
1
+ from . import _version
2
+
3
+ __version__ = _version.get_versions()['version']
@@ -0,0 +1,21 @@
1
+
2
+ # This file was generated by 'versioneer.py' (0.29) from
3
+ # revision-control system data, or from the parent directory name of an
4
+ # unpacked source archive. Distribution tarballs contain a pre-generated copy
5
+ # of this file.
6
+
7
+ import json
8
+
9
+ version_json = '''
10
+ {
11
+ "date": "2025-12-19T09:53:38+0000",
12
+ "dirty": false,
13
+ "error": null,
14
+ "full-revisionid": "afb231aec5bbd77563b4857880ccc3c9e6ef3a33",
15
+ "version": "1.3.1.post1"
16
+ }
17
+ ''' # END VERSION_JSON
18
+
19
+
20
+ def get_versions():
21
+ return json.loads(version_json)
@@ -0,0 +1,26 @@
1
+ from iplotDataAccess.dataAccess import DataAccess
2
+ from iplotLogging import setupLogger
3
+
4
+ logger = setupLogger.get_logger(__name__)
5
+
6
+
7
+ class AppDataAccess:
8
+ da: DataAccess = None
9
+ configured: bool = False
10
+
11
+ # ---------------
12
+
13
+ @staticmethod
14
+ def initialize(config_file=None):
15
+ if AppDataAccess.da is None:
16
+ AppDataAccess.da = DataAccess()
17
+ AppDataAccess.configured = AppDataAccess.da.load_config(config_file)
18
+ return AppDataAccess.configured
19
+
20
+ @staticmethod
21
+ def get_data_access():
22
+ return AppDataAccess.da
23
+
24
+ @staticmethod
25
+ def is_configured():
26
+ return AppDataAccess.configured
@@ -0,0 +1,191 @@
1
+ import os
2
+ import re
3
+ import pandas as pd
4
+ from pandas import DataFrame
5
+
6
+ from iplotDataAccess import dataCommon
7
+ from iplotDataAccess.dataSource import DataSource
8
+ import iplotLogging.setupLogger as setupLog
9
+
10
+ logger = setupLog.get_logger(__name__)
11
+
12
+
13
+ class CsvAccess(DataSource):
14
+ source_type = "CSV"
15
+
16
+ def __init__(self, name: str, config: dict):
17
+ super().__init__(name, config)
18
+
19
+ self.folder_path = config.get("path", "") # Store the folder path for accessing CSV files
20
+ self.def_pulse_location = self.folder_path.split('/')[-1]
21
+
22
+ def connect(self) -> bool:
23
+ self.connected = os.path.isdir(self.folder_path)
24
+ return self.connected
25
+
26
+ # Method to get data from a CSV file based on the 'pulse' and 'varname' arguments in kwargs
27
+ def get_data(self, **kwargs):
28
+
29
+ # Create a DataObj instance for storing the data
30
+ data_obj = dataCommon.DataObj()
31
+
32
+ # Transform the 'pulse' argument to a valid file path
33
+ path = self.transform_pulse_to_file_path(kwargs.get("pulse"))
34
+
35
+ # Read the CSV file into a pandas DataFrame
36
+ data = pd.read_csv(path)
37
+
38
+ if kwargs.get("tsS") is not None:
39
+ data = data[data['Time'] >= kwargs.get("tsS")]
40
+ if kwargs.get("tsE") is not None:
41
+ data = data[data['Time'] <= kwargs.get("tsE")]
42
+
43
+ sub_data = data.filter(like=kwargs.get("varname"))
44
+
45
+ # If multiple columns match 'varname', return an error
46
+ if len(sub_data.columns) > 1:
47
+ data_obj.set_err(-1, "Multiple columns with same variable name")
48
+ return data_obj
49
+
50
+ if len(sub_data.columns) == 0:
51
+ data_obj.set_err(-1, f"No columns found matching variable name '{kwargs.get('varname')}'")
52
+ return data_obj
53
+
54
+ # Set the data for the variable in the DataObj
55
+ data_obj.set_data(sub_data.iloc[:, 0].values, 2)
56
+ logger.debug(" found data_obj %s ", data_obj)
57
+
58
+ # Extract the unit from the variable's column name (if present)
59
+ yunit = re.findall(r' \((.*?)\)', sub_data.columns[0])
60
+ if yunit:
61
+ data_obj.yunit = yunit[0]
62
+
63
+ # Set the 'Time' column data in the DataObj and define xunit as "seconds"
64
+ data_obj.set_data(data["Time"].values, 1)
65
+ data_obj.xunit = "s"
66
+
67
+ return data_obj # Return the populated DataObj
68
+
69
+ # Method to get all pulses (files) in the folder matching a pattern as a list
70
+ def get_pulses_df(self, pattern='.*') -> DataFrame:
71
+ all_pulses = []
72
+ pstatus = "completed"
73
+
74
+ # Walk through all files and folders in the directory
75
+ for folder, _, files in os.walk(self.folder_path):
76
+ relative_folder = os.path.relpath(folder, self.folder_path).replace(os.sep, "/")
77
+ # For each file, create a key with the folder and cleaned file name
78
+ for file in files:
79
+ if not file.endswith(".csv"):
80
+ continue
81
+ value = f"{self.def_pulse_location}:{relative_folder}/{file.replace('.csv', '').replace('data_', '')};{pstatus}"
82
+ logger.debug(" found value and file %s %s", value, file)
83
+ if re.match(pattern, value):
84
+ all_pulses.append(value)
85
+
86
+ # Return the pulses
87
+ logger.debug(" pulses %s", all_pulses)
88
+ return pd.DataFrame([value.split(";") for value in all_pulses], columns=["pulseId", "Status"])
89
+
90
+ def get_cbs_list(self, pattern=".*"):
91
+ var_list = self.get_var_list(pattern)
92
+ cbs = set()
93
+
94
+ for v in var_list:
95
+ cbs.add(v + "?V")
96
+
97
+ logger.debug(" cbs list %s", cbs)
98
+ return cbs
99
+
100
+ def get_cbs_dictX(self, pattern='.*') -> dict:
101
+ cbs_list = self.get_cbs_list(pattern)
102
+ cbs_dict = dict()
103
+ for line in cbs_list:
104
+ cur_dict = cbs_dict
105
+ cbs = line.split(":")
106
+ for counter, s in enumerate(cbs):
107
+ data = '-'.join(cbs[0:counter])
108
+ if data not in cur_dict:
109
+ cur_dict = cur_dict.setdefault(data, {})
110
+ cur_dict = cur_dict.setdefault(line.replace('?V'), '')
111
+ logger.debug(" cbs list %s", cbs_dict)
112
+ return cbs_dict
113
+
114
+ def get_cbs_dict(self, pattern='.*') -> dict:
115
+ cbs_list = self.get_cbs_list(pattern)
116
+ cbs_dict = dict()
117
+ for line in cbs_list:
118
+ cur_dict = cbs_dict
119
+ list_line = line.split('-')
120
+ for var in list_line:
121
+ if var.endswith('?V'):
122
+ cur_dict = cur_dict.setdefault('-'.join(list_line).replace('?V', ''), '')
123
+ else:
124
+ cur_dict = cur_dict.setdefault(var, {})
125
+
126
+ return cbs_dict
127
+
128
+ def get_pulse_info(self, pulse, run):
129
+ all_pulses = self.get_pulses_df()
130
+ return None
131
+
132
+ def get_var_dict(self, pattern='.*'):
133
+ return self.get_cbs_dict(pattern)
134
+
135
+ def is_connected(self):
136
+ return self.connected is not None
137
+
138
+ def search_pulses_df(self, text) -> DataFrame:
139
+ return self.get_pulses_df(pattern=text)
140
+
141
+ # Method to get a list of all unique variables from CSV files in the folder
142
+ def get_var_list(self, patt='.*'):
143
+ all_variables = set() # Use a set to store unique variables
144
+
145
+ # Walk through all files and folders in the directory
146
+ for folder, _, files in os.walk(self.folder_path):
147
+ for file in files:
148
+ if not file.endswith(".csv"):
149
+ continue
150
+ file_path = os.path.join(folder, file) # Get the full file path
151
+ try:
152
+ # Open each file and read the header line to extract variables
153
+ with open(file_path, 'r') as f:
154
+ x = f.readline().split(",")[1:] # Skip the first column (Time)
155
+ all_variables = all_variables.union([i.split(" ")[0] for i in x])
156
+
157
+ except Exception as e:
158
+ logger.error("Could not open file %s for %s ", file_path, e) # Handle file reading errors
159
+ logger.debug(" all variable %s", all_variables)
160
+
161
+ # Filter variables matching the given pattern
162
+ filtered_vars = []
163
+ for variable in all_variables:
164
+ s = re.match(patt, variable.strip())
165
+
166
+ if s is not None:
167
+ filtered_vars.append(s[0])
168
+
169
+ logger.debug(" variable %s and pattern %s ", filtered_vars, patt)
170
+
171
+ return filtered_vars # Return the filtered list of variables
172
+
173
+ # Method to transform a pulse string into a file path
174
+ # Example: input 'ITER/COMM:111' into
175
+ # '{user_path}\\iplotdataaccess\\iplotDataAccess\\ITER\\COMM\\data_111.csv'
176
+ def transform_pulse_to_file_path(self, pulse):
177
+ # Split the pulse into folder and file
178
+ folders, file = pulse.split(":", 1)
179
+ f1, f2 = file.split("/", 1)
180
+ print(f" variable {pulse} ")
181
+ # Replace slashes with the OS-specific separator
182
+ folders = folders.replace("/", os.sep)
183
+ # Join all parts
184
+ result = f"{self.folder_path}{os.sep}{f1}{os.sep}data_{f2}.csv"
185
+ return result # Return the constructed file path
186
+
187
+ def clear_cache(self):
188
+ pass
189
+
190
+ def get_envelope(self):
191
+ pass
@@ -0,0 +1,195 @@
1
+ import json
2
+ import os
3
+ import importlib.util
4
+ import importlib.resources as pkg_resources
5
+ from typing import Dict, List, Union, Type
6
+
7
+ from iplotDataAccess.dataSource import DataSource
8
+ from iplotLogging import setupLogger
9
+ from iplotDataAccess.dataCommon import DataObj, DataEnvelope
10
+
11
+ logger = setupLogger.get_logger(__name__)
12
+
13
+
14
+ # class to interface with data source - here UDA
15
+ class DataAccess:
16
+ DEFAULT_DATA_SOURCES_CFG_FILE: str = 'mydatasources.cfg'
17
+
18
+ def __init__(self):
19
+ self.proto: Dict[str, Type[DataSource]] = self.get_supported_data_source()
20
+ self.ds_list: Dict[str, DataSource] = {}
21
+ self.default_ds: Union[DataSource, None] = None
22
+
23
+ @staticmethod
24
+ def get_supported_data_source() -> Dict[str, Type[DataSource]]:
25
+ supported_data_sources = {}
26
+
27
+ try:
28
+ file_path = str(pkg_resources.files('iplotDataAccess').joinpath('data_sources.cfg'))
29
+ with open(file_path, 'r') as file:
30
+
31
+ data_sources = json.load(file)
32
+ for key, value in data_sources.items():
33
+ try:
34
+ module = importlib.import_module(value['pymodule'])
35
+ imported_class = getattr(module, value['class'])
36
+ supported_data_sources[imported_class.source_type] = imported_class
37
+ except Exception as e:
38
+ logger.error(f"Error loading DataSource {key} -> {e}")
39
+
40
+ except Exception as e:
41
+ logger.error(f"Error loading DataSources config file ->{e}")
42
+
43
+ return supported_data_sources
44
+
45
+ def get_default_ds_name(self):
46
+ if self.default_ds is None:
47
+ return None
48
+ else:
49
+ return self.default_ds.name
50
+
51
+ def load_config(self, conf_file=None) -> bool:
52
+ conf_files = [conf_file, os.environ.get('IPLOT_SOURCES_CONFIG'), self.DEFAULT_DATA_SOURCES_CFG_FILE]
53
+ # Remove None values
54
+ conf_files = list(filter(None, conf_files))
55
+ for ix, file in enumerate(conf_files):
56
+ try:
57
+ return self.load_config_file(file)
58
+ except (OSError, IOError, FileNotFoundError) as _:
59
+ if ix < len(conf_files) - 1:
60
+ logger.warning(f"Error loading {file} data source file, fallback to {conf_files[ix + 1]}")
61
+ return False
62
+
63
+ def load_config_file(self, dspath: str) -> bool:
64
+ with open(dspath) as f:
65
+ try:
66
+ config = json.load(f)
67
+ except Exception as e:
68
+ logger.warning(f"Wrong json format in {dspath} -> {e}")
69
+ return False
70
+ for ds_name, ds_config in config.items():
71
+ ds_type = ds_config.get("type")
72
+ ds_class = self.proto.get(ds_type)
73
+ if not ds_class:
74
+ logger.warning(f"DataSource '{ds_name}' has an unsupported data source type-> {ds_type}")
75
+ continue
76
+ try:
77
+ data_source = ds_class(ds_name, ds_config)
78
+ if data_source.connect():
79
+ self.ds_list[ds_name] = data_source
80
+ except Exception as e:
81
+ logger.warning(f"Error importing class {ds_class} with error {e}")
82
+
83
+ if self.ds_list:
84
+ # Check which data source to set by default
85
+ # Set first DataSource that has default=true if no one has it, set the first one
86
+ self.default_ds = next(
87
+ (ds for ds in self.ds_list.values() if ds.default),
88
+ list(self.ds_list.values())[0])
89
+ return True
90
+
91
+ return False
92
+
93
+ def get_data_source(self, data_s_name):
94
+ logger.debug("entering getDataSource %s", data_s_name)
95
+ if data_s_name is None:
96
+ if self.default_ds is not None:
97
+ logger.info("default source used ")
98
+ return self.default_ds
99
+ else:
100
+ logger.error("DataSourceName is None and not default data source name has been defined")
101
+ return None
102
+ if data_s_name not in self.ds_list.keys():
103
+ logger.warning(" Data source %s not found", data_s_name)
104
+ return None
105
+ else:
106
+ ds = self.ds_list[data_s_name]
107
+ if ds is None:
108
+ logger.debug("Invalid data source pointer for ds name %s", data_s_name)
109
+ return ds
110
+
111
+ def get_data(self, data_s_name, **kwargs):
112
+ # we can use the var prefix to get the data source while we introduce
113
+ logger.debug("entering getdata %s", data_s_name)
114
+ if data_s_name is not None and data_s_name in self.ds_list.keys():
115
+ if self.ds_list[data_s_name] is None:
116
+ dobj = DataObj()
117
+
118
+ dobj.set_empty("Invalid data source pointer for ds name " + data_s_name)
119
+ logger.debug("Invalid data source pointer for ds name %s", data_s_name)
120
+ return dobj
121
+ else:
122
+
123
+ dobj = self.ds_list[data_s_name].get_data(**kwargs)
124
+ return dobj
125
+ else:
126
+ if data_s_name not in self.ds_list.keys():
127
+ logger.warning(" Invalid data source found %s ", data_s_name)
128
+ dobj = DataObj()
129
+ dobj.set_empty(f"Invalid data source name {data_s_name}")
130
+
131
+ return dobj
132
+ if self.default_ds is not None:
133
+ logger.info(" default source used ")
134
+ return self.default_ds.get_data(**kwargs)
135
+
136
+ return None
137
+
138
+ def start_subscription(self, data_s_name, **kwargs):
139
+ if data_s_name is not None and data_s_name in self.ds_list.keys():
140
+ self.ds_list[data_s_name].start_subscription(**kwargs)
141
+
142
+ def stop_subscription(self, data_s_name):
143
+ if data_s_name is not None and data_s_name in self.ds_list.keys():
144
+ logger.debug("stopSubscription A ")
145
+ self.ds_list[data_s_name].stop_subscription()
146
+
147
+ def get_next_data(self, data_s_name, vname):
148
+ if data_s_name is not None and data_s_name in self.ds_list.keys():
149
+ return self.ds_list[data_s_name].get_next_data(vname)
150
+ else:
151
+ dobj = DataObj()
152
+ dobj.set_empty(f"Invalid data source name {data_s_name}")
153
+ return dobj
154
+
155
+ def get_envelope(self, data_s_name, **kwargs):
156
+ if data_s_name is not None and data_s_name in self.ds_list.keys():
157
+ if self.ds_list[data_s_name] is None:
158
+ denv = DataEnvelope()
159
+ denv.set_empty(f"Invalid data source pointer for ds name {data_s_name}")
160
+
161
+ return denv
162
+ else:
163
+ return self.ds_list[data_s_name].get_envelope(**kwargs)
164
+ else:
165
+ if data_s_name not in self.ds_list.keys():
166
+ logger.warning(f"Invalid data source found {data_s_name}")
167
+ denv = DataEnvelope()
168
+ denv.set_empty(f"Invalid data source name {data_s_name}")
169
+
170
+ return denv
171
+ if self.default_ds is not None:
172
+ logger.info("default source used ")
173
+ return self.default_ds.get_envelope(**kwargs)
174
+
175
+ return None
176
+
177
+ def get_connected_data_source_names(self) -> List[str]:
178
+ data_sources = [self.get_default_ds_name()]
179
+ for ds_name, ds in self.ds_list.items():
180
+ if ds_name not in data_sources and ds.is_connected():
181
+ data_sources.append(ds_name)
182
+ return data_sources
183
+
184
+ def get_connected_data_sources(self) -> List[DataSource]:
185
+ data_sources = []
186
+ for ds_name, ds in self.ds_list.items():
187
+ if ds.is_connected():
188
+ data_sources.append(ds)
189
+ return data_sources
190
+
191
+ # Clear cache of all the dataSources
192
+ def clear_cache(self):
193
+ for ds in self.ds_list.values():
194
+ if ds.is_connected():
195
+ ds.clear_cache()