DIT168 Group9  0.9
Miniature remote-controlled vehicle using OpenDavinci and Libcluon
V2VService.cpp
1 #include "V2VService.hpp"
2 
3 std::shared_ptr<cluon::OD4Session> od4;
4 std::string followerIp;
5 std::queue <float> delayQueue;
6 
7 int main(int argc, char **argv) {
8  int returnValue = 0;
9  auto commandlineArguments = cluon::getCommandlineArguments(argc, argv);
10  if (0 == commandlineArguments.count("cid") || 0 == commandlineArguments.count("freq") ||
11  0 == commandlineArguments.count("ip") || 0 == commandlineArguments.count("id")) {
12  std::cerr << argv[0] << " sends and receives follower or leader status as defined in the V2V Protocol (DIT168)."
13  << std::endl;
14  std::cerr << "Usage: " << argv[0]
15  << " --cid=<OD4Session Session> --freq=<frequency> --ip=<localIP> --id=<DIT168Group>"
16  << std::endl;
17  std::cerr << "Example: " << argv[0]
18  << " --cid=111 --freq=125 --ip=127.0.0.1 --id=9 --partnerIp=1.1.1.1 --parnterId=8" << std::endl;
19  returnValue = 1;
20  } else {
21  const uint16_t CID = (uint16_t) std::stoi(commandlineArguments["cid"]);
22  const uint16_t FREQ = (uint16_t) std::stoi(commandlineArguments["freq"]);
23  const uint16_t QUEUE_MAX = (uint16_t) std::stoi(commandlineArguments["queueMax"]);
24  const std::string IP = commandlineArguments["ip"];
25  const std::string ID = commandlineArguments["id"];
26  const std::string PARTNER_IP = commandlineArguments["partnerIp"];
27  const std::string PARTNER_ID = commandlineArguments["partnerId"];
28  const std::string SPEED_AFTER = commandlineArguments["offsetSpeedAfter"];
29  const std::string LEFT = commandlineArguments["left"];
30  const std::string RIGHT = commandlineArguments["right"];
31 
32  std::shared_ptr<V2VService> v2vService = std::make_shared<V2VService>(IP, ID, PARTNER_IP, PARTNER_ID,
33  SPEED_AFTER, LEFT, RIGHT, QUEUE_MAX);
34 
35  float pedalPos = 0, steeringAngle = 0, distanceReading = 0;
36  uint16_t button = 0;
37  bool canMove = true;
38 
39  od4 =
40  std::make_shared<cluon::OD4Session>(CID,
41  [&v2vService, &pedalPos, &steeringAngle, &button, &PARTNER_IP](cluon::data::Envelope &&envelope) noexcept {
42  if (envelope.dataType() == 1041) {
43  opendlv::proxy::PedalPositionReading ppr =
44  cluon::extractMessage<opendlv::proxy::PedalPositionReading>(std::move(envelope));
45  pedalPos = ppr.position();
46  }
47  else if (envelope.dataType() == 1043) {
48  opendlv::proxy::ButtonPressed buttonPressed =
49  cluon::extractMessage<opendlv::proxy::ButtonPressed>(std::move(envelope));
50  button = buttonPressed.buttonNumber();
51  switch (button) {
52  case 0: // Square
53  v2vService->followRequest();
54  break;
55  case 1: // X
56  v2vService->followResponse();
57  break;
58  case 2: // Circle
59  v2vService->stopFollow(PARTNER_IP);
60  break;
61  case 3: // Triangle
62  v2vService->announcePresence();
63  break;
64  default:
65  std::cout << "You shouldn't be here" << std::endl;
66  break;
67  }
68  }
69  else if (envelope.dataType() == 1045) {
70  opendlv::proxy::GroundSteeringReading gsr =
71  cluon::extractMessage<opendlv::proxy::GroundSteeringReading>(std::move(envelope));
72  steeringAngle = gsr.groundSteering();
73  }
74  });
75 
76  auto atFrequency{[&v2vService, &pedalPos, &steeringAngle]() -> bool {
77  v2vService->followerStatus();
78  v2vService->leaderStatus(pedalPos, steeringAngle, 0);
79  return true;
80  }};
81  od4->timeTrigger(FREQ, atFrequency);
82  }
83 }
84 
88 V2VService::V2VService(std::string ip, std::string id, std::string partnerIp, std::string partnerId,
89  std::string speed_after, std::string left, std::string right, uint16_t queue_max) {
90  _IP = ip;
91  _ID = id;
92  _PARTNER_IP = partnerIp;
93  _PARTNER_ID = partnerId;
94  _SPEED_AFTER = speed_after;
95  _LEFT = left;
96  _RIGHT = right;
97  _QUEUE_MAX = queue_max;
98 
99  /*
100  * The broadcast field contains a reference to the broadcast channel which is an OD4Session. This is where
101  * AnnouncePresence messages will be received.
102  */
103  broadcast =
104  std::make_shared<cluon::OD4Session>(BROADCAST_CHANNEL,
105  [this](cluon::data::Envelope &&envelope) noexcept {
106  std::cout << "[OD4] ";
107  switch (envelope.dataType()) {
108  case ANNOUNCE_PRESENCE: {
109  AnnouncePresence ap = cluon::extractMessage<AnnouncePresence>(std::move(envelope));
110  std::cout << "received 'AnnouncePresence' from '"
111  << ap.vehicleIp() << "', GroupID '"
112  << ap.groupId() << "'!" << std::endl;
113  if (ap.vehicleIp() == _PARTNER_IP && ap.groupId() == _PARTNER_ID) {
114  isPresentPartner = true;
115  }
116  presentCars[ap.groupId()] = ap.vehicleIp();
117 
118  break;
119  }
120  default: std::cout << "[BROADCAST CHANNEL] Not announce presence." << std::endl;
121  break;
122  }
123  });
124 
125  /*
126  * Each car declares an incoming UDPReceiver for messages directed at them specifically. This is where messages
127  * such as FollowRequest, FollowResponse, StopFollow, etc. are received.
128  */
129  incoming =
130  std::make_shared<cluon::UDPReceiver>("0.0.0.0", DEFAULT_PORT,
131  [this](std::string &&data, std::string &&sender, std::chrono::system_clock::time_point &&ts) noexcept {
132  std::cout << "[UDP] ";
133  std::pair<int16_t, std::string> msg = extract(data);
134 
135  switch (msg.first) {
136  case FOLLOW_REQUEST: {
137  FollowRequest followRequest = decode<FollowRequest>(msg.second);
138  std::cout << "received '" << followRequest.LongName()
139  << "' from '" << sender << "'!" << std::endl;
140 
141  // After receiving a FollowRequest, check first if there is currently no car already following.
142  if (!isLeader) {
143  unsigned long len = sender.find(':'); // If no, add the requester to known follower slot
144  followerIp = sender.substr(0, len); // and establish a sending channel.
145  toFollower = std::make_shared<cluon::UDPSender>(followerIp, DEFAULT_PORT);
146  followResponse();
147  }
148  break;
149  }
150  case FOLLOW_RESPONSE: {
151  FollowResponse followResponse = decode<FollowResponse>(msg.second);
152  std::cout << "received '" << followResponse.LongName()
153  << "' from '" << sender << "'!" << std::endl;
154  isFollower = true;
155  break;
156  }
157  case STOP_FOLLOW: {
158  StopFollow stopFollow = decode<StopFollow>(msg.second);
159  std::cout << "received '" << stopFollow.LongName()
160  << "' from '" << sender << "'!" << std::endl;
161 
162  // Clear either follower or leader slot, depending on current role.
163  unsigned long len = sender.find(':');
164  if (sender.substr(0, len) == followerIp) {
165  isLeader = false;
166  std::cout << "Car was leader, no longer is\n";
167  followerIp = "";
168  toFollower.reset();
169  }
170  else if (sender.substr(0, len) == leaderIp) {
171  isFollower = false;
172  std::cout << "Car was follower, no longer is\n";
173  leaderIp = "";
174  toLeader.reset();
175  }
176  break;
177  }
178  case FOLLOWER_STATUS: {
179  FollowerStatus followerStatus = decode<FollowerStatus>(msg.second);
180  std::cout << "received '" << followerStatus.LongName()
181  << "' from '" << sender << "'!" << std::endl;
182 
183  break;
184  }
185  case LEADER_STATUS: {
186  opendlv::proxy::PedalPositionReading msgPedal;
187  opendlv::proxy::GroundSteeringReading msgSteering;
188  float calibratedAngle = 0.0f;
189 
190  LeaderStatus leaderStatus = decode<LeaderStatus>(msg.second);
191  std::cout << "received '" << leaderStatus.LongName()
192  << "' from '" << sender << "'!" << std::endl;
193  std::cout << "LeaderStatus Values, pedalPos: " << leaderStatus.speed() << " steeringAngle: "
194  << leaderStatus.steeringAngle() << std::endl;
195 
196  float floatSpeedAfter = std::stof(_SPEED_AFTER);
197  if (leaderStatus.speed() < floatSpeedAfter) {
198  msgPedal.position(leaderStatus.speed());
199  }
200  else {
201  msgPedal.position(leaderStatus.speed() + floatSpeedAfter);
202  }
203  od4->send(msgPedal);
204 
205  float floatLeft = std::stof(_LEFT);
206  float floatRight = std::stof(_RIGHT);
207 
208  if (leaderStatus.steeringAngle() == 0) {
209  calibratedAngle = leaderStatus.steeringAngle() + m_OFFSET;
210  }
211  else if (leaderStatus.steeringAngle() > 0) {
212  std::cout << "Left Value: " << (leaderStatus.steeringAngle() + floatLeft)
213  << std::endl;
214  calibratedAngle = (leaderStatus.steeringAngle() + floatLeft);
215  }
216  else if (leaderStatus.steeringAngle() < 0) {
217  std::cout << "Right Value: " << (leaderStatus.steeringAngle() + floatRight)
218  << std::endl;
219  calibratedAngle = leaderStatus.steeringAngle() + floatRight;
220  }
221 
222  if (leaderStatus.speed() != 0 && delayQueue.size() < _QUEUE_MAX) {
223  delayQueue.push(calibratedAngle);
224  }
225  else if (leaderStatus.speed() != 0 && delayQueue.size() >= _QUEUE_MAX) {
226  delayQueue.push(calibratedAngle);
227 
228  float delayedSteeringAngle = delayQueue.front();
229  msgSteering.groundSteering(delayedSteeringAngle);
230  od4->send(msgSteering);
231  delayQueue.pop();
232  }
233 
234  break;
235  }
236  default: std::cout << "¯\\_(ツ)_/¯" << std::endl;
237  break;
238  }
239  });
240 }
241 
247  if (isFollower) {
248  std::cout << "Announce presence returned! Already following of a car" << std::endl;
249  return;
250  }
251 
252  AnnouncePresence announcePresence;
253  announcePresence.vehicleIp(_IP);
254  announcePresence.groupId(_ID);
255  od4->send(announcePresence);
256  broadcast->send(announcePresence);
257 }
258 
265  if (!isPresentPartner || isFollower) {
266  std::cout << "Follow request returned! isFollower: " << isFollower << " isPresentPartner: " << isPresentPartner
267  << std::endl;
268  return;
269  }
270  toLeader = std::make_shared<cluon::UDPSender>(_PARTNER_IP, DEFAULT_PORT);
271  FollowRequest followRequest;
272  followRequest.temporaryValue("Follow request test");
273  od4->send(followRequest);
274  toLeader->send(encode(followRequest));
275 }
276 
282  if (isFollower || isLeader) {
283  std::cout << "Follow response returned! isFollower: " << isFollower << " isLeader: " << isLeader << std::endl;
284  return;
285  }
286 
287  FollowResponse followResponse;
288  followResponse.temporaryValue("Follow response test");
289  isLeader = true;
290  od4->send(followResponse);
291  toFollower->send(encode(followResponse));
292 }
293 
300 void V2VService::stopFollow(std::string vehicleIp) {
301  StopFollow stopFollow;
302  stopFollow.temporaryValue("Stop follow test");
303 
304  std::cout << "Inside stop follow, vehicleIp == _PARTNER_IP: " << (vehicleIp == _PARTNER_IP) << " isFollower: "
305  << isFollower << " isLeader: " << isLeader << std::endl;
306 
307  if (vehicleIp == _PARTNER_IP && isFollower) {
308  isFollower = false;
309  std::cout << "Car was follower\n";
310  toLeader->send(encode(stopFollow));
311  od4->send(stopFollow);
312  toLeader.reset();
313  }
314  if (vehicleIp == _PARTNER_IP && isLeader) {
315  isLeader = false;
316  std::cout << "Car was leader\n";
317  toFollower->send(encode(stopFollow));
318  od4->send(stopFollow);
319  toFollower.reset();
320  }
321 }
322 
332  if (!isFollower) return;
333  FollowerStatus followerStatus;
334  followerStatus.temporaryValue("Follower status test");
335  od4->send(followerStatus);
336  toLeader->send(encode(followerStatus));
337 }
338 
346 void V2VService::leaderStatus(float speed, float steeringAngle, uint8_t distanceTraveled) {
347  if (!isLeader) return;
348  LeaderStatus leaderStatus;
349  leaderStatus.timestamp(getTime());
350  leaderStatus.speed(speed);
351  std::cout << "Sending to david: " << (steeringAngle - m_OFFSET) << std::endl;
352  leaderStatus.steeringAngle(steeringAngle - m_OFFSET);
353  leaderStatus.distanceTraveled(distanceTraveled);
354  od4->send(leaderStatus);
355  toFollower->send(encode(leaderStatus));
356 }
357 
363 uint32_t V2VService::getTime() {
364  timeval now;
365  gettimeofday(&now, nullptr);
366  return (uint32_t ) now.tv_usec / 1000;
367 }
368 
375 std::pair<int16_t, std::string> V2VService::extract(std::string data) {
376  if (data.length() < 10) return std::pair<int16_t, std::string>(-1, "");
377  int id, len;
378  std::stringstream ssId(data.substr(0, 4));
379  std::stringstream ssLen(data.substr(4, 10));
380  ssId >> std::hex >> id;
381  ssLen >> std::hex >> len;
382  return std::pair<int16_t, std::string> (
383  data.length() -10 == len ? id : -1,
384  data.substr(10, data.length() -10)
385  );
386 };
387 
395 template <class T>
396 std::string V2VService::encode(T msg) {
397  cluon::ToProtoVisitor v;
398  msg.accept(v);
399  std::stringstream buff;
400  buff << std::hex << std::setfill('0')
401  << std::setw(4) << msg.ID()
402  << std::setw(6) << v.encodedData().length()
403  << v.encodedData();
404  return buff.str();
405 }
406 
414 template <class T>
415 T V2VService::decode(std::string data) {
416  std::stringstream buff(data);
417  cluon::FromProtoVisitor v;
418  v.decodeFrom(buff);
419  T tmp = T();
420  tmp.accept(v);
421  return tmp;
422 }
void followRequest()
Definition: V2VService.cpp:264
void followerStatus()
Definition: V2VService.cpp:331
void announcePresence()
Definition: V2VService.cpp:246
void stopFollow(std::string vehicleIp)
Definition: V2VService.cpp:300
void followResponse()
Definition: V2VService.cpp:281
V2VService(std::string ip, std::string id, std::string partnerIp, std::string partnerId, std::string speed_after, std::string left, std::string right, uint16_t queue_max)
Definition: V2VService.cpp:88
void leaderStatus(float speed, float steeringAngle, uint8_t distanceTraveled)
Definition: V2VService.cpp:346