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

Configuration system #5

Open
scalablecory opened this issue Sep 24, 2021 · 8 comments
Open

Configuration system #5

scalablecory opened this issue Sep 24, 2021 · 8 comments
Assignees
Labels
approved An enhancement has been approved and PRs are welcome enhancement New feature or request
Milestone

Comments

@scalablecory
Copy link
Owner

scalablecory commented Sep 24, 2021

Aether requires a configuration system. It must be able to save to a file to survive reboots.

This would describe things like:

  • What sensors are enabled.
    • I2C/etc. addresses.
    • Dependencies.
  • What displays are enabled.
    • SPI/etc. addresses.
    • Which theme to use, and what measurements/sensors to use in the theme.
@scalablecory scalablecory added enhancement New feature or request approved An enhancement has been approved and PRs are welcome labels Sep 24, 2021
@scalablecory scalablecory added this to the 1.0 milestone Sep 24, 2021
@AkwasiAkosah
Copy link

Configuration. Happy to learn

@scalablecory
Copy link
Owner Author

So the basic goal here is to store all the things needed to instantiate the app.

The instantiation itself might be handled by the glue layer, but needs to inform the design of the config.

  1. Open each configured device as an I2cDevice.
  2. Open each sensor against its I2cDevice.
    • Note sensors can depend on each-other, e.g. the SCD4x takes a dependency on barometric pressure to calibrate itself. So, sensors must be initialized in order of dependency and config must track these dependencies.
  3. Open display devices (e-Ink screen, RGB strips, Zigbee, etc.)
  4. Bind sensors to display devices.

@AkwasiAkosah
Copy link

Are there any suggestions on how to start? Or any documentation you think might be helpful.
Sorry if I am asking too many questions

@scalablecory
Copy link
Owner Author

scalablecory commented Oct 12, 2021

From a high level, I think the in-memory config could look something like this:

class RootConfig
{
    List<SensorConfig> Sensors;
    List<Theme> Themes;
}

class SensorConfig
{
    // used to lookup a sensor factory.
    string SensorName;

    GpioAddress Address;

    // used by e.g. the SCD40 that has a dependency on barometric pressure.
    List<Measure> Dependencies;
}

class GpioAddress
{
    int? I2cBus;
    int? I2cAddress;

    // I2C is well defined right now, but others aren't. We can add on later.
    // int? SpiAddress;
    // List<int> GpioPins;
}

class ThemeConfig
{
    // used to lookup a theme factory similar to SensorInfo factories; not made yet.
    string ThemeName;

    // a theme can have more than one spot to draw a measurement; this fills in the spots.
    List<Measure> ThemeMeasures;

    // the display to run the theme on.
    DisplayConfig Display;
}

class DisplayConfig
{
    // used to lookup a display factory similar to SensorInfo factories; not made yet.
    string DisplayName;
    GpioAddress Address;
}

And then we'll need some sort of load/save mechanism that the glue layer can call:

RootConfig config = await RootConfig.LoadAsync("path/to/config.txt");
await RootConfig.SaveAsync("path/to/config.txt", config);

In existing code, you can see an I2C sensor factory being looked up by name and instantiated, by user-provided "config" (command line inputs) here:

testi2cSensorCommand.Handler = CommandHandler.Create((string name, uint bus, uint address) => RunAndPrintSensorAsync(() =>
{
SensorInfo? sensorInfo = SensorInfo.Sensors.FirstOrDefault(x => x is I2cSensorInfo && string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase));
return sensorInfo switch
{
I2cSensorInfo i2c => i2c.OpenDevice((int)bus, (int)address, Observable.Empty<Measurement>()),
_ => throw new Exception("An I2C sensor by that name was not found.")
};
}));

And binding a theme to a display and (as a Subject) sensor output:

var lines = new[] { Measure.CO2, Measure.Humidity, Measure.BarometricPressure, Measure.Temperature };
using var driver = new SimulatedDisplayDriver("out", 296, 128, 112.399461802960f, 111.917383820998f);
using var sub = new Subject<Measurement>();
using IDisposable theme = MultiLineTheme.CreateTheme(driver, lines, sub);

@AkwasiAkosah
Copy link

I am assuming all these properties for the various classes should allow for getting and setting the values

@AkwasiAkosah
Copy link

In the process of initializing a giving sensor, is it safe to assume that the user knows information about the address of the device, name and other properties needed to establish the communication? Are we using any pre-made methods?

@scalablecory
Copy link
Owner Author

In the process of initializing a giving sensor, is it safe to assume that the user knows information about the address of the device, name and other properties needed to establish the communication? Are we using any pre-made methods?

If they are editing the file directly, yes.

If they use UI, some additional help is available: the SensorInfo class contains metadata for sensors like default address. SensorInfo.Sensors contains all supported sensors. Many users will be able to select their sensors from a list and use the defaults. Metadata for themes and displays will exist for the same usage.

@scalablecory
Copy link
Owner Author

scalablecory commented Oct 13, 2021

/// <summary>
/// A collection of all sensors supported by Aether.
/// </summary>
public static IReadOnlyList<SensorInfo> Sensors { get; } = new List<SensorInfo>
{
new ConcreteI2CSensorInfo<ObservableScd4x>(),
new ConcreteI2CSensorInfo<ObservableSht4x>(),
new SimulatedI2cSensorInfo<ObservableMs5637>()
};

public abstract string Manufacturer { get; }
public abstract string Name { get; }
public abstract string Uri { get; }
public abstract IEnumerable<MeasureInfo> Measures { get; }
public abstract IEnumerable<SensorDependency> Dependencies { get; }
public abstract IEnumerable<SensorCommand> Commands { get; }

internal abstract class I2cSensorInfo : SensorInfo
{
public abstract int DefaultAddress { get; }
public abstract ObservableSensor OpenSensor(I2cDevice device, IObservable<Measurement> dependencies);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved An enhancement has been approved and PRs are welcome enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants