PluggingController.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  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. using std::string;
  15. using std::string_view;
  16. using std::vector;
  17. namespace openmsx {
  18. PluggingController::PluggingController(MSXMotherBoard& motherBoard_)
  19. : motherBoard(motherBoard_)
  20. , plugCmd(
  21. motherBoard.getCommandController(),
  22. motherBoard.getStateChangeDistributor(),
  23. motherBoard.getScheduler())
  24. , unplugCmd(
  25. motherBoard.getCommandController(),
  26. motherBoard.getStateChangeDistributor(),
  27. motherBoard.getScheduler())
  28. , pluggableInfo (motherBoard.getMachineInfoCommand())
  29. , connectorInfo (motherBoard.getMachineInfoCommand())
  30. , connectionClassInfo(motherBoard.getMachineInfoCommand())
  31. {
  32. PluggableFactory::createAll(*this, motherBoard);
  33. }
  34. PluggingController::~PluggingController()
  35. {
  36. #ifndef NDEBUG
  37. // This is similar to an assert: it should never print anything,
  38. // but if it does, it helps to catch an error.
  39. for (auto& c : connectors) {
  40. std::cerr << "ERROR: Connector still registered at shutdown: "
  41. << c->getName() << '\n';
  42. }
  43. #endif
  44. }
  45. void PluggingController::registerConnector(Connector& connector)
  46. {
  47. connectors.push_back(&connector);
  48. getCliComm().update(CliComm::CONNECTOR, connector.getName(), "add");
  49. }
  50. void PluggingController::unregisterConnector(Connector& connector)
  51. {
  52. connector.unplug(motherBoard.getCurrentTime());
  53. move_pop_back(connectors, rfind_unguarded(connectors, &connector));
  54. getCliComm().update(CliComm::CONNECTOR, connector.getName(), "remove");
  55. }
  56. void PluggingController::registerPluggable(std::unique_ptr<Pluggable> pluggable)
  57. {
  58. pluggables.push_back(std::move(pluggable));
  59. }
  60. // === Commands ===
  61. // plug command
  62. PluggingController::PlugCmd::PlugCmd(
  63. CommandController& commandController_,
  64. StateChangeDistributor& stateChangeDistributor_,
  65. Scheduler& scheduler_)
  66. : RecordedCommand(commandController_, stateChangeDistributor_,
  67. scheduler_, "plug")
  68. {
  69. }
  70. void PluggingController::PlugCmd::execute(
  71. span<const TclObject> tokens, TclObject& result_, EmuTime::param time)
  72. {
  73. checkNumArgs(tokens, Between{1, 3}, Prefix{1}, "?connector? ?pluggable?");
  74. string result;
  75. auto& pluggingController = OUTER(PluggingController, plugCmd);
  76. switch (tokens.size()) {
  77. case 1:
  78. for (auto& c : pluggingController.connectors) {
  79. strAppend(result, c->getName(), ": ",
  80. c->getPlugged().getName(), '\n');
  81. }
  82. break;
  83. case 2: {
  84. auto& connector = pluggingController.getConnector(tokens[1].getString());
  85. strAppend(result, connector.getName(), ": ",
  86. connector.getPlugged().getName());
  87. break;
  88. }
  89. case 3: {
  90. string_view connName = tokens[1].getString();
  91. string_view plugName = tokens[2].getString();
  92. auto& connector = pluggingController.getConnector(connName);
  93. auto& pluggable = pluggingController.getPluggable(plugName);
  94. if (&connector.getPlugged() == &pluggable) {
  95. // already plugged, don't unplug/replug
  96. break;
  97. }
  98. if (connector.getClass() != pluggable.getClass()) {
  99. throw CommandException("plug: ", plugName,
  100. " doesn't fit in ", connName);
  101. }
  102. connector.unplug(time);
  103. try {
  104. connector.plug(pluggable, time);
  105. pluggingController.getCliComm().update(
  106. CliComm::PLUG, connName, plugName);
  107. } catch (PlugException& e) {
  108. throw CommandException("plug: plug failed: ", e.getMessage());
  109. }
  110. break;
  111. }
  112. }
  113. result_ = result; // TODO return Tcl list
  114. }
  115. string PluggingController::PlugCmd::help(const vector<string>& /*tokens*/) const
  116. {
  117. return "Plugs a plug into a connector\n"
  118. " plug [connector] [plug]";
  119. }
  120. void PluggingController::PlugCmd::tabCompletion(vector<string>& tokens) const
  121. {
  122. auto& pluggingController = OUTER(PluggingController, plugCmd);
  123. if (tokens.size() == 2) {
  124. // complete connector
  125. auto connectorNames = to_vector(view::transform(
  126. pluggingController.connectors,
  127. [](auto& c) { return c->getName(); }));
  128. completeString(tokens, connectorNames);
  129. } else if (tokens.size() == 3) {
  130. // complete pluggable
  131. vector<string_view> pluggableNames;
  132. auto* connector = pluggingController.findConnector(tokens[1]);
  133. string_view className = connector ? connector->getClass() : string_view{};
  134. for (auto& p : pluggingController.pluggables) {
  135. if (p->getClass() == className) {
  136. pluggableNames.emplace_back(p->getName());
  137. }
  138. }
  139. completeString(tokens, pluggableNames);
  140. }
  141. }
  142. bool PluggingController::PlugCmd::needRecord(span<const TclObject> tokens) const
  143. {
  144. return tokens.size() == 3;
  145. }
  146. // unplug command
  147. PluggingController::UnplugCmd::UnplugCmd(
  148. CommandController& commandController_,
  149. StateChangeDistributor& stateChangeDistributor_,
  150. Scheduler& scheduler_)
  151. : RecordedCommand(commandController_, stateChangeDistributor_,
  152. scheduler_, "unplug")
  153. {
  154. }
  155. void PluggingController::UnplugCmd::execute(
  156. span<const TclObject> tokens, TclObject& /*result*/, EmuTime::param time)
  157. {
  158. checkNumArgs(tokens, 2, "connector");
  159. auto& pluggingController = OUTER(PluggingController, unplugCmd);
  160. string_view connName = tokens[1].getString();
  161. auto& connector = pluggingController.getConnector(connName);
  162. connector.unplug(time);
  163. pluggingController.getCliComm().update(CliComm::PLUG, connName, {});
  164. }
  165. string PluggingController::UnplugCmd::help(const vector<string>& /*tokens*/) const
  166. {
  167. return "Unplugs a plug from a connector\n"
  168. " unplug [connector]";
  169. }
  170. void PluggingController::UnplugCmd::tabCompletion(vector<string>& tokens) const
  171. {
  172. if (tokens.size() == 2) {
  173. // complete connector
  174. auto connectorNames = to_vector(view::transform(
  175. OUTER(PluggingController, unplugCmd).connectors,
  176. [](auto* c) { return c->getName(); }));
  177. completeString(tokens, connectorNames);
  178. }
  179. }
  180. Connector* PluggingController::findConnector(string_view name) const
  181. {
  182. auto it = ranges::find_if(connectors,
  183. [&](auto* c) { return c->getName() == name; });
  184. return (it != end(connectors)) ? *it : nullptr;
  185. }
  186. Connector& PluggingController::getConnector(string_view name) const
  187. {
  188. if (auto* result = findConnector(name)) {
  189. return *result;
  190. }
  191. throw CommandException("No such connector: ", name);
  192. }
  193. Pluggable* PluggingController::findPluggable(string_view name) const
  194. {
  195. auto it = ranges::find_if(pluggables,
  196. [&](auto& p) { return p->getName() == name; });
  197. return (it != end(pluggables)) ? it->get() : nullptr;
  198. }
  199. Pluggable& PluggingController::getPluggable(string_view name) const
  200. {
  201. if (auto* result = findPluggable(name)) {
  202. return *result;
  203. }
  204. throw CommandException("No such pluggable: ", name);
  205. }
  206. CliComm& PluggingController::getCliComm()
  207. {
  208. return motherBoard.getMSXCliComm();
  209. }
  210. EmuTime::param PluggingController::getCurrentTime() const
  211. {
  212. return motherBoard.getCurrentTime();
  213. }
  214. // Pluggable info
  215. PluggingController::PluggableInfo::PluggableInfo(
  216. InfoCommand& machineInfoCommand)
  217. : InfoTopic(machineInfoCommand, "pluggable")
  218. {
  219. }
  220. void PluggingController::PluggableInfo::execute(
  221. span<const TclObject> tokens, TclObject& result) const
  222. {
  223. auto& pluggingController = OUTER(PluggingController, pluggableInfo);
  224. switch (tokens.size()) {
  225. case 2:
  226. result.addListElements(
  227. view::transform(pluggingController.pluggables,
  228. [](auto& p) { return p->getName(); }));
  229. break;
  230. case 3: {
  231. auto& pluggable = pluggingController.getPluggable(
  232. tokens[2].getString());
  233. result = pluggable.getDescription();
  234. break;
  235. }
  236. default:
  237. throw CommandException("Too many parameters");
  238. }
  239. }
  240. string PluggingController::PluggableInfo::help(const vector<string>& /*tokens*/) const
  241. {
  242. return "Shows a list of available pluggables. "
  243. "Or show info on a specific pluggable.";
  244. }
  245. void PluggingController::PluggableInfo::tabCompletion(vector<string>& tokens) const
  246. {
  247. if (tokens.size() == 3) {
  248. auto pluggableNames = to_vector(view::transform(
  249. OUTER(PluggingController, pluggableInfo).pluggables,
  250. [](auto& p) { return p->getName(); }));
  251. completeString(tokens, pluggableNames);
  252. }
  253. }
  254. // Connector info
  255. PluggingController::ConnectorInfo::ConnectorInfo(
  256. InfoCommand& machineInfoCommand)
  257. : InfoTopic(machineInfoCommand, "connector")
  258. {
  259. }
  260. void PluggingController::ConnectorInfo::execute(
  261. span<const TclObject> tokens, TclObject& result) const
  262. {
  263. auto& pluggingController = OUTER(PluggingController, connectorInfo);
  264. switch (tokens.size()) {
  265. case 2:
  266. result.addListElements(
  267. view::transform(pluggingController.connectors,
  268. [](auto& c) { return c->getName(); }));
  269. break;
  270. case 3: {
  271. auto& connector = pluggingController.getConnector(tokens[2].getString());
  272. result = connector.getDescription();
  273. break;
  274. }
  275. default:
  276. throw CommandException("Too many parameters");
  277. }
  278. }
  279. string PluggingController::ConnectorInfo::help(const vector<string>& /*tokens*/) const
  280. {
  281. return "Shows a list of available connectors.";
  282. }
  283. void PluggingController::ConnectorInfo::tabCompletion(vector<string>& tokens) const
  284. {
  285. if (tokens.size() == 3) {
  286. auto connectorNames = to_vector(view::transform(
  287. OUTER(PluggingController, connectorInfo).connectors,
  288. [](auto& c) { return c->getName(); }));
  289. completeString(tokens, connectorNames);
  290. }
  291. }
  292. // Connection Class info
  293. PluggingController::ConnectionClassInfo::ConnectionClassInfo(
  294. InfoCommand& machineInfoCommand)
  295. : InfoTopic(machineInfoCommand, "connectionclass")
  296. {
  297. }
  298. void PluggingController::ConnectionClassInfo::execute(
  299. span<const TclObject> tokens, TclObject& result) const
  300. {
  301. auto& pluggingController = OUTER(PluggingController, connectionClassInfo);
  302. switch (tokens.size()) {
  303. case 2: {
  304. std::vector<string_view> classes;
  305. classes.reserve(pluggingController.connectors.size());
  306. for (auto& c : pluggingController.connectors) {
  307. classes.push_back(c->getClass());
  308. }
  309. for (auto& p : pluggingController.pluggables) {
  310. auto c = p->getClass();
  311. if (!contains(classes, c)) classes.push_back(c);
  312. }
  313. result.addListElements(classes);
  314. break;
  315. }
  316. case 3: {
  317. const auto& arg = tokens[2].getString();
  318. if (auto* connector = pluggingController.findConnector(arg)) {
  319. result = connector->getClass();
  320. break;
  321. }
  322. if (auto* pluggable = pluggingController.findPluggable(arg)) {
  323. result = pluggable->getClass();
  324. break;
  325. }
  326. throw CommandException("No such connector or pluggable");
  327. }
  328. default:
  329. throw CommandException("Too many parameters");
  330. }
  331. }
  332. string PluggingController::ConnectionClassInfo::help(const vector<string>& /*tokens*/) const
  333. {
  334. return "Shows the class a connector or pluggable belongs to.";
  335. }
  336. void PluggingController::ConnectionClassInfo::tabCompletion(vector<string>& tokens) const
  337. {
  338. if (tokens.size() == 3) {
  339. auto& pluggingController = OUTER(PluggingController, connectionClassInfo);
  340. auto names = concat<string_view>(
  341. view::transform(pluggingController.connectors,
  342. [](auto& c) { return c->getName(); }),
  343. view::transform(pluggingController.pluggables,
  344. [](auto& p) { return p->getName(); }));
  345. completeString(tokens, names);
  346. }
  347. }
  348. } // namespace openmsx