1#!/usr/bin/env python3
  2
  3# SK 19/06/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 = 'ONBOARD-EVENTS'
 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 = 'ONBOARD-EVENTS'
 29
 30# tm_parameter = {
 31    # 'CFT0042F': 'PWR_VBUS1_AT_MIN_DETECT',
 32    # 'CFT00439': 'PWR_VBUS2_AT_MIN_DETECT',
 33    # 'CFT0042E': 'PWR_VBUS3_AT_MIN_DETECT',
 34    # 'CFTD6608': 'HKF_PWR_DOD'
 35# }
 36
 37# print("tm_parameter:")
 38# for k, v in tm_parameter.items():
 39    # print(f"{k}: {v}")
 40
 41logger = logging.getLogger()
 42
 43# from django.shortcuts import render
 44# from chartepssg_du.custom_app.views import custom_view
 45# from django.core.wsgi import get_wsgi_application
 46# from django.test import RequestFactory
 47    
 48# # Initialize Django application
 49# application = get_wsgi_application()
 50# request_factory = RequestFactory()
 51
 52# request = request_factory.get('/custom-view/')
 53
 54
 55def find_events(sid,
 56                start_time,
 57                start_time_eq,
 58                stop_time,
 59                event_classes,
 60                ordering,
 61                from_to,
 62                properties,
 63                count,
 64                single,
 65                filtering,
 66                multitable_extra_params,
 67                user: User = None):
 68    """Build Event records from records extracted from ts table in given time range.
 69
 70    Args:
 71        `sid` (SID): Source-ID to search for
 72        `start_time` (datetime): Start of time range to search
 73        `stop_time` (datetime):  End of time range to search
 74        `event_classes` (list of EventClass): Class definitions to search for - this is relevant for
 75            implementations of find_event() which are reading from tables which could return
 76            multiple different types of event.
 77        `ordering` (str or list of str): Field(s) to sort by
 78        `from_to` (tuple): (page_start, page_stop)
 79        `properties`: (list): list of property triplets (name, value, op)
 80        `count`: (bool): just return the number of events found
 81        `single`: (bool): just return the first event which matches input requirements
 82        `filtering` (EventTimeFilter): Specify if the start_time/stop_time range is applied
 83            to the sensing_time or the execution_time column
 84        `multitable_extra_params` (tuple): Special method of storing additional table-specific information
 85            in an Event object, which is passed via json to the event viewer table and back,
 86            and used again in this function to ecreate the original Event
 87
 88    Returns:
 89        event_count: (int) number of events which match input requirements
 90        events: list of events which match input requirements
 91    """
 92
 93    if from_to is not None:
 94        page_start, page_stop = from_to
 95
 96    # if returning a single record, set limit = 1
 97    # Note: there can be so many TM packets so when a single set, for the 'Snap' case
 98    #        set limit option to prevent database hanging. Do not set limit when single
 99    #        set due selecting event pop-up as handled by ts.select
100    limit = None
101    if single and start_time_eq is None:
102        limit = 1
103
104    # Instead of returning an iterator, we build the whole list and return it
105    events = []
106    # Total count of events. This may be larger than len(events) since we may be pagingating
107    # using the `from_to` parameter and as an optimisation, we don't bother to instantiate all
108    # the required Events objects, just enough for the event viewer to work. However we still
109    # want to tell the user how many events are available
110    event_count = 0
111
112    # Reset ordering for sensing_time
113    order_clauses = process_ordering_sensing_time(ordering)
114
115    table_info = TableInfo(TM_TABLE, sid=sid, fast_load=True)
116
117    if not single and count:
118        # just return count of events
119        event_count = ts.count(
120            table=table_info.storage_table,
121            field='SENSING_TIME',
122            method='ap',
123            sid=sid,
124            sensing_start=start_time,
125            sensing_stop=stop_time)
126
127        return event_count, events
128
129    fields = ('SENSING_TIME', 'SPID', 'SEQUENCE_COUNT', 'PAYLOAD', 'HEX')
130    # for key in tm_parameter.keys():
131        # fields += (table_info.fields.get(key),)
132
133    # get each row using ts select
134    for row in ts.select(sid=sid,
135                         sensing_start=start_time,
136                         sensing_stop=stop_time,
137                         sensing_time=start_time_eq,
138                         table=table_info.storage_table,
139                         fields=fields,
140                         ordering=order_clauses,
141                         limit=limit):
142
143        # sensing_time = row[0]
144        # CFT0042F_values = row[1]
145        # CFT00439_values = row[2]
146        # CFT0042E_values = row[3]
147        # CFTD6608_values = row[4]
148
149        # inst_properties = {
150                # 'SID': sid,
151                # 'CFT0042F': CFT0042F_values,
152                # 'CFT00439': CFT00439_values,
153                # 'CFT0042E': CFT0042E_values,
154                # 'CFTD6608': CFTD6608_values,
155                # }
156                
157        # get packet details for each spid
158        sensing_time = row[0]
159        spid = row[1]
160        sequence_count = row[2]
161        payload = row[3]
162        hexdata = row[4]
163        packet_reader = PacketDef.find_one(spid=spid, sid=sid)
164        severity = 0
165        
166        # onboard events are all Service 5 Packets
167        if packet_reader is not None and (packet_reader.service == 5 or \
168            (packet_reader.service == 1 and packet_reader.subservice in [2, 4, 6, 8])):
169            # logger.info('Got an onboard event ' + str(sensing_time))
170
171            # set severity according to
172            if packet_reader.service == 5:
173                if packet_reader.subservice in [1, 2, 3, 4]:
174                    severity = packet_reader.subservice
175
176            elif packet_reader.service == 1:
177                severity = 3
178
179            inst_properties = {
180                'Name': packet_reader.name,
181                'Severity': severity,
182                'Description': packet_reader.description,
183                'SPID': packet_reader.spid,
184                'Type': packet_reader.service,
185                'SType': packet_reader.subservice,
186                'APID': packet_reader.apid,
187                'SSC': sequence_count,
188                'Param1': packet_reader.param1,
189                'Param2': packet_reader.param2,
190                'Param_Data': payload,
191                'Hex_Data': hexdata
192            }
193
194            # if multitable_extra_params is not None:
195                # req_spid, req_sequence_count = multitable_extra_params
196                # if req_spid != packet_reader.spid or req_sequence_count != sequence_count:
197                    # continue
198
199            # now filter properties in inst_properties before keeping event for viewing
200            if not include_event_check(properties, inst_properties):
201                continue
202
203            # if we are counting, just bump the counter, else yield event and bump counter
204            # Note: must count all events which meet the filtering criterea to keep paging upto date
205            event_count += 1
206
207            if not count:
208                # build event and append to events to return
209                if from_to is None or (page_start <= event_count and event_count <= page_stop):
210                    # build event and append to events to return, if within Eventviewer display page
211                    events.append(Event(sid=sid,
212                                        event_classname=EVENT_CLASSNAME,
213                                        start_time=sensing_time,
214                                        #multitable_extra_params=(spid, sequence_count),
215                                        instance_properties=inst_properties))
216
217                if single:
218                    # just return first event, which matches search requirements
219                    break
220    
221    #return retrieve_events(request)
222    return event_count, events
223    
224# if __name__ == '__main__':
225    # from django.core.wsgi import get_wsgi_application
226    # from django.test import RequestFactory
227
228    # # Initialize Django application
229    # application = get_wsgi_application()
230    # request_factory = RequestFactory()
231
232    # # Create a request object
233    # request = request_factory.get('/custom-view/')
234
235    # # Call the trigger_custom_view function
236    # response = trigger_custom_view(request)
237
238    # # Print the response or perform further actions
239    # print(response)