blob: 07aad44076cbd8aa58c9f24737ef465894121443 [file] [log] [blame]
Alexei Frolov90980ad2024-07-16 21:26:21 +00001
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 Frolov6b8cb7d2024-08-05 17:44:38 +000018#include "modules/state_manager/state_manager.h"
Aaron Green2da5fea2024-08-05 21:18:36 +000019#include "pw_assert/check.h"
Alexei Frolov90980ad2024-07-16 21:26:21 +000020#include "pw_log/log.h"
Alexei Frolov6b8cb7d2024-08-05 17:44:38 +000021#include "pw_string/util.h"
Alexei Frolov90980ad2024-07-16 21:26:21 +000022
Keir Mierle4d11a122024-07-25 17:50:02 +000023namespace sense {
Alexei Frolov90980ad2024-07-16 21:26:21 +000024namespace {
25
26pubsub_Event EventToProto(const Event& event) {
27 pubsub_Event proto = pubsub_Event_init_default;
28
Aaron Green366f4582024-07-31 22:27:20 +000029 if (std::holds_alternative<ButtonA>(event)) {
Alexei Frolov90980ad2024-07-16 21:26:21 +000030 proto.which_type = pubsub_Event_button_a_pressed_tag;
Erik Gilling106fd592024-07-16 23:49:15 +000031 proto.type.button_a_pressed = std::get<ButtonA>(event).pressed();
32 } else if (std::holds_alternative<ButtonB>(event)) {
Alexei Frolov90980ad2024-07-16 21:26:21 +000033 proto.which_type = pubsub_Event_button_b_pressed_tag;
Erik Gilling106fd592024-07-16 23:49:15 +000034 proto.type.button_b_pressed = std::get<ButtonB>(event).pressed();
35 } else if (std::holds_alternative<ButtonX>(event)) {
Alexei Frolov90980ad2024-07-16 21:26:21 +000036 proto.which_type = pubsub_Event_button_x_pressed_tag;
Erik Gilling106fd592024-07-16 23:49:15 +000037 proto.type.button_x_pressed = std::get<ButtonX>(event).pressed();
38 } else if (std::holds_alternative<ButtonY>(event)) {
Alexei Frolov90980ad2024-07-16 21:26:21 +000039 proto.which_type = pubsub_Event_button_y_pressed_tag;
Erik Gilling106fd592024-07-16 23:49:15 +000040 proto.type.button_y_pressed = std::get<ButtonY>(event).pressed();
Aaron Greend56ce1a2024-07-31 21:05:52 +000041 } 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 Gilling106fd592024-07-16 23:49:15 +000050 } 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 Hepler18d0eda2024-07-17 17:59:42 +000053 } 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 Hepleree22f032024-07-30 18:18:28 +000056 } 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 Greenb0ca10f2024-07-25 21:59:27 +000060 } 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 Heplere2b8ed62024-07-27 00:33:18 +000063 } 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 Greendc5e51f2024-07-29 21:40:35 +000070 } 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 Frolov6b8cb7d2024-08-05 17:44:38 +000075 } 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 Pudlike57f6872024-08-06 22:18:58 +000081 if (const auto status =
82 pw::string::Copy(state.air_quality_description,
83 proto.type.sense_state.aq_description);
84 !status.ok()) {
Ted Pudlik66500202024-08-07 05:41:54 +000085 PW_LOG_ERROR("Description truncated to %zu characters: %s",
86 status.size(),
87 status.status().str());
Ted Pudlike57f6872024-08-06 22:18:58 +000088 }
Alexei Frolov6b8cb7d2024-08-05 17:44:38 +000089 } 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 Frolov90980ad2024-07-16 21:26:21 +0000106 } else {
107 PW_LOG_WARN("Unimplemented pubsub service event");
108 }
Alexei Frolov90980ad2024-07-16 21:26:21 +0000109 return proto;
110}
111
112pw::Result<Event> ProtoToEvent(const pubsub_Event& proto) {
113 switch (proto.which_type) {
Alexei Frolov90980ad2024-07-16 21:26:21 +0000114 case pubsub_Event_button_a_pressed_tag:
Erik Gilling106fd592024-07-16 23:49:15 +0000115 return ButtonA(proto.type.button_a_pressed);
Alexei Frolov90980ad2024-07-16 21:26:21 +0000116 case pubsub_Event_button_b_pressed_tag:
Erik Gilling106fd592024-07-16 23:49:15 +0000117 return ButtonB(proto.type.button_b_pressed);
Alexei Frolov90980ad2024-07-16 21:26:21 +0000118 case pubsub_Event_button_x_pressed_tag:
Erik Gilling106fd592024-07-16 23:49:15 +0000119 return ButtonX(proto.type.button_x_pressed);
Alexei Frolov90980ad2024-07-16 21:26:21 +0000120 case pubsub_Event_button_y_pressed_tag:
Erik Gilling106fd592024-07-16 23:49:15 +0000121 return ButtonY(proto.type.button_y_pressed);
Aaron Greend56ce1a2024-07-31 21:05:52 +0000122 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 Greendc5e51f2024-07-29 21:40:35 +0000132 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 Gilling106fd592024-07-16 23:49:15 +0000137 case pubsub_Event_proximity_tag:
138 return ProximityStateChange{.proximity = proto.type.proximity};
Aaron Greenb0ca10f2024-07-25 21:59:27 +0000139 case pubsub_Event_air_quality_tag:
140 return AirQuality{.score = static_cast<uint16_t>(proto.type.air_quality)};
Alexei Frolov6b8cb7d2024-08-05 17:44:38 +0000141 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 Frolov90980ad2024-07-16 21:26:21 +0000166 default:
167 return pw::Status::Unimplemented();
168 }
169}
170
171} // namespace
172
173void PubSubService::Init(PubSub& pubsub) {
174 pubsub_ = &pubsub;
175
Ted Pudlike57f6872024-08-06 22:18:58 +0000176 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 Frolov90980ad2024-07-16 21:26:21 +0000180}
181
182pw::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
198void 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 Mierle4d11a122024-07-25 17:50:02 +0000205} // namespace sense