Skip to content

Commit

Permalink
Merge pull request #14 from pre-martin/feature/access-all-properties
Browse files Browse the repository at this point in the history
It is now possible to access all SimHub properties.
  • Loading branch information
pre-martin authored Jan 18, 2023
2 parents 3dd9fa1 + e20c2c1 commit 2839224
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 9 deletions.
12 changes: 8 additions & 4 deletions PropertyServer.Plugin/Property/PropertyAccessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,10 @@ public static async Task<SimHubProperty> CreateProperty(string propertyName, Fun

if (source == null)
{
Log.Info($"Property {propertyName} does not start with a known source prefix");
await errorCallback.Invoke($"Property {propertyName} does not start with a known source prefix");
return null;
Log.Info($"Property {propertyName} does not start with a known source prefix. Treating as generic property.");
// We do not check if this property really exists (e.g. PluginManager.GetAllPropertyNames()), because properties
// can be added and removed dynamically.
return new SimHubPropertyGeneric(propertyName);
}

var simHubProperty = await CreateProperty(source.Value, propertyName, errorCallback);
Expand Down Expand Up @@ -93,6 +94,7 @@ private static async Task<SimHubProperty> CreateProperty(PropertySource source,
// Is it a property of type "getter"?
Log.Debug($"Trying to find property {name} in {source}");
var plainName = name.Contains('.') ? name.Substring(source.GetPropertyPrefix().Length + 1) : name;

var propertyInfo = source.GetPropertySourceType().GetProperty(plainName);
if (propertyInfo != null)
{
Expand Down Expand Up @@ -145,7 +147,9 @@ public static IEnumerable<SimHubProperty> GetAvailableProperties()
{
var result = new List<SimHubProperty>();

var sources = Enum.GetValues(typeof(PropertySource)).Cast<PropertySource>();
// Iterate over all known PropertySources and determine the accessible properties.
// But we really don't want to iterate on Generic properties.
var sources = Enum.GetValues(typeof(PropertySource)).Cast<PropertySource>().Where(source => source != PropertySource.Generic);
foreach (var source in sources)
{
var availableProperties = source.GetPropertySourceType().GetProperties();
Expand Down
10 changes: 9 additions & 1 deletion PropertyServer.Plugin/Property/PropertySource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@ public enum PropertySource
/// <summary>
/// Assetto Corsa Competizione - Rawdata "Physics"
/// </summary>
AccPhysics
AccPhysics,

/// <summary>
/// Generic access to a property via PluginManager.
/// </summary>
Generic
}

public static class PropertySourceEx
Expand All @@ -51,6 +55,8 @@ public static Type GetPropertySourceType(this PropertySource propertySource)
return Type.GetType("ACSharedMemory.ACC.MMFModels.Graphics, ACSharedMemory");
case PropertySource.AccPhysics:
return Type.GetType("ACSharedMemory.ACC.MMFModels.Physics, ACSharedMemory");
case PropertySource.Generic:
return typeof(PluginManager);
default:
throw new ArgumentException($"Unknown PropertySource {propertySource}");
}
Expand All @@ -71,6 +77,8 @@ public static string GetPropertyPrefix(this PropertySource propertySource)
return "acc.graphics";
case PropertySource.AccPhysics:
return "acc.physics";
case PropertySource.Generic:
return "";
default:
throw new ArgumentException($"Unknown PropertySource {propertySource}");
}
Expand Down
26 changes: 26 additions & 0 deletions PropertyServer.Plugin/Property/SimHubProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ private string TypeToString(Type type)
return "double";
case "System.Double":
return "double";
case "System.Object":
return "object";
default:
return "(unknown)";
}
Expand Down Expand Up @@ -185,4 +187,28 @@ protected override Type GetPropertyType()
return _methodInfo.ReturnType;
}
}

/// <summary>
/// This is a simple delegate that tries to retrieve the property value directly from the PluginManager.
/// </summary>
/// <remarks>
/// This method is much slower than the other property implementations. Use it only if the property is not available through
/// the other implementations.
/// </remarks>
public class SimHubPropertyGeneric : SimHubProperty
{
public SimHubPropertyGeneric(string propertyName) : base(PropertySource.Generic, propertyName)
{
}

protected override object GetValue(object obj)
{
return obj is PluginManager pm ? pm.GetPropertyValue(Name) : null;
}

protected override Type GetPropertyType()
{
return typeof(object);
}
}
}
3 changes: 3 additions & 0 deletions PropertyServer.Plugin/PropertyServerPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ private async void DataUpdateInternal(GameData data)
case PropertySource.AccPhysics:
await simHubProperty.UpdateFromObject(_rawDataManager.AccPhysics);
break;
case PropertySource.Generic:
await simHubProperty.UpdateFromObject(PluginManager);
break;
default:
throw new ArgumentException($"Unknown PropertySource {simHubProperty.PropertySource}");
}
Expand Down
62 changes: 58 additions & 4 deletions README.adoc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
= SimHub Property Server
:toc:
:sectnums:
ifdef::env-github[]
:tip-caption: :bulb:
endif::[]
Expand All @@ -14,7 +15,7 @@ TIP: Always read the *correct version* of the documentation, which matches the v

This is a plugin for https://www.simhubdash.com/[SimHub]. It allows access to SimHub properties via a tcp connection.

Clients can subscribe for property changes. They will then receive updates each time, when the value of a subscribed property changes.
Clients can subscribe for property changes. They will then receive updates each time, when the value of a subscribed property changes.

One use case is the project https://github.com/pre-martin/StreamDeckSimHubPlugin[StreamDeckSimHubPlugin], which allows updating the state of Stream Deck keys via SimHub properties.

Expand All @@ -25,11 +26,15 @@ Simply copy the file `PropertyServer.dll` into the root directory of your SimHub

Optionally, the checkbox "_Show in left menu_" can be activated. This will show an entry named "_Property Server_" in the left menu bar, which allows to adjust the settings of the plugin. If "_Show in left menu_" is not enabled, the settings of the plugin can be found under "_Additional Plugins_ > _Property Server_".

After installation, the checkbox "_Show in left menu_" can be found under "_Settings_ > _Plugins_".
After installation, the checkbox "_Show in left menu_" can be found under "_Settings_ > _Plugins_".


== Usage

If you are an end user, you can skip this section and jump to the https://github.com/pre-martin/StreamDeckSimHubPlugin[StreamDeckSimHubPlugin]. But make sure you read the section <<available-props-help>>, because it contains important information about the SimHub properties you can use.

=== Connection

Open a telnet connection to `localhost` on port `18082` (or whatever port has been set in the settings). The Property Server will respond with its name:

----
Expand All @@ -39,6 +44,8 @@ SimHub Property Server

Now simply send `help` in order to receive a list of subscribable properties and a list of supported commands.

=== Example

The following shows an example of the communication. The characters `<` and `>` are not part of the communication, they are just used in this example to illustrate what has been sent by the server (`>`) and what has been sent by the client (`<`):

----
Expand Down Expand Up @@ -66,14 +73,61 @@ Property names follow the convention of SimHub, e.g. `[DataCorePlugin.GameRunnin
At the moment, there are two limitations in effect:

. The plugin will send data only at a rate of 10 Hz.
. Only properties of type `bool`, `int`, `long`, `float` and `double` are supported. No arrays are supported.
. Only properties of the types `bool`, `int`, `long`, `float` and `double` are supported. No compound types and no arrays.

Limitation (1) was chosen because the plugin is not meant for real time communication. If real time is a requirement, then the UDP forwarding of SimHub should be used instead.

Limitation (2) could be changed, if there are requirements to read other properties. It's just a matter of implementing other data types.


== "Help"
[#available-props-help]
== Available properties and "Help"

=== Different sources of data

This plugin allows access to SimHub properties of different sources. These sources are distinguished by property name prefixes:

==== Prefix `dcp.*`

This prefix is mapped to a subset of the SimHub properties found in SimHub under "Available properties" in the section "DataCorePlugin". The available properties are listed in the section <<help>>.

This is the best performing option and the properties are returned in a typed manner (e.g. as `int` or `double`). This option should be preferred over the "generic" properties (see below).

To make the property names even shorter, the prefix `dcp.gd.\*` must be used to access properties which start with `DataCorePlugin.GameData.*`.

- Example 1: `dcp.GameRunning` must be used to access the SimHub property `DataCorePlugin.GameRunning` +
- Example 2: `dcp.gd.Brake` must be used to access the SimHub property `DataCorePlugin.GameData.Brake`

==== Prefix `acc.*`

This prefix is mapped to raw data properties from Assetto Corsa Competizione. The available properties are listed below in the section <<help>>.

Properties with this prefix perform as well as properties with the prefix `dcp` and they are also typed - just as `dcp` properties.

==== No prefix, also known as "Generic" properties

If a property name does not start with one of the prefixes listed above, then a generic access is used. This allows access to almost all properties of SimHub.

The number of available properties depends on the plugins that are enabled in SimHub, but in a usual setup there should be around 2000 properties available - including custom properties exported by NCalc scripts.

These properties are not listed in the section <<help>>! Just search the properties you are interested in in SimHub under "Available properties" and use the "Copy name" function from the context menu.

The downsides of generic properties are:

- Access is a little bit slower (but that shouldn't be a problem - it's fractions of a millisecond)
- These properties are not typed - they are just returned as `object`.

The consequence of this second point is that client applications have to take care of the interpretation of the values themselves. Therefore the other prefixes should be used, if possible.

- Example 1: `ShakeItWindPlugin.DynamicGain` allows access to just this property
- Example 2: `SystemInfoPlugin.CPULoadPercent` allows access to just this property
- Example 3: Of course, names like `DataCorePlugin.GameData.Brake` can be used. But consider using `dcp.gd.Brake` instead (for the reasons mentioned above)
- Example 4: Your SimHub installation contains an NCalc script example in the file `<SimHub>\NCalcScripts\samples.ini`, which exports the properties `DataCorePlugin.ExternalScript.BlinkingGear` and `DataCorePlugin.ExternalScript.BlinkingGearUP`. These custom properties can be accessed just by exactly these names. +
This allows you to convert values inside of SimHub with the help of NCalc for usage in external applications.


[#help,reftext=Help]
=== "Help"

This is the current output of the command `help`:

Expand Down

0 comments on commit 2839224

Please sign in to comment.