ATC6 get line status time

Hello guys,

I have a acquisition loop for take C6 range image, and during this loop I try to read the value of input 1 and input 2. I use this code to acces to the information :

bool CCameraCvbGenicamATC6::GetLineStatus(LineSelectorEnums line_selected) {
	switch (line_selected)
	{
	case CCameraCvbGenicamAT::LineSelectorEnums::Line0_IN1:
		getCvbEnumerationNode(m_node_name_for_device, "Std::LineSelector")->SetValue("Line0");
		break;
	case CCameraCvbGenicamAT::LineSelectorEnums::Line1_IN2:
		getCvbEnumerationNode(m_node_name_for_device, "Std::LineSelector")->SetValue("Line1");
		break;
	case CCameraCvbGenicamAT::LineSelectorEnums::Line2_EncoderA:
		getCvbEnumerationNode(m_node_name_for_device, "Std::LineSelector")->SetValue("Line2");
		break;
	case CCameraCvbGenicamAT::LineSelectorEnums::Line3_EncoderB:
		getCvbEnumerationNode(m_node_name_for_device, "Std::LineSelector")->SetValue("Line3");
		break;
	case CCameraCvbGenicamAT::LineSelectorEnums::Line4_EncoderZ:
		getCvbEnumerationNode(m_node_name_for_device, "Std::LineSelector")->SetValue("Line4");
		break;
	case CCameraCvbGenicamAT::LineSelectorEnums::Line5_OUT1:
		getCvbEnumerationNode(m_node_name_for_device, "Std::LineSelector")->SetValue("Line5");
		break;
	case CCameraCvbGenicamAT::LineSelectorEnums::Line6_OUT2:
		getCvbEnumerationNode(m_node_name_for_device, "Std::LineSelector")->SetValue("Line6");
		break;
	}
	return getCvbBooleanNode(m_node_name_for_device, "Std::LineStatus")->Value();
}

I measure the time of this function that I call each time I get a picture, it’s seem to take a long time, too long for my application at least.

Is there a way to read those signals faster ?

Thanks,

Hi @AxelDosSantosDoAlto

from one of your previous posts

https://forum.commonvisionblox.com/t/imager-use-vs-stream-use-for-c5-c6-acquisition/1978

I assume, that you are working with a framerate >800Hz.
This being said, accessing the NodeMap of the camera with every image is very optimistic. I would assume this to be the bottleneck of your application and there is not much we can do about it.

I forwarded the request to our AT CX experts maybe the camera has other features we can use for what you want to achieve.

One possible solution might be to have callback funktions everytime the LineSelector changes which you can then use to read the value.

Another idea is to read the stat of all LineSelectors from a binary node and interpret this value to get all states at once.

However, if there is a soluttion to this depends on the features of the camera which we will need to have a look into.

Cheers
Chris

@AxelDosSantosDoAlto

I do not know what exactly the function “getCvbEnumerationNode” does and how often it is called but I assume that it makes a new access to a node which would be ineffective. I would recommend to define a class member variable which holds the access to the node “LineSelector” and “LineStatus” and in the switch inside of your “GetLineStatus” function just set or get the node value and not additionally establish an access to it.

auto nodeMap = device_->NodeMap("Device");
lineSelectorNode_ = nodeMap->Node<Cvb::EnumerationNode>("LineSelector");
lineStatusNode_ = nodeMap->Node<Cvb::BooleanNode>("LineStatus");

bool CCameraCvbGenicamATC6::GetLineStatus(LineSelectorEnums line_selected) {
	switch (line_selected)
	{
	case CCameraCvbGenicamAT::LineSelectorEnums::Line0_IN1:
		lineSelectorNode_->SetValue("Line0");
		break;
	case CCameraCvbGenicamAT::LineSelectorEnums::Line1_IN2:
		lineSelectorNode_->SetValue("Line1");
		break;
	case CCameraCvbGenicamAT::LineSelectorEnums::Line2_EncoderA:
		lineSelectorNode_->SetValue("Line2");
		break;
	case CCameraCvbGenicamAT::LineSelectorEnums::Line3_EncoderB:
		lineSelectorNode_->SetValue("Line3");
		break;
	case CCameraCvbGenicamAT::LineSelectorEnums::Line4_EncoderZ:
		lineSelectorNode_->SetValue("Line4");
		break;
	case CCameraCvbGenicamAT::LineSelectorEnums::Line5_OUT1:
		lineSelectorNode_->SetValue("Line5");
		break;
	case CCameraCvbGenicamAT::LineSelectorEnums::Line6_OUT2:
		lineSelectorNode_->SetValue("Line6");
		break;
	}
	return lineStatusNode_->Value();
}

Hi @MandanaS

thank you for jumping in.
If I understand the code correctly, @AxelDosSantosDoAlto switches the LineSelector for each image.
First he switches it to eg. Line1, then he reads the value of the LineStatus for Line1.
Then he switches to Line2 and accordingly reads the LineStatus for this Line.

I think from a programatic point of view we do not have many possibilities to change this.

If you refer to my first answer, where I made some suggestions on how to tackle this problem from the cameraside rather than from the codeside, maybe you have an idea or tip on how to achieve this.

Maybe there is even a completely other way but this is beyond my understanding of the C6.

Cheers
Chris

Hi guys,

Sorry for my late answer, I need some time to test what you give me.

I try to do what @MandanaS explain in this subject, and it work very well, I now get a value in less than 3 millisec. It’s not as perfect as I’m looking for, but it’s a faster wayt to read the input 1 line status and draw in an image the trigger signal.

Maybe it’s possible to do like you say @Chris, and I think this way or doing is better : getting a callback for each electrical change of line impulse. Is AT answer you alreaydy ?

By the way, since the solution of @MandanaS work very well, I apply ot for any values of the camera that I need.

I look forward for the “callback” solution if it is plan by AT.

Thanks for your time guys. :slight_smile:

Hi @AxelDosSantosDoAlto,

the most stable way to read the line status for all lines should be to use the Chunk Line Status All.
Here you are able to collect information from every frame line what the current line status of all line was, after receiving a frame.
grafik
Line Status All is a 8 bit value that contains a boolean for each line. For more information on that have a look here: https://forum.commonvisionblox.com/t/working-with-3d-cameras-of-automation-technology-in-cvb/532/19
Std::LineStatusAll can also be tracked instead of each individual line. this should result in a better performance.

In case you want to use some kind of callback there are GEV Events available but they do not support the change of a line status. As some kind of workarround you might be able to trigger one of the available events using a line as event trigger and check for the GEV Event from the software side. This will probably not provide you with all desired information, but I think it’s worth mentioning.

1 Like

Hi @Simon,
Thanks for your indication.

To be honest, I find those chunk data for line status last week, I want to try it today and tomorrow before speak up about them in this subject. I would try it and report here my result if it’s ok for you.

Thanks for your information anyway. :slight_smile:

Hi @AxelDosSantosDoAlto,

sounds good!
Note that there is a difference between Std::LineStatusAll and Std::ChunkLineStatusAll.
Just wanted to add the node description for Std::LineStatusAll:

(Std::LineStatusAll)

Returns the current status of all available Line signals at time of polling in a single bitfield.

bit(0) : Line0 (IN1)

bit(1) : Line1 (IN2)

bit(2) : Line2 (Encoder-A)

bit(3) : Line3 (Encoder-B)

bit(4) : Line4 (Encoder-Z)

bit(8) : Line5 (OUT1)

bit(9) : Line6 (OUT2)

bit(10): Line7 (Laser)

Full Name: Std::LineStatusAll

Type: Integer

Access Mode: Read Only

Visibility: Expert

Caching Mode: NoCache

Streamable: false

Polling Time [ms]: 500

Min: 28

Max: 28

Representation: PureNumber

Generally it can be hard the get the hardware line status from the software side live without significant delay.

Hi @Simon, hi everyone,

I try to read the chunk data according to what you explain @Simon, and it work well !

I get my IN1 and IN2 values in less than 1 millisec each time I get a frame from the AT camera. I read it more than 1000 times, and no problem so far.

Thanks for your indications.

By the way, on a completly subject, do you know if it’s possible to get the number of CVB buffer that are use in the RAM ? And any others data relating those buffers ?

Thanks anyway for your time all.

Hi @AxelDosSantosDoAlto ,

values you see when opening the GenICam Browser and right clicking on your device are values that have to be changed bevore opening the camera.
You do this on the access token of the device after discovery:

auto discoveryInformation = DeviceFactory::Discover(DiscoverFlags::IgnoreVins | DiscoverFlags::IgnoreGevSD);
discoveryInformation[0].SetParameter("PixelFormat", "0");
discoveryInformation[0].SetParameter("PacketResend", "60");
discoveryInformation[0].SetParameter("NumBuffer", "3");

The values you are allowed to change (the string to be passed) can be read from the GenICam.ini that can be found on %cvbdata%drivers.

If you are not interested in the total amount of buffer used by Cvb but the currently used (filled, locked) buffers, you are left with a number of nodemaps that you can also inspect in the GenICam browser.

Here is an example of using the nodemap that comes with each wait() call and gives you information about the acquisition:

auto [image, status, nodeMaps] = stream->WaitFor(std::chrono::seconds(10));
auto vinBufferNodemap = nodeMaps["VinBuffer"];
auto frameIDNodeValue = vinBufferNodemap->Node<Cvb::IntegerNode>("FrameID")->Value();
auto frameTimeStampNodeValue = vinBufferNodemap->Node<Cvb::IntegerNode>("Timestamp")->Value();
auto incompleImageNodeValue = vinBufferNodemap->Node<Cvb::BooleanNode>("IsIncomplete")->Value();

Then there is the device nodemap:

auto deviceNodemap = device->NodeMap("Device");
frameRate_ = deviceNodemap->Node<Cvb::FloatNode>("Std::AcquisitionFrameRate")->Value();
auto packetNode = deviceNodemap->Node<Cvb::IntegerNode>("Std::GevSCPSPacketSize");
packetNode->SetValue(8192);

And also interesting the datastream Nodemap:


auto dataStreamNodemap = stream->NodeMap("TLDatastream");
auto streamLostFrameCount = dataStreamNodemap->Node<Cvb::IntegerNode>("StreamLostFrameCount")->Value();
auto numBuffersAcquiredTL = dataStreamNodemap->Node<Cvb::IntegerNode>("NumBuffersAcquired")->Value();

In the last nodemap there is also “Stream Output Buffer Count” which replaces the “NumBuffersPending” and thus should be exactly what you are looking for.

Cheers
Chris

Hello, similar to AxelDosSantosDoAlto, I would like to access the status of the digital inputs as well as offset, height, and other useful information in addition. In the C5 version, I already have the integrated structure described in the post mentioned by Simon.

The solution of accessing the parameters doesn’t seem to be the best for our applications, as we work at very high speeds and we would like to ensure the proper correspondence of the data with the profiles as done in C5.

Is there a way to read the parameters from the frame itself, just like it’s done in the C5 version? I can’t find any documentation or examples that explain how to access the information in the same way as C5 (leveraging the extra information of the frame and copying it to a structure).

The NodeMapEnumerator returned by the wait() of the stream provides information about the timestamp and frameId, but I can’t seem to access the rest of the parameters I’m interested in: offset, expo, and digital input status.

Can you help me? Thank you very much in advance.

Hi @Simon

could you have a look at Lei’s question and if there is a way to get the data he needs from the camera?

Cheers
Chris

Hi @Lei,

each time an image buffer is received by stream.wait() the DeviceNodeMap is renewed and the data are locked until stream.wait() is called for the next time. So it is valid to use this statement in a highspeed application as well as long as there is not multithreading used to access the DeviceNodeMap.

The following code is valid to receive the Chunk data using the DeviceNodeMap also using a C6:

using Stemmer.Cvb;
using Stemmer.Cvb.Driver;
using Stemmer.Cvb.GenApi;


namespace CVB_UsingChunkDataFromNodemap
{
  internal class Program
  {
    static void Main(string[] args)
    {
      DiscoveryInformationList discoveryList = DeviceFactory.Discover(DiscoverFlags.IgnoreVins);
      //Enable ChunkMode on Driver
      discoveryList[0].SetParameter("AttachChunk", "1");
      using (var device = DeviceFactory.Open(discoveryList[0].AccessToken,AcquisitionStack.GenTL) as GenICamDevice)//"C:\\Program Files\\STEMMER IMAGING\\Common Vision Blox\\Drivers\\GenICam.vin"))//
      {
        //Stream stream = device.Stream;
        var stream = device.GetStream<CompositeStream>(0);

        NodeMap deviceNodeMap = device.NodeMaps[NodeMapNames.Device];

        //Enable ChunkMode in device NodeMap
        BooleanNode ChunkModeActive = deviceNodeMap["Std::ChunkModeActive"] as BooleanNode;
        ChunkModeActive.Value = true;

        IntegerNode ChunkTimestampNode = deviceNodeMap["Std::ChunkTimestamp"] as IntegerNode;
        IntegerNode ChunkLineStatusAll = deviceNodeMap["Std::ChunkLineStatusAll"] as IntegerNode;

        stream.Start();
        for (int i = 0; i < 10; i++)
        {
          using (Composite composite = stream.Wait(out WaitStatus status))
          {
            using (MultiPartImage image = MultiPartImage.FromComposite(composite))
            {
              using (NodeMapDictionary nodeMaps = NodeMapDictionary.FromImage(image))
              {
                Console.WriteLine("ChunkTimestamp: " + ChunkTimestampNode.Value + " | ChunkLineStatusAll: " + ChunkLineStatusAll.Value);                
              }
            }
          }  
        }
        stream.Stop();
      }
      Console.ReadLine();
    }
  }
}

As you can see from the code, the ChunkTimestampNode and ChunkLineStatusAll are defined outside the acquisition loop while their value is accessed inside the acquisition loop. The values plotted are related to the currently received frame by stream.wait(), still in case the camera did already aquire multiple images afterwards.

There is still the option as for the C5 to read the Chunk Data from the image/composite buffer. Therefore read the payload size and calculate the image size. The chunk is always attachted to the end of the buffer. Note that in case of using multiple ROIs the chunk format changes.
To get the chunk size:

IntegerNode PayloadSize = deviceNodeMap["Std::PayloadSize"] as IntegerNode;
long buffersize = PayloadSize.Value;
IntegerNode Width = deviceNodeMap["Std::Width"] as IntegerNode;
IntegerNode Height = deviceNodeMap["Std::Height"] as IntegerNode;
long imagesize = Width.Value * Height.Value * 2; //(*2 for 16 bit)
long chunksize = buffersize - imagesize;

You can also read the chunksize from the NodeMap in GenICam-Browser when toggeling AttachChunk:
Payload_ChunkNotActive

Payload_ChunkActive

For accessing the rawdata have a look here for the datatypes of C6-ChunkFrameInfo: https://docs.automationtechnology.de/software/cxsdk/Classes/struct_c_x___c6___c_h_u_n_k___f_r_a_m_e___i_n_f_o/
and here for the region info:
https://docs.automationtechnology.de/software/cxsdk/Classes/struct_c_x___c6___c_h_u_n_k___s_c_a_n3_d___r_e_g_i_o_n___i_n_f_o/

2 Likes

Thank you very much, @Simon , for your response. I’d like to mention that we have already implemented the solution by parameters, and the application is working successfully. However, we would like to continue studying the option of using the structure to receive the data similarly to C5. I understand the structure clearly now, but I’m unsure about how to access the data. The method stream->wait() returns the following information:

 Cvb::CompositePtr composite; // std::shared_ptr<Composite>;
 Cvb::WaitStatus waitStatus;
 Cvb::NodeMapEnumerator enumerator;
std::tie(composite, waitStatus, enumerator) = stream->Wait();

How can we access the pointer to the aligned data? Up until now, we’ve been accessing each image using the itemAt() method, but the images are not even aligned.

Thank you again for your help.

Dear @Lei,

using the direct memory access for the C6 while receiving composite images is not that staigt forward.
Here is an example for accessing the Chunk Data in CVB C#. This can be translated to C++, note that pointer handling is a bit different there.

using Stemmer.Cvb;
using Stemmer.Cvb.Driver;
using Stemmer.Cvb.GenApi;


namespace CVB_UsingChunkDataFromNodemap
{
  internal class Program
  {
    static void Main(string[] args)
    {
      DiscoveryInformationList discoveryList = DeviceFactory.Discover(DiscoverFlags.IgnoreVins);
      //Enable ChunkMode on Driver
      discoveryList[0].SetParameter("AttachChunk", "1");
      using (var device = DeviceFactory.Open(discoveryList[0].AccessToken, AcquisitionStack.GenTL) as GenICamDevice)//"C:\\Program Files\\STEMMER IMAGING\\Common Vision Blox\\Drivers\\GenICam.vin"))//
      {
        //Stream stream = device.Stream;
        var stream = device.GetStream<CompositeStream>(0);

        NodeMap deviceNodeMap = device.NodeMaps[NodeMapNames.Device];

        //Enable ChunkMode in device NodeMap
        BooleanNode ChunkModeActive = deviceNodeMap["Std::ChunkModeActive"] as BooleanNode;
        ChunkModeActive.Value = true;

        IntegerNode ChunkTimestampNode = deviceNodeMap["Std::ChunkTimestamp"] as IntegerNode;
        IntegerNode ChunkLineStatusAll = deviceNodeMap["Std::ChunkLineStatusAll"] as IntegerNode;

        stream.Start();
        for (int i = 0; i < 10; i++)
        {
          using (Composite composite = stream.Wait(out WaitStatus status))
          {
            using (MultiPartImage image = MultiPartImage.FromComposite(composite))
            {
              //Extract Chunkdata from the Nodemap
              using (NodeMapDictionary nodeMaps = NodeMapDictionary.FromImage(image))
              {
                Console.WriteLine("ChunkTimestamp: " + ChunkTimestampNode.Value + " | ChunkLineStatusAll: " + ChunkLineStatusAll.Value);

              }

              //Extract Chunkdata from the memory
              int count = image.Parts.Count;
              INativeHandle imglastNH = image.Parts[count - 1];
              Image imglast = Image.FromHandle(imglastNH.Handle);
              long imagesize = imglast.Width * imglast.Height * 2; //alternatively to using *2 for 16 bit, get imglast.ColorFormat and define the value
              //Get Basepointer of the last image contained in the composite and add the image size to get memory access to the chunk data.
              IntPtr basptr = imglast.Planes[0].GetLinearAccess().BasePtr;
              ATC6ChunkInfo ATC6chunk = ATC6DereferenceManual(basptr, imagesize);
              Console.WriteLine("TimeStamp: " + ATC6chunk.timeStamp);
              Console.WriteLine("LineStatusAll: " + ATC6chunk.LineStatusAll);
            }
          }
        }
        stream.Stop();
      }

      Console.ReadLine();
    }
    private static ATC6ChunkInfo ATC6DereferenceManual(IntPtr BasePtr, long imagesize)
    {
      ATC6ChunkInfo ATC6chunk = new ATC6ChunkInfo();
      unsafe
      {
        IntPtr* basePTR = (IntPtr*)(BasePtr + (int)imagesize);
        byte* basePTRByte = (byte*)basePTR;
        basePTRByte = basePTRByte + 8;//8 byte buffer between image frame buffer end and chunk buffer start

        //TimeStamp
        UInt64* ptrTimeStamp = (UInt64*)basePTRByte;
        ATC6chunk.timeStamp = *ptrTimeStamp;

        //LineStatusAll
        basePTRByte += 40; //jump 40 bytes to LineStatusAll
        UInt16* ptrLineStatusAll = (UInt16*)basePTRByte;
        ATC6chunk.LineStatusAll = *ptrLineStatusAll;
      }
      return ATC6chunk;
    }
    struct ATC6ChunkInfo
    {      
      public UInt64 timeStamp;
      //...
      public UInt16 LineStatusAll;
    }
  }
}

Use the links above to get the register size of each value in the chunk data.

Thank you very much for your response, @Simon . I’ll try it and keep you posted.

1 Like