|
3 | 3 | #include <algorithm> |
4 | 4 | #include <cstring> |
5 | 5 | #include <set> |
| 6 | +#include <unordered_map> |
6 | 7 |
|
7 | 8 | #include "Adafruit_NeoPixel.h" |
8 | 9 | #include "RS485Comm.h" |
@@ -112,6 +113,10 @@ void PPUC::SetDebug(bool debug) { |
112 | 113 | m_debug = debug; |
113 | 114 | } |
114 | 115 |
|
| 116 | +void PPUC::SetDebugErrors(bool debugErrors) { |
| 117 | + m_pRS485Comm->SetDebugErrors(debugErrors); |
| 118 | +} |
| 119 | + |
115 | 120 | bool PPUC::GetDebug() { return m_debug; } |
116 | 121 |
|
117 | 122 | void PPUC::SetRom(const char* rom) { strcpy(m_rom, rom); } |
@@ -196,52 +201,46 @@ void PPUC::SendLedConfigBlock(const YAML::Node& items, uint32_t type, |
196 | 201 | bool PPUC::Connect() { |
197 | 202 | if (m_pRS485Comm->Connect(m_serial)) { |
198 | 203 | uint8_t index = 0; |
| 204 | + std::vector<uint8_t> configuredBoards; |
199 | 205 | std::vector<uint8_t> switchBoards; |
200 | 206 | std::set<uint16_t> coilNumbers; |
201 | 207 | std::set<uint16_t> lampNumbers; |
202 | 208 | std::set<uint16_t> switchNumbers; |
| 209 | + std::unordered_map<uint8_t, std::vector<uint16_t>> switchNumbersByBoard; |
203 | 210 | const YAML::Node& boards = m_ppucConfig["boards"]; |
204 | 211 | for (YAML::Node n_board : boards) { |
205 | | - m_pRS485Comm->SendConfigEvent(new ConfigEvent( |
206 | | - n_board["number"].as<uint8_t>(), (uint8_t)CONFIG_TOPIC_PLATFORM, 0, |
207 | | - (uint8_t)CONFIG_TOPIC_PLATFORM, m_platform)); |
208 | | - |
209 | | - m_pRS485Comm->SendConfigEvent( |
210 | | - new ConfigEvent(n_board["number"].as<uint8_t>(), |
211 | | - (uint8_t)CONFIG_TOPIC_COIN_DOOR_CLOSED_SWITCH, 0, |
212 | | - (uint8_t)CONFIG_TOPIC_NUMBER, |
213 | | - m_ppucConfig["coinDoorClosedSwitch"].as<uint8_t>())); |
214 | 212 | m_coinDoorClosedSwitch = |
215 | 213 | m_ppucConfig["coinDoorClosedSwitch"].as<uint8_t>(); |
216 | | - |
217 | | - m_pRS485Comm->SendConfigEvent( |
218 | | - new ConfigEvent(n_board["number"].as<uint8_t>(), |
219 | | - (uint8_t)CONFIG_TOPIC_GAME_ON_SOLENOID, 0, |
220 | | - (uint8_t)CONFIG_TOPIC_NUMBER, |
221 | | - m_ppucConfig["gameOnSolenoid"].as<uint8_t>())); |
222 | 214 | m_gameOnSolenoid = m_ppucConfig["gameOnSolenoid"].as<uint8_t>(); |
223 | 215 |
|
224 | 216 | if (n_board["pollEvents"].as<bool>()) { |
225 | 217 | const uint8_t b = n_board["number"].as<uint8_t>(); |
226 | 218 | m_pRS485Comm->RegisterSwitchBoard(b); |
227 | 219 | switchBoards.push_back(b); |
228 | 220 | } |
| 221 | + configuredBoards.push_back(n_board["number"].as<uint8_t>()); |
229 | 222 | } |
230 | 223 |
|
231 | 224 | const YAML::Node& switchMatrix = m_ppucConfig["switchMatrix"]; |
232 | 225 | if (switchMatrix) { |
233 | 226 | const YAML::Node& matrixSwitches = switchMatrix["switches"]; |
234 | 227 | if (matrixSwitches) { |
235 | 228 | for (YAML::Node n_switch : matrixSwitches) { |
236 | | - switchNumbers.insert(n_switch["number"].as<uint16_t>()); |
| 229 | + const uint16_t switchNumber = n_switch["number"].as<uint16_t>(); |
| 230 | + switchNumbers.insert(switchNumber); |
| 231 | + switchNumbersByBoard[n_switch["board"].as<uint8_t>()].push_back( |
| 232 | + switchNumber); |
237 | 233 | } |
238 | 234 | } |
239 | 235 | } |
240 | 236 |
|
241 | 237 | const YAML::Node& switches = m_ppucConfig["switches"]; |
242 | 238 | if (switches) { |
243 | 239 | for (YAML::Node n_switch : switches) { |
244 | | - switchNumbers.insert(n_switch["number"].as<uint16_t>()); |
| 240 | + const uint16_t switchNumber = n_switch["number"].as<uint16_t>(); |
| 241 | + switchNumbers.insert(switchNumber); |
| 242 | + switchNumbersByBoard[n_switch["board"].as<uint8_t>()].push_back( |
| 243 | + switchNumber); |
245 | 244 | } |
246 | 245 | } |
247 | 246 |
|
@@ -299,6 +298,24 @@ bool PPUC::Connect() { |
299 | 298 | switchMapping.resize(runtimeConfig.switchBits); |
300 | 299 | m_pRS485Comm->SetMappings(coilMapping, lampMapping, switchMapping); |
301 | 300 | m_pRS485Comm->SetRuntimeConfig(runtimeConfig); |
| 301 | + m_pRS485Comm->SetConfiguredBoards(configuredBoards); |
| 302 | + m_pRS485Comm->SetSwitchNumbersByBoard(switchNumbersByBoard); |
| 303 | + |
| 304 | + for (YAML::Node n_board : boards) { |
| 305 | + const uint8_t boardNumber = n_board["number"].as<uint8_t>(); |
| 306 | + m_pRS485Comm->SendConfigEvent(new ConfigEvent( |
| 307 | + boardNumber, (uint8_t)CONFIG_TOPIC_PLATFORM, 0, |
| 308 | + (uint8_t)CONFIG_TOPIC_PLATFORM, m_platform)); |
| 309 | + m_pRS485Comm->SendConfigEvent( |
| 310 | + new ConfigEvent(boardNumber, |
| 311 | + (uint8_t)CONFIG_TOPIC_COIN_DOOR_CLOSED_SWITCH, 0, |
| 312 | + (uint8_t)CONFIG_TOPIC_NUMBER, |
| 313 | + m_ppucConfig["coinDoorClosedSwitch"].as<uint8_t>())); |
| 314 | + m_pRS485Comm->SendConfigEvent( |
| 315 | + new ConfigEvent(boardNumber, (uint8_t)CONFIG_TOPIC_GAME_ON_SOLENOID, |
| 316 | + 0, (uint8_t)CONFIG_TOPIC_NUMBER, |
| 317 | + m_ppucConfig["gameOnSolenoid"].as<uint8_t>())); |
| 318 | + } |
302 | 319 |
|
303 | 320 | // Send switch matrix configuration to I/O boards |
304 | 321 | // IMPORTANT: This must be done before sending individual switch configs |
@@ -633,12 +650,22 @@ bool PPUC::Connect() { |
633 | 650 | } |
634 | 651 | } |
635 | 652 |
|
636 | | - // Configure token-ring handoff for switch-capable boards before the V2 |
637 | | - // setup frame starts runtime processing on the boards. |
638 | | - for (size_t i = 0; i < switchBoards.size(); ++i) { |
639 | | - const uint8_t current = switchBoards[i]; |
640 | | - const uint8_t next = (i + 1 < switchBoards.size()) |
641 | | - ? switchBoards[i + 1] |
| 653 | + m_pRS485Comm->FinalizeConfiguredBoardPresence(); |
| 654 | + |
| 655 | + std::vector<uint8_t> presentSwitchBoards; |
| 656 | + for (const uint8_t board : switchBoards) { |
| 657 | + if (m_pRS485Comm->IsBoardPresent(board)) { |
| 658 | + presentSwitchBoards.push_back(board); |
| 659 | + } |
| 660 | + } |
| 661 | + m_pRS485Comm->SetActiveSwitchBoards(presentSwitchBoards); |
| 662 | + |
| 663 | + // Configure token-ring handoff only across boards that acknowledged |
| 664 | + // configuration during startup. |
| 665 | + for (size_t i = 0; i < presentSwitchBoards.size(); ++i) { |
| 666 | + const uint8_t current = presentSwitchBoards[i]; |
| 667 | + const uint8_t next = (i + 1 < presentSwitchBoards.size()) |
| 668 | + ? presentSwitchBoards[i + 1] |
642 | 669 | : ppuc::v2::kNoBoard; |
643 | 670 | m_pRS485Comm->SendConfigEvent(new ConfigEvent( |
644 | 671 | current, (uint8_t)CONFIG_TOPIC_SWITCH_CHAIN, 0, |
@@ -692,6 +719,15 @@ void PPUC::SetGIState(int string, int brightness) { |
692 | 719 | ppuc::v2::ClampGiLevel(giBrightness))); |
693 | 720 | } |
694 | 721 |
|
| 722 | +void PPUC::SetSwitchState(int number, int state) { |
| 723 | + m_pRS485Comm->SetVirtualSwitchState(static_cast<uint16_t>(number), |
| 724 | + state == 0 ? 0 : 1); |
| 725 | +} |
| 726 | + |
| 727 | +bool PPUC::IsSwitchVirtualized(int number) { |
| 728 | + return m_pRS485Comm->IsSwitchVirtualized(static_cast<uint16_t>(number)); |
| 729 | +} |
| 730 | + |
695 | 731 | PPUCSwitchState* PPUC::GetNextSwitchState() { |
696 | 732 | return m_pRS485Comm->GetNextSwitchState(); |
697 | 733 | } |
@@ -741,6 +777,12 @@ void PPUC::CoilTest(uint8_t number) { |
741 | 777 |
|
742 | 778 | for (const auto& coil : GetCoils()) { |
743 | 779 | if (coil.type == PWM_TYPE_SOLENOID || coil.type == PWM_TYPE_FLASHER) { |
| 780 | + if (coil.number == GetGameOnSolenoid()) { |
| 781 | + // Keep the high-power enable coil asserted for the whole test. Pulsing |
| 782 | + // it as part of the test would drop power and make every following |
| 783 | + // coil appear dead. |
| 784 | + continue; |
| 785 | + } |
744 | 786 | if (number != 0 && coil.number != number) { |
745 | 787 | continue; |
746 | 788 | } |
|
0 commit comments