Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MF2025 - Config List View - First version #1934

Merged
merged 41 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
58862c6
Frontend shows config table = WIP
DocMoebiuz Jan 25, 2025
b09b0fb
First refactoring done for the ConfigItems
DocMoebiuz Jan 25, 2025
988710b
WIP - ConfigItem View receiving Config
DocMoebiuz Jan 25, 2025
d5adcd4
1st working version of the table with acutal config data
DocMoebiuz Jan 25, 2025
1fb1d6c
Orphaned Serials Dialog is working correctly.
DocMoebiuz Jan 25, 2025
15df100
Reverted accidental conflict of Name and DeviceName for config
DocMoebiuz Jan 26, 2025
b61bee3
Fixed wrong clone
DocMoebiuz Jan 26, 2025
c5361c7
Fixed facetted filtering
DocMoebiuz Jan 26, 2025
283e069
Hide Status and Tags
DocMoebiuz Jan 26, 2025
0b4e64f
Filters are working correctly for the table
DocMoebiuz Jan 26, 2025
2788537
Communication back and forth works for ConfigItem
DocMoebiuz Jan 26, 2025
5eb8bf5
Improve the JSON serialization including unit tests
DocMoebiuz Jan 26, 2025
bf4b759
Config Wizard opens correctly for OutputConfigItems
DocMoebiuz Jan 26, 2025
7a70846
Remove unused property "name"
DocMoebiuz Jan 27, 2025
6e141a6
Serialize InputAction correctly
DocMoebiuz Jan 27, 2025
658b678
Use dynamic way of serializeing ConfigItem and ModifierBase
DocMoebiuz Jan 27, 2025
44cb91a
Clone InputConfigItem correctly
DocMoebiuz Jan 27, 2025
fdfcdd9
fix the property name for ConfigEdit message
DocMoebiuz Jan 27, 2025
4299989
Correctly open InputConfigWizard
DocMoebiuz Jan 27, 2025
11878f4
Fixed some visuals for the table
DocMoebiuz Jan 27, 2025
bb89174
Use short device type name
DocMoebiuz Jan 27, 2025
f3b0afb
Improved table rendering and active state can be toggled.
DocMoebiuz Jan 27, 2025
f1055ea
Add output/input config items is possible
DocMoebiuz Jan 27, 2025
483cb78
Copy ConfigItem is possible
DocMoebiuz Jan 28, 2025
db9ddba
All context menu actions are working.
DocMoebiuz Jan 28, 2025
5bea955
Fix config ref title, code cleanup
DocMoebiuz Jan 28, 2025
6ea0338
Update workflow for PR build
DocMoebiuz Jan 28, 2025
e6fa004
Add xml attrib deserialization info for backward compatibility
DocMoebiuz Jan 28, 2025
7dab087
Add status icons and inline name editing for config items
DocMoebiuz Jan 28, 2025
b53e753
Fix the table width
DocMoebiuz Jan 28, 2025
fd2cddf
Improved responsivness
DocMoebiuz Jan 29, 2025
2503174
Working playwright tests for the config list
DocMoebiuz Feb 6, 2025
90c1705
Improving edit styles
DocMoebiuz Feb 6, 2025
4da67bf
Fixing linter errors
DocMoebiuz Feb 6, 2025
32dd20e
Status icons are working
DocMoebiuz Feb 6, 2025
9f185c8
Fixed playwright test
DocMoebiuz Feb 6, 2025
4c47b3b
Fix failing build
DocMoebiuz Feb 6, 2025
fca6296
Fix xml unit tests to preserve temporary backward compatiblity
DocMoebiuz Feb 6, 2025
ce37832
Fixed Equals method for ConfigItem
DocMoebiuz Feb 7, 2025
0adc316
Fix the update after input config item is edited
DocMoebiuz Feb 7, 2025
b618ab1
Display test mode status for config items
DocMoebiuz Feb 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: RunTests
on:
workflow_dispatch:
pull_request:
branches: [main]
branches: [main, mf2025/main]

env:
# Path to the solution file relative to the root of the project.
Expand Down Expand Up @@ -42,7 +42,11 @@ jobs:
run: "type Properties/AssemblyInfo.cs"
shell: pwsh

- name: Build
- name: Build frontend
working-directory: ${{env.GITHUB_WORKSPACE}}
run: cd frontend; npm install; npm run build

- name: Build core
working-directory: ${{env.GITHUB_WORKSPACE}}
# Add additional options to the MSBuild command line here (like platform or verbosity level).
# See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference
Expand Down
8 changes: 4 additions & 4 deletions Base/AppTelemetry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,12 @@ public void ConfigLoaded(ConfigFile configFile)
{
// Track config loaded event
EventTelemetry trackingEvent = new EventTelemetry("ConfigLoaded");
List<OutputConfigItem> outputConfigs = configFile.GetOutputConfigItems();
List<InputConfigItem> inputConfigs = configFile.GetInputConfigItems();
List<OutputConfigItem> outputConfigs = configFile.OutputConfigItems;
List<InputConfigItem> inputConfigs = configFile.InputConfigItems;

foreach (OutputConfigItem item in outputConfigs)
{
String key = "output." + item.DisplayType;
String key = "output." + item.DeviceType;
if (!trackingEvent.Metrics.ContainsKey(key)) trackingEvent.Metrics[key] = 0;
trackingEvent.Metrics[key] += 1;

Expand All @@ -86,7 +86,7 @@ public void ConfigLoaded(ConfigFile configFile)

foreach (InputConfigItem item in inputConfigs)
{
String key = "input." + item.Type;
String key = "input." + item.DeviceType;
if (item.ModuleSerial.Contains(Joystick.SerialPrefix))
{
key += ".joystick";
Expand Down
246 changes: 128 additions & 118 deletions Base/ConfigFile.cs
Original file line number Diff line number Diff line change
@@ -1,82 +1,72 @@
using System;
using MobiFlight.Base;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Data;
using System.IO;
using MobiFlight.InputConfig;
using System.Xml;
using System.Xml.Serialization;

namespace MobiFlight
{
[XmlRoot("MobiFlightConnector")]
public class ConfigFile
{
[XmlElement("outputs")]
[JsonIgnore]
public List<OutputConfigItem> OutputConfigItems { get; set; } = new List<OutputConfigItem>();

[XmlElement("inputs")]
[JsonIgnore]
public List<InputConfigItem> InputConfigItems { get; set; } = new List<InputConfigItem>();

[XmlIgnore]
public String FileName { get; set; }

// create read only property to get the output config items
[XmlIgnore]
public List<IConfigItem> ConfigItems
{
get { return GetConfigItems(); }
}

System.Xml.XmlDocument xmlConfig = new System.Xml.XmlDocument();

public ConfigFile() { }

public ConfigFile(String FileName)
{
this.FileName = FileName;
}

public void OpenFile()
{
if (FileName == null) throw new Exception("File yet not set");

xmlConfig.Load(FileName);
OutputConfigItems = GetOutputConfigItems();
InputConfigItems = GetInputConfigItems();
}

public void SaveFile(DataSet outputConfig, DataSet inputConfig)
public List<IConfigItem> GetConfigItems()
{
xmlConfig.RemoveAll();
XmlDeclaration xmlDeclaration = xmlConfig.CreateXmlDeclaration("1.0", "UTF-8", "yes");
xmlConfig.InsertBefore(xmlDeclaration, xmlConfig.DocumentElement);

XmlElement root = xmlConfig.CreateElement("MobiflightConnector");
// Create a new element and add it to the document.
StringWriter sw = new StringWriter();
outputConfig.WriteXml(sw, XmlWriteMode.IgnoreSchema);
string s = sw.ToString();
XmlDocument tmpDoc = new XmlDocument();
tmpDoc.LoadXml(s);

XmlElement outputs = xmlConfig.CreateElement("outputs");
outputs.InnerXml = tmpDoc.DocumentElement.SelectSingleNode("/outputs").InnerXml;

sw = new StringWriter();
inputConfig.WriteXml(sw, XmlWriteMode.IgnoreSchema);
s = sw.ToString();
tmpDoc = new XmlDocument();
tmpDoc.LoadXml(s);

XmlElement inputs = xmlConfig.CreateElement("inputs");
inputs.InnerXml = tmpDoc.DocumentElement.SelectSingleNode("/inputs").InnerXml;

root.AppendChild(outputs);
root.AppendChild(inputs);
xmlConfig.AppendChild(root);
xmlConfig.Save(FileName);
}
var result = new List<IConfigItem>();
OutputConfigItems.ForEach(item => result.Add(item));
InputConfigItems.ForEach(item => result.Add(item));

private XmlReader getConfig(String xpath)
{
// first try the new way... if this fails try the old way
System.Xml.XmlNode outputConfig = xmlConfig.DocumentElement.SelectSingleNode(xpath);
if (outputConfig == null) throw new InvalidExpressionException();

System.IO.StringReader reader = new System.IO.StringReader(outputConfig.OuterXml);
System.Xml.XmlReader xReader = System.Xml.XmlReader.Create(reader);
return xReader;
return result;
}

public List<OutputConfigItem> GetOutputConfigItems()
protected List<OutputConfigItem> GetOutputConfigItems()
{
List<OutputConfigItem> result = new List<OutputConfigItem>();

XmlNodeList outputs = xmlConfig.DocumentElement.SelectNodes("outputs/config/settings");
foreach(XmlNode item in outputs)
XmlNodeList outputs = xmlConfig.DocumentElement.SelectNodes("outputs/config");
foreach (XmlNode item in outputs)
{
OutputConfigItem config = new OutputConfigItem();
System.IO.StringReader reader = new System.IO.StringReader(item.OuterXml);
config.GUID = item.Attributes["guid"].Value;
config.Active = item.SelectSingleNode("active").InnerText == "true";
config.Name = item.SelectSingleNode("description").InnerText;

System.IO.StringReader reader = new System.IO.StringReader(item.SelectSingleNode("settings").OuterXml);
System.Xml.XmlReader xReader = System.Xml.XmlReader.Create(reader);
config.ReadXml(xReader);
result.Add(config);
Expand All @@ -85,15 +75,19 @@ public List<OutputConfigItem> GetOutputConfigItems()
return result;
}

internal List<InputConfigItem> GetInputConfigItems()
protected List<InputConfigItem> GetInputConfigItems()
{
List<InputConfigItem> result = new List<InputConfigItem>();

XmlNodeList inputs = xmlConfig.DocumentElement.SelectNodes("inputs/config/settings");
XmlNodeList inputs = xmlConfig.DocumentElement.SelectNodes("inputs/config");
foreach (XmlNode item in inputs)
{
InputConfigItem config = new InputConfigItem();
System.IO.StringReader reader = new System.IO.StringReader(item.OuterXml);
config.GUID = item.Attributes["guid"].Value;
config.Active = item.SelectSingleNode("active").InnerText == "true";
config.Name = item.SelectSingleNode("description").InnerText;

System.IO.StringReader reader = new System.IO.StringReader(item.SelectSingleNode("settings").OuterXml);
System.Xml.XmlReader xReader = System.Xml.XmlReader.Create(reader);
xReader.Read();
config.ReadXml(xReader);
Expand All @@ -103,75 +97,91 @@ internal List<InputConfigItem> GetInputConfigItems()
return result;
}

public XmlReader getInputConfig()
public void SaveFile()
{
XmlReader result = null;

try
{
if (xmlConfig.DocumentElement == null) OpenFile();

result = getConfig("/MobiflightConnector/inputs");
}
catch (InvalidExpressionException e)
{
throw e;
}
catch (Exception e)
{
throw new ConfigErrorException("Error reading config", e);
}
return result;
SaveFile(OutputConfigItems, InputConfigItems);
}

public XmlReader getOutputConfig()
private void SaveFile(List<OutputConfigItem> outputConfigItems, List<InputConfigItem> inputConfigItems)
{
XmlReader result = null;

bool fallback = false;
try
{
if (xmlConfig.DocumentElement == null) OpenFile();
result = getConfig("/MobiflightConnector/outputs");
}
catch (InvalidExpressionException e)
{
fallback = true;
}
catch (Exception e)
XmlSerializer serializer = new XmlSerializer(typeof(ConfigFileWrapperXML));
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add("", "");
//namespaces.Add("xsd", "https://www.w3.org/2001/XMLSchema");

var XmlConfig = new ConfigFileWrapperXML();
XmlConfig.outputConfigs = new List<OutputConfigFileXmlElement>();
outputConfigItems.ForEach(item => XmlConfig.outputConfigs.Add(new OutputConfigFileXmlElement() { guid = item.GUID, active = item.Active, description = item.Name, settings = item }));
XmlConfig.inputConfigs = new List<InputConfigFileXmlElement>();
inputConfigItems.ForEach(item => XmlConfig.inputConfigs.Add(new InputConfigFileXmlElement() { guid = item.GUID, active = item.Active, description = item.Name, settings = item }));

using (StreamWriter writer = new StreamWriter(FileName))
{
throw new ConfigErrorException("Error reading config", e);
serializer.Serialize(writer, XmlConfig, namespaces);
}

if (fallback)
{
fallback = false;
// fallback for old configs
try
{
result = getConfig("/MobiflightConnector");
}
catch (Exception ex)
{
fallback = true;
}
}

if (fallback)
{
fallback = false;
// fallback for old configs
try
{
result = getConfig("/ArcazeUsbConnector");
}
catch (Exception ex)
{
throw new Exception("Error: Loading config");
}
}

return result;
}

// <summary>
// due to the new settings-node there must be some routine to load
// data from legacy config files
// </summary>
//private void _applyBackwardCompatibilityLoading()
//{
// foreach (DataRow row in outputConfigPanel.ConfigDataTable.Rows)
// {
// if (row["settings"].GetType() == typeof(System.DBNull))
// {
// OutputConfigItem cfgItem = new OutputConfigItem();

// if (row["fsuipcOffset"].GetType() != typeof(System.DBNull))
// cfgItem.FSUIPC.Offset = Int32.Parse(row["fsuipcOffset"].ToString().Replace("0x", ""), System.Globalization.NumberStyles.HexNumber);

// if (row["fsuipcSize"].GetType() != typeof(System.DBNull))
// cfgItem.FSUIPC.Size = Byte.Parse(row["fsuipcSize"].ToString());

// if (row["mask"].GetType() != typeof(System.DBNull))
// cfgItem.FSUIPC.Mask = (row["mask"].ToString() != "") ? Int32.Parse(row["mask"].ToString()) : Int32.MaxValue;

// comparison
// if (row["comparison"].GetType() != typeof(System.DBNull))
// {
// cfgItem.Modifiers.Comparison.Active = true;
// cfgItem.Modifiers.Comparison.Operand = row["comparison"].ToString();
// }

// if (row["comparisonValue"].GetType() != typeof(System.DBNull))
// {
// cfgItem.Modifiers.Comparison.Value = row["comparisonValue"].ToString();
// }

// if (row["converter"].GetType() != typeof(System.DBNull))
// {
// if (row["converter"].ToString() == "Boolean")
// {
// cfgItem.Modifiers.Comparison.IfValue = "1";
// cfgItem.Modifiers.Comparison.ElseValue = "0";
// }
// }

// if (row["trigger"].GetType() != typeof(System.DBNull))
// {
// cfgItem.DisplayTrigger = row["trigger"].ToString();
// }

// if (row["usbArcazePin"].GetType() != typeof(System.DBNull))
// {
// cfgItem.DisplayType = MobiFlightOutput.TYPE;
// cfgItem.Pin.DisplayPin = row["usbArcazePin"].ToString();
// }

// if (row["arcazeSerial"].GetType() != typeof(System.DBNull))
// {
// cfgItem.DisplaySerial = row["arcazeSerial"].ToString();
// }

// row["settings"] = cfgItem;
// }
// }
//}
}
}
}
44 changes: 44 additions & 0 deletions Base/ConfigFileWrapperXML.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System.Collections.Generic;
using System.Xml.Serialization;

namespace MobiFlight.Base
{
[XmlRoot("config")]
public class ConfigFileXmlItem
{

[XmlAttribute("guid")]
public string guid { get; set; }

[XmlElement]
public bool active { get; set; }

[XmlElement]
public string description { get; set; }
}

public class OutputConfigFileXmlElement : ConfigFileXmlItem
{
[XmlElement]
public OutputConfigItem settings { get; set; }
}

public class InputConfigFileXmlElement : ConfigFileXmlItem
{
[XmlElement]
public InputConfigItem settings { get; set; }
}


[XmlRoot("MobiflightConnector")]
public class ConfigFileWrapperXML
{
[XmlArray("outputs")]
[XmlArrayItem("config")]
public List<OutputConfigFileXmlElement> outputConfigs;

[XmlArray("inputs")]
[XmlArrayItem("config")]
public List<InputConfigFileXmlElement> inputConfigs;
}
}
Loading