Alexei Frolov | 90980ad | 2024-07-16 21:26:21 +0000 | [diff] [blame] | 1 | |
| 2 | // Copyright 2024 The Pigweed Authors |
| 3 | // |
| 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| 5 | // use this file except in compliance with the License. You may obtain a copy of |
| 6 | // the License at |
| 7 | // |
| 8 | // https://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, WITHOUT |
| 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| 13 | // License for the specific language governing permissions and limitations under |
| 14 | // the License. |
| 15 | |
| 16 | #include "modules/pubsub/service.h" |
| 17 | |
Alexei Frolov | 6b8cb7d | 2024-08-05 17:44:38 +0000 | [diff] [blame] | 18 | #include "modules/state_manager/state_manager.h" |
Aaron Green | 2da5fea | 2024-08-05 21:18:36 +0000 | [diff] [blame] | 19 | #include "pw_assert/check.h" |
Alexei Frolov | 90980ad | 2024-07-16 21:26:21 +0000 | [diff] [blame] | 20 | #include "pw_log/log.h" |
Alexei Frolov | 6b8cb7d | 2024-08-05 17:44:38 +0000 | [diff] [blame] | 21 | #include "pw_string/util.h" |
Alexei Frolov | 90980ad | 2024-07-16 21:26:21 +0000 | [diff] [blame] | 22 | |
Keir Mierle | 4d11a12 | 2024-07-25 17:50:02 +0000 | [diff] [blame] | 23 | namespace sense { |
Alexei Frolov | 90980ad | 2024-07-16 21:26:21 +0000 | [diff] [blame] | 24 | namespace { |
| 25 | |
| 26 | pubsub_Event EventToProto(const Event& event) { |
| 27 | pubsub_Event proto = pubsub_Event_init_default; |
| 28 | |
Aaron Green | 366f458 | 2024-07-31 22:27:20 +0000 | [diff] [blame] | 29 | if (std::holds_alternative<ButtonA>(event)) { |
Alexei Frolov | 90980ad | 2024-07-16 21:26:21 +0000 | [diff] [blame] | 30 | proto.which_type = pubsub_Event_button_a_pressed_tag; |
Erik Gilling | 106fd59 | 2024-07-16 23:49:15 +0000 | [diff] [blame] | 31 | proto.type.button_a_pressed = std::get<ButtonA>(event).pressed(); |
| 32 | } else if (std::holds_alternative<ButtonB>(event)) { |
Alexei Frolov | 90980ad | 2024-07-16 21:26:21 +0000 | [diff] [blame] | 33 | proto.which_type = pubsub_Event_button_b_pressed_tag; |
Erik Gilling | 106fd59 | 2024-07-16 23:49:15 +0000 | [diff] [blame] | 34 | proto.type.button_b_pressed = std::get<ButtonB>(event).pressed(); |
| 35 | } else if (std::holds_alternative<ButtonX>(event)) { |
Alexei Frolov | 90980ad | 2024-07-16 21:26:21 +0000 | [diff] [blame] | 36 | proto.which_type = pubsub_Event_button_x_pressed_tag; |
Erik Gilling | 106fd59 | 2024-07-16 23:49:15 +0000 | [diff] [blame] | 37 | proto.type.button_x_pressed = std::get<ButtonX>(event).pressed(); |
| 38 | } else if (std::holds_alternative<ButtonY>(event)) { |
Alexei Frolov | 90980ad | 2024-07-16 21:26:21 +0000 | [diff] [blame] | 39 | proto.which_type = pubsub_Event_button_y_pressed_tag; |
Erik Gilling | 106fd59 | 2024-07-16 23:49:15 +0000 | [diff] [blame] | 40 | proto.type.button_y_pressed = std::get<ButtonY>(event).pressed(); |
Aaron Green | d56ce1a | 2024-07-31 21:05:52 +0000 | [diff] [blame] | 41 | } else if (std::holds_alternative<TimerRequest>(event)) { |
| 42 | proto.which_type = pubsub_Event_timer_request_tag; |
| 43 | auto& timer_request = std::get<TimerRequest>(event); |
| 44 | proto.type.timer_request.token = timer_request.token; |
| 45 | proto.type.timer_request.timeout_s = timer_request.timeout_s; |
| 46 | } else if (std::holds_alternative<TimerExpired>(event)) { |
| 47 | proto.which_type = pubsub_Event_timer_expired_tag; |
| 48 | auto& timer_expired = std::get<TimerExpired>(event); |
| 49 | proto.type.timer_expired.token = timer_expired.token; |
Erik Gilling | 106fd59 | 2024-07-16 23:49:15 +0000 | [diff] [blame] | 50 | } else if (std::holds_alternative<ProximityStateChange>(event)) { |
| 51 | proto.which_type = pubsub_Event_proximity_tag; |
| 52 | proto.type.proximity = std::get<ProximityStateChange>(event).proximity; |
Wyatt Hepler | 18d0eda | 2024-07-17 17:59:42 +0000 | [diff] [blame] | 53 | } else if (std::holds_alternative<ProximitySample>(event)) { |
| 54 | proto.which_type = pubsub_Event_proximity_level_tag; |
| 55 | proto.type.proximity_level = std::get<ProximitySample>(event).sample; |
Wyatt Hepler | ee22f03 | 2024-07-30 18:18:28 +0000 | [diff] [blame] | 56 | } else if (std::holds_alternative<AmbientLightSample>(event)) { |
| 57 | proto.which_type = pubsub_Event_ambient_light_lux_tag; |
| 58 | proto.type.ambient_light_lux = |
| 59 | std::get<AmbientLightSample>(event).sample_lux; |
Aaron Green | b0ca10f | 2024-07-25 21:59:27 +0000 | [diff] [blame] | 60 | } else if (std::holds_alternative<AirQuality>(event)) { |
| 61 | proto.which_type = pubsub_Event_air_quality_tag; |
| 62 | proto.type.air_quality = std::get<AirQuality>(event).score; |
Wyatt Hepler | e2b8ed6 | 2024-07-27 00:33:18 +0000 | [diff] [blame] | 63 | } else if (std::holds_alternative<MorseEncodeRequest>(event)) { |
| 64 | proto.which_type = pubsub_Event_morse_encode_request_tag; |
| 65 | const auto& morse = std::get<MorseEncodeRequest>(event); |
| 66 | auto& msg = proto.type.morse_encode_request.msg; |
| 67 | msg[morse.message.copy(proto.type.morse_encode_request.msg, |
| 68 | sizeof(msg) - 1)] = '\0'; |
| 69 | proto.type.morse_encode_request.repeat = morse.repeat; |
Aaron Green | dc5e51f | 2024-07-29 21:40:35 +0000 | [diff] [blame] | 70 | } else if (std::holds_alternative<MorseCodeValue>(event)) { |
| 71 | proto.which_type = pubsub_Event_morse_code_value_tag; |
| 72 | const auto& morse = std::get<MorseCodeValue>(event); |
| 73 | proto.type.morse_code_value.turn_on = morse.turn_on; |
| 74 | proto.type.morse_code_value.message_finished = morse.message_finished; |
Alexei Frolov | 6b8cb7d | 2024-08-05 17:44:38 +0000 | [diff] [blame] | 75 | } else if (std::holds_alternative<SenseState>(event)) { |
| 76 | proto.which_type = pubsub_Event_sense_state_tag; |
| 77 | const auto& state = std::get<SenseState>(event); |
| 78 | proto.type.sense_state.alarm_active = state.alarm; |
| 79 | proto.type.sense_state.alarm_threshold = state.alarm_threshold; |
| 80 | proto.type.sense_state.aq_score = state.air_quality; |
Ted Pudlik | e57f687 | 2024-08-06 22:18:58 +0000 | [diff] [blame] | 81 | if (const auto status = |
| 82 | pw::string::Copy(state.air_quality_description, |
| 83 | proto.type.sense_state.aq_description); |
| 84 | !status.ok()) { |
Ted Pudlik | 6650020 | 2024-08-07 05:41:54 +0000 | [diff] [blame] | 85 | PW_LOG_ERROR("Description truncated to %zu characters: %s", |
| 86 | status.size(), |
| 87 | status.status().str()); |
Ted Pudlik | e57f687 | 2024-08-06 22:18:58 +0000 | [diff] [blame] | 88 | } |
Alexei Frolov | 6b8cb7d | 2024-08-05 17:44:38 +0000 | [diff] [blame] | 89 | } else if (std::holds_alternative<StateManagerControl>(event)) { |
| 90 | proto.which_type = pubsub_Event_state_manager_control_tag; |
| 91 | const auto& control = std::get<StateManagerControl>(event); |
| 92 | switch (control.action) { |
| 93 | case StateManagerControl::kDecrementThreshold: |
| 94 | proto.type.state_manager_control.action = |
| 95 | pubsub_StateManagerControl_Action_DECREMENT_THRESHOLD; |
| 96 | break; |
| 97 | case StateManagerControl::kIncrementThreshold: |
| 98 | proto.type.state_manager_control.action = |
| 99 | pubsub_StateManagerControl_Action_INCREMENT_THRESHOLD; |
| 100 | break; |
| 101 | case StateManagerControl::kSilenceAlarms: |
| 102 | proto.type.state_manager_control.action = |
| 103 | pubsub_StateManagerControl_Action_SILENCE_ALARMS; |
| 104 | break; |
| 105 | } |
Alexei Frolov | 90980ad | 2024-07-16 21:26:21 +0000 | [diff] [blame] | 106 | } else { |
| 107 | PW_LOG_WARN("Unimplemented pubsub service event"); |
| 108 | } |
Alexei Frolov | 90980ad | 2024-07-16 21:26:21 +0000 | [diff] [blame] | 109 | return proto; |
| 110 | } |
| 111 | |
| 112 | pw::Result<Event> ProtoToEvent(const pubsub_Event& proto) { |
| 113 | switch (proto.which_type) { |
Alexei Frolov | 90980ad | 2024-07-16 21:26:21 +0000 | [diff] [blame] | 114 | case pubsub_Event_button_a_pressed_tag: |
Erik Gilling | 106fd59 | 2024-07-16 23:49:15 +0000 | [diff] [blame] | 115 | return ButtonA(proto.type.button_a_pressed); |
Alexei Frolov | 90980ad | 2024-07-16 21:26:21 +0000 | [diff] [blame] | 116 | case pubsub_Event_button_b_pressed_tag: |
Erik Gilling | 106fd59 | 2024-07-16 23:49:15 +0000 | [diff] [blame] | 117 | return ButtonB(proto.type.button_b_pressed); |
Alexei Frolov | 90980ad | 2024-07-16 21:26:21 +0000 | [diff] [blame] | 118 | case pubsub_Event_button_x_pressed_tag: |
Erik Gilling | 106fd59 | 2024-07-16 23:49:15 +0000 | [diff] [blame] | 119 | return ButtonX(proto.type.button_x_pressed); |
Alexei Frolov | 90980ad | 2024-07-16 21:26:21 +0000 | [diff] [blame] | 120 | case pubsub_Event_button_y_pressed_tag: |
Erik Gilling | 106fd59 | 2024-07-16 23:49:15 +0000 | [diff] [blame] | 121 | return ButtonY(proto.type.button_y_pressed); |
Aaron Green | d56ce1a | 2024-07-31 21:05:52 +0000 | [diff] [blame] | 122 | case pubsub_Event_timer_request_tag: |
| 123 | return TimerRequest{ |
| 124 | .token = proto.type.timer_request.token, |
| 125 | .timeout_s = |
| 126 | static_cast<uint16_t>(proto.type.timer_request.timeout_s), |
| 127 | }; |
| 128 | case pubsub_Event_timer_expired_tag: |
| 129 | return TimerExpired{ |
| 130 | .token = proto.type.timer_expired.token, |
| 131 | }; |
Aaron Green | dc5e51f | 2024-07-29 21:40:35 +0000 | [diff] [blame] | 132 | case pubsub_Event_morse_code_value_tag: |
| 133 | return MorseCodeValue{ |
| 134 | .turn_on = proto.type.morse_code_value.turn_on, |
| 135 | .message_finished = proto.type.morse_code_value.message_finished, |
| 136 | }; |
Erik Gilling | 106fd59 | 2024-07-16 23:49:15 +0000 | [diff] [blame] | 137 | case pubsub_Event_proximity_tag: |
| 138 | return ProximityStateChange{.proximity = proto.type.proximity}; |
Aaron Green | b0ca10f | 2024-07-25 21:59:27 +0000 | [diff] [blame] | 139 | case pubsub_Event_air_quality_tag: |
| 140 | return AirQuality{.score = static_cast<uint16_t>(proto.type.air_quality)}; |
Alexei Frolov | 6b8cb7d | 2024-08-05 17:44:38 +0000 | [diff] [blame] | 141 | case pubsub_Event_sense_state_tag: |
| 142 | return SenseState{ |
| 143 | .alarm = proto.type.sense_state.alarm_active, |
| 144 | .alarm_threshold = |
| 145 | static_cast<uint16_t>(proto.type.sense_state.alarm_threshold), |
| 146 | .air_quality = static_cast<uint16_t>(proto.type.sense_state.aq_score), |
| 147 | .air_quality_description = StateManager::AirQualityDescription( |
| 148 | proto.type.sense_state.aq_score), |
| 149 | }; |
| 150 | case pubsub_Event_state_manager_control_tag: |
| 151 | StateManagerControl::Action action; |
| 152 | switch (proto.type.state_manager_control.action) { |
| 153 | case pubsub_StateManagerControl_Action_DECREMENT_THRESHOLD: |
| 154 | action = StateManagerControl::kDecrementThreshold; |
| 155 | break; |
| 156 | case pubsub_StateManagerControl_Action_INCREMENT_THRESHOLD: |
| 157 | action = StateManagerControl::kIncrementThreshold; |
| 158 | break; |
| 159 | case pubsub_StateManagerControl_Action_SILENCE_ALARMS: |
| 160 | action = StateManagerControl::kSilenceAlarms; |
| 161 | break; |
| 162 | case pubsub_StateManagerControl_Action_UNKNOWN: |
| 163 | return pw::Status::InvalidArgument(); |
| 164 | } |
| 165 | return StateManagerControl(action); |
Alexei Frolov | 90980ad | 2024-07-16 21:26:21 +0000 | [diff] [blame] | 166 | default: |
| 167 | return pw::Status::Unimplemented(); |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | } // namespace |
| 172 | |
| 173 | void PubSubService::Init(PubSub& pubsub) { |
| 174 | pubsub_ = &pubsub; |
| 175 | |
Ted Pudlik | e57f687 | 2024-08-06 22:18:58 +0000 | [diff] [blame] | 176 | PW_CHECK(pubsub_->Subscribe([this](Event event) { |
| 177 | // Writing to an unopened stream is okay here, so we IgnoreError. |
| 178 | stream_.Write(EventToProto(event)).IgnoreError(); |
| 179 | })); |
Alexei Frolov | 90980ad | 2024-07-16 21:26:21 +0000 | [diff] [blame] | 180 | } |
| 181 | |
| 182 | pw::Status PubSubService::Publish(const pubsub_Event& request, |
| 183 | pw_protobuf_Empty& /*response*/) { |
| 184 | pw::Result<Event> maybe_event = ProtoToEvent(request); |
| 185 | if (!maybe_event.ok()) { |
| 186 | return maybe_event.status(); |
| 187 | } |
| 188 | |
| 189 | if (pubsub_ != nullptr) { |
| 190 | bool published = pubsub_->Publish(*maybe_event); |
| 191 | PW_LOG_INFO("%s event to pubsub system", |
| 192 | published ? "Published" : "Failed to publish"); |
| 193 | } |
| 194 | |
| 195 | return pw::OkStatus(); |
| 196 | } |
| 197 | |
| 198 | void PubSubService::Subscribe(const pw_protobuf_Empty&, |
| 199 | ServerWriter<pubsub_Event>& writer) { |
| 200 | PW_LOG_INFO("Streaming pubsub events over RPC channel %u", |
| 201 | writer.channel_id()); |
| 202 | stream_ = std::move(writer); |
| 203 | } |
| 204 | |
Keir Mierle | 4d11a12 | 2024-07-25 17:50:02 +0000 | [diff] [blame] | 205 | } // namespace sense |