diff --git a/SerialMonitor/Functions/BuiltInFunctions.cs b/SerialMonitor/Functions/BuiltInFunctions.cs
new file mode 100644
index 0000000..63c1f15
--- /dev/null
+++ b/SerialMonitor/Functions/BuiltInFunctions.cs
@@ -0,0 +1,32 @@
+//---------------------------------------------------------------------------
+//
+// Name: BuiltInFunctions.cs
+// Author: Vita Tucek
+// Created: 23.5.2024
+// License: MIT
+// Description: System functions
+//
+//---------------------------------------------------------------------------
+
+namespace SerialMonitor.Functions
+{
+ internal class BuiltInFunctions
+ {
+ public static readonly string[] Available = [nameof(Crc16)];
+
+ public static bool IsAvailable(string functionName)
+ {
+ return Available.Any(a => a.Equals(functionName, StringComparison.OrdinalIgnoreCase));
+ }
+
+ public static IFunction Get(string functionName)
+ {
+ var f = Available.First(x => x.Equals(functionName, StringComparison.OrdinalIgnoreCase));
+
+ if (f.Equals("Crc16"))
+ return Crc16.Instance;
+
+ throw new NotImplementedException("Function {functionName} is not implemented.");
+ }
+ }
+}
diff --git a/SerialMonitor/Functions/Crc16.cs b/SerialMonitor/Functions/Crc16.cs
new file mode 100644
index 0000000..20ea4c7
--- /dev/null
+++ b/SerialMonitor/Functions/Crc16.cs
@@ -0,0 +1,111 @@
+//---------------------------------------------------------------------------
+//
+// Name: Crc16.cs
+// Author: Vita Tucek
+// Created: 23.5.2024
+// License: MIT
+// Description: CRC16 (Modbus) calculation
+//
+//---------------------------------------------------------------------------
+
+namespace SerialMonitor.Functions
+{
+ public class Crc16 : IFunction
+ {
+ private static readonly ushort[] crc_table = {
+ 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
+ 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
+ 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
+ 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
+ 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
+ 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
+ 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
+ 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
+ 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
+ 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
+ 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
+ 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
+ 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
+ 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
+ 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
+ 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
+ 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
+ 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
+ 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
+ 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
+ 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
+ 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
+ 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
+ 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
+ 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
+ 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
+ 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
+ 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
+ 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
+ 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
+ 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
+ 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
+ };
+
+ public int Size => 2;
+
+ private static IFunction? _instance;
+ public static IFunction Instance => _instance ?? (_instance = new Crc16());
+
+ ///
+ /// Compute over specified length
+ ///
+ ///
+ ///
+ ///
+ public static ushort ComputeCrc(byte[] data, int length)
+ {
+ int datalen = length > data.Length ? data.Length : length;
+ ushort temp;
+ ushort crc = 0xFFFF;
+
+ for (int i = 0; i < datalen; i++)
+ {
+ temp = (ushort)((data[i] ^ crc) & 0x00FF);
+ crc >>= 8;
+ crc ^= crc_table[temp];
+ }
+
+ return crc;
+ }
+
+ public void Compute(byte[] data, int position)
+ {
+ var crc = Crc16.ComputeCrc(data, position);
+ data[position] = (byte)(crc & 0xFF);
+ data[position+1] = (byte)(crc >> 8 & 0xFF);
+ }
+
+ ///
+ /// Verify data with CRC included
+ ///
+ ///
+ ///
+ ///
+ public static bool Verify(byte[] data)
+ {
+ return Verify(data, data.Length);
+ }
+
+ ///
+ /// Verify data with CRC included
+ ///
+ ///
+ ///
+ ///
+ public static bool Verify(byte[] data, int length)
+ {
+ if (length < 3)
+ return false;
+ ushort computed = ComputeCrc(data, length - 2);
+ ushort received = (ushort)((ushort)(data[length - 1] << 8) + data[length - 2]);
+
+ return computed == received;
+ }
+ }
+}
diff --git a/SerialMonitor/Functions/IFunction.cs b/SerialMonitor/Functions/IFunction.cs
new file mode 100644
index 0000000..b8e93b8
--- /dev/null
+++ b/SerialMonitor/Functions/IFunction.cs
@@ -0,0 +1,28 @@
+//---------------------------------------------------------------------------
+//
+// Name: IFunction.cs
+// Author: Vita Tucek
+// Created: 23.5.2024
+// License: MIT
+// Description: Functions interface
+//
+//---------------------------------------------------------------------------
+
+namespace SerialMonitor.Functions
+{
+ public interface IFunction
+ {
+ ///
+ /// Data space needed in packet
+ ///
+ int Size { get; }
+ ///
+ /// Compute function over data in source from 0 to position. Result is put at position.
+ ///
+ ///
+ ///
+ void Compute(byte[] data, int position);
+
+ static IFunction Instance { get; }
+ }
+}
diff --git a/SerialMonitor/HexData.cs b/SerialMonitor/HexData.cs
new file mode 100644
index 0000000..9008457
--- /dev/null
+++ b/SerialMonitor/HexData.cs
@@ -0,0 +1,330 @@
+//---------------------------------------------------------------------------
+//
+// Name: HexData.cs
+// Author: Vita Tucek
+// Created: 23.5.2024
+// License: MIT
+// Description: Hexadecimal repeat data definition
+//
+//---------------------------------------------------------------------------
+
+using SerialMonitor.Functions;
+using System.Collections;
+using System.Globalization;
+using System.Linq.Expressions;
+using System.Text.RegularExpressions;
+
+namespace SerialMonitor
+{
+ internal class HexData
+ {
+ private static readonly Regex regWhite = new Regex("\\s+");
+ ///
+ /// Properties
+ ///
+ public ushort[]? MyProperty { get; private set; }
+ ///
+ /// Variable ID and Index in MyProperty array collection
+ ///
+ public Dictionary? Variables { get; private set; }
+ ///
+ /// Index in Property array and function ID collection
+ ///
+ public Dictionary? Functions { get; private set; }
+
+ private HexData()
+ {
+
+ }
+
+ ///
+ /// Create data instance
+ ///
+ ///
+ ///
+ public static HexData Create(string data)
+ {
+ ArgumentNullException.ThrowIfNull(data);
+
+ var hexData = new HexData();
+ var trimmed = data.Trim().Replace("0x", "", StringComparison.OrdinalIgnoreCase);
+
+ // delimited
+ if (regWhite.IsMatch(trimmed))
+ {
+ var bytes = regWhite.Split(trimmed);
+
+ var functions = bytes.Select((x, i) => new { Index = i, Item = x })
+ .Where(x => x.Item.StartsWith('@')).Select(x => new { x.Index, Item = x.Item[1..] });
+
+ if (!functions.Any())
+ {
+ hexData.MyProperty = bytes.Select(x => GetSingleByte(x)).ToArray();
+ }
+ else
+ {
+ foreach (var f in functions)
+ {
+ if (!BuiltInFunctions.IsAvailable(f.Item))
+ throw new RepeatFileException($"Function {f.Item} is not supported");
+ }
+ // create functions
+ hexData.Functions = functions.ToDictionary(x => x.Index, x => BuiltInFunctions.Get(x.Item));
+ // alocate properties (depend on function space needed)
+ hexData.MyProperty = new ushort[bytes.Length - hexData.Functions.Count + hexData.Functions.Sum(x => x.Value.Size)];
+ // fill properties
+ int move = 1;
+ for (int i = 0; i < bytes.Length; i++)
+ {
+ // function exists
+ if (!hexData.Functions.TryGetValue(i, out var func))
+ {
+ hexData.MyProperty[i] = GetSingleByte(bytes[i]);
+ move = 1;
+ }
+ else
+ {
+ hexData.MyProperty[i] = (ushort)0x200u;
+ if (func.Size > 1)
+ {
+ for(int j=1;j new { Index = i, Item = x })
+ .Where(x => IsVariable(x.Item)).ToDictionary(x => GetVariableId(x.Item), x => x.Index);
+
+ return hexData;
+ }
+
+ ///
+ /// Create data instance from data packet
+ ///
+ ///
+ ///
+ ///
+ public static HexData Create(byte[] data, int length)
+ {
+ var hexData = new HexData();
+ hexData.MyProperty = new ushort[length];
+ for (int i = 0; i < length; i++)
+ {
+ hexData.MyProperty[i] = data[i];
+ }
+
+ return hexData;
+ }
+
+ ///
+ /// Variable checker
+ ///
+ ///
+ ///
+ public static bool IsVariable(ushort data)
+ {
+ return ((data & 0x100u) == 0x100u);
+ }
+
+ ///
+ /// Return lower byte
+ ///
+ ///
+ ///
+ public static byte GetVariableId(ushort data)
+ {
+ return Convert.ToByte(data & 0xFFu);
+ }
+
+
+ ///
+ /// Function checker
+ ///
+ ///
+ ///
+ public static bool IsFunction(ushort data)
+ {
+ return ((data & 0x2FFu) == 0x200u);
+ }
+
+ ///
+ /// Function checker
+ ///
+ ///
+ ///
+ public static bool IsFunctionArea(ushort data)
+ {
+ return ((data & 0x200u) == 0x200u);
+ }
+
+ ///
+ /// Return byte representation.
+ /// Numeric value is as is.
+ /// Variable symbol $xx is converted into 0x01xx
+ ///
+ ///
+ ///
+ private static ushort GetSingleByte(string singlenumber)
+ {
+ return Convert.ToUInt16(singlenumber.StartsWith('$')
+ ? (0x100u + byte.Parse(singlenumber[1..]))
+ : byte.Parse(singlenumber, NumberStyles.HexNumber));
+ }
+ }
+
+ ///
+ /// HexData collection
+ ///
+ internal class HexDataCollection
+ {
+ private readonly Dictionary> _data = new Dictionary>(
+ new HexDataEqualityComparer());
+
+ ///
+ /// Clear collection
+ ///
+ public void Clear()
+ {
+ _data.Clear();
+ }
+ ///
+ /// Count
+ ///
+ public int Count => _data.Count;
+ ///
+ /// Add data/value data into collection
+ ///
+ ///
+ ///
+ ///
+ public bool TryAdd(HexData key, HexData value)
+ {
+ return _data.TryAdd(key, new Tuple(key, value));
+ }
+ ///
+ /// Get value by hex data
+ ///
+ ///
+ ///
+ ///
+ public bool TryGetValue(HexData key, out Tuple? value)
+ {
+ return _data.TryGetValue(key, out value);
+ }
+ ///
+ /// Get value by raw data
+ ///
+ ///
+ ///
+ ///
+ ///
+ public bool TryGetValue(byte[] data, int length, out byte[] value)
+ {
+ var hexkey = HexData.Create(data, length);
+
+ if (!_data.TryGetValue(hexkey, out var output))
+ {
+ value = [];
+ return false;
+ }
+ else
+ {
+ var hexvalue = output.Item2;
+ if (hexvalue.MyProperty == null)
+ {
+ value = [];
+ return false;
+ }
+
+ value = new byte[hexvalue.MyProperty.Length];
+
+ for (int i = 0; i < hexvalue.MyProperty.Length; i++)
+ {
+ if (HexData.IsVariable(hexvalue.MyProperty[i]))
+ {
+ // variables not defined
+ if (output.Item1.Variables == null)
+ {
+ value[i] = 0;
+ continue;
+ }
+ // get variable ID from answer
+ var id = HexData.GetVariableId(hexvalue.MyProperty[i]);
+ // look for variable in ask
+ if (!output.Item1.Variables.TryGetValue(id, out var index))
+ {
+ value[i] = 0;
+ }
+ else
+ {
+ // copy data from source from specified index
+ value[i] = data[index];
+ }
+ }
+ else if (hexvalue.Functions != null && HexData.IsFunction(hexvalue.MyProperty[i]))
+ {
+ hexvalue.Functions[i].Compute(value, i);
+ }
+ else if (HexData.IsFunctionArea(hexvalue.MyProperty[i]))
+ {
+ continue;
+ }
+ else
+ {
+ value[i] = Convert.ToByte(hexvalue.MyProperty[i] & 0xFF);
+ }
+ }
+ }
+
+ return true;
+ }
+ }
+
+ internal class HexDataEqualityComparer : IEqualityComparer
+ {
+ public bool Equals(HexData? a, HexData? b)
+ {
+ if (a?.MyProperty == null || b?.MyProperty == null)
+ return false;
+
+ if (a.MyProperty.Length != b.MyProperty.Length)
+ return false;
+
+ if (a.MyProperty?.SequenceEqual(b.MyProperty!) == true)
+ return true;
+
+ // compare with variables
+ for (int i = 0; i < a.MyProperty!.Length; i++)
+ {
+ // variable or function
+ if (((a.MyProperty[i] & 0x300u) == 0
+ && (b.MyProperty![i] & 0x300u) == 0)
+ && a.MyProperty[i] != b.MyProperty[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public int GetHashCode(HexData? p)
+ {
+ return 0;
+ }
+ }
+}
diff --git a/SerialMonitor/Program.cs b/SerialMonitor/Program.cs
index 2fd3be0..4799208 100644
--- a/SerialMonitor/Program.cs
+++ b/SerialMonitor/Program.cs
@@ -18,18 +18,25 @@ namespace SerialMonitor
class Program
{
static readonly ArgumentCollection arguments = new ArgumentCollection(new string[] { "baudrate", "parity", "databits", "stopbits",
- "repeatfile", "logfile", "logincomingonly", "showascii", "notime", "gaptolerance", "continuousmode", "nogui" });
+ "repeatfile", "logfile", "logincomingonly", "showascii", "notime", "gaptolerance", "continuousmode", "nogui", "service" });
static readonly string? version = System.Reflection.Assembly.GetEntryAssembly()?.GetName().Version?.ToString(3);
static long lastTimeReceved = 0;
static bool repeaterEnabled = false;
static bool repeaterUseHex = false;
- static readonly Dictionary repeaterMap = new Dictionary();
+ static readonly Dictionary repeaterStringMap = new Dictionary();
+ static readonly HexDataCollection repeaterHexMap = new HexDataCollection();
static bool logfile = false;
static bool logincomingonly = false;
- static string logFilename = "";
+ // default log file name
+ static readonly string logSystemFilename = logDataFilename = Path.Combine(OperatingSystem.IsLinux() ? "/var/log/serialmonitor" : Directory.GetCurrentDirectory(), "serialmonitor.log");
+ static string logDataFilename = $"log_{DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss")}.txt";
static int gapTolerance = 0;
static bool gapToleranceEnable = false;
static bool continuousMode = false;
+ static bool serviceMode = false;
+ static FileSystemWatcher? _watcher;
+ static readonly TraceSource trace = new TraceSource("System");
+ static readonly TraceSource traceData = new TraceSource("Data");
///
/// Flag for stop printing communication data. Log into file will continue.
///
@@ -58,7 +65,7 @@ class Program
static void Main(string[] args)
{
DateTime lastTry = DateTime.MinValue;
- setting.Port = System.OperatingSystem.IsWindows() ? "COM1" : "/dev/ttyS1";
+ setting.Port = OperatingSystem.IsWindows() ? "COM1" : "/dev/ttyS1";
if (args.Length > 0)
{
@@ -90,16 +97,26 @@ static void Main(string[] args)
}
}
+
+ trace.Switch = new SourceSwitch("SourceSwitch", "All");
+ trace.Switch.Level = SourceLevels.All;
+ trace.Listeners.Clear();
+ trace.Listeners.Add(new System.Diagnostics.TextWriterTraceListener(logSystemFilename, "System"));
+
// parse commandline arguments
arguments.Parse(args);
- continuousMode = arguments.GetArgument("continuousmode").Enabled || arguments.GetArgument("nogui").Enabled;
+ continuousMode = arguments.GetArgument("continuousmode").Enabled
+ || arguments.GetArgument("nogui").Enabled
+ || arguments.GetArgument("service").Enabled;
+
+ serviceMode = arguments.GetArgument("service").Enabled;
if (!continuousMode)
UI.Init();
- else
+ else if (!serviceMode)
ConsoleWriteLineNoTrace($"SerialMonitor v.{version}");
-
+
setting.BaudRate = 9600;
setting.Parity = Parity.None;
setting.DataBits = 8;
@@ -149,7 +166,7 @@ static void Main(string[] args)
port.PortName = setting.Port;
port.BaudRate = setting.BaudRate;
- port.Parity = setting.Parity;
+ port.Parity = setting.Parity;
port.DataBits = setting.DataBits;
port.StopBits = setting.StopBits;
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
@@ -177,11 +194,11 @@ static void Main(string[] args)
{
if (arg.Parameter.Length == 0)
{
- logFilename = "log_" + DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss") + ".txt";
- ConsoleWriteLine("Warning: Log file name not specified. Used " + logFilename);
+ if (!serviceMode)
+ ConsoleWriteLine(TraceEventType.Warning, "Warning: Log file name not specified. Used " + logDataFilename);
}
else
- logFilename = arg.Parameter;
+ logDataFilename = arg.Parameter;
logfile = true;
}
@@ -191,9 +208,8 @@ static void Main(string[] args)
if (!logfile)
{
logfile = true;
- logFilename = "log_" + DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss") + ".txt";
- ConsoleWriteLine("Warning: Parameter logfile not specified. Log enabled to the file: " + logFilename);
- logfile = true;
+ if (!serviceMode)
+ ConsoleWriteLine(TraceEventType.Warning, "Warning: Parameter logfile not specified. Log enabled to the file: " + logDataFilename);
}
logincomingonly = true;
}
@@ -205,44 +221,49 @@ static void Main(string[] args)
_ = int.TryParse(arg.Parameter, out gapTolerance);
if (gapTolerance == 0)
- ConsoleWriteLine("Warning: Parameter gaptolerance has invalid argument. Gap tolerance must be greater than zero.");
+ ConsoleWriteLine(TraceEventType.Warning, "Warning: Parameter gaptolerance has invalid argument. Gap tolerance must be greater than zero.");
else
gapToleranceEnable = true;
}
-
if (logfile)
{
//check path
- string? path = System.IO.Path.GetDirectoryName(logFilename);
+ string? path = Path.GetDirectoryName(logDataFilename);
if (path?.Length > 0)
{
- if (!System.IO.Directory.Exists(path))
+ if (!Directory.Exists(path))
try
{
- System.IO.Directory.CreateDirectory(path);
+ Directory.CreateDirectory(path);
}
catch (Exception ex)
{
- ConsoleWriteLine($"Warning: Cannot create directory {path}. {ex.Message}");
+ ConsoleWriteLine(TraceEventType.Warning, $"Warning: Cannot create directory {path}. {ex.Message}");
}
}
else
{
- logFilename = System.IO.Directory.GetCurrentDirectory() + "\\" + logFilename;
- }
+ if (OperatingSystem.IsLinux())
+ logDataFilename = Path.Combine("/var/log/serialmonitor", logDataFilename);
+ else
+ logDataFilename = Path.Combine(Directory.GetCurrentDirectory(), logDataFilename);
+ }
- if (!IsFileNameValid(logFilename))
+ if (!IsFileNameValid(logDataFilename))
{
- ConsoleWriteLine("\nPress [Enter] to exit");
- Console.ReadLine();
-
+ if (!serviceMode)
+ {
+ ConsoleWriteLine("\nPress [Enter] to exit");
+ Console.ReadLine();
+ }
return;
}
//assign file to listener
- if (Trace.Listeners["Default"] is DefaultTraceListener listener)
- listener.LogFileName = logFilename;
+ traceData.Switch = new SourceSwitch("SourceSwitch", "All");
+ traceData.Listeners.Clear();
+ traceData.Listeners.Add(new System.Diagnostics.TextWriterTraceListener(logDataFilename, "Data"));
}
/*
@@ -260,15 +281,17 @@ static void Main(string[] args)
{
if (!IsFileNameValid(repeatfile.Parameter))
{
- ConsoleWriteLine("\nPress [Enter] to exit");
- Console.ReadLine();
-
+ if (!serviceMode)
+ {
+ ConsoleWriteLine("\nPress [Enter] to exit");
+ Console.ReadLine();
+ }
return;
}
PrepareRepeatFile(repeatfile.Parameter);
}
-
- ConsoleWriteLine($"Opening port {port.PortName}: baudrate={port.BaudRate}b/s, parity={port.Parity}, databits={port.DataBits}, stopbits={port.StopBits}");
+
+ ConsoleWriteLine(TraceEventType.Information, $"Opening port {port.PortName}: baudrate={port.BaudRate}b/s, parity={port.Parity}, databits={port.DataBits}, stopbits={port.StopBits}");
bool exit = false;
@@ -287,6 +310,7 @@ static void Main(string[] args)
UI.FileHistory.AddRange(fileList);
fileList = null;
}
+ trace.Flush();
if (continuousMode)
{
@@ -301,20 +325,21 @@ static void Main(string[] args)
}
}
else
- {
+ {
UI.PrintAsHexToLogView = !setting.ShowAscii;
UI.ActionHelp = () => { PrintHelp(); };
UI.ActionPrint = (print) => { pausePrint = !print; };
UI.ActionPrintAsHex = (hex) => { setting.ShowAscii = !hex; };
UI.ActionOpenClose = (close) => { pauseConnection = close; if (close) port.Close(); UI.SetPortStatus(port); UI.SetPinStatus(port); };
- UI.ActionSend = (data) => { UserDataSend(port,data); };
+ UI.ActionSend = (data) => { UserDataSend(port, data); };
UI.ActionSendFile = (file) => { UserDataSendFile(port, file); };
UI.ActionRts = () => { port.RtsEnable = !port.RtsEnable; UI.SetPortStatus(port); UI.SetPinStatus(port); };
UI.ActionDtr = () => { port.DtrEnable = !port.DtrEnable; UI.SetPortStatus(port); UI.SetPinStatus(port); };
UI.ActionCommand = (text) => { ProcessCommand(text); };
UI.ActionSettingLoad = () => { return setting; };
- UI.ActionSettingSave = (setting) => {
- if(port.PortName != setting.Port || port.BaudRate != setting.BaudRate)
+ UI.ActionSettingSave = (setting) =>
+ {
+ if (port.PortName != setting.Port || port.BaudRate != setting.BaudRate)
{
if (port.IsOpen)
{
@@ -330,9 +355,9 @@ static void Main(string[] args)
port.PortName = setting.Port;
port.BaudRate = setting.BaudRate;
}
- UI.SetPortStatus(port);
+ UI.SetPortStatus(port);
}
-
+
return Config.SaveSetting(setting.Port, setting.BaudRate, setting.ShowTime, setting.ShowTimeGap, setting.ShowSentData, setting.ShowAscii);
};
@@ -356,6 +381,10 @@ static void Main(string[] args)
Exit();
}
+
+ _watcher?.Dispose();
+ trace.Flush();
+ trace.Close();
}
///
@@ -386,7 +415,7 @@ private static void ProcessCommand(string? text)
}
///
- /// Send tada typed by user
+ /// Send data typed by user
///
///
///
@@ -402,12 +431,12 @@ private static bool UserDataSend(SerialPort port, string? line)
bool hex = false;
byte[] data;
- if (line.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase))
+ if (line.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
hex = true;
if (hex)
{
- string prepared = line.Replace("0x", "");
+ string prepared = line.Replace("0x", "", StringComparison.OrdinalIgnoreCase);
Regex reg = new Regex("^([0-9A-Fa-f]{1,2}\\s*)+$");
if (!reg.IsMatch(prepared))
@@ -544,6 +573,10 @@ private static void PortConnectInfinite(SerialPort port)
ConsoleWrite(new string(' ', waitText.Length + 5));
ConsoleCursorLeftReset();
}
+ else
+ {
+ ConsoleWriteLine($" Port {port.PortName} opened");
+ }
// TODO:
/*
@@ -596,7 +629,7 @@ private static void PortClose(SerialPort port)
if (port.IsOpen)
port.Close();
}
-
+
///
/// Validating file name(path)
///
@@ -604,7 +637,7 @@ private static void PortClose(SerialPort port)
///
private static bool IsFileNameValid(string filePath)
{
- foreach (char c in System.IO.Path.GetInvalidPathChars())
+ foreach (char c in Path.GetInvalidPathChars())
{
if (filePath.Contains(c.ToString()))
{
@@ -614,9 +647,9 @@ private static bool IsFileNameValid(string filePath)
}
}
- foreach (char c in System.IO.Path.GetInvalidFileNameChars())
+ foreach (char c in Path.GetInvalidFileNameChars())
{
- if (System.IO.Path.GetFileName(filePath).Contains(c.ToString()))
+ if (Path.GetFileName(filePath).Contains(c.ToString()))
{
ConsoleWriteError($"File name {filePath} contains invalid character [{c}]. Enter right file name.");
@@ -627,6 +660,29 @@ private static bool IsFileNameValid(string filePath)
return true;
}
+ ///
+ /// Event of changed repeat file
+ ///
+ ///
+ ///
+ ///
+ private static void OnRepeatFileChanged(object source, FileSystemEventArgs e)
+ {
+ try
+ {
+ _watcher!.EnableRaisingEvents = false;
+
+ ConsoleWriteLine($"File {e.FullPath} was changed. Repeat file will be read now in a while...");
+ Thread.Sleep(500);
+ PrepareRepeatFile(e.FullPath);
+ }
+
+ finally
+ {
+ _watcher!.EnableRaisingEvents = true;
+ }
+ }
+
///
/// Procedure to read data from repeat file
///
@@ -639,99 +695,132 @@ private static void PrepareRepeatFile(string fileName)
{
try
{
+ if (_watcher == null)
+ {
+ var directory = Path.GetDirectoryName(fileName);
+ if (string.IsNullOrEmpty(directory))
+ directory = Directory.GetCurrentDirectory();
+ var fileNameFiltered = Path.GetFileName(fileName);
+ _watcher = new FileSystemWatcher(directory, fileNameFiltered);
+ _watcher.NotifyFilter = NotifyFilters.LastWrite;
+ _watcher.Changed += new FileSystemEventHandler(OnRepeatFileChanged);
+ _watcher.EnableRaisingEvents = true;
+ }
string[] lines = File.ReadAllLines(fileName);
if (lines.Length == 0)
- ConsoleWriteError($"Zero lines in file {fileName}");
+ {
+ ConsoleWriteError($"No line in file {fileName}");
+ return;
+ }
ConsoleWriteLine($"File {fileName} opened and {lines.Length} lines has been read");
- repeaterMap.Clear();
+ repeaterStringMap.Clear();
+ repeaterHexMap.Clear();
+
+ // check format file
+ // first line without comment
+ string? startLine = lines.FirstOrDefault(x => !x.Trim().StartsWith('#'));
+ if (startLine == null)
+ {
+ ConsoleWriteError($"No line without comment ('#') in file {fileName}");
+ return;
+ }
- //check format file
- string startLine = lines[0];
int linesWithData = 0;
- string ask = "";
- Regex reg = new Regex("^(0x[0-9A-Fa-f]{1,2}\\s*)+$");
+ Regex reg = new Regex(@"^(?!\s*$)(?:(0x[0-9A-Fa-f]{1,2})*|(\$\d+)*|(\@.+)*| )+$");
// match hex string
if (reg.IsMatch(startLine))
{
ConsoleWriteLine("First line corresponds hex format. File will be read and packets compared as HEX.");
- Regex regWhite = new Regex("\\s+");
-
+ HexData ask = HexData.Create(startLine);
+ ++linesWithData;
//check whole file
- for (int i = 0; i < lines.Length; i++)
+ for (int i = 1; i < lines.Length; i++)
{
- if (lines[i].Trim().Length > 0)
+ var line = lines[i].Trim();
+ // empty or commented line
+ if (line.Length == 0 || line.StartsWith('#'))
+ continue;
+
+ if (reg.IsMatch(line))
{
- if (reg.IsMatch(lines[i]))
- {
- if (++linesWithData % 2 == 1)
- ask = regWhite.Replace(lines[i].Replace("0x", ""), "");
- else
- repeaterMap.Add(ask, regWhite.Replace(lines[i].Replace("0x", ""), ""));
- }
+ var data = HexData.Create(line);
+
+ if (++linesWithData % 2 == 1)
+ ask = data;
else
- {
- throw new RepeatFileException("Line {0} not coresponds to hex format.", i);
- }
+ repeaterHexMap.TryAdd(ask, data);
+ }
+ else
+ {
+ throw new RepeatFileException("Line {0} not coresponds to hex format.", i);
}
}
repeaterUseHex = true;
+ ConsoleWriteLine($"{repeaterHexMap.Count} pairs ask/answer ready");
}
else
{
reg = new Regex("^([0-9A-Fa-f])+$");
// match hex string
if (reg.IsMatch(startLine))
- {
+ {
ConsoleWriteLine("First line corresponds hex format. File will be read and packets compared as HEX.");
-
+ HexData ask = HexData.Create(startLine);
//check whole file
for (int i = 0; i < lines.Length; i++)
{
- if (lines[i].Trim().Length > 0)
+ var line = lines[i].Trim();
+ // empty or commented line
+ if (line.Length == 0 || line.StartsWith('#'))
+ continue;
+
+ if (line.Length % 2 == 1)
+ {
+ throw new RepeatFileException("Line {0} has odd number of characters.", i);
+ }
+
+ if (reg.IsMatch(line))
{
- if (lines[i].Length % 2 == 1)
- {
- throw new RepeatFileException("Line {0} has odd number of characters.", i);
- }
-
- if (reg.IsMatch(lines[i]))
- {
- if (++linesWithData % 2 == 1)
- ask = lines[i];
- else
- repeaterMap.Add(ask, lines[i]);
- }
+ var data = HexData.Create(line);
+ if (++linesWithData % 2 == 1)
+ ask = data;
else
- {
- throw new RepeatFileException("Line {0} not coresponds to hex format.", i);
- }
+ repeaterHexMap.TryAdd(ask, data);
+ }
+ else
+ {
+ throw new RepeatFileException("Line {0} not coresponds to hex format.", i);
}
}
repeaterUseHex = true;
+ ConsoleWriteLine($"{repeaterHexMap.Count} pairs ask/answer ready");
}
else
{
// non hex string
ConsoleWriteLine("First line not corresponds hex format. File will be read and packets compared as ASCII.");
-
+ string ask = string.Empty;
//check whole file
for (int i = 0; i < lines.Length; i++)
{
- if (lines[i].Trim().Length > 0)
- {
- if (++linesWithData % 2 == 1)
- ask = lines[i];
- else
- repeaterMap.Add(ask, lines[i]);
- }
+ var line = lines[i].Trim();
+ // empty or commented line
+ if (line.Length == 0 || line.StartsWith('#'))
+ continue;
+ if (++linesWithData % 2 == 1)
+ ask = line;
+ else
+ repeaterStringMap.TryAdd(ask, line);
}
+
+ ConsoleWriteLine($"{repeaterStringMap.Count} pairs ask/answer ready");
}
}
@@ -739,13 +828,16 @@ private static void PrepareRepeatFile(string fileName)
ConsoleWriteError($"Odd number of lines in file {fileName} with code. One line ask, one line answer.");
repeaterEnabled = true;
-
- ConsoleWriteLine($"{repeaterMap.Count} pairs ask/answer ready");
+ }
+ catch (FormatException ex)
+ {
+ ConsoleWriteError($"Format mismatch in file {fileName}");
+ ConsoleWriteError(ex.Message);
}
catch (Exception ex)
{
ConsoleWriteError($"Cannot read file {fileName}");
- ConsoleWriteError(ex.ToString());
+ ConsoleWriteError(ex.Message);
}
}
}
@@ -779,7 +871,7 @@ static void port_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
///
///
static void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
- {
+ {
SerialPort port = ((SerialPort)sender);
int byteCount;
int cycle = 0;
@@ -795,7 +887,7 @@ static void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
if (incoming.Length < byteCount)
incoming = new byte[byteCount];
-
+
port.Read(incoming, 0, byteCount);
}
catch (Exception ex)
@@ -807,7 +899,7 @@ static void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
TimeSpan time = DateTime.Now.TimeOfDay;
bool applyGapTolerance = false;
- //print time since last receive only if not disabled
+ // print time since last receive only if not disabled
if (lastTimeReceved > 0)
{
double sinceLastReceive = ((double)(time.Ticks - lastTimeReceved) / 10000);
@@ -817,20 +909,20 @@ static void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
ConsoleWriteCommunication(ConsoleColor.Magenta, "\n+" + sinceLastReceive.ToString("F3") + " ms");
}
- //Write to output
+ // Write to output
string line = "";
if (setting.ShowAscii)
{
if (!setting.ShowTime || applyGapTolerance)
- line = ASCIIEncoding.ASCII.GetString(incoming,0,byteCount);
+ line = ASCIIEncoding.ASCII.GetString(incoming, 0, byteCount);
else
line = time.ToString() + " " + Encoding.ASCII.GetString(incoming, 0, byteCount);
}
else
{
if (!setting.ShowTime || applyGapTolerance)
- line = string.Join(" ", incoming.Take(byteCount).Select(x=> $"0x{x:X2}"));
+ line = string.Join(" ", incoming.Take(byteCount).Select(x => $"0x{x:X2}"));
else
line = time.ToString() + " " + string.Join(" ", incoming.Take(byteCount).Select(x => $"0x{x:X2}"));
}
@@ -874,17 +966,11 @@ static void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (repeaterUseHex)
{
- string ask = string.Join("", incoming.Take(byteCount).Select(x => x.ToString("X2")));
+ //HexData ask = string.Join("", incoming.Take(byteCount).Select(x => x.ToString("X2")));
- if (repeaterMap.ContainsKey(ask))
+ if (repeaterHexMap.TryGetValue(incoming, byteCount, out var answer))
{
- string answer = repeaterMap[ask];
- byte[] data = new byte[answer.Length / 2];
-
- for (int i = 0; i < data.Length; i++)
- {
- data[i] = Convert.ToByte(answer.Substring(2 * i, 2), 16);
- }
+ var data = answer;
if (!setting.ShowTime)
ConsoleWriteCommunication(ConsoleColor.Green, string.Join("\n ", Array.ConvertAll(data, x => $"0x{x:X2}")));
@@ -895,16 +981,16 @@ static void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
}
else
{
- ConsoleWriteLine("Repeater: Unknown ask");
+ ConsoleWriteLineNoTrace("\nRepeater: Unknown ask");
}
}
else
{
- string ask = ASCIIEncoding.ASCII.GetString(incoming,0,byteCount);
+ string ask = ASCIIEncoding.ASCII.GetString(incoming, 0, byteCount);
- if (repeaterMap.ContainsKey(ask))
+ if (repeaterStringMap.ContainsKey(ask))
{
- string answer = repeaterMap[ask];
+ string answer = repeaterStringMap[ask];
if (!setting.ShowTime)
ConsoleWriteCommunication(ConsoleColor.Green, "\n" + answer);
@@ -915,7 +1001,7 @@ static void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
}
else
{
- ConsoleWriteLine("Repeater: Unknown ask");
+ ConsoleWriteLineNoTrace("Repeater: Unknown ask");
}
}
@@ -928,7 +1014,7 @@ static void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
///
private static void ConsoleWriteError(string text)
{
- ConsoleWriteLine(ConsoleColor.Red, text);
+ ConsoleWriteLine(TraceEventType.Error, text);
}
///
@@ -939,11 +1025,8 @@ private static void ConsoleWrite(string message)
{
if (!continuousMode)
UI.Write(message);
- else
+ else if (!serviceMode)
Console.Write(message);
-
- if (logfile && !logincomingonly)
- Trace.Write(message);
}
///
@@ -956,15 +1039,16 @@ private static void ConsoleWriteLine(string message)
{
UI.WriteLine(message);
}
- else
+ else if (!serviceMode)
{
if (Console.CursorLeft > 0)
Console.WriteLine("");
Console.WriteLine(message);
}
-
- if (logfile && !logincomingonly)
- Trace.WriteLine(string.Format(message));
+ else
+ {
+ Trace(TraceEventType.Information, message);
+ }
}
///
@@ -972,21 +1056,30 @@ private static void ConsoleWriteLine(string message)
///
///
///
- private static void ConsoleWriteLine(ConsoleColor color, string message)
+ private static void ConsoleWriteLine(TraceEventType eventType, string message)
{
if (!continuousMode)
{
- UI.WriteLine(message, color);
+ UI.WriteLine(message,
+ eventType == TraceEventType.Critical ? ConsoleColor.DarkRed
+ : eventType == TraceEventType.Error ? ConsoleColor.Red
+ : eventType == TraceEventType.Warning ? ConsoleColor.Yellow
+ : ConsoleColor.White
+ );
}
- else
+ else if (!serviceMode || eventType <= TraceEventType.Information)
{
- Console.ForegroundColor = color;
+ Console.ForegroundColor = eventType == TraceEventType.Critical ? ConsoleColor.DarkRed
+ : eventType == TraceEventType.Error ? ConsoleColor.Red
+ : eventType == TraceEventType.Warning ? ConsoleColor.Yellow
+ : ConsoleColor.White;
Console.WriteLine(message);
Console.ResetColor();
}
-
- if (logfile && !logincomingonly)
- Trace.WriteLine(string.Format(message));
+ else
+ {
+ Trace(eventType, message);
+ }
}
///
@@ -997,7 +1090,7 @@ private static void ConsoleWriteLineNoTrace(string message)
{
if (!continuousMode)
UI.WriteLine(message);
- else
+ else if (!serviceMode)
Console.WriteLine(message);
}
@@ -1012,7 +1105,7 @@ private static void ConsoleWriteLineNoTrace(ConsoleColor color, string message)
{
UI.WriteLine(message, color);
}
- else
+ else if (!serviceMode)
{
Console.ForegroundColor = color;
Console.WriteLine(message);
@@ -1033,7 +1126,7 @@ private static void ConsoleWriteCommunication(ConsoleColor color, string message
{
UI.Write(message, color);
}
- else
+ else if (!serviceMode)
{
Console.ForegroundColor = color;
ConsoleWrite(message);
@@ -1042,7 +1135,11 @@ private static void ConsoleWriteCommunication(ConsoleColor color, string message
}
if (logfile)
- Trace.Write(string.Format(message));
+ {
+ // log all received data (yellov color) or others if enabled
+ if (!logincomingonly || color == ConsoleColor.Yellow)
+ TraceData(string.Format(message));
+ }
}
///
@@ -1052,9 +1149,13 @@ private static void ConsoleWriteCommunication(ConsoleColor color, string message
private static void ConsoleCursorLeft(int moveBy)
{
if (continuousMode)
- Console.CursorLeft += moveBy;
- //else
- // Cinout.CursorLeft(moveBy);
+ {
+ var newposition = Console.CursorLeft + moveBy;
+ if (newposition > 0 && newposition < Console.BufferWidth)
+ {
+ Console.CursorLeft += moveBy;
+ }
+ }
}
///
@@ -1064,8 +1165,6 @@ private static void ConsoleCursorLeftReset()
{
if (continuousMode)
Console.CursorLeft = 0;
- //else
- // Cinout.CursorLeftReset();
}
///
@@ -1087,7 +1186,8 @@ private static void PrintHelp()
ConsoleWriteLineNoTrace("-showascii: communication would be show in ASCII format (otherwise HEX is used)");
ConsoleWriteLineNoTrace("-notime: time information about incoming data would not be printed");
ConsoleWriteLineNoTrace("-gaptolerance {{time gap in ms}}: messages received within specified time gap will be printed together");
- ConsoleWriteLineNoTrace("-nogui: start program in normal console mode (scrolling list). Not with primitive text GUI");
+ ConsoleWriteLineNoTrace("-nogui: start program in normal console mode (scrolling list). Not with text GUI");
+ ConsoleWriteLineNoTrace("-service: start program in normal console mode with minimal verbose");
ConsoleWriteLineNoTrace("");
ConsoleWriteLineNoTrace("Example: serialmonitor COM1");
@@ -1100,18 +1200,19 @@ private static void PrintHelp()
ConsoleWriteLineNoTrace("F2: setup program");
ConsoleWriteLineNoTrace("F3: toggle between data print format (HEX / ASCII)");
ConsoleWriteLineNoTrace("F4: pause/resume connection to serial port");
- ConsoleWriteLineNoTrace("F5: send specified data (in HEX format if data start with 0x otherwise ASCII is send)");
- ConsoleWriteLineNoTrace("F6: send specified file)");
+ ConsoleWriteLineNoTrace("F5: send a data (in HEX format if data start with 0x otherwise ASCII is send)");
+ ConsoleWriteLineNoTrace("F6: send a file");
- ConsoleWriteLineNoTrace("F10: program exit");
- ConsoleWriteLineNoTrace("F11: toggle RTS pin");
- ConsoleWriteLineNoTrace("F12: toggle DTR pin");
+ ConsoleWriteLineNoTrace("^Q: program exit");
+ ConsoleWriteLineNoTrace("F11 or ^1: toggle RTS pin");
+ ConsoleWriteLineNoTrace("F12 or ^2: toggle DTR pin");
ConsoleWriteLineNoTrace("^P: pause / resume print on screen");
ConsoleWriteLineNoTrace("");
ConsoleWriteLineNoTrace("In program commands:");
ConsoleWriteLineNoTrace("help: print help");
ConsoleWriteLineNoTrace("send : send message");
+ ConsoleWriteLineNoTrace("exit: exit the program");
if (continuousMode)
{
@@ -1140,7 +1241,39 @@ private static void Exit()
Config.SaveHistory(UI.CommandHistory);
Config.SaveFileList(UI.FileHistory);
+ trace.Close();
UI.Exit();
}
+
+ ///
+ /// Log system message
+ ///
+ ///
+ ///
+ private static void Trace(TraceEventType eventType, string message)
+ {
+ if (trace.Switch.ShouldTrace(eventType))
+ {
+ string tracemessage = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.FFF")}\t[{eventType}]\t{string.Format(message)}";
+ foreach (TraceListener listener in trace.Listeners)
+ {
+ listener.WriteLine(tracemessage);
+ listener.Flush();
+ }
+ }
+ }
+
+ ///
+ /// Log data message
+ ///
+ ///
+ private static void TraceData(string message)
+ {
+ foreach (TraceListener listener in traceData.Listeners)
+ {
+ listener.WriteLine(message);
+ listener.Flush();
+ }
+ }
}
}
diff --git a/SerialMonitor/Properties/PublishProfiles/Linux64.pubxml b/SerialMonitor/Properties/PublishProfiles/Linux64.pubxml
new file mode 100644
index 0000000..2c7ef13
--- /dev/null
+++ b/SerialMonitor/Properties/PublishProfiles/Linux64.pubxml
@@ -0,0 +1,16 @@
+
+
+
+
+ Release
+ Any CPU
+ bin\Release\net8.0\publish\linux-x64\
+ FileSystem
+ net8.0
+ linux-x64
+ true
+ false
+
+
\ No newline at end of file
diff --git a/SerialMonitor/Properties/PublishProfiles/LinuxArm.pubxml b/SerialMonitor/Properties/PublishProfiles/LinuxArm.pubxml
new file mode 100644
index 0000000..f0ebb30
--- /dev/null
+++ b/SerialMonitor/Properties/PublishProfiles/LinuxArm.pubxml
@@ -0,0 +1,17 @@
+
+
+
+
+ Release
+ Any CPU
+ bin\Release\net8.0\publish\linux-arm\
+ FileSystem
+ net8.0
+ linux-arm
+ true
+ false
+ false
+
+
\ No newline at end of file
diff --git a/SerialMonitor/Properties/PublishProfiles/LinuxArm64.pubxml b/SerialMonitor/Properties/PublishProfiles/LinuxArm64.pubxml
new file mode 100644
index 0000000..ac82eb9
--- /dev/null
+++ b/SerialMonitor/Properties/PublishProfiles/LinuxArm64.pubxml
@@ -0,0 +1,17 @@
+
+
+
+
+ Release
+ Any CPU
+ bin\Release\net8.0\publish\linux-arm64\
+ FileSystem
+ net8.0
+ linux-arm64
+ true
+ false
+ false
+
+
\ No newline at end of file
diff --git a/SerialMonitor/Properties/PublishProfiles/Win64.pubxml b/SerialMonitor/Properties/PublishProfiles/Win64.pubxml
new file mode 100644
index 0000000..a2b937a
--- /dev/null
+++ b/SerialMonitor/Properties/PublishProfiles/Win64.pubxml
@@ -0,0 +1,16 @@
+
+
+
+
+ Release
+ Any CPU
+ bin\Release\net6.0\publish\win-x64\
+ FileSystem
+ net8.0
+ win-x64
+ false
+ false
+
+
diff --git a/SerialMonitor/Resources/changelog.Debian b/SerialMonitor/Resources/changelog.Debian
new file mode 100644
index 0000000..706b711
--- /dev/null
+++ b/SerialMonitor/Resources/changelog.Debian
@@ -0,0 +1,6 @@
+serialmonitor (2.1.0-1) stretch; urgency=medium
+
+ * Possibility to use variables in repeat file
+ * Fix loading repeat file
+
+ -- VitaTucek Mon, 27 May 2024 12:00:00 +0200
\ No newline at end of file
diff --git a/SerialMonitor/Resources/control b/SerialMonitor/Resources/control
new file mode 100644
index 0000000..8ffed3f
--- /dev/null
+++ b/SerialMonitor/Resources/control
@@ -0,0 +1,10 @@
+Package: serialmonitor
+Version: 2.1.0-1
+Section: utils
+Priority: standard
+Homepage: https://github.com/docbender/SerialMonitor
+Maintainer: Vita Tucek
+Description: Utility for communication over serial port
+ This utility could be use for monitor communication
+ at the serial port. It allow send user data, files and also
+ act as serial device emulator.
diff --git a/SerialMonitor/Resources/copyright b/SerialMonitor/Resources/copyright
new file mode 100644
index 0000000..4377281
--- /dev/null
+++ b/SerialMonitor/Resources/copyright
@@ -0,0 +1,4 @@
+serialmonitor
+
+Copyright: 2024 Vita Tucek
+
diff --git a/SerialMonitor/Resources/postinst b/SerialMonitor/Resources/postinst
new file mode 100644
index 0000000..5d11904
--- /dev/null
+++ b/SerialMonitor/Resources/postinst
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+ln -s /usr/share/serialmonitor/SerialMonitor /usr/bin/serialmonitor
diff --git a/SerialMonitor/Resources/postinst.service b/SerialMonitor/Resources/postinst.service
new file mode 100644
index 0000000..e342576
--- /dev/null
+++ b/SerialMonitor/Resources/postinst.service
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+ln -s /usr/share/serialmonitor/SerialMonitor /usr/bin/serialmonitor
+systemctl enable serialmonitor
+
diff --git a/SerialMonitor/Resources/postrm b/SerialMonitor/Resources/postrm
new file mode 100644
index 0000000..05a7907
--- /dev/null
+++ b/SerialMonitor/Resources/postrm
@@ -0,0 +1,2 @@
+#!/bin/bash
+
diff --git a/SerialMonitor/Resources/prerm b/SerialMonitor/Resources/prerm
new file mode 100644
index 0000000..85e3e26
--- /dev/null
+++ b/SerialMonitor/Resources/prerm
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+CMD=$1
+if [ $CMD=="remove" ] || [ $CMD=="upgrade" ]; then
+ rm /usr/bin/serialmonitor
+fi
diff --git a/SerialMonitor/Resources/prerm.service b/SerialMonitor/Resources/prerm.service
new file mode 100644
index 0000000..e163f2c
--- /dev/null
+++ b/SerialMonitor/Resources/prerm.service
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+CMD=$1
+if [ $CMD=="remove" ] || [ $CMD=="upgrade" ]; then
+ systemctl stop serialmonitor
+ rm /usr/bin/serialmonitor
+fi
diff --git a/SerialMonitor/SerialMonitor.csproj b/SerialMonitor/SerialMonitor.csproj
index a911b08..1d19cc5 100644
--- a/SerialMonitor/SerialMonitor.csproj
+++ b/SerialMonitor/SerialMonitor.csproj
@@ -2,20 +2,106 @@
Exe
- net6.0
+ net8.0
enable
enable
- 2.0.0.0
- 2.0.0.0
- 2.0.0.0.myversion
+ 2.1.0.0
+ 2.1.0.24173
+ 2.0.0.0.myversion
+ $(AssemblyName)
+ none
+ True
-
-
+
+ portable
+
+
+
+ portable
+
-
-
+
+
+
+
+
+
+ armhf
+
+
+
+
+ arm64
+
+
+
+
+ amd64
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SerialMonitor/UI.cs b/SerialMonitor/UI.cs
index 630205c..8aeae61 100644
--- a/SerialMonitor/UI.cs
+++ b/SerialMonitor/UI.cs
@@ -79,7 +79,7 @@ public static void Init()
};
// hotkeys
- Application.QuitKey = Key.F10;
+ Application.QuitKey = Key.CtrlMask | Key.q;
win.KeyUp += (e) =>
{
e.Handled = ProcessHotKey(e.KeyEvent);
@@ -208,7 +208,7 @@ public static void Shutdown()
}
private static bool ProcessHotKey(KeyEvent keyEvent)
- {
+ {
if (keyEvent.Key == Key.F1)
{
ActionHelp?.Invoke();
@@ -413,11 +413,11 @@ private static bool ProcessHotKey(KeyEvent keyEvent)
// Send file data
ActionSendFile?.Invoke(FileHistory[fileList.SelectedItem]);
}
- else if (keyEvent.Key == Key.F11)
+ else if (keyEvent.Key == Key.F11 || keyEvent.Key == (Key.D1 | Key.CtrlMask))
{
ActionRts?.Invoke();
}
- else if (keyEvent.Key == Key.F12)
+ else if (keyEvent.Key == Key.F12 || keyEvent.Key == (Key.D2 | Key.CtrlMask))
{
ActionDtr?.Invoke();
}
@@ -437,7 +437,7 @@ private static bool ProcessHotKey(KeyEvent keyEvent)
private static void SetBottomMenuText()
{
- menu.Text = $" F1 Help | F2 Setup | F3 {(!PrintAsHexToLogView ? "Hex " : "Text")} | F4 {(!RequestPortClose ? "Close" : "Open ")} | F5 Send | F6 SendFile | F10 Exit | F11 RTS | F12 DTR | ^P {(!PrintToLogView ? "Print" : "Pause")}";
+ menu.Text = $" F1 Help | F2 Setup | F3 {(!PrintAsHexToLogView ? "Hex " : "Text")} | F4 {(!RequestPortClose ? "Close" : "Open ")} | F5 Send | F6 SendFile | F11 RTS | F12 DTR | ^P {(!PrintToLogView ? "Print" : "Pause")} | ^Q Exit";
}
public static void Run(Func action)