/*-----------------------------------------------------------------------------
Module: CANoeFDX
Interfaces: -
-------------------------------------------------------------------------------
Fast Data eXchange (FDX)
Protocol definition for connecting a HIL system over UPD to CANoe
-------------------------------------------------------------------------------
Copyright (c) Vector Informatik GmbH. All rights reserved.
-----------------------------------------------------------------------------*/
using System;
using System.IO;
using System.Linq;
namespace FDXClient
{
// ==========================================================================
// Basic Helper for Information of CANoe FDX Protocol
// ==========================================================================
public static class FDXHelper
{
public const byte kFDXMajorVersion = 1;
public const byte kFDXMinorVersion = 2;
public const ulong kFDXSignature = 0x584446656F4E4143;
}
// ==========================================================================
// Helper for Sequence numbers
// ==========================================================================
public static class FDXHelperSequenceNumber
{
public const ushort kSequenceNumberUnused = 0x8000;
public const ushort kSequenceNumberStart = 0x000;
public const ushort kSequenceNumberSessionEndFlag = 0x8000;
}
// ==========================================================================
// Helper for Command Codes in the command header
// ==========================================================================
public static class FDXHelperCommandCode
{
public const ushort kCommandCodeStart = 0x0001;
public const ushort kCommandCodeStop = 0x0002;
public const ushort kCommandCodeKey = 0x0003;
public const ushort kCommandCodeStatus = 0x0004;
public const ushort kCommandCodeDataExchange = 0x0005;
public const ushort kCommandCodeDataRequest = 0x0006;
public const ushort kCommandCodeDataError = 0x0007;
public const ushort kCommandCodeFreeRunningReuqest = 0x0008;
public const ushort kCommandCodeFreeRunningCancel = 0x0009;
public const ushort kCommandCodeStatusRequest = 0x000A;
public const ushort kCommandCodeSequenceNumberError = 0x000B;
public const ushort kCommandCodeHardwareChanged = 0x0010;
}
// ==========================================================================
// Helper for Measurement states and stores the actual state
// ==========================================================================
public static class FDXHelperMeasurementState
{
public const byte kMeasurementStateNotRunning = 1;
public const byte kMeasurementStatePreStart = 2;
public const byte kMeasurementStateRunning = 3;
public const byte kMeasurementStateStop = 4;
public static byte CurrentMeasurementState { set; get; }
static FDXHelperMeasurementState()
{
CurrentMeasurementState = kMeasurementStateNotRunning;
}
}
// ==========================================================================
// Helper for FfreeRunningFlags in the FreeRunningRequestCommand
// ==========================================================================
public static class FDXHelperFreeRunningFlags
{
public const ushort kFreeRunningFlagTransmitAtPreStart = 1;
public const ushort kFreeRunningFlagTransmitAtStop = 2;
public const ushort kFreeRunningFlagTransmitCyclic = 4;
public const ushort kFreeRunningFlagTransmitAtTrigger = 5;
}
// ==========================================================================
// DatagramHeader
// Each datagram transmitted by the UPD protocol start with a header. The
// datagram header contains a signature (magic cookie) and the version of
// the CANoe FDX protocol.
// The header is followed by one ore more command structures. The field
// numberOfCommands specifies, how many command structures are following.
// The datagram header is equipped with a sequence number, that enable CANoe
// and his counterpart to detect lost datagrams.
// ==========================================================================
[Serializable]
public class DatagramHeader
{
public DatagramHeader()
{
this.Reserved = 0;
}
// FDX signature, that is used as a magic cookie.
// This field must contain the value kFDXSignature
// otherwise the datagram is ignored.
public ulong FDXSignature { get; set; }
// major version of CANoe FDX protocol
public byte FDXMajorVersion { get; set; }
// minor version of CANoe FDX protocol
public byte FDXMinorVersion { get; set; }
// number of commands, that are contained in the datagram
public ushort NumberOfCommands { get; set; }
// datagram sequence number of FDX session
public ushort SequenceNumber { get; set; }
//returns the Size of the header
public static int Size() { return 16; }
// 2 unused bytes for a better alignment, may be used in
// a future version of the protocol.
// This field should be initialized to zero.
public ushort Reserved { get; set; }
///
/// This function serialize the class to a byte array
///
/// byte array with the serialized data
public byte[] Serialize()
{
using (var m = new MemoryStream())
{
using (var writer = new BinaryWriter(m))
{
writer.Write(this.FDXSignature);
writer.Write(this.FDXMajorVersion);
writer.Write(this.FDXMinorVersion);
writer.Write(this.NumberOfCommands);
writer.Write(this.SequenceNumber);
writer.Write(this.Reserved);
}
return m.ToArray();
}
}
///
/// This function deserialize from a byte array and return a new class filled
/// with the data
///
/// serialized data that contains the information
/// a new class filled with the deserialized data
public static DatagramHeader DeSerialize(byte[] data)
{
var header = new DatagramHeader();
using (var m = new MemoryStream(data))
{
using (var reader = new BinaryReader(m))
{
header.FDXSignature = reader.ReadUInt64();
header.FDXMajorVersion = reader.ReadByte();
header.FDXMinorVersion = reader.ReadByte();
header.NumberOfCommands = reader.ReadUInt16();
header.SequenceNumber = reader.ReadUInt16();
}
}
return header;
}
}
// ==========================================================================
// CommandHeader
// Each command structure starts with a command header. The header contains
// the size of the complete command structure and the command code.
// ==========================================================================
[Serializable]
public class CommandHeader
{
// size of this command in bytes
public ushort CommandSize { get; set; }
// the kind of command
public ushort CommandCode { get; set; }
///
/// This function serialize the class to a byte array
///
/// byte array with the serialized data
public virtual byte[] Serialize()
{
using (var m = new MemoryStream())
{
using (var writer = new BinaryWriter(m))
{
writer.Write(this.CommandSize);
writer.Write(this.CommandCode);
}
return m.ToArray();
}
}
///
/// This function deserialize from a byte array and return a new class filled
/// with the data
///
/// serialized data that contains the information
/// a new class filled with the deserialized data
public static CommandHeader DeSerialize(byte[] data)
{
var header = new CommandHeader();
using (var m = new MemoryStream(data))
{
using (var reader = new BinaryReader(m))
{
header.CommandSize = reader.ReadUInt16();
header.CommandCode = reader.ReadUInt16();
}
}
return header;
}
}
// ==========================================================================
// KeyCommand
// command code: kCommandCode_Key
// Transmit a key stroke to the runtime kernel. This can invoke a 'on key'
// handler in CAPL.
// ==========================================================================
[Serializable]
public class KeyCommand : CommandHeader
{
// key code as used CAPL programs of CANoe
// use code 0 for KEYUP events
public uint CanoeKeyCode { get; set; }
///
/// This function serialize the class to a byte array
///
/// byte array with the serialized data
public override byte[] Serialize()
{
using (var m = new MemoryStream())
{
using (var writer = new BinaryWriter(m))
{
writer.Write(this.CanoeKeyCode);
}
return base.Serialize().Concat(m.ToArray()).ToArray();
}
}
}
// ==========================================================================
// StatusCommand
// command code: kCommandCode_Status
// This command stores status information from the CANoe-system
// ==========================================================================
[Serializable]
public class StatusCommand : CommandHeader
{
// state of measurement (NotRunning, PreStart,
// Running, Stop)
public byte MeasurementState { get; private set; }
public byte Reserved1 { get; private set; }
public byte Reserved2 { get; private set; }
public byte Reserved3 { get; private set; }
// current time of measurement in nanoseconds
public long Timestamp { get; private set; }
///
/// This function deserialize from a byte array and return a new class filled
/// with the data
///
/// serialized data that contains the information
/// a new class filled with the deserialized data
public new static StatusCommand DeSerialize(byte[] data)
{
var header = new StatusCommand();
using (var m = new MemoryStream(data))
{
using (var reader = new BinaryReader(m))
{
header.CommandSize = reader.ReadUInt16();
header.CommandCode = reader.ReadUInt16();
header.MeasurementState = reader.ReadByte();
header.Reserved1 = reader.ReadByte();
header.Reserved2 = reader.ReadByte();
header.Reserved3 = reader.ReadByte();
header.Timestamp = reader.ReadInt64();
}
}
return header;
}
}
// ==========================================================================
// DataExchangeCommand
// command code: kCommandCode_DataExchange
// This commands is used for exchanging data between CANoe and the FDX-Client
// ==========================================================================
[Serializable]
public class DataExchangeCommand : CommandHeader
{
// ID of the data group
public ushort GroupId { get; set; }
// size of the following array
public ushort DataSize { get; set; }
// the data bytes
public byte[] DataBytes { get; set; }
///
/// This function serialize the class to a byte array
///
/// byte array with the serialized data
public override byte[] Serialize()
{
using (var m = new MemoryStream())
{
using (var writer = new BinaryWriter(m))
{
writer.Write(this.GroupId);
writer.Write(this.DataSize);
writer.Write(this.DataBytes);
}
return base.Serialize().Concat(m.ToArray()).ToArray();
}
}
///
/// This function deserialize from a byte array and return a new class filled
/// with the data
///
/// serialized data that contains the information
/// a new class filled with the deserialized data
public new static DataExchangeCommand DeSerialize(byte[] data)
{
var header = new DataExchangeCommand();
using (var m = new MemoryStream(data))
{
using (var reader = new BinaryReader(m))
{
header.CommandSize = reader.ReadUInt16();
header.CommandCode = reader.ReadUInt16();
header.GroupId = reader.ReadUInt16();
header.DataSize = reader.ReadUInt16();
header.DataBytes = reader.ReadBytes(header.DataSize);
}
}
return header;
}
}
// ==========================================================================
// DataRequestCommand
// command code: kCommandCode_DataRequest
// ==========================================================================
[Serializable]
public class DataRequestCommand : CommandHeader
{
// ID of the requested data group
public ushort GroupId { get; set; }
///
/// This function serialize the class to a byte array
///
/// byte array with the serialized data
public override byte[] Serialize()
{
using (var m = new MemoryStream())
{
using (var writer = new BinaryWriter(m))
{
writer.Write(this.GroupId);
}
return base.Serialize().Concat(m.ToArray()).ToArray();
}
}
}
// ==========================================================================
// DataErrorCommand
// command code: kCommandCode_DataError
// ==========================================================================
[Serializable]
public class DataErrorCommand : CommandHeader
{
// ID of the data group, that was requested
public ushort GroupId { get; private set; }
// The reason, why CANoe cannot process the
// data request command
public ushort DataErrorCode { get; private set; }
///
/// This function deserialize from a byte array and return a new class filled
/// with the data
///
/// serialized data that contains the information
/// a new class filled with the deserialized data
public new static DataErrorCommand DeSerialize(byte[] data)
{
var com = new DataErrorCommand();
using (var m = new MemoryStream(data))
{
using (var reader = new BinaryReader(m))
{
com.CommandSize = reader.ReadUInt16();
com.CommandCode = reader.ReadUInt16();
com.GroupId = reader.ReadUInt16();
com.DataErrorCode = reader.ReadUInt16();
}
}
return com;
}
}
// ==========================================================================
// FreeRunningRequestCommand
// command code: kCommandCode_FreeRunningRequest
// ==========================================================================
[Serializable]
public class FreeRunningRequestCommand : CommandHeader
{
// ID of the data group, that is requested
public ushort GroupId { get; set; }
// Describe the transmit behavior of the data group
public ushort Flags { get; set; }
// Time period in nanoseconds
public uint CycleTime { get; set; }
// Time period in nanoseconds for the first transmit cycle
public uint FirstDuration { get; set; }
///
/// This function serialize the class to a byte array
///
/// byte array with the serialized data
public override byte[] Serialize()
{
using (var m = new MemoryStream())
{
using (var writer = new BinaryWriter(m))
{
writer.Write(this.GroupId);
writer.Write(this.Flags);
writer.Write(this.CycleTime);
writer.Write(this.FirstDuration);
}
return base.Serialize().Concat(m.ToArray()).ToArray();
}
}
}
// ==========================================================================
// FreeRunningCancelCommand
// command code: kCommandCode_FreeRunningCancel
// ==========================================================================
[Serializable]
public class FreeRunningCancelCommand : CommandHeader
{
// ID of the data group, for which the free
// running mode is canceled
public ushort GroupId { get; set; }
///
/// This function serialize the class to a byte array
///
/// byte array with the serialized data
public override byte[] Serialize()
{
using (var m = new MemoryStream())
{
using (var writer = new BinaryWriter(m))
{
writer.Write(this.GroupId);
}
return base.Serialize().Concat(m.ToArray()).ToArray();
}
}
}
// ==========================================================================
// SequenceNumberErrorCommand
// command code: kCommandCode_SequenceNumberError
// ==========================================================================
[Serializable]
public class SequenceNumberErrorCommand : CommandHeader
{
public ushort ReceivedSeqNr { get; private set; }
public ushort ExpectedSeqNr { get; private set; }
///
/// This function deserialize from a byte array and return a new class filled
/// with the data
///
/// serialized data that contains the information
/// a new class filled with the deserialized data
public new static SequenceNumberErrorCommand DeSerialize(byte[] data)
{
var com = new SequenceNumberErrorCommand();
using (var m = new MemoryStream(data))
{
using (var reader = new BinaryReader(m))
{
com.CommandSize = reader.ReadUInt16();
com.CommandCode = reader.ReadUInt16();
com.ReceivedSeqNr = reader.ReadUInt16();
com.ExpectedSeqNr = reader.ReadUInt16();
}
}
return com;
}
}
// ==========================================================================
// HardwareChangedCommand
// command code: kCommandCode_HardwareChanged
// ==========================================================================
[Serializable]
public class HardwareChangedCommand : CommandHeader
{
public uint Reserved { get; private set; }
///
/// This function deserialize from a byte array and return a new class filled
/// with the data
///
/// serialized data that contains the information
/// a new class filled with the deserialized data
public new static HardwareChangedCommand DeSerialize(byte[] data)
{
var com = new HardwareChangedCommand();
using (var m = new MemoryStream(data))
{
using (var reader = new BinaryReader(m))
{
com.CommandSize = reader.ReadUInt16();
com.CommandCode = reader.ReadUInt16();
com.Reserved = reader.ReadUInt32();
}
}
return com;
}
}
}