In the first part of this tutorial, we created a simple SignalF application. In the second part, I would now like to take a closer look at the configuration.

The configuration file

SignalF uses a configuration file based on XML. This is automatically read by the application when it is started.

Here is a short extract from the default configuration.

<?xml version="1.0" encoding="utf-8"?>
<?XMLDatabase schemaVersion=""?>

<ControllerConfiguration xsi:type="SignalF.Datamodel.Configuration.ControllerConfigurationType"
                         xmlns:xsi="" xmlns="urn:SignalF.1.0">
	<SignalProcessorConfigurations xsi:type="SignalF.Datamodel.Signals.SignalProcessorConfigurationListType">
		<SignalProcessorConfiguration xsi:type="SignalF.Datamodel.Workflow.ProcessControlConfigurationType"
		                              id="ID1f8177bc-b7d6-4332-bdb0-563f1d4831fa" Name="DefaultProcedure">
			<Configuration xsi:type="SignalF.Datamodel.Base.ConfigurationType" />
			<Definition xsi:type="SignalF.Datamodel.Signals.SignalProcessorDefinitionRefType"
			            idRef="IDeb24a346-dba1-4bc7-84b1-cdf86a86a3e4" />
			<SignalSources xsi:type="SignalF.Datamodel.Signals.SignalSourceConfigurationListType">
			<SignalSinks xsi:type="SignalF.Datamodel.Signals.SignalSinkConfigurationListType">
	<HardwareConfiguration xsi:type="SignalF.Datamodel.Hardware.HardwareConfigurationType">

Due to the size and high complexity of this file, manual creation of the configuration is very time-consuming and error-prone. I will therefore not go into the details here.

The datamodel

SignalF uses a comprehensive data model generated from the configuration schema files to access the configuration file.

Using the data model makes it much easier to create the configuration, but deep knowledge of the structure of the configuration is still required. One possible area of application would be, for example, the automated creation or customisation of configurations.

The fluent interface

SignalF comes with a fluent interface for simple and intuitive configuration.
However, before we can use this interface, we must integrate it into the application. To do this, we have to install the corresponding Nuget package.

Install-Package -IncludePrerelease SignalF.Extensions.Configuration

We then tell the application that we want to use the fluent interface.

        var hostBuilder = Host.CreateDefaultBuilder(args)
                              .ConfigureServices(services =>

In the next step, we create the class SystemConfiguration, which implements the ISystemConfiguration interface. This class will later contain the entire configuration of the system.

using System.Runtime.Versioning;
using SignalF.Configuration;
using SignalF.Controller.Configuration;
using SignalF.Datamodel.Configuration;
using Tutorial.Monitoring;
using Tutorial.Configuration;

namespace Tutorial.Configuration;

public class SystemConfiguration : ISystemConfiguration
    private readonly Func<ISignalFConfiguration> _configurationFactory;

    public SystemConfiguration(Func<ISignalFConfiguration> configurationFactory)
        _configurationFactory = configurationFactory;

    public void Configure(IControllerConfiguration configuration)
        var signalFConfiguration = _configurationFactory();

In order for the new configuration to be used, we need to register it in the application.

The Main() method should now look like this:

    public static async Task Main(string[] args)
        var hostBuilder = Host.CreateDefaultBuilder(args)
                              .ConfigureServices(services =>
                                  services.AddTransient<ISystemConfiguration, SystemConfiguration>();

        var host = hostBuilder.Build();

        await host.RunAsync();

The next time the SignalF application is started, it no longer uses the default configuration, but a configuration generated at runtime - which is still empty.🙂