001package jmri.jmrix.dccpp.swing.virtuallcd;
002
003import java.awt.*;
004import java.io.*;
005import java.util.ArrayList;
006
007import javax.swing.*;
008
009import jmri.jmrix.dccpp.*;
010import jmri.util.JmriJFrame;
011
012/**
013 * Frame to image the DCC-EX command station's OLED display
014 *   Also sends request to DCC-EX to send copies of all LCD messages to this instance of JMRI
015 *
016 * @author BobJacobsen  Copyright (C) 2023
017 * @author MSteveTodd   Copyright (C) 2023
018 */
019public class VirtualLCDFrame extends JmriJFrame implements DCCppListener  {
020
021    private DCCppTrafficController _tc = null;
022    private DCCppSystemConnectionMemo _memo;
023
024    final static int TOTALLINES = 64;
025    private ArrayList<JLabel> lines;
026    
027    public VirtualLCDFrame(DCCppSystemConnectionMemo memo) {
028        super();
029        _tc = memo.getDCCppTrafficController();
030        _memo = memo;
031        _tc.sendDCCppMessage(DCCppMessage.makeLCDRequestMsg(), null);        
032        lines = new ArrayList<>(TOTALLINES + 1);
033    }
034
035    /** 
036     * {@inheritDoc}
037     */
038    @Override
039    public void message(DCCppMessage msg) {
040    }
041    
042    /** 
043     * {@inheritDoc}
044     */
045    @Override
046    public void message(DCCppReply msg) {
047        if (msg.isLCDTextReply()) { // <@ display# line# "message text">
048            int displayNumber = msg.getLCDDisplayNumInt();
049            if (displayNumber == 0) {  //TODO: add support for multiple LCD displays
050                int lineNumber = msg.getLCDLineNumInt();
051                if (lineNumber < TOTALLINES) {
052                    lines.get(lineNumber).setText(msg.getLCDTextString()+"   "); // padding for appearance
053                    pack(); 
054                } else {
055                    log.warn("Received LCD message for line {}, but configured for TOTALLINES limit of {}", 
056                                lineNumber, TOTALLINES-1);
057                }
058                log.debug("Received LCD message for display# {}, only display 0 supported at this time.", displayNumber);
059            } 
060        }
061    }
062    
063    /** 
064     * {@inheritDoc}
065     */
066    @Override
067    public void notifyTimeout(DCCppMessage msg) {
068    }
069    
070    /**
071     * {@inheritDoc}
072     */
073    @Override
074    public void initComponents() {
075        super.initComponents();
076        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
077        
078        Font font = null;
079        // load the custom 5x8 found
080        try { 
081            InputStream stream = new FileInputStream(new File("resources/fonts/5x8_lcd_hd44780u_a02.ttf"));
082            font = Font.createFont(Font.TRUETYPE_FONT, stream).deriveFont(16f).deriveFont(Font.BOLD);
083        } catch (IOException e1) { log.error("failed to find or open font file");
084        } catch (FontFormatException e2) { log.error("font file not valid");
085        }
086        
087        var pane = new JPanel();
088        pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));
089        // initialize the list of display lines
090        for (int i = 0; i<TOTALLINES; i++) {
091            var label = new JLabel();
092            if (font != null) label.setFont(font);
093            label.setOpaque(true);
094            label.setBackground(Color.BLACK);
095            label.setForeground(Color.WHITE);
096            lines.add(label);
097            pane.add(lines.get(i));
098        }
099        pane.setOpaque(true);
100        pane.setBackground(Color.BLACK);
101        this.add(pane);
102        
103        // set the title, include prefix in event of multiple connections 
104        setTitle(Bundle.getMessage("VirtualLCDFrameTitle") + " (" + _memo.getSystemPrefix() + ")");
105        
106        // pack to layout display
107        pack();
108    }
109   
110    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(VirtualLCDFrame.class);
111
112}