Skip to content

Commit

Permalink
Start adding most of DataLogManager
Browse files Browse the repository at this point in the history
  • Loading branch information
ThadHouse committed Feb 22, 2024
1 parent 19107f4 commit e273f5e
Show file tree
Hide file tree
Showing 13 changed files with 469 additions and 24 deletions.
3 changes: 2 additions & 1 deletion codehelp/CodeHelpers/SymbolExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ public static string GetFullTypeName(this ITypeSymbol symbol)
return symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
}

public static string? GetNamespace(this ITypeSymbol symbol) {
public static string? GetNamespace(this ITypeSymbol symbol)
{
// TODO Stop using ToDisplayString
return symbol.ContainingNamespace is { IsGlobalNamespace: false } ns ? ns.ToDisplayString() : null;
}
Expand Down
2 changes: 1 addition & 1 deletion src/ntcore/NetworkTableInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ private void StartThread()
m_thread = new Thread(() =>
{
bool wasInterrupted = false;
ReadOnlySpan<int> handles = [m_poller.Handle, m_waitQueueEvent.Handle];
ReadOnlySpan<int> handles = [m_poller.Handle, m_waitQueueEvent.Handle.Handle];
Span<int> signaledStorage = [0, 0];
while (true)
{
Expand Down
377 changes: 377 additions & 0 deletions src/wpilibsharp/DataLogManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,377 @@
using System.Text;
using NetworkTables;
using NetworkTables.Handles;
using WPIUtil.Concurrent;
using WPIUtil.Logging;

namespace WPILib;

public static class DataLogManger
{
private static DataLog? m_log = null;
private static bool m_stopped;
private static string? m_logDir;
private static bool m_filenameOverride;
private static Thread? m_thread;
private static TimeZoneInfo m_utc = TimeZoneInfo.Utc;
private static bool m_ntLoggerEnabled = true;
private static NtDataLogger m_ntEntryLogger;
private static NtConnectionDataLogger m_ntConnLogger;
private static StringLogEntry? m_messageLog;
private static readonly object m_lockObject = new();
private static volatile int m_runThread = 1;

// if less than this much free space, delete log files until there is this much free space
// OR there are this many files remaining.
private const long FreeSpaceThreshold = 50000000L;
private const int FileCountThreshold = 10;

public static void Start(string dir = "", string filename = "", double period = 0.25)
{
lock (m_lockObject)
{
if (m_log is null)
{
m_logDir = MakeLogDir(dir);
m_filenameOverride = !string.IsNullOrWhiteSpace(filename);

// Delete all previously existing FRC_TBD_*.wpilog files. These only exist when the robot
// never connects to the DS, so they are very unlikely to have useful data and just clutter
// the filesystem.
var files = Directory.GetFiles(m_logDir).Where(name => Path.GetFileName(name).StartsWith("FRC_TBD_") && name.EndsWith(".wpilog"));
foreach (var file in files)
{
try
{
File.Delete(file);
}
catch (IOException)
{
Console.Error.WriteLine($"DataLogManager: could not delete {file}");
}
}
m_log = new DataLog(m_logDir, MakeLogFilename(filename), period);
m_messageLog = new StringLogEntry(m_log, "messages");

if (m_ntLoggerEnabled)
{
StartNtLog();
}
}
else if (m_stopped)
{
m_log.Filename = MakeLogFilename(filename);
m_log.Resume();
m_stopped = false;
}

if (m_thread is null)
{
m_thread = new Thread(LogMain)
{
Name = "DataLogDS",
IsBackground = true
};
m_thread.Start();
}
}
}

public static void Stop()
{
lock (m_lockObject)
{
m_thread = null;
if (m_log is not null)
{
m_log.Stop();
m_stopped = true;
}
}
}

public static void Log(string message)
{
lock (m_lockObject)
{
if (m_messageLog is null)
{
Start();
}
m_messageLog!.Append(message);
Console.WriteLine(message);
}
}

public static DataLog DataLog
{
get
{
lock (m_lockObject)
{
if (m_log is null)
{
Start();
}
return m_log!;
}
}
}

public static string LogDir
{
get
{
lock (m_lockObject)
{
return m_logDir ?? "";
}
}
}

public static void LogNetworkTables(bool enabled)
{
lock (m_lockObject)
{
bool wasEnabled = m_ntLoggerEnabled;
m_ntLoggerEnabled = enabled;
if (m_log is null)
{
Start();
return;
}
if (enabled && !wasEnabled)
{
StartNtLog();
}
else if (!enabled && wasEnabled)
{
StopNtLog();
}
}
}

private static string MakeLogDir(string dir)
{
if (!string.IsNullOrWhiteSpace(dir))
{
return dir;
}

if (RobotBase.IsReal)
{
try
{
Directory.CreateDirectory("/u/logs");
return "/u/logs";
}
catch (IOException)
{

}
if (RobotBase.RuntimeType == RuntimeType.RoboRIO)
{
DriverStation.ReportWarning("DataLogManager: Logging to RoboRIO 1 internal storage is not recommended!, Plug in a FAT32 formatted flash drive!", false);
}
try
{
Directory.CreateDirectory("/home/lvuser/logs");
}
catch (IOException)
{

}
return "/home/lvuser/logs";
}
string logDir = Filesystem.OperatingDirectory;
try
{
Directory.CreateDirectory(logDir);
}
catch (IOException)
{

}
return logDir;
}

private static string MakeLogFilename(string filenameOverride)
{
if (!string.IsNullOrWhiteSpace(filenameOverride))
{
return filenameOverride;
}

Random rnd = new Random();
StringBuilder filename = new();
filename.Append("FRC_TBD_");
for (int i = 0; i < 4; i++)
{
filename.Append(rnd.Next(0x10000).ToString("x4"));
}
filename.Append(".wpilog");
return filename.ToString();
}

private static void StartNtLog()
{
var inst = NetworkTableInstance.Default;
m_ntEntryLogger = inst.StartEntryDataLog(m_log!, "", "NT:");
m_ntConnLogger = inst.StartConnectionDataLog(m_log!, "NTConnection");
}

private static void StopNtLog()
{
NetworkTableInstance.StopEntryDataLog(m_ntEntryLogger);
NetworkTableInstance.StopConnectionDataLog(m_ntConnLogger);
}

private static void LogMain()
{
// based on free disk space, scan for "old" FRC_*.wpilog files and remove
{
string logDir = m_logDir!;
var fileInfo = new DirectoryInfo(logDir);
var driveInfo = new DriveInfo(fileInfo.FullName);
var freeSpace = driveInfo.AvailableFreeSpace;
if (freeSpace < FreeSpaceThreshold)
{
var files = Directory.GetFiles(logDir).Where(name =>
{
name = Path.GetFileName(name);
return name.StartsWith("FRC_")
&& name.EndsWith(".wpilog")
&& !name.StartsWith("FRC_TBD_");
}).OrderBy(File.GetLastWriteTimeUtc);
int count = 0;
foreach (var file in files)
{
--count;
if (count < FileCountThreshold)
{
break;
}
try
{
long length = new FileInfo(file).Length;
DriverStation.ReportWarning($"DataLogManager: Deleted {Path.GetFileName(file)}", false);
freeSpace += length;
if (freeSpace >= FreeSpaceThreshold)
{
break;
}
}
catch (IOException)
{
Console.Error.WriteLine($"DataLogManager: could not delete {file}");
}
}
}
else if (freeSpace < 2 * FreeSpaceThreshold)
{
DriverStation.ReportWarning(
"DataLogManager: Log storage device has "
+ freeSpace / 1000000
+ " MB of free space remaining! Logs will get deleted below "
+ FreeSpaceThreshold / 1000000
+ " MB of free space."
+ "Consider deleting logs off the storage device.",
false);
}
}

int timeoutCount = 0;
bool paused = false;
int dsAttachCount = 0;
int fmsAttachCount = 0;
bool dsRenamed = m_filenameOverride;
bool fmsRenamed = m_filenameOverride;
int sysTimeCount = 0;
IntegerLogEntry sysSimteEntry = new IntegerLogEntry(m_log!, "systemTime", "{\"source\":\"DataLogManager\",\"format\":\"time_t_us\"}");
using Event newDataEvent = new();
DriverStation.ProvideRefreshedDataEventHandle(newDataEvent.Handle);
while (Interlocked.CompareExchange(ref m_thread, null, null) != null)
{
var result = Synchronization.WaitForObject(newDataEvent.Handle.Handle, TimeSpan.FromSeconds(0.25));
if (result == SynchronizationResult.Cancelled || Interlocked.CompareExchange(ref m_thread, null, null) != null)
{
break;
}
if (result == SynchronizationResult.TimedOut)
{
timeoutCount++;
// pause logging after being disconnected for 10 seconds
if (timeoutCount > 40 && !paused)
{
timeoutCount = 0;
paused = true;
m_log!.Pause();
}
continue;
}
// when we connect to the DS, resume logging
timeoutCount = 0;
if (paused)
{
paused = false;
m_log!.Resume();
}

if (!dsRenamed)
{
// track ds attach
if (DriverStation.IsDSAttached)
{
dsAttachCount++;

}
else
{
dsAttachCount = 0;
}
if (dsAttachCount > 50) // 1 second
{
if (RobotController.IsSystemTimeValid)
{
var now = DateTime.UtcNow;
// TODO make this time match
m_log!.Filename = $"FRC_{now}.wpilog";
dsRenamed = true;
}
else
{
dsAttachCount = 0; // wait a bit and try again
}
}
}

if (!fmsRenamed)
{
// track FMS attach
if (DriverStation.IsFMSAttached)
{
fmsAttachCount++;
}
else
{
fmsAttachCount = 0;
}
if (fmsAttachCount > 250) // 5 seconds
{
}
}

// Write system time every ~5 seconds
sysTimeCount++;
if (sysTimeCount > 250)
{
sysTimeCount = 0;
if (RobotController.IsSystemTimeValid)
{
// Write time
}
}
}

}
}
Loading

0 comments on commit e273f5e

Please sign in to comment.