binary.Read does not handle struct padding as expected - struct

In a recent Go project I need to read a binary data file generated by Python, but due to padding, binary.Read in Go doesn't read it properly. Below is a minimal example of my problem.
The struct I deal with if of the following format
type Index struct{
A int32
B int32
C int32
D int64
}
As you can see the size of the struct is 4+4+4+8=20, but Python added an extra 4 bytes for alignment. So the size is actually 24.
Below is the runnable Python code I use to write this struct:
#!/usr/bin/env python
# encoding=utf8
import struct
if __name__ == '__main__':
data = range(1, 13)
format = 'iiiq' * 3
content = struct.pack(format, *data)
with open('index.bin', 'wb') as f:
f.write(content)
the iiiq format means there are three 32 bit integers and one 64 bit integer in the struct, which is the same with the Index struct I defined earlier. And running this code will generate a file named index.bin of size 72, which equals to 24 * 3.
And below is the Go code I use to read index.bin:
package main
import (
"encoding/binary"
"fmt"
"os"
"io"
"unsafe"
)
type Index struct {
A int32
B int32
C int32
D int64
}
func main() {
indexSize := unsafe.Sizeof(Index{})
fp, _ := os.Open("index.bin")
defer fp.Close()
info, _ := fp.Stat()
fileSize := info.Size()
entryCnt := fileSize / int64(indexSize)
fmt.Printf("entry cnt: %d\n", entryCnt)
readSlice := make([]Index, entryCnt)
reader := io.Reader(fp)
_ = binary.Read(reader, binary.LittleEndian, &readSlice)
fmt.Printf("After read:\n%#v\n", readSlice)
}
And this is the output:
entry cnt: 3
After read:
[]main.Index{main.Index{A:1, B:2, C:3, D:17179869184}, main.Index{A:0, B:5, C:6, D:7}, main.Index{A:8, B:0, C:9, D:47244640266}}
Obviously the output is messed up when reading from the Python generated file.
So my question is, how can I read the python generated file(with padding) in Go properly?

You can just pad your Go struct to match:
type Index struct {
A int32
B int32
C int32
_ int32
D int64
}
Which produces:
[]main.Index{main.Index{A:1, B:2, C:3, _:0, D:4}, main.Index{A:5, B:6, C:7, _:0, D:8}, main.Index{A:9, B:10, C:11, _:0, D:12}}
binary.Read knows to skip the _ field:
When reading into structs, the field data for fields with blank (_) field names is skipped; i.e., blank field names may be used for padding.
(So the 0 values for _ are not because the padding in the file was set to zero, but because the struct field was initialized to 0 and never changed, and the padding in the file was skipped rather than read.)

For example,
package main
import (
"bufio"
"encoding/binary"
"fmt"
"io"
"os"
)
type Index struct {
A int32
B int32
C int32
D int64
}
func readIndex(r io.Reader) (Index, error) {
var index Index
var buf [24]byte
_, err := io.ReadFull(r, buf[:])
if err != nil {
return index, err
}
index.A = int32(binary.LittleEndian.Uint32(buf[0:4]))
index.B = int32(binary.LittleEndian.Uint32(buf[4:8]))
index.C = int32(binary.LittleEndian.Uint32(buf[8:12]))
index.D = int64(binary.LittleEndian.Uint64(buf[16:24]))
return index, nil
}
func main() {
f, err := os.Open("index.bin")
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
defer f.Close()
r := bufio.NewReader(f)
indexes := make([]Index, 0, 1024)
for {
index, err := readIndex(r)
if err != nil {
if err == io.EOF {
break
}
fmt.Fprintln(os.Stderr, err)
return
}
indexes = append(indexes, index)
}
fmt.Println(indexes)
}
Output:
[{1 2 3 4} {5 6 7 8} {9 10 11 12}]
Input:
00000000 01 00 00 00 02 00 00 00 03 00 00 00 00 00 00 00 |................|
00000010 04 00 00 00 00 00 00 00 05 00 00 00 06 00 00 00 |................|
00000020 07 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 |................|
00000030 09 00 00 00 0a 00 00 00 0b 00 00 00 00 00 00 00 |................|
00000040 0c 00 00 00 00 00 00 00 |........|

#Barber's solution is workable, but I found adding a padding field not so comfortable. And I found a better way of doing it.
Below is the new golang read code which works perfectly:
package main
import (
"fmt"
"os"
"io"
"io/ioutil"
"unsafe"
)
type Index struct {
A int32
B int32
C int32
// Pad int32
D int64
}
func main() {
indexSize := unsafe.Sizeof(Index{})
fp, _ := os.Open("index.bin")
defer fp.Close()
info, _ := fp.Stat()
fileSize := info.Size()
entryCnt := fileSize / int64(indexSize)
reader := io.Reader(fp)
allBytes, _ := ioutil.ReadAll(reader)
readSlice := *((*[]Index)(unsafe.Pointer(&allBytes)))
realLen := len(allBytes) / int(indexSize)
readSlice = readSlice[:realLen]
fmt.Printf("After read:\n%#v\n", readSlice)
}
Output:
After read:
[]main.Index{main.Index{A:1, B:2, C:3, D:4}, main.Index{A:5, B:6, C:7, D:8}, main.Index{A:9, B:10, C:11, D:12}}
This solution needs no explicit padding field.
The essence here is that if you let golang convert the whole bytes to a slice of Index struct, it seems to be able to handle the padding well.

Related

Read DTC codes OBD2 using arduino

I try read dtc codes using a obd device (sparkfun obd based on stn1110) and an arduino. Running this code
uint16_t codes[6];
byte dtcCount = obd.readDTC(codes, 6);
if (dtcCount == 0) {
Serial.println("No DTC");
} else {
Serial.print(dtcCount);
Serial.print(" DTC:");
for (byte n = 0; n < dtcCount; n++) {
Serial.print(' ');
Serial.print(codes[n], HEX);
}
Serial.println();
}
delay(5000);
}
Returns:
2 DTC: 3303 100
How can I interpret those codes? I mean usually I saw error codes are like PXXXX ...
Thanks a lot
L.E.
I scanned same car using a "proffesional" tester and found following codes (see pictures).
What I am doing wrong with my code or how should I interpret my results in order to get same codes as the service scanner?
Basically my code show 2 dtc's but the scanner found 5 in total
Dig further and I think the function used for read dtc is somehow wrong:
this is the function
byte COBD::readDTC(uint16_t codes[], byte maxCodes)
{
byte codesRead = 0;
for (byte n = 0; n < 6; n++) {
char buffer[128];
sprintf_P(buffer, n == 0 ? PSTR("03\r") : PSTR("03%02X\r"), n);
write(buffer);
if (receive(buffer, sizeof(buffer)) > 0) {
Serial.print("received buffer :");
Serial.println(buffer);
if (!strstr_P(buffer, PSTR("NO DATA"))) {
char *p = strstr(buffer, "43");
if (p) {
while (codesRead < maxCodes && *p) {
p += 6;
if (*p == '\r') {
p = strchr(p, ':');
if (!p) break;
p += 2;
}
uint16_t code = hex2uint16(p);
if (code == 0) break;
codes[codesRead++] = code;
}
}
break;
}
}
}
return codesRead;
}
As you see I printed "received buffer" to see what I get from OBD and the result is:
received buffer :43 01 33 03 01 00 00
>
I think is something wrong here, or I miss something in the answer
why i get that">" on a new line?
also according to https://en.wikipedia.org/wiki/OBD-II_PIDs (chapter Service 03 (no PID required))
i delete "43" from my buffer and get "01 33 03 01 00 00"
then "01" means "C - Chassis"
but then nothing match to the decoding description...so I became almost sure that I don t ask / read as it should this thing. I think i get an invalid answer somehow ...
Again any help will be appreciated :)
Thanks
L.E.2 - did another progess step.
According to this document - https://www.elmelectronics.com/wp-content/uploads/2016/07/ELM327DS.pdf , on page 34
first should find out the number of dtc stored and then try to read them.
Sending >0101 to obd return me "41 01 82 07 61 01"
and if I do 82 - 80 => I have 2 trouble codes ?!?! why I have 5 of them on the tester?
From my previous response, also according to that document
"43 01 33 03 01 00 00" means P0133 and P0301
But on the tester, except those 2, I had also - P0303 , P0300 and C1513
How to get to read correctly the dtc codes?
Thanks again

What is the equivalent of perl's Win32::OLE::Variant in Python 3

I have the following code snippet in perl for automating an application script using Win32::OLE
use Win32::OLE;
use Win32::OLE::Variant;
my $app = new Win32::OLE 'Some.Application';
my $InfoPacket = "78 00 C4 10 95 B4
00 02 31 7F 80 FF";
my #Bytes = split(/[ \n][ \n]*/, $InfoPacket);
my #HexBytes;
foreach(#Bytes)
{
push #HexBytes, eval "0x$_";
}
my $Packet = pack("C12", #HexBytes);
my $VarPacket = Variant(VT_UI1, $Packet);
my $InfoObj = app -> ProcessPacket($VarPacket);
print $InfoObj -> Text();
I have converted the entire code in Python 3, except for the [exact] equivalent of pack() and Variant() functions.
from win32com.client import Dispatch
from struct import pack
app = Dispatch("Some.Application")
InfoPacket = "78 00 C4 10 95 B4 \
00 02 31 7F 80 FF"
Bytes = InfoPacket.split()
HexBytes = [int(b, 16) for b in Bytes]
Packet = pack('B'*12, *HexBytes) # This however, is not giving the exact same output as perl's...
VarPacket = ... # Need to know the python equivalent of above Variant() function...
InfoObj = app.ProcessPacket(VarPacket)
print (InfoObj.Text())
Please suggest the python equivalent of the pack() and Variant() functions used in perl script in the given context so that the final variable VarPacket can be used by Python's Dispatch object to properly generate the InfoObj object.
Thanks !!!
I am not sure about the Python equivalent of the Perl Variant, but for the first question about packing the unsigned char array, the following works for me:
from struct import pack
def gen_list():
info_packet = "78 00 C4 10 95 B4 00 02 31 7F 80 FF"
count = 0
for hex_str in info_packet.split():
yield int(hex_str, 16)
count += 1
for j in range(count, 120):
yield int(0)
packet = pack("120B", *list(gen_list()))
Edit
From the test file testPyComTest.py it looks like you can generate the variant like this:
import win32com.client
import pythoncom
variant = win32com.client.VARIANT(pythoncom.VT_ARRAY | pythoncom.VT_UI1, packet)

How to decode a Bluetooth LE Package / Frame / Beacon of a FreeTec PX-1737-919 Bluetooth 4.0 Temperature Sensor?

The Sensor advertises these Bluetooth LE Packages:
> 04 3E 26 02 01 03 01 B8 AB C0 5D 4C D9 1A 02 01 04 09 09 38
42 42 41 43 34 39 44 07 16 09 18 47 08 00 FE 04 16 0F 18 5B
B3
> 04 3E 26 02 01 03 01 B8 AB C0 5D 4C D9 1A 02 01 04 09 09 38
42 42 41 43 34 39 44 07 16 09 18 45 08 00 FE 04 16 0F 18 5A
BC
> 04 3E 26 02 01 03 01 B8 AB C0 5D 4C D9 1A 02 01 04 09 09 38
42 42 41 43 34 39 44 07 16 09 18 44 08 00 FE 04 16 0F 18 5B
B2
How do I decode it?
LE Advertising Report:
ADV_NONCONN_IND - Non connectable undirected advertising (3)
bdaddr D9:4C:5D:C0:AB:B8 (Random)
Flags: 0x04
Complete local name: '8BBAC49D'
Unknown type 0x16 with 6 bytes data
Unknown type 0x16 with 3 bytes data
RSSI: -77
It's not a beacon advertisement. The packets are the device sending three pieces of information.
The device's local name "8BBAC49D"
The Health Thermometer Service is available (with a current temperature measurement)
The Battery Service is available (with a current battery level measurement)
Breakdown of this BLE discovered packet:
> 04 3E 26 02 01 03 01 B8 AB C0 5D 4C D9 1A 02 01 04 09 09 38
42 42 41 43 34 39 44 07 16 09 18 44 08 00 FE 04 16 0F 18 5B
B2
If you look at your repeat packet, you will see that each temperature measurement varies slightly, as does the battery measurement.
Here is the breakdown of the packet:
B8 AB C0 5D 4C D9 1A # Bluetooth Mac Address
02 # Number of bytes that follow in first AD structure
01 # Flags AD type
04 # Flags value 0x04 = 000000100
bit 0 (OFF) LE Limited Discoverable Mode
bit 1 (OFF) LE General Discoverable Mode
bit 2 (ON) BR/EDR Not Supported
bit 3 (OFF) Simultaneous LE and BR/EDR to Same Device Capable (controller)
bit 4 (OFF) Simultaneous LE and BR/EDR to Same Device Capable (Host)
09 # Number of bytes that follow in the first AD Structure
09 # Complete Local Name AD Type
38 42 42 41 43 34 39 44 # "8BBAC49D"
07 # Number of bytes that follow in the second AD Structure
16 # Service Data AD Type
09 18 # 16-bit Service UUID 0x1809 = Health thermometer (org.bluetooth.service.health_thermometer)
44 08 00 FE # Additional Service Data 440800 (Temperature = 0x000844 x 10^-2) = 21.16 degrees
04 # Number of bytes that follow in the third AD Structure
16 # Service Data AD Type
0F 18 # 16-bit Service UUID 0x180F = Battery Service (org.bluetooth.service.battery_service)
5B # Additional Service Data (battery level)
B2 # checksum
See the bluetooth 16-bit service UUID definitions for more information:
https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.battery_service.xml
https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.health_thermometer.xml
You can use hcidump -w dump.log to record some packages and open it in Wireshark - which does most of the decoding for you.
The missing pices:
04 # HCI Packet Type: HCI Event (0x04)
3E # Event Code: LE Meta (0x3e)
26 # Parameter Total Length: 38
02 # Sub Event: LE Advertising Report (0x02)
01 # Num Reports: 1
03 # Event Type: Non-Connectable Undirected Advertising (0x03)
01 # Peer Address Type: Random Device Address (0x01)
Screenshot form Wireshark:
And here is the Packet in btsnoop.log format. Works with Wireshark and hcidump -r packet.log.
public class Util {
public static int convertU16ToInt(byte i) {
int firstByte = (0x000000FF & ((int)i));
return firstByte;
}
public static int bytesToInt(final byte[] array, final int start)
{
final ByteBuffer buf = ByteBuffer.wrap(array); // big endian by default
buf.position(start);
buf.put(array);
buf.position(start);
return buf.getInt();
}
public static int convertU32ToInt(byte b[], int start) {
return ((b[start] << 24) & 0xff000000 |(b[start + 1] << 16) & 0xff0000
| (b[start + 2] << 8) & 0xff00 | (b[start + 3]) & 0xff);
}
public static long int64Converter(byte buf[], int start) {
return ((buf[start] & 0xFFL) << 56) | ((buf[start + 1] & 0xFFL) << 48)
| ((buf[start + 2] & 0xFFL) << 40)
| ((buf[start + 3] & 0xFFL) << 32)
| ((buf[start + 4] & 0xFFL) << 24)
| ((buf[start + 5] & 0xFFL) << 16)
| ((buf[start + 6] & 0xFFL) << 8)
| ((buf[start + 7] & 0xFFL) << 0);
}
public static long convertU16ToInt(byte[] buf, int index) {
int firstByte = (0x000000FF & ((int)buf[index]));
int secondByte = (0x000000FF & ((int)buf[index+1]));
int thirdByte = (0x000000FF & ((int)buf[index+2]));
int fourthByte = (0x000000FF & ((int)buf[index+3]));
index = index+4;
long anUnsignedInt = ((long) (firstByte << 24
| secondByte << 16
| thirdByte << 8
| fourthByte))
& 0xFFFFFFFFL;
return anUnsignedInt;
}
public static short toUnsigned(byte b) {
return (short)(b & 0xff);
}
public static int convertU16ToInt(byte byte1, byte byte2) {
int N = (( 255 - byte1 & 0xff ) << 8 ) | byte2 & 0xff;
return N;
}
public static short UInt16Decode(byte inbyByteA, byte inbyByteB) {
short n = (short)(((inbyByteA & 0xFF) << 8) | (inbyByteB & 0xFF));
return n;
}
public static long UInt32Decode(int inbyByteA, int inbyByteB) {
int n = inbyByteA<< 16 | inbyByteB;
return n;
}
public static long decodeMeasurement16(byte byte3, byte byte4) {
return 0L;
}
public static double decodeMeasurement32(byte byte3, byte byte4, byte byte6, byte byte7) {
double outdblFloatValue = 0;
int outi16DecimalPointPosition = 0;
int ui16Integer1 = convertU16ToInt (byte3, byte4);
int ui16Integer2 = convertU16ToInt (byte6, byte7);
int ui32Integer = ( (int)UInt32Decode (ui16Integer1, ui16Integer2) ) & 0x07FFFFFF;
outi16DecimalPointPosition = ((0x000000FF - byte3 ) >> 3) - 15;
// Decode raw value, with Exampledata: 0x05FFFFFC
if ((100000000 + 0x2000000) > ui32Integer) {
// Data is a valid value
if (0x04000000 == (ui32Integer & 0x04000000)) {
ui32Integer = (ui32Integer | 0xF8000000);
// With Exampledata: 0xFDFFFFFC
}
ui32Integer = ui32Integer + 0x02000000; // with Exampledata: 0xFFFFFFFC
}
else {
// Data contains error code, decode error code
outdblFloatValue = (double)((ui32Integer - 0x02000000) - 16352.0);
outi16DecimalPointPosition = 0;
return -36; // Return value is error code
}
outdblFloatValue = (double)ui32Integer;
outdblFloatValue = outdblFloatValue / (Math.pow(10.0f, (double)outi16DecimalPointPosition));
return outdblFloatValue;
}
public static int toByte(int number) {
int tmp = number & 0xff;
return (tmp & 0x80) == 0 ? tmp : tmp - 256;
}
public static long getUnsignedInt(int x) {
return x & 0x00000000ffffffffL;
}
}
After getting bytes array, call Util.decodeMeasurement32(byte[9], byte[10], byte[11], byte[12]). These bytes are the temperature bytes.
The thermometer works like a BLE beacon, i.e. you cannot connect. All information is given in the advertising every 5 seconds or so. E.g. with Android & the blessed library (https://github.com/weliem/blessed-android):
lateinit var central: BluetoothCentralManager
val HTS_SERVICE_UUID = ParcelUuid.fromString("00001809-0000-1000-8000-00805f9b34fb")
val BTS_SERVICE_UUID = ParcelUuid.fromString("0000180f-0000-1000-8000-00805f9b34fb")
fun convertTemperature(bytes: ByteArray?): Float {
if (null == bytes)
return -273.15f
val bb: ByteBuffer = ByteBuffer.allocate(2)
bb.order(ByteOrder.LITTLE_ENDIAN)
bb.put(bytes[0])
bb.put(bytes[1])
return bb.getShort(0) / 100.0f
}
val bluetoothCentralManagerCallback: BluetoothCentralManagerCallback = object : BluetoothCentralManagerCallback() {
override fun onDiscoveredPeripheral(peripheral: BluetoothPeripheral, scanResult: ScanResult) {
val rssi = scanResult.rssi
val scanRec = scanResult.scanRecord
val payload = scanRec?.serviceData
val temperature = convertTemperature(payload?.get(key = HTS_SERVICE_UUID))
val other_data = payload?.get(key = HTS_SERVICE_UUID)?.sliceArray(2..3)
val battery_level = payload?.get(key = BTS_SERVICE_UUID)
central.stopScan()
tempLabel.text = getString(R.string.temp, temperature)
}
}
central = BluetoothCentralManager(applicationContext, bluetoothCentralManagerCallback, Handler(Looper.getMainLooper()))
central.scanForPeripheralsWithNames(arrayOf("7FE2183D"))
Every time you want a new reading you can start scanning again.

how to convert node Buffer to string like console.log show

I want log the buffer to string, but I am not want to use buffer.toString() method
console.log(new Buffer(12))
show
< Buffer 00 22 33 11 55 ...>
but console.log('buffer:' + new Buffer(12))
show
buffer: something can't read
I want
buffer: < Buffer 00 22 33 11 55 ...>
Doing
var b = new Buffer([0x41, 0x42, 0x43, 0x44]);
console.log(b);
// <Buffer 41 42 43 44>
is the same as doing
console.log(b.inspect());
whereas
var b = new Buffer([0x41, 0x42, 0x43, 0x44]);
console.log('str' + b);
// strABCD
is the same as doing
console.log('str' + b.toString());
because using string concatenation using + automatically converts both sides of the operator to strings using .toString(). console.log(...) on the other hand converts its arguments to strings by calling .inspect() when possible.
The easiest way to do what you want to do is to just let console.log do its thing by passing it multiple arguments
console.log('buffer:', new Buffer(12))
Note, the , instead of a +, so instead of concatenating using .toString, you let console.log stringify each of its arguments on its own.

C++ converting hex number to human readable string representation

I have a char[32] that consist of ASCII characters, some of which may be non printable characters, however, they are all in valid range of 0 to 255.
Let's say, it contains these numbers:
{ 9A 2E 5C 66 26 3D 5A 88 76 26 F1 09 B5 32 DE 10 91 3E 68 2F EA 7B C9 52 66 23 9D 77 16 BB C9 42 }
I'd like to print out or store in std::string as "9A2E5C66263D5A887626F109B532DE10913E682FEA7BC95266239D7716BBC942", however, if I print it out using printf or sprintf, it will yield the ASCII representative of each number, and instead it will print out "ö.\f&=Zàv&Ò µ2fië>h/Í{…Rf#ùwª…B", which is the correct ASCII character representation, since: ö = 9a, . = 2e, ...
How do I reliably get the string representation of the hex numbers? ie: I'd expect a char[64] which contains "9A2E5C66263D5A887626F109B532DE10913E682FEA7BC95266239D7716BBC942" instead.
Thanks!
void bytesToHex(char* dest, char* src, int size) {
for (unsigned int i = 0; i < size; i++) {
sprintf(&dest[i * 2], "%02x", src[i]);
}
}
You'd have to allocate your own memory here.
It would be used like this:
char myBuffer[32]
char result[65];
bytesToHex(result, myBuffer, 32);
result[64] = 0;
// print it
printf(result);
// or store it in an std::string
std::string str = string(result);
I tried:
char szStringRep[64];
for ( int i = 0; i < 32; i++ ) {
sprintf( &szStringRep[i*2], "%x", szHash[i] );
}
it works as intended, but if it encountered a < 0x10 number, it will null terminated as it is printing 0
You can encode your string to another system like Base64, which is widely used when using encryption algorithms.

Resources