Modbus read and write value conflict - c#

My colleague and I working on project, where some physical devices are connected to Modbus interface(e.g. Lamp), and an desktop app where we sending requests to modbus using NModbus package, and also from time to time(e.g. after every 1 second) reading data from Modbus. We have a classic read/write conflict. Reading has no issue, but sometimes when we writing new value to modbus, the physical lamp going crazy and changing his state every 1 second.
Reading data from modbus is in different task, so do writing new values to modbus.
What we`ve tried:
lock critical section(only writing), and ignoring reading data when new value comes. After that we have a problem with queue and very slow working
CancellationToken - had no effect, or I writing it bad
Currently we have a class that collects property date of last state change(registered in IoC) and when new writing value comes, then we updating this property, and blocking from reading data from Modbus. Unfortunately this code sometimes working, sometimes not.
Please help. We are going crazy with this issue and we don`t know how to fix it.
Edit: I posting current code.
This is task, where we executing GetCurrentState handler
public void Start()
{
var cancellationToken = _cancellationTokenSource.Token;
Task.Run(async () =>
{
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(ReadStateDelayInMiliseconds).ConfigureAwait(false);
if ((DateTime.Now - _lastCommandExecutionTimeContainer.LastCommandExecutionTime).TotalMilliseconds < _userCommandThresholdInMiliseconds)
continue;
try
{
await _service.GetCurrentState().ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.Error($"Error while checking current state. Message '{ex.Message}', Stack trace: '{ex.StackTrace}'.");
}
}
}, cancellationToken);
}
This is GetCurrentState handler, where we reading data from Modbus
protected override IgnisLightState Handle(GetState request)
{
if ((DateTime.Now - _lastCommandExecutionTimeContainer.LastCommandExecutionTime).TotalMilliseconds <= _configuration.UserCommandThresholdInMiliseconds)
{
Logger.Debug($"Ignore before read state from lights, time after last execution: '{(DateTime.Now - _lastCommandExecutionTimeContainer.LastCommandExecutionTime).TotalMilliseconds}'.");
return _state;
}
var currentLightState = _state.DeepClone();
foreach (var head in currentLightState.Heads)
{
try
{
ushort startAddress = 0x0000;
ushort numberOfPointsToRead = 0x0006;
var values = _modbusMaster.ReadHoldingRegisters((byte)head.UniqueId, startAddress, numberOfPointsToRead);
var isOn = values[IgnisRegistry.On.Value];
var isEndo = values[IgnisRegistry.Endo.Value];
var isCentrum = values[IgnisRegistry.Centrum.Value];
var tempValue = values[IgnisRegistry.Temp.Value];
var illuminanceValue = values[IgnisRegistry.Vol.Value];
head.ColorValue = Convert.ToInt32(tempValue);
head.IlluminanceValue = Convert.ToInt32(illuminanceValue);
head.IsCentrumOn = Convert.ToBoolean(isCentrum);
head.IsEndoOn = Convert.ToBoolean(isEndo);
head.IsTurnedOn = Convert.ToBoolean(isOn);
if (currentLightState.CameraState != null &&
_configuration.CameraHeadId.HasValue &&
_configuration.CameraHeadId.Value == head.UniqueId)
{
var camMode = values[IgnisRegistry.Cam.Value];
currentLightState.CameraState.IsTurnedOn = Convert.ToBoolean(isOn);
currentLightState.CameraState.CurrentMode = (IgnisCameraMode)Convert.ToInt32(camMode);
}
}
catch (Exception ex)
{
Logger.ErrorFixed(ex, $"Error while getting data from headId {head.UniqueId}.");
}
}
if (_state.Equals(currentLightState)
|| (DateTime.Now - _lastCommandExecutionTimeContainer.LastCommandExecutionTime).TotalMilliseconds < _configuration.UserCommandThresholdInMiliseconds)
{
Logger.Debug($"Ignore after read state from lights, time after last execution: '{(DateTime.Now - _lastCommandExecutionTimeContainer.LastCommandExecutionTime).TotalMilliseconds}'.");
return _state;
}
foreach (var currentHeadState in currentLightState.Heads)
{
_lightHeadStateUpdater.UpdateState(currentHeadState);
}
Logger.Debug($"Broadcast new state to clients '{JsonConvert.SerializeObject(currentLightState)}'.");
_hubContext.Clients.All.StateChanged(currentLightState);
return currentLightState;
}
This is an Turn on lamp handler where we writing new value to modbus:
protected override Response Handle(TurnOn request)
{
_lastCommandExecutionTimeContainer.SetLastCommandExecutionTime(DateTime.Now);
if (request.HeadId <= 0
|| !_state.Heads.Any(x=>x.UniqueId == request.HeadId))
{
return ResponseFactory.FromError(Error.NotExist);
}
var headState = _state.Heads.Single(x=>x.UniqueId == request.HeadId);
if (headState.IsTurnedOn)
return ResponseFactory.FromError(Error.AlreadyAsRequested);
_modbusMaster.WriteSingleRegister((byte)request.HeadId, IgnisRegistry.On.Value, 0x0001);
headState.IsTurnedOn = true;
if (_state.CameraState != null &&
_ignisLightConfiguration.CameraHeadId.HasValue &&
_ignisLightConfiguration.CameraHeadId.Value == request.HeadId)
{
_state.CameraState.IsTurnedOn = true;
}
Logger.Trace($"Turn head lamp {request.HeadId} on.");
_hubContext.Clients.All.HeadStateChanged(headState);
return ResponseFactory.Success;
}

Related

Assign passed functions result to object with variable type in C#

For an integration I'm running as a service once a day, I need to assign the result of API-calls to local variables. However, those API's might at any time decide to throw a 401 error, in which case I just want to try again, up to three times.
I've got a functioning code to do that:
List<APIEntityProject> projectList = null;
private bool SetProjectList(){
const maxRetries = 3;
const RetryPause = 3000;
int retries = 0;
do
{
try
{
projectList = ProjApi.GetProject(activeWorkspace.WorkspaceCode);
}
catch (ApiException e)
{
if (e.ErrorCode == 401) // Unauthorized error (e.g. user doesn't have access to this Workspace
{
Log.Warning("Unauthorized error while fetching projects from Workspace, try {retries}",retries);
retries++;
System.Threading.Thread.Sleep(RetryPause * retries);//Waits 3 and then 6 seconds before retrying.
}
else throw;
}
} while (projectList == null || retries < maxRetries);
if (retries == maxRetries)
{
Log.Error("An error has occured while trying to retrieve affected Projects, skipped document");
errorCount++;
return false;
}
return true;
}
But unfortunately I need to replicate this Logic so often I would like to use it in a function e.g. RetryNTimes (similar to This Solution
List<APIEntityProject> projectList = null;
List<APIEntityWBS> WBSList = null;
List<APIEntitySopeItem> SIList = null;
List<APIEntityScopeAsignment> SAList = null;
List<APIEntityActivity> ActList = null;
...
RetryNTimes(projectList,ProjApi.GetProject(activeWorkspace.WorkspaceCode),3,3000,"ProjectList");
RetryNTimes(WBSList, WBSApi.GetAllWBS(activeProject.ProjectID),3,3000,"WBSList");
RetryNTimes(SIList, SIApi.GetAllScopeItems(activeProject.ProjectID),3,3000,"ScopeItemsList");
RetryNTimes(SAList, SAApi.GetAllScopeAssignments(activeProject.ProjectID),3,3000,"ScopeAssignmentsList");
RetryNTimes(ActList, ActApi.GetAllActivities(activeProject.ProjectID),3,3000,"ActivityList");
...
private bool RetryNTimes(T object, Func<T> func, int times, int WaitInterval, string etext){
do
{
try
{
object = func();
}
catch (ApiException e)
{
if (e.ErrorCode == 401)
{
retries++;
Log.Warning("Unauthorized error while fetching {APIErrorSubject}, try {retries}",eText,retries);
System.Threading.Thread.Sleep(RetryPause * retries);//Waits 3 and then 6 seconds before retrying.
}
else throw;
}
} while (object == null || retries < maxRetries);
if (retries == maxRetries)
{
Log.Error("An error has occured while trying to retrieve {APIErrorSubject}, skipped document",eText);
errorCount++;
return false;
}
return true;
}
I've also read through typedef and function pointers but I'm not sure if it's possible to do with variable types.
Any Ideas?
That article refers to C language. In C# you can use delegates. Here's a link to start you off.
Based on the idea of asawyer and by looking through some other examples of delegates I've been able to make it work.
static T2 TryNTimes<T1,T2>(Func<T1,T2> func,T1 obj, int times, int WaitInterval)
{
while (times > 0)
{
try
{
T2 result = func.Invoke(obj);
return result;
}
catch (Exception e)
{
if (--times <= 0)
throw;
System.Threading.Thread.Sleep(WaitInterval * times);
}
}
return default;
}
Now I need only 2 steps in my main function
activeWorkspace = TryNTimes(WrkApi.WorkspaceCodeWorkspaceCodeFindByName17, ServiceSettings.sqlConnection.Workspace, 3, 3000)[0];
ProjectList = TryNTimes(WrkApi.GetProjectsByWorkspaceCode, activeWorkspace.code, 3, 3000);
The first one can still generate an error as the default List is empty and you can't take 0th element then. But I guess I can find another way around that issue.

Multithreading in c# but variables change too fast

I have a class that executes my function but the variable changes too fast for me to even append my file. I need the speed but I need the functionality in my multithreading. Here's what's in my program.cs that's really the main key in multithreading.
process process = new process();
Thread[] threads = new Thread[15];
static int refInt = 0;
for (int i = 0; i < threads.Count(); i++)
{
threads[i] = new Thread(process.checkCookies);
}
foreach (Thread threadStart in threads)
{
threadStart.Start();
}
That's my program.cs and here's my process library.
public void checkCookies()
{
try
{
while (Interlocked.Increment(ref refInt) < cookies.Count)
{
try
{
string data = functions.cookieToUserId(cookies[refInt]);
if (data == "The cookie is incorrect.")
{
ConsoleWrite("\nThe cookie is invalid.", ConsoleColor.DarkRed);
continue;
}
string cookiesValue = functions.getRobux(cookies[refInt]);
if (cookiesValue == "Invalid cookie.")
{
ConsoleWrite("\nThe cookie is invalid.", ConsoleColor.DarkRed);
continue;
}
else if (Convert.ToInt32(cookiesValue) < 5)
{
ConsoleWrite(string.Format("\nThe account has less than 5 currency. [{0}]", data), ConsoleColor.DarkRed);
continue;
}
else if (Convert.ToInt32(cookiesValue) > 5)
{
ConsoleWrite(string.Format("\nThe account has {0} currency. [{1}]", cookiesValue, data), ConsoleColor.DarkGreen);
functions.appendFile("config/checkedCookies.txt", cookies[refInt]);
continue;
}
}
catch
{
//exception
}
}
}
catch
{
//exception
}
}
My issue is that whenever there is a cookie with a currency integer greater than 5, when it appendsFile which is basically this function here.
public string appendFile(string file, string content)
{
try
{
using (StreamWriter writeStream = File.AppendText(file))
{
writeStream.WriteLine(content);
return "Appended the text successfully!";
}
}
catch (Exception)
{
return "Error appending the text.";
}
}
The refInt changes due to another thread running it. So if refInt is equal to 4, then after it goes through all the else if statements. The refInt changes to 20-25 because of the other threads running the code and changing the global variable so whenever I append text, it appends the wrong cookie. What are some methods to make it so the global variable doesn't get changed too fast, by the way. I need the speed to be as fast as that.
i dont know if its helps or kills ur multithreading idee but did u ever think about mutex lock?
https://www.c-sharpcorner.com/UploadFile/1d42da/threading-with-mutex/

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;
}

Waiting for a parse.com async task to finish in Unity3D

As part of my school project I'm trying to link two tables to decrease the amount of data stored in one table so I wanted to link my "Scores" class with my "CorrectAnswers" class via the ObjectID. However since the tasks are asynchronous, by the time one task is done saving, the other task has already begun or also finished saving and so the ObjectID returns as null.
Here's the code I'm using:
public void SaveScore()
{
ParseObject SendScore = new ParseObject("Scores");
SendScore["Score"] = CheckAnswer.score;
SendScore["user"] = ParseObject.CreateWithoutData("_User", ParseUser.CurrentUser.ObjectId);
SendScore["TestMode"] = MainMenu.testmode;
SendScore["TotalQuestions"] = QuestionCreation.TotalQuestions;
SendScore["CorrectQuestions"] = CheckAnswer.CorrectQuestions;
SendScore.SaveAsync().ContinueWith(t =>
{
ScoreObjectId = SendScore.ObjectId;
});
ParseObject SendCorrectTopics = new ParseObject("CorrectAnswers");
SendCorrectTopics["Score"] = SendScore.ObjectId;
for (int i = 0; i <= 9; i++)
{
string Topic = "Topic" + (i + 1).ToString();
SendCorrectTopics[Topic] = CheckAnswer.CorrectTopics[i];
}
SendCorrectTopics.SaveAsync();
SceneManager.LoadScene(0);
}
How would I be able to make the second save hold until the first save has finished? I'm somewhat new to C# and so don't quite know all it's features yet. I've looked into "await" but unity doesn't seem to like that. Any help would be greatly appreciated!
Thanks in advance,
EDIT: Okay, after a bit more reading on Unity's coroutines, I found a much better way of checking that only relies on checking when needed:
IEnumerator CheckSave()
{
while(ScoreObjectId == null & !DoneSave))
{
print("Running");
yield return new WaitForSeconds(0.5f);
}
DoneSave = false;
SaveTotalTopics();
}
This seems like a much better way of doing it.
Well, it seems the answer was something I've already done before, even if it is a little ugly.
Using Unity's update function I created a check to make sure the ObjectID is not null and the previous save had completed, as so:
void Update () {
if (ScoreObjectId != null & DoneSave)
{
DoneSave = false;
SaveTotalTopics();
}
Thus splitting it into two saves and creating:
public void SaveScore()
{
ParseObject SendScore = new ParseObject("Scores");
SendScore["Score"] = CheckAnswer.score;
SendScore["user"] = ParseObject.CreateWithoutData("_User", ParseUser.CurrentUser.ObjectId);
SendScore["TestMode"] = MainMenu.testmode;
SendScore["TotalQuestions"] = QuestionCreation.TotalQuestions;
SendScore["CorrectQuestions"] = CheckAnswer.CorrectQuestions;
Task SendingScores = SendScore.SaveAsync().ContinueWith(t =>
{
if (t.IsFaulted || t.IsCanceled)
{
DoneSave = false;
print(t.Exception);
}
else
{
DoneSave = true;
print("Setting object ID!");
ScoreObjectId = SendScore.ObjectId;
print(ScoreObjectId);
}
});
}
void SaveTotalTopics()
{
for (int i = 0; i <= 9; i++)
{
string Topic = "Topic" + (i + 1).ToString();
SendCorrectTopics[Topic] = CheckAnswer.CorrectTopics[i];
}
SendCorrectTopics["UserScore"] = ParseObject.CreateWithoutData("Scores", ScoreObjectId);
SendCorrectTopics.SaveAsync().ContinueWith(t =>
{
if(t.IsFaulted || t.IsCanceled)
{
print(t.Exception);
}
else
{
print("Saved!");
}
});
}
I'd also forgotten to use ParseObject.CreateWithoutData() so my first code snippet wouldn't have worked even if I'd found a better method...
So, although I'm not happy with the final result, at least it works and I don't think running an if statement every frame should significantly impact on my game's performance.
Why not use a bool and a while loop?
public IEnumerator SaveScore()
{
bool canContinue = false;
ParseObject SendScore = new ParseObject("Scores");
SendScore["Score"] = CheckAnswer.score;
SendScore["user"] = ParseObject.CreateWithoutData("_User", ParseUser.CurrentUser.ObjectId);
SendScore["TestMode"] = MainMenu.testmode;
SendScore["TotalQuestions"] = QuestionCreation.TotalQuestions;
SendScore["CorrectQuestions"] = CheckAnswer.CorrectQuestions;
SendScore.SaveAsync().ContinueWith(t =>
{
ScoreObjectId = SendScore.ObjectId;
//set the bool canContinue to true because the first portion of code has finished running
canContinue = true;
});
//wait while the canContinue bool is false
while(!canContinue){
yield return null;
}
//continue your parse code
ParseObject SendCorrectTopics = new ParseObject("CorrectAnswers");
SendCorrectTopics["Score"] = SendScore.ObjectId;
for (int i = 0; i <= 9; i++)
{
string Topic = "Topic" + (i + 1).ToString();
SendCorrectTopics[Topic] = CheckAnswer.CorrectTopics[i];
}
SendCorrectTopics.SaveAsync();
SceneManager.LoadScene(0);
return null;
}

Getting an exception while trying to receive objects over TCP with c#

I am trying to receive objects with TCP using C# and serialization. I am receiving objects constantly and each object is sent to a new task. I chose not to use threads because its too expensive. The problem is that if I am receiving only 1 object at a time everything goes just fine but if I am trying to receive more than 1 object, after a few seconds I am getting:
"the input stream is not a valid binary format. the starting contents (in bytes) are: ..."
This is my listening function:
public void Listen()
{
try
{
TcpObject tcpObject = new TcpObject();
IFormatter formatter = new BinaryFormatter();
bool offline = true;
Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
new Action(() => offline = Offline));
while (!offline)
{
tcpObject = (TcpObject)formatter.Deserialize(serverStream);
if (tcpObject.Command == Command.Transfer)
{
#region Task
Task.Factory.StartNew(() =>
{
SentAntenna sentAntenna = (SentAntenna)tcpObject.Object;
string antennaName = sentAntenna.Name;
if (MainWindow.SpectrumList.ContainsKey(antennaName))
{
PointCollection pointCollection = new PointCollection();
float minChan = sentAntenna.Min;
float maxChan = sentAntenna.Max;
if (MainWindow.SpectrumList[antennaName].spectrumViewModel.AbsoluteMinimum == -1)
{
Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
new Action(delegate
{
MainWindow.SpectrumList[antennaName].spectrumViewModel.AbsoluteMinimum = minChan;
MainWindow.SpectrumList[antennaName].spectrumViewModel.AbsoluteMaximum = maxChan;
MainWindow.SpectrumList[antennaName].spectrumViewModel.TBMinRange = minChan.ToString();
MainWindow.SpectrumList[antennaName].spectrumViewModel.TBMaxRange = maxChan.ToString();
MainWindow.SpectrumList[antennaName].spectrumViewModel.MinRange = minChan;
MainWindow.SpectrumList[antennaName].spectrumViewModel.MaxRange = maxChan;
MainWindow.SpectrumList[antennaName].spectrumViewModel.UpdateRange();
}));
}
float gap = maxChan - minChan;
foreach (Frequency f in sentAntenna.Frequencies)
{
float chan = ((f.Channel - minChan) / gap) * 310;
float inten = ((f.Intensity - 1) / 599) * 100;
pointCollection.Add(new Point(chan, inten));
}
pointCollection.Freeze();
Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
new Action(() => MainWindow.SpectrumList[antennaName].spectrumViewModel.AllAntennaPoints = pointCollection.Clone()));
}
Thread.Sleep(50);
});
#endregion
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message); // raise an event
}
}
What am I doing wrong?
Try moving
SentAntenna sentAntenna = (SentAntenna)tcpObject.Object;
to the line before the StartNew(). I believe this will fix your issue.
I don't think you want concurrent access to the tcpObject, since it's global to all the tasks.
Alternatively you could instantiate the TcpObject inside the while loop, which would then keep it local to each task.
There are several reasons for this error:
When two objects concurently writes to one connection
When somthing goes wrong with serverStream: stream have received a part of data or received 0 length data
You concurently acces to tcpObject. it's a bad idea.

Categories