-
Notifications
You must be signed in to change notification settings - Fork 11
SafetySharp.Odp
SafetySharp.Odp is a library that facilitates the modeling of self-organizing systems following the Organic Design Pattern (ODP) in S#. It provides base classes to extend, interfaces to implement and default implementations for several typical problems.
To create a new ODP-based model, create a new S# project and add SafetySharp.Odp as reference. You will need the following basic components:
- agents
- resources
- capabilities
- task
An ODP-based system consists of agents that process resources by applying capabilities to them, in the order defined by a task (for more details on these concepts refer to [x]).
We'll begin by extending the BaseAgent
class to define a type of agent. It is an abstract class with many different properties and methods, but the only abstract member is public IEnumerable<ICapability> AvailableCapabilities { get; }
, which defines the capabilities the respective agent can apply. Since we haven't defined any custom capabilities so far, we'll use 2 simple builtin ones:
using SafetySharp.Odp;
using System.Collections.Generic;
class MyAgent : BaseAgent
{
public override IEnumerable<ICapability> AvailableCapabilities { get; } =
new ICapability[] { new ProduceCapability(), new ConsumeCapability() };
}
Our case study can produce and consume resources, but isn't able to do anything else with them yet.
So our next step will be to define a custom capability - we could implement ICapability
ourselves, but in many cases it's easier to extend the Capability<T>
class:
class DoStuffCapability : Capability<DoStuffCapability>
{
public override CapabilityType CapabilityType { get; } = CapabilityType.Process;
}
We've simply defined a new class and specified that it's a process capability, i.e., it neither produces nor consumes a resource - instead it receives one, modifies it and returns it. If we had decided to implement ICapability
directly, we would have had to implement its void Execute(BaseAgent baseAgent)
method to define how exactly the resource is modified. With the Capability<T>
class, this is instead defined in the agent that actually processes the resource.
Before we start defining that part, let's first create a resource type:
class MyResource : Resource
{
public int Counter { get; set; } = 0;
public void OnProduced(ITask task)
{
Task = task;
}
}
We extend Resource
- there are no abstract members to implement, but we add a counter for our case study, and a method to inform the resource about its task.
Now, we will extend our agent to implement the capabilities it claims to have:
using SafetySharp.Odp;
using System.Collections.Generic;
class MyAgent : BaseAgent, ICapabilityHandler<ProduceCapability>, ICapabilityHandler<ConsumeCapability>, ICapabilityHandler<DoStuffCapability>
{
// since we can't dynamically create new objects in S#,
// we have this buffer of available resources
private readonly List<MyResource> _resources = new List<MyResource>() {
new MyResource(), new MyResource(), new MyResource(), new MyResource(), new MyResource()
}
public override IEnumerable<ICapability> AvailableCapabilities { get; } = new ICapability[] {
new ProduceCapability(),
new ConsumeCapability(),
new DoStuffCapability()
};
public void ApplyCapability(ProduceCapability produce)
{
Resource = _resources[_resources.Length-1];
Resource.OnProduced(RoleExecutor.Task); // RoleExecutor.Task is the current task
Resource.OnCapabilityApplied(produce);
_resources.RemoveAt(_resources.Length-1);
// TODO: once _resources is empty, we loose ProduceCapability
}
public void ApplyCapability(ConsumeCapability consume)
{
Resource.OnCapabilityApplied(consume);
Resource = null;
}
public void ApplyCapability(DoStuffCapability doStuff)
{
((MyResource)Resource).Counter++; // increment the counter
Resource.OnCapabilityApplied(doStuff); // never forget to call this
}
}
We implement ICapabilityHandler
for each type of capability we want to support. The resource to modify / produce / consume is available in the Resource
property.
The final missing concept is the task. We use a very simple implementation of the ITask
interface:
class MyTask : ITask
{
public bool IsCompleted { get; } = false; // never-ending task
public ICapability[] RequiredCapabilities { get; }
public MyTask(params ICapability[] capabilities)
{
RequiredCapabilities = capabilities;
}
}
Now that we have all the basic concepts needed, we just need to put them together and package them in a model:
using SafetySharp.Modeling;
using SafetySharp.Odp;
class MyModel : ModelBase
{
public MyModel()
{
var task = new MyTask(new ProduceCapability(), new DoStuffCapability(),
new DoStuffCapability(), new ConsumeCapability());
}
// TODO: create and configure agents, connect, add reconf
// TODO: add agents as components
}
Done! The model can now be simulated. For safety-checking, you can just add faults as you would for any other S# model.
TODO
TODO
TODO
TODO
TODO
Once a deficit in the current configuration is detected, reconfiguration is achieved by BaseAgents through usage of IReconfigurationStrategy implementations. There is a variety of implementations and parametrizations of these implementations. Three aspects must be considered:
- distribution: once a configuration is calculated, how is it distributed to agents?
- involved agents: a reconfiguration can be centralized / global, or it can be localized in a small part of the system.
- solution computation: given a problem and the set of involved agents, the actual reconfiguration solution can now be computed.
Each of these aspects is encapsulated in an interface. The implementations are parametrized with the strategy for the next aspect, respectively, which they orchestrate and whose results they process. Naming and exact responsibilities of these interfaces are probably not ideal yet.
IReconfigurationStrategy, the top-most interface, encapsulates the distribution aspect. There are 2 implementations: CentralReconfiguration (quite misnamed) simply assigns the computed roles directly to the respective agents. ReconfigurationAgentHandler on the other hand manages a set of reconfiguration agents for each BaseAgent, which communicate with their counterparts at other BaseAgents, in the end receive the role updates for their associated BaseAgent and apply them.
The selection of involved agents is the responsibility of the IController interface. This can either be a CentralizedController, which uses all currently available (i.e. not dead) agents in the system. Alternatively, CoalitionFormationController starts out with the BaseAgent that detected a misconfiguration and tries to only involve as many agents as necessary to find a solution.
Lastly, IConfigurationFinder attempts to solve the given configuration problem, using only the BaseAgents chosen by the enclosing IController instance. It finds a distribution (a sequence of BaseAgents that have the given sequence of required capabilities) and a resource flow (a sequence of connected agents, of which the distribution is a sub-sequence). These are then converted by the IController to role updates.
TODO: implementations
Although ideally all three aspects should be independent, there is some coupling. Specifically:
- Synchronicity: Some classes require their subcomponents to be synchronous implementations despite their asynchronous interfaces - such as ControllerReconfigurationAgent. This should be changed but will probably require some refactoring of interfaces, or usage of MicrostepScheduler.
- Coalitions: MinimalChangeDistributionFinder can only be used within a CoalitionFormationController as it requires access to and modifies the coalition.
TODO:
- async / sync
- single instance vs per-agent instances
- case-study adaptations & compositional controllers
IReconfigurationStrategy <|-----------+-------------------------------+
| |
CentralReconfiguration ReconfigurationAgentHandler
| |
| v
| IReconfigurationAgent <|---------+--------------------------------+
| | |
| ControllerReconfigurationAgent CoalitionReconfigurationAgent
| | | ^
+-----------------------+--------------------------------------------------+--------------------------------+ |
| |
v |
IController <|------------------+------------------------------------------------------------------------------------+ |
| | ^
CentralizedController CoalitionFormationController
| | ^
+-----------------------+---------------------------------------------------------------------------------+ |
| |
v |
IConfigurationFinder <|-------------+--------------------------------------------------------------+---------------------------+ |
| | | ^
FastConfigurationFinder <|-- OptimalConfigurationFinder AbstractMiniZincConfigurationFinder MinimalChangeDistributionFinder
TODO