/******************************************************************************* * See the NOTICE file distributed with this work for additional information * regarding copyright ownership. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package hr.fer.zemris.vhdllab.applets.editor.newtb.view.components2; import hr.fer.zemris.vhdllab.applets.editor.newtb.enums.ChangeStateEdge; import hr.fer.zemris.vhdllab.applets.editor.newtb.enums.Radix; import hr.fer.zemris.vhdllab.applets.editor.newtb.exceptions.UniformSignalChangeException; import hr.fer.zemris.vhdllab.applets.editor.newtb.exceptions.UniformTestbenchException; import hr.fer.zemris.vhdllab.applets.editor.newtb.listeners.TestbenchListener; import hr.fer.zemris.vhdllab.applets.editor.newtb.model.CombinatorialTestbench; import hr.fer.zemris.vhdllab.applets.editor.newtb.model.SingleClockTestbench; import hr.fer.zemris.vhdllab.applets.editor.newtb.model.Testbench; import hr.fer.zemris.vhdllab.applets.editor.newtb.model.TestbenchModel; import hr.fer.zemris.vhdllab.applets.editor.newtb.model.patterns.Pattern; import hr.fer.zemris.vhdllab.applets.editor.newtb.model.signals.ClockSignal; import hr.fer.zemris.vhdllab.applets.editor.newtb.model.signals.EditableSignal; import hr.fer.zemris.vhdllab.applets.editor.newtb.model.signals.ScalarSignal; import hr.fer.zemris.vhdllab.applets.editor.newtb.model.signals.Signal; import hr.fer.zemris.vhdllab.applets.editor.newtb.model.signals.SignalChange; import hr.fer.zemris.vhdllab.applets.editor.newtb.model.signals.VectorSignal; import hr.fer.zemris.vhdllab.applets.editor.newtb.util.RadixConverter; import hr.fer.zemris.vhdllab.applets.editor.newtb.view.PatternDialog; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Graphics; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.util.ArrayList; import java.util.List; import javax.swing.BorderFactory; import javax.swing.BoundedRangeModel; import javax.swing.DefaultBoundedRangeModel; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollBar; import javax.swing.border.LineBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; /** * * @author Davor Jurisic * */ public class JTestbench extends JPanel implements TestbenchListener { public static class Communicator { public Signal signal = null; public long time = 0; public boolean isSignalRenderArea = false; public int componentIndex = 0; @Override public String toString() { return "Signal name: " + signal.getName() + " | Time: " + time + " | Component index: " + componentIndex + " | IsRenderArea: " + isSignalRenderArea; } } private ChangeListener changeListener = null; private static final boolean DEBUG_MODE = false; private static final long serialVersionUID = 1L; private static final int initNOfPeriods = 8; private static final int minPeriodWidth = 100; protected TestbenchModel model = null; protected Communicator selectedSignal = null; private JDrawArea drawArea = null; private JScrollBar horScrollBar = null; private JScrollBar verScrollBar = null; private JPopupMenu popup = null; private JMenuItem setValueMenuItem = null; private JMenuItem applyPatternMenuItem = null; private JMenuItem resetSignalMenuItem = null; private BoundedRangeModel horScrollModel = null; protected BoundedRangeModel verScrollModel = null; private int beginSignalIndex; private int nOfSignalComponents; protected long renderBeginTime; protected long renderLength; private int nOfPeriods; private long lastSignalChangeTime = 0; private SetSignalChangeValueDialog setValueDialog = null; private Radix testbenchRadix = null; public JTestbench(TestbenchModel model) { super(new BorderLayout()); this.setModel(model); this.setValueDialog = new SetSignalChangeValueDialog(); this.testbenchRadix = Radix.Binary; } public void setModel(TestbenchModel model) { this.removeAll(); if(model != null) { if(model.getTestbench().getClass() == CombinatorialTestbench.class) { if(model.getTestbench().getSignals().size() < 1) { return; } } else if(model.getTestbench().getClass() == SingleClockTestbench.class) { if(model.getTestbench().getSignals().size() <= 1) { return; } } if(this.model != null) { this.model.removeTestbenchListener(this); } this.model = model; this.model.addTestbenchListener(this); this.initGUI(); this.initListeners(); this.initDrawAreaData(); this.lastSignalChangeTime = this.model.getTestbench().getSimulationLength(); } } private void initListeners() { this.horScrollModel.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { repaint(); } }); this.verScrollModel.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { setBeginSignalIndex(verScrollModel.getValue()); } }); this.drawArea.addMouseListener(new MouseListener() { @Override public void mouseClicked(MouseEvent e) { } @Override public void mouseEntered(MouseEvent e) { } @Override public void mouseExited(MouseEvent e) { } @Override public void mousePressed(MouseEvent e) { } @Override public void mouseReleased(MouseEvent e) { if(e.getButton() == MouseEvent.BUTTON3) { showPopup(e.getPoint()); } } }); this.setValueMenuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { setValue(); } }); this.applyPatternMenuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { applyPattern(); } }); this.resetSignalMenuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { resetSignal(); } }); } private void initGUI() { this.setBorder(new LineBorder(Color.GRAY)); this.drawArea = new JDrawArea(this.model.getTestbench()); this.horScrollBar = new JScrollBar(JScrollBar.HORIZONTAL); this.horScrollModel = new DefaultBoundedRangeModel(); this.horScrollBar.setModel(this.horScrollModel); this.verScrollBar = new JScrollBar(JScrollBar.VERTICAL); this.verScrollModel = new DefaultBoundedRangeModel(); this.verScrollBar.setModel(verScrollModel); this.horScrollBar.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, verScrollBar.getPreferredSize().width)); this.add(horScrollBar, BorderLayout.SOUTH); this.add(verScrollBar, BorderLayout.EAST); this.add(drawArea, BorderLayout.CENTER); this.popup = new JPopupMenu(); this.setValueMenuItem = new JMenuItem("Set signal value"); this.applyPatternMenuItem = new JMenuItem("Apply pattern"); this.resetSignalMenuItem = new JMenuItem("Reset signal"); this.popup.add(this.setValueMenuItem); this.popup.add(this.applyPatternMenuItem); this.popup.add(this.resetSignalMenuItem); } private void initDrawAreaData() { this.renderBeginTime = 0; this.renderLength = 0; this.nOfPeriods = initNOfPeriods; this.beginSignalIndex = 0; this.nOfSignalComponents = this.model.getTestbench().getSignals().size(); if(this.model.getTestbench().getClass() == SingleClockTestbench.class) { this.nOfSignalComponents -= 1; } } private void setDrawAreaData() { if(model == null) { return; } // // Horizontal scroll // long totalLength = this.model.getTestbench().getTestBenchLength(); long periodLength = this.model.getTestbench().getPeriodLength(); long totalPeriods = (long) Math.ceil(totalLength / periodLength); if(totalPeriods > Integer.MAX_VALUE) { totalPeriods = Integer.MAX_VALUE; } int beginPeriod = this.horScrollModel.getValue(); if(beginPeriod + this.nOfPeriods > totalPeriods) { beginPeriod = (int) Math.max(totalPeriods - this.nOfPeriods, 0); this.horScrollModel.setValue(beginPeriod); } this.renderBeginTime = periodLength * beginPeriod; this.renderLength = periodLength * this.nOfPeriods; this.horScrollModel.setExtent(this.nOfPeriods); this.horScrollModel.setMaximum((int) (totalPeriods)); // // Vertical scroll // this.verScrollModel.setExtent(1); this.verScrollModel.setMaximum(this.nOfSignalComponents); } private void setNewSimulationLength(long signalChangeTime) { if(model == null) { return; } if(signalChangeTime > this.lastSignalChangeTime) { this.lastSignalChangeTime = signalChangeTime; try { this.model.getTestbench().setSimulationLength(this.lastSignalChangeTime); } catch (UniformTestbenchException e) { e.printStackTrace(); } } } private void setNewTestbenchLength(long signalChangeTime) { if(model == null) { return; } long testbenchLength = this.model.getTestbench().getTestBenchLength(); long periodLength = this.model.getTestbench().getPeriodLength(); long newLength = 0; if(signalChangeTime + periodLength < testbenchLength) { return; } try { newLength = testbenchLength + testbenchLength; if(newLength > testbenchLength) { this.model.getTestbench().setTestBenchLength(newLength); return; } newLength = testbenchLength + (testbenchLength / 2); if(newLength > testbenchLength) { this.model.getTestbench().setTestBenchLength(newLength); return; } newLength = testbenchLength + periodLength; if(newLength > testbenchLength) { this.model.getTestbench().setTestBenchLength(newLength); return; } } catch (UniformTestbenchException e) { e.printStackTrace(); } } protected void clearSelectedSignal() { this.selectedSignal = null; } protected void openSetSignalDialog(String signalName, long time) { if(model == null) { return; } String newSignalValue = null; try { Signal signal = this.model.getTestbench().getSignal(signalName); long trueTime = this.calculateTrueTime(time); int counter = 1; int componentIndex = -1; if(this.selectedSignal != null) { componentIndex = this.selectedSignal.componentIndex; if(DEBUG_MODE) { System.out.println(this.selectedSignal); } } if(signal == null) return; if(signal.getClass() == ScalarSignal.class) { while(true) { try { this.setValueDialog.setVisibleRadixBox(false); this.setValueDialog.setSignalValue(signal.getSignalChange(trueTime).getSignalValue()); if(this.setValueDialog.show() != JOptionPane.OK_OPTION) { break; } newSignalValue = this.setValueDialog.getSignalValue(); ((EditableSignal)signal).setSignalChangeValue(trueTime, newSignalValue); this.setNewTestbenchLength(trueTime); this.setNewSimulationLength(trueTime); break; } catch (Exception e) { JOptionPane.showMessageDialog(null, "Invalid signal value.", "Error", JOptionPane.ERROR_MESSAGE); counter++; if(counter > 5) break; } } } else if(signal.getClass() == VectorSignal.class && componentIndex == -1) { while(true) { try { this.setValueDialog.setVisibleRadixBox(true); this.setValueDialog.setRadix(Radix.toInt(this.testbenchRadix)); this.setValueDialog.setSignalValue(RadixConverter.binToOtherString( signal.getSignalChange(trueTime).getSignalValue(), Radix.toInt(this.testbenchRadix), signal.getDimension(), false) ); if(this.setValueDialog.show() != JOptionPane.OK_OPTION) { break; } newSignalValue = RadixConverter.otherToBinString( this.setValueDialog.getSignalValue(), this.setValueDialog.getRadix()); if(newSignalValue.length() < signal.getDimension()) { StringBuilder sb = new StringBuilder(newSignalValue); for(int i = 0; i < signal.getDimension() - newSignalValue.length(); i++) { sb.insert(0, '0'); } newSignalValue = sb.toString(); } ((EditableSignal)signal).setSignalChangeValue(trueTime, newSignalValue); this.setNewTestbenchLength(trueTime); this.setNewSimulationLength(trueTime); break; } catch (Exception e) { JOptionPane.showMessageDialog(null, "Invalid signal value.", "Error", JOptionPane.ERROR_MESSAGE); counter++; if(counter > 5) break; } } } else if(signal.getClass() == VectorSignal.class && componentIndex >= 0) { while(true) { try { this.setValueDialog.setVisibleRadixBox(false); this.setValueDialog.setSignalValue(String.valueOf(signal.getSignalChange(trueTime).getSignalValue().charAt(componentIndex))); if(this.setValueDialog.show() != JOptionPane.OK_OPTION) { break; } newSignalValue = this.setValueDialog.getSignalValue(); ((VectorSignal)signal).setSignalChangeValue(trueTime, newSignalValue, componentIndex); this.setNewTestbenchLength(trueTime); this.setNewSimulationLength(trueTime); break; } catch (Exception e) { JOptionPane.showMessageDialog(null, "Invalid signal value.", "Error", JOptionPane.ERROR_MESSAGE); counter++; if(counter > 5) break; } } } } catch (UniformTestbenchException e) { e.printStackTrace(); } } protected void setSignalChange(String signalName, int componentIndex, long time) { if(model == null) { return; } try { Testbench tb = this.model.getTestbench(); Signal signal = tb.getSignal(signalName); long trueTime = this.calculateTrueTime(time); SignalChange signalChange = signal.getSignalChange(trueTime); char[] charArray = signalChange.getSignalValue().toCharArray(); if(charArray[componentIndex] == '1') { charArray[componentIndex] = '0'; } else { charArray[componentIndex] = '1'; } ((EditableSignal)signal).setSignalChangeValue(trueTime, new String(charArray)); this.setNewTestbenchLength(trueTime); this.setNewSimulationLength(trueTime); } catch (UniformSignalChangeException e) { e.printStackTrace(); } catch (UniformTestbenchException e) { e.printStackTrace(); } } protected void setSimulationLength(long simulationLength) { if(model == null) { return; } try { this.model.getTestbench().setSimulationLength(simulationLength); } catch (UniformTestbenchException e) { e.printStackTrace(); } } private long calculateTrueTime(long time) { if(model == null) { return 0; } Testbench tb = this.model.getTestbench(); long trueTime = 0; if(tb.getClass() == SingleClockTestbench.class) { long ist = ((SingleClockTestbench)tb).getInputSetupTime(); long ctl = ((SingleClockTestbench)tb).getClockTimeLow(); if(((SingleClockTestbench)tb).getChangeStateEdge() == ChangeStateEdge.rising) { if(time < ctl - ist) { trueTime = 0; } else { trueTime = time - ((time - ctl + ist) % tb.getPeriodLength()); } } else { if(time < tb.getPeriodLength() - ist) { trueTime = 0; } else { trueTime = time - ((time - tb.getPeriodLength() + ist) % tb.getPeriodLength()); } } } else { trueTime = time - (time % tb.getPeriodLength()); } return trueTime; } protected void resetSignal() { if(model == null) { return; } if(this.selectedSignal == null) { return; } try { Signal signal = this.selectedSignal.signal; if(signal.getClass() == ScalarSignal.class) { ((EditableSignal)signal).deleteSignalChangeValues(); } else if(signal.getClass() == VectorSignal.class) { if(this.selectedSignal.componentIndex == -1) { ((EditableSignal)signal).deleteSignalChangeValues(); } else { ((VectorSignal)signal).deleteSignalChangeValues(this.selectedSignal.componentIndex); } } } catch (Exception e) { e.printStackTrace(); } this.selectedSignal = null; } protected void setValue() { if(model == null) { return; } if(this.selectedSignal == null) { return; } this.openSetSignalDialog(this.selectedSignal.signal.getName(), this.selectedSignal.time); this.selectedSignal = null; } protected void applyPattern() { if(model == null) { return; } if(this.selectedSignal == null) { return; } Pattern p = null; Testbench tb = this.model.getTestbench(); long trueTime = this.calculateTrueTime(this.selectedSignal.time); if(this.selectedSignal.signal.getClass() == VectorSignal.class && this.selectedSignal.componentIndex == -1) { p = PatternDialog.getResultVector( this.selectedSignal.signal.getDimension(), tb.getPeriodLength() ); if(p != null) { try { List<SignalChange> signalChangeList = p.getChanges(trueTime); List<SignalChange> shiftedSignalChangeList = new ArrayList<SignalChange>(signalChangeList.size()); for(SignalChange sc : signalChangeList) { shiftedSignalChangeList.add(new SignalChange( sc.getSignalDimension(), sc.getSignalValue(), this.calculateTrueTime(sc.getTime())) ); } ((EditableSignal)this.selectedSignal.signal).setSignalChangeValue(shiftedSignalChangeList); this.setNewSimulationLength(this.selectedSignal.signal.getSignalLength()); } catch (Exception e) { e.printStackTrace(); } } } else if(this.selectedSignal.signal.getClass() == ScalarSignal.class || (this.selectedSignal.signal.getClass() == VectorSignal.class && this.selectedSignal.componentIndex >= 0)) { p = PatternDialog.getResultScalar( this.model.getTestbench().getPeriodLength() ); if(p != null) { try { List<SignalChange> signalChangeList = p.getChanges(trueTime); List<SignalChange> shiftedSignalChangeList = new ArrayList<SignalChange>(signalChangeList.size()); for(SignalChange sc : signalChangeList) { shiftedSignalChangeList.add(new SignalChange( sc.getSignalDimension(), sc.getSignalValue(), this.calculateTrueTime(sc.getTime())) ); } if(this.selectedSignal.signal.getClass() == VectorSignal.class) { ((VectorSignal)this.selectedSignal.signal).setSignalChangeValue(shiftedSignalChangeList, this.selectedSignal.componentIndex); this.setNewSimulationLength(this.selectedSignal.signal.getSignalLength()); } else { ((EditableSignal)this.selectedSignal.signal).setSignalChangeValue(shiftedSignalChangeList); this.setNewSimulationLength(this.selectedSignal.signal.getSignalLength()); } } catch (Exception e) { e.printStackTrace(); } } } this.selectedSignal = null; } protected void showPopup(Point point) { if(model == null) { return; } this.selectedSignal = this.drawArea.getClickInformation(point.x, point.y); if(this.selectedSignal == null || this.selectedSignal.signal == null) { return; } if(!this.selectedSignal.isSignalRenderArea || this.selectedSignal.signal.getClass() == ClockSignal.class) { return; } if(this.selectedSignal.time > this.model.getTestbench().getTestBenchLength()) { return; } this.popup.show(this, point.x, point.y); } @Override protected void paintComponent(Graphics g) { this.setDrawAreaData(); super.paintComponents(g); } @Override public void validate() { super.validate(); } @Override public void signalChanged(String signalName) { this.fireTestbenchChanged(); this.repaint(); if(DEBUG_MODE) { System.out.println("Signal changed: " + signalName); } } @Override public void simulationLengthChanged() { this.fireTestbenchChanged(); this.drawArea.moveMarker(this.model.getTestbench().getSimulationLength()); this.repaint(); if(DEBUG_MODE) { System.out.println("Simulation length changed: " + this.model.getTestbench().getSimulationLength()); } } @Override public void testBenchLengthChanged() { this.fireTestbenchChanged(); this.repaint(); if(DEBUG_MODE) { System.out.println("TestbenchLength changed: " + this.model.getTestbench().getTestBenchLength()); } } public void ZoomIn() { if(model == null) { return; } if(!(this.nOfPeriods / 2 < initNOfPeriods / 2)) { this.nOfPeriods /= 2; this.repaint(); } } public void ZoomOut() { if(model == null) { return; } int drawAreaWidth = this.drawArea.getSize().width; if(!((drawAreaWidth / this.nOfPeriods * 2) < minPeriodWidth)) { this.nOfPeriods *= 2; this.repaint(); } } public void OptimalZoom() { if(model == null) { return; } this.nOfPeriods = initNOfPeriods; this.repaint(); } protected int getBeginSignalIndex() { return beginSignalIndex; } protected void setBeginSignalIndex(int beginSignalIndex) { if(model == null) { return; } if(DEBUG_MODE) { System.out.println("beginSignalIndex changed: " + beginSignalIndex); } this.beginSignalIndex = beginSignalIndex; this.repaint(); } protected int getNOfSignalComponents() { return nOfSignalComponents; } protected void setNOfSignalComponents(int nOfSignalComponents) { if(model == null) { return; } if(DEBUG_MODE) { System.out.println("nOfSignalComponents changed: " + nOfSignalComponents); } this.nOfSignalComponents = nOfSignalComponents; this.verScrollModel.setMaximum(this.nOfSignalComponents); } public Radix getTestbenchRadix() { return testbenchRadix; } public void setTestbenchRadix(Radix testbenchRadix) { if(DEBUG_MODE) { System.out.println("testbenchRadix changed: " + testbenchRadix); } this.testbenchRadix = testbenchRadix; this.repaint(); } private void fireTestbenchChanged() { if(this.changeListener != null) { this.changeListener.stateChanged(new ChangeEvent(this)); } } public void setChangeListener(ChangeListener l) { this.changeListener = l; } }