Skip to content
Snippets Groups Projects
Commit bcf845a7 authored by Brutzman, Don's avatar Brutzman, Don
Browse files

log times and durations during text logging to confirm timing information correct

parent 9776027a
No related branches found
No related tags found
No related merge requests found
...@@ -4,6 +4,7 @@ import com.google.common.primitives.Longs; ...@@ -4,6 +4,7 @@ import com.google.common.primitives.Longs;
import edu.nps.moves.dis7.enumerations.DisPduType; import edu.nps.moves.dis7.enumerations.DisPduType;
import edu.nps.moves.dis7.pdus.Pdu; import edu.nps.moves.dis7.pdus.Pdu;
import edu.nps.moves.dis7.utilities.DisThreadedNetworkInterface; import edu.nps.moves.dis7.utilities.DisThreadedNetworkInterface;
import edu.nps.moves.dis7.utilities.DisTime;
import edu.nps.moves.dis7.utilities.PduFactory; import edu.nps.moves.dis7.utilities.PduFactory;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.File; import java.io.File;
...@@ -16,9 +17,11 @@ import java.nio.CharBuffer; ...@@ -16,9 +17,11 @@ import java.nio.CharBuffer;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.file.Path; import java.nio.file.Path;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime; import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Base64; import java.util.Base64;
...@@ -100,57 +103,30 @@ public class PduRecorder // implements PduReceiver ...@@ -100,57 +103,30 @@ public class PduRecorder // implements PduReceiver
private String encodingPduLog = ENCODING_PLAINTEXT; // default, TODO change to ENCODING_BINARY private String encodingPduLog = ENCODING_PLAINTEXT; // default, TODO change to ENCODING_BINARY
private boolean includeHeaders = encodingPduLog.equals(ENCODING_PLAINTEXT); private boolean includeHeaders = encodingPduLog.equals(ENCODING_PLAINTEXT);
public static final String UNDATED = "undated";
private String TRACE_PREFIX = ("[PduRecorder " + getDescriptor()).trim() + "] "; private String TRACE_PREFIX = ("[PduRecorder " + getDescriptor()).trim() + "] ";
private String descriptor = new String(); private String descriptor = new String();
private Writer logFileWriter; private Writer logFileWriter;
private File logFile; private File logFile;
private String logFileName = DEFAULT_FILE_NAME; private String logFileName = DEFAULT_FILE_NAME;
private DisThreadedNetworkInterface disThreadedNetworkInterface; private DisThreadedNetworkInterface disThreadedNetworkInterface;
private DisThreadedNetworkInterface.RawPduListener disRawPduListener; private DisThreadedNetworkInterface.RawPduListener disRawPduListener;
private PduFactory pduFactory = new PduFactory(); // default appid, country, etc.
private long startNanoTime = -1; // sentinel
private StringBuilder sb = new StringBuilder(); private long recordingStartNanoTime = -1; // sentinel
private Base64.Encoder base64Encoder = Base64.getEncoder(); private StringBuilder sb = new StringBuilder();
private int pduCount = 0; // debug private Base64.Encoder base64Encoder = Base64.getEncoder();
private boolean headerWritten = false; private int pduCount = 0; // debug
private boolean running = true; // starts recording by default private boolean headerWritten = false;
private boolean readableTimeStamp = true; // private boolean running = true; // starts recording by default
private boolean zeroBasedTimeStamp = true; // use normal date, time strings vice bytes private boolean readableTimeStamp = true; //
private long sessionDuration = -1; private boolean zeroBasedTimeStamp = true; // use normal date, time strings vice bytes
public static final String UNDATED = "undated"; private long recordingDurationNano = -1;
private LocalTime recordingDuration = null;
public enum TimeFormatterType private int pduTimestampFirst = 0;
{
SECONDS,
TENTHSECONDS,
HUNDREDTHSECONDS,
MILLISECONDS,
MICROSECONDS,
NANOSECONDS;
}
/** Format time <code>HH:mm:ss</code>
* @see java.time.format.DateTimeFormatter */
public static final DateTimeFormatter timeFormatterSeconds = DateTimeFormatter.ofPattern("HH:mm:ss");
/** Format time <code>HH:mm:ss.S</code>, default
* @see java.time.format.DateTimeFormatter */
public static final DateTimeFormatter timeFormatterTenthSeconds = DateTimeFormatter.ofPattern("HH:mm:ss.S");
/** Format time <code>HH:mm:ss.SS</code>
* @see java.time.format.DateTimeFormatter */
public static final DateTimeFormatter timeFormatterHundredthSeconds = DateTimeFormatter.ofPattern("HH:mm:ss.SS");
/** Format time <code>HH:mm:ss.SSS</code>
* @see java.time.format.DateTimeFormatter */
public static final DateTimeFormatter timeFormatterMilliSeconds = DateTimeFormatter.ofPattern("HH:mm:ss.SSS");
/** Format time <code>HH:mm:ss.SSSSSS</code>
* @see java.time.format.DateTimeFormatter */
public static final DateTimeFormatter timeFormatterMicroSeconds = DateTimeFormatter.ofPattern("HH:mm:ss.SSSSSS");
/** Format time <code>HH:mm:ss.SSSSSSSSS</code>
* @see java.time.format.DateTimeFormatter */
public static final DateTimeFormatter timeFormatterNanoSeconds = DateTimeFormatter.ofPattern("HH:mm:ss.SSSSSSSSS");
private DateTimeFormatter timeFormatter = timeFormatterTenthSeconds;
private void initialize() private void initialize()
{ {
...@@ -338,39 +314,61 @@ public class PduRecorder // implements PduReceiver ...@@ -338,39 +314,61 @@ public class PduRecorder // implements PduReceiver
} }
byte[] oldBuffer; byte[] oldBuffer;
LocalDateTime sessionStartTime = null;
/** receivePdu from DIS data stream /** receivePdu from DIS data stream, invoked via callback from DisThreadedNetworkInterface.RawPduListener
* @param newBuffer byte array for receiving data * @param newBuffer byte array for receiving data
* @param newLength length of byte array * @param newLength length of byte array
*/ */
// @Override // @Override
public void receivePdu(byte[] newBuffer, int newLength) public void receivePdu(byte[] newBuffer, int newLength)
{ {
if (sessionStartTime == null)
sessionStartTime = LocalDateTime.now();
if (java.util.Arrays.equals(newBuffer, oldBuffer)) if (java.util.Arrays.equals(newBuffer, oldBuffer))
System.err.println ("PduRecorder.receivePdu() warning: PDU newBuffer equals PDU oldBuffer"); // debug System.err.println ("PduRecorder.receivePdu() warning: PDU newBuffer equals PDU oldBuffer"); // debug
if(!isRunning()) if(!isRunning())
return; return; // thread operations no longer in progress, ignore this received PDU
String localDateString = LocalDate.now().toString();
LocalTime localTime = LocalTime.now();
long packetReceivedNanoTime = localTime.toNanoOfDay(); // formerly System.nanoTime();
if (startNanoTime == -1)
startNanoTime = packetReceivedNanoTime;
sessionDuration = packetReceivedNanoTime - startNanoTime;
if (isZeroBasedTimeStamp())
{
localDateString = UNDATED;
localTime = LocalTime.ofNanoOfDay(sessionDuration);
}
String localTimeString = localTime.format(timeFormatter);
// String localDateString = LocalDate.now().toString();
LocalTime sessionTime = LocalTime.now();
long packetReceivedNanoTime = sessionTime.toNanoOfDay();
byte[] byteBufferSized = Arrays.copyOf(newBuffer, newLength);
// direct access: DisPduType is 3rd byte, see Table 98—PDU Header record (TODO course diagrams are erroneous)
DisPduType pduType = DisPduType.getEnumForValue(Byte.toUnsignedInt(byteBufferSized[2])); // 3rd byte
Pdu newPdu = pduFactory.createPdu(byteBufferSized);
int pduTimestampInt = newPdu.getTimestamp();
int pduDurationInt = pduTimestampInt - pduTimestampFirst;
// DIS timestamp is 8 bytes in length, converted from Java long time into byte array // DIS timestamp is 8 bytes in length, converted from Java long time into byte array
byte[] timeByteArray = Longs.toByteArray(packetReceivedNanoTime - startNanoTime); // https://stackoverflow.com/questions/1026761/how-to-convert-a-byte-array-to-its-numeric-value-java
//System.out.println(TRACE_PREFIX + "wrote time "+(packetReceivedNanoTime - startNanoTime)); // debug byte[] timestampByteArray = Arrays.copyOfRange(newBuffer, 4, 7);
// timestamp bytes 4..7 (fifth through eighth bytes), see Table 98—PDU Header record (TODO course diagrams are erroneous)
byte[] byteBufferSized = Arrays.copyOf(newBuffer, newLength); byte[] receiptTimeByteArray = Longs.toByteArray(packetReceivedNanoTime - recordingStartNanoTime);
DisPduType pduType = DisPduType.getEnumForValue(Byte.toUnsignedInt(byteBufferSized[2])); // 3rd byte ByteBuffer receiptTimeByteBuffer = ByteBuffer.wrap(receiptTimeByteArray);
int receiptTimeBufferInt = receiptTimeByteBuffer.getInt();
String receiptTimeBufferString = DisTime.convertToLocalDateTime(receiptTimeBufferInt).format(DisTime.getTimeFormatter());
if (recordingStartNanoTime == -1) // initialization
{
recordingStartNanoTime = packetReceivedNanoTime;
pduTimestampFirst = pduTimestampInt;
pduDurationInt = 0;
}
// String pduTimestampString = DisTime.convertToString(pduTimestampInt);
String pduTimestampString = DisTime.convertToLocalDateTime(pduTimestampInt).format(DisTime.getTimeFormatter());
String pduDurationString = DisTime.convertToLocalDateTime(pduDurationInt).format(DisTime.getTimeFormatter());
recordingDurationNano = packetReceivedNanoTime - recordingStartNanoTime;
// sessionDuration = Duration.between(Instant.ofEpochMilli(startSessionNanoTime),Instant.ofEpochMilli(packetReceivedNanoTime)).abs();
recordingDuration = LocalTime.ofNanoOfDay(recordingDurationNano); // LocalTime of duration value yields HH:MM
String sessionTimeString = sessionTime.format(DisTime.getTimeFormatter());
String sessionDurationString = recordingDuration.format(DisTime.getTimeFormatter());
if (includeHeaders && !headerWritten) if (includeHeaders && !headerWritten)
{ {
...@@ -385,8 +383,8 @@ public class PduRecorder // implements PduReceiver ...@@ -385,8 +383,8 @@ public class PduRecorder // implements PduReceiver
break; break;
case ENCODING_BASE64: case ENCODING_BASE64:
byte[] mergedByteArray = Arrays.copyOf(timeByteArray, timeByteArray.length + byteBufferSized.length); byte[] mergedByteArray = Arrays.copyOf(receiptTimeByteArray, receiptTimeByteArray.length + byteBufferSized.length);
System.arraycopy(byteBufferSized, 0, mergedByteArray, timeByteArray.length, byteBufferSized.length); System.arraycopy(byteBufferSized, 0, mergedByteArray, receiptTimeByteArray.length, byteBufferSized.length);
sb.append(base64Encoder.encodeToString(mergedByteArray)); sb.append(base64Encoder.encodeToString(mergedByteArray));
/* /*
// from Rick // from Rick
...@@ -408,12 +406,15 @@ public class PduRecorder // implements PduReceiver ...@@ -408,12 +406,15 @@ public class PduRecorder // implements PduReceiver
case ENCODING_PLAINTEXT: case ENCODING_PLAINTEXT:
if (includesReadableTimeStamp()) if (includesReadableTimeStamp())
{ {
sb.append(COMMENT_MARKER).append(" ").append(pduType).append(","); sb.append(COMMENT_MARKER).append(" ").append(pduType).append(", ");
sb.append(localDateString).append(",").append(localTimeString); sb.append("Session time " ).append(sessionTimeString ).append(", ");
sb.append("session duration ").append(sessionDurationString).append(", ");
sb.append("Pdu timestamp " ).append(pduTimestampInt ).append(" ").append(pduTimestampString).append(", ");
sb.append("simulation stream interval " ).append(pduDurationInt ).append(" ").append(pduDurationString);
sb.append("\n"); sb.append("\n");
} }
// Timestamp bytes, remove square brackets to end up with pure CSV // Not Timestamp but receipt bytes (TODO needed?) remove square brackets to end up with pure CSV
sb.append(Arrays.toString(timeByteArray).replace(" ", "").replace("[","").replace("]","")); sb.append(Arrays.toString(receiptTimeByteArray).replace(" ", "").replace("[","").replace("]",""));
sb.append(","); sb.append(",");
// PDU contents, remove square brackets to end up with pure CSV // PDU contents, remove square brackets to end up with pure CSV
sb.append(Arrays.toString(byteBufferSized).replace(" ", "").replace("[","").replace("]","")); sb.append(Arrays.toString(byteBufferSized).replace(" ", "").replace("[","").replace("]",""));
...@@ -492,14 +493,14 @@ public class PduRecorder // implements PduReceiver ...@@ -492,14 +493,14 @@ public class PduRecorder // implements PduReceiver
logFileWriter.write(START_COMMENT_MARKER + encodingPduLog + ", " + TRACE_PREFIX + timeStamp + ", DIS capture file, " + logFile.getPath()); logFileWriter.write(START_COMMENT_MARKER + encodingPduLog + ", " + TRACE_PREFIX + timeStamp + ", DIS capture file, " + logFile.getPath());
((PrintWriter) logFileWriter).println(); ((PrintWriter) logFileWriter).println();
if (encodingPduLog.equals(ENCODING_PLAINTEXT) && includesReadableTimeStamp()) // if (encodingPduLog.equals(ENCODING_PLAINTEXT) && includesReadableTimeStamp())
{ // {
logFileWriter.write(COMMENT_MARKER + " DisPduType,ReceiptDate,ReceiptTime"); // logFileWriter.write(COMMENT_MARKER + " DisPduType,ReceiptDate,ReceiptTime");
((PrintWriter) logFileWriter).println(); // ((PrintWriter) logFileWriter).println();
} // }
if (encodingPduLog.equals(ENCODING_PLAINTEXT)) if (encodingPduLog.equals(ENCODING_PLAINTEXT))
{ {
logFileWriter.write(COMMENT_MARKER + " Timestamp(8 bytes),ProtocolVersion,CompatibilityVersion,ExcerciseID,PduType,PduStatus,HeaderLength,PduLength,then PDU-specific data"); logFileWriter.write(COMMENT_MARKER + " Timestamp(8 bytes),ProtocolVersion,CompatibilityVersion,ExerciseID,PduType,PduStatus,HeaderLength,PduLength,then PDU-specific data");
((PrintWriter) logFileWriter).println(); ((PrintWriter) logFileWriter).println();
} }
if (encodingPduLog.equals(ENCODING_PLAINTEXT) && includesReadableTimeStamp()) if (encodingPduLog.equals(ENCODING_PLAINTEXT) && includesReadableTimeStamp())
...@@ -610,7 +611,6 @@ public class PduRecorder // implements PduReceiver ...@@ -610,7 +611,6 @@ public class PduRecorder // implements PduReceiver
initialize(); initialize();
System.out.println("dis7.utilities.stream.PduRecorder main() performs self-test by sending full set of PDUs"); System.out.println("dis7.utilities.stream.PduRecorder main() performs self-test by sending full set of PDUs");
PduFactory factory = new PduFactory(); // default appid, country, etc.
PduRecorder pduRecorder; PduRecorder pduRecorder;
DisThreadedNetworkInterface disNetworkInterface; DisThreadedNetworkInterface disNetworkInterface;
DisPduType allPDUTypesArray[] = DisPduType.values(); DisPduType allPDUTypesArray[] = DisPduType.values();
...@@ -639,13 +639,15 @@ public class PduRecorder // implements PduReceiver ...@@ -639,13 +639,15 @@ public class PduRecorder // implements PduReceiver
disNetworkInterface = pduRecorder.getDisThreadedNetworkInterface(); // must reinitialize after each begin disNetworkInterface = pduRecorder.getDisThreadedNetworkInterface(); // must reinitialize after each begin
System.out.println("dis7.utilities.stream.PduRecorder pduRecorder started... isRunning()=" + pduRecorder.isRunning()); System.out.println("dis7.utilities.stream.PduRecorder pduRecorder started... isRunning()=" + pduRecorder.isRunning());
for (int i=1; i < allPDUTypesArray.length; i = i + 1) for (int index=0; index < allPDUTypesArray.length; index++)
{ {
DisPduType pduTypeValue = allPDUTypesArray[i]; DisPduType pduTypeValue = allPDUTypesArray[index];
if (pduTypeValue != DisPduType.OTHER) if (pduTypeValue != DisPduType.OTHER)
{ {
try { try {
Pdu nextPdu = factory.createPdu(allPDUTypesArray[i]); Pdu nextPdu = pduFactory.createPdu(allPDUTypesArray[index]);
nextPdu.setTimestamp(index * 10); // seconds
// nextPdu.getTimestamp(); // debug
disNetworkInterface.send(nextPdu); disNetworkInterface.send(nextPdu);
Thread.sleep (100L); // let send/receive threads and streams catch up Thread.sleep (100L); // let send/receive threads and streams catch up
} }
...@@ -893,10 +895,10 @@ public class PduRecorder // implements PduReceiver ...@@ -893,10 +895,10 @@ public class PduRecorder // implements PduReceiver
/** /**
* get duration of the current session, measured from time of first PDU receipt * get duration of the current session, measured from time of first PDU receipt
* @return the sessionDuration * @return the sessionDurationNano
*/ */
public long getSessionDuration() { public long getSessionDuration() {
return sessionDuration; return recordingDurationNano;
} }
/** /**
...@@ -914,34 +916,4 @@ public class PduRecorder // implements PduReceiver ...@@ -914,34 +916,4 @@ public class PduRecorder // implements PduReceiver
public void setZeroBasedTimeStamp(boolean zeroBasedTimeStamp) { public void setZeroBasedTimeStamp(boolean zeroBasedTimeStamp) {
this.zeroBasedTimeStamp = zeroBasedTimeStamp; this.zeroBasedTimeStamp = zeroBasedTimeStamp;
} }
/**
* Set time format for text logging
* @param timeFormatterChoice enumeration for the new timeFormatter to set
*/
public void setTimeFormatter(TimeFormatterType timeFormatterChoice)
{
switch (timeFormatterChoice)
{
case SECONDS:
timeFormatter = timeFormatterSeconds;
break;
case TENTHSECONDS:
timeFormatter = timeFormatterTenthSeconds;
break;
case HUNDREDTHSECONDS:
timeFormatter = timeFormatterHundredthSeconds;
break;
case MILLISECONDS:
timeFormatter = timeFormatterMilliSeconds;
break;
case MICROSECONDS:
timeFormatter = timeFormatterMicroSeconds;
break;
case NANOSECONDS:
timeFormatter = timeFormatterNanoSeconds;
break;
// no others allowed
}
}
} }
AAAAAAAAAAAHAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= AABGKWI0sJUHAAEBAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAYAWLAHAAICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= AAAAAAZV4SwHAAICAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAz5hJgHAAMCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== AAAAAAzvWLAHAAMCAAAAHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
AAAAABMY7HQHAAQBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= AAAAABM5wlAHAAQBAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAABllmPwHAAUDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAABmVXLgHAAUDAAAAMgAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAB+hRlgHAAYDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAB/jMVgHAAYDAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAACZ1yLQHAAcDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAACYIQggHAAcDAAAARgAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAACyzmPAHAAgDAAAAAAAAAAAAAAAAAAAAAAAAAAA= AAAAACya2iwHAAgDAAAAUAAAAAAAAAAAAAAAAAAAAAA=
AAAAADM1i1wHAAkDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAADMCvLgHAAkDAAAAWgAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAADk9rWgHAAoDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAADmugWQHAAoDAAAAZAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAD8v6TQHAAsFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAEB+blAHAAsFAAAAbgAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAEWjKLwHAAwFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAEZ4gFwHAAwFAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAEu7UFgHAA0FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== AAAAAE0tVugHAA0FAAAAggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
AAAAAFI8YDQHAA4FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAFNgw8QHAA4FAAAAjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAFiCcoAHAA8FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAA== AAAAAFmTSDQHAA8FAAAAlgAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAA==
AAAAAF60AmgHABAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAF+502gHABAFAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAGT1FjAHABEFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAGYRZJwHABEFAAAAqgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAGsc4OwHABIFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAGw/K2AHABIFAAAAtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAHGyRRQHABMFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAHKundwHABMFAAAAvgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAHfR6qgHABQFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAHjVfdQHABQFAAAAyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAH6FP0QHABUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAH/PDiAHABUFAAAA0gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAIUjA5AHABYFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== AAAAAIXPKwwHABYFAAAA3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
AAAAAIuL6wwHABcGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAIvV2TgHABcGAAAA5gAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAJGqTAQHABgGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAJKoPpAHABgGAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAJhnLGAHABkEAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAJlnn/AHABkEAAAA+gAAAAAAAAAAAAAAAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAJ9dIaAHABoEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAA AAAAAJ/EkIgHABoEAAABBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAA
AAAAAKVwIbQHABsEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= AAAAAKW6klgHABsEAAABDgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAKwJMnwHABwGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= AAAAAKwav3QHABwGAAABGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAALIP+nAHAB0GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== AAAAALJ3WPAHAB0GAAABIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
AAAAALiEsiQHAB4GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAALkTAcgHAB4GAAABLAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAL7nHkAHAB8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAA= AAAAAL8KDsgHAB8EAAABNgAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAA=
AAAAAMUeP/AHACAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAMXIEcQHACAEAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAMtCIeQHACEHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAMu4PXAHACEHAAABSgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAANFNpfQHACIHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAANIrrvgHACIHAAABVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAANffMugHACMHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAANlEPaQHACMHAAABXgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAN3ai4QHACQHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAN/Z//AHACQHAAABaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAOTk3gwHACUIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= AAAAAOYNpQwHACUIAAABcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAOu4fdgHACYIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAOxFBTwHACYIAAABfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAPIE2egHACcIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== AAAAAPNGIMgHACcIAAABhgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
AAAAAPhxXgAHACgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== AAAAAPnN5pwHACgIAAABkAAAAAAAAAAAAAAAAAAAAAAAAA==
AAAAAP8S4awHACkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== AAAAAQCU1GgHACkJAAABmgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
AAAAAQUvNNwHACoJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAQb060AHACoJAAABpAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAQuXxBAHACsJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAQ3G3VwHACsJAAABrgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAARGeEogHACwJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAARSOPTgHACwJAAABuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAARe/YLgHAC0JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAARrF6ZQHAC0JAAABwgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAR4TvywHAC4LAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= AAAAASEW9zQHAC4LAAABzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAASU8MEAHAC8LAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAScWtmAHAC8LAAAB1gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAStsVBgHADALAAAAAAAAAAAAAAAAAA== AAAAAS1kTnQHADALAAAB4AAAAAAAAAAAAA==
AAAAATGwdxQHADELAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAATOA46gHADELAAAB6gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAATfqASwHADILAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAATnPjyAHADILAAAB9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAT3wczAHADMKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== AAAAAUAUhdQHADMKAAAB/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
AAAAAUP6EuAHADQKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== AAAAAUZVXKwHADQKAAACCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
AAAAAUoMGugHADUKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= AAAAAUyX/CgHADUKAAACEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAVARGQwHADYKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAVMnSuQHADYKAAACHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAVZ6ZhgHADcKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAA== AAAAAVlTQGwHADcKAAACJgAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAA==
AAAAAVzQVPAHADgKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== AAAAAWALwzQHADgKAAACMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
AAAAAWNGk0QHADkKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAWZtZPgHADkKAAACOgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAWl9YVwHADoKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== AAAAAW0gPPgHADoKAAACRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
AAAAAW+q73wHADsKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAXNIQ9wHADsKAAACTgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAXXUxOAHADwKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAXmvMRgHADwKAAACWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAXw5ZjgHAD0KAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAX/l9WwHAD0KAAACYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAYKOSkQHAD4KAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== AAAAAYYEeMQHAD4KAAACbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
AAAAAYloT5AHAD8KAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= AAAAAYwqVJQHAD8KAAACdgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAY+hP8AHAEAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAZIrnqwHAEAKAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAZZDpygHAEEKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAZh/jWgHAEEKAAACigAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAZ0gqOgHAEIBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAZ8x2MgHAEIBAAAClAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAaRTNRgHAEMBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= AAAAAaVKArwHAEMBAAACngAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAap6GogHAEQCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAauVZlwHAEQCAAACqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAbGHltQHAEUCAAAAAAAAAAAAAAAAAAAAAAAAAAA= AAAAAbH0POQHAEUCAAACsgAAAAAAAAAAAAAAAAAAAAA=
AAAAAbeaskAHAEYNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== AAAAAbgFgTgHAEYNAAACvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
AAAAAb4CAFwHAEcNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAb5ZWCAHAEcNAAACxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAcQ+hVgHAEgBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA== AAAAAcRimIAHAEgBAAAC0AAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA==
No preview for this file type
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment