Virtual Comport reading

Moderators: TomKerekes, dynomotion

Post Reply
katiefangffs
Posts: 14
Joined: Sat Feb 17, 2024 9:57 am

Virtual Comport reading

Post by katiefangffs » Sat Feb 24, 2024 2:51 pm

I'm attempting to display a weight measurement on the KMotionCNC UI screen using a scale connected to the PC, which also connects to my Kogna board.Due to encoding issues, the scale cannot be directly programmed by KMotion so I've managed to read the scale's output to a specific COM port using C++ code. I believe one way to display a specific value on the KMotionCNC screen is by saving the variable in KMotion and assigning it to a DRO button. Consequently, I'm trying to have the board read this value via the virtual COM port. The C++ code writes the read data to a specific file, and then the KMotion code opens that file to save the data to a variable. The challenge arises because the file cannot be opened by both programs simultaneously. To address this, I'm considering implementing a lock mechanism to lock the file while data is being written to it. The writing process will pause for a moment, during which the file will be unlocked, allowing the board to access and record the data.

The C++ code functions perfectly, handling file writing, printing, and pausing at the set moments. However, the KMotion code only reads the correct data once before causing the C++ code to crash, as if it can no longer access the file.

These are the code I am currently using :

*C++:
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#define INTERVAL 5000 // Interval in milliseconds for reading/pausing

bool CreateLockFile(const char* lockFilePath) {
FILE* lockFile = NULL;
errno_t err = fopen_s(&lockFile, lockFilePath, "w");
if (err == 0) {
fclose(lockFile);
return true;
}
return false;
}

bool IsLockFileExists(const char* lockFilePath) {
FILE* lockFile = NULL;
errno_t err = fopen_s(&lockFile, lockFilePath, "r");
if (err == 0) {
fclose(lockFile);
return true;
}
return false;
}

void DeleteLockFile(const char* lockFilePath) {
remove(lockFilePath);
}

int main() {
HANDLE hSerial;
DCB dcbSerialParams = { 0 };
DWORD bytesRead, lastSwitchTime = GetTickCount();
FILE* file = NULL;
char buffer[2560];
const char* lockFilePath = "C:\\Users\\user\\Desktop\\readcomport\\readcomport\\lockfile.txt";
const char* dataFilePath = "C:\\Users\\user\\Desktop\\readcomport\\readcomport\\weight.txt";
bool readingPhase = true;

// Open the serial port
hSerial = CreateFile(TEXT("\\\\.\\COM7"), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hSerial == INVALID_HANDLE_VALUE) {
fprintf(stderr, "Error opening serial port\n");
return 1;
}

// Set up the serial port parameters
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
if (!GetCommState(hSerial, &dcbSerialParams)) {
fprintf(stderr, "Error getting serial port state\n");
CloseHandle(hSerial);
return 1;
}
dcbSerialParams.BaudRate = CBR_9600;
dcbSerialParams.ByteSize = 8;
dcbSerialParams.StopBits = ONESTOPBIT;
dcbSerialParams.Parity = NOPARITY;
if (!SetCommState(hSerial, &dcbSerialParams)) {
fprintf(stderr, "Error setting serial port state\n");
CloseHandle(hSerial);
return 1;
}

while (1) {
DWORD currentTime = GetTickCount();
if (currentTime - lastSwitchTime >= INTERVAL) {
readingPhase = !readingPhase;
lastSwitchTime = currentTime;

if (!readingPhase) {
// End reading phase: Close file and delete lock file
if (file) {
fclose(file);
file = NULL;
}
DeleteLockFile(lockFilePath);
printf("Pausing...\n");
}
else {
// Begin reading phase: Create lock file
CreateLockFile(lockFilePath);
printf("Reading...\n");
}
}

if (readingPhase && !file) {
errno_t err = fopen_s(&file, dataFilePath, "a");
if (err != 0) {
fprintf(stderr, "Error opening data file for writing\n");
continue;
}
}

if (readingPhase && file) {
if (ReadFile(hSerial, buffer, sizeof(buffer) - 1, &bytesRead, NULL) && bytesRead > 0) {
buffer[bytesRead] = '\0'; // Ensure null-terminated string

// Assuming buffer contains a string representation of the weight in kg
double weightInKg;
if (sscanf_s(buffer, "%lf", &weightInKg) == 1) { // Successfully parsed the weight
double weightInG = weightInKg * 1000; // Convert kg to g
fprintf(file, "%f g\n", weightInG); // Write the weight in grams to the file
printf("Data: %f g\n", weightInG); // Also print the weight in grams to the console
}
else {
fprintf(stderr, "Error parsing weight data\n");
}
}
}
}
// Cleanup
CloseHandle(hSerial);
if (file) {
fclose(file);
}

return 0;
}


*Kmotion:
#include "KMotionDef.h"

// Define the paths to your data and lock files
char *dataFilePath = "C:\\Users\\user\\Desktop\\readcomport\\readcomport\\weight.txt";
char *lockFilePath = "C:\\Users\\user\\Desktop\\readcomport\\readcomport\\lockfile.txt";

FILE *dataFile, *lockFile;
char line[1024]; // Buffer to hold each line read from the file

int main() {
while (1) {
// Attempt to open the lock file to check if it exists
lockFile = fopen(lockFilePath, "rt");

if (lockFile == NULL) {
// Lock file does not exist, proceed to read the weight file
dataFile = fopen(dataFilePath, "rt");

if (dataFile != NULL) {
// Successfully opened the weight file, read and print each line
while (fgets(line, sizeof(line), dataFile) != NULL) {
Print(line); // Print each line read from the file
}
fclose(dataFile); // Close the file after reading
} else {
Print("Error opening weight file.\n");
}

Delay_sec(1);
} else {
// Lock file exists, close it and wait before trying again
//fclose(lockFile);


Delay_sec(1); // Delay for 1 second
}
}

return 0; // This line is technically unreachable
}

Thank you.

User avatar
TomKerekes
Posts: 2605
Joined: Mon Dec 04, 2017 1:49 am

Re: Virtual Comport reading

Post by TomKerekes » Mon Feb 26, 2024 7:59 pm

I don't think that is a good method. There are a number of issues with the code. Print() prints a string that expects a newline character at the end. Printing partial lines interferes with KFLOP file IO. KFLOP's fopen returns NULL if there is no data in the file to read, so add some data to the file. The C odes wasn't closing the file. There isn't any synchronization between the PC and KFLOP so both are creating/reading at somewhat random times. I have modified code that seems to work if you are still interested.

But I think a much better, faster, more robust approach would be to use our .NET Libraries to place data directly into KFLOP's memory.

Here is a C++ CLR .NET example that writes a float value to a KFLOP Persist Variable. Attached is a zip of the project. Note VS2022 must have C++/CLI Support for v143 build tools (Latest) installed under Individual Components | Compilers, build tools, and runtimes

Code: Select all

using namespace System;
using namespace KMotion_dotNet;

int main(array<System::String ^> ^args)
{
    float Weight = 123.456f;
    KM_Controller KM{0, true};  // any board no console messages
    
    for (;;)
    {
        KM.SetUserDataFloat(150, Weight);  // pass weight as float value in persist variable 150 in KFLOP
        Weight += 1.0f;  // make some change to simulate different weights
        System::Threading::Thread::Sleep(1000); // update every second
    }
    return 0;
}
Here is a KFLOP C Program that prints the value every time it changes

Code: Select all

#include "KMotionDef.h"

void main()
{
	float OldWeight=-1, NewWeight;
	for(;;)
	{
		NewWeight = *(float *)&persist.UserData[150];  // get weight as a float
		
		if (NewWeight != OldWeight)
		{
			printf("Weight = %.3f\n", NewWeight);
			OldWeight = NewWeight;
		}
	}
}
HTH
Attachments
PrintWeight.c
(307 Bytes) Downloaded 19 times
WeightToKFLOP.zip
(885.37 KiB) Downloaded 21 times
Regards,

Tom Kerekes
Dynomotion, Inc.

katiefangffs
Posts: 14
Joined: Sat Feb 17, 2024 9:57 am

Re: Virtual Comport reading

Post by katiefangffs » Tue Feb 27, 2024 3:26 am

Hi,
I'm interested in the code you have modified to make it work. Greatly appreciate it if you could share it. I have also been trying to use the .NET libraries to save the data directly into Kflop's memory but I'm getting error evreytime once the code gets to the ddl file.

This is the current code I am using, which reads the COMPort just fine:

Code: Select all

[/
using namespace System;
using namespace System::IO::Ports;  // For SerialPort
using namespace System::Threading;  // For Thread::Sleep
using namespace KMotion_dotNet;

int main(array<System::String^>^ args)
{
    // Declare a managed string with C++/CLI syntax
    System::String^ portName = "COM6";
    int baudRate = 9600;
    Parity parity = Parity::None;
    int dataBits = 8;
    StopBits stopBits = StopBits::One;

    // Initialize the serial port
    SerialPort^ port = gcnew SerialPort(portName, baudRate, parity, dataBits, stopBits);

    try
    {
        port->Open();  // Try to open the port
        Console::WriteLine("Port opened, starting to read...");
    }
    catch (Exception^ ex)
    {
        Console::WriteLine("Error opening serial port: " + ex->Message);
        return 1;  // Exit if unable to open the serial port
    }

    // Reading loop
    while (true)
    {
        try
        {
            String^ data = port->ReadLine();  // Attempt to read a line of data
            Console::WriteLine("Data read: " + data);
            // Additional processing as needed
            float Weight;
            KM_Controller KM{ 0, true };  // any board no console messages
            // Attempt to parse the incoming data as a float
            if (float::TryParse(data, Weight))
            {
                // If parsing successful, update the KMotion variable
                KM.SetUserDataFloat(150, Weight);  // pass weight as float value in persist variable 150 in KFLOP
                Console::WriteLine("Weight updated in KMotion: {0} kg", Weight);
            }
            else
            {
                Console::WriteLine("Failed to parse weight data.");
            }
        }
        catch (TimeoutException^)
        {
            Console::WriteLine("Read timeout.");
        }
        catch (Exception^ ex)
        {
            Console::WriteLine("Error: " + ex->Message);
            break;  // Exit the loop on error
        }

        Thread::Sleep(10);  // Mimic a pause between reads
    }

    if (port->IsOpen)
    {
        port->Close();  // Ensure the port is closed on exit
    }

    return 0;
}



//CONSOLE OUTPUT:

[code][/Port opened, starting to read...
Data read: 0.03,kg,
Error: Dll Not Found Exception thrown :  Caller - [KMotion_dotNet.KM_Controller] :: Member - [KM_Controller]

C:\KMotionSrcKogna\KMotion\Debug\WeightToKFLOP.exe (process 358448) exited with code 0.
To automatically close the console when debugging stops, enable Tools->Options->Debugging->Automatically close the console when debugging stops.
Press any key to close this window . . .

User avatar
TomKerekes
Posts: 2605
Joined: Mon Dec 04, 2017 1:49 am

Re: Virtual Comport reading

Post by TomKerekes » Tue Feb 27, 2024 5:00 pm

I've attached the PC and KFLOP programs that work for me using files.

The normal reason for DLL not found is that your executable in not being placed in the folder with all the other binaries and dlls. Target your App to be in the \KMotion\Debug (or Release) of the KMotion Installation you are using in your project's properties. See below:

OuputDir.png
OuputFile.png

Another possible problem is your App is not a 32-bit (x86) App. See below:

TargetMachine.png
Attachments
KFLOP_Weight.c
(1.31 KiB) Downloaded 22 times
Lock.cpp
(2.03 KiB) Downloaded 23 times
Regards,

Tom Kerekes
Dynomotion, Inc.

Post Reply