7 #include "uic9183parser.h"
9 #include "rct2ticket.h"
10 #include "uic9183block.h"
11 #include "uic9183head.h"
12 #include "uic9183header.h"
13 #include "uic9183ticketlayout.h"
14 #include "vendor0080block.h"
15 #include "vendor1154block.h"
17 #include "era/fcbticket.h"
18 #include "era/fcbutil.h"
22 #include <QJsonDocument>
23 #include <QJsonObject>
43 Uic9183Parser::Uic9183Parser()
44 : d(new Uic9183ParserPrivate)
49 Uic9183Parser::~Uic9183Parser() =
default;
60 if (
block.isA(name)) {
69 if (name.size() != 6 || d->m_payload.isEmpty()) {
73 #define BLOCK_FROM_NAME(Type) \
74 if (name == QLatin1String(Type::RecordId)) { \
75 const auto block = findBlock<Type>(); \
76 return block.isValid() ? QVariant::fromValue(block) : QVariant(); \
86 #undef BLOCK_FROM_NAME
95 void Uic9183Parser::parse(
const QByteArray &data)
107 d->m_payload.resize(4096);
109 stream.zalloc =
nullptr;
110 stream.zfree =
nullptr;
111 stream.opaque =
nullptr;
112 stream.avail_in = data.
size() -
header.compressedMessageOffset();
113 stream.next_in =
reinterpret_cast<unsigned char*
>(
const_cast<char*
>(data.
data() +
header.compressedMessageOffset()));
114 stream.avail_out = d->m_payload.size();
115 stream.next_out =
reinterpret_cast<unsigned char*
>(d->m_payload.
data());
117 inflateInit(&stream);
118 const auto res = inflate(&stream, Z_NO_FLUSH);
124 qCWarning(
Log) <<
"UIC 918.3 payload zlib decompression failed" << stream.msg;
128 d->m_payload.truncate(d->m_payload.size() - stream.avail_out);
132 if (d->m_payload.size() > 600 && d->m_payload.startsWith(
"U_HEAD0100531071") && d->m_payload[54] ==
'U' && d->m_payload[36] ==
' ') {
133 qCWarning(
Log) <<
"Applying Renfe workaround for broken UIC 913.8 content...";
134 d->m_payload.remove(36, 1);
135 const auto idx = d->m_payload.indexOf(
"U_TLAY00");
136 if (idx < d->m_payload.size() + 400 && std::strncmp(d->m_payload.
constData() + idx + 12,
"RCT2", 4) != 0) {
137 d->m_payload.insert(idx + 7,
"1");
138 d->m_payload.replace(idx + 12, 4,
"RCT2");
139 d->m_payload.remove(idx + 20, 1);
140 qCDebug(
Log) << d->m_payload;
145 bool Uic9183Parser::isValid()
const
147 return !d->m_payload.isEmpty();
150 template <
typename T>
151 static QString fcbReference(
const T &data)
153 if (!data.referenceIA5.isEmpty()) {
156 if (data.referenceNumIsSet()) {
162 QString Uic9183Parser::pnr()
const
164 if (
const auto head = findBlock<Uic9183Head>(); head.isValid()) {
165 const auto key = head.ticketKey().trimmed();
166 const auto issuerId = head.issuerCompanyCodeNumeric();
169 if (issuerId == 80 && (key.size() == 8 || key.size() == 9) && key.at(6) ==
QLatin1Char(
'-') && key.at(7).isDigit()) {
172 if (issuerId == 80 && key.size() == 13 && key.endsWith(
QLatin1String(
"0101"))) {
175 if ((issuerId == 1088 || issuerId == 1184) && key.size() == 9 && key.at(7) ==
QLatin1Char(
'_') && key.at(8).isDigit()) {
182 if (
const auto fcb = findBlock<Fcb::UicRailTicketData>(); fcb.isValid()) {
183 if (!fcb.issuingDetail.issuerPNR.isEmpty()) {
186 if (!fcb.transportDocument.isEmpty()) {
187 const auto doc = fcb.transportDocument.at(0);
189 if (doc.ticket.userType() == qMetaTypeId<Fcb::ReservationData>()) {
191 }
else if (doc.ticket.userType() == qMetaTypeId<Fcb::OpenTicketData>()) {
193 }
else if (doc.ticket.userType() == qMetaTypeId<Fcb::PassData>()) {
205 template <
typename T>
206 static QString fcbTariffName(
const T &data)
208 if (data.tariffs.isEmpty()) {
211 return data.tariffs.
at(0).tariffDesc;
214 QString Uic9183Parser::name()
const
217 if (
const auto fcb = findBlock<Fcb::UicRailTicketData>(); fcb.isValid() && !fcb.transportDocument.isEmpty()) {
218 const auto doc = fcb.transportDocument.
at(0);
220 if (doc.ticket.userType() == qMetaTypeId<Fcb::ReservationData>()) {
222 }
else if (doc.ticket.userType() == qMetaTypeId<Fcb::OpenTicketData>()) {
224 }
else if (doc.ticket.userType() == qMetaTypeId<Fcb::PassData>()) {
227 if (!
name.isEmpty()) {
233 if (
const auto b = findBlock<Vendor0080BLBlock>(); b.isValid()) {
234 const auto sblock = b.findSubBlock(
"001");
235 if (!sblock.isNull()) {
248 QString Uic9183Parser::carrierId()
const
250 if (
const auto head = findBlock<Uic9183Head>(); head.isValid()) {
251 return head.issuerCompanyCodeString();
253 if (
const auto fcb = findBlock<Fcb::UicRailTicketData>(); fcb.isValid()) {
254 const auto issue = fcb.issuingDetail;
255 if (issue.issuerNumIsSet()) {
258 if (issue.issuerIA5IsSet()) {
262 return header().signerCompanyCode();
269 if (
const auto fcb = findBlock<Fcb::UicRailTicketData>(); fcb.isValid() && fcb.issuingDetail.issuerNameIsSet()) {
270 issuer.setName(fcb.issuingDetail.issuerName);
275 QDateTime Uic9183Parser::validFrom()
const
278 if (
const auto fcb = findBlock<Fcb::UicRailTicketData>(); fcb.isValid() && !fcb.transportDocument.isEmpty()) {
279 const auto issue = fcb.issuingDetail.issueingDateTime();
280 const auto doc = fcb.transportDocument.at(0).ticket;
281 if (doc.userType() == qMetaTypeId<Fcb::ReservationData>()) {
284 if (doc.userType() == qMetaTypeId<Fcb::OpenTicketData>()) {
287 if (doc.userType() == qMetaTypeId<Fcb::PassData>()) {
293 if (
const auto b = findBlock<Vendor0080BLBlock>(); b.isValid() && b.orderBlockCount() == 1) {
294 return QDateTime(b.orderBlock(0).validFrom(), {0, 0, 0});
298 if (
const auto b =
findBlock(
"118199"); !b.isNull()) {
302 if (dt.date().year() < 2000) {
303 dt = dt.addYears(100);
311 if (
const auto b = findBlock<Vendor1154UTBlock>(); b.isValid()) {
312 const auto subBlock = b.findSubBlock(
"OD");
313 qDebug() << subBlock.toString();
314 if (!subBlock.isNull()) {
321 const auto dt = rct2.firstDayOfValidity();
322 if (dt.month() != 1 || dt.day() != 1 || !rct2.outboundDepartureStation().isEmpty()) {
326 const auto dep = rct2.outboundDepartureTime();
333 QDateTime Uic9183Parser::validUntil()
const
336 if (
const auto fcb = findBlock<Fcb::UicRailTicketData>(); fcb.isValid() && !fcb.transportDocument.isEmpty()) {
337 const auto issue = fcb.issuingDetail.issueingDateTime();
338 const auto doc = fcb.transportDocument.at(0).ticket;
339 if (doc.userType() == qMetaTypeId<Fcb::ReservationData>()) {
342 if (doc.userType() == qMetaTypeId<Fcb::OpenTicketData>()) {
345 if (doc.userType() == qMetaTypeId<Fcb::PassData>()) {
351 if (
const auto b = findBlock<Vendor0080BLBlock>(); b.isValid() && b.orderBlockCount() == 1) {
352 return QDateTime(b.orderBlock(0).validTo(), {23, 59, 59});
356 if (
const auto b =
findBlock(
"118199"); !b.isNull()) {
360 if (dt.date().year() < 2000) {
361 dt = dt.addYears(100);
369 if (
const auto b = findBlock<Vendor1154UTBlock>(); b.isValid()) {
370 const auto subBlock = b.findSubBlock(
"DO");
371 if (!subBlock.isNull()) {
379 const auto validityRange =
ticketLayout().text(3, 1, 36, 1).trimmed();
380 const auto idx = std::max(validityRange.lastIndexOf(
QLatin1Char(
' ')), validityRange.lastIndexOf(
QLatin1Char(
'-')));
384 return rct2.outboundArrivalTime();
390 Person Uic9183Parser::person()
const
393 if (
const auto fcb = findBlock<Fcb::UicRailTicketData>(); fcb.isValid() && fcb.travelerDetailIsSet() && fcb.travelerDetail.traveler.size() == 1) {
394 const auto traveler = fcb.travelerDetail.traveler.at(0);
396 p.setGivenName(
QString(traveler.firstName +
QLatin1Char(
' ') + traveler.secondName).trimmed());
397 p.setFamilyName(traveler.lastName);
398 if (traveler.firstNameIsSet() || traveler.lastNameIsSet()) {
404 if (
const auto b = findBlock<Vendor0080BLBlock>(); b.isValid()) {
406 auto sblock = b.findSubBlock(
"028");
407 if (!sblock.isNull()) {
408 const auto endIt = sblock.content() + sblock.contentSize();
409 auto it = std::find(sblock.content(), endIt,
'#');
412 p.setGivenName(
QString::fromUtf8(sblock.content(), std::distance(sblock.content(), it)));
419 sblock = b.findSubBlock(
"023");
420 if (!sblock.isNull()) {
422 p.setName(sblock.toString());
427 if (
const auto b = findBlock<Vendor1154UTBlock>(); b.isValid()) {
428 const auto subBlock = b.findSubBlock(
"KJ");
429 if (!subBlock.isNull()) {
431 p.setName(subBlock.toString());
438 if (rct2.isValid()) {
439 const auto name = rct2.passengerName();
440 if (!
name.isEmpty()) {
455 if (station.identifier().startsWith(
QLatin1String(
"uic:80"))) {
457 addr.setAddressCountry(QStringLiteral(
"DE"));
458 station.setAddress(addr);
459 station.setIdentifier(
QString());
463 TrainStation Uic9183Parser::outboundDepartureStation()
const
469 station.setName(rtc2.outboundDepartureStation());
473 if (
const auto b = findBlock<Vendor0080BLBlock>(); b.isValid()) {
474 if (
const auto sblock = b.findSubBlock(
"015"); !sblock.isNull()) {
475 station.setName(sblock.toString());
478 if (
const auto sblock = b.findSubBlock(
"035"); !sblock.isNull() && sblock.contentSize() <= 7) {
479 QString ibnr = QStringLiteral(
"ibnr:8000000");
480 const auto s = sblock.toString();
481 station.setIdentifier(ibnr.
replace(ibnr.
size() - s.size(), s.size(), s));
486 if (
const auto fcb = findBlock<Fcb::UicRailTicketData>(); fcb.isValid() && !fcb.transportDocument.isEmpty()) {
487 const auto doc = fcb.transportDocument.at(0);
488 if (doc.ticket.userType() == qMetaTypeId<Fcb::ReservationData>()) {
490 station.setName(irt.fromStationNameUTF8);
492 }
else if (doc.ticket.userType() == qMetaTypeId<Fcb::OpenTicketData>()) {
494 station.setName(nrt.fromStationNameUTF8);
497 fixFcbStationCode(station);
503 TrainStation Uic9183Parser::outboundArrivalStation()
const
509 station.setName(rtc2.outboundArrivalStation());
512 if (
const auto b = findBlock<Vendor0080BLBlock>(); b.isValid()) {
513 if (
const auto sblock = b.findSubBlock(
"016"); !sblock.isNull()) {
514 station.setName(sblock.toString());
517 if (
const auto sblock = b.findSubBlock(
"036"); !sblock.isNull() && sblock.contentSize() <= 7) {
518 QString ibnr = QStringLiteral(
"ibnr:8000000");
519 const auto s = sblock.toString();
520 station.setIdentifier(ibnr.
replace(ibnr.
size() - s.size(), s.size(), s));
525 if (
const auto fcb = findBlock<Fcb::UicRailTicketData>(); fcb.isValid() && !fcb.transportDocument.isEmpty()) {
526 const auto doc = fcb.transportDocument.at(0);
527 if (doc.ticket.userType() == qMetaTypeId<Fcb::ReservationData>()) {
529 station.setName(irt.toStationNameUTF8);
531 }
else if (doc.ticket.userType() == qMetaTypeId<Fcb::OpenTicketData>()) {
533 station.setName(nrt.toStationNameUTF8);
536 fixFcbStationCode(station);
542 TrainStation Uic9183Parser::returnDepartureStation()
const
548 station.setName(rtc2.returnDepartureStation());
551 const auto outboundArrival = outboundArrivalStation();
553 if (
const auto b = findBlock<Vendor0080BLBlock>(); b.isValid()) {
554 if (
const auto sblock = b.findSubBlock(
"017"); !sblock.isNull()) {
555 station.setName(sblock.toString());
557 if (outboundArrival.name() == station.name()) {
558 station.setIdentifier(outboundArrival.identifier());
563 if (
const auto fcb = findBlock<Fcb::UicRailTicketData>(); fcb.isValid() && !fcb.transportDocument.isEmpty()) {
564 const auto doc = fcb.transportDocument.at(0);
565 if (doc.ticket.userType() == qMetaTypeId<Fcb::OpenTicketData>()) {
567 if (nrt.returnIncluded && nrt.returnDescriptionIsSet()) {
568 station.setName(nrt.returnDescription.fromStationNameUTF8);
570 }
else if (nrt.returnIncluded) {
571 if (outboundArrival.name() == station.name()) {
572 station.setIdentifier(outboundArrival.identifier());
576 fixFcbStationCode(station);
582 TrainStation Uic9183Parser::returnArrivalStation()
const
588 station.setName(rtc2.returnArrivalStation());
591 const auto outboundDeparture = outboundDepartureStation();
593 if (
const auto b = findBlock<Vendor0080BLBlock>(); b.isValid()) {
594 if (
const auto sblock = b.findSubBlock(
"018"); !sblock.isNull()) {
595 station.setName(sblock.toString());
597 if (outboundDeparture.name() == station.name()) {
598 station.setIdentifier(outboundDeparture.identifier());
603 if (
const auto fcb = findBlock<Fcb::UicRailTicketData>(); fcb.isValid() && !fcb.transportDocument.isEmpty()) {
604 const auto doc = fcb.transportDocument.at(0);
605 if (doc.ticket.userType() == qMetaTypeId<Fcb::OpenTicketData>()) {
607 if (nrt.returnIncluded && nrt.returnDescriptionIsSet()) {
608 station.setName(nrt.returnDescription.toStationNameUTF8);
610 }
else if (nrt.returnIncluded) {
611 if (outboundDeparture.name() == station.name()) {
612 station.setIdentifier(outboundDeparture.identifier());
616 fixFcbStationCode(station);
622 QString Uic9183Parser::seatingType()
const
624 if (
const auto fcb = findBlock<Fcb::UicRailTicketData>(); fcb.isValid() && fcb.transportDocument.size() == 1) {
625 const auto doc = fcb.transportDocument.
at(0);
626 if (doc.ticket.userType() == qMetaTypeId<Fcb::ReservationData>()) {
629 if (doc.ticket.userType() == qMetaTypeId<Fcb::OpenTicketData>()) {
632 if (doc.ticket.userType() == qMetaTypeId<Fcb::PassData>()) {
637 if (
const auto b = findBlock<Vendor0080BLBlock>(); b.isValid()) {
639 const auto sblock = b.findSubBlock(
"014");
640 if (!sblock.isNull()) {
641 const auto s = sblock.toString();
642 return s.startsWith(
QLatin1Char(
'S')) ? s.right(1) : s;
647 return rct2.outboundClass();
654 return findBlock<Uic9183TicketLayout>();
657 QVariant Uic9183Parser::ticketLayoutVariant()
const
666 const auto u_head = findBlock<Uic9183Head>();
667 rct2.setContextDate(u_head.issuingDateTime());
671 QVariant Uic9183Parser::rct2TicketVariant()
const
674 if (rct2.isValid()) {
696 #include "moc_uic9183parser.cpp"