Getting an exception while trying to receive objects over TCP with c# - 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.

Related

Thread-safe buffer that propagates the latest data

I have a data source which creates (produces) a PointF every 15 to 20 milliseconds.
I need to store (consume) such points every 10ms. My approach is to use a 3 points wide buffer and pointers to achieve a lock-free access:
protected class PosBuffer
{
PointF[] m_Buffer = new PointF[3];
volatile int m_ReadPointer = 0;
volatile int m_WritePointer = 1;
internal PosBuffer()
{
m_Buffer[0] = new PointF(0, 0);
m_Buffer[1] = new PointF(0, 0);
m_Buffer[2] = new PointF(0, 0);
}
internal void Write(PointF point)
{
m_Buffer[m_WritePointer] = point;
m_ReadPointer++;
if (m_ReadPointer == 3) m_ReadPointer = 0;
m_WritePointer++;
if (m_WritePointer == 3) m_WritePointer = 0;
}
internal PointF Read()
{
return m_Buffer[m_ReadPointer];
}
}
My idea is that
as soon as new data arrives it will be stored 'above' the old data. Then the read pointer is set to this position and then the write pointer is incremented.
in case now new data has been produced the consumer thread reads the old data again.
This construction allows different or inconstant read and write rates.
My questions are:
would this approach work?
Do I need locks/monitors/critical sections...
Would I need to disable optimization?
Are there known better solutions?
Try running this code:
async Task Main()
{
var cts = new CancellationTokenSource();
var ct = cts.Token;
var pb = new PosBuffer();
var tw = Task.Run(() =>
{
while (true)
{
if (ct.IsCancellationRequested)
break;
pb.Write(new PointF());
}
});
var tr = Task.Run(() =>
{
while (true)
{
if (ct.IsCancellationRequested)
break;
pb.Read();
}
});
await Task.Delay(TimeSpan.FromSeconds(5.0));
cts.Cancel();
}
Fairly quickly it throws IndexOutOfRangeException. You're letting the value of the "pointers" (bad name by the way) be 3 before dropping back to zero and in the time it takes to change it down the read operation throws.
The problem goes away if you increment like this:
m_ReadPointer = (m_ReadPointer == m_Buffer.Length - 1) ? 0 : m_ReadPointer + 1;
m_WritePointer = (m_WritePointer == m_Buffer.Length - 1) ? 0 : m_WritePointer + 1;
Now, if you have multiple writers then you're definitely going to need locking.
You could consider using a BroadcastBlock<T> from the TPL Dataflow library:
Provides a buffer for storing at most one element at time, overwriting each message with the next as it arrives.
using System.Threading.Tasks.Dataflow;
// Initialize
BroadcastBlock<PointF> block = new(x => x);
// Write the latest value
block.Post(new PointF(0, 0));
// Fetch the latest value
PointF point = await block.ReceiveAsync();
Another idea is to use a BehaviorSubject<T> from the Rx library.
Represents a value that changes over time.
using System.Reactive.Subjects;
// Initialize
BehaviorSubject<PointF> subject = new(new PointF(0, 0));
// Write the latest value
subject.OnNext(new PointF(0, 0));
// Get the latest value
PointF point = subject.Value;
Both classes (BroadcastBlock<T> and BehaviorSubject<T>) are thread-safe.
For your case of single value, I would suggest ReaderWriterLockSlim assuming
you need thread safe reads and writes with multiple threads.
protected class PosBuffer
{
private PointF m_Buffer;
private ReaderWriterLockSlim m_Lock = new();
internal void Write(PointF point)
{
m_Lock.EnterWriteLock();
try
{
m_Buffer = point;
}
finally
{
m_Lock.ExitWriteLock();
}
}
internal PointF Read()
{
m_Lock.EnterReadLock();
try
{
return m_Buffer;
}
finally
{
m_Lock.ExitReadLock();
}
}
}

Modbus read and write value conflict

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

Error when pulling from database to create objects after successful creation

Let me start by apologizing for the complexity of this post. hopefully I am missing something simple but in order to find out I have to make a lengthy explanation.
I'm building a staff tracking app that allows users to draw polygons on a map. The polygon is used as a zone. If the device location is inside the zone it will set them to one status such as "In" and then set them to another status like "Out" when they leave. I'm using several nugets; SQLite, TK.CustomMap, PopupPlugin, to accomplish as much of this as possible in the Shared Project. The SQLite data model is based on the structure of the remote database for the solution which also has a desktop application, web app, and many other interfaces so that structure must be maintained.
The tables involved in this operation are Zones, ZonePoints and StatusClass. When the Zone is saved the Points of the TK.Polygon are saved into the points table. The Statuses assigned to the in and out status of the Zone are assigned to the zone as is the zone name.
The process works this way - first the user clicks on a button to add the zone. This creates a new zone, saves it to the database and gets its id from the table. For now, the ZoneSys (Primary Key in the remote db) is also set to the id although when the API is ready this will be Auto Incremented in the remote db. Here is that command and the objects it references. I did not include the database method definitions given that they work on the first time through, but if anyone thinks they will help solve this riddle please let me know -
public static List<Position> ActiveZonePositions = new List<Position>();
public static int ActiveZoneID;
public static Zone ActiveZone;
public static int PointCount;
public static bool NewZoneOpen = false;
public Command<EventArgs> OpenNewZone
{
get
{
return new Command<EventArgs>(async e =>
{
NewZoneOpen = true;
PointCount = 0;
ActiveZonePositions.Clear();
Zone ZoneToAdd = new Zone
{
Contactsys = MobileUser.ContactSys,
ZoneTypeSys = 1,
OrganizationSys = MobileUser.OrganizationSys,
ZoneName = null
};
ActiveZoneID = await AddZoneToDBAsync(ZoneToAdd);
ZoneToAdd.ID = ActiveZoneID;
ZoneToAdd.ZoneSys = ActiveZoneID;
await AddZoneToDBAsync(ZoneToAdd);
ActiveZone = await App.Database.GetZoneAsync(ActiveZoneID);
});
}
}
As the user click points in the map the polygon is drawn using those positions and those positions are also used to create points which are added to a static list. Here is the MapClicked_Command -
public Command<Position> MapClickedCommand
{
get
{
return new Command<Position>(async position =>
{
if (NewZoneOpen)
{
bool isPointInPolygon = IsPointInAnyPolygon(position);
if (isPointInPolygon)
{
var action = await Application.Current.MainPage.DisplayActionSheet(
"Region Collides with Another Region",
"Cancel",
null,
"Try Again",
"Close Zone Editor");
if (action == "Close Zone Editor")
{
await RemoveZoneAsync(ActiveZoneID);
}
if (action == "Try Again")
{
return;
}
}
else if (!isPointInPolygon)
{
ActiveZonePositions.Add(position);
}
if (ActiveZonePositions.Count == 2)
{
ZonePolyLine.LineCoordinates = ActiveZonePositions;
_lines.Remove(ZonePolyLine);
_lines.Add(ZonePolyLine);
}
else if (ActiveZonePositions.Count == 3)
{
ActiveZonePositions.Add(position);
_lines.Remove(ZonePolyLine);
TKPolygon poly = new TKPolygon
{
StrokeColor = System.Drawing.Color.CornflowerBlue,
StrokeWidth = 2f,
Color = System.Drawing.Color.CornflowerBlue
};
foreach (Position pos in ActiveZonePositions)
{
poly.Coordinates.Add(pos);
}
_polygons.Add(poly);
_currentPolygon.Clear();
_currentPolygon.Add(poly);
currentPolygonRendering = true;
}
else if (ActiveZonePositions.Count > 3)
{
ActiveZonePositions.Add(position);
TKPolygon poly = new TKPolygon
{
StrokeColor = System.Drawing.Color.CornflowerBlue,
StrokeWidth = 2f,
Color = System.Drawing.Color.CornflowerBlue
};
foreach (Position pos in ActiveZonePositions)
{
poly.Coordinates.Add(pos);
}
_polygons.Remove(_polygons.Last());
_polygons.Add(poly);
_currentPolygon.Clear();
_currentPolygon.Add(poly);
}
var pin = new TKCustomMapPin
{
Position = new TK.CustomMap.Position(position.Latitude, position.Longitude),
Title = string.Format("Pin {0}, {1}", position.Latitude, position.Longitude),
IsVisible = true,
IsDraggable = true,
ShowCallout = true
};
_pins.Add(pin);
await CreatePointAsync(position);
PointCount++;
}
else if (EditZoneOpen)
{
ActiveZonePositions.Add(position);
var poly = _polygons[0];
poly.Coordinates.Clear();
foreach (Position pos in ActiveZonePositions)
{
poly.Coordinates.Add(pos);
}
_polygons.Remove(_polygons.Last());
_polygons.Add(poly);
_currentPolygon.Clear();
_currentPolygon.Add(poly);
var pin = new TKCustomMapPin
{
Position = new TK.CustomMap.Position(position.Latitude, position.Longitude),
Title = string.Format("Pin {0}, {1}", position.Latitude, position.Longitude),
IsVisible = true,
IsDraggable = true,
ShowCallout = true
};
_pins.Add(pin);
await CreatePointAsync(position);
PointCount++;
}
});
}
}
Here is the CreatePointAsyncMethod -
public async Task CreatePointAsync(TK.CustomMap.Position position)
{
var zone = await RetrieveZoneAsync(ActiveZoneID);
Model.Point PointToAdd = new Model.Point
{
ZoneSys = zone.ZoneSys,
PointName = "",
Latitude = position.Latitude,
Longitude = position.Longitude,
PointOrder = PointCount + 1
};
ActiveZonePoints.Add(PointToAdd);
}
Here is the IsPointInAnyPolygon method that checks against the list of polygons to ensure the point clicked is not inside any of them as well as its supporting methods.
private bool IsPointInAnyPolygon(Position position)
{
bool inBounds = false;
for (var i = 0; i < ZonePolygons.Count(); i++)
foreach (ZonePolygon zpoly in ZonePolygons)
{
TKPolygon tkpoly = zpoly.Zpolygon;
inBounds = IsPointInPolygon(position, tkpoly.Coordinates);
if (inBounds)
{
ActiveZoneID = zpoly.ID;
return inBounds;
}
}
return inBounds;
}
private bool IsPointInPolygon(TK.CustomMap.Position position, List<Position> coords)
{
int intersectCount = 0;
for (int j = 0; j < coords.Count() - 1; j++)
{
if (j+1 >= coords.Count())
{
if (rayCastIntersect(position, coords[j], coords[0]))
{
intersectCount++;
}
} else if (rayCastIntersect(position, coords[j], coords[j + 1]))
{
intersectCount++;
}
}
return ((intersectCount % 2) == 1); // odd = inside, even = outside;
}
private bool rayCastIntersect(TK.CustomMap.Position position, TK.CustomMap.Position vertA, TK.CustomMap.Position vertB)
{
double aY = vertA.Latitude;
double bY = vertB.Latitude;
double aX = vertA.Longitude;
double bX = vertB.Longitude;
double pY = position.Latitude;
double pX = position.Longitude;
if ((aY > pY && bY > pY) | (aY < pY && bY < pY)
| (aX < pX && bX < pX))
{
return false; // a and b can't both be above or below pt.y, and a or
// b must be east of pt.x
}
double m = (aY - bY) / (aX - bX); // Rise over run
double bee = (-aX) * m + aY; // y = mx + b
double x = (pY - bee) / m; // algebra is neat!
return x > pX;
}
Upon clicking the save button a popup opens that allows the user to give the Zone a name, define the statuses it will be assigned and the points are added to the database. There is a ZoneSys column in the Points table which allows the points to be matched to their respective zones when retrieved. This is done withe the UpdateZone command
public Command<EventArgs> UpdateZone
{
get
{
return new Command<EventArgs>(async e =>
{
Zone zone = await App.Database.GetZoneAsync(ActiveZoneID);
zone.ZoneName = ZoneParameters.ZoneName;
zone.StatusSys = ZoneParameters.InStatus.StatusSys;
zone.OutOfZoneStatusSys = ZoneParameters.OutStatus.StatusSys;
await AddZoneToDBAsync(zone);
if (MapPage.SaveZoneInfoPopupPageOpen)
{
SavePointsOnExit();
MapPage.SaveZoneInfoPopupPageOpen = false;
}
});
}
}
The UpdateZone command calls the SavePointsOnExit method
private async void SavePointsOnExit()
{
ActiveZonePoints.OrderBy(o => o.PointOrder);
for (var i = 0; i < ActiveZonePoints.Count(); i++)
{
Model.Point PointToAdd = new Model.Point();
PointToAdd = ActiveZonePoints[i];
ActivePointID = await AddPointToDBAsync(PointToAdd);
PointToAdd.ID = ActivePointID;
PointToAdd.PointSys = ActivePointID;
await AddPointToDBAsync(PointToAdd);
}
try
{
Zone zone = await RetrieveZoneAsync(ActiveZoneID);
}
catch
{
await Application.Current.MainPage.DisplayActionSheet("no zone returned", "database error", "cancel");
}
try
{
Zone zone = await RetrieveZoneAsync(ActiveZoneID);
await CreateZonedPolygonAsync(zone);
}
catch
{
await Application.Current.MainPage.DisplayActionSheet("Could not create ZonePolygon", "object error", "cancel");
}
ActiveZonePoints.Clear();
ActiveZonePositions.Clear();
NewZoneOpen = false;
ClearPins();
PointCount = 0;
PopulatePoints();
}
In addition to saving the points to the db the SaveZonePointsOnExit method also creates the ZonePolygon and adds it to an observable collection using the CreateZonedPolygonAsync method -
private async Task<ZonePolygon> CreateZonedPolygonAsync(Zone zone)
{
int StatusSys = zone.StatusSys;
var status = await App.Database.GetStatusBySysAsync(StatusSys);
int OutStatusSys = zone.OutOfZoneStatusSys;
var outStatus = await App.Database.GetStatusBySysAsync(OutStatusSys);
var points = await App.Database.GetZonePointsAsync(zone.ZoneSys);
ZonePolygon zonePolygon = new ZonePolygon
{
ID = zone.ID
};
TKPolygon poly = new TKPolygon();
foreach (Model.Point point in points)
{
poly.Coordinates.Add(new Position(point.Latitude, point.Longitude));
}
poly.Color = Color.FromHex(status.ColorCode);
poly.StrokeColor = Color.Firebrick;
poly.StrokeWidth = 5f;
_polygons.Add(poly);
ZonePolygons.Add(zonePolygon);
return zonePolygon;
}
So far all of this works to a point. I have been successful in creating the first Polygon. I don't run into a problem until I attempt to create a second Zone. When I click on the AddZone button a second time that works fine but when I click on the map to begin creating the second zone a nullreference exception occurs.
Given that the first zone is created without issue I think the problem must be arising from something that occurs when the IsPointInAnyPolygon method no longer immediately returns false because the ZonePolygons list is no longer empty. So something about the retrieving of zones from the database to check against is the problem or possibly adding coordinates when the TKPolygon is created. I don't know what has a null reference. I would think that since I am creating the Zones directly from the database that all the objects would be saved properly and their previous references wouldn't matter. I'm very stuck on this.
TL;DR there is an issue with either the CreateZonedPolygonAsync Method or the IsPointInAnyPolygon method
I figured this out. I feel rather silly because I have been stuck on this for several hours spread out over a couple of weeks. Kept coming back to it and couldn't figure it out. The issue was that in the CreateZonedPolygonAsync method I never assigned the TKPolygon created via points to the ZonePolygon object being created. So when I tried to reference it it didn't exist. All that existed was the ID. Can't believe I missed this for this long.
Of course now I'm having brand new problems but at least this is fixed.
All I had to do was add zonePolygon.Zpolygon = poly; as shown here and it works now
private async Task<ZonePolygon> CreateZonedPolygonAsync(Zone zone)
{
int StatusSys = zone.StatusSys;
var status = await App.Database.GetStatusBySysAsync(StatusSys);
int OutStatusSys = zone.OutOfZoneStatusSys;
var outStatus = await App.Database.GetStatusBySysAsync(OutStatusSys);
var points = await App.Database.GetZonePointsAsync(zone.ZoneSys);
ZonePolygon zonePolygon = new ZonePolygon
{
ID = zone.ID
};
TKPolygon poly = new TKPolygon();
foreach (Model.Point point in points)
{
poly.Coordinates.Add(new Position(point.Latitude, point.Longitude));
}
poly.Color = Color.FromHex(status.ColorCode);
poly.StrokeColor = Color.Firebrick;
poly.StrokeWidth = 5f;
zonePolygon.Zpolygon = poly;
_polygons.Add(poly);
ZonePolygons.Add(zonePolygon);
return zonePolygon;
}

Unity crashes at Get- Call in RestSharp

I use RestSharp to send post and get calls to a robot which is connected via LAN through a switch with my computer. My get call:
public Vector3 getEffectorPos()
{
var c = new RestClient("http://" + robotIP);
var r = new RestRequest("/present/effector_position.json", Method.GET);
List<String> queryResult = c.Execute<List<String>>(r).Data;
string[] tmp = queryResult[0].Split('[', ']');
Vector4 x = parseStringToFloat4(tmp[2]);
Vector4 y = parseStringToFloat4(tmp[4]);
Vector4 z = parseStringToFloat4(tmp[6]);
Vector3 pos = new Vector3(x.w, y.w, z.w);
effPos.setPos(x.w, y.w, z.w);
effPos.cos = x.x;
effPos.sin = -x.y;
return pos;
}
Because the robot has the static ip 192.168.200.99 I have to switch to a static ip adress in the .200 subnet every time I want to run the project. When I first started the project I would get an error like NullReference or Bad Gateway if the robot was not connected.
But now if I forget to switch from dynamic ip to a static one and the programm can't connect to the robot Unity crashes. And afterwards even if I switch back to a static ip Unity proceeds to crash and it seems like nothing helps but waiting and after some time it will just work again without me doing anything.
It seems like I don't have the same problem with the post call:
public bool postEffectroPos(Vector3 pos, float speed_n)
{
var client = new RestClient("http://" + robotIP);
var request = new RestRequest("/present/effector_position_with_speed.json", Method.POST) { RequestFormat = RestSharp.DataFormat.Json };
effectorPos a = new effectorPos();
a.x = pos.x;
a.y = pos.y;
a.z = pos.z;
a.speed = speed_n;
request.AddBody(a);
bool move = false;
try
{
client.ExecuteAsync(request, response =>
{
if (response.StatusCode == HttpStatusCode.OK)
{
string[] tmp = response.Content.Split(':');
string t = tmp[1].Remove(tmp[1].Length - 1);
move = bool.Parse(t);
//Debug.Log(move);
}
else
{
Debug.Log(response.StatusCode);
}
});
}
catch (Exception error)
{
Debug.Log(error);
}
return move;
}
The post call is async but I didn't figure out yet how to a return a value with async(returns always zero or false, depending on type) and I need the return value of the get call.

How can I de-reference an object in C#?

I have a buffer that cycles between two indices and I want to write out the object at the current index in a task and allow the rest of the program to continue processing things. I have attempted to simplify the process while maintaining all the pertinent parts.
object[] buffer = new object[2]
int currentIndex = 0
while(true){
buffer[currentIndex].field1 = newdatahere //data grabbed by sensor bundle
buffer[currentIndex].field2 = newdatahere //data grabbed by camera bundle
buffer[currentIndex].field3 = newdatahere //data grabbed from system snapshot
task.factory.starnew(()=>{
writeOutObject(buffer[currentIndex])
}
buffer[currentIndex] = new object();
currentIndex = 1 - currentIndex //cycle between the 0 and 1 indices
}
void writeOutObject(Object obj){
//do file IO here
//write out field1, field2, field3
}
The problem is that by assigning the buffer item to a new object I am killing the writeOutObject method because the obj no longer exists by the time the task runs. I want to be able to keep the old object until it is written out and have the buffer point to a new object.
What I want to do:
object obj1 = new object();
obj1.field1 = data1;
obj1.field2 = data2;
obj1.field3 = data3;
obj2 = obj1;
//de-reference obj1 from the object that it was pointed to and associate it to a new object
// i want this to write out data1,data2,data3 but instead it is
// writing out data4,data5,data6 or some mixture because it has
// been overwritten halfway through the file IO
task.factory.startnew(()=>{ write out obj2 }
obj1.field1 = data4;
obj1.field2 = data5;
obj1.field3 = data6;
Maybe something like:
obj1 = new object()
obj2* = &obj1
obj1* = &new object
I need to break the reference of obj1 back to obj2 once it has been assigned. Simply doing this won't work:
obj1 = new object()
obj2 = obj1
obj1 = null // or new object()
As requested, "The Real Code"
CancellationTokenSource cts = new CancellationTokenSource();
public void StartMachine()
{
Task.Factory.StartNew(() =>
{
_isFirstData = true;
_expiredFlag = false;
Plc.StartPLC();
Plc.Start();
while (true)
{
if (!_paused && !Plc.IsInputStackEmpty() && !Plc.IsOutputSlideOpen())
{
CameraFront.SnapAquire();
// If this is the first data set the wait handles
if (!_isFirstData)
{
CameraBack.SnapAquire();
}
else
{
_imageBackRecieved.Set();
_databaseInfoRecieved.Set();
//_isFirstCard = false;
}
// Wait for 3 things! Image Front, Image Back, Database
bool gotEvents = WaitHandle.WaitAll(_waitHandles, TIMEOUT);
if (gotEvents)
{
if (!_isFirstData)
{
if (Buffer[1 - NextDataOutIndex].IsDataComplete())
{
if (Buffer[1 - NextDataOutIndex].EvaluateData())
{
OnPassFailNotification()
Plc.Pass();
}
else
{
OnPassFailNotification()
Plc.Fail();
}
}
else
{
OnPassFailNotification()
Plc.Fail();
Common.Logging
}
}
else
{
_isFirstData = false;
}
}
else
{
Common.Logging("WARNING: Wait handle timed out"
Plc.Fail();
}
Data temp = Buffer[1 - NextDataOutIndex];
Task.Factory.Startnew(()=>{
Data.WriteData(temp);
}
Buffer[1 - NextDataOutIndex] = new Data();
// Swap card buffers - alternate between 1 and 0
NextdataOutIndex = 1 - NextDataOutIndex;
// Do this
Plc.WheelAdvance();
}
else
{
}
}
}, cts.Token);
}
public static void WriteData(Data data)
{
if(WRITE_BATCH_FILES)
try
{
if (data.ImageFront != null)
{
string filenameforfront = "blahlbah-front.tiff";
OperatorSet.WriteImage(data.ImageFront, "tiff", 0, filenameforfront);
}
if (data.ImageBack != null)
{
string filenameforback = "blahblah-back.tiff";
HOperatorSet.WriteImage(data.ImageBack, "tiff", 0, filenameforback);
}
}
catch (Exception ex)
{
Common.Logging.
//throw ex;
}
//TODO: Write out data in xml
//TODO: Write out metrics
}
just before you task.factory.StartNew do the following
while(...)
{
... bunch of other code
buildTask(buffer[currentIndex]);
buffer[currentIndex] = new object();
... bunch of other code
}
// Within this method, all references to detachedBuffer object will remain pointing to the same
// memory location no matter whether the variable passed in is reassigned.
public void buildTask(object detachedBuffer)
{
task.factory.starnew(()=>{
writeOutObject(detachedBuffer);
};
}
Sounds like a job for Semaphores!
Semaphores are a form of inter-thread communication that are ideal for this situation as they allow one thread to lock the semaphore but another to release it again. In the code sample below, the sem.WaitOne() line will wait until the sem.Release() method has been called. This blocks your main thread for just long enough that your task gets hold of the data it needs.
object[] buffer = new object[2]
int currentIndex = 0
while(true){
buffer(currentIndex).field1 = newdatahere //data grabbed by sensor bundle
buffer(currentIndex).field2 = newdatahere //data grabbed by camera bundle
buffer(currentIndex).field3 = newdatahere //data grabbed from system snapshot
Semaphore sem = new Semaphore(1,1); //Initialise the semaphore so that it is checked out
task.factory.starnew(()=>{
object item = buffer[currentIndex]; //Create local reference to the data item
sem.Release(); //Check-in the semaphore (let the WaitOne method return)
writeOutObject(item)
}
sem.WaitOne(); //Block until the semaphore has returned
buffer[currentIndex] = new object();
currentIndex = 1 - currentIndex //cycle between the 0 and 1 indices
}
void writeOutObject(Object obj){
//do file IO here
//write out field1, field2, field3
}

Categories