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 "outer.hh"
11 #include "ranges.hh"
12 #include "view.hh"
13 #include <iostream>
14 
15 using std::string;
16 using std::vector;
17 
18 namespace openmsx {
19 
21  : motherBoard(motherBoard_)
22  , plugCmd(
23  motherBoard.getCommandController(),
24  motherBoard.getStateChangeDistributor(),
25  motherBoard.getScheduler())
26  , unplugCmd(
27  motherBoard.getCommandController(),
28  motherBoard.getStateChangeDistributor(),
29  motherBoard.getScheduler())
30  , pluggableInfo (motherBoard.getMachineInfoCommand())
31  , connectorInfo (motherBoard.getMachineInfoCommand())
32  , connectionClassInfo(motherBoard.getMachineInfoCommand())
33 {
34  PluggableFactory::createAll(*this, motherBoard);
35 }
36 
38 {
39 #ifndef NDEBUG
40  // This is similar to an assert: it should never print anything,
41  // but if it does, it helps to catch an error.
42  for (auto& c : connectors) {
43  std::cerr << "ERROR: Connector still registered at shutdown: "
44  << c->getName() << '\n';
45  }
46 #endif
47 }
48 
50 {
51  connectors.push_back(&connector);
52  getCliComm().update(CliComm::CONNECTOR, connector.getName(), "add");
53 }
54 
56 {
57  connector.unplug(motherBoard.getCurrentTime());
58  move_pop_back(connectors, rfind_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  : RecordedCommand(commandController_, stateChangeDistributor_,
77  scheduler_, "plug")
78 {
79 }
80 
81 void PluggingController::PlugCmd::execute(
82  span<const TclObject> tokens, TclObject& result_, EmuTime::param time)
83 {
84  checkNumArgs(tokens, Between{1, 3}, Prefix{1}, "?connector? ?pluggable?");
85  string result;
86  auto& pluggingController = OUTER(PluggingController, plugCmd);
87  switch (tokens.size()) {
88  case 1:
89  for (auto& c : pluggingController.connectors) {
90  strAppend(result, c->getName(), ": ",
91  c->getPlugged().getName(), '\n');
92  }
93  break;
94  case 2: {
95  auto& connector = pluggingController.getConnector(tokens[1].getString());
96  strAppend(result, connector.getName(), ": ",
97  connector.getPlugged().getName());
98  break;
99  }
100  case 3: {
101  string_view connName = tokens[1].getString();
102  string_view 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  }
124  result_ = result; // TODO return Tcl list
125 }
126 
127 string PluggingController::PlugCmd::help(const vector<string>& /*tokens*/) const
128 {
129  return "Plugs a plug into a connector\n"
130  " plug [connector] [plug]";
131 }
132 
133 void PluggingController::PlugCmd::tabCompletion(vector<string>& tokens) const
134 {
135  auto& pluggingController = OUTER(PluggingController, plugCmd);
136  if (tokens.size() == 2) {
137  // complete connector
138  auto connectorNames = to_vector(view::transform(
139  pluggingController.connectors,
140  [](auto& c) { return c->getName(); }));
141  completeString(tokens, connectorNames);
142  } else if (tokens.size() == 3) {
143  // complete pluggable
144  vector<string_view> pluggableNames;
145  auto* connector = pluggingController.findConnector(tokens[1]);
146  string_view className = connector ? connector->getClass() : string_view{};
147  for (auto& p : pluggingController.pluggables) {
148  if (p->getClass() == className) {
149  pluggableNames.emplace_back(p->getName());
150  }
151  }
152  completeString(tokens, pluggableNames);
153  }
154 }
155 
156 bool PluggingController::PlugCmd::needRecord(span<const TclObject> tokens) const
157 {
158  return tokens.size() == 3;
159 }
160 
161 
162 // unplug command
163 
164 PluggingController::UnplugCmd::UnplugCmd(
165  CommandController& commandController_,
166  StateChangeDistributor& stateChangeDistributor_,
167  Scheduler& scheduler_)
168  : RecordedCommand(commandController_, stateChangeDistributor_,
169  scheduler_, "unplug")
170 {
171 }
172 
173 void PluggingController::UnplugCmd::execute(
174  span<const TclObject> tokens, TclObject& /*result*/, EmuTime::param time)
175 {
176  checkNumArgs(tokens, 2, "connector");
177  auto& pluggingController = OUTER(PluggingController, unplugCmd);
178  string_view connName = tokens[1].getString();
179  auto& connector = pluggingController.getConnector(connName);
180  connector.unplug(time);
181  pluggingController.getCliComm().update(CliComm::PLUG, connName, {});
182 }
183 
184 string PluggingController::UnplugCmd::help(const vector<string>& /*tokens*/) const
185 {
186  return "Unplugs a plug from a connector\n"
187  " unplug [connector]";
188 }
189 
190 void PluggingController::UnplugCmd::tabCompletion(vector<string>& tokens) const
191 {
192  if (tokens.size() == 2) {
193  // complete connector
194  auto connectorNames = to_vector(view::transform(
195  OUTER(PluggingController, unplugCmd).connectors,
196  [](auto* c) { return c->getName(); }));
197  completeString(tokens, connectorNames);
198  }
199 }
200 
202 {
203  auto it = ranges::find_if(connectors,
204  [&](auto* c) { return c->getName() == name; });
205  return (it != end(connectors)) ? *it : nullptr;
206 }
207 
208 Connector& PluggingController::getConnector(string_view name) const
209 {
210  if (auto* result = findConnector(name)) {
211  return *result;
212  }
213  throw CommandException("No such connector: ", name);
214 }
215 
217 {
218  auto it = ranges::find_if(pluggables,
219  [&](auto& p) { return p->getName() == name; });
220  return (it != end(pluggables)) ? it->get() : nullptr;
221 }
222 
223 Pluggable& PluggingController::getPluggable(string_view name) const
224 {
225  if (auto* result = findPluggable(name)) {
226  return *result;
227  }
228  throw CommandException("No such pluggable: ", name);
229 }
230 
232 {
233  return motherBoard.getMSXCliComm();
234 }
235 
237 {
238  return motherBoard.getCurrentTime();
239 }
240 
241 
242 // Pluggable info
243 
244 PluggingController::PluggableInfo::PluggableInfo(
245  InfoCommand& machineInfoCommand)
246  : InfoTopic(machineInfoCommand, "pluggable")
247 {
248 }
249 
250 void PluggingController::PluggableInfo::execute(
251  span<const TclObject> tokens, TclObject& result) const
252 {
253  auto& pluggingController = OUTER(PluggingController, pluggableInfo);
254  switch (tokens.size()) {
255  case 2:
256  result.addListElements(
257  view::transform(pluggingController.pluggables,
258  [](auto& p) { return p->getName(); }));
259  break;
260  case 3: {
261  auto& pluggable = pluggingController.getPluggable(
262  tokens[2].getString());
263  result = pluggable.getDescription();
264  break;
265  }
266  default:
267  throw CommandException("Too many parameters");
268  }
269 }
270 
271 string PluggingController::PluggableInfo::help(const vector<string>& /*tokens*/) const
272 {
273  return "Shows a list of available pluggables. "
274  "Or show info on a specific pluggable.";
275 }
276 
277 void PluggingController::PluggableInfo::tabCompletion(vector<string>& tokens) const
278 {
279  if (tokens.size() == 3) {
280  auto pluggableNames = to_vector(view::transform(
281  OUTER(PluggingController, pluggableInfo).pluggables,
282  [](auto& p) { return p->getName(); }));
283  completeString(tokens, pluggableNames);
284  }
285 }
286 
287 // Connector info
288 
289 PluggingController::ConnectorInfo::ConnectorInfo(
290  InfoCommand& machineInfoCommand)
291  : InfoTopic(machineInfoCommand, "connector")
292 {
293 }
294 
295 void PluggingController::ConnectorInfo::execute(
296  span<const TclObject> tokens, TclObject& result) const
297 {
298  auto& pluggingController = OUTER(PluggingController, connectorInfo);
299  switch (tokens.size()) {
300  case 2:
301  result.addListElements(
302  view::transform(pluggingController.connectors,
303  [](auto& c) { return c->getName(); }));
304  break;
305  case 3: {
306  auto& connector = pluggingController.getConnector(tokens[2].getString());
307  result = connector.getDescription();
308  break;
309  }
310  default:
311  throw CommandException("Too many parameters");
312  }
313 }
314 
315 string PluggingController::ConnectorInfo::help(const vector<string>& /*tokens*/) const
316 {
317  return "Shows a list of available connectors.";
318 }
319 
320 void PluggingController::ConnectorInfo::tabCompletion(vector<string>& tokens) const
321 {
322  if (tokens.size() == 3) {
323  auto connectorNames = to_vector(view::transform(
324  OUTER(PluggingController, connectorInfo).connectors,
325  [](auto& c) { return c->getName(); }));
326  completeString(tokens, connectorNames);
327  }
328 }
329 
330 // Connection Class info
331 
332 PluggingController::ConnectionClassInfo::ConnectionClassInfo(
333  InfoCommand& machineInfoCommand)
334  : InfoTopic(machineInfoCommand, "connectionclass")
335 {
336 }
337 
338 void PluggingController::ConnectionClassInfo::execute(
339  span<const TclObject> tokens, TclObject& result) const
340 {
341  auto& pluggingController = OUTER(PluggingController, connectionClassInfo);
342  switch (tokens.size()) {
343  case 2: {
344  std::vector<string_view> classes;
345  classes.reserve(pluggingController.connectors.size());
346  for (auto& c : pluggingController.connectors) {
347  classes.push_back(c->getClass());
348  }
349  for (auto& p : pluggingController.pluggables) {
350  auto c = p->getClass();
351  if (!contains(classes, c)) classes.push_back(c);
352  }
353  result.addListElements(classes);
354  break;
355  }
356  case 3: {
357  const auto& arg = tokens[2].getString();
358  if (auto* connector = pluggingController.findConnector(arg)) {
359  result = connector->getClass();
360  break;
361  }
362  if (auto* pluggable = pluggingController.findPluggable(arg)) {
363  result = pluggable->getClass();
364  break;
365  }
366  throw CommandException("No such connector or pluggable");
367  }
368  default:
369  throw CommandException("Too many parameters");
370  }
371 }
372 
373 string PluggingController::ConnectionClassInfo::help(const vector<string>& /*tokens*/) const
374 {
375  return "Shows the class a connector or pluggable belongs to.";
376 }
377 
378 void PluggingController::ConnectionClassInfo::tabCompletion(vector<string>& tokens) const
379 {
380  if (tokens.size() == 3) {
381  auto& pluggingController = OUTER(PluggingController, connectionClassInfo);
382  auto names = concat<string_view>(
383  view::transform(pluggingController.connectors,
384  [](auto& c) { return c->getName(); }),
385  view::transform(pluggingController.pluggables,
386  [](auto& p) { return p->getName(); }));
387  completeString(tokens, names);
388  }
389 }
390 
391 } // namespace openmsx
auto transform(Range &&range, UnaryOp op)
Definition: view.hh:312
Represents something you can plug devices into.
Definition: Connector.hh:20
const std::string & getMessage() const &
Definition: MSXException.hh:23
Definition: span.hh:34
bool contains(ITER first, ITER last, const VAL &val)
Check if a range contains a given value, using linear search.
Definition: stl.hh:106
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.
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:191
CliComm & getCliComm()
Access to the MSX specific CliComm, so that Connectors can get it.
Pluggable * findPluggable(string_view name) const
Return the Pluggable with given name or nullptr if there is none with this name.
void strAppend(std::string &result, Ts &&...ts)
Definition: strCat.hh:648
auto find_if(InputRange &&range, UnaryPredicate pred)
Definition: ranges.hh:113
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.
This class implements a (close approximation) of the std::string_view class.
Definition: string_view.hh:16
virtual void update(UpdateType type, string_view name, string_view value)=0
EmuTime::param getCurrentTime()
Convenience method: This is the same as getScheduler().getCurrentTime().
void unregisterConnector(Connector &connector)
auto rfind_unguarded(RANGE &range, const VAL &val)
Similar to the find(_if)_unguarded functions above, but searches from the back to front...
Definition: stl.hh:166
#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
void addListElements(ITER first, ITER last)
Definition: TclObject.hh:122
Connector * findConnector(string_view name) const
Return the Connector with given name or nullptr if there is none with this name.
auto to_vector(Range &&range) -> std::vector< detail::ToVectorType< T, decltype(std::begin(range))>>
Definition: stl.hh:325
auto end(const string_view &x)
Definition: string_view.hh:152