Soup to Nuts: Cisco Codec – Part 1

In this series of posts, we’ll develop a basic module for controlling Cisco Room Kit devices. It can be used in SIMPL Windows or SIMPL# Pro programs. It won’t be as exhaustive as the official module available from the Application Market, but it will be enough to handle most video conferencing tasks.

Create a new SIMPL# Library

Create a new SIMPL# Library in Visual Studio 2008. I’ve named mine CiscoRoomKit:

We’ll start out by adding a new class named Device to our project:

Right-click on your project in the Solution Explorer and select Add > Class

You can delete the Class1.cs file from our project. Your Solution Explorer should look like this:

Now open up Device.cs and enter in this skeleton code to get us up and running:

using System;
using Crestron.SimplSharp;

namespace CiscoRoomKit
{
   public class Device
   {
      public string Host { get; set; }
      public string User { get; set; }
      public string Password { get; set; }

      public Device()
      {
      }

      public void Connect()
      {

      }

      public void Disconnect()
      {

      }
   }
}

This class will handle connecting and disconnecting from a network device. For that, it needs to know the Host, User, and Password to connect. Remember that we need a default constructor to work with SIMPL+, so we can’t pass any of this info in when the object is created.

We’ll keep things simple for now and only support SSH connections. We’ll add a few using statements and an SSH client to our class:

using System;
using Crestron.SimplSharp;
using Crestron.SimplSharp.Ssh;
using Crestron.SimplSharp.Ssh.Common;

namespace CiscoRoomKit
{
   public class Device
   {
      private SshClient _ssh;

      public string Host { get; set; }
      public string User { get; set; }
      public string Password { get; set; }

      public Device()
      {
      }

      public void Connect()
      {
         // Handle interactive authentication (typing in password)
         var auth = new KeyboardInteractiveAuthenticationMethod(User);
         auth.AuthenticationPrompt += HandleAuthentication;

         // Create connection info
         var conn = new ConnectionInfo(Host, User, auth);

         // Connect SSH session
         _ssh = new SshClient(conn);
         _ssh.ErrorOccurred += HandleError;
         _ssh.Connect();
      }

      public void Disconnect()
      {
         if (_ssh != null)
         {
            // Disconnect and dispose of SSH session
            _ssh.Disconnect();
            _ssh.Dispose();
            _ssh = null;
         }
      }

      private void HandleAuthentication(object sender,
         AuthenticationPromptEventArgs args)
      {

      }

      private void HandleError(object sender,
         ExceptionEventArgs args)
      {

      }
   }
}

We register an AuthenticationPromptEventHandler named HandleAuthentication. We also register an ErrorOccurredEventHandler named HandleError. These methods are private to our class. Inside our HandleAuthentication event handler, we really just need to look for a password prompt and respond to it:

private void HandleAuthentication(object sender,
   AuthenticationPromptEventArgs args)
{
   foreach (var prompt in args.Prompts)
   {
      // Look for password prompt and respond
      if (prompt.Request.Contains("Password:"))
      {
         prompt.Response = Password;
      }
   }
}

But how will we notify SIMPL+ once we’ve actually connected? Or what happens if we accidentally disconnect? We need to create a few events to notify SIMPL+ when these things happen:

public class Device
{
  private SshClient _ssh;

  public string Host { get; set; }
  public string User { get; set; }
  public string Password { get; set; }

  public event EventHandler OnConnect;
  public event EventHandler OnDisconnect;

  public Device()
  {

Update Connect to call OnConnect:

public void Connect()
{
   // Handle interactive authentication (typing in password)
   var auth = new KeyboardInteractiveAuthenticationMethod(User);
   auth.AuthenticationPrompt += HandleAuthentication;

   // Create connection info
   var conn = new ConnectionInfo(Host, User, auth);

   // Connect SSH session
   _ssh = new SshClient(conn);
   _ssh.ErrorOccurred += HandleError;
   _ssh.Connect();

   if (OnConnect != null)
   {
       OnConnect(this, new EventArgs());
   }
}

Update Disconnect to call OnDisconnect:

public void Disconnect()
{
   if (_ssh != null)
   {
      // Disconnect and dispose of SSH session
      _ssh.Disconnect();
      _ssh.Dispose();
      _ssh = null;

      if (OnDisconnect != null)
      {
         OnDisconnect(this, new EventArgs());
      }
   }
}

And update HandleError to see if we disconnected:

private void HandleError(object sender,
   ExceptionEventArgs args)
{
   if (!_ssh.IsConnected)
   {
      if (OnDisconnect != null)
      {
         OnDisconnect(this, new EventArgs());
      }
   }
}

Build the library and now let’s switch over to SIMPL+ to write the wrapper we can use in our programs.

Create a SIMPL+ wrapper

Copy the CiscoRoomKit.clz file to a new directory where we can work on the SIMPL pieces. In the SIMPL+ editor, create a new module named Cisco Codec Wrapper.usp:

// --- Compiler Directives ---

#DEFAULT_VOLATILE
#ENABLE_STACK_CHECKING
#ENABLE_TRACE

#DEFINE_CONSTANT MAX_NAME_LEN 50

#USER_SIMPLSHARP_LIBRARY "CiscoRoomKit"

// --- Inputs ---

DIGITAL_INPUT Connect;

// --- Outputs ---

DIGITAL_OUTPUT Connect_Fb;

// --- Parameters ---

STRING_PARAMETER Host[MAX_NAME_LEN];
STRING_PARAMETER User[MAX_NAME_LEN];
STRING_PARAMETER Password[MAX_NAME_LEN];

// --- Global Variables ---

Device Codec;

// --- Events ---

PUSH Connect
{
	Codec.Connect();
}

RELEASE Connect
{
	Codec.Disconnect();
}

EVENTHANDLER Codec_OnConnect (Device sender, EventArgs args)
{
	Connect_Fb = 1;
}

EVENTHANDLER Codec_OnDisconnect (Device sender, EventArgs args)
{
	Connect_Fb = 0;
}

// --- Main ---

Function Main()
{
	Codec.Host = Host;
	Codec.User = User;
	Codec.Password = Password;

	RegisterEvent(Codec, OnConnect, Codec_OnConnect);
	RegisterEvent(Codec, OnDisconnect, Codec_OnDisconnect);

	WaitForInitializationComplete();
}

We’ll pass the connection information in using the parameters: Host, User, and Password. The first thing we want to do in Main is assign these parameters to our Codec device. Then we’ll wait for a push on Connect to trigger our Connect method. When Connect is released, we’ll call our Disconnect method.

Our event handlers for Codec_OnConnect and Codec_OnDisconnect handle updating Connect_Fb to our SIMPL program.

Save and compile this module.

The SIMPL Windows Demo

Now you need to create a new SIMPL Windows program to demo the module. Save this program in the same directory where you saved your SIMPL+ wrapper. Drop the wrapper module into your program and fill in the connection information:

Give the Connect and Connect_Fb signals names

Save and compile this program and load to your processor (in my case a CP3), then fire up SIMPL Debugger to watch what happens. If you have an actual device to connect to, you should see:

Connecting takes a second to negotiate the authentication

That’s it for now. In the next part we’ll look at sending commands to the codec and receiving responses. Source code is available on GitHub here: https://github.com/kielthecoder/SoupToNutsModules/tree/main

Thanks for reading!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s