Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ This library is currently used by [Visual Pinball Standalone](https://github.com
- **[WemosD1MPStripController](https://github.com/aetios50/PincabLedStrip)** - Wemos D1 Mini Pro based WS2812 LED strip controller
- **[PacLED64](https://www.ultimarc.com/output/led-and-output-controllers/pacled64/)** - Ultimarc's 64-output LED controller with PWM support
- **[LedWiz](https://groovygamegear.com/webstore/index.php?main_page=product_info&products_id=239)** - LED-Wiz's 32-port USB compatible lighting and output controller
- **[PinOne](https://www.clevelandsoftwaredesign.com/pinball-parts/pinone)** - Cleveland Software Design controller with 63 outputs

### **Implemented & Ready To Test**
- **DudesCab** - RP2040-based controller with 128 PWM outputs
- **ArtNet/DMX** - Professional lighting control via Ethernet (all platforms)
- **PinControl** - Arduino-based controller with 10 outputs
- **PinOne** - Cleveland Software Design controller with 63 outputs
- **FTDI Controllers** - FT245R bitbang controllers
- **WS2811/WS2812 LED Strips** - Addressable LED strip support
- **PAC Controllers** (PacDrive, PacUIO) - Cross-platform libusb implementation
Expand Down
17 changes: 5 additions & 12 deletions src/cab/out/pinone/PinOneAutoConfigurator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ std::string PinOneAutoConfigurator::TestSerialPort(const char* portName)
if (sp_get_port_by_name(portName, &port) != SP_OK)
return "";

// Check if port file actually exists before trying to open it
std::string portPath(portName);
std::ifstream portFile(portPath);
if (!portFile.good())
Expand All @@ -102,7 +101,6 @@ std::string PinOneAutoConfigurator::TestSerialPort(const char* portName)
return "";
}

// Set very short timeouts to prevent hanging
sp_set_baudrate(port, 2000000);
sp_set_bits(port, 8);
sp_set_parity(port, SP_PARITY_NONE);
Expand All @@ -127,6 +125,10 @@ std::string PinOneAutoConfigurator::TestSerialPort(const char* portName)
{
buffer[bytesRead] = '\0';
std::string result(buffer);

while (!result.empty() && (result.back() == '\r' || result.back() == '\n'))
result.pop_back();

if (result == "DEBUG,CSD Board Connected")
{
sp_close(port);
Expand All @@ -135,7 +137,6 @@ std::string PinOneAutoConfigurator::TestSerialPort(const char* portName)
}
}

// Force flush and set non-blocking mode before closing to prevent hang
sp_flush(port, SP_BUF_BOTH);
sp_set_rts(port, SP_RTS_OFF);
sp_set_dtr(port, SP_DTR_OFF);
Expand All @@ -154,15 +155,14 @@ std::string PinOneAutoConfigurator::TestSerialPort(const char* portName)
sp_set_dtr(port, SP_DTR_OFF);
}
catch (...)
{ /* Ignore errors in cleanup */
{
}

sp_close(port);
sp_free_port(port);
}
}

// Small delay to ensure port cleanup completes
std::this_thread::sleep_for(std::chrono::milliseconds(10));

return "";
Expand All @@ -174,17 +174,12 @@ std::string PinOneAutoConfigurator::GetDevice()
if (sp_list_ports(&portList) != SP_OK)
return "";

int portCount = 0;
while (portList[portCount] != nullptr)
portCount++;

for (int i = 0; portList[i] != nullptr; i++)
{
char* portName = sp_get_port_name(portList[i]);
if (!portName)
continue;

// Test each port synchronously with timeouts matching C# version (100ms)
std::string result = TestSerialPort(portName);
if (!result.empty())
{
Expand All @@ -198,9 +193,7 @@ std::string PinOneAutoConfigurator::GetDevice()
std::string comPort = "";
PinOneCommunication communication("");
if (communication.ConnectToServer())
{
comPort = communication.GetCOMPort();
}

return comPort;
}
Expand Down
27 changes: 14 additions & 13 deletions src/general/StringExtensions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,24 +176,25 @@ std::string ReplaceArgument(const std::string& format, int argIndex, const std::
std::string result = format;
std::string pattern = "{" + std::to_string(argIndex);

size_t pos = result.find(pattern);
if (pos != std::string::npos)
size_t pos = 0;
while ((pos = result.find(pattern, pos)) != std::string::npos)
{
size_t endPos = result.find("}", pos);
if (endPos != std::string::npos)
{
std::string placeholder = result.substr(pos, endPos - pos + 1);
std::string formattedArg = arg;
if (endPos == std::string::npos)
break;

size_t colonPos = placeholder.find(":");
if (colonPos != std::string::npos)
{
std::string formatSpec = placeholder.substr(colonPos + 1, placeholder.length() - colonPos - 2);
formattedArg = FormatArgument(arg, formatSpec);
}
std::string placeholder = result.substr(pos, endPos - pos + 1);
std::string formattedArg = arg;

result.replace(pos, endPos - pos + 1, formattedArg);
size_t colonPos = placeholder.find(":");
if (colonPos != std::string::npos)
{
std::string formatSpec = placeholder.substr(colonPos + 1, placeholder.length() - colonPos - 2);
formattedArg = FormatArgument(arg, formatSpec);
}

result.replace(pos, endPos - pos + 1, formattedArg);
pos += formattedArg.length();
}
return result;
}
Expand Down