1 // Copyright 2022 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "controller/scene_controller.h"
16
17 #include <chrono>
18 #include <cmath>
19 #include <cstddef>
20 #include <optional>
21
22 #include "controller/device_notify_manager.h"
23 #include "util/log.h"
24
25 namespace netsim {
26 namespace controller {
27 namespace {
28 constexpr std::chrono::seconds kInactiveLimitToShutdown(300);
29 }
30
31 /* static */
Singleton()32 SceneController &SceneController::Singleton() {
33 static SceneController *kInstance = new SceneController();
34 return *kInstance;
35 }
36
Get()37 model::Scene SceneController::Get() {
38 model::Scene scene;
39 for (auto &[_, device] : devices_) {
40 scene.add_devices()->CopyFrom(device->Get());
41 }
42 return scene;
43 }
44
AddChip(const std::string & guid,const std::string & device_name,common::ChipKind chip_kind,const std::string & chip_name,const std::string & manufacturer,const std::string & product_name)45 std::tuple<uint32_t, uint32_t, uint32_t> SceneController::AddChip(
46 const std::string &guid, const std::string &device_name,
47 common::ChipKind chip_kind, const std::string &chip_name,
48 const std::string &manufacturer, const std::string &product_name) {
49 auto device = GetDevice(guid, device_name);
50 // TODO: catch case of similar name chips
51 auto [chip_id, facade_id] =
52 device->AddChip(chip_kind, chip_name, manufacturer, product_name);
53 inactive_timestamp_.reset();
54 return {device->id, chip_id, facade_id};
55 }
56
GetDevice(const std::string & guid,const std::string & name)57 std::shared_ptr<Device> SceneController::GetDevice(const std::string &guid,
58 const std::string &name) {
59 std::unique_lock<std::mutex> lock(this->mutex_);
60 for (auto &[_, device] : devices_) {
61 if (device->guid == guid) return device;
62 }
63 static uint32_t identifier = 0;
64 auto device = std::make_shared<Device>(identifier, guid, name);
65 devices_[identifier++] = device;
66 return device;
67 }
68
RemoveDevice(uint32_t id)69 void SceneController::RemoveDevice(uint32_t id) {
70 if (devices_.find(id) != devices_.end()) {
71 auto device = devices_[id];
72 BtsLog("SceneController::RemoveDevice - removing %s", device->name.c_str());
73 device->Remove();
74 devices_.erase(id);
75 } else {
76 BtsLog("Device not found in remove %d", id);
77 }
78 }
79
RemoveChip(uint32_t device_id,uint32_t chip_id)80 void SceneController::RemoveChip(uint32_t device_id, uint32_t chip_id) {
81 std::unique_lock<std::mutex> lock(this->mutex_);
82 BtsLog("Scene RemoveChip %d", chip_id);
83 if (devices_.find(device_id) != devices_.end()) {
84 auto device = devices_[device_id];
85 if (device->RemoveChip(chip_id)) {
86 BtsLog("SceneController::RemoveChip device %d, no more chips", device_id);
87 this->RemoveDevice(device_id);
88 if (devices_.empty())
89 inactive_timestamp_.emplace(std::chrono::system_clock::now());
90 }
91 } else {
92 std::cerr << "Trying to remove chip from unknown device" << std::endl;
93 }
94 }
95
96 // Returns a Device shared_ptr or nullptr
MatchDevice(const std::string & name)97 std::shared_ptr<Device> SceneController::MatchDevice(const std::string &name) {
98 std::shared_ptr<Device> target = nullptr;
99 bool multiple_matches = false;
100 if (name.empty()) {
101 return nullptr;
102 }
103 for (auto &[_, device] : devices_) {
104 // match by device name
105 auto pos = device->name.find(name);
106 if (pos != std::string::npos) {
107 // check for exact match
108 if (device->name == name) return device;
109 // record if multiple ambiguous matches were found
110 if (target != nullptr) multiple_matches = true;
111 target = device;
112 }
113 }
114 // return nullptr if multiple ambiguous matches were found
115 if (multiple_matches) return nullptr;
116 return target;
117 }
118
119 // UI requesting a change in device info
PatchDevice(const model::Device & request)120 bool SceneController::PatchDevice(const model::Device &request) {
121 std::unique_lock<std::mutex> lock(this->mutex_);
122 if (request.name().empty()) {
123 return false;
124 }
125 auto device = MatchDevice(request.name());
126 if (device == nullptr) return false;
127 device->Patch(request);
128 DeviceNotifyManager::Get().Notify();
129 return true;
130 }
131
132 // Euclidian distance between two devices.
GetDistance(uint32_t id,uint32_t other_id)133 float SceneController::GetDistance(uint32_t id, uint32_t other_id) {
134 if (devices_.find(id) == devices_.end() ||
135 devices_.find(other_id) == devices_.end()) {
136 BtsLog("Error in GetDistance %d, %d", id, other_id);
137 return 0.0;
138 }
139 auto a = devices_[id]->position;
140 auto b = devices_[other_id]->position;
141 return sqrt(
142 (pow(a.x() - b.x(), 2) + pow(a.y() - b.y(), 2) + pow(a.z() - b.z(), 2)));
143 }
144
Reset()145 void SceneController::Reset() {
146 std::unique_lock<std::mutex> lock(this->mutex_);
147 for (auto &[_, device] : devices_) {
148 device->Reset();
149 }
150 DeviceNotifyManager::Get().Notify();
151 }
152
GetShutdownTime()153 std::optional<std::chrono::seconds> SceneController::GetShutdownTime() {
154 if (!inactive_timestamp_.has_value()) return std::nullopt;
155
156 auto inactive_seconds = std::chrono::duration_cast<std::chrono::seconds>(
157 std::chrono::system_clock::now() - inactive_timestamp_.value());
158 auto remaining_seconds = kInactiveLimitToShutdown - inactive_seconds;
159 return std::optional<std::chrono::seconds>(remaining_seconds);
160 }
161 } // namespace controller
162 } // namespace netsim
163