enode-host 0.1.0__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.
enode_host/storage.py ADDED
@@ -0,0 +1,93 @@
1
+ from numpy import floor
2
+ import datetime
3
+ from pandas import DataFrame, to_datetime
4
+ import time
5
+ import copy
6
+ import logging
7
+ import os
8
+ try:
9
+ from .constants import RT_STREAM_HD5_DIR
10
+ except ImportError:
11
+ from constants import RT_STREAM_HD5_DIR
12
+ import re
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ class Storage:
17
+
18
+ def __init__(self, file_length_in_sec):
19
+ self.file_buf = {}
20
+ # the buffer for file outputs
21
+ # {
22
+ # 'acc1':{'t':[], 'y':[], 'last_gridTime':-1},
23
+ # 'acc2':{'t':[], 'y':[], 'last_gridTime':-1},
24
+ # }
25
+ self.file_length_in_sec = file_length_in_sec
26
+ self.tmp_length_in_sec = 24 * 60 * 60
27
+ os.makedirs(RT_STREAM_HD5_DIR, exist_ok=True)
28
+
29
+ def _length_for_node(self, nodeID):
30
+ if isinstance(nodeID, int):
31
+ return self.file_length_in_sec
32
+ match = re.match(r"([A-Za-z]+)", str(nodeID))
33
+ if match and match.group(1).upper() == "TMP":
34
+ return self.tmp_length_in_sec
35
+ return self.file_length_in_sec
36
+
37
+ def get_gridTime(self, t, nodeID):
38
+ length = self._length_for_node(nodeID)
39
+ return floor(t / length) * length
40
+
41
+ def get_filepath(self, nodeID, t):
42
+ timestamp = datetime.datetime.fromtimestamp(t).strftime('%Y_%m%d_%H%M')
43
+ label = None
44
+ if isinstance(nodeID, int):
45
+ label = f"acc{nodeID:02d}"
46
+ else:
47
+ match = re.match(r"([A-Za-z]+)(\\d+)$", str(nodeID))
48
+ if match:
49
+ node_type = match.group(1).lower()
50
+ node_num = int(match.group(2))
51
+ label = f"{node_type}{node_num:02d}"
52
+ else:
53
+ label = str(nodeID).lower()
54
+ return os.path.join(RT_STREAM_HD5_DIR, f"{timestamp}-{label}.hd5")
55
+
56
+ def push(self, nodeID, t, y):
57
+ if not nodeID in self.file_buf.keys():
58
+ self.file_buf[nodeID] = {'last_gridTime': -1, 't':[], 'y':[]}
59
+
60
+ for t_, y_ in zip(t, y):
61
+ current_gridTime = self.get_gridTime(t_.timestamp(), nodeID)
62
+ if self.file_buf[nodeID]['last_gridTime'] == -1:
63
+ self.file_buf[nodeID]['last_gridTime'] = current_gridTime
64
+ elif self.file_buf[nodeID]['last_gridTime'] != current_gridTime:
65
+ # Save to disk in HDF
66
+ df = DataFrame(self.file_buf[nodeID]['y'], columns = ['X', 'Y', 'Z'])
67
+ df.index = to_datetime(self.file_buf[nodeID]['t'])
68
+ filepath = self.get_filepath(nodeID, self.file_buf[nodeID]['last_gridTime'])
69
+ df.to_hdf(filepath, key='df', mode='w')
70
+ logger.info("file saved: {}".format(filepath))
71
+ # Reinitialise
72
+ self.file_buf[nodeID]['last_gridTime'] = copy.copy(current_gridTime)
73
+ self.file_buf[nodeID]['t'] = []
74
+ self.file_buf[nodeID]['y'] = []
75
+
76
+ self.file_buf[nodeID]['t'].append(t_)
77
+ self.file_buf[nodeID]['y'].append(y_)
78
+
79
+
80
+
81
+ if __name__ == '__main__':
82
+ import pandas as pd
83
+
84
+ t = [datetime.datetime(2024,1,1,0,i,j) for i in range(3) for j in range(60)]
85
+ y = [[i,2*i,3*i] for i in range(180)]
86
+ storage = Storage(60)
87
+ i = 0
88
+ # t[60*i:60*(i+1)]
89
+ # y[60*i:60*(i+1)]
90
+ for i in range(3):
91
+ storage.push(1, t[60*i:60*(i+1)], y[60*i:60*(i+1)])
92
+
93
+
@@ -0,0 +1,79 @@
1
+ from numpy import empty, append, array, where, logical_and, delete
2
+ from scipy.interpolate import interp1d
3
+ try:
4
+ from . import config
5
+ except ImportError:
6
+ import config
7
+ import struct
8
+ import datetime
9
+
10
+ class Timestamping():
11
+
12
+ def __init__(self):
13
+
14
+ self.CC_ppsEpoch = empty((0, 2), dtype = float) # array( [[cc0, epoch_PPS0], [cc1, epoch_PPS1]] )
15
+ self.CC_ACC = empty((0, 4), dtype = float) # array( [[cc, acc_x, acc_y, acc_z], ...])
16
+
17
+ def push_cc_acc(self, cc_acc):
18
+
19
+ # only add when it is a new data
20
+ if self.CC_ppsEpoch.shape[0] > 1 and cc_acc[0] > self.CC_ppsEpoch[1, 0]:
21
+ self.CC_ACC = append(self.CC_ACC, array([cc_acc]), axis=0) # CC, acc_x, acc_y, acc_z
22
+
23
+ def push_cc_pps(self, CC, epoch_time):
24
+
25
+ # Populating CC_ppsEpoch Table
26
+ self.CC_ppsEpoch = append(self.CC_ppsEpoch, array([[CC, epoch_time]]), axis = 0)
27
+ if len(self.CC_ppsEpoch) == 1:
28
+ return [], []
29
+ elif len(self.CC_ppsEpoch) == 3:
30
+ self.CC_ppsEpoch = self.CC_ppsEpoch[1:, :] # shifting CC_ppsEpoch
31
+
32
+ # timestamping
33
+ cc0 = self.CC_ppsEpoch[0, 0]
34
+ cc1 = self.CC_ppsEpoch[1, 0]
35
+ idx = where(logical_and(self.CC_ACC[:,0] >= cc0, self.CC_ACC[:,0] < cc1))
36
+ if len(idx[0]) > 0:
37
+ cc_acc = self.CC_ACC[idx[0], :]
38
+ linear_interp = interp1d(self.CC_ppsEpoch[:2, 0], self.CC_ppsEpoch[:2, 1], kind = 'linear')
39
+ x_interp = cc_acc[:, 0]
40
+ y_interp = linear_interp(x_interp)
41
+ t = []
42
+ for i in range(cc_acc.shape[0]):
43
+ epoch = int(y_interp[i])
44
+ suseconds = int((y_interp[i] - epoch) * 1e6)
45
+ t_ = datetime.datetime.fromtimestamp(epoch, tz=datetime.timezone.utc)
46
+ t_ = t_.replace(microsecond = suseconds)
47
+ t.append(t_)
48
+ y = cc_acc[:, 1:4]
49
+ # Delete already timestamped points
50
+ mask = self.CC_ACC[:, 0] >= cc1
51
+ self.CC_ACC = self.CC_ACC[mask,...]
52
+ return t, y
53
+
54
+ return [], []
55
+
56
+
57
+
58
+ # rawFileLength_sec = config.rawFileLength_sec
59
+ # def getGridTime(self, epoch):
60
+ # return int(epoch / rawFileLength_sec) * rawFileLength_sec
61
+ # # FILE SAVING
62
+ # if 0:
63
+ # if epoch_time > 0:
64
+ # if fid == -1:
65
+ # latest_fileName = self.getFileName(streamID, epoch_time)
66
+ # fid = open(latest_fileName, "wb")
67
+ # elif self.getFileName(streamID, epoch_time) != latest_fileName:
68
+ # fid.close()
69
+ # latest_fileName = self.getFileName(streamID, epoch_time)
70
+ # fid = open(latest_fileName, "wb")
71
+ # if fid != -1:
72
+ # fid.write(packet[2:])
73
+ # data = data[packet_size:]
74
+
75
+ # def getFileName(self, streamID, epoch_time):
76
+ # nodeNumber = (streamID & 0x0FF0) >> 4
77
+ # streamID_str = 'ACC{}-ACC'.format(nodeNumber)
78
+ # t = datetime.datetime.fromtimestamp(epoch_time)
79
+ # return os.path.join(target_dir, '{}-{}'.format(datetime.datetime.strftime(t, '%Y_%m%d_%H%M'), streamID_str))
enode_host/types.py ADDED
@@ -0,0 +1,38 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Dict, List, Optional
5
+ import datetime
6
+
7
+
8
+ @dataclass
9
+ class NodeStatus:
10
+ node_id: int
11
+ node_label: str
12
+ connection: str = "waiting"
13
+ speed_kb_s: float = 0.0
14
+ level: Optional[int] = None
15
+ parent: str = ""
16
+ rssi_db: Optional[int] = None
17
+ children: str = ""
18
+ pps_age: str = ""
19
+ cmd: str = ""
20
+ pps_time: Optional[datetime.datetime] = None
21
+ pps_flash_time: Optional[datetime.datetime] = None
22
+ daq_time: Optional[datetime.datetime] = None
23
+ parent_mac: Optional[str] = None
24
+ self_mac: Optional[str] = None
25
+ conn_rpt_time: Optional[datetime.datetime] = None
26
+
27
+
28
+ @dataclass
29
+ class PpsFlashState:
30
+ flash_until: Optional[datetime.datetime] = None
31
+
32
+
33
+ @dataclass
34
+ class PlotBuffers:
35
+ timehistory_xdata: List[datetime.datetime] = field(default_factory=list)
36
+ timehistory_ydata: List[List[float]] = field(default_factory=list)
37
+ psd_xdata: List[float] = field(default_factory=list)
38
+ psd_ydata: Dict[int, List[List[float]]] = field(default_factory=dict)