DCCL v4
dynamic_conditions.cpp
1 // Copyright 2022-2023:
2 // GobySoft, LLC (2013-)
3 // Community contributors (see AUTHORS file)
4 // File authors:
5 // Toby Schneider <toby@gobysoft.org>
6 //
7 //
8 // This file is part of the Dynamic Compact Control Language Library
9 // ("DCCL").
10 //
11 // DCCL is free software: you can redistribute it and/or modify
12 // it under the terms of the GNU Lesser General Public License as published by
13 // the Free Software Foundation, either version 2.1 of the License, or
14 // (at your option) any later version.
15 //
16 // DCCL is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU Lesser General Public License for more details.
20 //
21 // You should have received a copy of the GNU Lesser General Public License
22 // along with DCCL. If not, see <http://www.gnu.org/licenses/>.
23 #include "dynamic_conditions.h"
24 #include "exception.h"
25 
26 #if DCCL_HAS_LUA
27 #include "thirdparty/sol/sol.hpp"
28 // symbol in lua-protobuf/pb.c so we can load this using sol's require call
29 LUALIB_API int luaopen_pb(lua_State* L);
30 #define SOL_ALL_SAFETIES_ON 1
31 #define SOL_PRINT_ERRORS 1
32 #endif
33 
34 void build_file_desc_set(const google::protobuf::FileDescriptor* file_desc,
35  google::protobuf::FileDescriptorSet& file_desc_set)
36 {
37  for (int i = 0, n = file_desc->dependency_count(); i < n; ++i)
38  build_file_desc_set(file_desc->dependency(i), file_desc_set);
39 
40  google::protobuf::FileDescriptorProto* file_desc_proto = file_desc_set.add_file();
41  file_desc->CopyTo(file_desc_proto);
42 }
43 
44 dccl::DynamicConditions::DynamicConditions() = default;
45 
46 dccl::DynamicConditions::~DynamicConditions()
47 {
48 #if DCCL_HAS_LUA
49  if (lua_)
50  {
51  // without this, we get a segfault in Ubuntu jammy
52  lua_->script("pb.clear()");
53  delete lua_;
54  }
55 #endif
56 }
57 
58 void dccl::DynamicConditions::regenerate(const google::protobuf::Message* this_msg,
59  const google::protobuf::Message* root_msg, int index)
60 {
61  if (index >= 0)
62  set_repeated_index(index);
63 
64  this_msg_ = this_msg;
65  root_msg_ = root_msg;
66  if (!this_msg_)
67  this_msg_ = root_msg_;
68 
69 #if DCCL_HAS_LUA
70  if (this_msg_ && root_msg_)
71  {
72  if (!lua_)
73  {
74  lua_ = new sol::state;
75  lua_->open_libraries();
76  lua_->require("pb", luaopen_pb);
77  }
78 
79  sol::load_result desc_load = lua_->load(R"(local desc = ...; return pb.load(desc) )");
80 
81  google::protobuf::FileDescriptorSet file_desc_set;
82  build_file_desc_set(root_msg_->GetDescriptor()->file(), file_desc_set);
83 
84  std::tuple<bool, int> desc_load_result = desc_load(file_desc_set.SerializeAsString());
85  assert(std::get<0>(desc_load_result));
86  const auto& decode_script = R"(
87  local this_encoded_msg, this_type, root_encoded_msg, root_type, cpp_index = ...;
88  pb.option('use_default_metatable');
89  root = pb.decode(root_type, root_encoded_msg);
90  this = pb.decode(this_type, this_encoded_msg); this_index = cpp_index+1;
91  return this;
92  )";
93 
94  sol::load_result decode_message = lua_->load(decode_script);
95  if (!decode_message.valid())
96  {
97  sol::error err = decode_message;
98  throw(Exception(std::string("Failed to load condition script into the Lua program: ") +
99  err.what(),
100  this_msg_->GetDescriptor()));
101  }
102 
103  auto index = index_;
104 
105  sol::table decoded_message = decode_message(
106  this_msg_->SerializePartialAsString(), this_msg_->GetDescriptor()->full_name(),
107  root_msg_->SerializePartialAsString(), root_msg_->GetDescriptor()->full_name(), index);
108  }
109 #endif
110 }
111 
112 const dccl::DCCLFieldOptions::Conditions& dccl::DynamicConditions::conditions()
113 {
114  if (field_desc_)
115  return field_desc_->options().GetExtension(dccl::field).dynamic_conditions();
116  else
117  throw(Exception("Null field_desc"));
118 }
119 
120 bool dccl::DynamicConditions::required()
121 {
122 #if DCCL_HAS_LUA
123  if (is_initialized())
124  {
125  if (conditions().has_required_if())
126  {
127  auto condition_script = return_prefix(conditions().required_if());
128  bool required = lua_->script(condition_script);
129  return required;
130  }
131  else if (conditions().has_only_if())
132  {
133  auto condition_script = return_prefix(conditions().only_if());
134  bool only = lua_->script(condition_script);
135  return only;
136  }
137  else
138  {
139  return false;
140  }
141  }
142  else
143  {
144  return false;
145  }
146 
147 #else
148  throw(Exception("DCCL built without Lua support: cannot use dynamic_conditions"));
149 #endif
150 }
151 
152 bool dccl::DynamicConditions::omit()
153 {
154 #if DCCL_HAS_LUA
155  if (is_initialized())
156  {
157  if (conditions().has_omit_if())
158  {
159  auto condition_script = return_prefix(conditions().omit_if());
160  bool omit = lua_->script(condition_script);
161  return omit;
162  }
163  else if (conditions().has_only_if())
164  {
165  auto condition_script = return_prefix(conditions().only_if());
166  bool only = lua_->script(condition_script);
167  return !only;
168  }
169  else
170  {
171  return false;
172  }
173  }
174  else
175  {
176  return false;
177  }
178 #else
179  throw(Exception("DCCL built without Lua support: cannot use dynamic_conditions"));
180 #endif
181 }
182 
183 double dccl::DynamicConditions::min()
184 {
185 #if DCCL_HAS_LUA
186  if (is_initialized())
187  {
188  auto condition_script = return_prefix(conditions().min());
189  double v = lua_->script(condition_script);
190  return v;
191  }
192  else
193  {
194  return -std::numeric_limits<double>::infinity();
195  }
196 #else
197  throw(Exception("DCCL built without Lua support: cannot use dynamic_conditions"));
198 #endif
199 }
200 
201 double dccl::DynamicConditions::max()
202 {
203 #if DCCL_HAS_LUA
204  if (is_initialized())
205  {
206  auto condition_script = return_prefix(conditions().max());
207  double v = lua_->script(condition_script);
208  return v;
209  }
210  else
211  {
212  return std::numeric_limits<double>::infinity();
213  }
214 #else
215  throw(Exception("DCCL built without Lua support: cannot use dynamic_conditions"));
216 #endif
217 }
dccl::DCCLFieldOptions_Conditions
Definition: option_extensions.pb.h:282
sol::basic_table_core
Definition: sol_forward.hpp:249
Message