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 "openmsx.hh"
15 #include <cassert>
16 #include <iostream>
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  , cliComm(motherBoard.getMSXCliComm())
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  cliComm.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  cliComm.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.cliComm.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.cliComm.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 cliComm;
318 }
319 
320 
321 // Pluggable info
322 
324  PluggingController& pluggingController_)
325  : InfoTopic(machineInfoCommand, "pluggable")
326  , pluggingController(pluggingController_)
327 {
328 }
329 
330 void PluggableInfo::execute(const vector<TclObject>& tokens,
331  TclObject& result) const
332 {
333  switch (tokens.size()) {
334  case 2:
335  for (auto& p : pluggingController.pluggables) {
336  result.addListElement(p->getName());
337  }
338  break;
339  case 3: {
340  auto& pluggable = pluggingController.getPluggable(
341  tokens[2].getString());
342  result.setString(pluggable.getDescription());
343  break;
344  }
345  default:
346  throw CommandException("Too many parameters");
347  }
348 }
349 
350 string PluggableInfo::help(const vector<string>& /*tokens*/) const
351 {
352  return "Shows a list of available pluggables. "
353  "Or show info on a specific pluggable.";
354 }
355 
356 void PluggableInfo::tabCompletion(vector<string>& tokens) const
357 {
358  if (tokens.size() == 3) {
359  vector<string_ref> pluggables;
360  for (auto& p : pluggingController.pluggables) {
361  pluggables.push_back(p->getName());
362  }
363  completeString(tokens, pluggables);
364  }
365 }
366 
367 // Connector info
368 
370  PluggingController& pluggingController_)
371  : InfoTopic(machineInfoCommand, "connector")
372  , pluggingController(pluggingController_)
373 {
374 }
375 
376 void ConnectorInfo::execute(const vector<TclObject>& tokens,
377  TclObject& result) const
378 {
379  switch (tokens.size()) {
380  case 2:
381  for (auto& c : pluggingController.connectors) {
382  result.addListElement(c->getName());
383  }
384  break;
385  case 3: {
386  auto& connector = pluggingController.getConnector(tokens[2].getString());
387  result.setString(connector.getDescription());
388  break;
389  }
390  default:
391  throw CommandException("Too many parameters");
392  }
393 }
394 
395 string ConnectorInfo::help(const vector<string>& /*tokens*/) const
396 {
397  return "Shows a list of available connectors.";
398 }
399 
400 void ConnectorInfo::tabCompletion(vector<string>& tokens) const
401 {
402  if (tokens.size() == 3) {
403  vector<string_ref> connectors;
404  for (auto& c : pluggingController.connectors) {
405  connectors.push_back(c->getName());
406  }
407  completeString(tokens, connectors);
408  }
409 }
410 
411 // Connection Class info
412 
414  InfoCommand& machineInfoCommand,
415  PluggingController& pluggingController_)
416  : InfoTopic(machineInfoCommand, "connectionclass")
417  , pluggingController(pluggingController_)
418 {
419 }
420 
421 void ConnectionClassInfo::execute(const vector<TclObject>& tokens,
422  TclObject& result) const
423 {
424  switch (tokens.size()) {
425  case 2: {
426  std::set<string_ref> classes; // filter duplicates
427  for (auto& c : pluggingController.connectors) {
428  classes.insert(c->getClass());
429  }
430  for (auto& p : pluggingController.pluggables) {
431  classes.insert(p->getClass());
432  }
433  result.addListElements(classes);
434  break;
435  }
436  case 3: {
437  const auto& arg = tokens[2].getString();
438  if (auto* connector = pluggingController.findConnector(arg)) {
439  result.setString(connector->getClass());
440  break;
441  }
442  if (auto* pluggable = pluggingController.findPluggable(arg)) {
443  result.setString(pluggable->getClass());
444  break;
445  }
446  throw CommandException("No such connector or pluggable");
447  break;
448  }
449  default:
450  throw CommandException("Too many parameters");
451  }
452 }
453 
454 string ConnectionClassInfo::help(const vector<string>& /*tokens*/) const
455 {
456  return "Shows the class a connector or pluggable belongs to.";
457 }
458 
459 void ConnectionClassInfo::tabCompletion(vector<string>& tokens) const
460 {
461  if (tokens.size() == 3) {
462  vector<string_ref> names;
463  for (auto& c : pluggingController.connectors) {
464  names.push_back(c->getName());
465  }
466  for (auto& p : pluggingController.pluggables) {
467  names.push_back(p->getName());
468  }
469  completeString(tokens, names);
470  }
471 }
472 
473 } // namespace openmsx