[ANN] aDSA 1.3.0 — ZeroMQ-backed Annex E (DSA) runtime for GNAT — now runs on Windows and FreeBSD

Yes, I’ve already did this kind of things (in C) with previous designs. Works well.

That makes me think that I did use IPV6 local addresses. This way, you do not have to worry about network subnet addresses like with IPV4.
I guess IPV6 can be used with aDSA. Right ?

Hi Nicolas — good questions, not dumb at all.

What the name server does. Your picture (3+ processes) is right. The name
server is a small bootstrap/rendezvous service.
Partitions register with it at elaboration (“I host RCI units X and Y, at
these endpoints”), and callers ask it once per unit “who hosts X?” — after
that, all RPC traffic flows directly between the partitions; the name
server isn’t in the data path. Besides the RCI registry, it hands out partition
ids, stores each unit’s RM E.3 version hash (more below) and coordinates orderly
shutdown.

Launching after a discovery phase — yes. Nothing about addresses is baked
in at build time. The only thing every process must be told at launch is where
the name server is (DSA_NAME_SERVER=tcp://<host>:<port>, environment or
per-partition aDSA.config file); serving partitions additionally get
DSA_HOST=<their own routable address> so remote callers can reach them.
Partitions bind ephemeral ports and register whatever they bound — so
dynamically-discovered IPs are fine. Declare pragma Starter (None); in the
.cfg and launch the partition executables from your own orchestration once
mDNS discovery + user selection has happened. (The generated start-*.sh is
just a convenience for the static case.)

Several PCs, each controlling its own equipment — yes. Each PC + its
equipment is simply an independent deployment with its own name server
(naturally running on that PC). Deployments don’t know about each other. One
caution: within a deployment, re-registration of an RCI unit is “latest wins”
(that’s the restart-recovery feature), so an equipment process mistakenly
pointed at the wrong PC’s name server could hijack a unit registration
there. To prevent this, give each deployment its own CurveZMQ key pair
(pcs_keygen) … a process with the wrong keys can’t complete a handshake with
the wrong deployment at all, so “no overlap” is enforced cryptographically,
not just by discipline.

Two equipments under one PC (here’s the real constraint). Annex E programs
are statically partitioned: the .cfg fixes, at build time, which
partitions exist and which RCI unit each one hosts, and an RCI unit is a
singleton in the program. You can’t run two copies of the same RCI-hosting
partition inside one program. Two clean ways to handle N identical devices:

  1. One deployment per device (my recommendation for truly dynamic N):
    same binaries, one name server per deployment on its own port on the PC,
    launched per device after discovery. Cheap and simple; the PC runs one
    client + one NS per attached device.
  2. One program with the device count bounded in the .cfg: distinct
    partitions (Equipment_1, Equipment_2, …) hosting distinct unit names,
    or a design where the PC hosts a factory/registry RCI and devices are
    represented as RACW objects … the objects are then dynamic, but the
    partition set is still fixed.

And no, neither the “server” nor the name server has to live on the PC. Any
partition can host RCI units (the .cfg decides), and the name server can run
on any host every partition can reach; the PC is just the natural place since
it’s your stable node.

Version compatibility is enforced automatically (RM E.3). GNAT computes a
hash of each RCI unit’s spec source; the hosting partition registers it, and
every caller compares its own compiled-in hash on first resolution of that
unit. A mismatch raises Program_Error before any call is made, rather than
silently exchanging incompatible data. So client and server must be built from
the same version of the shared spec (bodies can differ freely). This works
across different GNAT versions and OSes. We’ve verified identical hashes
GNAT 14↔16 on x86_64↔ARM and Linux↔FreeBSD↔Windows.

The User’s Guide covers all of this in more depth … §5 (configuration language),
§9 (running without a Starter), §10 (cross-host), §11 (env-var reference).

One honest caveat since your instruments are mDNS-discovered: aDSA is
IPv4-only today.
The transport underneath (ZeroMQ) fully supports IPv6, but
it’s off by default per socket and we don’t yet enable it, and the endpoint
plumbing hasn’t been audited for bracketed v6 literals. It’s a small, planned
enhancement but it hasn’t been done or verified as yet, so perhaps
plan on IPv4 addresses from your discovery phase for now. Also worth knowing
if it lands: mDNS on instrument LANs often yields link-local v6 addresses
(fe80::…), which need a zone index and are generally a sharp edge — global
or ULA addresses (or just IPv4) are the comfortable path on a bench network.

I’ll take a look at IPv6 now,

Regards.

IPv6 addresses are a part of RFC 3897. Here is an implementation of.

Hi Rod,

I’ve run some tests.
Compiling and running gd-echo locally on my (work) PC is OK. Same for my RaspberryPi.

PC :

nicolas@Lenovo-Nicolas MINGW64 /z/ada/adsa-1.5.1/examples/gd-echo
$ export DSA_NAME_SERVER="tcp://127.0.0.1:5700"

nicolas@Lenovo-Nicolas MINGW64 /z/ada/adsa-1.5.1/examples/gd-echo
$ ../../build/pcs_nameserver &
[1] 1244

nicolas@Lenovo-Nicolas MINGW64 /z/ada/adsa-1.5.1/examples/gd-echo
$ [nameserver] listening on tcp://127.0.0.1:5700 (clients use tcp://127.0.0.1:5700)


nicolas@Lenovo-Nicolas MINGW64 /z/ada/adsa-1.5.1/examples/gd-echo
$ ./bin/server_partition &
[2] 1245

nicolas@Lenovo-Nicolas MINGW64 /z/ada/adsa-1.5.1/examples/gd-echo
$ [nameserver] REG pid=1 rep=tcp://127.0.0.1:57038 pull=tcp://127.0.0.1:57048 units=1
[nameserver] CTL pid=1 ctl=tcp://127.0.0.1:57051


nicolas@Lenovo-Nicolas MINGW64 /z/ada/adsa-1.5.1/examples/gd-echo
$ ./bin/client_partition &
[3] 1246

nicolas@Lenovo-Nicolas MINGW64 /z/ada/adsa-1.5.1/examples/gd-echo
$ [nameserver] REG pid=2 rep=- pull=- units=0
[client] calling Echo.Add (20, 22) ...
[server] Echo.Add ( 20, 22 )
[client] result = 42
[client] calling Echo.Note (99) ...
[server] Echo.Note 99
[client] done
[nameserver] TERM -> broadcasting shutdown
[nameserver] done

[1]   Done                       ../../build/pcs_nameserver
[2]-  Done                       ./bin/server_partition
[3]+  Done                       ./bin/client_partition


RaspberryPi :

nico@RaspberryPi:~/ada/adsa/examples/gd-echo $ export DSA_NAME_SERVER="tcp://127.0.0.1:5700"
nico@RaspberryPi:~/ada/adsa/examples/gd-echo $
nico@RaspberryPi:~/ada/adsa/examples/gd-echo $
nico@RaspberryPi:~/ada/adsa/examples/gd-echo $ ../../build/pcs_nameserver &
[1] 2060
nico@RaspberryPi:~/ada/adsa/examples/gd-echo $ [nameserver] listening on tcp://127.0.0.1:5700 (clients use tcp://127.0.0.1:5700)

nico@RaspberryPi:~/ada/adsa/examples/gd-echo $ ./bin/server_partition &
[2] 2064
nico@RaspberryPi:~/ada/adsa/examples/gd-echo $ [nameserver] REG pid=1 rep=tcp://127.0.0.1:46243 pull=tcp://127.0.0.1:44531 units=1
[nameserver] CTL pid=1 ctl=tcp://127.0.0.1:34179

nico@RaspberryPi:~/ada/adsa/examples/gd-echo $ ./bin/client_partition &
[3] 2079
nico@RaspberryPi:~/ada/adsa/examples/gd-echo $ [nameserver] REG pid=2 rep=- pull=- units=0
[client] calling Echo.Add (20, 22) ...
[server] Echo.Add ( 20, 22 )
[client] result = 42
[client] calling Echo.Note (99) ...
[server] Echo.Note 99
[client] done
[nameserver] TERM -> broadcasting shutdown
[nameserver] done

[1]   Done                    ../../build/pcs_nameserver
[2]-  Done                    ./bin/server_partition
[3]+  Done                    ./bin/client_partition

Note : On my PC, I have to run from MSYS2 prompt. Running from Alire prompt fails because of PATH problems (wrong dll used). But this is not aDSA business.

Running cross machines do not work.

I tried running the name server on both sides. No way, the client hangs.

On the RaspberryPi :

nico@RaspberryPi:~/ada/adsa/examples/gd-echo $ export DSA_NAME_SERVER="tcp://192.168.30.136:5700"
nico@RaspberryPi:~/ada/adsa/examples/gd-echo $ ../../build/pcs_nameserver &
[1] 2115
nico@RaspberryPi:~/ada/adsa/examples/gd-echo $ [nameserver] listening on tcp://192.168.30.136:5700 (clients use tcp://192.168.30.136:5700)
export DSA_NAME_SERVER="tcp://192.168.30.136:57./bin/server_partition &
[2] 2118
nico@RaspberryPi:~/ada/adsa/examples/gd-echo $ [nameserver] REG pid=1 rep=tcp://127.0.0.1:38579 pull=tcp://127.0.0.1:34957 units=1
[nameserver] CTL pid=1 ctl=tcp://127.0.0.1:36185

On the PC :

nicolas@Lenovo-Nicolas MINGW64 /z/ada/adsa-1.5.1/examples/gd-echo
$ export DSA_NAME_SERVER="tcp://192.168.30.136:5700"

nicolas@Lenovo-Nicolas MINGW64 /z/ada/adsa-1.5.1/examples/gd-echo
$ ./bin/client_partition &
[1] 1247

nicolas@Lenovo-Nicolas MINGW64 /z/ada/adsa-1.5.1/examples/gd-echo
$ [client] calling Echo.Add (20, 22) ...

On the RaspberryPi after running client_partition :

nico@RaspberryPi:~/ada/adsa/examples/gd-echo $ [nameserver] REG pid=2 rep=- pull=- units=0

Everything is stuck until I kill processes (or I didn’t wait long enough).

The behaviour is the same when running the name server and the server partition on the PC and running the client on the RaspberryPi.

Edit :
Running the name server on the RaspberryPi and running the server partition and the client partition on the PC works.
Looking at name server outputs, it looks like it tells the server and client partitions to use 127.0.0.1. So runing both partitions on the same machine is OK. But running the partitions on different machines is not OK.

Regards,
Nicolas

It’s a little late here (.au) atm to look at this properly but I notice that when you are exporting DSA_NAME_SERVER you have set the port to 57, rather than 5700, when running the server_partition.

Perhaps try the test again using a consistent port for DSA_NAME_SERVER and see if that helps ?

Also, I have a medical procedure tomorrow, so it might be a day or so before I can reply again, sorry.

Rod,

The port used is 5700 on both sides. I don’t know why the output capture shows a mix of two (partial) commands.

Tried again. Same behaviour.

No problem. I’m absolutely not in a hurry. Take care of you.

Regards,
Nicolas

Hi Nicolas,

Your name-server log has the answer in it — look at the registration line on
the RaspberryPi:

[nameserver] REG pid=1 rep=tcp://127.0.0.1:38579 ...

The server partition registered loopback endpoints. DSA_NAME_SERVER only
tells a process where the name server is; it doesn’t change what the partition
itself binds and advertises. Without DSA_HOST, a partition stays
loopback-only (the safe same-host default) — so your PC client resolved Echo
to tcp://127.0.0.1:38579 and connected to its own loopback, where nothing
listens. That’s the hang.

The fix is one extra line on the machine that hosts the server partition
(the RaspberryPi in your first test):

export DSA_NAME_SERVER="tcp://192.168.30.136:5700"
export DSA_HOST=192.168.30.136        # <- bind all interfaces, advertise this IP
../../build/pcs_nameserver &
./bin/server_partition &

and on the PC only:

export DSA_NAME_SERVER="tcp://192.168.30.136:5700"
./bin/client_partition

You’ll see the difference immediately in the REG line —
rep=tcp://192.168.30.136:… instead of 127.0.0.1. The rule: any partition
that must be reachable from another machine needs DSA_HOST=<its own routable IP>; pure clients (like gd-echo’s) don’t. This is §10 of the
users-guide (“Cross-host deployment”).

Two related notes:

  • Run exactly one name server per deployment and point every process at
    it. Two name servers means two registries — whichever one a process picks,
    the other knows nothing.
  • For the reverse direction (server partition on the Windows PC), you’ll also
    need Windows Defender Firewall to allow the partition inbound — partitions
    bind ephemeral ports, so allow the executable, not a port number. And your
    Edit’s observation is exactly right — that working case works because both
    partitions being on one machine makes their loopback endpoints mutually
    reachable.

Debugging tip: export DSA_CALL_TIMEOUT=3000 while setting a deployment up —
misconfigurations then fail in 3 s with Communication_Error instead of
hanging silently.

I’ll also add a check for this kind of misconfiguration and have the nameserver issue a warning when such is detected.

Regards,
Rod

Rod,

I was re-reading the documentation when you answered me. I was reading the DSA_HOST documentation.

Your message confirm it’s the source of the problem.

One remark :
In chapter 10 / point 2, it is written

_“Each partition sets DSA_HOST=<its-own-routable-ip> so it binds all interfaces and advertises an address its peers can reach (not loopback).”_

I suggest you modify it to explicitly say which process needs it (name server or server partition ?) and for what usage (inform where is the server partition located).
As it is written, I thought DSA_HOSThas to be set on all machines containing a partition and initialized with the IP address of the machine.

One suggestion :
Can the server partition IP address be dynamically discovered at connection time ?

Adding DSA_HOST to the RaspberryPi side (running name server and server partition) did the trick.

I now have a cross-host application running !!!

Next step is to enter the details of aDSA possibilities.

Thanks a lot for your help and your patience.

Regards,
Nicolas