-
Xcode Command Line Tools:
- If not already installed, open Terminal and run:
xcode-select --install
- If not already installed, open Terminal and run:
-
Qt 6 SDK:
-
UBPM requires Qt 6. You can install it in a couple of ways:
- Via Homebrew (Recommended for ease):
After installation, Homebrew might provide instructions to add Qt to your PATH if it's not done automatically. This usually involves adding a line to your shell configuration file (e.g.,
brew install qt@6
~/.zshrc
or~/.bash_profile
)
- Via Homebrew (Recommended for ease):
-
Verify
qmake
: After installation, ensureqmake
from your Qt 6 installation is accessible. Open a new terminal window and type:qmake --version
You should see output indicating Qt version 6.x.x. If not, you may need to adjust your system's PATH environment variable to include the
bin
directory of your Qt 6 installation (e.g.,~/Qt/6.x.x/macos/bin
).
-
-
Navigate to Sources & Configure with qmake: If you're in the
ubpm
root directory:cd sources qmake
If you are already in
ubpm/sources
, just run:qmake
(This prepares the project for compilation. Ensure
qmake
from your Qt installation is in your PATH.) -
Compile with make: Still in the
ubpm/sources
directory, compile the application:make
This will build the
ubpm.app
bundle in themainapp
subdirectory.
After a successful build (from the ubpm/sources
directory):
- From Terminal:
./mainapp/ubpm.app/Contents/MacOS/ubpm
- From Finder:
Navigate to
ubpm/sources/mainapp/
and double-clickubpm.app
.
- Make your code modifications.
- In Terminal, ensure you are in the
ubpm/sources
directory. - Optional (but recommended for .pro file changes):
qmake
- Recompile:
make
- Run the application again using one of the methods above.
This version of UBPM includes several key modifications:
The application can be launched or invoked using a custom URL scheme ubpm://
.
-
Registration (macOS): The URL scheme is registered in
sources/mainapp/ubpm.app/Contents/Info.plist
:<key>CFBundleURLTypes</key> <array> <dict> <key>CFBundleTypeRole</key> <string>Viewer</string> <key>CFBundleURLName</key> <string>page.codeberg.lazyt.ubpm</string> <!-- Bundle ID --> <key>CFBundleURLSchemes</key> <array> <string>ubpm</string> </array> </dict> </array>
-
Processing Logic: Incoming URLs are caught by an
eventFilter
insources/mainapp/MainWindow.cpp
(ormain.cpp
if the filter is onQApplication
). This filter typically calls a handler method likeMainWindow::handleUrl(const QUrl &url)
if the scheme isubpm
.// Simplified example from MainWindow.cpp eventFilter or main.cpp // protected: // bool YourApplicationSubclass::eventFilter(QObject *obj, QEvent *event) // { // if (event->type() == QEvent::FileOpen) { // For macOS URL handling // QFileOpenEvent *fileEvent = static_cast<QFileOpenEvent*>(event); // QUrl url = fileEvent->url(); // if (url.scheme() == "ubpm") { // qDebug() << "Received URL:" << url.toString(); // if (mainWindowInstance) { // Assuming mainWindowInstance is accessible // mainWindowInstance->handleUrl(url); // } // return true; // } // } // return QApplication::eventFilter(obj, event); // Or QObject::eventFilter // } // In MainWindow.cpp // bool MainWindow::handleUrl(const QUrl &url) { // // Parse url.queryItems() for parameters like transaction_id // // Trigger actions based on parameters, e.g., initiate a device reading. // // ... logic ... // this->raise(); // this->activateWindow(); // return true; // }
The specific
eventFilter
implementation you provided forMainWindow.cpp
directly callsmainWindow->handleUrl(url)
if themainWindow
pointer is valid.
The application has been modified to send the latest blood pressure reading for the current user to a configurable external API.
-
**Core Sending Function (
sources/mainapp/MainWindow.cpp
): TheMainWindow::sendLatestBPToAPI()
function handles this. As of the last update for testing, it's hardcoded to send tohttp://localhost:3000/bpdata
and uses the actual latest reading:void MainWindow::sendLatestBPToAPI() { if (!networkManager) { networkManager = new QNetworkAccessManager(this); } QUrl apiUrl("http://localhost:3000/bpdata"); // Test endpoint if (database[view.user].isEmpty()) { /* ... handle no data ... */ return; } HEALTHDATA latestReading = database[view.user].last(); QNetworkRequest request(apiUrl); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); QJsonObject readingObject; readingObject["sys"] = latestReading.sys; readingObject["dia"] = latestReading.dia; readingObject["bpm"] = latestReading.bpm; readingObject["timestamp"] = QDateTime::fromSecsSinceEpoch(latestReading.dts).toString(Qt::ISODate); readingObject["ihb"] = latestReading.ihb; readingObject["mov"] = latestReading.mov; readingObject["patient_id"] = "test-patient-001_hardcoded"; // Test patient ID QJsonObject payload; payload["blood_pressure_reading"] = readingObject; QJsonDocument doc(payload); QNetworkReply *reply = networkManager->post(request, doc.toJson()); connect(reply, &QNetworkReply::finished, this, [this, reply]() { // ... (Handles success/error with QMessageBox & deletes reply) }); }
-
**API Configuration UI (
sources/mainapp/DialogSettings.ui
&DialogSettings.cpp
): A new "API" tab was added to the Settings dialog. This allows configuration of:- API Endpoint (
lineEdit_api_endpoint
) - Patient ID (
lineEdit_api_patient_id
) - Send Automatically (Checkbox -
checkBox_api_send_auto
) - Timeout (Spinbox -
spinBox_api_timeout
) The values are loaded and saved from/to thesettings.api
struct members inDialogSettings.cpp
.
- API Endpoint (
-
**Settings Persistence (
sources/mainapp/MainWindow.cpp
&sources/deviceinterface.h
): TheSETTINGS::api
struct indeviceinterface.h
holds these configurations:// In sources/deviceinterface.h, within SETTINGS struct: struct API_SETTINGS // Or struct api { QString apiEndpoint; bool sendAutomatically; QString patientId; int timeout; } api;
These settings are saved to and read from the application's
.ini
file viaMainWindow::saveSettings()
andMainWindow::readSettings()
under an[API]
group.// In MainWindow::readSettings() & MainWindow::saveSettings() ini.beginGroup("API"); // settings.api.apiEndpoint = ini.value("API/Endpoint", ...); // ini.setValue("API/Endpoint", settings.api.apiEndpoint); // ... (and similar for patientId, sendAutomatically, timeout) ini.endGroup();
(Note: The
sendLatestBPToAPI
function is currently bypassing these stored settings for its endpoint for direct testing.)
A menu action (likely connected to a toolbar button or menu item named "Send to API") triggers the data sending process.
- **Action Handler (
sources/mainapp/MainWindow.cpp
):This slot is connected to the UI action and callsvoid MainWindow::on_actionSendToAPI_triggered() { if (view.records == 0) { /* ... handle no records ... */ return; } sendLatestBPToAPI(); }
sendLatestBPToAPI()
.