Skip to content

Commit 02d38fd

Browse files
committed
removed lagacy event transmisson support, added ackknowledge
1 parent 5d6b822 commit 02d38fd

5 files changed

Lines changed: 494 additions & 280 deletions

File tree

AGENTS.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,29 @@ Current transport-tuning points to remember in `RS485Comm`:
212212
- switch-reply receive window in `ReceiveSwitchStateFrame()`
213213
- stale-input flush after a missed switch-reply chain
214214
- miss-count threshold before `m_needSessionResync = true`
215+
- output-frame pacing in the runtime loop
216+
- serial write timeout / short-write retry behavior
217+
218+
## Current Freeze Status
219+
220+
- Long-run attract-mode testing is still not robust.
221+
- One run may work for several minutes, while another can freeze in under a minute.
222+
- The visible freeze still correlates with missed switch-reply chains and the host recovery path.
223+
- Host timing changes clearly affect the symptom, but that does not prove the root cause is host-only.
224+
- Treat firmware as still in scope, especially the board-side switch reply path and token forwarding behavior.
225+
226+
## Virtual Board Implementation Notes
227+
228+
- First implementation slice is host-side only.
229+
- `libppuc` now tracks configured boards and switch ownership by board from YAML.
230+
- Board presence is now determined by explicit firmware-backed `ConfigAck` responses during startup config transmission.
231+
- Every `ConfigFrame` must be acknowledged by the addressed board.
232+
- Missing configured boards become host-side virtual boards if they do not acknowledge config during startup.
233+
- Config frames that are not acknowledged are printed as startup errors with board/topic/index/key details.
234+
- All switches owned by a virtual board are initialized to `open`.
235+
- Host-side switch injection is now limited to switches owned by virtualized boards.
236+
- This is the intended basis for bench setups where the cabinet board is absent.
237+
- Current limitation: virtual switch boards are not yet full synthetic participants in the runtime switch-reply cycle, and host-side virtual switch updates are not yet pushed into board-local high-power gating logic.
215238

216239
## Next Bring-Up Focus
217240

src/PPUC.cpp

Lines changed: 65 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <algorithm>
44
#include <cstring>
55
#include <set>
6+
#include <unordered_map>
67

78
#include "Adafruit_NeoPixel.h"
89
#include "RS485Comm.h"
@@ -112,6 +113,10 @@ void PPUC::SetDebug(bool debug) {
112113
m_debug = debug;
113114
}
114115

116+
void PPUC::SetDebugErrors(bool debugErrors) {
117+
m_pRS485Comm->SetDebugErrors(debugErrors);
118+
}
119+
115120
bool PPUC::GetDebug() { return m_debug; }
116121

117122
void PPUC::SetRom(const char* rom) { strcpy(m_rom, rom); }
@@ -196,52 +201,46 @@ void PPUC::SendLedConfigBlock(const YAML::Node& items, uint32_t type,
196201
bool PPUC::Connect() {
197202
if (m_pRS485Comm->Connect(m_serial)) {
198203
uint8_t index = 0;
204+
std::vector<uint8_t> configuredBoards;
199205
std::vector<uint8_t> switchBoards;
200206
std::set<uint16_t> coilNumbers;
201207
std::set<uint16_t> lampNumbers;
202208
std::set<uint16_t> switchNumbers;
209+
std::unordered_map<uint8_t, std::vector<uint16_t>> switchNumbersByBoard;
203210
const YAML::Node& boards = m_ppucConfig["boards"];
204211
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>()));
214212
m_coinDoorClosedSwitch =
215213
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>()));
222214
m_gameOnSolenoid = m_ppucConfig["gameOnSolenoid"].as<uint8_t>();
223215

224216
if (n_board["pollEvents"].as<bool>()) {
225217
const uint8_t b = n_board["number"].as<uint8_t>();
226218
m_pRS485Comm->RegisterSwitchBoard(b);
227219
switchBoards.push_back(b);
228220
}
221+
configuredBoards.push_back(n_board["number"].as<uint8_t>());
229222
}
230223

231224
const YAML::Node& switchMatrix = m_ppucConfig["switchMatrix"];
232225
if (switchMatrix) {
233226
const YAML::Node& matrixSwitches = switchMatrix["switches"];
234227
if (matrixSwitches) {
235228
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);
237233
}
238234
}
239235
}
240236

241237
const YAML::Node& switches = m_ppucConfig["switches"];
242238
if (switches) {
243239
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);
245244
}
246245
}
247246

@@ -299,6 +298,24 @@ bool PPUC::Connect() {
299298
switchMapping.resize(runtimeConfig.switchBits);
300299
m_pRS485Comm->SetMappings(coilMapping, lampMapping, switchMapping);
301300
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+
}
302319

303320
// Send switch matrix configuration to I/O boards
304321
// IMPORTANT: This must be done before sending individual switch configs
@@ -633,12 +650,22 @@ bool PPUC::Connect() {
633650
}
634651
}
635652

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]
642669
: ppuc::v2::kNoBoard;
643670
m_pRS485Comm->SendConfigEvent(new ConfigEvent(
644671
current, (uint8_t)CONFIG_TOPIC_SWITCH_CHAIN, 0,
@@ -692,6 +719,15 @@ void PPUC::SetGIState(int string, int brightness) {
692719
ppuc::v2::ClampGiLevel(giBrightness)));
693720
}
694721

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+
695731
PPUCSwitchState* PPUC::GetNextSwitchState() {
696732
return m_pRS485Comm->GetNextSwitchState();
697733
}
@@ -741,6 +777,12 @@ void PPUC::CoilTest(uint8_t number) {
741777

742778
for (const auto& coil : GetCoils()) {
743779
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+
}
744786
if (number != 0 && coil.number != number) {
745787
continue;
746788
}

src/PPUC.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class PPUCAPI PPUC {
3434

3535
void LoadConfiguration(const char* configFile);
3636
void SetDebug(bool debug);
37+
void SetDebugErrors(bool debugErrors);
3738
bool GetDebug();
3839
void SetRom(const char* rom);
3940
const char* GetRom();
@@ -47,6 +48,8 @@ class PPUCAPI PPUC {
4748
void SetSolenoidState(int number, int state);
4849
void SetLampState(int number, int state);
4950
void SetGIState(int string, int brightness);
51+
void SetSwitchState(int number, int state);
52+
bool IsSwitchVirtualized(int number);
5053
PPUCSwitchState* GetNextSwitchState();
5154

5255
uint8_t GetCoinDoorClosedSwitch() { return m_coinDoorClosedSwitch; };

0 commit comments

Comments
 (0)