001package jmri.jmrix.can.adapters.gridconnect.canrs; 002 003import jmri.jmrix.can.CanMessage; 004import jmri.jmrix.can.adapters.gridconnect.GridConnectMessage; 005 006/** 007 * Class for messages for a MERG CAN-RS hardware adapter. 008 * <p> 009 * The MERG variant of the GridConnect protocol encodes messages as an ASCII 010 * string of up to 24 characters of the form: :ShhhhNd0d1d2d3d4d5d6d7; hhhh is 011 * the two byte (11 useful bits) header The S indicates a standard CAN frame 012 * :XhhhhhhhhNd0d1d2d3d4d5d6d7; The X indicates an extended CAN frame Strict 013 * Gridconnect protocol allows a variable number of header characters, e.g., a 014 * header value of 0x123 could be encoded as S123 rather than S0123 or 015 * X00000123. We choose a fixed number, either 4 or 8 bytes when sending 016 * GridConnectMessages to keep MERG CAN-RS/USB adapters happy. The 11 bit 017 * standard header is left justified in these 4 bytes. The 29 bit standard 018 * header is sent as {@code <11 bit SID><0><1><0>< 18 bit EID>} 019 * N or R indicates a normal or remote frame, in position 6 or 10 d0 - d7 are 020 * the (up to) 8 data bytes 021 * 022 * @author Andrew Crosland Copyright (C) 2008 023 */ 024public class MergMessage extends GridConnectMessage { 025 026 // Creates a new instance of GridConnectMessage 027 public MergMessage() { 028 super(); 029 } 030 031 public MergMessage(CanMessage m) { 032 this(); 033 034 // Standard or extended frame 035 setExtended(m.isExtended()); 036 037 // Copy the header 038 MergMessage.this.setHeader(m.getHeader()); 039 040 // Normal or Remote frame? 041 MergMessage.this.setRtr(m.isRtr()); 042 043 // Data payload 044 for (int i = 0; i < m.getNumDataElements(); i++) { 045 MergMessage.this.setByte(m.getElement(i), i); 046 } 047 // Terminator 048 int offset = isExtended() ? 11 : 7; // differs here from superclass 049 setElement(offset + m.getNumDataElements() * 2, ';'); 050 setNumDataElements(offset + 1 + m.getNumDataElements() * 2); 051 log.debug("encoded as {}", MergMessage.this); 052 } 053 054 /** 055 * Set the header in MERG format 056 * 057 * @param header A valid CAN header value 058 */ 059 @Override 060 public void setHeader(int header) { 061 int munged; 062 if (isExtended()) { 063 munged = ((header << 3) & 0xFFE00000) | 0x80000 | (header & 0x3FFFF); 064 log.debug("Extended header is {}, munged header is {}", header, munged); 065 super.setHeader(munged); 066 } else { 067 // 11 header bits are left justified 068 munged = header << 5; 069 log.debug("Standard header is {}, munged header is {}", header, munged); 070 // Can't use super() as we want to send 4 digits 071 setHexDigit((munged >> 12) & 0xF, 2); 072 setHexDigit((munged >> 8) & 0xF, 3); 073 setHexDigit((munged >> 4) & 0xF, 4); 074 setHexDigit(0, 5); 075 } 076 } 077 078 @Override 079 public void setRtr(boolean rtr) { 080 int offset = isExtended() ? 10 : 6; 081 setElement(offset, rtr ? 'R' : 'N'); 082 } 083 084 /** 085 * Set a byte as two ASCII hex digits 086 * <p> 087 * Data bytes are encoded as two ASCII hex digits starting at byte 7 of the 088 * message. 089 * 090 * @param val the value to set, must be in range 0 - 255 091 * @param n the index of the byte to be set 092 */ 093 @Override 094 public void setByte(int val, int n) { 095 if ((n >= 0) && (n <= 7)) { 096 int index = n * 2 + (isExtended() ? 11 : 7); // differs here from superclass 097 if ((val < 0) || (val > 255)) { 098 log.error("Byte value {} out of range 0-255 for MergMessage data payload", val, new IllegalArgumentException()); 099 val = 0; 100 } 101 setHexDigit((val / 16) & 0xF, index++); 102 setHexDigit(val & 0xF, index); 103 return; 104 } 105 log.error("Byte Index {} out of range 0-7 for MergMessage data payload", n, new IllegalArgumentException()); 106 } 107 108 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(MergMessage.class); 109}