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 + 35;
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                Alm.isBdl716CapsRpt(l)) {
231            if (Alm.isDs74CapsRpt(l) || Alm.isDs78vCapsRpt(l) ) {
232                switch ((l.getElement(10) & 0x1e) >>1) {
233                    case 0:
234                        mode = "LN_MSG_ALM_HELPER_DEV_MODE_PS"; // NOI18N
235                        be = bs + 3;
236                        break;
237                    case 1:
238                        mode = "LN_MSG_ALM_HELPER_DEV_MODE_SM"; // NOI18N
239                        be = bs + 3;
240                        break;
241                    case 2:
242                        mode = "LN_MSG_ALM_HELPER_DEV_MODE_S2"; // NOI18N
243                        be = bs + 7;
244                        break;
245                    case 5:
246                        mode = "LN_MSG_ALM_HELPER_DEV_MODE_LT"; // NOI18N
247                        be = bs + 7;
248                        break;
249                    case 6:
250                        mode = "LN_MSG_ALM_HELPER_DEV_MODE_S3"; // NOI18N
251                        be = bs + 15;
252                        break;
253                    default:
254                        mode = "LN_MSG_ALM_HELPER_DEV_MODE_UNDEF"; // NOI18N
255                        be = bs;
256                        break;
257                }
258            } else if (Alm.isSe74CapsRpt(l)) {
259                if (((l.getElement(10) & 0x3e) >>1) == 0x10) {
260                    mode = "LN_MSG_ALM_HELPER_DEV_MODE_ASPECT";
261                    be = bs + 3;
262                } else if (((l.getElement(10) & 0x3E) >>1) == 0x00) {
263                    mode = "LN_MSG_ALM_HELPER_DEV_MODE_SIG"; // NOI18N
264                    // addressing has already been set above
265                } else {
266                    mode = "LN_MSG_ALM_HELPER_DEV_MODE_UNDEF";
267                    // assume addressing has already been set above
268                } // NOI18N
269            } else if (Alm.isPm74CapsRpt(l)) { // element 10 observed at 0
270                mode = "LN_MSG_ALM_HELPER_DEV_MODE_UNDEF"; // NOI18N
271                // addressing has already been set above
272            } else if (Alm.isBdl716CapsRpt(l)) {
273                 mode = "LN_MSG_ALM_HELPER_DEV_MODE_NOT_APPLICABLE"; // NOI18N
274                 
275                // addressing has already been set above
276            } else {
277                be = bs;  // only show one address
278                mode = "LN_MSG_ALM_HELPER_DEV_MODE_UNDEF"; // NOI18N
279            }
280
281            mode = Bundle.getMessage(mode);
282
283            if ((Alm.isPm74CapsRpt(l)) || (Alm.isBdl716CapsRpt(l))) {
284                return Bundle.getMessage("LN_MSG_DEVICE_NO_ROUTES_CAPABILITIES_REPLY",
285                   dev, ser, bs );
286            }
287
288            return Bundle.getMessage("LN_MSG_DEVICE_ROUTES_CAPABILITIES_REPLY",
289                   dev, ser, mode, enable, bs, be, rts, ents );
290        }
291
292        LocoNetMessage seldev = new LocoNetMessage(new int[] {
293            LnConstants.OPC_IMM_PACKET_2, 0x10, 0x02, 0x0e, 0,0,0,0,0,0,0,0,0,0,0,0
294        });
295        int sdm[] = {255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0};
296        if (l.equals(seldev, sdm)) {
297            return Bundle.getMessage("LN_MSG_DEVICE_ROUTES_SELECT_REQUEST",
298                    dev, ser, bs, be);
299        }
300        seldev.setElement(0, LnConstants.OPC_ALM_READ);
301        sdm[4] = 0; sdm[5] = 0; sdm[6] = 0; sdm[7] = 0; sdm[8] = 0;
302        if (l.equals(seldev, sdm)) {
303           return Bundle.getMessage("LN_MSG_DEV_ROUTES_SELECT_REPLY", dev, ser, Integer.toString(bs), Integer.toString(be));
304        }
305
306        if (Alm.isDevBAW(l)) {
307            // change starting turnout address
308            return Bundle.getMessage("LN_MSG_ALM_DEVICE_CHG_SA", dev, ser, Integer.toString(bs));
309        }
310
311        return EMPTY;
312    }
313
314    private static int getSN(LocoNetMessage l) {
315        return l.getElement(11) + (l.getElement(12)<<7);
316    }
317
318    private static int getBS(LocoNetMessage l) {
319        return l.getElement(13) + (l.getElement(14)<<7) + 1;
320    }
321
322    private static boolean getEnb(LocoNetMessage l) {
323        return (l.getElement(10)& 0x40) == 0x40;
324    }
325    private static DevMode getMode(LocoNetMessage l) {
326        if (l.getElement(9) == LnConstants.RE_IPL_DIGITRAX_HOST_DS74) {
327            switch (l.getElement(10) & 0x1E) {
328                case 0:
329                    return DevMode.DS74_SOLE;
330                case 2:
331                    return DevMode.DS74_STALL;
332                case 0xA:
333                    return DevMode.DS74_LIGHT;
334                default:
335                    break;
336            }
337        } else if (l.getElement(9) == LnConstants.RE_IPL_DIGITRAX_HOST_DS78V) {
338            switch (l.getElement(10) & 0xF) {
339                case 4:
340                    return DevMode.DS78V_2_POS;
341                case 0xC:
342                    return DevMode.DS78V_3_POS;
343                default:
344                    break;
345            }
346        }
347        return DevMode.UNKN;
348    }
349
350    private static final String EMPTY = "";
351
352    private static final LocoNetMessage DESEL = new LocoNetMessage(new int[] {
353    LnConstants.OPC_IMM_PACKET_2, 0x10, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3});
354
355    private static final LocoNetMessage almRouteCapabilitiesQuery = new LocoNetMessage(new int[] {
356        LnConstants.OPC_IMM_PACKET_2, 0x10, 1, 0, 0, 0, 0, 0,
357        0, 0, 0, 0, 0, 0, 0, 0});
358
359    private static final LocoNetMessage almRouteCapabilitiesQuery2 = new LocoNetMessage(new int[] {
360        LnConstants.OPC_IMM_PACKET_2, 0x10, 1, 0, 0, 0, 15, 0,
361        0, 0, 0, 0, 0, 0, 0, 0});
362
363    private static final LocoNetMessage cmdStnRoutesCap = new LocoNetMessage(new int[] {
364            LnConstants.OPC_ALM_READ, 0x10, 1, 0, 0x40, 0, 3, 2, 8,
365            0x7F, 0, 0, 0, 0, 0, 0x64});
366
367    private static final LocoNetMessage cmdStnRoutesCap2 = new LocoNetMessage(new int[] {
368            LnConstants.OPC_ALM_READ, 0x10, 1, 0, 0, 2, 3, 2, 0x10,
369            0x7F, 0, 0, 0, 0, 0, 0x64});
370
371    private static final LocoNetMessage AlmRoutesDataQuery = new LocoNetMessage(new int[] {
372        LnConstants.OPC_IMM_PACKET_2, 0x10, 1, 2, 0, 0, 0, 0,
373        0, 0, 0, 0, 0, 0, 0, 0});
374
375    private static final int[] ardqm = new int[] {255, 255, 255, 255, 0, 0, 0, 0,
376        0, 0, 0, 0, 0, 0, 0, 0};
377
378    private enum DevMode {
379        DS74_SOLE,
380        DS74_STALL,
381        DS74_LIGHT,
382        DS78V_2_POS,
383        DS78V_3_POS,
384        UNKN
385    }
386//    private static final Logger log = LoggerFactory.getLogger(Almir.class);
387
388}