diff --git a/libuavcan/include/uavcan/protocol/param_server.hpp b/libuavcan/include/uavcan/protocol/param_server.hpp index 666db73b59..2c23ac2bb5 100644 --- a/libuavcan/include/uavcan/protocol/param_server.hpp +++ b/libuavcan/include/uavcan/protocol/param_server.hpp @@ -79,22 +79,48 @@ class UAVCAN_EXPORT ParamServer void (ParamServer::*)(const protocol::param::ExecuteOpcode::Request&, protocol::param::ExecuteOpcode::Response&)> ExecuteOpcodeCallback; +public: + /** + * This class can automatically enforce that parameter names are not case-sensitive, by means of + * automatic conversion to either upper or lower case whenever a parameter name is received. + * For instance, if the lower-case conversion is enabled, then a UAVCAN request for a parameter + * named "Foo" will be passed to the application as "foo". Similarly, when the application reports + * that it has a parameter named "FOO", the class will convert the name to "foo". + */ + enum ParamNameCaseConversion + { + ParamNameCaseConversionDisabled, ///< Do not convert parameter names. The case will remain intact. + ParamNameCaseConversionToLower, ///< Convert parameter names to lower case. + ParamNameCaseConversionToUpper ///< Convert parameter names to upper case. + }; + +private: ServiceServer get_set_srv_; ServiceServer save_erase_srv_; IParamManager* manager_; + const ParamNameCaseConversion param_name_case_conversion_mode_; static bool isValueNonEmpty(const protocol::param::Value& value); + void convertParamNameCase(IParamManager::ParamName& name) const; + void handleGetSet(const protocol::param::GetSet::Request& request, protocol::param::GetSet::Response& response); void handleExecuteOpcode(const protocol::param::ExecuteOpcode::Request& request, protocol::param::ExecuteOpcode::Response& response); public: - explicit ParamServer(INode& node) + /** + * @param param_name_case_conversion Specifies the parameter name conversion mode. Lower case is default, + * which means that an external request for a parameter named "Foo" will + * be relayed to the application as "foo". + */ + explicit ParamServer(INode& node, + ParamNameCaseConversion param_name_case_conversion = ParamNameCaseConversionToLower) : get_set_srv_(node) , save_erase_srv_(node) , manager_(NULL) + , param_name_case_conversion_mode_(param_name_case_conversion) { } /** diff --git a/libuavcan/src/protocol/uc_param_server.cpp b/libuavcan/src/protocol/uc_param_server.cpp index 1062332d3b..4d5f54b7e3 100644 --- a/libuavcan/src/protocol/uc_param_server.cpp +++ b/libuavcan/src/protocol/uc_param_server.cpp @@ -18,6 +18,22 @@ bool ParamServer::isValueNonEmpty(const protocol::param::Value& value) !value.value_string.empty(); } +void ParamServer::convertParamNameCase(IParamManager::ParamName& name) const +{ + if (param_name_case_conversion_mode_ == ParamNameCaseConversionToLower) + { + name.convertToLowerCaseASCII(); + } + else if (param_name_case_conversion_mode_ == ParamNameCaseConversionToUpper) + { + name.convertToUpperCaseASCII(); + } + else + { + ; // Conversion is not needed + } +} + void ParamServer::handleGetSet(const protocol::param::GetSet::Request& in, protocol::param::GetSet::Response& out) { UAVCAN_ASSERT(manager_ != NULL); @@ -33,6 +49,8 @@ void ParamServer::handleGetSet(const protocol::param::GetSet::Request& in, proto out.name = in.name; } + convertParamNameCase(out.name); + // Assign if needed, read back if (isValueNonEmpty(in.value)) { diff --git a/libuavcan/test/protocol/param_server.cpp b/libuavcan/test/protocol/param_server.cpp index 861ca41cd3..054b007395 100644 --- a/libuavcan/test/protocol/param_server.cpp +++ b/libuavcan/test/protocol/param_server.cpp @@ -145,7 +145,7 @@ TEST(ParamServer, Basic) // Get by name get_set_rq = uavcan::protocol::param::GetSet::Request(); - get_set_rq.name = "foobar"; + get_set_rq.name = "FOOBAR"; // Requesting in upper case doCall(get_set_cln, get_set_rq, nodes); ASSERT_STREQ("foobar", get_set_cln.collector.result->response.name.c_str()); ASSERT_TRUE(get_set_cln.collector.result->response.value.value_bool.empty()); @@ -171,3 +171,68 @@ TEST(ParamServer, Basic) ASSERT_STREQ("foobar", get_set_cln.collector.result->response.name.c_str()); ASSERT_FLOAT_EQ(424242, get_set_cln.collector.result->response.value.value_float[0]); } + +TEST(ParamServer, UpperCaseConversion) +{ + InterlinkedTestNodesWithSysClock nodes; + + uavcan::ParamServer server(nodes.a, uavcan::ParamServer::ParamNameCaseConversionToUpper); + + ParamServerTestManager mgr; + + uavcan::GlobalDataTypeRegistry::instance().reset(); + uavcan::DefaultDataTypeRegistrator _reg1; + uavcan::DefaultDataTypeRegistrator _reg2; + + ASSERT_LE(0, server.start(&mgr)); + + ServiceClientWithCollector get_set_cln(nodes.b); + + mgr.kv["foobar"] = 0.0; // Will be ignored because not upper case + mgr.kv["FOOBAR"] = 123.456; + + /* + * Get/set + */ + uavcan::protocol::param::GetSet::Request get_set_rq; + get_set_rq = uavcan::protocol::param::GetSet::Request(); + get_set_rq.name = "foobar"; // Requesting in upper case + doCall(get_set_cln, get_set_rq, nodes); + ASSERT_STREQ("FOOBAR", get_set_cln.collector.result->response.name.c_str()); + ASSERT_TRUE(get_set_cln.collector.result->response.value.value_bool.empty()); + ASSERT_TRUE(get_set_cln.collector.result->response.value.value_int.empty()); + ASSERT_FLOAT_EQ(123.456F, get_set_cln.collector.result->response.value.value_float[0]); +} + +TEST(ParamServer, NoCaseConversion) +{ + InterlinkedTestNodesWithSysClock nodes; + + uavcan::ParamServer server(nodes.a, uavcan::ParamServer::ParamNameCaseConversionDisabled); + + ParamServerTestManager mgr; + + uavcan::GlobalDataTypeRegistry::instance().reset(); + uavcan::DefaultDataTypeRegistrator _reg1; + uavcan::DefaultDataTypeRegistrator _reg2; + + ASSERT_LE(0, server.start(&mgr)); + + ServiceClientWithCollector get_set_cln(nodes.b); + + mgr.kv["foobar"] = 0.0; + mgr.kv["FooBar"] = 123.456; + mgr.kv["FOOBAR"] = 0.0; + + /* + * Get/set + */ + uavcan::protocol::param::GetSet::Request get_set_rq; + get_set_rq = uavcan::protocol::param::GetSet::Request(); + get_set_rq.name = "FooBar"; + doCall(get_set_cln, get_set_rq, nodes); + ASSERT_STREQ("FooBar", get_set_cln.collector.result->response.name.c_str()); + ASSERT_TRUE(get_set_cln.collector.result->response.value.value_bool.empty()); + ASSERT_TRUE(get_set_cln.collector.result->response.value.value_int.empty()); + ASSERT_FLOAT_EQ(123.456F, get_set_cln.collector.result->response.value.value_float[0]); +}