org.bidib.jbidibc.ch341a.lococard.LocoCardDecoder Maven / Gradle / Ivy
package org.bidib.jbidibc.ch341a.lococard;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Arrays;
import javax.imageio.ImageIO;
import org.apache.commons.lang3.StringUtils;
import org.bidib.jbidibc.messages.utils.ByteUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@code LocoCardDecoder} decodes the data of the M*rklin and Trix loco card.
*
* The main work was done by Gerhard Bertelsmann () and I owe him a beer ... {@link https
* ://github.com/GBert/railroad/blob/master/can2udp/src/sc/read_loccard.c}
*
*/
public class LocoCardDecoder {
private static final Logger LOGGER = LoggerFactory.getLogger(LocoCardDecoder.class);
// preamble for mfx
private static final byte[] PRE_MFX = { 0x02, (byte) 0xf5, 0x00 };
// preamble for other protocols
private static final byte[] PRE_OTHER = { 0x02, (byte) 0xc5, 0x00 };
private static final byte[] MAGIC_NUMBER_PNG = new byte[] { (byte) 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
/**
* Decode the data of a loco card.
*
* @param rawLocoCard
* the data from the loco card
* @param imageDir
* the image dir to store the filename
*/
public void decodeLocoCardData(byte[] rawLocoCard, final File imageDir) {
// check the preamble to distinguish between mfx and dcc
byte[] preamble = ByteUtils.subArray(rawLocoCard, 0, 3);
if (Arrays.equals(PRE_MFX, preamble)) {
LOGGER.info("Loco card is of type: mfx");
}
else if (Arrays.equals(PRE_OTHER, preamble)) {
LOGGER.info("Loco card is of type: other");
}
else {
LOGGER.warn("The loco card has an unknown preamble: {}", ByteUtils.bytesToHex(preamble));
throw new RuntimeException("The loco card has an unknown preamble: " + ByteUtils.bytesToHex(preamble));
}
String locoPictureName = null;
// start index after preamble
int i = 3;
int index = 0;
int length = 0;
int temp = 0;
int id = 0;
while (i < rawLocoCard.length) {
index = ByteUtils.getInt(rawLocoCard[i++]);
length = ByteUtils.getInt(rawLocoCard[i++]);
switch (index) {
case 0:
LOGGER.info(String.format("index [0x%02x @ 0x%04x] sub-indexes [%d]: ", index, i, length));
temp = ByteUtils.getInt(rawLocoCard[i++]);
length = (ByteUtils.getInt(rawLocoCard[i++]) << 8) + temp;
LOGGER.info(" total length [{}]", length);
id = ByteUtils.getInt(rawLocoCard[i++]);
while ((id != 0) && (id != 255)) {
length = rawLocoCard[i++];
LOGGER
.info(String
.format("i 0x%02x [i] 0x%02x length %d, id: %02x\n", i, rawLocoCard[i], length, id));
switch (id) {
case 0x1e:
// get the loco name
byte[] rawLocoName = ByteUtils.subArray(rawLocoCard, i, length);
String locoName = new String(rawLocoName, Charset.forName("ISO-8859-1"));
LOGGER.info("loco name: >{}<", locoName);
i += length;
break;
case 0x1f:
// get the proto name
byte[] rawProtoName = ByteUtils.subArray(rawLocoCard, i, length);
String protoName = new String(rawProtoName, Charset.forName("ISO-8859-1"));
LOGGER.info("proto name: >{}<", protoName);
i += length;
break;
case 0x20:
// get the picture name
byte[] rawPictureName = ByteUtils.subArray(rawLocoCard, i, length);
String pictureName = new String(rawPictureName, Charset.forName("ISO-8859-1"));
LOGGER.info("picture name: >{}<", pictureName);
locoPictureName = pictureName;
i += length;
break;
default:
// LOGGER.warn("decoding problem:\n");
byte[] rawName = ByteUtils.subArray(rawLocoCard, i, length);
String name = new String(rawName, Charset.forName("ISO-8859-1"));
LOGGER.info("unknown name: >{}<", name);
i += length;
break;
}
id = rawLocoCard[i++];
LOGGER.info(String.format("Next id: %02x", id));
}
break;
case 9:
LOGGER.info(String.format("index [0x%02x @ 0x%04x] length [%d]: ", index, i, length));
int func = 0;
LOGGER.info("\n");
for (int j = 0; j < length / 10; j++) {
StringBuilder sb = new StringBuilder();
sb.append(String.format(" function %2d: ", func++));
for (int k = 0; k < 10; k++) {
sb.append(String.format(" 0x%02x", rawLocoCard[i++]));
}
LOGGER.info("{}", sb);
}
break;
default:
LOGGER.info(String.format("index [0x%02x @ 0x%04x] length [%d]: ", index, i, length));
byte[] sub = null;
if (length <= 4) {
LOGGER.info("Len is <= 4");
sub = ByteUtils.subArray(rawLocoCard, i, length);
}
switch (index) {
case 1:
// loco_data->long_uid = temp;
LOGGER.info(" long UID : {}", ByteUtils.bytesToHex(sub));
break;
case 2:
// loco_data->uid = temp;
LOGGER.info(" short UID : {}", ByteUtils.bytesToHex(sub));
break;
case 3:
// loco_data->acc_delay = temp;
LOGGER.info("acceleration delay : {}", ByteUtils.bytesToHex(sub));
break;
case 4:
// loco_data->slow_down_delay = temp;
LOGGER.info(" slow down delay : {}", ByteUtils.bytesToHex(sub));
break;
case 5:
// loco_data->vmin = temp;
LOGGER.info(" Vmin : {}", ByteUtils.bytesToHex(sub));
break;
case 6:
// loco_data->vmax = temp;
LOGGER.info(" Vmax : {}", ByteUtils.bytesToHex(sub));
break;
case 8:
// loco_data->volume = temp;
LOGGER.info(" Volume : {}", ByteUtils.bytesToHex(sub));
break;
default:
LOGGER.info(" unknown : {}", ByteUtils.bytesToHex(sub));
break;
}
LOGGER.info("raw value: {}", ByteUtils.bytesToHex(ByteUtils.subArray(rawLocoCard, i, length)));
i += length;
break;
}
LOGGER.info("Current index: {}, current i: {}", index, i);
if (index == 0) {
int totalLen = rawLocoCard.length;
int startIndex = i + 3;
LOGGER
.info("Try to read image from startIndex: {}, totalLen: {}, byte: {}", startIndex, totalLen,
ByteUtils.byteToHex(rawLocoCard[startIndex]));
// check if the picture starts with magic number for png
byte[] check = ByteUtils.subArray(rawLocoCard, startIndex, 8);
if (!Arrays.equals(check, MAGIC_NUMBER_PNG)) {
LOGGER.warn("No valid picture data found!");
}
else {
if (StringUtils.isBlank(locoPictureName)) {
locoPictureName = "locoimage";
}
locoPictureName = locoPictureName + ".png";
try (ByteArrayInputStream bais =
new ByteArrayInputStream(ByteUtils.subArray(rawLocoCard, startIndex, totalLen - startIndex))) {
BufferedImage locoImage = ImageIO.read(bais);
ImageIO.write(locoImage, "png", new File(imageDir, locoPictureName));
}
catch (IOException ex) {
LOGGER.warn("Load loco image failed.", ex);
}
}
break;
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy