void RacomClient::onUdpSocketReadyRead() { if(status() != Status::Running) return; QUdpSocket *sock = udpSocket(); QNetworkDatagram dg = sock->receiveDatagram(); QByteArray data = dg.data(); qfInfo() << data.toHex(); // remove possible initial garbage int i = 0; for(i=0; i<data.size(); i++) if(data.at(i) == 0x02) break; data = data.mid(i); // remove possible multiple 0x02 for(i=1; i<data.size(); i++) if(data.at(i) != 0x02) break; data = data.mid(i-1); qfInfo() << "stripped data:" << data.toHex(); if(!m_siDriver) { m_siDriver = new siut::DeviceDriver(this); CardReader::CardReaderPlugin *plugin = cardReaderPlugin(); if(plugin) { connect(m_siDriver, &siut::DeviceDriver::messageReady, plugin, &CardReader::CardReaderPlugin::emitSiMessagereceived); } } m_siDriver->processSiPacket(data); }
void RacomClient::onRawSIDataUdpSocketReadyRead() { if(status() != Status::Running) return; #if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)) QUdpSocket *sock = rawDataUdpSocket(); QNetworkDatagram dg = sock->receiveDatagram(); QByteArray data = dg.data(); qfInfo() << data.toHex(); // remove possible initial garbage int i = 0; for(i=0; i<data.size(); i++) if(data.at(i) == 0x02) break; data = data.mid(i); // remove possible multiple 0x02 for(i=1; i<data.size(); i++) if(data.at(i) != 0x02) break; data = data.mid(i-1); qfInfo() << "stripped data:" << data.toHex(); CardReader::CardReaderPlugin *plugin = cardReaderPlugin(); if(plugin) { if(!m_siDriver) { m_siDriver = new siut::DeviceDriver(this); connect(m_siDriver, &siut::DeviceDriver::siTaskFinished, plugin, &CardReader::CardReaderPlugin::emitSiTaskFinished); } m_siDriver->processData(data); } #else qfWarning() << "Racom client is not supported with Qt < 5.8"; #endif }
QVariantMap ReceiptsPlugin::readCardTablesData(int card_id) { qfLogFuncFrame() << card_id; QVariantMap ret; CardReader::ReadCard read_card = cardReaderPlugin()->readCard(card_id); { qfu::TreeTable tt; tt.appendColumn("position", QVariant::Int); tt.appendColumn("code", QVariant::Int); tt.appendColumn("punchTimeMs", QVariant::Int); tt.appendColumn("stpTimeMs", QVariant::Int); tt.appendColumn("lapTimeMs", QVariant::Int); QMapIterator<QString, QVariant> it(read_card); while(it.hasNext()) { it.next(); if(it.key() != QLatin1String("punches")) tt.setValue(it.key(), it.value()); } int position = 0; int start_time_ms = read_card.startTime(); if(start_time_ms == 0xeeee) start_time_ms = read_card.checkTime(); start_time_ms *= 1000; int prev_stp_time_ms = 0; for(auto v : read_card.punches()) { CardReader::ReadPunch punch(v.toMap()); int punch_time_ms = punch.time() * 1000 + punch.msec(); int stp_time_ms = quickevent::og::TimeMs::msecIntervalAM(start_time_ms, punch_time_ms); qfu::TreeTableRow ttr = tt.appendRow(); ++position; int code = punch.code(); ttr.setValue("position", position); ttr.setValue("code", code); ttr.setValue("punchTimeMs", punch_time_ms); ttr.setValue("stpTimeMs", stp_time_ms); ttr.setValue("lapTimeMs", stp_time_ms - prev_stp_time_ms); prev_stp_time_ms = stp_time_ms; } { qf::core::sql::QueryBuilder qb; qb.select2("config", "ckey, cvalue, ctype") .from("config") .where("ckey LIKE 'event.%'"); qf::core::sql::Query q; q.exec(qb.toString()); while(q.next()) { QVariant v = qf::core::Utils::retypeStringValue(q.value("cvalue").toString(), q.value("ctype").toString()); tt.setValue(q.value("ckey").toString(), v); } } tt.setValue("stageCount", eventPlugin()->stageCount()); tt.setValue("currentStageId", eventPlugin()->currentStageId()); qfDebug() << "card:\n" << tt.toString(); ret["card"] = tt.toVariant(); } return ret; }
QVariantMap ReceiptsPlugin::receiptTablesData(int card_id) { qfLogFuncFrame() << card_id; QF_TIME_SCOPE("receiptTablesData()"); QVariantMap ret; CardReader::ReadCard read_card = cardReaderPlugin()->readCard(card_id); CardReader::CheckedCard checked_card = cardReaderPlugin()->checkCard(read_card); int current_stage_id = eventPlugin()->currentStageId(); int run_id = checked_card.runId(); int course_id = checked_card.courseId(); int current_standings = 0; int competitors_finished = 0; QMap<int, int> best_laps; //< position->time ///QMap<int, int> missing_codes; //< pos->code ///QSet<int> out_of_order_codes; { qf::core::model::SqlTableModel model; qf::core::sql::QueryBuilder qb; qb.select2("competitors", "*") .select2("runs", "*") .select2("classes", "name") .select("COALESCE(competitors.lastName, '') || ' ' || COALESCE(competitors.firstName, '') AS competitorName") .from("runs") .join("runs.competitorId", "competitors.id") .join("competitors.classId", "classes.id") .where("runs.id=" QF_IARG(run_id)); model.setQuery(qb.toString()); model.reload(); if(model.rowCount() == 1) { int class_id = model.value(0, "competitors.classId").toInt(); { // find best laps for competitors class qf::core::sql::QueryBuilder qb_minlaps; // TODO: remove position field from DB in 0.1.5 qb_minlaps.select("runlaps.position, MIN(runlaps.lapTimeMs) AS minLapTimeMs") .from("competitors") .joinRestricted("competitors.id", "runs.competitorId", "runs.stageId=" QF_IARG(current_stage_id) " AND competitors.classId=" QF_IARG(class_id), "JOIN") .joinRestricted("runs.id", "runlaps.runId", "runlaps.position > 0 AND runlaps.lapTimeMs > 0", "JOIN") .groupBy("runlaps.position"); QString qs = qb_minlaps.toString(); //qfInfo() << qs; qf::core::sql::Query q; q.exec(qs); while(q.next()) { int position = q.value("position").toInt(); if(position == 0) { qfWarning() << "position == 0 in best runlaps"; continue; } int lap = q.value("minLapTimeMs").toInt(); if(lap == 0) { qfWarning() << "minLapTimeMs == 0 in best runlaps"; continue; } best_laps[position] = lap; //qfInfo() << "bestlaps[" << pos << "] =" << lap; } } if(checked_card.isOk()) { // find current standings qf::core::sql::QueryBuilder qb; qb.select2("runs", "timeMs") .select("runs.disqualified OR NOT runs.isRunning OR runs.isRunning IS NULL OR runs.misPunch AS dis") .from("competitors") .joinRestricted("competitors.id", "runs.competitorId", "runs.stageId=" QF_IARG(current_stage_id) " AND competitors.classId=" QF_IARG(class_id)) .where("runs.finishTimeMs > 0") .orderBy("misPunch, disqualified, isRunning, runs.timeMs"); //qfInfo() << qb.toString(); qf::core::sql::Query q; q.exec(qb.toString(), qf::core::Exception::Throw); while (q.next()) { bool dis = q.value("dis").toBool(); int time = q.value("timeMs").toInt(); if(!dis) { if(time <= checked_card.timeMs()) current_standings++; } competitors_finished++; } } } qfu::TreeTable tt = model.toTreeTable(); { qf::core::sql::QueryBuilder qb; qb.select2("courses", "length, climb") .select("(SELECT COUNT(*) FROM coursecodes WHERE courseId=courses.id) AS controlCount") .from("courses") .where("courses.id=" QF_IARG(course_id)); qf::core::sql::Query q; q.exec(qb.toString()); if(q.next()) { QSqlRecord rec = q.record(); for (int i = 0; i < rec.count(); ++i) { QString fld_name = rec.fieldName(i); tt.setValue(fld_name, rec.value(i)); } } } { qf::core::sql::QueryBuilder qb; qb.select2("config", "ckey, cvalue, ctype") .from("config") .where("ckey LIKE 'event.%'"); qf::core::sql::Query q; q.exec(qb.toString()); while(q.next()) { QVariant v = qf::core::Utils::retypeStringValue(q.value("cvalue").toString(), q.value("ctype").toString()); tt.setValue(q.value("ckey").toString(), v); } } tt.setValue("stageCount", eventPlugin()->stageCount()); tt.setValue("currentStageId", eventPlugin()->currentStageId()); qfDebug() << "competitor:\n" << tt.toString(); ret["competitor"] = tt.toVariant(); } { qfu::TreeTable tt; tt.appendColumn("position", QVariant::Int); tt.appendColumn("code", QVariant::Int); tt.appendColumn("stpTimeMs", QVariant::Int); tt.appendColumn("lapTimeMs", QVariant::Int); tt.appendColumn("lossMs", QVariant::Int); QMapIterator<QString, QVariant> it(checked_card); while(it.hasNext()) { it.next(); if(it.key() != QLatin1String("punches")) tt.setValue(it.key(), it.value()); } tt.setValue("isOk", checked_card.isOk()); int position = 0; for(auto v : checked_card.punches()) { CardReader::CheckedPunch punch(v.toMap()); qfu::TreeTableRow ttr = tt.appendRow(); ++position; int code = punch.code(); ttr.setValue("position", position); ttr.setValue("code", code); ttr.setValue("stpTimeMs", punch.stpTimeMs()); int lap = punch.lapTimeMs(); ttr.setValue("lapTimeMs", lap); int best_lap = best_laps.value(position); if(lap > 0 && best_lap > 0) { int loss = lap - best_lap; ttr.setValue("lossMs", loss); } } /* { // runlaps table contains also finish time entry, it is under FINISH_PUNCH_POS // currently best_laps[999] contains best finish lap time for this class int loss = 0; int best_lap = best_laps.value(CardReader::CardReaderPlugin::FINISH_PUNCH_POS); if(best_lap > 0) loss = checked_card.finishLapTimeMs() - best_lap; //qfInfo() << "control_count:" << control_count << "finishLapTimeMs:" << checked_card.finishLapTimeMs() << "- best_lap:" << best_lap << "=" << loss; tt.setValue("finishLossMs", loss); } */ { QSet<int> correct_codes; for (int i = 0; i < checked_card.punchCount(); ++i) { correct_codes << checked_card.punchAt(i).code(); } QVariantList xc; for (int i = 0; i < read_card.punchCount(); ++i) { int code = read_card.punchAt(i).code(); if(!correct_codes.contains(code)) { xc.insert(xc.count(), QVariantList() << (i+1) << code); } } tt.setValue("extraCodes", xc); } tt.setValue("currentStandings", current_standings); tt.setValue("competitorsFinished", competitors_finished); tt.setValue("timeMs", checked_card.timeMs()); qfDebug() << "card:\n" << tt.toString(); ret["card"] = tt.toVariant(); } return ret; }
void RacomClientSirxdConnection::onReadyRead() { m_data += m_socket->readAll(); while (true) { int ix = m_data.indexOf('\n'); if(ix < 0) break; QByteArray rec = m_data.mid(0, ix).trimmed(); m_data = m_data.mid(ix + 1); static const QByteArray READOUT("Readout;"); static const QByteArray SPLIT("Split;"); if(rec.startsWith(READOUT)) { enum { ColCardNumber = 1, ColCheckTime = 2, ColStartTime = 4, ColFinishTime = 6, ColPunchCount = 8, }; QList<QByteArray> splits = rec.split(';'); siut::SICard card; card.setCardNumber(splits.value(ColCardNumber).toInt()); int secs, msecs; parse_time(splits.value(ColCheckTime), secs, msecs); card.setCheckTime(secs); parse_time(splits.value(ColStartTime), secs, msecs); card.setStartTime(secs); parse_time(splits.value(ColFinishTime), secs, msecs); card.setFinishTime(secs); card.setFinishTimeMs(msecs); int punch_cnt = splits.value(ColPunchCount).toInt(); QVariantList punches; for (int i = 0; i < punch_cnt; ++i) { siut::SIPunch punch; punch.setCode(splits.value(ColPunchCount + 1 + 3*i + 0).toInt()); parse_time(splits.value(ColPunchCount + 1 + 3*i + 1), secs, msecs); punch.setTime(secs); punch.setMsec(msecs); punches << punch; } card.setPunches(punches); CardReader::CardReaderPlugin *plugin = cardReaderPlugin(); plugin->emitSiTaskFinished((int)siut::SiTask::Type::CardRead, card); } else if(rec.startsWith(SPLIT)) { enum { ColCardNumber = 1, ColCode = 2, ColTime = 3, }; QList<QByteArray> splits = rec.split(';'); siut::SIPunch punch; punch.setCardNumber(splits.value(ColCardNumber).toInt()); punch.setCode(splits.value(ColCode).toInt()); int secs, msecs; parse_time(splits.value(ColTime), secs, msecs); punch.setTime(secs); punch.setMsec(msecs); CardReader::CardReaderPlugin *plugin = cardReaderPlugin(); plugin->emitSiTaskFinished((int)siut::SiTask::Type::Punch, punch); } else { qfWarning() << "Throwing away unrecognised sirxd message:" << QString::fromUtf8(rec); } } }