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