001package jmri.jmrix.loconet.alm.almi;
002
003import jmri.jmrix.loconet.LnConstants;
004import jmri.jmrix.loconet.LocoNetMessage;
005import jmri.jmrix.loconet.alm.Alm;
006
007//import org.slf4j.Logger;
008//import org.slf4j.LoggerFactory;
009
010/**
011 * ALM Interpretation for Routes
012 *
013 * @author Bob Milhaupt  Copyright (C) 2022
014 */
015public class Almir {
016    private Almir () {
017        throw new IllegalStateException("Utility class"); // NOI18N
018    }
019
020    static String interpretAlmRoutes(LocoNetMessage l) {
021        if ((l.getElement(2) != 1) && (l.getElement(2) != 2)) {
022            return EMPTY;
023        }
024        if (l.getNumDataElements() != 0x10) {
025            return EMPTY;
026        }
027        if (l.equals(DESEL)) {
028            return Bundle.getMessage("MSG_LN_ALM_DEVICE_DESEL");
029        }
030
031        String ret;
032        ret = cardq(l);
033        if (ret.length() > 1) {
034            return ret;
035        }
036        String key = EMPTY;
037        if ((l.getOpCode() == LnConstants.OPC_ALM_READ) && (l.getElement(3) == 0x02)) {
038            if (l.getElement(2) == 1) {
039                key = "LN_MSG_ALM_ROUTE_CMD_STN_REPORT"; // NOI18N
040            } else if (l.getElement(2) != 2) {
041                return EMPTY;
042            } else {
043                key = "LN_MSG_ALM_ROUTE_DEV_REPORT"; // NOI18N
044            }
045        } else if ((l.getOpCode() == LnConstants.OPC_IMM_PACKET_2) && (l.getElement(3) == 3)) {
046            if (l.getElement(2) == 1) {
047                key = "LN_MSG_ALM_ROUTE_CMD_STN_WRITE"; // NOI18N
048            } else if (l.getElement(2) != 2) {
049                return EMPTY;
050            } else {
051                key = "LN_MSG_ALM_ROUTE_DEV_WRITE"; // NOI18N
052            }
053        }
054        if (key.length() > 1) {
055            return Bundle.getMessage(key,
056                    1 + (((l.getElement(4) + l.getElement(5)*128)/2) & 0x7f),
057                    1 + ((l.getElement(4) & 0x1)<< 2),
058                    4 + ((l.getElement(4) & 0x1)<< 2),
059                    1 + (((l.getElement(4) + l.getElement(5)*128)/4) & 0x3F),
060                    1 + ((l.getElement(4) & 0x3) << 2),
061                    4 + ((l.getElement(4) & 0x3) << 2),
062                    getTurnoutNum(l, 0), getTurnoutStat(l, 0),
063                    getTurnoutNum(l, 1), getTurnoutStat(l, 1),
064                    getTurnoutNum(l, 2), getTurnoutStat(l, 2),
065                    getTurnoutNum(l, 3), getTurnoutStat(l, 3));
066        }
067
068        ret = checkarcq(l);
069        if (ret.length() > 1) {
070            return ret;
071        }
072
073        ret = checkCsrc1(l);
074        if (ret.length()>1) {
075            return ret;
076        }
077
078        ret = checkCsrc2(l);
079        if (ret.length()>1) {
080            return ret;
081        }
082
083        ret = dealWithAlmStyle2(l);
084        if (ret.length() > 1) {
085            return ret;
086        }
087
088        return EMPTY;
089    }
090
091    private static String checkarcq(LocoNetMessage l) {
092        if ((l.equals(almRouteCapabilitiesQuery)) || (l.equals(almRouteCapabilitiesQuery2))) {
093            return Bundle.getMessage("LN_MSG_ALM_RTS_CAP_Q");
094        }
095        return EMPTY;
096    }
097
098    private static String cardq(LocoNetMessage l) {
099        if (l.equals(AlmRoutesDataQuery, ardqm)) {
100            return Bundle.getMessage("LN_MSG_CMD_STN_ROUTE_QUERY",
101                    getRouteNum(l),
102                    getTurnoutGroup(l),
103                    (getTurnoutGroup(l) + 3),
104                    getAltRouteNum(l),
105                    getAltTurnoutGroup(l),
106                    (getAltTurnoutGroup(l) + 3));
107            }
108        return EMPTY;
109    }
110
111    private static int getTurnoutGroup(LocoNetMessage l) {
112        return 1 + ((l.getElement(4) & 0x1)<< 2);
113    }
114
115    private static int getAltTurnoutGroup(LocoNetMessage l) {
116        return 1 + ((l.getElement(4) & 0x3) << 2);
117    }
118
119    private static int getRouteNum(LocoNetMessage l) {
120        return 1 + (((l.getElement(4) + l.getElement(5)*128)/2) & 0x7f);
121    }
122
123    private static int getAltRouteNum(LocoNetMessage l) {
124        return 1 + (((l.getElement(4) + l.getElement(5)*128)/4) & 0x3F);
125    }
126
127    private static String checkCsrc1 (LocoNetMessage l) {
128        if (l.equals(cmdStnRoutesCap)) {
129            return Bundle.getMessage("LN_MSG_ALM_RTS_CAP_R");
130        }
131        return EMPTY;
132    }
133
134    private static String checkCsrc2 (LocoNetMessage l) {
135        if (l.equals(cmdStnRoutesCap2)) {
136            return Bundle.getMessage("LN_MSG_ALM_RTS_CAP_R2");
137        }
138        return EMPTY;
139    }
140
141    private static String getTurnoutNum(LocoNetMessage l, int num) {
142        if ((l.getElement(7+(num*2)) == 0x7f) && (l.getElement(8+(num*2)) == 0x7f)) {
143            return Bundle.getMessage("LN_ROUTE_UNUSED_ENTRY_HELPER");
144        }
145        if ((num < 0) || (num > 3)) {
146            throw new java.lang.IllegalArgumentException();
147        }
148        int val = 1 + l.getElement(7+(num*2)) + ((l.getElement(8+(num*2)) & 15)<<7);
149        return Integer.toString(val);
150    }
151
152    private static String getTurnoutStat(LocoNetMessage l, int num) {
153        if ((l.getElement(7+(num*2)) == 0x7f) && (l.getElement(8+(num*2)) == 0x7f)) {
154            return "";
155        }
156        if ((num <0) || (num > 3)) {
157            throw new java.lang.IllegalArgumentException();
158        }
159        boolean isClosed;
160        isClosed = (l.getElement(8 + (num*2)) & 0x20) == 0x20;
161        return isClosed ? Bundle.getMessage("LN_SW_CLOSED")
162                        :Bundle.getMessage("LN_SW_THROWN");
163    }
164
165    private static String dealWithAlmStyle2(LocoNetMessage l) {
166        int sn = getSN(l);
167        String ser = Integer.toHexString(sn);
168        int bs = getBS(l); // starting address
169        int be;            // ending address
170        String enable;
171        boolean enb = getEnb(l);
172        enable = (enb ? Bundle.getMessage("LN_MSG_HELPER_DISABLED")
173                : Bundle.getMessage("LN_MSG_HELPER_ENABLED"));
174        DevMode mod = getMode(l);
175        String mode;
176        String dev;
177        int rts;            // number of routes
178        int ents;           // number of entries in routes
179        switch (l.getElement(9)) {
180            case LnConstants.RE_IPL_DIGITRAX_HOST_DS74:
181                dev = "DS74"; //NOI18N
182                rts = 8;
183                ents = 8;
184                be = ((mod == DevMode.DS74_LIGHT)?(bs + 7):(bs + 3));
185                break;
186            case LnConstants.RE_IPL_DIGITRAX_HOST_DS78V:
187                dev = "DS78V"; //NOI18N
188                rts = 16;
189                ents = 8;
190                be = ((mod == DevMode.DS78V_3_POS)?(bs + 15):(bs + 7));
191                break;
192            case LnConstants.RE_IPL_DIGITRAX_HOST_SE74:
193                dev = "SE74"; //NOI18N
194                rts = 64;
195                ents = 16;
196                be = bs + 36;
197                break;
198            case LnConstants.RE_IPL_DIGITRAX_HOST_PM74:
199                dev = "PM74"; //NOI18N
200                rts = 0;
201                ents = 0;
202                be = bs + 7;
203                break;
204
205            case LnConstants.RE_IPL_DIGITRAX_HOST_BDL716:
206                dev = "BDL716"; //NOI18N
207                rts = 0;
208                ents = 0;
209                be = bs + 15;
210                break;
211
212            default:
213                dev = Bundle.getMessage("LN_MSG_ALM_HELPER_DEVICE_UNKNOWN");
214                be = bs;
215                rts = 0;
216                ents = 0;
217        }
218
219        int rn = 1 + (l.getElement(4) / 2) + ((l.getElement(5) & 3)<<6);
220        int re = ((l.getElement(4)& 1) == 1)?5:1;
221        if (Alm.isDs7xRQ(l)) {
222            // This code (and associated key/value pair) will require update if
223            // any device is introduced which supports ALM-based routes with anything
224            // other than 16 entries.
225            return Bundle.getMessage("LN_MSG_ALM_SEL_ROUTE_QUERY", rn, re, re+3);
226        }
227
228        if (Alm.isDs74CapsRpt(l) || Alm.isDs78vCapsRpt(l) ||
229                Alm.isSe74CapsRpt(l) || Alm.isPm74CapsRpt(l) ) {
230            if (Alm.isDs74CapsRpt(l) || Alm.isDs78vCapsRpt(l) ) {
231                switch ((l.getElement(10) & 0x1e) >>1) {
232                    case 0:
233                        mode = "LN_MSG_ALM_HELPER_DEV_MODE_PS"; // NOI18N
234                        be = bs + 3;
235                        break;
236                    case 1:
237                        mode = "LN_MSG_ALM_HELPER_DEV_MODE_SM"; // NOI18N
238                        be = bs + 3;
239                        break;
240                    case 2:
241                        mode = "LN_MSG_ALM_HELPER_DEV_MODE_S2"; // NOI18N
242                        be = bs + 7;
243                        break;
244                    case 5:
245                        mode = "LN_MSG_ALM_HELPER_DEV_MODE_LT"; // NOI18N
246                        be = bs + 7;
247                        break;
248                    case 6:
249                        mode = "LN_MSG_ALM_HELPER_DEV_MODE_S3"; // NOI18N
250                        be = bs + 15;
251                        break;
252                    default:
253                        mode = "LN_MSG_ALM_HELPER_DEV_MODE_UNDEF"; // NOI18N
254                        be = bs;
255                        break;
256                }
257            } else if (Alm.isSe74CapsRpt(l)) { // element 10 observed at 0
258                mode = "LN_MSG_ALM_HELPER_DEV_MODE_UNDEF"; // NOI18N
259                // addressing has already been set above
260            } else if (Alm.isPm74CapsRpt(l)) { // element 10 observed at 0
261                mode = "LN_MSG_ALM_HELPER_DEV_MODE_UNDEF"; // NOI18N
262                // addressing has already been set above
263            } else {
264                be = bs;  // only show one address
265                mode = "LN_MSG_ALM_HELPER_DEV_MODE_UNDEF"; // NOI18N
266            }
267
268            mode = Bundle.getMessage(mode);
269
270            if (Alm.isPm74CapsRpt(l)) {
271                return Bundle.getMessage("LN_MSG_DEVICE_NO_ROUTES_CAPABILITIES_REPLY",
272                   dev, ser, bs );
273            }
274
275            return Bundle.getMessage("LN_MSG_DEVICE_ROUTES_CAPABILITIES_REPLY",
276                   dev, ser, mode, enable, bs, be, rts, ents );
277        }
278
279        LocoNetMessage seldev = new LocoNetMessage(new int[] {
280            LnConstants.OPC_IMM_PACKET_2, 0x10, 0x02, 0x0e, 0,0,0,0,0,0,0,0,0,0,0,0
281        });
282        int sdm[] = {255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0};
283        if (l.equals(seldev, sdm)) {
284            return Bundle.getMessage("LN_MSG_DEVICE_ROUTES_SELECT_REQUEST",
285                    dev, ser, bs, be);
286        }
287        seldev.setElement(0, LnConstants.OPC_ALM_READ);
288        sdm[4] = 0; sdm[5] = 0; sdm[6] = 0; sdm[7] = 0; sdm[8] = 0;
289        if (l.equals(seldev, sdm)) {
290           return Bundle.getMessage("LN_MSG_DEV_ROUTES_SELECT_REPLY", dev, ser, Integer.toString(bs), Integer.toString(be));
291        }
292
293        if (Alm.isDevBAW(l)) {
294            // change starting turnout address
295            return Bundle.getMessage("LN_MSG_ALM_DEVICE_CHG_SA", dev, ser, Integer.toString(bs));
296        }
297
298        return EMPTY;
299    }
300
301    private static int getSN(LocoNetMessage l) {
302        return l.getElement(11) + (l.getElement(12)<<7);
303    }
304
305    private static int getBS(LocoNetMessage l) {
306        return l.getElement(13) + (l.getElement(14)<<7) + 1;
307    }
308
309    private static boolean getEnb(LocoNetMessage l) {
310        return (l.getElement(10)& 0x40) == 0x40;
311    }
312    private static DevMode getMode(LocoNetMessage l) {
313        if (l.getElement(9) == LnConstants.RE_IPL_DIGITRAX_HOST_DS74) {
314            switch (l.getElement(10) & 0x1E) {
315                case 0:
316                    return DevMode.DS74_SOLE;
317                case 2:
318                    return DevMode.DS74_STALL;
319                case 0xA:
320                    return DevMode.DS74_LIGHT;
321                default:
322                    break;
323            }
324        } else if (l.getElement(9) == LnConstants.RE_IPL_DIGITRAX_HOST_DS78V) {
325            switch (l.getElement(10) & 0xF) {
326                case 4:
327                    return DevMode.DS78V_2_POS;
328                case 0xC:
329                    return DevMode.DS78V_3_POS;
330                default:
331                    break;
332            }
333        }
334        return DevMode.UNKN;
335    }
336
337    private static final String EMPTY = "";
338
339    private static final LocoNetMessage DESEL = new LocoNetMessage(new int[] {
340    LnConstants.OPC_IMM_PACKET_2, 0x10, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3});
341
342    private static final LocoNetMessage almRouteCapabilitiesQuery = new LocoNetMessage(new int[] {
343        LnConstants.OPC_IMM_PACKET_2, 0x10, 1, 0, 0, 0, 0, 0,
344        0, 0, 0, 0, 0, 0, 0, 0});
345
346    private static final LocoNetMessage almRouteCapabilitiesQuery2 = new LocoNetMessage(new int[] {
347        LnConstants.OPC_IMM_PACKET_2, 0x10, 1, 0, 0, 0, 15, 0,
348        0, 0, 0, 0, 0, 0, 0, 0});
349
350    private static final LocoNetMessage cmdStnRoutesCap = new LocoNetMessage(new int[] {
351            LnConstants.OPC_ALM_READ, 0x10, 1, 0, 0x40, 0, 3, 2, 8,
352            0x7F, 0, 0, 0, 0, 0, 0x64});
353
354    private static final LocoNetMessage cmdStnRoutesCap2 = new LocoNetMessage(new int[] {
355            LnConstants.OPC_ALM_READ, 0x10, 1, 0, 0, 2, 3, 2, 0x10,
356            0x7F, 0, 0, 0, 0, 0, 0x64});
357
358    private static final LocoNetMessage AlmRoutesDataQuery = new LocoNetMessage(new int[] {
359        LnConstants.OPC_IMM_PACKET_2, 0x10, 1, 2, 0, 0, 0, 0,
360        0, 0, 0, 0, 0, 0, 0, 0});
361
362    private static final int[] ardqm = new int[] {255, 255, 255, 255, 0, 0, 0, 0,
363        0, 0, 0, 0, 0, 0, 0, 0};
364
365    private enum DevMode {
366        DS74_SOLE,
367        DS74_STALL,
368        DS74_LIGHT,
369        DS78V_2_POS,
370        DS78V_3_POS,
371        UNKN
372    }
373//    private final static Logger log = LoggerFactory.getLogger(Almir.class);
374
375}