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 ExecuteType _executeType = ExecuteType.ExecuteOnChange;
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        copy.setExecuteType(_executeType);
042        return manager.registerAction(copy).deepCopyChildren(this, systemNames, userNames);
043    }
044
045    /** {@inheritDoc} */
046    @Override
047    public Category getCategory() {
048        return Category.OTHER;
049    }
050
051    /**
052     * Get the execute type.
053     * @return the type
054     */
055    public ExecuteType getExecuteType() {
056        return _executeType;
057    }
058
059    /**
060     * Set the execute type.
061     * @param type the type
062     */
063    public void setExecuteType(ExecuteType type) {
064        _executeType = type;
065    }
066
067    /** {@inheritDoc} */
068    @Override
069    public void execute() throws JmriException {
070        boolean result = _expressionSocket.evaluate();
071
072        if ((_executeType == ExecuteType.ExecuteAlways) || (result != _lastExpressionResult)) {
073            _actionSocket.execute(result);
074        }
075        _lastExpressionResult = result;
076    }
077
078    /** {@inheritDoc} */
079    @Override
080    public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException {
081        switch (index) {
082            case 0:
083                return _expressionSocket;
084
085            case 1:
086                return _actionSocket;
087
088            default:
089                throw new IllegalArgumentException(
090                        String.format("index has invalid value: %d", index));
091        }
092    }
093
094    /** {@inheritDoc} */
095    @Override
096    public int getChildCount() {
097        return 2;
098    }
099
100    /** {@inheritDoc} */
101    @Override
102    public void connected(FemaleSocket socket) {
103        if (socket == _expressionSocket) {
104            _expressionSocketSystemName = socket.getConnectedSocket().getSystemName();
105        } else if (socket == _actionSocket) {
106            _actionSocketSystemName = socket.getConnectedSocket().getSystemName();
107        } else {
108            throw new IllegalArgumentException("unkown socket");
109        }
110    }
111
112    /** {@inheritDoc} */
113    @Override
114    public void disconnected(FemaleSocket socket) {
115        if (socket == _expressionSocket) {
116            _expressionSocketSystemName = null;
117        } else if (socket == _actionSocket) {
118            _actionSocketSystemName = null;
119        } else {
120            throw new IllegalArgumentException("unkown socket");
121        }
122    }
123
124    /** {@inheritDoc} */
125    @Override
126    public String getShortDescription(Locale locale) {
127        return Bundle.getMessage(locale, "Logix_Short");
128    }
129
130    /** {@inheritDoc} */
131    @Override
132    public String getLongDescription(Locale locale) {
133        return Bundle.getMessage(locale, "Logix_Long", _executeType.toString());
134    }
135
136    public FemaleDigitalExpressionSocket getExpressionSocket() {
137        return _expressionSocket;
138    }
139
140    public String getExpressionSocketSystemName() {
141        return _expressionSocketSystemName;
142    }
143
144    public void setExpressionSocketSystemName(String systemName) {
145        _expressionSocketSystemName = systemName;
146    }
147
148    public FemaleDigitalBooleanActionSocket getActionSocket() {
149        return _actionSocket;
150    }
151
152    public String getActionSocketSystemName() {
153        return _actionSocketSystemName;
154    }
155
156    public void setActionSocketSystemName(String systemName) {
157        _actionSocketSystemName = systemName;
158    }
159
160    /** {@inheritDoc} */
161    @Override
162    public void setup() {
163        try {
164            if ( !_expressionSocket.isConnected()
165                    || !_expressionSocket.getConnectedSocket().getSystemName()
166                            .equals(_expressionSocketSystemName)) {
167
168                String socketSystemName = _expressionSocketSystemName;
169                _expressionSocket.disconnect();
170                if (socketSystemName != null) {
171                    MaleSocket maleSocket =
172                            InstanceManager.getDefault(DigitalExpressionManager.class)
173                                    .getBySystemName(socketSystemName);
174                    if (maleSocket != null) {
175                        _expressionSocket.connect(maleSocket);
176                        maleSocket.setup();
177                    } else {
178                        log.error("cannot load digital expression {}", socketSystemName);
179                    }
180                }
181            } else {
182                _expressionSocket.getConnectedSocket().setup();
183            }
184
185            if ( !_actionSocket.isConnected()
186                    || !_actionSocket.getConnectedSocket().getSystemName()
187                            .equals(_actionSocketSystemName)) {
188
189                String socketSystemName = _actionSocketSystemName;
190                _actionSocket.disconnect();
191                if (socketSystemName != null) {
192                    MaleSocket maleSocket =
193                            InstanceManager.getDefault(DigitalBooleanActionManager.class)
194                                    .getBySystemName(socketSystemName);
195                    _actionSocket.disconnect();
196                    if (maleSocket != null) {
197                        _actionSocket.connect(maleSocket);
198                        maleSocket.setup();
199                    } else {
200                        log.error("cannot load digital boolean action {}", socketSystemName);
201                    }
202                }
203            } else {
204                _actionSocket.getConnectedSocket().setup();
205            }
206        } catch (SocketAlreadyConnectedException ex) {
207            // This shouldn't happen and is a runtime error if it does.
208            throw new RuntimeException("socket is already connected");
209        }
210    }
211
212    /** {@inheritDoc} */
213    @Override
214    public void registerListenersForThisClass() {
215    }
216
217    /** {@inheritDoc} */
218    @Override
219    public void unregisterListenersForThisClass() {
220    }
221
222    /** {@inheritDoc} */
223    @Override
224    public void disposeMe() {
225    }
226
227
228    /**
229     * The type of Action. If the type is changed, the action is aborted if it
230     * is currently running.
231     */
232    public enum ExecuteType {
233        /**
234         * The "then" or "else" action is executed when the expression changes
235         * its result. If the expression has returned "false", but now returns
236         * "true", the "then" action is executed. If the expression has
237         * returned "true", but now returns "false", the "else" action is executed.
238         */
239        ExecuteOnChange(Bundle.getMessage("Logix_ExecuteOnChange")),
240
241        /**
242         * The "then" or "else" action is always executed when this action is
243         * executed. If the expression returns "true", the "then" action is
244         * executed. If the expression returns "false", the "else" action is
245         * executed.
246         */
247        ExecuteAlways(Bundle.getMessage("Logix_ExecuteAlways"));
248
249        private final String _text;
250
251        private ExecuteType(String text) {
252            this._text = text;
253        }
254
255        @Override
256        public String toString() {
257            return _text;
258        }
259
260    }
261
262
263    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(Logix.class);
264
265}