GithubHelp home page GithubHelp logo

Comments (5)

ahoarau avatar ahoarau commented on May 28, 2024

@meyerj gentle ping on this issue.

from rtt.

meyerj avatar meyerj commented on May 28, 2024

There is not much you can do about that without refactoring the whole scripting engine. Orocos does not evaluate every line of a function or program on the fly. In a first step it parses the function and already creates all the necessary data sources, operation callers etc., in order to be able to execute it with real-time constraints (under some additional assumptions and depending on the data types involved). At the time the parser sees the line ros.import("test") it has to know that ros resolves to a service registered globally and that this service provides an operation named import with a certain signature, but the function has not been executed yet and hence the ros service does not exist.

It might be possible to automatically delay the evaluation of a line until the function is actually executed in case of an error during the initial parsing step, so basically to interpret the first definition of import_stuff() like in the second in your example. But that does not appear to be a sensible approach to me if real-time execution really matters and might hide potential problems it the function is really called repeatedly in a real-time context.

Your use case looks like import_stuff() only needs to be called once during deployment, but ROS is optional in your application and you do not want to import package rtt_ros unconditionally, outside the function? In that case you could move the body of the function into a separate file import_stuff.ops and "call" it with scripting.runScript("import_stuff.ops") instead?

from rtt.

ahoarau avatar ahoarau commented on May 28, 2024

I'm trying to get to a sort-of-dynamic deployment, where you load a big orocos lib that allows users to create components in one line (instead of the whole loadComponent, setActivity, configure, start, connect to the provided component etc). Something like :

import("rtt_rospack")
ros.import("components_lib.ops") // <-- Defines loadComp1(), loadComp2() etc
loadComp1()

And loadComp1() is defined like :

global void loadComp1()
{
   loadComponent("hello","OCL::HelloWorld")
   hello.do_stuff()
}

This code returns : Parse error at line 6: Service or Task "Deployer" has no Peer or Service hello

Orocos does not evaluate every line of a function or program on the fly.

Now I get why I have this behavior and I think i'm using the scripting service the wrong way.
It's true that It could be better to have :

scripting.runScript("import_comp1.ops")
scripting.runScript("import_comp2.ops")

But I'd like to have arguments to the functions, and that is difficult to achieve without ros.

Maybe something like :

scripting.runScriptWithArgs("import_comp.ops", strings("is_sim","my_robot","/link_0"))

from rtt.

meyerj avatar meyerj commented on May 28, 2024

There are some possibilities how to fix this, but the by far simplest solution is to use scripting.eval("...") inside your generic loadComp() function where necessary, for example:

export void myLoadComponent(string package, string type, string name)
{
  import("rtt_ros")
  scripting.eval("ros.import(\"" + package + "\")")
  loadComponent(name, type)

  // configure component...
}

That approach works for me.

Use TaskContext and Service argument and return types for the DeploymentComponent

Additionally, to avoid lots of eval statements in myLoadComponent(), loadComponent() could actually return a TaskContext * which is already a valid type in the Orocos type system. Instead of the approach you suggested in #249, all deployer operations that operate on TaskContexts should expect TaskContext (or Service) pointer arguments and the parsers could directly operate on pointers instead of strings (with some implicit conversion from string literals to TaskContext * for backwards compatibility). That would be much cleaner and additionally provide some type safety compared to plain strings. Some operations of the DeploymentComponent could be deprecated because there are already equivalent operations in the TaskContext interface.

Example:

export void myLoadComponent(string package, string type, string name)
{
  import("rtt_ros")
  scripting.eval("ros.import(\"" + package + "\")")
  var TaskContext tc = loadComponent(name, type)
  // or
  //   var TaskContext tc = "name" (implicit conversion from string)
  setActivity(tc, 1.0, 0, ORO_SCHED_OTHER)
  tc.loadService("foo")
  var TaskContext tc_peer = tc.peer
  // ...
}

eval keyword

Introduce a new eval keyword to Orocos scripting, similar to the bash built-in eval, in order to delay the parsing of a statement or function body until actual execution, e.g.

import("rtt_rospack")
eval ros.import("test")   // equivalent to scripting.eval("ros.import(\"test\")"

or

export eval void myLoadComponent(string package, string type, string name)
{
   import("rtt_ros")
   ros.import(package)
   loadComponent(name, type)
}
// equivalent to
//   export void myLoadComponent(string package, string type, string name)
//   {
//      scripting.eval("import(\"rtt_ros\")\n" +
//                    "ros.import(\"" + package + "\")\n" +
//                    "loadComponent(\"" + name + "\", \"" + type + "\")
//   }

The first variant might be trivial and just adds some syntactic sugar, while the latter is only a vague idea. Both use cases probably require some effort to properly bind argument values independent of their type. At the moment statements evaluated with scripting.eval() or scripts called with scripting.runScript() cannot access variables in the scope of their caller.

Script arguments

Arguments (and maybe a return value) for scripts would be possible, too, but not trivial to implement either. How would you expect to access the arguments in a script? As argc/argv like in C? Or should arguments always have explicit names assigned?

from rtt.

ahoarau avatar ahoarau commented on May 28, 2024

Thanks for the detailed answer. I think having loadComponent returning a TaskContext is a great alternative. The eval keyword would also be a nice plus, not having to use scripting.eval, and used like eval ros.import("test").

Regarding the script arguments, something like scripting.runScriptWithArgs("file.ops","myrobot true false") could be sufficient to be parsed with argc/argv. It'll probably add way too much overhead to have proper arguments with names.

from rtt.

Related Issues (20)

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.