DCCL v4
Loading...
Searching...
No Matches
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
29LUALIB_API int luaopen_pb(lua_State* L);
30#define SOL_ALL_SAFETIES_ON 1
31#define SOL_PRINT_ERRORS 1
32#endif
33
34void 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
44dccl::DynamicConditions::DynamicConditions() = default;
45
46dccl::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
58void 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
112const 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
120bool 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
152bool 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
183double 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
201double 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}