Porting C# application onto GRV native linux

I can confirm that asp.net core 3.1 apps will run on EPIC as well.

An update for all. I downloaded the dotnet CLI Core 3.1 and manually installed to EPIC. There are three environment variables to set and then it works well. At least I was able to compile and run a .NET HelloWorld from the SSH Shell. I used the scp command to ssh copy files over as needed and then shell into the epic machine and make things play. I will post back here some instructions as soon as I get to where I can do that. (working through several levels of virtual remote computing here at the moment.)


I am working from home at the moment (WA state). I can successfully use the Optommp4.dll with VS Studio Community 2019 on my Win10 machine to run a simple c# console program to read from EPIC. I noticed the dependency on Framework vs Core. It would be very nice if a build against Core 3.1 (etc) could be provided.

I do have the dotnet cli running on EPIC directly. It is not clear to me if the Optommp4.dll is dependent on a non-arm (Windows/Intel hardware). I plan to give it a try by adding the reference to dotnet on arm and see if it will run with it.

I will also be playing with the C++ SDK too. I could do a few simple functions that get called from the C# code as in a library or such.

Yes it would be nice if the Managed MMP SDK was recompiled to target core. As I mentioned before, I wrote my own managed MMP library before Opto22 had one. It was based off the C++ SDK, which included source code. I changed the target from .net framework to .net core and recompiled and it worked, no changes needed.

I’ve attached the c# source of my library. This works fine for the applications I’ve used it for - all PAC stuff. I’ve only tested it with EPIC for this forum thread, so be sure to test it thoroughly.

Opto22LibraryCore.zip (21.8 KB)

Thanks Philip, darn useful!

An update for folks interested. I have been able to “port” (a console application which runs in the Windows environment) over to the EPIC by simply copying the files to a directory where I have created a dotnet project (using dotnet new console).

dotmet run and it builds and goes! Performing I/O and displaying messages on the shell console.

I’ve added some support for SNAP high density digital modules and groov digital modules to the .net core library since jvdelisle needed that. I’ve only done somewhere between zero and basic testing.

Opto22LibraryCore.zip (22.1 KB)

1 Like

I have not been in touch here for a while. All my testing is good. I performed some timing tests To read all channels I have installed (36) is taking about 5-7 millisecons. Plenty of head room for my needs.

Thanks phillip


Gads, almost a year gone by. Having to restart on a bunch of things including RIO. My dotnet applications are running fine with the new EPIC firmware. I should mention that RIO hits a sweet spot for some things, no doubt. I am anxious for it to arrive so I can dig in.

Ran into something today on the RIO. I am reading the analog channels fine with dotnet. I am having a problem finding the two relays to control them. Are they considered module 0, points 8 and 9 of the optommp world? I tried that combination and no luck. Module 1? These are a unique type of “module” so it is not clear.

To clarify, I am trying to use:

public void SetHDDigPtState(short Module, short Point, bool State)

EDIT: A problem in my own code. Put together a quick test program and confirmed the relays are Module 0, and points 8 and 9.



You are correct, they are module 0 point 8 and 9. It looks like the relays are not responding to the HDD read and write though – I tested this in the Generic MMP tool in manage and they don’t work.

There is a new area of the memory map called Expanded Digital Channel read/write which does work on these points. I will need to add these to the library.


public void SetGroovDigPtState(short Module, short Point, bool State)


public bool GetGroovDigPtState(short Module, short Point)

Opto22LibraryCore.zip (23.1 KB)

I will have a look at what you did there. SetHDDigPtState(short Module, short Point, bool State) appears to set the state OK here. However, I did not try to get the state. Your naming conventions are clearer for sure.

Hmm, maybe I was doing something wrong in the MMP tool as I couldn’t get them to work at the HDD locations. Well now there are choices :wink:

1 Like

Is this still possible? I am currently trying to run a stand alone .Net 6.0 console application and keep getting a segmentation fault. I then tried downloading the .Net 6.0 Linux SDK from Microsoft and I received a segmentation fault from running the dotnet tool as well. I am assuming this is because of the of some dependency but philip and you have not mentioned any. I would like to know your current experience before I go down the rabbit hole of finding and building dependencies.
Any help would be greatly appreciated

Welcome to the forums Patrick.

This .Net stuff is out of my wheelhouse (beyond I figured out a workflow that will get some .Net5 stuff on a RIO).

Lets page @torchard & @philip @JerryD to see if they have any updates or answers.

1 Like

I’m seeing the same segmentation fault on a GRV-EPIC-PR1 using either .NET 6 or 7. As a wild guess, .NET requires something newer than we’re shipping.

Both .NET 6 and .NET 7 work on a GRV-R7-MM1001-10, which ships with a newer kernel/libc (I think). Both complain about missing information though:

admin@jfischer-rio:~/dotnet7$ ./dotnet
./dotnet: /usr/lib/libstdc++.so.6: no version information available (required by ./dotnet)

I’ll have to dig around to see when it stopped working.

@Jonathan_Fischer looks to be correct - It looks like .net 6 may require a newer glibc version than what is installed on the PR1, which is 2.22 on mine.

See Segfault with net6.0 self-contained on linux-arm · Issue #60686 · dotnet/runtime · GitHub

.net 5 is complaining about missing libgssapi_krb5.so.2
(sudo apt-get install krb5 takes care of that)


.NET 5 on RIO also complains about ‘no version’ and Google confidently tells me I can ignore it, so I do.

Thank you all.
I’ve tried installing all the dependencies that Microsoft suggested (used the debian 9.0 versions since they depended on a lower version of libc6 (2.17)). The odd part of the segmentation fault (attached below) is that it seems to not complain about a missing library. I am hesitant to install a newer version of libc6 since a lot of libraries depend on it. Has anyone successfully done so?

execve(“./dotnet”, [“./dotnet”], [/* 20 vars */]) = 0
brk(0) = 0x553ec000
uname({sysname=“Linux”, nodename=“24288groov”, …}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x76f85000
access(“/etc/ld.so.preload”, R_OK) = -1 ENOENT (No such file or directory)
open(“/etc/ld.so.cache”, O_RDONLY|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=41480, …}) = 0
mmap2(NULL, 41480, PROT_READ, MAP_PRIVATE, 3, 0) = 0x76f7a000
close(3) = 0
open(“/lib/libpthread.so.0”, O_RDONLY|O_CLOEXEC) = 3
read(3, “\177ELF\1\1\1\3\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0lI\0\0004\0\0\0”…, 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=92696, …}) = 0
mmap2(NULL, 164436, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x76f2e000
mprotect(0x76f43000, 65536, PROT_NONE) = 0
mmap2(0x76f53000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x15000) = 0x76f53000
mmap2(0x76f55000, 4692, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x76f55000
close(3) = 0
open(“/lib/libdl.so.2”, O_RDONLY|O_CLOEXEC) = 3
read(3, “\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0,\t\0\0004\0\0\0”…, 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=9716, …}) = 0
mmap2(NULL, 73912, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x76f1b000
mprotect(0x76f1d000, 61440, PROT_NONE) = 0
mmap2(0x76f2c000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1000) = 0x76f2c000
close(3) = 0
open(“/usr/lib/libstdc++.so.6”, O_RDONLY|O_CLOEXEC) = 3
read(3, “\177ELF\1\1\1\3\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0000\302\6\0004\0\0\0”…, 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1240640, …}) = 0
mmap2(NULL, 1312912, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x76dda000
mprotect(0x76f02000, 65536, PROT_NONE) = 0
mmap2(0x76f12000, 28672, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x128000) = 0x76f12000
mmap2(0x76f19000, 6288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x76f19000
close(3) = 0
open(“/lib/libm.so.6”, O_RDONLY|O_CLOEXEC) = 3
read(3, “\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\0<\0\0004\0\0\0”…, 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=431604, …}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x76f79000
mmap2(NULL, 495760, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x76d60000
mprotect(0x76dc9000, 61440, PROT_NONE) = 0
mmap2(0x76dd8000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x68000) = 0x76dd8000
close(3) = 0
open(“/lib/libgcc_s.so.1”, O_RDONLY|O_CLOEXEC) = 3
read(3, “\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0`\321\0\0004\0\0\0”…, 512) = 512
fstat64(3, {st_mode=S_IFREG|0644, st_size=114532, …}) = 0
mmap2(NULL, 178724, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x76d34000
mprotect(0x76d50000, 61440, PROT_NONE) = 0
mmap2(0x76d5f000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b000) = 0x76d5f000
close(3) = 0
open(“/lib/libc.so.6”, O_RDONLY|O_CLOEXEC) = 3
read(3, “\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0008m\1\0004\0\0\0”…, 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1222220, …}) = 0
mmap2(NULL, 1291624, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x76bf8000
mprotect(0x76d1f000, 61440, PROT_NONE) = 0
mmap2(0x76d2e000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x126000) = 0x76d2e000
mmap2(0x76d31000, 9576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x76d31000
close(3) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x76f78000
set_tls(0x76f78870, 0x76f78f68, 0x76f87050, 0x76f78870, 0x76f87050) = 0
mprotect(0x76d2e000, 8192, PROT_READ) = 0
mprotect(0x76dd8000, 4096, PROT_READ) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x76f77000
mprotect(0x76f12000, 20480, PROT_READ) = 0
mprotect(0x76f2c000, 4096, PROT_READ) = 0
mprotect(0x76f53000, 4096, PROT_READ) = 0
— SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_ACCERR, si_addr=0x54bab5bc} —
+++ killed by SIGSEGV (core dumped) +++
Segmentation fault (core dumped)