1 | unit CommFrame;
|
---|
2 |
|
---|
3 | interface
|
---|
4 |
|
---|
5 | uses
|
---|
6 | Classes, Dialogs, SysUtils, SpecializedList, BinarySerializer,
|
---|
7 | CommPin;
|
---|
8 |
|
---|
9 | type
|
---|
10 | TFrameState = (fsOutside, fsStart, fsInside, fsEnd);
|
---|
11 |
|
---|
12 | { TCommFrame }
|
---|
13 |
|
---|
14 | TCommFrame = class(TCommNode)
|
---|
15 | private
|
---|
16 | LastCharIsSpecialChar: Boolean;
|
---|
17 | ReceiveBuffer: TBinarySerializer;
|
---|
18 | FrameState: TFrameState;
|
---|
19 | FFrameErrorCount: Integer;
|
---|
20 | FCRCErrorCount: Integer;
|
---|
21 | function GetStreamCRC8(Stream: TListByte): Byte;
|
---|
22 | procedure RawDataReceive(Sender: TCommPin; Stream: TListByte);
|
---|
23 | procedure RawSetStatus(Sender: TCommPin; Status: Integer);
|
---|
24 | procedure FrameDataReceive(Sender: TCommPin; Stream: TListByte);
|
---|
25 | procedure FrameSetStatus(Sender: TCommPin; Status: Integer);
|
---|
26 | public
|
---|
27 | RawDataPin: TCommPin;
|
---|
28 | FrameDataPin: TCommPin;
|
---|
29 | PacketLoss: Real;
|
---|
30 | SpecialChar: Byte;
|
---|
31 | ControlCodeFrameStart: Byte;
|
---|
32 | ControlCodeFrameEnd: Byte;
|
---|
33 | ControlCodeSpecialChar: Byte;
|
---|
34 | function ComputeRawSize(DataStream: TListByte): Integer;
|
---|
35 | constructor Create(AOwner: TComponent); override;
|
---|
36 | destructor Destroy; override;
|
---|
37 | property FrameErrorCount: Integer read FFrameErrorCount;
|
---|
38 | property CRCErrorCount: Integer read FCRCErrorCount;
|
---|
39 | end;
|
---|
40 |
|
---|
41 |
|
---|
42 | implementation
|
---|
43 |
|
---|
44 | { TCommFrame }
|
---|
45 |
|
---|
46 | constructor TCommFrame.Create(AOwner: TComponent);
|
---|
47 | begin
|
---|
48 | inherited;
|
---|
49 | ReceiveBuffer := TBinarySerializer.Create;
|
---|
50 | ReceiveBuffer.List := TListByte.Create;
|
---|
51 | ReceiveBuffer.OwnsList := True;
|
---|
52 | RawDataPin := TCommPin.Create;
|
---|
53 | RawDataPin.OnReceive := RawDataReceive;
|
---|
54 | RawDataPin.Node := Self;
|
---|
55 | FrameDataPin := TCommPin.Create;
|
---|
56 | FrameDataPin.OnReceive := FrameDataReceive;
|
---|
57 | FrameDataPin.Node := Self;
|
---|
58 | PacketLoss := 0;
|
---|
59 | SpecialChar := $fe;
|
---|
60 | ControlCodeFrameStart := $fd;
|
---|
61 | ControlCodeFrameEnd := $fc;
|
---|
62 | ControlCodeSpecialChar := $fb;
|
---|
63 | end;
|
---|
64 |
|
---|
65 | destructor TCommFrame.Destroy;
|
---|
66 | begin
|
---|
67 | FreeAndNil(RawDataPin);
|
---|
68 | FreeAndNil(FrameDataPin);
|
---|
69 | FreeAndNil(ReceiveBuffer);
|
---|
70 | inherited;
|
---|
71 | end;
|
---|
72 |
|
---|
73 | procedure TCommFrame.FrameDataReceive(Sender: TCommPin; Stream: TListByte);
|
---|
74 | var
|
---|
75 | RawData: TBinarySerializer;
|
---|
76 | I: Integer;
|
---|
77 | Character: Byte;
|
---|
78 | CRC: Byte;
|
---|
79 | begin
|
---|
80 | // Write CRC code to end of frame
|
---|
81 | CRC := GetStreamCRC8(Stream);
|
---|
82 |
|
---|
83 | // Byte stuffing
|
---|
84 | try
|
---|
85 | RawData := TBinarySerializer.Create;
|
---|
86 | RawData.List := TListByte.Create;
|
---|
87 | RawData.OwnsList := True;
|
---|
88 | RawData.WriteByte(SpecialChar);
|
---|
89 | RawData.WriteByte(ControlCodeFrameStart);
|
---|
90 | for I := 0 to Stream.Count - 1 do begin
|
---|
91 | Character := Stream[I];
|
---|
92 | if Character = SpecialChar then begin
|
---|
93 | RawData.WriteByte(SpecialChar);
|
---|
94 | RawData.WriteByte(ControlCodeSpecialChar);
|
---|
95 | end else RawData.WriteByte(Character);
|
---|
96 | end;
|
---|
97 |
|
---|
98 | Character := CRC;
|
---|
99 | if Character = SpecialChar then begin
|
---|
100 | RawData.WriteByte(SpecialChar);
|
---|
101 | RawData.WriteByte(ControlCodeSpecialChar);
|
---|
102 | end else RawData.WriteByte(Character);
|
---|
103 |
|
---|
104 | RawData.WriteByte(SpecialChar);
|
---|
105 | RawData.WriteByte(ControlCodeFrameEnd);
|
---|
106 | if Random >= PacketLoss then
|
---|
107 | RawDataPin.Send(RawData.List);
|
---|
108 | finally
|
---|
109 | RawData.Free;
|
---|
110 | end;
|
---|
111 | end;
|
---|
112 |
|
---|
113 | procedure TCommFrame.FrameSetStatus(Sender: TCommPin; Status: Integer);
|
---|
114 | begin
|
---|
115 | RawDataPin.Status := Status;
|
---|
116 | end;
|
---|
117 |
|
---|
118 | function TCommFrame.ComputeRawSize(DataStream: TListByte): Integer;
|
---|
119 | var
|
---|
120 | I: Integer;
|
---|
121 | begin
|
---|
122 | Result := 5; // FrameStart + CRC + FrameEnd
|
---|
123 | for I := 0 to DataStream.Count - 1 do
|
---|
124 | if DataStream[I] = SpecialChar then Inc(Result, 2)
|
---|
125 | else Inc(Result, 1);
|
---|
126 | end;
|
---|
127 |
|
---|
128 | procedure TCommFrame.RawDataReceive(Sender: TCommPin; Stream: TListByte);
|
---|
129 | var
|
---|
130 | Character: Byte;
|
---|
131 | CRC: Byte;
|
---|
132 | ExpectedCRC: Byte;
|
---|
133 | I: Integer;
|
---|
134 | begin
|
---|
135 | for I := 0 to Stream.Count - 1 do begin
|
---|
136 | Character := Stream[I];
|
---|
137 | if LastCharIsSpecialChar then begin
|
---|
138 | if Character = ControlCodeSpecialChar then begin
|
---|
139 | ReceiveBuffer.WriteByte(SpecialChar)
|
---|
140 | end else
|
---|
141 | if Character = ControlCodeFrameStart then begin
|
---|
142 | if FrameState = fsInside then
|
---|
143 | Inc(FFrameErrorCount);
|
---|
144 | ReceiveBuffer.List.Count := 0;
|
---|
145 | ReceiveBuffer.Position := 0;
|
---|
146 | FrameState := fsInside;
|
---|
147 | end else
|
---|
148 | if Character = ControlCodeFrameEnd then begin
|
---|
149 | if FrameState = fsInside then begin
|
---|
150 | // Check CRC
|
---|
151 | if ReceiveBuffer.List.Count > 0 then begin
|
---|
152 | ReceiveBuffer.Position := ReceiveBuffer.List.Count - 1;
|
---|
153 | CRC := ReceiveBuffer.ReadByte;
|
---|
154 | ReceiveBuffer.List.Count := ReceiveBuffer.List.Count - 1;
|
---|
155 | ExpectedCRC := GetStreamCRC8(ReceiveBuffer.List);
|
---|
156 |
|
---|
157 | if ExpectedCRC <> CRC then Inc(FCRCErrorCount)
|
---|
158 | else begin
|
---|
159 | //if Random >= PacketLoss then
|
---|
160 | FrameDataPin.Send(ReceiveBuffer.List);
|
---|
161 | end;
|
---|
162 | end else Inc(FCRCErrorCount);
|
---|
163 | end else Inc(FFrameErrorCount);
|
---|
164 | FrameState := fsOutside;
|
---|
165 | end;
|
---|
166 | LastCharIsSpecialChar := False;
|
---|
167 | end else begin
|
---|
168 | if Character = SpecialChar then LastCharIsSpecialChar := True
|
---|
169 | else ReceiveBuffer.WriteByte(Character);
|
---|
170 | end;
|
---|
171 | end;
|
---|
172 | end;
|
---|
173 |
|
---|
174 | procedure TCommFrame.RawSetStatus(Sender: TCommPin; Status: Integer);
|
---|
175 | begin
|
---|
176 | FrameDataPin.Status := Status;
|
---|
177 | end;
|
---|
178 |
|
---|
179 | function TCommFrame.GetStreamCRC8(Stream: TListByte): Byte;
|
---|
180 | var
|
---|
181 | I: Integer;
|
---|
182 | B: Integer;
|
---|
183 | Pom: Byte;
|
---|
184 | const
|
---|
185 | Polynom: Byte = $18;
|
---|
186 | begin
|
---|
187 | Pom := 0;
|
---|
188 | Result := 0;
|
---|
189 | for I := 0 to Stream.Count - 1 do begin
|
---|
190 | Pom := Stream[I];
|
---|
191 | for B := 0 to 7 do begin
|
---|
192 | if ((Result xor Pom) and 1) = 1 then
|
---|
193 | Result := ((Result xor Polynom) shr 1) or $80
|
---|
194 | else Result := Result shr 1;
|
---|
195 | Pom := (Pom shr 1) or ((Pom shl 7) and $80); // Rotace vpravo
|
---|
196 | end;
|
---|
197 | end;
|
---|
198 | end;
|
---|
199 |
|
---|
200 | end.
|
---|