pytrms 0.9.0__py3-none-any.whl → 0.9.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.
@@ -7,19 +7,10 @@ import numpy as np
7
7
  import pandas as pd
8
8
 
9
9
  from .._base import itype
10
+ from ..helpers import convert_labview_to_posix
10
11
 
11
12
  __all__ = ['IoniTOFReader', 'GroupNotFoundError']
12
13
 
13
- def convert_labview_to_posix(lv_time_utc, utc_offset_sec):
14
- '''Create a `pandas.Timestamp` from LabView time.'''
15
- # change epoch from 01.01.1904 to 01.01.1970:
16
- posix_time = lv_time_utc - 2082844800
17
- # the tz must be specified in isoformat like '+02:30'..
18
- tz_sec = int(utc_offset_sec)
19
- tz_designator = '{0}{1:02d}:{2:02d}'.format(
20
- '+' if tz_sec >= 0 else '-', tz_sec // 3600, tz_sec % 3600 // 60)
21
-
22
- return pd.Timestamp(posix_time, unit='s', tz=tz_designator)
23
14
 
24
15
  class GroupNotFoundError(KeyError):
25
16
  pass
@@ -79,7 +70,7 @@ class IoniTOFReader:
79
70
  # well it didn't work..
80
71
  pass
81
72
  finally:
82
- self.hf = h5py.File(path, 'r', swmr=True)
73
+ self.hf = h5py.File(path, 'r', swmr=False)
83
74
 
84
75
  @property
85
76
  def number_of_timebins(self):
@@ -106,7 +97,7 @@ class IoniTOFReader:
106
97
  return float(self.hf.attrs.get('Single Spec Duration (ms)'))
107
98
 
108
99
  def __init__(self, path):
109
- self.hf = h5py.File(path, 'r', swmr=True)
100
+ self.hf = h5py.File(path, 'r', swmr=False)
110
101
  self.filename = os.path.abspath(self.hf.filename)
111
102
 
112
103
  table_locs = {
@@ -448,14 +439,14 @@ class IoniTOFReader:
448
439
  raise ValueError(msg) from exc
449
440
 
450
441
  try:
451
- data = self._read_datainfo(tracedata, prefix=prefix) # may raise KeyError
452
- pt = self._read_datainfo(tracedata, prefix='PeakTable') # may raise KeyError
453
- labels = [b.decode('latin1') for b in pt['label']]
442
+ data = self._read_datainfo(tracedata, prefix=prefix)
443
+ pt = self._read_datainfo(tracedata, prefix='PeakTable')
454
444
  except KeyError as exc:
455
445
  raise KeyError(f'unknown group {exc}. filetype is not supported yet.') from exc
456
446
 
447
+ labels = [b.decode('latin1') for b in pt['label']]
457
448
  mapper = dict(zip(data.columns, labels))
458
- data.rename(columns=mapper)
449
+ data.rename(columns=mapper, inplace=True)
459
450
  data.index = list(self.iter_index(index))
460
451
 
461
452
  return data
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pytrms
3
- Version: 0.9.0
3
+ Version: 0.9.2
4
4
  Summary: Python bundle for proton-transfer reaction mass-spectrometry (PTR-MS).
5
5
  License: GPL-2.0
6
6
  Author: Moritz Koenemann
@@ -13,7 +13,7 @@ Classifier: Programming Language :: Python :: 3.11
13
13
  Classifier: Programming Language :: Python :: 3.12
14
14
  Requires-Dist: h5py (>=3.12.1,<4.0.0)
15
15
  Requires-Dist: matplotlib (>=3.9.2,<4.0.0)
16
- Requires-Dist: paho-mqtt (>=1.6.1,<1.7.0)
16
+ Requires-Dist: paho-mqtt (>=1.6.1,<3.0)
17
17
  Requires-Dist: pandas (>=2.2.3,<3.0.0)
18
- Requires-Dist: pyModbusTCP (>=0.3.0,<0.4.0)
18
+ Requires-Dist: pyModbusTCP (>=0.1.9)
19
19
  Requires-Dist: requests (>=2.32.3,<3.0.0)
@@ -0,0 +1,27 @@
1
+ pytrms/__init__.py,sha256=e7CfZbs2LclwfPeL74K17jqM4xgnc6q4xLsJ9meXlgw,973
2
+ pytrms/_base/__init__.py,sha256=GBALqAy1kUPMc0CWnWRmn_Cg_HGKGCeE-V2rdZEmN8A,836
3
+ pytrms/_base/ioniclient.py,sha256=f4xWW3PL0p1gP7pczBaDEva3WUUb_J73n7yTIY5pW4s,826
4
+ pytrms/_base/mqttclient.py,sha256=taO-pHfsqTDTiTV306ehY6G5fpq86RA5YaJZrRrHbsc,5157
5
+ pytrms/_version.py,sha256=fXD69jGmDocNcc-uipbNIhjWxC7EuyhPUQ6BrBvK0Wg,854
6
+ pytrms/clients/__init__.py,sha256=7q-mRjyf0xH01ohU1pPVvMgm2Gj4Q0eWf3BT50CewhM,1415
7
+ pytrms/clients/db_api.py,sha256=pfMmfOlDCRmPCpsawwf-Rh4rix2sp3gHTF-afhXXMAQ,7556
8
+ pytrms/clients/ioniclient.py,sha256=pHGzaNtKuVrjZ2O2nTmZYJObNMDkxNwLcNerlFit1vA,2477
9
+ pytrms/clients/modbus.py,sha256=BbluHm8iYQY4V2Wbxd4eSiWJtIORyxRWRjhJo1_1JzQ,20585
10
+ pytrms/clients/mqtt.py,sha256=BJ4CESjR4DTrggL3gWCr54c9Xm1_SSZ_FCKWduPPgEk,31217
11
+ pytrms/clients/ssevent.py,sha256=zYS7MM4pkplC8wAj9ZY2Cb3fG3e-HtkyLt1qOosx-bk,3028
12
+ pytrms/compose/__init__.py,sha256=gRkwGezTsuiMLA4jr5hhQsY7XX_FonBeWcvDfDuMFnY,30
13
+ pytrms/compose/composition.py,sha256=hlV8g6n6HaLLLKileSh7sk8EtwPvaIQjOFXCEKLDKJ0,12161
14
+ pytrms/data/IoniTofPrefs.ini,sha256=e1nU7Hyuh0efpgfN-G5-ERAFC6ZeehUiotsM4VtZIT0,1999
15
+ pytrms/data/ParaIDs.csv,sha256=eWQxmHFfeTSxFfMcFpqloiZWDyK4VLZaV9zCnzLHNYs,27996
16
+ pytrms/helpers.py,sha256=QfJgdQ6b0RcyiAuNHbDGGfuRfeZIs20dPF5flZVR12o,4979
17
+ pytrms/instrument.py,sha256=BVQLFac5466PgppipmaBDInakau6lxztijQgQueDz1U,4289
18
+ pytrms/measurement.py,sha256=yjTAskKKwXPzkW3I-Y4GawJxmJpLNxA7GQB8j1RDb8g,5445
19
+ pytrms/peaktable.py,sha256=Ms0_dyHg8e6Oc8mwSTk8rD70EPjosX92pDXstf4f6Tk,17572
20
+ pytrms/plotting/__init__.py,sha256=vp5mAa3npo4kP5wvXXNDxKnryFK693P4PSwC-BxEUH8,66
21
+ pytrms/plotting/plotting.py,sha256=t7RXUhjOwXchtYXVgb0rJPD_9aVMDCT6DCAGu8TZQfE,702
22
+ pytrms/readers/__init__.py,sha256=F1ZX4Jv7NcY8nuSWnIbwjYnNFC2JsTnEp0BnfBHUMSM,76
23
+ pytrms/readers/ionitof_reader.py,sha256=9O_enLxLYWgwRlpjZdK8wcX5rMCzt2B6PkgHJM9bPzw,18096
24
+ pytrms-0.9.2.dist-info/LICENSE,sha256=GJsa-V1mEVHgVM6hDJGz11Tk3k0_7PsHTB-ylHb3Fns,18431
25
+ pytrms-0.9.2.dist-info/METADATA,sha256=tPXvyFroNSiZAKAFwA4Krsd_I2o7BGvA1pH7iouqszM,761
26
+ pytrms-0.9.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
27
+ pytrms-0.9.2.dist-info/RECORD,,
@@ -1,169 +0,0 @@
1
- import os
2
- import json
3
- from contextlib import contextmanager
4
- import logging
5
-
6
- import requests
7
-
8
- from . import ionitof_url
9
-
10
- log = logging.getLogger()
11
-
12
-
13
- class Template:
14
- '''A template for uploading a collection item.
15
-
16
- >>> t = Template()
17
- >>> t.template == Template.default_template
18
- True
19
-
20
- >>> t.render('AME_FooNumber', 42)
21
- '{"template": {"data": [{"name": "ParaID", "value": "AME_FooNumber", "prompt": "the
22
- parameter ID"}, {"name": "ValAsString", "value": "42", "prompt": "the new value"}]}}'
23
-
24
- '''
25
-
26
- default_template = {
27
- "data": [
28
- {"name": "ParaID", "value": "AME_RunNumber", "prompt": "the parameter ID"},
29
- {"name": "ValAsString", "value": "5.000000", "prompt": "the new value"},
30
- {"name": "DataType", "value": "", "prompt": "datatype (int, float, string)"},
31
- ]
32
- }
33
-
34
- @staticmethod
35
- def download(url=ionitof_url, endpoint='/api/schedule'):
36
- r = requests.get(url + endpoint, headers={'accept': 'application/vnd.collection+json'})
37
- r.raise_for_status()
38
- j = r.json()
39
-
40
- return Template(j["collection"]["template"])
41
-
42
- def __init__(self, template=None):
43
- if template is None:
44
- template = Template.default_template
45
- self.template = dict(template)
46
-
47
- self._inserts = dict() # cache data-items sorted by semantic meaning..
48
- for insert in self.template["data"]:
49
- if 'ID' in insert["name"] or 'parameter' in insert["prompt"]:
50
- self._inserts["parID"] = insert
51
- if 'set' in insert["name"].lower() or 'value' in insert["prompt"]:
52
- self._inserts["value"] = insert
53
- if 'typ' in insert["name"].lower() or 'datatype' in insert["prompt"]:
54
- self._inserts["dtype"] = insert
55
-
56
- assert len(self._inserts) == 3, "missing or unknown name in template"
57
-
58
- def render(self, parID, value):
59
- """Prepare a request for uploading."""
60
- dtype = 'float'
61
- if isinstance(value, int): dtype = 'int'
62
- if isinstance(value, str): dtype = 'string'
63
-
64
- parID_insert = dict(self._inserts["parID"])
65
- value_insert = dict(self._inserts["value"])
66
- dtype_insert = dict(self._inserts["dtype"])
67
-
68
- parID_insert.update(value=str(parID)),
69
- value_insert.update(value=str(value)),
70
- dtype_insert.update(value=str(dtype)),
71
-
72
- return json.dumps({
73
- "template": dict(data=[
74
- parID_insert,
75
- value_insert,
76
- dtype_insert,
77
- ])}
78
- )
79
-
80
- def render_many(self, new_values):
81
- for key, value in new_values.items():
82
- yield self.render(key, value)
83
-
84
-
85
- class Dirigent:
86
-
87
- def __init__(self, url=ionitof_url, template=None):
88
- if template is None:
89
- template = Template.download(url)
90
-
91
- self.url = url
92
- self.template = template
93
- self._session = None # TODO :: ?
94
-
95
- def push(self, parID, new_value, future_cycle):
96
- uri = self.url + '/api/schedule/' + str(int(future_cycle))
97
- payload = self.template.render(parID, new_value)
98
- r = self._make_request('PUT', uri, payload=payload)
99
-
100
- return r.status_code
101
-
102
- def push_filename(self, path, future_cycle):
103
- return self.push('ACQ_SRV_SetFullStorageFile', path.replace('/', '\\'), future_cycle - 2)
104
-
105
- def find_scheduled(self, parID):
106
- uri = self.url + '/api/schedule/search'
107
- r = self._make_request('GET', uri, params={'name': str(parID)})
108
- j = r.json()
109
-
110
- return [item['href'].split('/')[-1] for item in j['collection']['items']]
111
-
112
- def _make_request(self, method, uri, params=None, payload=None):
113
- if self._session is None:
114
- session = requests # not using a session..
115
- else:
116
- session = self._session
117
-
118
- try:
119
- r = session.request(method, uri, params=params, data=payload, headers={
120
- 'content-type': 'application/vnd.collection+json'
121
- })
122
- except requests.exceptions.ConnectionError as exc:
123
- # Note: the LabVIEW-webservice seems to implement a weird HTTP:
124
- # we may get a [85] Custom Error (Bad status line) from urllib3
125
- # even though we just mis-spelled a parameter-ID ?!
126
- log.error(exc)
127
- raise KeyError(str(params, payload))
128
-
129
- log.debug(f"request to <{uri}> returned [{r.status_code}]")
130
- if not r.ok and payload:
131
- log.error(payload)
132
- r.raise_for_status()
133
-
134
- return r
135
-
136
- def wait_until(self, future_cycle):
137
- if self._session is None:
138
- session = requests # not using a session..
139
- else:
140
- session = self._session
141
-
142
- r = session.get(self.url + '/api/timing/' + str(int(future_cycle)))
143
- # ...
144
- if r.status_code == 410:
145
- # 410 Client Error: Gone
146
- log.warning("we're late, better return immediately!")
147
- return r.status_code
148
-
149
- r.raise_for_status()
150
- log.debug(f"waited until {r.json()['TimeCycle']}")
151
-
152
- return r.status_code
153
-
154
- @contextmanager
155
- def open_session(self):
156
- '''open a session for faster upload (to be used as a contextmanager).'''
157
- if self._session is None:
158
- self._session = requests.Session()
159
- try:
160
- yield self
161
- finally:
162
- self._session.close()
163
- self._session = None
164
-
165
-
166
- if __name__ == '__main__':
167
- import doctest
168
- doctest.testmod(verbose=True, optionflags=doctest.ELLIPSIS)
169
-
pytrms/tracebuffer.py DELETED
@@ -1,108 +0,0 @@
1
- import time
2
- import json
3
- import queue
4
- from itertools import chain
5
- from enum import Enum
6
- from threading import Thread, Condition
7
-
8
- import pandas as pd
9
-
10
- from .helpers import convert_labview_to_posix, PTRConnectionError
11
-
12
-
13
- def parse(response, trace='raw'):
14
- jsonized = json.loads(response)
15
- info = jsonized['TimeCycle']
16
- ts = convert_labview_to_posix(info['AbsTime'])
17
-
18
- data = [list(info.values())] + [a['Data'] for a in jsonized['AddData']] + [jsonized[trace]]
19
- desc = [list(info.keys())] + [a['Desc'] for a in jsonized['AddData']] + [jsonized['masses']]
20
- chained_data = chain(*data)
21
- chained_desc = chain(*desc)
22
-
23
- return pd.Series(data=chained_data, index=chained_desc, name=ts)
24
-
25
-
26
- class TraceBuffer(Thread):
27
-
28
- poll = 0.2 # seconds
29
-
30
- class State(Enum):
31
- CONNECTING = -1
32
- IDLE = 0
33
- ACTIVE = 1
34
-
35
- def __init__(self, client):
36
- """'client' must provide a `.get_traces()` method that returns raw json data.
37
- """
38
- Thread.__init__(self)
39
- self.daemon = True
40
- self.client = client
41
- self.queue = queue.Queue()
42
- self.state = TraceBuffer.State.CONNECTING
43
- self._cond = Condition()
44
-
45
- def is_connected(self):
46
- return self.state != TraceBuffer.State.CONNECTING
47
-
48
- def is_idle(self):
49
- return self.state == TraceBuffer.State.IDLE
50
-
51
- def wait_for_connection(self, timeout=5):
52
- '''
53
- will raise a PTRConnectionError if not connected after `timeout`
54
- '''
55
- waited = 0
56
- dt = 0.01
57
- while not self.is_connected():
58
- waited += dt
59
- if waited > timeout:
60
- raise PTRConnectionError('no connection to instrument')
61
- time.sleep(dt)
62
-
63
- def run(self):
64
- last = -753 # the year Rome was founded is never a valid cycle
65
- while True:
66
- #TODO :: das kann ja gar nicht funktionieren!?!?
67
- #if not self.is_connected():
68
- # time.sleep(self.poll)
69
- # continue
70
-
71
- time.sleep(self.poll)
72
-
73
- with self._cond: # .acquire()`s the underlying lock
74
- raw = self.client.get_traces()
75
- #try:
76
- #except PTRConnectionError as exc:
77
- # print(exc)
78
- # break
79
- if not len(raw):
80
- continue
81
-
82
- jsonized = json.loads(raw)
83
- ts = jsonized['TimeCycle']['AbsTime']
84
- oc = jsonized['TimeCycle']['OverallCycle']
85
- # the client returns the "current", i.e. last known trace data, even if
86
- # the machine is currently stopped. we want to definitely reflect this
87
- # idle state of the (actual) machine in our Python objects!
88
- # TODO :: *ideally*, the state is returned by a webAPI-call.. but as long
89
- # as this doesn't work perfectly, let's just do the next best thing and
90
- # watch the current cycle:
91
- if last < 0: last = oc
92
-
93
- if oc > last:
94
- pd_series = parse(raw)
95
- self.queue.put(pd_series)
96
- self.state = TraceBuffer.State.ACTIVE
97
- else:
98
- self.state = TraceBuffer.State.IDLE
99
- last = oc
100
-
101
- # This method releases the underlying lock, and then blocks until it is
102
- # awakened by a notify() or notify_all() call for the same condition variable
103
- # in another thread, or until the optional timeout occurs. Once awakened or
104
- # timed out, it re-acquires the lock and returns. The return value is True
105
- # unless a given timeout expired, in which case it is False.
106
- if self._cond.wait(self.poll):
107
- break
108
-
@@ -1,29 +0,0 @@
1
- pytrms/__init__.py,sha256=U4ixhTjfFkiVlLXV4JXcRq-eLFkdKlGlU4e52Zlbpsg,970
2
- pytrms/_base/__init__.py,sha256=GBALqAy1kUPMc0CWnWRmn_Cg_HGKGCeE-V2rdZEmN8A,836
3
- pytrms/_base/ioniclient.py,sha256=f4xWW3PL0p1gP7pczBaDEva3WUUb_J73n7yTIY5pW4s,826
4
- pytrms/_base/mqttclient.py,sha256=iua-AK7S_3rH1hsuLepqIjf7ivF5Ihb6T9OJNafJXXE,4233
5
- pytrms/_version.py,sha256=fXD69jGmDocNcc-uipbNIhjWxC7EuyhPUQ6BrBvK0Wg,854
6
- pytrms/clients/__init__.py,sha256=RejfXqq7zSKLMZbUHdxtBvLiyeshLX-JQzgthmpYeMw,1404
7
- pytrms/clients/db_api.py,sha256=exgE4iUHdACprztYYs330NzYI88f6TwF8PjtBzohgjk,6658
8
- pytrms/clients/dirigent.py,sha256=9WZPfqXxLmFCcT2RC8wxm7uixe4n-Y8vbsdpzc61tsI,5742
9
- pytrms/clients/ioniclient.py,sha256=5s3GPDr6AKH4Ia0Jix6UI8Xie_t3muKxr218MNRk2Yw,2853
10
- pytrms/clients/modbus.py,sha256=E1IfoHJm2gVHo-GcKDWba359ii5e0Ymaplv-DO88FqM,19223
11
- pytrms/clients/mqtt.py,sha256=QRJgC10GWFKzT3r4yNGOafXshgHlA5poGEzouEporEU,30231
12
- pytrms/clients/ssevent.py,sha256=OziYX9amJunVzTN0MiQmLg2mICHdnVC0LXT5o_ZcwcE,2546
13
- pytrms/compose/__init__.py,sha256=gRkwGezTsuiMLA4jr5hhQsY7XX_FonBeWcvDfDuMFnY,30
14
- pytrms/compose/composition.py,sha256=c3ae3PgQg0b741RhRU6oQMAiM7B2xRvbweGp0tCZatc,7906
15
- pytrms/data/IoniTofPrefs.ini,sha256=e1nU7Hyuh0efpgfN-G5-ERAFC6ZeehUiotsM4VtZIT0,1999
16
- pytrms/data/ParaIDs.csv,sha256=eWQxmHFfeTSxFfMcFpqloiZWDyK4VLZaV9zCnzLHNYs,27996
17
- pytrms/helpers.py,sha256=pNLYWlHM1n7vOkCi2vwESMG5baIJ5v2OD4z94XpXU1k,108
18
- pytrms/instrument.py,sha256=OIFTbS6fuhos6oYMsrA_qdgvs_7T-H-sOSl1a0qpxZ8,3977
19
- pytrms/measurement.py,sha256=RqACqedT0JQDkv5cmKxcXVSgl1n0Fbp1mQj81aOsZkg,5507
20
- pytrms/peaktable.py,sha256=b3KkILn5DctEfTi1-tOC9LZv1yrRcoH23VaE5JEqyk4,17536
21
- pytrms/plotting/__init__.py,sha256=vp5mAa3npo4kP5wvXXNDxKnryFK693P4PSwC-BxEUH8,66
22
- pytrms/plotting/plotting.py,sha256=t7RXUhjOwXchtYXVgb0rJPD_9aVMDCT6DCAGu8TZQfE,702
23
- pytrms/readers/__init__.py,sha256=F1ZX4Jv7NcY8nuSWnIbwjYnNFC2JsTnEp0BnfBHUMSM,76
24
- pytrms/readers/ionitof_reader.py,sha256=vhwVfhJ-ly8I_02okpgxoVqnB0QRDuqkufGZY21vFi4,18583
25
- pytrms/tracebuffer.py,sha256=30HfMJtxZ4Dnf4mPeyinXqESOA-0Xpu9FR4jaBvb_nA,3912
26
- pytrms-0.9.0.dist-info/LICENSE,sha256=GJsa-V1mEVHgVM6hDJGz11Tk3k0_7PsHTB-ylHb3Fns,18431
27
- pytrms-0.9.0.dist-info/METADATA,sha256=sTHS7ZvxCdgcaTj_hCI_Vb8erA1nIUBCs4JMGb06mRg,770
28
- pytrms-0.9.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
29
- pytrms-0.9.0.dist-info/RECORD,,
File without changes