Jump to content
The simFlight Network Forums

Recommended Posts

Posted

Hello,

I've finally started work on the software for my simpit. I'm building the hardware entirely from scratch. No EPIC or the like, I can't afford it.

Right now I want to have the FS2004 computer talk to another computer over the network, and share data with it.

I can't use WideFS, since this other computer isn't running Windows.

I know barely enough about TCP/IP programming to get both computers to happily swap strings of text across the network, now, I need to get them to do something useful with them.

This is what I need help on. This network takes a request that has come over the network in the form of an array of chars, turns them into DWORDs so it can feed those values into the FSUIPC_Read() function. I'm not really sure how else to do this, and I can't really test what I have yet since I forgot to actually buy a copy of FSUIPC, which I'll try to get around to tonight.

readOffset(char *buffer, DWORD *rerr)
{
	DWORD fsOffset = 0;
	DWORD fsSize = 0;
	if(buffer [0] == 0xF0)
	{
		fsOffset = buffer [1];
		fsOffset << 4;
		fsOffset | buffer [2];
		fsOffset << 4;
		fsOffset | buffer [3];
		fsOffset << 4;
		fsOffset | buffer [4];

		fsSize = buffer [5];
		fsSize << 4;
		fsSize | buffer [6];
		fsSize << 4;
		fsSize | buffer [7];
		fsSize << 4;
		fsSize | buffer [8];
		char *x = new char [fsSize];
		FSUIPC_Read(fsOffset, fsSize, x, rerr);
		if(!FSUIPC_Process(rerr)) return 0;
		buffer = x;
	}
	return 1;
}

Would this even work? Is there a better way to do this?

Any help or advice would be deeply appreciated. I'm pretty tired, so forgive me if I didn't explain this very well.

Posted

This network takes a request that has come over the network in the form of an array of chars, turns them into DWORDs ...

Whilst there's nothing wrong with "converting" data from arrays of bytes into DWORDs or vice versa, it is not usually necessary. Why not just send them as DWORDs in the first place?

if(buffer [0] == 0xF0)

I assume this is some sort of command header --for FSUIPC_Read?

fsOffset = buffer [1];

fsOffset << 4;

fsOffset | buffer [2];

fsOffset << 4;

fsOffset | buffer [3];

fsOffset << 4;

fsOffset | buffer [4];

Is your other, non-Windows, PC using a 68000 processor or some other one which stores numbers with the high byte first (HI-LO) instead of last, like Intel (LO-HI)?

If not, why not simply send the data as a sequence, or structure:

BYTE command; // 0xF0 for Read?

DWORD offset;

DWORD size;

Even if you can't do that in the other PC, if the order is LO-HI (not HI-LO as implied in your code) you can refer to the 4 bytes at buffer[1] as a DWORD simply by casting, thus:

*((DWORD *) &buffer[1])

This is the actual offset value, assuming of course that the offset is in standard Intel Lo-Hi order. (Otherwise you do, indeed, have to reverse the bytes in the way you are doing).

Same for the size of course.

FSUIPC_Read(fsOffset, fsSize, x, rerr);

if(!FSUIPC_Process(rerr)) return 0;

buffer = x;

You are reading the data into a local array (x) then copying into this "buffer". Two questions. One, why not read directly into the buffer? Two, how do you know it (the buffer) is big enough?

I think, for safety in case of data corruption somewhere, you ought to put a cap on the maximum size in any case. FSUIPC and its library certainly has a limit (about 30000) but that would be far bigger that you are ever likely to need in any case.

Anyway, the above are only comments and suggestions on the programming, not the interface to FSUIPC, which looks okay.

However, this is only for one read at one offset. Assuming you'd normally want to read more than one thing at a time, you really need to separate the FSUIPC_Process call. You'd need to loop around doing FSUIPC_Reads for each offset/size pair, THEN do one Process call. This is even more reason why you would need to have the Read executed to read back into the Buffer space allocated -- otherwise you'd not be able to sort out which read data was from which offset.

In other words, if the block coming from your remote PC was structured:

(header)

offset1

size1

array of size1 bytes for result

offset2

size2

array of size2 bytes for result

...

(trailer)

then instead of your 'x' to read the data in, the address used would to the place in this buffer of the relevant result area.

After the Process you'd then ship the entire block back to the remote PC.

This is more or less how WideFS started out, six or seven years ago. For the last few years, however, it has been streamlined considerably -- it doesn't need the data space in the incoming blocks and doesn't "Process" for every request. It simply adds the requested offsets/sizes to a list for that Client, then another part (in another thread) monitors those offsets in the FS PC and sends them when the values change. If they don't change, it doesn't send them (of course the first request is considered a "change"). That is, unless the remote PC gets disconnected, then they are all considered changed. WideClient, in the remote PC, maintains a "carbon copy" of the offset area and stores the changes being sent for any local applications which read them.

Sorry, I've got carried away a bit. I hope this helps rather than confuses!

Regards,

Pete

Posted

Oh, it's definitely helpful.

The other PC is an AMD XP 2500 running FreeBSD 5.3. So no problems there.

The windows "send" function wants an array of chars for its data, could I:

struct this_data
{
DWORD offset;
DWORD size;
char data [WHATEVER_SIZE_WORKS_BEST]
};
and then send it like
send(socket, (char) this_data

and so on...?

Posted

send(socket, (char) this_data

[/code] and so on...?

Nearly, but not quite. the cast is not (char) but (char *) -- i.e. you are providing a pointer to an array of characters, not a literal single character.

As far as any byte-oriented procedures are concerned, the fact that your data is actually some complex mixture of bytes, WORDs, DWORDs, or all sorts of other types and structs is not relevant. To such procedures (and the FSUIPC ones are the same), data is data is data -- it isn't interpreting what it means, so it can be a string of bytes or anything you like.

The value being passed is simply a pointer (i.e. memory address) to the first one. The size is always in bytes -- the value (address + size - 1) points to the last byte. Whatever the data is doesn't matter.

Just be more careful when using casts like this, because you are effectively telling the compiler "I know best -- treat this value like this", so you don't get the value checked. For example, if your value "this_data" was not declared as an array but simply a DWORD:

DWORD this_data;

then the cast "(char *) this_data" would take the contents of this_data, not the address of it. You'd need to put "(char *) &this_data" to take the address of this_data (i.e. make a pointer to it).

This is why, in my programming, I prefer to stick to the rule of naming variables with a prefix showing their type. e.g.

"dwThisData" for the DWORD above,

"chThisData" for a character

I precede things with a 'p' if they are pointers, thus:

DWORD *pdwThisData = &dwThisData;

and so on.

With arrays, like

char chThisData[nSize];

The value "chThisData" is actually a pointer. However, I still tend to take the address of the first one when I want it to be very clear that it is a pointer, so:

"&chThisData[0]"

This is actually the same as "chThisData" but it is now completely obvious it is a pointer to the first element, and not an actual single character value.

Regards,

Pete

Posted
error C2440: 'type cast' : cannot convert from 'struct fsData' to 'char *'

No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called

When I try this. What am I doing wrong?

Posted
error C2440: 'type cast' : cannot convert from 'struct fsData' to 'char *'

No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called

When I try this. What am I doing wrong?

Ah, sorry, that's probably my fault -- I didn't read your original well eoungh. Structs aren't the same as Arrays. You'll need to explicitly provide the pointer to it.

i.e. not "(char *) fsData" but "(char *) &fsData".

The "&" prefix means "address of" -- i.e. it makes a pointer to it, and you can cast pointers to one type into pointers of any other type.

Pete

Posted

I re-wrote the whole thing, since it was becoming pretty hard for me to follow. This is the new function for handling read/write requests:

fsInterface::fsStart()
{
	fsData dat;
	char * chData;
	int x = 0;

	for( ; ; )
	{
		x = recv(*fsSock, (char *) &amp;dat, sizeof(fsData), 0);
		if(x = 0)
		{
			printf("Connection closed\n");
			return 0;
		}
		if(x = SOCKET_ERROR)
		{
			printf("Error %i while receiving data\n", WSAGetLastError());
			return 0;
		}

		if (dat.header == FS_READ)
		{
			chData = new char [dat.dwSize];
			if(!FSUIPC_Read(dat.dwOffset, dat.dwSize, chData, &amp;err))
			{
				printf("Error %i while reading from FSUIPC\n", err);
				return 0;
			}

			if(!FSUIPC_Process(&amp;err))
			{
				printf("Error %i while calling FSUIPC_Process\n", err);
				return 0;
			}

			if(send(*fsSock, (char *) &amp;dat, sizeof(fsData), 0)== SOCKET_ERROR)
			{
				printf("Error %i while sending data\n", WSAGetLastError());
				return 0;
			}

			if(send(*fsSock, chData, dat.dwSize, 0) == SOCKET_ERROR)
			{
				printf("Error %i while sending data\n", WSAGetLastError());
			}
		}




		if (dat.header == FS_WRITE)
		{
			chData = new char [dat.dwSize];
			x = recv(*fsSock, chData, dat.dwSize, 0);

			if(x = 0)
			{
				printf("Connection closed\n");
				return 0;
			}
			if(x = SOCKET_ERROR)
			{
				printf("Error %i while receiving data\n", WSAGetLastError());
				return 0;
			}

			if(!FSUIPC_Write(dat.dwOffset, dat.dwSize, chData, &amp;err))
			{
				printf("Error %i while writing to FSUIPC\n", err);
				return 0;
			}
			if(!FSUIPC_Process(&amp;err))
			{
				printf("Error %i while calling FSUIPC_Process\n", err);
				return 0;
			}
		}
	}

}

At least I know the whole thing will build, and the TCP/IP code I wrote works. Still can't test it without a registration key, I'll try to get around to taking care of that tomorrow.

Posted
I re-wrote the whole thing, since it was becoming pretty hard for me to follow. This is the new function for handling read/write requests

You are still performing a separate FSUIPC_Process for each individual Read and Write. If you are only interested in a couple of things this may not be too bad, but otherwise it will be terribly inefficient for the FS machine and probably painfully slow for whatever you are running on the remote PC.

Each time you do an FSUIPC_Process you are actually causing a process switch into FS. Your program is then suspended waiting for the response. Each time the response is ready, you are generating a process switch out of FS. So both processes are suffering. This is all okay when they more or less coincide with a normal sort of timeslice in any case, but if you are reading, say 20 or 30 different items of information in one exchange on the Network, you are orcing everything to run too slowly. The 20 or 30 requests could build up to 2 or 3 seconds whereas your Network turn-round should be only 30-100 mSecs normally.

The whole reason the FSUIPC_Process call is separated from the Read and Write calls is to allow a batch operation. The FSUIPC_Read and FSUIPC_Write calls do nothing but build up a data block containing all the requests, along with the space for the replies. They are all internal to your program and very very fast.

Try to remove the Process calls and do one Process call per block of requests from the remote.

Really, you'd be far better off formatting the blocks on your Network connection for FSUIPC_Process directly, and never use FSUIPC_Read and FSUIPC_Write in the FS PC. This should be easy enough for you to work out -- the source of the FSUIPC library you are using is provided. Take a look at what the Read and Write calls are doing, and do the same in the remote PC. Send the resulting block to the FS PC and use that block as it stands in an FSUIPC_Process call.

Regards,

Pete

Posted

Hmm. What if I have the second computer control the calls to FSUIPC_Process and have a second thread running in that program that'll tell it to process everything at regular intervals?

Posted
Hmm. What if I have the second computer control the calls to FSUIPC_Process and have a second thread running in that program that'll tell it to process everything at regular intervals?

Sorry, I don't understand what you are saying there.

You need to batch reads/writes in the remote PC in any case. You surely don't want the overhead of the Ethernet protocols added to every single individual read and write?

All I was suggesting is that your batch of reads and writes, arriving at the FS PC, are formatted for one FSUIPC_Process call, and the result forms the return block to the remote. How you convert from your block format to that needed by FSUIPC and back again is completely up to you -- you can use FSUIPC_Read and FSUIPC_Write, or you can make it more efficient and do the original blocks in an FSUIPC-compatible format in the first place.

If you are, indeed, only sending one read or one write per block from the remote, then your code extract is probably fine. You won't be able to achieve an FSUIPC_Process frequency of more than about 20 per second (50 mSecs intervals) on average in any case, which won't have any noticeable impact on FS. It will have an impact on the program running in the Remore PC though, unless that has a very low data requirement.

Regards

Pete

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use. Guidelines Privacy Policy We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.