Class Dcc4PcOpsModeProgrammer

All Implemented Interfaces:
PropertyChangeListener, EventListener, AddressedProgrammer, PropertyChangeFirer, PropertyChangeProvider, Disposable, Programmer

Provides an Ops mode proxy programming interface for a RailCom Reader. This forwards the read request to the command station to forward on and handles sending back the CV reading results from the RailCom message

A read sends the POM read packet through the other connection's command station, and waits only for the decoder's RailCom reply, decoded by the reader. It doesn't use that connection's own ops-mode read: a command station that can't read RailCom keeps its programmer busy for seconds after each read, waiting for a value it will never get.

Packets are paced: each is handed to the command station only once it has had time to send the last one with its repeats, because a LocoNet command station drops a packet it's given while still busy, without telling sendPacket. A packet with no reply is sent again every RETRY_AFTER, up to MAX_SENDS times in all. Resending every second instead (2026-10-13) built a backlog the command station was still working through 20 s later.

Without a command station, the read goes through the other connection's programmer instead, and finishes with the first value that arrives: the RailCom reply, or the value that programmer reads back.

A write is sent the same way. The decoder answers a POM write with the value it then holds, so the write is done when that matches; if it never does, the write ends with ConfirmFailed. Without a command station, the other connection's programmer does the write and reports the result.

See Also:
  • Field Details

    • pAddress

    • pLongAddress

      boolean pLongAddress
    • rcTag

    • cv

      int cv
    • progListener

    • awaitingValue

      boolean awaitingValue
      True while a read or confirm is waiting for its value.
    • READ_TIMEOUT

      static final int READ_TIMEOUT
      Longest a read waits when it goes through the other connection's programmer (no command station to send the packets ourselves).
      See Also:
    • RAILCOM_TIMEOUT

      static final int RAILCOM_TIMEOUT
      Longest a read or write waits when we send the POM packets: time for the last of the MAX_SENDS sends to be answered (answers come 160-270 ms after a send). With 5 s, the few CVs that got no answer at all took 40% of a Read All (2026-10-16).
      See Also:
    • READ_REPEATS

      static final int READ_REPEATS
      How many times the command station sends a POM read packet. A decoder may need a moment to fetch the value, and then sends it in later cutouts; only one that follows the read packet itself can be matched to the CV, so it helps if the packet is still being repeated by then.
      See Also:
    • WRITE_REPEATS

      static final int WRITE_REPEATS
      How many times the command station sends a POM write packet. A decoder only writes after two identical write packets in a row.
      See Also:
    • GAP_PER_REPEAT

      static final long GAP_PER_REPEAT
      Time to leave per repeat before handing the command station another packet. A LocoNet command station drops a packet it's given while still sending the last one (it answers "busy", which sendPacket doesn't report), so on 2026-10-13 every write sent straight after another was lost. This is an estimate: about one DCC packet per repeat, with room for the command station's own refresh traffic.

      2026-10-15: 150 ms made no difference to how many sends went unanswered, so the losses aren't the command station being busy.

      See Also:
    • RETRY_AFTER

      static final int RETRY_AFTER
      If no reply has come after this long, the packet is sent once more, in case it or its answer was lost. Answers normally come 160-270 ms after the send (2026-10-15); 1500 ms made each lost one cost far more than the answer itself.
      See Also:
    • MAX_SENDS

      static final int MAX_SENDS
      How many times a packet is sent in all while no answer comes, RETRY_AFTER apart.
      See Also:
    • defaultManager

    • defaultProgrammer

    • commandStation

      Sends the POM packets; null to go through defaultProgrammer instead.
    • writeValue

      The value being written, or -1 for a read. A decoder answers a POM write with the value it then holds, so the write is done when that matches.
    • lastWriteReply

      The last value a write was answered with, or -1 for none yet.
  • Constructor Details

  • Method Details

    • pomReadPacket

      @Nonnull static byte[] pomReadPacket(int address, boolean longAddr, int cvNum)
      The NMRA ops-mode "verify byte" packet (S-9.2.1 long-form CV access, instruction 1110 01VV), which a RailCom decoder answers with the CV's value on channel 2. The data byte doesn't matter for a read.
      Parameters:
      address - the loco's address
      longAddr - true for a long address
      cvNum - the CV, 1 to 1024
      Returns:
      the packet, as NmraPacket builds it for CommandStation.sendPacket(byte[], int), or empty if the address or CV is invalid
    • writeCV

      public void writeCV(String CVname, int val, ProgListener p) throws ProgrammerException
      Perform a CV write in the system-specific manner, and using the specified programming mode.

      Handles a general address space through a String address. Each programmer defines the acceptable formats.

      Note that this returns before the write is complete; you have to provide a ProgListener to hear about completion. For simplicity, expect the return to be on the GUI thread.

      Exceptions will only be thrown at the start, not during the actual programming sequence. A typical exception would be due to an invalid mode (though that should be prevented earlier) The POM write packet goes out through the command station, and the write is done when the decoder's RailCom reply shows the new value. Without a command station, the other connection's programmer does the write and reports the result.

      Specified by:
      writeCV in interface Programmer
      Specified by:
      writeCV in class AbstractProgrammer
      Parameters:
      CVname - the CV to write
      val - the value to write
      p - the listener that will be notified of the write
      Throws:
      ProgrammerException - if unable to communicate
    • readCV

      public void readCV(String CVname, ProgListener p) throws ProgrammerException
      Perform a CV read in the system-specific manner, and using the specified programming mode.

      Handles a general address space through a String address. Each programmer defines the acceptable formats.

      Note that this returns before the write is complete; you have to provide a ProgListener to hear about completion. For simplicity, expect the return to be on the GUI thread.

      Exceptions will only be thrown at the start, not during the actual programming sequence. A typical exception would be due to an invalid mode (though that should be prevented earlier)

      Specified by:
      readCV in interface Programmer
      Specified by:
      readCV in class AbstractProgrammer
      Parameters:
      CVname - the CV to read
      p - the listener that will be notified of the read
      Throws:
      ProgrammerException - if unable to communicate
    • resetPacing

      static void resetPacing()
      For tests: forget the pacing left by earlier sends.
    • confirmCV

      public void confirmCV(String cvName, int val, ProgListener p) throws ProgrammerException
      Confirm the value of a CV using the specified programming mode. On some systems, this is faster than a read.

      Handles a general address space through a String address. Each programmer defines the acceptable formats.

      Note that this returns before the write is complete; you have to provide a ProgListener to hear about completion. For simplicity, expect the return to be on the GUI thread.

      Exceptions will only be thrown at the start, not during the actual programming sequence. A typical exception would be due to an invalid mode (though that should be prevented earlier) Reads the CV and lets the listener know whether it matches. The command station's own confirm can't be used: it may only report that it sent the packets.

      Specified by:
      confirmCV in interface Programmer
      Specified by:
      confirmCV in class AbstractProgrammer
      Parameters:
      cvName - the CV to confirm
      val - the value to confirm
      p - the listener that will be notified of the confirmation
      Throws:
      ProgrammerException - if unable to communicate
    • getSupportedModes

      Get the list of ProgrammingMode supported by this Programmer. If the order is significant, earlier modes are better. Types implemented here.
      Specified by:
      getSupportedModes in interface Programmer
      Specified by:
      getSupportedModes in class AbstractProgrammer
      Returns:
      the list of supported modes or an empty list
    • timeout

      protected void timeout()
      Internal routine to handle a timeout, should be synchronized!
      Specified by:
      timeout in class AbstractProgrammer
    • propertyChange

      The loco's RailCom tag has a POM reply for the CV being read or written.
      Specified by:
      propertyChange in interface PropertyChangeListener
    • getLongAddress

      public boolean getLongAddress()
      Specified by:
      getLongAddress in interface AddressedProgrammer
    • getAddressNumber

      public int getAddressNumber()
      Specified by:
      getAddressNumber in interface AddressedProgrammer
    • getAddress

      public String getAddress()
      Specified by:
      getAddress in interface AddressedProgrammer