Apollo 11.0
自动驾驶开放平台
hmi_worker.cc
浏览该文件的文档.
1/******************************************************************************
2 * Copyright 2018 The Apollo Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *****************************************************************************/
16
18
19#include "absl/strings/str_cat.h"
20#include "absl/strings/str_join.h"
21#include "absl/strings/str_split.h"
22
23#include "cyber/proto/dag_conf.pb.h"
24#include "modules/common_msgs/monitor_msgs/system_status.pb.h"
25#include "modules/dreamview/proto/scenario.pb.h"
26
27#include "cyber/common/file.h"
41
42namespace apollo {
43namespace dreamview {
44namespace {
45
60using apollo::monitor::ComponentStatus;
61using apollo::monitor::SystemStatus;
62using google::protobuf::util::JsonStringToMessage;
63using RLock = boost::shared_lock<boost::shared_mutex>;
64using WLock = boost::unique_lock<boost::shared_mutex>;
65using Json = nlohmann::json;
66
67constexpr char kNavigationModeName[] = "Navigation";
68
69template <class FlagType, class ValueType>
70void SetGlobalFlag(std::string_view flag_name, const ValueType &value,
71 FlagType *flag) {
72 constexpr char kGlobalFlagfile[] =
73 "/apollo/modules/common/data/global_flagfile.txt";
74 if (*flag != value) {
75 *flag = value;
76 // Overwrite global flagfile.
77 std::ofstream fout(kGlobalFlagfile, std::ios_base::app);
78 ACHECK(fout) << "Fail to open global flagfile " << kGlobalFlagfile;
79 fout << "\n--" << flag_name << "=" << value << std::endl;
80 }
81}
82
83void System(std::string_view cmd) {
84 const int ret = std::system(cmd.data());
85 if (ret == 0) {
86 AINFO << "SUCCESS: " << cmd;
87 } else {
88 AERROR << "FAILED(" << ret << "): " << cmd;
89 }
90}
91
92} // namespace
93
94HMIWorker::HMIWorker(const std::shared_ptr<Node> &node)
95 : config_(util::HMIUtil::LoadConfig(FLAGS_dv_hmi_modes_config_path)),
96 node_(node) {
97 InitStatus();
98}
99
100void HMIWorker::Start(DvCallback callback_api) {
101 callback_api_ = callback_api;
102 InitReadersAndWriters();
103 RegisterStatusUpdateHandler(
104 [this](const bool status_changed, HMIStatus *status) {
105 apollo::common::util::FillHeader("HMI", status);
106 status_writer_->Write(*status);
107 status->clear_header();
108 });
109 ResetComponentStatusTimer();
110 thread_future_ = cyber::Async(&HMIWorker::StatusUpdateThreadLoop, this);
111}
112
113void HMIWorker::Stop() {
114 stop_ = true;
115 if (thread_future_.valid()) {
116 thread_future_.get();
117 }
118}
119
120bool HMIWorker::LoadVehicleDefinedMode(const std::string &mode_config_path,
121 const std::string &current_vehicle_path,
122 HMIMode *self_defined_mode) {
123 const std::string mode_file_name =
124 cyber::common::GetFileName(mode_config_path);
125 const std::string vehicle_mode_config_path =
126 current_vehicle_path + "/dreamview_conf/hmi_modes/" +
127 mode_file_name;
128 if (!cyber::common::PathExists(vehicle_mode_config_path)) {
129 return false;
130 }
131 ACHECK(cyber::common::GetProtoFromFile(vehicle_mode_config_path,
132 self_defined_mode))
133 << "Unable to parse vehicle self defined HMIMode from file "
134 << vehicle_mode_config_path;
135 util::HMIUtil::TranslateCyberModules(self_defined_mode);
136 return true;
137}
138
139void HMIWorker::InitStatus() {
140 static constexpr char kDockerImageEnv[] = "DOCKER_IMG";
141 status_.set_docker_image(cyber::common::GetEnv(kDockerImageEnv));
142 status_.set_utm_zone_id(FLAGS_local_utm_zone_id);
143
144 // Populate modes and current_mode.
145 const auto &modes = config_.modes();
146 for (const auto &iter : modes) {
147 const std::string &mode = iter.first;
148 status_.add_modes(mode);
149 if (mode == FLAGS_vehicle_calibration_mode) {
150 FuelMonitorManager::Instance()->RegisterFuelMonitor(
151 mode, std::make_unique<DataCollectionMonitor>());
152 FuelMonitorManager::Instance()->RegisterFuelMonitor(
153 mode, std::make_unique<PreprocessMonitor>());
154 } else if (mode == FLAGS_lidar_calibration_mode) {
155 FuelMonitorManager::Instance()->RegisterFuelMonitor(
156 mode, std::make_unique<PreprocessMonitor>("lidar_to_gnss"));
157 } else if (mode == FLAGS_camera_calibration_mode) {
158 FuelMonitorManager::Instance()->RegisterFuelMonitor(
159 mode, std::make_unique<PreprocessMonitor>("camera_to_lidar"));
160 }
161 }
162
163 // Populate maps and current_map.
164 for (const auto &map_entry : config_.maps()) {
165 status_.add_maps(map_entry.first);
166
167 // If current FLAG_map_dir is available, set it as current_map.
168 if (map_entry.second == FLAGS_map_dir) {
169 status_.set_current_map(map_entry.first);
170 }
171 }
172
173 // Populate vehicles and current_vehicle.
174 for (const auto &vehicle : config_.vehicles()) {
175 status_.add_vehicles(vehicle.first);
176 }
177
178 // Initial HMIMode by priority:
179 // 1. NavigationMode if --use_navigation_mode is specified explicitly.
180 // 2. CachedMode if it's stored in KV database.
181 // 3. default_hmi_mode if it is available.
182 // 4. Pick the first available mode.
183 const std::string cached_mode =
184 KVDB::Get(FLAGS_current_mode_db_key).value_or("");
185 if (FLAGS_use_navigation_mode && ContainsKey(modes, kNavigationModeName)) {
186 ChangeMode(kNavigationModeName);
187 } else if (ContainsKey(modes, cached_mode)) {
188 ChangeMode(cached_mode);
189 } else if (ContainsKey(modes, FLAGS_default_hmi_mode)) {
190 ChangeMode(FLAGS_default_hmi_mode);
191 } else {
192 ChangeMode(modes.begin()->first);
193 }
194}
195
196void HMIWorker::InitReadersAndWriters() {
197 status_writer_ = node_->CreateWriter<HMIStatus>(FLAGS_hmi_status_topic);
198 action_command_client_ = node_->CreateClient<ActionCommand, CommandStatus>(
199 FLAGS_action_command_topic);
200 lane_follow_command_client_ =
201 node_->CreateClient<LaneFollowCommand, CommandStatus>(
202 FLAGS_lane_follow_command_topic);
203 audio_event_writer_ =
204 node_->CreateWriter<AudioEvent>(FLAGS_audio_event_topic);
205 drive_event_writer_ =
206 node_->CreateWriter<DriveEvent>(FLAGS_drive_event_topic);
207
208 node_->CreateReader<SystemStatus>(
209 FLAGS_system_status_topic,
210 [this](const std::shared_ptr<SystemStatus> &system_status) {
211 this->ResetComponentStatusTimer();
212
213 WLock wlock(status_mutex_);
214
215 const bool is_realtime_msg =
216 FLAGS_use_sim_time
217 ? system_status->is_realtime_in_simulation()
219 system_status->header().timestamp_sec() <
220 FLAGS_system_status_lifetime_seconds;
221 // Update modules running status from realtime SystemStatus.
222 if (is_realtime_msg) {
223 for (auto &iter : *status_.mutable_modules()) {
224 auto *status = FindOrNull(system_status->hmi_modules(), iter.first);
225 iter.second =
226 status != nullptr && status->status() == ComponentStatus::OK;
227 }
228 }
229 // Update monitored components status.
230 for (auto &iter : *status_.mutable_monitored_components()) {
231 auto *status = FindOrNull(system_status->components(), iter.first);
232 if (status != nullptr) {
233 iter.second = status->summary();
234 } else {
235 iter.second.set_status(ComponentStatus::UNKNOWN);
236 iter.second.set_message("Status not reported by Monitor.");
237 }
238 }
239
240 // Update other components status.
241 for (auto &iter : *status_.mutable_other_components()) {
242 auto *status =
243 FindOrNull(system_status->other_components(), iter.first);
244 if (status != nullptr) {
245 iter.second.CopyFrom(*status);
246 } else {
247 iter.second.set_status(ComponentStatus::UNKNOWN);
248 iter.second.set_message("Status not reported by Monitor.");
249 }
250 }
251
252 // Check if the status is changed.
253 const size_t new_fingerprint =
255 if (last_status_fingerprint_ != new_fingerprint) {
256 status_changed_ = true;
257 last_status_fingerprint_ = new_fingerprint;
258 }
259 });
260
261 localization_reader_ =
262 node_->CreateReader<LocalizationEstimate>(FLAGS_localization_topic);
263 // Received Chassis, trigger action if there is high beam signal.
264 chassis_reader_ = node_->CreateReader<Chassis>(
265 FLAGS_chassis_topic, [this](const std::shared_ptr<Chassis> &chassis) {
266 if (Clock::NowInSeconds() - chassis->header().timestamp_sec() <
267 FLAGS_system_status_lifetime_seconds) {
268 if (chassis->signal().high_beam()) {
269 // Currently we do nothing on high_beam signal.
270 const bool ret = Trigger(HMIAction::NONE);
271 AERROR_IF(!ret) << "Failed to execute high_beam action.";
272 }
273 }
274 });
275}
276
277bool HMIWorker::Trigger(const HMIAction action) {
278 AERROR << "HMIAction " << HMIAction_Name(action) << " was triggered!";
279 switch (action) {
280 case HMIAction::NONE:
281 break;
282 case HMIAction::SETUP_MODE:
283 SetupMode();
284 break;
285 case HMIAction::ENTER_AUTO_MODE:
286 return ChangeDrivingMode(Chassis::COMPLETE_AUTO_DRIVE);
287 case HMIAction::DISENGAGE:
288 return ChangeDrivingMode(Chassis::COMPLETE_MANUAL);
289 case HMIAction::RESET_MODE:
290 ResetMode();
291 break;
292 case HMIAction::LOAD_SCENARIOS:
293 LoadScenarios();
294 break;
295 case HMIAction::LOAD_DYNAMIC_MODELS:
296 LoadDynamicModels();
297 break;
298 case HMIAction::LOAD_RECORDS:
299 LoadRecords();
300 break;
301 case HMIAction::STOP_RECORD:
302 StopRecordPlay();
303 break;
304 default:
305 AERROR << "HMIAction not implemented, yet!";
306 return false;
307 }
308 return true;
309}
310
311bool HMIWorker::Trigger(const HMIAction action, const std::string &value) {
312 AINFO << "HMIAction " << HMIAction_Name(action) << "(" << value
313 << ") was triggered!";
314 switch (action) {
315 case HMIAction::CHANGE_MODE:
316 ChangeMode(value);
317 break;
318 case HMIAction::CHANGE_MAP:
319 ChangeMap(value);
320 break;
321 case HMIAction::CHANGE_VEHICLE:
322 ChangeVehicle(value);
323 break;
324 case HMIAction::START_MODULE:
325 StartModule(value);
326 break;
327 case HMIAction::STOP_MODULE:
328 StopModule(value);
329 break;
330 case HMIAction::CHANGE_SCENARIO_SET:
331 ChangeScenarioSet(value);
332 break;
333 case HMIAction::DELETE_SCENARIO_SET:
334 DeleteScenarioSet(value);
335 ChangeScenario("");
336 break;
337 case HMIAction::CHANGE_SCENARIO:
338 ChangeScenario(value);
339 break;
340 case HMIAction::CHANGE_DYNAMIC_MODEL:
341 ChangeDynamicModel(value);
342 break;
343 case HMIAction::DELETE_DYNAMIC_MODEL:
344 DeleteDynamicModel(value);
345 break;
346 case HMIAction::DELETE_RECORD:
347 DeleteRecord(value);
348 break;
349 case HMIAction::CHANGE_RECORD:
350 ChangeRecord(value);
351 break;
352 default:
353 AERROR << "HMIAction not implemented, yet!";
354 return false;
355 }
356 return true;
357}
358
359void HMIWorker::SubmitAudioEvent(const uint64_t event_time_ms,
360 const int obstacle_id, const int audio_type,
361 const int moving_result,
362 const int audio_direction,
363 const bool is_siren_on) {
364 std::shared_ptr<AudioEvent> audio_event = std::make_shared<AudioEvent>();
365 apollo::common::util::FillHeader("HMI", audio_event.get());
366 // Here we reuse the header time field as the event occurring time.
367 // A better solution might be adding an event time field to DriveEvent proto
368 // to make it clear.
369 audio_event->mutable_header()->set_timestamp_sec(
370 static_cast<double>(event_time_ms) / 1000.0);
371 audio_event->set_id(obstacle_id);
372 audio_event->set_audio_type(
373 static_cast<apollo::audio::AudioType>(audio_type));
374 audio_event->set_moving_result(
375 static_cast<apollo::audio::MovingResult>(moving_result));
376 audio_event->set_audio_direction(
377 static_cast<apollo::audio::AudioDirection>(audio_direction));
378 audio_event->set_siren_is_on(is_siren_on);
379
380 // Read the current localization pose
381 localization_reader_->Observe();
382 if (localization_reader_->Empty()) {
383 AERROR << "Failed to get localization associated with the audio event: "
384 << audio_event->DebugString() << "\n Localization reader is empty!";
385 return;
386 }
387
388 const std::shared_ptr<LocalizationEstimate> localization =
389 localization_reader_->GetLatestObserved();
390 audio_event->mutable_pose()->CopyFrom(localization->pose());
391 AINFO << "AudioEvent: " << audio_event->DebugString();
392
393 audio_event_writer_->Write(audio_event);
394}
395
396void HMIWorker::SubmitDriveEvent(const uint64_t event_time_ms,
397 const std::string &event_msg,
398 const std::vector<std::string> &event_types,
399 const bool is_reportable) {
400 std::shared_ptr<DriveEvent> drive_event = std::make_shared<DriveEvent>();
401 apollo::common::util::FillHeader("HMI", drive_event.get());
402 // TODO(xiaoxq): Here we reuse the header time field as the event occurring
403 // time. A better solution might be adding the field to DriveEvent proto to
404 // make it clear.
405 drive_event->mutable_header()->set_timestamp_sec(
406 static_cast<double>(event_time_ms) / 1000.0);
407 drive_event->set_event(event_msg);
408 drive_event->set_is_reportable(is_reportable);
409 for (const auto &type_name : event_types) {
410 DriveEvent::Type type;
411 if (DriveEvent::Type_Parse(type_name, &type)) {
412 drive_event->add_type(type);
413 } else {
414 AERROR << "Failed to parse drive event type:" << type_name;
415 }
416 }
417 drive_event_writer_->Write(drive_event);
418}
419
420void HMIWorker::SensorCalibrationPreprocess(const std::string &task_type) {
421 std::string start_command = absl::StrCat(
422 "nohup bash /apollo/modules/tools/sensor_calibration/extract_data.sh -t ",
423 task_type, " &");
424 System(start_command);
425}
426
427void HMIWorker::VehicleCalibrationPreprocess() {
428 std::string start_command = absl::StrCat(
429 "nohup bash /apollo/modules/tools/vehicle_calibration/preprocess.sh "
430 "--vehicle_type=\"",
431 status_.current_vehicle(), "\" --record_num=", record_count_, " &");
432 System(start_command);
433}
434
435bool HMIWorker::ChangeDrivingMode(const Chassis::DrivingMode mode) {
436 // Always reset to MANUAL mode before changing to other mode.
437 const std::string mode_name = Chassis::DrivingMode_Name(mode);
438 if (mode != Chassis::COMPLETE_MANUAL) {
439 if (!ChangeDrivingMode(Chassis::COMPLETE_MANUAL)) {
440 AERROR << "Failed to reset to MANUAL before changing to " << mode_name;
441 return false;
442 }
443 }
444
445 auto command = std::make_shared<ActionCommand>();
446
447 switch (mode) {
448 case Chassis::COMPLETE_MANUAL:
449 command->set_command(
451 break;
452 case Chassis::COMPLETE_AUTO_DRIVE:
453 command->set_command(
455 break;
456 default:
457 AFATAL << "Change driving mode to " << mode_name << " not implemented!";
458 return false;
459 }
460
461 static constexpr int kMaxTries = 3;
462 static constexpr auto kTryInterval = std::chrono::milliseconds(500);
463 for (int i = 0; i < kMaxTries; ++i) {
464 // Send driving action periodically until entering target driving mode.
465 common::util::FillHeader("HMI", command.get());
466 action_command_client_->SendRequest(command);
467
468 std::this_thread::sleep_for(kTryInterval);
469
470 chassis_reader_->Observe();
471 if (chassis_reader_->Empty()) {
472 AERROR << "No Chassis message received!";
473 } else if (chassis_reader_->GetLatestObserved()->driving_mode() == mode) {
474 return true;
475 }
476 }
477 AERROR << "Failed to change driving mode to " << mode_name;
478 return false;
479}
480
481bool HMIWorker::ChangeMap(const std::string &map_name,
482 const bool restart_dynamic_model) {
483 if (status_.current_map() == map_name && restart_dynamic_model == false) {
484 return true;
485 }
486 const std::string *map_dir = FindOrNull(config_.maps(), map_name);
487 if (map_dir == nullptr) {
488 AERROR << "Unknown map " << map_name;
489 return false;
490 }
491
492 if (map_name != status_.current_map()) {
493 {
494 // Update current_map status.
495 WLock wlock(status_mutex_);
496 status_.set_current_map(map_name);
497 status_changed_ = true;
498 }
499 SetGlobalFlag("map_dir", *map_dir, &FLAGS_map_dir);
500 ResetMode();
501 }
502
503 // true : restart dynamic model on change map
504 // false : change scenario not restart
505 // 1. 场景切换到空场景
506 // 2. 场景切换到其他地图
507 if (restart_dynamic_model) {
508 callback_api_("RestartDynamicModel", {});
509 // 场景id不为空进行切换地图需要停止sim_obstacle
510 StopModuleByCommand(FLAGS_sim_obstacle_stop_command);
511 {
512 WLock wlock(status_mutex_);
513 status_.set_current_scenario_id("");
514 status_changed_ = true;
515 }
516 }
517 return true;
518}
519
520void HMIWorker::UpdateModeModulesAndMonitoredComponents() {
521 status_.clear_modules();
522 for (const auto &iter : current_mode_.modules()) {
523 status_.mutable_modules()->insert({iter.first, false});
524 }
525
526 // Update monitored components of current mode.
527 status_.clear_monitored_components();
528 for (const auto &iter : current_mode_.monitored_components()) {
529 status_.mutable_monitored_components()->insert({iter.first, {}});
530 }
531}
532
533void HMIWorker::ChangeVehicle(const std::string &vehicle_name) {
534 const std::string *vehicle_dir = FindOrNull(config_.vehicles(), vehicle_name);
535 if (vehicle_dir == nullptr) {
536 AERROR << "Unknown vehicle " << vehicle_name;
537 return;
538 }
539 std::string current_mode;
540
541 {
542 // Update current_vehicle status.
543 WLock wlock(status_mutex_);
544 current_mode = status_.current_mode();
545 if (status_.current_vehicle() == vehicle_name) {
546 return;
547 }
548 try {
549 // try to get vehicle type from calibration data directory
550 // TODO(jinping): add vehicle config specs and move to vehicle config
551 const std::string vehicle_type_file_path = *vehicle_dir + "/vehicle_type";
552 std::string vehicle_type_str;
553 cyber::common::GetContent(vehicle_type_file_path, &vehicle_type_str);
554 int vehicle_type = std::stoi(vehicle_type_str);
555 status_.set_current_vehicle_type(vehicle_type);
556 } catch (const std::exception &e) {
557 AWARN << "get vehicle type config failed: " << e.what();
558 status_.clear_current_vehicle_type();
559 }
560 status_.set_current_vehicle(vehicle_name);
561 status_changed_ = true;
562 }
563 ResetMode();
564 // before reset mode
565 HMIMode vehicle_defined_mode;
566 const std::string mode_config_path = config_.modes().at(current_mode);
567 if (LoadVehicleDefinedMode(mode_config_path,
568 *vehicle_dir, &vehicle_defined_mode)) {
569 MergeToCurrentMode(&vehicle_defined_mode);
570 } else {
571 // modules may have been modified the last time selected a vehicle
572 // need to be recovery by load mode
573 current_mode_ = util::HMIUtil::LoadMode(mode_config_path);
574 }
575 {
576 WLock wlock(status_mutex_);
577 UpdateModeModulesAndMonitoredComponents();
578 }
579 ACHECK(VehicleManager::Instance()->UseVehicle(*vehicle_dir));
580 // Restart Fuel Monitor
581 auto *monitors = FuelMonitorManager::Instance()->GetCurrentMonitors();
582 if (monitors != nullptr) {
583 for (const auto &monitor : *monitors) {
584 if (monitor.second->IsEnabled()) {
585 monitor.second->Restart();
586 }
587 }
588 }
589}
590
591void HMIWorker::MergeToCurrentMode(HMIMode *mode) {
592 current_mode_.clear_modules();
593 current_mode_.clear_cyber_modules();
594 current_mode_.clear_monitored_components();
595 current_mode_.mutable_modules()->swap(*(mode->mutable_modules()));
596 current_mode_.mutable_monitored_components()->swap(
597 *(mode->mutable_monitored_components()));
598}
599
600void HMIWorker::ChangeMode(const std::string &mode_name) {
601 if (!ContainsKey(config_.modes(), mode_name)) {
602 AERROR << "Cannot change to unknown mode " << mode_name;
603 return;
604 }
605
606 {
607 RLock rlock(status_mutex_);
608 // Skip if mode doesn't actually change.
609 if (status_.current_mode() == mode_name) {
610 return;
611 }
612 }
613 ResetMode();
614
615 {
616 WLock wlock(status_mutex_);
617 status_.set_current_mode(mode_name);
618 current_mode_ = util::HMIUtil::LoadMode(config_.modes().at(mode_name));
619 // for vehicle self-defined module
620 HMIMode vehicle_defined_mode;
621 const std::string *vehicle_dir =
622 FindOrNull(config_.vehicles(), status_.current_vehicle());
623 if (vehicle_dir != nullptr &&
624 LoadVehicleDefinedMode(config_.modes().at(mode_name), *vehicle_dir,
625 &vehicle_defined_mode)) {
626 MergeToCurrentMode(&vehicle_defined_mode);
627 }
628 UpdateModeModulesAndMonitoredComponents();
629
630 status_.clear_other_components();
631 for (const auto &iter : current_mode_.other_components()) {
632 status_.mutable_other_components()->insert({iter.first, {}});
633 }
634 status_changed_ = true;
635 }
636
637 FuelMonitorManager::Instance()->SetCurrentMode(mode_name);
638 KVDB::Put(FLAGS_current_mode_db_key, mode_name);
639
640 // Toggle Mode Set scenario to empty
641 {
642 WLock wlock(status_mutex_);
643 status_.set_current_scenario_id("");
644 status_changed_ = true;
645 }
646 StopModuleByCommand(FLAGS_sim_obstacle_stop_command);
647}
648
649void HMIWorker::StartModule(const std::string &module) const {
650 const Module *module_conf = FindOrNull(current_mode_.modules(), module);
651 if (module_conf != nullptr) {
652 System(module_conf->start_command());
653 } else {
654 AERROR << "Cannot find module " << module;
655 }
656
657 if (module == "Recorder") {
658 auto *monitors = FuelMonitorManager::Instance()->GetCurrentMonitors();
659 if (monitors != nullptr) {
660 auto iter = monitors->find(FLAGS_data_collection_monitor_name);
661 if (iter != monitors->end()) {
662 auto *data_collection_monitor = iter->second.get();
663 if (data_collection_monitor->IsEnabled() && record_count_ == 0) {
664 data_collection_monitor->Restart();
665 }
666 }
667 ++record_count_;
668 }
669 }
670}
671
672void HMIWorker::StopModule(const std::string &module) const {
673 const Module *module_conf = FindOrNull(current_mode_.modules(), module);
674 if (module_conf != nullptr) {
675 System(module_conf->stop_command());
676 } else {
677 AERROR << "Cannot find module " << module;
678 }
679}
680
681HMIStatus HMIWorker::GetStatus() const {
682 RLock rlock(status_mutex_);
683 return status_;
684}
685
686void HMIWorker::SetupMode() const {
687 for (const auto &iter : current_mode_.modules()) {
688 System(iter.second.start_command());
689 }
690}
691
692void HMIWorker::ResetMode() const {
693 for (const auto &iter : current_mode_.modules()) {
694 System(iter.second.stop_command());
695 }
696 record_count_ = 0;
697}
698
699void HMIWorker::StatusUpdateThreadLoop() {
700 constexpr int kLoopIntervalMs = 200;
701 while (!stop_) {
702 std::this_thread::sleep_for(std::chrono::milliseconds(kLoopIntervalMs));
703 UpdateComponentStatus();
704 bool status_changed = false;
705 {
706 WLock wlock(status_mutex_);
707 status_changed = status_changed_;
708 status_changed_ = false;
709 }
710 // If status doesn't change, check if we reached update interval.
711 if (!status_changed) {
712 static double next_update_time = 0;
713 const double now = Clock::NowInSeconds();
714 if (now < next_update_time) {
715 continue;
716 }
717 next_update_time = now + FLAGS_status_publish_interval;
718 }
719
720 // Trigger registered status change handlers.
721 HMIStatus status = GetStatus();
722 for (const auto handler : status_update_handlers_) {
723 handler(status_changed, &status);
724 }
725 }
726}
727
728void HMIWorker::ResetComponentStatusTimer() {
729 last_status_received_s_ = Clock::NowInSeconds();
730 last_status_fingerprint_ = 0;
731}
732
733void HMIWorker::UpdateComponentStatus() {
734 constexpr double kSecondsTillTimeout(2.5);
735 const double now = Clock::NowInSeconds();
736 if (now - last_status_received_s_.load() > kSecondsTillTimeout) {
737 if (!monitor_timed_out_) {
738 WLock wlock(status_mutex_);
739
740 const uint64_t now_ms = static_cast<uint64_t>(now * 2e3);
741 static constexpr bool kIsReportable = true;
742 SubmitDriveEvent(now_ms, "Monitor timed out", {"PROBLEM"}, kIsReportable);
743 AWARN << "System fault. Auto disengage.";
744 Trigger(HMIAction::DISENGAGE);
745
746 for (auto &monitored_component :
747 *status_.mutable_monitored_components()) {
748 monitored_component.second.set_status(ComponentStatus::UNKNOWN);
749 monitored_component.second.set_message(
750 "Status not reported by Monitor.");
751 }
752 status_changed_ = true;
753 }
754 monitor_timed_out_ = true;
755 } else {
756 monitor_timed_out_ = false;
757 }
758}
759
760void HMIWorker::ChangeScenarioSet(const std::string &scenario_set_id) {
761 {
762 RLock rlock(status_mutex_);
763 auto &scenario_set = status_.scenario_set();
764 if ((!scenario_set_id.empty()) &&
765 (scenario_set.find(scenario_set_id) == scenario_set.end())) {
766 AERROR << "Cannot change to unknown scenario set!";
767 return;
768 }
769 if (status_.current_scenario_set_id() == scenario_set_id) {
770 return;
771 }
772 }
773
774 {
775 WLock wlock(status_mutex_);
776 status_.set_current_scenario_set_id(scenario_set_id);
777 status_changed_ = true;
778 }
779 return;
780}
781
782void HMIWorker::GetScenarioResourcePath(std::string *scenario_resource_path) {
783 CHECK_NOTNULL(scenario_resource_path);
784 const std::string home = cyber::common::GetEnv("HOME");
785 *scenario_resource_path = home + FLAGS_resource_scenario_path;
786}
787
788void HMIWorker::GetScenarioSetPath(const std::string &scenario_set_id,
789 std::string *scenario_set_path) {
790 CHECK_NOTNULL(scenario_set_path);
791 GetScenarioResourcePath(scenario_set_path);
792 *scenario_set_path = *scenario_set_path + scenario_set_id;
793 return;
794}
795
796bool HMIWorker::StopModuleByCommand(const std::string &stop_command) const {
797 int ret = std::system(stop_command.data());
798 if (ret < 0 || !WIFEXITED(ret)) {
799 // 256 does not means failure
800 AERROR << "Failed to stop sim obstacle";
801 return false;
802 }
803 return true;
804}
805
806bool HMIWorker::ResetSimObstacle(const std::string &scenario_id) {
807 bool startObstacle = true;
808 std::string cur_scenario_id;
809 if (scenario_id.empty()) {
810 startObstacle = false;
811 cur_scenario_id = status_.current_scenario_id();
812 } else {
813 cur_scenario_id = scenario_id;
814 }
815
816 std::string absolute_path = FLAGS_sim_obstacle_path;
817
818 // found sim_obstacle binary
819 const std::string command = "which sim_obstacle";
820 std::string result = GetCommandRes(command);
821 if (result != "") {
822 absolute_path = result;
823 } else if (!cyber::common::PathExists(absolute_path)) {
824 AWARN << "Not found sim obstacle";
825 startObstacle = false;
826 }
827 AINFO << "sim_obstacle binary path : " << absolute_path;
828
829 StopModuleByCommand(FLAGS_sim_obstacle_stop_command);
830 std::string scenario_set_id;
831 {
832 RLock rlock(status_mutex_);
833 scenario_set_id = status_.current_scenario_set_id();
834 }
835 std::string scenario_set_path;
836 GetScenarioSetPath(scenario_set_id, &scenario_set_path);
837 const std::string scenario_path =
838 scenario_set_path + "/scenarios/" + cur_scenario_id + ".json";
839 AINFO << "scenario path : " << scenario_path;
840 if (!cyber::common::PathExists(scenario_path)) {
841 AERROR << "Failed to find scenario!";
842 return false;
843 }
844 std::string map_name = "";
845 double x;
846 double y;
847 bool need_to_change_map = true;
848 {
849 RLock rlock(status_mutex_);
850 auto &scenario_set = status_.scenario_set();
851 if (scenario_set.find(scenario_set_id) == scenario_set.end()) {
852 AERROR << "Failed to find scenario set!";
853 return false;
854 }
855 for (auto &scenario : scenario_set.at(scenario_set_id).scenarios()) {
856 if (scenario.scenario_id() == cur_scenario_id) {
857 map_name = scenario.map_name();
858 x = scenario.start_point().x();
859 y = scenario.start_point().y();
860 break;
861 }
862 }
863 if (map_name.empty()) {
864 AERROR << "Failed to find scenario and get map dir!";
865 return false;
866 }
867 need_to_change_map = (status_.current_map() != map_name);
868 }
869 // resetmodule before save open modules
870 std::vector<std::string> modules_open;
871 auto modulesMap = status_.modules();
872 for (auto it = modulesMap.begin(); it != modulesMap.end(); ++it) {
873 if (it->second == true) {
874 modules_open.push_back(it->first);
875 }
876 }
877 if (need_to_change_map) {
878 if (!ChangeMap(map_name, false)) {
879 AERROR << "Failed to change map!";
880 return false;
881 }
882 callback_api_("MapServiceReloadMap", {});
883 }
884 // TODO(huanguang): if not changing map don't need to reset module
885 // for (const auto &module : modules_open) {
886 // StartModule(module);
887 // }
888 // After changing the map, reset the start point from the scenario by
889 // sim_control
890 Json info;
891 info["x"] = x;
892 info["y"] = y;
893 callback_api_("SimControlRestart", info);
894 if (startObstacle) {
895 // 启动sim obstacle
896 const std::string start_command = "nohup " + absolute_path + " " +
897 scenario_path + FLAGS_gflag_command_arg +
898 " &";
899 AINFO << "start sim_obstacle command : " << start_command;
900 int ret = std::system(start_command.data());
901 if (ret != 0) {
902 AERROR << "Failed to start sim obstacle";
903 return false;
904 }
905 }
906
907 return true;
908}
909
910std::string HMIWorker::GetCommandRes(const std::string &cmd) {
911 if (cmd.size() > 128) {
912 AERROR << "command size exceeds 128";
913 return "";
914 }
915 const char *cmdPtr = cmd.c_str();
916 char buffer[128];
917 std::string result = "";
918
919 FILE *pipe = popen(cmdPtr, "r");
920 while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
921 result += buffer;
922 }
923 if (result.back() == '\n') {
924 result.pop_back();
925 }
926 pclose(pipe);
927 return result;
928}
929
930void HMIWorker::ChangeScenario(const std::string &scenario_id) {
931 {
932 RLock rlock(status_mutex_);
933 // Skip if mode doesn't actually change.
934 if (status_.current_scenario_id() == scenario_id) {
935 return;
936 }
937 if (scenario_id.empty()) {
938 // stop sim obstacle
939 // todo: add check status
940 // directly think pkill successful
941 StopModuleByCommand(FLAGS_sim_obstacle_stop_command);
942 } else {
943 auto scenario_set = status_.mutable_scenario_set();
944 auto &scenario_set_id = status_.current_scenario_set_id();
945 if (scenario_set->find(scenario_set_id) == scenario_set->end()) {
946 AERROR << "Current scenario set is invalid!";
947 return;
948 }
949 bool find_res = false;
950 for (auto &scenario : (*scenario_set)[scenario_set_id].scenarios()) {
951 if (scenario.scenario_id() == scenario_id) {
952 find_res = true;
953 break;
954 }
955 }
956 if (!find_res) {
957 AERROR << "Cannot change to unknown scenario!";
958 return;
959 }
960 }
961 }
962
963 // restart sim obstacle
964 // move sim obstacle position for rlock wlock together will result to dead
965 // lock
966 if (!ResetSimObstacle(scenario_id)) {
967 AERROR << "Cannot start sim obstacle by new scenario!";
968 return;
969 }
970
971 {
972 WLock wlock(status_mutex_);
973 status_.set_current_scenario_id(scenario_id);
974 status_changed_ = true;
975 }
976 return;
977}
978
979void HMIWorker::ChangeDynamicModel(const std::string &dynamic_model_name) {
980 // To avoid toggle sim control and always choose simulation perfect control
981 // {
982 // RLock rlock(status_mutex_);
983 // // Skip if mode doesn't actually change.
984 // if (status_.current_dynamic_model() == dynamic_model_name) {
985 // return;
986 // }
987 // }
988 if (dynamic_model_name.empty()) {
989 AERROR << "Failed to change empty dynamic model!";
990 return;
991 }
992 Json param_json({});
993 param_json["dynamic_model_name"] = dynamic_model_name;
994 Json callback_res = callback_api_("ChangeDynamicModel", param_json);
995 if (!callback_res.contains("result") || !callback_res["result"]) {
996 // badcase1:sim control is not enabled. badcase2:miss params
997 // badcase3:change dynamic model is not registered. resolution:return with
998 // no action,keep sim control not enabled or use original dynamic model!
999 AERROR << "Failed to change dynamic model! Please check if the param is "
1000 "valid!";
1001 return;
1002 }
1003 std::string current_dynamic_model_name = dynamic_model_name;
1004 {
1005 WLock wlock(status_mutex_);
1006 status_.set_current_dynamic_model(dynamic_model_name);
1007 status_changed_ = true;
1008 }
1009
1010 return;
1011}
1012
1013bool HMIWorker::UpdateScenarioSetToStatus(
1014 const std::string &scenario_set_id, const std::string &scenario_set_name) {
1015 ScenarioSet new_scenario_set;
1016 if (!UpdateScenarioSet(scenario_set_id, scenario_set_name,
1017 &new_scenario_set)) {
1018 AERROR << "Failed to update scenario_set!";
1019 return false;
1020 }
1021 {
1022 WLock wlock(status_mutex_);
1023 auto scenario_set = status_.mutable_scenario_set();
1024 scenario_set->erase(scenario_set_id);
1025 (*scenario_set)[scenario_set_id] = new_scenario_set;
1026 status_changed_ = true;
1027 }
1028 return true;
1029}
1030
1031bool HMIWorker::UpdateDynamicModelToStatus(
1032 const std::string &dynamic_model_name) {
1033 Json param_json({});
1034 param_json["dynamic_model_name"] = dynamic_model_name;
1035 Json callback_res = callback_api_("AddDynamicModel", param_json);
1036 if (!callback_res.contains("result") || !callback_res["result"]) {
1037 AERROR << "Failed to add dynamic model to local dynamic model list for "
1038 "register failed!";
1039 return false;
1040 }
1041 {
1042 WLock wlock(status_mutex_);
1043 for (const auto &iter : status_.dynamic_models()) {
1044 if (iter == dynamic_model_name) {
1045 AERROR << "Do not need to add new dynamic model for is duplicate!";
1046 return true;
1047 }
1048 }
1049 status_.add_dynamic_models(dynamic_model_name);
1050 status_changed_ = true;
1051 }
1052 return true;
1053}
1054
1055bool HMIWorker::UpdateScenarioSet(const std::string &scenario_set_id,
1056 const std::string &scenario_set_name,
1057 ScenarioSet *new_scenario_set) {
1058 std::string scenario_set_directory_path;
1059 GetScenarioSetPath(scenario_set_id, &scenario_set_directory_path);
1060 scenario_set_directory_path = scenario_set_directory_path + "/scenarios/";
1061 new_scenario_set->set_scenario_set_name(scenario_set_name);
1062 if (!cyber::common::PathExists(scenario_set_directory_path)) {
1063 AERROR << "Scenario set has no scenarios!";
1064 return true;
1065 }
1066 DIR *directory = opendir(scenario_set_directory_path.c_str());
1067 if (directory == nullptr) {
1068 AERROR << "Cannot open directory " << scenario_set_directory_path;
1069 return false;
1070 }
1071
1072 struct dirent *file;
1073 while ((file = readdir(directory)) != nullptr) {
1074 // skip directory_path/. and directory_path/..
1075 if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) {
1076 continue;
1077 }
1078 const std::string file_name = file->d_name;
1079 if (!absl::EndsWith(file_name, ".json")) {
1080 continue;
1081 }
1082 const int index = file_name.rfind(".json");
1083 if (index == 0) {
1084 // name: ".json" is invalid.
1085 continue;
1086 }
1087 const std::string scenario_id = file_name.substr(0, index);
1088 const std::string file_path = scenario_set_directory_path + file_name;
1089 SimTicket new_sim_ticket;
1090 if (!cyber::common::GetProtoFromJsonFile(file_path, &new_sim_ticket)) {
1091 AERROR << "Cannot parse this scenario:" << file_path;
1092 return false;
1093 }
1094 if (!new_sim_ticket.has_scenario()) {
1095 AERROR << "Cannot get scenario.";
1096 return false;
1097 }
1098 if (!new_sim_ticket.description_en_tokens_size()) {
1099 AERROR << "Cannot get scenario name.";
1100 return false;
1101 }
1102 if (!new_sim_ticket.scenario().has_map_dir()) {
1103 AERROR << "Cannot get scenario map dir.";
1104 return false;
1105 }
1106 if (!new_sim_ticket.scenario().has_start()) {
1107 AERROR << "Cannot get scenario start_point.";
1108 return false;
1109 }
1110 auto &scenario_start_point = new_sim_ticket.scenario().start();
1111 if (!scenario_start_point.has_x() || !scenario_start_point.has_y()) {
1112 AERROR << "Scenario start_point is invalid!";
1113 return false;
1114 }
1115 std::string scenario_name = new_sim_ticket.description_en_tokens(0);
1116 for (int i = 1; i < new_sim_ticket.description_en_tokens_size(); i++) {
1117 scenario_name =
1118 scenario_name + "_" + new_sim_ticket.description_en_tokens(i);
1119 }
1120 ScenarioInfo *scenario_info = new_scenario_set->add_scenarios();
1121 scenario_info->set_scenario_id(scenario_id);
1122 scenario_info->set_scenario_name(scenario_name);
1123 // change scenario json map dir to map name
1124 // format:modules/map/data/${map_name}
1125 const std::string map_dir = new_sim_ticket.scenario().map_dir();
1126 size_t idx = map_dir.find_last_of('/');
1127 if (idx == map_dir.npos) {
1128 AERROR << "Cannot get scenario map name.";
1129 return false;
1130 }
1131 const std::string map_name = map_dir.substr(idx + 1);
1132 if (map_name.empty()) {
1133 AERROR << "Cannot get scenario map name.";
1134 return false;
1135 }
1136 // replay engine use xx_xx like:apollo_map
1137 // dv need Apollo Map
1138 scenario_info->set_map_name(util::HMIUtil::TitleCase(map_name));
1139 auto start_point = scenario_info->mutable_start_point();
1140 start_point->set_x(scenario_start_point.x());
1141 start_point->set_y(scenario_start_point.y());
1142 }
1143 closedir(directory);
1144 return true;
1145}
1146
1147bool HMIWorker::LoadScenarios() {
1148 std::string directory_path;
1149 GetScenarioResourcePath(&directory_path);
1150 if (!cyber::common::PathExists(directory_path)) {
1151 AERROR << "Failed to find scenario_set!";
1152 return false;
1153 }
1154 DIR *directory = opendir(directory_path.c_str());
1155 if (directory == nullptr) {
1156 AERROR << "Cannot open directory " << directory_path;
1157 return false;
1158 }
1159 struct dirent *file;
1160 std::map<std::string, ScenarioSet> scenario_sets;
1161 while ((file = readdir(directory)) != nullptr) {
1162 // skip directory_path/. and directory_path/..
1163 if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) {
1164 continue;
1165 }
1166 if (file->d_type != DT_DIR) {
1167 continue;
1168 }
1169 const std::string scenario_set_id = file->d_name;
1170 const std::string scenario_set_json_path =
1171 directory_path + scenario_set_id + "/scenario_set.json";
1172 // scenario_set.json use message:UserAdsGroup
1173 UserAdsGroup user_ads_group_info;
1174 if (!cyber::common::GetProtoFromJsonFile(scenario_set_json_path,
1175 &user_ads_group_info)) {
1176 AERROR << "Unable to parse UserAdsGroup from file "
1177 << scenario_set_json_path;
1178 continue;
1179 }
1180 if (!user_ads_group_info.has_name()) {
1181 AERROR << "Failed to get ads group name!";
1182 continue;
1183 }
1184 const std::string scenario_set_name = user_ads_group_info.name();
1185 ScenarioSet new_scenario_set;
1186 if (!UpdateScenarioSet(scenario_set_id, scenario_set_name,
1187 &new_scenario_set)) {
1188 AERROR << "Failed to update scenario_set!";
1189 continue;
1190 }
1191 scenario_sets[scenario_set_id] = new_scenario_set;
1192 }
1193 closedir(directory);
1194 {
1195 WLock wlock(status_mutex_);
1196 auto scenario_set = status_.mutable_scenario_set();
1197 // clear old data
1198 scenario_set->clear();
1199 for (auto iter = scenario_sets.begin(); iter != scenario_sets.end();
1200 iter++) {
1201 (*scenario_set)[iter->first] = iter->second;
1202 }
1203 status_changed_ = true;
1204 }
1205 return true;
1206}
1207
1208bool HMIWorker::LoadDynamicModels() {
1209 Json load_res = callback_api_("LoadDynamicModels", {});
1210 if (!load_res.contains("result") || !load_res["result"]) {
1211 return false;
1212 }
1213
1214 {
1215 WLock wlock(status_mutex_);
1216 auto dynamic_models = status_.mutable_dynamic_models();
1217 // clear old data
1218 for (auto iter = dynamic_models->begin(); iter != dynamic_models->end();) {
1219 iter = dynamic_models->erase(iter);
1220 }
1221 for (const auto &dynamic_model : load_res["loaded_dynamic_models"]) {
1222 status_.add_dynamic_models(dynamic_model);
1223 }
1224 status_changed_ = true;
1225 }
1226 return load_res["result"];
1227}
1228
1229void HMIWorker::DeleteScenarioSet(const std::string &scenario_set_id) {
1230 if (scenario_set_id.empty()) {
1231 return;
1232 }
1233 std::string directory_path;
1234 GetScenarioResourcePath(&directory_path);
1235 directory_path = directory_path + scenario_set_id;
1236 if (!cyber::common::PathExists(directory_path)) {
1237 AERROR << "Failed to find scenario_set!";
1238 return;
1239 }
1240 std::string command = "rm -fr " + directory_path;
1241 // use cyber::common::removeFiles do not support sub-directory
1242 // use rmdir do not support not empty directory
1243 if (std::system(command.data()) != 0) {
1244 AERROR << "Failed to delete scenario set directory for: "
1245 << std::strerror(errno);
1246 return;
1247 }
1248
1249 {
1250 RLock rlock(status_mutex_);
1251 auto &scenario_set = status_.scenario_set();
1252 if (scenario_set.find(scenario_set_id) == scenario_set.end()) {
1253 AERROR << "Cannot find unknown scenario set!";
1254 return;
1255 }
1256 // do not allowed remove changed current scenario set
1257 if (scenario_set_id == status_.current_scenario_set_id()) {
1258 AERROR << "Cannotdelete current scenario set!";
1259 return;
1260 }
1261 }
1262
1263 {
1264 WLock wlock(status_mutex_);
1265 status_.mutable_scenario_set()->erase(scenario_set_id);
1266 status_changed_ = true;
1267 }
1268 return;
1269}
1270
1271void HMIWorker::DeleteDynamicModel(const std::string &dynamic_model_name) {
1272 if (dynamic_model_name.empty()) {
1273 AERROR << "Invalid param:empty dynamic model name!";
1274 return;
1275 }
1276 {
1277 RLock rlock(status_mutex_);
1278 // do not allowed remove changed current dynamic model
1279 if (dynamic_model_name == status_.current_dynamic_model()) {
1280 AERROR << "Cannot delete current dynamic model!";
1281 return;
1282 }
1283 if (dynamic_model_name == FLAGS_sim_perfect_control) {
1284 AERROR << "Cannot delete default sim control:SimPerfectControl!";
1285 return;
1286 }
1287 }
1288 Json param_json({});
1289 param_json["dynamic_model_name"] = dynamic_model_name;
1290 Json callback_res = callback_api_("DeleteDynamicModel", param_json);
1291 if (!callback_res.contains("result") || !callback_res["result"]) {
1292 // badcase1: sim control is not enable. badcase2: miss param
1293 // badcase3: Failed to delete file
1294 AERROR << "Failed to delete dynamic model!";
1295 return;
1296 }
1297 {
1298 WLock wlock(status_mutex_);
1299 auto iter = status_.dynamic_models().begin();
1300 while (iter != status_.dynamic_models().end()) {
1301 if (*iter == dynamic_model_name) {
1302 break;
1303 }
1304 iter++;
1305 }
1306 if (iter != status_.dynamic_models().end()) {
1307 status_.mutable_dynamic_models()->erase(iter);
1308 status_changed_ = true;
1309 } else {
1310 AWARN << "Can not find dynamic model to delete!";
1311 }
1312 }
1313 return;
1314}
1315
1316void HMIWorker::GetRecordPath(std::string *record_path) {
1317 CHECK_NOTNULL(record_path);
1318 const std::string home = cyber::common::GetEnv("HOME");
1319 *record_path = home + FLAGS_resource_record_path;
1320}
1321
1322bool HMIWorker::RePlayRecord(const std::string &record_id) {
1323 std::string record_path;
1324 GetRecordPath(&record_path);
1325 record_path = record_path + record_id + ".record";
1326
1327 if (!cyber::common::PathExists(record_path)) {
1328 AERROR << "Failed to find record!";
1329 return false;
1330 }
1331 // play the record
1332 const std::string play_command =
1333 absl::StrCat("nohup cyber_recorder play -l -f ", record_path, " &");
1334 int ret = std::system(play_command.data());
1335 if (ret != 0) {
1336 AERROR << "Failed to start cyber play command";
1337 return false;
1338 }
1339 return true;
1340}
1341void HMIWorker::StopRecordPlay() {
1342 WLock wlock(status_mutex_);
1343 { status_.set_current_record_id(""); }
1344 if (!StopModuleByCommand(FLAGS_cyber_recorder_stop_command)) {
1345 AERROR << "stop record failed";
1346 }
1347 status_changed_ = true;
1348}
1349void HMIWorker::ChangeRecord(const std::string &record_id) {
1350 StopRecordPlay();
1351 {
1352 RLock rlock(status_mutex_);
1353 auto status_records = status_.mutable_records();
1354 if (status_records->find(record_id) == status_records->end()) {
1355 AERROR << "Cannot change to unknown record!";
1356 return;
1357 }
1358 if (!RePlayRecord(record_id)) {
1359 return;
1360 }
1361 }
1362 WLock wlock(status_mutex_);
1363 status_.set_current_record_id(record_id);
1364 status_changed_ = true;
1365 return;
1366}
1367bool HMIWorker::LoadRecords() {
1368 std::string directory_path;
1369 GetRecordPath(&directory_path);
1370 if (!cyber::common::PathExists(directory_path)) {
1371 AERROR << "Failed to find records!";
1372 return false;
1373 }
1374 DIR *directory = opendir(directory_path.c_str());
1375 if (directory == nullptr) {
1376 AERROR << "Cannot open record directory" << directory_path;
1377 return false;
1378 }
1379 struct dirent *file;
1380 std::map<std::string, LoadRecordInfo> new_records;
1381 while ((file = readdir(directory)) != nullptr) {
1382 if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) {
1383 continue;
1384 }
1385 if (file->d_type == DT_DIR) {
1386 continue;
1387 }
1388 const std::string record_id = file->d_name;
1389 const int index = record_id.rfind(".record");
1390 // Skip format that dv cannot parse: record not ending in record
1391 size_t record_suffix_length = 7;
1392 if (record_id.length() - index != record_suffix_length) {
1393 continue;
1394 }
1395 if (index != -1 && record_id[0] != '.') {
1396 const std::string local_record_resource = record_id.substr(0, index);
1397 // compatible records with dv and dv_plus
1398 new_records[local_record_resource] = {};
1399 new_records[local_record_resource].set_download_status(1);
1400 }
1401 }
1402 closedir(directory);
1403 {
1404 WLock wlock(status_mutex_);
1405 auto status_records = status_.mutable_records();
1406 status_records->clear();
1407 for (auto iter = new_records.begin(); iter != new_records.end(); iter++) {
1408 (*status_records)[iter->first] = iter->second;
1409 }
1410 status_changed_ = true;
1411 }
1412 return true;
1413}
1414
1415void HMIWorker::DeleteRecord(const std::string &record_id) {
1416 StopRecordPlay();
1417 if (record_id.empty()) {
1418 return;
1419 }
1420 std::string record_path;
1421 GetRecordPath(&record_path);
1422 record_path = record_path + record_id + ".record";
1423 if (!cyber::common::PathExists(record_path)) {
1424 return;
1425 }
1426 // find the delete record if exist and judge the record whether playing now
1427 {
1428 RLock rlock(status_mutex_);
1429 auto &status_records = status_.records();
1430 if (status_records.find(record_id) == status_records.end()) {
1431 return;
1432 }
1433 if (record_id == status_.current_record_id()) {
1434 return;
1435 }
1436 }
1437 {
1438 WLock wlock(status_mutex_);
1439 status_.mutable_records()->erase(record_id);
1440 status_changed_ = true;
1441 }
1442
1443 // delete record from disk
1444 std::string command = "rm -rf " + record_path;
1445 if (std::system(command.data()) != 0) {
1446 AERROR << "Failed to delete record for: " << std::strerror(errno);
1447 return;
1448 }
1449 return;
1450}
1451bool HMIWorker::ReloadVehicles() {
1452 AINFO << "load config";
1453 HMIConfig config = util::HMIUtil::LoadConfig(FLAGS_dv_hmi_modes_config_path);
1454 std::string msg;
1455 AINFO << "serialize new config";
1456 config.SerializeToString(&msg);
1457
1458 WLock wlock(status_mutex_);
1459 AINFO << "parse new config";
1460 config_.ParseFromString(msg);
1461 AINFO << "init status";
1462 // status_.clear_modes();
1463 // status_.clear_maps();
1464 AINFO << "clear vehicles";
1465 status_.clear_vehicles();
1466 // InitStatus();
1467 // Populate vehicles and current_vehicle.
1468 AINFO << "reload vehicles";
1469 for (const auto &vehicle : config_.vehicles()) {
1470 status_.add_vehicles(vehicle.first);
1471 }
1472 status_changed_ = true;
1473 return true;
1474}
1475
1476void HMIWorker::UpdateCameraSensorChannelToStatus(
1477 const std::string &channel_name) {
1478 {
1479 WLock wlock(status_mutex_);
1480 if (status_.current_camera_sensor_channel() == channel_name) {
1481 AINFO << "Input channel name is current camera sensor channel";
1482 return;
1483 }
1484 status_.set_current_camera_sensor_channel(channel_name);
1485 status_changed_ = true;
1486 }
1487}
1488
1489void HMIWorker::UpdatePointCloudChannelToStatus(
1490 const std::string &channel_name) {
1491 {
1492 WLock wlock(status_mutex_);
1493 if (status_.current_point_cloud_channel() == channel_name) {
1494 AINFO << "Input channel name is current camera sensor channel";
1495 return;
1496 }
1497 status_.set_current_point_cloud_channel(channel_name);
1498 status_changed_ = true;
1499 }
1500}
1501} // namespace dreamview
1502} // namespace apollo
Lightweight key-value database to store system-wide parameters.
Definition kv_db.h:38
a singleton clock that can be used to get the current timestamp.
Definition clock.h:39
static double NowInSeconds()
gets the current time in second.
Definition clock.cc:56
Node is the fundamental building block of Cyber RT.
Definition node.h:44
std::function< nlohmann::json(const std::string &function_name, const nlohmann::json &param_json)> DvCallback
Definition hmi_worker.h:59
#define ACHECK(cond)
Definition log.h:80
#define AERROR
Definition log.h:44
#define AERROR_IF(cond)
Definition log.h:74
#define AFATAL
Definition log.h:45
#define AINFO
Definition log.h:42
#define AWARN
Definition log.h:43
Some map util functions.
Some string util functions.
nlohmann::json Json
size_t MessageFingerprint(const google::protobuf::Message &message)
bool LoadConfig(const std::string &relative_path, T *config)
Definition file.h:265
boost::shared_lock< boost::shared_mutex > RLock
boost::unique_lock< boost::shared_mutex > WLock
class register implement
Definition arena_queue.h:37
repeated string description_en_tokens
optional apollo::simulation::Scenario scenario