Случайные символы от преобразования строки Arduino в Excel BSTR через C++ DLL

Так что в некотором коде Arduino я Serial.print несколько номеров, как это (var1=##,var2=##,var3=##). У меня есть C++ DLL, которую я делаю, чтобы получить эту информацию и разобрать ее в массив вариантов, как это ("var1=",##.##,"var2=",##.##,"var3=",##.##) хранение частей строки в качестве варианта BSTR и #S как вариант удваивается. Этот вариант массива происходит из Excel, и цель моей библиотеки C++ состоит в том, чтобы разрешить последовательный обмен данными с Excel и платой Arduino и из нее.

Моя проблема в том, что вместо того, чтобы возвращать информацию, как мне хотелось бы, я получаю много лишней тарабарщины в конце и не могу понять, откуда она берется. Я включаю свой связанный код раньше, пропуская последовательную связь, потому что я знаю, что эта часть работает точно. На данный момент код Arduino просто отправляет (##,##,##) затем Serial.println(); чтобы сделать вещи проще, так как нет никаких строк, только цифры.

Код VBA:

'collects all rows available currently up to 1000(global numRows) lines each time, stores result in dataArray
Public Function getAllData()
    Dim stringArray() As Variant
    Dim variantArray() As Variant

    ReDim stringArray(0 To 3000) As Variant
    ReDim variantArray(0 To 3000) As Variant


    If Main.dataCollectionActive Then
        Dim staringDataRow As Integer
        staringDataRow = currentDataRow

        Dim returnValue As Long

        returnValue = GetAllDataTypes(stringArray(), variantArray(), currentDataRow)
        If Not (returnValue = 0) Then
            If Not (returnValue = -1) Then
                If printWhileCollectingData = True Then
                    ' Call printVariantData
                    Dim row As Integer
                    Dim col As Integer
                    For row = startingDataRow To (currentDataRow - 1)
                        Main.dataBox.Text = Main.dataBox.Text & stringArray(row)
                        For col = 0 To (getNumColumns() - 1)
                             Main.Cells(row + startingRow, col + startingCol).Value = variantArray(col + (row * getNumColumns()))
                        Next col
                    Next row
                End If
            End If
        End If

        'only stoped in Main sheets code for stopDataButton or if the array has reached its limit
        If Not ((currentDataRow * getNumColumns) = UBound(variantArray())) Then
            Application.OnTime Now + TimeValue("00:00:01"), "getAllData"
        End If
        Main.Range("$L$4").Value = currentDataRow
    End If
End Function

Код C++ DLL:

//end1 comes before end2, so end1 = i-1 and end2 = i characters
DLL_EXPORT bool WINAPI isEndLine(char end1, char end2){
    //check for CRLF
    if (end1 == '\r') {
        if (end2 == '\n') {
            return true;
        }
    }

    //check for normal endLine \0
    else if ((end1 == '\0')) {
        return true;
    }

    //check for users own endLine character
    else{
        for(int i = 0; i < MAX_ENDLINE_LENGTH; i++){
            if (end1 == endLine[i]){
                return true;
            }
        }
    }
    return false;
}

//reads a single character from buffer, if there is nothing returned then the global, bufferAvailable is set to false, so that no more characters are asked for, for now
DLL_EXPORT char WINAPI readCharFromSerial() {
    char dataChar[1];
    DWORD dwBytesRead = 0; //number of data bytes read in
    if(!ReadFile(hSerial, dataChar, 1, &dwBytesRead, NULL)) { //gets data if successful, if not then notifies user
        ErrorExit("ReadFile, reading a character");
    }
    if (dwBytesRead == 0) {
        bufferAvailable = false;
    }
    return dataChar[0]; //returns read in data
}

//reads a single line by pulling one character at a time until it finds the end of line character, if the buffer has characters in it
DLL_EXPORT void WINAPI readLineFromSerialPort(char* line, int length) {
    bufferAvailable = true;
    int i = 0;
    line[i] = readCharFromSerial();
    i++;
    if (bufferAvailable){
        do{
            line[i] = readCharFromSerial();
            i++;
        }while((!isEndLine(line[i-2], line[i-1])) && (i < length));

        if(line[i-2] == '\r') {
            line[i-2] = '\n';
            line[i-1] = '\0';
        }

        if(!(i<length)){
            MessageBoxA(NULL, (LPCSTR)("string length not long enough"), TEXT("readLineFromSerialPort"), MB_OK);
        }
    }
}


DLL_EXPORT void WINAPI PlaceDblInVarDbl(varArr* VD, double data) {
    (VD->ptr[VD->index]).vt = VT_R8; //FIXME add function to do this
    (VD->ptr[VD->index]).dblVal = data;
    VD->index++;
}

DLL_EXPORT void WINAPI PlaceCharPtrInVarBstr(varArr* VD, char* cString) {
    BSTR bstr = new OLECHAR[MAX_DATA_LENGTH];
    const char* Cstring = (const char*)cString;
    mbstowcs(bstr, Cstring, MAX_DATA_LENGTH);
    VD->ptr[VD->index].vt = VT_BSTR;
    SysReAllocString(&((VD->ptr[VD->index]).bstrVal), bstr);
    VD->index++;
}

DLL_EXPORT int  WINAPI ParseCharPtrToVarBstrAndVarDbl(varArr* VD, char* dataString) {
    //FIXME perhaps make an array of chars for strings that are allowed without being coppied, this would be added to the if begging with !isdigit (like the '.')
    bool hasLetters = false;
    int endIndex = 0;
    int startIndex = 0;
    //    MessageBoxA(NULL, (LPCSTR)("entered"), TEXT("ParseCharPtrToVarBstrAndVarDbl"), MB_OK);
    while (!(isEndLine(dataString[endIndex], dataString[endIndex+1]))) {
        hasLetters = false;
        startIndex = endIndex;
        while (!isDelim(dataString[endIndex]) && (!(isEndLine(dataString[endIndex-1], dataString[endIndex])))) {
            if (!(isdigit(dataString[endIndex])) && !(dataString[endIndex] == ' ') && !(dataString[endIndex] == '.')) {
                hasLetters = true;
            }
            endIndex++;
        }
       // MessageBoxA(NULL, (LPCSTR)("delimeter found"), TEXT("ParseCharPtrToVarBstrAndVarDbl"), MB_OK);
        if((startIndex + 1 == endIndex) || (startIndex == endIndex)/* && !(isEndLine(dataString[endIndex-1], dataString[endIndex]))*/) {
            //FIXME odd way to fix CRLF problem
           // MessageBoxA(NULL, (LPCSTR)("triggered if"), TEXT("ParseCharPtrToVarBstrAndVarDbl"), MB_OK);
        }
        else if (hasLetters) { //string
            char smallerString[MAX_DATA_LENGTH];
            const char* DStr = (const char*)(&(dataString[startIndex]));
            strncpy(smallerString, DStr, endIndex-startIndex+1);
            smallerString[endIndex-startIndex+1] = '\0';
            PlaceCharPtrInVarBstr(VD, smallerString);
           // MessageBoxA(NULL, (LPCSTR)("triggered hasLetters"), TEXT("ParseCharPtrToVarBstrAndVarDbl"), MB_OK);
        }
        else { //double
            //FIXME remove whitespace
            char* start = &dataString[startIndex];
            char* eOS = &(dataString[endIndex]);
            char** endOfString = &eOS;

            double data = strtod(start, endOfString);
            //FIXME do some error checking
            PlaceDblInVarDbl(VD, data);
           // MessageBoxA(NULL, (LPCSTR)("triggered does not hasLetters"), TEXT("ParseCharPtrToVarBstrAndVarDbl"), MB_OK);
        }
        endIndex++;
    }
   //, MessageBoxA(NULL, (LPCSTR)("exited"), TEXT("ParseCharPtrToVarBstrAndVarDbl"), MB_OK);
    return VD->startingIndex - VD->index;
}

//first read a lines from the serial port and then parse it into dataArray, does this until there is no data in buffer or array is full
//returns how many rows that it read
DLL_EXPORT int WINAPI GetAllDataTypes(LPSAFEARRAY* unparsedData, LPSAFEARRAY* parsedData, int* currentDataRow) {
    bufferAvailable = true;
    varArr UPD;
    OpenVariantSafeArray(&UPD, unparsedData, *currentDataRow); if (UPD.failed) { return -1; }
    varArr PD;
    OpenVariantSafeArray(&PD, parsedData, *currentDataRow);    if (PD.failed)  { return -1; }

    while (bufferAvailable && ((PD.index + 10) < PD.uBound)) {
        char dataString[MAX_DATA_LENGTH];
        readLineFromSerialPort(dataString, MAX_DATA_LENGTH);
        if (bufferAvailable) {
            PlaceCharPtrInVarBstr(&UPD, dataString);
            ParseCharPtrToVarBstrAndVarDbl(&PD, dataString);
        }
    }

    CloseVariantSafeArray(&UPD, unparsedData); if (UPD.failed) { return -1; }
    CloseVariantSafeArray(&PD, parsedData);    if (PD.failed)  { return -1; }
    *currentDataRow +=  UPD.index - UPD.startingIndex;
    bufferAvailable = true;
    return UPD.index - UPD.startingIndex;
}

Результирующая ошибка:

Это необработанная строка, возвращаемая при печати в текстовом поле в Excel.

0,00,0.01,0,00 0,10,0.02,0.01 0,20,0.03,0.02 0,30,0.04,0.03 0,40,0,05,0,04 0,50,0,06,0,05 0,60,0,07,0,06 0,70,0,0,0,07 0,80,0,09,0,00,90,0,10,0.09 1.00,0.10,0.10

Однако проанализированная строка, которая печатается в ячейках VBA для циклов. Все еще есть случайные символы. Похоже, что с моим разбором в DLL что-то не так, так как последний double всегда заканчивается строкой, а затем он всегда имеет случайные строки.

0,000 0,010 "0,00
"
- 0,100 0,020
"0,01
"- 0.200
0,030 "0,02
"   -
0,300   0,040 "0,03
"
- 0,400   0,050
0,900 0,100 "0,09
"
- 0,000 0,100
"0,10
"- 0,100
0,110 "0,11
"-
0,200 0,120 "0,12
"
- 0,300   0,130
"0,13
"- 0,400
0,140   "0,14
"   -
0,500   0,150   "0,15
"
-   0,600   0,160
"0,16
"   -   
"0,25
"   -   0,600
0,240   "0,26
"   -
0,700   0,250   "0,27
"
-       

1 ответ

Решение

Последний endIndex++ в ParseCharPtrToVarBstrAndVarDbl() в коде DLL для пропуска разделителя, пропущенного через нулевой терминатор в строках. Исправление состоит в том, чтобы добавить последний оператор if для конца строки, и если это так, вернуть.

Другие вопросы по тегам