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)