001package jmri.jmrit.logixng.actions;
002
003import java.util.Locale;
004import java.util.Map;
005
006import jmri.InstanceManager;
007import jmri.JmriException;
008import jmri.jmrit.logixng.*;
009
010/**
011 * Emulates Logix.
012 * 
013 * @author Daniel Bergqvist Copyright 2018
014 */
015public class Logix extends AbstractDigitalAction
016        implements FemaleSocketListener {
017
018    private boolean _executeOnChange = true;
019    private boolean _lastExpressionResult = false;
020    private String _expressionSocketSystemName;
021    private String _actionSocketSystemName;
022    private final FemaleDigitalExpressionSocket _expressionSocket;
023    private final FemaleDigitalBooleanActionSocket _actionSocket;
024    
025    public Logix(String sys, String user) {
026        super(sys, user);
027        _expressionSocket = InstanceManager.getDefault(DigitalExpressionManager.class)
028                .createFemaleSocket(this, this, "E");
029        _actionSocket = InstanceManager.getDefault(DigitalBooleanActionManager.class)
030                .createFemaleSocket(this, this, "A");
031    }
032    
033    @Override
034    public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws JmriException {
035        DigitalActionManager manager = InstanceManager.getDefault(DigitalActionManager.class);
036        String sysName = systemNames.get(getSystemName());
037        String userName = userNames.get(getSystemName());
038        if (sysName == null) sysName = manager.getAutoSystemName();
039        Logix copy = new Logix(sysName, userName);
040        copy.setComment(getComment());
041        return manager.registerAction(copy).deepCopyChildren(this, systemNames, userNames);
042    }
043    
044    /** {@inheritDoc} */
045    @Override
046    public Category getCategory() {
047        return Category.OTHER;
048    }
049
050    /** {@inheritDoc} */
051    @Override
052    public void execute() throws JmriException {
053        boolean result = _expressionSocket.evaluate();
054        boolean hasChangedToTrue = result && !_lastExpressionResult;
055        boolean hasChangedToFalse = !result && _lastExpressionResult;
056        
057        if (!_executeOnChange || (result != _lastExpressionResult)) {
058            _actionSocket.execute(hasChangedToTrue, hasChangedToFalse);
059        }
060        _lastExpressionResult = result;
061    }
062    
063    /**
064     * Sets whenether actions should only be executed when the result of the
065     * evaluation of the expression changes, or if the actions should always
066     * be executed.
067     * <p>
068     * This is the counterpart of Conditional.setTriggerOnChange()
069     * 
070     * @param b if true, execution is only done on change. if false, execution
071     *          is always done.
072     */
073    public void setExecuteOnChange(boolean b) {
074        _executeOnChange = b;
075    }
076    
077    /**
078     * Determines whenether actions should only be executed when the result of
079     * the evaluation of the expression changes, or if the actions should always
080     * be executed.
081     * <p>
082     * This is the counterpart of Conditional.getTriggerOnChange()
083     * 
084     * @return true if execution is only done on change, false otherwise
085     */
086    public boolean isExecuteOnChange() {
087        return _executeOnChange;
088    }
089    
090    /** {@inheritDoc} */
091    @Override
092    public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException {
093        switch (index) {
094            case 0:
095                return _expressionSocket;
096                
097            case 1:
098                return _actionSocket;
099                
100            default:
101                throw new IllegalArgumentException(
102                        String.format("index has invalid value: %d", index));
103        }
104    }
105    
106    /** {@inheritDoc} */
107    @Override
108    public int getChildCount() {
109        return 2;
110    }
111    
112    /** {@inheritDoc} */
113    @Override
114    public void connected(FemaleSocket socket) {
115        if (socket == _expressionSocket) {
116            _expressionSocketSystemName = socket.getConnectedSocket().getSystemName();
117        } else if (socket == _actionSocket) {
118            _actionSocketSystemName = socket.getConnectedSocket().getSystemName();
119        } else {
120            throw new IllegalArgumentException("unkown socket");
121        }
122    }
123    
124    /** {@inheritDoc} */
125    @Override
126    public void disconnected(FemaleSocket socket) {
127        if (socket == _expressionSocket) {
128            _expressionSocketSystemName = null;
129        } else if (socket == _actionSocket) {
130            _actionSocketSystemName = null;
131        } else {
132            throw new IllegalArgumentException("unkown socket");
133        }
134    }
135    
136    /** {@inheritDoc} */
137    @Override
138    public String getShortDescription(Locale locale) {
139        return Bundle.getMessage(locale, "Logix_Short");
140    }
141    
142    /** {@inheritDoc} */
143    @Override
144    public String getLongDescription(Locale locale) {
145        return Bundle.getMessage(locale, "Logix_Long",
146                _expressionSocket.getName(),
147                _actionSocket.getName());
148    }
149    
150    public FemaleDigitalExpressionSocket getExpressionSocket() {
151        return _expressionSocket;
152    }
153    
154    public String getExpressionSocketSystemName() {
155        return _expressionSocketSystemName;
156    }
157    
158    public void setExpressionSocketSystemName(String systemName) {
159        _expressionSocketSystemName = systemName;
160    }
161    
162    public FemaleDigitalBooleanActionSocket getActionSocket() {
163        return _actionSocket;
164    }
165    
166    public String getActionSocketSystemName() {
167        return _actionSocketSystemName;
168    }
169    
170    public void setActionSocketSystemName(String systemName) {
171        _actionSocketSystemName = systemName;
172    }
173    
174    /** {@inheritDoc} */
175    @Override
176    public void setup() {
177        try {
178            if ( !_expressionSocket.isConnected()
179                    || !_expressionSocket.getConnectedSocket().getSystemName()
180                            .equals(_expressionSocketSystemName)) {
181                
182                String socketSystemName = _expressionSocketSystemName;
183                _expressionSocket.disconnect();
184                if (socketSystemName != null) {
185                    MaleSocket maleSocket =
186                            InstanceManager.getDefault(DigitalExpressionManager.class)
187                                    .getBySystemName(socketSystemName);
188                    if (maleSocket != null) {
189                        _expressionSocket.connect(maleSocket);
190                        maleSocket.setup();
191                    } else {
192                        log.error("cannot load digital expression {}", socketSystemName);
193                    }
194                }
195            } else {
196                _expressionSocket.getConnectedSocket().setup();
197            }
198            
199            if ( !_actionSocket.isConnected()
200                    || !_actionSocket.getConnectedSocket().getSystemName()
201                            .equals(_actionSocketSystemName)) {
202                
203                String socketSystemName = _actionSocketSystemName;
204                _actionSocket.disconnect();
205                if (socketSystemName != null) {
206                    MaleSocket maleSocket =
207                            InstanceManager.getDefault(DigitalBooleanActionManager.class)
208                                    .getBySystemName(socketSystemName);
209                    _actionSocket.disconnect();
210                    if (maleSocket != null) {
211                        _actionSocket.connect(maleSocket);
212                        maleSocket.setup();
213                    } else {
214                        log.error("cannot load digital boolean action {}", socketSystemName);
215                    }
216                }
217            } else {
218                _actionSocket.getConnectedSocket().setup();
219            }
220        } catch (SocketAlreadyConnectedException ex) {
221            // This shouldn't happen and is a runtime error if it does.
222            throw new RuntimeException("socket is already connected");
223        }
224    }
225    
226    /** {@inheritDoc} */
227    @Override
228    public void registerListenersForThisClass() {
229    }
230    
231    /** {@inheritDoc} */
232    @Override
233    public void unregisterListenersForThisClass() {
234    }
235    
236    /** {@inheritDoc} */
237    @Override
238    public void disposeMe() {
239    }
240    
241    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(Logix.class);
242
243}