Skip to content

Commit 9d607f1

Browse files
ADPP v1 Parity and API Boundary Corrections
1 parent e9effdd commit 9d607f1

16 files changed

Lines changed: 475 additions & 31 deletions

CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ set(SOURCES
9595
# Layer 1: Protocol core
9696
src/core/main.cpp
9797
src/core/handlers.cpp
98+
src/core/health.cpp
9899
src/core/transport/framed_stdio.cpp
99100

100101
# Layer 2: Devices
@@ -224,6 +225,10 @@ if (WIN32)
224225
target_compile_definitions(anolis-provider-sim PRIVATE _CRT_SECURE_NO_WARNINGS)
225226
endif()
226227

228+
target_compile_definitions(anolis-provider-sim PRIVATE
229+
ANOLIS_PROVIDER_SIM_VERSION="${PROJECT_VERSION}"
230+
)
231+
227232
if(BUILD_TESTING)
228233
find_package(Python3 COMPONENTS Interpreter REQUIRED)
229234

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ Makes a device appear unavailable for a specified duration.
130130
- `DescribeDevice` returns `NOT_FOUND` (unknown/unavailable target)
131131
- `ReadSignals` for explicit signal requests returns `NOT_FOUND`
132132
- `CallFunction` returns `INVALID_ARGUMENT` with injected fault message
133+
- Rejects invalid requests where `duration_ms <= 0`
133134
- Automatically clears after duration expires
134135

135136
#### `inject_signal_fault`
@@ -146,6 +147,7 @@ Forces a signal to report `FAULT` quality for a specified duration.
146147

147148
- Signal quality becomes `FAULT`
148149
- Signal value freezes at current value
150+
- Rejects invalid requests where `duration_ms <= 0`
149151
- Automatically clears after duration expires
150152

151153
#### `inject_call_latency`
@@ -160,6 +162,7 @@ Adds artificial latency to all function calls on a device.
160162
**Behavior:**
161163

162164
- All CallFunction requests delayed by specified amount
165+
- Rejects invalid requests where `latency_ms < 0`
163166
- Useful for testing timeout handling and responsiveness under load
164167

165168
#### `inject_call_failure`
@@ -176,6 +179,8 @@ Causes a specific function to fail probabilistically.
176179

177180
- Function returns `INVALID_ARGUMENT` at specified rate
178181
- Uses uniform random distribution for probabilistic failures
182+
- Requires numeric string `function_id` (for example, `"1"`)
183+
- Requires `failure_rate` in `[0.0, 1.0]`
179184

180185
#### `clear_faults`
181186

src/chaos/chaos_control_device.cpp

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#include "chaos/chaos_control_device.hpp"
22
#include "chaos/fault_injection.hpp"
33

4+
#include <algorithm>
5+
#include <cctype>
46
#include <thread>
57

68
namespace sim_devices {
@@ -73,6 +75,12 @@ static FunctionPolicy make_policy(FunctionPolicy::Category cat) {
7375
return p;
7476
}
7577

78+
static bool is_numeric_function_id(const std::string &function_id) {
79+
return !function_id.empty() &&
80+
std::all_of(function_id.begin(), function_id.end(),
81+
[](unsigned char ch) { return std::isdigit(ch) != 0; });
82+
}
83+
7684
// -----------------------------
7785
// Capabilities
7886
// -----------------------------
@@ -132,7 +140,7 @@ CapabilitySet get_capabilities() {
132140
*f.add_args() = make_arg("device_id", ValueType::VALUE_TYPE_STRING, true,
133141
"Target device ID");
134142
*f.add_args() = make_arg("function_id", ValueType::VALUE_TYPE_STRING, true,
135-
"Target function name");
143+
"Target function ID as string (e.g. '1')");
136144
*f.add_args() = make_arg("failure_rate", ValueType::VALUE_TYPE_DOUBLE, true,
137145
"Failure probability (0.0-1.0)");
138146
*f.mutable_policy() = make_policy(FunctionPolicy::CATEGORY_ACTUATE);
@@ -179,6 +187,9 @@ CallResult call_function(uint32_t function_id,
179187
if (!get_arg_int64(args, "duration_ms", duration_ms)) {
180188
return bad("missing or invalid duration_ms");
181189
}
190+
if (duration_ms <= 0) {
191+
return bad("duration_ms must be > 0");
192+
}
182193

183194
fault_injection::inject_device_unavailable(device_id, duration_ms);
184195
return ok();
@@ -199,6 +210,9 @@ CallResult call_function(uint32_t function_id,
199210
if (!get_arg_int64(args, "duration_ms", duration_ms)) {
200211
return bad("missing or invalid duration_ms");
201212
}
213+
if (duration_ms <= 0) {
214+
return bad("duration_ms must be > 0");
215+
}
202216

203217
fault_injection::inject_signal_fault(device_id, signal_id, duration_ms);
204218
return ok();
@@ -215,6 +229,9 @@ CallResult call_function(uint32_t function_id,
215229
if (!get_arg_int64(args, "latency_ms", latency_ms)) {
216230
return bad("missing or invalid latency_ms");
217231
}
232+
if (latency_ms < 0) {
233+
return bad("latency_ms must be >= 0");
234+
}
218235

219236
fault_injection::inject_call_latency(device_id, latency_ms);
220237
return ok();
@@ -232,9 +249,18 @@ CallResult call_function(uint32_t function_id,
232249
if (!get_arg_string(args, "function_id", function_id_str)) {
233250
return bad("missing or invalid function_id");
234251
}
252+
if (!is_numeric_function_id(function_id_str)) {
253+
return bad("function_id must be a numeric string (e.g. '1')");
254+
}
255+
if (function_id_str == "0") {
256+
return bad("function_id must be >= 1");
257+
}
235258
if (!get_arg_double(args, "failure_rate", failure_rate)) {
236259
return bad("missing or invalid failure_rate");
237260
}
261+
if (failure_rate < 0.0 || failure_rate > 1.0) {
262+
return bad("failure_rate must be in [0.0, 1.0]");
263+
}
238264

239265
fault_injection::inject_call_failure(device_id, function_id_str,
240266
failure_rate);

src/chaos/fault_injection.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ void inject_call_failure(const std::string &device_id,
157157
std::lock_guard<std::mutex> lock(s.mutex);
158158
CallFailureFault fault;
159159
fault.function_id = function_id;
160-
fault.failure_rate = std::clamp(failure_rate, 0.0, 1.0);
160+
fault.failure_rate = failure_rate;
161161

162162
// Replace existing fault for this function, or add new one
163163
auto &faults = s.call_failure_faults[device_id];

src/core/handlers.cpp

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include "core/handlers.hpp"
22

3+
#include <cstdint>
4+
#include <map>
35
#include <string>
46
#include <vector>
57

@@ -19,6 +21,10 @@ using anolis::deviceprovider::v1::ReadSignalsRequest;
1921
using anolis::deviceprovider::v1::Status;
2022
using anolis::deviceprovider::v1::WaitReadyRequest;
2123

24+
#ifndef ANOLIS_PROVIDER_SIM_VERSION
25+
#define ANOLIS_PROVIDER_SIM_VERSION "0.0.0"
26+
#endif
27+
2228
static inline void set_status_ok(anolis::deviceprovider::v1::Response &resp) {
2329
resp.mutable_status()->set_code(Status::CODE_OK);
2430
resp.mutable_status()->set_message("ok");
@@ -41,7 +47,7 @@ void handle_hello(const HelloRequest &req,
4147
auto *hello = resp.mutable_hello();
4248
hello->set_protocol_version("v1");
4349
hello->set_provider_name("anolis-provider-sim");
44-
hello->set_provider_version("0.0.3");
50+
hello->set_provider_version(ANOLIS_PROVIDER_SIM_VERSION);
4551

4652
(*hello->mutable_metadata())["transport"] = "stdio+uint32_le";
4753
(*hello->mutable_metadata())["max_frame_bytes"] =
@@ -60,8 +66,13 @@ void handle_list_devices(const ListDevicesRequest &req,
6066
*out->add_devices() = d;
6167
}
6268

63-
// v1 sim: we ignore include_health for now (device_health omitted
64-
// intentionally).
69+
if (req.include_health()) {
70+
const auto device_health = sim_health::make_list_devices_health(devices);
71+
for (const auto &health : device_health) {
72+
*out->add_device_health() = health;
73+
}
74+
}
75+
6576
set_status_ok(resp);
6677
}
6778

@@ -149,11 +160,26 @@ void handle_call(const CallRequest &req,
149160
return;
150161
}
151162

152-
// v1: only function_id supported (matches our DescribeDevice contract).
163+
uint32_t resolved_function_id = req.function_id();
164+
153165
if (req.function_id() == 0) {
154-
set_status(resp, Status::CODE_UNIMPLEMENTED,
155-
"function_name lookup not implemented in sim provider v1");
156-
return;
166+
const auto function_id =
167+
sim_devices::resolve_function_id(req.device_id(), req.function_name());
168+
if (!function_id.has_value()) {
169+
set_status(resp, Status::CODE_NOT_FOUND,
170+
"unknown function_name '" + req.function_name() +
171+
"' for device_id '" + req.device_id() + "'");
172+
return;
173+
}
174+
resolved_function_id = *function_id;
175+
} else if (!req.function_name().empty()) {
176+
const auto by_name =
177+
sim_devices::resolve_function_id(req.device_id(), req.function_name());
178+
if (by_name.has_value() && *by_name != req.function_id()) {
179+
set_status(resp, Status::CODE_INVALID_ARGUMENT,
180+
"function_id/function_name mismatch");
181+
return;
182+
}
157183
}
158184

159185
std::map<std::string, anolis::deviceprovider::v1::Value> args;
@@ -162,7 +188,7 @@ void handle_call(const CallRequest &req,
162188
}
163189

164190
const auto result =
165-
sim_devices::call_function(req.device_id(), req.function_id(), args);
191+
sim_devices::call_function(req.device_id(), resolved_function_id, args);
166192
if (result.code != Status::CODE_OK) {
167193
set_status(resp, result.code, result.message);
168194
return;
@@ -176,19 +202,14 @@ void handle_call(const CallRequest &req,
176202

177203
void handle_get_health(const GetHealthRequest & /*req*/,
178204
anolis::deviceprovider::v1::Response &resp) {
205+
const auto init_report =
206+
anolis_provider_sim::DeviceFactory::get_initialization_report();
179207
auto *out = resp.mutable_get_health();
180-
*out->mutable_provider() = sim_health::make_provider_health_ok();
208+
*out->mutable_provider() = sim_health::make_provider_health(init_report);
181209

182-
// v1 sim: device health is optional; include basic OK states for known
183-
// devices
184-
const auto devices = sim_devices::list_devices(false);
185-
for (const auto &d : devices) {
186-
auto *dh = out->add_devices();
187-
dh->set_device_id(d.device_id());
188-
dh->set_state(anolis::deviceprovider::v1::DeviceHealth::STATE_OK);
189-
dh->set_message("ok");
190-
// last_seen omitted in v1 sim
191-
(*dh->mutable_metrics())["impl"] = "sim";
210+
const auto device_health = sim_health::make_get_health_devices(init_report);
211+
for (const auto &health : device_health) {
212+
*out->add_devices() = health;
192213
}
193214

194215
set_status_ok(resp);
@@ -205,10 +226,24 @@ void handle_wait_ready(const WaitReadyRequest & /*req*/,
205226

206227
std::cerr << "[WaitReady] Processing wait_ready() request\n";
207228

229+
const auto init_report =
230+
anolis_provider_sim::DeviceFactory::get_initialization_report();
208231
auto *out = resp.mutable_wait_ready();
209232
(*out->mutable_diagnostics())["init_time_ms"] = "0";
210233
(*out->mutable_diagnostics())["device_count"] =
211234
std::to_string(sim_devices::list_devices(false).size());
235+
(*out->mutable_diagnostics())["startup_policy"] =
236+
sim_health::startup_policy_name(init_report.startup_policy);
237+
(*out->mutable_diagnostics())["startup_configured_devices"] =
238+
std::to_string(init_report.configured_device_count);
239+
(*out->mutable_diagnostics())["startup_initialized_devices"] =
240+
std::to_string(init_report.successful_device_ids.size());
241+
(*out->mutable_diagnostics())["startup_failed_devices"] =
242+
std::to_string(init_report.failed_devices.size());
243+
(*out->mutable_diagnostics())["startup_degraded"] =
244+
init_report.failed_devices.empty() ? "false" : "true";
245+
(*out->mutable_diagnostics())["provider_version"] =
246+
ANOLIS_PROVIDER_SIM_VERSION;
212247
(*out->mutable_diagnostics())["provider_impl"] = "sim";
213248

214249
set_status_ok(resp);

0 commit comments

Comments
 (0)