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