Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.arch.embedded > #28274 > unrolled thread

How to transmit the value of an enumeration

Started bypozz <pozzugno@gmail.com>
First post2018-10-24 13:18 +0200
Last post2018-10-26 20:30 -0500
Articles 17 — 8 participants

Back to article view | Back to comp.arch.embedded


Contents

  How to transmit the value of an enumeration pozz <pozzugno@gmail.com> - 2018-10-24 13:18 +0200
    Re: How to transmit the value of an enumeration Jack <jack4747@gmail.com> - 2018-10-24 04:45 -0700
      Re: How to transmit the value of an enumeration pozz <pozzugno@gmail.com> - 2018-10-24 15:18 +0200
        Re: How to transmit the value of an enumeration David Brown <david.brown@hesbynett.no> - 2018-10-24 17:05 +0200
    Re: How to transmit the value of an enumeration David Brown <david.brown@hesbynett.no> - 2018-10-24 15:10 +0200
      Re: How to transmit the value of an enumeration pozz <pozzugno@gmail.com> - 2018-10-24 15:27 +0200
        Re: How to transmit the value of an enumeration David Brown <david.brown@hesbynett.no> - 2018-10-24 16:59 +0200
        Re: How to transmit the value of an enumeration Clifford Heath <no.spam@please.net> - 2018-10-25 08:57 +1100
    Re: How to transmit the value of an enumeration Joe Chisolm <jchisolm6@earthlink.net> - 2018-10-24 12:48 -0500
      Re: How to transmit the value of an enumeration pozz <pozzugno@gmail.com> - 2018-10-24 23:20 +0200
        Re: How to transmit the value of an enumeration Joe Chisolm <jchisolm6@earthlink.net> - 2018-10-24 22:21 -0500
      Re: How to transmit the value of an enumeration David Brown <david.brown@hesbynett.no> - 2018-10-25 09:31 +0200
        Re: How to transmit the value of an enumeration Rob Gaddi <rgaddi@highlandtechnology.invalid> - 2018-10-25 09:58 -0700
        Re: How to transmit the value of an enumeration Joe Chisolm <jchisolm6@earthlink.net> - 2018-10-25 18:38 -0500
          Re: How to transmit the value of an enumeration David Brown <david.brown@hesbynett.no> - 2018-10-26 09:24 +0200
    Re: How to transmit the value of an enumeration George Neuner <gneuner2@comcast.net> - 2018-10-24 15:19 -0400
    Re: How to transmit the value of an enumeration Les Cargill <lcargill99@comcast.com> - 2018-10-26 20:30 -0500

#28274 — How to transmit the value of an enumeration

Frompozz <pozzugno@gmail.com>
Date2018-10-24 13:18 +0200
SubjectHow to transmit the value of an enumeration
Message-ID<pqpkdn$ed3$1@dont-email.me>
enum type_t {
   TYPE_1,
   TYPE_2,
   ...
   TYPE_100,
}

void tx_byte(unsigned char c);

enum type_t t;
...
tx_byte((unsigned char)t);


If the maximum value in the enumeration is lower than 256, you can cast 
to (unsigned char) and transmit it as a byte.


However the receiver must have the same enumeration.  The transmitter 
and the receiver must agreed on *the exact values* of the enumeration.

What happens if the developer of the transmitter thinks that it is 
better to swap some values during a future release?  The enumeration 
isn't used only for transmission, it is also used for general logic of 
the software.

Until now I didn't found a good solution.  I only write some comments 
before the enumeration declaration:

/* WARNING!! DON'T CHANGE THE ORDER OF THE VALUES INSIDE ENUMERATION
  * They are also used in the transmission */

I sometimes prefer explicit the value transmitted:

if (t == TYPE_1) tx_byte(0);
if (t == TYPE_2) tx_byte(1);
...


What is your strategy?

[toc] | [next] | [standalone]


#28275

FromJack <jack4747@gmail.com>
Date2018-10-24 04:45 -0700
Message-ID<2f1efaa1-e0f4-47ba-a052-3b86f43c8d56@googlegroups.com>
In reply to#28274
On Wednesday, October 24, 2018 at 1:18:20 PM UTC+2, pozz wrote:
> enum type_t {
>    TYPE_1,
>    TYPE_2,
>    ...
>    TYPE_100,
> }
> 
> void tx_byte(unsigned char c);
> 
> enum type_t t;
> ...
> tx_byte((unsigned char)t);
> 
> 
> If the maximum value in the enumeration is lower than 256, you can cast 
> to (unsigned char) and transmit it as a byte.
> 
> 
> However the receiver must have the same enumeration.  The transmitter 
> and the receiver must agreed on *the exact values* of the enumeration.
> 
> What happens if the developer of the transmitter thinks that it is 
> better to swap some values during a future release?  The enumeration 
> isn't used only for transmission, it is also used for general logic of 
> the software.
> 
> Until now I didn't found a good solution.  I only write some comments 
> before the enumeration declaration:
> 
> /* WARNING!! DON'T CHANGE THE ORDER OF THE VALUES INSIDE ENUMERATION
>   * They are also used in the transmission */
> 
> I sometimes prefer explicit the value transmitted:
> 
> if (t == TYPE_1) tx_byte(0);
> if (t == TYPE_2) tx_byte(1);
> ...
> 
> 
> What is your strategy?

Long answer: read the definition of "enumeration"

Short answer:
An enumeration is a normal type disguised and is implementation dependent. Usually is signed int (whatever it means) but you have to check your compiler doc to be sure.

You can also explicitly set the value of every member of an enum:
(and if you use an enum in a packet that is sent to someone else it may be a very good idea to do so)

enum type_t {
    TYPE_1 = 34,
    TYPE_2 = 56,
    TYPE_3,
    TYPE_4 = 0,
    TYPE_5 = 0,
    TYPE_100,
 } 

Exercise left to the reader: what's the value of TYPE_3 and TYPE_100?

If you need to send something to someone else there is a thing called "protocol" that explain in detail (hopefully) what's inside every byte you send. If for some reason the protocol changes...both transmitter and receiver needs to be modified.

Bye Jack

[toc] | [prev] | [next] | [standalone]


#28277

Frompozz <pozzugno@gmail.com>
Date2018-10-24 15:18 +0200
Message-ID<pqprem$pnv$1@dont-email.me>
In reply to#28275
Il 24/10/2018 13:45, Jack ha scritto:
> On Wednesday, October 24, 2018 at 1:18:20 PM UTC+2, pozz wrote:
>> enum type_t {
>>     TYPE_1,
>>     TYPE_2,
>>     ...
>>     TYPE_100,
>> }
>>
[...]
>>
>> What is your strategy?
> 
> Long answer: read the definition of "enumeration"
> 
> Short answer:
> An enumeration is a normal type disguised and is implementation dependent. Usually is signed int (whatever it means) but you have to check your compiler doc to be sure.

These details are generally important, but not for my question.  I don't 
know if C standard guarantees that, if not specified, the first value is 
zero and the others are contiguous.  If it is guaranteed, my example 
above works.  Otherwise, the declaration of my enumeration should have been:

enum type_t {
     TYPE_1 = 0,
     TYPE_2,
     ...
     TYPE_100,
}


> You can also explicitly set the value of every member of an enum:
> (and if you use an enum in a packet that is sent to someone else it may be a very good idea to do so)
> 
> enum type_t {
>      TYPE_1 = 34,
>      TYPE_2 = 56,
>      TYPE_3,
>      TYPE_4 = 0,
>      TYPE_5 = 0,
>      TYPE_100,
>   }
> 
> Exercise left to the reader: what's the value of TYPE_3 and TYPE_100?
> 
> If you need to send something to someone else there is a thing called "protocol" that explain in detail (hopefully) what's inside every byte you send. If for some reason the protocol changes...both transmitter and receiver needs to be modified.

I know that, but I often have a piece of code that manages values from a 
long enumeration.  It's ok to swap the members or change explicitly the 
values of them.  The code will always work while you insist using the 
name and not the value in the code, as it should be (otherwise what is 
the purpose of the enumeration?).

I often need to share some info with another device (or PC) connected in 
some way (serial line, Ethernet, Internet...)[*].
So I often need to transfer the value of an enumeration. The simplest 
way would be to transfer the (explicitly or implicitly) integer value of 
the enumeration (here it's not important if signed or unsigned, 1 2 or 4 
bytes).  This is very handy, because I only need a cast.

I understand this isn't the more robust way, because the enumeration 
could be changed in the future, broking the protocol.

What is the alternative? Re-define the long enumeration with another 
long list of values used in the protocol only and write a long piece of 
code:

   enum proto_type_t {
     PROTO_TYPE_1,
     PROTO_TYPE_2,
     ...
     PROTO_TYPE_100
   };

   if (type == TYPE_1) transmit(PROTO_TYPE_1);
   if (type == TYPE_2) transmit(PROTO_TYPE_2);
   ...

Most of the time, both enumerations are identical, so this long piece of 
error-prone code seems stupid.

Another alternative is to be sure the enumeration members aren't changed 
in the future. But is a simple comment sufficient?



[*] This will be the origin of another post in this ng.

[toc] | [prev] | [next] | [standalone]


#28281

FromDavid Brown <david.brown@hesbynett.no>
Date2018-10-24 17:05 +0200
Message-ID<pqq1nl$4ja$1@dont-email.me>
In reply to#28277
On 24/10/18 15:18, pozz wrote:
> Il 24/10/2018 13:45, Jack ha scritto:
>> On Wednesday, October 24, 2018 at 1:18:20 PM UTC+2, pozz wrote:
>>> enum type_t {
>>>     TYPE_1,
>>>     TYPE_2,
>>>     ...
>>>     TYPE_100,
>>> }
>>>
> [...]
>>>
>>> What is your strategy?
>>
>> Long answer: read the definition of "enumeration"
>>
>> Short answer:
>> An enumeration is a normal type disguised and is implementation
>> dependent. Usually is signed int (whatever it means) but you have to
>> check your compiler doc to be sure.
> 
> These details are generally important, but not for my question.  I don't
> know if C standard guarantees that, if not specified, the first value is
> zero and the others are contiguous.  If it is guaranteed, my example
> above works.  Otherwise, the declaration of my enumeration should have
> been:
> 

Enumeration constants in C are always of type "int", unless your
compiler has non-conforming modes that it documents and that you
actively use.  (In C++, you can specify the underlying type and the
enumeration itself is a type.)

The first enumerator has value 0 if it is not given explicitly.  Each
other enumerator is one more than the previous enumerator, if there is
no explicit value given.

When giving enumerators explicitly, it is fine to have duplicates and
overlaps.

[toc] | [prev] | [next] | [standalone]


#28276

FromDavid Brown <david.brown@hesbynett.no>
Date2018-10-24 15:10 +0200
Message-ID<pqpqvr$nep$1@dont-email.me>
In reply to#28274
On 24/10/18 13:18, pozz wrote:
> enum type_t {
>   TYPE_1,
>   TYPE_2,
>   ...
>   TYPE_100,
> }
> 
> void tx_byte(unsigned char c);
> 
> enum type_t t;
> ...
> tx_byte((unsigned char)t);
> 
> 
> If the maximum value in the enumeration is lower than 256, you can cast
> to (unsigned char) and transmit it as a byte.
> 
> 
> However the receiver must have the same enumeration.  The transmitter
> and the receiver must agreed on *the exact values* of the enumeration.
> 
> What happens if the developer of the transmitter thinks that it is
> better to swap some values during a future release?  The enumeration
> isn't used only for transmission, it is also used for general logic of
> the software.
> 
> Until now I didn't found a good solution.  I only write some comments
> before the enumeration declaration:
> 
> /* WARNING!! DON'T CHANGE THE ORDER OF THE VALUES INSIDE ENUMERATION
>  * They are also used in the transmission */
> 
> I sometimes prefer explicit the value transmitted:
> 
> if (t == TYPE_1) tx_byte(0);
> if (t == TYPE_2) tx_byte(1);
> ...
> 
> 
> What is your strategy?

It doesn't matter whether you are sending "raw" integers, enumerations,
text strings, lead balloons - the sender and the receiver need to agree
on the protocol and the messages sent.  The easiest way is to make sure
things like your enumeration here are in a common header that both the
receiver and the transmitter include in their code - then it is hard to
get out of sync.

It can also be worth including some sort of version information in the
initial handshake of the protocol - any changes in the enumeration or
other details should be accompanied by a change in the version number,
so that the other end can see that something has changed.

[toc] | [prev] | [next] | [standalone]


#28278

Frompozz <pozzugno@gmail.com>
Date2018-10-24 15:27 +0200
Message-ID<pqps0e$t0l$1@dont-email.me>
In reply to#28276
Il 24/10/2018 15:10, David Brown ha scritto:
> On 24/10/18 13:18, pozz wrote:
>> enum type_t {
>>    TYPE_1,
>>    TYPE_2,
>>    ...
>>    TYPE_100,
>> }
>>
>> void tx_byte(unsigned char c);
>>
>> enum type_t t;
>> ...
>> tx_byte((unsigned char)t);
>>
>>
>> If the maximum value in the enumeration is lower than 256, you can cast
>> to (unsigned char) and transmit it as a byte.
>>
>>
>> However the receiver must have the same enumeration.  The transmitter
>> and the receiver must agreed on *the exact values* of the enumeration.
>>
>> What happens if the developer of the transmitter thinks that it is
>> better to swap some values during a future release?  The enumeration
>> isn't used only for transmission, it is also used for general logic of
>> the software.
>>
>> Until now I didn't found a good solution.  I only write some comments
>> before the enumeration declaration:
>>
>> /* WARNING!! DON'T CHANGE THE ORDER OF THE VALUES INSIDE ENUMERATION
>>   * They are also used in the transmission */
>>
>> I sometimes prefer explicit the value transmitted:
>>
>> if (t == TYPE_1) tx_byte(0);
>> if (t == TYPE_2) tx_byte(1);
>> ...
>>
>>
>> What is your strategy?
> 
> It doesn't matter whether you are sending "raw" integers, enumerations,
> text strings, lead balloons - the sender and the receiver need to agree
> on the protocol and the messages sent.  The easiest way is to make sure
> things like your enumeration here are in a common header that both the
> receiver and the transmitter include in their code - then it is hard to
> get out of sync.
> 
> It can also be worth including some sort of version information in the
> initial handshake of the protocol - any changes in the enumeration or
> other details should be accompanied by a change in the version number,
> so that the other end can see that something has changed.

The problem here is not to share a new version of the protocol, but to 
avoid that a developer could change some part of the code (an 
enumeration) and broke the protocol *inadvertently*.

[toc] | [prev] | [next] | [standalone]


#28280

FromDavid Brown <david.brown@hesbynett.no>
Date2018-10-24 16:59 +0200
Message-ID<pqq1cd$12c$2@dont-email.me>
In reply to#28278
On 24/10/18 15:27, pozz wrote:
> Il 24/10/2018 15:10, David Brown ha scritto:
>> On 24/10/18 13:18, pozz wrote:
>>> enum type_t {
>>>    TYPE_1,
>>>    TYPE_2,
>>>    ...
>>>    TYPE_100,
>>> }
>>>
>>> void tx_byte(unsigned char c);
>>>
>>> enum type_t t;
>>> ...
>>> tx_byte((unsigned char)t);
>>>
>>>
>>> If the maximum value in the enumeration is lower than 256, you can cast
>>> to (unsigned char) and transmit it as a byte.
>>>
>>>
>>> However the receiver must have the same enumeration.  The transmitter
>>> and the receiver must agreed on *the exact values* of the enumeration.
>>>
>>> What happens if the developer of the transmitter thinks that it is
>>> better to swap some values during a future release?  The enumeration
>>> isn't used only for transmission, it is also used for general logic of
>>> the software.
>>>
>>> Until now I didn't found a good solution.  I only write some comments
>>> before the enumeration declaration:
>>>
>>> /* WARNING!! DON'T CHANGE THE ORDER OF THE VALUES INSIDE ENUMERATION
>>>   * They are also used in the transmission */
>>>
>>> I sometimes prefer explicit the value transmitted:
>>>
>>> if (t == TYPE_1) tx_byte(0);
>>> if (t == TYPE_2) tx_byte(1);
>>> ...
>>>
>>>
>>> What is your strategy?
>>
>> It doesn't matter whether you are sending "raw" integers, enumerations,
>> text strings, lead balloons - the sender and the receiver need to agree
>> on the protocol and the messages sent.  The easiest way is to make sure
>> things like your enumeration here are in a common header that both the
>> receiver and the transmitter include in their code - then it is hard to
>> get out of sync.
>>
>> It can also be worth including some sort of version information in the
>> initial handshake of the protocol - any changes in the enumeration or
>> other details should be accompanied by a change in the version number,
>> so that the other end can see that something has changed.
> 
> The problem here is not to share a new version of the protocol, but to
> avoid that a developer could change some part of the code (an
> enumeration) and broke the protocol *inadvertently*.

That is a people problem, not a technical one.

[toc] | [prev] | [next] | [standalone]


#28285

FromClifford Heath <no.spam@please.net>
Date2018-10-25 08:57 +1100
Message-ID<2V5AD.568057$cL2.409885@fx43.iad>
In reply to#28278
On 25/10/18 12:27 am, pozz wrote:
> Il 24/10/2018 15:10, David Brown ha scritto:
>> On 24/10/18 13:18, pozz wrote:
>>> enum type_t {
>>>    TYPE_1,
>>>    TYPE_2,
>>>    ...
>>>    TYPE_100,
>>> }
>>>
>>> void tx_byte(unsigned char c);
>>>
>>> enum type_t t;
>>> ...
>>> tx_byte((unsigned char)t);
>>>
>>>
>>> If the maximum value in the enumeration is lower than 256, you can cast
>>> to (unsigned char) and transmit it as a byte.
>>>
>>>
>>> However the receiver must have the same enumeration.  The transmitter
>>> and the receiver must agreed on *the exact values* of the enumeration.
>>>
>>> What happens if the developer of the transmitter thinks that it is
>>> better to swap some values during a future release?  The enumeration
>>> isn't used only for transmission, it is also used for general logic of
>>> the software.
>>>
>>> Until now I didn't found a good solution.  I only write some comments
>>> before the enumeration declaration:
>>>
>>> /* WARNING!! DON'T CHANGE THE ORDER OF THE VALUES INSIDE ENUMERATION
>>>   * They are also used in the transmission */
>>>
>>> I sometimes prefer explicit the value transmitted:
>>>
>>> if (t == TYPE_1) tx_byte(0);
>>> if (t == TYPE_2) tx_byte(1);
>>> ...
>>>
>>>
>>> What is your strategy?
>>
>> It doesn't matter whether you are sending "raw" integers, enumerations,
>> text strings, lead balloons - the sender and the receiver need to agree
>> on the protocol and the messages sent.  The easiest way is to make sure
>> things like your enumeration here are in a common header that both the
>> receiver and the transmitter include in their code - then it is hard to
>> get out of sync.
>>
>> It can also be worth including some sort of version information in the
>> initial handshake of the protocol - any changes in the enumeration or
>> other details should be accompanied by a change in the version number,
>> so that the other end can see that something has changed.
> 
> The problem here is not to share a new version of the protocol, but to 
> avoid that a developer could change some part of the code (an 
> enumeration) and broke the protocol *inadvertently*.

If you've got a developer that might do that, fire them.

Protocol structures and parameters should be isolated
from everything else into a header file that is heavily
labeled "Everything in here is critical to compatibility".

Any developer who doesn't know *exactly* what that means
has no business working on such a system.

Clifford Heath

[toc] | [prev] | [next] | [standalone]


#28282

FromJoe Chisolm <jchisolm6@earthlink.net>
Date2018-10-24 12:48 -0500
Message-ID<846dnZ-IQIryLU3GnZ2dnUU7-bmdnZ2d@earthlink.com>
In reply to#28274
On Wed, 24 Oct 2018 13:18:15 +0200, pozz wrote:

> enum type_t {
>    TYPE_1,
>    TYPE_2,
>    ...
>    TYPE_100,
> }
> 
> void tx_byte(unsigned char c);
> 
> enum type_t t;
> ...
> tx_byte((unsigned char)t);
> 
> 
> If the maximum value in the enumeration is lower than 256, you can cast 
> to (unsigned char) and transmit it as a byte.
> 
> 
> However the receiver must have the same enumeration.  The transmitter 
> and the receiver must agreed on *the exact values* of the enumeration.
> 
> What happens if the developer of the transmitter thinks that it is 
> better to swap some values during a future release?  The enumeration 
> isn't used only for transmission, it is also used for general logic of 
> the software.
> 
> Until now I didn't found a good solution.  I only write some comments 
> before the enumeration declaration:
> 
> /* WARNING!! DON'T CHANGE THE ORDER OF THE VALUES INSIDE ENUMERATION
>   * They are also used in the transmission */
> 
> I sometimes prefer explicit the value transmitted:
> 
> if (t == TYPE_1) tx_byte(0);
> if (t == TYPE_2) tx_byte(1);
> ...
> 
> 
> What is your strategy?

Your protocol document defines what is sent on the communications channel.
In your code you may have:
 enum type_t {
    TYPE_2,
    TYPE_1,
    ...
    TYPE_100
 }

But in your protocol handler you have
 enum protocol_type_t {
    PTYPE_1,
    PTYPE_2,
    ...
    PTYPE_100
 }
switch(t) {
case TYPE_1:
  tx_byte(PTYPE_1);
 
....

You convert your internal type_t to the external protocol protocol_type_t
and protocol_type_t always (well should always) matches the spec.  If the code
passes a type_t that does not match a protocol_type_t you have a error and about
to violate the protocol - logs and alarms happen. It's up to the receiver to
decide what to do if a protocol_type_t does not match a value it can deal with.

If your protocol says protocol_type_t is sent as 8 bits I think it's a good idea
to fill out the enum with all 256 possibilities defined.
PTYPE_100,
SPARE_PTYPE_101,
SPARE_PTYPE_102,
...

The protocol doc defines what PTYPE_1 to PTYPE_100 mean and notes 101 to max value
are reserved for future use.

-- 
Chisolm
Republic of Texas

[toc] | [prev] | [next] | [standalone]


#28284

Frompozz <pozzugno@gmail.com>
Date2018-10-24 23:20 +0200
Message-ID<pqqnma$pme$1@dont-email.me>
In reply to#28282
Il 24/10/2018 19:48, Joe Chisolm ha scritto:
 > On Wed, 24 Oct 2018 13:18:15 +0200, pozz wrote:
 >
 >> enum type_t {
 >>     TYPE_1,
 >>     TYPE_2,
 >>     ...
 >>     TYPE_100,
 >> }
 >>
 >> void tx_byte(unsigned char c);
 >>
 >> enum type_t t;
 >> ...
 >> tx_byte((unsigned char)t);
 >>
 >>
 >> If the maximum value in the enumeration is lower than 256, you can cast
 >> to (unsigned char) and transmit it as a byte.
 >>
 >>
 >> However the receiver must have the same enumeration.  The transmitter
 >> and the receiver must agreed on *the exact values* of the enumeration.
 >>
 >> What happens if the developer of the transmitter thinks that it is
 >> better to swap some values during a future release?  The enumeration
 >> isn't used only for transmission, it is also used for general logic of
 >> the software.
 >>
 >> Until now I didn't found a good solution.  I only write some comments
 >> before the enumeration declaration:
 >>
 >> /* WARNING!! DON'T CHANGE THE ORDER OF THE VALUES INSIDE ENUMERATION
 >>    * They are also used in the transmission */
 >>
 >> I sometimes prefer explicit the value transmitted:
 >>
 >> if (t == TYPE_1) tx_byte(0);
 >> if (t == TYPE_2) tx_byte(1);
 >> ...
 >>
 >>
 >> What is your strategy?
 >
 > Your protocol document defines what is sent on the communications 
channel.
 > In your code you may have:
 >   enum type_t {
 >      TYPE_2,
 >      TYPE_1,
 >      ...
 >      TYPE_100
 >   }
 >
 > But in your protocol handler you have
 >   enum protocol_type_t {
 >      PTYPE_1,
 >      PTYPE_2,
 >      ...
 >      PTYPE_100
 >   }
 > switch(t) {
 > case TYPE_1:
 >    tx_byte(PTYPE_1);
 >
 > .....
 >
 > You convert your internal type_t to the external protocol protocol_type_t
 > and protocol_type_t always (well should always) matches the spec.

Yes, it seems *THE* best solution in order to avoid problems.  Even if 
it is tedious, because you have two enumerations instead of one and you 
have a long piece of translation code that is silly.

[toc] | [prev] | [next] | [standalone]


#28286

FromJoe Chisolm <jchisolm6@earthlink.net>
Date2018-10-24 22:21 -0500
Message-ID<qs6dndL8xKdZq0zGnZ2dnUU7-eudnZ2d@earthlink.com>
In reply to#28284
On Wed, 24 Oct 2018 23:20:11 +0200, pozz wrote:

> Il 24/10/2018 19:48, Joe Chisolm ha scritto:
>  > On Wed, 24 Oct 2018 13:18:15 +0200, pozz wrote:
>  >
>  >> enum type_t {
>  >>     TYPE_1,
>  >>     TYPE_2,
>  >>     ...
>  >>     TYPE_100,
>  >> }
>  >>
>  >> void tx_byte(unsigned char c);
>  >>
>  >> enum type_t t;
>  >> ...
>  >> tx_byte((unsigned char)t);
>  >>
>  >>
>  >> If the maximum value in the enumeration is lower than 256, you can cast
>  >> to (unsigned char) and transmit it as a byte.
>  >>
>  >>
>  >> However the receiver must have the same enumeration.  The transmitter
>  >> and the receiver must agreed on *the exact values* of the enumeration.
>  >>
>  >> What happens if the developer of the transmitter thinks that it is
>  >> better to swap some values during a future release?  The enumeration
>  >> isn't used only for transmission, it is also used for general logic of
>  >> the software.
>  >>
>  >> Until now I didn't found a good solution.  I only write some comments
>  >> before the enumeration declaration:
>  >>
>  >> /* WARNING!! DON'T CHANGE THE ORDER OF THE VALUES INSIDE ENUMERATION
>  >>    * They are also used in the transmission */
>  >>
>  >> I sometimes prefer explicit the value transmitted:
>  >>
>  >> if (t == TYPE_1) tx_byte(0);
>  >> if (t == TYPE_2) tx_byte(1);
>  >> ...
>  >>
>  >>
>  >> What is your strategy?
>  >
>  > Your protocol document defines what is sent on the communications 
> channel.
>  > In your code you may have:
>  >   enum type_t {
>  >      TYPE_2,
>  >      TYPE_1,
>  >      ...
>  >      TYPE_100
>  >   }
>  >
>  > But in your protocol handler you have
>  >   enum protocol_type_t {
>  >      PTYPE_1,
>  >      PTYPE_2,
>  >      ...
>  >      PTYPE_100
>  >   }
>  > switch(t) {
>  > case TYPE_1:
>  >    tx_byte(PTYPE_1);
>  >
>  > .....
>  >
>  > You convert your internal type_t to the external protocol protocol_type_t
>  > and protocol_type_t always (well should always) matches the spec.
> 
> Yes, it seems *THE* best solution in order to avoid problems.  Even if 
> it is tedious, because you have two enumerations instead of one and you 
> have a long piece of translation code that is silly.

It can suck big time but depends on how much you can automate the code
generation.  For byte values above I'd probably do some type of
lookup table with min/max value checking on "t".  Docs can be built
from the code or code from the docs - pick your tools.  It's a pain
but you try and decouple the protocol from the rest of the code.
The protocol handler is a black box, the translator from your
internal world to the external world.
As others have said, having version information is important, either
in the individual messages or in a setup/handshake message.  Transmitter
can know the receiver is v1 and take necessary actions.
Any changes to the protocol handler code receive special scrutiny
in code review and QA.




-- 
Chisolm
Republic of Texas

[toc] | [prev] | [next] | [standalone]


#28287

FromDavid Brown <david.brown@hesbynett.no>
Date2018-10-25 09:31 +0200
Message-ID<pqrrh3$d0$1@dont-email.me>
In reply to#28282
On 24/10/18 19:48, Joe Chisolm wrote:
> On Wed, 24 Oct 2018 13:18:15 +0200, pozz wrote:
> 
>> enum type_t {
>>    TYPE_1,
>>    TYPE_2,
>>    ...
>>    TYPE_100,
>> }
>>
>> void tx_byte(unsigned char c);
>>
>> enum type_t t;
>> ...
>> tx_byte((unsigned char)t);
>>
>>
>> If the maximum value in the enumeration is lower than 256, you can cast 
>> to (unsigned char) and transmit it as a byte.
>>
>>
>> However the receiver must have the same enumeration.  The transmitter 
>> and the receiver must agreed on *the exact values* of the enumeration.
>>
>> What happens if the developer of the transmitter thinks that it is 
>> better to swap some values during a future release?  The enumeration 
>> isn't used only for transmission, it is also used for general logic of 
>> the software.
>>
>> Until now I didn't found a good solution.  I only write some comments 
>> before the enumeration declaration:
>>
>> /* WARNING!! DON'T CHANGE THE ORDER OF THE VALUES INSIDE ENUMERATION
>>   * They are also used in the transmission */
>>
>> I sometimes prefer explicit the value transmitted:
>>
>> if (t == TYPE_1) tx_byte(0);
>> if (t == TYPE_2) tx_byte(1);
>> ...
>>
>>
>> What is your strategy?
> 
> Your protocol document defines what is sent on the communications channel.
> In your code you may have:
>  enum type_t {
>     TYPE_2,
>     TYPE_1,
>     ...
>     TYPE_100
>  }
> 
> But in your protocol handler you have
>  enum protocol_type_t {
>     PTYPE_1,
>     PTYPE_2,
>     ...
>     PTYPE_100
>  }
> switch(t) {
> case TYPE_1:
>   tx_byte(PTYPE_1);
>  
> ....
> 
> You convert your internal type_t to the external protocol protocol_type_t
> and protocol_type_t always (well should always) matches the spec.  If the code
> passes a type_t that does not match a protocol_type_t you have a error and about
> to violate the protocol - logs and alarms happen. It's up to the receiver to
> decide what to do if a protocol_type_t does not match a value it can deal with.
> 
> If your protocol says protocol_type_t is sent as 8 bits I think it's a good idea
> to fill out the enum with all 256 possibilities defined.
> PTYPE_100,
> SPARE_PTYPE_101,
> SPARE_PTYPE_102,
> ...
> 
> The protocol doc defines what PTYPE_1 to PTYPE_100 mean and notes 101 to max value
> are reserved for future use.
> 

I can't help thinking this is extremely inefficient, and completely
unnecessary.

In reality, in a small system you might have perhaps a dozen different
message types - "protocol types".  Make an enum for these.  Make
documentation for them (which will use the real numbers).  Have a little
static assert at the end of the enum definition to make sure you have
not missed one out by accident.  Put the enum definition, and other
protocol-related common declarations, in a single header that is shared
by both the client and server sides.  If the client programmers and the
server programmers get mixed up versions or make their own independent
changes, fire the project manager (not the programmers).

All this extra code - the endless unnecessary declarations of "spare"
values, the vast useless switches - they are nothing but magnets for
errors, typos, copy-and-paste errors, maintenance nightmares, and are
going to be completely untestable.


[toc] | [prev] | [next] | [standalone]


#28288

FromRob Gaddi <rgaddi@highlandtechnology.invalid>
Date2018-10-25 09:58 -0700
Message-ID<pqssnq$6bm$1@dont-email.me>
In reply to#28287
On 10/25/18 12:31 AM, David Brown wrote:
> On 24/10/18 19:48, Joe Chisolm wrote:
>> On Wed, 24 Oct 2018 13:18:15 +0200, pozz wrote:
>>
>>> enum type_t {
>>>     TYPE_1,
>>>     TYPE_2,
>>>     ...
>>>     TYPE_100,
>>> }
>>>
>>> void tx_byte(unsigned char c);
>>>
>>> enum type_t t;
>>> ...
>>> tx_byte((unsigned char)t);
>>>
>>>
>>> If the maximum value in the enumeration is lower than 256, you can cast
>>> to (unsigned char) and transmit it as a byte.
>>>
>>>
>>> However the receiver must have the same enumeration.  The transmitter
>>> and the receiver must agreed on *the exact values* of the enumeration.
>>>
>>> What happens if the developer of the transmitter thinks that it is
>>> better to swap some values during a future release?  The enumeration
>>> isn't used only for transmission, it is also used for general logic of
>>> the software.
>>>
>>> Until now I didn't found a good solution.  I only write some comments
>>> before the enumeration declaration:
>>>
>>> /* WARNING!! DON'T CHANGE THE ORDER OF THE VALUES INSIDE ENUMERATION
>>>    * They are also used in the transmission */
>>>
>>> I sometimes prefer explicit the value transmitted:
>>>
>>> if (t == TYPE_1) tx_byte(0);
>>> if (t == TYPE_2) tx_byte(1);
>>> ...
>>>
>>>
>>> What is your strategy?
>>
>> Your protocol document defines what is sent on the communications channel.
>> In your code you may have:
>>   enum type_t {
>>      TYPE_2,
>>      TYPE_1,
>>      ...
>>      TYPE_100
>>   }
>>
>> But in your protocol handler you have
>>   enum protocol_type_t {
>>      PTYPE_1,
>>      PTYPE_2,
>>      ...
>>      PTYPE_100
>>   }
>> switch(t) {
>> case TYPE_1:
>>    tx_byte(PTYPE_1);
>>   
>> ....
>>
>> You convert your internal type_t to the external protocol protocol_type_t
>> and protocol_type_t always (well should always) matches the spec.  If the code
>> passes a type_t that does not match a protocol_type_t you have a error and about
>> to violate the protocol - logs and alarms happen. It's up to the receiver to
>> decide what to do if a protocol_type_t does not match a value it can deal with.
>>
>> If your protocol says protocol_type_t is sent as 8 bits I think it's a good idea
>> to fill out the enum with all 256 possibilities defined.
>> PTYPE_100,
>> SPARE_PTYPE_101,
>> SPARE_PTYPE_102,
>> ...
>>
>> The protocol doc defines what PTYPE_1 to PTYPE_100 mean and notes 101 to max value
>> are reserved for future use.
>>
> 
> I can't help thinking this is extremely inefficient, and completely
> unnecessary.
> 
> In reality, in a small system you might have perhaps a dozen different
> message types - "protocol types".  Make an enum for these.  Make
> documentation for them (which will use the real numbers).  Have a little
> static assert at the end of the enum definition to make sure you have
> not missed one out by accident.  Put the enum definition, and other
> protocol-related common declarations, in a single header that is shared
> by both the client and server sides.  If the client programmers and the
> server programmers get mixed up versions or make their own independent
> changes, fire the project manager (not the programmers).
> 
> All this extra code - the endless unnecessary declarations of "spare"
> values, the vast useless switches - they are nothing but magnets for
> errors, typos, copy-and-paste errors, maintenance nightmares, and are
> going to be completely untestable.
> 

I tend to agree.  The one thing in all of what's been discussed that I'm 
all for though is a protocol version.  Either as a static part of every 
message, or as a handshake negotiation before actual information starts 
moving.  Then David's static_assert serves as a reminder that the 
protocol version needs to be rolled and the two sides at least know 
what's going on.

If you don't mind the inefficiency, I'd definitely abstract the logic out to

int *payload_to_packet(
   linkparams_t * lp,
   const payload_t * payload,
   uint8_t *buffer
)

int *packet_to_payload(
   linkparams_t * lp,
   const uint8_t * buffer,
   payload_t *buffer
)

Those functions can do nothing but a memcpy, maybe a memcpy with a value 
set or two, for as long as your protocol can stand it and only get 
ensmartened when and if it turns out necessary.  Protocol version 
checks, checksums, if you want any of that you just bury it in there.

-- 
Rob Gaddi, Highland Technology -- www.highlandtechnology.com
Email address domain is currently out of order.  See above to fix.

[toc] | [prev] | [next] | [standalone]


#28289

FromJoe Chisolm <jchisolm6@earthlink.net>
Date2018-10-25 18:38 -0500
Message-ID<H_GdncB1YOxpzk_GnZ2dnUU7-VWdnZ2d@earthlink.com>
In reply to#28287
On Thu, 25 Oct 2018 09:31:47 +0200, David Brown wrote:

> On 24/10/18 19:48, Joe Chisolm wrote:
>> On Wed, 24 Oct 2018 13:18:15 +0200, pozz wrote:
>> 
>>> enum type_t {
>>>    TYPE_1,
>>>    TYPE_2,
>>>    ...
>>>    TYPE_100,
>>> }
>>>
>>> void tx_byte(unsigned char c);
>>>
>>> enum type_t t;
>>> ...
>>> tx_byte((unsigned char)t);
>>>
>>>
>>> If the maximum value in the enumeration is lower than 256, you can cast 
>>> to (unsigned char) and transmit it as a byte.
>>>
>>>
>>> However the receiver must have the same enumeration.  The transmitter 
>>> and the receiver must agreed on *the exact values* of the enumeration.
>>>
>>> What happens if the developer of the transmitter thinks that it is 
>>> better to swap some values during a future release?  The enumeration 
>>> isn't used only for transmission, it is also used for general logic of 
>>> the software.
>>>
>>> Until now I didn't found a good solution.  I only write some comments 
>>> before the enumeration declaration:
>>>
>>> /* WARNING!! DON'T CHANGE THE ORDER OF THE VALUES INSIDE ENUMERATION
>>>   * They are also used in the transmission */
>>>
>>> I sometimes prefer explicit the value transmitted:
>>>
>>> if (t == TYPE_1) tx_byte(0);
>>> if (t == TYPE_2) tx_byte(1);
>>> ...
>>>
>>>
>>> What is your strategy?
>> 
>> Your protocol document defines what is sent on the communications channel.
>> In your code you may have:
>>  enum type_t {
>>     TYPE_2,
>>     TYPE_1,
>>     ...
>>     TYPE_100
>>  }
>> 
>> But in your protocol handler you have
>>  enum protocol_type_t {
>>     PTYPE_1,
>>     PTYPE_2,
>>     ...
>>     PTYPE_100
>>  }
>> switch(t) {
>> case TYPE_1:
>>   tx_byte(PTYPE_1);
>>  
>> ....
>> 
>> You convert your internal type_t to the external protocol protocol_type_t
>> and protocol_type_t always (well should always) matches the spec.  If the code
>> passes a type_t that does not match a protocol_type_t you have a error and about
>> to violate the protocol - logs and alarms happen. It's up to the receiver to
>> decide what to do if a protocol_type_t does not match a value it can deal with.
>> 
>> If your protocol says protocol_type_t is sent as 8 bits I think it's a good idea
>> to fill out the enum with all 256 possibilities defined.
>> PTYPE_100,
>> SPARE_PTYPE_101,
>> SPARE_PTYPE_102,
>> ...
>> 
>> The protocol doc defines what PTYPE_1 to PTYPE_100 mean and notes 101 to max value
>> are reserved for future use.
>> 
> 
> I can't help thinking this is extremely inefficient, and completely
> unnecessary.
> 


switch(t) {
case TYPE_1 ... TYPE_100:
	out=table[t];  // table being defined for values of t in the range
	break;
case TYPE_101 ... TYPE_256:
error_log();
break;
default:
worse_error_log();
break;
}

Even having

case TYPE_1:
blah
case TYPE_2:
blah
...
case TYPE_101:
case TYPE_102:
case TYPE_103:
error_log();
break;
default:
worse_error_log();
break;
}
a good compiler is going to roll all that together.

> In reality, in a small system you might have perhaps a dozen different
> message types - "protocol types".  Make an enum for these.  Make
> documentation for them (which will use the real numbers).  Have a little
> static assert at the end of the enum definition to make sure you have
> not missed one out by accident.  Put the enum definition, and other
> protocol-related common declarations, in a single header that is shared
> by both the client and server sides.  If the client programmers and the
> server programmers get mixed up versions or make their own independent
> changes, fire the project manager (not the programmers).
> 

And eventually this gets shipped and you have a customer with version 1
box talking to a version 2 box talking to a version 3 box because they
dont want to update all the boxes at the same time.  The protocol
handler is a black box that you can unit test with a reasonably robust
test harness.  That test suite validates the protocol and gives you a
known base.  Transmitter test vectors are compared against receiver test
vectors.  Once built these test suites only have to be changed if the
protocol changes but get used in verification for each release. If it
is duplex you have a->b vectors and b->a vectors.

> All this extra code - the endless unnecessary declarations of "spare"
> values, the vast useless switches - they are nothing but magnets for
> errors, typos, copy-and-paste errors, maintenance nightmares, and are
> going to be completely untestable.

That's because you live in the old world of actually typing all this
stuff in.  You have abundant tools at your disposal to create this
code.   You have a  input file of some type, a state diagram or other
"document" for the protocol and your tools auto create the code from that.
The make file auto generates the headers and code.  Hell, even lowly
awk can generate code for you.  20 years ago we were using simple
tools to take register definition files and spit out VHDL and C 
headers and functions. We did the same thing with a CAN bus protocol.
This is robot work, make the computer do it.

-- 
Chisolm
Republic of Texas

[toc] | [prev] | [next] | [standalone]


#28290

FromDavid Brown <david.brown@hesbynett.no>
Date2018-10-26 09:24 +0200
Message-ID<pquff6$cmo$1@dont-email.me>
In reply to#28289
On 26/10/18 01:38, Joe Chisolm wrote:
> On Thu, 25 Oct 2018 09:31:47 +0200, David Brown wrote:
> 
<snip>
>>
>> I can't help thinking this is extremely inefficient, and completely
>> unnecessary.
>>
> 
> 
> switch(t) {
> case TYPE_1 ... TYPE_100:
> 	out=table[t];  // table being defined for values of t in the range
> 	break;
> case TYPE_101 ... TYPE_256:
> error_log();
> break;
> default:
> worse_error_log();
> break;
> }
> 
> Even having
> 
> case TYPE_1:
> blah
> case TYPE_2:
> blah
> ...
> case TYPE_101:
> case TYPE_102:
> case TYPE_103:
> error_log();
> break;
> default:
> worse_error_log();
> break;
> }
> a good compiler is going to roll all that together.

I have several points here:

First, my biggest concern about efficiency is /developer/ efficiency.
Writing endless realms (the second version) of unnecessary "translation"
code is a big waste of time and effort - and a very easy source of bugs
and maintenance problems, especially copy-and-paste errors.  Even with
the case range version, you still have the table to define.

Second, there is device efficiency.  Not everyone has good compilers -
and not everyone with good compilers is able to use them correctly.
Sure, many of use use mainly gcc with its useful extensions and solid
optimisations.  But others have to use weaker tools, or (for good
reasons or bad reasons) stick to restricted subsets of the language, or
use cpu cores for which there really is no efficient way to handle such
code.  And a depressingly large proportion of embedded developers work
with little or no optimisation because "optimisation breaks my code"
(i.e., they don't properly understand the language and how to code it).

Third, you are adding an extra layer for developer confusion.  Instead
of "message types" that everyone can agree on, you now have "message
types" and "message implementation types" which are different and
confusing.  Imagine the conversation between the server programmer and
client programmer trying to figure out a problem:

"There's a problem with message 3."
"Is that /your/ message type 3 or /my/ message type 3?"
"I don't know, maybe it is protocol message type 3?  I just got an error
message saying there was a bad length for message type 3."
"When I looked in the debug log, it said type 7."
"But the value shown in my debugger was type 73 when the breakpoint hit."
"There is no message type 73 - something has gone badly wrong.  No,
wait, did you mean /your/ message type 73 rather than /my/ message type 73?"


You are trying to over-engineer a very simple situation.  Write the
message definitions /once/, simply and consistently.  Don't write
translation layers unless you actually need to translate things.

> 
>> In reality, in a small system you might have perhaps a dozen different
>> message types - "protocol types".  Make an enum for these.  Make
>> documentation for them (which will use the real numbers).  Have a little
>> static assert at the end of the enum definition to make sure you have
>> not missed one out by accident.  Put the enum definition, and other
>> protocol-related common declarations, in a single header that is shared
>> by both the client and server sides.  If the client programmers and the
>> server programmers get mixed up versions or make their own independent
>> changes, fire the project manager (not the programmers).
>>
> 
> And eventually this gets shipped and you have a customer with version 1
> box talking to a version 2 box talking to a version 3 box because they
> dont want to update all the boxes at the same time.  

I said in an early post in this thread that you want to add protocol
version numbering, and several others have confirmed that as a good
idea.  Message number 1 in any protocol should be a handshake or
"helo/ehlo" to establish compatibility of protocol versions, software
versions, etc.


> The protocol
> handler is a black box that you can unit test with a reasonably robust
> test harness.  That test suite validates the protocol and gives you a
> known base.  Transmitter test vectors are compared against receiver test
> vectors.  Once built these test suites only have to be changed if the
> protocol changes but get used in verification for each release. If it
> is duplex you have a->b vectors and b->a vectors.
> 
>> All this extra code - the endless unnecessary declarations of "spare"
>> values, the vast useless switches - they are nothing but magnets for
>> errors, typos, copy-and-paste errors, maintenance nightmares, and are
>> going to be completely untestable.
> 
> That's because you live in the old world of actually typing all this
> stuff in.  You have abundant tools at your disposal to create this
> code.   You have a  input file of some type, a state diagram or other
> "document" for the protocol and your tools auto create the code from that.
> The make file auto generates the headers and code.  Hell, even lowly
> awk can generate code for you.  20 years ago we were using simple
> tools to take register definition files and spit out VHDL and C 
> headers and functions. We did the same thing with a CAN bus protocol.
> This is robot work, make the computer do it.
> 

I can automate code generation fine - I do so when it is useful.  I
don't do it when it is unnecessary, and I don't use it as an excuse to
add extra layers in the design instead of getting /one/ layer correct.

In an ideal world, you may be defining your protocols using some
high-level tool that automatically generates your code.  Very few
embedded programmers live in ideal worlds - many are going to do this by
hand.  And many more are going be handed existing code that came from
somewhere else - perhaps another developer who left the company years
ago - and be asked to "just make this simple fix" or "just add this one
extra message type".  Imagine you no longer have a copy of the original
state diagram files, or the software for generating the code.  Or
imagine that you /do/ have these, but somewhere between the last run of
the tools and the current state of the code, someone manually changed
the code because it was faster and easier than going through all that
"UML nonsense".  This is reality for many people.

So do not make things more complicated than they have to be.  By all
means make layers and black boxes when they make sense, but don't add
them "just in case".

[toc] | [prev] | [next] | [standalone]


#28283

FromGeorge Neuner <gneuner2@comcast.net>
Date2018-10-24 15:19 -0400
Message-ID<4eg1tdhuprafpccpdg6g5upru0qhflg54v@4ax.com>
In reply to#28274
On Wed, 24 Oct 2018 13:18:15 +0200, pozz <pozzugno@gmail.com> wrote:

>enum type_t {
>   TYPE_1,
>   TYPE_2,
>   ...
>   TYPE_100,
>}
>
>void tx_byte(unsigned char c);
>
>enum type_t t;
>...
>tx_byte((unsigned char)t);
>
>
>If the maximum value in the enumeration is lower than 256, you can cast 
>to (unsigned char) and transmit it as a byte.

And if not you can agree to use a "network" interchange format.  IP
specifies big-endian byte order for binary integers, and socket
libraries provide the functions: 
   htons - host to network short
   htonl - host to network long
   ntohs - network to host short
   ntohl - network to host long
for conversions between host and network formats.  If the host is
big-endian, these functions are no-ops.

[Also sometimes htonll and ntohll (longlong for 64-bit data.]


>However the receiver must have the same enumeration.  The transmitter 
>and the receiver must agreed on *the exact values* of the enumeration.
>
>What happens if the developer of the transmitter thinks that it is 
>better to swap some values during a future release?  The enumeration 
>isn't used only for transmission, it is also used for general logic of 
>the software.

If this really is an issue, use indexed tables instead of an
enumerations and make exchange/distribution of the tables part of your
protocol.

YMMV,
George

[toc] | [prev] | [next] | [standalone]


#28291

FromLes Cargill <lcargill99@comcast.com>
Date2018-10-26 20:30 -0500
Message-ID<pr0f3r$7bb$1@dont-email.me>
In reply to#28274
pozz wrote:
> enum type_t {
>    TYPE_1,
>    TYPE_2,
>    ...
>    TYPE_100,
> }
> 
> void tx_byte(unsigned char c);
> 
> enum type_t t;
> ...
> tx_byte((unsigned char)t);
> 
> 
> If the maximum value in the enumeration is lower than 256, you can cast 
> to (unsigned char) and transmit it as a byte.
> 
> 
> However the receiver must have the same enumeration.  The transmitter 
> and the receiver must agreed on *the exact values* of the enumeration.
> 
> What happens if the developer of the transmitter thinks that it is 
> better to swap some values during a future release?  The enumeration 
> isn't used only for transmission, it is also used for general logic of 
> the software.
> 
> Until now I didn't found a good solution.  I only write some comments 
> before the enumeration declaration:
> 
> /* WARNING!! DON'T CHANGE THE ORDER OF THE VALUES INSIDE ENUMERATION
>   * They are also used in the transmission */
> 
> I sometimes prefer explicit the value transmitted:
> 
> if (t == TYPE_1) tx_byte(0);
> if (t == TYPE_2) tx_byte(1);
> ...
> 
> 
> What is your strategy?


char *type_t_strings[]  = {
"TYPE_1",
"TYPE_2",
...
"TYPE_100",
};

--
Les Cargill

[toc] | [prev] | [standalone]


Back to top | Article view | comp.arch.embedded


csiph-web