diff --git a/src/AddDeviceWizard.cpp b/src/AddDeviceWizard.cpp index b9b8f1de9..767350e2d 100644 --- a/src/AddDeviceWizard.cpp +++ b/src/AddDeviceWizard.cpp @@ -24,6 +24,7 @@ // 20. Scan for Device / select Serial // 30. Firmware for Fortius // 50. Pair for ANT +// 55. Pair for BTLE // 60. Finalise // @@ -48,7 +49,8 @@ AddDeviceWizard::AddDeviceWizard(MainWindow *main) : QWizard(main), main(main) setPage(10, new AddType(this)); // done setPage(20, new AddSearch(this)); // done setPage(30, new AddFirmware(this)); // done - setPage(50, new AddPair(this)); // todo + setPage(50, new AddPair(this)); // done + setPage(55, new AddPairBTLE(this)); // done setPage(60, new AddFinal(this)); // todo -- including virtual power done = false; @@ -131,10 +133,10 @@ AddType::clicked(QString p) if (wizard->found == false) next =20; else { switch(wizard->deviceTypes.Supported[wizard->current].type) { - case DEV_KICKR : - case DEV_BT40 : + case DEV_BT40 : next = 55; break; case DEV_ANTLOCAL : next = 50; break; // pair default: + case DEV_KICKR : case DEV_CT : next = 60; break; // confirm and add case DEV_FORTIUS : next = 30; break; // confirm and add } @@ -437,7 +439,7 @@ AddSearch::nextId() const else { switch(wizard->deviceTypes.Supported[wizard->current].type) { case DEV_ANTLOCAL : return 50; break; // pair - case DEV_BT40 : //XXX need a BT40 pair screen + case DEV_BT40 : return 55; break; // pair BT devices default: case DEV_KICKR : case DEV_CT : return 60; break; // confirm and add @@ -813,6 +815,230 @@ AddPair::validatePage() return true; } +// Pair devices +AddPairBTLE::AddPairBTLE(AddDeviceWizard *parent) : QWizardPage(parent), wizard(parent) +{ + setTitle(tr("Pair Devices")); + setSubTitle(tr("Search for and pair Bluetooth 4.0 devices")); + + signalMapper = NULL; + + QVBoxLayout *layout = new QVBoxLayout; + setLayout(layout); + + channelWidget = new QTreeWidget(this); + layout->addWidget(channelWidget); +} + +void +AddPairBTLE::cleanupPage() +{ + updateValues.stop(); + if (wizard->controller) { + wizard->controller->stop(); +#ifdef WIN32 + Sleep(1000); +#else + sleep(1); +#endif + delete wizard->controller; + wizard->controller = NULL; + } +} + +void +AddPairBTLE::initializePage() +{ + // setup the controller and start it off so we can + // manipulate it + if (wizard->controller) delete wizard->controller; + if (signalMapper) delete signalMapper; + wizard->controller = new ANTlocalController(NULL,NULL); + dynamic_cast(wizard->controller)->setDevice(wizard->portSpec); + dynamic_cast(wizard->controller)->myANTlocal->setConfigurationMode(true); //XXX + wizard->controller->start(); + wizard->profile=""; // clear any thing thats there now + signalMapper = new QSignalMapper(this); + + // Channel 0, look for any (0 devicenumber) speed and distance device + + // wait for it to start +#ifdef WIN32 + Sleep(1000); +#else + sleep(1); +#endif + int channels = dynamic_cast(wizard->controller)->channels(); + + // Tree Widget of the channel controls + channelWidget->clear(); + channelWidget->headerItem()->setText(0, tr("Sensor")); + channelWidget->headerItem()->setText(1, tr("ANT+ Id")); + channelWidget->headerItem()->setText(2, tr("Value")); + channelWidget->headerItem()->setText(3, tr("Status")); + channelWidget->setColumnCount(4); + channelWidget->setSelectionMode(QAbstractItemView::NoSelection); + //channelWidget->setEditTriggers(QAbstractItemView::SelectedClicked); // allow edit + channelWidget->setUniformRowHeights(true); + channelWidget->setIndentation(0); + + channelWidget->header()->resizeSection(0,175); // type + channelWidget->header()->resizeSection(1,75); // id + channelWidget->header()->resizeSection(2,120); // value + channelWidget->header()->resizeSection(3,110); // status + + // defaults + static const int index4[4] = { 1,2,3,5 }; + static const int index8[8] = { 1,2,3,4,5,0,0,0 }; + const int *index = channels == 4 ? index4 : index8; + + // how many devices we got then? + for (int i=0; i< channels; i++) { + + QTreeWidgetItem *add = new QTreeWidgetItem(channelWidget->invisibleRootItem()); + add->setFlags(add->flags() | Qt::ItemIsEditable); + + // sensor type + QComboBox *sensorSelector = new QComboBox(this); + addSensorTypes(dynamic_cast(wizard->controller)->myANTlocal, sensorSelector); + sensorSelector->setCurrentIndex(index[i]); + channelWidget->setItemWidget(add, 0, sensorSelector); + + // sensor id + QLineEdit *sensorId = new QLineEdit(this); + sensorId->setEnabled(false); + sensorId->setText("none"); + channelWidget->setItemWidget(add, 1, sensorId); + + // value + QLabel *value = new QLabel(this); + QFont bigger; + bigger.setPointSize(25); + value->setFont(bigger); + value->setAlignment(Qt::AlignCenter | Qt::AlignVCenter); + value->setText("0"); + channelWidget->setItemWidget(add, 2, value); + + // status + QLabel *status = new QLabel(this); + status->setText("Un-Paired"); + channelWidget->setItemWidget(add, 3, status); + + //channelWidget->verticalHeader()->resizeSection(i,40) + connect(sensorSelector, SIGNAL(currentIndexChanged(int)), signalMapper, SLOT(map())); + signalMapper->setMapping(sensorSelector, i); + } + channelWidget->setCurrentItem(channelWidget->invisibleRootItem()->child(0)); + enableDisable(channelWidget); + + updateValues.start(200); // 5hz + connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(sensorChanged(int))); + connect(&updateValues, SIGNAL(timeout()), this, SLOT(getChannelValues())); + connect(wizard->controller, SIGNAL(foundDevice(int,int,int)), this, SLOT(channelInfo(int,int,int))); + connect(wizard->controller, SIGNAL(searchTimeout(int)), this, SLOT(searchTimeout(int))); + //connect(wizard->controller, SIGNAL(lostDevice(int)), this, SLOT(searchTimeout(int))); + + // now we're ready to get notifications - set channels + for (int i=0; iinvisibleRootItem()->child(channel); + enableDisable(channelWidget); + + // first off lets unassign this channel + dynamic_cast(wizard->controller)->myANTlocal->setChannel(channel, -1, 0); + dynamic_cast(channelWidget->itemWidget(item,1))->setText("none"); + dynamic_cast(channelWidget->itemWidget(item,2))->setText(0); + + // what is it then? unused or restart scan? + QComboBox *p = dynamic_cast(channelWidget->itemWidget(item,0)); + int channel_type = p->itemData(p->currentIndex()).toInt(); + if (channel_type == ANTChannel::CHANNEL_TYPE_UNUSED) { + dynamic_cast(channelWidget->itemWidget(item,3))->setText("Unused"); + } else { + dynamic_cast(channelWidget->itemWidget(item,3))->setText("Searching..."); + dynamic_cast(wizard->controller)->myANTlocal->setChannel(channel, 0, channel_type); + } +} + +void +AddPairBTLE::channelInfo(int channel, int device_number, int device_id) +{ + Q_UNUSED(device_id); + QTreeWidgetItem *item = channelWidget->invisibleRootItem()->child(channel); + dynamic_cast(channelWidget->itemWidget(item,1))->setText(QString("%1").arg(device_number)); + dynamic_cast(channelWidget->itemWidget(item,3))->setText(QString("Paired")); +} + +void +AddPairBTLE::searchTimeout(int channel) +{ + // Kick if off again, just mimic user reselecting the same sensor type + sensorChanged(channel); +} + + +void +AddPairBTLE::getChannelValues() +{ + if (wizard->controller == NULL) return; + + // enable disable widgets based upon sensor selection + for (int i=0; i< channelWidget->invisibleRootItem()->childCount(); i++) { + QTreeWidgetItem *item = channelWidget->invisibleRootItem()->child(i); + + // is it selected or not? + bool enable = (dynamic_cast(channelWidget->itemWidget(item,0))->currentIndex() != 0); + + if (enable) { + QComboBox *p =dynamic_cast(channelWidget->itemWidget(item,0)); + + // speed+cadence is two values! + if (p->itemData(p->currentIndex()) == ANTChannel::CHANNEL_TYPE_SandC) { + dynamic_cast(channelWidget->itemWidget(item,2))->setText(QString("%1 %2") + .arg((int)dynamic_cast(wizard->controller)->myANTlocal->channelValue2(i) //speed + * (appsettings->value(NULL, GC_WHEELSIZE, 2100).toInt()/1000) * 60 / 1000) + .arg((int)dynamic_cast(wizard->controller)->myANTlocal->channelValue(i))); // cad + } else { + dynamic_cast(channelWidget->itemWidget(item,2))->setText(QString("%1") + .arg((int)dynamic_cast(wizard->controller)->myANTlocal->channelValue(i))); + } + } + } + +} + +bool +AddPairBTLE::validatePage() +{ + // when next is clicked we need to get the paired values + // and create a profile, a blank profile will be created if + // no devices have been paired. This means devices will be + // automatically paired at runtime + wizard->profile=""; + for (int i=0; i< channelWidget->invisibleRootItem()->childCount(); i++) { + QTreeWidgetItem *item = channelWidget->invisibleRootItem()->child(i); + + // what is it then? unused or restart scan? + QComboBox *p = dynamic_cast(channelWidget->itemWidget(item,0)); + int channel_type = p->itemData(p->currentIndex()).toInt(); + + if (channel_type == ANTChannel::CHANNEL_TYPE_UNUSED) continue; // not paired + + int device_number = dynamic_cast(channelWidget->itemWidget(item,1))->text().toInt(); + + if (device_number) + wizard->profile += QString(wizard->profile != "" ? ", %1%2" : "%1%2") + .arg(device_number) + .arg(ANT::deviceIdCode(channel_type)); + } + return true; +} + // Final confirmation AddFinal::AddFinal(AddDeviceWizard *parent) : QWizardPage(parent), wizard(parent) { diff --git a/src/AddDeviceWizard.h b/src/AddDeviceWizard.h index 15593c3ad..aacfaac0d 100644 --- a/src/AddDeviceWizard.h +++ b/src/AddDeviceWizard.h @@ -168,6 +168,34 @@ class AddPair : public QWizardPage QTimer updateValues; }; +class AddPairBTLE : public QWizardPage +{ + Q_OBJECT + + public: + AddPairBTLE(AddDeviceWizard *); + int nextId() const { return 60; } + void initializePage(); + bool validatePage(); + void cleanupPage(); + + public slots: + + void getChannelValues(); + // we found a device on a channel + void channelInfo(int channel, int device_number, int device_id); + // we failed to find a device on the channel + void searchTimeout(int channel); + + // user interactions + void sensorChanged(int channel); // sensor selection changed + private: + AddDeviceWizard *wizard; + QTreeWidget *channelWidget; + QSignalMapper *signalMapper; + QTimer updateValues; +}; + class AddFinal : public QWizardPage { Q_OBJECT