Discussion:
BridgeWorks -> TIG
(too old to reply)
Arne Vajhøj
2024-08-01 12:20:28 UTC
Permalink
I could not get BridgeWorks to work. And the VMS side was
VMS Alpha only. And the non-Java option for client side
was COM.

But I do like the concept. So I tried creating something
similar that works with newer stuff. TIG (Transparent
Interface Generation).

VMS code:

data.inc:

structure /mydata/
integer*4 iv
character*256 sv
end structure

demof.for:

integer*4 function add(a, b, res)
implicit none
integer*4 a, b, res
res = a + b
add = 1
end
c
integer*4 function conc(a, b, res)
implicit none
character*(*) a, b, res
res = a // b
conc = 1
end
c
integer*4 function modify(o)
implicit none
include 'data.inc'
record /mydata/o
o.iv = o.iv + 1
o.sv = trim(o.sv) // 'X'
modify = 1
end
c
integer*4 function getid(id)
implicit none
character*(*) id
id = 'Fortran on VMS'
getid = 1
end

(trivial I know, but ...)

So to make it all work I describe the functions in an XML file.

demof.xml:

<config>
<image>demof</image>
<port>12346</port>
<mt>false</mt>
<server>
<language>dk.vajhoej.vms.tig.server.JavaServerGen</language>
</server>
<client>
<language>dk.vajhoej.vms.tig.client.JavaClientGen</language>
<language>dk.vajhoej.vms.tig.client.CSharpClientGen</language>
<language>dk.vajhoej.vms.tig.client.CClientGen</language>
</client>
<methods>
<method>
<name>add</name>
<args>
<arg>
<name>a</name>
<type>LongWord</type>
<pass>Reference</pass>
<use>In</use>
</arg>
<arg>
<name>b</name>
<type>LongWord</type>
<pass>Reference</pass>
<use>In</use>
</arg>
<arg>
<name>res</name>
<type>LongWord</type>
<pass>Reference</pass>
<use>Out</use>
</arg>
</args>
</method>
<method>
<name>conc</name>
<args>
<arg>
<name>a</name>
<type>FixedCharacterString</type>
<pass>Descriptor</pass>
<use>In</use>
</arg>
<arg>
<name>b</name>
<type>FixedCharacterString</type>
<pass>Descriptor</pass>
<use>In</use>
</arg>
<arg>
<name>res</name>
<type>FixedCharacterString</type>
<pass>Descriptor</pass>
<use>Out</use>
</arg>
</args>
</method>
<method>
<name>modify</name>
<args>
<arg>
<name>o</name>
<type>Block</type>
<pass>Reference</pass>
<use>InOut</use>
<impl>Dataf</impl>
</arg>
</args>
</method>
<method>
<name>getid</name>
<args>
<arg>
<name>res</name>
<type>FixedCharacterString</type>
<pass>Descriptor</pass>
<use>Out</use>
</arg>
</args>
</method>
</methods>
</config>

(I prefer XML config file over GUI!)

Generate some code for both VMS side and client side.

Java test client:

Dataf.java:

import dk.vajhoej.record.FieldType;
import dk.vajhoej.record.Struct;
import dk.vajhoej.record.StructField;

@Struct
public class Dataf {
@StructField(n=0, type=FieldType.INT4)
private int iv;
@StructField(n=1, type=FieldType.FIXSTR, length=256, pad=true,
padchar=' ')
private String sv;
public Dataf() {
this(0, "");
}
public Dataf(int iv, String sv) {
this.iv = iv;
this.sv = sv;
}
public int getIv() {
return iv;
}
public void setIv(int iv) {
this.iv = iv;
}
public String getSv() {
return sv;
}
public void setSv(String sv) {
this.sv = sv;
}
@Override
public String toString() {
return String.format("{iv: %d, sv: %s}", iv, sv);
}
}

Testf.java:

import java.io.IOException;

import dk.vajhoej.record.RecordException;

public class Testf {
public static void main(String[] args) throws IOException,
RecordException {
int stat;
DemofClient cli = new DemofClient("192.168.68.40");
int v1 = 123;
int v2 = 456;
int[] v3 = new int[1];
stat = cli.add(v1, v2, v3);
System.out.printf("stat = %d\n", stat);
System.out.printf("%d + %d = %d\n", v1, v2, v3[0]);
String s1 = "ABC";
String s2 = "DEF";
String[] s3 = new String[1];
stat = cli.conc(s1, s2, s3);
System.out.printf("stat = %d\n", stat);
System.out.printf("|%s| + |%s| = |%s|\n", s1, s2, s3[0]);
Dataf[] o = new Dataf[1];
o[0] = new Dataf(123, "ABC");
stat = cli.modify(o);
System.out.printf("stat = %d\n", stat);
System.out.printf("o = %s\n", o[0]);
String[] id = new String[1];
stat = cli.getid(id);
System.out.printf("stat = %d\n", stat);
System.out.printf("id = %s\n", id[0]);
}
}

C# test client:

Dataf.cs:

using Vajhoej.Record;

[Struct]
public class Dataf
{
[StructField(N=0, Type=FieldType.INT4)]
private int iv;
[StructField(N=1, Type=FieldType.FIXSTR, Length=256, Pad=true,
PadChar=' ')]
private string sv;
public int Iv
{
get { return iv; }
set { iv = value; }
}
public string Sv
{
get { return sv; }
set { sv = value; }
}
public override string ToString()
{
return string.Format("{{iv: {0}, sv: {1}}}", iv, sv);
}
}

Testf.cs:

using System;

public class Testf
{
public static void Main(string[] args)
{
int stat;
DemofClient cli = new DemofClient("192.168.68.40");
int v1 = 123;
int v2 = 456;
int v3 = 0;
stat = cli.Add(v1, v2, ref v3);
Console.WriteLine("stat = {0}", stat);
Console.WriteLine("{0} + {1} = {2}", v1, v2, v3);
string s1 = "ABC";
string s2 = "DEF";
string s3 = "";
stat = cli.Conc(s1, s2, ref s3);
Console.WriteLine("stat = {0}", stat);
Console.WriteLine("|{0}| + |{1}| = |{2}|", s1, s2, s3);
Dataf o = new Dataf { Iv = 123, Sv = "ABC" };
stat = cli.Modify(ref o);
Console.WriteLine("stat = {0}", stat);
Console.WriteLine("o = {0}", o);
String id = "";
stat = cli.Getid(ref id);
Console.WriteLine("stat = {0}", stat);
Console.WriteLine("id = {0}", id);
}
}

C test client:

dataf.h:

struct dataf_t
{
int iv;
char sv[256];
};

typedef struct dataf_t *dataf;

testf.c:

#include <stdio.h>
#include <stdint.h>

#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#endif

#include "demof_client.h"

int main()
{
#ifdef WIN32
WSADATA WSAData;
WSAStartup(0x0101, &WSAData);
#endif
int32_t stat;
struct tig_t cli;
demof_client_init(&cli, "192.168.68.40");
int v1 = 123;
int v2 = 456;
int v3 = 0;
stat = demof_client_add(&cli, v1, v2, &v3);
printf("stat = %d\n", stat);
printf("%d + %d = %d\n", v1, v2, v3);
char *s1 = "ABC";
char *s2 = "DEF";
char *s3 = NULL;
stat = demof_client_conc(&cli, s1, s2, &s3);
printf("stat = %d\n", stat);
printf("|%s| + |%s| = |%s|\n", s1, s2, s3);
dataf o = malloc(sizeof(struct dataf_t));
o->iv = 123;
strcpy(o->sv, "ABC");
stat = demof_client_modify(&cli, &o);
printf("stat = %d\n", stat);
printf("{iv: %d, sv: %s}\n", o->iv, o->sv);
char *id = NULL;
stat = demof_client_getid(&cli, &id);
printf("stat = %d\n", stat);
printf("id = %s\n", id);
#ifdef WIN32
WSACleanup();
#endif
return 0;
}

Clients tested on Windows, but they should work fine on Linux as well -
standard Java/C#/C.

I think the client code looks very reasonable.

Question is whether it is worth continuing working on.

Lots of outstanding work:
* comment code
* document wire protocol
* add support for more data types including VAX float
* add support for arrays outside of structs
* add Python client generation
* add C++ client generation
* add C server generation (there is not really any reason for
server to be in Java except that it is easier to write a
multi-threaded server in Java than in C)

Thoughts?

Arne
Arne Vajhøj
2024-08-01 13:24:13 UTC
Permalink
Post by Arne Vajhøj
I could not get BridgeWorks to work. And the VMS side was
VMS Alpha only. And the non-Java option for client side
was COM.
But I do like the concept. So I tried creating something
similar that works with newer stuff. TIG (Transparent
Interface Generation).
Clients tested on Windows, but they should work fine on Linux as well -
standard Java/C#/C.
I think the client code looks very reasonable.
Question is whether it is worth continuing working on.
* comment code
* document wire protocol
* add support for more data types including VAX float
* add support for arrays outside of structs
* add Python client generation
* add C++ client generation
* add C server generation (there is not really any reason for
  server to be in Java except that it is easier to write a
  multi-threaded server in Java than in C)
Thoughts?
* look at endianess for C# and C - the Java client is endian
neutral, but the C# and C clients assume running on little
endian
* add buffer size arguments to C client API - it is the 21st century

Arne
Simon Clubley
2024-08-02 12:04:46 UTC
Permalink
Post by Arne Vajhøj
* look at endianess for C# and C - the Java client is endian
neutral, but the C# and C clients assume running on little
endian
* add buffer size arguments to C client API - it is the 21st century
* Switch to JSON instead of using XML - it is the year 2024. :-)

Simon.
--
Simon Clubley, ***@remove_me.eisner.decus.org-Earth.UFP
Walking destinations on a map are further away than they appear.
Arne Vajhøj
2024-08-04 01:11:38 UTC
Permalink
Post by Simon Clubley
* Switch to JSON instead of using XML - it is the year 2024. :-)
I could.

But I think this is a case where XML is more readable than JSON.

Arne
Lawrence D'Oliveiro
2024-08-04 01:25:38 UTC
Permalink
Post by Arne Vajhøj
...
<method>
<name>add</name> <args>
<arg>
<name>a</name> <type>LongWord</type>
<pass>Reference</pass> <use>In</use>
</arg>
<arg>
<name>b</name> <type>LongWord</type>
<pass>Reference</pass> <use>In</use>
</arg>
<arg>
<name>res</name> <type>LongWord</type>
<pass>Reference</pass> <use>Out</use>
</arg>
</args>
</method>
...
This looks so much like the D-Bus introspection format
<https://www.freedesktop.org/wiki/Software/dbus/>,
<https://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format>

See also Varlink <https://varlink.org/>, which uses a JSON-based
channel data format.
Arne Vajhøj
2024-08-11 23:03:25 UTC
Permalink
Post by Arne Vajhøj
I could not get BridgeWorks to work. And the VMS side was
VMS Alpha only. And the non-Java option for client side
was COM.
But I do like the concept. So I tried creating something
similar that works with newer stuff. TIG (Transparent
Interface Generation).
Question is whether it is worth continuing working on.
* comment code
* document wire protocol
* add support for more data types including VAX float
* add support for arrays outside of structs
* add Python client generation
* add C++ client generation
* add C server generation (there is not really any reason for
  server to be in Java except that it is easier to write a
  multi-threaded server in Java than in C)
Thoughts?
Not much feedback, but I did some of the outstanding tasks and
packed it up.

Start here:

https://www.vajhoej.dk/arne/opensource/vms/doc/tig/doc/

Download links:

https://www.vajhoej.dk/arne/opensource/vms/vmstig-bin-v0_1.zip
https://www.vajhoej.dk/arne/opensource/vms/vmstig-client-v0_1.zip
https://www.vajhoej.dk/arne/opensource/vms/vmstig-src-v0_1.zip

Arne
Arne Vajhøj
2024-08-14 14:27:25 UTC
Permalink
Post by Arne Vajhøj
Not much feedback, but I did some of the outstanding tasks and
packed it up.
https://www.vajhoej.dk/arne/opensource/vms/doc/tig/doc/
https://www.vajhoej.dk/arne/opensource/vms/vmstig-bin-v0_1.zip
https://www.vajhoej.dk/arne/opensource/vms/vmstig-client-v0_1.zip
https://www.vajhoej.dk/arne/opensource/vms/vmstig-src-v0_1.zip
I have updated.

Despite the native languages on VMS being non-OO then it
bothered me that the client side API was really non-OO
(classes were used but without any data in the class
it is not much OO).

So I came up with a hack.

VMS code (C in this case):

struct ooctx
{
int counter;
};

static int counter = 0;

long increment(struct ooctx *ctx)
{
counter++;
ctx->counter++;
return 1;
}

long get(struct ooctx *ctx, long *c1, long *c2)
{
*c1 = counter;
*c2 = ctx->counter;
return 1;
}

Silly example, but it should illustrate the difference
between a global variable and a client specific context.

XML to describe API:

<config>
<image>demoo</image>
<port>12350</port>
<mt>false</mt>
<oocontext>o</oocontext> <!-- <=============== this is the trick -->
<server>
<language>dk.vajhoej.vms.tig.server.JavaServerGen</language>
</server>
<client>
<language>dk.vajhoej.vms.tig.client.JavaClientGen</language>
<language>dk.vajhoej.vms.tig.client.CSharpClientGen</language>
<language>dk.vajhoej.vms.tig.client.PyClientGen</language>
<language>dk.vajhoej.vms.tig.client.CppClientGen</language>
</client>
<methods>
<method>
<name>increment</name>
<args/>
</method>
<method>
<name>get</name>
<args>
<arg>
<name>c1</name>
<type>LongWord</type>
<pass>Reference</pass>
<use>Out</use>
</arg>
<arg>
<name>c2</name>
<type>LongWord</type>
<pass>Reference</pass>
<use>Out</use>
</arg>
</args>
</method>
</methods>
</config>

And let us take client in Python.

First O.py for client access to context:

import struct

class O:
def __init__(self, counter = 0):
self.counter = counter
def pack(self):
return struct.pack('<l', self.counter)
def unpack(self, blk):
self.counter = struct.unpack('<l', blk)[0]

Test:

from sys import argv

from DemooClient import *

for i in range(3):
cli = DemooClient(argv[1])
for j in range(3):
stat = cli.increment()
stat, c1, c2 = cli.get()
print('%d %d (%d)' % (c1, c2, cli.oocontext().counter))

Output:

C:\Code\VMS\tig\examples>python testo.py 192.168.68.40
1 1 (1)
2 2 (2)
3 3 (3)
4 1 (1)
5 2 (2)
6 3 (3)
7 1 (1)
8 2 (2)
9 3 (3)

If one is not interested in the context client side (private context
in OO world), then one can just specify the size of the context instead
of the type.

<oocontext>o</oocontext>

->

<oocontext>4</oocontext>

and drop O.py implementation and do not try to access context
from client.

Python client:

from sys import argv

from DemoqClient import *

for i in range(3):
cli = DemoqClient(argv[1])
for j in range(3):
stat = cli.increment()
stat, c1, c2 = cli.get()
print('%d %d' % (c1, c2))

https://www.vajhoej.dk/arne/opensource/vms/vmstig-bin-v0_2.zip
https://www.vajhoej.dk/arne/opensource/vms/vmstig-client-v0_2.zip
https://www.vajhoej.dk/arne/opensource/vms/vmstig-src-v0_2.zip

Arne

Loading...