Skip to content

Commands with Enum parameters failing - typeof is returning System.Enum #12

@molzy

Description

@molzy

Firstly, thanks for writing this package!

Issue description

I'm having trouble with a few of the Command and Configuration methods not performing any action - upon closer inspection I found that the problem commands all share the usage of one of the parameter Enums, and are generating the below exception.

System.Collections.Generic.KeyNotFoundException: 'The given key 'System.Enum' was not present in the dictionary.'

I wrote a short test program to isolate the issue:

using CSxAPI;
using CSxAPI.API;
using CSxAPI.API.Data;

var _xAPI = new CSxAPIClient("...", "...", "...")
{
    AllowSelfSignedTls = true,
    ConsoleTracing = true
};

await _xAPI.Connect();
_xAPI.Status.Video.Layout.LayoutFamily.LocalChanged += LayoutChanged;

await Task.Delay(500);
LayoutNext();

await Task.Delay(500);
_xAPI.Dispose();

async void Layout(
    CommandVideoLayoutLayoutFamilySetLayoutFamily layoutFamily,
    CommandVideoLayoutLayoutFamilySetTarget target = CommandVideoLayoutLayoutFamilySetTarget.Local)
{
    Console.WriteLine($"CiscoCodec Layout({layoutFamily})");
    IDictionary<string, object> test = await _xAPI.Command.Video.Layout.LayoutFamily.Set(layoutFamily: (CommandVideoLayoutLayoutFamilySetLayoutFamily)layoutFamily, target: (CommandVideoLayoutLayoutFamilySetTarget)CommandVideoLayoutLayoutFamilySetTarget.Local);
    Console.WriteLine(string.Join(", ", test.Select(pair => $"{pair.Key} => {pair.Value}")));
}
void LayoutNext()
{
    Layout(CommandVideoLayoutLayoutFamilySetLayoutFamily.Auto);
}
void LayoutChanged(string newValue)
{
    Console.WriteLine($"LayoutChanged: {newValue.ToString()}");
}

Since we are using async void the unhandled exception crashes the program. Abridged call stack:

	[Exception] System.Private.CoreLib.dll!System.ThrowHelper.ThrowKeyNotFoundException<T>(T key) Line 213	C#
 	[Exception] System.Private.CoreLib.dll!System.Collections.Generic.Dictionary<TKey, TValue>.this[TKey].get(TKey key) Line 199	C#
 	[Exception] CSxAPI.dll!CSxAPI.API.Serialization.EnumSerializer.Serialize<T>(T deserialized) Line 17	C#
 	[Exception] CSxAPI.dll!CSxAPI.API.Commands.CSxAPI.API.IVideoLayoutLayoutFamilyCommands.Set(string customLayoutName, CSxAPI.API.Data.CommandVideoLayoutLayoutFamilySetLayoutFamily? layoutFamily, CSxAPI.API.Data.CommandVideoLayoutLayoutFamilySetTarget? target) Line 2212	C#
 	[Exception] TestCSxAPI.dll!Program.<Main>$.__Layout|0(CSxAPI.API.Data.CommandVideoLayoutLayoutFamilySetLayoutFamily layoutFamily, CSxAPI.API.Data.CommandVideoLayoutLayoutFamilySetTarget target) Line 25	C#

Investigation so far

It appears that the call to ValueSerializer -> EnumSerializer for these parameters is losing the specific type information at some point, and typeof(T) is returning System.Enum in EnumSerializer.Serialize, and thus not finding the correct serialize method for that type.

Relevant generated code snippets:

// Commands.cs
async Task<IDictionary<string, object>> IVideoLayoutLayoutFamilyCommands.Set(string? customLayoutName, CommandVideoLayoutLayoutFamilySetLayoutFamily? layoutFamily, CommandVideoLayoutLayoutFamilySetTarget? target) {
    return await this.transport.CallMethod(new[] { "xCommand", "Video", "Layout", "LayoutFamily", "Set" }, new Dictionary<string, object?> { { "CustomLayoutName", customLayoutName }, { "LayoutFamily", ValueSerializer.Serialize<CommandVideoLayoutLayoutFamilySetLayoutFamily>(layoutFamily) }, { "Target", ValueSerializer.Serialize<CommandVideoLayoutLayoutFamilySetTarget>(target) } }).ConfigureAwait(false);
}

// ValueSerializer.cs:
public static string? Serialize(Enum? deserialized) => deserialized is null ? null : EnumSerializer.Serialize(deserialized);

// EnumSerializer.cs:
public static string Serialize<T>(T deserialized) where T: Enum => enumSerializers[typeof(T)].serialize(deserialized);
...
enumSerializers.Add(typeof(CommandVideoLayoutLayoutFamilySetLayoutFamily) ...

Possible solution

I have tried some workarounds and the best option appears to be checking the runtime type in EnumSerialiser.Serialize:

public static string Serialize(Enum? deserialized)
{
    if (deserialized != null)
    {
        var type = deserialized.GetType();
        return enumSerializers[type].serialize(deserialized);
    }
    else
        return "";
}

Due to events.xml I have not been successful in building a working package yet, but I am still working on this and will update if I can verify the fix.

Let me know if you would like me to submit an (untested) pull request.

Working command examples

  • Command.Audio.Microphones.Mute()
  • Command.Bookings.List(dayOffset: 0)
  • Command.Dial(number: "1234567890")
  • Feedback registrations

Problem command examples

  • Command.Video.Layout.LayoutFamily.Set(layoutFamily: CommandVideoLayoutLayoutFamilySetLayoutFamily.Auto, target: CommandVideoLayoutLayoutFamilySetTarget.Local)
  • Configuration.Video.Monitors(ConfigurationVideoMonitors.Single)
  • Command.Video.Selfview.Set(mode: CommandVideoSelfviewSetMode.On)
  • Command.Camera.Ramp(cameraId: 1, pan: CommandCameraRampPan.Right)

I haven't exhaustively tested all commands but the pattern appears to be that each of the problematic commands is called with a typed Enum parameter.

Software versions

Tested with .NET 6.0 and .NET 8.0, compiled with VS2022 on Windows.
Tested all compiled package versions available on nuget.

If you would like me to run any tests in my environment let me know.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions