05-29-2024 12:59 PM
Is it possible R/W LabVIEW Shared Network Variables using Python? If so, does anyone have any example code?
Solved! Go to Solution.
05-29-2024 05:49 PM
I have never seen any support for network variables outside of LabVIEW, regardless of the language... but I was curious so I did some searching. This forum post might be a path to look into:
If that doesn't work, a couple things to try:
Good luck!
05-30-2024 04:50 AM - edited 05-30-2024 05:07 AM
@LuminaryKnight wrote:
Is it possible R/W LabVIEW Shared Network Variables using Python? If so, does anyone have any example code?
Well, you can create a LabVIEW-based Wrapper DLL as suggested above, and this will work for sure, but you will get a relatively heavy-weight DLL, which is dependent on the LabVIEW Run-Time. There is a simpler way available (at least "simpler" from my point of view).
If you have CVI installed, you can open an example called NVPolling, which is exactly intended for reading/writing Network Shared Variables, available in the Example Finder:
Technically the Reader and Writer created by calling of CNVCreateBufferedSubscriber and CNVCreateBufferedWriter. That almost all what you needed. These (and other required) functions called from ninetv.dll, located in "%ProgramFiles%\National Instruments\Shared\Network Variable" Folder.
If you haven't CVI installed, I can attach both here:
Reader source code:
//-----------------------------------------------------------------------------
// CVI Network Variable simple polling reader example
//
// NOTE - Build and run the writer program before running the reader program.
//
//-----------------------------------------------------------------------------
#include <windows.h>
#include <cvinetv.h>
#include <ansi_c.h>
#include <utility.h>
#include <cvirte.h>
#define errChk(f) if (error = (f), error < 0) goto Error; else
static const char * NETWORK_VARIABLE_PATH = "\\\\localhost\\system\\int32var";
int main (int argc, char *argv[])
{
int error;
CNVBufferedSubscriber subscriber = 0;
CNVBufferDataStatus status;
CNVData data = 0;
if (InitCVIRTE (0, argv, 0) == 0) return -1;
DisableBreakOnLibraryErrors();
// Create a buffered subscriber
errChk(CNVCreateBufferedSubscriber(NETWORK_VARIABLE_PATH, 0, 0, 64, 5000, 0, &subscriber));
fprintf(stdout, "Hit enter to quit\n");
while (!KeyHit()){
// Get next data available in client buffer
errChk(CNVGetDataFromBuffer(subscriber, &data, &status));
// Check for new data; ignore empty buffer (CNVNoData) and stale data (CNVStaleData)
if (status == CNVNewData || status == CNVDataWasLost){ //1 or 4
int value;
// If data is the expected integer type, get the value and print it
if (CNVGetScalarDataValue(data, CNVInt32, &value) >= 0)
fprintf(stdout, "%d\n", value);
}
// Dispose data
if (data) {
CNVDisposeData(data);
data = 0;
}
// IMPORTANT - Sleep to avoid starving library threads; results in better performance.
Sleep(0);
}
Error:
// Cleanup
if (data) CNVDisposeData(data);
if (subscriber) CNVDispose(subscriber);
// Cleanup Network Variable Library
CNVFinish();
// Report error
if (error < 0){
fprintf(stderr, "\n%s", CNVGetErrorDescription(error));
fflush(stdin);
fgetc(stdin);
}
return 0;
}
and Writer:
//-----------------------------------------------------------------------------
// CVI Network Variable simple polling writer example
//
// NOTE - Build and run the writer program before running the reader program.
//
//-----------------------------------------------------------------------------
#include <windows.h>
#include <cvinetv.h>
#include <ansi_c.h>
#include <utility.h>
#include <cvirte.h>
#define errChk(f) if (error = (f), error < 0) goto Error; else
static const char * NETWORK_VARIABLE_PATH = "\\\\localhost\\system\\int32var";
int main (int argc, char *argv[])
{
int error, i = 0;
CNVBufferedWriter writer = 0;
CNVData data = 0;
if (InitCVIRTE (0, argv, 0) == 0) return -1;
DisableBreakOnLibraryErrors();
SetStdioWindowOptions(0, 0, 0);
// Create a buffered writer
errChk(CNVCreateBufferedWriter(NETWORK_VARIABLE_PATH, 0, 0, 0, 64, 5000, 0, &writer));
fprintf(stdout, "Hit enter or Ctrl+C to quit\n");
errChk(CNVCreateScalarDataValue(&data, CNVInt32, 0));
while (!KeyHit()) {
// Create data and put it in client buffer for writing
errChk(CNVSetScalarDataValue(data, CNVInt32, ++i));
errChk(CNVPutDataInBuffer(writer, data, 5000));
// IMPORTANT - Sleep to avoid starving library threads; results in better performance.
fprintf(stdout, "%d\n", i);
Sleep(500);
}
Error:
// Cleanup
if (data) CNVDisposeData(data);
if (writer) CNVDispose(writer);
CNVFinish(); // Cleanup Network Variable Library
// Report error
if (error < 0) {
fprintf(stderr, "\n%s", CNVGetErrorDescription(error));
fflush(stdin);
fgetc(stdin);
}
return 0;
}
Now, all you need to do is the same in Python. Let's say I'll read and write a variable named "int32var", placed in the System Library.
In LabVIEW (I'm using the 2024 Q1 64-bit version), you will do something like shown below. This is the project with the variable:
This is the Reader:
and this is the Writer:
Now according code in Python (I using 3.12.3 x64)
This is the code for Reader:
import os
import ctypes as ct
import time
import keyboard
nvDll = ct.cdll.LoadLibrary ("C:\\Program Files\\National Instruments\\Shared\\Network Variable\\ninetv.dll")
# nvDll = ct.cdll.LoadLibrary ("C:\\Program Files (x86)\\National Instruments\\Shared\\Network Variable\\ninetv.dll")
nvDll.CNVCreateBufferedSubscriber.argtypes = (ct.c_char_p, ct.c_int, ct.c_int, ct.c_int, ct.c_int, ct.c_int, ct.c_void_p)
nvDll.CNVGetDataFromBuffer.argtypes = (ct.c_size_t, ct.c_void_p, ct.c_void_p)
nvDll.CNVGetScalarDataValue.argtypes = (ct.c_size_t, ct.c_int, ct.c_void_p)
nvDll.CNVDisposeData.argtype = (ct.c_size_t)
nvDll.CNVDispose.argtype = (ct.c_size_t)
varPath = "\\\\localhost\\system\\int32var".encode('ascii')
CNVInt32 = 3
CNVNewData = 1
subscriber = ct.c_size_t()
data = ct.c_size_t()
status = ct.c_size_t()
value = ct.c_int()
ret = nvDll.CNVCreateBufferedSubscriber(varPath, 0, 0, 64, 5000, 0, ct.byref(subscriber))
if (ret == 0):
running = True
print("Hit Esc to Exit")
while(running):
ret1 = nvDll.CNVGetDataFromBuffer(subscriber, ct.byref(data), ct.byref(status))
if status.value == CNVNewData:
ret2 = nvDll.CNVGetScalarDataValue(data, CNVInt32, ct.byref(value))
print(value.value)
time.sleep(0.1)
if keyboard.is_pressed('Esc'):
running = False
nvDll.CNVDisposeData(data)
nvDll.CNVDispose(subscriber)
else:
print("Error {} on connect!".format(ret))
# Error -6345 is usually by wrong address
And this is the code for Writer:
import os
import ctypes as ct
import time
import keyboard
nvDll = ct.cdll.LoadLibrary ("C:\\Program Files\\National Instruments\\Shared\\Network Variable\\ninetv.dll")
# nvDll = ct.cdll.LoadLibrary ("C:\\Program Files (x86)\\National Instruments\\Shared\\Network Variable\\ninetv.dll")
nvDll.CNVCreateBufferedWriter.argtypes = (ct.c_char_p, ct.c_int, ct.c_int, ct.c_int, ct.c_int, ct.c_int, ct.c_int, ct.c_void_p)
nvDll.CNVCreateScalarDataValue.argtypes = (ct.c_void_p, ct.c_int, ct.c_int)
nvDll.CNVSetScalarDataValue.argtypes = (ct.c_size_t, ct.c_int, ct.c_int)
nvDll.CNVPutDataInBuffer.argtypes = (ct.c_size_t, ct.c_size_t, ct.c_void_p)
nvDll.CNVDisposeData.argtype = (ct.c_size_t)
nvDll.CNVDispose.argtype = (ct.c_size_t)
varPath = "\\\\localhost\\system\\int32var".encode('ascii')
CNVInt32 = 3
i = 0
writer = ct.c_size_t()
data = ct.c_size_t()
status = ct.c_size_t()
value = ct.c_int()
ret = nvDll.CNVCreateBufferedWriter(varPath, 0, 0, 0, 64, 5000, 0, ct.byref(writer))
if (ret == 0):
running = True
print("Hit Esc to Exit")
nvDll.CNVCreateScalarDataValue(ct.byref(data), CNVInt32, 0)
while(running):
value = ct.c_int(i)
nvDll.CNVSetScalarDataValue(data, CNVInt32, value)
nvDll.CNVPutDataInBuffer(writer, data, 5000)
time.sleep(0.5)
print(i)
i = i + 1
if keyboard.is_pressed('Esc'):
running = False
nvDll.CNVDisposeData(data)
nvDll.CNVDispose(writer)
else:
print("Error {} on connect!".format(ret))
If you are on a 32-bit environment, then you should call the appropriate DLL from (x86), just uncomment the line above.
If you don't have Python's keyboard module installed, just execute pip install keyboard.
Error checking is minimal; this is just a "quick and dirty" example, but it works at least.
Here Python is Writer and LabVIEW is Reader:
and opposite direction:
This variable is also available in NI Distributed System Manager:
That is. The project in the attachment.
In theory, you can read variables without polling, but this will involve a callback (I guess), and then your Python code will become more complicated because you should provide a callback function.
And you need to pay a little bit of attention to who will deploy the variables on the first run.
05-30-2024 07:06 AM
I've said it many times, but I really do think Network Published Shared Variables are horrible and should not be used. I would rewrite your code to use simple TCP/IP communications to pass data back and forth.
05-30-2024 08:26 AM
@crossrulz wrote:
I've said it many times, but I really do think Network Published Shared Variables are horrible and should not be used. I would rewrite your code to use simple TCP/IP communications to pass data back and forth.
I've used Network Shared Variables with many projects, also including communication with cRIO. It's a very nice, simple and convenient technology that has saved me a lot of development time. I also use pure TCP/IP as well as network streams, but it depends on the situation. Sometimes they fit into the overall architecture pretty well. I haven't had any significant problems. I would not recommend this for 'heavy loads' (there are some performance issues when too many variables are accessed from parallel threads), but for simple, trivial communication — why not?
05-30-2024 09:03 AM - edited 05-30-2024 09:04 AM
@Andrey_Dmitriev
This post needs to be highly KUDO'd. Maybe consider writing a white paper and submitting?
Thank you!
05-30-2024 09:12 AM
@Andrey_Dmitriev wrote:
@LuminaryKnight wrote:
Is it possible R/W LabVIEW Shared Network Variables using Python? If so, does anyone have any example code?
Well, you can create a LabVIEW-based Wrapper DLL as suggested above, and this will work for sure, but you will get a relatively heavy-weight DLL, which is dependent on the LabVIEW Run-Time. There is a simpler way available (at least "simpler" from my point of view).
If you have CVI installed, you can open an example called NVPolling, which is exactly intended for reading/writing Network Shared Variables, available in the Example Finder:
Technically the Reader and Writer created by calling of CNVCreateBufferedSubscriber and CNVCreateBufferedWriter. That almost all what you needed. These (and other required) functions called from ninetv.dll, located in "%ProgramFiles%\National Instruments\Shared\Network Variable" Folder.
If you haven't CVI installed, I can attach both here:
Reader source code:
Spoiler//----------------------------------------------------------------------------- // CVI Network Variable simple polling reader example // // NOTE - Build and run the writer program before running the reader program. // //----------------------------------------------------------------------------- #include <windows.h> #include <cvinetv.h> #include <ansi_c.h> #include <utility.h> #include <cvirte.h> #define errChk(f) if (error = (f), error < 0) goto Error; else static const char * NETWORK_VARIABLE_PATH = "\\\\localhost\\system\\int32var"; int main (int argc, char *argv[]) { int error; CNVBufferedSubscriber subscriber = 0; CNVBufferDataStatus status; CNVData data = 0; if (InitCVIRTE (0, argv, 0) == 0) return -1; DisableBreakOnLibraryErrors(); // Create a buffered subscriber errChk(CNVCreateBufferedSubscriber(NETWORK_VARIABLE_PATH, 0, 0, 64, 5000, 0, &subscriber)); fprintf(stdout, "Hit enter to quit\n"); while (!KeyHit()){ // Get next data available in client buffer errChk(CNVGetDataFromBuffer(subscriber, &data, &status)); // Check for new data; ignore empty buffer (CNVNoData) and stale data (CNVStaleData) if (status == CNVNewData || status == CNVDataWasLost){ //1 or 4 int value; // If data is the expected integer type, get the value and print it if (CNVGetScalarDataValue(data, CNVInt32, &value) >= 0) fprintf(stdout, "%d\n", value); } // Dispose data if (data) { CNVDisposeData(data); data = 0; } // IMPORTANT - Sleep to avoid starving library threads; results in better performance. Sleep(0); } Error: // Cleanup if (data) CNVDisposeData(data); if (subscriber) CNVDispose(subscriber); // Cleanup Network Variable Library CNVFinish(); // Report error if (error < 0){ fprintf(stderr, "\n%s", CNVGetErrorDescription(error)); fflush(stdin); fgetc(stdin); } return 0; }
and Writer:
Spoiler//----------------------------------------------------------------------------- // CVI Network Variable simple polling writer example // // NOTE - Build and run the writer program before running the reader program. // //----------------------------------------------------------------------------- #include <windows.h> #include <cvinetv.h> #include <ansi_c.h> #include <utility.h> #include <cvirte.h> #define errChk(f) if (error = (f), error < 0) goto Error; else static const char * NETWORK_VARIABLE_PATH = "\\\\localhost\\system\\int32var"; int main (int argc, char *argv[]) { int error, i = 0; CNVBufferedWriter writer = 0; CNVData data = 0; if (InitCVIRTE (0, argv, 0) == 0) return -1; DisableBreakOnLibraryErrors(); SetStdioWindowOptions(0, 0, 0); // Create a buffered writer errChk(CNVCreateBufferedWriter(NETWORK_VARIABLE_PATH, 0, 0, 0, 64, 5000, 0, &writer)); fprintf(stdout, "Hit enter or Ctrl+C to quit\n"); errChk(CNVCreateScalarDataValue(&data, CNVInt32, 0)); while (!KeyHit()) { // Create data and put it in client buffer for writing errChk(CNVSetScalarDataValue(data, CNVInt32, ++i)); errChk(CNVPutDataInBuffer(writer, data, 5000)); // IMPORTANT - Sleep to avoid starving library threads; results in better performance. fprintf(stdout, "%d\n", i); Sleep(500); } Error: // Cleanup if (data) CNVDisposeData(data); if (writer) CNVDispose(writer); CNVFinish(); // Cleanup Network Variable Library // Report error if (error < 0) { fprintf(stderr, "\n%s", CNVGetErrorDescription(error)); fflush(stdin); fgetc(stdin); } return 0; }
Now, all you need to do is the same in Python. Let's say I'll read and write a variable named "int32var", placed in the System Library.
In LabVIEW (I'm using the 2024 Q1 64-bit version), you will do something like shown below. This is the project with the variable:
This is the Reader:
and this is the Writer:
Now according code in Python (I using 3.12.3 x64)
This is the code for Reader:
import os import ctypes as ct import time import keyboard nvDll = ct.cdll.LoadLibrary ("C:\\Program Files\\National Instruments\\Shared\\Network Variable\\ninetv.dll") # nvDll = ct.cdll.LoadLibrary ("C:\\Program Files (x86)\\National Instruments\\Shared\\Network Variable\\ninetv.dll") nvDll.CNVCreateBufferedSubscriber.argtypes = (ct.c_char_p, ct.c_int, ct.c_int, ct.c_int, ct.c_int, ct.c_int, ct.c_void_p) nvDll.CNVGetDataFromBuffer.argtypes = (ct.c_size_t, ct.c_void_p, ct.c_void_p) nvDll.CNVGetScalarDataValue.argtypes = (ct.c_size_t, ct.c_int, ct.c_void_p) nvDll.CNVDisposeData.argtype = (ct.c_size_t) nvDll.CNVDispose.argtype = (ct.c_size_t) varPath = "\\\\localhost\\system\\int32var".encode('ascii') CNVInt32 = 3 CNVNewData = 1 subscriber = ct.c_size_t() data = ct.c_size_t() status = ct.c_size_t() value = ct.c_int() ret = nvDll.CNVCreateBufferedSubscriber(varPath, 0, 0, 64, 5000, 0, ct.byref(subscriber)) if (ret == 0): running = True print("Hit Esc to Exit") while(running): ret1 = nvDll.CNVGetDataFromBuffer(subscriber, ct.byref(data), ct.byref(status)) if status.value == CNVNewData: ret2 = nvDll.CNVGetScalarDataValue(data, CNVInt32, ct.byref(value)) print(value.value) time.sleep(0.1) if keyboard.is_pressed('Esc'): running = False nvDll.CNVDisposeData(data) nvDll.CNVDispose(subscriber) else: print("Error {} on connect!".format(ret)) # Error -6345 is usually by wrong address
And this is the code for Writer:
import os import ctypes as ct import time import keyboard nvDll = ct.cdll.LoadLibrary ("C:\\Program Files\\National Instruments\\Shared\\Network Variable\\ninetv.dll") # nvDll = ct.cdll.LoadLibrary ("C:\\Program Files (x86)\\National Instruments\\Shared\\Network Variable\\ninetv.dll") nvDll.CNVCreateBufferedWriter.argtypes = (ct.c_char_p, ct.c_int, ct.c_int, ct.c_int, ct.c_int, ct.c_int, ct.c_int, ct.c_void_p) nvDll.CNVCreateScalarDataValue.argtypes = (ct.c_void_p, ct.c_int, ct.c_int) nvDll.CNVSetScalarDataValue.argtypes = (ct.c_size_t, ct.c_int, ct.c_int) nvDll.CNVPutDataInBuffer.argtypes = (ct.c_size_t, ct.c_size_t, ct.c_void_p) nvDll.CNVDisposeData.argtype = (ct.c_size_t) nvDll.CNVDispose.argtype = (ct.c_size_t) varPath = "\\\\localhost\\system\\int32var".encode('ascii') CNVInt32 = 3 i = 0 writer = ct.c_size_t() data = ct.c_size_t() status = ct.c_size_t() value = ct.c_int() ret = nvDll.CNVCreateBufferedWriter(varPath, 0, 0, 0, 64, 5000, 0, ct.byref(writer)) if (ret == 0): running = True print("Hit Esc to Exit") nvDll.CNVCreateScalarDataValue(ct.byref(data), CNVInt32, 0) while(running): value = ct.c_int(i) nvDll.CNVSetScalarDataValue(data, CNVInt32, value) nvDll.CNVPutDataInBuffer(writer, data, 5000) time.sleep(0.5) print(i) i = i + 1 if keyboard.is_pressed('Esc'): running = False nvDll.CNVDisposeData(data) nvDll.CNVDispose(writer) else: print("Error {} on connect!".format(ret))
If you are on a 32-bit environment, then you should call the appropriate DLL from (x86), just uncomment the line above.
If you don't have Python's keyboard module installed, just execute pip install keyboard.
Error checking is minimal; this is just a "quick and dirty" example, but it works at least.Here Python is Writer and LabVIEW is Reader:
and opposite direction:
This variable is also available in NI Distributed System Manager:
That is. The project in the attachment.
In theory, you can read variables without polling, but this will involve a callback (I guess), and then your Python code will become more complicated because you should provide a callback function.
And you need to pay a little bit of attention to who will deploy the variables on the first run.
I recommend hosting this as a python package on Github and Pypi so it benefits more users.
05-31-2024 08:05 AM
@crossrulz wrote:
I've said it many times, but I really do think Network Published Shared Variables are horrible and should not be used. I would rewrite your code to use simple TCP/IP communications to pass data back and forth.
I would mostly agree, as I never used them in my own projects. They are nice and easy to use but also suffer the black box syndrome, where you have no control about them. It is a proprietary technology that NI never documented in any way and only properly supports in LabVIEW and LabWindows/CVI. Access from other programming languages is, while not impossible, very cumbersome and you have to rely on hacks like here, where you have to interface to an API that is outside of LabWindows/CVI not really documented. The predecessor to them, PSP never was really better in any of these respects.
When NI was still very active on all these software fronts, you could at least hope that the solution you were coming up with would be operational across all NI technologies. With most NI software projects including LabWindows/CVI being in abandon mode, you basically lock yourself more and more into a niche market by using such technologies.
So the NSV may be easy to use between applications but it is not the most performant way to share data and you paint yourself into a more and more confined NI corner by using it. Unless NI changes its course radically and documents these technologies more openly, I'm not buying into that ticket.
05-31-2024 08:30 AM
@rolfk wrote:
@crossrulz wrote:
I've said it many times, but I really do think Network Published Shared Variables are horrible and should not be used. I would rewrite your code to use simple TCP/IP communications to pass data back and forth.
I would mostly agree, as I never used them in my own projects. They are nice and easy to use but also suffer the black box syndrome, where you have no control about them. It is a proprietary technology that NI never documented in any way and only properly supports in LabVIEW and LabWindows/CVI. Access from other programming languages is, while not impossible, very cumbersome and you have to rely on hacks like here, where you have to interface to an API that is outside of LabWindows/CVI not really documented. The predecessor to them, PSP never was really better in any of these respects.
When NI was still very active on all these software fronts, you could at least hope that the solution you were coming up with would be operational across all NI technologies. With most NI software projects including LabWindows/CVI being in abandon mode, you basically lock yourself more and more into a niche market by using such technologies.
So the NSV may be easy to use between applications but it is not the most performant way to share data and you paint yourself into a more and more confined NI corner by using it. Unless NI changes its course radically and documents these technologies more openly, I'm not buying into that ticket.
You make good arguments. For my use case, I'm thinking @crossrulz suggestion of TCP/IP is pretty legit and feasible.
10-15-2024 08:56 AM
Do you have or can you point me to an example of the TCP/IP method of sharing variables between two computers? I am new to LabVIEW and not very savvy on TCP/IP communications. I am trying to run a LabVIEW VI on one PC which is acquiring data (temperatures and pressures) from a cDAQ-9189 and I want to "publish" 4 or 5 values on the network that will be read by a second computer running a Windows application. Thank you for your help.