TLCProF is an open source framework for programming vehicle dependent traffic light controller software. The abbreviation TLCProF stands for: Traffic Light Controller Programming Framework. The project is currently in beta phase, and both its name and code base may undergo considerable changes in the near future.
The framework has been written in C#. It was originally made with .NET Framework and compatible with Mono, and was therefore from the onset compatible with Linux. The current version (as of 27th of Feb. 2025) targets .NET8. The controller UI was not tested on platforms other than Windows 11. The headless version runs fine on Linux (tested on Ubuntu).
Please note: the framework is the result of a pilot project, and has never been extensively used in a production environment. Use at your own risk, the authors of the framework assume no liability whatsoever; also refer to the LICENSE.
Contents of the README:
TLCProF consists of three parts:
- CodingConnected.TLCProF: contains the actual traffic light controller algorithms. It also provides some common functionality, such as a simple "controller host" that allows running a controller process in a loop, a command parser class and a xml deserialization class.
- CodingConnected.TLCProF.Sim: a relatively straightforward simulation module, allowing a semi-random simulation to be ran on a TLCProF controller.
- CodingConnected.TLCProF.BmpUI: provides a cross platform user interface, using a clickable bitmap. This is meant for testing purposes, to functionaly test and check a controller.
The example controller in Visual project "CodingConnected.TLCProF.ExampleController" provides a simple example that combines all three parts to create a controller with a user interface that allows testing its functionality.
Make sure to add references to the libraries that are used in a project.
Semantically, a controller consists of two parts: its settings and state on the one hand, its logic and algorithms on the other. In TLCProF, roughly the following design decisions have been made:
- The settings and state reside in a single object of type
ControllerModel. This object and objects that are member of it do have logic, but that logic strictly (that is: as strictly as possible) partains to the object in question itself. For example, aSignalGroupModelexposes a methodHandleStateRequests(), that causes it to determine its own new inner state based on its settings and requests it has received. This method is called by theControllerManager(see below). - Logic that operates on controller objects resides in manager classes, that inject their functinality into the main manager: an instance of
ControllerManager. For example, a manager causes a request for green to be set on a signalgroup based on the state of its detection. this could be done inside theSignalGroupModelobject,
In practice this distinction is now always as strict as is described here. However, this design facilitates a modular setup, allowing easy addition without the need to change existing functionality. Also, a certain functionality can be programmed in a single place, which makes code changes trivial, while maintaining a good overview. Finally, unit testing (yet absent) of functionality is made possible.
To use the library to program a controller, an object of type ControllerModel should be created first. This object holds all relevant data and settings, and represents the state of the controlling process at a given moment in time. This can either be done manually, or by deserializing from XML. Below are two basic exmaples; please refer to the user manual and framework specification for more detailed descriptions of the various settings and functionalities TLCProF provides.
First, instantiate a new controller model:
var controller = new ControllerModel();
Next, create some signalgroups and add them to the controller:
var sg02 = new SignalGroupModel("02", greengar: 40, greenfix: 50, greenex: 250, amber:
30, redgar: 10, redfix: 20, headmax: 100);
Controller.SignalGroups.Add(sg02);
This example uses named arguments to clarify the code, which of course is not mandatory. Note that not all settings for a signalgroup are set via its constructor. For example, the properties FixedRequest and FixedRequestDelay can be used to cause a signal group to always have a (delayed or direct) request. A typical signalgroup will have one or more detectors that cause a request for green:
sg02.Detectors.Add(new DetectorModel("021a", request:
DetectorRequestTypeEnum.RedNonGuaranteed, extend: DetectorExtendingTypeEnum.HeadMax,
occupied: 10, gap: 30, errorhi: 20, errorlo: 360));
Add intergreen times to the signalgroups to build the conflict matrix. Note that the resulting matrix should be symmetrivc, otherwise an exception will be raised.
sg02.InterGreenTimes.Add(new InterGreenTimeModel("02", "05", 30));
The current version of TLCProF provides a block structure as a mechanism to take runtime decisions about order of realistions of signalgroups. To build this structure:
var block1 = new BlockModel("B1");
block1.AddSignalGroup("02");
controller.BlockStructure.Blocks.Add(block1);
Controller.BlockStructure.WaitingBlockName = "B1";
The controller object now has a very basic, but complete and working setup.
Instead of coding a ControllerModel object by hand, it can be instantiated by deserializing from XML. The example controller uses this mecahnism. For example:
var ser = new TLCPROFSerializer();
var filename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "tint1_tlcprof.xml");
controller = ser.DeserializeController(filename);
Of course, some exception handling should be in place here to check existence and integrity of the file.
Note: for Mono compatibility, the
TLCPROFSerializerclass internally usesDataContractSerializer. This means the XML should match exactly with the C# classes. Even a very minor deviation will cause an exeption to be raised. Therefor, it is advised not to write an XML by hand, but instead use a generator (such as this one) to compose the XML.
To generate a ControllerModel XML file using the application TLCGen, do the following:
- Download TLCGen from here
- Build the project CodingConnected.TLCProF.TLCGenGen (see below for building from source)
- Copy the resulting file "CodingConnected.TLCProF.TLCGenGen.dll" to the Plugins folder in the folder where TLCGen.exe is located
- Copy file "CodingConnected.TLCProF.dll" to the folder where TLCGen.exe is located
- You can now select TLCProF as a generator from within TLCGen, and generate TLCProF XML files with the application
The ControllerModel does nothing by itself, but instead is "managed" by a class that will cause state changes based on input state, and call relevant methods on objects inside the controller when a control step is taken. To instantiate the manager:
ControllerManager Manager = new ControllerManager(Controller);
The manager exposes a method ExecuteStep() that will "move" the controlling proces forward a giving amount of miliseconds. This method is typically called in a loop, for example by using an instance of SimpleControllerHost as is descibed next.
the controller object cannot "run itself", but instead needs to be hosted. TLCProF provides a relatively simple class to help with this:
var host = new SimpleControllerHost(controllermanager, null, 100, 100, true, false);
host.StartController();
var s = "";
while (s != "exit") { s = Console.ReadLine(); }
Please refer to the TLCProF specification for more details about this class and the meaning of it constructor arguments.
To build the project, download or clone the sources and use an IDE compatible with .NET8 to compile the project. Note that you need to restore dependencies first.
The sources can be downloaded as a zip, but preferably are downloaded via git from within Visual Studio, so staying up to date is easier. To do this, click menu Team > Manage connections. In the Manage Connections dropdown menu, select Connect to Project. Sign in with your Visual Studio account, and close the CodingConnected.TLCProF project. This will create a local clone of the repository, which can be easily kept up to date with remote code changes.
The project uses Paket as its package manager. To build from source, we first need to restore any dependencies.
- Use the menu Tools > Nuget Packet Manager > Packet Manager Console, to open the console.
- Follow the instructions on the Paket website to install Paket as a dotnet global tool
- Use paket to restore any dependencies:
dotnet paket restore(or install or update) - A
dotnet restoremight be needed beforehand
Now that dependencies have been restored, we can build the solution.
- Rebuild the solution via menu Build > Rebuild Solution. The build should succeed without errors.
- To check that everything worked, run the example controller, by setting the project "CodingConnected.TLCProF.ExampleController" as Startup Project (in the context menu that appears after a right mouseclick on the project). Now use the menu Debug > Start Without Debugging to start the project (or use the shortcut key Ctrl-F5)