Tsctl: Difference between revisions

From embeddedTS Manuals
(Moved the object reference to the end of the document)
No edit summary
Line 127: Line 127:


= Hardware Map =
= Hardware Map =
This section documents the tsctl objects available for each board.  For CPU boards, the instance numbers will correspond directly to the value to pass to the corresponding <tt>Init</tt> function.  For base board and PC-104 boards, the number is relative to the peripheral board itself.  To get the actual number to pass, you will need to add the relative number to the total number of instances on all boards before it.  For the purpose of this calculation, the CPU board comes first, followed by the base board, followed by any PC104 boards.  Since there can be multiple PC104 boards, it is necessary to know the order the boards are initialized in.  However, for the existing supported boards there are only Pin and DIO which are accessible via instance 0.
[[tsctl Hardware Map]]
 
== TS-4200 ==
Time 0:  Linux gettimeofday() based
 
Bus 0: FPGA syscon registers (includes FPGA DIO registers)
 
Bus 1: read/write memory area for test purposes
 
Bus 2: MuxBus
 
Bus 3: caches access to TS-4200 DIO registers
 
Bus 4: CPU DIO bank 1 registers
 
Bus 5: caches CPU DIO bank 1 registers
 
Bus 6: CPU DIO bank 2 registers
 
Bus 7: caches CPU DIO bank 2 registers
 
Bus 8: CPU DIO bank 3 registers
 
Bus 9: caches CPU DIO bank 3 registers
 
Bus 10: 8-bit bus used by MuxBus
 
Bus 11: 16-bit bus used by MuxBus
 
Pin 0: TS-4200 pin control
 
DIORaw 0: CPU DIO bank 1 raw access
 
DIORaw 1: CPU DIO bank 2 raw access
 
DIORaw 2: CPU DIO bank 3 raw access
 
DIORaw 3: FPGA DIO raw access
 
DIO 0: aggregate DIO access to all banks
 
DIO 1: CPU DIO bank 1
 
DIO 2: CPU DIO bank 2
 
DIO 3: CPU DIO bank 3
 
DIO 4: FPGA DIO
 
TWI 0: TW_CLK on DIO 24, TW_DAT on DIO 23
 
 
== TS-4500 and TS-75XX ==
Time 0: Linux gettimeofday() based
 
Time 1: FPGA 1us timer based
 
Bus 0: SBus (includes syscon and FPGA DIO registers)
 
Bus 1: read/write memory area for test purposes
 
Bus 2: MuxBus (only valid with OpenCore or other bitstream with MuxBus support)
 
Bus 3: caches FPGA DIO registers
 
Bus 4: CPU registers control pin functions
 
Bus 5: caches CPU DIO registers
 
Bus 6: CPU DIO registers
 
Bus 7: SBus Window Bus (Type 1)
 
Bus 8: SBus Window Bus (Type 2)
 
Pin 0: TS-4500 pin control
 
CAN 0: FPGA CAN implementation (TX on DIO 15, RX on DIO 16)
 
DIORaw 0: FPGA DIO raw access
 
DIORaw 1: CPU DIO bank raw access
 
DIO 0: aggregate DIO access to all banks
 
DIO 1: FPGA DIO
 
DIO 2: CPU DIO
 
TWI 0: TW_CLK on DIO 70, TW_DAT on DIO 69
== TS-4700 ==
Time 0: Linux gettimeofday() based
 
Bus 0: syscon (includes FPGA DIO registers)
 
Bus 1: read/write memory area for test purposes
 
Bus 2: MuxBus
 
Bus 3: CPU DIO registers
 
Bus 4: CAN registers
 
Bus 5: caches FPGA DIO registers
 
Bus 6: caches CPU DIO registers
 
Bus 7: CPU TWI registers
 
Bus 8: CPU MFP (pin function) registers
 
Bus 9: 8-bit bus used by MuxBus
 
Bus 10: 16-bit bus used by MuxBus
 
Pin 0: TS-4700 pin control
 
CAN 0: FPGA CAN implementation (TX on DIO 15, RX on DIO 16)
 
DIORaw 0: FPGA DIO raw access
 
DIORaw 1: CPU DIO bank raw access
 
DIO 0: aggregate DIO access to all banks
 
DIO 1: FPGA DIO
 
DIO 2: CPU DIO
 
TWI 0: TW_CLK on DIO 150, TW_DAT on DIO 149
== TS-4800 ==
Time 0: Linux gettimeofday() based
 
Time 1: FPGA 1us timer based
 
Bus 0: syscon (includes FPGA DIO registers)
 
Bus 1: read/write memory area for test purposes
 
Bus 2: MuxBus
 
Bus 3: CPU DIO bank 1 registers
 
Bus 4: CPU DIO bank 2 registers
 
Bus 5: CPU DIO bank 3 registers
 
Bus 6: CPU DIO bank 4 registers
 
Bus 7: CAN 0 registers
 
Bus 8: CAN 1 registers
 
Bus 9: caches FPGA DIO registers
 
Bus 10: caches CPU DIO bank 1
 
Bus 11: caches CPU DIO bank 2
 
Bus 12: caches CPU DIO bank 3
 
Bus 13: caches CPU DIO bank 4
 
Bus 14: Window bus into CAN 0 registers
 
Bus 15: Window bus into CAN 1 registers
 
Pin 0: TS-4800 pin control
 
CAN 0: FPGA CAN implementation (TX on DIO 47, RX on DIO 45)
 
CAN 1: FPGA CAN implementation (TX on DIO 10, RX on DIO 11)
 
DIORaw 0: FPGA DIO raw access
 
DIORaw 1: CPU DIO bank 1 raw access
 
DIORaw 2: CPU DIO bank 2 raw access
 
DIORaw 3: CPU DIO bank 3 raw access
 
DIORaw 4: CPU DIO bank 4 raw access
 
DIO 0: aggregate DIO access to all banks
 
DIO 1: FPGA DIO
 
DIO 2: CPU DIO bank 1
 
DIO 3: CPU DIO bank 2
 
DIO 4: CPU DIO bank 3
 
DIO 5: CPU DIO bank 4
 
TWI 0: uses Linux /dev/i2c-1 device
 
== Objects ==
{{:tsctl Objects}}

Revision as of 16:29, 25 September 2013

tsctl
Tsctl.png
Files
Latest tsctl sources

Overview

Tsctl was created as a way to ease software development, allow programs developed for one board to be easily re-used on other boards, and provide a way to control boards from any language and operating system that supports TCP/IP. You can also write your program to talk directly to the hardware via libtsctl. Tsctl has a consistent client API across all access methods. Currently it supports programming in C, Python, shell, Javascript, and Java, as well as any other language in which you can write code to talk to a TCP socket. tsctl is object oriented, and currently defines classes for AIO (Analog I/O, i.e. DAC and ADC), Bus (useful for direct register access), CAN, DIO, EDIO (Extended DIO for PWM, Quadrature counting and more), Pin, Time (timekeeping), TWI, and SPI hardware as well as a System class. Tsctl server runs as a single (user-space) multi-threaded server instance on the board to provide access to board features over TCP/IP.

The older canctl, dioctl, and spictl servers are implemented as libtsctl applications to provide the ability to run those applications on any hardware supported by tsctl.

Support

The following boards are currently supported by tsctl:

TS-Socket CPU
TS-4200
TS-4500
TS-4700
TS-4800
TS-Socket Baseboards
TS-8100
TS-8160
TS-8200
TS-8390
TS-8820
TS-8900
TS Single Board Computers
TS-7500
TS-7550
TS-7552
TS-7553
TS-7558
PC-104 Peripheral Boards
TS-RELAY8
TS-DIO24

Design Philosophy

Technologic System develops and manufactures single-board computers. Newly designed boards have features and configuration intended to meet newly emerging needs in the marketplace, as well as to respond to customer feedback concerning what is useful to them. However, over time many boards developed by Technologic Systems share in common certain features such as Ethernet, serial ports, CAN, and so forth. Some of these features are well supported by the Linux operating system which our boards ship with. However some are not, and in other cases the interface to accessing a particular feature has changed over time, sometimes dramatically.

Technologic System products are targeted towards the embedded market. The software development model in this space is often quite different from the rest of the software industry. Embedded devices typically go through a development cycle which terminates in a "frozen" state for all software on the board. Unlike other parts of the industry, upgrades or change of any kind in the state of the software as shipped is undesirable as it represents a high degrees of risk, risk that assumptions other software relies on will no longer be valid resulting in "breakage" of previously working software. As such, it is important that Technologic System provide software for the features on its board which reaches a frozen state quickly, allowing developers using our products to confidently freeze their own software in a timely manner.

At the same time, there are other important, but sometimes conflicting goals. These are:

  1. To bring new products to market quickly.
  2. To provide software for all the features on the board.
  3. To have a uniform interface to each feature allowing code developed for one board to work on another.

To meet these goals, tsctl does the following:

  • defines object classes, which encapsulates a specific piece of TS hardware behind a standard, uniform API across all supported TS products.
  • provides easy extensibility to add new objects and new API calls on existing objects without breaking any existing code.
  • provides a single, centralized, self-contained, backward compatible binary for each platform containing all TS functionality (which conforms to the tsctl spec)
  • utilizes pthreads, which allows for high performance locking of individual resources
  • provides a consistent interface across C, text and binary protocols, and various languages facilitates backward compatibility at the command line and protocol level with canctl, dioctl, and spictl
  • optimizes individual objects for maximum performance where applicable, i.e. CAN which can transmit and receive close to wire speed at 1Mbps via the high performance canctl protocol.
  • provides the new tsctl network protocol which allows all objects to be used through a single port, providing sequentiality and simultaneous access to multiple objects through the same protocol and connection.

Getting Started

The tsctl command line client for direct access

To help you get up and running with Technologic Systems hardware as fast as possible we provide tsctl, a libtsctl client/server application.

First, download the latest pre-compiled tsctl binary for your platform.

For the TS-4500, TS-7552, TS-7553 or TS-7558, download the "cavium" binary here

For the TS-4200, TS-4700, or TS-4800, download the "noncavium" binary here

Install the binary anywhere in your PATH. Before installing, you may wish to make sure you do not already have an older version of tsctl by attempting to run the "tsctl" command. If the command is found, be sure to replace the existing binary with the newly downloaded one in order to ensure you are running the most up to date copy.

Now you can see immediate results. Locate the red and green LEDs on your board. For TS-SOCKET boards these LEDs are usually on the base board (e.g. the TS-8XXX series) while TS-75XX boards have the LEDs directly on the board. Type the following commands:

tsctl DIO SetAsync GREEN_LED LOW
tsctl DIO SetAsync RED_LED LOW
tsctl DIO SetAsync GREEN_LED HIGH
tsctl DIO SetAsync RED_LED HIGH

You should see the green and red LEDs go off and on. (If they are already off then the first two commands will have no effect.)

Now let's talk about how the above commands work. Unlike many Linux utilities, tsctl provides direct access to a number of different functions across several classes using a "generic to specific" way of invoking the functionality you want.

First, you specify the host that you want to invoke the functionality on. This part is optional. The host is specified as the @ symbol followed by an IP address or host name, and indicates that the TCP/IP protocol is to be used. If no host is specified (as in the examples above), localhost is used if the tsctl server is running on the board, otherwise direct access is used.

Second, you specify what class of functionality you want to use. The available classes, in alphabetical order, are AIO, Bus, CAN, DIO, DIORaw, EDIO, Mode, Pin, SPI, System, Time, and TWI. If you specify "?" instead of a class, the available classes will be shown.

Third, you specify which function in the given class you want to invoke. If you specify "?" the list of functions available for that class will be shown. Above, we specified the SetAsync function.

Fourth, you specify the parameters for the function. If you specify "?" the expected parameters will be shown. The format of any parameter is quite flexible: you can enter a numeric value in octal, decimal, hexadecimal, or using a special string name which has a mapping to a value in the System lookup table, which we will discuss later. The SetAsync function takes two parameters: the DIO number and the state to set the DIO to. We used a special string for each: GREEN_LED, for example, having an entry in the mapping that corresponds to the DIO number for the GREEN_LED, and LOW/HIGH being enumerated values for 0 and 1, respectively.

Note that the SetAsync function does not returned any data, so the above commands produce no output. For our next example, let's use the GetAsync function to obtain the current state of the green LED pin:

tsctl DIO GetAsync GREEN_LED

When you run the above command, what output will you get? The answer to this is that it depends on the current output mode. The default mode is to output a name=value pair for each function, where the name is the name of the function you invoked, with an underscore and a number after it, and where the value is formatted in hexadecimal for integers and as an escaped, quoted string for arrays of 8-bit integers, and colon separated hexadecimal integers for all other arrays. If the output is an enumerated value, the default is to print the string corresponding to the enum value if the enum is not a flags enum, and otherwise to print the string value for each flag set separated by a plus symbol. The output mode can be changed by calling functions in the Mode class.

In the case of GetAsync, the value returned is an ordinary (non-flags) enumerated value, which can be one of the following: HIGH,LOW, INPUT, INPUT_LOW, and INPUT_HIGH. So in the following example we see the default output if the green LED was on:

$ tsctl DIO GetAsync GREEN_LED
GetAsync_0=HIGH

You might be wondering why there is an underscore and a number after the function name in the output. The reason is that tsctl allows you to issue multiple commands in a single invocation of tsctl. Each output has a unique name (accomplished by appending a different number for each command) in order to allow the results to be read by a shell script and assigned to shell variables. There are two schemes that can be used. By default (for backward compatibility), each function has its own counter, starting at 0, and incrementing by 1 each time it is called. The second method that can be used is (for slightly better performance) to use a single counter for all functions, so that this counter will increment once each time any command is called.

There are several ways that you can issue multiple commands with only a single invocation of tsctl. The first is to string together multiple tsctl commands by separating each one with a semi-colon. Since most shells use the semi-colon to separate shell commands, usually you will need to quote the semi-colon. So for example you could get the value of both the red and green LED with only a single invocation of tsctl as follows:

tsctl DIO GetAsync GREEN_LED \; DIO GetAsync RED_LED

Depending on your shell, the following should also work:

tsctl "DIO GetAsync GREEN_LED;DIO GetAsync RED_LED"

Note that no space is necessary on either sides of the semi-colon. The only white-space required is to separate each argument, and this white-space can be in any amount. The only thing to be aware of is that the newline character both terminates a command and triggers an invocation of all commands on the current line, so it is a bit more efficient to separate commands with semi-colons rather than white space unless you need a given output before issuing the next command.

If the red LED is on and the green LED is off, the default output of either of the above commands will look like this:

GetAsync_0=LOW
GetAsync_1=HIGH

Another way to issue multiple commands to a single invocation of tsctl is by reading the commands directly from the standard input, or from a file.

To read commands directly from the standard input until end of file (e.g. CTRL+D is pressed) invoke tsctl as follows:

tsctl --

To read commands directly from a file, invoke tsctl as follows:

tsctl -f filename

The format of each command when reading from standard input or a file is identical to that of the command line. Every command always is of the form:

@host class command arguments...

where @host is optional.

The Command Stack

Sometimes you may want to issue several commands, and the first part of each command will be the same. As a simple example, consider reading the example of reading the red and green LEDs given above. If read from a file, these commands would look like this:

DIO GetAsync GREEN_LED
DIO GetAsync RED_LED

Each command starts with a common prefix, namely DIO GetAsync. To reduce the amount of typing (and length of the command) needed, tsctl provides a "command stack".

The tsctl command stack can be thought of as follows. When you specify a command, it consists of several words, separated by whitespace, starting with the (optional) host name and continuing until all the arguments have been specified. Conceptually, as each word is read, it is pushed onto a stack. Once a complete command is on the stack, it is executed. However, if the current command terminates without a complete command being present, then the current command stack is then saved as the context for future commands. Each future command will then be read with this context, this pre-initialized stack. Each time an empty command is encountered, the last element on the stack - if present - is popped off.

In the above example the result would look like this:

DIO GetAsync
GREEN_LED
RED_LED

The first line effectively selects the DIO class and the GetAsync function. Subsequent commands then operate in this context and only need to specify the arugments to the GetAsync function. If we wanted to use another DIO function we would enter a blank line to pop the "GetAsync" function, and if we were done with DIO we could enter another blank line to pop the "DIO" class from the stack. Any extra blank lines would be ignored.

If you wanted to issue both commands in the same server invocation you could simply the above set of commands to the following:

DIO GetAsync;GREEN_LED;RED_LED

This can be a very efficient method for calling the same function repeatedly with the same arguments. For instance, suppose you wanted to read the SYSCON registers at offset 0,2,4,6,8,and 10 of Bus 0, and then call System function ModelId you could do so as follows:

Bus Peek16;0;2;4;6;8;10;;;System Model

The empty commands are used to pop the command stack, two pops are necessary to remove first the "Peek16" functiom, and then the "Bus" class, so that the System class can be used.

Due to the command stack, it is not considered an error if there is a partial command left when tsctl exits. It is important to remember this to avoid confusion in case you accidentally specify a partial command. For instance, the following command will generate no output and no error, because it does not do anything.

tsctl Bus Peek16


The last way to issue multiple commands with a single invocation of tsctl is to start the tsctl shell, as follows:

tsctl

There are a few differences between running the tsctl shell versus providing commands directly from the command line or from a file. The readline library is used to allow command editing and history. Starting with version 0.91, output in shell mode is in decimal (Mode Dec) and the values from each command are printed without an an assigment (Mode NoAssign).

This will print a startup message and then give the tsctl shell prompt, "tsctl> ". To supress the startup message use the "--quiet" option:

tsctl --quiet

Note: UNIMPLEMENTED

The tsctl command shell displays "tsctl" plus the current command stack (if any) before the prompt. The above example of using the command stack has been extended (to show popping the stack) in the tsctl shell.

$ tsctl
tsctl 0.91-ts (Nov 30 2012 22:06:57)  
Type "?" to get context-sensitive help.
tsctl> DIO
tsctl DIO> GetAsync
tsctl DIO GetAsync> GREEN_LED
HIGH
tsctl DIO GetAsync> RED_LED
LOW
tsctl DIO GetAsync>
tsctl DIO>
tsctl>

You can also use semi-colons to put multiple commands or parts of a command on a single line.

To exit the tsctl shell, press CTRL+D, or enter the word "end" by itself on the current line with no extra whitespace.

You can control the format of the output of each command using the Mode class. The output mode is divided into two parts: the encoding and the base.

The output encoding is one of the following: Raw, Newline, Assign, and JSON.

Raw encoding outputs each value returned by the command in the current base, one after the other with no other characters in between each value. It is mainly intended for base -1 and base 0, as with all other bases it would be difficult or impossible to determine where one value ended and the next started.
Newline encoding outputs each value returned by the command in the current base, with a new-line character after each value.
Assign encoding outputs a "name=value" pair for each value returned by the command, where value is in the current base, and name is the name of the function, with an underscore and numeric count appended.
JSON encoding wraps each output in a "name":value format, and separates each element with a comma as well as wrapping arrays in brackets and the entire reply in braces.

Output base is one of the following: -1 (escaped binary), 0 (binary), 2 (ASCII binary), 8 (octal), 10 (decimal), or 16 (hexadecimal). Separate bases are active for character strings and other numbers. Note that for ASCII base output (2 and above) there are no identifying characters to tell what base it is. For instance, there is no "0x" preceding the hexadecimal value in base 16 mode.

Base 0 is the raw internal, little-endian "binary" representation of the value. Although it is the actual binary format of the value this should not be confused with base 2, which is the ASCII representation of the binary value, which consists of only '0' and '1' characters.
Base -1 is the same as Base 0, except that only 8-bit values in the range of 32 and 126 (excluding 92, which is the ASCII backslash character) are output as-is. All other values are output as a backslash followed by three octal digits representing the ASCII value of the byte.

In addition, there input modes, and is divided into two parts: the encoding and the base.

The input encoding is one of the following: HTTP and Command.

Command encoding indicates that the server immediately processes input as commands. This is the default and only mode available in client mode.
HTTP encoding is only available through the TCP port. In this mode, an HTTP POST header is assumed to occur before the start of any commands. Two empty newlines terminate the header and put the connection back into Command mode.

The input base is either Binary or Text.

The tsctl client, whether invoked as a command-line sequence of commands, as the tsctl client, or reading commands from the standard input or a file, defaults to input encoding Command, input base Text, output encoding Assign, output base 16.

You can find more information in the tsctl "Getting Started" guide for the hardware you are using.

The tsctl command line client for remote access

The previous section alluded to being able to specify an @host parameter to tsctl commands. In order for this to work, it is first necessary to run the tsctl server on the board you want to control. The tsctl server listens for connections on various TCP ports, providing the ability to control the board remotely. Client code was be written in C, HTML/Javascript, Python, Javascript, or any other language that can talk to a TCP/IP socket or which can generate an HTTP POST. However, to start with, we will use the tsctl client itself.

First, start the tsctl server on the board you wish to control:

tsctl --server
Note: If you link against uclibc, the server must be manually backgrounded due to issues with the daemon() call interacting with pthreads in uclibc.

Once the server is started, you can now invoke tsctl functionality remotely, by adding the @host parameter to any tsctl command. If you are running the tsctl client on the same board as the server, communication will still be over TCP/IP by specifying localhost (or 127.0.0.1) as the host. If you do not specify a host and the server is running, localhost will be used automatically. By using the client/server access mechanism rather than direct access you gain the added flexibility of being able to simultaneously control the board from multiple processes or machines, or to control multiple boards from the same client.

Let's look at our previous example of controlling the LEDs, this time done using the client/server approach:

$ tsctl
tsctl> @localhost DIO
tsctl @localhost DIO> SetAsync GREEN_LED LOW
tsctl @localhost DIO> SetAsync GREEN_LED HIGH

The only difference from the previous usage is that we have added the host name before the class. The tsctl program distinguishes between whether the first argument is a host or a class by case. All classes start with an upper-case letter, so if the first argument starts with an upper-case letter it is assumed to be a class. Therefore, you must specify the host using either a numeric IP address or with a host name that starts with a lower-case letter. Host names are usually specified using lower case anyway but since they are case insensitive all host names can be distinguished from class names by tsctl.

When the tsctl sever has started, it listens on two TCP ports: 5002 and 8000. Port 5002 is intended for use by the tsctl and other clients, and defaults to input encoding Command, input base Binary, output encoding Raw, output base 0. Port 8000 is intended for use by HTML/Javascript clients, and defaults to input encoding HTTP, input base Text, output encoding JSON, output base 10.

Internally, tsctl converts all (available) commands to a binary encoding before executing them. This provides for a uniform consistency in execution time between text and binary mode, as all the overhead of parsing the text command occurs before execution starts. It simplifies server design, as a single interpreter of the binary protocol is sufficient in all cases. Likewise the data returned from executing commands is in binary format, which is then rendered in the current output mode. However, since the reply data does not come back all at once in some cases (depending on the command, loading, network latency and other condition), reply data is collected as much as possible at once and then rendered as output before repeating the process until all reply data has been output. This provides for both responsive output even when certain commands (such as Time delays) produce noticable delays before giving output, while providing for maximal uniformity and performance similar to the request.

The separate internal conversion process also provides for the ability to pre-compile tsctl text commands to binary format ahead of time. This makes possible the ease of writing readable text commands with the greater performance of issuing commands in binary format. To this end, these are several command-line options provided for compiling and using pre-compiled commands:

--compile reads all commands provided and then produces a binary output containing two sections. The first contains the binary format the for requests and the second section containing the look-up table. Support is planned for outputting these sections in a format suitable for reading in subsequent invocations of tsctl, or for putting in your own C, Python, Javascript, etc. program.
--binary skips reading commands and instead reads the output of --compile as its input. It then performs the look-up specified in the look-up table, and then directly interprets the binary commands to produce output.
--skip-lookup is only used in conjunction with --binary, and causes the look-up step of that option to not be performed.
--lookup-only is used only in conjunction with --binary. It only performs only the look-up stage, and then outputs the resulting binary protocol for subsequent use.
--raw-output skips the output rendering. The raw binary reply data from the server is output. The result is the same as if raw binary output mode is explicitly specified in the request for the entire duration of the requests.
--raw-untagged disables output tagging. Implies and automatically enables the --raw-output option, as the normal output stages rely on tagging to render output.

Note: UNIMPLEMENTED

Using the tsctl server from HTML/Javascript

The tsctl server (tsctl --server) has built in support for HTML/Javascript access by acting in a very limited capacity as a web server on port 8000 on the board the server is started on. It does so by accepting (and ignoring) any and all HTTP headers sent, which are terminated by two empty new-lines. Then follows the actual data, which it interprets in the same manner as from the tsctl command line or from a file. The main difference between the tsctl web server and the command-line client is that the former defaults to "Mode JSON" while the latter defaults to "Mode Assign" or (for the tsctl shell) to "Mode NoAssign".

Note: The tsctl web server returns the header "Access-Control-Allow-Origin: *". This allows you to create a single HTML page that can control multiple devices.

The first step to using the tsctl with HTML and Javascript is to create the interface in HTML. This can consist of form elements or any other interface mechanism available to the web browser(s) you are creating your page for. As a simple example, suppose you want to create a web page to display the current A/D value on the board. You might create the following markup in your HTML page:

ADC value = <span id='adc1'></span>V

The creates an empty span with an id we can use to fill in the value later. Next, you will need to write some javascript code to talk to the tsctl server to fetch the A/D value.

First, here is a Javascript function that will create an XMLHttpRequest for talking to the server, then make the request, and when the reply has been received it will call the callback passed.

function AJaXRequest(url,parms,success,failure) {
    var ob;

    var callback = function() {
        if (this.readyState == 4) {
            if (typeof success == 'function') success(this.responseText)
        }
    }
    if (window.XMLHttpRequest) {
        ob=new XMLHttpRequest()
    } else if (window.ActiveXObject) {
        ob=new ActiveXObject("Microsoft.XMLHTTP")
    }
    if (ob) {
        ob.onreadystatechange=callback;
        ob.open("POST",url,(callback != null));
        ob.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
        try {
            ob.send(parms)
        } catch (e) {
            if (typeof failure == 'function') failure(e)
        }
    }
    return ob;
}

If you are already using your own javascript library such as jQuery, you may already have such a function available to you.

Next, here is another function using the above definition that will send the tsctl command to get ADC channel 1 and then put the results in the 'adc1' span created above, assuming a 10-bit A/D with a 0-10V range:

function getADC1(server) {
   AJaXRequest(server,"AIO Get 1\nend\n", function(reply) {
       var ret = JSON.parse(reply)
       document.getElementById('adc1').innerHTML = Math.round(ret["AIO_Get_0"] * 10000 / 32768)/1000
   })
}

The server parameter is the URL of the tsctl server, such as "http://192.168.0.50:8000". Note that the actual command to be sent has newlines quoted ("\n"), and ends with the "end" command. This is a special command that causes the tsctl server to immediately terminate the connection. Without this command the browser may hang until a timeout occurs to close the connection.

The data returned by the server uses the same name/value pairing that the command line does. Therefore if, for example, a second "AIO Get" command was issued in the above request, the reply would be in the ret["AIO_Get_1"] field of the reply.

Using the tsctl server from Python

Coming Soon!

Using the tsctl server from Java

Coming Soon!

Writing your own libtsctl direct access application in C

While other sections cover how to get started controlling our boards without needing to write any code (using the tsctl command line), you may wish to develop your own application to directly access the Technologic Systems hardware. This is the superior solution if you need high performance and do not need the ability to control the board functions remotely. Note that libtsctl is designed to work best when only a single application at a time is using it. If multiple applications are linked against libtsctl with the idea of being run simultaneously, special care is needed to be taken to prevent them from "stepping on each other's toes".

Direct access applications can be written in any language that has the capability to interface with C. We provide an (unsupported) SWIG example to demonstrate how it is possible to do this from Python. However in this section we will discuss writing directly in C.

To begin, first get the latest source code from GitHub: https://github.com/embeddedTS/libtsctl

The top-most directory of the project contains the Makefile and the source code to the sample apps. The ts directory contains the libtsctl source code. The dioctl.config directory contains the config files for all the architecture, and a script to create the compressed version of these for inclusion into libtsctl - note that this is already done so unless you want to modify an existing config you will not need to touch this directory.

Once you have the source code on your system there are two approaches to adding libtsctl to your application:

1. Adding your application to the libtsctl directory

This is probably the simplest approach, especially if your application consists of only a single source code file. All of the sample applications included with libtsctl use this approach. Your application will need to include "libtsctl.h", and you will need to add a couple of entries to the supplied Makefile, one to direct builds of your application to the proper sub-directory, and the other to list the dependencies for your project. The easiest way to do this is probably to copy/paste/modify an existing entry for one of the sample apps provided. For example:

MapDump: $(DIR)/MapDump
       @true

$(DIR)/MapDump: $(addprefix $(DIR)/,MapDump.o Arch.o NoThread.o $(ARCHLIBS) libtsctl.a) -lbz2 

In the above example, you would replace "MapDump" with the name of your application source file - if there are multiple source files you would replace MapDump.o with an object file for each source file. If you need pthreads you would change NoThread.o to PThread.o and add -lpthread to the end. You will need pthreads if you are running the CAN server in your application, even if your own app does not use pthreads.


2. Linking libtsctl in your own project directory

This is the ideal choice if you already have a build process in place for your application and simply want to drop in libtsctl support.

The first step is to build all the libraries (e.g. "make libtsctl.a") and then copy libtsctl.a and libtsctl-export.h (renaming it libtsctl.h) to somewhere that your project build process expects them, and the library for each architecture you want to support. For instance, if you have a TS-4200 with a TS-8100 base board you would need libtsctl.a, libts4200.a and libts81x0.a. Then, include libtsctl.h anywhere you need it, and link against the .a files. Note that because of circular dependencies between libtsctl.a and the architecture support libraries you will either need to link against libtsctl twice (once at the beginning and once at the end) or wrap all the libraries with -Wl,--start-group" and "-Wl,--end-group" in order to avoid undefined symbol errors, e.g.

-ltsctl -lts4200 -lts81x0 -ltsctl

or

-Wl,--start-group -ltsctl -lts4200 -lts81x0 -Wl,--end-group

Note that in some cases, in addition to libtsctl.h you may also need to copy and include "Array.h" (if any libtsctl functions used take or return an "Array", which by convention is denoted by the "*" in the pointer being bound to the type e.g. with no spaces.

Using the tsctl server from C

Writing a C application to talk to a tsctl server uses much of the same API as a direct access client application. The main difference is that instead of including "libtsctl.h", you will instead include "NetTsctl.h", which is a short-cut to include all of the Net class files. (The file "NetTsctl-export.h" in the net/ directory embeds everything into a single file so you can easily import it into your own project.) Then, instead of calling the class Init functions such as DIOInit, you will first call TsctlClient for each tsctl server you wish to connect to, following by the Net Init function (such as NetDIOInit) for each class on that server. Unlike the direct access counterparts, Net classes can throw exceptions which should be caught as shown in the next section.

When you write code to use the libnettsctl API your code does not need to run on a Technologic Systems board, as the API operates entirely over TCP/IP and does not depend on any direct hardware access.

The TsctlClient function takes two arguments: the host name of the tsctl server (such as "127.0.0.1", or "localhost"), and the tsctl Net Mode. The latter is one of the following:

  • NetModeBlocking
In this mode, each call to a tsctl class function will block until the server returns completion.
  • NetModeNonBlocking
In this mode, functions which return void and also return no data (i.e. pass no non-const pointers or arrays) will return immediately, without waiting for the server to reply. This can improve client performance by increasing the rate at which commands can be sent, while still invoking the functionality without delay.
  • NetModeQueued
In this mode, functions which return void and also return no data (i.e. pass no non-const pointers or arrays) will queue the command for transmission to the server and return immediately. No commands will be sent until either a function is called which does return data, or else the transmission buffer is filled up. The default buffer size is 1436 bytes, but this can be changed by editing Net2.c.
This mode is useful for cases where overhead to the server should be minimized (by not sending each command in a separate TCP packet), or where several commands must be sent to the server all at once. For instance, using NetModeQueued, it is possible to send a command to turn off a relay, wait for the relay to actually physically turn off, and then turn it on again, even if the relay controls the power to the machine issuing the command. In other modes this would not work, because as soon as the command to turn off the relay was sent there would be a race between the board being shut down and the next command being sent, and even if the machine was able to send the delay command, it would never be able to send the command to turn the relay back on.

The tsctl TCP Net classes generate exceptions in case of network errors, such as an unexpectedly closed connection or the server no longer being reachable. Exceptions are implemented using the setjmp/longjmp calls. The tsctl connection struct returned by TsctlClient contains the space to hold the exception variable used by setjmp. A reasonable approach would be to wrap a sequence of calls to tsctl Net classes in a setjmp condition as follows:

   tsctl *conn;
   ...
   conn = TsctlClient(host,mode);
   ...
   if (!setjmp(conn->exception)) {
     // make API calls here
   } else {
     // handle exception here
   }


A couple of sample programs are included which demonstrate how to create an application to talk to the tsctl server. NetTest2 sends several commands to determine the round trip latency for sending commands. NetTest adds the functionality of printing the Model Id, Base Board Id, and then toggling the red LED on the board 100 times. After this, it does a Map test.

For use in your own projects, simply copy net/NetTsctl-export.h into your own project, renaming it to NetTsctl.h. Then, build libnettsctl.a (e.g. "make libnettsctl.a") and import it into your project.

Concepts

Objects

Tsctl uses objects to perform most important functionality. These objects are divided into classes which correspond to various resources available on the board: AIO, Bus, CAN, DIORaw, DIO, EDIO, Pin, System, Time, TWI, and SPI. These objects are implement in C using a structure which contains a pointer to the API for the class. To use an object, it is first necessary to call its initialization function to get a pointer to the object. Objects are referenced by initialization by number. Some numbers for a given class are standard across all hardware, while others are specified to a given board.

Locking

libtsctl is design to make it possible to share resources. It does this by allowing resources to be locked. A lock can be a shared lock, such as might be appropriate for reading a value, or it can be an exclusive lock, which would be appropriate for changing a value.

All resources that provide an interface to lock and unlock them must be locked before use and unlocked after use. This is because in some cases the locking mechanism, in addition to acquiring shared or exclusive access to the resource, also performs per access initialization. For instance, multi-function pins are automatically set to the correct function during locking.

From the perspective of a client there are object locks, which in the server ultimately result in one or more system locks being held, under control of the object being locked and any sub-objects it contains. All tsctl objects which provide a Lock function offer the client to hold an object lock on that object. Each class defines the circumstances under which the client must hold an object lock. A client should not hold an object lock any longer than necessary.

  • Bus: the Bus lock must be held before while other functions in the Bus are called.
  • Time: does not use locks
  • Pin: the lock for a given Pin must be held while ModeSet is called on that pin
  • DIORaw: the lock for a given DIO must be held while any function is called for it
  • DIO: the lock for a given DIO must be held while any function is called for it
  • TWI: the TWI lock must be held before other functions in TWI are called.
  • CAN does not use locks
  • SPI: the SPI lock must be held before other functions in TWI are called.

The Lock function has several flags. (TO DO)

A connection may hold the same lock more than once, a so-called "recursive" lock. Each lock must be released the same number of times as it is acquired before the connection truly has released the lock. In the server, the connection either holds a system lock or it doesn't; the server keeps track of the system lock count and only acquires the system lock if the count is being incremented from zero, and only releases it if the count is being decremented to zero. (UNIMPLEMENTED)

The server automatically releases all locks held by a connection when it detects that the connection has closed and has read all pending opcodes from the connection.

While a connection can hold multiple locks at once, they must be acquired one at a time.

A server object may acquire additional locks during the course of calling other functions in it. If such a call would cause a deadlock an exception will be generated on the connection, which will close the connection. (UNIMPLEMENTED)

dioctl.config

The libtsctl library contains a mechanism to map arbitrary strings to numeric values. The naming of this file is historical, deriving from the original dioctl program. The primary function of this file is to map DIO names to logical DIO numbers for convenience and cross-platform usage. For instance, the name "GREEN_LED" is defined to refer to the primary green on many Technologic Systems boards. In addition, this file is used to define DIO attributes, pin mappings, and even wire routing for loopback testing.

The dioctl.config file must be installed in the /etc directory. The source tarball contains all available config files in libtsctl-src/config. Each individual architecture has its own dioctl.config file. In the source directory, the arch name is prepended to ".dioctl.config" to distinguish config files.For example ts4200-ts81x0.dioctl.config is the dioctl.config file for arch ts4200-ts81x0. These names are to identify the individual files to the user - when a config file is installed it must be renamed to dioctl.config and it must be in the /etc directory on the board from the viewpoint of the running program.

Note: The "logical DIO number" referenced in this section refers to the DIO number for DIO instance 0, which is the "Aggregate" DIO object combining all DIOs individual DIO banks into a single linear set of logical pins. This also corresponds to the pin numbers taken by functions of the Pin object.

The dioctl.config file contains one assignment per line of text. Each assignments is one of the following:

# Comment

All lines which begin with the "#" character are comments and are ignored. Config files with multiple sub-architectures typically separate each sub-architecture with a comment indicating the arch to follow.

NAME=#

The specific name maps to the given number. In some cases the name corresponds to a human readable term like "GREEN_LED" while in others it corresponds to a DIO or pad name on a chip (e.g. "PA6"). All text lines which do not conform to subsequent forms must fall into this category.
If the specified # is not a number, it is treated as a previously defined name. This allows for aliases to connector pins, which have different logical DIO based on the TS-SOCKET board in use, but which have a common name on the base board.

attrib.Connector.Name.CNAME=#

This defines the name of a connector. The string CNAME is the name of the connector, and the number (#) is the connector number. A TS-SOCKET board has two connectors, named CN1_ and CN2_ per the schematic. Additional architectures tack additional connectors on. Comments in the config file indicate when definitions for a new sub-architecture begins.

attrib.Connector.x.y=#

This defines the mapping between a connector pin and the logical DIO number. The x and y parts of the line are numbers indicating the connector number and connector pin, respectively.
The tsctl library (specifically the System object, through which the map is exposed) automatically creates NAME=# mappings for each connector definition. The name is the connector name followed by the pin number, and the # is the same as given in the connector. So for example, if connector 1 is "CN1_" then pin 5 on that connector would be named "CN1_5".

attrib.Connector.#.Pins=n

This is an informational attribute which says that the given connector # has n pins. Not every pin needs to correspond to a logical DIO.

attrib.Connector.Count=n

This is an informational attribute which says that there are n connectors defined.


The following attributes are used for DIO testing. Wires are defines as sets of one or more pins that are connected together. On some baseboards connect two or more pins together so they are electrically a short. These are used for testing DIOs by setting all the DIOs on the wire except one to input and verifying that each DIO can drive and the other DIOs correctly read the value driven. In some cases, such as when no base board is present, a special harness must be made which uses jumpers or physical wires between header pins. The ID field is the model number of the base board, if present, or the CPU board otherwise. For example, "8200" for the TS-8200, or "7552" for the TS-7552.

attrib.ID.Wire.MaxConnections=n

This attribute is used for DIO testing. Wires are defines as sets of one or more pins that are connected together. The n value is a number indicating the maximum number of connections to any wire defined.

attrib.ID.Wire.Count=n

This entry indicates that there are a total of n wires defined.

attrib.ID.Wire.Connector.x.y=n

This entry defines connection y of wire x to be connected to connector n.

attrib.ID.Wire.Pin.x.y=#

This entry defines connection y of wire x to be connected to pin n on the connector defined by the attribute above.

attrib.ID.Wire.Drive.x.y=#

This entry defines connection y of wire x to be connected in such a way that its drive capabilities are specified by #. This is used when DIOs are externally limited - for instance, a DIO might be connected on a base board through a buffer. In this case, even though the DIO on the CPU board can function as both an input and an output, the test harness limits it to an output. The entry is used so that the DIO test knows that it should not expect the DIO to work as an input, even though it will report that is has that capability.
The # value must be one of the following (from DIO.h):
1 - Input Only
2 - Can only Drive High
4 - Can only Drive Low
6 - Output Only (Can only Drive High or Low)
7 - No restrictions (as this is the default, normally this value is omitted)

Threads

Tsctl Threads

tsctl C API

tsctl C API

tsctl TCP protocol

tsctl TCP API

tsctl command line reference

tsctl command line reference

Hardware Map

tsctl Hardware Map