Reputation: 925
I'm building a C# wrapper class library for a well known software in robotics simulation named V-REP. The API works well but I confront strange behavior. After I get joint feedback from the program using an external library supplied by software, UI is not updated until sub routine (having while loop) is finished. In other words, it only displays last value although I can log all values on output window. I even made large time delay, still no luck. I used the dispatcher technique to update the UI, still not working.
private void SetJointPosition(object sender, RoutedEventArgs e)
{
MoveJointsButton.IsEnabled = false;
// Get the vrep handle of each position
Vrep.simxGetObjectHandle(ClientId, "PhantomXPincher_joint1", out JointHandles[0], simx_opmode.oneshot_wait);
Vrep.simxGetObjectHandle(ClientId, "PhantomXPincher_joint2", out JointHandles[1], simx_opmode.oneshot_wait);
Vrep.simxGetObjectHandle(ClientId, "PhantomXPincher_joint3", out JointHandles[2], simx_opmode.oneshot_wait);
Vrep.simxGetObjectHandle(ClientId, "PhantomXPincher_joint4", out JointHandles[3], simx_opmode.oneshot_wait);
////Assign Target positions
TargetJointPosition[0] = (float)(JointBase.Value * (Math.PI / 180.0));
TargetJointPosition[1] = (float)(JointShoulder.Value * (Math.PI / 180.0));
TargetJointPosition[2] = (float)(JointElbow.Value * (Math.PI / 180.0));
TargetJointPosition[3] = (float)(JointWrist.Value * (Math.PI / 180.0));
//Set the position of each joint
Vrep.SimSetJointTargetPosition(ClientId, JointHandles[0], TargetJointPosition[0]);
Vrep.SimSetJointTargetPosition(ClientId, JointHandles[1], TargetJointPosition[1]);
Vrep.SimSetJointTargetPosition(ClientId, JointHandles[2], TargetJointPosition[2]);
Vrep.SimSetJointTargetPosition(ClientId, JointHandles[3], TargetJointPosition[3]);
//Read Joint Position (start streaming)
Vrep.SimGetJointPositionInit(ClientId, JointHandles[0]);
Vrep.SimGetJointPositionInit(ClientId, JointHandles[1]);
Vrep.SimGetJointPositionInit(ClientId, JointHandles[2]);
Vrep.SimGetJointPositionInit(ClientId, JointHandles[3]);
while ((Math.Abs(ActualJointPosition[0] - TargetJointPosition[0]) > 0.1) ||
(Math.Abs(ActualJointPosition[1] - TargetJointPosition[1]) > 0.1) ||
(Math.Abs(ActualJointPosition[2] - TargetJointPosition[2]) > 0.1) ||
(Math.Abs(ActualJointPosition[3] - TargetJointPosition[3]) > 0.1))
{
// Get current angle of each joint
ActualJointPosition[0]= Vrep.SimGetJointPositionRadian(ClientId, JointHandles[0]);
ActualJointPosition[1] = Vrep.SimGetJointPositionRadian(ClientId, JointHandles[1]);
ActualJointPosition[2] = Vrep.SimGetJointPositionRadian(ClientId, JointHandles[2]);
ActualJointPosition[3] = Vrep.SimGetJointPositionRadian(ClientId, JointHandles[3]);
//Update guages
GBase.Dispatcher.Invoke(()=>
{
GBase.Scales[0].Needles[0].Value = Math.Round(ActualJointPosition[0] * (180.0 / Math.PI));
Debug.WriteLine(DateTime.Now + " " + GBase.Scales[0].Needles[0].Value);
}) ;
GShoulder.Dispatcher.Invoke(() =>
{
GShoulder.Scales[0].Needles[0].Value = ActualJointPosition[1] * (180.0 / Math.PI);
});
GElbow.Dispatcher.Invoke(() =>
{
GElbow.Scales[0].Needles[0].Value = ActualJointPosition[2] * (180.0 / Math.PI);
});
GWrist.Dispatcher.Invoke(() =>
{
GWrist.Scales[0].Needles[0].Value = ActualJointPosition[3] * (180.0 / Math.PI);
});
}
Vrep.SimGetJointPositionEnd(ClientId, JointHandles[0]);
Vrep.SimGetJointPositionEnd(ClientId, JointHandles[1]);
Vrep.SimGetJointPositionEnd(ClientId, JointHandles[2]);
Vrep.SimGetJointPositionEnd(ClientId, JointHandles[3]);
MoveJointsButton.IsEnabled = true;
}}
}
As you may see, I log joint position with no problem on the output window.
I use a WPF application.
Upvotes: 0
Views: 75
Reputation: 169380
This is because the while loop is being executed on the UI thread. You can't run a loop on and the update the UI on the same thread simultaneously.
That's why you should always perform any long-running work on a background thread and update UI at regular intervals using the dispatcher. Please refer to my answer here for more information about this:
How to create percentage of loop(processing) [c#]
You could try to execute while loop in a task, e.g:
Task.Run(() =>
{
while (...)
{
//...
//Update guages
GBase.Dispatcher.Invoke(() =>
{
GBase.Scales[0].Needles[0].Value = Math.Round(ActualJointPosition[0] * (180.0 / Math.PI));
Debug.WriteLine(DateTime.Now + " " + GBase.Scales[0].Needles[0].Value);
});
//..
}
}).ContinueWith(
(t) =>
{
Vrep.SimGetJointPositionEnd(ClientId, JointHandles[0]);
Vrep.SimGetJointPositionEnd(ClientId, JointHandles[1]);
Vrep.SimGetJointPositionEnd(ClientId, JointHandles[2]);
Vrep.SimGetJointPositionEnd(ClientId, JointHandles[3]);
MoveJointsButton.IsEnabled = true;
}, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
Make sure that you marshal any code that access a UI control inside the task back to the UI thread using the dispatcher as controls can only be accessed on the thread on which they were originally created, i.e. the UI thread.
Upvotes: 1