Skip to content

Screenshot Screenshot

The Sen shell

Deprecated in 0.8.0

This shell will be deprecated in Sen 0.8.0 and removed in a later release. Its replacement is the term component, which is in beta now. It does what the shell does, in a full-screen terminal, with a guided form for method arguments and log lines in the same stream as everything else. Try it with sen term, and say what it is missing.

Getting it started

The Sen shell allows you to interact with all the objects that are published to the buses in the kernel.

It makes use of the extensive introspection capabilities that are built into Sen. That's why you get auto-complete, error-checking, argument parsing and even the ability to read the documentation.

You can run a stand-alone shell by typing:

sen shell

And you can load the shell component into any kernel. You do it like this:

load:
  - name: shell
    group: 2     # you decide when to start the shell

The configuration options and the commands the shell offers are defined in the component's STL:

Shell configuration
// How lists should be printed
enum ListStyle: u8
{
  treeStyle,   // use a tree
  plainStyle,  // simple list
}

// How timestamps should be printed
enum TimeStyle: u8
{
  timestampUTC,    // use UTC timestamps
  timestampLocal,  // use local time timestamps
}

// How buffers should be printed
enum BufferStyle: u8
{
  size,     // just print the buffer size
  hexdump,  // print the buffer content has hex numbers
}

// Holds information about a named query
struct Query
{
  name      : string,  // the name of the query (cannot contain spaces or dots)
  selection : string   // the query expression
}

sequence<string> SourcesList;
sequence<Query> QueryList;

// Configuration params for the shell
struct Configuration
{
  prompt        : string,       // defaults to 'sen> '
  timeStyle     : TimeStyle,    // how to display time
  bufferStyle   : BufferStyle,  // how to display buffers
  serverEnabled : bool,         // enables remote shell access (false by default)
  serverPort    : u16,          // 0 means default
  noLogo        : bool,         // true if you don't want a welcoming logo
  open          : SourcesList,  // list of sources to open automatically
  query         : QueryList,    // list of queries to create automatically
  logBus        : string        // where to find the logmaster, if any (default: local.log)
}

Once you start the shell, you can use tab to auto-complete and to see the available commands. If you type help you will see more info about all the options you have.

Screenshot

To shut down, type Ctrl+D on an empty line, or use the shutdown command. Both stop the whole kernel. If you loaded the shell next to other components, those stop too.

Command reference

What you type is either one of the built-in commands below, or the address of something on a bus followed by the member you want and its arguments.

The commands, from the shell's own interface
  // clears the screen
  fn clear() [const];

  // basic usage help
  fn help() [const];

  // lists previous commands
  fn history() [const];

  // lists all the existing objects and sources.
  fn ls() [const];

  // opens a source (bus or session) without any condition
  fn open(source: string);

  // opens a named source using a condition expression
  fn query(name: string, condition: string);

  // closes a previously-opened source (session, bus or query)
  fn close(source: string);

  // prints information about a type or instance
  fn info(term: string) [const];

  // requests the kernel to be shut-down
  fn shutdown() [const];

  // lists the current sources
  fn src();

Anything else is an address of the form session.bus.object.member:

To do this Type
Read a property local.counters.myCounter.getValue
Write a [writable] property my.tutorial.timer.setNextProgram "20 s"
Call a method, one argument my.tutorial.fibManager.computeFibonacci 8
Call a method, several arguments my.tutorial.calc1.add 10, 5
Call a method taking strings local.replay.replayer.open "my_replay", "school_recording"
Call a method that returns nothing my.tutorial.client1.useCalculator
Dump an object's current state my.tutorial.elon.print
Inspect a type or instance info local.counters.myCounter

Arguments are separated by commas. Strings, durations and enumeration values are quoted; numbers are not. Properties are read with the generated get<Prop>() accessor and written with setNext<Prop>(), exactly as they are from C++, and only [writable] properties expose a setter.

A getter prints the property name alongside its value, a method prints the returned value on its own, strings come back quoted, and a setter that worked prints nothing whatsoever:

sen:host/config> local.example.myObject.getProp5
- prop5: 453

sen:host/config> local.example.myObject.addNumbers 10, 5
15

sen:host/config> local.example.myObject.echo hello
"hello"

sen:host/config> local.example.myObject.setNextProp5 42

sen:host/config>

So the empty line after setNextProp5 is success, not a swallowed error. Read the property back if you want to see the effect, bearing in mind that the write lands on the next cycle rather than this one, and that if the object assigns that property itself during update() then your value is whatever the object last wrote, not the one you typed.

Calls do not block. Sen is asynchronous, so the shell returns immediately and prints the result when it arrives, which may be a cycle or two later.

Opening and closing buses

The ls command is especially useful because it allows you to see all the objects that are visible by the shell.

The shell does not see any objects by default. The only thing you can see are the available sessions. You need to open sessions and their buses so that the shell can start discovering objects.

The open command can be used to connect to a bus or to open a session and discover the available buses. This command has auto-complete capabilities to help you. Try it.

Screenshot

You can see that the shell lets you know when objects are discovered. The ls command shows them. The close command does the inverse.

Sometimes you will find yourself having to open some buses every time you load the shell. This is because you are interested in working with some objects and the shell is not opening any bus by default. To change this, you can set the open parameter in your configuration file and the shell will automatically open them at the start:

load:
  - name: shell
    group: 2
    open: [ local.kernel ]  # automatically open the 'local.kernel' bus

Calling methods on objects

The shell allows you to invoke methods on objects, no matter if they are local or remote. Name the object, then the method, then the arguments:

my.tutorial.calc1.add 10, 5

The animation below shows the same thing against an object's addNumbers method.

Screenshot

The shell is meant to be an interactive interface. For scripting, see what ships with Sen; several of those components expose the same objects to code.

Introspecting the system

You can use the shell to get information about the system. For example, you can use the kernel object to find out the available types, units and the build information of the kernel and all components.

And there's also a method named info, that you can use to inspect all the details of the available types (the built-in types, and also any type received over the network during execution).

Screenshot

Creating queries

When you open a bus, you are declaring an interest in all the objects that are published in it. You can inspect the list of active interests by using the src command. Calling open local.kernel and then src shows this:

  Name               Query
  local.kernel.all   SELECT * FROM local.kernel

That is an active query named "local.kernel.all", declaring an interest in all objects of that bus.

You can create your own queries, and therefore use the shell to monitor certain objects. For example, create a query that only notifies about entities in the se.env bus whose force ID property is "neutral" or "friendly", named "friendly_fire":

Screenshot

You can now see that the shell automatically opened the se.env bus and created a query named se.env.friendly_fire.

As soon as the query is active, Sen automatically evaluates the objects on the bus. The shell will:

  • Notify you of discovered objects that meet the query conditions. Once an object has been discovered, you can interact with it as usual.
  • Notify you when an object no longer meets the query conditions (or is destroyed), meaning it has been removed from your view.

You can always run the ls command to see the objects currently matching your active queries.

Note: Currently, you cannot see which object on a bus belongs to a specific query.

To close/delete a query, use the close command and pass the name of the query.

If you want the shell to automatically open some predefined queries, do as follows:

Configuring the shell to auto open queries
load:
  - name: shell
    query:
      - name: lat_in_range
        selection: "SELECT rpr.PhysicalEntity FROM se.env WHERE spatial.SpatialFPStruct.worldLocation.x BETWEEN 0.0 AND 500.0"
      - name: friendly_fire
        selection: "SELECT rpr.PhysicalEntity FROM se.env WHERE forceIdentifier IN (\"friendly\", \"neutral\")"

Connecting to a remote shell

Sometimes, during development, you will run your Sen application in a remote computer. Maybe you don't have ssh access, or the process was started by a script, launcher or orchestrator. How would you connect to the Sen process and access its shell? Enter the remote shell.

You can connect to a remote shell by doing sen rshell. For example:

connecting to a remote shell
sen rshell 192.168.1.44:8094

This process is very light and doesn't run any Sen kernel at all. It just talks to a shell running in a remote Sen kernel over a TCP channel.

Remote shell access is off unless you set serverEnabled, and the connection is unauthenticated, so it is intended for trusted networks.

It simply sends your key presses to the remote shell and receives low-level terminal drawing commands from it.

This means that you have all the power of the shell in your local terminal, even if your local terminal is Windows and the Sen process is on a Linux computer.

This starts a Sen process hosting a shell reachable only from outside, then connects to it with sen rshell.

Screenshot

To configure your shell to accept remote connections you need to set the serverEnabled and serverPort parameters. The last example used the following:

configuring a remote shell
# A shell meant to be reached remotely, via serverEnabled. serverPort is
# optional and sets a custom port.
#
# If you start this process, you will see nothing, but if you start a connection
# to it, you will see that it is like working locally. To do so run:
#
#   sen rshell <hostname>:8094
#
load:
  - name: shell
    group: 2
    open: [ local.tutorial ]  # to see the created objects
    serverEnabled: true
    serverPort: 8094