openMSX
PluggingController.cc
Go to the documentation of this file.
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
15using std::string;
16using std::string_view;
17
18namespace 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
63void PluggingController::registerPluggable(std::unique_ptr<Pluggable> pluggable)
64{
65 pluggables.push_back(std::move(pluggable));
66}
67
68
69// === Commands ===
70// plug command
71
72PluggingController::PlugCmd::PlugCmd(
73 CommandController& commandController_,
74 StateChangeDistributor& stateChangeDistributor_,
75 Scheduler& scheduler_)
76 : RecordedCommand(commandController_, stateChangeDistributor_,
77 scheduler_, "plug")
78{
79}
80
81void PluggingController::PlugCmd::execute(
82 std::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
127string PluggingController::PlugCmd::help(std::span<const TclObject> /*tokens*/) const
128{
129 return "Plugs a plug into a connector\n"
130 " plug [connector] [plug]";
131}
132
133void PluggingController::PlugCmd::tabCompletion(std::vector<string>& tokens) const
134{
135 auto& pluggingController = OUTER(PluggingController, plugCmd);
136 if (tokens.size() == 2) {
137 // complete connector
138 completeString(tokens, view::transform(
139 pluggingController.connectors,
140 [](auto& c) -> std::string_view { return c->getName(); }));
141 } else if (tokens.size() == 3) {
142 // complete pluggable
143 auto* connector = pluggingController.findConnector(tokens[1]);
144 string_view className = connector ? connector->getClass() : string_view{};
145 completeString(tokens, view::transform(view::filter(pluggingController.pluggables,
146 [&](auto& p) { return p->getClass() == className; }),
147 [](auto& p) -> string_view { return p->getName(); }));
148 }
149}
150
151bool PluggingController::PlugCmd::needRecord(std::span<const TclObject> tokens) const
152{
153 return tokens.size() == 3;
154}
155
156
157// unplug command
158
159PluggingController::UnplugCmd::UnplugCmd(
160 CommandController& commandController_,
161 StateChangeDistributor& stateChangeDistributor_,
162 Scheduler& scheduler_)
163 : RecordedCommand(commandController_, stateChangeDistributor_,
164 scheduler_, "unplug")
165{
166}
167
168void PluggingController::UnplugCmd::execute(
169 std::span<const TclObject> tokens, TclObject& /*result*/, EmuTime::param time)
170{
171 checkNumArgs(tokens, 2, "connector");
172 auto& pluggingController = OUTER(PluggingController, unplugCmd);
173 string_view connName = tokens[1].getString();
174 auto& connector = pluggingController.getConnector(connName);
175 connector.unplug(time);
176 pluggingController.getCliComm().update(CliComm::PLUG, connName, {});
177}
178
179string PluggingController::UnplugCmd::help(std::span<const TclObject> /*tokens*/) const
180{
181 return "Unplugs a plug from a connector\n"
182 " unplug [connector]";
183}
184
185void PluggingController::UnplugCmd::tabCompletion(std::vector<string>& tokens) const
186{
187 if (tokens.size() == 2) {
188 // complete connector
189 completeString(tokens, view::transform(
190 OUTER(PluggingController, unplugCmd).connectors,
191 [](auto* c) -> std::string_view { return c->getName(); }));
192 }
193}
194
196{
197 auto it = ranges::find(connectors, name, &Connector::getName);
198 return (it != end(connectors)) ? *it : nullptr;
199}
200
201Connector& PluggingController::getConnector(string_view name) const
202{
203 if (auto* result = findConnector(name)) {
204 return *result;
205 }
206 throw CommandException("No such connector: ", name);
207}
208
210{
211 auto it = ranges::find(pluggables, name, &Pluggable::getName);
212 return (it != end(pluggables)) ? it->get() : nullptr;
213}
214
215Pluggable& PluggingController::getPluggable(string_view name) const
216{
217 if (auto* result = findPluggable(name)) {
218 return *result;
219 }
220 throw CommandException("No such pluggable: ", name);
221}
222
224{
225 return motherBoard.getMSXCliComm();
226}
227
229{
230 return motherBoard.getCurrentTime();
231}
232
233
234// Pluggable info
235
236PluggingController::PluggableInfo::PluggableInfo(
237 InfoCommand& machineInfoCommand)
238 : InfoTopic(machineInfoCommand, "pluggable")
239{
240}
241
242void PluggingController::PluggableInfo::execute(
243 std::span<const TclObject> tokens, TclObject& result) const
244{
245 auto& pluggingController = OUTER(PluggingController, pluggableInfo);
246 switch (tokens.size()) {
247 case 2:
248 result.addListElements(
249 view::transform(pluggingController.pluggables,
250 [](auto& p) { return p->getName(); }));
251 break;
252 case 3: {
253 auto& pluggable = pluggingController.getPluggable(
254 tokens[2].getString());
255 result = pluggable.getDescription();
256 break;
257 }
258 default:
259 throw CommandException("Too many parameters");
260 }
261}
262
263string PluggingController::PluggableInfo::help(std::span<const TclObject> /*tokens*/) const
264{
265 return "Shows a list of available pluggables. "
266 "Or show info on a specific pluggable.";
267}
268
269void PluggingController::PluggableInfo::tabCompletion(std::vector<string>& tokens) const
270{
271 if (tokens.size() == 3) {
272 completeString(tokens, view::transform(
273 OUTER(PluggingController, pluggableInfo).pluggables,
274 [](auto& p) -> std::string_view { return p->getName(); }));
275 }
276}
277
278// Connector info
279
280PluggingController::ConnectorInfo::ConnectorInfo(
281 InfoCommand& machineInfoCommand)
282 : InfoTopic(machineInfoCommand, "connector")
283{
284}
285
286void PluggingController::ConnectorInfo::execute(
287 std::span<const TclObject> tokens, TclObject& result) const
288{
289 auto& pluggingController = OUTER(PluggingController, connectorInfo);
290 switch (tokens.size()) {
291 case 2:
292 result.addListElements(
293 view::transform(pluggingController.connectors,
294 [](auto& c) { return c->getName(); }));
295 break;
296 case 3: {
297 auto& connector = pluggingController.getConnector(tokens[2].getString());
298 result = connector.getDescription();
299 break;
300 }
301 default:
302 throw CommandException("Too many parameters");
303 }
304}
305
306string PluggingController::ConnectorInfo::help(std::span<const TclObject> /*tokens*/) const
307{
308 return "Shows a list of available connectors.";
309}
310
311void PluggingController::ConnectorInfo::tabCompletion(std::vector<string>& tokens) const
312{
313 if (tokens.size() == 3) {
314 completeString(tokens, view::transform(
315 OUTER(PluggingController, connectorInfo).connectors,
316 [](auto& c) -> std::string_view { return c->getName(); }));
317 }
318}
319
320// Connection Class info
321
322PluggingController::ConnectionClassInfo::ConnectionClassInfo(
323 InfoCommand& machineInfoCommand)
324 : InfoTopic(machineInfoCommand, "connectionclass")
325{
326}
327
328void PluggingController::ConnectionClassInfo::execute(
329 std::span<const TclObject> tokens, TclObject& result) const
330{
331 auto& pluggingController = OUTER(PluggingController, connectionClassInfo);
332 switch (tokens.size()) {
333 case 2: {
334 std::vector<string_view> classes;
335 classes.reserve(pluggingController.connectors.size());
336 for (auto& c : pluggingController.connectors) {
337 classes.push_back(c->getClass());
338 }
339 for (auto& p : pluggingController.pluggables) {
340 auto c = p->getClass();
341 if (!contains(classes, c)) classes.push_back(c);
342 }
343 result.addListElements(classes);
344 break;
345 }
346 case 3: {
347 const auto& arg = tokens[2].getString();
348 if (auto* connector = pluggingController.findConnector(arg)) {
349 result = connector->getClass();
350 break;
351 }
352 if (auto* pluggable = pluggingController.findPluggable(arg)) {
353 result = pluggable->getClass();
354 break;
355 }
356 throw CommandException("No such connector or pluggable");
357 }
358 default:
359 throw CommandException("Too many parameters");
360 }
361}
362
363string PluggingController::ConnectionClassInfo::help(std::span<const TclObject> /*tokens*/) const
364{
365 return "Shows the class a connector or pluggable belongs to.";
366}
367
368void PluggingController::ConnectionClassInfo::tabCompletion(std::vector<string>& tokens) const
369{
370 if (tokens.size() == 3) {
371 auto& pluggingController = OUTER(PluggingController, connectionClassInfo);
372 auto names = concat(
373 view::transform(pluggingController.connectors,
374 [](auto& c) -> std::string_view { return c->getName(); }),
375 view::transform(pluggingController.pluggables,
376 [](auto& p) -> std::string_view { return p->getName(); }));
377 completeString(tokens, names);
378 }
379}
380
381} // 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:14
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...
constexpr double e
Definition: Math.hh:20
This file implemented 3 utility functions:
Definition: Autofire.cc:9
auto find(InputRange &&range, const T &value)
Definition: ranges.hh:160
auto filter(ForwardRange &&range, Predicate pred)
Definition: view.hh:476
constexpr auto transform(Range &&range, UnaryOp op)
Definition: view.hh:458
#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:125
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:100
auto concat(const Range &range, Tail &&... tail)
Definition: stl.hh:363
constexpr bool contains(ITER first, ITER last, const VAL &val)
Check if a range contains a given value, using linear search.
Definition: stl.hh:23
void strAppend(std::string &result, Ts &&...ts)
Definition: strCat.hh:620
constexpr auto end(const zstring_view &x)