3434#include < ur_client_library/comm/tcp_socket.h>
3535#include < ur_client_library/control/motion_primitives.h>
3636#include < cmath>
37+ #include < limits>
3738#include " ur_client_library/exceptions.h"
3839
3940using namespace urcl ;
@@ -227,8 +228,8 @@ class TrajectoryPointInterfaceTest : public ::testing::Test
227228 (double )spl.pos [4 ] / control::TrajectoryPointInterface::MULT_JOINTSTATE ,
228229 (double )spl.pos [5 ] / control::TrajectoryPointInterface::MULT_JOINTSTATE ,
229230 },
230- (double )spl.blend_radius_or_spline_type / control::TrajectoryPointInterface::MULT_TIME ,
231- std::chrono::milliseconds (spl.goal_time ),
231+ (double )spl.blend_radius_or_spline_type / control::TrajectoryPointInterface::MULT_JOINTSTATE ,
232+ std::chrono::microseconds (spl.goal_time ),
232233 (double )spl.acc [0 ] / control::TrajectoryPointInterface::MULT_JOINTSTATE ,
233234 (double )spl.vel [0 ] / control::TrajectoryPointInterface::MULT_JOINTSTATE );
234235 }
@@ -241,8 +242,8 @@ class TrajectoryPointInterfaceTest : public ::testing::Test
241242 ((double )spl.pos [3 ]) / control::TrajectoryPointInterface::MULT_JOINTSTATE ,
242243 ((double )spl.pos [4 ]) / control::TrajectoryPointInterface::MULT_JOINTSTATE ,
243244 ((double )spl.pos [5 ]) / control::TrajectoryPointInterface::MULT_JOINTSTATE },
244- (double )spl.blend_radius_or_spline_type / control::TrajectoryPointInterface::MULT_TIME ,
245- std::chrono::milliseconds (spl.goal_time ),
245+ (double )spl.blend_radius_or_spline_type / control::TrajectoryPointInterface::MULT_JOINTSTATE ,
246+ std::chrono::microseconds (spl.goal_time ),
246247 (double )spl.acc [0 ] / control::TrajectoryPointInterface::MULT_JOINTSTATE ,
247248 (double )spl.vel [0 ] / control::TrajectoryPointInterface::MULT_JOINTSTATE );
248249 }
@@ -255,7 +256,7 @@ class TrajectoryPointInterfaceTest : public ::testing::Test
255256 ((double )spl.pos [3 ]) / control::TrajectoryPointInterface::MULT_JOINTSTATE ,
256257 ((double )spl.pos [4 ]) / control::TrajectoryPointInterface::MULT_JOINTSTATE ,
257258 ((double )spl.pos [5 ]) / control::TrajectoryPointInterface::MULT_JOINTSTATE },
258- (double )spl.blend_radius_or_spline_type / control::TrajectoryPointInterface::MULT_TIME ,
259+ (double )spl.blend_radius_or_spline_type / control::TrajectoryPointInterface::MULT_JOINTSTATE ,
259260 (double )spl.acc [0 ] / control::TrajectoryPointInterface::MULT_JOINTSTATE ,
260261 (double )spl.vel [0 ] / control::TrajectoryPointInterface::MULT_JOINTSTATE );
261262 }
@@ -274,7 +275,7 @@ class TrajectoryPointInterfaceTest : public ::testing::Test
274275 ((double )spl.pos [3 ]) / control::TrajectoryPointInterface::MULT_JOINTSTATE ,
275276 ((double )spl.pos [4 ]) / control::TrajectoryPointInterface::MULT_JOINTSTATE ,
276277 ((double )spl.pos [5 ]) / control::TrajectoryPointInterface::MULT_JOINTSTATE },
277- (double )spl.blend_radius_or_spline_type / control::TrajectoryPointInterface::MULT_TIME ,
278+ (double )spl.blend_radius_or_spline_type / control::TrajectoryPointInterface::MULT_JOINTSTATE ,
278279 (double )spl.acc [1 ] / control::TrajectoryPointInterface::MULT_JOINTSTATE ,
279280 (double )spl.acc [0 ] / control::TrajectoryPointInterface::MULT_JOINTSTATE ,
280281 static_cast <int32_t >(round ((double )spl.acc [2 ] / control::TrajectoryPointInterface::MULT_JOINTSTATE )));
@@ -384,7 +385,7 @@ TEST_F(TrajectoryPointInterfaceTest, write_quintic_joint_spline)
384385 EXPECT_EQ (send_acc[5 ], ((double )received_data.acc [5 ]) / traj_point_interface_->MULT_JOINTSTATE );
385386
386387 // Goal time
387- EXPECT_EQ (send_goal_time, ((double )received_data.goal_time / traj_point_interface_->MULT_TIME ));
388+ EXPECT_EQ (send_goal_time, ((double )received_data.goal_time / traj_point_interface_->MULT_JOINTSTATE ));
388389
389390 // Spline type
390391 EXPECT_EQ (static_cast <int32_t >(control::TrajectorySplineType::SPLINE_QUINTIC ),
@@ -428,7 +429,7 @@ TEST_F(TrajectoryPointInterfaceTest, write_cubic_joint_spline)
428429 EXPECT_EQ (send_acc[5 ], ((double )received_data.acc [5 ]) / traj_point_interface_->MULT_JOINTSTATE );
429430
430431 // Goal time
431- EXPECT_EQ (send_goal_time, ((double )received_data.goal_time ) / traj_point_interface_->MULT_TIME );
432+ EXPECT_EQ (send_goal_time, ((double )received_data.goal_time ) / traj_point_interface_->MULT_JOINTSTATE );
432433
433434 // Spline type
434435 EXPECT_EQ (static_cast <int32_t >(control::TrajectorySplineType::SPLINE_CUBIC ),
@@ -479,7 +480,62 @@ TEST_F(TrajectoryPointInterfaceTest, write_goal_time)
479480 traj_point_interface_->writeTrajectoryPoint (&send_positions, send_goal_time, 0 , false );
480481 int32_t received_goal_time = client_->getGoalTime ();
481482
482- EXPECT_EQ (send_goal_time, ((float )received_goal_time) / traj_point_interface_->MULT_TIME );
483+ EXPECT_EQ (send_goal_time, ((float )received_goal_time) / traj_point_interface_->MULT_JOINTSTATE );
484+ }
485+
486+ // Wire format: int32 microseconds (MULT_TIME). Duration must reach writeMotionPrimitive as seconds
487+ // in double form so encoding uses round(seconds * MULT_TIME), not trunc(int(ms)).
488+ TEST_F (TrajectoryPointInterfaceTest, write_goal_time_preserves_submillisecond_precision)
489+ {
490+ urcl::vector6d_t send_positions = { 0 , 0 , 0 , 0 , 0 , 0 };
491+ const float send_goal_time = 0 .5006f ;
492+ const int32_t expected_encoded = static_cast <int32_t >(
493+ std::round (static_cast <double >(send_goal_time) * static_cast <double >(traj_point_interface_->MULT_TIME )));
494+
495+ traj_point_interface_->writeTrajectoryPoint (&send_positions, send_goal_time, 0 , false );
496+ EXPECT_EQ (expected_encoded, client_->getGoalTime ());
497+
498+ traj_point_interface_->writeTrajectoryPoint (&send_positions, 1 .4f , 1 .05f , send_goal_time, 0 , false );
499+ EXPECT_EQ (expected_encoded, client_->getGoalTime ());
500+
501+ // High-precision duration: must match round(goal_time * MULT_TIME), not trunc(goal_time * MULT_TIME).
502+ const float precise_goal_time = 1 .234567f ;
503+ const int32_t expected_precise = static_cast <int32_t >(
504+ std::round (static_cast <double >(precise_goal_time) * static_cast <double >(traj_point_interface_->MULT_TIME )));
505+ traj_point_interface_->writeTrajectoryPoint (&send_positions, precise_goal_time, 0 , false );
506+ EXPECT_EQ (expected_precise, client_->getGoalTime ());
507+ traj_point_interface_->writeTrajectoryPoint (&send_positions, 1 .4f , 1 .05f , precise_goal_time, 0 , false );
508+ EXPECT_EQ (expected_precise, client_->getGoalTime ());
509+
510+ urcl::vector6d_t send_vel = { 0.0 , 0.0 , 0.0 , 0.0 , 0.0 , 0.0 };
511+ urcl::vector6d_t send_acc = { 0.0 , 0.0 , 0.0 , 0.0 , 0.0 , 0.0 };
512+ traj_point_interface_->writeTrajectorySplinePoint (&send_positions, &send_vel, &send_acc, send_goal_time);
513+ EXPECT_EQ (expected_encoded, client_->getGoalTime ());
514+ traj_point_interface_->writeTrajectorySplinePoint (&send_positions, &send_vel, &send_acc, precise_goal_time);
515+ EXPECT_EQ (expected_precise, client_->getGoalTime ());
516+ }
517+
518+ // Segment duration is capped by int32 microseconds on the wire (~35.79 minutes).
519+ TEST_F (TrajectoryPointInterfaceTest, write_rejects_goal_time_above_max_encodable_duration)
520+ {
521+ const double max_goal_time_seconds = static_cast <double >(std::numeric_limits<int32_t >::max ()) /
522+ static_cast <double >(urcl::control::TrajectoryPointInterface::MULT_TIME );
523+ const float too_long_goal_time = static_cast <float >(max_goal_time_seconds + 1.0 );
524+ const float almost_too_long_goal_time = static_cast <float >(max_goal_time_seconds - 1.0 );
525+
526+ urcl::vector6d_t send_positions = { 0 , 0 , 0 , 0 , 0 , 0 };
527+ EXPECT_FALSE (traj_point_interface_->writeTrajectoryPoint (&send_positions, too_long_goal_time, 0 , false ));
528+ EXPECT_FALSE (traj_point_interface_->writeTrajectoryPoint (&send_positions, 1 .4f , 1 .05f , too_long_goal_time, 0 , false ));
529+ EXPECT_TRUE (traj_point_interface_->writeTrajectoryPoint (&send_positions, almost_too_long_goal_time, 0 , false ));
530+ EXPECT_TRUE (
531+ traj_point_interface_->writeTrajectoryPoint (&send_positions, 1 .4f , 1 .05f , almost_too_long_goal_time, 0 , false ));
532+
533+ urcl::vector6d_t send_vel = { 0.0 , 0.0 , 0.0 , 0.0 , 0.0 , 0.0 };
534+ urcl::vector6d_t send_acc = { 0.0 , 0.0 , 0.0 , 0.0 , 0.0 , 0.0 };
535+ EXPECT_FALSE (
536+ traj_point_interface_->writeTrajectorySplinePoint (&send_positions, &send_vel, &send_acc, too_long_goal_time));
537+ EXPECT_TRUE (traj_point_interface_->writeTrajectorySplinePoint (&send_positions, &send_vel, &send_acc,
538+ almost_too_long_goal_time));
483539}
484540
485541TEST_F (TrajectoryPointInterfaceTest, write_acceleration_velocity)
@@ -500,7 +556,7 @@ TEST_F(TrajectoryPointInterfaceTest, write_acceleration_velocity)
500556
501557 EXPECT_EQ (send_move_acceleration, ((float )received_move_acceleration) / traj_point_interface_->MULT_JOINTSTATE );
502558 EXPECT_EQ (send_move_velocity, ((float )received_move_velocity) / traj_point_interface_->MULT_JOINTSTATE );
503- EXPECT_EQ (send_goal_time, ((float )received_goal_time) / traj_point_interface_->MULT_TIME );
559+ EXPECT_EQ (send_goal_time, ((float )received_goal_time) / traj_point_interface_->MULT_JOINTSTATE );
504560}
505561
506562TEST_F (TrajectoryPointInterfaceTest, write_blend_radius)
@@ -510,7 +566,7 @@ TEST_F(TrajectoryPointInterfaceTest, write_blend_radius)
510566 traj_point_interface_->writeTrajectoryPoint (&send_positions, 0 , send_blend_radius, false );
511567 int32_t received_blend_radius = client_->getBlendRadius ();
512568
513- EXPECT_EQ (send_blend_radius, ((float )received_blend_radius) / traj_point_interface_->MULT_TIME );
569+ EXPECT_EQ (send_blend_radius, ((float )received_blend_radius) / traj_point_interface_->MULT_JOINTSTATE );
514570}
515571
516572TEST_F (TrajectoryPointInterfaceTest, write_cartesian)
0 commit comments