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(const vector<string>& /*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(const vector<string>& /*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_if(connectors,
205  [&](auto* c) { return c->getName() == name; });
206  return (it != end(connectors)) ? *it : nullptr;
207 }
208 
209 Connector& PluggingController::getConnector(string_view name) const
210 {
211  if (auto* result = findConnector(name)) {
212  return *result;
213  }
214  throw CommandException("No such connector: ", name);
215 }
216 
218 {
219  auto it = ranges::find_if(pluggables,
220  [&](auto& p) { return p->getName() == name; });
221  return (it != end(pluggables)) ? it->get() : nullptr;
222 }
223 
224 Pluggable& PluggingController::getPluggable(string_view name) const
225 {
226  if (auto* result = findPluggable(name)) {
227  return *result;
228  }
229  throw CommandException("No such pluggable: ", name);
230 }
231 
233 {
234  return motherBoard.getMSXCliComm();
235 }
236 
238 {
239  return motherBoard.getCurrentTime();
240 }
241 
242 
243 // Pluggable info
244 
245 PluggingController::PluggableInfo::PluggableInfo(
246  InfoCommand& machineInfoCommand)
247  : InfoTopic(machineInfoCommand, "pluggable")
248 {
249 }
250 
251 void PluggingController::PluggableInfo::execute(
252  span<const TclObject> tokens, TclObject& result) const
253 {
254  auto& pluggingController = OUTER(PluggingController, pluggableInfo);
255  switch (tokens.size()) {
256  case 2:
257  result.addListElements(
258  view::transform(pluggingController.pluggables,
259  [](auto& p) { return p->getName(); }));
260  break;
261  case 3: {
262  auto& pluggable = pluggingController.getPluggable(
263  tokens[2].getString());
264  result = pluggable.getDescription();
265  break;
266  }
267  default:
268  throw CommandException("Too many parameters");
269  }
270 }
271 
272 string PluggingController::PluggableInfo::help(const vector<string>& /*tokens*/) const
273 {
274  return "Shows a list of available pluggables. "
275  "Or show info on a specific pluggable.";
276 }
277 
278 void PluggingController::PluggableInfo::tabCompletion(vector<string>& tokens) const
279 {
280  if (tokens.size() == 3) {
281  auto pluggableNames = to_vector(view::transform(
282  OUTER(PluggingController, pluggableInfo).pluggables,
283  [](auto& p) { return p->getName(); }));
284  completeString(tokens, pluggableNames);
285  }
286 }
287 
288 // Connector info
289 
290 PluggingController::ConnectorInfo::ConnectorInfo(
291  InfoCommand& machineInfoCommand)
292  : InfoTopic(machineInfoCommand, "connector")
293 {
294 }
295 
296 void PluggingController::ConnectorInfo::execute(
297  span<const TclObject> tokens, TclObject& result) const
298 {
299  auto& pluggingController = OUTER(PluggingController, connectorInfo);
300  switch (tokens.size()) {
301  case 2:
302  result.addListElements(
303  view::transform(pluggingController.connectors,
304  [](auto& c) { return c->getName(); }));
305  break;
306  case 3: {
307  auto& connector = pluggingController.getConnector(tokens[2].getString());
308  result = connector.getDescription();
309  break;
310  }
311  default:
312  throw CommandException("Too many parameters");
313  }
314 }
315 
316 string PluggingController::ConnectorInfo::help(const vector<string>& /*tokens*/) const
317 {
318  return "Shows a list of available connectors.";
319 }
320 
321 void PluggingController::ConnectorInfo::tabCompletion(vector<string>& tokens) const
322 {
323  if (tokens.size() == 3) {
324  auto connectorNames = to_vector(view::transform(
325  OUTER(PluggingController, connectorInfo).connectors,
326  [](auto& c) { return c->getName(); }));
327  completeString(tokens, connectorNames);
328  }
329 }
330 
331 // Connection Class info
332 
333 PluggingController::ConnectionClassInfo::ConnectionClassInfo(
334  InfoCommand& machineInfoCommand)
335  : InfoTopic(machineInfoCommand, "connectionclass")
336 {
337 }
338 
339 void PluggingController::ConnectionClassInfo::execute(
340  span<const TclObject> tokens, TclObject& result) const
341 {
342  auto& pluggingController = OUTER(PluggingController, connectionClassInfo);
343  switch (tokens.size()) {
344  case 2: {
345  std::vector<string_view> classes;
346  classes.reserve(pluggingController.connectors.size());
347  for (auto& c : pluggingController.connectors) {
348  classes.push_back(c->getClass());
349  }
350  for (auto& p : pluggingController.pluggables) {
351  auto c = p->getClass();
352  if (!contains(classes, c)) classes.push_back(c);
353  }
354  result.addListElements(classes);
355  break;
356  }
357  case 3: {
358  const auto& arg = tokens[2].getString();
359  if (auto* connector = pluggingController.findConnector(arg)) {
360  result = connector->getClass();
361  break;
362  }
363  if (auto* pluggable = pluggingController.findPluggable(arg)) {
364  result = pluggable->getClass();
365  break;
366  }
367  throw CommandException("No such connector or pluggable");
368  }
369  default:
370  throw CommandException("Too many parameters");
371  }
372 }
373 
374 string PluggingController::ConnectionClassInfo::help(const vector<string>& /*tokens*/) const
375 {
376  return "Shows the class a connector or pluggable belongs to.";
377 }
378 
379 void PluggingController::ConnectionClassInfo::tabCompletion(vector<string>& tokens) const
380 {
381  if (tokens.size() == 3) {
382  auto& pluggingController = OUTER(PluggingController, connectionClassInfo);
383  auto names = concat<string_view>(
384  view::transform(pluggingController.connectors,
385  [](auto& c) { return c->getName(); }),
386  view::transform(pluggingController.pluggables,
387  [](auto& p) { return p->getName(); }));
388  completeString(tokens, names);
389  }
390 }
391 
392 } // namespace openmsx
openmsx::PluggingController::~PluggingController
~PluggingController()
Definition: PluggingController.cc:38
openmsx::Scheduler
Definition: Scheduler.hh:34
openmsx::PluggableFactory::createAll
static void createAll(PluggingController &controller, MSXMotherBoard &motherBoard)
Definition: PluggableFactory.cc:40
openmsx::MSXMotherBoard::getCurrentTime
EmuTime::param getCurrentTime()
Convenience method: This is the same as getScheduler().getCurrentTime().
Definition: MSXMotherBoard.cc:503
openmsx::CommandController
Definition: CommandController.hh:18
TclObject.hh
openmsx::InfoTopic
Definition: InfoTopic.hh:16
contains
bool contains(ITER first, ITER last, const VAL &val)
Check if a range contains a given value, using linear search.
Definition: stl.hh:92
openmsx::StateChangeDistributor
Definition: StateChangeDistributor.hh:15
Pluggable.hh
openmsx::CliComm::update
virtual void update(UpdateType type, std::string_view name, std::string_view value)=0
ranges.hh
PlugException.hh
PluggingController.hh
strAppend
void strAppend(std::string &result, Ts &&...ts)
Definition: strCat.hh:644
openmsx::Pluggable
Definition: Pluggable.hh:12
span
Definition: span.hh:126
OUTER
#define OUTER(type, member)
Definition: outer.hh:41
move_pop_back
void move_pop_back(VECTOR &v, typename VECTOR::iterator it)
Erase the pointed to element from the given vector.
Definition: stl.hh:177
view::transform
auto transform(Range &&range, UnaryOp op)
Definition: view.hh:306
openmsx::Connector::getName
const std::string & getName() const
Name that identifies this connector.
Definition: Connector.hh:29
openmsx::PluggingController::findPluggable
Pluggable * findPluggable(std::string_view name) const
Return the Pluggable with given name or nullptr if there is none with this name.
Definition: PluggingController.cc:217
openmsx::InfoCommand
Definition: InfoCommand.hh:12
openmsx::MSXMotherBoard
Definition: MSXMotherBoard.hh:61
PluggableFactory.hh
openmsx::PluggingController::getCliComm
CliComm & getCliComm()
Access to the MSX specific CliComm, so that Connectors can get it.
Definition: PluggingController.cc:232
view.hh
openmsx::CliComm::PLUG
@ PLUG
Definition: CliComm.hh:25
openmsx::Connector::unplug
virtual void unplug(EmuTime::param time)
This unplugs the currently inserted Pluggable from this Connector.
Definition: Connector.cc:31
openmsx::PluggingController::getCurrentTime
EmuTime::param getCurrentTime() const
Convenience method: get current time.
Definition: PluggingController.cc:237
openmsx::PluggingController::unregisterConnector
void unregisterConnector(Connector &connector)
Definition: PluggingController.cc:56
outer.hh
openmsx::CliComm::CONNECTOR
@ CONNECTOR
Definition: CliComm.hh:30
span::size
constexpr index_type size() const noexcept
Definition: span.hh:296
openmsx::PluggingController::registerPluggable
void registerPluggable(std::unique_ptr< Pluggable > pluggable)
Add a Pluggable to the registry.
Definition: PluggingController.cc:64
Connector.hh
openmsx::RecordedCommand
Commands that directly influence the MSX state should send and events so that they can be recorded by...
Definition: RecordedCommand.hh:46
openmsx::PluggingController::registerConnector
void registerConnector(Connector &connector)
Connectors must be (un)registered.
Definition: PluggingController.cc:50
openmsx::CliComm
Definition: CliComm.hh:11
openmsx::PluggingController::PluggingController
PluggingController(MSXMotherBoard &motherBoard)
Definition: PluggingController.cc:21
openmsx::Connector
Represents something you can plug devices into.
Definition: Connector.hh:21
openmsx::Scheduler
Scheduler
Definition: Scheduler.cc:132
ranges::find_if
auto find_if(InputRange &&range, UnaryPredicate pred)
Definition: ranges.hh:113
CliComm.hh
openmsx::PluggingController::findConnector
Connector * findConnector(std::string_view name) const
Return the Connector with given name or nullptr if there is none with this name.
Definition: PluggingController.cc:202
CommandException.hh
rfind_unguarded
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:152
openmsx
This file implemented 3 utility functions:
Definition: Autofire.cc:5
to_vector
auto to_vector(Range &&range) -> std::vector< detail::ToVectorType< T, decltype(std::begin(range))>>
Definition: stl.hh:311
MSXMotherBoard.hh
openmsx::MSXMotherBoard::getMSXCliComm
CliComm & getMSXCliComm()
Definition: MSXMotherBoard.cc:383