diff --git a/src/org/openlcb/EventID.java b/src/org/openlcb/EventID.java index 9bc12381..c8c31f77 100644 --- a/src/org/openlcb/EventID.java +++ b/src/org/openlcb/EventID.java @@ -48,6 +48,15 @@ public EventID(@NonNull String value) { System.arraycopy(data, 0, this.contents, 0, BYTECOUNT); } + // note long's 64th bit is a sign + @CheckReturnValue + public EventID(long value) { + this.contents = new byte[BYTECOUNT]; + for (int index = 0; index < 8; index++) { + contents[index] = (byte)((value>>(8*(7-index))) & 0xFF); + } + } + byte[] contents; @CheckReturnValue @@ -117,4 +126,166 @@ public long toLong() { } return ret; } + + /** + * Take the eventID from a range, and return + * the lower flag bytes as a hex dotted string. + */ + public long rangeSuffix() { + // find the mask value + long eid = this.toLong(); + long sampleBit = eid & 0x01; + long mask = 0L; + while ( (eid &0x01L) == sampleBit) { + mask = (mask <<1) | 0x01; + eid = eid >> 1; + } + return mask; + } + + + /** + * Decode well-known and specifically defined event IDs + * @return "" if nothing interesting about the event + */ + public String parse() { + String eid = this.toShortString().substring(0, 2); + switch (eid) { + case "00": + return reserved(); + case "01": + return wellKnown(); + case "09": + if (this.toShortString().startsWith("09.00.99.FF")) { + return trainSearch(); + } + // deliberately falling through + //$FALL-THROUGH$ + default: + return ""; + } + } + + protected String reserved() { + return "Reserved "+this.toShortString(); + } + + protected String wellKnown() { + String eid = this.toShortString(); + switch (eid) { + case "01.00.00.00.00.00.FF.FF": + return "Emergency off"; + case "01.00.00.00.00.00.FF.FE": + return "Clear Emergency Off"; + case "01.00.00.00.00.00.FF.FD": + return "Emergency stop of all operations"; + case "01.00.00.00.00.00.FF.FC": + return "Clear emergency stop of all operations"; + case "01.00.00.00.00.00.FF.F8": + return "Node recorded a new log entry"; + case "01.00.00.00.00.00.FF.F1": + return "Power supply brownout detected below minimum required by node"; + case "01.00.00.00.00.00.FF.F0": + return "Power supply brownout detected below minimum required by standard"; + case "01.00.00.00.00.00.FE.00": + return "Ident button combination pressed"; + case "01.00.00.00.00.00.FD.01": + return "Link error code 1 – the specific meaning is link wire protocol specific"; + case "01.00.00.00.00.00.FD.02": + return "Link error code 2"; + case "01.00.00.00.00.00.FD.03": + return "Link error code 3"; + case "01.00.00.00.00.00.FD.04": + return "Link error code 4"; + + case "01.01.00.00.00.00.02.01": + return "Duplicate Node ID Detected"; + case "01.01.00.00.00.00.03.03": + return "This node is a Train"; + case "01.01.00.00.00.00.03.04": + return "This node is a Train Control Proxy"; + case "01.01.00.00.00.00.06.01": + return "Firmware Corrupted"; + case "01.01.00.00.00.00.06.02": + return "Firmware Upgrade Request by Hardware Switch"; + + default: + // check for fastclock and DCC ranges + if (eid.startsWith("01.01.00.00.01")) { + return fastClock(); + } else if (eid.startsWith("01.01.02")) { + return dccRange(); + } else { + return "Well-Known "+eid; + } + } + } + + protected String fastClock() { + String clockNum = this.toShortString().substring(16, 17); + byte[] contents = this.getContents(); + int lowByte = contents[7]&0xFF; + int highByte = contents[6]&0xFF; + int highByteMasked = 0x7F&highByte; + int bothBytes = highByte*256+lowByte; + String function = ""; + + String set = ((0x80 & highByte) == 0x80) ? "Set " : ""; + + if ((highByte & 0xF0) == 0xC0) { // set rate + int rate = (highByte&0xF)*256+lowByte; + function = "Set rate "+(rate/4.); + } else if (bothBytes == 0xF000) { // + function = "Query"; + } else if (bothBytes == 0xF001) { // + function = "Stop"; + } else if (bothBytes == 0xF002) { // + function = "Start"; + } else if (bothBytes == 0xF003) { // + function = "Date Rollover"; + } else if (highByteMasked < 24) { // time + String lowString = "00"+Integer.toString(lowByte); + lowString = lowString.substring(lowString.length()-2); + function = set+"time "+highByteMasked+":"+lowString; + } else if (highByteMasked <= 0x2C) { // date + String lowString = "00"+Integer.toString(lowByte); + lowString = lowString.substring(lowString.length()-2); + function = set+"date "+(highByteMasked-0x20)+"/"+lowString; + } else if (highByteMasked < 0x40) { // year + int year = (highByteMasked*256+lowByte)-0x3000; + function = set+"year "+year; + } else { + function = "reserved"; + } + + return "Fast Clock "+clockNum+" "+function; + } + + protected String dccRange() { + String eid = this.toShortString(); + if (eid.startsWith("01.01.02.00.00.FF")) { + return "DCC Basic Acc Addr Activate "+(this.toLong()&0x7FFL); + } else if (eid.startsWith("01.01.02.00.00.FE")) { + return "DCC Basic Acc Addr Deactivate "+(this.toLong()&0x7FFL); + } else if (eid.startsWith("01.01.02.00.00.FD")) { + return "DCC Turnout Feedback On "+(this.toLong()&0x7FFL); + } else if (eid.startsWith("01.01.02.00.00.FC")) { + return "DCC Turnout Feedback Off "+(this.toLong()&0x7FFL); + } else if (eid.startsWith("01.01.02.00.00.FB")) { + return "DCC Sensor On "+(this.toLong()&0xFFFL); + } else if (eid.startsWith("01.01.02.00.00.FA")) { + return "DCC Sensor Off "+(this.toLong()&0xFFFL); + } else if (eid.startsWith("01.01.02.00.01")) { + return "DCC Extended Accessory " + +((this.toLong()>>8)&0x7FFL) + +" "+(this.toLong()&0xFF); + } else { + return "DCC Well-Known "+this; + } + } + + protected String trainSearch() { + return "Train Search"; + } + } diff --git a/src/org/openlcb/ProducerConsumerEventReportMessage.java b/src/org/openlcb/ProducerConsumerEventReportMessage.java index 5c0f7739..ed1594e4 100644 --- a/src/org/openlcb/ProducerConsumerEventReportMessage.java +++ b/src/org/openlcb/ProducerConsumerEventReportMessage.java @@ -88,7 +88,7 @@ public byte[] getPayloadArray() { @Override public String toString() { - String retval = " Producer/Consumer Event Report "+eventID.toString(); + String retval = super.toString() + " Producer/Consumer Event Report "+eventID.toString(); if ( getPayloadSize() > 0 ) { retval = retval + " payload of "+getPayloadSize()+" : "; diff --git a/src/util/CollapsiblePanel.java b/src/util/CollapsiblePanel.java index 8bd3b6be..d8309d37 100644 --- a/src/util/CollapsiblePanel.java +++ b/src/util/CollapsiblePanel.java @@ -162,6 +162,8 @@ public void toggleSelection() { contentPanel_.setVisible(selected); validate(); + javax.swing.JFrame top = (javax.swing.JFrame)getTopLevelAncestor(); + if (top != null) top.pack(); headerPanel_.repaint(); } diff --git a/test/org/openlcb/EventIDTest.java b/test/org/openlcb/EventIDTest.java index 5deaae4e..0cd5a6a9 100644 --- a/test/org/openlcb/EventIDTest.java +++ b/test/org/openlcb/EventIDTest.java @@ -152,4 +152,56 @@ public void testToLong() { Assert.assertEquals(-2L, new EventID(new byte[]{(byte)0xff,(byte)0xff,(byte)0xff,(byte)0xff, (byte)0xff,(byte)0xff,(byte)0xff,(byte)0xfe}).toLong()); } + + @Test + public void testFromLong() { + EventID eid; + eid = new EventID(0x12345678); + Assert.assertEquals(0x12345678L, eid.toLong()); + } + + @Test + public void testBoring() { + EventID eid = new EventID("02.02.59.00.00.00.00.00"); + Assert.assertEquals("", eid.parse()); + } + + @Test + public void testDefault() { + EventID eid = new EventID("00.00.00.00.00.00.02.00"); + Assert.assertEquals("Reserved 00.00.00.00.00.00.02.00", eid.parse()); + } + + @Test + public void testWellKnown() { + EventID eid = new EventID("01.00.00.00.00.00.FF.FE"); + Assert.assertEquals("Clear Emergency Off", eid.parse()); + } + + @Test + public void testFastClock() { + EventID eid = new EventID("01.01.00.00.01.01.09.02"); + Assert.assertEquals("Fast Clock 1 time 9:02", eid.parse()); + eid = new EventID("01.01.00.00.01.01.09.32"); + Assert.assertEquals("Fast Clock 1 time 9:50", eid.parse()); + eid = new EventID("01.01.00.00.01.01.89.32"); + Assert.assertEquals("Fast Clock 1 Set time 9:50", eid.parse()); + eid = new EventID("01.01.00.00.01.01.F0.02"); + Assert.assertEquals("Fast Clock 1 Start", eid.parse()); + } + + @Test + public void testRangeSuffix() { + EventID eid = new EventID("00.00.00.00.00.00.FF.FF"); + Assert.assertEquals(0xFFFF, eid.rangeSuffix()); + eid = new EventID("00.00.00.00.00.FF.00.00"); + Assert.assertEquals(0xFFFF, eid.rangeSuffix()); + + eid = new EventID("00.00.00.00.00.FF.80.00"); + Assert.assertEquals(0x7FFF, eid.rangeSuffix()); + + eid = new EventID("00.00.00.00.00.00.00.03"); + Assert.assertEquals(0x03, eid.rangeSuffix()); + } + } diff --git a/test/org/openlcb/ProducerConsumerEventReportMessageTest.java b/test/org/openlcb/ProducerConsumerEventReportMessageTest.java index 0f138dcb..1e2809ef 100644 --- a/test/org/openlcb/ProducerConsumerEventReportMessageTest.java +++ b/test/org/openlcb/ProducerConsumerEventReportMessageTest.java @@ -93,7 +93,7 @@ public void testPayloadToString() { ProducerConsumerEventReportMessage m2 = new ProducerConsumerEventReportMessage( nodeID1, eventID1, payload1 ); - Assert.assertEquals(" Producer/Consumer Event Report EventID:01.00.00.00.00.00.01.00 payload of 2 : 12.34", m2.toString()); + Assert.assertEquals("01.02.03.04.05.06 Producer/Consumer Event Report EventID:01.00.00.00.00.00.01.00 payload of 2 : 12.34", m2.toString()); } @Test diff --git a/test/org/openlcb/cdi/swing/CdiPanelDemo.java b/test/org/openlcb/cdi/swing/CdiPanelDemo.java index c65fbf3d..9dbbfafa 100644 --- a/test/org/openlcb/cdi/swing/CdiPanelDemo.java +++ b/test/org/openlcb/cdi/swing/CdiPanelDemo.java @@ -80,6 +80,14 @@ static public void main(String[] args) { fname = args[0]; System.out.println("Using input file " + fname); } + + // configure a L&F + try { + // Set cross-platform Java L&F (also called "Metal") + javax.swing.UIManager.setLookAndFeel( + javax.swing.UIManager.getCrossPlatformLookAndFeelClassName()); + } catch (Exception e) {}; + d.displayFile(fname); } }