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 "stl.hh"
12 #include <cassert>
13 #include <iostream>
14 #include <set>
15 
16 using std::string;
17 using std::vector;
18 
19 namespace openmsx {
20 
22  : motherBoard(motherBoard_)
23  , plugCmd(
24  motherBoard.getCommandController(),
25  motherBoard.getStateChangeDistributor(),
26  motherBoard.getScheduler(), *this)
27  , unplugCmd(
28  motherBoard.getCommandController(),
29  motherBoard.getStateChangeDistributor(),
30  motherBoard.getScheduler(), *this)
31  , pluggableInfo (motherBoard.getMachineInfoCommand(), *this)
32  , connectorInfo (motherBoard.getMachineInfoCommand(), *this)
33  , connectionClassInfo(motherBoard.getMachineInfoCommand(), *this)
34 {
35  PluggableFactory::createAll(*this, motherBoard);
36 }
37 
39 {
40 #ifndef NDEBUG
41  // This is similar to an assert: it should never print anything,
42  // but if it does, it helps to catch an error.
43  for (auto& c : connectors) {
44  std::cerr << "ERROR: Connector still registered at shutdown: "
45  << c->getName() << std::endl;
46  }
47 #endif
48 }
49 
51 {
52  connectors.push_back(&connector);
53  getCliComm().update(CliComm::CONNECTOR, connector.getName(), "add");
54 }
55 
57 {
58  connectors.erase(find_unguarded(connectors, &connector));
59  getCliComm().update(CliComm::CONNECTOR, connector.getName(), "remove");
60 }
61 
62 
63 void PluggingController::registerPluggable(std::unique_ptr<Pluggable> pluggable)
64 {
65  pluggables.push_back(std::move(pluggable));
66 }
67 
68 
69 // === Commands ===
70 // plug command
71 
72 PluggingController::PlugCmd::PlugCmd(
73  CommandController& commandController,
74  StateChangeDistributor& stateChangeDistributor,
75  Scheduler& scheduler,
76  PluggingController& pluggingController_)
77  : RecordedCommand(commandController, stateChangeDistributor,
78  scheduler, "plug")
79  , pluggingController(pluggingController_)
80 {
81 }
82 
83 void PluggingController::PlugCmd::execute(
84  array_ref<TclObject> tokens, TclObject& result_, EmuTime::param time)
85 {
86  StringOp::Builder result;
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  if (tokens.size() == 2) {
138  // complete connector
139  vector<string_ref> connectors;
140  for (auto& c : pluggingController.connectors) {
141  connectors.push_back(c->getName());
142  }
143  completeString(tokens, connectors);
144  } else if (tokens.size() == 3) {
145  // complete pluggable
146  vector<string_ref> pluggables;
147  auto* connector = pluggingController.findConnector(tokens[1]);
148  string_ref className = connector ? connector->getClass() : "";
149  for (auto& p : pluggingController.pluggables) {
150  if (p->getClass() == className) {
151  pluggables.push_back(p->getName());
152  }
153  }
154  completeString(tokens, pluggables);
155  }
156 }
157 
158 bool PluggingController::PlugCmd::needRecord(array_ref<TclObject> tokens) const
159 {
160  return tokens.size() == 3;
161 }
162 
163 
164 // unplug command
165 
166 PluggingController::UnplugCmd::UnplugCmd(
167  CommandController& commandController,
168  StateChangeDistributor& stateChangeDistributor,
169  Scheduler& scheduler,
170  PluggingController& pluggingController_)
171  : RecordedCommand(commandController, stateChangeDistributor,
172  scheduler, "unplug")
173  , pluggingController(pluggingController_)
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  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  for (auto& c : pluggingController.connectors) {
201  connectors.push_back(c->getName());
202  }
203  completeString(tokens, connectors);
204  }
205 }
206 
208 {
209  for (auto& c : connectors) {
210  if (c->getName() == name) {
211  return c;
212  }
213  }
214  return nullptr;
215 }
216 
217 Connector& PluggingController::getConnector(string_ref name) const
218 {
219  if (auto* result = findConnector(name)) {
220  return *result;
221  }
222  throw CommandException("No such connector: " + name);
223 }
224 
226 {
227  for (auto& p : pluggables) {
228  if (p->getName() == name) {
229  return p.get();
230  }
231  }
232  return nullptr;
233 }
234 
235 Pluggable& PluggingController::getPluggable(string_ref name) const
236 {
237  if (auto* result = findPluggable(name)) {
238  return *result;
239  }
240  throw CommandException("No such pluggable: " + name);
241 }
242 
244 {
245  return motherBoard.getMSXCliComm();
246 }
247 
249 {
250  return motherBoard.getCurrentTime();
251 }
252 
253 
254 // Pluggable info
255 
256 PluggingController::PluggableInfo::PluggableInfo(
257  InfoCommand& machineInfoCommand,
258  PluggingController& pluggingController_)
259  : InfoTopic(machineInfoCommand, "pluggable")
260  , pluggingController(pluggingController_)
261 {
262 }
263 
264 void PluggingController::PluggableInfo::execute(
265  array_ref<TclObject> tokens, TclObject& result) const
266 {
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  for (auto& p : pluggingController.pluggables) {
295  pluggables.push_back(p->getName());
296  }
297  completeString(tokens, pluggables);
298  }
299 }
300 
301 // Connector info
302 
303 PluggingController::ConnectorInfo::ConnectorInfo(
304  InfoCommand& machineInfoCommand,
305  PluggingController& pluggingController_)
306  : InfoTopic(machineInfoCommand, "connector")
307  , pluggingController(pluggingController_)
308 {
309 }
310 
311 void PluggingController::ConnectorInfo::execute(
312  array_ref<TclObject> tokens, TclObject& result) const
313 {
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  for (auto& c : pluggingController.connectors) {
340  connectors.push_back(c->getName());
341  }
342  completeString(tokens, connectors);
343  }
344 }
345 
346 // Connection Class info
347 
348 PluggingController::ConnectionClassInfo::ConnectionClassInfo(
349  InfoCommand& machineInfoCommand,
350  PluggingController& pluggingController_)
351  : InfoTopic(machineInfoCommand, "connectionclass")
352  , pluggingController(pluggingController_)
353 {
354 }
355 
356 void PluggingController::ConnectionClassInfo::execute(
357  array_ref<TclObject> tokens, TclObject& result) const
358 {
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  for (auto& c : pluggingController.connectors) {
399  names.push_back(c->getName());
400  }
401  for (auto& p : pluggingController.pluggables) {
402  names.push_back(p->getName());
403  }
404  completeString(tokens, names);
405  }
406 }
407 
408 } // 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:114
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)
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
const EmuTime & param
Definition: EmuTime.hh:20
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)
const std::string & getName() const
Name that identifies this connector.
Definition: Connector.hh:27
EmuTime::param getCurrentTime() const
Convenience method: get current time.