OSDP.Net 5.2.1
A .NET library for implementing the Open Supervised Device Protocol (OSDP)
Loading...
Searching...
No Matches
FileTransferReceiver.cs
1using System;
4
5namespace OSDP.Net;
6
32{
38 public const int DefaultMaximumFileSize = 16 * 1024 * 1024;
39
40 private readonly int _maximumFileSize;
41
42 private byte[] _buffer;
43 private int _highWaterMark;
44
52 public FileTransferReceiver(int maximumFileSize = DefaultMaximumFileSize)
53 {
54 if (maximumFileSize <= 0)
55 {
56 throw new ArgumentOutOfRangeException(nameof(maximumFileSize), maximumFileSize,
57 "The maximum file size must be greater than zero.");
58 }
59
60 _maximumFileSize = maximumFileSize;
61 }
62
66 public bool IsTransferInProgress => _buffer != null;
67
71 public bool IsComplete => _buffer != null && _highWaterMark >= _buffer.Length;
72
77 public byte FileType { get; private set; }
78
82 public int TotalSize => _buffer?.Length ?? 0;
83
87 public int ReceivedSize => _highWaterMark;
88
94
99 public ushort RequestedDelay { get; set; }
100
105 public ushort UpdateMessageMaximum { get; set; }
106
119 {
120 if (fragment == null) throw new ArgumentNullException(nameof(fragment));
121
122 var data = fragment.Fragment;
123 var fragmentData = data.DataFragment ?? [];
124
125 // A fragment that is internally inconsistent is malformed, regardless of transfer state.
126 if (data.TotalSize < 0 || data.Offset < 0 || data.TotalSize > _maximumFileSize ||
127 data.Offset > data.TotalSize || data.FragmentSize > data.TotalSize - data.Offset ||
128 data.FragmentSize > fragmentData.Length)
129 {
130 Reset();
131 return Status(FileTransferStatus.StatusDetail.FileDataUnacceptable);
132 }
133
134 // Offset zero starts a transfer, which also makes an ACU retry from the beginning recover
135 // cleanly rather than colliding with the abandoned buffer.
136 if (data.Offset == 0)
137 {
138 _buffer = new byte[data.TotalSize];
139 _highWaterMark = 0;
140 FileType = fragment.Type;
141 }
142 else if (_buffer == null)
143 {
144 // The ACU resumed mid-file with no transfer established, so there is nothing to append to.
145 return Status(FileTransferStatus.StatusDetail.FileDataUnacceptable);
146 }
147
148 // Changing the declared size or type mid-transfer would silently corrupt the assembled file.
149 if (data.TotalSize != _buffer.Length || fragment.Type != FileType)
150 {
151 Reset();
152 return Status(FileTransferStatus.StatusDetail.FileDataUnacceptable);
153 }
154
155 // A fragment starting past the contiguous region would leave a hole the PD cannot fill, since
156 // osdp_FTSTAT has no way to request a specific offset.
157 if (data.Offset > _highWaterMark)
158 {
159 Reset();
160 return Status(FileTransferStatus.StatusDetail.FileDataUnacceptable);
161 }
162
163 if (data.FragmentSize > 0)
164 {
165 Array.Copy(fragmentData, 0, _buffer, data.Offset, data.FragmentSize);
166 _highWaterMark = Math.Max(_highWaterMark, data.Offset + data.FragmentSize);
167 }
168
169 return Status(IsComplete
170 ? FileTransferStatus.StatusDetail.FileContentsProcessed
171 : FileTransferStatus.StatusDetail.OkToProceed);
172 }
173
179 public byte[] GetFile()
180 {
181 if (!IsComplete)
182 {
183 throw new InvalidOperationException(
184 "The file transfer has not completed, so the assembled contents are not available.");
185 }
186
187 return (byte[])_buffer.Clone();
188 }
189
193 public void Reset()
194 {
195 _buffer = null;
196 _highWaterMark = 0;
197 FileType = 0;
198 }
199
200 private FileTransferStatus Status(FileTransferStatus.StatusDetail detail) =>
202}
byte[] GetFile()
Returns a copy of the assembled file.
byte FileType
Gets the file transfer type declared by the ACU, or zero when no transfer is in progress.
const int DefaultMaximumFileSize
Largest file accepted when the caller does not specify a limit.
FileTransferStatus AcceptFragment(FileTransferFragment fragment)
Accepts one file transfer fragment and produces the status reply to send back to the ACU.
bool IsComplete
Gets a value indicating whether every byte of the current transfer has been received.
bool IsTransferInProgress
Gets a value indicating whether a transfer has been started and has not yet been reset.
int ReceivedSize
Gets the number of contiguous bytes received so far.
FileTransferStatus.ControlFlags Action
Gets or sets the control flags reported to the ACU with every status reply.
ushort RequestedDelay
Gets or sets the delay in milliseconds the ACU is asked to wait before sending the next osdp_FILETRAN...
ushort UpdateMessageMaximum
Gets or sets the alternate maximum fragment size reported to the ACU.
int TotalSize
Gets the total size of the file being transferred, or zero when no transfer is in progress.
FileTransferReceiver(int maximumFileSize=DefaultMaximumFileSize)
Initializes a new instance of the FileTransferReceiver class.
void Reset()
Discards any transfer in progress and returns the receiver to its initial state.
Command data to send a data fragment of a file to a PD.
MessageDataFragment Fragment
Get the message data fragment.
The PD file transfer status sent as a reply.