22import os , threading , time , json , logging , zmq , copy , glob
33from datetime import datetime
44
5- from so .core import load_scenario , Backend , OverrideState , Status
5+ from so .core import load_scenario , Backend , OverrideState , Status , ObjectStore , TTCState , Products , Quality
66from so .ground_station import GroundStationSim
7- from so .spacecraft import SpacecraftSim
7+ from so .spacecraft import SpacecraftSim , SpacePacketHandler
88
99logger = logging .getLogger (__name__ )
1010
1313LAST_GS_STATE , LAST_SC_STATE = None , None
1414CONTROL_HIST = []
1515OV_STATE = OverrideState ()
16+ SIM_UID = None
17+
18+ SO_GEN_PRODUCTS = bool (int (os .getenv ('SO_GEN_PRODUCTS' , 1 )))
19+ if SO_GEN_PRODUCTS :
20+ OBJ_STORE = ObjectStore ()
21+ SPH = SpacePacketHandler ()
22+ PRODUCTS = Products (object_store = OBJ_STORE )
23+ else :
24+ OBJ_STORE = None
25+ SPH = None
26+ PRODUCTS = None
1627
1728def sim_loop ():
1829 logger .info ('Starting sim loop' )
@@ -29,31 +40,55 @@ def sim_loop():
2940 sc_state = SC_SIM .ping (ts , gs_state = gs_state , ov_state = OV_STATE )
3041 if sc_state :
3142 BACKEND .publish ('spacecraft' , sc_state .to_dict ())
43+ # handle packet store
44+ if SO_GEN_PRODUCTS :
45+ # FIXME handle high priority tm edge case
46+ c1 = sc_state .status_dl == TTCState ['FRAME_LOCK' ] and sc_state .frame_quality == Quality .good and sc_state .frame_checks == Status .enabled and sc_state .ov_no_tm is not True
47+ c2 = sc_state .status_dl == TTCState ['FRAME_LOCK' ] and sc_state .frame_quality == Quality .good and sc_state .frame_checks == Status .disabled
48+ c3 = sc_state .status_dl == TTCState ['FRAME_LOCK' ] and sc_state .frame_quality != Quality .good and gs_state .frame_checks == Status .disabled
49+ if c1 or c2 or c3 :
50+ # store packet every 5s
51+ if int (ts ) % 5 == 0 :
52+ # scrub packet data if only high priority is available
53+ if sc_state .ttc_obc == Status .error :
54+ OBJ_STORE .store (SIM_UID + '-tm' , str (int (ts )), SPH .space_packet (sc_state .scrub ()).as_bytes ())
55+ else :
56+ OBJ_STORE .store (SIM_UID + '-tm' , str (int (ts )), SPH .space_packet (sc_state ).as_bytes ())
3257
3358 end = time .time ()
3459 delta = SCENARIO .time_step - (end - start )
3560
3661 ts += delta
62+ if delta <= 0 :
63+ delta = 1
64+
3765 time .sleep (delta )
3866
3967def start_sim (uid ):
4068 logger .info (f"Starting sim { uid } " )
4169
42- global SCENARIO , GS_SIM , SC_SIM , BACKEND , END_SIM , LAST_GS_STATE , LAST_SC_STATE
70+ global SCENARIO , GS_SIM , SC_SIM , BACKEND , END_SIM , LAST_GS_STATE , LAST_SC_STATE , SIM_UID
4371 END_SIM = threading .Event ()
4472
4573 SCENARIO = load_scenario (uid )
4674 GS_SIM = GroundStationSim (SCENARIO , initial_state = LAST_GS_STATE )
4775 SC_SIM = SpacecraftSim (SCENARIO , initial_state = LAST_SC_STATE )
4876 BACKEND = Backend (SCENARIO )
4977
78+ SIM_UID = 'sim-' + datetime .utcnow ().isoformat (sep = 'T' , timespec = 'minutes' ).replace ('-' ,'.' ).replace (':' ,'h' ).replace ('T' , '-' )
79+ logger .info (f"Sim UID set to: { SIM_UID } " )
80+
81+ if SO_GEN_PRODUCTS :
82+ logger .info ('Products handling: flight dynamics start sim' )
83+ PRODUCTS .fd_start_sim ()
84+
5085 t = threading .Thread (target = sim_loop )
5186 t .start ()
5287
5388def stop_sim ():
5489 logger .info (f"Stopping sim" )
5590
56- global END_SIM , GS_SIM , SC_SIM , LAST_GS_STATE , LAST_SC_STATE , CONTROL_HIST
91+ global END_SIM , GS_SIM , SC_SIM , LAST_GS_STATE , LAST_SC_STATE , CONTROL_HIST , SIM_UID
5792 if END_SIM == None or SCENARIO == None or GS_SIM == None or SC_SIM == None :
5893 logger .info ("No sim running" )
5994 return
@@ -63,6 +98,10 @@ def stop_sim():
6398 LAST_GS_STATE = copy .deepcopy (GS_SIM .state ) if GS_SIM else None
6499 LAST_SC_STATE = copy .deepcopy (SC_SIM .state ) if SC_SIM else None
65100
101+ if SO_GEN_PRODUCTS :
102+ logger .info ('Products handling: flight dynamics stop sim' )
103+ PRODUCTS .fd_stop_sim (SIM_UID , LAST_GS_STATE )
104+
66105 # save control history to file and reset
67106 _path , _now = os .path .join ('data' , 'hist' ), int (datetime .utcnow ().timestamp ())
68107 if not os .path .exists (_path ):
@@ -71,6 +110,8 @@ def stop_sim():
71110 json .dump (CONTROL_HIST , fout )
72111 CONTROL_HIST = []
73112
113+ SIM_UID = None
114+
74115def _log_control (data , result , ts ):
75116 global CONTROL_HIST
76117
@@ -124,21 +165,24 @@ def control_loop():
124165
125166 _ok , _fail = { 'status' : 'OK' }, { 'status' : 'FAIL' }
126167 _admin = False
127- if 'admin' in data :
128- _admin = data ['admin' ]
168+ if 'admin' in data and data ['admin' ] is True :
169+ _admin = True
170+ else :
171+ _admin = False
129172
130173 # handle spacecraft controls
131- if 'system' in data and data ['system' ] == 'spacecraft' :
174+ if 'system' in data and data ['system' ] == 'spacecraft' and _admin is False :
132175 _fail = { 'status' : 'r:REL r:ACC r:FAIL' }
133176
134- # need at least ground station U/L carrier enabled to send command
135- if _admin is False and ( GS_SIM is None or GS_SIM .state .carrier_ul is None or GS_SIM .state .carrier_ul == Status .off ) :
177+ # need at least ground station U/L carrier enabled and sweep done to send commands
178+ if GS_SIM is None or GS_SIM .state .carrier_ul is None or GS_SIM .state .carrier_ul == Status .off or GS_SIM . state . sweep_done is False :
136179 result = { 'status' : 'r:REL r:ACC r:FAIL' }
137180 _ts = GS_SIM .state .ts
138181 elif GS_SIM .state .power_ul < 50 :
139182 result = { 'status' : 'g:REL r:ACC r:FAIL' }
140183 _ts = GS_SIM .state .ts
141184 else :
185+ print ('try' , data )
142186 try :
143187 if OV_STATE .no_tc is not True :
144188 result = SC_SIM .control (data , ov_state = OV_STATE , admin = _admin )
@@ -184,6 +228,14 @@ def control_loop():
184228 case other :
185229 result = _fail
186230
231+ # handle admin control for spacecraft, without overrides and requiring TC
232+ elif 'system' in data and data ['system' ] == 'spacecraft' and _admin is True :
233+ result = SC_SIM .control (data , admin = _admin )
234+ if 'OK' in result ['status' ]:
235+ result = _ok
236+ else :
237+ result = _fail
238+
187239 # handle overrides
188240 elif 'system' in data and data ['system' ] == 'override' :
189241 if data ['value' ] is None or len (data ['value' ]) == 0 :
0 commit comments