How can I pass variable to Action? - c#

below is the code sample, I want the components in the queue to take the local variable "entity", how can I achive this? thx
private void DoComparison(StuffEntity entity)
{
try
{
bool dataFlag = CheckIsNewData(entity.PickingTime, entity.WarningPeriod);
if (dataFlag)
{
Queue<Action<StuffEntity>> queue = new Queue<Action<StuffEntity>>();
//How can I let the queue stuff take the entity?
queue.Enqueue(DelaySendingMessageOut);
if (!QueueItem.ContainsKey(entity.FridgeID))
{
QueueItem.Add(entity.FridgeID, queue);
}
}
}
catch (Exception ex)
{
CommonUnity.WriteLog(ex.Message);
CommonUnity.WriteLog(ex.StackTrace);
}
}
private void DelaySendingMessageOut(StuffEntity entity)
{
int pendingPeroid = entity.PendingTime.ToInt();
if (pendingPeroid <= 0)
pendingPeroid = 5;
Thread.Sleep(pendingPeroid * 60 * 1000);
TriggerCheckingBeforeSendMessageOut(entity);
}

queue.Enqueue((e) => DelaySendingMessageOut(entity));
But you can use Queue<Action> since you will not use the argument e:
Queue<Action> queue = new Queue<Action>();
queue.Enqueue(() => DelaySendingMessageOut(entity));

Related

How to Use a Boolean to Compare Action<int>

I have a method as below
public void RequestTPCost(Action<int> callback, Action<int> enemyCallback)
{
if (OnTPSelectionNeeded == null)
{
callback?.Invoke(0);
enemyCallback?.Invoke(0);
return;
}
if (TurnSystem.Instance.IsPlayerTurn())
{
//Player turn use shooting unit is selected and target is enemy
OnTPSelectionNeeded?.Invoke(this, new TPSelectionInfo()
{
callback = callback,
enemyCallback = enemyCallback,
TPAvailable = selectedUnit.GetTacticalPoints(),
enemyTPAvailable = targetUnit.GetTacticalPoints(),
});
}
else
{
OnTPSelectionNeeded?.Invoke(this, new TPSelectionInfo()
{
callback = callback,
enemyCallback = enemyCallback,
TPAvailable = targetUnit.GetTacticalPoints(),
enemyTPAvailable = selectedUnit.GetTacticalPoints()
});
}
}
and I use it elsewhere like this
private void UnitActionSystem_OnTPSelectionNeeded(object sender, UnitActionSystem.TPSelectionInfo e)
{
maxTP = e.TPAvailable;
maxEnemyTP = e.enemyTPAvailable;
callback = e.callback;
enemyCallback = e.enemyCallback;
TPAmount = 1;
enemyTPAmount = 0;
}
public void TPConfirm()
{
callback?.Invoke(TPAmount);
UnitActionSystem.Instance.GetSelectedUnit().
SpendTacticalPoints(TPAmount);
enemyTPAmount = Random.Range(0, (maxEnemyTP + 1));
enemyCallback?.Invoke(enemyTPAmount);
UnitActionSystem.Instance.GetTargetUnit().
SpendTacticalPoints(enemyTPAmount);
}
private void NextState()
{
switch (state)
{
case State.Aiming:
state = State.Choosing;
stateTimer = 0.1f;
UnitActionSystem.Instance.RequestTPCost(ConfirmTPCost,
ConfirmEnemyTPCost);
break;
case State.Choosing:
state = State.Shooting;
float shootingStateTime = 0.1f; //Not a frame
stateTimer = shootingStateTime;
break;
case State.Shooting:
state = State.Cooloff;
float coolOffStateTime = 0.5f;
stateTimer = coolOffStateTime;
Debug.Log("Shooting");
break;
case State.Cooloff:
ActionComplete();
break;
}
}
private void ConfirmTPCost(int value)
{
Debug.Log($"TP = {value}");
NextState();
}
private void ConfirmEnemyTPCost(int value)
{
Debug.Log($"EnemyTP = {value}");
//NextState();
}
Now I want to check if ConfirmTPCost < ConfirmEnemyTPCost but am not sure how to simply do that.
I have a roundabout way using events and more UnityActionSystem functions to call the instance and work through that but it seems very clunky and prone to breaking. Is there a better way to get these values and check which is more?
var cost = 0;
var enemyCost = 0;
UnitActionSystem.Instance.RequestTPCost(i => cost = i, i => enemyCost = i);
Debug.Log($"TP = {cost}");
Debug.Log($"EnemyTP = {enemyCost}");
if (cost < enemyCost)
{
//Do your stuff here
}
As said it sounds like there is actually some asynchronous parts involved.
So you either will want to properly implement async and await patterns and wait for both values to be available.
Or alternatively you could somewhat fake that behavior using e.g. nullables and a local function ike
int? cost = null;
int? enemyCost = null;
UnitActionSystem.Instance.RequestTPCost(i =>
{
cost = i;
if(enemyCost != null)
{
Validate();
}
}, i =>
{
enemyCost = i;
if(cost != null)
{
Validate();
}
});
// Can be a local function
// otherwise pass in the two int values via parameters
void Validate()
{
Debug.Log($"TP = {cost.Value}");
Debug.Log($"EnemyTP = {enemyCost.Value}");
if (cost.Value < enemyCost.Value)
{
//Do your stuff here
}
}
Idea here: No matter which of both callbacks fires first, it is ignored and only for the second one both values are already available and the actual handler method can be invoked

Attempt and retry

How can I formalize this to be more generic, where I can specify an X exceptions to throw and X exceptions to try again all while improving the code readability.
private const int RetrySegmentCount = 3;
private const int SecondsBetweenRetry = 30;
var retryCounter = 0;
while (true)
{
try
{
ExecuteProcessThatMayThrow();
break;
}
catch (NotSupportedException) // Do no retry if this is thrown
{
throw;
}
catch (Exception)
{
if (retryCounter < RetrySegmentCount)
{
retryCounter++;
Thread.Sleep(SecondsBetweenRetry * 1000);
}
else
{
throw;
}
}
}
An ideal syntax in puesdocode might be
Repeat(3, 30, [NotSupportedException], [Exception]) => ExecuteProcessThatMayThrow();
Repeat(3, 30) => ExecuteProcessThatMayThrow(); // This will repeat on all
Repeat(3, 30, [NotSupportedException, VeryBadException], [RetryableException]) => ExecuteProcessThatMayThrow();
You can create a reusable method that has multiple result depending on the error type. Here a small modified version of what i use
This method handles the different conditions and retry
public static bool TryExecute(Action action, int retry, int secondBeforeRetry, List<Type> notSupportedExceptions, List<Type> veryBadExceptions, List<Type> retryableExceptions)
{
var success = false;
// keep trying to run the action
for (int i = 0; i < retry; i++)
{
try
{
// run action
action.Invoke();
// if it reached here it was successful
success = true;
// break the loop
break;
}
catch (Exception ex)
{
// if the exception is not retryable
if (!retryableExceptions.Contains(ex.GetType()))
{
// if its a not supported exception
if (notSupportedExceptions.Contains(ex.GetType()))
{
throw new Exception("No supported");
}
else if (veryBadExceptions.Contains(ex.GetType()))
{
throw new Exception("Very bad");
}
}
else
{
System.Threading.Thread.Sleep(secondBeforeRetry * 1000);
}
}
}
return success;
}
To call this method it before very easy as they can all be easily change to optional parameters. here is and example :
// sample action that force an error to be thrown
var a = new Action(() =>
{
var test = "";
var test2 = test[3]; // throw out of range exception
});
try
{
var success = TryExecute(a, 5, 30, new List<Type>() { typeof(IndexOutOfRangeException) }, new List<Type>(), new List<Type>());
}
catch (Exception ex)
{
// handle whatever you want
}

Cassandra CSharp driver batch statement failing

I am trying to use a batch statement instead of a single binded insert statement.
Even though this is a very small change this fails and I am not looking for a good way for the error handling and to find out which part is the issue. One issue is definetly that the Java API has a getStatements method which is missing in the C# driver.
The pseudo code looks like this:
private BatchStatement batchStatement = new BatchStatement();
private const int blockingFactor = 5;
private int i = 0;
private object locker = new object();
public CassandraBufferHandler()
{
Cluster = Cluster.Builder().AddContactPoints("localhost").Build();
Session = Cluster.Connect("my_keyspace");
InsertStatement = Session.Prepare("Insert into ticks (instrumentcode, timestamp, type, exchange, price, volume) values(?,?,?,?,?,?) if not exists;");
}
public void OnEvent(TickCassandra tickCassandra, long sequence, bool endOfBatch)
{
try
{
lock (locker)
batchStatement.Add(
InsertStatement.Bind(tickCassandra.Instrumentcode,
tickCassandra.Timestamp,
tickCassandra.Type,
tickCassandra.Exchange,
tickCassandra.Price,
tickCassandra.Volume));
if (i++ % blockingFactor == 0)
{
BatchStatement tmp;
lock (locker)
{
tmp = batchStatement;
tmp.EnableTracing();
batchStatement = new BatchStatement();
}
Session.ExecuteAsync(tmp).ContinueWith(t =>
{
if (t.Exception != null)
{
ErrorCount++;
Log.Error(t.Exception.Message + tmp.ToString());
}
else
InsertCount++;
});
}
}
catch (Exception ex)
{
Log.Error("Exception:" + ex);
Active = false;
}

using do-while and skipping the next lines

I want to run a method _doing() that loops infinitely until a shutdownEvent is triggered. This is basically executed/started on a new Thread(). But I need not _doSomething if it is true somewhere. what should i do after if (_doSomething)? Code snippet below. Thanks.
private HighResolutionLapseTimer _lapseTimer;
private int _timeToNext
{
get
{
int lapseTime = _lapseTimer.LapseTime();
int next = DO_PERIOD - lapseTime;
if (next > 0)
{
return next;
}
else
{
return 1;
}
}
}
int DO_PERIOD = 60000;
private void _doing()
{
int _nextDoing = DO_PERIOD;
Thread.Sleep(_nextDoing);
do
{
LogInfo("Starting _doing");
lock (this)
{
if (_doSomething)
{
// skip this time because we are currently archiving
}
_doSomething = true;
}
try
{
_lapseTimer.Start();
DoSomethingHere(); //takes long processing
}
catch (Exception ex)
{
LogException(ex);
}
finally
{
lock (this)
{
_nextDoing = (int)_timeToNext;
_doSomething = false;
}
}
} while (!shutdownWaitHandle.WaitOne(_nextDoing, false));
LogInfo("Stopping _doing");
}
You could use the continue; statement.
Ohw! I just realized that a do-while is similar to a while. And to 'skip' the execution, you just have to use the continue; if the if statement is true

How to pause the "for" loop until getting response

I am using a for loop for making Calls for a list of numbers.
I want to take the first number from the list and make a call and to wait for the response and then proceed to the next number in the list.
I have used AutoResetEvent to do this.But it is not working.
for (int k = 0; k < list_Items.Count; k++) {
Number_To_Call = "9" + list_Items[k].ToString();
phoneCall.Start();
waitingToPickUp.Set(); //AutoReset Event
Thread.Sleep();
waitingToPickUp.WaitOne();
string detector = VoiceDetected;
if (detector == "Machine") {
//code
} else if (detector == "Human") {
//code
} else {
//code
}
}
Code for getting response form the call
void phoneCall_CallStateChanged(object sender, VoIPEventArgs<CallState> e)
{
if (e.Item.IsInCall())
{
phoneCallAudioReceiver.AttachToCall(phoneCall);
phoneCallAudioSender.AttachToCall(phoneCall);
manchineDetector.Start();
waitingToPickUp.Set();
string str = VoiceDetected;
}
else if (e.Item.IsCallEnded())
{
phoneCallAudioReceiver.Detach();
phoneCallAudioSender.Detach();
manchineDetector.Stop();
phoneCall = null;
//Number_To_Call = string.Empty;
InvokeOnGUIThread(() =>
{
Number_To_Call = string.Empty;
});
}
}
Code for Detecting Machine or Human
void manchineDetector_DetectionCompleted(object sender, VoIPEventArgs<AnswerMachineDetectionResult> e)
{
try
{
string VoiceDetected = e.Item.ToString();
}
catch (Exception ex)
{
}
}
Set and immediately WaitOne makes no sense - wait will not need to wait for anything and immediately continue.
Most likely should be reset-call-wait:
waitingToPickUp.Reset();
phoneCall.Start();
waitingToPickUp.WaitOne();

Categories