Code Monkey home page Code Monkey logo

taskolib / taskolib Goto Github PK

View Code? Open in Web Editor NEW
4.0 1.0 3.0 53.99 MB

The Taskolib library helps to automate processes. Its main automatization unit is a sequence of steps which are executed in order or through control flow statements. The behavior of each step is defined in the Lua scripting language.

Home Page: https://taskolib.github.io/taskolib/

License: GNU Lesser General Public License v2.1

C 7.38% CSS 0.05% C++ 92.06% Python 0.12% Meson 0.39%
automatization cpp cpp17 library

taskolib's Introduction

Taskolib Library

The Taskolib library helps to automate processes. Its main automatization unit is a sequence of steps which are executed in order or through control flow statements. The behavior of each step is defined in the Lua scripting language.

The library API is documented with Doxygen on the web page https://taskolib.github.io/taskolib/ .

License

The Taskolib library is free software distributed under the terms of the GNU Lesser General Public License version 2.1 (LGPLv2.1). Included third-party software carries additional copyright statements and licenses (which are, however, compatible with the LGPLv2.1). Please refer to the web page or to the source file data/doxygen.h for details.

Building

The library is built with Meson/Ninja. Building is done in two stages: You start by setting up the build system with a call to Meson. All configurable options are set in this step. Then, you start the actual build with the ninja command.

To set up a build directory builddir with the default configuration and build the library in it:

  meson builddir
  ninja -C builddir

By convention, directory names starting with build or subdirectories of a build*/ folder are used.

Debug and Release Builds

Meson differentiates between debug and release builds at configuration time (when setting up the build directory). These are the most often used variants:

  meson --buildtype=debug builddir
  meson --buildtype=debugoptimized builddir
  meson --buildtype=release builddir

Building the Documentation

This library carries documentation embedded in the source code. Run the following tool to generate HTML documentation:

  ./tools/make-doc.py

This runs the open-source tool Doxygen and generates a web page in the directory docs/html.

Inspect Parameters

To see all the possible and the active settings, change into the build directory and call meson configure. You can see some of the built-in possibilities there, like unity builds and sanitizer to use.

Examples

like: make

  meson --buildtype debug builddir

Not optimized build in builddir/ (optimized for debugging in gdb for ex.)

like: make install

  meson builddir

Fully optimized build in builddir/, installs in /usr/lib and /usr/lib/include.

This can be used together with debuild to create packages, after adapting the debian/rules (not recommended, just use the existing rules). The staging directory will be transfered via DESTDIR.

like: make localinstall

(install in $HOME/???/lib)

  meson --prefix ???/somewhere builddir

Installs into the user's home directory.

To use it one has to set LD_LIBRARY_PATH, PKG_CONFIG_PATH, and CPATH (probably).

After the setup phase

... one can call any of these:

  ninja -C builddir
  ninja -C builddir test
  ninja -C builddir install
  ninja -C builddir clean
  rm -Rf builddir

taskolib's People

Contributors

alt-graph avatar finii avatar misspeppermintpatty avatar ohensler avatar wyrnat avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar

taskolib's Issues

Use named types for Lua types

The question comes up 'everywhere', I believe we should go away from long long (and the LL suffix) to some more explicit type name. That also makes it clear for users of the library why what happens.

Fair enough. πŸ‘
But that is a different PR. :)

Originally posted by @alt-graph in #45 (comment)

Feature request: File system I/O

So far, our sandbox prohibits access to the file system. It would be nice to allow some file I/O in a specific folder (or a list of folders) and, possibly, subdirectories. Typical code like this should work:

filename_bs = 'MeasuredBeamSize.txt'
fid_bs = io.open(filename_bs, 'w')

Feature request: Local version control

Local sequences and all of their modifications should automatically be versioned with Git. In a first step, there will be no API for users of the library, but the repository can still be accessed from the command line.

"Run step" functionality is missing

We need a function to run a single step in isolation, taking into account

  • the step setup function
  • the step setup script
  • the step itself.

The Taskomat server currently does not execute the step setup script when running individual steps.

Cannot build with Meson 0.53.2 (Ubuntu 20.04 LTS)

One of the last PRs added a Meson feature that seems not to be supported on our main build system:

Message: Generating self-containment tests

tests/meson.build:51:8: ERROR: Unknown method "substring" for a string.

Variables not synched after Lua terminate_script()

And possibly after other things like timeouts.

Scenario:

  • Create a Sequence with Steps that store a variable in the Context
  • Change the variable in every Step
  • In one Step call terminate_script() after changing the variable
  • Check variable in context

For example this simple change (on 516ba5d in my case)

diff --git a/tests/test_Sequence.cc b/tests/test_Sequence.cc
index ba82abb..9e751d4 100644
--- a/tests/test_Sequence.cc
+++ b/tests/test_Sequence.cc
@@ -3416,7 +3416,7 @@ TEST_CASE("Sequence: terminate sequence with Lua exit function", "[Sequence]")
     step_increment.set_used_context_variable_names(VariableNames{"a"});
 
     step_check_termination.set_label("exist sequence when a == 4");
-    step_check_termination.set_script("if a == 4 then terminate_sequence() end");
+    step_check_termination.set_script("if a == 4 then a = 5 terminate_sequence() end");
     step_check_termination.set_used_context_variable_names(VariableNames{"a"});
 
     step_while_end.set_label("end loop");

The test still succeeds although a is 5 before we terminate and a's value is tested to be 4 in the test:

      REQUIRE(std::get<long long>(ctx.variables["a"] ) == 4LL);
      REQUIRE(not queue.empty());
      REQUIRE(queue.size() == 26);
      auto msg = queue.back();
      REQUIRE(msg.get_type() == Message::Type::sequence_stopped);
      REQUIRE(msg.get_text() == "Sequence explicitly terminated");

This can be a problem if people use a variable as flag if something really happened like

    do stuff
    set_control_system_value(xxx)
    x = 1 -- did it!
    more stuff
    terminate_sequence() -- for some reason

Now you want to use x (in the context) to see if that control-system call happened or not.
This is broken.

Or maybe intended?

Idea: Allow client to hook into message queue processing

Another thing I noticed, from a client perspective it might be nice if one could hook into the message processing, when all the messages are processed in update(), to add custom behavior. For example do something on Sequence-stop or on Sequence-stop-with-errors.
For example right now if Olaf's server is in run-continuously mode and we would say that the sequence should be restarted ONLY if the previous sequence run had no errors... we can not really find that out.

Hope that makes sense.

(Formerly https://mcs-gitlab.desy.de/doocs/doocs-high-level-support/libtaskomat/-/issues/18)

Feature request: Pause sequences

It should be possible to pause sequences and to resume their execution afterwards. In a first step, pausing between steps is probably sufficient.

Implement useful functions from os

  • Problem: some os commands (e.g. os.execute()) are not recommended to present to the user if the applications runs on a server with root access. Therefore, the user is not able to use any os commands.

  • Request: Some os commands like os.time are useful if not necessary

  • Objective: Write custom commands for
    -- os.time
    -- os.date
    -- os.difftime

Do not use errno when reporting std::filesystem errors

In several places where std::filesystem errors are handled we use errno to generate an error message, e.g. like this:

        auto err = errno;
        throw Error(gul14::cat("I/O error: ", e.what(), ", error=", std::strerror(err)));

Unfortunately the C++ standard gives no guarantee about the contents of errno after std::filesystem calls. Although it seems to work on Linux systems, it is not portable. We should strictly report the errors reported by the calls themselves.

Regression: Executor context gets overwritten in is_busy()

The context_ member inside an Executor object stores not only variables, but also the four output functions. Unfortunately since one of the last PRs we copy it back from the parallel thread in is_busy():

bool Executor::is_busy()
{
    if (not future_.valid())
        return false;

    const auto status = future_.wait_for(0s);

    if (status == std::future_status::timeout)
        return true;

    context_ = future_.get();
    return false;
}

This overwrites the correct local output functions by the "send-a-message" output functions from the parallel thread. Hence, output does not work anymore as intented. This can be observed in the current Taskomat builds.

I'll prepare a minimal PR to address this, but we might also want to think about disentangling variables and output functions.

Ping to @Finii @MissPeppermintPatty...

Feature request: User-maintainable Lua libraries

(by J. WΓΆhnert)

  • Add a feature to create libraries with input parameters and a return value
  • those libraries can be used by any sequence via an import

Example library function

  • Create file magnets_bib.lua with the following content
function autocycle_quads(magnets, design)
--[[
@param magnets: array of magnet names
@param design: design kick of magnets according to beam path
                options: ["DESIGN_I1D", "DESIGN_B1D", "DESIGN_B2D", "DESIGN_TLD", "DESIGN_T4D", "DESIGN_T5D"]
receive list of magnets and set to current design strength
]]--
        mag_addr = {}
        mag_addr_busy = {}
        print("cycling quadrupols to design strength...")
        for i, mag in ipairs(magnets)
        do
                mag_addr[i] = facility..".MAGNETS/MAGNET.ML/"..mag
                mag_addr_busy[i] = mag_addr[i].."/BUSY"
                dset(mag_addr[i].."/KICK.AUTOCYCLE", dget(design_path..mag.."/"..design) )
        end
        dget_until{
                        addresses = mag_addr_busy,
                        values = {0,0,0,0}
                  }
        print("all quadrupols are cycled to design strength")
end

Example library call

require("magnets_bib")
magnets= {"QI.60.I1", "QI.61.I1", "QI.63.I1D", "QI.64.I1D"}
design = "DESIGN_I1D"
autocycle_quads(magnets, design)

Executor::cancel() is broken

While working on #51 (comment) I noticed that Executor::cancel() is only working in a very limited set of conditions.

Lets take this very simple scenario, where a user writes a Sequence with an infinite loop and wants then decides to push 'STOP' on the Gui (or something alike):

diff --git a/tests/test_Executor.cc b/tests/test_Executor.cc
index 7c69c60..6c2adc1 100644
--- a/tests/test_Executor.cc
+++ b/tests/test_Executor.cc
@@ -151,6 +151,30 @@ TEST_CASE("Executor: Run a failing sequence asynchronously", "[Executor]")
     REQUIRE(sequence.get_error_message() != "");
 }
 
+TEST_CASE("Executor: cancel() endless step loop", "[Executor]")
+{
+    Context context;
+    context.log_error_function = nullptr; // suppress error output on console
+
+    Sequence sequence{ "test_sequence" };
+    sequence.push_back(Step{ Step::type_while }.set_script("return true"));
+    sequence.push_back(Step{ Step::type_action }.set_script("a = 1"));
+    sequence.push_back(Step{ Step::type_end });
+
+    Executor executor;
+
+    executor.run_asynchronously(sequence, context);
+
+    gul14::sleep(5ms);
+    executor.cancel();
+
+    while (executor.update(sequence))
+        gul14::sleep(5ms);
+
+    // Make sure that exactly the desired error message comes out
+    REQUIRE(sequence.get_error_message() == "Sequence aborted: Stop on user request");
+}
+
 TEST_CASE("Executor: cancel() within LUA sleep()", "[Executor]")
 {
     Context context;

The test fails because it takes too long:

$ ninja -C ../build test
ninja: Entering directory `../build'
[0/1] Running all tests.
1/3 taskolib / format        OK              0.01s
2/3 libgul14 / all           OK              0.91s
taskolib / all time out (After 10 seconds)
3/3 taskolib / all           TIMEOUT        10.02s   killed by signal 15 SIGTERM
>>> MALLOC_PERTURB_=27 /home/fini/git/taskolib/build/tests/taskolib_test
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― βœ€  ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
taskolib_test is a Catch v2.13.7 host application.
Run with -? for options

-------------------------------------------------------------------------------
Executor: cancel() endless step loop
-------------------------------------------------------------------------------
../tests/test_Executor.cc:154
...............................................................................

../tests/test_Executor.cc:154: FAILED:
due to a fatal error condition:
  SIGTERM - Termination request signal

===============================================================================
test cases:   21 |   20 passed | 1 failed
assertions: 1027 | 1026 passed | 1 failed

――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――


Summary of Failures:

3/3 taskolib / all    TIMEOUT        10.02s   killed by signal 15 SIGTERM

Ok:                 2   
Expected Fail:      0   
Fail:               0   
Unexpected Pass:    0   
Skipped:            0   
Timeout:            1   

The problem is that the immediate_abort flag is only checked in the hook that is called every 100 Lua lines, and we have only one line in each of the scripts!

image

I believe each Step should maybe check BEFORE it executes if the flag has been set in the comm-channel.
And / or the Sequence should abort.
But the flag is checked nowhere.

Possible bug in Executor test?

[why]
During a discussion with Fini a test fails on Executor:

...
-------------------------------------------------------------------------------
Executor: Run a failing sequence asynchronously
-------------------------------------------------------------------------------
../tests/test_Executor.cc:122
...............................................................................

../tests/test_Executor.cc:143: FAILED:
  REQUIRE( executor.is_busy() == true )
with expansion:
  false == true

See this comment for more details.

[how]
I would suspect that this is a timing problem. Maybe we should tweak the sequence and wait 10ms before the next Step fails:

TEST_CASE("Executor: Run a failing sequence asynchronously", "[Executor]")
{
    Context context;

    Step step_wait(Step::type_action);
    step_wait.set_script("sleep(0.01)");

    Step step_fault(Step::type_action);
    step_fault.set_script("not valid LUA");

    Sequence sequence{ "test_sequence" };
    sequence.push_back(std::move(step_wait));
    sequence.push_back(std::move(step_fault));
...
    Executor executor;
    executor.run_asynchronously(sequence, context);
...
    REQUIRE(executor.is_busy() == true);
...
}

...πŸ€”

Sequence without label

For me it is unclear why we have this constellation:

  • Sequences without label can be created
  • Sequences without label can not get a label afterwards
  • Sequences can not have their labels changed
  • Sequences without label can be serialized
  • Sequences without label can not be deserialized

Especially the last point is interesting, because this is not technical but a deliberate decision in the code.

For me this makes no sense. Several sets can be logical:

  • Sequences without label can be created
  • AND Sequences can get a label afterwards OR labels can always be changed

and

  • Sequences without label can NOT be serialized
  • AND Sequences without label can not be deserialized

The changes are simple. What do we want (discussion)?

Disallow sequence names starting with a period

Sequence names are designed to be machine-friendly, and as such are often used as folder or file names. On unixoid operating systems, file names starting with a period designate hidden files. We might want to avoid that by requiring that the first character of a sequence name must be alphanumeric.

Feature request: Call another sequence

It might be useful to synchronously call another sequence from a parent sequence. This could take the form of a "call" step (or function) that performs the following functions:

  1. Locate the sequence specified as an argument, throw an error if it cannot be found
  2. Start the "child" sequence, throwing an error if it is already running
  3. Wait until the child sequence terminates

Better support for copying sequences between instances

Users wanting to transfer sequences from one Taskolib instance to another could easily copy the sequence folders in previous versions of the library. With the introduction of our git repository, this has become much more tedious: Effectively, new files have to be added and committed while the process is stopped. Then, the process has to be restarted.

I guess we should think about supporting git remotes sooner rather than later. Some API extensions for that would be helpful.

Sequence with unique ID

There is a wish to have per Sequence a unique identifier, ID, to distinguish between the sequences.

One discussed example is to have a reference to the committed Sequence in the git repo that can be 'sorted' against their IDs. It differs from the unique git identifier.

Requirement

  • the ID should be maintained by SequenceManager
  • by creating a Sequence it should be set as an additional parameter
    • Question: do we need this? Means at least lots of possible changes to the test cases.
  • the ID should be encapsulated inside the SequenceManager.

Possibility to create and maintain the ID

For those ID I see currently those possibilities:

  • use an incremented integral of int64 starting from 1
    • advantage: easy to implement
    • disadvantage: possible problems to restore them from saved sequences and what the next ID will be.
  • use hash code

Further discussion

@alt-graph Should be discussed on how to proceed.

Add an 'initialization script' to a sequence

Idea

We need somehow an 'initialisation step' or better 'initialization (Lua) script' for a sequence. In this preliminary step the user has the possibility to initialize 'global' parameters for the sequence. The initialization step is transparent to the user as he only injects the initialization script and afterwards execute the sequence with Executer::run_asynchronously(Sequence,Context).

Beside the initialization of parameters you can also add some native Lua functions or custom ones, for example dget.

Any issues, comments, thumbs up or down, etc. is very welcome πŸ˜„ .

Code enhancement

As it is an initialization of a sequence I would append it to the Sequence class:

class Sequence
{
public:
    ...
    void create_initialization_script(std::string initialization_script);
}

Example

For example when a user create a sequence by defining a prefix of a doocs address. In the follow-up steps the already initialized doocs prefix address is provided for dget and dset:

Step action_get{ Step::type_action };
action_get.set_script("a = dget(prefix .. '/PROPERTY1')");

Step action_set{ Step::type_action };
action_set.set_script("dset(prefix .. '/PROPERTY2', 0)");

Sequence s{ "sequence with doocs address prefix" };
s.create_initialization_script("prefix = 'PREFIX/FOR/DOOCS'")
s.push_back(action_get);
s.push_back(action_set);

Context ctx;
ctx.variables["prefix"] = VariableValue( "" );
ctx.lua_init_function = ... /* from Taskomat server: make_context(...) -> task::Context */

Executor e;
e.run_asynchronously(s,ctx);

Question:

  1. Do you see any other possibility to transfer variables through the stepwise Lua sandboxes without using Context?
  2. For me it is not clear how to use Context::lua_init_function and 'Sequence::create_initialization_script`... πŸ€”

Test case

TEST_CASE("execute(): Sequence with initialization script", "[Sequence]")
{
    // Precondition: pre_string = "FOO"
    // 0 ACTION: a = pre_string .. " and BAR"
    // Post-condition: a = "FOO and BOO"
    // 1 ACTION: b = pre_string .. " or FOOBAR"
    // Post-condition: b = "FOO or FOOBAR"

    Step action1{ Step::type_action };
    action1.set_script("a = pre_string .. ' and BAR'");
    Step action2{ Step::type_action };
    action2.set_script("b = pre_string .. ' or FOOBAR'");

    Sequence s{ "test sequence with initialization script" };
    s.create_initialization_script("pre_string = 'FOO'")
    s.push_back(action1);
    s.push_back(action2);

    Context ctx;
    ctx.variables["a"] = VariableValue{ "" };
    ctx.variables["b"] = VariableValue{ "" };

    s.execute(ctx, nullptr);
    REQUIRE(ctx.variables["a"] == "FOO and BAR");
    REQUIRE(ctx.variables["b"] == "FOO or FOOBAR");
}

Lua got updated

image

5.4.4 is out a year now, we still have 5.4.3.

Reported by: Soeren

Exception handling broken (again)

The exception handling is a bit fragile with Lua/Sol.
We should carefully think about our goals as it seems that we can not have everything.

The question is where shall exceptions work out of the box:

  1. In our (library) code
  2. In user's code added via Context

There is a Sol setting that influences this: SOL_EXCEPTIONS_SAFE_PROPAGATION

  • We started the project with that feature turned off.
  • With 6026126 Sol: Tell Sol our Lua is exception safe I turned the feature on (Jun 25, 2022)
  • Then 45c6a8e Revert "Sol: Tell Sol our Lua is exception safe" I reverted it (Jul 6, 2022)
  • And finally b0efb4d Enable SOL_EXCEPTIONS_SAFE_PROPAGATION Lars turned in on again (Aug 30, 2022)

Maybe he was not aware that I tried and discarded the settings before, although I seem to remember we specifically talked about that. Anyhow. Let me specify the pros and cons:

When we use 1. (feature turned on) the exceptions within taskolib work effortlessly. When we throw an exception within the library it can be caught as expected. The con of 1. is that user exceptions become impossible. If the user has code executed in the Lua context, and it throws, this is a CRITICAL_EXCEPTION_FAILURE error.

With solution 2 our own exception handling has to be adapted (i.e. does not work as it is), but user's exception handling is working as expected.

The question is now:

  • Shall exceptions be easy for us, or
  • shall exceptions be possible for library users AT ALL

I added a branch with a test case, so that we know about what we are talking bugfix/user-exceptions. Check that out and run the tests. The branch already has the aformentioned feature turned off. Try to turn it on and see the Rainbows go away and they are replaced by CRITICAL_EXCEPTION_FAILURE errors.


The test code is very simple, Pretending to be a taskolib user we add a custom Lua function, and that function just throws:

// A function that would return the integer value 10 in Lua
// if we do not throw ;-)
auto lua_testfun(sol::this_state s) -> sol::object
{
    throw task::Error{ "Rainbows!" };
    return sol::make_object(s, 10);
}

[... later ...]

Context ctx{ };

ctx.lua_init_function = [](sol::state& s) {
    s.set_function("testfun", &lua_testfun);
};

Sequence seq{ };
step.set_script("a = testfun()");

Now when the script/Step/Sequence is executed the taskolib user might expect to actually see Rainbows or anyhting helpful for the script writer, because that is the only means they have to communicate.

This is broken with the current setting.

Usecase:
Imagine the taskolib user wants to include it into a bigger environment. That environment usually/often is based on exceptions that are thrown when something happens that needs reporting. Like imagine they use Doocs and want to call something. Would it not be nice when the Lua-Script write can see the actual Doocs exceptions?

On the other hand the taskolib user could try-catch all doocs calls. But still there is no possible way to communicate the error to the Script user. Well, except they design all custom Lua functions in a way that they optionally return an error string that the scripter has to evaluate. Cough.

My personal opinion is (obviously) to have the feature turned OFF.
With the current library Olaf's taskomat server has no way to communicate doocs errors to the script user. Well, one could imagine that all doocs calls are wrapped into try-catch and exception texts then directly inserted into some reporting property.
Is that really what we want? My opinion is we should make it easy for taskolib users to write exception enabled code and handle that, and do the extra mile in OUR OWN code (not theirs).

Tests fail on Github CI

==================================== 1/2 =====================================
test:         taskomat / all
start time:   11:11:37
duration:     0.68s
result:       exit status 1
command:      MALLOC_PERTURB_=40 /home/runner/work/taskolib/taskolib/build.asan/taskomat_test
----------------------------------- stdout -----------------------------------

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
taskomat_test is a Catch v2.13.7 host application.
Run with -? for options

-------------------------------------------------------------------------------
Executor: Run a failing sequence asynchronously
-------------------------------------------------------------------------------
../tests/test_Executor.cc:122
...............................................................................

../tests/test_Executor.cc:143: FAILED:
  REQUIRE( executor.is_busy() == true )
with expansion:
  false == true

ERROR: Script execution error: sol: runtime error: [string "if a == 4 then terminate_sequence() end"]:1: πŸ›‘ABORTπŸ›‘πŸ›‘ABORTπŸ›‘
stack traceback:
	[C]: in function 'base.terminate_sequence'
	[string "if a == 4 then terminate_sequence() end"]:1: in main chunk
===============================================================================
test cases:   193 |   192 passed | 1 failed
assertions: 12111 | 12110 passed | 1 failed

See also #17 (comment)

Feature request: Sub-sequences

We should make it possible for a sequence to include another sequence. One possible way of doing this is to add an "include" step that automatically expands to the steps of the sub-sequence when the sequence is run. Maybe some special work on parameter passing is required, maybe no.

This is probably rather involved. It might be easier to implement #71 first.

Compiler warning with Sol

[46/71] Compiling C++ object libtaskomat.so.0.1.2.p/src_lua_details.cc.o
In file included from ../src/lua_details.h:31,
                 from ../src/lua_details.cc:27:
../include/taskomat/sol/sol/sol.hpp: In function β€˜task::Message::IndexType task::get_step_idx_from_registry(lua_State*)’:
../include/taskomat/sol/sol/sol.hpp:14751:61: warning: array subscript β€˜const char [37][0]’ is partly outside array bounds of β€˜const char [20]’ [-Warray-bounds]
14751 |                                                 lua_getfield(L, tableindex, &key[0]);
      |                                                 ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
../src/lua_details.cc:35:19: note: while referencing β€˜{anonymous}::step_index_key’
   35 | static const char step_index_key[] =
      |                   ^~~~~~~~~~~~~~
C++ compiler for the host machine: c++ (gcc 11.2.0 "c++ (Ubuntu 11.2.0-19ubuntu1) 11.2.0")
C++ linker for the host machine: c++ ld.bfd 2.38
Host machine cpu family: x86_64
Host machine cpu: x86_64

Feature request: Timeout for sequences

We already have a timeout for individual steps, but a timeout for the entire sequence might also be handy. Like the one for steps, it should be settable to infinity to disable the timeout check.

Magic token assumes too much

We have a magic token (in the exception message string) that consisted of the string

[ABORT]

before it has been fancy-ised with MR https://mcs-gitlab.desy.de/doocs/doocs-high-level-support/libtaskomat/-/merge_requests/82

Now it is

u8"\U0001F6D1ABORT\U0001F6D1" == "πŸ›‘ABORTπŸ›‘"

which looks really really bad on systems that do not have 'all unicode symbols that could exist' installed:

image

As this is just a magic token, it can be anything 'longish' to prevent accidential triggers. Maybe use something that is displayable on all platforms. Like being at least in the non-extended unicode range (i.e. below u_10000). Or use tabs or just the previous angle square brackets.

Can we revert that commit or at least find some not-so-fancy solution?

(Btw: This also depends on used terminal emulator, not only on system and installed fonts. A lot of coders use kitty and alacritty, and that will NOT display even the Tofu, I guess.)

Edit: Explanation: Tofu are the unknown-unicode-glyph boxes with the code itself in them

Misnomer Sequence::pop_back()

    /**
     * Remove the last element from the sequence.
     *
     * Calling pop_back() on an empty Sequence returns silently. Iterators and references to the
     * last element as well as the end() iterator are invalidated.
     */             
    void pop_back(); 

Well, it is not pop-ping the Step from the Sequence, it drops it.
If there is push and pop I would assume the usual functionality.

Not that people would care (I guess), but we could move the Step object out instead just trashing it?

Error information is not updated when editing sequence

When a sequence fails, the Error object that can be obtained via Sequence::get_error() contains the index of the step that caused the error. This also shows up as a red indicator in the Taskomat GUI.

If a step is deleted or inserted before the error step, the index is not updated. This means that the wrong step is now indicated in red.

Variable handover from Step to Step broken for bool

Assume this test:

    SECTION("Hand bool variable over context without initial value") {
         Step s1;
         s1.set_script("a = 2; b = true"); // semicolon for the C people, Lua ignores it
         s1.set_used_context_variable_names(VariableNames{"a", "b"});
         Step s2;
         s2.set_script("if b then a = a + 2 end");
         s2.set_used_context_variable_names(VariableNames{"a", "b"});
 
         seq.push_back(s1);
         seq.push_back(s2);
         seq.execute(ctx, nullptr);
         CAPTURE(std::get<long long>(ctx.variables["a"]));
         REQUIRE(ctx.variables["a"] == VariableValue{ 4LL } );
     }

This breaks, because b (the bool variable) is not handed over from Step1 to Step2 (but a the integer is).

../tests/test_Sequence.cc:1224: FAILED:
  REQUIRE( ctx.variables["a"] == VariableValue{ 4LL } )
with expansion:
  {?} == {?}
with message:
  std::get<long long>(ctx.variables["a"]) := 2

The reason is obvious, but handling it silently is somehow strange:

void Step::copy_used_variables_from_lua_to_context(const sol::state& lua, Context& context)
{
    const VariableNames export_varnames = get_used_context_variable_names();

    for (const VariableName& varname : export_varnames)
    {
        sol::object var = lua.get<sol::object>(varname.string());
        switch (var.get_type())
        {
            case sol::type::number:
                // For this check to work, SOL_SAFE_NUMERICS needs to be set to 1
                if (var.is<long long>())
                    context.variables[varname] = VariableValue{ var.as<long long>() };
                else
                    context.variables[varname] = VariableValue{ var.as<double>() };
                break;
            case sol::type::string:
                context.variables[varname] = VariableValue{ var.as<std::string>() };
                break;
            default:
                break;
        }
    }
}

Of course this also breaks on any other type (other than lua-integer, lua-float, and string), especially on user defined types.
Imagine some user that manipulates a Doocs IIII or IFFF object in Lua and wants to hand that over from one Step to another. πŸ˜’

Planned:

  • will create a commit with the additional tests I set up specifically for variable 'forwarding'
  • will also fix the behavior, which looks simple (*cough*)
  • improve the documentation on the variable over context thing, I find it very sparse (any hints where it is hidden?)

Extend Sequence: Add final-clause in try-catch-block

Requirements and Design

After a discussion with a colleague from MXL (DESY) the desire arose to extend the existing try-catch-block with a final-clause. It must be set after the catch-block and named with the literal final:

Step 1: try
Step 2:    action
Step 3: catch
Step 4:    action
Step 5: final
Step 6:    action
Step 7: end

Goal & Implementation

  1. The final-clause will always executed independent of the result of the previous try- or catch-block.
  2. The final-clause will be executed after the try-block finishes and before the catch is processed.
  3. The final-clause is optional.
  4. Only one final-clause is allowed.
  5. If the final-clause throws an error the step will exist immediately and return control to the calling code. It will at least forward the error by throwing a C++ exception with a proper error massage.

Note: one can also catch errors in the final-block with a nested try-catch-block:

Step  1: try
Step  2:    action
Step  3: catch
Step  4:    action
Step  5: final
Step  6:    try
Step  7:       action
Step  8:    catch
Step  9:       action
Step 10:    end
Step 11: end

Not implemented

By signalling a stop from an external source the sequence will immediately quite its execution and exit without any possiblity too clean-up. It is in the responsibility of the user to proceed with an appropriate code.

Priority

This issue is not so urgent and has low periority (... until anybody else wants to push it up ;-)

Examples

In the following sequence the bool variable i_am_happy is injected and passed through all steps with the possibility of defining a Context variable.

Example with try-block that throws an error

Step 1: try
Step 2:    action: throw error
Step 3: catch
Step 4:    action: print warning
Step 5: final
Step 6:    action: i_am_happy=true & print
Step 7: end

Output:

[code: i_am_happy=false]
i_am_happy=true
warning

Example with try-block that exits normally

Step 1: try
Step 2:    action: print try
Step 3: catch
Step 4:    action: print warning
Step 5: final
Step 6:    action: i_am_happy=true & print
Step 7: end

Output:

[code: i_am_happy=false]
try
i_am_happy=true

Example with final-clause that throws an error

Step 1: try
Step 2:    action: print try
Step 3: catch
Step 4:    action: print catch
Step 5: final
Step 6:    action: throw error
Step 6:    action: i_am_happy=true & print
Step 7: end

Output:

[code: i_am_happy=false]
try
[code: i_am_happy is still 'false']

Reference

As a reference Java specification for JDK 20 is used.

secure filepath operations against directory hopping

Problem

filepaths like "../../.." can be passed as parameters leaving the secure local sequence directory and the may access root directory.

Solution

  • In Sequence Manager: load all sequences and check the user-provided path against the sequence list. Throw error if the path is not in the list
  • In serialize_sequence: disallow double points and absolute paths

Affected functions

The following functionas have to be changed:

  • serialize_sequence.cc
    • remove_sequence
    • store_sequence
    • remove_path
  • Sequencemanager.cc
    • remove_all_sequences_and_repository
    • remove_sequence
    • rename_sequence
    • create_sequence

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.