openMSX
PluggingController.cc
Go to the documentation of this file.
1 #include "PluggingController.hh"
2 #include "PlugException.hh"
3 #include "Connector.hh"
4 #include "Pluggable.hh"
5 #include "PluggableFactory.hh"
6 #include "TclObject.hh"
7 #include "CommandException.hh"
8 #include "MSXMotherBoard.hh"
9 #include "CliComm.hh"
10 #include "outer.hh"
11 #include "ranges.hh"
12 #include "view.hh"
13 #include <iostream>
14 
15 using std::string;
16 using std::string_view;
17 
18 namespace openmsx {
19 
21  : motherBoard(motherBoard_)
22  , plugCmd(
23  motherBoard.getCommandController(),
24  motherBoard.getStateChangeDistributor(),
25  motherBoard.getScheduler())
26  , unplugCmd(
27  motherBoard.getCommandController(),
28  motherBoard.getStateChangeDistributor(),
29  motherBoard.getScheduler())
30  , pluggableInfo (motherBoard.getMachineInfoCommand())
31  , connectorInfo (motherBoard.getMachineInfoCommand())
32  , connectionClassInfo(motherBoard.getMachineInfoCommand())
33 {
34  PluggableFactory::createAll(*this, motherBoard);
35 }
36 
38 {
39 #ifndef NDEBUG
40  // This is similar to an assert: it should never print anything,
41  // but if it does, it helps to catch an error.
42  for (auto& c : connectors) {
43  std::cerr << "ERROR: Connector still registered at shutdown: "
44  << c->getName() << '\n';
45  }
46 #endif
47 }
48 
50 {
51  connectors.push_back(&connector);
52  getCliComm().update(CliComm::CONNECTOR, connector.getName(), "add");
53 }
54 
56 {
57  connector.unplug(motherBoard.getCurrentTime());
58  move_pop_back(connectors, rfind_unguarded(connectors, &connector));
59  getCliComm().update(CliComm::CONNECTOR, connector.getName(), "remove");
60 }
61 
62 
63 void PluggingController::registerPluggable(std::unique_ptr<Pluggable> pluggable)
64 {
65  pluggables.push_back(std::move(pluggable));
66 }
67 
68 
69 // === Commands ===
70 // plug command
71 
72 PluggingController::PlugCmd::PlugCmd(
73  CommandController& commandController_,
74  StateChangeDistributor& stateChangeDistributor_,
75  Scheduler& scheduler_)
76  : RecordedCommand(commandController_, stateChangeDistributor_,
77  scheduler_, "plug")
78 {
79 }
80 
81 void PluggingController::PlugCmd::execute(
82  span<const TclObject> tokens, TclObject& result_, EmuTime::param time)
83 {
84  checkNumArgs(tokens, Between{1, 3}, Prefix{1}, "?connector? ?pluggable?");
85  string result;
86  auto& pluggingController = OUTER(PluggingController, plugCmd);
87  switch (tokens.size()) {
88  case 1:
89  for (auto& c : pluggingController.connectors) {
90  strAppend(result, c->getName(), ": ",
91  c->getPlugged().getName(), '\n');
92  }
93  break;
94  case 2: {
95  auto& connector = pluggingController.getConnector(tokens[1].getString());
96  strAppend(result, connector.getName(), ": ",
97  connector.getPlugged().getName());
98  break;
99  }
100  case 3: {
101  string_view connName = tokens[1].getString();
102  string_view plugName = tokens[2].getString();
103  auto& connector = pluggingController.getConnector(connName);
104  auto& pluggable = pluggingController.getPluggable(plugName);
105  if (&connector.getPlugged() == &pluggable) {
106  // already plugged, don't unplug/replug
107  break;
108  }
109  if (connector.getClass() != pluggable.getClass()) {
110  throw CommandException("plug: ", plugName,
111  " doesn't fit in ", connName);
112  }
113  connector.unplug(time);
114  try {
115  connector.plug(pluggable, time);
116  pluggingController.getCliComm().update(
117  CliComm::PLUG, connName, plugName);
118  } catch (PlugException& e) {
119  throw CommandException("plug: plug failed: ", e.getMessage());
120  }
121  break;
122  }
123  }
124  result_ = result; // TODO return Tcl list
125 }
126 
127 string PluggingController::PlugCmd::help(span<const TclObject> /*tokens*/) const
128 {
129  return "Plugs a plug into a connector\n"
130  " plug [connector] [plug]";
131 }
132 
133 void PluggingController::PlugCmd::tabCompletion(std::vector<string>& tokens) const
134 {
135  auto& pluggingController = OUTER(PluggingController, plugCmd);
136  if (tokens.size() == 2) {
137  // complete connector
138  completeString(tokens, view::transform(
139  pluggingController.connectors,
140  [](auto& c) -> std::string_view { return c->getName(); }));
141  } else if (tokens.size() == 3) {
142  // complete pluggable
143  auto* connector = pluggingController.findConnector(tokens[1]);
144  string_view className = connector ? connector->getClass() : string_view{};
145  completeString(tokens, view::transform(view::filter(pluggingController.pluggables,
146  [&](auto& p) { return p->getClass() == className; }),
147  [](auto& p) -> string_view { return p->getName(); }));
148  }
149 }
150 
151 bool PluggingController::PlugCmd::needRecord(span<const TclObject> tokens) const
152 {
153  return tokens.size() == 3;
154 }
155 
156 
157 // unplug command
158 
159 PluggingController::UnplugCmd::UnplugCmd(
160  CommandController& commandController_,
161  StateChangeDistributor& stateChangeDistributor_,
162  Scheduler& scheduler_)
163  : RecordedCommand(commandController_, stateChangeDistributor_,
164  scheduler_, "unplug")
165 {
166 }
167 
168 void PluggingController::UnplugCmd::execute(
169  span<const TclObject> tokens, TclObject& /*result*/, EmuTime::param time)
170 {
171  checkNumArgs(tokens, 2, "connector");
172  auto& pluggingController = OUTER(PluggingController, unplugCmd);
173  string_view connName = tokens[1].getString();
174  auto& connector = pluggingController.getConnector(connName);
175  connector.unplug(time);
176  pluggingController.getCliComm().update(CliComm::PLUG, connName, {});
177 }
178 
179 string PluggingController::UnplugCmd::help(span<const TclObject> /*tokens*/) const
180 {
181  return "Unplugs a plug from a connector\n"
182  " unplug [connector]";
183 }
184 
185 void PluggingController::UnplugCmd::tabCompletion(std::vector<string>& tokens) const
186 {
187  if (tokens.size() == 2) {
188  // complete connector
189  completeString(tokens, view::transform(
190  OUTER(PluggingController, unplugCmd).connectors,
191  [](auto* c) -> std::string_view { return c->getName(); }));
192  }
193 }
194 
196 {
197  auto it = ranges::find(connectors, name, &Connector::getName);
198  return (it != end(connectors)) ? *it : nullptr;
199 }
200 
201 Connector& PluggingController::getConnector(string_view name) const
202 {
203  if (auto* result = findConnector(name)) {
204  return *result;
205  }
206  throw CommandException("No such connector: ", name);
207 }
208 
210 {
211  auto it = ranges::find(pluggables, name, &Pluggable::getName);
212  return (it != end(pluggables)) ? it->get() : nullptr;
213 }
214 
215 Pluggable& PluggingController::getPluggable(string_view name) const
216 {
217  if (auto* result = findPluggable(name)) {
218  return *result;
219  }
220  throw CommandException("No such pluggable: ", name);
221 }
222 
224 {
225  return motherBoard.getMSXCliComm();
226 }
227 
229 {
230  return motherBoard.getCurrentTime();
231 }
232 
233 
234 // Pluggable info
235 
236 PluggingController::PluggableInfo::PluggableInfo(
237  InfoCommand& machineInfoCommand)
238  : InfoTopic(machineInfoCommand, "pluggable")
239 {
240 }
241 
242 void PluggingController::PluggableInfo::execute(
243  span<const TclObject> tokens, TclObject& result) const
244 {
245  auto& pluggingController = OUTER(PluggingController, pluggableInfo);
246  switch (tokens.size()) {
247  case 2:
248  result.addListElements(
249  view::transform(pluggingController.pluggables,
250  [](auto& p) { return p->getName(); }));
251  break;
252  case 3: {
253  auto& pluggable = pluggingController.getPluggable(
254  tokens[2].getString());
255  result = pluggable.getDescription();
256  break;
257  }
258  default:
259  throw CommandException("Too many parameters");
260  }
261 }
262 
263 string PluggingController::PluggableInfo::help(span<const TclObject> /*tokens*/) const
264 {
265  return "Shows a list of available pluggables. "
266  "Or show info on a specific pluggable.";
267 }
268 
269 void PluggingController::PluggableInfo::tabCompletion(std::vector<string>& tokens) const
270 {
271  if (tokens.size() == 3) {
272  completeString(tokens, view::transform(
273  OUTER(PluggingController, pluggableInfo).pluggables,
274  [](auto& p) -> std::string_view { return p->getName(); }));
275  }
276 }
277 
278 // Connector info
279 
280 PluggingController::ConnectorInfo::ConnectorInfo(
281  InfoCommand& machineInfoCommand)
282  : InfoTopic(machineInfoCommand, "connector")
283 {
284 }
285 
286 void PluggingController::ConnectorInfo::execute(
287  span<const TclObject> tokens, TclObject& result) const
288 {
289  auto& pluggingController = OUTER(PluggingController, connectorInfo);
290  switch (tokens.size()) {
291  case 2:
292  result.addListElements(
293  view::transform(pluggingController.connectors,
294  [](auto& c) { return c->getName(); }));
295  break;
296  case 3: {
297  auto& connector = pluggingController.getConnector(tokens[2].getString());
298  result = connector.getDescription();
299  break;
300  }
301  default:
302  throw CommandException("Too many parameters");
303  }
304 }
305 
306 string PluggingController::ConnectorInfo::help(span<const TclObject> /*tokens*/) const
307 {
308  return "Shows a list of available connectors.";
309 }
310 
311 void PluggingController::ConnectorInfo::tabCompletion(std::vector<string>& tokens) const
312 {
313  if (tokens.size() == 3) {
314  completeString(tokens, view::transform(
315  OUTER(PluggingController, connectorInfo).connectors,
316  [](auto& c) -> std::string_view { return c->getName(); }));
317  }
318 }
319 
320 // Connection Class info
321 
322 PluggingController::ConnectionClassInfo::ConnectionClassInfo(
323  InfoCommand& machineInfoCommand)
324  : InfoTopic(machineInfoCommand, "connectionclass")
325 {
326 }
327 
328 void PluggingController::ConnectionClassInfo::execute(
329  span<const TclObject> tokens, TclObject& result) const
330 {
331  auto& pluggingController = OUTER(PluggingController, connectionClassInfo);
332  switch (tokens.size()) {
333  case 2: {
334  std::vector<string_view> classes;
335  classes.reserve(pluggingController.connectors.size());
336  for (auto& c : pluggingController.connectors) {
337  classes.push_back(c->getClass());
338  }
339  for (auto& p : pluggingController.pluggables) {
340  auto c = p->getClass();
341  if (!contains(classes, c)) classes.push_back(c);
342  }
343  result.addListElements(classes);
344  break;
345  }
346  case 3: {
347  const auto& arg = tokens[2].getString();
348  if (auto* connector = pluggingController.findConnector(arg)) {
349  result = connector->getClass();
350  break;
351  }
352  if (auto* pluggable = pluggingController.findPluggable(arg)) {
353  result = pluggable->getClass();
354  break;
355  }
356  throw CommandException("No such connector or pluggable");
357  }
358  default:
359  throw CommandException("Too many parameters");
360  }
361 }
362 
363 string PluggingController::ConnectionClassInfo::help(span<const TclObject> /*tokens*/) const
364 {
365  return "Shows the class a connector or pluggable belongs to.";
366 }
367 
368 void PluggingController::ConnectionClassInfo::tabCompletion(std::vector<string>& tokens) const
369 {
370  if (tokens.size() == 3) {
371  auto& pluggingController = OUTER(PluggingController, connectionClassInfo);
372  auto names = concat(
373  view::transform(pluggingController.connectors,
374  [](auto& c) -> std::string_view { return c->getName(); }),
375  view::transform(pluggingController.pluggables,
376  [](auto& p) -> std::string_view { return p->getName(); }));
377  completeString(tokens, names);
378  }
379 }
380 
381 } // namespace openmsx
virtual void update(UpdateType type, std::string_view name, std::string_view value)=0
Represents something you can plug devices into.
Definition: Connector.hh:21
const std::string & getName() const
Name that identifies this connector.
Definition: Connector.hh:29
virtual void unplug(EmuTime::param time)
This unplugs the currently inserted Pluggable from this Connector.
Definition: Connector.cc:31
EmuTime::param getCurrentTime()
Convenience method: This is the same as getScheduler().getCurrentTime().
static void createAll(PluggingController &controller, MSXMotherBoard &motherBoard)
virtual std::string_view getName() const
Name used to identify this pluggable.
Definition: Pluggable.cc:14
void registerPluggable(std::unique_ptr< Pluggable > pluggable)
Add a Pluggable to the registry.
void unregisterConnector(Connector &connector)
Pluggable * findPluggable(std::string_view name) const
Return the Pluggable with given name or nullptr if there is none with this name.
PluggingController(MSXMotherBoard &motherBoard)
void registerConnector(Connector &connector)
Connectors must be (un)registered.
Connector * findConnector(std::string_view name) const
Return the Connector with given name or nullptr if there is none with this name.
CliComm & getCliComm()
Access to the MSX specific CliComm, so that Connectors can get it.
EmuTime::param getCurrentTime() const
Convenience method: get current time.
Commands that directly influence the MSX state should send and events so that they can be recorded by...
Definition: span.hh:126
constexpr index_type size() const noexcept
Definition: span.hh:296
This file implemented 3 utility functions:
Definition: Autofire.cc:9
auto find(InputRange &&range, const T &value)
Definition: ranges.hh:130
auto filter(ForwardRange &&range, Predicate pred)
Definition: view.hh:410
constexpr auto transform(Range &&range, UnaryOp op)
Definition: view.hh:392
#define OUTER(type, member)
Definition: outer.hh:41
void move_pop_back(VECTOR &v, typename VECTOR::iterator it)
Erase the pointed to element from the given vector.
Definition: stl.hh:134
auto rfind_unguarded(RANGE &range, const VAL &val, Proj proj={})
Similar to the find(_if)_unguarded functions above, but searches from the back to front.
Definition: stl.hh:109
auto concat(const Range &range, Tail &&... tail)
Definition: stl.hh:372
constexpr bool contains(ITER first, ITER last, const VAL &val)
Check if a range contains a given value, using linear search.
Definition: stl.hh:32
void strAppend(std::string &result, Ts &&...ts)
Definition: strCat.hh:669
constexpr auto end(const zstring_view &x)
Definition: zstring_view.hh:84