Displaying the ASCII Table Number for each item in a byte array - vb.net

I am writing a Message builder and in it I have to send a byte array to a remote machine requesting data.
I have an 8 bit number that counts how many items are being requested (count) and am trying to get the number inserted into a byte array.
The Byte array locations are in positions 70 & 71 of the array and in little endian order.
I am trying to just convert the byte and then insert the converted bytes with the following code.
Dim count As Byte = 3
Dim countbyteArray As Byte() = BitConverter.GetBytes(count)
sendBytes(70) = (countbyteArray(0))
sendBytes(71) = (countbyteArray(1))
when I do this for anything greater than 1 it does not get accepted by the device I am sending the byte array to.
Am I in the right ball Park or completely wrong as I am not completely up to speed with Bytes and Bits. Also any example or explanation would be greatly appreciated.

Little endian is the natural order in .NET so it is easy to work with.
As for a message builder, wouldn't it be easier to say
Dim message = New Message With { .Command = Command.Request, .Device = &hF, .Count = 8, .Filter = &h1234 }
Dim bytes = message.GetBytes()
' Hex for debugging
Dim hex = BitConverter.ToString(bytes)
You just have to layout the structure of the message. One way is with LayoutKind.Explicit. I'd probably choose the LayoutKind that most closely aligns with the device documentation.
Public Enum Command As Byte
Reset = 0
Request = 1
End Enum
<StructLayout(LayoutKind.Explicit)>
Public Structure Message
<FieldOffset(0)> Public Command As Command
<FieldOffset(1)> Public Device As Byte
' unused bytes default to 0
<FieldOffset(10)> Public Count As UInt16
<FieldOffset(12)> Public Filter As Int16
Public Function GetBytes() As Byte()
Dim rawData(0 To Marshal.SizeOf(Me) - 1) As Byte
Dim handle = GCHandle.Alloc(rawData, GCHandleType.Pinned)
Try
Dim rawDataPtr = handle.AddrOfPinnedObject()
Marshal.StructureToPtr(Me, rawDataPtr, False)
Return rawData
Finally
handle.Free()
End Try
End Function
End Structure
'TODO define enums, etc to make it easier for caller.
'TODO update struct per device documentation

Related

How can I read a specific byte in a byte array in VB.NET?

I have a binary file being loaded into a Byte array using Dim settingsBinary As Byte() = My.Computer.FileSystem.ReadAllBytes(settingsFile) where settingsFile is the path to my binary file.
Let's say that my binary file has got three bytes and I want to read the first byte as a Boolean (00 = False, 01 = True). How can I get those bytes? I have tried to use this question's answer to read those three bytes, but I can't wrap my head around it.
Just to clarify, I need to get the three bytes separately: Get the first byte, then set CheckBox1.Checked to the first byte, and so on with the other bytes.
A byte array works just like any other array: You can use an indexer to access a specific element.
Dim firstByte As Byte = settingsBinary(0)
Dim secondByte As Byte = settingsBinary(1)
Dim thirdByte As Byte = settingsBinary(2)
And then you can convert the byte into a boolean:
Dim firstBoolean As Boolean
Select Case firstByte
Case 0
firstBoolean = False
Case 1
firstBoolean = True
Case Else
Throw New Exception("Invalid first byte in settings file: " & firstByte)
End Select
(Generalizing the conversion logic into a method that can be used for all three bytes is left as an exercise to the reader.)

Is there a VB function to convert 32 bit float to little endian hex

As shown here: https://gregstoll.com/~gregstoll/floattohex/
I need to convert a 32 bit float to a little endian hex (click the swap endiness button before converting). I've managed to do this in python by converting to big endian then reordering, but I have no idea how to approach this issue in VB as I'm entirely new to the language. Using the Hex inbuilt function returns 19a, which i assume means its not correctly evaluating my input as a single.
I've found a recommended solution here but cant get it working:
https://www.tek-tips.com/faqs.cfm?fid=6404
Any suggestions would be great, thanks in advance.
There are a number of ways you could do this - the most obvious being the Copy Memory API. Some time ago, a pretty neat solution was published here: Extracting bits from a float in vba which avoided the need for the API
Basically, you'd just need a couple of short functions:
Option Explicit
Type SingleType
Value As Single
End Type
Type FourBytesType
Value(3) As Byte
End Type
Private Function SingleToBytes(f As Single) As Variant
Dim sngType As SingleType
Dim bytesType As FourBytesType
sngType.Value = f
LSet bytesType = sngType
SingleToBytes = bytesType.Value
End Function
Private Function BytesToHex(bytes As Variant) As String
Dim result As String
Dim i As Long
For i = LBound(bytes) To UBound(bytes)
result = result & IIf(bytes(i) < 16, "0", "") & Hex(bytes(i))
Next
BytesToHex = result
End Function
If you wanted to test Endianness and reverse the array, then something like the following, which kind of uses a Byte Order Mark, could be added. I haven't tested it on a big-endian processor but I think it'd work:
Private Function IsLittleEndianProcessor() As Boolean
Const BOM As Single = 1
Const MSB As Byte = 63
Dim bytes() As Byte
Dim n As Long
bytes = SingleToBytes(BOM)
n = UBound(bytes)
IsLittleEndianProcessor = (bytes(n) = MSB)
End Function
Private Function ChangeEndianness(bytes As Variant) As Variant
Dim result() As Byte
Dim n As Long, m As Long
ReDim result(UBound(bytes))
m = UBound(bytes)
For n = LBound(bytes) To UBound(bytes)
result(m) = bytes(n)
m = m - 1
Next
ChangeEndianness = result
End Function
I'm not actually sure how you want the hex string displayed but you could step backwards through the array to write the hex if needed. Sample test would be:
Public Sub TestMe()
Dim bytes As Variant
Dim output As String
bytes = SingleToBytes(3.1415)
If Not IsLittleEndianProcessor Then
bytes = ChangeEndianness(bytes)
End If
output = BytesToHex(bytes)
Debug.Print output
End Sub

Get first three bits from a byte

My question is simple, i need to convert the first three bits of a byte to a integer (Enum) value. However the things i try i always get a 0 back. This is wat the document says:
Bit 0-3: These bits indicate the connection state.
Value 0 to 14 indicate the connection state. Value 3 = Connected
Now the response i get (from a serial device) is a encoded byte stream of ASCII hex values, so i first need to convert it from a hex string to byte array and then get the bits from it. This is my code so far:
Dim strResonse As String = "0C03" 'This should result in a connection state value of 3
Dim ConState(2) As Byte
ConState = HexStringToByteArray(strResonse)
Dim int As Integer = ConState(1) << 1 And ConState(1) << 2 And ConState(1) << 3
Debug.Print(int.ToString)
Private Function HexStringToByteArray(ByVal shex As String) As Byte()
Dim B As Byte() = Enumerable.Range(0, shex.Length).Where(Function(x) x Mod 2 = 0).[Select](Function(x) Convert.ToByte(shex.Substring(x, 2), 16)).ToArray()
Return Enumerable.Range(0, shex.Length).Where(Function(x) x Mod 2 = 0).[Select](Function(x) Convert.ToByte(shex.Substring(x, 2), 16)).ToArray()
End Function
It would be easier to use bit operation
Dim connectionState As Integer
Dim response As Integer = &HC03
' Get the first 4 bits. 15 in binary is 1111
connectionState = response And 15
If your input is really a string, there a built-in way to convert to integer.
Dim response As Integer = Convert.ToInt32("C03", 16)
If you really want to get an array, I suggest you use the built in method.
Dim allBits As Byte() = BitConverter.GetBytes(response)
There also the BitArray class that can be handy.

How to enlarge a List(of String)(bytes)

I have a variable Queue in which I write information from a stream. The variable is initiated as follows:
Public Shared Queue As List(Of String) = New List(Of String)(1024)
The code to read the stream is
Public Shared Sub ReadStreamForever(ByVal stream As Stream)
Dim encoder = New UTF8Encoding()
Dim buffer = New Byte(2047) {}
Dim counter as Integer = 0
While True
If stream.CanRead Then
Dim len As Integer = stream.Read(buffer, 0, 2048)
Counter = Counter + 1
If len > 0 Then
Dim text = encoder.GetString(buffer, 0, len)
SSEApplication.Push(text)
Else
Exit While
End If
Else
Exit While
End If
End While
End Sub
Where the push methode just does a few string manipulation and adds line after line into the Queue Variable
Public Shared Sub Push(ByVal text As String)
If String.IsNullOrWhiteSpace(text) Then
Return
End If
Dim lines = text.Trim().Split(vbLf)
SSEApplication.Queue.AddRange(lines)
End Sub
I have different big datasets I want to stream but the Queue length after filling it up is always 2691, so it looks like it is kind of limited in length. I just do not know where I limit the Queue Variable and how to enlarge it. Could anyone help me here?
In general, List doesn't have fixed length, Add method resizes List and makes space for another element.
If you want to have fixed length, you could use simple array: Dim Queue(1024) As string
But then, you will get an exception when trying to add more elements, so you can check the condition in Push method:
If lines.Count < 1024 Then
SSEApplication.Queue.AddRange(lines)
End If
That check will also prevent having more than 1024 elements when using List, but if you have collection of fixed length, I would recommend using simple array.
Useful resource: Arrays in Visual Basic, there you can also read, how to enlarge array, when you want to add extra elements using ReDim keyword.

VB.Net - Converting multi-byte hex values to decimal

How would I convert the two individual byte values (in hex) to a decimal value representing the concatenated hex values? For example, if I have the
Dim byte1 As Byte = &H99
Dim byte2 As Byte = &H99
' I want the decimal representation of the hex value "9999"
' (byte1.ToString + byte2.ToString) which should be "39321"
Currently I use the following code:
Dim decVal as integer
decVal = Val("&H" + Hex$(byte1).ToString + Hex$(byte2).ToString)
However, when I use this, the value (decVal) comes out to "-26215"
What am I doing wrong in the conversion here?
Try the following:
decVal = byte1 * 256 + byte2
Your problem is when you call Val("&H9999"), if you use CInt("&H9999") or Integer.Parse("9999", System.Globalization.NumberStyles.HexNumber) you would get the correct answer (in this case at least)
If you look at the output from:
decVal = Val("&H9999")
Console.WriteLine(decVal.ToString("X"))
you get FFFF9999
I'm not sure why this is happening, but I would see this as a reason not to use the Val function to parse hexadecimal strings
#Patrick McDonald has a good inline way of doing it and if that's what you're looking for then I recommend using that. But I can't resist not giving you the overly complicated but cross-platform and expanded versions. One thing to note, you are asking to convert bytes in big-endian mode which is a very important topic to understand if you're playing with bytes.
Both functions below take a param array and perform conversions but in two different ways. One handles an arbitrarily large byte array (bound only by platform restrictions) and the other uses the built-in converter class that reads byte arrays.
Once again, let me stress the "overly complicated" part.
Private Shared Function ConvertBytes2(ByVal ParamArray bytes() As Byte) As UInt32
''//Reverse the array order
Dim NewBytes = bytes.Reverse().ToList().ToArray()
''//Our return value
Dim Dec As UInt32 = 0
''//Temporary value for bit shifting
Dim T As UInt32
''//Loop through the bytes from left to right
For I = (NewBytes.Count - 1) To 0 Step -1
''//Grab the byte
T = NewBytes(I)
''//Shift it and add to our return value
Dec += T << (8 * I)
Next
Return Dec
End Function
Private Shared Function ConvertBytes1(ByVal ParamArray bytes() As Byte) As UInt32
''//We want to read the bytes in big-endian order but BitConverter works in little-endian mode on most Windows systems (but not all) so convert if needed
Dim NewBytes() As Byte
If BitConverter.IsLittleEndian Then
NewBytes = bytes.Reverse().ToList().ToArray()
Else
NewBytes = bytes
End If
''//Our return value
Dim Dec As UInt32
''//BitConverter can return UIn16, 32 or 64, we're only supporting 16 and 32 below
If NewBytes.Count = 2 Then
Dec = BitConverter.ToUInt16(NewBytes, 0)
ElseIf NewBytes.Count = 4 Then
Dec = BitConverter.ToUInt32(NewBytes, 0)
Else
''//Invalid number of bytes sent
Throw New ArgumentOutOfRangeException("bytes")
End If
Return Dec
End Function
Dim Byte1 As Byte = &HA
Dim Byte2 As Byte = &H99
''//&h0A99 = 2,713
Trace.WriteLine(ConvertBytes1(Byte1, Byte2))
Trace.WriteLine(ConvertBytes2(Byte1, Byte2))