Node js classes and functions within classes

Hey all,

I have some issues understanding NODEJS syntaxing. Im used to working with php code.
So im trying to do something I did prior in node js but failing :slight_smile:

'''

inside a class Machine I use this code

      interpolate_lin_curve_points(i_curve, o_min, o_max){
        //declare internal vars 
        let o_curve = []; // output is an array
        let i_min = 0;
        let i_max = 0;

        //define min and max of curve 
        i_min = Math.min(i_curve);
        i_max = Math.max(i_curve);


        //Loop over results and write code in curve
        i_curve.forEach(fillcurve);

                    
        //define function to fill output curve
        function fillcurve(value, index){
          o_curve[index] = this.interpolate_lin_single_point(value, i_min, i_max, o_min, o_max);
        }

        return o_curve;
      }

'''

The above code gives me the following error :

TypeError: Cannot read properties of undefined (reading 'interpolate_lin_single_point')

When I use this directly within another defined function like this :

'''

limit_input(input,min,max){

        //declare internal vals
        let output;

        //limit function below or above min or max
        if ( input < min){
            output = min;
            this.warning = "Minimum limit reached";
        }
        else if ( input > max){
            output = max;
            this.warning = "Maximum limit reached";
        }
        else{
            output = input;
        }
        return output;
      }
      
      //Interpolate 1 single point
      interpolate_lin_single_point (i_number, i_min, i_max, o_min, o_max){
        //declare internal vars
        let o_number;
        
        //if min isnt equal to max interpolate else the output is equal to the input
        if ( i_min != i_max){
          o_number = o_min + ( i_number - i_min ) * ( o_max - o_min ) / ( i_max - i_min );
        }
        else{
          o_number = i_number;
        }

        //cap min and max to output min and max
        o_number = this.limit_input(o_number,o_min,o_max);
        
        return o_number;
      }

'''

it works perfectly fine...
Im just trying to loop over some array and allready having headaches.

Cheers for whoever can point me in the right direction !

Functions in JavaScript are declared as follows...

function interpolate_lin_curve_points(i_curve, o_min, o_max) {
   //...
   // do stuff
   // return result
}

or

const interpolate_lin_curve_points = (i_curve, o_min, o_max) => {
   //...
   // do stuff
   // return result
} 

If your array is in msg.payload then the "low-code" approach is to pass it through a split node, then you get each element as a single object you can work on. If needed, you can re-combine the data back into an array using the join node.

Hey steve ,

How is this different than what i wrote in the class function?

I dont want a low code approach since im writing my own node.

Ill simplify it some more :

I have a class in my node code

Class Machine {

// in my class i have some functions defined in the class

somefunction1(){
//doing stuff
}

//i have a second function which uses somefunction1
somefunction2(){
//doing stuff with function 1
test = this.somefunction1();
}

// Above works fine when called outside of the function 
//Below is the problem

somefunction3(){

//uses a function within this function because apparantly I cant use a foreach loop in another manner within nodejs

//Loop over results using a function
somearray.forEach(someinternalfunction);

//declare someinternal function
function someinternalfunction(){
//telling the function what to do using another class function 
test = this.somefunction1();
}

}



}

That wasn't clear (and your post is in the wrong section (I'll move it)

Can you post the class code & where you call it from - your post is a bit unclear. If I were to guess then you have a this issue or are trying to call the function on an uninstantiated instance or similar.

Also please always wrap code in backticks to improve readability and prevent the forum renderer messing up the code.

```
paste code with 3 backticks above and below (like this)
```

Here is the entire code :slight_smile:

class Machine {
        //construct default pump data
          constructor() {
            //general vars
            this.type = "";                   // Type of machine to pass on to child classes
            this.id = "";                     // Unique machine number starting from 1
            this.state = 1;                   // State = 0 the machine is turned off , state = 1 the machine is turn
            //stored variables
            this.prev_state           = 1;    // Stored previous output state of machine
            this.prev_internal_state  = 1;
            this.internal_state       = 1;

            //used in calculation
            this.best_powerresult_ctrl  = 0   ;   // Use this to store best power output before deciding what the actual
            this.best_powerresult_flow  = 0   ;   // Use this to store best power output before deciding what the actual
            this.best_powerresult_power = 0   ;   // Use this to store best power output before deciding what the actual
            this.best_flowresult_ctrl   = 0   ;   // Use this to store best flow output before deciding what the actual 
            this.best_flowresult_flow   = 0   ;   // Use this to store best flow output before deciding what the actual 
            this.best_flowresult_power  = 0   ;   // Use this to store best flow output before deciding what the actual 

            //output values
            this.o_ctrl          = -1            ;   // Stored output ctrl of machine
            this.o_flow          = 0             ;   // Stored output flow of machine
            this.o_power         = 0             ;   // Stored output power of machine
            this.o_error_code    = 0             ;   // Stored output error code
            this.o_error_text    = ""            ;   // Stored output error code
            this.warning         = ""            ;   // return warnings to user 
            
            //specifics of this machine
            this.name            = "undefined"   ;   // Name of machine
            this.time_on         = 0             ;   // Total time this was turned on in seconds
            this.time_off        = 0             ;   // Total time this was turned off in seconds+
            this.cooldown        = 5             ;   // When the machine turns off it needs to cool down. Enter the
            this.warmup          = 10            ;   // When the machines turns on it needs to warm up before it do
            this.cooldown_timer  = 0             ;   // Keep track of the cooldown until it hits zero
            this.warmup_timer    = 0             ;   // Keep track of the warmup until it hits zero
            this.sync_delay      = 0.01          ;   // Store sync delay expressed in seconds per % from min to max
            this.startup         = 10            ;   // when the machine turns on it requires some time to push its
            this.startup_timer   = 0             ;   // Keep track of the startup until it hits zero. 
            
            //normalize curve between min and max ctrl
            this.min_normalize           = 0         ;        // by default 0
            this.max_normalize           = 1000      ;        // The higher this is the more accurate the result ca
            this.open_curve_number       = 0         ;        // this value is set in the construction fase of the 
            this.open_curve_divider      = 1.35      ;        // division of opening curve the higher this number t
            this.init                    = false     ;        // When this function is constructed this will be 
            this.max_precision                       ;        // define max precision per individual machine
          
          }

          //////////////////////////////////Getter / Setters /////////////////////////////////////
          //set cooldown timer
          set cooldown_timer(value){
              this._cooldown_timer = value;
          }

          //get cooldown timer (we use _ to not create a getter loop)
          get cooldown_timer(){
            return this._cooldown_timer;
          }

          //set new cooldown value
          set cooldown(value){
            this._cooldown = value;
          }

          //get cooldown default value 
          get cooldown(){
            return this._cooldown;
          }

          //set new sync delay value
          set sync_delay(value){
            this._sync_delay = value;
          }
          
          //get sync delay default value in % / seconds 
          get sync_delay(){
            return this._sync_delay;
          }

          //set new machine number value
          set number(value){
            this._number = value;
          }

          //get machine number 
          get number(){
            return this._number;
          }
          
          //set new machine number value
          set type(value){
            this._type = value;
          }

          //get sync delay default value in % / seconds 
          get type(){
            return this._type;
          }

          //set new machine number value
          set state(value){
            this._state = value;
          }          

          //fetch current state
          get state(){
            return this._state;
          }   

          //set new machine name value
          set name(value){
            this._name = value;
          }          

          //fetch current state
          get name(){
            return this._name;
          } 

          //set resolution value
          set min_normalize(value){
            this._min_normalize = value;
          }          

          //fetch resolution
          get min_normalize(){
            return this._min_normalize;
          }

          //set resolution value
          set max_normalize(value){
            this._max_normalize = value;
          }          

          //fetch current state
          get max_normalize(){
            return this._max_normalize;
          }

          //set warning value
          set warning(value){
            this._warning = value;
          }          

          //fetch current state
          get warning(){
            return this._warning;
          }       



          ////////////////////////////////////FUNCTIONS/////////////////////////////
          //turn machine on
          switch_on(){
            //change state
            this.state = 1;  
            // and control to min? 
          }

          //turn the machine off 
          switch_off(){
            //turn state to zero
            this.state = 0;
            //turn control to zero ? 
          }

          //hand control from scada overwrites automatic control algo cant use this machine 
          force_on(){
            this.state = 3
          }

          //hand control from scada overwrites auto control algo cant use this machine 
          force_off(){
            this.state = 4;
          }

          //Limit numbers between min and max
          limit_input(input,min,max){
            
            //declare internal vals
            let output;

            //limit function below or above min or max
            if ( input < min){
                output = min;
                this.warning = "Minimum limit reached";
            }
            else if ( input > max){
                output = max;
                this.warning = "Maximum limit reached";
            }
            else{
                output = input;
            }
            return output;
          }
          
          //Interpolate 1 single point
          interpolate_lin_single_point (i_number, i_min, i_max, o_min, o_max){
            //declare internal vars
            let o_number;
            
            //if min isnt equal to max interpolate else the output is equal to the input
            if ( i_min != i_max){
              o_number = o_min + ( i_number - i_min ) * ( o_max - o_min ) / ( i_max - i_min );
            }
            else{
              o_number = i_number;
            }

            //cap min and max to output min and max
            o_number = this.limit_input(o_number,o_min,o_max);
            
            return o_number;
          }          

          //make a new curve and convert it between defined min and max
          //we use this to normalize the pressure curve so that all comparisons with points are done on the same starting and end pressures
          interpolate_lin_curve_points(i_curve, o_min, o_max){
            //declare internal vars 
            let o_curve = []; // output is an array
            let i_min = 0;
            let i_max = 0;

            //define min and max of curve 
            i_min = Math.min(i_curve);
            i_max = Math.max(i_curve);


            //Loop over results and write code in curve
            i_curve.forEach(fillcurve);

                        
            //define function to fill output curve
            function fillcurve(value, index){
              o_curve[index] = this.interpolate_lin_single_point(value, i_min, i_max, o_min, o_max);
            }

            return o_curve;
          }

  







      }

      //what to do on input
      node.on('input', function(msg) {

        //make new machine 
        var machine = new Machine();
        var msg1 = {payload : 0,topic:""};
          
          /*example
          //Set the cooldown timer to the machine default cooldown time
          machine.cooldown_timer = machine.cooldown; //start cooldown timer at default value
          
          //return the cooldown timer variable
          msg.payload = machine.cooldown_timer; // Will output 'Hello, my name is Martin, I have ID: id_1'
          msg.topic = "cooldown_timeleft";

          */
        
          //example usage lin interpolation functions
          //msg1.payload = machine.interpolate_lin_single_point(msg.payload, 0, 10, 0, 100);
          let test = [0,234,433,693];
          //example usage curve interpolation
          msg1.payload = machine.interpolate_lin_curve_points(test, 0, 10000);
          
        
          //return state of machine
          msg.topic = "Machine state";
          msg.payload = machine.state;

        node.send([msg,msg1]);

        // If an error is hit, report it to the runtime
          
      });
    }
    RED.nodes.registerType("pump",pump);
}

Msg1.payload is giving me the error. Im calling the function from within a class function.

Since interpolate_lin_single_point is a pure function, make it static then you can call it anywhere, without making a new instance, but instead, by calling it from the ClassName.Function() e.g. Machine.interpolate_lin_single_point()

So how would you solve this? Ive been looking at static methods but i want to do it from within the class. As shown in my code.. i mean in php its so simple why is this so hard in java?

Make the function static & call it like I said. Not sure what else to say

Yeah thats not what I wanted :stuck_out_tongue: I sorted it in another way..

So all the code remains the same apart from this bit :

 interpolate_lin_curve_points(i_curve, o_min, o_max){
            //declare internal vars 
            let o_curve = []; // output is an array
            let i_min = 0;
            let i_max = 0;

            //define min and max of curve 
            i_min = Math.min(...i_curve);
            i_max = Math.max(...i_curve);


            //Loop over results and write code in curve
            i_curve.forEach((val,index) => {
              o_curve[index] = this.interpolate_lin_single_point(val, i_min, i_max, o_min, o_max)
            });


            return o_curve;
          }

the looping over results uses a => apparantly to fix the this problem and gives me the same as the php did before like this :

//convert a curve to o min and o max points
    public function interpolate_lin_curve_points($i_curve, $o_min, $o_max){

        //define min and max of curve 
        $i_min = min($i_curve);
        $i_max = max($i_curve);

        //Loop over results and write code in curve
        foreach($i_curve as $i_number) 
        {                
            $o_curve[] = $this->interpolate_lin_single_point($i_number, $i_min, $i_max, $o_min, $o_max);
        }
        return $o_curve;
    }

So the foreach was the issue here trying to call the class function with this. Calling it with the arrow function binds the this or something.... like I said before I prefere the php syntax (or rather Im used to it :slight_smile:

Anyway cheers for thinking with me. sure your suggestion would of worked also but thats not what I wanted :wink:

Yeah, as I suspected ...

But I'm glad you found it for yourself - you will remember far better.

One thing to note. By calling that function from a new instance is rather resource wasteful since every time your node receives a message, your code instantiates a new object that ultimately requires garbage collecting at some point. Making the function static means you do not need to create a new object each time.

PS, stick with the JavaScript/node stuff, once you get familiar, it's not a bad language. I went through a similar pain point as you.

1 Like

Yup I loaded the new machine bit into the on msg just to test some stuff. I moved it up now so it only gets created once the node is loaded.

I was reading your link on static. so it means that if you use static you have to call the classes name and not the instantiated object anymore?

Example
class name = foo
bar = new foo;

calling bar.somefunctionoffoo
wouldnt work ?

I understand that when it gets instantiated alot there are memory issues. But in my use I moved it up when the node gets instantiated and I dont use it anywhere else but. So only 1 copy of the instantiation exists. Does it still make sense to add static in front of all the functions?

If the method does not use any calls to this. within, then you can tag it as static. But then, you have to use the classname to prefix the method MyClass.callTheFunc(); You can still use this within the static method, but it will refer to the class and static properties and other static methods instead of any instances of the class.

By no means is my code a great example of anything special, but I do make use of classes and have a couple static methods and properties that you can peak at. Maybe a real world usage example might make it a little clearer.

1 Like

Can you not call a static function using an instance of the object? I know it is not necessary to have an instance, but I would have expected it to work.

You do not have to call it from an instance. Think of how you sometimes may use Date.now() in your code to get a current timestamp. now is a static method of the Date class. Date - JavaScript | MDN

Of course, you would have to require the class to make it available to be able to call its static method if it was defined in some other location.

I know that, that is the point of static. You said

Which implies that you cannot call it on an instance. I thought you could call a static using either an instance or by using the class name.

Sorry for not being clear. A class static method can be called from anywhere, including an instance or some external code. The instance has no special privilege when calling it and must still use the class name prefix.

I think the confusion was when using on or from when stating how the method could be called. Right, the static method cannot be called on an instance, but it can be called from an instance (or anywhere else).

I have to apologise, you are right. I wonder why that is not allowed. In C++ a static method can be called using either the class name or a class instance.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.