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  connector.unplug(motherBoard.getCurrentTime());
60  move_pop_back(connectors, rfind_unguarded(connectors, &connector));
61  getCliComm().update(CliComm::CONNECTOR, connector.getName(), "remove");
62 }
63 
64 
65 void PluggingController::registerPluggable(std::unique_ptr<Pluggable> pluggable)
66 {
67  pluggables.push_back(std::move(pluggable));
68 }
69 
70 
71 // === Commands ===
72 // plug command
73 
74 PluggingController::PlugCmd::PlugCmd(
75  CommandController& commandController_,
76  StateChangeDistributor& stateChangeDistributor_,
77  Scheduler& scheduler_)
78  : RecordedCommand(commandController_, stateChangeDistributor_,
79  scheduler_, "plug")
80 {
81 }
82 
83 void PluggingController::PlugCmd::execute(
84  array_ref<TclObject> tokens, TclObject& result_, EmuTime::param time)
85 {
86  StringOp::Builder result;
87  auto& pluggingController = OUTER(PluggingController, plugCmd);
88  switch (tokens.size()) {
89  case 1:
90  for (auto& c : pluggingController.connectors) {
91  result << c->getName() << ": "
92  << c->getPlugged().getName() << '\n';
93  }
94  break;
95  case 2: {
96  auto& connector = pluggingController.getConnector(tokens[1].getString());
97  result << connector.getName() << ": "
98  << connector.getPlugged().getName();
99  break;
100  }
101  case 3: {
102  string_ref connName = tokens[1].getString();
103  string_ref plugName = tokens[2].getString();
104  auto& connector = pluggingController.getConnector(connName);
105  auto& pluggable = pluggingController.getPluggable(plugName);
106  if (&connector.getPlugged() == &pluggable) {
107  // already plugged, don't unplug/replug
108  break;
109  }
110  if (connector.getClass() != pluggable.getClass()) {
111  throw CommandException("plug: " + plugName +
112  " doesn't fit in " + connName);
113  }
114  connector.unplug(time);
115  try {
116  connector.plug(pluggable, time);
117  pluggingController.getCliComm().update(
118  CliComm::PLUG, connName, plugName);
119  } catch (PlugException& e) {
120  throw CommandException("plug: plug failed: " + e.getMessage());
121  }
122  break;
123  }
124  default:
125  throw SyntaxError();
126  }
127  result_.setString(result); // TODO return Tcl list
128 }
129 
130 string PluggingController::PlugCmd::help(const vector<string>& /*tokens*/) const
131 {
132  return "Plugs a plug into a connector\n"
133  " plug [connector] [plug]";
134 }
135 
136 void PluggingController::PlugCmd::tabCompletion(vector<string>& tokens) const
137 {
138  auto& pluggingController = OUTER(PluggingController, plugCmd);
139  if (tokens.size() == 2) {
140  // complete connector
141  vector<string_ref> connectorNames;
142  for (auto& c : pluggingController.connectors) {
143  connectorNames.push_back(c->getName());
144  }
145  completeString(tokens, connectorNames);
146  } else if (tokens.size() == 3) {
147  // complete pluggable
148  vector<string_ref> pluggableNames;
149  auto* connector = pluggingController.findConnector(tokens[1]);
150  string_ref className = connector ? connector->getClass() : "";
151  for (auto& p : pluggingController.pluggables) {
152  if (p->getClass() == className) {
153  pluggableNames.push_back(p->getName());
154  }
155  }
156  completeString(tokens, pluggableNames);
157  }
158 }
159 
160 bool PluggingController::PlugCmd::needRecord(array_ref<TclObject> tokens) const
161 {
162  return tokens.size() == 3;
163 }
164 
165 
166 // unplug command
167 
168 PluggingController::UnplugCmd::UnplugCmd(
169  CommandController& commandController_,
170  StateChangeDistributor& stateChangeDistributor_,
171  Scheduler& scheduler_)
172  : RecordedCommand(commandController_, stateChangeDistributor_,
173  scheduler_, "unplug")
174 {
175 }
176 
177 void PluggingController::UnplugCmd::execute(
178  array_ref<TclObject> tokens, TclObject& /*result*/, EmuTime::param time)
179 {
180  if (tokens.size() != 2) {
181  throw SyntaxError();
182  }
183  auto& pluggingController = OUTER(PluggingController, unplugCmd);
184  string_ref connName = tokens[1].getString();
185  auto& connector = pluggingController.getConnector(connName);
186  connector.unplug(time);
187  pluggingController.getCliComm().update(CliComm::UNPLUG, connName, "");
188 }
189 
190 string PluggingController::UnplugCmd::help(const vector<string>& /*tokens*/) const
191 {
192  return "Unplugs a plug from a connector\n"
193  " unplug [connector]";
194 }
195 
196 void PluggingController::UnplugCmd::tabCompletion(vector<string>& tokens) const
197 {
198  if (tokens.size() == 2) {
199  // complete connector
200  vector<string_ref> connectorNames;
201  auto& pluggingController = OUTER(PluggingController, unplugCmd);
202  for (auto& c : pluggingController.connectors) {
203  connectorNames.push_back(c->getName());
204  }
205  completeString(tokens, connectorNames);
206  }
207 }
208 
210 {
211  for (auto& c : connectors) {
212  if (c->getName() == name) {
213  return c;
214  }
215  }
216  return nullptr;
217 }
218 
219 Connector& PluggingController::getConnector(string_ref name) const
220 {
221  if (auto* result = findConnector(name)) {
222  return *result;
223  }
224  throw CommandException("No such connector: " + name);
225 }
226 
228 {
229  for (auto& p : pluggables) {
230  if (p->getName() == name) {
231  return p.get();
232  }
233  }
234  return nullptr;
235 }
236 
237 Pluggable& PluggingController::getPluggable(string_ref name) const
238 {
239  if (auto* result = findPluggable(name)) {
240  return *result;
241  }
242  throw CommandException("No such pluggable: " + name);
243 }
244 
246 {
247  return motherBoard.getMSXCliComm();
248 }
249 
251 {
252  return motherBoard.getCurrentTime();
253 }
254 
255 
256 // Pluggable info
257 
258 PluggingController::PluggableInfo::PluggableInfo(
259  InfoCommand& machineInfoCommand)
260  : InfoTopic(machineInfoCommand, "pluggable")
261 {
262 }
263 
264 void PluggingController::PluggableInfo::execute(
265  array_ref<TclObject> tokens, TclObject& result) const
266 {
267  auto& pluggingController = OUTER(PluggingController, pluggableInfo);
268  switch (tokens.size()) {
269  case 2:
270  for (auto& p : pluggingController.pluggables) {
271  result.addListElement(p->getName());
272  }
273  break;
274  case 3: {
275  auto& pluggable = pluggingController.getPluggable(
276  tokens[2].getString());
277  result.setString(pluggable.getDescription());
278  break;
279  }
280  default:
281  throw CommandException("Too many parameters");
282  }
283 }
284 
285 string PluggingController::PluggableInfo::help(const vector<string>& /*tokens*/) const
286 {
287  return "Shows a list of available pluggables. "
288  "Or show info on a specific pluggable.";
289 }
290 
291 void PluggingController::PluggableInfo::tabCompletion(vector<string>& tokens) const
292 {
293  if (tokens.size() == 3) {
294  vector<string_ref> pluggableNames;
295  auto& pluggingController = OUTER(PluggingController, pluggableInfo);
296  for (auto& p : pluggingController.pluggables) {
297  pluggableNames.push_back(p->getName());
298  }
299  completeString(tokens, pluggableNames);
300  }
301 }
302 
303 // Connector info
304 
305 PluggingController::ConnectorInfo::ConnectorInfo(
306  InfoCommand& machineInfoCommand)
307  : InfoTopic(machineInfoCommand, "connector")
308 {
309 }
310 
311 void PluggingController::ConnectorInfo::execute(
312  array_ref<TclObject> tokens, TclObject& result) const
313 {
314  auto& pluggingController = OUTER(PluggingController, connectorInfo);
315  switch (tokens.size()) {
316  case 2:
317  for (auto& c : pluggingController.connectors) {
318  result.addListElement(c->getName());
319  }
320  break;
321  case 3: {
322  auto& connector = pluggingController.getConnector(tokens[2].getString());
323  result.setString(connector.getDescription());
324  break;
325  }
326  default:
327  throw CommandException("Too many parameters");
328  }
329 }
330 
331 string PluggingController::ConnectorInfo::help(const vector<string>& /*tokens*/) const
332 {
333  return "Shows a list of available connectors.";
334 }
335 
336 void PluggingController::ConnectorInfo::tabCompletion(vector<string>& tokens) const
337 {
338  if (tokens.size() == 3) {
339  vector<string_ref> connectorNames;
340  auto& pluggingController = OUTER(PluggingController, connectorInfo);
341  for (auto& c : pluggingController.connectors) {
342  connectorNames.push_back(c->getName());
343  }
344  completeString(tokens, connectorNames);
345  }
346 }
347 
348 // Connection Class info
349 
350 PluggingController::ConnectionClassInfo::ConnectionClassInfo(
351  InfoCommand& machineInfoCommand)
352  : InfoTopic(machineInfoCommand, "connectionclass")
353 {
354 }
355 
356 void PluggingController::ConnectionClassInfo::execute(
357  array_ref<TclObject> tokens, TclObject& result) const
358 {
359  auto& pluggingController = OUTER(PluggingController, connectionClassInfo);
360  switch (tokens.size()) {
361  case 2: {
362  std::set<string_ref> classes; // filter duplicates
363  for (auto& c : pluggingController.connectors) {
364  classes.insert(c->getClass());
365  }
366  for (auto& p : pluggingController.pluggables) {
367  classes.insert(p->getClass());
368  }
369  result.addListElements(classes);
370  break;
371  }
372  case 3: {
373  const auto& arg = tokens[2].getString();
374  if (auto* connector = pluggingController.findConnector(arg)) {
375  result.setString(connector->getClass());
376  break;
377  }
378  if (auto* pluggable = pluggingController.findPluggable(arg)) {
379  result.setString(pluggable->getClass());
380  break;
381  }
382  throw CommandException("No such connector or pluggable");
383  break;
384  }
385  default:
386  throw CommandException("Too many parameters");
387  }
388 }
389 
390 string PluggingController::ConnectionClassInfo::help(const vector<string>& /*tokens*/) const
391 {
392  return "Shows the class a connector or pluggable belongs to.";
393 }
394 
395 void PluggingController::ConnectionClassInfo::tabCompletion(vector<string>& tokens) const
396 {
397  if (tokens.size() == 3) {
398  vector<string_ref> names;
399  auto& pluggingController = OUTER(PluggingController, connectionClassInfo);
400  for (auto& c : pluggingController.connectors) {
401  names.push_back(c->getName());
402  }
403  for (auto& p : pluggingController.pluggables) {
404  names.push_back(p->getName());
405  }
406  completeString(tokens, names);
407  }
408 }
409 
410 } // namespace openmsx
Pluggable * findPluggable(string_ref name) const
Return the Pluggable with given name or nullptr if there is none with this name.
Represents something you can plug devices into.
Definition: Connector.hh:20
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.
PluggingController(MSXMotherBoard &motherBoard)
const std::string & getName() const
Name that identifies this connector.
Definition: Connector.hh:29
EmuTime::param getCurrentTime() const
Convenience method: get current time.
void move_pop_back(VECTOR &v, typename VECTOR::iterator it)
Erase the pointed to element from the given vector.
Definition: stl.hh:192
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
const std::string & getMessage() const
Definition: MSXException.hh:14
auto rfind_unguarded(RANGE &range, const VAL &val) -> decltype(std::begin(range))
Similar to the find(_if)_unguarded functions above, but searches from the back to front...
Definition: stl.hh:164
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
void registerPluggable(std::unique_ptr< Pluggable > pluggable)
Add a Pluggable to the registry.
size_type size() const
Definition: array_ref.hh:61
void addListElement(string_ref element)
Definition: TclObject.cc:69
void setString(string_ref value)
Definition: TclObject.cc:14
EmuTime::param getCurrentTime()
Convenience method: This is the same as getScheduler().getCurrentTime().
void unregisterConnector(Connector &connector)
#define OUTER(type, member)
Definition: outer.hh:38
Thrown when a plug action fails.
virtual void unplug(EmuTime::param time)
This unplugs the currently inserted Pluggable from this Connector.
Definition: Connector.cc:31
Connector * findConnector(string_ref name) const
Return the Connector with given name or nullptr if there is none with this name.
void addListElements(ITER begin, ITER end)
Definition: TclObject.hh:156