1#!/usr/bin/env python3
  2
  3# SK 17/05/2023 created
  4
  5"""Convert TM raw telemetry packets entries to TM events."""
  6
  7import logging
  8
  9from chart.db import ts
 10from chart.events.event import Event
 11from chart.events.event import include_event_check
 12from chart.products.events.multitable_utils import process_ordering_sensing_time
 13from chart.products.pus.packetdef import PacketDef
 14from chart.products.pus.packetdef import PacketDomain
 15from chart import settings
 16from chart.db.model.table import TableInfo
 17from chart.common.traits import is_listlike
 18from chart.web.user import User
 19
 20# This module is used for event classes where <db-table> matches DECODER_ID
 21DECODER_ID = 'TM'
 22
 23# Table to read all telemetry from
 24TM_TABLE = 'TM'
 25
 26# ??? !!! For some reason we can't get this from the events XML file where we should get it
 27# from, so as a terrible and lazy hack it's hard-coded here
 28EVENT_CLASSNAME = 'SGA1-NUMBER-PULSES'
 29
 30# tm_parameter = {
 31    # # 'ASTD06E0': 'HKA_MODE',
 32    # # 'ASTD07D6': 'HKA_SUBMODE',
 33    # # 'PFET0015': 'SADEA CAL. POSITION A',
 34    # 'AST0155A': 'THRONTACC(1)',
 35    # 'AST00F01': 'THRONTACC(2)',
 36    # 'AST013A9': 'THRONTACC(3)',
 37    # 'AST010E1': 'THRONTACC(4)',
 38    # 'AST01249': 'THRONTACC(5)',
 39    # 'AST008F3': 'THRONTACC(6)',
 40    # 'AST01690': 'THRONTACC(7)',
 41    # 'AST0101A': 'THRONTACC(8)',
 42    # # 'AST01E19': 'THRABONTACC(1)',
 43    # # 'AST01DF8': 'THRABONTACC(2)',
 44    # # 'AST01E71': 'THRABONTACC(3)',
 45    # # 'AST01DD8': 'THRABONTACC(4)',
 46    # # 'AST00AB5': 'THRNUMPULSESACC(1)',
 47    # # 'AST01409': 'THRNUMPULSESACC(2)',
 48    # # 'AST00ED4': 'THRNUMPULSESACC(3)',
 49    # # 'AST016CA': 'THRNUMPULSESACC(4)',
 50    # # 'AST0062A': 'THRNUMPULSESACC(5)',
 51    # # 'AST00F0A': 'THRNUMPULSESACC(6)',
 52    # # 'AST0126B': 'THRNUMPULSESACC(7)',
 53    # # 'AST01386': 'THRNUMPULSESACC(8)',
 54    # # 'AST01F2A': 'THRABNUMPULSESACC(1)',
 55    # # 'AST01C54': 'THRABNUMPULSESACC(2)',
 56    # # 'AST01E00': 'THRABNUMPULSESACC(3)',
 57    # # 'AST01D2F': 'THRABNUMPULSESACC(4)',
 58    # # 'AST0060A': 'FINEATTRATE_C1',
 59    # # 'AST004E8': 'FINEATTRATE_C2',
 60    # # 'AST00731': 'FINEATTRATE_C3',
 61    # # 'AST012EE': 'DVESTSUMSTATELOG_C(1)',
 62    # # 'AST015EC': 'DVESTSUMSTATELOG_C(2)',
 63    # # 'AST00720': 'DVESTSUMSTATELOG_C(3)',
 64    # # 'AST01F2B': 'DVESTSUMSTATELOG_O(1)',
 65    # # 'AST01ECC': 'DVESTSUMSTATELOG_O(2)',
 66    # # 'AST01F86': 'DVESTSUMSTATELOG_O(3)',
 67    # # 'AST016A9': 'THTANKPRESSEST',
 68    # # 'AST011E5': 'THRFUELMASSCONSTOT'
 69#}
 70
 71# print("tm_parameter:")
 72# for k, v in tm_parameter.items():
 73    # print(f"{k}: {v}")
 74
 75logger = logging.getLogger()
 76
 77def find_events(sid,
 78                start_time,
 79                start_time_eq,
 80                stop_time,
 81                event_classes,
 82                ordering,
 83                from_to,
 84                properties,
 85                count,
 86                single,
 87                filtering,
 88                multitable_extra_params,
 89                user: User = None):
 90    """Build Event records from records extracted from ts table in given time range.
 91
 92    Args:
 93        `sid` (SID): Source-ID to search for
 94        `start_time` (datetime): Start of time range to search
 95        `stop_time` (datetime):  End of time range to search
 96        `event_classes` (list of EventClass): Class definitions to search for - this is relevant for
 97            implementations of find_event() which are reading from tables which could return
 98            multiple different types of event.
 99        `ordering` (str or list of str): Field(s) to sort by
100        `from_to` (tuple): (page_start, page_stop)
101        `properties`: (list): list of property triplets (name, value, op)
102        `count`: (bool): just return the number of events found
103        `single`: (bool): just return the first event which matches input requirements
104        `filtering` (EventTimeFilter): Specify if the start_time/stop_time range is applied
105            to the sensing_time or the execution_time column
106        `multitable_extra_params` (tuple): Special method of storing additional table-specific information
107            in an Event object, which is passed via json to the event viewer table and back,
108            and used again in this function to ecreate the original Event
109
110    Returns:
111        event_count: (int) number of events which match input requirements
112        events: list of events which match input requirements
113    """
114
115    if from_to is not None:
116        page_start, page_stop = from_to
117
118    # if returning a single record, set limit = 1
119    # Note: there can be so many TM packets so when a single set, for the 'Snap' case
120    #        set limit option to prevent database hanging. Do not set limit when single
121    #        set due selecting event pop-up as handled by ts.select
122    limit = None
123    if single and start_time_eq is None:
124        limit = 1
125
126    # Instead of returning an iterator, we build the whole list and return it
127    events = []
128    # Total count of events. This may be larger than len(events) since we may be pagingating
129    # using the `from_to` parameter and as an optimisation, we don't bother to instantiate all
130    # the required Events objects, just enough for the event viewer to work. However we still
131    # want to tell the user how many events are available
132    event_count = 0
133
134    # Reset ordering for sensing_time
135    order_clauses = process_ordering_sensing_time(ordering)
136
137    table_info = TableInfo(TM_TABLE, sid=sid, fast_load=True)
138
139    if not single and count:
140        # just return count of events
141        event_count = ts.count(
142            table=table_info.storage_table,
143            field='SENSING_TIME',
144            method='ap',
145            sid=sid,
146            sensing_start=start_time,
147            sensing_stop=stop_time)
148
149        return event_count, events
150
151                         
152    for sensing_time, spid, payload in ts.select(
153        table=table_info.storage_table,
154        sid=sid,
155        sensing_start=start_time,
156        sensing_stop=stop_time,
157        sensing_time=start_time_eq,
158        fields=('SENSING_TIME', 'SPID', 'PAYLOAD'),
159        where=('spid in (30622, 30507)', 
160               '(payload?\'AST00AB5\' or payload?\'AST01409\' or payload?\'AST00ED4\' or payload?\'AST016CA\' or payload?\'AST0062A\' or payload?\'AST00F0A\' or payload?\'AST0126B\' or payload?\'AST01386\' or payload?\'AST01F2A\' or payload?\'AST01C54\' or payload?\'AST01E00\' or payload?\'AST01D2F\')'),):
161
162            # print(sensing_time, spid, payload.get('AST00AB5'), payload.get('AST01D2F'))
163            sensing_time = sensing_time
164        # ASTD06E0_values = row[1]
165        # ASTD07D6_values = row[2]
166        # PFET0015_values = row[1]
167            # AST0155A_values = payload.get('AST0155A')
168            # AST00F01_values = payload.get('AST00F01')
169            # AST013A9_values = payload.get('AST013A9')
170            # AST010E1_values = payload.get('AST010E1')
171            # AST01249_values = payload.get('AST01249')
172            # AST008F3_values = payload.get('AST008F3')
173            # AST01690_values = payload.get('AST01690')
174            # AST0101A_values = payload.get('AST0101A')
175            # AST01E19_values = payload.get('AST01E19')
176            # AST01DF8_values = payload.get('AST01DF8')
177            # AST01E71_values = payload.get('AST01E71')
178            # AST01DD8_values = payload.get('AST01DD8')
179            AST00AB5_values = payload.get('AST00AB5')
180            AST01409_values = payload.get('AST01409')
181            AST00ED4_values = payload.get('AST00ED4')
182            AST016CA_values = payload.get('AST016CA')
183            AST0062A_values = payload.get('AST0062A')
184            AST00F0A_values = payload.get('AST00F0A')
185            AST0126B_values = payload.get('AST0126B')
186            AST01386_values = payload.get('AST01386')
187            AST01F2A_values = payload.get('AST01F2A')
188            AST01C54_values = payload.get('AST01C54')
189            AST01E00_values = payload.get('AST01E00')
190            AST01D2F_values = payload.get('AST01D2F')
191        # AST0060A_values = row[28]
192        # AST004E8_values = row[29]
193        # AST00731_values = row[30]
194        # AST012EE_values = row[31]
195        # AST015EC_values = row[32]
196        # AST00720_values = row[33]
197        # AST01F2B_values = row[34]
198        # AST01ECC_values = row[35]
199        # AST01F86_values = row[36]
200        # AST016A9_values = row[37]
201        # AST011E5_values = row[38]
202
203            inst_properties = {
204                    'SID': sid,
205                    # 'ASTD06E0': ASTD06E0_values,
206                    # 'ASTD07D6': ASTD07D6_values,
207                    # 'PFET0015': PFET0015_values,
208                    # 'AST0155A': AST0155A_values,
209                    # 'AST00F01': AST00F01_values,
210                    # 'AST013A9': AST013A9_values,
211                    # 'AST010E1': AST010E1_values,
212                    # 'AST01249': AST01249_values,
213                    # 'AST008F3': AST008F3_values,
214                    # 'AST01690': AST01690_values,
215                    # 'AST0101A': AST0101A_values,
216                    # 'AST01E19': AST01E19_values,
217                    # 'AST01DF8': AST01DF8_values,
218                    # 'AST01E71': AST01E71_values,
219                    # 'AST01DD8': AST01DD8_values,
220                    'AST00AB5': AST00AB5_values,
221                    'AST01409': AST01409_values,
222                    'AST00ED4': AST00ED4_values,
223                    'AST016CA': AST016CA_values,
224                    'AST0062A': AST0062A_values,
225                    'AST00F0A': AST00F0A_values,
226                    'AST0126B': AST0126B_values,
227                    'AST01386': AST01386_values,
228                    'AST01F2A': AST01F2A_values,
229                    'AST01C54': AST01C54_values,
230                    'AST01E00': AST01E00_values,
231                    'AST01D2F': AST01D2F_values,
232                    # 'AST0060A': AST0060A_values,
233                    # 'AST004E8': AST004E8_values,
234                    # 'AST00731': AST00731_values,
235                    # 'AST012EE': AST012EE_values,
236                    # 'AST015EC': AST015EC_values,
237                    # 'AST00720': AST00720_values,
238                    # 'AST01F2B': AST01F2B_values,
239                    # 'AST01ECC': AST01ECC_values,
240                    # 'AST01F86': AST01F86_values,
241                    # 'AST016A9': AST016A9_values,
242                    # 'AST011E5': AST011E5_values,
243                    }
244                    
245            #print("inst_properties",inst_properties)
246    
247            if multitable_extra_params is not None:
248                req_spid, req_sequence_count = multitable_extra_params
249                if req_spid != packet_reader.spid or req_sequence_count != sequence_count:
250                    continue
251    
252            # now filter properties in inst_properties before keeping event for viewing
253            if not include_event_check(properties, inst_properties):
254                continue
255    
256            # if we are counting, just bump the counter, else yield event and bump counter
257            # Note: must count all events which meet the filtering criterea to keep paging upto date
258            event_count += 1
259    
260            if not count:
261                # build event and append to events to return
262                if from_to is None or (page_start <= event_count and event_count <= page_stop):
263                    # build event and append to events to return, if within Eventviewer display page
264                    events.append(Event(sid=sid,
265                                        event_classname=EVENT_CLASSNAME,
266                                        start_time=sensing_time,
267                                        #multitable_extra_params=(spid, sequence_count),
268                                        instance_properties=inst_properties))
269    
270                if single:
271                    # just return first event, which matches search requirements
272                    break
273
274    return event_count, events