openMSX
PluggingController.cc
Go to the documentation of this file.
1 #include "PluggingController.hh"
2 #include "PlugException.hh"
3 #include "RecordedCommand.hh"
4 #include "InfoTopic.hh"
5 #include "Connector.hh"
6 #include "Pluggable.hh"
7 #include "PluggableFactory.hh"
8 #include "TclObject.hh"
9 #include "CommandException.hh"
10 #include "MSXMotherBoard.hh"
11 #include "CliComm.hh"
12 #include "StringOp.hh"
13 #include "memory.hh"
14 #include "stl.hh"
15 #include <cassert>
16 #include <iostream>
17 #include <set>
18 
19 using std::string;
20 using std::vector;
21 
22 namespace openmsx {
23 
24 class PlugCmd final : public RecordedCommand
25 {
26 public:
27  PlugCmd(CommandController& commandController,
28  StateChangeDistributor& stateChangeDistributor,
29  Scheduler& scheduler,
30  PluggingController& pluggingController);
31  void execute(array_ref<TclObject> tokens, TclObject& result,
32  EmuTime::param time) override;
33  string help (const vector<string>& tokens) const override;
34  void tabCompletion(vector<string>& tokens) const override;
35  bool needRecord(array_ref<TclObject> tokens) const override;
36 private:
37  PluggingController& pluggingController;
38 };
39 
40 class UnplugCmd final : public RecordedCommand
41 {
42 public:
43  UnplugCmd(CommandController& commandController,
44  StateChangeDistributor& stateChangeDistributor,
45  Scheduler& scheduler,
46  PluggingController& pluggingController);
47  void execute(array_ref<TclObject> tokens, TclObject& result,
48  EmuTime::param time) override;
49  string help (const vector<string>& tokens) const override;
50  void tabCompletion(vector<string>& tokens) const override;
51 private:
52  PluggingController& pluggingController;
53 };
54 
55 class PluggableInfo final : public InfoTopic
56 {
57 public:
58  PluggableInfo(InfoCommand& machineInfoCommand,
59  PluggingController& pluggingController);
60  void execute(array_ref<TclObject> tokens,
61  TclObject& result) const override;
62  string help(const vector<string>& tokens) const override;
63  void tabCompletion(vector<string>& tokens) const override;
64 private:
65  PluggingController& pluggingController;
66 };
67 
68 class ConnectorInfo final : public InfoTopic
69 {
70 public:
71  ConnectorInfo(InfoCommand& machineInfoCommand,
72  PluggingController& pluggingController);
73  void execute(array_ref<TclObject> tokens,
74  TclObject& result) const override;
75  string help(const vector<string>& tokens) const override;
76  void tabCompletion(vector<string>& tokens) const override;
77 private:
78  PluggingController& pluggingController;
79 };
80 
81 class ConnectionClassInfo final : public InfoTopic
82 {
83 public:
84  ConnectionClassInfo(InfoCommand& machineInfoCommand,
85  PluggingController& pluggingController);
86  void execute(array_ref<TclObject> tokens,
87  TclObject& result) const override;
88  string help(const vector<string>& tokens) const override;
89  void tabCompletion(vector<string>& tokens) const override;
90 private:
91  PluggingController& pluggingController;
92 };
93 
94 
96  : plugCmd(make_unique<PlugCmd>(
97  motherBoard_.getCommandController(),
98  motherBoard_.getStateChangeDistributor(),
99  motherBoard_.getScheduler(), *this))
100  , unplugCmd(make_unique<UnplugCmd>(
101  motherBoard_.getCommandController(),
102  motherBoard_.getStateChangeDistributor(),
103  motherBoard_.getScheduler(), *this))
104  , pluggableInfo(make_unique<PluggableInfo>(
105  motherBoard_.getMachineInfoCommand(), *this))
106  , connectorInfo(make_unique<ConnectorInfo>(
107  motherBoard_.getMachineInfoCommand(), *this))
108  , connectionClassInfo(make_unique<ConnectionClassInfo>(
109  motherBoard_.getMachineInfoCommand(), *this))
110  , motherBoard(motherBoard_)
111 {
112  PluggableFactory::createAll(*this, motherBoard);
113 }
114 
116 {
117 #ifndef NDEBUG
118  // This is similar to an assert: it should never print anything,
119  // but if it does, it helps to catch an error.
120  for (auto& c : connectors) {
121  std::cerr << "ERROR: Connector still registered at shutdown: "
122  << c->getName() << std::endl;
123  }
124 #endif
125 }
126 
128 {
129  connectors.push_back(&connector);
130  getCliComm().update(CliComm::CONNECTOR, connector.getName(), "add");
131 }
132 
134 {
135  connectors.erase(find_unguarded(connectors, &connector));
136  getCliComm().update(CliComm::CONNECTOR, connector.getName(), "remove");
137 }
138 
139 
140 void PluggingController::registerPluggable(std::unique_ptr<Pluggable> pluggable)
141 {
142  pluggables.push_back(std::move(pluggable));
143 }
144 
145 
146 // === Commands ===
147 // plug command
148 
150  StateChangeDistributor& stateChangeDistributor,
151  Scheduler& scheduler,
152  PluggingController& pluggingController_)
153  : RecordedCommand(commandController, stateChangeDistributor,
154  scheduler, "plug")
155  , pluggingController(pluggingController_)
156 {
157 }
158 
160  EmuTime::param time)
161 {
162  StringOp::Builder result;
163  switch (tokens.size()) {
164  case 1:
165  for (auto& c : pluggingController.connectors) {
166  result << c->getName() << ": "
167  << c->getPlugged().getName() << '\n';
168  }
169  break;
170  case 2: {
171  auto& connector = pluggingController.getConnector(tokens[1].getString());
172  result << connector.getName() << ": "
173  << connector.getPlugged().getName();
174  break;
175  }
176  case 3: {
177  string_ref connName = tokens[1].getString();
178  string_ref plugName = tokens[2].getString();
179  auto& connector = pluggingController.getConnector(connName);
180  auto& pluggable = pluggingController.getPluggable(plugName);
181  if (&connector.getPlugged() == &pluggable) {
182  // already plugged, don't unplug/replug
183  break;
184  }
185  if (connector.getClass() != pluggable.getClass()) {
186  throw CommandException("plug: " + plugName +
187  " doesn't fit in " + connName);
188  }
189  connector.unplug(time);
190  try {
191  connector.plug(pluggable, time);
192  pluggingController.getCliComm().update(
193  CliComm::PLUG, connName, plugName);
194  } catch (PlugException& e) {
195  throw CommandException("plug: plug failed: " + e.getMessage());
196  }
197  break;
198  }
199  default:
200  throw SyntaxError();
201  }
202  result_.setString(result); // TODO return Tcl list
203 }
204 
205 string PlugCmd::help(const vector<string>& /*tokens*/) const
206 {
207  return "Plugs a plug into a connector\n"
208  " plug [connector] [plug]";
209 }
210 
211 void PlugCmd::tabCompletion(vector<string>& tokens) const
212 {
213  if (tokens.size() == 2) {
214  // complete connector
215  vector<string_ref> connectors;
216  for (auto& c : pluggingController.connectors) {
217  connectors.push_back(c->getName());
218  }
219  completeString(tokens, connectors);
220  } else if (tokens.size() == 3) {
221  // complete pluggable
222  vector<string_ref> pluggables;
223  auto* connector = pluggingController.findConnector(tokens[1]);
224  string_ref className = connector ? connector->getClass() : "";
225  for (auto& p : pluggingController.pluggables) {
226  if (p->getClass() == className) {
227  pluggables.push_back(p->getName());
228  }
229  }
230  completeString(tokens, pluggables);
231  }
232 }
233 
235 {
236  return tokens.size() == 3;
237 }
238 
239 
240 // unplug command
241 
243  StateChangeDistributor& stateChangeDistributor,
244  Scheduler& scheduler,
245  PluggingController& pluggingController_)
246  : RecordedCommand(commandController, stateChangeDistributor,
247  scheduler, "unplug")
248  , pluggingController(pluggingController_)
249 {
250 }
251 
253  EmuTime::param time)
254 {
255  if (tokens.size() != 2) {
256  throw SyntaxError();
257  }
258  string_ref connName = tokens[1].getString();
259  auto& connector = pluggingController.getConnector(connName);
260  connector.unplug(time);
261  pluggingController.getCliComm().update(CliComm::UNPLUG, connName, "");
262 }
263 
264 string UnplugCmd::help(const vector<string>& /*tokens*/) const
265 {
266  return "Unplugs a plug from a connector\n"
267  " unplug [connector]";
268 }
269 
270 void UnplugCmd::tabCompletion(vector<string>& tokens) const
271 {
272  if (tokens.size() == 2) {
273  // complete connector
274  vector<string_ref> connectors;
275  for (auto& c : pluggingController.connectors) {
276  connectors.push_back(c->getName());
277  }
278  completeString(tokens, connectors);
279  }
280 }
281 
283 {
284  for (auto& c : connectors) {
285  if (c->getName() == name) {
286  return c;
287  }
288  }
289  return nullptr;
290 }
291 
292 Connector& PluggingController::getConnector(string_ref name) const
293 {
294  if (auto* result = findConnector(name)) {
295  return *result;
296  }
297  throw CommandException("No such connector: " + name);
298 }
299 
301 {
302  for (auto& p : pluggables) {
303  if (p->getName() == name) {
304  return p.get();
305  }
306  }
307  return nullptr;
308 }
309 
310 Pluggable& PluggingController::getPluggable(string_ref name) const
311 {
312  if (auto* result = findPluggable(name)) {
313  return *result;
314  }
315  throw CommandException("No such pluggable: " + name);
316 }
317 
319 {
320  return motherBoard.getMSXCliComm();
321 }
322 
324 {
325  return motherBoard.getCurrentTime();
326 }
327 
328 
329 // Pluggable info
330 
332  PluggingController& pluggingController_)
333  : InfoTopic(machineInfoCommand, "pluggable")
334  , pluggingController(pluggingController_)
335 {
336 }
337 
339  TclObject& result) const
340 {
341  switch (tokens.size()) {
342  case 2:
343  for (auto& p : pluggingController.pluggables) {
344  result.addListElement(p->getName());
345  }
346  break;
347  case 3: {
348  auto& pluggable = pluggingController.getPluggable(
349  tokens[2].getString());
350  result.setString(pluggable.getDescription());
351  break;
352  }
353  default:
354  throw CommandException("Too many parameters");
355  }
356 }
357 
358 string PluggableInfo::help(const vector<string>& /*tokens*/) const
359 {
360  return "Shows a list of available pluggables. "
361  "Or show info on a specific pluggable.";
362 }
363 
364 void PluggableInfo::tabCompletion(vector<string>& tokens) const
365 {
366  if (tokens.size() == 3) {
367  vector<string_ref> pluggables;
368  for (auto& p : pluggingController.pluggables) {
369  pluggables.push_back(p->getName());
370  }
371  completeString(tokens, pluggables);
372  }
373 }
374 
375 // Connector info
376 
378  PluggingController& pluggingController_)
379  : InfoTopic(machineInfoCommand, "connector")
380  , pluggingController(pluggingController_)
381 {
382 }
383 
385  TclObject& result) const
386 {
387  switch (tokens.size()) {
388  case 2:
389  for (auto& c : pluggingController.connectors) {
390  result.addListElement(c->getName());
391  }
392  break;
393  case 3: {
394  auto& connector = pluggingController.getConnector(tokens[2].getString());
395  result.setString(connector.getDescription());
396  break;
397  }
398  default:
399  throw CommandException("Too many parameters");
400  }
401 }
402 
403 string ConnectorInfo::help(const vector<string>& /*tokens*/) const
404 {
405  return "Shows a list of available connectors.";
406 }
407 
408 void ConnectorInfo::tabCompletion(vector<string>& tokens) const
409 {
410  if (tokens.size() == 3) {
411  vector<string_ref> connectors;
412  for (auto& c : pluggingController.connectors) {
413  connectors.push_back(c->getName());
414  }
415  completeString(tokens, connectors);
416  }
417 }
418 
419 // Connection Class info
420 
422  InfoCommand& machineInfoCommand,
423  PluggingController& pluggingController_)
424  : InfoTopic(machineInfoCommand, "connectionclass")
425  , pluggingController(pluggingController_)
426 {
427 }
428 
430  TclObject& result) const
431 {
432  switch (tokens.size()) {
433  case 2: {
434  std::set<string_ref> classes; // filter duplicates
435  for (auto& c : pluggingController.connectors) {
436  classes.insert(c->getClass());
437  }
438  for (auto& p : pluggingController.pluggables) {
439  classes.insert(p->getClass());
440  }
441  result.addListElements(classes);
442  break;
443  }
444  case 3: {
445  const auto& arg = tokens[2].getString();
446  if (auto* connector = pluggingController.findConnector(arg)) {
447  result.setString(connector->getClass());
448  break;
449  }
450  if (auto* pluggable = pluggingController.findPluggable(arg)) {
451  result.setString(pluggable->getClass());
452  break;
453  }
454  throw CommandException("No such connector or pluggable");
455  break;
456  }
457  default:
458  throw CommandException("Too many parameters");
459  }
460 }
461 
462 string ConnectionClassInfo::help(const vector<string>& /*tokens*/) const
463 {
464  return "Shows the class a connector or pluggable belongs to.";
465 }
466 
467 void ConnectionClassInfo::tabCompletion(vector<string>& tokens) const
468 {
469  if (tokens.size() == 3) {
470  vector<string_ref> names;
471  for (auto& c : pluggingController.connectors) {
472  names.push_back(c->getName());
473  }
474  for (auto& p : pluggingController.pluggables) {
475  names.push_back(p->getName());
476  }
477  completeString(tokens, names);
478  }
479 }
480 
481 } // namespace openmsx
bool needRecord(array_ref< TclObject > tokens) const override
It's possible that in some cases the command doesn't need to be recorded after all (e...
Represents something you can plug devices into.
Definition: Connector.hh:21
size_type size() const
Definition: array_ref.hh:61
ITER find_unguarded(ITER first, ITER last, const VAL &val)
Faster alternative to 'find' when it's guaranteed that the value will be found (if not the behavior i...
Definition: stl.hh:114
string help(const vector< string > &tokens) const override
Print help for this command.
Commands that directly influence the MSX state should send and events so that they can be recorded by...
void registerConnector(Connector &connector)
Connectors must be (un)registered.
This class implements a subset of the proposal for std::string_ref (proposed for the next c++ standar...
Definition: string_ref.hh:18
Central administration of Connectors and Pluggables.
Pluggable * findPluggable(string_ref name) const
Return the Pluggable with given name or nullptr if there is none with this name.
PluggingController(MSXMotherBoard &motherBoard)
void tabCompletion(vector< string > &tokens) const override
Attempt tab completion for this topic.
CliComm & getCliComm()
Access to the MSX specific CliComm, so that Connectors can get it.
ConnectionClassInfo(InfoCommand &machineInfoCommand, PluggingController &pluggingController)
PlugCmd(CommandController &commandController, StateChangeDistributor &stateChangeDistributor, Scheduler &scheduler, PluggingController &pluggingController)
virtual void update(UpdateType type, string_ref name, string_ref value)=0
string help(const vector< string > &tokens) const override
Print help for this command.
This class implements a subset of the proposal for std::array_ref (proposed for the next c++ standard...
Definition: array_ref.hh:19
void tabCompletion(vector< string > &tokens) const override
Attempt tab completion for this command.
void tabCompletion(vector< string > &tokens) const override
Attempt tab completion for this topic.
string help(const vector< string > &tokens) const override
Print help for this topic.
const std::string & getMessage() const
Definition: MSXException.hh:14
static void createAll(PluggingController &controller, MSXMotherBoard &motherBoard)
void execute(array_ref< TclObject > tokens, TclObject &result) const override
Show info on this topic.
PluggableInfo(InfoCommand &machineInfoCommand, PluggingController &pluggingController)
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:7
Connector * findConnector(string_ref name) const
Return the Connector with given name or nullptr if there is none with this name.
void tabCompletion(vector< string > &tokens) const override
Attempt tab completion for this topic.
void registerPluggable(std::unique_ptr< Pluggable > pluggable)
Add a Pluggable to the registry.
void addListElement(string_ref element)
Definition: TclObject.cc:110
static void completeString(std::vector< std::string > &tokens, const RANGE &possibleValues, bool caseSensitive=true)
Definition: Completer.hh:88
void setString(string_ref value)
Definition: TclObject.cc:55
void execute(array_ref< TclObject > tokens, TclObject &result, EmuTime::param time) override
This is like the execute() method of the Command class, it only has an extra time parameter...
void execute(array_ref< TclObject > tokens, TclObject &result, EmuTime::param time) override
This is like the execute() method of the Command class, it only has an extra time parameter...
void execute(array_ref< TclObject > tokens, TclObject &result) const override
Show info on this topic.
EmuTime::param getCurrentTime()
Convenience method: This is the same as getScheduler().getCurrentTime().
void execute(array_ref< TclObject > tokens, TclObject &result) const override
Show info on this topic.
void unregisterConnector(Connector &connector)
Thrown when a plug action fails.
virtual void unplug(EmuTime::param time)
This unplugs the currently inserted Pluggable from this Connector.
Definition: Connector.cc:30
const std::string & getName() const
Name that identifies this connector.
Definition: Connector.hh:27
UnplugCmd(CommandController &commandController, StateChangeDistributor &stateChangeDistributor, Scheduler &scheduler, PluggingController &pluggingController)
void addListElements(ITER begin, ITER end)
Definition: TclObject.hh:80
ConnectorInfo(InfoCommand &machineInfoCommand, PluggingController &pluggingController)
std::unique_ptr< T > make_unique()
Definition: memory.hh:27
string help(const vector< string > &tokens) const override
Print help for this topic.
void tabCompletion(vector< string > &tokens) const override
Attempt tab completion for this command.
string help(const vector< string > &tokens) const override
Print help for this topic.
EmuTime::param getCurrentTime() const
Convenience method: get current time.