October 26, 2005
Regan Heath schrieb:

> 
> I haven't got any of those machines to compile upon and chances are I have missed some symbol or import that is required.

I also think so... :)
Lets see, compiling with "dmd -c com.d" I get:
-----
com.d(295): alias com.XCSETA conflicts with com.XCSETA at com.d(285)
com.d(296): alias com.XCSETAW conflicts with com.XCSETAW at com.d(286)
com.d(297): alias com.XCSETAF conflicts with com.XCSETAF at com.d(287)
com.d(298): alias com.XCSETAF conflicts with com.XCSETAF at com.d(287)
/opt/dmd/src/phobos/std/stream.d(2593): TArrayStream!(ubyte[]) is used as a type
/opt/dmd/src/phobos/std/stream.d(2593): class std.stream.MemoryStream base type must be class or interface, not void
/opt/dmd/src/phobos/std/stream.d(2613): function std.stream.MemoryStream.writeBlock function writeBlock does not override any
/opt/dmd/src/phobos/std/stream.d(2670): TArrayStream!(MmFile) is used as a type
/opt/dmd/src/phobos/std/stream.d(2670): class std.stream.MmFileStream base type must be class or interface, not void
/opt/dmd/src/phobos/std/stream.d(2679): function std.stream.MmFileStream.flush function flush does not override any
/opt/dmd/src/phobos/std/stream.d(2686): function std.stream.MmFileStream.close function close does not override any
com.d(295): identifier 'TCSANOW' is not defined
com.d(295): TCSANOW is used as a type
com.d(296): identifier 'TCSADRAIN' is not defined
com.d(296): TCSADRAIN is used as a type
com.d(297): identifier 'TCSAFLUSH' is not defined
com.d(297): TCSAFLUSH is used as a type
com.d(298): identifier 'TCSADFLUSH' is not defined
com.d(298): TCSADFLUSH is used as a type
-----

And compiling with "dmd -c -version=M_ELF com.d" I get:
-----
com.d(318): undefined identifier B110
com.d(318): undefined identifier B300
com.d(318): undefined identifier B600
com.d(318): undefined identifier B1200
com.d(319): undefined identifier B2400
com.d(319): undefined identifier B4800
com.d(319): undefined identifier B9600
com.d(319): undefined identifier B19200
com.d(320): undefined identifier B38400
com.d(320): undefined identifier B57600
com.d(320): undefined identifier B115200
-----

I rewrite part of your code like:

(approx @ line 318)
 private static int BaudRates[BaudRate.B115200+1] = [
    BaudRate.B110,  BaudRate.B300,  BaudRate.B600,  BaudRate.B1200,
    BaudRate.B2400, BaudRate.B4800, BaudRate.B9600, BaudRate.B19200,
    BaudRate.B38400,BaudRate.B57600,BaudRate.B115200
  ];

Compiling with "dmd -c -version=M_ELF com.d" I then get:
-----
com.d(327): undefined identifier strerror
com.d(327): function expected before (), not strerror of type int
com.d(327): incompatible types for ((msg ~ " {") ~ (strerror(getErrno()))): 'char[]' and 'int'
com.d(327): Can only concatenate arrays, not (char[] ~ int)
com.d(327): incompatible types for ((msg ~ " {" ~ strerror(getErrno())) ~ ("}")): 'int' and 'char[1]'
com.d(327): Can only concatenate arrays, not (int ~ char[1])
com.d(327): constructor object.Exception.this (char[]) does not match argument types (int)
com.d(327): cannot implicitly convert expression (msg ~ " {" ~ strerror(getErrno()) ~ "}") of type int to char[]
com.d(338): voids have no value
com.d(338): cannot implicitly convert expression (this.open(port,cast(BaudRate)2050)) of type void to int
com.d(341): undefined identifier tcgetattr
com.d(341): function expected before (), not tcgetattr of type int
com.d(350): cannot implicitly convert expression (CS8 | CREAD | CLOCAL | BaudRates[baud]) of type int to tcflag_t
com.d(357): undefined identifier tcsetattr
com.d(357): function expected before (), not tcsetattr of type int
com.d(365): function com.ComStream.close () does not match argument types (int)
com.d(365): Error: expected 0 arguments, not 1

com.d(378): cannot implicitly convert expression (FD_ISSET(this.file,&readfd)) of type int to bit
com.d(387): function std.stream.Stream.read (ubyte[]) does not match argument types (int,void*,uint)
com.d(387): Error: expected 1 arguments, not 3

com.d(387): cannot implicitly convert expression (this.file) of type int to wchar[]
com.d(387): cast(wchar[])(this.file) is not an lvalue
com.d(387): voids have no value
com.d(387): cannot implicitly convert expression (cast(Stream)(this).read(cast(wchar[])(this.file),result,len)) of type void to int
com.d(406): function std.stream.Stream.write (ubyte[]) does not match argument types (int,void*,uint)
com.d(406): Error: expected 1 arguments, not 3

com.d(406): cannot implicitly convert expression (this.file) of type int to wchar[]
com.d(406): voids have no value
com.d(406): cannot implicitly convert expression (cast(Stream)(this).write(cast(wchar[])(this.file),result,len)) of type void to int
-----

Oops... something is very wrong here :(
Tried to quickly fix the code but no success...
Also did not find "tcgetattr" defined in D anywhere.

Best,
Tiago

-- 
Tiago Gasiba (MSc.) - http://www.gasiba.de
Everything should be made as simple as possible, but not simpler.
October 27, 2005
On Wed, 26 Oct 2005 13:22:52 +0200, Tiago Gasiba <tiago.gasiba@gmail.com> wrote:
> Regan Heath schrieb:
>
>>
>> I haven't got any of those machines to compile upon and chances are I have
>> missed some symbol or import that is required.
>
> I also think so... :)
> Lets see, compiling with "dmd -c com.d" I get:
> -----
> com.d(295): alias com.XCSETA conflicts with com.XCSETA at com.d(285)
> com.d(296): alias com.XCSETAW conflicts with com.XCSETAW at com.d(286)
> com.d(297): alias com.XCSETAF conflicts with com.XCSETAF at com.d(287)
> com.d(298): alias com.XCSETAF conflicts with com.XCSETAF at com.d(287)

Oops. alias lines are backwards, try:
	alias XCSETA  TCSANOW;
	alias XCSETAW TCSADRAIN;
	alias XCSETAF TCSAFLUSH;
	alias XCSETAF TCSADFLUSH;

> /opt/dmd/src/phobos/std/stream.d(2593): TArrayStream!(ubyte[]) is used as a type
> /opt/dmd/src/phobos/std/stream.d(2593): class std.stream.MemoryStream base type must be class or interface, not void
> /opt/dmd/src/phobos/std/stream.d(2613): function std.stream.MemoryStream.writeBlock function writeBlock does not override any
> /opt/dmd/src/phobos/std/stream.d(2670): TArrayStream!(MmFile) is used as a type
> /opt/dmd/src/phobos/std/stream.d(2670): class std.stream.MmFileStream base type must be class or interface, not void
> /opt/dmd/src/phobos/std/stream.d(2679): function std.stream.MmFileStream.flush function flush does not override any
> /opt/dmd/src/phobos/std/stream.d(2686): function std.stream.MmFileStream.close function close does not override any

Not sure what that's on about.

> com.d(295): identifier 'TCSANOW' is not defined
> com.d(295): TCSANOW is used as a type
> com.d(296): identifier 'TCSADRAIN' is not defined
> com.d(296): TCSADRAIN is used as a type
> com.d(297): identifier 'TCSAFLUSH' is not defined
> com.d(297): TCSAFLUSH is used as a type
> com.d(298): identifier 'TCSADFLUSH' is not defined
> com.d(298): TCSADFLUSH is used as a type

should be fixed by alias lines.

> -----
>
> And compiling with "dmd -c -version=M_ELF com.d" I get:
> -----
> com.d(318): undefined identifier B110
> com.d(318): undefined identifier B300
> com.d(318): undefined identifier B600
> com.d(318): undefined identifier B1200
> com.d(319): undefined identifier B2400
> com.d(319): undefined identifier B4800
> com.d(319): undefined identifier B9600
> com.d(319): undefined identifier B19200
> com.d(320): undefined identifier B38400
> com.d(320): undefined identifier B57600
> com.d(320): undefined identifier B115200
> -----
>
> I rewrite part of your code like:
>
> (approx @ line 318)
>  private static int BaudRates[BaudRate.B115200+1] = [
>     BaudRate.B110,  BaudRate.B300,  BaudRate.B600,  BaudRate.B1200,
>     BaudRate.B2400, BaudRate.B4800, BaudRate.B9600, BaudRate.B19200,
>     BaudRate.B38400,BaudRate.B57600,BaudRate.B115200
>   ];

This is wrong. This array translates BaudRate values into the OS values for baud rate, i.e. B110 etc. They should be defined in a header somewhere... (I'll repost when I have fixed it)

> Compiling with "dmd -c -version=M_ELF com.d" I then get:
> -----
> com.d(327): undefined identifier strerror

Add "char* strerror(int);" to extern C block.

<snip>

> Oops... something is very wrong here :(
> Tried to quickly fix the code but no success...

I'll fix these and repost.

> Also did not find "tcgetattr" defined in D anywhere.

Yep. Need to add that def too.

Regan
October 27, 2005
Thanks Regan.

Unfortunately, the timeouts don't work in that code. Not for me, anyway. (In fact, I've never been able to get COM timeouts to work in Windows, except when I open the port in OVERLAPPED mode). For my application, timeouts are extremely important (a timeout occurs about once every five minutes). For every application I've used, a 0.5 second timeout has worked well. I modified your code to get this to work, but it's too hacky and specific to post here at the moment.

Thanks again, you've saved me quite a bit of time.
-Don



Regan Heath wrote:
> On Mon, 24 Oct 2005 13:03:16 +0200, Don Clugston <dac@nospam.com.au> wrote:
> 
>> Has anyone accessed serial (COM) ports using D (under Windows)?
> 
> 
> Yes.
> 
>> Does Mango support them?
>> If there is currently nothing in D, does anyone know of a candidate that  could be ported to D?
>> It ought to be possible to create something that would work on both  Windows and Linux PCs, since the hardware is the same.
> 
> 
> The attached source is a COM stream for windows only.
> 
> I have also written COM code on linux and freebsd and could probably add  support for these two to the attached source, let me know if you're  interested.
> 
> Regan
October 27, 2005
On Thu, 27 Oct 2005 08:30:24 +0200, Don Clugston <dac@nospam.com.au> wrote:
> Thanks Regan.
>
> Unfortunately, the timeouts don't work in that code. Not for me, anyway. (In fact, I've never been able to get COM timeouts to work in Windows, except when I open the port in OVERLAPPED mode). For my application, timeouts are extremely important (a timeout occurs about once every five minutes). For every application I've used, a 0.5 second timeout has worked well. I modified your code to get this to work, but it's too hacky and specific to post here at the moment.

So... you want it to timeout after 0.5 seconds on a read? What sort of timeout occurs once every 5 mins?

ReadFile should return immediately .. and you could use msleep to create a timeout (though it may not be 100% acurate). Is that what you did?

Opening using OVERLAPPED then using WaitCommEvent should allow you to do a "wait for data then read" sort of thing.

> Thanks again, you've saved me quite a bit of time.

Glad I could help.

Regan

> Regan Heath wrote:
>> On Mon, 24 Oct 2005 13:03:16 +0200, Don Clugston <dac@nospam.com.au> wrote:
>>
>>> Has anyone accessed serial (COM) ports using D (under Windows)?
>>   Yes.
>>
>>> Does Mango support them?
>>> If there is currently nothing in D, does anyone know of a candidate that  could be ported to D?
>>> It ought to be possible to create something that would work on both  Windows and Linux PCs, since the hardware is the same.
>>   The attached source is a COM stream for windows only.
>>  I have also written COM code on linux and freebsd and could probably add  support for these two to the attached source, let me know if you're  interested.
>>  Regan

October 27, 2005
Regan Heath wrote:
> On Thu, 27 Oct 2005 08:30:24 +0200, Don Clugston <dac@nospam.com.au> wrote:
> 
>> Thanks Regan.
>>
>> Unfortunately, the timeouts don't work in that code. Not for me, anyway.  (In fact, I've never been able to get COM timeouts to work in Windows,  except when I open the port in OVERLAPPED mode). For my application,  timeouts are extremely important (a timeout occurs about once every five  minutes). For every application I've used, a 0.5 second timeout has  worked well. I modified your code to get this to work, but it's too  hacky and specific to post here at the moment.
> 
> 
> So... you want it to timeout after 0.5 seconds on a read? What sort of  timeout occurs once every 5 mins?

The item of measurement equipment I'm currently working on has occasional glitches where it ignores commands. Such glitches occur about ten times per hour on this machine.
It can happen when the PLC inside the equipment is momentarily overworked, and dumps its command buffer because it can't keep up.
(Poor design, but it seems to be relatively common behaviour amongst engineering tools). The other case is when the power to the machine has been cut temporarily so that the PLC has reset, again clearing the command buffer, or ignoring commands for the few milliseconds it takes to restart. It's an important issue when your program needs to run 24/7.

> ReadFile should return immediately .. and you could use msleep to create a  timeout (though it may not be 100% acurate). Is that what you did?

In this case, yes, and it's been enough to get it going. The length of the timeout period is generally not very critical, it's just important that the system not hang.

> Opening using OVERLAPPED then using WaitCommEvent should allow you to do a  "wait for data then read" sort of thing.

This is what I plan to do in the longer term. I've done it in C++, but in D there are delegates and better multithreading support, so it can
potentially be much better. It really isn't much different to a web client, you cannot guarantee that you'll get a response to all packets you send, this is why I thought it has commonality with Mango.
October 27, 2005
Ok, here is my latest version.

I can compile it on a ubuntu linux vmware virtual machine but it wont run, it errors on the "tcsetattr" call. How about you?

Regan

October 27, 2005
Regan Heath schrieb:

> Ok, here is my latest version.
> 
> I can compile it on a ubuntu linux vmware virtual machine but it wont run, it errors on the "tcsetattr" call. How about you?
> 
> Regan

Same here.
The B57600,B115200 that you can not find are declared in termbits.h under /usr/include/asm. I give you a small <snip> of the file:

<snip>
#define    B57600 0010001
#define   B115200 0010002
#define   B230400 0010003
#define   B460800 0010004
#define   B500000 0010005
#define   B576000 0010006
#define   B921600 0010007
#define  B1000000 0010010
#define  B1152000 0010011
#define  B1500000 0010012
#define  B2000000 0010013
#define  B2500000 0010014
#define  B3000000 0010015
#define  B3500000 0010016
#define  B4000000 0010017
<snip>

Furthermore, I think that there is something wrong with your IOCTL. Lets see - I have downloaded and compiled the following program: http://gpsbots.com/tutorials/tut_linux_serial.php
Compiled with g++ and ran the program with "strace", i.e. "strace tut_linux_serial".
The program runs perfectly and I get the following (relevant) system calls:

<snip>
ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 -opost -isig -icanon -echo ...}) = 0
ioctl(3, TCFLSH, 0)                     = 0
ioctl(3, SNDCTL_TMR_START or TCSETS, {B38400 -opost -isig -icanon -echo ...}) = 0
ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 -opost -isig -icanon -echo ...}) = 0
<snip>

With your D module, I have written the following program:

<snip - test.d>
import com;

int main(){
  ComStream COM = new ComStream;

  COM.open("/dev/ttyS0",B9600);

  return 0;
}
<snip - test.d>

And I get the following (relevant) system calls:
ioctl(3, 0x7801, 0x40191fa4)            = -1 EINVAL (Invalid argument)
ioctl(3, 0x7801, 0x40191fa4)            = -1 EINVAL (Invalid argument)


Conclusion:
I think that you are calling the IOCTL wrongly. I have further investigated which number TCGETS should be.
It appears in /usr/include/asm/ioctls.h and its value is 0x5401.
I have replaced the IOCTL call with:

int tcgetattr(int fd, termios *termios_p) { return ioctl(fd, 0x5401, termios_p); }
and it works! now, the system call trace looks like:

ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 -opost -isig -icanon -echo ...}) = 0
ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 -opost -isig -icanon -echo ...}) = 0

BUT, the next error is:
Error: ArrayBoundsError com(396)
ie the line
if (cfsetispeed(&attr,cast(speed_t)BaudRates[baud]) != 0) throw new ComException("Failed to set output speed");

For now, that's all...

Best regards,
Tiago
-- 
Tiago Gasiba (MSc.) - http://www.gasiba.de
Everything should be made as simple as possible, but not simpler.
October 27, 2005
Tiago Gasiba schrieb:


> BUT, the next error is:
> Error: ArrayBoundsError com(396)
> ie the line
> if (cfsetispeed(&attr,cast(speed_t)BaudRates[baud]) != 0) throw new ComException("Failed to set output speed");

I should have known... in test.d I have written

  COM.open("/dev/ttyS0",B9600);

instead of

  COM.open("/dev/ttyS0",BaudRate.B9600);

This is a serious source of bugs and I think that you should remove completely the BaudRate.B9600.
Just declare the consts and use them/pass them as parameters!
If necessary, use different definitions for Windows/Linux.

After correcting this small bug, the next one pops out here:
open("/dev/ttyS0", O_RDWR|O_NONBLOCK)   = 3
ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 opost isig icanon echo ...}) = 0
ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 opost isig icanon echo ...}) = 0
ioctl(3, 0x7802, 0x40191fa4)            = -1 EINVAL (Invalid argument)

All the IOCTL's have to be reviewed: The better solution is to rewrite the ioctls's consts. You can find a first attempt in attachment.

Best,
Tiago

-- 
Tiago Gasiba (MSc.) - http://www.gasiba.de
Everything should be made as simple as possible, but not simpler.

October 27, 2005
On Thu, 27 Oct 2005 18:02:40 +0200, Tiago Gasiba <tiago.gasiba@gmail.com> wrote:
>> BUT, the next error is:
>> Error: ArrayBoundsError com(396)
>> ie the line
>> if (cfsetispeed(&attr,cast(speed_t)BaudRates[baud]) != 0) throw new ComException("Failed to set output speed");
>
> I should have known... in test.d I have written
>
>   COM.open("/dev/ttyS0",B9600);
>
> instead of
>
>   COM.open("/dev/ttyS0",BaudRate.B9600);
>
> This is a serious source of bugs and I think that you should remove completely the BaudRate.B9600.
> Just declare the consts and use them/pass them as parameters!
> If necessary, use different definitions for Windows/Linux.

No. In order to make it a cross-platform we have to use the same definitions, that is why BaudRate was created. There should be a way to make it error when you fail to pass a BaudRate enum value.

Regan
October 27, 2005
On Thu, 27 Oct 2005 15:04:22 +0200, Tiago Gasiba <tiago.gasiba@gmail.com> wrote:
>> Ok, here is my latest version.
>>
>> I can compile it on a ubuntu linux vmware virtual machine but it wont run,
>> it errors on the "tcsetattr" call. How about you?
>>
>> Regan
>
> Same here.
> The B57600,B115200 that you can not find are declared in termbits.h under /usr/include/asm. I give you a small <snip> of the file:
<snip>

I found them in the headers on my ubuntu system, but, they're not defined in the Digital Mars (DMC) headers. As DMD links with the DMC libraries (it does on Windows) I thought we could only use stuff that was present there. Am I wrong? is it linking with the ubuntu (or other) libraries?

> Furthermore, I think that there is something wrong with your IOCTL. Lets see - I have downloaded and compiled the following program: http://gpsbots.com/tutorials/tut_linux_serial.php
> Compiled with g++ and ran the program with "strace", i.e. "strace tut_linux_serial".
> The program runs perfectly and I get the following (relevant) system calls:
>
> <snip>
> ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 -opost -isig -icanon -echo ...}) = 0
> ioctl(3, TCFLSH, 0)                     = 0
> ioctl(3, SNDCTL_TMR_START or TCSETS, {B38400 -opost -isig -icanon -echo ...}) = 0
> ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 -opost -isig -icanon -echo ...}) = 0
> <snip>
>
> With your D module, I have written the following program:
>
> <snip - test.d>
> import com;
>
> int main(){
>   ComStream COM = new ComStream;
>
>   COM.open("/dev/ttyS0",B9600);
>
>   return 0;
> }
> <snip - test.d>
>
> And I get the following (relevant) system calls:
> ioctl(3, 0x7801, 0x40191fa4)            = -1 EINVAL (Invalid argument)
> ioctl(3, 0x7801, 0x40191fa4)            = -1 EINVAL (Invalid argument)
>
>
> Conclusion:
> I think that you are calling the IOCTL wrongly. I have further investigated which number TCGETS should be.
> It appears in /usr/include/asm/ioctls.h and its value is 0x5401.
> I have replaced the IOCTL call with:
>
> int tcgetattr(int fd, termios *termios_p) { return ioctl(fd, 0x5401, termios_p); }
> and it works! now, the system call trace looks like:
>
> ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 -opost -isig -icanon -echo ...}) = 0
> ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 -opost -isig -icanon -echo ...}) = 0

Did you compile with -version=M_ELF?
tcgetattr works for me, it was tcsetattr that was failing.
The TCGets value I am using is from the DMC headers (not the ubuntu ones).

How do you use strace. I have tried using it on my D executable and all it doesn't seem to show anything of my own code, i.e. I cannot see the ioctl calls. I see some open calls (I assume this is the code DMD adds to the start of every executable).

> BUT, the next error is:
> Error: ArrayBoundsError com(396)
> ie the line
> if (cfsetispeed(&attr,cast(speed_t)BaudRates[baud]) != 0) throw new ComException("Failed to set output speed");

This was due to you using the wrong baud value.

Regan.