Package modbuspal.automation

Source Code of modbuspal.automation.Automation

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package modbuspal.automation;

import modbuspal.generator.Generator;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import modbuspal.generator.GeneratorListener;
import modbuspal.instanciator.InstantiableManager;
import modbuspal.toolkit.InstanceCounter;
import modbuspal.toolkit.XMLTools;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
* Defines an automation.
* @author nnovic
*/
public class Automation
implements Runnable
{
    /** default name of an automation */
    public final static String DEFAULT_NAME = "no name";
    private ArrayList<Generator> generators = new ArrayList<Generator>();
    private double stepDelay = 1.0;
    private Thread thread = null;
    private boolean loop = true;
    private String uniqueName="";
    private boolean suspended = false;
    private boolean quit = false;
    private ArrayList<AutomationEditionListener> automationEditionListeners = new ArrayList<AutomationEditionListener>();
    private InstanceCounter<AutomationExecutionListener> automationExecutionListeners = new InstanceCounter<AutomationExecutionListener>();
    private ArrayList<GeneratorListener> generatorListeners = new ArrayList<GeneratorListener>();
    private double currentValue = 0.0;
    private double initialValue = 0.0;

    /**
     * This is the constructor to use when creating the automation from an
     * xml file (i.e: loading a xmpp project). It uses the "loadAttributes"
     * function to parse the "importable" attributes of the "automation" tag,
     * and it also parses the non-importable attributes (like "id").
     * @param attributes xml attributes of the "automation" node
     */
    public Automation(NamedNodeMap attributes)
    {
        loadAttributes(attributes);
    }

    /**
     * This is the constructor that is used to create an new, empty automation.
     * You have to provide a name that is unique in the scope of the current project.
     * The constructor won't check that it is unique; the ModbusPal.addAutomation()
     * may modify it if it isn't. You can also use the ModbusPal.automationExists()
     * method to verify if a name is unique.
     * @param name name of the automation
     */
    public Automation(String name)
    {
        uniqueName = name;
    }

    /**
     * Changes the order of the generators by putting the specified generator
     * one rank down in the list. A "generators swapped" event is then fired.
     * Usually, you don't call this method directly; it is triggered by clicking
     * on the "down" button of a GeneratorRenderer.
     * @param gen the generator to push down
     */
    void down(Generator gen)
    {
        int index = generators.indexOf(gen);
        int max = generators.size();
        if( (index+1) < max )
        {
            swap(index,index+1);
        }
    }

    /**
     * Gets a list of the generators in this automation.
     * @return an array containing the references of all the
     * generators in this automation.
     */
    Generator[] getGenerators()
    {
        Generator[] list = new Generator[0];
        return generators.toArray(list);
    }


    /**
     * Setup the attributes of the automation by extracting
     * the values from an xml node.
     * @param attributes the xml node representing the attributes
     * of the automation
     */
    public final void loadAttributes(NamedNodeMap attributes)
    {
        Node stepNode = attributes.getNamedItem("step");
        String stepValue = stepNode.getNodeValue();
        double newStepDelay = Double.parseDouble(stepValue);
        if( stepDelay != newStepDelay )
        {
            stepDelay = newStepDelay;
            fireStepDelayHasChanged(stepDelay);
        }
       
        Node loopNode = attributes.getNamedItem("loop");
        String loopValue = loopNode.getNodeValue();
        boolean newLoop = Boolean.parseBoolean(loopValue);
        if( loop != newLoop )
        {
            loop = newLoop;
            fireLoopEnabled(loop);
        }

        Node nameNode = attributes.getNamedItem("name");
        String nameValue = nameNode.getNodeValue();
        if( uniqueName.compareTo(nameValue) != 0 )
        {
            uniqueName = nameValue;
            fireNameChanged(uniqueName);
        }

        // Extract initial value of the whole automation
        Node initNode = attributes.getNamedItem("init");
        if( initNode!= null )
        {
            String initValue = initNode.getNodeValue();
            double newInit = Double.parseDouble(initValue);
            if( initialValue != newInit )
            {
                initialValue = newInit;
                fireInitialValueChanged(initialValue);
            }
        }
    }

    /**
     * Returns the duration of the steps, in seconds. A step is the delay
     * between two updates of the automation's current value.
     * @return the duration of the steps, in seconds.
     */
    public double getStepDelay()
    {
        return stepDelay;
    }

    /**
     * Adds a generator at the end of the current list of generators.
     * @param gen the generator to add
     * @return index of the added generator
     */
    public int addGenerator(Generator gen)
    {
        generators.add(gen);
        int index = generators.indexOf(gen);
        fireGeneratorAdded(gen,index);
        return index;
    }

    /**
     * Removes a generator from the current list of generators.
     * @param gen the generator to remove.
     */
    public void removeGenerator(Generator gen)
    {
        generators.remove(gen);
        fireGeneratorRemoved(gen);
    }

    /**
     * Removes all generators from the automation.
     */
    public void removeAllGenerators()
    {
        Generator list[] = new Generator[0];
        list = generators.toArray(list);
        for( int i=0; i<list.length; i++ )
        {
            removeGenerator( list[i] );
        }
    }


    /**
     * Removes all generators from the automation that have
     * the specified classname
     * @param classname classname of the generators to remove
     */
    public void removeAllGenerators(String classname)
    {
        Generator list[] = new Generator[0];
        list = generators.toArray(list);
        for( int i=0; i<list.length; i++ )
        {
            if( list[i].getClassName().compareTo(classname)==0 )
            {
                removeGenerator( list[i] );
            }
        }
    }


    void notifyGeneratorHasEnded(Generator gen)
    {
        for(GeneratorListener l:generatorListeners)
        {
            l.generatorHasEnded(gen);
        }
    }

    void notifyGeneratorHasStarted(Generator gen)
    {
        for(GeneratorListener l:generatorListeners)
        {
            l.generatorHasStarted(gen);
        }
    }


    /**
     * Adds a GeneratorListener to the list of listeners
     * @param l the GeneratorListener to add
     */
    public void addGeneratorListener(GeneratorListener l)
    {
        assert( generatorListeners.contains(l) == false );
        generatorListeners.add(l);
    }

    /**
     * Removes a GeneratorListener from the list of listeners
     * @param l the GeneratorListener to remove
     */
    public void removeGeneratorListener(GeneratorListener l)
    {
        assert( generatorListeners.contains(l) == true );
        generatorListeners.remove(l);
    }



    /**
     * This method is used to add generators into the automation, by
     * using the content of the node list. The generators are instantiated
     * by using the provided InstantiableManager object.
     * @param nodes
     * @param gf the "generator factory", object that holds the instantiable generators
     * @throws java.lang.InstantiationException
     * @throws java.lang.IllegalAccessException
     */
    public void loadGenerators(NodeList nodes, InstantiableManager<Generator> gf)
    throws InstantiationException, IllegalAccessException
    {
        for(int i=0; i<nodes.getLength(); i++)
        {
            Node node = nodes.item(i);
            if( node.getNodeName().compareTo("generator")==0 )
            {
                String className = XMLTools.getAttribute("class", node);
                Generator gen = gf.newInstance( className );

                if( gen==null )
                {
                    throw new InstantiationException("Generator "+className+" cannot be instanciated");
                }
               
                gen.load(node);
                addGenerator(gen);
            }
        }
    }

    /**
     * Saves the configuration of the automation, in XML format.
     * @param out the outpustream where the configuration is written
     * @throws IOException
     */
    public void save(OutputStream out)
    throws IOException
    {
        String openTag = createOpenTag();
        out.write( openTag.getBytes() );

        for(Generator g:generators)
        {
            g.save(out);
        }

        String closeTag = "</automation>\r\n";
        out.write( closeTag.getBytes() );
    }

    /**
     * Changes the name of this automation. Caution: this method
     * won't check that it is unique, but it has to be. A "name changed"
     * events is then fired.
     * @param newName the new name  of the automation.
     */
    public void setName(String newName)
    {
        uniqueName = newName;
        fireNameChanged(uniqueName);
    }

    /**
     * Creates and starts a new thread in order to execute this
     * automation. The thread's name is set to the name of the automation.
     * You can only have on running thread at the same time.
     */
    public void start()
    {
        if(thread==null)
        {
            thread = new Thread(this);
            thread.setName(uniqueName);
            suspended = false;
            quit = false;
            thread.start();
        }
    }

    /**
     * Stops the execution of the automation. The method will be blocked
     * until the thread is terminated.
     */
    public void stop()
    {
        if( thread != null )
        {
            quit = true;
            synchronized(this)
            {
                notifyAll();
            }
            try
            {
                thread.join( (long)(stepDelay*2000.0) );
            }
            catch (InterruptedException ex)
            {
                Logger.getLogger(Automation.class.getName()).log(Level.SEVERE, null, ex);
            }
            thread=null;
        }
    }

    /**
     * Suspends the execution of the automation.
     */
    public void suspend()
    {
        suspended = true;
    }

    /**
     * Resumes the execution of the automation.
     */
    public void resume()
    {
        suspended = false;
        synchronized(this)
        {
            notifyAll();
        }
    }

    /**
     * Check if the "loop" option is enabled for this automation.
     * If so, the execution of the automation continues with the first
     * generator of the list when the last generator is finished.
     * @return true if "loop" is enabled, false otherwise
     */
    public boolean isLoopEnabled()
    {
        return loop;
    }

    /**
     * Defines if "loop" option is enabled or not.
     * @param enabled
     */
    void setLoopEnabled(boolean enabled)
    {
        loop = enabled;
    }

    /**
     * Returns the name of this automation.
     * @return name of the automation.
     */
    public String getName()
    {
        return uniqueName;
    }

    /**
     * Returns the initial value that is configured for this automation.
     * @return initial value of the automation.
     */
    double getInitialValue()
    {
        return initialValue;
    }


    private double getTotalDuration()
    {
        double total= 0;
        for( Generator gen:generators )
        {
            total+=gen.getDuration();
        }
        return total;
    }


    /**
     * Returns se current value of the automation. If the automation is running,
     * this value will return the current value of the current generator.
     * Otherwise, it will return the initial value.
     * @return current value of the automation.
     */
    public double getCurrentValue()
    {
        return currentValue;
    }

   
    @Override
    public void run()
    {
        System.out.println("start automation thread");
       
        // Get generators
        Generator genList[] = new Generator[generators.size()];
        genList = generators.toArray(genList);

        // init automation:
        int currentIndex = 0;
        currentValue = initialValue;
        boolean reloaded = false;
        double currentTime = 0.0;
        double startTime = 0.0;

        // init historic:
        double totalDuration = getTotalDuration();
        int histoNbPoints = (int)Math.ceil( totalDuration / stepDelay );

        fireAutomationHasStarted();

        while( (currentIndex < genList.length) && (quit==false) )
        {
            if( reloaded )
            {
                fireAutomationReloaded();
            }

            // prepare to execute generator:
            Generator currentGen = genList[currentIndex];
            currentGen.setInitialValue(currentValue);
            double duration = currentGen.getDuration();
            startTime = currentTime;
            notifyGeneratorHasStarted(currentGen);

            while( (currentTime < startTime + duration) && (quit==false) )
            {
                // set current automation value:
                currentValue = currentGen.getValue( currentTime-startTime );
                //if( previousValue != currentValue )
                //{
                    fireCurrentValueChanged(currentTime, currentValue);
                //}

                try
                {
                    while( (suspended == true) && (quit == false) )
                    {
                        System.out.println("suspended");
                        synchronized(this)
                        {
                            wait();
                        }
                    }
                    suspended = false;
                    if( quit == false )
                    {
                        Thread.sleep( (long)(stepDelay*1000.0) );
                    }
                }
                catch (InterruptedException ex)
                {
                    Logger.getLogger(Automation.class.getName()).log(Level.SEVERE, null, ex);
                }
                currentTime += stepDelay;
                //previousValue = currentValue;
            }

            // finish the execution of the generator
            //previousValue = currentValue;
            currentValue = currentGen.getValue(duration);
            notifyGeneratorHasEnded(currentGen);

            currentIndex++;
            if( currentIndex >= genList.length )
            {
                if( loop == true )
                {
                    currentIndex = 0;
                    reloaded=true;
                }
            }
        }
       
        System.out.println("end of automation thread");
       
        currentValue = 0.0;
        fireCurrentValueChanged(currentTime, currentValue);

        suspended = false;
        if( quit==true )
        {
            quit = false;
        }
        else
        {
            thread=null;
        }
        fireAutomationHasEnded();
    }

    /**
     * Returns the current "suspended" state of the automation.
     * @return true if the automation is running but suspended. false
     * otherwise.
     */
    public boolean isSuspended()
    {
        return suspended;
    }


    /**
     * Verifies if the automation is currently running or not.
     * @return true is the automation is running, false otherwise
     */
    public boolean isRunning()
    {
        return (thread!=null);
    }

    /**
     * ModbusPal will call this function when the automation is removed from
     * the project. It should not be called directly.
     */
    public final void disconnect()
    {
        automationEditionListeners.clear();
        automationExecutionListeners.clear();
        generatorListeners.clear();
        generators.clear();
    }

    /**
     * Adds an AutomationEditionListener to the list of listeners
     * @param l the AutomationEditionListener to add
     */
    public void addAutomationEditionListener(AutomationEditionListener l)
    {
        assert( automationEditionListeners.contains(l) == false );
        automationEditionListeners.add(l);
    }

    /**
     * Removes an AutomationEditionListener from the list of listeners
     * @param l the AutomationEditionListener to remove
     * @return true if removed correctly
     */
    public boolean removeAutomationEditionListener(AutomationEditionListener l)
    {
        return automationEditionListeners.remove(l);
    }

    /**
     * Adds an AutomationExecutionListener to the list of listeners
     * @param l the AutomationExecutionListener to add
     * @return true if added correctly
     */
    public boolean addAutomationExecutionListener(AutomationExecutionListener l)
    {
        if( automationExecutionListeners.addInstance(l)==true )
        {
            System.out.println("AutomationExecutionListener added: "+l.hashCode());
            return true;
        }
        return false;
    }

    /**
     * Removes an AutomationExecutionListener from the list of listeners
     * @param l the AutomationExecutionListener to remove
     * @return true if removed properly
     */
    public boolean removeAutomationExecutionListener(AutomationExecutionListener l)
    {

        if( automationExecutionListeners.removeInstance(l)==true )
        {
            System.out.println("AutomationExecutionListener removed: "+l.hashCode());
            return true;
        }
        return false;
    }


    /**
     * Redefines the step delay of the automation.
     * @param delay new step delay in seconds.
     */
    void setStepDelay(double delay)
    {
        stepDelay = delay;
    }

    /**
     * Redefines the initial value of the automation.
     * @param dval
     */
    void setInitialValue(double val)
    {
        initialValue = val;
    }

    /**
     * Changes the order of the generators by putting the specified generator
     * one rank up in the list. A "generators swapped" event is then fired.
     * Usually, you don't call this method directly; it is triggered by clicking
     * on the "up" button of a GeneratorRenderer.
     * @param gen
     */
    void up(Generator gen)
    {
        int index = generators.indexOf(gen);
        if( index >= 1 )
        {
            swap(index,index-1);
        }
    }

    private String createOpenTag()
    {
        StringBuilder tag = new StringBuilder("<automation");
        tag.append(" name=\"").append(uniqueName).append("\"");
        tag.append(" step=\"").append(String.valueOf(stepDelay)).append("\"");
        tag.append(" loop=\"").append(Boolean.toString(loop)).append("\"");
        tag.append(" init=\"").append(Double.toString(initialValue)).append("\"");
        tag.append(">\r\n");
        return tag.toString();
    }

    private void fireAutomationHasEnded()
    {
        for(AutomationExecutionListener l:automationExecutionListeners)
        {
            l.automationHasEnded(this);
        }
    }

    private void fireAutomationHasStarted()
    {
        for(AutomationExecutionListener l:automationExecutionListeners)
        {
            l.automationHasStarted(this);
        }
    }

    private void fireAutomationReloaded()
    {
        for(AutomationExecutionListener l:automationExecutionListeners)
        {
            l.automationReloaded(this);
        }
    }

    private void fireGeneratorAdded(Generator gen, int index)
    {
        for(AutomationEditionListener l:automationEditionListeners)
        {
            l.generatorHasBeenAdded(this, gen, index);
        }
    }

    private void fireGeneratorRemoved(Generator gen)
    {
        for(AutomationEditionListener l:automationEditionListeners)
        {
            l.generatorHasBeenRemoved(this, gen);
        }
    }

    private void fireGeneratorSwap(Generator g1, Generator g2)
    {
        for(AutomationEditionListener l:automationEditionListeners)
        {
            l.generatorsHaveBeenSwapped(this, g1, g2);
        }
    }

    private void fireInitialValueChanged(double init)
    {
        for(AutomationEditionListener l:automationEditionListeners)
        {
            l.automationInitialValueChanged(this, init);
        }
    }

    private void fireLoopEnabled(boolean enabled)
    {
        for(AutomationEditionListener l:automationEditionListeners)
        {
            l.automationLoopEnabled(this, enabled);
        }
    }

    private void fireNameChanged(String newName)
    {
        for(AutomationEditionListener l:automationEditionListeners)
        {
            l.automationNameHasChanged(this, newName);
        }
    }

    private void fireCurrentValueChanged(double currentTime, double currentValue)
    {
        for(AutomationExecutionListener l:automationExecutionListeners)
        {
            l.automationValueHasChanged(this, currentTime, currentValue);
        }
    }

    private void fireStepDelayHasChanged(double step)
    {
        for(AutomationEditionListener l:automationEditionListeners)
        {
            l.automationStepHasChanged(this, step);
        }
    }

    private void swap(int i1, int i2)
    {
        Generator g1 = generators.get(i1);
        Generator g2 = generators.get(i2);
        generators.set(i1, g2);
        generators.set(i2, g1);
        fireGeneratorSwap(g1,g2);
    }
}
TOP

Related Classes of modbuspal.automation.Automation

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.