Skip to content

Debugging

Jason T Alborough edited this page Feb 21, 2020 · 12 revisions

Methods of Debugging

  1. You can use Visual Studio step debugging
    • Pros:
      • Detailed real time debugging into with breakpoints and object inspection
    • Cons:
      • Doesn't really work remotely
      • On processors with Control Subnet, you must be connected to the CS interface to use step debugging. Often not practical or possible.
      • No logging
      • Using breakpoints stops the program and can interrupt system usage
      • Requires the running application to be built in debug mode, not release mode
  2. You can use the Debug class features build into the PepperDash.Core library.
    • Pros:
      • Can be easily enabled from console
      • Allows for setting the level of verbosity
      • Works when troubleshooting remotely and doesn't require a connection to the CS interface of the processor.
      • Allows for logging to the Crestron error log or a custom log stored on removable media
      • Works regardless of build type setting (debug/release)
      • Can easily identify which class instance is generating console messages
      • Can use console commands to view the state of public properties on devices
      • Can use console commands to call methods on devices
      • Doesn't stop the program
    • Cons:
      • No detailed object inspection in real time
      • Only prints console statements already in code
      • When enabled at the highest level of verbosity, it can produce a significant amount of data in console. Can be hard to find messages easily.
      • No current mechanism to filter messages by device. (can be filtered by 3rd party tools easily, though)
      • Not very effective in debugging applications running on the VC-4 platform as only log messages get printed to the Syslog

How to use the PepperDash.Core Debug Class

The majority of interaction is done via console, preferably via an SSH session through Crestron Toolbox, PuTTy or any other suitable application.

In code, the most useful method is Debug.Console() which has several overloads. All variations take an integer value for the level (0-2) as the first argument. Level 0 will ALWAYS print. Level 1 is for typical debug messages and level 2 is for verbose debugging. In cases where the overloads that accept a Debug.ErrorLogLevel parameter are used, the message will ALWAYS be logged, but will only print to console if the current debug level is the same or higher than the level set in the Debug.Console() statement.

All statements printed to console are prefixed by a timestamp which can be greatly helpful in debugging order of operations.

// The most basic use, sets the level (0) and the message to print.
Debug.Console(0, "Hello World");
// prints: [timestamp]App 1:Hello World

// The string parameter has a built in string.Format() that takes params object[] items
string world = "World";
Debug.Console(0, "Hello {0}", world);
// prints: [timestamp]App 1:Hello World

// This overload takes an IKeyed as the second parameter and the resulting statement will
// print the Key of the device in console to help identify the class instance the message
// originated from
Debug.Console(0, this, "Hello World");
// prints: [timestamp]App 1:[deviceKey]Hello World

// Each of the above overloads has a corresponding variant that takes an argument to indicate
// the level of error to log the message at as well as printing to console
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Hello World");
// prints: [timestamp]App 1:Hello World

Console Commands

The following console commands all perform actions on devices that have been registered with the PepperDash.Essentials.Core.DeviceManager static class

Appdebug:[slot] [0-2]

Gets or sets the current debug level where 0 is the lowest setting and 2 is the most verbose

Example:

RMC3>appdebug:1 // Gets current level
RMC3>AppDebug level = 0

RMC3>appdebug:1 1 // Sets level to 1 (all messages level 1 or lower will print)
RMC3>[Application 1], Debug level set to 1

Devlist:[slot]

Gets the current list of devices from DeviceManager

Prints in the form [deviceKey] deviceName

Example:

// Get the list of devices for program 1
RMC3>devlist:1

RMC3>[16:34:05.819]App 1:28 Devices registered with Device Mangager:
[16:34:05.834]App 1:  [cec-1] Tx 5 cec 1
[16:34:05.835]App 1:  [cec-1-cec]
[16:34:05.835]App 1:  [cec-5] Rmc 1 cec 1
[16:34:05.836]App 1:  [cec-5-cec]
[16:34:05.836]App 1:  [cec-6] Dm Chassis In 1 cec 1
[16:34:05.837]App 1:  [cec-6-cec]
[16:34:05.837]App 1:  [cec-7] Dm Chassis Out 1 cec 1
[16:34:05.838]App 1:  [cec-7-cec]
[16:34:05.838]App 1:  [comm-1] Generic comm 1
[16:34:05.838]App 1:  [comm-1-com]
[16:34:05.839]App 1:  [comm-2] Rmc comm 1
[16:34:05.839]App 1:  [comm-2-com]
[16:34:05.840]App 1:  [comm-3] Rmc comm 2
[16:34:05.840]App 1:  [comm-3-com]
[16:34:05.841]App 1:  [dmMd8x8-1] DM-MD8x8 Chassis 1
[16:34:05.842]App 1:  [dmRmc100C-1] DM-RMC-100-C Out 3
[16:34:05.843]App 1:  [dmRmc200C-1] DM-RMC-200-C Out 2
[16:34:05.843]App 1:  [dmRmc4kScalerC-1] DM-RMC-4K-SCALER-C Out 1
[16:34:05.844]App 1:  [dmTx201C-1] DM-TX-201C 1
[16:34:05.845]App 1:  [eisc-1A]
[16:34:05.845]App 1:  [gls-odt-1] GLS-ODT-CN 1
[16:34:05.846]App 1:  [gls-oir-1] GLS-OIR-CN 1
[16:34:05.846]App 1:  [processor]
[16:34:05.847]App 1:  [ssh-1] Generic SSH 1
[16:34:05.847]App 1:  [ssh-1-ssh]
[16:34:05.848]App 1:  [systemMonitor]
[16:34:05.848]App 1:  [tcp-1] Generic TCP 1
[16:34:05.849]App 1:  [tcp-1-tcp]

Devprops:[slot] [devicekey]

Gets the list of public properties on the device with the corresponding deviceKey

Example:

// Get the properties on the device with Key 'cec-1-cec'
// This device happens to be a CEC port on a DM-TX-201-C's HDMI input
RMC3>devprops:1 cec-1-cec
[
  {
    "Name": "IsConnected",
    "Type": "Boolean",
    "Value": "True",
    "CanRead": true,
    "CanWrite": false
  },
  {
    "Name": "Key",
    "Type": "String",
    "Value": "cec-1-cec",
    "CanRead": true,
    "CanWrite": true
  },
  {
    "Name": "Name",
    "Type": "String",
    "Value": "",
    "CanRead": true,
    "CanWrite": true
  },
  {
    "Name": "Enabled",
    "Type": "Boolean",
    "Value": "False",
    "CanRead": true,
    "CanWrite": true
  }
]

RMC3>

Devmethods:[slot] [devicekey]

Gets the list of public methods available on the device

Example:

// Get the methods on the device with Key 'cec-1-cec'
RMC3>devmethods:1 cec-1-cec
[
  {
    "Name": "SendText",
    "Params": [
      {
        "Name": "text",
        "Type": "String"
      }
    ]
  },
  {
    "Name": "SendBytes",
    "Params": [
      {
        "Name": "bytes",
        "Type": "Byte[]"
      }
    ]
  },
  {
    "Name": "SimulateReceive",
    "Params": [
      {
        "Name": "s",
        "Type": "String"
      }
    ]
  },
  //... Response abbreviated for clarity ...
]

RMC3>

Devjson:[slot] [json formatted object {"devicekey", "methodname", "params"}]

Used in conjunction with devmethods, this command allows any of the public methods to be called from console and the appropriate arguments can be passed in to the method via a JSON object.

This command is most useful for testing without access to hardware as it allows both simulated input and output for a device.

Example:

// This command will call the SendText(string text) method on the
// device with the Key 'cec-1-cec' and pass in "hello world" as the
// argument parameter.  On this particular device, it would cause
// the string to be sent via the CEC Transmit
RMC3>devjson:1 {"deviceKey":"cec-1-cec", "methodName":"SendText", "params": ["hello world\r"]}

// This command will call SimulateReceive(string text) on the device with Key 'cec-1-cec'
// This would simulate receiving data on the CEC port of the DM-TX-201-C's HDMI input
RMC3>devjson:1 {"deviceKey":"cec-1-cec", "methodName":"SimulateReceive", "params": ["hello citizen of Earth\r"]}

For additional examples, see this file.