/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 */ #include "gmp.h" #include QHash GmpProtocol::frameFieldCountMap; GmpProtocol::GmpProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { // field count may change based on msgType - so don't cache field offsets _cacheFlags &= ~FieldFrameBitOffsetCache; } GmpProtocol::~GmpProtocol() { } AbstractProtocol::ProtocolIdType GmpProtocol::protocolIdType() const { return ProtocolIdIp; } int GmpProtocol::fieldCount() const { return FIELD_COUNT; } int GmpProtocol::frameFieldCount() const { int type = msgType(); // frameFieldCountMap contains the frameFieldCounts for each // msgType - this is built on demand and cached for subsequent use // lookup if we have already cached ... if (frameFieldCountMap.contains(type)) return frameFieldCountMap.value(type); // ... otherwise calculate and cache int count = 0; for (int i = 0; i < FIELD_COUNT; i++) { if (fieldFlags(i).testFlag(AbstractProtocol::FrameField)) count++; } frameFieldCountMap.insert(type, count); return count; } AbstractProtocol::FieldFlags GmpProtocol::fieldFlags(int index) const { AbstractProtocol::FieldFlags flags; flags = AbstractProtocol::fieldFlags(index); flags &= ~FrameField; switch(index) { // Frame Fields - check against msgType() case kType: case kRsvdMrtCode: flags |= FrameField; break; case kChecksum: flags |= FrameField; flags |= CksumField; break; case kMldMrt: case kMldRsvd: // MLD subclass should handle suitably break; case kGroupAddress: if (!isSsmReport()) flags |= FrameField; break; case kRsvd1: case kSFlag: case kQrv: case kQqic: case kSourceCount: case kSources: if (isSsmQuery()) flags |= FrameField; break; case kRsvd2: case kGroupRecordCount: case kGroupRecords: if (isSsmReport()) flags |= FrameField; break; // Meta Fields case kIsOverrideChecksum: case kGroupMode: case kGroupCount: case kGroupPrefix: case kIsOverrideSourceCount: case kIsOverrideGroupRecordCount: flags |= MetaField; break; default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return flags; } QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { switch (index) { case kType: { uint type = data.type(); switch(attrib) { case FieldName: return QString("Type"); case FieldValue: return type; case FieldTextValue: return QString("%1").arg(quint8(type)); case FieldFrameValue: return QByteArray(1, quint8(type)); default: break; } break; } case kRsvdMrtCode: { quint8 rsvd = 0; switch(attrib) { case FieldName: return QString("Reserved"); case FieldValue: return rsvd; case FieldTextValue: return QString("%1").arg(rsvd); case FieldFrameValue: return QByteArray(1, rsvd); default: break; } break; } case kChecksum: { switch(attrib) { case FieldName: return QString("Checksum"); case FieldBitSize: return 16; default: break; } quint16 cksum = data.is_override_checksum() ? data.checksum() : checksum(streamIndex); switch(attrib) { case FieldValue: return cksum; case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian(cksum, (uchar*) fv.data()); return fv; } case FieldTextValue: return QString("0x%1").arg(cksum, 4, BASE_HEX, QChar('0')); default: break; } break; } case kMldMrt: case kMldRsvd: // XXX: Present only in MLD - hence handled by the mld subclass break; case kGroupAddress: // XXX: Handled by each subclass break; case kRsvd1: { quint8 rsvd = 0; switch(attrib) { case FieldName: return QString("Reserved"); case FieldValue: return rsvd; case FieldTextValue: return QString("%1").arg(rsvd); case FieldFrameValue: return QByteArray(1, char(rsvd)); case FieldBitSize: return 4; default: break; } break; } case kSFlag: { switch(attrib) { case FieldName: return QString("S Flag"); case FieldValue: return data.s_flag(); case FieldTextValue: return data.s_flag() ? QString("True") : QString("False"); case FieldFrameValue: return QByteArray(1, char(data.s_flag())); case FieldBitSize: return 1; default: break; } break; } case kQrv: { int qrv = data.qrv() & 0x7; switch(attrib) { case FieldName: return QString("QRV"); case FieldValue: return qrv; case FieldTextValue: return QString("%1").arg(qrv); case FieldFrameValue: return QByteArray(1, char(qrv)); case FieldBitSize: return 3; default: break; } break; } case kQqic: { int qqi = data.qqi(); switch(attrib) { case FieldName: return QString("QQIC"); case FieldValue: return qqi; case FieldTextValue: return QString("%1").arg(qqi); case FieldFrameValue: { char qqicode = char(qqic(qqi)); return QByteArray(1, qqicode); } default: break; } break; } case kSourceCount: { quint16 count = data.sources_size(); if (data.is_override_source_count()) count = data.source_count(); switch(attrib) { case FieldName: return QString("Number of Sources"); case FieldValue: return count; case FieldTextValue: return QString("%1").arg(count); case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian(count, (uchar*) fv.data()); return fv; } default: break; } break; } case kSources: // XXX: Handled by each subclass break; case kRsvd2: { quint16 rsvd = 0; switch(attrib) { case FieldName: return QString("Reserved"); case FieldValue: return rsvd; case FieldTextValue: return QString("%1").arg(rsvd); case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian(rsvd, (uchar*) fv.data()); return fv; } default: break; } break; } case kGroupRecordCount: { quint16 count = data.group_records_size(); if (data.is_override_group_record_count()) count = data.group_record_count(); switch(attrib) { case FieldName: return QString("Number of Group Records"); case FieldValue: return count; case FieldTextValue: return QString("%1").arg(count); case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian(count, (uchar*) fv.data()); return fv; } default: break; } break; } case kGroupRecords: { switch(attrib) { case FieldName: return QString("Group List"); case FieldValue: { QVariantList grpRecords; for (int i = 0; i < data.group_records_size(); i++) { QVariantMap grpRec; OstProto::Gmp::GroupRecord rec = data.group_records(i); grpRec["groupRecordType"] = rec.type(); // grpRec["groupRecordAddress"] = subclass responsibility grpRec["overrideGroupRecordSourceCount"] = rec.is_override_source_count(); grpRec["groupRecordSourceCount"] = rec.source_count(); // grpRec["groupRecordSourceList"] = subclass responsibility grpRec["overrideAuxDataLength"] = rec.is_override_aux_data_length(); grpRec["auxDataLength"] = rec.aux_data_length(); grpRec["auxData"] = QByteArray(rec.aux_data().data(), rec.aux_data().size()); grpRecords.append(grpRec); } return grpRecords; } case FieldFrameValue: { QVariantList fv; for (int i = 0; i < data.group_records_size(); i++) { OstProto::Gmp::GroupRecord rec = data.group_records(i); QByteArray rv; quint16 srcCount; rv.resize(4); rv[0] = rec.type(); rv[1] = rec.is_override_aux_data_length() ? rec.aux_data_length() : rec.aux_data().size()/4; if (rec.is_override_source_count()) srcCount = rec.source_count(); else srcCount = rec.sources_size(); qToBigEndian(srcCount, (uchar*)(rv.data()+2)); // group_address => subclass responsibility // source list => subclass responsibility rv.append(QByteArray(rec.aux_data().data(), rv[1]*4)); fv.append(rv); } return fv; } case FieldTextValue: { QStringList list; for (int i = 0; i < data.group_records_size(); i++) { OstProto::Gmp::GroupRecord rec = data.group_records(i); QString str; str.append(" Type: "); switch(rec.type()) { case OstProto::Gmp::GroupRecord::kIsInclude: str.append("IS_INCLUDE"); break; case OstProto::Gmp::GroupRecord::kIsExclude: str.append("IS_EXCLUDE"); break; case OstProto::Gmp::GroupRecord::kToInclude: str.append("TO_INCLUDE"); break; case OstProto::Gmp::GroupRecord::kToExclude: str.append("TO_EXCLUDE"); break; case OstProto::Gmp::GroupRecord::kAllowNew: str.append("ALLOW_NEW"); break; case OstProto::Gmp::GroupRecord::kBlockOld: str.append("BLOCK_OLD"); break; default: str.append("UNKNOWN"); break; } int auxLen = rec.is_override_aux_data_length() ? rec.aux_data_length() : rec.aux_data().size()/4; str.append(QString("; AuxLen: %1").arg(auxLen)); str.append(QString("; Source Count: %1").arg( rec.is_override_source_count() ? rec.source_count(): rec.sources_size())); // NOTE: subclass should replace the XXX below with // group address and source list str.append(QString("; XXX")); str.append(QString("; AuxData: ").append( QByteArray(rec.aux_data().data(), auxLen*4) .toHex())); list.append(str); } return list; } default: break; } break; } // Meta Fields case kIsOverrideChecksum: { switch(attrib) { case FieldValue: return data.is_override_checksum(); default: break; } break; } case kGroupMode: { switch(attrib) { case FieldValue: return data.group_mode(); default: break; } break; } case kGroupCount: { switch(attrib) { case FieldValue: return data.group_count(); default: break; } break; } case kGroupPrefix: { switch(attrib) { case FieldValue: return data.group_prefix(); default: break; } break; } case kIsOverrideSourceCount: { switch(attrib) { case FieldValue: return data.is_override_source_count(); default: break; } break; } case kIsOverrideGroupRecordCount: { switch(attrib) { case FieldValue: return data.is_override_group_record_count(); default: break; } break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool GmpProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { bool isOk = false; if (attrib != FieldValue) goto _exit; switch (index) { case kType: { uint type = value.toUInt(&isOk); if (isOk) data.set_type(type); break; } case kRsvdMrtCode: { uint val = value.toUInt(&isOk); if (isOk) data.set_rsvd_code(val); break; } case kChecksum: { uint csum = value.toUInt(&isOk); if (isOk) data.set_checksum(csum); break; } case kMldMrt: { uint mrt = value.toUInt(&isOk); if (isOk) data.set_max_response_time(mrt); break; } case kGroupAddress: // XXX: Handled by subclass isOk = false; break; case kRsvd1: isOk = false; break; case kSFlag: { bool flag = value.toBool(); data.set_s_flag(flag); isOk = true; break; } case kQrv: { uint qrv = value.toUInt(&isOk); if (isOk) data.set_qrv(qrv); break; } case kQqic: { uint qqi = value.toUInt(&isOk); if (isOk) data.set_qqi(qqi); break; } case kSourceCount: { uint count = value.toUInt(&isOk); if (isOk) data.set_source_count(count); break; } case kSources: // XXX: Handled by subclass isOk = false; break; case kRsvd2: isOk = false; break; case kGroupRecordCount: { uint count = value.toUInt(&isOk); if (isOk) data.set_group_record_count(count); break; } case kGroupRecords: { QVariantList list = value.toList(); data.clear_group_records(); for (int i = 0; i < list.count(); i++) { QVariantMap grpRec = list.at(i).toMap(); OstProto::Gmp::GroupRecord *rec = data.add_group_records(); rec->set_type(OstProto::Gmp::GroupRecord::RecordType( grpRec["groupRecordType"].toInt())); // NOTE: rec->group_address => subclass responsibility rec->set_is_override_source_count( grpRec["overrideGroupRecordSourceCount"].toBool()); rec->set_source_count(grpRec["groupRecordSourceCount"].toUInt()); // NOTE: rec->sources => subclass responsibility rec->set_is_override_aux_data_length( grpRec["overrideAuxDataLength"].toBool()); rec->set_aux_data_length(grpRec["auxDataLength"].toUInt()); QByteArray ba = grpRec["auxData"].toByteArray(); // pad to word boundary if (ba.size() % 4) ba.append(QByteArray(4 - (ba.size() % 4), char(0))); rec->set_aux_data(std::string(ba.constData(), ba.size())); } break; } // Meta Fields case kIsOverrideChecksum: { bool ovr = value.toBool(); data.set_is_override_checksum(ovr); isOk = true; break; } case kGroupMode: { uint mode = value.toUInt(&isOk); if (isOk && data.GroupMode_IsValid(mode)) data.set_group_mode((OstProto::Gmp::GroupMode)mode); break; } case kGroupCount: { uint count = value.toUInt(&isOk); if (isOk) data.set_group_count(count); break; } case kGroupPrefix: { uint prefix = value.toUInt(&isOk); if (isOk) data.set_group_prefix(prefix); break; } case kIsOverrideSourceCount: { bool ovr = value.toBool(); data.set_is_override_source_count(ovr); isOk = true; break; } case kIsOverrideGroupRecordCount: { bool ovr = value.toBool(); data.set_is_override_group_record_count(ovr); isOk = true; break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } _exit: return isOk; } int GmpProtocol::protocolFrameSize(int streamIndex) const { // TODO: Calculate to reduce processing cost return AbstractProtocol::protocolFrameValue(streamIndex, true).size(); } int GmpProtocol::protocolFrameVariableCount() const { int count = AbstractProtocol::protocolFrameVariableCount(); // No fields vary for Ssm Report if (isSsmReport()) return count; // For all other msg types, check the group mode if (fieldData(kGroupMode, FieldValue).toUInt() != uint(OstProto::Gmp::kFixed)) { count = AbstractProtocol::lcm(count, fieldData(kGroupCount, FieldValue).toUInt()); } return count; }