Im trying to send a command to a dev. board and then receive a reply
E.G
(me) set attrib=yes
(Dev. Board) O.K
or
(Dev. Board) E.R.R
But it doesn't bounce back anything ... not an O.K or an E.R.R
while the board is booting echo is on .. so if I send the commands while the board is booting it
it will bounce back an 'set attrib=yes' and once booted an 'E.R.R' because you cant send commands while booting.
my best guess is that it isn't reading the reply in time or trying to read it too soon.
procedure TReaderProgrammer.Button2Click(Sender: TObject);
Var
Buffer: AnsiString;
RxData: string;
Count : integer;
begin
if ComPort1.Connected then
begin
ComPort1.WriteStr(edtSendText.Text+#13);
comport1.ReadStr(RxData,Count);
Buffer := Buffer + Rxdata;
memoRxData.Text := memoRxData.Text+Buffer+#13+#10;
end;
end;
Here are several open questions in the air, so I have to make some assumptions that might be wrong, but let's see.
I don't know what comm port library you are using, so I'm assuming it is the CPort library from SourceForge. I have never used it myself, but I have read that it is made Unicode aware, such that you can call the write methods with a unicodestring parameter which will be converted by the library to ansistring before sending. Similarily when receiving ansistring from the outer world, the library will convert to unicodestring for the Read methods.
Due to the asynchronous nature of serial communication, it is important to understand that when you send something using the write method, the method returns immediately while the library and OS spit out the characters one at at time at a pace defined by the baud rate. As a result your first code never received anything, because you were already attempting to read from the comm port before the external device even received the first character.
It is good to see that you have now taken the first step to success by implementing an event handler for (presumably library event) OnRxChar.
The OnRxChar probably fires for each character (or couple of characters). You need to have a buffer that is persistent between these events. A local var (as you have it now and which is allocated on the stack) in the event handler is not persistent, it is lost every time the event handler exits.
You should declare the Buffer variable as a field of TReaderProgrammer. I don't know why you defined the buffer to be AnsiString, but I suggest you try with string (ref discussion above regarding Unicode awareness).
type
TReaderProgrammer = class
..
RxBuffer: string;
..
end;
The buffer needs to be cleared when you send a new command to the external device in order for it to be ready to receive new data as a response to your command.
EDIT: Alternatively you can clear the RxBuffer immediately when you have received and processed a full response.
The TReaderProgrammer.ComPort1RxChar should look like this sofar:
procedure TReaderProgrammer.ComPort1RxChar(Sender: TObject; Count: Integer);
var
RxData : string;
begin
(Sender as TComPort).ReadStr(RxData,Count);
RxBuffer := RxBuffer + Rxdata;
...
end;
The rest of the event handler is, I guess, probably just to see progress of reception, so nothing more about that.
Related
i need to process a received data from a tcp link.the data are frames of hex string at length of 203 bytes.
i save them at the end of tstringlist
MyList.Add( input );
and from a second thread read the first string and process it and remove firs from the list
procedure TMyThread.Execute;
var str : string;
begin
while not Terminated do
begin
FTermEvent.WaitFor(100);
if not Terminated then
begin
str := MyList[0];
MyList.Delete(0);
//some process
end;
end
end;
The question is , is this thread safe?!
If you are afraid to loose input data using database, you can try to use TThreadStringList.
I imagine that your software receives data from multiple devices simultaneously (and in this case you should create a multi-thread socked to make you sure to receive all the data) if you are receiving data from a single device instead , you should make sure that the tcp protocol supports a sort of aknowlage system to avoid losing data or at least to report in a log, the data that your application has not been able to receive completely.
Anyway, TThreadStringList is a simple wrapper for TStringList.
It should makes possible to access a String List from different threads without any conflicts.
I can't test it but for you should be easy and quick to try.
The link:
TThreadStringList
I have a Delphi program which sits as a server receiving data and sending back small acknowledgement packets. I previously used this code (edited for brevity)
procedure TdmServer.OnExecuteTCPServer(AThread: TIdPeerThread);
var
IncomingPacket : string;
ResponsePacket : string;
begin
IncomingPacket := AThread.Connection.Readln(#$03);
if IncomingPacket <> '' then
begin
ResponsePacket := ProcessTelegram(IncomingPacket);
AThread.Connection.writeln(ResponsePacket);
end;
AThread.Connection.Disconnect;
end;
This almost works fine, apart from appending an end of line CRLF as it sends, which the client (not under my control) doesn't like.
So I changed it to:
AThread.Connection.Write(ResponsePacket);
and nothing is received on the client.
Then I tried
AThread.Connection.WriteBuffer(ResponsePacket, length(ResponsePacket), true);
to try and get it to write immediately, but it still doesn't send at all.
I have put in delays, tried opening the buffer, flushing and closing it again (like the help file), but still no joy and any time FlushWriteBuffer is called I get an AV.
I'm stuck. Please could anyone offer any words of wisdom?
ReadLn() strips off the terminator from its Result. Is your ProcessTelegram() accounting for that? If it needs the terminator, you will have to add it back in manually, eg:
ProcessTelegram(IncomingPacket + #03)
Is ResponsePacket being formatted correctly for what the client is expecting? If the client is not expecting a terminating CRLF, then using Write() instead of WriteLn() is the correct thing to do.
If you use WriteBuffer(), you must dereference a String in order to pass the correct data address to WriteBuffer(), eg:
WriteBuffer(ResponsePacket[1], Length(ResponsePacket), True)
Or:
WriteBuffer(PChar(ResponsePacket)^, Length(ResponsePacket), True)
If the client is still not receiving the response correctly, then either you are not sending anything at all, or you are not sending the terminator that the client is expecting. Use a packet sniffer, such as Wireshark, to see what the client is actually receiving, if anything.
I'm working on a small component for writing and reading AT Commands using an old Sony Ericsson phone.
Sending and writing to/from the phone is no problem at all, however I would like to be able to pause my SendATCmd function and wait for the COM Port component to notify me with a Notification Event, and then resume the SendATCmd function again.
Scenario: I want to get the count of SMS messages in the phone.
Normally I'd just tell the phone: Hey, how many SMS messages do you have?
and the phone would reply in the notification event.
Thats all good.
But what I really want to do is something like
if SendATCmd('CountSMS')>0 then
for 0 to SMSCount do
AddSMSToList;
The code for SendATCmd looks like this:
function TSE_Z1010.SendATCmd(Cmd: string): TATResult;
begin
fCOMPort.PutString(Cmd); //Sending AT command
//Here is where I would like to pause this function
//wait for the fCOMPort to notify me when data is available
//and then resume this function again.
result:=fTMPATResult;
end;
I've tried using a while-loop, pause, etc etc, but nothing's worked except for one thing, and that's when I put a ShowMessage where the pause should be.
I don't know how ShowMessage works internally but it seems that it doesn't halt the program like while-loop and pause do.
====================
Fixed it.
All I had to do was to add Forms in the uses clause, and then I added while fTMPATResult.Full=false do Application.ProcessMessages; in the part where I wanted to pause the procedure.
"fTMPATResult" is the variable where the incoming COM Port data is stored, globally within the component.
While AsyncPro does have some solutions for this (ontriggerdata), they are event based and make code difficult to read/understand.
here is SendAndWaitForResponse with AsyncPro (like Remy suggested):
TForm1 = class(TForm)
...
private
IOEvent : THandle; // used for IO events
IORx : string;
Comport : TapdComport;
...
procedure TForm1.ComportTriggerAvail(CP: TObject; Count: Word);
var i : integer;
begin
for i:=1 to Count do
IORx:=IORx+Comport.GetChar;
SetEvent(IOEvent);
end;
function TForm1.SerialSAWR(tx : string; TimeOut : integer) : boolean;
begin
Result := False;
try
IORx := ''; // your global var
ResetEvent(IOEvent);
Comport.PutString(tx);
Result := WaitForSingleObject(IOEvent, TimeOut) = WAIT_OBJECT_0;
except
on E : Exception do
// dosomething with exception
end;
end;
// constructor part
IOEvent := CreateEvent(nil, True, False, nil);
// destructor part
if IOEvent <> 0 then
CloseHandle(IOEvent);
Best solution is to create a thread with the comport so your GUI won't be blocked.
I have several applications in production with Asyncpro this way and it works like a charm...
Any time you need to call Application.ProcessMessages() manually, you need to rethink your code design. Doubly so when calling it in a loop.
I do not know how Asynch Pro works, but the Win32 API has a WaitCommEvent() function that does what you are asking for. You call that function to ask the serial port for notification of the desired event(s) and then you can use either WaitForOverlappedResult() or WaitForSingleObject() to wait for those events to actually occur, depending on whether the serial port is operating in overlapped mode or not. No message processing is needed. I would be surprised if Asynch Pro does not somehow expose that functionality.
I'm again in a situation where I've spend an obscene amount of time on trying to customize datasnap callback samples to my needs.
I'm old school OOP programmer and have several very large Object hierakies in my "toolbox" PODO style :-) .. and having this great datasnap feature, I want to utilize the forces of the callback.
BUT - when I implement it ... it simply fails ... (FASTMM4 reports mem leaks).
Try and create a simple VCL datasnap server - TCP.
And add a button and this source ...
procedure TForm1.Button1Click(Sender: TObject);
var
// AObject : TObject;
aJSONVal : TJSONValue;
begin
// AObject := TObject.Create;
// ServerContainer1.DSServer1.BroadcastObject('SomeChannel','SomeCallbackID', AObject);
// AObject.Free;
aJSONVal := TJSONObject.Create;
ServerContainer1.DSServer1.BroadcastMessage('SomeChannel','SomeCallbackID',aJSONVal);
// aJSONVal.Free; // Mat pointed out that this is done by the broadcast.
end;
It will work - as long as you keep using TJSONValue ...
But try and switch the commented code - and you will see what I mean.
I could of course change all my existing code to JSON ... but that is simply not acceptable.
Does anyone have any idea on how to use the BroadcastOBJECT or NotifyOBJECT ?
Regards
Bjarne
The object which you give to a Notify or Broadcast call is then owned by that call. Therefore do not call "AObject.Free;" or "aJSONVal.Free;". Doing so will often result in an Access Violation or other memory management related problems.
Note also that Broadcasted messages get put in a queue on the server and are later sent, in a different thread. Meaning, when your call to Broadcast returns, it hasn't actually sent the message to all the clients yet.
I hope that helps,
Mat
Possible answer: Your question was vague but based on what you've said, I'd start here:
Delphi XE help: (ms-help://embarcadero.rs_xe/vcl/DSServer.TDSServer.BroadcastObject.html): function BroadcastObject(const ChannelName: String; const CallbackId: String; const Msg: TObject): boolean; overload;
The second overload sends an object to all client callbacks with a given registered callback identifier. For this purpose, an additional CallbackId parameter is required in the call."
You are using the second overload which takes 3 params - are your callback identifiers set up properly?
i am trying to send a picture from 'C:\picture.bmp' to 'c:\temp\picture.bmp' using server and client socket
clients onconnect event handler is as follow:
procedure TForm2.ClientSocket1Connect(Sender: TObject;
Socket: TCustomWinSocket);
var
fs : tfilestream;
begin
fs := TFileStream.create('C:\picture.bmp', fmOpenRead);//picture allready exists
socket.SendStream(fs);
fs.free;
end;
and servers onclientread as :
procedure TForm2.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
fmm : tfilestream;
iLen: Integer;
Bfr: Pointer;
begin
iLen := Socket.ReceiveLength;
GetMem(Bfr, iLen);
fmm := TFileStream.Create('c:\temp\picture.bmp', fmCreate or
fmShareDenyWrite);
try
Socket.ReceiveBuf(Bfr^, iLen);
fmm.Write(Bfr^, iLen);
finally
FreeMem(Bfr);
fmm.Free;
end;
end;
picture is recieved/created but is either corrupt on was never recieved i.e created because of tfilestream.create method?
please help!what am i doing wrong?
Despite its name, SendStream() is NOT guaranteed to send the entire stream (especially if you are using a non-blocking socket). Its return value returns how many bytes are actually sent. If less than the full size of the stream are sent in one call, you have to call SendStream() again, potentially many times, to finish sending the entire stream (the same problems exists with SendText() as well).
On the other side, ReceiveLength() only reports how many bytes are available on the socket AT THAT MOMENT. That is likely to be less than the full stream being sent (likewise, ReceiveText() may not receive a full sent string either because it uses ReceiveLength() internally).
The best way to send a stream (or any arbitrary data in general) is to send the data's size first, then send the actual data afterwards. Keep calling SendBuf/Stream/Text() until that size is reached (if -1 is returned by a non-blocking socket without raising an exception, you have to wait for the socket's OnWrite event to trigger before the socket can accept more data again). On the receiving end, read the size first, then keep reading until the specified size is reached. You may have to read in multiple triggering of the OnRead event before you get all of the data.
Go to http://www.deja.com and http://forums.embarcadero.com to search the Borland/CodeGear/Embarcadero newsgroup/forum archives. I have posted example code many times before.
I don't know what's wrong, but I'd try troubleshooting a simpler problem. i.e. can you even transfer somethign simple? See if you can transfer c:\hello.txt containing just "Hello" and have it arrive in the right order. It should be easier to examine the stream and resulting file, to see if/where things are getting garbled. If you don't receive "Hello" on the server, then you know it's got nothing to do with the size or complexity of the data.