@@ -15,6 +15,12 @@ See the License for the specific language governing permissions and
1515limitations under the License.
1616------------------------------------------------------------------------
1717
18+ Version 0.2
19+ Enumerate protocol error types and add ProtocolError exception class
20+ Implement error handling via expected<T, ProtocolError> and error_or_warning
21+ Removed model member and required model reference in run(), check_support(), and initialize()
22+ Minor refactoring and style changes
23+
1824Version 0.1
1925Virtual interface for BMI protocols
2026*/
@@ -23,9 +29,53 @@ Virtual interface for BMI protocols
2329
2430#include " Bmi_Adapter.hpp"
2531#include " JSONProperty.hpp"
26-
32+ # include < nonstd/expected.hpp >
2733
2834namespace models { namespace bmi { namespace protocols {
35+ using nonstd::expected;
36+ using nonstd::make_unexpected;
37+
38+ enum class Error {
39+ UNITIALIZED_MODEL,
40+ UNSUPPORTED_PROTOCOL,
41+ INTEGRATION_ERROR,
42+ PROTOCOL_ERROR,
43+ PROTOCOL_WARNING
44+ };
45+
46+ class ProtocolError : public std ::exception {
47+ public:
48+ ProtocolError () = delete ;
49+ ProtocolError (Error err, const std::string& message=" " ) : err(std::move(err)), message(std::move(message)) {}
50+ ProtocolError (const ProtocolError& other) = default ;
51+ ProtocolError (ProtocolError&& other) noexcept = default ;
52+ ProtocolError& operator =(const ProtocolError& other) = default ;
53+ ProtocolError& operator =(ProtocolError&& other) noexcept = default ;
54+ ~ProtocolError () = default ;
55+
56+ auto to_string () const -> std::string {
57+ switch (err) {
58+ case Error::UNITIALIZED_MODEL: return " Error(Uninitialized Model)::" + message;
59+ case Error::UNSUPPORTED_PROTOCOL: return " Warning(Unsupported Protocol)::" + message;
60+ case Error::INTEGRATION_ERROR: return " Error(Integration)::" + message;
61+ case Error::PROTOCOL_ERROR: return " Error(Protocol)::" + message;
62+ case Error::PROTOCOL_WARNING: return " Warning(Protocol)::" + message;
63+ default : return " Unknown Error: " + message;
64+ }
65+ }
66+
67+ auto error_code () const -> const Error& { return err; }
68+ auto get_message () const -> const std::string& { return message; }
69+
70+ char const *what () const noexcept override {
71+ message = to_string ();
72+ return message.c_str ();
73+ }
74+
75+ private:
76+ Error err;
77+ mutable std::string message;
78+ };
2979
3080struct Context {
3181 const int current_time_step;
@@ -34,55 +84,125 @@ struct Context{
3484 const std::string& id;
3585};
3686
87+ using ModelPtr = std::shared_ptr<models::bmi::Bmi_Adapter>;
88+ using Properties = geojson::PropertyMap;
89+
3790class NgenBmiProtocol {
3891 /* *
3992 * @brief Abstract interface for a generic BMI protocol
4093 *
4194 */
4295
4396 public:
97+
4498 /* *
45- * @brief Run the BMI protocol
99+ * @brief Construct a new Ngen Bmi Protocol object
100+ *
101+ * By default, the protocol is considered unsupported.
102+ * Subclasses are responsible for implementing the check_support() method,
103+ * and ensuring that is_supported is properly set based on the protocol's
104+ * requirements.
46105 *
47106 */
48- virtual void run ( const Context& ctx) const = 0;
107+ NgenBmiProtocol () : is_supported( false ) {}
49108
50109 virtual ~NgenBmiProtocol () = default ;
51110
52- private :
111+ protected :
53112 /* *
54- * @brief Check if the BMI protocol is supported by the model
113+ * @brief Handle a ProtocolError by either throwing it or logging it as a warning
114+ *
115+ * @param err The ProtocolError to handle
116+ * @return expected<void, ProtocolError> Returns an empty expected if the error was logged as a warning,
117+ * otherwise throws the ProtocolError.
55118 *
119+ * @throws ProtocolError if the error is of type PROTOCOL_ERROR
56120 */
57- virtual void check_support () = 0;
121+ static auto error_or_warning (const ProtocolError& err) -> expected<void, ProtocolError> {
122+ // Log warnings, but throw errors
123+ switch (err.error_code ()){
124+ case Error::PROTOCOL_ERROR:
125+ throw err;
126+ break ;
127+ case Error::INTEGRATION_ERROR:
128+ case Error::UNITIALIZED_MODEL:
129+ case Error::UNSUPPORTED_PROTOCOL:
130+ case Error::PROTOCOL_WARNING:
131+ std::cerr << err.to_string () << std::endl;
132+ return make_unexpected<ProtocolError>( ProtocolError (std::move (err) ) );
133+ default :
134+ throw err;
135+ }
136+ assert (false && " Unreachable code reached in error_or_warning" );
137+ }
138+
58139 /* *
59- * @brief Initialize the BMI protocol from a set of key/value properties
140+ * @brief Run the BMI protocol against the given model
60141 *
61- * @param properties
142+ * Execute the logic of the protocol with the provided context and model.
143+ * It is the caller's responsibility to ensure that the model provided is
144+ * consistent with the model provided to the object's initialize() and
145+ * check_support() methods, hence the protected nature of this function.
146+ *
147+ * @param ctx Contextual information for the protocol run
148+ * @param model A shared pointer to a Bmi_Adapter object which should be
149+ * initialized before being passed to this method.
150+ *
151+ * @return expected<void, ProtocolError> May contain a ProtocolError if
152+ * the protocol fails for any reason. Errors of ProtocolError::PROTOCOL_WARNING
153+ * severity should be logged as warnings, but not cause the simulation to fail.
62154 */
63- virtual void initialize (const geojson::PropertyMap& properties) = 0;
155+ [[nodiscard]] virtual auto run (const ModelPtr& model, const Context& ctx) const -> expected<void, ProtocolError> = 0;
64156
65- protected:
66-
67157 /* *
68- * @brief Constructor for subclasses to create NgenBmiProtocol objects
158+ * @brief Check if the BMI protocol is supported by the model
159+ *
160+ * It is the caller's responsibility to ensure that the model provided is
161+ * consistent with the model provided to the object's initialize() and
162+ * run() methods, hence the protected nature of this function.
69163 *
70164 * @param model A shared pointer to a Bmi_Adapter object which should be
71- * initialized before being passed to this constructor.
165+ * initialized before being passed to this method.
166+ *
167+ * @return expected<void, ProtocolError> May contain a ProtocolError if
168+ * the protocol is not supported by the model.
72169 */
73- NgenBmiProtocol (std::shared_ptr<models::bmi::Bmi_Adapter> model): model(model){}
170+ [[nodiscard]] virtual expected< void , ProtocolError> check_support ( const ModelPtr& model) = 0;
74171
75172 /* *
76- * @brief The Bmi_Adapter object used by the protocol
173+ * @brief Initialize the BMI protocol from a set of key/value properties
77174 *
175+ * It is the caller's responsibility to ensure that the model provided is
176+ * consistent with the model provided to the object's run() and
177+ * check_support() methods, hence the protected nature of this function.
178+ *
179+ * @param properties key/value pairs for initializing the protocol
180+ * @param model A shared pointer to a Bmi_Adapter object which should be
181+ * initialized before being passed to this method.
182+ *
183+ * @return expected<void, ProtocolError> May contain a ProtocolError if
184+ * initialization fails for any reason, since the protocol must
185+ * be effectively "optional", failed initialization results in
186+ * the protocol being disabled for the duration of the simulation.
78187 */
79- std::shared_ptr<models::bmi::Bmi_Adapter> model ;
188+ virtual auto initialize ( const ModelPtr& model, const Properties& properties) -> expected<void, ProtocolError> = 0 ;
80189
81190 /* *
82191 * @brief Whether the protocol is supported by the model
83192 *
84193 */
85194 bool is_supported = false ;
195+
196+ /* *
197+ * @brief Friend class for managing one or more protocols
198+ *
199+ * This allows the NgenBmiProtocols container class to access the protected `run()`
200+ * method. This allows the container to ensure consistent application of the
201+ * protocol with a particular bmi model instance throughout the lifecycle of a given
202+ * protocol.
203+ *
204+ */
205+ friend class NgenBmiProtocols ;
86206};
87207
88208}}}
0 commit comments