Commit 186d09f0 authored by Volker Krause's avatar Volker Krause
Browse files

Decode UIC 918.3 0080VU vendor blocks

Those contain local public transport extensions for DB long distance
tickets, using elements of the VDV ticket format.
parent f33666d6
Pipeline #51644 passed with stage
in 11 minutes and 52 seconds
......@@ -60,6 +60,7 @@ set(kitinerary_lib_srcs
uic9183/uic9183parser.cpp
uic9183/uic9183ticketlayout.cpp
uic9183/vendor0080block.cpp
uic9183/vendor0080vublockdata.cpp
vdv/iso9796_2decoder.cpp
vdv/vdvcertificate.cpp
......
......@@ -5,6 +5,7 @@
*/
#include "vendor0080block.h"
#include "vendor0080vublockdata.h"
#include "logging.h"
#include <QString>
......@@ -150,3 +151,34 @@ int Vendor0080BLBlock::subblockOffset(const Uic9183Block& block)
const auto certSize = block.version() == 2 ? 46 : 26;
return 3 + certSize * certCount + 2;
}
Vendor0080VUBlock::Vendor0080VUBlock(const Uic9183Block &block)
{
if (block.isNull() || block.contentSize() < (int)sizeof(Vendor0080VUCommonData)) {
return;
}
m_block = block;
}
bool Vendor0080VUBlock::isValid() const
{
return !m_block.isNull();
}
const Vendor0080VUCommonData* Vendor0080VUBlock::commonData() const
{
return m_block.isNull() ? nullptr : reinterpret_cast<const Vendor0080VUCommonData*>(m_block.content());
}
const Vendor0080VUTicketData* Vendor0080VUBlock::ticketData(int index) const
{
auto offset = sizeof(Vendor0080VUCommonData);
auto ticket = reinterpret_cast<const Vendor0080VUTicketData*>(m_block.content() + offset);
while (index-- > 0) {
offset += sizeof(Vendor0080VUTicketData) + ticket->validityAreaDataSize - sizeof(VdvTicketValidityAreaData);
ticket = reinterpret_cast<const Vendor0080VUTicketData*>(m_block.content() + offset);
}
return ticket;
}
......@@ -63,6 +63,26 @@ private:
Uic9183Block m_block;
};
class Vendor0080VUCommonData;
class Vendor0080VUTicketData;
/** UIC 918.3 0080VU vendor data block (DB local public transport extensions).
* @see UIC918.3* Interoperabilität Barcode DB Online-Ticker VDV-KA
*/
class KITINERARY_EXPORT Vendor0080VUBlock
{
public:
Vendor0080VUBlock(const Uic9183Block &block);
bool isValid() const;
const Vendor0080VUCommonData* commonData() const;
const Vendor0080VUTicketData* ticketData(int index) const;
static constexpr const char RecordId[] = "0080VU";
private:
Uic9183Block m_block;
};
}
#endif // KITINERARY_VENDOR0080BLOCK_H
/*
SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "vendor0080vublockdata.h"
#include "moc_vendor0080vublockdata.cpp"
/*
SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KITINERARY_VENDOR0080VUBLOCKDATA_H
#define KITINERARY_VENDOR0080VUBLOCKDATA_H
#include "../vdv/vdvticketcontent.h"
namespace KItinerary {
#define VDV_NUM_PROPERTY(Name, Size) \
public: \
VdvNumber<Size> Name; \
Q_PROPERTY(uint Name MEMBER Name)
#define VDV_DATETIME_PROPERTY(Name) \
public: \
VdvDateTimeCompact Name; \
Q_PROPERTY(QDateTime Name MEMBER Name)
#pragma pack(push)
#pragma pack(1)
class KITINERARY_EXPORT Vendor0080VUCommonData
{
Q_GADGET
VDV_NUM_PROPERTY(terminalNumber, 2)
VDV_NUM_PROPERTY(samNumber, 3)
VDV_NUM_PROPERTY(numberOfPersons, 1)
VDV_NUM_PROPERTY(numberOfTickets, 1)
};
class KITINERARY_EXPORT Vendor0080VUTicketData
{
Q_GADGET
VDV_NUM_PROPERTY(authorizationNumber, 4)
VDV_NUM_PROPERTY(kvpOrgId, 2)
VDV_NUM_PROPERTY(productNumber, 2)
VDV_NUM_PROPERTY(pvOrgId, 2)
VDV_DATETIME_PROPERTY(validFrom)
VDV_DATETIME_PROPERTY(validUntil)
VDV_NUM_PROPERTY(price, 3) // in Euro-Cent
VDV_NUM_PROPERTY(samSequnceNumber, 4)
VDV_NUM_PROPERTY(areaListLength, 1)
uint8_t validityAreaTag; // fixed 0xDC
uint8_t validityAreaDataSize;
VdvTicketValidityAreaData validityArea;
};
#pragma pack(pop)
#undef VDV_NUM_PROPERTY
#undef VDV_DATETIME_PROPERTY
}
#endif // KITINERARY_VENDOR0080VUBLOCKDATA_H
......@@ -6,6 +6,7 @@
#include "../lib/era/ssbticket.h"
#include "../lib/uic9183/uic9183head.h"
#include "../lib/uic9183/vendor0080vublockdata.h"
#include "../lib/vdv/vdvticketcontent.h"
#include "../lib/tlv/berelement_p.h"
......@@ -77,6 +78,9 @@ static void dumpRawData(const char *data, std::size_t size)
template <typename T>
static void dumpGadget(const T *gadget, const char* indent)
{
if (!gadget) {
return;
}
for (auto i = 0; i < T::staticMetaObject.propertyCount(); ++i) {
const auto prop = T::staticMetaObject.property(i);
const auto value = prop.readOnGadget(gadget);
......@@ -90,7 +94,6 @@ static void dumpUic9183(const QByteArray &data)
parser.parse(data);
// TODO dump header
for (auto block = parser.firstBlock(); !block.isNull(); block = block.nextBlock()) {
std::cout << "Block: ";
std::cout.write(block.name(), 6);
......@@ -117,6 +120,16 @@ static void dumpUic9183(const QByteArray &data)
dumpRawData(sub.content(), sub.contentSize());
std::cout << std::endl;
}
} else if (block.isA<Vendor0080VUBlock>()) {
Vendor0080VUBlock vendor(block);
dumpGadget(vendor.commonData(), " ");
for (int i = 0; i < (int)vendor.commonData()->numberOfTickets; ++i) {
const auto ticket = vendor.ticketData(i);
std::cout << " Ticket " << (i + 1) << ":" << std::endl;
dumpGadget(ticket, " ");
dumpGadget(&ticket->validityArea, " ");
std::cout << " payload: (hex) " << QByteArray((const char*)&ticket->validityArea + sizeof(VdvTicketValidityAreaData), ticket->validityAreaDataSize - sizeof(VdvTicketValidityAreaData)).toHex().constData() << std::endl;
}
} else {
std::cout << " Content: ";
dumpRawData(block.content(), block.contentSize());
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment