I'm working on an interface to an electronic scale. I'm communicating with the scale via an online stream to a IP address and port no.
I was planning to the tcpClient and tcpListener dotnet functions to read and write to the stream. So far so good.
The problem that appears is that NAV is running this on the server, while the IP/port is only available from the client, as the scale is attached via the local USB port. So the communication needs to happen locally on the client.
Do you know if this is possible?
Hi Erik, Each Dotnet variable has a properties "RunOnClient". If this property is set to yes, navision will read(excecuted) from local not from server.
Thanks a lot Xhevat Tafaj. [:)]
I believe that you're right, it may be the right direction to get it to work.
It just didn't work. At least not until I can overcome the next error:
The server "net.tcp://server/instance/Service" is either unavailable or your connection has been lost.Do you want to attempt to reconnect?
Wondering if the RunOnClient should be for all the DotNet vars in the function?
Changing to RunOnClient in all the DotNet's is giving a new error:
A call to System.Net.Sockets.TcpListener.Start failed with this message: Adressen, der blev anmodet om, er ikke gyldig i sin kontekst (the requested address is not valid in its context).
What I have found out so far, is that it means the address does not exist from where the program runs. RunOnClient should have made it run there and then it would work. But it seems that it still is not really calling it, in the content of the server.
I'm not sure if this helps, but in this example of retrieving data from an external source (a Windows Dialog in that case), all DotNet vars are defined as RunOnClient.
In your case, the NAV client plays the role of the server (TCP listener), is this correct? While the electronic scale is the TCP client. That is, the communication is only initiated by the electronic scale (i.e. when it has to send some measurement to the computer). In this case, IMHO the original error message ("... is either unavailable or your connection has been lost.") has nothing to do with the actual TCP communication you are implementing, but with NAV communication internals between NAV server and NAV Windows Client. Is this correct?
UPDATE (for the newer error message):
How about wrapping all the communication logic in a single DLL, then use a single DotNet var in C/AL. And this single one var would be "RunOnClient".
Also, do you think there are some permission issues? Opening a TCP Listener may require more privileges? How about the firewall? Does the computer have more Network Cards? Are they all configured correctly? Though, most probably it wouldn't matter, since perhaps the driver of the electronic scale... And right now I realise: isn't it the driver of the scale which should be the TCP server? Why do you need a TCP listener?
What is the lifespan of the TCP listener? When do you run the C/AL code that starts it? On a Page Load of something similar?
This last paragraph only makes sense if there is an actual need of a TCP listener (and perhaps only as a last resort):
If acceptable from a deployment point of view, a Windows Service can be installed on the computer and be the TCP listener itself. Then, it would update a file on the local file system when a new measurements arrives from the electronic scale. Then, when the NAV user requires the latest measurement data (say, to fill in a field on a page), a piece of C/AL code can read the contents of that file.
[P.S.: Sorry for the many edits]
If it helps, I sketched a tiny simulator for the electronic scale and also sketched a .NET wrapper that may be called from C/AL. Assuming that the electronic scale plays the role of the server, and the NAV client connects to it whenever it wants to fetch the weight (or push some other data), then you do not need to use a TCP Listener IMHO. The listener is most probably implemented on the side of the scale, perhaps by the software driver that was shipped with the scale (you mentioned that it connects via USB, thus I assume somebody has to start a background process on the machine in order to open the TCP socket).
Perhaps the scale has a higher level API like HTTP, though?
I do not know the structure of the data provided by the scale, thus in my simulator example I use something like "WEIGHT;other data; other data" + a newline. If you provide more details, I can adjust the example.
For testing the code, you can use LINQPad (instead of downloading and installing Visual Studio). Does the communication work? If it doesn't perhaps there are other issues that have to be investigated before actually integrating the code into NAV (C/AL).
The scale simulator (server side):
public static void Main(string args)
var server = new TcpListener(IPAddress.Any, 15000);
Console.WriteLine("Server is running.");
using (var client = server.AcceptTcpClient())
Console.WriteLine("We have a client!");
static void SendDataToClient(TcpClient client)
var eternity = DateTime.Now + TimeSpan.FromSeconds(10);
using (var streamWriter = new StreamWriter(client.GetStream()))
streamWriter.AutoFlush = true;
while (DateTime.Now < eternity)
The client wrapper (to be called from C/AL):
public class ScaleClientWrapper
private string address;
private int port;
public ScaleClientWrapper(string address, int port)
this.address = address;
this.port = port;
public double GetWeight()
using (var client = new TcpClient(address, port))
using (var streamReader = new StreamReader(client.GetStream()))
var line = streamReader.ReadLine();
var weight = double.Parse(line.Split(';'));
public void Tear()
// Example of using the wrapper.
// Delete before compiling into a DLL.
public static void Main(string args)
var wrapper = new ScaleClientWrapper("127.0.0.1", 15000);
var weight = wrapper.GetWeight();
Putting the code within a DLL wrapper would not solve the permission issue IMHO since the process itself has to be executed in elevated mode. However, the good thing is that most probably a tcpListener is not needed. Do you think it is possible to execute the following in the command prompt? telnet 169.254.1.2 52253 We should see the data :)
How does it look like?
If we see the data and it is interpretable, then the C/AL code should do the following:
I think it helped with some sleep!
All it really took to read from the stream was:
tcpClient := tcpClient.TcpClient;
ClientStream := tcpClient.GetStream;
IF ClientStream.CanRead THEN BEGIN
Reader := Reader.StreamReader(ClientStream);
ResultText := Reader.ReadLine;
Who said "keep it simple"? [:)]
The ResultText is a simple text string that I just need to read from the right positions.
Next phase is then also to send commands to the weight, but I am assuming I can just use StreamWriter in the same way.