add logging mechanism;

pull/1/head
Bryan Biedenkapp 11 months ago
parent b6c144af64
commit e3dbbbb0e1

@ -34,7 +34,7 @@ namespace dvmconsole
if (audioData.Length != origLen) if (audioData.Length != origLen)
{ {
Trace.WriteLine($"Invalid PCM length: {audioData.Length}, expected: {origLen}"); Log.WriteLine($"Invalid PCM length: {audioData.Length}, expected: {origLen}");
return chunks; return chunks;
} }
@ -57,7 +57,7 @@ namespace dvmconsole
{ {
if (chunks.Count * expectedLength != origLen) if (chunks.Count * expectedLength != origLen)
{ {
Trace.WriteLine($"Invalid number of chunks: {chunks.Count}, expected total length: {origLen}"); Log.WriteLine($"Invalid number of chunks: {chunks.Count}, expected total length: {origLen}");
return null; return null;
} }

@ -134,20 +134,20 @@ namespace dvmconsole
switch (level) switch (level)
{ {
case LogLevel.WARNING: case LogLevel.WARNING:
Trace.WriteLine(message); Log.WriteWarning(message);
break; break;
case LogLevel.ERROR: case LogLevel.ERROR:
Trace.WriteLine(message); Log.WriteError(message);
break; break;
case LogLevel.DEBUG: case LogLevel.DEBUG:
Trace.WriteLine(message); Log.WriteLine($"[DEBUG] {message}");
break; break;
case LogLevel.FATAL: case LogLevel.FATAL:
Trace.WriteLine(message); Log.WriteError(message);
break; break;
case LogLevel.INFO: case LogLevel.INFO:
default: default:
Trace.WriteLine(message); Log.WriteLine(message);
break; break;
} }
}; };

@ -95,7 +95,7 @@ namespace dvmconsole
{ {
if (child == null) if (child == null)
{ {
Trace.WriteLine("A child in ChannelsCanvas.Children is null."); Log.WriteLine("A child in ChannelsCanvas.Children is null.");
continue; continue;
} }
@ -107,14 +107,14 @@ namespace dvmconsole
Codeplug.System system = Codeplug.GetSystemForChannel(channelBox.ChannelName); Codeplug.System system = Codeplug.GetSystemForChannel(channelBox.ChannelName);
if (system == null) if (system == null)
{ {
Trace.WriteLine($"System not found for {channelBox.ChannelName}"); Log.WriteLine($"System not found for {channelBox.ChannelName}");
continue; continue;
} }
Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channelBox.ChannelName); Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channelBox.ChannelName);
if (cpgChannel == null) if (cpgChannel == null)
{ {
Trace.WriteLine($"Channel not found for {channelBox.ChannelName}"); Log.WriteLine($"Channel not found for {channelBox.ChannelName}");
continue; continue;
} }
@ -123,7 +123,7 @@ namespace dvmconsole
if (channelBox.Crypter == null) if (channelBox.Crypter == null)
{ {
Trace.WriteLine($"Crypter is null for channel {channelBox.ChannelName}"); Log.WriteLine($"Crypter is null for channel {channelBox.ChannelName}");
continue; continue;
} }

@ -0,0 +1,342 @@
// SPDX-License-Identifier: AGPL-3.0-only
/**
* Digital Voice Modem - Desktop Dispatch Console
* AGPLv3 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Desktop Dispatch Console
* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL
*
*/
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
namespace dvmconsole
{
/// <summary>
///
/// </summary>
/// <param name="message"></param>
public delegate void LogWriteLine(string message);
/// <summary>
/// Implements the logger system.
/// </summary>
public class Log
{
public static FileStream logStream = null;
/// <summary>
/// Flag indicating logging should display on a console window.
/// </summary>
public static bool DisplayToConsole = false;
private static TextWriter tw;
private const string LOG_TIME_FORMAT = "MM/dd/yyyy HH:mm:ss";
private static ConsoleColor defColor = Console.ForegroundColor;
/// <summary>
/// Flag indicating logging should stop.
/// </summary>
public static bool StopLogging = false;
/*
** Properties
*/
/// <summary>
/// Gets or sets a delegate to also write logs to.
/// </summary>
public static LogWriteLine LogWriter { get; set; } = null;
/*
** Methods
*/
/// <summary>
/// Sets up the trace logging.
/// </summary>
/// <param name="directoryPath"></param>
/// <param name="logFile"></param>
public static void SetupTextWriter(string directoryPath, string logFile)
{
// destroy existing log file
if (File.Exists(directoryPath + Path.DirectorySeparatorChar + logFile))
File.Delete(directoryPath + Path.DirectorySeparatorChar + logFile);
// open a new log stream
logStream = new FileStream(directoryPath + Path.DirectorySeparatorChar + logFile, FileMode.CreateNew);
tw = new StreamWriter(logStream);
}
/// <summary>
/// Writes a log entry to the text log.
/// </summary>
/// <param name="message"></param>
/// <param name="noTimeStamp"></param>
public static void WriteLog(string message, bool noTimeStamp = false)
{
string logTime = DateTime.Now.ToString(LOG_TIME_FORMAT) + " ";
if (tw != null)
{
if (noTimeStamp)
tw.WriteLine(message);
else
tw.WriteLine(logTime + message);
tw.Flush();
}
if (noTimeStamp)
System.Diagnostics.Trace.WriteLine(message);
else
System.Diagnostics.Trace.WriteLine(logTime + message);
if (LogWriter != null)
LogWriter(message);
if (DisplayToConsole)
{
if (!noTimeStamp)
Console.Write(logTime);
if (message.StartsWith("WARN"))
Console.ForegroundColor = ConsoleColor.Yellow;
else if (message.StartsWith("FAIL") || message.StartsWith("ERROR"))
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(message);
Console.ForegroundColor = defColor;
}
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <param name="dynamic"></param>
/// <returns></returns>
public static string GenericToString<T>(T value, bool dynamic)
{
return GenericToString<T>(value, dynamic, ", ");
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <param name="dynamic"></param>
/// <param name="separator"></param>
/// <returns></returns>
public static string GenericToString<T>(T value, bool dynamic, string separator)
{
return GenericToString<T>(dynamic ? value.GetType() : typeof(T), ref value, separator);
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="type"></param>
/// <param name="value"></param>
/// <param name="separator"></param>
/// <returns></returns>
private static string GenericToString<T>(Type type, [In] ref T value, string separator)
{
var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
List<string> strs = new List<string>();
foreach (var property in properties)
{
if (property.CanRead)
{
try { strs.Add(string.Format("{0} = {1}", property.Name, property.GetValue(value, null))); }
catch (InvalidOperationException) { }
}
}
return string.Format("{0} {{{1}}}", type.Name, string.Join(separator, strs.ToArray()));
}
/// <summary>
/// Writes the exception stack trace to the console/trace log
/// </summary>
/// <param name="throwable">Exception to obtain information from</param>
/// <param name="reThrow"></param>
public static void StackTrace(Exception throwable, bool reThrow = true)
{
StackTrace(string.Empty, throwable, reThrow);
}
/// <summary>
/// Writes the exception stack trace to the console/trace log
/// </summary>
/// <param name="msg"></param>
/// <param name="throwable">Exception to obtain information from</param>
/// <param name="reThrow"></param>
public static void StackTrace(string msg, Exception throwable, bool reThrow = true)
{
MethodBase mb = new System.Diagnostics.StackTrace().GetFrame(1).GetMethod();
ParameterInfo[] param = mb.GetParameters();
string funcParams = string.Empty;
for (int i = 0; i < param.Length; i++)
if (i < param.Length - 1)
funcParams += param[i].ParameterType.Name + ", ";
else
funcParams += param[i].ParameterType.Name;
Exception inner = throwable.InnerException;
WriteError("caught an unrecoverable exception! " + msg);
WriteLog("---- TRACE SNIP ----");
WriteLog(throwable.Message + (inner != null ? " (Inner: " + inner.Message + ")" : ""));
WriteLog(throwable.GetType().ToString());
WriteLog("<" + mb.ReflectedType.Name + "::" + mb.Name + "(" + funcParams + ")>");
WriteLog(throwable.Source);
foreach (string str in throwable.StackTrace.Split(new string[] { Environment.NewLine }, StringSplitOptions.None))
WriteLog(str);
if (inner != null)
foreach (string str in throwable.StackTrace.Split(new string[] { Environment.NewLine }, StringSplitOptions.None))
WriteLog("inner trace: " + str);
WriteLog("---- TRACE SNIP ----");
if (reThrow)
throw throwable;
}
/// <summary>
/// Writes a error trace message w/ calling function information.
/// </summary>
/// <param name='message'>Message to print</param>
public static void WriteWarning(string message)
{
WriteLog("WARN: " + message);
}
/// <summary>
/// Writes a error trace message w/ calling function information.
/// </summary>
/// <param name='message'>Message to print</param>
public static void WriteError(string message)
{
WriteLog("ERROR: " + message);
}
/// <summary>
/// Writes a trace message w/ calling function information.
/// </summary>
/// <param name="message">Message to print to debug window</param>
/// <param name="myTraceFilter"></param>
/// <param name="frame"></param>
/// <param name="dropToConsole"></param>
/// <param name="noTimeStamp"></param>
public static void WriteLine(string message, int frame = 1, bool dropToConsole = false, bool noTimeStamp = false)
{
string trace = string.Empty;
MethodBase mb = new System.Diagnostics.StackTrace().GetFrame(frame).GetMethod();
ParameterInfo[] param = mb.GetParameters();
string funcParams = string.Empty;
for (int i = 0; i < param.Length; i++)
if (i < param.Length - 1)
funcParams += param[i].ParameterType.Name + ", ";
else
funcParams += param[i].ParameterType.Name;
trace += "<" + mb.ReflectedType.Name + "::" + mb.Name + "(" + funcParams + ")> ";
trace += message;
WriteLog(trace, noTimeStamp);
}
/// <summary>
/// Helper to display the ASCII representation of a hex dump.
/// </summary>
/// <param name="buffer"></param>
/// <param name="offset"></param>
/// <returns></returns>
private static string DisplayHexChars(byte[] buffer, int offset)
{
int bCount = 0;
string _out = string.Empty;
for (int i = offset; i < buffer.Length; i++)
{
// stop every 16 bytes...
if (bCount == 16)
break;
byte b = buffer[i];
char c = Convert.ToChar(b);
// make control and illegal characters spaces
if (c >= 0x00 && c <= 0x1F)
c = ' ';
if (c >= 0x7F)
c = ' ';
_out += c;
bCount++;
}
return _out;
}
/// <summary>
/// Perform a hex dump of a buffer.
/// </summary>
/// <param name="message"></param>
/// <param name="buffer"></param>
/// <param name="maxLength"></param>
/// <param name="myTraceFilter"></param>
/// <param name="startOffset"></param>
/// <param name="dropToConsole"></param>
public static void TraceHex(string message, byte[] buffer, int maxLength = 32, int startOffset = 0)
{
int bCount = 0, j = 0, lenCount = 0;
// iterate through buffer printing all the stored bytes
string traceMsg = message + "\nDUMP " + j.ToString("X4") + ": ";
for (int i = startOffset; i < buffer.Length; i++)
{
byte b = buffer[i];
// split the message every 16 bytes...
if (bCount == 16)
{
traceMsg += "\t*" + DisplayHexChars(buffer, j) + "*";
WriteLine(traceMsg, 2, false, true);
bCount = 0;
j += 16;
traceMsg = "DUMP " + j.ToString("X4") + ": ";
}
else
traceMsg += (bCount > 0) ? " " : "";
traceMsg += b.ToString("X2");
bCount++;
// increment the length counter, and check if we've exceeded the specified
// maximum, then break the loop
lenCount++;
if (lenCount > maxLength)
break;
}
// if the byte count at this point is non-zero print the message
if (bCount != 0)
{
traceMsg += "\t*" + DisplayHexChars(buffer, j) + "*";
WriteLine(traceMsg, 2, false, true);
}
}
} // public class Log
} // namespace dvmconsole

@ -309,7 +309,7 @@ namespace dvmconsole
// hook FNE events // hook FNE events
peer.peer.PeerConnected += (sender, response) => peer.peer.PeerConnected += (sender, response) =>
{ {
Trace.WriteLine("FNE Peer connected"); Log.WriteLine("FNE Peer connected");
Dispatcher.Invoke(() => Dispatcher.Invoke(() =>
{ {
EnableCommandControls(); EnableCommandControls();
@ -320,7 +320,7 @@ namespace dvmconsole
peer.peer.PeerDisconnected += (response) => peer.peer.PeerDisconnected += (response) =>
{ {
Trace.WriteLine("FNE Peer disconnected"); Log.WriteLine("FNE Peer disconnected");
Dispatcher.Invoke(() => Dispatcher.Invoke(() =>
{ {
DisableCommandControls(); DisableCommandControls();
@ -529,7 +529,7 @@ namespace dvmconsole
Codeplug.System system = Codeplug.GetSystemForChannel(channel.ChannelName); Codeplug.System system = Codeplug.GetSystemForChannel(channel.ChannelName);
if (system == null) if (system == null)
{ {
Trace.WriteLine($"{channel.ChannelName} refers to an {INVALID_SYSTEM} {channel.SystemName}. {ERR_INVALID_CODEPLUG}. {ERR_SKIPPING_AUDIO}."); Log.WriteLine($"{channel.ChannelName} refers to an {INVALID_SYSTEM} {channel.SystemName}. {ERR_INVALID_CODEPLUG}. {ERR_SKIPPING_AUDIO}.");
channel.IsSelected = false; channel.IsSelected = false;
selectedChannelsManager.RemoveSelectedChannel(channel); selectedChannelsManager.RemoveSelectedChannel(channel);
continue; continue;
@ -538,7 +538,7 @@ namespace dvmconsole
Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channel.ChannelName); Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channel.ChannelName);
if (cpgChannel == null) if (cpgChannel == null)
{ {
Trace.WriteLine($"{channel.ChannelName} refers to an {INVALID_CODEPLUG_CHANNEL}. {ERR_INVALID_CODEPLUG}. {ERR_SKIPPING_AUDIO}."); Log.WriteLine($"{channel.ChannelName} refers to an {INVALID_CODEPLUG_CHANNEL}. {ERR_INVALID_CODEPLUG}. {ERR_SKIPPING_AUDIO}.");
channel.IsSelected = false; channel.IsSelected = false;
selectedChannelsManager.RemoveSelectedChannel(channel); selectedChannelsManager.RemoveSelectedChannel(channel);
continue; continue;
@ -547,7 +547,7 @@ namespace dvmconsole
PeerSystem fne = fneSystemManager.GetFneSystem(system.Name); PeerSystem fne = fneSystemManager.GetFneSystem(system.Name);
if (fne == null) if (fne == null)
{ {
Trace.WriteLine($"{channel.ChannelName} has a {ERR_INVALID_FNE_REF}. {ERR_INVALID_CODEPLUG}. {ERR_SKIPPING_AUDIO}."); Log.WriteLine($"{channel.ChannelName} has a {ERR_INVALID_FNE_REF}. {ERR_INVALID_CODEPLUG}. {ERR_SKIPPING_AUDIO}.");
channel.IsSelected = false; channel.IsSelected = false;
selectedChannelsManager.RemoveSelectedChannel(channel); selectedChannelsManager.RemoveSelectedChannel(channel);
continue; continue;
@ -777,7 +777,7 @@ namespace dvmconsole
Codeplug.System system = Codeplug.GetSystemForChannel(channel.ChannelName); Codeplug.System system = Codeplug.GetSystemForChannel(channel.ChannelName);
if (system == null) if (system == null)
{ {
Trace.WriteLine($"{channel.ChannelName} refers to an {INVALID_SYSTEM} {channel.SystemName}. {ERR_INVALID_CODEPLUG}. {ERR_SKIPPING_AUDIO}."); Log.WriteLine($"{channel.ChannelName} refers to an {INVALID_SYSTEM} {channel.SystemName}. {ERR_INVALID_CODEPLUG}. {ERR_SKIPPING_AUDIO}.");
channel.IsSelected = false; channel.IsSelected = false;
selectedChannelsManager.RemoveSelectedChannel(channel); selectedChannelsManager.RemoveSelectedChannel(channel);
continue; continue;
@ -786,7 +786,7 @@ namespace dvmconsole
Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channel.ChannelName); Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channel.ChannelName);
if (cpgChannel == null) if (cpgChannel == null)
{ {
Trace.WriteLine($"{channel.ChannelName} refers to an {INVALID_CODEPLUG_CHANNEL}. {ERR_INVALID_CODEPLUG}. {ERR_SKIPPING_AUDIO}."); Log.WriteLine($"{channel.ChannelName} refers to an {INVALID_CODEPLUG_CHANNEL}. {ERR_INVALID_CODEPLUG}. {ERR_SKIPPING_AUDIO}.");
channel.IsSelected = false; channel.IsSelected = false;
selectedChannelsManager.RemoveSelectedChannel(channel); selectedChannelsManager.RemoveSelectedChannel(channel);
continue; continue;
@ -795,7 +795,7 @@ namespace dvmconsole
PeerSystem fne = fneSystemManager.GetFneSystem(system.Name); PeerSystem fne = fneSystemManager.GetFneSystem(system.Name);
if (fne == null) if (fne == null)
{ {
Trace.WriteLine($"{channel.ChannelName} has a {ERR_INVALID_FNE_REF}. {ERR_INVALID_CODEPLUG}. {ERR_SKIPPING_AUDIO}."); Log.WriteLine($"{channel.ChannelName} has a {ERR_INVALID_FNE_REF}. {ERR_INVALID_CODEPLUG}. {ERR_SKIPPING_AUDIO}.");
channel.IsSelected = false; channel.IsSelected = false;
selectedChannelsManager.RemoveSelectedChannel(channel); selectedChannelsManager.RemoveSelectedChannel(channel);
continue; continue;
@ -813,7 +813,7 @@ namespace dvmconsole
if (chunk.Length == PCM_SAMPLES_LENGTH) if (chunk.Length == PCM_SAMPLES_LENGTH)
P25EncodeAudioFrame(chunk, fne, channel, cpgChannel, system); P25EncodeAudioFrame(chunk, fne, channel, cpgChannel, system);
else else
Trace.WriteLine("bad sample length: " + chunk.Length); Log.WriteLine("bad sample length: " + chunk.Length);
} }
}); });
} }
@ -1091,7 +1091,7 @@ namespace dvmconsole
Codeplug.System system = Codeplug.GetSystemForChannel(channel.ChannelName); Codeplug.System system = Codeplug.GetSystemForChannel(channel.ChannelName);
if (system == null) if (system == null)
{ {
Trace.WriteLine($"{channel.ChannelName} refers to an {INVALID_SYSTEM} {channel.SystemName}. {ERR_INVALID_CODEPLUG}."); Log.WriteLine($"{channel.ChannelName} refers to an {INVALID_SYSTEM} {channel.SystemName}. {ERR_INVALID_CODEPLUG}.");
channel.IsSelected = false; channel.IsSelected = false;
selectedChannelsManager.RemoveSelectedChannel(channel); selectedChannelsManager.RemoveSelectedChannel(channel);
continue; continue;
@ -1100,7 +1100,7 @@ namespace dvmconsole
Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channel.ChannelName); Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channel.ChannelName);
if (cpgChannel == null) if (cpgChannel == null)
{ {
Trace.WriteLine($"{channel.ChannelName} refers to an {INVALID_CODEPLUG_CHANNEL}. {ERR_INVALID_CODEPLUG}."); Log.WriteLine($"{channel.ChannelName} refers to an {INVALID_CODEPLUG_CHANNEL}. {ERR_INVALID_CODEPLUG}.");
channel.IsSelected = false; channel.IsSelected = false;
selectedChannelsManager.RemoveSelectedChannel(channel); selectedChannelsManager.RemoveSelectedChannel(channel);
continue; continue;
@ -1704,7 +1704,7 @@ namespace dvmconsole
Codeplug.System system = Codeplug.GetSystemForChannel(channel.ChannelName); Codeplug.System system = Codeplug.GetSystemForChannel(channel.ChannelName);
if (system == null) if (system == null)
{ {
Trace.WriteLine($"{channel.ChannelName} refers to an {INVALID_SYSTEM} {channel.SystemName}. {ERR_INVALID_CODEPLUG}."); Log.WriteLine($"{channel.ChannelName} refers to an {INVALID_SYSTEM} {channel.SystemName}. {ERR_INVALID_CODEPLUG}.");
channel.IsSelected = false; channel.IsSelected = false;
selectedChannelsManager.RemoveSelectedChannel(channel); selectedChannelsManager.RemoveSelectedChannel(channel);
continue; continue;
@ -1713,7 +1713,7 @@ namespace dvmconsole
Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channel.ChannelName); Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channel.ChannelName);
if (cpgChannel == null) if (cpgChannel == null)
{ {
Trace.WriteLine($"{channel.ChannelName} refers to an {INVALID_CODEPLUG_CHANNEL}. {ERR_INVALID_CODEPLUG}."); Log.WriteLine($"{channel.ChannelName} refers to an {INVALID_CODEPLUG_CHANNEL}. {ERR_INVALID_CODEPLUG}.");
channel.IsSelected = false; channel.IsSelected = false;
selectedChannelsManager.RemoveSelectedChannel(channel); selectedChannelsManager.RemoveSelectedChannel(channel);
continue; continue;
@ -1722,7 +1722,7 @@ namespace dvmconsole
PeerSystem fne = fneSystemManager.GetFneSystem(system.Name); PeerSystem fne = fneSystemManager.GetFneSystem(system.Name);
if (fne == null) if (fne == null)
{ {
Trace.WriteLine($"{channel.ChannelName} has a {ERR_INVALID_FNE_REF}. {ERR_INVALID_CODEPLUG}."); Log.WriteLine($"{channel.ChannelName} has a {ERR_INVALID_FNE_REF}. {ERR_INVALID_CODEPLUG}.");
channel.IsSelected = false; channel.IsSelected = false;
selectedChannelsManager.RemoveSelectedChannel(channel); selectedChannelsManager.RemoveSelectedChannel(channel);
continue; continue;
@ -1857,7 +1857,7 @@ namespace dvmconsole
Codeplug.System system = Codeplug.GetSystemForChannel(channel.ChannelName); Codeplug.System system = Codeplug.GetSystemForChannel(channel.ChannelName);
if (system == null) if (system == null)
{ {
Trace.WriteLine($"{channel.ChannelName} refers to an {INVALID_SYSTEM} {channel.SystemName}. {ERR_INVALID_CODEPLUG}."); Log.WriteLine($"{channel.ChannelName} refers to an {INVALID_SYSTEM} {channel.SystemName}. {ERR_INVALID_CODEPLUG}.");
channel.IsSelected = false; channel.IsSelected = false;
selectedChannelsManager.RemoveSelectedChannel(channel); selectedChannelsManager.RemoveSelectedChannel(channel);
continue; continue;
@ -1866,7 +1866,7 @@ namespace dvmconsole
Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channel.ChannelName); Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channel.ChannelName);
if (cpgChannel == null) if (cpgChannel == null)
{ {
Trace.WriteLine($"{channel.ChannelName} refers to an {INVALID_CODEPLUG_CHANNEL}. {ERR_INVALID_CODEPLUG}."); Log.WriteLine($"{channel.ChannelName} refers to an {INVALID_CODEPLUG_CHANNEL}. {ERR_INVALID_CODEPLUG}.");
channel.IsSelected = false; channel.IsSelected = false;
selectedChannelsManager.RemoveSelectedChannel(channel); selectedChannelsManager.RemoveSelectedChannel(channel);
continue; continue;
@ -1981,7 +1981,7 @@ namespace dvmconsole
if (tone > 0) if (tone > 0)
{ {
MBEToneGenerator.IMBEEncodeSingleTone((ushort)tone, imbe); MBEToneGenerator.IMBEEncodeSingleTone((ushort)tone, imbe);
Trace.WriteLine($"({system.Name}) P25D: {tone} HZ TONE DETECT"); Log.WriteLine($"({system.Name}) P25D: {tone} HZ TONE DETECT");
} }
else else
{ {
@ -2112,7 +2112,7 @@ namespace dvmconsole
else else
pktSeq = peer.pktSeq(); pktSeq = peer.pktSeq();
//Trace.WriteLine($"({channel.SystemName}) P25D: Traffic *VOICE FRAME * PEER {handler.PeerId} SRC_ID {srcId} TGID {dstId} [STREAM ID {channel.txStreamId}]"); Log.WriteLine($"({channel.SystemName}) P25D: Traffic *VOICE FRAME * PEER {handler.PeerId} SRC_ID {srcId} TGID {dstId} [STREAM ID {channel.TxStreamId}]");
byte[] payload = new byte[200]; byte[] payload = new byte[200];
handler.CreateNewP25MessageHdr((byte)P25DUID.LDU1, callData, ref payload, cpgChannel.GetAlgoId(), cpgChannel.GetKeyId(), channel.mi); handler.CreateNewP25MessageHdr((byte)P25DUID.LDU1, callData, ref payload, cpgChannel.GetAlgoId(), cpgChannel.GetKeyId(), channel.mi);
@ -2130,7 +2130,7 @@ namespace dvmconsole
else else
pktSeq = peer.pktSeq(); pktSeq = peer.pktSeq();
//Trace.WriteLine($"({channel.SystemName}) P25D: Traffic *VOICE FRAME * PEER {handler.PeerId} SRC_ID {srcId} TGID {dstId} [STREAM ID {channel.txStreamId}]"); Log.WriteLine($"({channel.SystemName}) P25D: Traffic *VOICE FRAME * PEER {handler.PeerId} SRC_ID {srcId} TGID {dstId} [STREAM ID {channel.TxStreamId}]");
byte[] payload = new byte[200]; byte[] payload = new byte[200];
handler.CreateNewP25MessageHdr((byte)P25DUID.LDU2, callData, ref payload, cpgChannel.GetAlgoId(), cpgChannel.GetKeyId(), channel.mi); handler.CreateNewP25MessageHdr((byte)P25DUID.LDU2, callData, ref payload, cpgChannel.GetAlgoId(), cpgChannel.GetKeyId(), channel.mi);
@ -2214,9 +2214,7 @@ namespace dvmconsole
if (samples != null) if (samples != null)
{ {
//Log.Logger.Debug($"({Config.Name}) P25D: Traffic *VOICE FRAME * PEER {e.PeerId} SRC_ID {e.SrcId} TGID {e.DstId} VC{n} ERRS {errs} [STREAM ID {e.StreamId}]"); //Log.WriteLine($"P25D: Traffic *VOICE FRAME * PEER {e.PeerId} SRC_ID {e.SrcId} TGID {e.DstId} VC{n} [STREAM ID {e.StreamId}]");
//Log.Logger.Debug($"IMBE {FneUtils.HexDump(imbe)}");
//Trace.WriteLine($"SAMPLE BUFFER {FneUtils.HexDump(samples)}");
channel.VolumeMeterLevel = 0; channel.VolumeMeterLevel = 0;
@ -2253,7 +2251,7 @@ namespace dvmconsole
} }
catch (Exception ex) catch (Exception ex)
{ {
Trace.WriteLine($"Audio Decode Exception: {ex.Message}"); Log.WriteLine($"Audio Decode Exception: {ex.Message}");
} }
} }
@ -2263,21 +2261,21 @@ namespace dvmconsole
/// <param name="e"></param> /// <param name="e"></param>
public void KeyResponseReceived(KeyResponseEvent e) public void KeyResponseReceived(KeyResponseEvent e)
{ {
//Trace.WriteLine($"Message ID: {e.KmmKey.MessageId}"); //Log.WriteLine($"Message ID: {e.KmmKey.MessageId}");
//Trace.WriteLine($"Decrypt Info Format: {e.KmmKey.DecryptInfoFmt}"); //Log.WriteLine($"Decrypt Info Format: {e.KmmKey.DecryptInfoFmt}");
//Trace.WriteLine($"Algorithm ID: {e.KmmKey.AlgId}"); //Log.WriteLine($"Algorithm ID: {e.KmmKey.AlgId}");
//Trace.WriteLine($"Key ID: {e.KmmKey.KeyId}"); //Log.WriteLine($"Key ID: {e.KmmKey.KeyId}");
//Trace.WriteLine($"Keyset ID: {e.KmmKey.KeysetItem.KeysetId}"); //Log.WriteLine($"Keyset ID: {e.KmmKey.KeysetItem.KeysetId}");
//Trace.WriteLine($"Keyset Alg ID: {e.KmmKey.KeysetItem.AlgId}"); //Log.WriteLine($"Keyset Alg ID: {e.KmmKey.KeysetItem.AlgId}");
//Trace.WriteLine($"Keyset Key Length: {e.KmmKey.KeysetItem.KeyLength}"); //Log.WriteLine($"Keyset Key Length: {e.KmmKey.KeysetItem.KeyLength}");
//Trace.WriteLine($"Number of Keys: {e.KmmKey.KeysetItem.Keys.Count}"); //Log.WriteLine($"Number of Keys: {e.KmmKey.KeysetItem.Keys.Count}");
foreach (var key in e.KmmKey.KeysetItem.Keys) foreach (var key in e.KmmKey.KeysetItem.Keys)
{ {
//Trace.WriteLine($" Key Format: {key.KeyFormat}"); //Log.WriteLine($" Key Format: {key.KeyFormat}");
//Trace.WriteLine($" SLN: {key.Sln}"); //Log.WriteLine($" SLN: {key.Sln}");
//Trace.WriteLine($" Key ID: {key.KeyId}"); //Log.WriteLine($" Key ID: {key.KeyId}");
//Trace.WriteLine($" Key Data: {BitConverter.ToString(key.GetKey())}"); //Log.WriteLine($" Key Data: {BitConverter.ToString(key.GetKey())}");
Dispatcher.Invoke(() => Dispatcher.Invoke(() =>
{ {
@ -2370,7 +2368,7 @@ namespace dvmconsole
{ {
channel.IsReceiving = true; channel.IsReceiving = true;
slot.RxStart = pktTime; slot.RxStart = pktTime;
Trace.WriteLine($"({system.Name}) P25D: Traffic *CALL START * PEER {e.PeerId} SRC_ID {e.SrcId} TGID {e.DstId} [STREAM ID {e.StreamId}]"); Log.WriteLine($"({system.Name}) P25D: Traffic *CALL START * PEER {e.PeerId} SRC_ID {e.SrcId} TGID {e.DstId} [STREAM ID {e.StreamId}]");
FneUtils.Memset(channel.mi, 0x00, P25Defines.P25_MI_LENGTH); FneUtils.Memset(channel.mi, 0x00, P25Defines.P25_MI_LENGTH);
@ -2401,7 +2399,7 @@ namespace dvmconsole
{ {
channel.IsReceiving = false; channel.IsReceiving = false;
TimeSpan callDuration = pktTime - slot.RxStart; TimeSpan callDuration = pktTime - slot.RxStart;
Trace.WriteLine($"({system.Name}) P25D: Traffic *CALL END * PEER {e.PeerId} SRC_ID {e.SrcId} TGID {e.DstId} DUR {callDuration} [STREAM ID {e.StreamId}]"); Log.WriteLine($"({system.Name}) P25D: Traffic *CALL END * PEER {e.PeerId} SRC_ID {e.SrcId} TGID {e.DstId} DUR {callDuration} [STREAM ID {e.StreamId}]");
channel.Background = ChannelBox.BLUE_GRADIENT; channel.Background = ChannelBox.BLUE_GRADIENT;
channel.VolumeMeterLevel = 0; channel.VolumeMeterLevel = 0;
callHistoryWindow.ChannelUnkeyed(cpgChannel.Name, (int)e.SrcId); callHistoryWindow.ChannelUnkeyed(cpgChannel.Name, (int)e.SrcId);

@ -14,7 +14,6 @@
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace dvmconsole namespace dvmconsole
@ -118,6 +117,11 @@ namespace dvmconsole
/// </summary> /// </summary>
public string UserBackgroundImage { get; set; } = null; public string UserBackgroundImage { get; set; } = null;
/// <summary>
/// Flag enabling trace logging.
/// </summary>
public bool SaveTraceLog { get; set; }
/* /*
** Methods ** Methods
*/ */
@ -173,6 +177,14 @@ namespace dvmconsole
UserBackgroundImage = loadedSettings.UserBackgroundImage; UserBackgroundImage = loadedSettings.UserBackgroundImage;
SaveTraceLog = loadedSettings.SaveTraceLog;
if (SaveTraceLog)
Log.SetupTextWriter(Environment.CurrentDirectory, "dvmconsole.log");
Log.WriteLine("Digital Voice Modem - Desktop Dispatch Console");
Log.WriteLine("Copyright (c) 2025 DVMProject (https://github.com/dvmproject) Authors.");
Log.WriteLine(">> Desktop Dispatch Console");
return true; return true;
} }
@ -180,7 +192,8 @@ namespace dvmconsole
} }
catch (Exception ex) catch (Exception ex)
{ {
Trace.WriteLine($"Error loading settings: {ex.Message}"); Log.WriteLine($"Error loading settings: {ex.Message}");
Log.StackTrace(ex, false);
return false; return false;
} }
} }
@ -200,7 +213,8 @@ namespace dvmconsole
} }
catch (Exception ex) catch (Exception ex)
{ {
Trace.WriteLine($"Error saving settings: {ex.Message}"); Log.WriteLine($"Error saving settings: {ex.Message}");
Log.StackTrace(ex, false);
} }
} }

Loading…
Cancel
Save

Powered by TurnKey Linux.