OXYGENHUB
◆ main openmsx / src / PluggingController.cc
11.0 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "PluggingController.hh"
2
3
#include "CommandException.hh"
4
#include "Connector.hh"
5
#include "MSXCliComm.hh"
6
#include "MSXMotherBoard.hh"
7
#include "PlugException.hh"
8
#include "Pluggable.hh"
9
#include "PluggableFactory.hh"
10
#include "TclObject.hh"
11
12
#include "outer.hh"
13
14
#include <algorithm>
15
#include <iostream>
16
#include <ranges>
17
18
namespace openmsx {
19
20
PluggingController::PluggingController(MSXMotherBoard& motherBoard_)
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
37
PluggingController::~PluggingController()
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
49
void PluggingController::registerConnector(Connector& connector)
50
{
51
	connectors.push_back(&connector);
52
	getCliComm().update(CliComm::UpdateType::CONNECTOR, connector.getName(), "add");
53
}
54
55
void PluggingController::unregisterConnector(Connector& connector)
56
{
57
	connector.unplug(motherBoard.getCurrentTime());
58
	move_pop_back(connectors, rfind_unguarded(connectors, &connector));
59
	getCliComm().update(CliComm::UpdateType::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
	std::span<const TclObject> tokens, TclObject& result_, EmuTime time)
83
{
84
	checkNumArgs(tokens, Between{1, 3}, Prefix{1}, "?connector? ?pluggable?");
85
	std::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
		const auto& connector = pluggingController.getConnector(tokens[1].getString());
96
		strAppend(result, connector.getName(), ": ",
97
		          connector.getPlugged().getName());
98
		break;
99
	}
100
	case 3: {
101
		std::string_view connName = tokens[1].getString();
102
		std::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::UpdateType::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
std::string 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
133
void PluggingController::PlugCmd::tabCompletion(std::vector<std::string>& tokens) const
134
{
135
	auto& pluggingController = OUTER(PluggingController, plugCmd);
136
	if (tokens.size() == 2) {
137
		// complete connector
138
		completeString(tokens, std::views::transform(
139
			pluggingController.connectors,
140
			[](auto& c) -> std::string_view { return c->getName(); }));
141
	} else if (tokens.size() == 3) {
142
		// complete pluggable
143
		const auto* connector = pluggingController.findConnector(tokens[1]);
144
		std::string_view className = connector ? connector->getClass() : std::string_view{};
145
		completeString(tokens, std::views::transform(std::views::filter(pluggingController.pluggables,
146
			[&](auto& p) { return p->getClass() == className; }),
147
			[](auto& p) -> std::string_view { return p->getName(); }));
148
	}
149
}
150
151
bool PluggingController::PlugCmd::needRecord(std::span<const TclObject> tokens) const
152
{
153
	return tokens.size() == 3;
154
}
155
156
157
//  unplug command
158
159
PluggingController::UnplugCmd::UnplugCmd(
160
		CommandController& commandController_,
161
		StateChangeDistributor& stateChangeDistributor_,
162
		Scheduler& scheduler_)
163
	: RecordedCommand(commandController_, stateChangeDistributor_,
164
	                  scheduler_, "unplug")
165
{
166
}
167
168
void PluggingController::UnplugCmd::execute(
169
	std::span<const TclObject> tokens, TclObject& /*result*/, EmuTime time)
170
{
171
	checkNumArgs(tokens, 2, "connector");
172
	auto& pluggingController = OUTER(PluggingController, unplugCmd);
173
	std::string_view connName = tokens[1].getString();
174
	auto& connector = pluggingController.getConnector(connName);
175
	connector.unplug(time);
176
	pluggingController.getCliComm().update(CliComm::UpdateType::PLUG, connName, {});
177
}
178
179
std::string PluggingController::UnplugCmd::help(std::span<const TclObject> /*tokens*/) const
180
{
181
	return "Unplugs a plug from a connector\n"
182
	       " unplug [connector]";
183
}
184
185
void PluggingController::UnplugCmd::tabCompletion(std::vector<std::string>& tokens) const
186
{
187
	if (tokens.size() == 2) {
188
		// complete connector
189
		completeString(tokens, std::views::transform(
190
			OUTER(PluggingController, unplugCmd).connectors,
191
			[](auto* c) -> std::string_view { return c->getName(); }));
192
	}
193
}
194
195
Connector* PluggingController::findConnector(std::string_view name) const
196
{
197
	auto it = std::ranges::find(connectors, name, &Connector::getName);
198
	return (it != end(connectors)) ? *it : nullptr;
199
}
200
201
Connector& PluggingController::getConnector(std::string_view name) const
202
{
203
	if (auto* result = findConnector(name)) {
204
		return *result;
205
	}
206
	throw CommandException("No such connector: ", name);
207
}
208
209
Pluggable* PluggingController::findPluggable(std::string_view name) const
210
{
211
	auto it = std::ranges::find(pluggables, name, &Pluggable::getName);
212
	return (it != end(pluggables)) ? it->get() : nullptr;
213
}
214
215
Pluggable& PluggingController::getPluggable(std::string_view name) const
216
{
217
	if (auto* result = findPluggable(name)) {
218
		return *result;
219
	}
220
	throw CommandException("No such pluggable: ", name);
221
}
222
223
CliComm& PluggingController::getCliComm()
224
{
225
	return motherBoard.getMSXCliComm();
226
}
227
228
EmuTime PluggingController::getCurrentTime() const
229
{
230
	return motherBoard.getCurrentTime();
231
}
232
233
234
// Pluggable info
235
236
PluggingController::PluggableInfo::PluggableInfo(
237
		InfoCommand& machineInfoCommand)
238
	: InfoTopic(machineInfoCommand, "pluggable")
239
{
240
}
241
242
void 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
			std::views::transform(pluggingController.pluggables,
250
			                [](auto& p) { return p->getName(); }));
251
		break;
252
	case 3: {
253
		const 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
263
std::string 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
269
void PluggingController::PluggableInfo::tabCompletion(std::vector<std::string>& tokens) const
270
{
271
	if (tokens.size() == 3) {
272
		completeString(tokens, std::views::transform(
273
			OUTER(PluggingController, pluggableInfo).pluggables,
274
			[](auto& p) -> std::string_view { return p->getName(); }));
275
	}
276
}
277
278
// Connector info
279
280
PluggingController::ConnectorInfo::ConnectorInfo(
281
		InfoCommand& machineInfoCommand)
282
	: InfoTopic(machineInfoCommand, "connector")
283
{
284
}
285
286
void 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
			std::views::transform(pluggingController.connectors,
294
			                [](auto& c) { return c->getName(); }));
295
		break;
296
	case 3: {
297
		const 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
306
std::string PluggingController::ConnectorInfo::help(std::span<const TclObject> /*tokens*/) const
307
{
308
	return "Shows a list of available connectors.";
309
}
310
311
void PluggingController::ConnectorInfo::tabCompletion(std::vector<std::string>& tokens) const
312
{
313
	if (tokens.size() == 3) {
314
		completeString(tokens, std::views::transform(
315
			OUTER(PluggingController, connectorInfo).connectors,
316
			[](auto& c) -> std::string_view { return c->getName(); }));
317
	}
318
}
319
320
// Connection Class info
321
322
PluggingController::ConnectionClassInfo::ConnectionClassInfo(
323
		InfoCommand& machineInfoCommand)
324
	: InfoTopic(machineInfoCommand, "connectionclass")
325
{
326
}
327
328
void PluggingController::ConnectionClassInfo::execute(
329
	std::span<const TclObject> tokens, TclObject& result) const
330
{
331
	const auto& pluggingController = OUTER(PluggingController, connectionClassInfo);
332
	switch (tokens.size()) {
333
	case 2: {
334
		std::vector<std::string_view> classes;
335
		classes.reserve(pluggingController.connectors.size());
336
		for (const auto& c : pluggingController.connectors) {
337
			classes.push_back(c->getClass());
338
		}
339
		for (const 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 (const auto* connector = pluggingController.findConnector(arg)) {
349
			result = connector->getClass();
350
			break;
351
		}
352
		if (const 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
363
std::string PluggingController::ConnectionClassInfo::help(std::span<const TclObject> /*tokens*/) const
364
{
365
	return "Shows the class a connector or pluggable belongs to.";
366
}
367
368
void PluggingController::ConnectionClassInfo::tabCompletion(std::vector<std::string>& tokens) const
369
{
370
	if (tokens.size() == 3) {
371
		auto& pluggingController = OUTER(PluggingController, connectionClassInfo);
372
		auto names = concat(
373
			std::views::transform(pluggingController.connectors,
374
			                [](auto& c) -> std::string_view { return c->getName(); }),
375
			std::views::transform(pluggingController.pluggables,
376
			                [](auto& p) -> std::string_view { return p->getName(); }));
377
		completeString(tokens, names);
378
	}
379
}
380
381
} // namespace openmsx