270 lines
8.4 KiB
C++
270 lines
8.4 KiB
C++
/*
|
|
* ModelRailroadTimetablePlanner
|
|
* Copyright 2016-2023, Filippo Gentile
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "nextprevrsjobsmodel.h"
|
|
|
|
#include "utils/jobcategorystrings.h"
|
|
#include "utils/rs_utils.h"
|
|
|
|
#include <QFont>
|
|
|
|
#include <sqlite3pp/sqlite3pp.h>
|
|
|
|
NextPrevRSJobsModel::NextPrevRSJobsModel(sqlite3pp::database &db, QObject *parent) :
|
|
QAbstractTableModel(parent),
|
|
mDb(db)
|
|
{
|
|
}
|
|
|
|
QVariant NextPrevRSJobsModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
{
|
|
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
|
|
{
|
|
switch (section)
|
|
{
|
|
case JobIdCol:
|
|
return tr("Job");
|
|
case RsNameCol:
|
|
return tr("RS item");
|
|
case TimeCol:
|
|
return tr("Time");
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return QAbstractTableModel::headerData(section, orientation, role);
|
|
}
|
|
|
|
int NextPrevRSJobsModel::rowCount(const QModelIndex &p) const
|
|
{
|
|
return p.isValid() ? 0 : m_data.size();
|
|
}
|
|
|
|
int NextPrevRSJobsModel::columnCount(const QModelIndex &p) const
|
|
{
|
|
return p.isValid() ? 0 : NCols;
|
|
}
|
|
|
|
QVariant NextPrevRSJobsModel::data(const QModelIndex &idx, int role) const
|
|
{
|
|
if (!idx.isValid() || idx.row() >= m_data.size() || idx.column() >= NCols)
|
|
return QVariant();
|
|
|
|
const Item &item = m_data.at(idx.row());
|
|
|
|
switch (role)
|
|
{
|
|
case Qt::DisplayRole:
|
|
{
|
|
switch (idx.column())
|
|
{
|
|
case JobIdCol:
|
|
{
|
|
if (item.otherJobCat == JobCategory::NCategories)
|
|
return tr("Depot"); // Rollingstock item taken from/released to depot
|
|
return JobCategoryName::jobName(item.otherJobId, item.otherJobCat);
|
|
}
|
|
case RsNameCol:
|
|
return item.rsName;
|
|
case TimeCol:
|
|
return item.opTime;
|
|
}
|
|
break;
|
|
}
|
|
case Qt::FontRole:
|
|
{
|
|
if (idx.column() == JobIdCol && item.otherJobCat == JobCategory::NCategories)
|
|
{
|
|
// Rollingstock item taken from/released to depot, distinguish font from job names
|
|
QFont f;
|
|
f.setItalic(true);
|
|
return f;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
void NextPrevRSJobsModel::refreshData()
|
|
{
|
|
// GROUP by rollingstock
|
|
QByteArray sql =
|
|
"SELECT c.id, c.rs_id, rs_list.number, rs_models.name, rs_models.suffix, rs_models.type, "
|
|
"stops.%time0,"
|
|
" %min(s1.%time0), stops.id, s1.job_id AS job_id, j1.category"
|
|
" FROM stops"
|
|
" JOIN coupling c ON c.stop_id=stops.id"
|
|
" JOIN rs_list ON rs_list.id=c.rs_id"
|
|
" JOIN rs_models ON rs_models.id=rs_list.model_id"
|
|
" LEFT JOIN coupling c1 ON c1.rs_id=c.rs_id"
|
|
" LEFT JOIN stops s1 ON s1.id=c1.stop_id"
|
|
" LEFT JOIN jobs j1 ON j1.id=s1.job_id"
|
|
" WHERE stops.job_id=?1 AND c.operation=%rem AND c1.operation=%add AND s1.%time0 %gt "
|
|
"stops.%time1"
|
|
" GROUP BY c.rs_id"
|
|
" UNION ALL "
|
|
"SELECT c.id, c.rs_id, rs_list.number, rs_models.name, rs_models.suffix, rs_models.type, "
|
|
"stops.%time0,"
|
|
" %max(s1.%time0), stops.id, NULL AS job_id, NULL"
|
|
" FROM stops"
|
|
" JOIN coupling c ON c.stop_id=stops.id"
|
|
" JOIN rs_list ON rs_list.id=c.rs_id"
|
|
" JOIN rs_models ON rs_models.id=rs_list.model_id"
|
|
" LEFT JOIN coupling c1 ON c1.rs_id=c.rs_id"
|
|
" LEFT JOIN stops s1 ON s1.id=c1.stop_id"
|
|
" WHERE stops.job_id=?1 AND c.operation=%rem AND c1.operation=%add"
|
|
" GROUP BY c.rs_id"
|
|
" HAVING s1.%time1 %lt stops.%time0"
|
|
" ORDER BY stops.%time0, job_id, rs_models.type, rs_models.name, rs_list.number"
|
|
" LIMIT 100";
|
|
|
|
if (m_mode == NextJobs)
|
|
{
|
|
sql.replace("%time0", "arrival");
|
|
sql.replace("%time1", "departure");
|
|
sql.replace("%min", "MIN");
|
|
sql.replace("%max", "MAX");
|
|
sql.replace("%rem", "0");
|
|
sql.replace("%add", "1");
|
|
sql.replace("%gt", ">=");
|
|
sql.replace("%lt", "<=");
|
|
}
|
|
else
|
|
{
|
|
// Invert all
|
|
sql.replace("%time0", "departure");
|
|
sql.replace("%time1", "arrival");
|
|
sql.replace("%min", "MAX");
|
|
sql.replace("%max", "MIN");
|
|
sql.replace("%rem", "1");
|
|
sql.replace("%add", "0");
|
|
sql.replace("%gt", "<=");
|
|
sql.replace("%lt", ">=");
|
|
}
|
|
|
|
/*
|
|
//GROUP by next job_id (NOTE: added COUNT(1) column)
|
|
QByteArray sql1 = "SELECT c.id, c.rs_id, c.operation, rs_models.name, rs_list.number,"
|
|
" stops.arrival, MIN(s1.arrival), s1.job_id, c1.operation, COUNT(1)"
|
|
" FROM stops"
|
|
" JOIN coupling c ON c.stop_id=stops.id"
|
|
" JOIN rs_list ON rs_list.id=c.rs_id"
|
|
" JOIN rs_models ON rs_models.id=rs_list.model_id"
|
|
" LEFT JOIN coupling c1 ON c1.rs_id=c.rs_id"
|
|
" LEFT JOIN stops s1 ON s1.id=c1.stop_id"
|
|
" WHERE stops.job_id=16816 AND c.operation=0 AND c1.operation=1"
|
|
" AND s1.arrival >= stops.departure"
|
|
" GROUP BY s1.job_id"
|
|
" UNION ALL "
|
|
"SELECT *, COUNT(1) FROM (" //TODO: better way than sub query?
|
|
" SELECT c.id, c.rs_id, c.operation, rs_models.name, rs_list.number,"
|
|
" stops.arrival, MAX(s1.arrival), NULL, c1.operation, COUNT(1)"
|
|
" FROM stops"
|
|
" JOIN coupling c ON c.stop_id=stops.id"
|
|
" JOIN rs_list ON rs_list.id=c.rs_id"
|
|
" JOIN rs_models ON rs_models.id=rs_list.model_id"
|
|
" LEFT JOIN coupling c1 ON c1.rs_id=c.rs_id"
|
|
" LEFT JOIN stops s1 ON s1.id=c1.stop_id"
|
|
" WHERE stops.job_id=16816 AND c.operation=0 AND c1.operation=1"
|
|
" GROUP BY c.rs_id"
|
|
" HAVING s1.departure <= stops.arrival"
|
|
")";
|
|
*/
|
|
|
|
sqlite3pp::query q(mDb, sql);
|
|
q.bind(1, m_jobId);
|
|
|
|
beginResetModel();
|
|
m_data.clear();
|
|
|
|
for (auto row : q)
|
|
{
|
|
Item item;
|
|
item.couplingId = row.get<db_id>(0);
|
|
item.rsId = row.get<db_id>(1);
|
|
|
|
int number = row.get<int>(2);
|
|
int modelNameLen = sqlite3_column_bytes(q.stmt(), 3);
|
|
const char *modelName = reinterpret_cast<char const *>(sqlite3_column_text(q.stmt(), 3));
|
|
|
|
int modelSuffixLen = sqlite3_column_bytes(q.stmt(), 4);
|
|
const char *modelSuffix = reinterpret_cast<char const *>(sqlite3_column_text(q.stmt(), 4));
|
|
RsType rsType = RsType(row.get<int>(5));
|
|
|
|
item.rsName = rs_utils::formatNameRef(modelName, modelNameLen, number, modelSuffix,
|
|
modelSuffixLen, rsType);
|
|
|
|
item.opTime = row.get<QTime>(6);
|
|
// Ignore column 7, which is used just for MIN/MAX
|
|
item.stopId = row.get<db_id>(8);
|
|
item.otherJobId = row.get<db_id>(9);
|
|
item.otherJobCat = JobCategory(row.get<int>(10));
|
|
if (row.column_type(10) == SQLITE_NULL)
|
|
item.otherJobCat = JobCategory::NCategories;
|
|
|
|
m_data.append(item);
|
|
}
|
|
|
|
endResetModel();
|
|
}
|
|
|
|
void NextPrevRSJobsModel::clearData()
|
|
{
|
|
beginResetModel();
|
|
m_data.clear();
|
|
m_data.squeeze();
|
|
endResetModel();
|
|
}
|
|
|
|
db_id NextPrevRSJobsModel::jobId() const
|
|
{
|
|
return m_jobId;
|
|
}
|
|
|
|
void NextPrevRSJobsModel::setJobId(db_id newJobId)
|
|
{
|
|
m_jobId = newJobId;
|
|
if (m_jobId)
|
|
refreshData();
|
|
else
|
|
clearData();
|
|
}
|
|
|
|
NextPrevRSJobsModel::Mode NextPrevRSJobsModel::mode() const
|
|
{
|
|
return m_mode;
|
|
}
|
|
|
|
void NextPrevRSJobsModel::setMode(Mode newMode)
|
|
{
|
|
m_mode = newMode;
|
|
if (m_jobId)
|
|
refreshData();
|
|
}
|
|
|
|
NextPrevRSJobsModel::Item NextPrevRSJobsModel::getItemAtRow(int row) const
|
|
{
|
|
return m_data.value(row, Item());
|
|
}
|