Menu

NetJoy UDP DataStructure

2020-01-05
2020-01-13
  • Juergen Dodek

    Juergen Dodek - 2020-01-05

    Hi,
    thanks for sharing this software. It works pretty well.
    Currently I am trying the network joystick feature.
    Is there any documentaion about the TCP/UDP data structure?

    I would like to send the X/Y positions via UDP telegramms.

    Best regards

    Juergen

     
  • VIRTUAL

    VIRTUAL - 2020-01-07

    As far as I understand (due to the need for knowledge of the TCP/UDP data structure), you want to write your program for managing NetJoy. If not, write.

    It is not so simple to describe the whole structure of transmission-reception, due to the dependence on the settings.

    The main composition of the transfer:
    - device number;
    - some settings;
    - type of control (button, axis, ...);
    - The new value of the control.

    You can simply examine the transmitted packages for changes in axis values to quickly understand where the values are stored in the package. For simplicity, do not use a buffer (Data-Hold Time = 0).

    Associated Code С# (only the necessary fragments):

    public enum DeviceType : byte
    {
        None = 0,
      ...
        InternalJoystick = 250,
       ...        
    }
    
    public enum ControlType : byte
    {
        None = 0,
        Button = 1,
        Axis = 2,
        Pov = 3,
        ...
    }
    
    public enum ControlModifierType : byte
    {
        None = 0,
        Press = 1,
        ...
        Set = 21,
        ...
        Release = 252,
        ...       
    }
    
    public enum AxisType : uint
    {
        None = 0,
        X = 48,
        Y = 49,
        Z = 50,
        RX = 51,
        RY = 52,
        RZ = 53,
        SL0 = 54,
        SL1 = 55,
        WHL = 56,
        POV = 57,
        X1 = 100,
        X2 = 101,
        X3 = 102,
        X4 = 103,
        X5 = 104,
        X6 = 105
    }
    
    public class IOEventArgs
    {
        public DeviceType DeviceType;
        public Int64 DeviceNumber;
        public ControlType Control;
        public int Number;
        public ControlModifierType Modifier;
       ...
        public double Value1;
        public double Value2;
        ...
        public DateTime TimeStamp;
    
        public void Init()
        {
            DeviceType = DeviceType.None;
            DeviceNumber = 0;
            Control = ControlType.None;
            Number = 0;
            Modifier = ControlModifierType.None;
            Value1 = 0.0;
            Value2 = 0.0;
            ...
        }
    
        public void CopyFrom(IOEventArgs Target)
        {
            Init();
            DeviceType = Target.DeviceType;
            DeviceNumber = Target.DeviceNumber;
            Control = Target.Control;
            Number = Target.Number;
            Modifier = Target.Modifier;
            Value1 = Target.Value1;
            Value2 = Target.Value2;
        }
    
        public IOEventArgs()
        {
            Init();
        }
    }
    
    //===================================================================================================================
    
    public class NetJoyPacket
        {
            public const int MinInfoBlockSize = 2;
            public const int MaxInfoBlockSize = MinInfoBlockSize + 8;
            public const int MaxIOEventSize = 19;
            public const int MinIOEventSize = 11;
            public const int MinPacketSize = MinInfoBlockSize + MinIOEventSize;
    
            public bool InstantMode = false; // if "false" then need store SendingTimeStamp
            public DateTime SendingTimeStamp;
            public DateTime ReceptionTimeStamp;
            public TimeSpan Mismatch = new TimeSpan(0);
            public TimeSpan Delay = new TimeSpan(0);
            public byte DeviceNumber = 0;
            public List<IOEventArgs> IOEArgs = new List<IOEventArgs>();
    
    
            public static int GetIOEventSize(IOEventArgs IOEArgs)
            {
                if(IOEArgs.Control != ControlType.Button)
                {
                    return MaxIOEventSize;
                }
                else
                {
                    return MinIOEventSize;
                }
            }
    
            public int GetSize()
            {
                int Size = 2;         // DeviceNumber + EventCount(+instantBit)
                if (!InstantMode)
                {
                    Size += 8;        // TimeStamp
                }
                for (int i = 0; i < IOEArgs.Count; i++)
                {
                    Size += NetJoyPacket.GetIOEventSize(IOEArgs[i]);
                }
                return Size;
            }
    
            public static byte[] GetBytes(IOEventArgs IOEArgs)
            {
                int Size = 11;
                if (IOEArgs.Control != ControlType.Button)
                {
                    Size += 8;
                }
                byte[] Result = new byte[Size];
                Result[0] = (byte)IOEArgs.Control;
                Result[1] = (byte)IOEArgs.Number;
                Result[2] = (byte)IOEArgs.Modifier;
                int Offset = 3;
                if (IOEArgs.Control != ControlType.Button)
                {
                    byte[] ValueBytes = BitConverter.GetBytes(IOEArgs.Value1);
                    for (int i = 0; i < ValueBytes.Length; i++)
                    {
                        Result[Offset + i] = ValueBytes[i];
                    }
                    Offset += 8;
                }
                Int64 Ticks = 0;
                if (IOEArgs.TimeStamp != null)
                {
                    Ticks = IOEArgs.TimeStamp.Ticks;
                }
                byte[] DateBytes = BitConverter.GetBytes(Ticks);
                for (int i = 0; i < DateBytes.Length; i++)
                {
                    Result[Offset + i] = DateBytes[i];
                }
                return Result;
            }
    
            public static IOEventArgs GetIOEArgs(byte[] Bytes, int Offset, byte DeviceNumber)
            {
                IOEventArgs IOEArgs = new IOEventArgs();
                IOEArgs.DeviceType = DeviceType.NetJoystick;
                IOEArgs.DeviceNumber = DeviceNumber;
                IOEArgs.Control = (ControlType)Bytes[Offset + 0];                     
                IOEArgs.Number = Bytes[Offset + 1];
                IOEArgs.Modifier = (ControlModifierType)Bytes[Offset + 2];
                Offset += 3;
                if (IOEArgs.Control != ControlType.Button)
                {
                    IOEArgs.Value1 = BitConverter.ToDouble(Bytes, Offset);
                    Offset += 8;
                }
                Int64 Ticks = BitConverter.ToInt64(Bytes, Offset);
                IOEArgs.TimeStamp = new DateTime(Ticks);
                return IOEArgs;
            }
    
            public byte[] GetBytes()
            {
                int Offset = 0;
                int TimeStampLength = 0;
                byte[] ClientTimeBytes = null;
                Int64 Ticks = 0;
                int Size = MinInfoBlockSize;
                if (!InstantMode)
                {
                    SendingTimeStamp = DateTime.Now;
                    Ticks = SendingTimeStamp.Ticks;
                    ClientTimeBytes = BitConverter.GetBytes(Ticks);
                    TimeStampLength = ClientTimeBytes.Length;
                    Size += TimeStampLength;
                }         
                int EventsSize = 0;
                List<byte[]> IOEArgsBytes = new List<byte[]>();
                foreach (IOEventArgs Args in IOEArgs)
                {
                    byte[] Data = NetJoyPacket.GetBytes(Args);
                    IOEArgsBytes.Add(Data);
                    EventsSize += Data.Length;
                }
                Size += EventsSize;
                byte[] Result = new byte[Size];
                Result[Offset] = DeviceNumber;
                Offset += 1;
                Result[Offset] = (byte)IOEArgs.Count;
                if (InstantMode)
                {
                    Result[Offset] += 128; //add bit flag "instant"
                }            
                Offset += 1;
                if (!InstantMode)
                {
                    for (int i = 0; i < TimeStampLength; i++)
                    {
                        Result[i + Offset] = ClientTimeBytes[i];
                    }
                    Offset += TimeStampLength;
                }            
                for (int j = 0; j < IOEArgsBytes.Count; j++)
                {
                    byte[] EventData = IOEArgsBytes[j];
                    for (int i = 0; i < EventData.Length; i++)
                    {
                        Result[i + Offset] = EventData[i];
                    }
                    Offset += EventData.Length;
                }
                return Result;
            }
    
            public NetJoyPacket()
            {
            }
    
            public NetJoyPacket(byte[] Bytes, int Offset)
            {
                SendingTimeStamp = DateTime.Now;
                DeviceNumber = Bytes[Offset];
                Offset += 1;
                byte EventCount = Bytes[Offset];
                Offset += 1;
                if (EventCount >= 128)
                {
                    InstantMode = true;
                    EventCount -= 128;
                }
                else
                {
                    InstantMode = false;
                }
                if(!InstantMode)
                {
                    Int64 Ticks = BitConverter.ToInt64(Bytes, Offset);
                    SendingTimeStamp = new DateTime(Ticks);
                    Offset += 8;
                }           
                for (int i = 0; i < EventCount; i++)
                {
                    IOEventArgs Arg = GetIOEArgs(Bytes, Offset, DeviceNumber);
                    IOEArgs.Add(Arg);
                    Offset += NetJoyPacket.GetIOEventSize(Arg);
                }
                if (InstantMode)
                {
                    if (EventCount > 0)
                    {
                        SendingTimeStamp = IOEArgs[EventCount - 1].TimeStamp;
                    }
                    else
                    {
                        SendingTimeStamp = DateTime.MinValue;
                    }
                }
                ReceptionTimeStamp = DateTime.Now;
                Mismatch = ReceptionTimeStamp - SendingTimeStamp;
            }
        }
    
    
    //===================================================================================================================
    
                    public void SendButtonData(int ButtonNumber, bool IsPressed)
                {
                    IOEventArgs IEventArgs = new IOEventArgs();
                    IEventArgs.DeviceType = DeviceType.NetJoystick;
                    IEventArgs.DeviceNumber = Number;
                    IEventArgs.Control = ControlType.Button;
                    IEventArgs.Number = ButtonNumber - 1;
                    if (IsPressed == true)
                    {
                        IEventArgs.Modifier = ControlModifierType.Press;
                    }
                    else
                    {
                        IEventArgs.Modifier = ControlModifierType.Release;
                    }
                    IEventArgs.Value1 = 0;
                    SendData(IEventArgs);
                }
    
                public void SendAxisData(AxisType Axis, double Value)
                {
                    IOEventArgs IEventArgs = new IOEventArgs();
                    IEventArgs.DeviceType = DeviceType.NetJoystick;
                    IEventArgs.DeviceNumber = Number;
                    IEventArgs.Control = ControlType.Axis;
                    IEventArgs.Number = (int)Axis;
                    IEventArgs.Modifier = ControlModifierType.Set;
                    IEventArgs.Value1 = Value;
                    SendData(IEventArgs);
                }
    
                public void SendPovData(int PovNumber, int Value)
                {
                    IOEventArgs IEventArgs = new IOEventArgs();
                    IEventArgs.DeviceType = DeviceType.NetJoystick;
                    IEventArgs.DeviceNumber = Number;
                    IEventArgs.Control = ControlType.Pov;
                    IEventArgs.Number = PovNumber - 1;
                    IEventArgs.Modifier = ControlModifierType.Set;
                    IEventArgs.Value1 = Value;
                    SendData(IEventArgs);
                }
    
                public bool SendData(IOEventArgs IOEArgs)
                {
                    lock (SyncRoot)
                    {
                        bool Result = false;
                        IOEArgs.TimeStamp = DateTime.Now;
                        if (Packet == null)
                        {
                            Packet = new NetJoyPacket();
                            Packet.DeviceNumber = Number;
                        }
                        Packet.IOEArgs.Add(IOEArgs);
                        if ((TimeInterval == 0) || (Packet.IOEArgs.Count >= 127) || (Packet.GetSize() + NetJoyPacket.MaxIOEventSize > MTU))
                        {
                            Packet.InstantMode = true;
                            byte[] NetData = Packet.GetBytes();
                            switch (Protocol)
                            {
                                case ProtocolType.Tcp:
                                    {
                                        Result = SendTCPData(NetData);
                                        break;
                                    }
                                case ProtocolType.Udp:
                                    {
                                        Result = SendUDPData(NetData);
                                        break;
                                    }
                            }
                            Packet = null;
                        }
                        else
                        {
                            if (!Timer.Enabled)
                            {
                                Timer.Start();
                            }
                            Result = true;
                        }
                        return Result;
                    }
                }
    
     

    Last edit: VIRTUAL 2020-01-07
  • Juergen Dodek

    Juergen Dodek - 2020-01-11

    Hi Virtual,

    thanks for showing me to handle the NetJoy. With your posting I should be able to do it.
    But when analysing the code I was wondering if this is the best way for solving my task by using
    Virtual Controller.
    The main aim is to bring a CAN based industrial joystick or vehicle shiftconsole into Windows and
    emulate it as a generic game pad like the xbox controllers. For the first evaluation I am using a CAN/Ethernet gateway. Or in other words, I would just like to be CAN independent here. At the moment is is pretty easy to send the hole process structure in a single UDP telegramm to windows.
    Now I am dealing with the idea to try/use xbox-interface API and write a small UDP Client for windows to communicate with the gateway.

    Thanks for your support
    and best regards

    Juergen

     
  • VIRTUAL

    VIRTUAL - 2020-01-13

    In your case (you do not need a user interface; I/O devices are known; there is a known way to transfer data; you are able to write a bit of your own code) the best option would be to use “vXbox API”. This way should provide a minimum consumption of computer resources to achieve your goal.

    https://github.com/shauleiz/ScpVBus/releases
    https://github.com/shauleiz/vGen/releases

     

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.