'Cyclic transfer of Object
I'm trying to model blood flow. As simple as it gets I am trying to have an event (TimerTick) trigger the transfer of a resource object from unit A to unit B, B's to C, C's to A. I can't seem to have reproducible transfer of the resources from one iteration to another, and I've tried a lot more of different ways than what's pasted below. The units are connected as a triangle after construction. I'm aware that this leaves the first unit to be erroneous, and have tried numerous ways to deal with it (and I'm not sure if that is the only problem) but since none have worked I won't post them. An idea of event set-up where any one unit doesn't need to know about any others would be totally appreciated. But just a way to make this work would also be appreciated.
class Unit{
Resource currentResource;
Resource incomingResource;
Unit preUnit;
Unit postUnit;
public Unit( int resource, Timer t)
{
this.currentResource = new Resource(resource);
t.TimerTick += T_TimerTick;
}
private void T_TimerTick(object sender, TimerTickEventArgs e)
{
postUnit.receiveResource(currentResource);
currentResource = incomingResource;
}
void receiveResource(Resource resource)
{
incomingResource = resource;
}
//pre and post units connected appropriately to Cycle ...-A-B-C-A-...
// with one cycle per TimerTick
}
Solution 1:[1]
I would suggest Inversion of control with Observer pattern
Here is a working setup, to explain my suggestion. In my code, each unit just increments the resource by 1.
And gererates the following output
*Initiating Unit A value 1
Unit A, output 1
Unit B, input 1
Unit B, output 2
Unit C, input 2
Unit C, output 3
Unit A, input 3
Unit A, Cycle complete*
client code
IInitiatorUnit A = new InitiatorUnit("A");
IUnit B = new Unit("B");
IUnit C = new Unit("C");
B.RegisterUnit(A); // B is listening to A
C.RegisterUnit(B); // C is listening to B
A.RegisterUnit(C); // A is listinig to C
void heartBeatTimer_Tick(object sender, EventArgs e)
{
A.GenerateResource(1); // Start the process
}
I have designed unit such that it takes an input, processes it & fires an event for
interface IUnit
{
event EventHandler<ResourceGeneratedEventArgs> ResourceGenerated;
void RegisterUnit(IUnit inputUnit);
string Name { get; }
}
class Unit : IUnit
{
IUnit mInputUnit;
public event EventHandler<ResourceGeneratedEventArgs> ResourceGenerated;
public string Name { get; private set; }
public Unit(string name)
{
this.Name = name;
}
public void RegisterUnit(IUnit inputUnit)
{
this.mInputUnit = inputUnit;
this.mInputUnit.ResourceGenerated += mInputUnitResourceGenrated;
}
void mInputUnitResourceGenrated(object sender, ResourceGeneratedEventArgs inputResource)
{
//take the input resorce. pocess it & fire the event;
//I'm just adding 1 to it as sample
int outputResource = inputResource.Resource + 1;
Console.WriteLine("Unit {0}, input {1} ", this.Name, inputResource.Resource, outputResource);
OnResourceGenerated(outputResource);
}
protected virtual void OnResourceGenerated(int outputResource)
{
Console.WriteLine("Unit {0}, output {1}", this.Name, outputResource);
if (ResourceGenerated != null)
ResourceGenerated(this, new ResourceGeneratedEventArgs(outputResource));
}
}
public class ResourceGeneratedEventArgs : EventArgs
{
public ResourceGeneratedEventArgs(int resource)
{
Resource = resource;
}
public int Resource { get; private set; }
}
/// <summary>
/// This is just to start the process here, Nothing great here
/// </summary>
interface IInitiatorUnit : IUnit
{
void GenerateResource(int initialValue);
}
class InitiatorUnit : Unit, IInitiatorUnit
{
//prevents the cycle from going on & on
bool resetFlag;
public InitiatorUnit(string name):base(name)
{
}
public void GenerateResource(int initialValue)
{
resetFlag = false;
Console.WriteLine("Initiating Unit {0} value {1}", this.Name, initialValue);
OnResourceGenerated(initialValue);
}
protected override void OnResourceGenerated(int outputResource)
{
//Dont raise the event. if the cycle has completed
if (resetFlag == false)
{
resetFlag = true;
base.OnResourceGenerated(outputResource);
}
else
{
//do nothing, cycle has completed
Console.WriteLine("Unit {0}, Cycle complete", this.Name);
}
}
}
Edit 1 : Approach for very large number of units
In my 1st approach, each unit called the subsequent one creating a chain, something like this -
As the nodes would increase so would be the depth of call stack, therefore when the number reaches in thousands we can overrun the stack limit causing a stack over flow.
Therefore the new approach is a just uses an iterates over the units, giving each unit a request, getting a response back and feeding the response to the subsequent unit as request. Something like this -
Here is a working sample & it generates the following output -
Unit A, input 0 Unit A, output 1 Unit B, input 1 Unit B, output 2 Unit C, input 2 Unit C, output 3 Cycle completed result 3
Client Code
private void ClientCode()
{
// Create an array of units
IUnit[] units = new IUnit[] {
new Unit("A"), // A will be called 1st . It needs to be given an initial value to start processing
new Unit("B"), // B will get A's Output to process.
new Unit("C"), // C will get B's Output to process.
};
// pass the array to workflow to process
cycle = new WorkFlow(units);
Console.ReadLine();
}
Heart beat timer
void heartBeatTimer_Tick(object sender, EventArgs e)
{
var result = cycle.Execute(new Resource(0)); // starting the cycle with initial value of 0
Console.WriteLine("Cycle completed result {0}", result.Value);
}
The infrastructure is much simpler now,
interface IUnit
{
Resource ProcessResource(Resource inputResource);
string Name { get; }
}
class Unit : IUnit
{
public string Name { get; private set; }
public Unit(string name)
{
this.Name = name;
}
public Resource ProcessResource(Resource inputResource)
{
//take the input resorce. pocess it & fire the event;
//I'm just adding 1 to it as sample
int outputResource = inputResource.Value + 1;
Console.WriteLine("Unit {0}, input {1} ", this.Name, inputResource.Value);
Console.WriteLine("Unit {0}, output {1} ", this.Name, outputResource);
return new Resource(outputResource);
}
}
class WorkFlow
{
IUnit[] mUnits;
public WorkFlow(IUnit[] units)
{
this.mUnits = units;
}
public Resource Execute(Resource initiatingResource)
{
Resource result = initiatingResource; // initialise result with the input of the cycle.
for (int i = 0; i < mUnits.Length; i++)
{
// the result is passed as input.
//IUnit.ProcessResource function gives back a new result which is encached as input for subsequent resource
result = mUnits[i].ProcessResource(result);
}
return result; // after all are processed,
}
}
public class Resource
{
public Resource(int resourceValue)
{
Value = resourceValue;
}
public int Value { get; private set; }
}
Hope all will work fine now. Please write me a comment if any bit is unclear.
Solution 2:[2]
Are those elements and method named, Unit, within a class which is also named Unit?
Try moving the methods and elements to another class.
I would make three static classes for your units:
public static class UnitA
{
var foo1;
var foo2;
}
public static class UnitB
{
var foo1;
var foo2;
}
public static class UnitC
{
var foo1;
var foo2;
}
For each tic, you can handle the resources from any other class:
FooHandler_Tick
{
// Store C's content.
var tempFoo1 = UnitC.foo1;
var tempFoo2 = UnitC.foo2;
// B to C.
UnitC.foo1 = UnitB.foo1;
UnitC.foo2 = UnitB.foo2;
// A to B.
UnitB.foo1 = UnitA.foo1;
UnitB.foo2 = UnitA.foo2;
// C to A.
UnitC.foo1 = tempFoo1;
UnitC.foo2 = tempFoo2;
}
Does this work with your project?
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Tyler Pantuso |