mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-30 14:20:28 +01:00
First implementation of graph vehicles
This commit is contained in:
278
vehicle/graph/blocks/lua_block.cpp
Normal file
278
vehicle/graph/blocks/lua_block.cpp
Normal file
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "vehicle/graph/graph_catalog.h"
|
||||
#include "vehicle/graph/graph_format.h"
|
||||
#include "vehicle/graph/graph_lua.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#ifdef WITH_LUA
|
||||
#include <lua.hpp>
|
||||
#endif
|
||||
|
||||
// function block defined by a lua script. its ports and numeric parameters come from the script
|
||||
namespace vgraph
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
bool resolve_lua_block(node_desc const &Node, std::vector<port_def> &Ports, std::vector<param_def> &Params, std::string &Error)
|
||||
{
|
||||
if (false == lua_available())
|
||||
{
|
||||
Error = "lua blocks need a simulator built with lua support (WITH_LUA)";
|
||||
return false;
|
||||
}
|
||||
if (Node.script.empty())
|
||||
{
|
||||
Error = "lua block without a script";
|
||||
return false;
|
||||
}
|
||||
lua_block_definition definition;
|
||||
if (false == parse_lua_block(Node.script, Node.id, definition, Error))
|
||||
{
|
||||
Error = "lua: " + Error;
|
||||
return false;
|
||||
}
|
||||
Ports = std::move(definition.ports);
|
||||
Params = std::move(definition.params);
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef WITH_LUA
|
||||
|
||||
class lua_block final : public block
|
||||
{
|
||||
public:
|
||||
explicit lua_block(block_setup const &Setup) : block(Setup), m_host(Setup.lua), m_nodeid(Setup.node_id)
|
||||
{
|
||||
for (std::size_t idx = 0; idx < Setup.ports.size(); ++idx)
|
||||
{
|
||||
auto const &port{Setup.ports[idx]};
|
||||
(port.direction == port_direction::in ? m_inputs : m_outputs).push_back({idx, port.key, port.type});
|
||||
}
|
||||
if (m_host == nullptr || m_host->state() == nullptr)
|
||||
{
|
||||
fail("no lua state");
|
||||
return;
|
||||
}
|
||||
std::string error;
|
||||
m_definition = m_host->load_block(Setup.script, Setup.node_id, error);
|
||||
if (m_definition < 0)
|
||||
{
|
||||
fail(error);
|
||||
return;
|
||||
}
|
||||
auto *L{m_host->state()};
|
||||
auto const top{lua_gettop(L)};
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, m_definition);
|
||||
auto const definition{lua_gettop(L)};
|
||||
lua_getfield(L, definition, "rate");
|
||||
auto const rate{lua_isnumber(L, -1) ? lua_tonumber(L, -1) : 0.0};
|
||||
lua_pop(L, 1);
|
||||
m_period = (rate > 0.0 ? 1.0 / rate : 0.0);
|
||||
// self: copy of the state table, and the parameters with values from the graph for the numeric ones
|
||||
lua_newtable(L);
|
||||
auto const self{lua_gettop(L)};
|
||||
lua_getfield(L, definition, "state");
|
||||
if (lua_istable(L, -1))
|
||||
{
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, -2) != 0)
|
||||
{
|
||||
lua_pushvalue(L, -2);
|
||||
lua_insert(L, -2);
|
||||
lua_rawset(L, self);
|
||||
}
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
lua_getfield(L, definition, "params");
|
||||
if (lua_istable(L, -1))
|
||||
{
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, -2) != 0)
|
||||
{
|
||||
if (lua_type(L, -2) == LUA_TSTRING && lua_type(L, -1) == LUA_TNUMBER)
|
||||
{
|
||||
auto const paramindex{find_param(Setup.param_defs, std::string{"lua."} + lua_tostring(L, -2))};
|
||||
if (paramindex >= 0)
|
||||
{
|
||||
lua_pop(L, 1);
|
||||
lua_pushnumber(L, Setup.params[paramindex].number);
|
||||
}
|
||||
}
|
||||
lua_pushvalue(L, -2);
|
||||
lua_insert(L, -2);
|
||||
lua_rawset(L, self);
|
||||
}
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
m_self = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
lua_newtable(L);
|
||||
m_in = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
lua_newtable(L);
|
||||
m_out = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
lua_settop(L, top);
|
||||
}
|
||||
|
||||
~lua_block() override
|
||||
{
|
||||
if (m_host != nullptr)
|
||||
{
|
||||
m_host->release(m_definition);
|
||||
m_host->release(m_self);
|
||||
m_host->release(m_in);
|
||||
m_host->release(m_out);
|
||||
}
|
||||
}
|
||||
|
||||
void update(frame &Frame) override
|
||||
{
|
||||
if (m_fault)
|
||||
{
|
||||
// outputs keep their last values
|
||||
return;
|
||||
}
|
||||
auto dt{Frame.dt};
|
||||
if (m_period > 0.0)
|
||||
{
|
||||
m_accumulated += Frame.dt;
|
||||
if (m_accumulated + 1e-9 < m_period)
|
||||
{
|
||||
return;
|
||||
}
|
||||
dt = m_accumulated;
|
||||
m_accumulated = 0.0;
|
||||
}
|
||||
auto *L{m_host->state()};
|
||||
auto const top{lua_gettop(L)};
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, m_in);
|
||||
for (auto const &input : m_inputs)
|
||||
{
|
||||
auto const value{get(Frame, input.port)};
|
||||
switch (input.type)
|
||||
{
|
||||
case value_type::boolean:
|
||||
lua_pushboolean(L, value > 0.5);
|
||||
break;
|
||||
case value_type::integer:
|
||||
lua_pushnumber(L, std::round(value));
|
||||
break;
|
||||
default:
|
||||
lua_pushnumber(L, value);
|
||||
break;
|
||||
}
|
||||
lua_setfield(L, -2, input.key.c_str());
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, m_definition);
|
||||
lua_getfield(L, -1, "update");
|
||||
lua_remove(L, -2);
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, m_self);
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, m_in);
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, m_out);
|
||||
lua_pushnumber(L, dt);
|
||||
std::string error;
|
||||
if (false == m_host->call(4, error))
|
||||
{
|
||||
lua_settop(L, top);
|
||||
fail(error);
|
||||
return;
|
||||
}
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, m_out);
|
||||
for (auto const &output : m_outputs)
|
||||
{
|
||||
lua_getfield(L, -1, output.key.c_str());
|
||||
auto value{0.0};
|
||||
if (lua_isboolean(L, -1))
|
||||
{
|
||||
value = (lua_toboolean(L, -1) ? 1.0 : 0.0);
|
||||
}
|
||||
else if (lua_type(L, -1) == LUA_TNUMBER)
|
||||
{
|
||||
value = lua_tonumber(L, -1);
|
||||
if (output.type == value_type::integer)
|
||||
{
|
||||
value = std::round(value);
|
||||
}
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
set(Frame, output.port, value);
|
||||
}
|
||||
lua_settop(L, top);
|
||||
}
|
||||
|
||||
std::size_t state_count() const override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
char const *state_name(std::size_t) const override
|
||||
{
|
||||
return "fault";
|
||||
}
|
||||
double state_value(std::size_t) const override
|
||||
{
|
||||
return m_fault ? 1.0 : 0.0;
|
||||
}
|
||||
|
||||
private:
|
||||
struct port_entry
|
||||
{
|
||||
std::size_t port;
|
||||
std::string key;
|
||||
value_type type;
|
||||
};
|
||||
|
||||
void fail(std::string const &Reason)
|
||||
{
|
||||
// a failing block stops, and the rest of the vehicle keeps working
|
||||
m_fault = true;
|
||||
log_message("lua [" + m_nodeid + "]: block stopped: " + Reason);
|
||||
}
|
||||
|
||||
lua_host *m_host;
|
||||
std::string m_nodeid;
|
||||
std::vector<port_entry> m_inputs;
|
||||
std::vector<port_entry> m_outputs;
|
||||
int m_definition{-1};
|
||||
int m_self{-1};
|
||||
int m_in{-1};
|
||||
int m_out{-1};
|
||||
double m_period{0.0};
|
||||
double m_accumulated{0.0};
|
||||
bool m_fault{false};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
void register_script_blocks(catalog &Catalog)
|
||||
{
|
||||
block_type type;
|
||||
type.id = "lua.block";
|
||||
type.category = "script";
|
||||
type.name = "Lua block";
|
||||
type.description = "Function block defined by a lua 5.1 script: block \"name\" { inputs, outputs, params, state, rate, update = function(self, i, o, dt) ... end }.";
|
||||
type.origin = block_origin::script;
|
||||
type.dynamic_ports = true;
|
||||
type.resolve = resolve_lua_block;
|
||||
#ifdef WITH_LUA
|
||||
type.create = make_block<lua_block>;
|
||||
#else
|
||||
// graphs with lua blocks are rejected when loaded, so blocks of this type are never created
|
||||
type.create = nullptr;
|
||||
#endif
|
||||
Catalog.add(std::move(type));
|
||||
}
|
||||
|
||||
} // namespace vgraph
|
||||
Reference in New Issue
Block a user