All Downloads are FREE. Search and download functionalities are using the official Maven repository.

us.ihmc.scs2.session.mcap.MCAPConsoleLogManager Maven / Gradle / Ivy

The newest version!
package us.ihmc.scs2.session.mcap;

import us.ihmc.log.LogTools;
import us.ihmc.scs2.session.mcap.MCAPBufferedChunk.ChunkBundle;
import us.ihmc.scs2.session.mcap.encoding.CDRDeserializer;
import us.ihmc.scs2.session.mcap.specs.MCAP;
import us.ihmc.scs2.session.mcap.specs.records.Channel;
import us.ihmc.scs2.session.mcap.specs.records.Message;
import us.ihmc.scs2.session.mcap.specs.records.Record;
import us.ihmc.scs2.session.mcap.specs.records.Schema;
import us.ihmc.scs2.session.mcap.specs.records.Opcode;
import us.ihmc.scs2.simulation.SpyList;

import java.io.IOException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;

public class MCAPConsoleLogManager
{
   public static final String FOXGLOVE_LOG = "foxglove::Log";
   private final long desiredLogDT;

   public enum MCAPLogLevel
   {UNKNOWN, DEBUG, INFO, WARNING, ERROR, FATAL}

   private final SpyList allConsoleLogItems = new SpyList<>();

   private int channelId = -1;

   public MCAPConsoleLogManager(MCAP mcap, MCAPBufferedChunk chunkBuffer, long desiredLogDT) throws IOException
   {
      this.desiredLogDT = desiredLogDT;
      Optional logSchema = mcap.records()
                                       .stream()
                                       .filter(record -> record.op() == Opcode.SCHEMA)
                                       .map(record -> (Schema) record.body())
                                       .filter(schema -> schema.name().equals(FOXGLOVE_LOG))
                                       .findFirst();

      if (logSchema.isPresent())
      {
         int id = logSchema.get().id();

         Optional logChannel = mcap.records()
                                            .stream()
                                            .filter(record -> record.op() == Opcode.CHANNEL)
                                            .map(record -> (Channel) record.body())
                                            .filter(channel -> channel.schemaId() == id)
                                            .findFirst();
         if (logChannel.isPresent())
         {
            if (!"cdr".equalsIgnoreCase(logChannel.get().messageEncoding()))
            {
               LogTools.error("Unsupported message encoding: {}", logChannel.get().messageEncoding());
               return;
            }

            channelId = logChannel.get().id();

            Thread loadingThread = createLoadingThread(chunkBuffer, desiredLogDT);
            loadingThread.start();
         }
      }
   }

   private Thread createLoadingThread(MCAPBufferedChunk chunkBuffer, long desiredLogDT)
   {
      Runnable loadingTask = () ->
      {
         int counter = 0;
         for (ChunkBundle bundle : chunkBuffer.getChunkBundles())
         {
            bundle.requestLoadChunkBundle(true, false, false);

            List orderedItems = new ArrayList<>();

            for (Record record : bundle.getChunkRecords())
            {
               if (record.op() != Opcode.MESSAGE)
                  continue;
               Message message = (Message) record.body();
               if (message.channelId() == channelId)
                  orderedItems.add(parseLogItem(message, desiredLogDT));
            }

            counter++;
            if (counter % (chunkBuffer.getChunkBundles().length / 10) == 0 || counter == chunkBuffer.getChunkBundles().length - 1)
               LogTools.info("Loaded {} log items from chunk bundle {}/{}", orderedItems.size(), counter, chunkBuffer.getChunkBundles().length);
            orderedItems.sort(Comparator.comparingLong(MCAPConsoleLogItem::logTime));
            allConsoleLogItems.addAll(orderedItems);
         }
      };
      Thread loadingThread = new Thread(loadingTask, getClass().getSimpleName() + "-LoadingThread");
      loadingThread.setDaemon(true);
      return loadingThread;
   }

   public SpyList getAllConsoleLogItems()
   {
      return allConsoleLogItems;
   }

   private static MCAPConsoleLogItem parseLogItem(Message message, long desiredLogDT)
   {
      /*
       * @formatter:off
       * Expected schema:
       * struct type_010f744a0100bb8d02000000_030c3_1
       * {
       *     unsigned long sec;
       *     unsigned long nsec;
       * };
       *
       * struct foxglove::Log
       * {
       *     type_010f744a0100bb8d02000000_030c3_1 instant;
       *     octet level;
       *     string message;
       *     string name;
       *     string file;
       *     unsigned long line;
       * };
       * @formatter:on
       */
      long logTime = MCAPMessageManager.round(message.logTime(), desiredLogDT);
      CDRDeserializer deserializer = new CDRDeserializer();
      deserializer.initialize(message.messageBuffer(), 0, message.dataLength());
      Instant instant = Instant.ofEpochSecond(deserializer.read_uint32(), deserializer.read_uint32());
      MCAPLogLevel logLevel = MCAPLogLevel.values()[deserializer.read_uint8()];
      String logMessage = deserializer.read_string();
      String processName = deserializer.read_string();
      String filename = deserializer.read_string();
      long lineNumberInFile = deserializer.read_uint32();
      deserializer.finalize(true);
      return new MCAPConsoleLogItem(logTime, instant, logLevel, logMessage, processName, filename, lineNumberInFile);
   }

   public record MCAPConsoleLogItem(long logTime, Instant instant, MCAPLogLevel logLevel, String message, String processName, String filename,
                                    long lineNumberInFile)
   {
   }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy