//////////////////////////////////////////////////////////////////////////////////// /// /// \brief If the IsLargeDataSet method returns true, then this method is used /// to convert message payload data into a multi-packet stream /// according to rules defined by the JAUS standards documents. /// /// This method should only be overloaded if your message contains data /// larger than Header::MaxSize - Header::MinSize (e.g. image data) and you wish /// to optimize how data is written. By default, this method calls the /// WriteMessageBody method to generate a single large byte array, which is then /// broken up into a Packet::List for the transport layer. /// /// \param[out] stream Multi-packet stream containing serialized message /// following rules of large data sets. /// \param[out] streamHeaders Headers for packet in the stream. /// \param[in] maxPayloadSize This is the maximum packet size including /// payload, does not include transport, General /// Transport Header, or Message Code (USHORT). /// \param[in] transportHeader Additional transport header data to precede /// the general transport header of each packet. /// \param[in] startingSequenceNumber Sequence number to use for packets. /// \param[in] broadcastFlags Values to use to signify if message should be /// sent using any broadcast options (e.g. /// multicast). 0 = no options, 1 = local broadcast, /// 2 = global broadcast. /// /// \return FAILURE on error, otherwise number of packets written. /// //////////////////////////////////////////////////////////////////////////////////// int Message::WriteLargeDataSet(Packet::List& stream, Header::List& streamHeaders, const UShort maxPayloadSize, const Packet* transportHeader, const UShort startingSequenceNumber, const Byte broadcastFlags) const { Header header; header.mDestinationID = mDestinationID; header.mSourceID = mSourceID; header.mPriorityFlag = mPriority; header.mBroadcastFlag = broadcastFlags; stream.clear(); streamHeaders.clear(); Packet* temp = ((Packet *)(&mStreamPayload)); temp->Clear(); if(IsLargeDataSet() && WriteMessageBody(*temp) >= 0) { LargeDataSet::CreateLargeDataSet(header, mMessageCode, *temp, stream, streamHeaders, transportHeader, maxPayloadSize, startingSequenceNumber); return (int)stream.size(); } return FAILURE; }
//////////////////////////////////////////////////////////////////////////////////// /// /// \brief Tests large data set creation and merging. /// //////////////////////////////////////////////////////////////////////////////////// void TestLargeDataSets() { Packet payload; // Large data set payload. Header header; // Message header data. UShort messageCode = 50; // Random message type. Packet::List stream; // Multi-packet stream sequence. Header::List streamHeaders; // Mutli-packet stream sequence headers. unsigned int payloadSize = 60000; // Payload data size to create. header.mSourceID(50, 1, 1); header.mDestinationID(51, 1, 1); for(unsigned int i = 0; i < payloadSize/UINT_SIZE; i++) { payload.Write(i); } LargeDataSet::CreateLargeDataSet(header, messageCode, payload, stream, streamHeaders, NULL, 1437, 30); payload.Destroy(); messageCode = 0; header = Header(); LargeDataSet::MergeLargeDataSet(header, messageCode, payload, stream, NULL); unsigned int data; for(unsigned int i = 0; i < payloadSize/UINT_SIZE; i++) { payload.Read(data); if(data != i) { std::cout << "Large Data Sets Error: Data Does Not Match!\n"; return; } } std::random_shuffle(stream.begin(), stream.end()); LargeDataSet dataSet; for(unsigned int i = 0; i < (unsigned int)stream.size(); i++) { Header header; UShort messageCode; stream[i].SetReadPos(0); header.Read(stream[i]); stream[i].Read(messageCode); if(dataSet.AddPacket(header, messageCode, stream[i]) == false) { std::cout << "Large Data Sets Error: Could Not Collect Stream.\n"; } } // Now merge the data. LargeDataSet::MergeLargeDataSet(header, messageCode, payload, dataSet.mStream, NULL); for(unsigned int i = 0; i < payloadSize/UINT_SIZE; i++) { payload.Read(data); if(data != i) { std::cout << "Large Data Sets Error: Data Does Not Match!\n"; return; } } }
//////////////////////////////////////////////////////////////////////////////////// /// /// \brief Converts the message data into JUDP packets for transmission. /// /// \param[in] message Message to convert/serialize. /// \param[out] stream Packets to send. /// \param[out] streamHeaders Header info for packets. /// \param[in] startingSequenceNumber Sequence number to use for packets. /// \param[in] broadcastFlags Values to use to signify if message should be /// sent using any broadcast options (e.g. /// multicast). 0 = no options, 1 = local broadcast, /// 2 = global broadcast. /// /// \return True on success, false on failure. /// //////////////////////////////////////////////////////////////////////////////////// bool JUDP::SerializeMessage(const Message* message, Packet::List& stream, Header::List& streamHeaders, const UShort startingSequenceNumber, const int broadcastFlags) const { Packet packet; Header header; // Pre-allocate memory. packet.Reserve(mMaxPayloadSize + OverheadSizeBytes); // Clear stream/headers. stream.clear(); streamHeaders.clear(); // If the message is a large data set, create a multi-packet stream. if(message->IsLargeDataSet(mMaxPayloadSize)) { return message->WriteLargeDataSet(stream, streamHeaders, mMaxPayloadSize, &mTransportHeader, startingSequenceNumber) > 0; } // Single packet. else if(message->Write(packet, header, &mTransportHeader, true, startingSequenceNumber) > 0) { stream.push_back(packet); streamHeaders.push_back(header); return true; } return false; }
//////////////////////////////////////////////////////////////////////////////////// /// /// \brief If the Transport services of a component receive a multi-packet /// stream for a message, this method is used to read the contents. /// /// This method should only be overloaded if you wish to optimize the /// de-serialization of mutli-packet streams for your message (e.g. video data). /// By default, this method converts the stream to a larger continuous packet /// to be used by the ReadMessageBody method. /// /// \param[in] stream Multi-packet stream containing serialized message /// following rules of large data sets. /// \param[in] transportHeader Additional transport header data that precedes /// the general transport header of each packet. /// /// \return FAILURE on error, otherwise number of packets read. /// //////////////////////////////////////////////////////////////////////////////////// int Message::ReadLargeDataSet(const Packet::List& stream, const Packet* transportHeader) { Header header; UShort messageCode; if(LargeDataSet::MergeLargeDataSet(header, messageCode, mStreamPayload, stream, transportHeader)) { if(ReadMessageBody(mStreamPayload)) { mSourceID = header.mSourceID; mDestinationID = header.mDestinationID; mPriority = header.mPriorityFlag; return (int)stream.size(); } } return FAILURE; }
//////////////////////////////////////////////////////////////////////////////////// /// /// \brief Test method to verify ability to read/write message data from /// an example wireshark log provided for testing systems for the JAUS /// Interoperability Challenge hosted at several AUVSI competitions. /// //////////////////////////////////////////////////////////////////////////////////// void VerifyAgainstLog() { Packet::List packets; Packet::List::iterator packet; Component component; component.DiscoveryService()->SetSubsystemIdentification(Subsystem::OCU, "OCP"); component.AddService(new LocalPoseSensor()); component.AddService(new VelocityStateSensor()); component.AddService(new ListManager()); component.AddService(new TestLocalWaypointListDriver()); Packet::LoadWiresharkCapturePacketExport("logs/example-capture.txt", packets); Address cop(42, 1, 1); Address sim(6000, 1, 1); //Packet::LoadWiresharkCapturePacketExport("logs/example_capture-2.txt", packets); Message::List messages; Message::List::iterator message; // Delete UDP header data to get to just the JAUS message. for(packet = packets.begin(); packet != packets.end();) { // Delete UDP header data. packet->Delete(43); // Read message header data. Header jausHeader; if(jausHeader.Read(*packet)) { UShort messageCode = 0; packet->Read(messageCode, Header::PayloadOffset); Message* jausMessage = component.TransportService()->CreateMessage(messageCode); if(jausMessage) { packet->SetReadPos(0); if(jausMessage->Read(*packet)) { messages.push_back(jausMessage); if(jausMessage->GetMessageCode() == SET_ELEMENT) { Element::List::iterator m; SetElement* setElement = (SetElement *)jausMessage; for(m = setElement->GetElementList()->begin(); m != setElement->GetElementList()->end(); m++) { UShort code = 0; m->mPayload.SetReadPos(0); m->mPayload.Read(code); Message* element = component.TransportService()->CreateMessage(code); if(element) { element->CopyHeaderData(jausMessage); if(element->ReadMessageBody(m->mPayload)) { messages.push_back(element); } else { std::cout << "Failed to Read Message Data [" << std::hex << element->GetMessageCode() << "]:\n"; delete element; } } } } } else { std::cout << "Failed to Read Message Data [" << std::hex << jausMessage->GetMessageCode() << "]:\n"; } } else { std::cout << "Unknown Message Type [" << std::hex << messageCode << "]:\n"; } } else { std::cout << "Bad Header!\n"; } packet++; } component.Initialize(cop); message = messages.begin(); for(message = messages.begin(); message != messages.end(); message++) { //(*message)->Print(); if((*message)->GetSourceID() == cop) { (*message)->SetDestinationID(sim); component.Send((*message)); } CxUtils::SleepMs(1); delete (*message); } }