Skip to main content

File system functions

This chapter is dedicated to all file system related features, that is, functions that allows a script to access files on the host computer either for writing or reading of data. Please note for security reason, a script cannot read or write to/from any file by default: Only a file path that have been specified by the user in the GUI using file system GUI items can be accessed. For instance, to be able to access a file from a script, there must be one of those GUI elements:

ScanaStudio.gui_add_file_save(id,...)
ScanaStudio.gui_add_file_load(id,...)
ScanaStudio.gui_add_directory_path(id,...)

Please refer to the GUI functions chapter for more details about the usage of those functions. It's worth reminding though that the id parameter of this function is what is needed to access the file set by the user, as it will be explained in details in this chapter. (This confirms that at no time, the scripts gets to know anything about the host's file system or the actual path to the file).

This is enforced all the way down: calling ScanaStudio.gui_get_value() on one of those GUI elements returns the element's own id, not the path the user chose. So both of the lines below are equivalent, and both are correct:

var file = ScanaStudio.file_system_open("my_file_id","r");
var file = ScanaStudio.file_system_open(ScanaStudio.gui_get_value("my_file_id"),"r");

The rare script that genuinely needs a path of its own — a logger writing to a fixed location, or one composing a file name under a directory the user picked — can be allowed to name one. That is a per-script permission the user grants, and it is described in Direct file system access at the end of this chapter.

To access a file for reading, first, you need to add a GUI elements in a relevant GUI (like the signal builder GUI). Below is an example:

ScanaStudio.gui_add_file_load("my_file_id", "Select CSV file", "*.csv");

which should create a GUI element that looks like this:

script-import-csv-file

Please note that the text string "my_file_id" must be a unique ID in your script's GUI, and is used to open a file.

ScanaStudio.file_system_open("file_id","mode");​

Description: This function open a file before being able to read or write to it.

Parameters:

  • "file_id": the unique text string ID of the GUI elements used to define the file path. When the script has been granted direct file system access, anything that is not an id of one of its own file GUI elements is used as a path instead.
  • "mode": a single character text string that defines the access mode, according the the table below
ModeDescription
"r"Read only
"w"Write only (Deletes existing file content before writing)
"a"Append (Preserves existing file content)

Return value: In case of error (unable to open the file) this function returns -1. otherwise, this function returns the file handle that can later be used for writing and reading.

-1 is also what you get when the user left the file field empty, which is the usual way to let a script offer an optional file: simply test the handle, and skip writing when it is negative. Passing anything that is not the id of a file GUI element of your script (an arbitrary path, for instance) is refused the same way, with an error message in the console — unless the script has been granted direct file system access, in which case that argument is used as a path.

Context: Global context

ScanaStudio.file_system_close(file_handle)​

Description: This function is used to close a file.

Parameters:

  • file_handle: The handle that was returned by the function ScanaStudio.file_system_open().

Return value: None.

Context: Global context

ScanaStudio.file_system_read_binary(file_handle)​

Description: This function read the content of a file and return the content in binary format

Parameters:

  • file_handle: The handle that was returned by the function ScanaStudio.file_system_open().

Return value: Returns an array of integers between 0 and 255, each integer representing one byte of the file. Reading resumes where the previous call stopped, and an empty array means the end of the file was reached.

Context: Global context

ScanaStudio.file_system_read_text(file_handle,"encoding")​

Description: This function is used to read the content of a file in text format.

Parameters:

  • file_handle: The handle that was returned by the function ScanaStudio.file_system_open().

  • "encoding": A text string that defines the encoding used to read the file in text format, e.g. "UTF-16". The supported encodings are:

    • UTF-8 (this is what virtually every script uses)
    • UTF-16, UTF-16LE and UTF-16BE
    • ISO 8859-1, also spelled Latin-1

    Any other name falls back to UTF-8, with a warning in the console. Dashes, underscores and letter case are ignored, so "UTF-8", "utf8" and "utf_8" are the same thing. No byte order mark is written; one found at the start of a file being read is honoured.

Return value: Returns a text string containing the whole remaining content of the file. Note this function is not line based: it reads everything at once, so a second call on the same handle returns an empty string. Scripts usually split the result themselves, e.g. with data.match(/[^\r\n]+/g).

Context: Global context

ScanaStudio.file_system_write_binary(file_handle,data_array)​

Description: This function writes binary data to a file (i.e. an array of bytes)

Parameters:

  • file_handle: the handle that was returned by the function ScanaStudio.file_system_open().
  • data_array: An array containing the bytes that should be written to the file.

Return value: None.

Context: Global context

ScanaStudio.file_system_write_text(file_handle, "text", "encoding")​

Description: This function writes text to a file.

Parameters:

  • file_handle: the handle that was returned by the function ScanaStudio.file_system_open().

  • "text": the text to be written to the file

  • "encoding": A text string that defines the encoding used to write to the file in text format, e.g. "UTF-16". The supported encodings are:

    • UTF-8 (this is what virtually every script uses)
    • UTF-16, UTF-16LE and UTF-16BE
    • ISO 8859-1, also spelled Latin-1

    Any other name falls back to UTF-8, with a warning in the console. Dashes, underscores and letter case are ignored, so "UTF-8", "utf8" and "utf_8" are the same thing. No byte order mark is written; one found at the start of a file being read is honoured.

Return value: None.

Context: Global context

Direct file system access​

Everything above describes what a script can do out of the box. A few scripts cannot live inside it: one that logs to a fixed location, one that writes a file per capture under a directory the user picked, one ported from a set-up where the path was simply hard-coded. For those, ScanaStudio can lift the restriction for that one script.

Open the Script manager, select the script, and tick Enable direct file system access for this script in the panel on the right. It is off for every script, and stays off until someone ticks it.

⚠️ A granted script can read and write anything the account running the server can. Grant it only to a script you trust, and only when it asks for it.

Two things change for a granted script, and nothing else does:

  • ScanaStudio.file_system_open() still tries its argument as the id of one of the script's own file GUI elements first. Only when this GUI has no such element is the argument used as a path. So a script that passes an id keeps working exactly as before, granted or not.
  • ScanaStudio.gui_get_value() on a gui_add_file_save, gui_add_file_load or gui_add_directory_path element returns the path the user chose rather than the element's id. That is what makes a directory element usable, since a file name can then be composed under it:
var dir = ScanaStudio.gui_get_value("out_dir"); // "/home/me/logs"
var file = ScanaStudio.file_system_open(dir + "/capture_" + n + ".csv", "w");

A minimal example​

The whole mechanism in one script. Install it, run it once with the tick box off and once with it on, and watch the console:

/* Protocol meta info:
<NAME> Direct Access Demo </NAME>
<DESCRIPTION> Shows what "Enable direct file system access for this script" changes. </DESCRIPTION>
<VERSION> 0.1 </VERSION>
<AUTHOR_NAME> Ikalogic </AUTHOR_NAME>
*/

function on_draw_gui_decoder()
{
ScanaStudio.gui_add_directory_path("out_dir", "Output folder");
}

function on_decode_signals(resume)
{
if (resume) return;

// Without the permission this is the string "out_dir"; with it, the folder
// the user picked. That is the whole difference.
var dir = ScanaStudio.gui_get_value("out_dir");
ScanaStudio.console_info_msg("out_dir reads back as: " + dir);

// Composing a name only works on a real path, so this needs the permission.
var h = ScanaStudio.file_system_open(dir + "/demo.txt", "w");
if (h < 0) return; // the console says why

ScanaStudio.file_system_write_text(h, "written under " + dir + "\n", "UTF-8");
ScanaStudio.file_system_close(h);
}

Off, the console shows out_dir reads back as: out_dir, and the open is refused with a line naming the tick box. On, it shows the real folder and demo.txt appears in it.

A relative path is resolved against the working directory of the server process, which is rarely somewhere obvious once ScanaStudio is installed. Give an absolute path, or build one from a directory the user picked.

The permission is attached to the script's file name. Renaming the script loses it, and a copy made with Edit a copy… starts without it — in both cases the tick box simply comes back empty, and can be ticked again. Updating the script from the online catalogue keeps it, since the file name does not change.

Full example​

A good example that demonstrates the file system features is the CSV import script, which takes a CSV file containing some samples, and builds logic signals from those samples. Those samples can be used to generate signals using a compatible signal generator (like the SQ series logic analyzer devices). Below is simplified version of the script:


var ENCODING = "UTF-8";

//Signal builder GUI
function on_draw_gui_signal_builder()
{
ScanaStudio.gui_add_file_load("csv_file","Select CSV file","*.csv");
ScanaStudio.gui_add_text_input("sep","Column separator",";");
ScanaStudio.gui_add_new_tab("CSV Mapping",false);
var ch;
for (ch = 0; ch < ScanaStudio.get_device_channels_count(); ch++)
{
ScanaStudio.gui_add_combo_box("col_ch"+ch,"CH " + (ch+1).toString());
ScanaStudio.gui_add_item_to_combo_box("Do not import",false);
for (col = 0; col <= ScanaStudio.get_device_channels_count(); col++)
{
ScanaStudio.gui_add_item_to_combo_box("Column "+(col).toString(), ((col == (ch+1))?true:false));
}
}
ScanaStudio.gui_end_tab();
}


//Function called to build siganls (to be generate by capable device)
function on_build_signals()
{
//Use the function below to get the number of samples to be built
var samples_to_build = ScanaStudio.builder_get_maximum_samples_count();
var sample_rate = ScanaStudio.builder_get_sample_rate();
var max_time = samples_to_build / sample_rate;
var file = ScanaStudio.file_system_open("csv_file","r");
if (file < 0) //Is the file successfully opened?
{
return;
}
var separator = ScanaStudio.gui_get_value("sep");
var data = ScanaStudio.file_system_read_text(file,ENCODING);;
var lines = data.match(/[^\r\n]+/g);
ScanaStudio.file_system_close(file);
var ch_map = [];
var ch;
for (ch = 0; ch < ScanaStudio.get_device_channels_count(); ch++)
{
ch_map.push(ScanaStudio.gui_get_value("col_ch"+ch)-1);
}
var samples_acc = 0;
var i = 0;
for (i=0; i < lines.length; i++)
{
var cols = lines[i].split(separator);
var new_line = "";
//process one line:
samples_acc += 1;
if (samples_acc > samples_to_build)
{
break;
}
for (ch = 0; ch < ScanaStudio.get_device_channels_count(); ch++)
{
if (ch_map[ch] != -1)
{
sample_val = parseInt(cols[ch_map[ch]]);
ScanaStudio.builder_add_samples(ch,sample_val,1);
}
}
}
}