ifw-daq  1.0.0
IFW Data Acquisition modules
simulator.py
Go to the documentation of this file.
1 """
2 @file
3 @ingroup daq_sim_metadaqsim
4 @copyright 2021 ESO - European Southern Observatory
5 
6 @brief Implements simulation of metadaqif
7 """
8 # pylint: disable=no-name-in-module,import-error,no-self-use
9 import os
10 import json
11 import logging
12 import calendar
13 import inspect
14 
15 from ModMetadaqif.Metadaqif import (
16  DaqReply,
17  DaqStopReply,
18  DaqState,
19  DaqStatus,
20  DaqException,
21  )
22 
23 from ModMetadaqif.Metadaqif.MetaDaq import MetaDaqSyncService
24 from ModDaqsimif.Daqsimif import Error as SimCtlError
25 
26 
27 class Daq:
28  """Holds simulated status for DAQ
29  """
30 
31  def __init__(self, id):
32  self.id = id
33  self.state = DaqState.NotStarted
34  self.keywords = ""
35  self.files = []
36 
37 
38 class Simulator: # pylint: disable=invalid-name
39  """Simulator that implements Modmetadaqsimif and sets up
40  simulation behaviour for Simulator.
41 
42  Registered hooks are run once and then removed before executed.
43  Each hook is a callable that takes the result from the default implementation
44  as well as the arguments given to the original implementation.
45  The hook can then choose to modify the existing result or replace it.
46  """
47 
48  def __init__(self, name, mal):
49  self.name = name
50  self.mal = mal
51  self.current_daq = None
52  self.daqs = {} # id: status
53  # Oneshot commands
54  self.sim_hooks = {}
55 
56  def StartDaq(self, daq_id: str) -> DaqReply:
57  """@implements MetaDaq.StartDaq
58  """
59  logging.info("Request: StartDaq(%s)", daq_id)
60  if daq_id in self.daqs:
61  raise DaqException(daq_id, "Data acquisition with id already exist")
62  self.current_daq = daq = Daq(daq_id)
63  daq.state = DaqState.Acquiring
64  self.daqs[daq_id] = daq
65  result = self.make_daq_reply(daq_id)
66  return self.run_sim_hook(result, daq_id)
67 
68  def StopDaq(self, daq_id: str) -> DaqStopReply:
69  """@implements MetaDaq.StopDaq
70  """
71  logging.info("Request: StopDaq(%s)", daq_id)
72  daq = self.get_daq(daq_id)
73  daq.files = ["/tmp/%s.fits" % daq_id]
74  result = self.make_daq_stop_reply(daq_id)
75  return self.run_sim_hook(result, daq_id)
76 
77  def AbortDaq(self, daq_id: str) -> DaqReply:
78  """@implements MetaDaq.AbortDaq
79  """
80  logging.info("Request: AbortDaq(%s)", daq_id)
81  daq = self.get_daq(daq_id)
82  daq.state = DaqState.Failed
83  self.daqs[daq_id] = daq
84  result = self.make_daq_reply(daq_id)
85  return self.run_sim_hook(result, daq_id)
86 
87  def GetDaqStatus(self, daq_id: str) -> DaqStatus:
88  """@implements MetaDaq.GetDaqStatus
89  """
90  logging.info("Request: GetDaqStatus(%s)", daq_id)
91  result = self.make_daq_status(daq_id)
92  return self.run_sim_hook(result, daq_id)
93 
94  def get_daq(self, daq_id) -> Daq:
95  """Get daq or raise DaqException.
96  """
97  if daq_id in self.daqs:
98  return self.daqs[daq_id]
99  raise DaqException(daq_id, "Unknown daq id '%s'" % daq_id)
100 
101  def make_daq_reply(self, daq_id) -> DaqReply:
102  logging.debug("Creating return type: DaqReply")
103  reply = self.mal.createDataEntity(DaqReply)
104  reply.setId(daq_id)
105  logging.debug("Creating return type: Done")
106  return reply
107 
108  def make_daq_status(self, daq_id) -> DaqStatus:
109  """Create DaqStatus instance
110  """
111  daq = self.get_daq(daq_id)
112  status = self.mal.createDataEntity(DaqStatus)
113  status.setId(daq.id)
114  status.setState(daq.state)
115  status.setFiles(daq.files)
116  status.setTimestamp(calendar.timegm())
117  return status
118 
119  def make_daq_stop_reply(self, daq_id) -> DaqStopReply:
120  """Create DaqStopReply instance
121  """
122  daq = self.get_daq(daq_id)
123  reply = self.mal.createDataEntity(DaqStopReply)
124  reply.setId(daq.id)
125  reply.setFiles(daq.files)
126  reply.setKeywords(daq.keywords)
127  return reply
128 
129  def add_sim_hook(self, cmd_name: str, hook):
130  """Add the simulation hook `hook` for the specified command `cmd_name` (e.g.
131  "StartDaq").
132 
133  Any previous hooks will be replaced.
134  """
135  self.sim_hooks[cmd_name] = hook
136 
137  def reset(self):
138  """Reset simulator to initial default state.
139  """
140  self.clear_sim_hooks()
141  self.daqs = {}
142  self.current_daq = None
143 
144  def clear_sim_hooks(self):
145  """Remove all simulation hooks.
146  """
147  self.sim_hooks = {}
148 
149  def run_sim_hook(self, result, *args):
150  """Runs simulation hook that may modify or replace the default implementation.
151 
152  The hook to execute is automatically determined from the call stack, so
153  this should only ever be executed directly from the service command
154  implementation method.
155  """
156  cmd_name = inspect.stack()[1][3]
157  hook = self.sim_hooks.get(cmd_name)
158  logging.debug("Checking for hook for command %s", cmd_name)
159  if hook:
160  logging.debug("Running simulation hook for command %s", cmd_name)
161  del self.sim_hooks[cmd_name]
162  return hook(result, *args)
163  return result
164 
165 
166 class SimulatorCtl: # pylint: skip-file
167  """Simulator controller that implements Modmetadaqsimif and sets up
168  simulation behaviour for Simulator.
169  """
170 
171  def __init__(self, name, mal, server):
172  self.name = name
173  self.mal = mal
174  self.server = server
175 
176  self.sim = Simulator(name, mal)
177  logging.info("Registering service 'daq'")
178  self.server.registerService("daq", MetaDaqSyncService, self.sim)
179 
180  def Setup(self, spec):
181  """@implements SimCtl.Setup.
182  """
183  logging.info('SimCtl.Setup: spec="%s"', spec)
184  s = json.loads(spec)
185  try:
186  cmd = s.get("command")
187  action = s.get("action")
188  if action == "throw":
189  def throw(res, *args):
190  raise DaqException(self.sim.current_daq.id, "Simulated error")
191  self.sim.add_sim_hook(cmd, throw)
192  else:
193  raise SimCtlError("Unknown action '%s' provided" % action)
194  logging.info('CimCtl.Setup: Returning empty string')
195  return json.dumps("")
196  except SimCtlError:
197  raise
198  except Exception as e:
199  raise SimCtlError("\"%s\"" % str(e))
200 
201  def Reset(self):
202  """@implements SimCtl.Reset
203  """
204  try:
205  self.sim.reset()
206  except Exception as e:
207  raise SimCtlError(str(e))
208 
209  def ForceExit(self):
210  """@implements SimCtl.ForceExit.
211  """
212  # Notify main thread that it should exit
213  logging.warning("Force Exiting without cleanup!")
214  os._exit() # pylint: disable=protected-access
metadaqsim.simulator.Simulator.name
name
Definition: simulator.py:49
metadaqsim.simulator.SimulatorCtl.ForceExit
def ForceExit(self)
Definition: simulator.py:211
metadaqsim.simulator.SimulatorCtl.server
server
Definition: simulator.py:174
metadaqsim.simulator.Simulator.StopDaq
DaqStopReply StopDaq(self, str daq_id)
Definition: simulator.py:70
metadaqsim.simulator.SimulatorCtl.Setup
def Setup(self, spec)
Definition: simulator.py:182
metadaqsim.simulator.Daq
Holds simulated status for DAQ.
Definition: simulator.py:29
metadaqsim.simulator.Simulator.daqs
daqs
Definition: simulator.py:52
metadaqsim.simulator.Simulator.make_daq_reply
DaqReply make_daq_reply(self, daq_id)
Definition: simulator.py:101
metadaqsim.simulator.Simulator.AbortDaq
DaqReply AbortDaq(self, str daq_id)
Definition: simulator.py:79
metadaqsim.simulator.Simulator
Simulator that implements Modmetadaqsimif and sets up simulation behaviour for Simulator.
Definition: simulator.py:46
metadaqsim.simulator.Simulator.GetDaqStatus
DaqStatus GetDaqStatus(self, str daq_id)
Definition: simulator.py:89
metadaqsim.simulator.Simulator.run_sim_hook
def run_sim_hook(self, result, *args)
Runs simulation hook that may modify or replace the default implementation.
Definition: simulator.py:155
metadaqsim.simulator.Daq.state
state
Definition: simulator.py:33
metadaqsim.simulator.Daq.keywords
keywords
Definition: simulator.py:34
metadaqsim.simulator.Simulator.make_daq_stop_reply
DaqStopReply make_daq_stop_reply(self, daq_id)
Create DaqStopReply instance.
Definition: simulator.py:121
metadaqsim.simulator.Simulator.reset
def reset(self)
Reset simulator to initial default state.
Definition: simulator.py:139
metadaqsim.simulator.SimulatorCtl.Reset
def Reset(self)
Definition: simulator.py:203
metadaqsim.simulator.SimulatorCtl
Simulator controller that implements Modmetadaqsimif and sets up simulation behaviour for Simulator.
Definition: simulator.py:169
metadaqsim.simulator.Simulator.mal
mal
Definition: simulator.py:50
metadaqsim.simulator.Simulator.get_daq
Daq get_daq(self, daq_id)
Get daq or raise DaqException.
Definition: simulator.py:96
metadaqsim.simulator.Simulator.add_sim_hook
def add_sim_hook(self, str cmd_name, hook)
Add the simulation hook hook for the specified command cmd_name (e.g.
Definition: simulator.py:134
metadaqsim.simulator.SimulatorCtl.sim
sim
Definition: simulator.py:176
metadaqsim.simulator.Simulator.current_daq
current_daq
Definition: simulator.py:51
metadaqsim.simulator.Daq.files
files
Definition: simulator.py:35
metadaqsim.simulator.SimulatorCtl.name
name
Definition: simulator.py:172
metadaqsim.simulator.Simulator.sim_hooks
sim_hooks
Definition: simulator.py:54
metadaqsim.simulator.Simulator.make_daq_status
DaqStatus make_daq_status(self, daq_id)
Create DaqStatus instance.
Definition: simulator.py:110
metadaqsim.simulator.SimulatorCtl.__init__
def __init__(self, name, mal, server)
Definition: simulator.py:171
metadaqsim.simulator.Daq.__init__
def __init__(self, id)
Definition: simulator.py:31
metadaqsim.simulator.Simulator.clear_sim_hooks
def clear_sim_hooks(self)
Remove all simulation hooks.
Definition: simulator.py:146
metadaqsim.simulator.Daq.id
id
Definition: simulator.py:32
metadaqsim.simulator.Simulator.StartDaq
DaqReply StartDaq(self, str daq_id)
Definition: simulator.py:58
metadaqsim.simulator.Simulator.__init__
def __init__(self, name, mal)
Definition: simulator.py:48
metadaqsim.simulator.SimulatorCtl.mal
mal
Definition: simulator.py:173