Skip to content

Automatic update of signals using intrinsic functions

Bernd Schuster edited this page Aug 20, 2020 · 21 revisions

home

You can learn here:

  • how to use intrinsic functions to automatically update signals from cycle to cycle

HINT: A brief description of more elbfisch features you can find here

HINT: A brief lecture you can find here

Intrinsic functions are available since Elbfisch V2.×. Look here on how to install the latest version

They are helpful to continously update the value of signals during the life time of an elbfisch module. This mechanism frees the programmer from dealing with the calculation of signals in every state a certain module runs through.

The example below shows a module called “IntrinsicFunctionModule” which receives the actual position of an axis, for instance from a rotary encoder (in this example simulated by a module called “MovementSimulationModule”), and continously calculates the actual velocity (signal Velocity) of a body driven by the axis.

The signal “Velocity” is instantiated in the constructor of IntrinsicFunctionModule as follows:

velocity      = new Decimal(this,"Velocity", () -> computeVelocity());

Check the code of the IntrinsicFunctionModule below for details of computeVelocity().

  • The signal changes its state to invalid whenever the computation of its intrinsic functions fails (throws an exception).
  • If the module invalidates the signal explicitly by use of invalidate() its intrinsic function is dropped and cannot be reactivated.
  • Signals driven by an intrinsic functions cannot be connect()’ed as target.
  • Computation of intrinsic functions is automatically stopped when the containing module is terminated (returns from work()).

Module “IntrinsicFunctionModule”:



/**
 * PROJECT   : Elbfisch - java process automation controller (jPac)
 * MODULE    : IntrinsicFunctionModule.java
 * VERSION   : -
 * DATE      : -
 * PURPOSE   : 
 * AUTHOR    : Bernd Schuster, MSK Gesellschaft fuer Automatisierung mbH, Schenefeld
 * REMARKS   : -
 * CHANGES   : CH#n <Kuerzel> <datum> <Beschreibung>
 *
 * This file is part of the jPac process automation controller.
 * jPac is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * jPac is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with the jPac If not, see <http://www.gnu.org/licenses/>.
 */

package org.elbfisch.elbfisch.samples;

import org.jpac.Decimal;
import org.jpac.InputInterlockException;
import org.jpac.Logical;
import org.jpac.Module;
import org.jpac.OutputInterlockException;
import org.jpac.PeriodOfTime;
import org.jpac.ProcessEvent;
import org.jpac.ProcessException;
import org.jpac.ShutdownRequestException;
import org.jpac.SignalInvalidException;

/**
 *
 * @author berndschuster
 */
public class IntrinsicFunctionModule extends Module{

    private Decimal                  velocity;
    private Decimal                  position;
    private Logical                  positionValid;
    private Long                     lastNanoTime;
    private Double                   lastPosition;
    private MovementSimulationModule movementSimulationModule;  
    
    public IntrinsicFunctionModule(Module containingModule, String identifier){
        super(containingModule, identifier);
        try{
            lastNanoTime  = System.nanoTime();
            position      = new Decimal(this,"Position");
            velocity      = new Decimal(this,"Velocity", () -> computeVelocity());
            positionValid = new Logical(this,"PositionValid",() -> position.isValid());
            //instantiate the module, which simulates the movement of a physical body
            movementSimulationModule = new MovementSimulationModule(this, "MovementSimulationModule");
            //connect its positon signal to my own position
            movementSimulationModule.getPosition().connect(position);
        }
        catch(Exception exc){
            Log.error("Error: ", exc);
        }
    }
    
    @Override
    public void start(){
        //start movement simulation module
        movementSimulationModule.start();
        // start myself
        super.start();
    }
    
    @Override
    protected void work() throws ProcessException {
        boolean      done = false;
        ProcessEvent pot  = new PeriodOfTime(500 * ms);
        Log.info("started ...");
        do{
            try{
                pot.await();
                Log.info(velocity.toString());
            } catch(ShutdownRequestException exc){
                done = true;
            }
        } 
        while(!done);
        try{Thread.sleep(500);}catch(Exception exc){};
        Log.info("finished.");
    }
    
    public double computeVelocity() throws SignalInvalidException{
        long   actualNanoTime;
        double deltaTime;
        double deltaPosition;
        actualNanoTime = System.nanoTime();
        deltaTime      = ((double)(actualNanoTime - lastNanoTime))/(double)sec;
        lastNanoTime   = actualNanoTime;
        if (!position.isValid()){
            //velocity is invalid since the position gets valid ...
            lastPosition = null;
            throw new SignalInvalidException(position.getQualifiedIdentifier());
        }
        if (lastPosition == null){
            //Initialize lastPosition on first cycle "position" gets valid
            lastPosition = position.get();
            //but the velocity can't be computed in this state either
            throw new SignalInvalidException(position.getQualifiedIdentifier());            
        }
        //position is valid and lastPosition is initialized
        deltaPosition = position.get() - lastPosition;
        lastPosition = position.get();
        return deltaPosition/deltaTime;
    }

    @Override
    protected void preCheckInterlocks() throws InputInterlockException {
        //nothing to do here
    }

    @Override
    protected void postCheckInterlocks() throws OutputInterlockException {
        //nothing to do here
    }

    @Override
    protected void inEveryCycleDo() throws ProcessException {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

}

Module “MovementSimulationModule”:



/**
 * PROJECT   : Elbfisch - java process automation controller (jPac)
 * MODULE    : MovementSimulationModule.java
 * VERSION   : -
 * DATE      : -
 * PURPOSE   : 
 * AUTHOR    : Bernd Schuster, MSK Gesellschaft fuer Automatisierung mbH, Schenefeld
 * REMARKS   : -
 * CHANGES   : CH#n <Kuerzel> <datum> <Beschreibung>
 *
 * This file is part of the jPac process automation controller.
 * jPac is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * jPac is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with the jPac If not, see <http://www.gnu.org/licenses/>.
 */

package org.elbfisch.elbfisch.samples;

import org.jpac.Decimal;
import org.jpac.InputInterlockException;
import org.jpac.Module;
import org.jpac.NextCycle;
import org.jpac.OutputInterlockException;
import org.jpac.PeriodOfTime;
import org.jpac.ProcessException;

/**
 *
 * @author berndschuster
 */
public class MovementSimulationModule extends Module{

    private Decimal position;
    private double  rad;
    
    public MovementSimulationModule(Module containingModule, String identifier){
        super(containingModule, identifier);
        this.rad = 0.0;
        
        try{
            position  = new Decimal(this,"Position",0.0);
        }
        catch(Exception exc){
            Log.error("Error: ", exc);
        }
    }
    @Override
    protected void work() throws ProcessException {
        NextCycle    nextCycle = new NextCycle();
        PeriodOfTime pot5sec   = new PeriodOfTime(5 * sec);
        try{
            do{
                Log.info("started...");
                //generate a position signal for 100 cycles
                for(int i = 0; i < 100; i++){
                    position.set(Math.sin(rad += 0.1));
                    nextCycle.await();
                }
                //simulate a sensor failure which longs for 5 sec.
                position.invalidate();
                pot5sec.await();
            } while(true);
            
        } finally{
            Log.info("finished.");
        }
    }
    
    public Decimal getPosition(){
        return this.position;
    }

    @Override
    protected void preCheckInterlocks() throws InputInterlockException {
        //nothing to do here
    }

    @Override
    protected void postCheckInterlocks() throws OutputInterlockException {
        //nothing to do here
    }

    @Override
    protected void inEveryCycleDo() throws ProcessException {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

}

That’s all you need to know !

home

Clone this wiki locally