source: trunk/Packages/synapse/source/lib/synaser.pas

Last change on this file was 2, checked in by chronos, 12 years ago
  • Přidáno: Základní kostra projektu.
  • Přidáno: Knihovna synapse.
File size: 66.9 KB
Line 
1{==============================================================================|
2| Project : Ararat Synapse | 007.005.002 |
3|==============================================================================|
4| Content: Serial port support |
5|==============================================================================|
6| Copyright (c)2001-2011, Lukas Gebauer |
7| All rights reserved. |
8| |
9| Redistribution and use in source and binary forms, with or without |
10| modification, are permitted provided that the following conditions are met: |
11| |
12| Redistributions of source code must retain the above copyright notice, this |
13| list of conditions and the following disclaimer. |
14| |
15| Redistributions in binary form must reproduce the above copyright notice, |
16| this list of conditions and the following disclaimer in the documentation |
17| and/or other materials provided with the distribution. |
18| |
19| Neither the name of Lukas Gebauer nor the names of its contributors may |
20| be used to endorse or promote products derived from this software without |
21| specific prior written permission. |
22| |
23| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
24| AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
25| IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
26| ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR |
27| ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
28| DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
29| SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
30| CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
31| LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
32| OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
33| DAMAGE. |
34|==============================================================================|
35| The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).|
36| Portions created by Lukas Gebauer are Copyright (c)2001-2011. |
37| All Rights Reserved. |
38|==============================================================================|
39| Contributor(s): |
40| (c)2002, Hans-Georg Joepgen (cpom Comport Ownership Manager and bugfixes) |
41|==============================================================================|
42| History: see HISTORY.HTM from distribution package |
43| (Found at URL: http://www.ararat.cz/synapse/) |
44|==============================================================================}
45
46{: @abstract(Serial port communication library)
47This unit contains a class that implements serial port communication
48 for Windows, Linux, Unix or MacOSx. This class provides numerous methods with
49 same name and functionality as methods of the Ararat Synapse TCP/IP library.
50
51The following is a small example how establish a connection by modem (in this
52case with my USB modem):
53@longcode(#
54 ser:=TBlockSerial.Create;
55 try
56 ser.Connect('COM3');
57 ser.config(460800,8,'N',0,false,true);
58 ser.ATCommand('AT');
59 if (ser.LastError <> 0) or (not ser.ATResult) then
60 Exit;
61 ser.ATConnect('ATDT+420971200111');
62 if (ser.LastError <> 0) or (not ser.ATResult) then
63 Exit;
64 // you are now connected to a modem at +420971200111
65 // you can transmit or receive data now
66 finally
67 ser.free;
68 end;
69#)
70}
71
72//old Delphi does not have MSWINDOWS define.
73{$IFDEF WIN32}
74 {$IFNDEF MSWINDOWS}
75 {$DEFINE MSWINDOWS}
76 {$ENDIF}
77{$ENDIF}
78
79//Kylix does not known UNIX define
80{$IFDEF LINUX}
81 {$IFNDEF UNIX}
82 {$DEFINE UNIX}
83 {$ENDIF}
84{$ENDIF}
85
86{$IFDEF FPC}
87 {$MODE DELPHI}
88 {$IFDEF MSWINDOWS}
89 {$ASMMODE intel}
90 {$ENDIF}
91 {define working mode w/o LIBC for fpc}
92 {$DEFINE NO_LIBC}
93{$ENDIF}
94{$Q-}
95{$H+}
96{$M+}
97
98unit synaser;
99
100interface
101
102uses
103{$IFNDEF MSWINDOWS}
104 {$IFNDEF NO_LIBC}
105 Libc,
106 KernelIoctl,
107 {$ELSE}
108 termio, baseunix, unix,
109 {$ENDIF}
110 {$IFNDEF FPC}
111 Types,
112 {$ENDIF}
113{$ELSE}
114 Windows, registry,
115 {$IFDEF FPC}
116 winver,
117 {$ENDIF}
118{$ENDIF}
119 synafpc,
120 Classes, SysUtils, synautil;
121
122const
123 CR = #$0d;
124 LF = #$0a;
125 CRLF = CR + LF;
126 cSerialChunk = 8192;
127
128 LockfileDirectory = '/var/lock'; {HGJ}
129 PortIsClosed = -1; {HGJ}
130 ErrAlreadyOwned = 9991; {HGJ}
131 ErrAlreadyInUse = 9992; {HGJ}
132 ErrWrongParameter = 9993; {HGJ}
133 ErrPortNotOpen = 9994; {HGJ}
134 ErrNoDeviceAnswer = 9995; {HGJ}
135 ErrMaxBuffer = 9996;
136 ErrTimeout = 9997;
137 ErrNotRead = 9998;
138 ErrFrame = 9999;
139 ErrOverrun = 10000;
140 ErrRxOver = 10001;
141 ErrRxParity = 10002;
142 ErrTxFull = 10003;
143
144 dcb_Binary = $00000001;
145 dcb_ParityCheck = $00000002;
146 dcb_OutxCtsFlow = $00000004;
147 dcb_OutxDsrFlow = $00000008;
148 dcb_DtrControlMask = $00000030;
149 dcb_DtrControlDisable = $00000000;
150 dcb_DtrControlEnable = $00000010;
151 dcb_DtrControlHandshake = $00000020;
152 dcb_DsrSensivity = $00000040;
153 dcb_TXContinueOnXoff = $00000080;
154 dcb_OutX = $00000100;
155 dcb_InX = $00000200;
156 dcb_ErrorChar = $00000400;
157 dcb_NullStrip = $00000800;
158 dcb_RtsControlMask = $00003000;
159 dcb_RtsControlDisable = $00000000;
160 dcb_RtsControlEnable = $00001000;
161 dcb_RtsControlHandshake = $00002000;
162 dcb_RtsControlToggle = $00003000;
163 dcb_AbortOnError = $00004000;
164 dcb_Reserveds = $FFFF8000;
165
166 {:stopbit value for 1 stopbit}
167 SB1 = 0;
168 {:stopbit value for 1.5 stopbit}
169 SB1andHalf = 1;
170 {:stopbit value for 2 stopbits}
171 SB2 = 2;
172
173{$IFNDEF MSWINDOWS}
174const
175 INVALID_HANDLE_VALUE = THandle(-1);
176 CS7fix = $0000020;
177
178type
179 TDCB = record
180 DCBlength: DWORD;
181 BaudRate: DWORD;
182 Flags: Longint;
183 wReserved: Word;
184 XonLim: Word;
185 XoffLim: Word;
186 ByteSize: Byte;
187 Parity: Byte;
188 StopBits: Byte;
189 XonChar: CHAR;
190 XoffChar: CHAR;
191 ErrorChar: CHAR;
192 EofChar: CHAR;
193 EvtChar: CHAR;
194 wReserved1: Word;
195 end;
196 PDCB = ^TDCB;
197
198const
199{$IFDEF UNIX}
200 {$IFDEF DARWIN}
201 MaxRates = 18; //MAC
202 {$ELSE}
203 MaxRates = 30; //UNIX
204 {$ENDIF}
205{$ELSE}
206 MaxRates = 19; //WIN
207{$ENDIF}
208 Rates: array[0..MaxRates, 0..1] of cardinal =
209 (
210 (0, B0),
211 (50, B50),
212 (75, B75),
213 (110, B110),
214 (134, B134),
215 (150, B150),
216 (200, B200),
217 (300, B300),
218 (600, B600),
219 (1200, B1200),
220 (1800, B1800),
221 (2400, B2400),
222 (4800, B4800),
223 (9600, B9600),
224 (19200, B19200),
225 (38400, B38400),
226 (57600, B57600),
227 (115200, B115200),
228 (230400, B230400)
229{$IFNDEF DARWIN}
230 ,(460800, B460800)
231 {$IFDEF UNIX}
232 ,(500000, B500000),
233 (576000, B576000),
234 (921600, B921600),
235 (1000000, B1000000),
236 (1152000, B1152000),
237 (1500000, B1500000),
238 (2000000, B2000000),
239 (2500000, B2500000),
240 (3000000, B3000000),
241 (3500000, B3500000),
242 (4000000, B4000000)
243 {$ENDIF}
244{$ENDIF}
245 );
246{$ENDIF}
247
248{$IFDEF DARWIN}
249const // From fcntl.h
250 O_SYNC = $0080; { synchronous writes }
251{$ENDIF}
252
253const
254 sOK = 0;
255 sErr = integer(-1);
256
257type
258
259 {:Possible status event types for @link(THookSerialStatus)}
260 THookSerialReason = (
261 HR_SerialClose,
262 HR_Connect,
263 HR_CanRead,
264 HR_CanWrite,
265 HR_ReadCount,
266 HR_WriteCount,
267 HR_Wait
268 );
269
270 {:procedural prototype for status event hooking}
271 THookSerialStatus = procedure(Sender: TObject; Reason: THookSerialReason;
272 const Value: string) of object;
273
274 {:@abstract(Exception type for SynaSer errors)}
275 ESynaSerError = class(Exception)
276 public
277 ErrorCode: integer;
278 ErrorMessage: string;
279 end;
280
281 {:@abstract(Main class implementing all communication routines)}
282 TBlockSerial = class(TObject)
283 protected
284 FOnStatus: THookSerialStatus;
285 Fhandle: THandle;
286 FTag: integer;
287 FDevice: string;
288 FLastError: integer;
289 FLastErrorDesc: string;
290 FBuffer: AnsiString;
291 FRaiseExcept: boolean;
292 FRecvBuffer: integer;
293 FSendBuffer: integer;
294 FModemWord: integer;
295 FRTSToggle: Boolean;
296 FDeadlockTimeout: integer;
297 FInstanceActive: boolean; {HGJ}
298 FTestDSR: Boolean;
299 FTestCTS: Boolean;
300 FLastCR: Boolean;
301 FLastLF: Boolean;
302 FMaxLineLength: Integer;
303 FLinuxLock: Boolean;
304 FMaxSendBandwidth: Integer;
305 FNextSend: LongWord;
306 FMaxRecvBandwidth: Integer;
307 FNextRecv: LongWord;
308 FConvertLineEnd: Boolean;
309 FATResult: Boolean;
310 FAtTimeout: integer;
311 FInterPacketTimeout: Boolean;
312 FComNr: integer;
313{$IFDEF MSWINDOWS}
314 FPortAddr: Word;
315 function CanEvent(Event: dword; Timeout: integer): boolean;
316 procedure DecodeCommError(Error: DWord); virtual;
317 function GetPortAddr: Word; virtual;
318 function ReadTxEmpty(PortAddr: Word): Boolean; virtual;
319{$ENDIF}
320 procedure SetSizeRecvBuffer(size: integer); virtual;
321 function GetDSR: Boolean; virtual;
322 procedure SetDTRF(Value: Boolean); virtual;
323 function GetCTS: Boolean; virtual;
324 procedure SetRTSF(Value: Boolean); virtual;
325 function GetCarrier: Boolean; virtual;
326 function GetRing: Boolean; virtual;
327 procedure DoStatus(Reason: THookSerialReason; const Value: string); virtual;
328 procedure GetComNr(Value: string); virtual;
329 function PreTestFailing: boolean; virtual;{HGJ}
330 function TestCtrlLine: Boolean; virtual;
331{$IFDEF UNIX}
332 procedure DcbToTermios(const dcb: TDCB; var term: termios); virtual;
333 procedure TermiosToDcb(const term: termios; var dcb: TDCB); virtual;
334 function ReadLockfile: integer; virtual;
335 function LockfileName: String; virtual;
336 procedure CreateLockfile(PidNr: integer); virtual;
337{$ENDIF}
338 procedure LimitBandwidth(Length: Integer; MaxB: integer; var Next: LongWord); virtual;
339 procedure SetBandwidth(Value: Integer); virtual;
340 public
341 {: data Control Block with communication parameters. Usable only when you
342 need to call API directly.}
343 DCB: Tdcb;
344{$IFDEF UNIX}
345 TermiosStruc: termios;
346{$ENDIF}
347 {:Object constructor.}
348 constructor Create;
349 {:Object destructor.}
350 destructor Destroy; override;
351
352 {:Returns a string containing the version number of the library.}
353 class function GetVersion: string; virtual;
354
355 {:Destroy handle in use. It close connection to serial port.}
356 procedure CloseSocket; virtual;
357
358 {:Reconfigure communication parameters on the fly. You must be connected to
359 port before!
360 @param(baud Define connection speed. Baud rate can be from 50 to 4000000
361 bits per second. (it depends on your hardware!))
362 @param(bits Number of bits in communication.)
363 @param(parity Define communication parity (N - None, O - Odd, E - Even, M - Mark or S - Space).)
364 @param(stop Define number of stopbits. Use constants @link(SB1),
365 @link(SB1andHalf) and @link(SB2).)
366 @param(softflow Enable XON/XOFF handshake.)
367 @param(hardflow Enable CTS/RTS handshake.)}
368 procedure Config(baud, bits: integer; parity: char; stop: integer;
369 softflow, hardflow: boolean); virtual;
370
371 {:Connects to the port indicated by comport. Comport can be used in Windows
372 style (COM2), or in Linux style (/dev/ttyS1). When you use windows style
373 in Linux, then it will be converted to Linux name. And vice versa! However
374 you can specify any device name! (other device names then standart is not
375 converted!)
376
377 After successfull connection the DTR signal is set (if you not set hardware
378 handshake, then the RTS signal is set, too!)
379
380 Connection parameters is predefined by your system configuration. If you
381 need use another parameters, then you can use Config method after.
382 Notes:
383
384 - Remember, the commonly used serial Laplink cable does not support
385 hardware handshake.
386
387 - Before setting any handshake you must be sure that it is supported by
388 your hardware.
389
390 - Some serial devices are slow. In some cases you must wait up to a few
391 seconds after connection for the device to respond.
392
393 - when you connect to a modem device, then is best to test it by an empty
394 AT command. (call ATCommand('AT'))}
395 procedure Connect(comport: string); virtual;
396
397 {:Set communication parameters from the DCB structure (the DCB structure is
398 simulated under Linux).}
399 procedure SetCommState; virtual;
400
401 {:Read communication parameters into the DCB structure (DCB structure is
402 simulated under Linux).}
403 procedure GetCommState; virtual;
404
405 {:Sends Length bytes of data from Buffer through the connected port.}
406 function SendBuffer(buffer: pointer; length: integer): integer; virtual;
407
408 {:One data BYTE is sent.}
409 procedure SendByte(data: byte); virtual;
410
411 {:Send the string in the data parameter. No terminator is appended by this
412 method. If you need to send a string with CR/LF terminator, you must append
413 the CR/LF characters to the data string!
414
415 Since no terminator is appended, you can use this function for sending
416 binary data too.}
417 procedure SendString(data: AnsiString); virtual;
418
419 {:send four bytes as integer.}
420 procedure SendInteger(Data: integer); virtual;
421
422 {:send data as one block. Each block begins with integer value with Length
423 of block.}
424 procedure SendBlock(const Data: AnsiString); virtual;
425
426 {:send content of stream from current position}
427 procedure SendStreamRaw(const Stream: TStream); virtual;
428
429 {:send content of stream as block. see @link(SendBlock)}
430 procedure SendStream(const Stream: TStream); virtual;
431
432 {:send content of stream as block, but this is compatioble with Indy library.
433 (it have swapped lenght of block). See @link(SendStream)}
434 procedure SendStreamIndy(const Stream: TStream); virtual;
435
436 {:Waits until the allocated buffer is filled by received data. Returns number
437 of data bytes received, which equals to the Length value under normal
438 operation. If it is not equal, the communication channel is possibly broken.
439
440 This method not using any internal buffering, like all others receiving
441 methods. You cannot freely combine this method with all others receiving
442 methods!}
443 function RecvBuffer(buffer: pointer; length: integer): integer; virtual;
444
445 {:Method waits until data is received. If no data is received within
446 the Timeout (in milliseconds) period, @link(LastError) is set to
447 @link(ErrTimeout). This method is used to read any amount of data
448 (e. g. 1MB), and may be freely combined with all receviving methods what
449 have Timeout parameter, like the @link(RecvString), @link(RecvByte) or
450 @link(RecvTerminated) methods.}
451 function RecvBufferEx(buffer: pointer; length: integer; timeout: integer): integer; virtual;
452
453 {:It is like recvBufferEx, but data is readed to dynamicly allocated binary
454 string.}
455 function RecvBufferStr(Length: Integer; Timeout: Integer): AnsiString; virtual;
456
457 {:Read all available data and return it in the function result string. This
458 function may be combined with @link(RecvString), @link(RecvByte) or related
459 methods.}
460 function RecvPacket(Timeout: Integer): AnsiString; virtual;
461
462 {:Waits until one data byte is received which is returned as the function
463 result. If no data is received within the Timeout (in milliseconds) period,
464 @link(LastError) is set to @link(ErrTimeout).}
465 function RecvByte(timeout: integer): byte; virtual;
466
467 {:This method waits until a terminated data string is received. This string
468 is terminated by the Terminator string. The resulting string is returned
469 without this termination string! If no data is received within the Timeout
470 (in milliseconds) period, @link(LastError) is set to @link(ErrTimeout).}
471 function RecvTerminated(Timeout: Integer; const Terminator: AnsiString): AnsiString; virtual;
472
473 {:This method waits until a terminated data string is received. The string
474 is terminated by a CR/LF sequence. The resulting string is returned without
475 the terminator (CR/LF)! If no data is received within the Timeout (in
476 milliseconds) period, @link(LastError) is set to @link(ErrTimeout).
477
478 If @link(ConvertLineEnd) is used, then the CR/LF sequence may not be exactly
479 CR/LF. See the description of @link(ConvertLineEnd).
480
481 This method serves for line protocol implementation and uses its own
482 buffers to maximize performance. Therefore do NOT use this method with the
483 @link(RecvBuffer) method to receive data as it may cause data loss.}
484 function Recvstring(timeout: integer): AnsiString; virtual;
485
486 {:Waits until four data bytes are received which is returned as the function
487 integer result. If no data is received within the Timeout (in milliseconds) period,
488 @link(LastError) is set to @link(ErrTimeout).}
489 function RecvInteger(Timeout: Integer): Integer; virtual;
490
491 {:Waits until one data block is received. See @link(sendblock). If no data
492 is received within the Timeout (in milliseconds) period, @link(LastError)
493 is set to @link(ErrTimeout).}
494 function RecvBlock(Timeout: Integer): AnsiString; virtual;
495
496 {:Receive all data to stream, until some error occured. (for example timeout)}
497 procedure RecvStreamRaw(const Stream: TStream; Timeout: Integer); virtual;
498
499 {:receive requested count of bytes to stream}
500 procedure RecvStreamSize(const Stream: TStream; Timeout: Integer; Size: Integer); virtual;
501
502 {:receive block of data to stream. (Data can be sended by @link(sendstream)}
503 procedure RecvStream(const Stream: TStream; Timeout: Integer); virtual;
504
505 {:receive block of data to stream. (Data can be sended by @link(sendstreamIndy)}
506 procedure RecvStreamIndy(const Stream: TStream; Timeout: Integer); virtual;
507
508 {:Returns the number of received bytes waiting for reading. 0 is returned
509 when there is no data waiting.}
510 function WaitingData: integer; virtual;
511
512 {:Same as @link(WaitingData), but in respect to data in the internal
513 @link(LineBuffer).}
514 function WaitingDataEx: integer; virtual;
515
516 {:Returns the number of bytes waiting to be sent in the output buffer.
517 0 is returned when the output buffer is empty.}
518 function SendingData: integer; virtual;
519
520 {:Enable or disable RTS driven communication (half-duplex). It can be used
521 to communicate with RS485 converters, or other special equipment. If you
522 enable this feature, the system automatically controls the RTS signal.
523
524 Notes:
525
526 - On Windows NT (or higher) ir RTS signal driven by system driver.
527
528 - On Win9x family is used special code for waiting until last byte is
529 sended from your UART.
530
531 - On Linux you must have kernel 2.1 or higher!}
532 procedure EnableRTSToggle(value: boolean); virtual;
533
534 {:Waits until all data to is sent and buffers are emptied.
535 Warning: On Windows systems is this method returns when all buffers are
536 flushed to the serial port controller, before the last byte is sent!}
537 procedure Flush; virtual;
538
539 {:Unconditionally empty all buffers. It is good when you need to interrupt
540 communication and for cleanups.}
541 procedure Purge; virtual;
542
543 {:Returns @True, if you can from read any data from the port. Status is
544 tested for a period of time given by the Timeout parameter (in milliseconds).
545 If the value of the Timeout parameter is 0, the status is tested only once
546 and the function returns immediately. If the value of the Timeout parameter
547 is set to -1, the function returns only after it detects data on the port
548 (this may cause the process to hang).}
549 function CanRead(Timeout: integer): boolean; virtual;
550
551 {:Returns @True, if you can write any data to the port (this function is not
552 sending the contents of the buffer). Status is tested for a period of time
553 given by the Timeout parameter (in milliseconds). If the value of
554 the Timeout parameter is 0, the status is tested only once and the function
555 returns immediately. If the value of the Timeout parameter is set to -1,
556 the function returns only after it detects that it can write data to
557 the port (this may cause the process to hang).}
558 function CanWrite(Timeout: integer): boolean; virtual;
559
560 {:Same as @link(CanRead), but the test is against data in the internal
561 @link(LineBuffer) too.}
562 function CanReadEx(Timeout: integer): boolean; virtual;
563
564 {:Returns the status word of the modem. Decoding the status word could yield
565 the status of carrier detect signaland other signals. This method is used
566 internally by the modem status reading properties. You usually do not need
567 to call this method directly.}
568 function ModemStatus: integer; virtual;
569
570 {:Send a break signal to the communication device for Duration milliseconds.}
571 procedure SetBreak(Duration: integer); virtual;
572
573 {:This function is designed to send AT commands to the modem. The AT command
574 is sent in the Value parameter and the response is returned in the function
575 return value (may contain multiple lines!).
576 If the AT command is processed successfully (modem returns OK), then the
577 @link(ATResult) property is set to True.
578
579 This function is designed only for AT commands that return OK or ERROR
580 response! To call connection commands the @link(ATConnect) method.
581 Remember, when you connect to a modem device, it is in AT command mode.
582 Now you can send AT commands to the modem. If you need to transfer data to
583 the modem on the other side of the line, you must first switch to data mode
584 using the @link(ATConnect) method.}
585 function ATCommand(value: AnsiString): AnsiString; virtual;
586
587 {:This function is used to send connect type AT commands to the modem. It is
588 for commands to switch to connected state. (ATD, ATA, ATO,...)
589 It sends the AT command in the Value parameter and returns the modem's
590 response (may be multiple lines - usually with connection parameters info).
591 If the AT command is processed successfully (the modem returns CONNECT),
592 then the ATResult property is set to @True.
593
594 This function is designed only for AT commands which respond by CONNECT,
595 BUSY, NO DIALTONE NO CARRIER or ERROR. For other AT commands use the
596 @link(ATCommand) method.
597
598 The connect timeout is 90*@link(ATTimeout). If this command is successful
599 (@link(ATresult) is @true), then the modem is in data state. When you now
600 send or receive some data, it is not to or from your modem, but from the
601 modem on other side of the line. Now you can transfer your data.
602 If the connection attempt failed (@link(ATResult) is @False), then the
603 modem is still in AT command mode.}
604 function ATConnect(value: AnsiString): AnsiString; virtual;
605
606 {:If you "manually" call API functions, forward their return code in
607 the SerialResult parameter to this function, which evaluates it and sets
608 @link(LastError) and @link(LastErrorDesc).}
609 function SerialCheck(SerialResult: integer): integer; virtual;
610
611 {:If @link(Lasterror) is not 0 and exceptions are enabled, then this procedure
612 raises an exception. This method is used internally. You may need it only
613 in special cases.}
614 procedure ExceptCheck; virtual;
615
616 {:Set Synaser to error state with ErrNumber code. Usually used by internal
617 routines.}
618 procedure SetSynaError(ErrNumber: integer); virtual;
619
620 {:Raise Synaser error with ErrNumber code. Usually used by internal routines.}
621 procedure RaiseSynaError(ErrNumber: integer); virtual;
622{$IFDEF UNIX}
623 function cpomComportAccessible: boolean; virtual;{HGJ}
624 procedure cpomReleaseComport; virtual; {HGJ}
625{$ENDIF}
626 {:True device name of currently used port}
627 property Device: string read FDevice;
628
629 {:Error code of last operation. Value is defined by the host operating
630 system, but value 0 is always OK.}
631 property LastError: integer read FLastError;
632
633 {:Human readable description of LastError code.}
634 property LastErrorDesc: string read FLastErrorDesc;
635
636 {:Indicates if the last @link(ATCommand) or @link(ATConnect) method was successful}
637 property ATResult: Boolean read FATResult;
638
639 {:Read the value of the RTS signal.}
640 property RTS: Boolean write SetRTSF;
641
642 {:Indicates the presence of the CTS signal}
643 property CTS: boolean read GetCTS;
644
645 {:Use this property to set the value of the DTR signal.}
646 property DTR: Boolean write SetDTRF;
647
648 {:Exposes the status of the DSR signal.}
649 property DSR: boolean read GetDSR;
650
651 {:Indicates the presence of the Carrier signal}
652 property Carrier: boolean read GetCarrier;
653
654 {:Reflects the status of the Ring signal.}
655 property Ring: boolean read GetRing;
656
657 {:indicates if this instance of SynaSer is active. (Connected to some port)}
658 property InstanceActive: boolean read FInstanceActive; {HGJ}
659
660 {:Defines maximum bandwidth for all sending operations in bytes per second.
661 If this value is set to 0 (default), bandwidth limitation is not used.}
662 property MaxSendBandwidth: Integer read FMaxSendBandwidth Write FMaxSendBandwidth;
663
664 {:Defines maximum bandwidth for all receiving operations in bytes per second.
665 If this value is set to 0 (default), bandwidth limitation is not used.}
666 property MaxRecvBandwidth: Integer read FMaxRecvBandwidth Write FMaxRecvBandwidth;
667
668 {:Defines maximum bandwidth for all sending and receiving operations
669 in bytes per second. If this value is set to 0 (default), bandwidth
670 limitation is not used.}
671 property MaxBandwidth: Integer Write SetBandwidth;
672
673 {:Size of the Windows internal receive buffer. Default value is usually
674 4096 bytes. Note: Valid only in Windows versions!}
675 property SizeRecvBuffer: integer read FRecvBuffer write SetSizeRecvBuffer;
676 published
677 {:Returns the descriptive text associated with ErrorCode. You need this
678 method only in special cases. Description of LastError is now accessible
679 through the LastErrorDesc property.}
680 class function GetErrorDesc(ErrorCode: integer): string;
681
682 {:Freely usable property}
683 property Tag: integer read FTag write FTag;
684
685 {:Contains the handle of the open communication port.
686 You may need this value to directly call communication functions outside
687 SynaSer.}
688 property Handle: THandle read Fhandle write FHandle;
689
690 {:Internally used read buffer.}
691 property LineBuffer: AnsiString read FBuffer write FBuffer;
692
693 {:If @true, communication errors raise exceptions. If @false (default), only
694 the @link(LastError) value is set.}
695 property RaiseExcept: boolean read FRaiseExcept write FRaiseExcept;
696
697 {:This event is triggered when the communication status changes. It can be
698 used to monitor communication status.}
699 property OnStatus: THookSerialStatus read FOnStatus write FOnStatus;
700
701 {:If you set this property to @true, then the value of the DSR signal
702 is tested before every data transfer. It can be used to detect the presence
703 of a communications device.}
704 property TestDSR: boolean read FTestDSR write FTestDSR;
705
706 {:If you set this property to @true, then the value of the CTS signal
707 is tested before every data transfer. It can be used to detect the presence
708 of a communications device. Warning: This property cannot be used if you
709 need hardware handshake!}
710 property TestCTS: boolean read FTestCTS write FTestCTS;
711
712 {:Use this property you to limit the maximum size of LineBuffer
713 (as a protection against unlimited memory allocation for LineBuffer).
714 Default value is 0 - no limit.}
715 property MaxLineLength: Integer read FMaxLineLength Write FMaxLineLength;
716
717 {:This timeout value is used as deadlock protection when trying to send data
718 to (or receive data from) a device that stopped communicating during data
719 transmission (e.g. by physically disconnecting the device).
720 The timeout value is in milliseconds. The default value is 30,000 (30 seconds).}
721 property DeadlockTimeout: Integer read FDeadlockTimeout Write FDeadlockTimeout;
722
723 {:If set to @true (default value), port locking is enabled (under Linux only).
724 WARNING: To use this feature, the application must run by a user with full
725 permission to the /var/lock directory!}
726 property LinuxLock: Boolean read FLinuxLock write FLinuxLock;
727
728 {:Indicates if non-standard line terminators should be converted to a CR/LF pair
729 (standard DOS line terminator). If @TRUE, line terminators CR, single LF
730 or LF/CR are converted to CR/LF. Defaults to @FALSE.
731 This property has effect only on the behavior of the RecvString method.}
732 property ConvertLineEnd: Boolean read FConvertLineEnd Write FConvertLineEnd;
733
734 {:Timeout for AT modem based operations}
735 property AtTimeout: integer read FAtTimeout Write FAtTimeout;
736
737 {:If @true (default), then all timeouts is timeout between two characters.
738 If @False, then timeout is overall for whoole reading operation.}
739 property InterPacketTimeout: Boolean read FInterPacketTimeout Write FInterPacketTimeout;
740 end;
741
742{:Returns list of existing computer serial ports. Working properly only in Windows!}
743function GetSerialPortNames: string;
744
745implementation
746
747constructor TBlockSerial.Create;
748begin
749 inherited create;
750 FRaiseExcept := false;
751 FHandle := INVALID_HANDLE_VALUE;
752 FDevice := '';
753 FComNr:= PortIsClosed; {HGJ}
754 FInstanceActive:= false; {HGJ}
755 Fbuffer := '';
756 FRTSToggle := False;
757 FMaxLineLength := 0;
758 FTestDSR := False;
759 FTestCTS := False;
760 FDeadlockTimeout := 30000;
761 FLinuxLock := True;
762 FMaxSendBandwidth := 0;
763 FNextSend := 0;
764 FMaxRecvBandwidth := 0;
765 FNextRecv := 0;
766 FConvertLineEnd := False;
767 SetSynaError(sOK);
768 FRecvBuffer := 4096;
769 FLastCR := False;
770 FLastLF := False;
771 FAtTimeout := 1000;
772 FInterPacketTimeout := True;
773end;
774
775destructor TBlockSerial.Destroy;
776begin
777 CloseSocket;
778 inherited destroy;
779end;
780
781class function TBlockSerial.GetVersion: string;
782begin
783 Result := 'SynaSer 7.5.0';
784end;
785
786procedure TBlockSerial.CloseSocket;
787begin
788 if Fhandle <> INVALID_HANDLE_VALUE then
789 begin
790 Purge;
791 RTS := False;
792 DTR := False;
793 FileClose(FHandle);
794 end;
795 if InstanceActive then
796 begin
797 {$IFDEF UNIX}
798 if FLinuxLock then
799 cpomReleaseComport;
800 {$ENDIF}
801 FInstanceActive:= false
802 end;
803 Fhandle := INVALID_HANDLE_VALUE;
804 FComNr:= PortIsClosed;
805 SetSynaError(sOK);
806 DoStatus(HR_SerialClose, FDevice);
807end;
808
809{$IFDEF MSWINDOWS}
810function TBlockSerial.GetPortAddr: Word;
811begin
812 Result := 0;
813 if Win32Platform <> VER_PLATFORM_WIN32_NT then
814 begin
815 EscapeCommFunction(FHandle, 10);
816 asm
817 MOV @Result, DX;
818 end;
819 end;
820end;
821
822function TBlockSerial.ReadTxEmpty(PortAddr: Word): Boolean;
823begin
824 Result := True;
825 if Win32Platform <> VER_PLATFORM_WIN32_NT then
826 begin
827 asm
828 MOV DX, PortAddr;
829 ADD DX, 5;
830 IN AL, DX;
831 AND AL, $40;
832 JZ @K;
833 MOV AL,1;
834 @K: MOV @Result, AL;
835 end;
836 end;
837end;
838{$ENDIF}
839
840procedure TBlockSerial.GetComNr(Value: string);
841begin
842 FComNr := PortIsClosed;
843 if pos('COM', uppercase(Value)) = 1 then
844 FComNr := StrToIntdef(copy(Value, 4, Length(Value) - 3), PortIsClosed + 1) - 1;
845 if pos('/DEV/TTYS', uppercase(Value)) = 1 then
846 FComNr := StrToIntdef(copy(Value, 10, Length(Value) - 9), PortIsClosed - 1);
847end;
848
849procedure TBlockSerial.SetBandwidth(Value: Integer);
850begin
851 MaxSendBandwidth := Value;
852 MaxRecvBandwidth := Value;
853end;
854
855procedure TBlockSerial.LimitBandwidth(Length: Integer; MaxB: integer; var Next: LongWord);
856var
857 x: LongWord;
858 y: LongWord;
859begin
860 if MaxB > 0 then
861 begin
862 y := GetTick;
863 if Next > y then
864 begin
865 x := Next - y;
866 if x > 0 then
867 begin
868 DoStatus(HR_Wait, IntToStr(x));
869 sleep(x);
870 end;
871 end;
872 Next := GetTick + Trunc((Length / MaxB) * 1000);
873 end;
874end;
875
876procedure TBlockSerial.Config(baud, bits: integer; parity: char; stop: integer;
877 softflow, hardflow: boolean);
878begin
879 FillChar(dcb, SizeOf(dcb), 0);
880 GetCommState;
881 dcb.DCBlength := SizeOf(dcb);
882 dcb.BaudRate := baud;
883 dcb.ByteSize := bits;
884 case parity of
885 'N', 'n': dcb.parity := 0;
886 'O', 'o': dcb.parity := 1;
887 'E', 'e': dcb.parity := 2;
888 'M', 'm': dcb.parity := 3;
889 'S', 's': dcb.parity := 4;
890 end;
891 dcb.StopBits := stop;
892 dcb.XonChar := #17;
893 dcb.XoffChar := #19;
894 dcb.XonLim := FRecvBuffer div 4;
895 dcb.XoffLim := FRecvBuffer div 4;
896 dcb.Flags := dcb_Binary;
897 if softflow then
898 dcb.Flags := dcb.Flags or dcb_OutX or dcb_InX;
899 if hardflow then
900 dcb.Flags := dcb.Flags or dcb_OutxCtsFlow or dcb_RtsControlHandshake
901 else
902 dcb.Flags := dcb.Flags or dcb_RtsControlEnable;
903 dcb.Flags := dcb.Flags or dcb_DtrControlEnable;
904 if dcb.Parity > 0 then
905 dcb.Flags := dcb.Flags or dcb_ParityCheck;
906 SetCommState;
907end;
908
909procedure TBlockSerial.Connect(comport: string);
910{$IFDEF MSWINDOWS}
911var
912 CommTimeouts: TCommTimeouts;
913{$ENDIF}
914begin
915 // Is this TBlockSerial Instance already busy?
916 if InstanceActive then {HGJ}
917 begin {HGJ}
918 RaiseSynaError(ErrAlreadyInUse);
919 Exit; {HGJ}
920 end; {HGJ}
921 FBuffer := '';
922 FDevice := comport;
923 GetComNr(comport);
924{$IFDEF MSWINDOWS}
925 SetLastError (sOK);
926{$ELSE}
927 {$IFNDEF FPC}
928 SetLastError (sOK);
929 {$ELSE}
930 fpSetErrno(sOK);
931 {$ENDIF}
932{$ENDIF}
933{$IFNDEF MSWINDOWS}
934 if FComNr <> PortIsClosed then
935 FDevice := '/dev/ttyS' + IntToStr(FComNr);
936 // Comport already owned by another process? {HGJ}
937 if FLinuxLock then
938 if not cpomComportAccessible then
939 begin
940 RaiseSynaError(ErrAlreadyOwned);
941 Exit;
942 end;
943{$IFNDEF FPC}
944 FHandle := THandle(Libc.open(pchar(FDevice), O_RDWR or O_SYNC));
945{$ELSE}
946 FHandle := THandle(fpOpen(FDevice, O_RDWR or O_SYNC));
947{$ENDIF}
948 if FHandle = INVALID_HANDLE_VALUE then //because THandle is not integer on all platforms!
949 SerialCheck(-1)
950 else
951 SerialCheck(0);
952 {$IFDEF UNIX}
953 if FLastError <> sOK then
954 if FLinuxLock then
955 cpomReleaseComport;
956 {$ENDIF}
957 ExceptCheck;
958 if FLastError <> sOK then
959 Exit;
960{$ELSE}
961 if FComNr <> PortIsClosed then
962 FDevice := '\\.\COM' + IntToStr(FComNr + 1);
963 FHandle := THandle(CreateFile(PChar(FDevice), GENERIC_READ or GENERIC_WRITE,
964 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_OVERLAPPED, 0));
965 if FHandle = INVALID_HANDLE_VALUE then //because THandle is not integer on all platforms!
966 SerialCheck(-1)
967 else
968 SerialCheck(0);
969 ExceptCheck;
970 if FLastError <> sOK then
971 Exit;
972 SetCommMask(FHandle, 0);
973 SetupComm(Fhandle, FRecvBuffer, 0);
974 CommTimeOuts.ReadIntervalTimeout := MAXWORD;
975 CommTimeOuts.ReadTotalTimeoutMultiplier := 0;
976 CommTimeOuts.ReadTotalTimeoutConstant := 0;
977 CommTimeOuts.WriteTotalTimeoutMultiplier := 0;
978 CommTimeOuts.WriteTotalTimeoutConstant := 0;
979 SetCommTimeOuts(FHandle, CommTimeOuts);
980 FPortAddr := GetPortAddr;
981{$ENDIF}
982 SetSynaError(sOK);
983 if not TestCtrlLine then {HGJ}
984 begin
985 SetSynaError(ErrNoDeviceAnswer);
986 FileClose(FHandle); {HGJ}
987 {$IFDEF UNIX}
988 if FLinuxLock then
989 cpomReleaseComport; {HGJ}
990 {$ENDIF} {HGJ}
991 Fhandle := INVALID_HANDLE_VALUE; {HGJ}
992 FComNr:= PortIsClosed; {HGJ}
993 end
994 else
995 begin
996 FInstanceActive:= True;
997 RTS := True;
998 DTR := True;
999 Purge;
1000 end;
1001 ExceptCheck;
1002 DoStatus(HR_Connect, FDevice);
1003end;
1004
1005function TBlockSerial.SendBuffer(buffer: pointer; length: integer): integer;
1006{$IFDEF MSWINDOWS}
1007var
1008 Overlapped: TOverlapped;
1009 x, y, Err: DWord;
1010{$ENDIF}
1011begin
1012 Result := 0;
1013 if PreTestFailing then {HGJ}
1014 Exit; {HGJ}
1015 LimitBandwidth(Length, FMaxSendBandwidth, FNextsend);
1016 if FRTSToggle then
1017 begin
1018 Flush;
1019 RTS := True;
1020 end;
1021{$IFNDEF MSWINDOWS}
1022 result := FileWrite(Fhandle, Buffer^, Length);
1023 serialcheck(result);
1024{$ELSE}
1025 FillChar(Overlapped, Sizeof(Overlapped), 0);
1026 SetSynaError(sOK);
1027 y := 0;
1028 if not WriteFile(FHandle, Buffer^, Length, DWord(Result), @Overlapped) then
1029 y := GetLastError;
1030 if y = ERROR_IO_PENDING then
1031 begin
1032 x := WaitForSingleObject(FHandle, FDeadlockTimeout);
1033 if x = WAIT_TIMEOUT then
1034 begin
1035 PurgeComm(FHandle, PURGE_TXABORT);
1036 SetSynaError(ErrTimeout);
1037 end;
1038 GetOverlappedResult(FHandle, Overlapped, Dword(Result), False);
1039 end
1040 else
1041 SetSynaError(y);
1042 ClearCommError(FHandle, err, nil);
1043 if err <> 0 then
1044 DecodeCommError(err);
1045{$ENDIF}
1046 if FRTSToggle then
1047 begin
1048 Flush;
1049 CanWrite(255);
1050 RTS := False;
1051 end;
1052 ExceptCheck;
1053 DoStatus(HR_WriteCount, IntToStr(Result));
1054end;
1055
1056procedure TBlockSerial.SendByte(data: byte);
1057begin
1058 SendBuffer(@Data, 1);
1059end;
1060
1061procedure TBlockSerial.SendString(data: AnsiString);
1062begin
1063 SendBuffer(Pointer(Data), Length(Data));
1064end;
1065
1066procedure TBlockSerial.SendInteger(Data: integer);
1067begin
1068 SendBuffer(@data, SizeOf(Data));
1069end;
1070
1071procedure TBlockSerial.SendBlock(const Data: AnsiString);
1072begin
1073 SendInteger(Length(data));
1074 SendString(Data);
1075end;
1076
1077procedure TBlockSerial.SendStreamRaw(const Stream: TStream);
1078var
1079 si: integer;
1080 x, y, yr: integer;
1081 s: AnsiString;
1082begin
1083 si := Stream.Size - Stream.Position;
1084 x := 0;
1085 while x < si do
1086 begin
1087 y := si - x;
1088 if y > cSerialChunk then
1089 y := cSerialChunk;
1090 Setlength(s, y);
1091 yr := Stream.read(PAnsiChar(s)^, y);
1092 if yr > 0 then
1093 begin
1094 SetLength(s, yr);
1095 SendString(s);
1096 Inc(x, yr);
1097 end
1098 else
1099 break;
1100 end;
1101end;
1102
1103procedure TBlockSerial.SendStreamIndy(const Stream: TStream);
1104var
1105 si: integer;
1106begin
1107 si := Stream.Size - Stream.Position;
1108 si := Swapbytes(si);
1109 SendInteger(si);
1110 SendStreamRaw(Stream);
1111end;
1112
1113procedure TBlockSerial.SendStream(const Stream: TStream);
1114var
1115 si: integer;
1116begin
1117 si := Stream.Size - Stream.Position;
1118 SendInteger(si);
1119 SendStreamRaw(Stream);
1120end;
1121
1122function TBlockSerial.RecvBuffer(buffer: pointer; length: integer): integer;
1123{$IFNDEF MSWINDOWS}
1124begin
1125 Result := 0;
1126 if PreTestFailing then {HGJ}
1127 Exit; {HGJ}
1128 LimitBandwidth(Length, FMaxRecvBandwidth, FNextRecv);
1129 result := FileRead(FHandle, Buffer^, length);
1130 serialcheck(result);
1131{$ELSE}
1132var
1133 Overlapped: TOverlapped;
1134 x, y, Err: DWord;
1135begin
1136 Result := 0;
1137 if PreTestFailing then {HGJ}
1138 Exit; {HGJ}
1139 LimitBandwidth(Length, FMaxRecvBandwidth, FNextRecv);
1140 FillChar(Overlapped, Sizeof(Overlapped), 0);
1141 SetSynaError(sOK);
1142 y := 0;
1143 if not ReadFile(FHandle, Buffer^, length, Dword(Result), @Overlapped) then
1144 y := GetLastError;
1145 if y = ERROR_IO_PENDING then
1146 begin
1147 x := WaitForSingleObject(FHandle, FDeadlockTimeout);
1148 if x = WAIT_TIMEOUT then
1149 begin
1150 PurgeComm(FHandle, PURGE_RXABORT);
1151 SetSynaError(ErrTimeout);
1152 end;
1153 GetOverlappedResult(FHandle, Overlapped, Dword(Result), False);
1154 end
1155 else
1156 SetSynaError(y);
1157 ClearCommError(FHandle, err, nil);
1158 if err <> 0 then
1159 DecodeCommError(err);
1160{$ENDIF}
1161 ExceptCheck;
1162 DoStatus(HR_ReadCount, IntToStr(Result));
1163end;
1164
1165function TBlockSerial.RecvBufferEx(buffer: pointer; length: integer; timeout: integer): integer;
1166var
1167 s: AnsiString;
1168 rl, l: integer;
1169 ti: LongWord;
1170begin
1171 Result := 0;
1172 if PreTestFailing then {HGJ}
1173 Exit; {HGJ}
1174 SetSynaError(sOK);
1175 rl := 0;
1176 repeat
1177 ti := GetTick;
1178 s := RecvPacket(Timeout);
1179 l := System.Length(s);
1180 if (rl + l) > Length then
1181 l := Length - rl;
1182 Move(Pointer(s)^, IncPoint(Buffer, rl)^, l);
1183 rl := rl + l;
1184 if FLastError <> sOK then
1185 Break;
1186 if rl >= Length then
1187 Break;
1188 if not FInterPacketTimeout then
1189 begin
1190 Timeout := Timeout - integer(TickDelta(ti, GetTick));
1191 if Timeout <= 0 then
1192 begin
1193 SetSynaError(ErrTimeout);
1194 Break;
1195 end;
1196 end;
1197 until False;
1198 delete(s, 1, l);
1199 FBuffer := s;
1200 Result := rl;
1201end;
1202
1203function TBlockSerial.RecvBufferStr(Length: Integer; Timeout: Integer): AnsiString;
1204var
1205 x: integer;
1206begin
1207 Result := '';
1208 if PreTestFailing then {HGJ}
1209 Exit; {HGJ}
1210 SetSynaError(sOK);
1211 if Length > 0 then
1212 begin
1213 Setlength(Result, Length);
1214 x := RecvBufferEx(PAnsiChar(Result), Length , Timeout);
1215 if FLastError = sOK then
1216 SetLength(Result, x)
1217 else
1218 Result := '';
1219 end;
1220end;
1221
1222function TBlockSerial.RecvPacket(Timeout: Integer): AnsiString;
1223var
1224 x: integer;
1225begin
1226 Result := '';
1227 if PreTestFailing then {HGJ}
1228 Exit; {HGJ}
1229 SetSynaError(sOK);
1230 if FBuffer <> '' then
1231 begin
1232 Result := FBuffer;
1233 FBuffer := '';
1234 end
1235 else
1236 begin
1237 //not drain CPU on large downloads...
1238 Sleep(0);
1239 x := WaitingData;
1240 if x > 0 then
1241 begin
1242 SetLength(Result, x);
1243 x := RecvBuffer(Pointer(Result), x);
1244 if x >= 0 then
1245 SetLength(Result, x);
1246 end
1247 else
1248 begin
1249 if CanRead(Timeout) then
1250 begin
1251 x := WaitingData;
1252 if x = 0 then
1253 SetSynaError(ErrTimeout);
1254 if x > 0 then
1255 begin
1256 SetLength(Result, x);
1257 x := RecvBuffer(Pointer(Result), x);
1258 if x >= 0 then
1259 SetLength(Result, x);
1260 end;
1261 end
1262 else
1263 SetSynaError(ErrTimeout);
1264 end;
1265 end;
1266 ExceptCheck;
1267end;
1268
1269
1270function TBlockSerial.RecvByte(timeout: integer): byte;
1271begin
1272 Result := 0;
1273 if PreTestFailing then {HGJ}
1274 Exit; {HGJ}
1275 SetSynaError(sOK);
1276 if FBuffer = '' then
1277 FBuffer := RecvPacket(Timeout);
1278 if (FLastError = sOK) and (FBuffer <> '') then
1279 begin
1280 Result := Ord(FBuffer[1]);
1281 System.Delete(FBuffer, 1, 1);
1282 end;
1283 ExceptCheck;
1284end;
1285
1286function TBlockSerial.RecvTerminated(Timeout: Integer; const Terminator: AnsiString): AnsiString;
1287var
1288 x: Integer;
1289 s: AnsiString;
1290 l: Integer;
1291 CorCRLF: Boolean;
1292 t: ansistring;
1293 tl: integer;
1294 ti: LongWord;
1295begin
1296 Result := '';
1297 if PreTestFailing then {HGJ}
1298 Exit; {HGJ}
1299 SetSynaError(sOK);
1300 l := system.Length(Terminator);
1301 if l = 0 then
1302 Exit;
1303 tl := l;
1304 CorCRLF := FConvertLineEnd and (Terminator = CRLF);
1305 s := '';
1306 x := 0;
1307 repeat
1308 ti := GetTick;
1309 //get rest of FBuffer or incomming new data...
1310 s := s + RecvPacket(Timeout);
1311 if FLastError <> sOK then
1312 Break;
1313 x := 0;
1314 if Length(s) > 0 then
1315 if CorCRLF then
1316 begin
1317 if FLastCR and (s[1] = LF) then
1318 Delete(s, 1, 1);
1319 if FLastLF and (s[1] = CR) then
1320 Delete(s, 1, 1);
1321 FLastCR := False;
1322 FLastLF := False;
1323 t := '';
1324 x := PosCRLF(s, t);
1325 tl := system.Length(t);
1326 if t = CR then
1327 FLastCR := True;
1328 if t = LF then
1329 FLastLF := True;
1330 end
1331 else
1332 begin
1333 x := pos(Terminator, s);
1334 tl := l;
1335 end;
1336 if (FMaxLineLength <> 0) and (system.Length(s) > FMaxLineLength) then
1337 begin
1338 SetSynaError(ErrMaxBuffer);
1339 Break;
1340 end;
1341 if x > 0 then
1342 Break;
1343 if not FInterPacketTimeout then
1344 begin
1345 Timeout := Timeout - integer(TickDelta(ti, GetTick));
1346 if Timeout <= 0 then
1347 begin
1348 SetSynaError(ErrTimeout);
1349 Break;
1350 end;
1351 end;
1352 until False;
1353 if x > 0 then
1354 begin
1355 Result := Copy(s, 1, x - 1);
1356 System.Delete(s, 1, x + tl - 1);
1357 end;
1358 FBuffer := s;
1359 ExceptCheck;
1360end;
1361
1362
1363function TBlockSerial.RecvString(Timeout: Integer): AnsiString;
1364var
1365 s: AnsiString;
1366begin
1367 Result := '';
1368 s := RecvTerminated(Timeout, #13 + #10);
1369 if FLastError = sOK then
1370 Result := s;
1371end;
1372
1373function TBlockSerial.RecvInteger(Timeout: Integer): Integer;
1374var
1375 s: AnsiString;
1376begin
1377 Result := 0;
1378 s := RecvBufferStr(4, Timeout);
1379 if FLastError = 0 then
1380 Result := (ord(s[1]) + ord(s[2]) * 256) + (ord(s[3]) + ord(s[4]) * 256) * 65536;
1381end;
1382
1383function TBlockSerial.RecvBlock(Timeout: Integer): AnsiString;
1384var
1385 x: integer;
1386begin
1387 Result := '';
1388 x := RecvInteger(Timeout);
1389 if FLastError = 0 then
1390 Result := RecvBufferStr(x, Timeout);
1391end;
1392
1393procedure TBlockSerial.RecvStreamRaw(const Stream: TStream; Timeout: Integer);
1394var
1395 s: AnsiString;
1396begin
1397 repeat
1398 s := RecvPacket(Timeout);
1399 if FLastError = 0 then
1400 WriteStrToStream(Stream, s);
1401 until FLastError <> 0;
1402end;
1403
1404procedure TBlockSerial.RecvStreamSize(const Stream: TStream; Timeout: Integer; Size: Integer);
1405var
1406 s: AnsiString;
1407 n: integer;
1408begin
1409 for n := 1 to (Size div cSerialChunk) do
1410 begin
1411 s := RecvBufferStr(cSerialChunk, Timeout);
1412 if FLastError <> 0 then
1413 Exit;
1414 Stream.Write(PAnsichar(s)^, cSerialChunk);
1415 end;
1416 n := Size mod cSerialChunk;
1417 if n > 0 then
1418 begin
1419 s := RecvBufferStr(n, Timeout);
1420 if FLastError <> 0 then
1421 Exit;
1422 Stream.Write(PAnsichar(s)^, n);
1423 end;
1424end;
1425
1426procedure TBlockSerial.RecvStreamIndy(const Stream: TStream; Timeout: Integer);
1427var
1428 x: integer;
1429begin
1430 x := RecvInteger(Timeout);
1431 x := SwapBytes(x);
1432 if FLastError = 0 then
1433 RecvStreamSize(Stream, Timeout, x);
1434end;
1435
1436procedure TBlockSerial.RecvStream(const Stream: TStream; Timeout: Integer);
1437var
1438 x: integer;
1439begin
1440 x := RecvInteger(Timeout);
1441 if FLastError = 0 then
1442 RecvStreamSize(Stream, Timeout, x);
1443end;
1444
1445{$IFNDEF MSWINDOWS}
1446function TBlockSerial.WaitingData: integer;
1447begin
1448{$IFNDEF FPC}
1449 serialcheck(ioctl(FHandle, FIONREAD, @result));
1450{$ELSE}
1451 serialcheck(fpIoctl(FHandle, FIONREAD, @result));
1452{$ENDIF}
1453 if FLastError <> 0 then
1454 Result := 0;
1455 ExceptCheck;
1456end;
1457{$ELSE}
1458function TBlockSerial.WaitingData: integer;
1459var
1460 stat: TComStat;
1461 err: DWORD;
1462begin
1463 if ClearCommError(FHandle, err, @stat) then
1464 begin
1465 SetSynaError(sOK);
1466 Result := stat.cbInQue;
1467 end
1468 else
1469 begin
1470 SerialCheck(sErr);
1471 Result := 0;
1472 end;
1473 ExceptCheck;
1474end;
1475{$ENDIF}
1476
1477function TBlockSerial.WaitingDataEx: integer;
1478begin
1479 if FBuffer <> '' then
1480 Result := Length(FBuffer)
1481 else
1482 Result := Waitingdata;
1483end;
1484
1485{$IFNDEF MSWINDOWS}
1486function TBlockSerial.SendingData: integer;
1487begin
1488 SetSynaError(sOK);
1489 Result := 0;
1490end;
1491{$ELSE}
1492function TBlockSerial.SendingData: integer;
1493var
1494 stat: TComStat;
1495 err: DWORD;
1496begin
1497 SetSynaError(sOK);
1498 if not ClearCommError(FHandle, err, @stat) then
1499 serialcheck(sErr);
1500 ExceptCheck;
1501 result := stat.cbOutQue;
1502end;
1503{$ENDIF}
1504
1505{$IFNDEF MSWINDOWS}
1506procedure TBlockSerial.DcbToTermios(const dcb: TDCB; var term: termios);
1507var
1508 n: integer;
1509 x: cardinal;
1510begin
1511 //others
1512 cfmakeraw(term);
1513 term.c_cflag := term.c_cflag or CREAD;
1514 term.c_cflag := term.c_cflag or CLOCAL;
1515 term.c_cflag := term.c_cflag or HUPCL;
1516 //hardware handshake
1517 if (dcb.flags and dcb_RtsControlHandshake) > 0 then
1518 term.c_cflag := term.c_cflag or CRTSCTS
1519 else
1520 term.c_cflag := term.c_cflag and (not CRTSCTS);
1521 //software handshake
1522 if (dcb.flags and dcb_OutX) > 0 then
1523 term.c_iflag := term.c_iflag or IXON or IXOFF or IXANY
1524 else
1525 term.c_iflag := term.c_iflag and (not (IXON or IXOFF or IXANY));
1526 //size of byte
1527 term.c_cflag := term.c_cflag and (not CSIZE);
1528 case dcb.bytesize of
1529 5:
1530 term.c_cflag := term.c_cflag or CS5;
1531 6:
1532 term.c_cflag := term.c_cflag or CS6;
1533 7:
1534{$IFDEF FPC}
1535 term.c_cflag := term.c_cflag or CS7;
1536{$ELSE}
1537 term.c_cflag := term.c_cflag or CS7fix;
1538{$ENDIF}
1539 8:
1540 term.c_cflag := term.c_cflag or CS8;
1541 end;
1542 //parity
1543 if (dcb.flags and dcb_ParityCheck) > 0 then
1544 term.c_cflag := term.c_cflag or PARENB
1545 else
1546 term.c_cflag := term.c_cflag and (not PARENB);
1547 case dcb.parity of
1548 1: //'O'
1549 term.c_cflag := term.c_cflag or PARODD;
1550 2: //'E'
1551 term.c_cflag := term.c_cflag and (not PARODD);
1552 end;
1553 //stop bits
1554 if dcb.stopbits > 0 then
1555 term.c_cflag := term.c_cflag or CSTOPB
1556 else
1557 term.c_cflag := term.c_cflag and (not CSTOPB);
1558 //set baudrate;
1559 x := 0;
1560 for n := 0 to Maxrates do
1561 if rates[n, 0] = dcb.BaudRate then
1562 begin
1563 x := rates[n, 1];
1564 break;
1565 end;
1566 cfsetospeed(term, x);
1567 cfsetispeed(term, x);
1568end;
1569
1570procedure TBlockSerial.TermiosToDcb(const term: termios; var dcb: TDCB);
1571var
1572 n: integer;
1573 x: cardinal;
1574begin
1575 //set baudrate;
1576 dcb.baudrate := 0;
1577 {$IFDEF FPC}
1578 //why FPC not have cfgetospeed???
1579 x := term.c_oflag and $0F;
1580 {$ELSE}
1581 x := cfgetospeed(term);
1582 {$ENDIF}
1583 for n := 0 to Maxrates do
1584 if rates[n, 1] = x then
1585 begin
1586 dcb.baudrate := rates[n, 0];
1587 break;
1588 end;
1589 //hardware handshake
1590 if (term.c_cflag and CRTSCTS) > 0 then
1591 dcb.flags := dcb.flags or dcb_RtsControlHandshake or dcb_OutxCtsFlow
1592 else
1593 dcb.flags := dcb.flags and (not (dcb_RtsControlHandshake or dcb_OutxCtsFlow));
1594 //software handshake
1595 if (term.c_cflag and IXOFF) > 0 then
1596 dcb.flags := dcb.flags or dcb_OutX or dcb_InX
1597 else
1598 dcb.flags := dcb.flags and (not (dcb_OutX or dcb_InX));
1599 //size of byte
1600 case term.c_cflag and CSIZE of
1601 CS5:
1602 dcb.bytesize := 5;
1603 CS6:
1604 dcb.bytesize := 6;
1605 CS7fix:
1606 dcb.bytesize := 7;
1607 CS8:
1608 dcb.bytesize := 8;
1609 end;
1610 //parity
1611 if (term.c_cflag and PARENB) > 0 then
1612 dcb.flags := dcb.flags or dcb_ParityCheck
1613 else
1614 dcb.flags := dcb.flags and (not dcb_ParityCheck);
1615 dcb.parity := 0;
1616 if (term.c_cflag and PARODD) > 0 then
1617 dcb.parity := 1
1618 else
1619 dcb.parity := 2;
1620 //stop bits
1621 if (term.c_cflag and CSTOPB) > 0 then
1622 dcb.stopbits := 2
1623 else
1624 dcb.stopbits := 0;
1625end;
1626{$ENDIF}
1627
1628{$IFNDEF MSWINDOWS}
1629procedure TBlockSerial.SetCommState;
1630begin
1631 DcbToTermios(dcb, termiosstruc);
1632 SerialCheck(tcsetattr(FHandle, TCSANOW, termiosstruc));
1633 ExceptCheck;
1634end;
1635{$ELSE}
1636procedure TBlockSerial.SetCommState;
1637begin
1638 SetSynaError(sOK);
1639 if not windows.SetCommState(Fhandle, dcb) then
1640 SerialCheck(sErr);
1641 ExceptCheck;
1642end;
1643{$ENDIF}
1644
1645{$IFNDEF MSWINDOWS}
1646procedure TBlockSerial.GetCommState;
1647begin
1648 SerialCheck(tcgetattr(FHandle, termiosstruc));
1649 ExceptCheck;
1650 TermiostoDCB(termiosstruc, dcb);
1651end;
1652{$ELSE}
1653procedure TBlockSerial.GetCommState;
1654begin
1655 SetSynaError(sOK);
1656 if not windows.GetCommState(Fhandle, dcb) then
1657 SerialCheck(sErr);
1658 ExceptCheck;
1659end;
1660{$ENDIF}
1661
1662procedure TBlockSerial.SetSizeRecvBuffer(size: integer);
1663begin
1664{$IFDEF MSWINDOWS}
1665 SetupComm(Fhandle, size, 0);
1666 GetCommState;
1667 dcb.XonLim := size div 4;
1668 dcb.XoffLim := size div 4;
1669 SetCommState;
1670{$ENDIF}
1671 FRecvBuffer := size;
1672end;
1673
1674function TBlockSerial.GetDSR: Boolean;
1675begin
1676 ModemStatus;
1677{$IFNDEF MSWINDOWS}
1678 Result := (FModemWord and TIOCM_DSR) > 0;
1679{$ELSE}
1680 Result := (FModemWord and MS_DSR_ON) > 0;
1681{$ENDIF}
1682end;
1683
1684procedure TBlockSerial.SetDTRF(Value: Boolean);
1685begin
1686{$IFNDEF MSWINDOWS}
1687 ModemStatus;
1688 if Value then
1689 FModemWord := FModemWord or TIOCM_DTR
1690 else
1691 FModemWord := FModemWord and not TIOCM_DTR;
1692 {$IFNDEF FPC}
1693 ioctl(FHandle, TIOCMSET, @FModemWord);
1694 {$ELSE}
1695 fpioctl(FHandle, TIOCMSET, @FModemWord);
1696 {$ENDIF}
1697{$ELSE}
1698 if Value then
1699 EscapeCommFunction(FHandle, SETDTR)
1700 else
1701 EscapeCommFunction(FHandle, CLRDTR);
1702{$ENDIF}
1703end;
1704
1705function TBlockSerial.GetCTS: Boolean;
1706begin
1707 ModemStatus;
1708{$IFNDEF MSWINDOWS}
1709 Result := (FModemWord and TIOCM_CTS) > 0;
1710{$ELSE}
1711 Result := (FModemWord and MS_CTS_ON) > 0;
1712{$ENDIF}
1713end;
1714
1715procedure TBlockSerial.SetRTSF(Value: Boolean);
1716begin
1717{$IFNDEF MSWINDOWS}
1718 ModemStatus;
1719 if Value then
1720 FModemWord := FModemWord or TIOCM_RTS
1721 else
1722 FModemWord := FModemWord and not TIOCM_RTS;
1723 {$IFNDEF FPC}
1724 ioctl(FHandle, TIOCMSET, @FModemWord);
1725 {$ELSE}
1726 fpioctl(FHandle, TIOCMSET, @FModemWord);
1727 {$ENDIF}
1728{$ELSE}
1729 if Value then
1730 EscapeCommFunction(FHandle, SETRTS)
1731 else
1732 EscapeCommFunction(FHandle, CLRRTS);
1733{$ENDIF}
1734end;
1735
1736function TBlockSerial.GetCarrier: Boolean;
1737begin
1738 ModemStatus;
1739{$IFNDEF MSWINDOWS}
1740 Result := (FModemWord and TIOCM_CAR) > 0;
1741{$ELSE}
1742 Result := (FModemWord and MS_RLSD_ON) > 0;
1743{$ENDIF}
1744end;
1745
1746function TBlockSerial.GetRing: Boolean;
1747begin
1748 ModemStatus;
1749{$IFNDEF MSWINDOWS}
1750 Result := (FModemWord and TIOCM_RNG) > 0;
1751{$ELSE}
1752 Result := (FModemWord and MS_RING_ON) > 0;
1753{$ENDIF}
1754end;
1755
1756{$IFDEF MSWINDOWS}
1757function TBlockSerial.CanEvent(Event: dword; Timeout: integer): boolean;
1758var
1759 ex: DWord;
1760 y: Integer;
1761 Overlapped: TOverlapped;
1762begin
1763 FillChar(Overlapped, Sizeof(Overlapped), 0);
1764 Overlapped.hEvent := CreateEvent(nil, True, False, nil);
1765 try
1766 SetCommMask(FHandle, Event);
1767 SetSynaError(sOK);
1768 if (Event = EV_RXCHAR) and (Waitingdata > 0) then
1769 Result := True
1770 else
1771 begin
1772 y := 0;
1773 if not WaitCommEvent(FHandle, ex, @Overlapped) then
1774 y := GetLastError;
1775 if y = ERROR_IO_PENDING then
1776 begin
1777 //timedout
1778 WaitForSingleObject(Overlapped.hEvent, Timeout);
1779 SetCommMask(FHandle, 0);
1780 GetOverlappedResult(FHandle, Overlapped, DWord(y), True);
1781 end;
1782 Result := (ex and Event) = Event;
1783 end;
1784 finally
1785 SetCommMask(FHandle, 0);
1786 CloseHandle(Overlapped.hEvent);
1787 end;
1788end;
1789{$ENDIF}
1790
1791{$IFNDEF MSWINDOWS}
1792function TBlockSerial.CanRead(Timeout: integer): boolean;
1793var
1794 FDSet: TFDSet;
1795 TimeVal: PTimeVal;
1796 TimeV: TTimeVal;
1797 x: Integer;
1798begin
1799 TimeV.tv_usec := (Timeout mod 1000) * 1000;
1800 TimeV.tv_sec := Timeout div 1000;
1801 TimeVal := @TimeV;
1802 if Timeout = -1 then
1803 TimeVal := nil;
1804 {$IFNDEF FPC}
1805 FD_ZERO(FDSet);
1806 FD_SET(FHandle, FDSet);
1807 x := Select(FHandle + 1, @FDSet, nil, nil, TimeVal);
1808 {$ELSE}
1809 fpFD_ZERO(FDSet);
1810 fpFD_SET(FHandle, FDSet);
1811 x := fpSelect(FHandle + 1, @FDSet, nil, nil, TimeVal);
1812 {$ENDIF}
1813 SerialCheck(x);
1814 if FLastError <> sOK then
1815 x := 0;
1816 Result := x > 0;
1817 ExceptCheck;
1818 if Result then
1819 DoStatus(HR_CanRead, '');
1820end;
1821{$ELSE}
1822function TBlockSerial.CanRead(Timeout: integer): boolean;
1823begin
1824 Result := WaitingData > 0;
1825 if not Result then
1826 Result := CanEvent(EV_RXCHAR, Timeout) or (WaitingData > 0);
1827 //check WaitingData again due some broken virtual ports
1828 if Result then
1829 DoStatus(HR_CanRead, '');
1830end;
1831{$ENDIF}
1832
1833{$IFNDEF MSWINDOWS}
1834function TBlockSerial.CanWrite(Timeout: integer): boolean;
1835var
1836 FDSet: TFDSet;
1837 TimeVal: PTimeVal;
1838 TimeV: TTimeVal;
1839 x: Integer;
1840begin
1841 TimeV.tv_usec := (Timeout mod 1000) * 1000;
1842 TimeV.tv_sec := Timeout div 1000;
1843 TimeVal := @TimeV;
1844 if Timeout = -1 then
1845 TimeVal := nil;
1846 {$IFNDEF FPC}
1847 FD_ZERO(FDSet);
1848 FD_SET(FHandle, FDSet);
1849 x := Select(FHandle + 1, nil, @FDSet, nil, TimeVal);
1850 {$ELSE}
1851 fpFD_ZERO(FDSet);
1852 fpFD_SET(FHandle, FDSet);
1853 x := fpSelect(FHandle + 1, nil, @FDSet, nil, TimeVal);
1854 {$ENDIF}
1855 SerialCheck(x);
1856 if FLastError <> sOK then
1857 x := 0;
1858 Result := x > 0;
1859 ExceptCheck;
1860 if Result then
1861 DoStatus(HR_CanWrite, '');
1862end;
1863{$ELSE}
1864function TBlockSerial.CanWrite(Timeout: integer): boolean;
1865var
1866 t: LongWord;
1867begin
1868 Result := SendingData = 0;
1869 if not Result then
1870 Result := CanEvent(EV_TXEMPTY, Timeout);
1871 if Result and (Win32Platform <> VER_PLATFORM_WIN32_NT) then
1872 begin
1873 t := GetTick;
1874 while not ReadTxEmpty(FPortAddr) do
1875 begin
1876 if TickDelta(t, GetTick) > 255 then
1877 Break;
1878 Sleep(0);
1879 end;
1880 end;
1881 if Result then
1882 DoStatus(HR_CanWrite, '');
1883end;
1884{$ENDIF}
1885
1886function TBlockSerial.CanReadEx(Timeout: integer): boolean;
1887begin
1888 if Fbuffer <> '' then
1889 Result := True
1890 else
1891 Result := CanRead(Timeout);
1892end;
1893
1894procedure TBlockSerial.EnableRTSToggle(Value: boolean);
1895begin
1896 SetSynaError(sOK);
1897{$IFNDEF MSWINDOWS}
1898 FRTSToggle := Value;
1899 if Value then
1900 RTS:=False;
1901{$ELSE}
1902 if Win32Platform = VER_PLATFORM_WIN32_NT then
1903 begin
1904 GetCommState;
1905 if value then
1906 dcb.Flags := dcb.Flags or dcb_RtsControlToggle
1907 else
1908 dcb.flags := dcb.flags and (not dcb_RtsControlToggle);
1909 SetCommState;
1910 end
1911 else
1912 begin
1913 FRTSToggle := Value;
1914 if Value then
1915 RTS:=False;
1916 end;
1917{$ENDIF}
1918end;
1919
1920procedure TBlockSerial.Flush;
1921begin
1922{$IFNDEF MSWINDOWS}
1923 SerialCheck(tcdrain(FHandle));
1924{$ELSE}
1925 SetSynaError(sOK);
1926 if not Flushfilebuffers(FHandle) then
1927 SerialCheck(sErr);
1928{$ENDIF}
1929 ExceptCheck;
1930end;
1931
1932{$IFNDEF MSWINDOWS}
1933procedure TBlockSerial.Purge;
1934begin
1935 {$IFNDEF FPC}
1936 SerialCheck(ioctl(FHandle, TCFLSH, TCIOFLUSH));
1937 {$ELSE}
1938 {$IFDEF DARWIN}
1939 SerialCheck(fpioctl(FHandle, TCIOflush, TCIOFLUSH));
1940 {$ELSE}
1941 SerialCheck(fpioctl(FHandle, TCFLSH, Pointer(PtrInt(TCIOFLUSH))));
1942 {$ENDIF}
1943 {$ENDIF}
1944 FBuffer := '';
1945 ExceptCheck;
1946end;
1947{$ELSE}
1948procedure TBlockSerial.Purge;
1949var
1950 x: integer;
1951begin
1952 SetSynaError(sOK);
1953 x := PURGE_TXABORT or PURGE_TXCLEAR or PURGE_RXABORT or PURGE_RXCLEAR;
1954 if not PurgeComm(FHandle, x) then
1955 SerialCheck(sErr);
1956 FBuffer := '';
1957 ExceptCheck;
1958end;
1959{$ENDIF}
1960
1961function TBlockSerial.ModemStatus: integer;
1962begin
1963 Result := 0;
1964{$IFNDEF MSWINDOWS}
1965 {$IFNDEF FPC}
1966 SerialCheck(ioctl(FHandle, TIOCMGET, @Result));
1967 {$ELSE}
1968 SerialCheck(fpioctl(FHandle, TIOCMGET, @Result));
1969 {$ENDIF}
1970{$ELSE}
1971 SetSynaError(sOK);
1972 if not GetCommModemStatus(FHandle, dword(Result)) then
1973 SerialCheck(sErr);
1974{$ENDIF}
1975 ExceptCheck;
1976 FModemWord := Result;
1977end;
1978
1979procedure TBlockSerial.SetBreak(Duration: integer);
1980begin
1981{$IFNDEF MSWINDOWS}
1982 SerialCheck(tcsendbreak(FHandle, Duration));
1983{$ELSE}
1984 SetCommBreak(FHandle);
1985 Sleep(Duration);
1986 SetSynaError(sOK);
1987 if not ClearCommBreak(FHandle) then
1988 SerialCheck(sErr);
1989{$ENDIF}
1990end;
1991
1992{$IFDEF MSWINDOWS}
1993procedure TBlockSerial.DecodeCommError(Error: DWord);
1994begin
1995 if (Error and DWord(CE_FRAME)) > 1 then
1996 FLastError := ErrFrame;
1997 if (Error and DWord(CE_OVERRUN)) > 1 then
1998 FLastError := ErrOverrun;
1999 if (Error and DWord(CE_RXOVER)) > 1 then
2000 FLastError := ErrRxOver;
2001 if (Error and DWord(CE_RXPARITY)) > 1 then
2002 FLastError := ErrRxParity;
2003 if (Error and DWord(CE_TXFULL)) > 1 then
2004 FLastError := ErrTxFull;
2005end;
2006{$ENDIF}
2007
2008//HGJ
2009function TBlockSerial.PreTestFailing: Boolean;
2010begin
2011 if not FInstanceActive then
2012 begin
2013 RaiseSynaError(ErrPortNotOpen);
2014 result:= true;
2015 Exit;
2016 end;
2017 Result := not TestCtrlLine;
2018 if result then
2019 RaiseSynaError(ErrNoDeviceAnswer)
2020end;
2021
2022function TBlockSerial.TestCtrlLine: Boolean;
2023begin
2024 result := ((not FTestDSR) or DSR) and ((not FTestCTS) or CTS);
2025end;
2026
2027function TBlockSerial.ATCommand(value: AnsiString): AnsiString;
2028var
2029 s: AnsiString;
2030 ConvSave: Boolean;
2031begin
2032 result := '';
2033 FAtResult := False;
2034 ConvSave := FConvertLineEnd;
2035 try
2036 FConvertLineEnd := True;
2037 SendString(value + #$0D);
2038 repeat
2039 s := RecvString(FAtTimeout);
2040 if s <> Value then
2041 result := result + s + CRLF;
2042 if s = 'OK' then
2043 begin
2044 FAtResult := True;
2045 break;
2046 end;
2047 if s = 'ERROR' then
2048 break;
2049 until FLastError <> sOK;
2050 finally
2051 FConvertLineEnd := Convsave;
2052 end;
2053end;
2054
2055
2056function TBlockSerial.ATConnect(value: AnsiString): AnsiString;
2057var
2058 s: AnsiString;
2059 ConvSave: Boolean;
2060begin
2061 result := '';
2062 FAtResult := False;
2063 ConvSave := FConvertLineEnd;
2064 try
2065 FConvertLineEnd := True;
2066 SendString(value + #$0D);
2067 repeat
2068 s := RecvString(90 * FAtTimeout);
2069 if s <> Value then
2070 result := result + s + CRLF;
2071 if s = 'NO CARRIER' then
2072 break;
2073 if s = 'ERROR' then
2074 break;
2075 if s = 'BUSY' then
2076 break;
2077 if s = 'NO DIALTONE' then
2078 break;
2079 if Pos('CONNECT', s) = 1 then
2080 begin
2081 FAtResult := True;
2082 break;
2083 end;
2084 until FLastError <> sOK;
2085 finally
2086 FConvertLineEnd := Convsave;
2087 end;
2088end;
2089
2090function TBlockSerial.SerialCheck(SerialResult: integer): integer;
2091begin
2092 if SerialResult = integer(INVALID_HANDLE_VALUE) then
2093{$IFDEF MSWINDOWS}
2094 result := GetLastError
2095{$ELSE}
2096 {$IFNDEF FPC}
2097 result := GetLastError
2098 {$ELSE}
2099 result := fpGetErrno
2100 {$ENDIF}
2101{$ENDIF}
2102 else
2103 result := sOK;
2104 FLastError := result;
2105 FLastErrorDesc := GetErrorDesc(FLastError);
2106end;
2107
2108procedure TBlockSerial.ExceptCheck;
2109var
2110 e: ESynaSerError;
2111 s: string;
2112begin
2113 if FRaiseExcept and (FLastError <> sOK) then
2114 begin
2115 s := GetErrorDesc(FLastError);
2116 e := ESynaSerError.CreateFmt('Communication error %d: %s', [FLastError, s]);
2117 e.ErrorCode := FLastError;
2118 e.ErrorMessage := s;
2119 raise e;
2120 end;
2121end;
2122
2123procedure TBlockSerial.SetSynaError(ErrNumber: integer);
2124begin
2125 FLastError := ErrNumber;
2126 FLastErrorDesc := GetErrorDesc(FLastError);
2127end;
2128
2129procedure TBlockSerial.RaiseSynaError(ErrNumber: integer);
2130begin
2131 SetSynaError(ErrNumber);
2132 ExceptCheck;
2133end;
2134
2135procedure TBlockSerial.DoStatus(Reason: THookSerialReason; const Value: string);
2136begin
2137 if assigned(OnStatus) then
2138 OnStatus(Self, Reason, Value);
2139end;
2140
2141{======================================================================}
2142
2143class function TBlockSerial.GetErrorDesc(ErrorCode: integer): string;
2144begin
2145 Result:= '';
2146 case ErrorCode of
2147 sOK: Result := 'OK';
2148 ErrAlreadyOwned: Result := 'Port owned by other process';{HGJ}
2149 ErrAlreadyInUse: Result := 'Instance already in use'; {HGJ}
2150 ErrWrongParameter: Result := 'Wrong parameter at call'; {HGJ}
2151 ErrPortNotOpen: Result := 'Instance not yet connected'; {HGJ}
2152 ErrNoDeviceAnswer: Result := 'No device answer detected'; {HGJ}
2153 ErrMaxBuffer: Result := 'Maximal buffer length exceeded';
2154 ErrTimeout: Result := 'Timeout during operation';
2155 ErrNotRead: Result := 'Reading of data failed';
2156 ErrFrame: Result := 'Receive framing error';
2157 ErrOverrun: Result := 'Receive Overrun Error';
2158 ErrRxOver: Result := 'Receive Queue overflow';
2159 ErrRxParity: Result := 'Receive Parity Error';
2160 ErrTxFull: Result := 'Tranceive Queue is full';
2161 end;
2162 if Result = '' then
2163 begin
2164 Result := SysErrorMessage(ErrorCode);
2165 end;
2166end;
2167
2168
2169{---------- cpom Comport Ownership Manager Routines -------------
2170 by Hans-Georg Joepgen of Stuttgart, Germany.
2171 Copyright (c) 2002, by Hans-Georg Joepgen
2172
2173 Stefan Krauss of Stuttgart, Germany, contributed literature and Internet
2174 research results, invaluable advice and excellent answers to the Comport
2175 Ownership Manager.
2176}
2177
2178{$IFDEF UNIX}
2179
2180function TBlockSerial.LockfileName: String;
2181var
2182 s: string;
2183begin
2184 s := SeparateRight(FDevice, '/dev/');
2185 result := LockfileDirectory + '/LCK..' + s;
2186end;
2187
2188procedure TBlockSerial.CreateLockfile(PidNr: integer);
2189var
2190 f: TextFile;
2191 s: string;
2192begin
2193 // Create content for file
2194 s := IntToStr(PidNr);
2195 while length(s) < 10 do
2196 s := ' ' + s;
2197 // Create file
2198 try
2199 AssignFile(f, LockfileName);
2200 try
2201 Rewrite(f);
2202 writeln(f, s);
2203 finally
2204 CloseFile(f);
2205 end;
2206 // Allow all users to enjoy the benefits of cpom
2207 s := 'chmod a+rw ' + LockfileName;
2208{$IFNDEF FPC}
2209 FileSetReadOnly( LockfileName, False ) ;
2210 // Libc.system(pchar(s));
2211{$ELSE}
2212 fpSystem(s);
2213{$ENDIF}
2214 except
2215 // not raise exception, if you not have write permission for lock.
2216 on Exception do
2217 ;
2218 end;
2219end;
2220
2221function TBlockSerial.ReadLockfile: integer;
2222{Returns PID from Lockfile. Lockfile must exist.}
2223var
2224 f: TextFile;
2225 s: string;
2226begin
2227 AssignFile(f, LockfileName);
2228 Reset(f);
2229 try
2230 readln(f, s);
2231 finally
2232 CloseFile(f);
2233 end;
2234 Result := StrToIntDef(s, -1)
2235end;
2236
2237function TBlockSerial.cpomComportAccessible: boolean;
2238var
2239 MyPid: integer;
2240 Filename: string;
2241begin
2242 Filename := LockfileName;
2243 {$IFNDEF FPC}
2244 MyPid := Libc.getpid;
2245 {$ELSE}
2246 MyPid := fpGetPid;
2247 {$ENDIF}
2248 // Make sure, the Lock Files Directory exists. We need it.
2249 if not DirectoryExists(LockfileDirectory) then
2250 CreateDir(LockfileDirectory);
2251 // Check the Lockfile
2252 if not FileExists (Filename) then
2253 begin // comport is not locked. Lock it for us.
2254 CreateLockfile(MyPid);
2255 result := true;
2256 exit; // done.
2257 end;
2258 // Is port owned by orphan? Then it's time for error recovery.
2259 //FPC forgot to add getsid.. :-(
2260 {$IFNDEF FPC}
2261 if Libc.getsid(ReadLockfile) = -1 then
2262 begin // Lockfile was left from former desaster
2263 DeleteFile(Filename); // error recovery
2264 CreateLockfile(MyPid);
2265 result := true;
2266 exit;
2267 end;
2268 {$ENDIF}
2269 result := false // Sorry, port is owned by living PID and locked
2270end;
2271
2272procedure TBlockSerial.cpomReleaseComport;
2273begin
2274 DeleteFile(LockfileName);
2275end;
2276
2277{$ENDIF}
2278{----------------------------------------------------------------}
2279
2280{$IFDEF MSWINDOWS}
2281function GetSerialPortNames: string;
2282var
2283 reg: TRegistry;
2284 l, v: TStringList;
2285 n: integer;
2286begin
2287 l := TStringList.Create;
2288 v := TStringList.Create;
2289 reg := TRegistry.Create;
2290 try
2291{$IFNDEF VER100}
2292{$IFNDEF VER120}
2293 reg.Access := KEY_READ;
2294{$ENDIF}
2295{$ENDIF}
2296 reg.RootKey := HKEY_LOCAL_MACHINE;
2297 reg.OpenKey('\HARDWARE\DEVICEMAP\SERIALCOMM', false);
2298 reg.GetValueNames(l);
2299 for n := 0 to l.Count - 1 do
2300 v.Add(reg.ReadString(l[n]));
2301 Result := v.CommaText;
2302 finally
2303 reg.Free;
2304 l.Free;
2305 v.Free;
2306 end;
2307end;
2308{$ENDIF}
2309{$IFNDEF MSWINDOWS}
2310function GetSerialPortNames: string;
2311var
2312 Index: Integer;
2313 Data: string;
2314 TmpPorts: String;
2315 sr : TSearchRec;
2316begin
2317 try
2318 TmpPorts := '';
2319 if FindFirst('/dev/ttyS*', $FFFFFFFF, sr) = 0 then
2320 begin
2321 repeat
2322 if (sr.Attr and $FFFFFFFF) = Sr.Attr then
2323 begin
2324 data := sr.Name;
2325 index := length(data);
2326 while (index > 1) and (data[index] <> '/') do
2327 index := index - 1;
2328 TmpPorts := TmpPorts + ' ' + copy(data, 1, index + 1);
2329 end;
2330 until FindNext(sr) <> 0;
2331 end;
2332 FindClose(sr);
2333 finally
2334 Result:=TmpPorts;
2335 end;
2336end;
2337{$ENDIF}
2338
2339end.
Note: See TracBrowser for help on using the repository browser.