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 "StringOp.hh"
11 #include "outer.hh"
12 #include "stl.hh"
13 #include <cassert>
14 #include <iostream>
15 #include <set>
16 
17 using std::string;
18 using std::vector;
19 
20 namespace openmsx {
21 
23  : motherBoard(motherBoard_)
24  , plugCmd(
25  motherBoard.getCommandController(),
26  motherBoard.getStateChangeDistributor(),
27  motherBoard.getScheduler())
28  , unplugCmd(
29  motherBoard.getCommandController(),
30  motherBoard.getStateChangeDistributor(),
31  motherBoard.getScheduler())
32  , pluggableInfo (motherBoard.getMachineInfoCommand())
33  , connectorInfo (motherBoard.getMachineInfoCommand())
34  , connectionClassInfo(motherBoard.getMachineInfoCommand())
35 {
36  PluggableFactory::createAll(*this, motherBoard);
37 }
38 
40 {
41 #ifndef NDEBUG
42  // This is similar to an assert: it should never print anything,
43  // but if it does, it helps to catch an error.
44  for (auto& c : connectors) {
45  std::cerr << "ERROR: Connector still registered at shutdown: "
46  << c->getName() << std::endl;
47  }
48 #endif
49 }
50 
52 {
53  connectors.push_back(&connector);
54  getCliComm().update(CliComm::CONNECTOR, connector.getName(), "add");
55 }
56 
58 {
59  connectors.erase(find_unguarded(connectors, &connector));
60  getCliComm().update(CliComm::CONNECTOR, connector.getName(), "remove");
61 }
62 
63 
64 void PluggingController::registerPluggable(std::unique_ptr<Pluggable> pluggable)
65 {
66  pluggables.push_back(std::move(pluggable));
67 }
68 
69 
70 // === Commands ===
71 // plug command
72 
73 PluggingController::PlugCmd::PlugCmd(
74  CommandController& commandController,
75  StateChangeDistributor& stateChangeDistributor,
76  Scheduler& scheduler)
77  : RecordedCommand(commandController, stateChangeDistributor,
78  scheduler, "plug")
79 {
80 }
81 
82 void PluggingController::PlugCmd::execute(
83  array_ref<TclObject> tokens, TclObject& result_, EmuTime::param time)
84 {
85  StringOp::Builder result;
86  auto& pluggingController = OUTER(PluggingController, plugCmd);
87  switch (tokens.size()) {
88  case 1:
89  for (auto& c : pluggingController.connectors) {
90  result << c->getName() << ": "
91  << c->getPlugged().getName() << '\n';
92  }
93  break;
94  case 2: {
95  auto& connector = pluggingController.getConnector(tokens[1].getString());
96  result << connector.getName() << ": "
97  << connector.getPlugged().getName();
98  break;
99  }
100  case 3: {
101  string_ref connName = tokens[1].getString();
102  string_ref 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  default:
124  throw SyntaxError();
125  }
126  result_.setString(result); // TODO return Tcl list
127 }
128 
129 string PluggingController::PlugCmd::help(const vector<string>& /*tokens*/) const
130 {
131  return "Plugs a plug into a connector\n"
132  " plug [connector] [plug]";
133 }
134 
135 void PluggingController::PlugCmd::tabCompletion(vector<string>& tokens) const
136 {
137  auto& pluggingController = OUTER(PluggingController, plugCmd);
138  if (tokens.size() == 2) {
139  // complete connector
140  vector<string_ref> connectors;
141  for (auto& c : pluggingController.connectors) {
142  connectors.push_back(c->getName());
143  }
144  completeString(tokens, connectors);
145  } else if (tokens.size() == 3) {
146  // complete pluggable
147  vector<string_ref> pluggables;
148  auto* connector = pluggingController.findConnector(tokens[1]);
149  string_ref className = connector ? connector->getClass() : "";
150  for (auto& p : pluggingController.pluggables) {
151  if (p->getClass() == className) {
152  pluggables.push_back(p->getName());
153  }
154  }
155  completeString(tokens, pluggables);
156  }
157 }
158 
159 bool PluggingController::PlugCmd::needRecord(array_ref<TclObject> tokens) const
160 {
161  return tokens.size() == 3;
162 }
163 
164 
165 // unplug command
166 
167 PluggingController::UnplugCmd::UnplugCmd(
168  CommandController& commandController,
169  StateChangeDistributor& stateChangeDistributor,
170  Scheduler& scheduler)
171  : RecordedCommand(commandController, stateChangeDistributor,
172  scheduler, "unplug")
173 {
174 }
175 
176 void PluggingController::UnplugCmd::execute(
177  array_ref<TclObject> tokens, TclObject& /*result*/, EmuTime::param time)
178 {
179  if (tokens.size() != 2) {
180  throw SyntaxError();
181  }
182  auto& pluggingController = OUTER(PluggingController, unplugCmd);
183  string_ref connName = tokens[1].getString();
184  auto& connector = pluggingController.getConnector(connName);
185  connector.unplug(time);
186  pluggingController.getCliComm().update(CliComm::UNPLUG, connName, "");
187 }
188 
189 string PluggingController::UnplugCmd::help(const vector<string>& /*tokens*/) const
190 {
191  return "Unplugs a plug from a connector\n"
192  " unplug [connector]";
193 }
194 
195 void PluggingController::UnplugCmd::tabCompletion(vector<string>& tokens) const
196 {
197  if (tokens.size() == 2) {
198  // complete connector
199  vector<string_ref> connectors;
200  auto& pluggingController = OUTER(PluggingController, unplugCmd);
201  for (auto& c : pluggingController.connectors) {
202  connectors.push_back(c->getName());
203  }
204  completeString(tokens, connectors);
205  }
206 }
207 
209 {
210  for (auto& c : connectors) {
211  if (c->getName() == name) {
212  return c;
213  }
214  }
215  return nullptr;
216 }
217 
218 Connector& PluggingController::getConnector(string_ref name) const
219 {
220  if (auto* result = findConnector(name)) {
221  return *result;
222  }
223  throw CommandException("No such connector: " + name);
224 }
225 
227 {
228  for (auto& p : pluggables) {
229  if (p->getName() == name) {
230  return p.get();
231  }
232  }
233  return nullptr;
234 }
235 
236 Pluggable& PluggingController::getPluggable(string_ref name) const
237 {
238  if (auto* result = findPluggable(name)) {
239  return *result;
240  }
241  throw CommandException("No such pluggable: " + name);
242 }
243 
245 {
246  return motherBoard.getMSXCliComm();
247 }
248 
250 {
251  return motherBoard.getCurrentTime();
252 }
253 
254 
255 // Pluggable info
256 
257 PluggingController::PluggableInfo::PluggableInfo(
258  InfoCommand& machineInfoCommand)
259  : InfoTopic(machineInfoCommand, "pluggable")
260 {
261 }
262 
263 void PluggingController::PluggableInfo::execute(
264  array_ref<TclObject> tokens, TclObject& result) const
265 {
266  auto& pluggingController = OUTER(PluggingController, pluggableInfo);
267  switch (tokens.size()) {
268  case 2:
269  for (auto& p : pluggingController.pluggables) {
270  result.addListElement(p->getName());
271  }
272  break;
273  case 3: {
274  auto& pluggable = pluggingController.getPluggable(
275  tokens[2].getString());
276  result.setString(pluggable.getDescription());
277  break;
278  }
279  default:
280  throw CommandException("Too many parameters");
281  }
282 }
283 
284 string PluggingController::PluggableInfo::help(const vector<string>& /*tokens*/) const
285 {
286  return "Shows a list of available pluggables. "
287  "Or show info on a specific pluggable.";
288 }
289 
290 void PluggingController::PluggableInfo::tabCompletion(vector<string>& tokens) const
291 {
292  if (tokens.size() == 3) {
293  vector<string_ref> pluggables;
294  auto& pluggingController = OUTER(PluggingController, pluggableInfo);
295  for (auto& p : pluggingController.pluggables) {
296  pluggables.push_back(p->getName());
297  }
298  completeString(tokens, pluggables);
299  }
300 }
301 
302 // Connector info
303 
304 PluggingController::ConnectorInfo::ConnectorInfo(
305  InfoCommand& machineInfoCommand)
306  : InfoTopic(machineInfoCommand, "connector")
307 {
308 }
309 
310 void PluggingController::ConnectorInfo::execute(
311  array_ref<TclObject> tokens, TclObject& result) const
312 {
313  auto& pluggingController = OUTER(PluggingController, connectorInfo);
314  switch (tokens.size()) {
315  case 2:
316  for (auto& c : pluggingController.connectors) {
317  result.addListElement(c->getName());
318  }
319  break;
320  case 3: {
321  auto& connector = pluggingController.getConnector(tokens[2].getString());
322  result.setString(connector.getDescription());
323  break;
324  }
325  default:
326  throw CommandException("Too many parameters");
327  }
328 }
329 
330 string PluggingController::ConnectorInfo::help(const vector<string>& /*tokens*/) const
331 {
332  return "Shows a list of available connectors.";
333 }
334 
335 void PluggingController::ConnectorInfo::tabCompletion(vector<string>& tokens) const
336 {
337  if (tokens.size() == 3) {
338  vector<string_ref> connectors;
339  auto& pluggingController = OUTER(PluggingController, connectorInfo);
340  for (auto& c : pluggingController.connectors) {
341  connectors.push_back(c->getName());
342  }
343  completeString(tokens, connectors);
344  }
345 }
346 
347 // Connection Class info
348 
349 PluggingController::ConnectionClassInfo::ConnectionClassInfo(
350  InfoCommand& machineInfoCommand)
351  : InfoTopic(machineInfoCommand, "connectionclass")
352 {
353 }
354 
355 void PluggingController::ConnectionClassInfo::execute(
356  array_ref<TclObject> tokens, TclObject& result) const
357 {
358  auto& pluggingController = OUTER(PluggingController, connectionClassInfo);
359  switch (tokens.size()) {
360  case 2: {
361  std::set<string_ref> classes; // filter duplicates
362  for (auto& c : pluggingController.connectors) {
363  classes.insert(c->getClass());
364  }
365  for (auto& p : pluggingController.pluggables) {
366  classes.insert(p->getClass());
367  }
368  result.addListElements(classes);
369  break;
370  }
371  case 3: {
372  const auto& arg = tokens[2].getString();
373  if (auto* connector = pluggingController.findConnector(arg)) {
374  result.setString(connector->getClass());
375  break;
376  }
377  if (auto* pluggable = pluggingController.findPluggable(arg)) {
378  result.setString(pluggable->getClass());
379  break;
380  }
381  throw CommandException("No such connector or pluggable");
382  break;
383  }
384  default:
385  throw CommandException("Too many parameters");
386  }
387 }
388 
389 string PluggingController::ConnectionClassInfo::help(const vector<string>& /*tokens*/) const
390 {
391  return "Shows the class a connector or pluggable belongs to.";
392 }
393 
394 void PluggingController::ConnectionClassInfo::tabCompletion(vector<string>& tokens) const
395 {
396  if (tokens.size() == 3) {
397  vector<string_ref> names;
398  auto& pluggingController = OUTER(PluggingController, connectionClassInfo);
399  for (auto& c : pluggingController.connectors) {
400  names.push_back(c->getName());
401  }
402  for (auto& p : pluggingController.pluggables) {
403  names.push_back(p->getName());
404  }
405  completeString(tokens, names);
406  }
407 }
408 
409 } // namespace openmsx
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:121
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
const EmuTime & param
Definition: EmuTime.hh:20
Pluggable * findPluggable(string_ref name) const
Return the Pluggable with given name or nullptr if there is none with this name.
PluggingController(MSXMotherBoard &motherBoard)
CliComm & getCliComm()
Access to the MSX specific CliComm, so that Connectors can get it.
virtual void update(UpdateType type, string_ref name, string_ref value)=0
This class implements a subset of the proposal for std::array_ref (proposed for the next c++ standard...
Definition: array_ref.hh:19
static void createAll(PluggingController &controller, MSXMotherBoard &motherBoard)
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
Connector * findConnector(string_ref name) const
Return the Connector with given name or nullptr if there is none with this name.
void registerPluggable(std::unique_ptr< Pluggable > pluggable)
Add a Pluggable to the registry.
EmuTime::param getCurrentTime()
Convenience method: This is the same as getScheduler().getCurrentTime().
void unregisterConnector(Connector &connector)
#define OUTER(type, member)
Definition: outer.hh:38
const std::string & getName() const
Name that identifies this connector.
Definition: Connector.hh:27
EmuTime::param getCurrentTime() const
Convenience method: get current time.