restructure

This commit is contained in:
NinjaPug
2025-04-14 12:46:10 -04:00
parent 1cf596b379
commit 21b6ad3d75
22 changed files with 1257 additions and 990 deletions

View File

@@ -0,0 +1,24 @@
namespace PCPalConfigurator.Core
{
/// <summary>
/// Stores configuration data for PCPal displays
/// </summary>
public class ConfigData
{
// Common settings
public string LastUsedPort { get; set; }
public string ScreenType { get; set; } // "1602", "TFT4_6", or "OLED"
// LCD-specific settings
public string Line1Selection { get; set; }
public string Line1CustomText { get; set; }
public string Line2Selection { get; set; }
public string Line2CustomText { get; set; }
public string Line1PostText { get; set; }
public string Line2PostText { get; set; }
// OLED-specific settings
public string OledMarkup { get; set; }
public string LastIconDirectory { get; set; }
}
}

View File

@@ -0,0 +1,189 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using LibreHardwareMonitor.Hardware;
using PCPalConfigurator.Rendering;
using PCPalConfigurator.Rendering.Elements;
namespace PCPalConfigurator.Core
{
/// <summary>
/// Parses OLED markup into renderable elements
/// </summary>
public class MarkupParser
{
private readonly Dictionary<string, float> sensorValues;
public MarkupParser(Dictionary<string, float> sensorValues)
{
this.sensorValues = sensorValues ?? new Dictionary<string, float>();
}
/// <summary>
/// Processes sensor variables in markup
/// </summary>
public string ProcessVariables(string markup)
{
if (string.IsNullOrEmpty(markup))
return string.Empty;
// Replace variables with actual values
foreach (var sensor in sensorValues)
{
// Look for {variable} syntax in the markup
string variablePattern = $"{{{sensor.Key}}}";
// Format value based on type (integers vs decimals)
string formattedValue;
if (Math.Abs(sensor.Value - Math.Round(sensor.Value)) < 0.01)
{
formattedValue = $"{sensor.Value:F0}";
}
else
{
formattedValue = $"{sensor.Value:F1}";
}
markup = markup.Replace(variablePattern, formattedValue);
}
return markup;
}
/// <summary>
/// Parses markup and returns a list of preview elements
/// </summary>
public List<PreviewElement> ParseMarkup(string markup)
{
var elements = new List<PreviewElement>();
if (string.IsNullOrEmpty(markup))
return elements;
// Process variables in the markup first
markup = ProcessVariables(markup);
// Parse text elements - <text x=0 y=10 size=1>Hello</text>
foreach (Match match in Regex.Matches(markup, @"<text\s+x=(\d+)\s+y=(\d+)(?:\s+size=(\d+))?>([^<]*)</text>"))
{
elements.Add(new TextElement
{
X = int.Parse(match.Groups[1].Value),
Y = int.Parse(match.Groups[2].Value),
Size = match.Groups[3].Success ? int.Parse(match.Groups[3].Value) : 1,
Text = match.Groups[4].Value
});
}
// Parse bar elements - <bar x=0 y=20 w=100 h=8 val=75 />
foreach (Match match in Regex.Matches(markup, @"<bar\s+x=(\d+)\s+y=(\d+)\s+w=(\d+)\s+h=(\d+)\s+val=(\d+)\s*/>"))
{
elements.Add(new BarElement
{
X = int.Parse(match.Groups[1].Value),
Y = int.Parse(match.Groups[2].Value),
Width = int.Parse(match.Groups[3].Value),
Height = int.Parse(match.Groups[4].Value),
Value = int.Parse(match.Groups[5].Value)
});
}
// Parse rect elements - <rect x=0 y=0 w=20 h=10 />
foreach (Match match in Regex.Matches(markup, @"<rect\s+x=(\d+)\s+y=(\d+)\s+w=(\d+)\s+h=(\d+)\s*/>"))
{
elements.Add(new RectElement
{
X = int.Parse(match.Groups[1].Value),
Y = int.Parse(match.Groups[2].Value),
Width = int.Parse(match.Groups[3].Value),
Height = int.Parse(match.Groups[4].Value),
Filled = false
});
}
// Parse box elements - <box x=0 y=0 w=20 h=10 />
foreach (Match match in Regex.Matches(markup, @"<box\s+x=(\d+)\s+y=(\d+)\s+w=(\d+)\s+h=(\d+)\s*/>"))
{
elements.Add(new RectElement
{
X = int.Parse(match.Groups[1].Value),
Y = int.Parse(match.Groups[2].Value),
Width = int.Parse(match.Groups[3].Value),
Height = int.Parse(match.Groups[4].Value),
Filled = true
});
}
// Parse line elements - <line x1=0 y1=0 x2=20 y2=20 />
foreach (Match match in Regex.Matches(markup, @"<line\s+x1=(\d+)\s+y1=(\d+)\s+x2=(\d+)\s+y2=(\d+)\s*/>"))
{
elements.Add(new LineElement
{
X1 = int.Parse(match.Groups[1].Value),
Y1 = int.Parse(match.Groups[2].Value),
X2 = int.Parse(match.Groups[3].Value),
Y2 = int.Parse(match.Groups[4].Value)
});
}
// Parse icon elements - <icon x=0 y=0 name=cpu />
foreach (Match match in Regex.Matches(markup, @"<icon\s+x=(\d+)\s+y=(\d+)\s+name=([a-zA-Z0-9_]+)\s*/>"))
{
elements.Add(new IconElement
{
X = int.Parse(match.Groups[1].Value),
Y = int.Parse(match.Groups[2].Value),
Name = match.Groups[3].Value
});
}
return elements;
}
/// <summary>
/// Creates an example OLED markup using available sensors
/// </summary>
public static string CreateExampleMarkup(SensorManager sensorManager)
{
StringBuilder markup = new StringBuilder();
string cpuLoad = sensorManager.FindFirstSensorOfType(HardwareType.Cpu, SensorType.Load);
string cpuTemp = sensorManager.FindFirstSensorOfType(HardwareType.Cpu, SensorType.Temperature);
string gpuLoad = sensorManager.FindFirstSensorOfType(HardwareType.GpuNvidia, SensorType.Load);
string gpuTemp = sensorManager.FindFirstSensorOfType(HardwareType.GpuNvidia, SensorType.Temperature);
string ramUsed = sensorManager.FindFirstSensorOfType(HardwareType.Memory, SensorType.Data);
markup.AppendLine("<text x=0 y=12 size=2>System Monitor</text>");
if (!string.IsNullOrEmpty(cpuLoad) && !string.IsNullOrEmpty(cpuTemp))
{
markup.AppendLine($"<text x=0 y=30 size=1>CPU: {{{cpuLoad}}}% ({{{cpuTemp}}}°C)</text>");
markup.AppendLine($"<bar x=0 y=35 w=128 h=6 val={{{cpuLoad}}} />");
}
else if (!string.IsNullOrEmpty(cpuLoad))
{
markup.AppendLine($"<text x=0 y=30 size=1>CPU: {{{cpuLoad}}}%</text>");
markup.AppendLine($"<bar x=0 y=35 w=128 h=6 val={{{cpuLoad}}} />");
}
if (!string.IsNullOrEmpty(gpuLoad) && !string.IsNullOrEmpty(gpuTemp))
{
markup.AppendLine($"<text x=130 y=30 size=1>GPU: {{{gpuLoad}}}% ({{{gpuTemp}}}°C)</text>");
markup.AppendLine($"<bar x=130 y=35 w=120 h=6 val={{{gpuLoad}}} />");
}
else if (!string.IsNullOrEmpty(gpuLoad))
{
markup.AppendLine($"<text x=130 y=30 size=1>GPU: {{{gpuLoad}}}%</text>");
markup.AppendLine($"<bar x=130 y=35 w=120 h=6 val={{{gpuLoad}}} />");
}
if (!string.IsNullOrEmpty(ramUsed))
{
markup.AppendLine($"<text x=0 y=50 size=1>RAM: {{{ramUsed}}} GB</text>");
}
return markup.ToString();
}
}
}

View File

@@ -0,0 +1,237 @@
using System;
using System.Collections.Generic;
using LibreHardwareMonitor.Hardware;
namespace PCPalConfigurator.Core
{
/// <summary>
/// Manages hardware sensors and provides access to sensor data
/// </summary>
public class SensorManager : IDisposable
{
private readonly Computer computer;
private readonly Dictionary<string, float> sensorValues = new Dictionary<string, float>();
private bool isDisposed = false;
public SensorManager()
{
computer = new Computer
{
IsCpuEnabled = true,
IsGpuEnabled = true,
IsMemoryEnabled = true,
IsMotherboardEnabled = true,
IsControllerEnabled = true,
IsStorageEnabled = true
};
computer.Open();
}
/// <summary>
/// Updates all sensor values from hardware
/// </summary>
public void UpdateSensorValues()
{
foreach (var hardware in computer.Hardware)
{
hardware.Update();
foreach (var sensor in hardware.Sensors)
{
if (sensor.Value.HasValue)
{
string sensorId = GetSensorVariableName(hardware.Name, sensor.Name);
sensorValues[sensorId] = sensor.Value.Value;
}
}
}
}
/// <summary>
/// Gets a dictionary of all current sensor values
/// </summary>
public Dictionary<string, float> GetAllSensorValues()
{
return new Dictionary<string, float>(sensorValues);
}
/// <summary>
/// Gets the value of a specific sensor
/// </summary>
public float? GetSensorValue(string sensorId)
{
if (sensorValues.TryGetValue(sensorId, out float value))
{
return value;
}
return null;
}
/// <summary>
/// Finds the first available sensor of a specific type
/// </summary>
public string FindFirstSensorOfType(HardwareType hardwareType, SensorType sensorType)
{
foreach (var hardware in computer.Hardware)
{
if (hardware.HardwareType == hardwareType)
{
foreach (var sensor in hardware.Sensors)
{
if (sensor.SensorType == sensorType && sensor.Value.HasValue)
{
return GetSensorVariableName(hardware.Name, sensor.Name);
}
}
}
}
return string.Empty;
}
/// <summary>
/// Creates a variable name for a sensor that's safe to use in markup
/// </summary>
public static string GetSensorVariableName(string hardwareName, string sensorName)
{
string name = $"{hardwareName}_{sensorName}"
.Replace(" ", "_")
.Replace("%", "Percent")
.Replace("#", "Num")
.Replace("/", "_")
.Replace("\\", "_")
.Replace("(", "")
.Replace(")", "")
.Replace(",", "");
return name;
}
/// <summary>
/// Gets the appropriate unit for a sensor type
/// </summary>
public static string GetSensorUnit(SensorType sensorType)
{
return sensorType switch
{
SensorType.Temperature => "°C",
SensorType.Load => "%",
SensorType.Clock => "MHz",
SensorType.Power => "W",
SensorType.Fan => "RPM",
SensorType.Flow => "L/h",
SensorType.Control => "%",
SensorType.Level => "%",
_ => ""
};
}
/// <summary>
/// Formats a sensor value according to its type
/// </summary>
public static string FormatSensorValue(float value, SensorType sensorType)
{
return sensorType switch
{
SensorType.Temperature => value.ToString("F1"),
SensorType.Clock => value.ToString("F0"),
SensorType.Load => value.ToString("F1"),
SensorType.Fan => value.ToString("F0"),
SensorType.Power => value.ToString("F1"),
SensorType.Data => (value > 1024) ? (value / 1024).ToString("F1") : value.ToString("F1"),
_ => value.ToString("F1")
};
}
/// <summary>
/// Gets all hardware sensors grouped by type
/// </summary>
public Dictionary<SensorType, List<SensorInfo>> GetAllSensorsGroupedByType()
{
var result = new Dictionary<SensorType, List<SensorInfo>>();
foreach (var hardware in computer.Hardware)
{
hardware.Update();
foreach (var sensor in hardware.Sensors)
{
if (sensor.Value.HasValue)
{
var sensorType = sensor.SensorType;
if (!result.ContainsKey(sensorType))
{
result[sensorType] = new List<SensorInfo>();
}
string sensorId = GetSensorVariableName(hardware.Name, sensor.Name);
result[sensorType].Add(new SensorInfo
{
Id = sensorId,
Name = $"{hardware.Name} {sensor.Name}",
Value = sensor.Value.Value,
SensorType = sensorType
});
}
}
}
return result;
}
/// <summary>
/// Gets all hardware for populating dropdown lists
/// </summary>
public List<string> GetSensorOptionsForDropdown()
{
var options = new List<string>();
foreach (var hardware in computer.Hardware)
{
hardware.Update();
foreach (var sensor in hardware.Sensors)
{
if (sensor.SensorType == SensorType.Load ||
sensor.SensorType == SensorType.Temperature ||
sensor.SensorType == SensorType.Data ||
sensor.SensorType == SensorType.Fan)
{
string option = $"{hardware.HardwareType}: {sensor.Name} ({sensor.SensorType})";
options.Add(option);
}
}
}
options.Add("Custom Text");
return options;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!isDisposed)
{
if (disposing)
{
computer.Close();
}
isDisposed = true;
}
}
}
/// <summary>
/// Represents information about a sensor
/// </summary>
public class SensorInfo
{
public string Id { get; set; }
public string Name { get; set; }
public float Value { get; set; }
public SensorType SensorType { get; set; }
}
}