Arne Vajhøj
2024-08-01 12:20:28 UTC
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
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