forked from serbod/dataport
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDataPortUART.pas
379 lines (330 loc) · 11.3 KB
/
DataPortUART.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
{
Serial communication port (UART). In Windows it COM-port, real or virtual.
In Linux it /dev/ttyS or /dev/ttyUSB. Also, Linux use file /var/FLock/LCK..ttyS for port FLocking
(C) Sergey Bodrov, 2012-2018
Properties:
Port - port name (COM1, /dev/ttyS01)
BaudRate - data excange speed
DataBits - default 8 (5 for Baudot code, 7 for true ASCII)
Parity - (N - None, O - Odd, E - Even, M - Mark or S - Space) default N
StopBits - (stb1, stb15, stb2), default stb1
FlowControl - (sfcNone, sfcSend, sfcReady, sfcSoft) default sfcNone
sfcSend - SEND signal pair CTS/RTS, used for hardware flow control
sfcReady - READY signal pair DTR/DSR, used for modem control
sfcSoft - software flow control XON/XOFF byte ($11 for resume and $13 for pause transmission)
MinDataBytes - minimal bytes count in buffer for triggering event OnDataAppear
Methods:
Open() - Opens port. As parameter it use port initialization string:
InitStr = 'Port,BaudRate,DataBits,Parity,StopBits,SoftFlow,HardFlow'
Port - COM port name (COM1, /dev/ttyS01)
BaudRate - connection speed (50..4000000 bits per second), default 9600
DataBits - default 8
Parity - (N - None, O - Odd, E - Even, M - Mark or S - Space) default N
StopBits - (1, 1.5, 2) default 0
SoftFlow - Enable XON/XOFF handshake, default 0
HardFlow - Enable CTS/RTS handshake, default 0
Events:
OnOpen - Triggered after sucсessful connection.
OnClose - Triggered after disconnection.
Roles:
Data Terminal Equipment (DTE) - computer terminal
Data Circuit-terminating Equipment (DCE) - modem, peripreral device
}
unit DataPortUART;
interface
uses
SysUtils, Classes, DataPort;
type
TSerialStopBits = (stb1, stb15, stb2);
TSerialFlowControl = (sfcNone, sfcSend, sfcReady, sfcSoft);
TModemStatus = record
{ RTS (Request to send) signal (w) - DTE requests the DCE prepare to transmit data. }
{ RTR (Ready To Receive) (w) - DTE is ready to receive data from DCE. If in use, RTS is assumed to be always asserted. }
RTS: Boolean;
{ CTS (Clear to send) signal (r) - DCE is ready to accept data from the DTE. }
CTS: boolean;
{ DTR (Data Terminal Ready) signal (w) - DTE is ready to receive, initiate, or continue a call. }
DTR: Boolean;
{ DSR (Data Set Ready) signal (r) - DCE is ready to receive and send data. }
DSR: Boolean;
{ Data Carrier Detect (r) - DCE is receiving a carrier from a remote DCE. }
Carrier: Boolean;
{ Ring Indicator (r) - DCE has detected an incoming ring signal on the telephone line. }
Ring: Boolean;
end;
{ TDataPortUART - serial DataPort }
TDataPortUART = class(TDataPort)
private
FOnModemStatus: TNotifyEvent;
procedure SetHardFlow(AValue: Boolean);
procedure SetSoftFlow(AValue: Boolean);
protected
FReadDataStr: AnsiString;
FLock: TMultiReadExclusiveWriteSynchronizer;
FPort: string;
FBaudRate: Integer;
FDataBits: Integer;
FParity: AnsiChar;
FStopBits: TSerialStopBits;
FFlowControl: TSerialFlowControl;
FSoftFlow: Boolean;
FHardFlow: Boolean;
FMinDataBytes: Integer;
FHalfDuplex: Boolean;
FModemStatus: TModemStatus;
procedure SetBaudRate(AValue: Integer); virtual;
procedure SetDataBits(AValue: Integer); virtual;
procedure SetParity(AValue: AnsiChar); virtual;
procedure SetStopBits(AValue: TSerialStopBits); virtual;
procedure SetFlowControl(AValue: TSerialFlowControl); virtual;
procedure OnIncomingMsgHandler(Sender: TObject; const AMsg: string); virtual;
procedure OnErrorHandler(Sender: TObject; const AMsg: string); virtual;
procedure OnConnectHandler(Sender: TObject); virtual;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy(); override;
{ Open serial DataPort
InitStr = 'Port,BaudRate,DataBits,Parity,StopBits,SoftFlow,HardFlow'
Port - COM port name (COM1, /dev/tty01)
BaudRate - connection speed (50..4000000 bits per second), default 9600
DataBits - default 8
Parity - (N - None, O - Odd, E - Even, M - Mark or S - Space) default N
StopBits - (1, 1.5, 2)
SoftFlow - Enable XON/XOFF handshake, default 0
HardFlow - Enable CTS/RTS handshake, default 0 }
procedure Open(const AInitStr: string = ''); override;
function Pull(ASize: Integer = MaxInt): AnsiString; override;
function Peek(ASize: Integer = MaxInt): AnsiString; override;
function PeekSize(): Cardinal; override;
{ Get modem wires status (DSR,CTS,Ring,Carrier) }
function GetModemStatus(): TModemStatus; virtual;
{ Set DTR (Data Terminal Ready) signal }
procedure SetDTR(AValue: Boolean); virtual;
{ Set RTS (Request to send) signal }
procedure SetRTS(AValue: Boolean); virtual;
{ Modem wires status }
property ModemStatus: TModemStatus read FModemStatus;
published
{ Serial port name (COM1, /dev/ttyS01) }
property Port: string read FPort write FPort;
{ BaudRate - connection speed (50..4000000 bits per second), default 9600 }
property BaudRate: Integer read FBaudRate write SetBaudRate;
{ DataBits - default 8 (5 for Baudot code, 7 for true ASCII) }
property DataBits: Integer read FDataBits write SetDataBits;
{ Parity - (N - None, O - Odd, E - Even, M - Mark or S - Space) default N }
property Parity: AnsiChar read FParity write SetParity;
{ StopBits - (stb1, stb15, stb2), default stb1 }
property StopBits: TSerialStopBits read FStopBits write SetStopBits;
{ Flow control - (sfcNone, sfcRTS, sfcDTR, sfcXON) default sfcNone
sfcSend - SEND signal pair CTS/RTS, used for hardware flow control
sfcReady - READY signal pair DTR/DSR, used for modem control
sfcSoft - software flow control XON/XOFF byte ($11 for resume and $13 for pause transmission) }
property FlowControl: TSerialFlowControl read FFlowControl write SetFlowControl;
{ deprecated, set to False and use FlowControl }
property SoftFlow: Boolean read FSoftFlow write SetSoftFlow; {$ifdef FPC}deprecated;{$endif}
{ deprecated, set to False and use FlowControl }
property HardFlow: Boolean read FHardFlow write SetHardFlow; {$ifdef FPC}deprecated;{$endif}
{ Minimum bytes in incoming buffer to trigger OnDataAppear }
property MinDataBytes: Integer read FMinDataBytes write FMinDataBytes;
{ Use half-duplex for send and receive data }
property HalfDuplex: Boolean read FHalfDuplex write FHalfDuplex;
property Active;
property OnDataAppear;
property OnError;
property OnOpen;
property OnClose;
{ Triggered when modem status changed }
property OnModemStatus: TNotifyEvent read FOnModemStatus write FOnModemStatus;
end;
function ExtractFirstWord(var s: string; const delimiter: string = ' '): string;
implementation
function ExtractFirstWord(var s: string; const delimiter: string = ' '): string;
var
i: Integer;
begin
Result := '';
i := Pos(delimiter, s);
if i > 0 then
begin
Result := Copy(s, 1, i - 1);
s := Copy(s, i + 1, maxint);
end
else
begin
Result := s;
s := '';
end;
end;
{ TDataPortUART }
constructor TDataPortUART.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FLock := TMultiReadExclusiveWriteSynchronizer.Create();
FPort := 'COM1';
FBaudRate := 9600;
FDataBits := 8;
FParity := 'N';
FStopBits := stb1;
FFlowControl := sfcNone;
FMinDataBytes := 1;
FActive := False;
//Self.slReadData := TStringList.Create();
FReadDataStr := '';
end;
procedure TDataPortUART.Open(const AInitStr: string = '');
var
s, ss: string;
begin
ss := AInitStr;
// Port
s := ExtractFirstWord(ss, ',');
if s <> '' then
FPort := s;
// BaudRate
s := ExtractFirstWord(ss, ',');
FBaudRate := StrToIntDef(s, FBaudRate);
// DataBits
s := ExtractFirstWord(ss, ',');
FDataBits := StrToIntDef(s, FDataBits);
// Parity
s := ExtractFirstWord(ss, ',');
if s <> '' then
FParity := s[1];
if Pos(FParity, 'NOEMSnoems') = 0 then
FParity := 'N';
// StopBits
s := ExtractFirstWord(ss, ',');
if s = '1' then
FStopBits := stb1
else if s = '1.5' then
FStopBits := stb15
else if s = '2' then
FStopBits := stb2;
FFlowControl := sfcNone;
// SoftFlow
s := ExtractFirstWord(ss, ',');
if s = '1' then
FFlowControl := sfcSoft;
// HardFlow
s := ExtractFirstWord(ss, ',');
if s = '1' then
FFlowControl := sfcSend;
// don't inherits Open() - OnOpen event will be after successfull connection
end;
destructor TDataPortUART.Destroy();
begin
FreeAndNil(FLock);
inherited Destroy();
end;
procedure TDataPortUART.OnIncomingMsgHandler(Sender: TObject; const AMsg: string);
begin
if AMsg <> '' then
begin
if FLock.BeginWrite then
begin
FReadDataStr := FReadDataStr + AMsg;
FLock.EndWrite;
if Assigned(FOnDataAppear) then
FOnDataAppear(Self);
end;
end
else
begin
FModemStatus := GetModemStatus();
if Assigned(OnModemStatus) then
OnModemStatus(Self);
end;
end;
procedure TDataPortUART.OnErrorHandler(Sender: TObject; const AMsg: string);
begin
FActive := False;
if (AMsg <> '') and Assigned(OnError) then
OnError(Self, AMsg)
else if Assigned(OnClose) then
OnClose(Self);
end;
procedure TDataPortUART.OnConnectHandler(Sender: TObject);
begin
FActive := True;
if Assigned(OnOpen) then
OnOpen(Self);
end;
function TDataPortUART.Peek(ASize: Integer): AnsiString;
begin
FLock.BeginRead();
try
Result := Copy(FReadDataStr, 1, ASize);
finally
FLock.EndRead();
end;
end;
function TDataPortUART.PeekSize(): Cardinal;
begin
FLock.BeginRead();
try
Result := Cardinal(Length(FReadDataStr));
finally
FLock.EndRead();
end;
end;
function TDataPortUART.GetModemStatus(): TModemStatus;
begin
Result := FModemStatus;
end;
procedure TDataPortUART.SetDTR(AValue: Boolean);
begin
FModemStatus.DTR := AValue;
end;
procedure TDataPortUART.SetRTS(AValue: Boolean);
begin
FModemStatus.RTS := AValue;
end;
function TDataPortUART.Pull(ASize: Integer): AnsiString;
begin
Result := '';
if FLock.BeginWrite() then
begin
try
Result := Copy(FReadDataStr, 1, ASize);
Delete(FReadDataStr, 1, ASize);
finally
FLock.EndWrite();
end;
end;
end;
procedure TDataPortUART.SetHardFlow(AValue: Boolean);
begin
FHardFlow := AValue;
if FHardFlow then
FFlowControl := sfcSend;
end;
procedure TDataPortUART.SetSoftFlow(AValue: Boolean);
begin
FSoftFlow := AValue;
if FSoftFlow then
FFlowControl := sfcSoft;
end;
procedure TDataPortUART.SetBaudRate(AValue: Integer);
begin
FBaudRate := AValue;
end;
procedure TDataPortUART.SetDataBits(AValue: Integer);
begin
if (AValue < 5) or (AValue > 9) then
Exit;
FDataBits := AValue;
end;
procedure TDataPortUART.SetFlowControl(AValue: TSerialFlowControl);
begin
FFlowControl := AValue;
end;
procedure TDataPortUART.SetParity(AValue: AnsiChar);
begin
if Pos(AValue, 'NOEMSnoems') > 0 then
FParity := AValue;
end;
procedure TDataPortUART.SetStopBits(AValue: TSerialStopBits);
begin
FStopBits := AValue;
end;
end.