How to download txt files using the JS object and configure loggers

Introduction:

This guide explains using JavaScript (JS) objects in EasyBuilder Pro to download text from an ASCII object and save it to a text file in the USB1 folder. We will also cover how to set up a logger so print statements can be made directly on to JS objects.

Warning

JS objects provide powerful customization features, but misusing them can result in system error or performance degradation. Please use JS objects carefully.

Software Version:

The JS Object is exclusive to cMT-X series HMIs
EasyBuilder Pro 6.05.01+

Related Tutorials:

How to transfer device data using the JS object
Video - Introduction to Weintek’s JS Object (JavaScript)
Video - How to draw shapes with Weintek’s JS Object (JavaScript)
JS Action/Object SDK Documentation

Instructions

  1. Navigate to the Object tab and select JS Object under the JS-related options.

  2. Within the Config tab, select the New Value button at the bottom of the page.

  3. Set the Name attribute as desired; in this example, we will use “StringOut” for the name. Additionally, select Word for the Type attribute and set a memory address. For this example, we use address LW 0. We will use a 16-bit unsigned type, but you may modify this as needed. After making the changes, select OK.


  4. Next, navigate to the Source Code tab. This section is where the JS code will be applied and compiled.

  5. First, we need to initialize our variables.

const cstdio = await import('@weintek/libc/stdio'); // Library needed to open file
var utf8decoder = new TextDecoder(); // Object to decode ASCII object text

var mouseArea = new MouseArea(); // On click object which will allow a function to run when the JS object is clicked on. 
this.widget.add(mouseArea);
  1. Next, we will instantiate the on-click object, which will run the function when the JS object is clicked.
mouseArea.on('click', (mouseEvent) => { // Instantiate mouseArea object 

// on click of object these functions will run

    });
    
});
  1. Within the mouseArea function, the text will be pulled from the ASCII object, decoded, and inserted into the specified file when clicked. An error catch is also implemented.
mouseArea.on('click', (mouseEvent) => {

    driver.getData(this.config.StringOut, 11, (err, data) => { // gets data from ASCII object. 
// this.config.StringOut == Variable set up in the config tab 
// 11 = number of words that will transferred. 
// (err,data) == error catching
    if (err) {
        console.log('Error:', err.message); // Error catch 
    } else {

 // Set up a middle array to a temporary holding area for the text from ASCII object 
        var bufferAsUint8Array = new Uint8Array(data.buffer);
        var message =  utf8decoder.decode(bufferAsUint8Array)
        console.log("string:", message); // Print the message to the console (Optional)
        
// Set up an encoder object which will insert text into the specified file. 
        const encoder = new TextEncoder();
// Open file. Modify as needed. 
        const file = cstdio.fopen('usb1', '/test2.txt', 'a+');
// 'usb1' == File selected in EasyBuilder Pro files to send text to
// '/test2.txt' == Created file within the usb1 folder that will receive the text 
// 'a+' == Text will be appended to the file. This will keep a running count within the test2.txt file of all sent text. 

 
        cstdio.fwrite(file, encoder.encode(message).buffer);//message is written into the file
        cstdio.fclose(file); // Close the file
    }
    });
    
});
  1. After implementing the code, click the Check Code button at the top right to check for any errors. If no errors are found, click Apply and OK to place the object in your project.



    Note: For visual clarity, you can add a shape to your JS object in the Shape tab.

  2. Next, we will set up the ASCII object to write our messages into. Navigate to the Object tab and select ASCII.

  3. Ensure the Read/Write address in the ASCII object matches the JS object address. To allow for more words in the ASCII object, select the yellow tag button, which will enable you to modify the number of words entered into the ASCII object.


    image

  4. The Encoding data format within the ASCII object needs to be changed to Unicode. This ensures that the text within the object remains unchanged during data transfer to USB. Place the object within your project after applying modification to the ASCII object.

  5. Lastly, create the specified files on your PC as referenced in the JS object. In this example, I have created the test2.txt file within the usb1 folder. The file path may vary slightly depending on where the EasyBuilder Pro application was initially downloaded.

const file = cstdio.fopen('usb1', '/test2.txt', 'a+'); // This is the line that decides which file to open and send the message to. 
  1. To run project navigate tot he Project tab and select Offline Simulation

  2. After selecting the JS object the message within the ASCII object will be transferred to the text file.


Full Code:

const cstdio = await import('@weintek/libc/stdio');
var utf8decoder = new TextDecoder();

var mouseArea = new MouseArea();
this.widget.add(mouseArea);

var canvas = new Canvas();

this.widget.add(canvas);


mouseArea.on('click', (mouseEvent) => {

    driver.getData(this.config.StringOut, 11, (err, data) => {
    if (err) {
        console.log('Error:', err.message);
    } else {

        var bufferAsUint8Array = new Uint8Array(data.buffer);
        var message =  utf8decoder.decode(bufferAsUint8Array);
        console.log("string:", message);
        
        
        const encoder = new TextEncoder();
        const file = cstdio.fopen('usb1', '/test2.txt', 'a+');
        
        cstdio.fwrite(file, encoder.encode(message).buffer);
       
       
        cstdio.fclose(file);
        
    }
     
    });
    
    
});

More Info on Logger
A logger allows you to log messages or set markers throughout your JavaScript script, ensuring they appear on your JavaScript object. Essentially, it functions similarly to the console.log() feature but outputs to the JavaScript object instead of the console. This output can then be viewed within the diagnostics tool in EasyBuilder Pro.

  1. First, access the JS Resource within the JS-related object.
  2. Save the code below as a JS file on your computer.
class CanvasLog {
    offset = 24;
    lineWidth = 1;

    constructor(canvas, opt) {
        this.canvas = canvas;

        var opt = opt || {};
        opt.fillStyle = opt.hasOwnProperty("fillStyle") ? opt.fillStyle : "black";
        opt.fontSize = opt.hasOwnProperty("fontSize") ? opt.fontSize : 12;
        opt.padding = opt.hasOwnProperty("padding") ? opt.padding : opt.fontSize;

        this.opt = opt;
        this.offsetReset();
    }

    offsetIncrement() {
        this.offset += this.opt.fontSize;
    }

    offsetReset() {
        this.offset = this.opt.fontSize;
    }

    log() {
        var text = "[" + (new Date()).toISOString() + "]";
        for (var i = 0; i < arguments.length; i++) {
            text += " " + arguments[i].toString();
        }
        console.log(text);
        this.drawText(text);
    }

    drawText(text) {
        this.canvas.fillStyle = this.opt.fillStyle;
        this.canvas.font = `${this.opt.fontSize}px Consolas`;

        // calculate linewidth once if not init
        if (this.lineWidth == 1) {
            for (; this.lineWidth > 0 && this.lineWidth < 2000; this.lineWidth++) {
                var tempText = this.canvas.measureText("1".repeat(this.lineWidth));
                if (tempText.width >= (this.canvas.width - this.opt.padding * 2)) {
                    this.lineWidth--;
                    break;
                }
            }
        }

        text.split("\n").forEach((line) => {
            for (var i = 0; i < line.length; i += this.lineWidth) {
                this.canvas.fillText(line.substr(i, this.lineWidth), this.opt.padding, this.offset);
                this.offsetIncrement();
                if (this.offset > this.canvas.height) {
                    this.canvas.clearRect(0, 0, this.canvas.width, this.canvas.height);
                    this.offsetReset();
                }
            }
        })
    }

    clear() {
        this.offset = this.opt.fontSize;
        this.canvas.clearRect(0, 0, this.canvas.width, this.canvas.height);
    }
}

module.exports = CanvasLog;
  1. Next, create a folder if desired by using the New Folder button, then add the previously mentioned JS code using the Add File button. In this example, my folder is named “Logger,” and the file holding the JS code is called “canvas-log-0.0.1.js.” After your file is added, select the file and click the Copy Path button to save the path to your clipboard.

  2. Add the following code to your JavaScript source code near the top. This ensures that all subsequent functions can use the logger tools to print statements to the object:

const CanvasLog = require('/Logger/canvas-log-0.0.1.js');

var canvas = new Canvas();
var logger = new CanvasLog(canvas, {fillStyle: "black"});

this.widget.add(canvas);

window.logger = logger;

  1. Now, you can create logs within your JS object and display them using the logger.log() function. The function must be added with the source code.
logger.log("Message or variables go in here!");  

Logger For Loop Use Case Example
Within this example I will out the results of a for loop using a logger. It will print the start of the loop, print values from 1 -5, and then signify when the loop ends:

const cstdio = await import('@weintek/libc/stdio');

//mouse area and canvas set up 
var mouseArea = new MouseArea();
this.widget.add(mouseArea);

var canvas = new Canvas();

this.widget.add(canvas);



// Import of the logger function from the imported file
const CanvasLog = require('/Logger/canvas-log-0.0.1.js');

// Setting up new canvas
var canvas = new Canvas();
var logger = new CanvasLog(canvas, {fillStyle: "black"}); // Text color is set to black each time logger is called. 

this.widget.add(canvas);

window.logger = logger;


mouseArea.on('click', (mouseEvent) => { // When object is clicked on fucntion will trigger

    logger.log("Start of loop"); // Logger will print "Start of loop"
    for(let i = 0; i <=5; i++){
        logger.log(i); // Prints value of i on each iteration. 
    }
    logger.log("End of loop"); // When loop is finished, logger prints "End of loop"
    
    
});

Result
image
All logged values are printed.

Full Code With Logger
User will write to file and JS object will be populated with messages on the processing of the file from the source code.

const cstdio = await import('@weintek/libc/stdio');
var utf8decoder = new TextDecoder();

var mouseArea = new MouseArea();
this.widget.add(mouseArea);

const CanvasLog = require('/Logger/canvas-log-0.0.1.js');

var canvas = new Canvas();
var logger = new CanvasLog(canvas, {fillStyle: "black"});

this.widget.add(canvas);

window.logger = logger;


mouseArea.on('click', (mouseEvent) => {

    driver.getData(this.config.StringOut, 11, (err, data) => {
    if (err) {
        console.log('Error:', err.message);
        logger.log("Error.");
    } else {

        logger.log("Getting Data")
        var bufferAsUint8Array = new Uint8Array(data.buffer);
        var message =  utf8decoder.decode(bufferAsUint8Array);
        console.log("string:", message);
        
        
        const encoder = new TextEncoder();
        const file = cstdio.fopen('usb1', '/test5.txt', 'w+');
        logger.log("Opening file")
        cstdio.fwrite(file, encoder.encode(message).buffer);
        logger.log("Writing to file")
       
        cstdio.fclose(file);
        logger.log("Finished")
        
    }
     
    });
    
    
});

Results of USB Transfer with Logger


Note logger JS file needs to be added to JS resource to use logger function.

Note


To change the file access mode modify the encoder of the JS script. For example within this example “a+” was used or the file needed to be made within the USB 1 file, but if changed to “W+” a new file will be written.

const encoder io.fopen('usb1', '/test2.txt', 'a+');
1 Like