How to utilise Cvb.Error.CVC_ERROR_CODES with C#

Hello,

as my brain is malfunctioning today, maybe you can shed some light. I want to display a readable text of the error code on my GUI. Therefore, I cast it first. This seems to be fine, if it returns CVC_E_OK but as soon as the number is negative, it does not cast it to an enum and displays me the timeout as -474837744 (or something similiar :)).

Can you shed some light please how I can improve my function?

  Cvb.Error.CVC_ERROR_CODES errorCode = (Cvb.Error.CVC_ERROR_CODES)Cvb.Driver.IGrabber.Snap(_hImg);
  if (Cvb.Error.CVC_ERROR_CODES.CVC_E_OK == errorCode) 
  {
    Image = _hImg;
    // Save Images
    string imageName = Settings.FilePath + Settings.FileName + "_Camera" + _cameraPort + "_" +  fileNamePostfix + ".bmp";

    if (!Cvb.Image.WriteImageFile(_hImg, imageName))
    {
      TestText = "Failed to save image: " + imageName;
    }
    TestText = "Done";
  }
  else
  {
    TestText = "Error snapping image: " + errorCode.ToString("g"); 
  }

image

Do you have a code-snippet showing the optimal solution for it (thus it says “timeout”).

Thanks in advance.

2 Likes

I’m also interested in a nice solution for this. It would have to be something similar to what is available in the C++ API with CVC_ERROR_FROM_HRES.

You tried to simply cast the value returned by Snap() into a Cvb.Error.CVC_ERROR_CODES enum. That won’t work. Well, technically speaking it works, but it doesn’t yield anything useful. Functions in the C-API of :cvb: return either a bool value or a HRESULT-like value (there are very few - and unfortunate - exceptions to that rule). The HRESULT convention dictates that the uppermost bit of the return value is to be set if an error occurred (making the error check fairly easy: Anything less than zero is an error, zero or positive values aren’t). Values like -2147483644 (= 0x8000 0004) are, however, quite the piece of work to write into an enumeration. Add to that the fact that e.g. the old Borland C++ compilers did by default treat enums are 8-bit variables and you have the mess that prompted the pattern that is being used: Any function returning an error will internally always use the CVC_ERROR macro from CVCError.h to generate its return values…

… and anyone wanting to evaluate the return value in terms of the CVC_ERROR_CODES will need to apply the inverse operation CVC_ERROR_FROM_HRES (all these two macros ever really do is set and unset the uppermost bit in the integer). When the original C# wrappers were written this pattern was simply transcribed to C#. C# has the added benefit of being able to directly convert the enum values to strings (you got that part right, however your integer value could not by mapped to one of the enum values and was therefore just output as a number), so in order to improve the output I’d recommend something like this:

Cvb.Error.CVC_ERROR_CODES errorCode = (Cvb.Error.CVC_ERROR_CODES)Cvb.Error.CVC_ERROR_FROM_HRES(Cvb.Driver.IGrabber.Snap(_hImg));

… and as you asked for suggestions to improve the code:

  • use var where possible - especially if the name of the type involved is poorly readable
  • I’m not a fan of Yoda conditions (https://blog.codinghorror.com/new-programming-jargon/, https://en.wikipedia.org/wiki/Yoda_conditions) as they are counter-intuitive when read (and readability greatly contributes to maintainability), but that is probably just a matter of preference
  • in line 12 you overwrite the text you did potentially just set - probably not intended that way
  • I’d recommend to stick with one naming convention for variable names; you used lower camel case for most of them, but camel case for TestText
  using Cvb;

  ...

  var errorCode = (Cvb.Error.CVC_ERROR_CODES)Error.CVC_ERROR_FROM_HRES(Driver.IGrabber.Snap(_hImg));
  if (errorCode == Error.CVC_ERROR_CODES.CVC_E_OK) 
  {
    Image = _hImg;
    // Save Images
    var imageName = string.Format("{0}{1}_Camera{2}_{3}.bmp", Settings.FilePath, Settings.FileName, _cameraPort, fileNamePostfix);
    if (!Cvb.Image.WriteImageFile(_hImg, imageName))
      testText = "Failed to save image: " + imageName;
    else
      testText = "Done";
  }
  else
  {
    testText = "Error snapping image: " + errorCode.ToString(); 
  }

That is about as readable as it gets with the C API. With Cvb.Net your code would roughly look like this:

  var status = WaitStatus.Ok;
  var snappedImage = myDriver.GetSnapshot(myTimeout, out status);
  if (snappedImage != null)
  {
    var imageName = string.Format("{0}{1}_Camera{2}_{3}.bmp", Settings.FilePath, Settings.FileName, _cameraPort, fileNamePostfix);
    try
    {
      snappedImage.Save(imageName);
      testText = "Done";
    }
    catch(Exception ex)
    {
      testText = string.Format("Failed to save image {0}: {1}", imageName, ex.Message);
    }
  }
  else
    testText = status.ToString();

Fairly similar. Slightly more pleasant to read, mostly due to the lack of all-caps names :slightly_smiling_face:

3 Likes

Thank you illusive. This solved my issue. :grinning:

Well, in that case I take the liberty of marking my own reply as “the solution” :smile: