/*! * * Copyright (c) 2021-2025 Diality Inc. - All Rights Reserved. * \copyright * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * \file VBluetooth.cpp * \author (last) Vy * \date (last) 05-Sep-2023 * \author (original) Behrouz NematiPour * \date (original) 24-Aug-2021 * */ #include "VBluetooth.h" // Qt // Project #include "ApplicationController.h" #include "GuiController.h" #include "BluetoothInterface.h" using namespace Bluetooth; using namespace Model; View::VBluetooth::VBluetooth(QObject *parent) : QAbstractListModel(parent) { initConnections(); } /*! * \brief VBluetooth::initConnections * Makes the necessary connections. Called inside VIEW_DEF_CLASS */ void View::VBluetooth::initConnections() { connect(&_BluetoothInterface, SIGNAL(didStateChange (BluetoothData )), this , SLOT( onStateChange (BluetoothData ))); connect(&_BluetoothInterface, SIGNAL(didDeviceChange(BluetoothDeviceData)), this , SLOT( onDeviceChange(BluetoothDeviceData))); connect(this , SIGNAL(didDeviceSelect(QString, QString )), this , SLOT( onDeviceSelect(QString, QString ))); } /*! * \brief View::VBluetooth::rowCount * \details QAbstractItemModel::rowCount override * \return Returns the number of rows under the given parent. When the parent is valid it means that rowCount is returning the number of children of parent. */ int View::VBluetooth::rowCount(const QModelIndex &) const { return _devices.count(); } /*! * \brief View::VBluetooth::data * \details QAbstractItemModel::data override * \param index - the row index * \param role - the row role * \return Returns the data stored under the given role for the item referred to by the index. */ QVariant View::VBluetooth::data(const QModelIndex &index, int role) const { if (index.row() < rowCount()) switch (role) { case ToStringRole : return _devices.at(index.row()).toString(); case AddrRole : return _devices.at(index.row()).addr; case NameRole : return _devices.at(index.row()).name; case PairRole : return _devices.at(index.row()).pair; default: return QVariant(); } return QVariant(); } /*! * \brief View::VBluetooth::roleNames * \details QAbstractItemModel::roleNames override * \return Returns the model's role names. */ QHash View::VBluetooth::roleNames() const { static const QHash roles { { ToStringRole, "toString" }, { AddrRole, "addr" }, { NameRole, "name" }, { PairRole, "pair" } }; return roles; } /*! * \brief View::VBluetooth::reset * \details Clears the list of the devices. */ void View::VBluetooth::reset() { beginResetModel(); _devices.clear(); endResetModel(); } /*! * \brief View::VBluetooth::onStateChange * \details Updates the View when the Bluetooth Interface state change signal is received. * \param vData - The Bluetooth Interface information data. */ void View::VBluetooth::onStateChange(const BluetoothData &vData) { Model::MBluetooth::InterfaceStates state = vData.state; if ( state == MBluetooth::eIS_Local_Error_POST ) { isInvalid( true ); notify( state ); return; } // TODO: re-evaluate the states for the scan button enable/disable. // scanEnabled(state == MBluetooth::eIS_Idle // || state == MBluetooth::eIS_Scan_Done // || state == MBluetooth::eIS_Scan_NotFound // || state == MBluetooth::eIS_Device_Error // || state == MBluetooth::eIS_Device_Waiting // ); switch (state) { // The device name is not always available and the interface may be in investigation. case MBluetooth::eIS_Local_Init : localAddr (vData.localAddr ); localName (vData.localName ); break; case MBluetooth::eIS_Scan_Start : pairedAddr(""); // DEBUG: LOG_APPED_UI(tr("Bluetooth unpaired." )); // pairedAddr == "" implies unpaired scanEnabled(false); // disable the scan button while the device is scanning reset(); break; case MBluetooth::eIS_Scan_Done : if (! _devices.count()) state = MBluetooth::eIS_Scan_NotFound; scanEnabled(true); // enable the scan button when scanning is not running break; case MBluetooth::eIS_Device_Waiting : // Selected device connected and is in waiting mode for read. pairedAddr(vData.deviceAddr); pairedBatt(vData.deviceBatt); scanEnabled(true); break; case MBluetooth::eIS_Device_Connect : if ( vData.devicePair != QBluetoothLocalDevice::Unpaired ) LOG_APPED_UI(tr("Bluetooth reconnected." )); else LOG_APPED_UI(tr("Bluetooth paired and connected." )); break; case MBluetooth::eIS_Device_Disconnect : LOG_APPED_UI(tr("Bluetooth paired and disconnected." )); break; case MBluetooth::eIS_Idle : case MBluetooth::eIS_Local_Connect : case MBluetooth::eIS_Local_Error_Invalid : case MBluetooth::eIS_Local_Error_POST : case MBluetooth::eIS_Local_Error_Off : case MBluetooth::eIS_Local_Error_IO : case MBluetooth::eIS_Local_Error : case MBluetooth::eIS_Local_Disconnect : case MBluetooth::eIS_Scan_Reject : case MBluetooth::eIS_Scan_NotFound : case MBluetooth::eIS_Scan_Discover : case MBluetooth::eIS_Scan_Found : case MBluetooth::eIS_Scan_Stop : case MBluetooth::eIS_Device_Init : case MBluetooth::eIS_Device_Start : case MBluetooth::eIS_Device_Error_Init : case MBluetooth::eIS_Device_Error : case MBluetooth::eIS_Device_Done : case MBluetooth::eIS_Service_Start : case MBluetooth::eIS_Service_Invalid : case MBluetooth::eIS_Service_Error : case MBluetooth::eIS_Service_Discover : case MBluetooth::eIS_Service_Detail : case MBluetooth::eIS_Service_Detail_Error : case MBluetooth::eIS_Service_Detail_Done : case MBluetooth::eIS_Service_Done : case MBluetooth::eIS_Detail_Change : case MBluetooth::eIS_Detail_Read : case MBluetooth::eIS_Detail_Write : case MBluetooth::eIS_Config_Read : case MBluetooth::eIS_Config_Write : case MBluetooth::eIS_Close : break; } error (vData.error ); notify( state ); //DEBUG: qDebug()<< "bt state : " << state << " currently paired to addr: "<< _pairedAddr; } /*! * \brief View::VBluetooth::onDeviceChange * \details updates the list of devices when a device data has been received. Used with Discovery Agent to populate list of discovered devices to the QML/UI. * \param vDevice - The device information */ void View::VBluetooth::onDeviceChange(const BluetoothDeviceData &vDevice) { int row = 0; while (row < _devices.count()) ++row; beginInsertRows(QModelIndex(), row, row); _devices.insert(row, vDevice); endInsertRows(); /* DEBUG: Found BCuff devices debug qDebug() << _devices.count(); for (auto device: _devices) { qDebug() << device.addr << device.name << device.pair; } */ } /*! * \brief View::VBluetooth::onDeviceSelect * \details The signal handler of the device selection by user on the Bluetooth Cuff setting screen * \param vAddr - The selected device address * \param vName - The selected device name */ void View::VBluetooth::onDeviceSelect(const QString &vAddr, const QString &vName) { BluetoothDeviceData data; data.addr = vAddr; data.name = vName; emit _BluetoothInterface.didDeviceSelect(data); } /*! * \brief View::VBluetooth::toText * \details Maps the BluetoothInterface state vState to its corresponding text message. * \param vState - current state. * \return corresponding message text mapped from the state cState. */ QString View::VBluetooth::toText(MBluetooth::InterfaceStates vState) const { QString message; switch (vState) { case MBluetooth::eIS_Idle : message = "" ; break; case MBluetooth::eIS_Close : message = tr("BluetoothInterface Closed" ); break; // Used BluetoothInterface to be consistent with the case MBluetooth::eIS_Local_Init : message = tr("The Bluetooth Adapter Is Ready %1" ).arg(_localAddr ); break; case MBluetooth::eIS_Local_Connect : message = tr("The Bluetooth Adapter Connected" ); break; case MBluetooth::eIS_Local_Disconnect : message = tr("The Bluetooth Adapter Disconnected" ); break; case MBluetooth::eIS_Local_Error_Invalid : message = tr("No Valid Bluetooth Adapter" ); break; case MBluetooth::eIS_Local_Error_POST : message = tr("The Bluetooth Adapter POST Failed" ); break; case MBluetooth::eIS_Local_Error_Off : message = tr("The Bluetooth Adapter Is Off" ); break; case MBluetooth::eIS_Local_Error_IO : message = tr("The Bluetooth Adapter IO Error" ); break; case MBluetooth::eIS_Local_Error : message = tr("The Bluetooth Adapter Unknown Error" ); break; case MBluetooth::eIS_Scan_NotFound : message = tr("No Valid device found" ); break; case MBluetooth::eIS_Scan_Start : message = tr("Scanning ..." ); break; case MBluetooth::eIS_Scan_Reject : message = tr("Scanning Rejected" ); break; case MBluetooth::eIS_Scan_Discover : message = tr("Device Discovering ..." ); break; case MBluetooth::eIS_Scan_Found : message = tr("Blood Pressure Device Found" ); break; case MBluetooth::eIS_Scan_Stop : message = tr("Scanning Stopped" ); break; case MBluetooth::eIS_Scan_Done : message = tr("Scanning Finished" ); break; case MBluetooth::eIS_Device_Init : message = tr("Device Initializing ..." ); break; case MBluetooth::eIS_Device_Error_Init : message = tr("Device Initialization Error" ); break; case MBluetooth::eIS_Device_Start : message = tr("Device Connecting ..." ); break; case MBluetooth::eIS_Device_Waiting : message = tr("Device Waiting For Measurement ..." ); break; // received feedback and changed the "Waiting" to this to avoid user confusion. case MBluetooth::eIS_Device_Error : message = tr("Device Connection Error" ); break; case MBluetooth::eIS_Device_Connect : message = tr("Device Connected" ); break; case MBluetooth::eIS_Device_Done : message = tr("Device Clean Up" ); break; case MBluetooth::eIS_Device_Disconnect : message = tr("Device In Power Saving Mode" ); break; // received feedback and changed the "Disconnected" to this to avoid user confusion. case MBluetooth::eIS_Service_Start : message = tr("Service Scanning ..." ); break; case MBluetooth::eIS_Service_Error : message = tr("Service Error: %1" ).arg(_error ); break; case MBluetooth::eIS_Service_Invalid : message = tr("Service Invalid" ); break; case MBluetooth::eIS_Service_Discover : message = tr("Service Discovered" ); break; case MBluetooth::eIS_Service_Detail : message = tr("Service Detail Discovering ..." ); break; case MBluetooth::eIS_Service_Detail_Error : message = tr("Service Detail Error" ); break; case MBluetooth::eIS_Service_Detail_Done : message = tr("Service Detail Done" ); break; case MBluetooth::eIS_Service_Done : message = tr("Service Clean Up" ); break; case MBluetooth::eIS_Detail_Change : message = tr("Service Characteristic Changed" ); break; case MBluetooth::eIS_Detail_Read : message = tr("Service Characteristic Read" ); break; case MBluetooth::eIS_Detail_Write : message = tr("Service Characteristic Write" ); break; case MBluetooth::eIS_Config_Read : message = tr("Service Descriptor Read" ); break; case MBluetooth::eIS_Config_Write : message = tr("Service Descriptor Write" ); break; // IMPORTANT: Do not use the "default:" to let compiler check for all the enumeration which are not handled. } return message; } /*! * \brief View::VBluetooth::notify * \details Notifies the QML/UI about the state change and sends its test mapping to the QML/UI observers. * \param vState - The current state */ void View::VBluetooth::notify(MBluetooth::InterfaceStates vState) { QString message = toText(vState); notification(message); // Console Log // DEBUG: message = _deviceAddr + " " + message + " " + _detailName + " " + _detailValue; // message = message.trimmed().simplified(); // qDebug().noquote().nospace() << message; // Service Log if ( vState != MBluetooth::eIS_Device_Start ) { // this is the "Device Connecting ..." state and has the interval of 1s and will fill up the service log fast, so removed from log. LOG_DEBUG(message); } } /*! * \brief View::VBluetooth::doScan * \details calls the Bluetooth Interface scan to start discovering the Bluetooth devices. */ void View::VBluetooth::doScan() { _BluetoothInterface.doScan(); }