+
Skip to content

blekenbleu/JSONio

Repository files navigation

SimHub JSONio plugin

from SimHubPluginSdk

What

 
A common list of custom SimHub properties with some values potentially specific to each sim and car.

In this example, properties are managed for ShakeIt Wheel Slip haptics:

  • a global Property list
    • each Property object a Name and Value
  • a Game list
    • each Game object a name, game-specific defaults Property List<> and CList of Car objects
      • each Car object a carID and its List<> of Property objects
        • each Property object a Name and Value
  • Properties to be managed are configured in JSONio.ini.

How this plugin was developed

  • instead of just copying that SimHubPluginSdk repository
    • created a new Visual Studio JSONio WPF project, then quit
    • deleted everything in that project except JSONio.sln and JSONio.csproj
    • copied Properties/ and source files from SimHubPluginSdk/
    • performed GVIM split diff on JSONio.sln and JSONio.csproj to preserve new ProjectGuid, etc
    • forgot to update namespace from User.PluginSdk to JSONio e.g. in Properties/...!
    • easily missed namespace in .xaml called xmlns:local="clr-namespace:..."
  • this.AddAction("ChangeProperties",(a, b) => saves changed Car properties list,
    then loads properties matching changed Car.ID and Game gname.
    • updates Values list from defaults for Game gname if no Car.ID match
    • could implement this.AddEvent("CarChange");
      in public void Init(PluginManager pluginManager),
      • then this.TriggerEvent("CarChange");
        in public void DataUpdate(PluginManager pluginManager, ref GameData data)
    • but instead, by GitHub NCalc 'CarChange' event in JSONio.ini:
       [ExportEvent]
       name='CarChange'
       trigger=changed(20, [DataCorePlugin.GameData.CarId]) 
      


- my experience:  SimHub ignored this Source when JSONio.ini was first loaded...

  • in JSONio.cs Init()
    • create and update global property settings
    • create and maintain games object and JSON file
      • populate from configured .json file, if existing
    • populate simValues object from saved Settings, games and JASONio.ini
    • in this.AddAction("ChangeProperties",(a, b)
      • create new Game object in games when none match current Game name
      • set game for matching games.data.name
    • additional this.AddAction()s for modifying Car and defaults values

Jagged List<> supports e.g. dynamically adding and deleting elements (based on JSONio.ini)
better than jagged array.

To Do   24 Apr 2024

  • done stop mouse click messing with selected property in UI
  • done property indicating a new car
  • done manage some SimHub properties not-per car, e.g. ShakeIt frequency limits
    • some tricks:
      • conversion to/from Car class has uses only first pCount List<Values>,
        which GameHandler class uses to save cars into Games data for .json file.
      • JSONio knows about properties beyond pCount in List<Values> simValues...
        • these are NOT saved in .json files
        • these ARE available as SimHub properties e.g. for ShakeIt
        • these ARE available for value changes in user interface
    • refactored to use only List<Values> simValues for user interface;
      • using JSONio.ini, convert from/to DataPluginSettings Settings in Init()/End()
      • restore/save simValue.Current values from/to GameHandler games.Car from/to JSONio.json
  • done slim .json format storing only one instance of property names, instead of redundantly per-car

To Do   5 Feb 2025 V1.24

  • done: generate release .zip file for Release 1.25 and newer builds
  • done document error messages
  • done SetDefault(): {Gname} not in slim.data.gList
    • trying to change a default on first car
    • try CarChange()
  • done disable sorting property columns in UI
  • done UI feedback for property value change
  • done buttons not enabled when bad JSON is loaded
  • done handle too short slim.data.gList[gndx].cList[0].Vlist.Count
    • KEY CONCEPT: slim.data should be ready for saving as JSON after Init()
      • whether or not user changes any property values
      • no practical use for write flag;
        write new JSON only when users change certain values:
        • current per-car values
        • default global or per-game values
  • done add distinct JSONio.ini configuration for per-game properties:
    • global saved to, restored from SimHub DataPluginSettings
    • per game saved to, restored from JSON per DataCorePlugin.ExternalScript.JSONio.file
    • per car same as per game; game properties are first "car" in game cList
  • done handle property moves (by .ini reconfiguration) among per-car, per-game and global.
    • use ini default values for moves
  • done Init() bugs:  substantial refactoring
    • done when JSONio.file property not 'PluginsData/JSONio.json',
      initially generated JSONio.file is wrong... better later..??!
    • done save global properties and all property names to Settings
  • SimHub [WatchDog] Stacks dump for OOps() popups
    • invoke MessageBox.Show() by TriggerEvent() .. but not in Init()...
    • MessageBox.Show() in Init() does not display (UI thread not yet launched).
  • done Help link
  • done Change() for slider
  • done set write true in SetDefault(); run DefaultCopy() in End() if write

To Do   16 Feb 2025 V1.24

  • done: SaveCar() in Changed()only if badgndxorcndx`
  • done: skip OOpa() in Load() if empty Msg`
  • done: pList should be gCount, not simValues.Count
  • done: convert slider TextBox to a button
    • click reassigns slider to current Selection value
  • done: bug fix: SaveCar() now explicitly saves per-car and per-game changes for existing cars
  • done: bug fix: preserve saved settings only for first instance of previous game car
  • OxyScope integration

New to me

  • C# List<> patterns, particularly with non-trivial objects.
  • C# JSON
    • In Visual Studio, add Newtonsoft.Json.NET package...
    • pretty-print JSON from C# AKA serialize
       	using Newtonsoft.Json;
      
       	if (games.Save_Car(current, gname) || changed)
       	{
       		string js = JsonConvert.SerializeObject(games.data, Formatting.Indented);
      
    • Eventually, Read and Parse a JSON File in C# AKA deserialize
       	if (File.Exists(path))  
       	{  
       		games = JsonSerializer.Deserialize<Games>(File.ReadAllText(path));  
       	} else changed = true;  
      
  • C# WPF DataGrid in XAML  -  more references
    • 4 column table:
      • property name
      • default value
      • previous value
      • current value
    • XML header of row labels, as above
    • programatically add a row for each property configured
    • highlight current value of only selected property
    • first steps in Visual Studio:
      • click Control.xaml
      • select View->Designer
      • drag in DataGrid from Common WPF Controls
      • drag in buttons for previous, next, +, -, etc
  • C# Dispatcher.Invoke()
    • WPF DataGrid user interface updates want a method.
    • Invoking method on WPF DataGrid resources is disallowed from other threads.
    • Dispatcher.Invoke() is less code than subscribing to PropertyChanged events
  • invoke MessageBox.Show()
    • should be able to run on plugin's View (UI) thread..?
    • but plugin UI thread starts (GetWPFSettingsControl()) after Init()
      • queue Msg until UI thread launch, then View.Dispatcher.Invoke(() => View.OOpsMB());
      • [WatchDog] Abnormal Inactivity detected gets provoked if message box displays > 5 seconds...

Updates

			<DataGrid.Columns>
				<DataGridTextColumn Header="Property" Binding="{Binding Name}" />
				<DataGridTextColumn Header="Default" Binding="{Binding Default}" />
				<DataGridTextColumn Header="Current" Binding="{Binding Current}" />
				<DataGridTextColumn Header="Previous" Binding="{Binding Previous}" />
			</DataGrid.Columns>
		...
		public class Values
		{
			public string Name { get; set; }
			public string Default { get; set; }
			public string Current { get; set; }
			public string Previous { get; set; }
		}
		...
		public List<Values> simValues;

		public SettingsControl()
		{
			InitializeComponent();

			simValues = new List<Values>();
			dg.ItemsSource = simValues;
		}
- bind WPF button clicks directly to Plugin Action methods
  • 4 Apr: - test buttons
    - populate List<Values> from existing current, previous, etc

  • 5 Apr: - fully functional by buttons
    - Select hightlight forced for button changes, will not work for dashboard
    - simValues updated from original Lists, pending refactor
    - property updates by dashboard should work...

  • 7 Apr: - per-car fully functional by buttons and dashboard
    - thanks to arguably sketchy code rearranging...
    - still to do:  fully integrate simValues in JSONio.cs

  • C# WPF XY plot:  SimHub already uses OxyPlot

  • 12 May 2024 - MessageBox

    • SimHub Event triggers do not work from plugin Init()
    • MessageBox() during Init() (e.g. for JASONio.ini configuration errors)
      provokes extensive SimHub log error message.
    • for game runtime, added "JSONioOOps" TriggerEvent and
      this.AddAction("OopsMessageBox", (a, b) => OOpsMB());
  • 12 Oct - sync most code to SlipGrip branch

13 Oct - configurable slider

  • instead of hard-coded to Gscale
  • configured in NCalcScripts/JSONio.ini,
    where value may be any name in JSONio.properties, e.g.:
[ExportProperty]
name='JSONio.slider'
value='Gscale' 

6 Feb 2025 build release .zip

  • removed obsolete Documentation\release.sh
  • in JSONio.csproj:
  <PropertyGroup>
    <PostBuildEvent>
      cd "$(ProjectDir)"
      if $(ConfigurationName) == Release (7z u R:\TEMP\JSONio.zip NCalcScripts\JSONio.ini)
      cd "bin\Release"
      if $(ConfigurationName) == Release (7z u R:\TEMP\JSONio.zip JSONio.dll)
    </PostBuildEvent>
  </PropertyGroup>

Init() refactor

  • restore Settings
    • reconcile with JSONio.ini global properties
  • load Slim .json
    • reconcile JSONio.ini car- and game -specific
  • bool changed set true if any .json mismatches

About

Read and write JSON e.g. for car properties

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载