Page 3 of 3

Re: Reset timestamps

Posted: Wed 24. Jul 2024, 07:39
by jorgz
Hello Marvin,

This is how I initialize the CAN channel:

Code: Select all

protected override void Initialize()
        {
            lock (_PortLock)
            {
                TPCANStatus ret = PCANBasic.Initialize(
                        PCANHandle,
                        PCANBaudrate);

                if (ret != TPCANStatus.PCAN_ERROR_OK)
                    throw new IOException(String.Format("{0} failed to connect: {1}", Name, ret));

                //to save sent frames on receive side with timestamp
                UInt32 iBuffer;
                iBuffer = PCANBasic.PCAN_PARAMETER_ON;
                TPCANStatus ret2 = PCANBasic.SetValue(PCANHandle, TPCANParameter.PCAN_ALLOW_ECHO_FRAMES, ref iBuffer, sizeof(UInt32));

                if (ret2 != TPCANStatus.PCAN_ERROR_OK)
                   throw new IOException(String.Format("{0} failed to activate echo frames: {1}", Name, ret2));


            }
        }
This is where the CAN message read is done:

Code: Select all

protected override Frame ReadInternal(int timeout)
        {
            TPCANMsg pcanMsg;
            TPCANTimestamp timestamp;
            long ticks;

            // start the timeout watch
            Stopwatch watch = new Stopwatch();
            watch.Start();

            TPCANStatus ret;
            // keep trying to read if the receive queue is empty
            do
            {
                lock (_PortLock)
                {
                    ret = PCANBasic.Read(PCANHandle, out pcanMsg, out timestamp);
                }
            } while (ret == TPCANStatus.PCAN_ERROR_QRCVEMPTY && (Timeout.IsInfinite(timeout) || watch.ElapsedMilliseconds < timeout));

            // if there was still no success then the operation has timed out
            if (ret == TPCANStatus.PCAN_ERROR_QRCVEMPTY)
            {
                // if there is no timeout then simply return null
                if (timeout == Timeout.None)
                    return null;
                else
                    throw new TimeoutException(String.Format("{0} read timed out", Name));
            }

            // if it was not a timeout then we got a different error
            if (ret != TPCANStatus.PCAN_ERROR_OK)
                throw new CANException(String.Format("{0} failed to read: {1}", Name, ret));

            //ticks according to peakcan
            ticks = timestamp.micros + timestamp.millis * 1000 + 0x100000000 * 1000 * timestamp.millis_overflow;
          

            if ((pcanMsg.MSGTYPE & TPCANMessageType.PCAN_MESSAGE_ECHO) == TPCANMessageType.PCAN_MESSAGE_ECHO)
            {
              return new Frame((int)pcanMsg.ID, FrameType.Standard_echo, pcanMsg.DATA, ticks);
            }

            return new Frame((int)pcanMsg.ID, FrameTypeFromPCAN(pcanMsg.MSGTYPE), pcanMsg.DATA,ticks);

            
        }
This is where is log is controlled:

Code: Select all

private void OnFrameReceived(Frame frame, Message msg)
        {
            var timestamp = frame.Ticks;
            
            if (frame.Type == FrameType.Standard_echo)
            {
                frame.Type = FrameType.Extended;
                Log(frame, msg, timestamp, TraceEntryDirection.TX);
                
            }
            else
            {
                Log(frame, msg, timestamp, TraceEntryDirection.RX);
            }
        }
The log:

Code: Select all

private void Log(Frame frame, Message msg, DateTime timeStamp, TraceEntryDirection direction)
        {
            var entry = new TraceEntry(frame, msg, timeStamp, direction);

            foreach (var sink in _Sinks)
                sink.Write(entry);
        }
then put into text file:

Code: Select all

public void Write(TraceEntry entry)
        {
            var ts = entry.Ticks;
            var data = String.Join(" ", entry.Frame.Data.Select((b) => $"{b:X2}"));
            var line = $"[{ts}] Dir: {entry.Direction} Name: {entry.Message?.Name} ID: {entry.Frame.Id:X} Type: {entry.Frame.Type} Data: {data}";

            lock (_Lock)
            {
                _Writer?.WriteLine(line);
            }
        }
Thanks for the support.

Re: Reset timestamps

Posted: Wed 24. Jul 2024, 09:56
by K.Wagner
Hello,

please be careful when using literal numbers in calculations, and using intrinsec data type casting.

It is true, that you find in our documentaiton the following:
Total microseconds calculation from the structure TPCANTimestamp
Total microseconds calculation from the structure TPCANTimestamp
Microseconds-Calculation.png (4.06 KiB) Viewed 2889 times

But keep in mind that this is not a "code example", but a formula. If you just use this literally, you will have your compiler interpreting the two literals "1000" as integers. Having this so, you will get at some point an overflow, having the compiler making a cast from the 64 bit result into a 32 bit value. This will look like the variable was reset, as the upper bits are truncated.

Here you can see the assumtion that the compiler does with your literal
Calculation using literals without suffix
Calculation using literals without suffix
calculation-Int32.png (11.1 KiB) Viewed 2889 times

You can force those literals to be from the expected type (long) by using suffixes. 'L' is used in C#, for example:
Calculation using literals with suffix
Calculation using literals with suffix
calculation-Int64.png (11.44 KiB) Viewed 2889 times

To sum up: Using literals in calculations may end in different or wrong results, if unintended data type conversion occurs and truncation/overflow take place.