package module.decode.p25.message.pdu.confirmed;
import module.decode.p25.message.pdu.PDUMessage;
import module.decode.p25.reference.DataUnitID;
import module.decode.p25.reference.PDUType;
import module.decode.p25.reference.Vendor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import alias.AliasList;
import bits.BinaryMessage;
import edac.CRC;
import edac.CRCP25;
public class PDUConfirmedMessage extends PDUMessage
{
public final static Logger mLog =
LoggerFactory.getLogger( PDUConfirmedMessage.class );
public static final int SEQUENCE_RESET_FLAG = 128; //SYN
public static final int[] PACKET_SEQUENCE_NUMBER = { 129,130,131 };
public static final int FINAL_FRAGMENT_FLAG = 132;
public static final int[] FRAGMENT_SEQUENCE_NUMBER = { 133,134,135 };
public static final int[] PDU_TYPE = { 176,177,178,179 };
public PDUConfirmedMessage( BinaryMessage message, AliasList aliasList )
{
super( message, DataUnitID.PDUC, aliasList );
checkCRC();
}
/**
* Constructor to support subclassing of this message. Initial construction
* of the message enables CRC checks to be calculated and message type to
* be inspected so that we can construct an accurate subclass message,
* without having to recalculate CRC checksums.
*/
protected PDUConfirmedMessage( PDUConfirmedMessage message )
{
super( message.getSourceMessage(), DataUnitID.PDUC, message.getAliasList() );
mCRC = message.getCRCResults();
}
private void checkCRC()
{
int blocks = getBlocksToFollowCount();
/* The NID and Header have already passed CRC */
mCRC = new CRC[ 2 + blocks ];
mCRC[ 0 ] = CRC.PASSED;
mCRC[ 1 ] = CRC.PASSED;
for( int x = 0; x < getBlocksToFollowCount(); x++ )
{
/* Data blocks start at 160 and every 144 thereafter */
mCRC[ x + 2 ] = CRCP25.checkCRC9( mMessage, 160 + ( x * 144 ) );
}
//TODO: check the final 4-byte end of block sequence CRC
}
/* Override for now */
public boolean isValid()
{
return true;
}
@Override
public String getMessage()
{
StringBuilder sb = new StringBuilder();
sb.append( "[" + this.getClass() + "]" );
sb.append( "NAC:" );
sb.append( getNAC() );
sb.append( " " );
sb.append( getDUID().getLabel() );
sb.append( " " );
sb.append( getDirection() );
sb.append( " CRC[" );
sb.append( getErrorStatus() );
sb.append( "] LLID:" );
sb.append( getLogicalLinkID() );
sb.append( " " );
sb.append( getFormat().getLabel() );
sb.append( " SAP:" );
sb.append( getServiceAccessPoint().getLabel() );
if( getVendor() != Vendor.STANDARD )
{
sb.append( " VEND:" );
sb.append( getVendor().getLabel() );
}
sb.append( " PACKET #" );
sb.append( getPacketSequenceNumber() );
if( isFinalFragment() && getFragmentSequenceNumber() == 0 )
{
sb.append( ".C" );
}
else
{
sb.append( "." );
sb.append( getFragmentSequenceNumber() );
if( isFinalFragment() )
{
sb.append( "C" );
}
}
sb.append( " BLOCKS:" );
sb.append( getBlocksToFollowCount() );
sb.append( " " );
sb.append( mMessage.toString() );
return sb.toString();
}
/**
* Packet Data Unit Type
*/
public PDUType getPDUType()
{
return PDUType.fromValue( mMessage.getInt( PDU_TYPE ), isOutbound() );
}
/**
* Number of bytes/octets contained in the message, exclusive of the data
* header.
*/
public int getOctetCount()
{
/* 16 octets per block, and 12 octets in final block */
int count = getBlocksToFollowCount() * 16 - 4;
int padding = getPadOctetCount();
/* Hack: motorola sndcp packet data control - when it says there are 15
* pad octets and only 1 data block of 12 data octets, that means there
* are more pad octets than available data and they appear to suppress
* the final padded data block containing the 12 pad octets. The data
* block count should be 2, but it says 1, so they're either not sending
* the final block, or there is an error in the block count */
if( padding > count )
{
count -= ( padding - 12 );
}
else
{
count -= padding;
}
count -= getDataHeaderOffset();
return count;
}
/**
* Indicates that the receiver should accept this packet, despite the
* apparent out-of-order packet and fragment sequence numbering. This
* indicates that something happened to the synchronization between
* the tower and the radio and now the tower commands the radio to reset
* the sequence numbers and accept this packet/fragment instead of discarding
* it as a duplicate.
*/
public boolean isSequenceReset()
{
return mMessage.get( SEQUENCE_RESET_FLAG );
}
/**
* Packet sequence number. Numbers run from 0 to 7 and will continually
* roll-over if there are more than 8 total packets.
*/
public int getPacketSequenceNumber()
{
return mMessage.getInt( PACKET_SEQUENCE_NUMBER );
}
/**
* Indicates that this packet fragment is the final fragment
*/
public boolean isFinalFragment()
{
return mMessage.get( FINAL_FRAGMENT_FLAG );
}
/**
* Packet fragment sequence number
*/
public int getFragmentSequenceNumber()
{
return mMessage.getInt( FRAGMENT_SEQUENCE_NUMBER );
}
public String toString()
{
return getMessage();
}
}