1use bacnet_types::enums::{
7 ErrorClass, ErrorCode, FileAccessMethod, ObjectType, PropertyIdentifier,
8};
9use bacnet_types::error::Error;
10use bacnet_types::primitives::{Date, ObjectIdentifier, PropertyValue, Time};
11use std::borrow::Cow;
12use std::sync::Arc;
13
14use crate::clock::ClockReader;
15use crate::common::{self, read_identity_properties};
16use crate::traits::BACnetObject;
17
18mod metadata;
19mod resize;
20
21pub const DEFAULT_MAX_FILE_SIZE: u64 = 1_048_576;
34
35pub const DEFAULT_MAX_RECORD_COUNT: u64 = 10_000;
46
47const MAX_RECORD_CAP: u64 = DEFAULT_MAX_RECORD_COUNT;
50
51#[doc(hidden)]
59pub trait FileConfiguration: Send + Sync {
60 fn set_access_method(&mut self, method: FileAccessMethod);
62
63 fn set_stream_data(&mut self, data: Vec<u8>) -> Result<(), Error>;
65
66 fn stream_data(&self) -> Result<&[u8], Error>;
68
69 fn set_record_data(&mut self, records: Vec<Vec<u8>>) -> Result<(), Error>;
71
72 fn record_data(&self) -> Result<&[Vec<u8>], Error>;
74
75 fn set_max_file_size(&mut self, max_octets: u64) -> u64;
77
78 fn set_max_record_count(&mut self, max_records: u64) -> u64;
80}
81
82#[derive(Debug, Clone, PartialEq, Eq)]
84pub struct FileStreamRead {
85 pub data: Vec<u8>,
88 pub end_of_file: bool,
91}
92
93#[derive(Debug, Clone, PartialEq, Eq)]
95pub struct FileRecordRead {
96 pub records: Vec<Vec<u8>>,
99 pub end_of_file: bool,
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq)]
110pub enum FileWriteStart {
111 At(u64),
114 Append,
116}
117
118pub trait FileStorage: Send + Sync {
152 fn read_stream(&self, _start: u64, _count: u64) -> Result<FileStreamRead, Error> {
154 Err(file_access_denied())
155 }
156
157 fn write_stream(&mut self, _start: FileWriteStart, _data: &[u8]) -> Result<u64, Error> {
162 Err(file_access_denied())
163 }
164
165 fn read_records(&self, _start: u64, _count: u64) -> Result<FileRecordRead, Error> {
167 Err(file_access_denied())
168 }
169
170 fn write_records(
174 &mut self,
175 _start: FileWriteStart,
176 _records: &[Vec<u8>],
177 ) -> Result<u64, Error> {
178 Err(file_access_denied())
179 }
180}
181
182fn invalid_file_access_method() -> Error {
183 common::protocol_error(ErrorClass::SERVICES, ErrorCode::INVALID_FILE_ACCESS_METHOD)
184}
185
186fn file_access_denied() -> Error {
187 common::protocol_error(ErrorClass::SERVICES, ErrorCode::FILE_ACCESS_DENIED)
188}
189
190fn invalid_file_start_position() -> Error {
191 common::protocol_error(ErrorClass::SERVICES, ErrorCode::INVALID_FILE_START_POSITION)
192}
193
194fn file_full() -> Error {
195 common::protocol_error(ErrorClass::OBJECT, ErrorCode::FILE_FULL)
196}
197
198const MAX_REPRESENTABLE: u64 = i32::MAX as u64;
201
202fn octet_total(records: &[Vec<u8>]) -> u64 {
203 records.iter().map(|r| r.len() as u64).sum()
204}
205
206pub struct FileObject {
217 oid: ObjectIdentifier,
218 name: String,
219 description: String,
220 file_type: String,
221 file_size: u64,
222 modification_date: (Date, Time),
223 archive: bool,
224 read_only: bool,
225 file_access_method: u32,
228 record_count: Option<u64>,
230 data: Vec<u8>,
232 records: Vec<Vec<u8>>,
234 max_file_size: u64,
236 max_record_count: u64,
238 clock: Option<Arc<dyn ClockReader>>,
240}
241
242impl FileObject {
243 pub fn new(
248 instance: u32,
249 name: impl Into<String>,
250 file_type: impl Into<String>,
251 ) -> Result<Self, Error> {
252 let oid = ObjectIdentifier::new(ObjectType::FILE, instance)?;
253 Ok(Self {
254 oid,
255 name: name.into(),
256 description: String::new(),
257 file_type: file_type.into(),
258 file_size: 0,
259 modification_date: crate::clock::UNSPECIFIED_DATETIME,
260 archive: false,
261 read_only: false,
262 file_access_method: FileAccessMethod::STREAM_ACCESS.to_raw(),
263 record_count: None,
264 data: Vec::new(),
265 records: Vec::new(),
266 max_file_size: DEFAULT_MAX_FILE_SIZE,
267 max_record_count: DEFAULT_MAX_RECORD_COUNT,
268 clock: None,
269 })
270 }
271
272 pub fn set_description(&mut self, desc: impl Into<String>) {
274 self.description = desc.into();
275 }
276
277 pub fn set_file_type(&mut self, ft: impl Into<String>) {
279 self.file_type = ft.into();
280 }
281
282 pub fn set_data(&mut self, data: Vec<u8>) {
287 self.data = data;
288 if self.file_access_method != FileAccessMethod::RECORD_ACCESS.to_raw() {
289 self.file_size = self.data.len() as u64;
290 }
291 self.mark_modified();
292 }
293
294 pub fn data(&self) -> &[u8] {
296 &self.data
297 }
298
299 pub fn set_file_access_method(&mut self, method: u32) {
307 let old_file_size = self.file_size;
308 self.file_access_method = method;
309 if method == FileAccessMethod::RECORD_ACCESS.to_raw() {
310 self.record_count = Some(self.records.len() as u64);
311 self.file_size = octet_total(&self.records);
312 } else {
313 self.record_count = None;
314 self.file_size = self.data.len() as u64;
315 }
316 if self.file_size != old_file_size {
317 self.mark_modified();
318 }
319 }
320
321 pub fn set_records(&mut self, records: Vec<Vec<u8>>) {
324 self.records = records;
325 if self.file_access_method == FileAccessMethod::RECORD_ACCESS.to_raw() {
326 self.record_count = Some(self.records.len() as u64);
327 self.file_size = octet_total(&self.records);
328 }
329 self.mark_modified();
330 }
331
332 pub fn records(&self) -> &[Vec<u8>] {
334 &self.records
335 }
336
337 pub fn set_modification_date(&mut self, date: Date, time: Time) {
339 let modification_date = (date, time);
340 if self.modification_date != modification_date {
341 self.modification_date = modification_date;
342 self.archive = false;
343 }
344 }
345
346 pub fn set_archive(&mut self, archive: bool) {
348 self.archive = archive;
349 }
350
351 pub fn set_read_only(&mut self, read_only: bool) {
353 self.read_only = read_only;
354 }
355
356 pub fn file_size(&self) -> u64 {
358 self.file_size
359 }
360
361 pub fn archive(&self) -> bool {
363 self.archive
364 }
365
366 pub fn read_only(&self) -> bool {
368 self.read_only
369 }
370
371 pub fn set_max_file_size(&mut self, max_octets: u64) {
378 self.max_file_size = max_octets.min(MAX_REPRESENTABLE);
379 }
380
381 pub fn max_file_size(&self) -> u64 {
383 self.max_file_size
384 }
385
386 pub fn set_max_record_count(&mut self, max_records: u64) {
396 self.max_record_count = max_records.min(MAX_RECORD_CAP).min(MAX_REPRESENTABLE);
397 }
398
399 pub fn max_record_count(&self) -> u64 {
401 self.max_record_count
402 }
403
404 fn require_access(&self, method: FileAccessMethod) -> Result<(), Error> {
407 if self.file_access_method == method.to_raw() {
408 Ok(())
409 } else {
410 Err(invalid_file_access_method())
411 }
412 }
413
414 fn mark_modified(&mut self) {
415 self.modification_date = crate::clock::stamp_datetime(self.clock.as_deref());
416 self.archive = false;
417 }
418}
419
420impl FileStorage for FileObject {
421 fn read_stream(&self, start: u64, count: u64) -> Result<FileStreamRead, Error> {
422 self.require_access(FileAccessMethod::STREAM_ACCESS)?;
423 let len = self.data.len() as u64;
424 if start > len {
425 return Err(invalid_file_start_position());
426 }
427 let end = start.saturating_add(count).min(len);
428 Ok(FileStreamRead {
429 data: self.data[start as usize..end as usize].to_vec(),
430 end_of_file: len == 0 || (end >= len && end > start),
431 })
432 }
433
434 fn write_stream(&mut self, start: FileWriteStart, data: &[u8]) -> Result<u64, Error> {
435 self.require_access(FileAccessMethod::STREAM_ACCESS)?;
436 let len = self.data.len() as u64;
437 let start = match start {
438 FileWriteStart::At(offset) => offset,
439 FileWriteStart::Append => len,
440 };
441 let end = start.checked_add(data.len() as u64).ok_or_else(file_full)?;
442 if end > self.max_file_size.max(len) {
443 return Err(file_full());
444 }
445 let (start_idx, end_idx) = (
446 usize::try_from(start).map_err(|_| file_full())?,
447 usize::try_from(end).map_err(|_| file_full())?,
448 );
449 let mut updated = std::mem::take(&mut self.data);
450 if end_idx > updated.len() {
451 updated.resize(end_idx, 0);
452 }
453 updated[start_idx..end_idx].copy_from_slice(data);
454 self.set_data(updated);
455 Ok(start)
456 }
457
458 fn read_records(&self, start: u64, count: u64) -> Result<FileRecordRead, Error> {
459 self.require_access(FileAccessMethod::RECORD_ACCESS)?;
460 let len = self.records.len() as u64;
461 if start > len {
462 return Err(invalid_file_start_position());
463 }
464 let end = start.saturating_add(count).min(len);
465 Ok(FileRecordRead {
466 records: self.records[start as usize..end as usize].to_vec(),
467 end_of_file: len == 0 || (end >= len && end > start),
468 })
469 }
470
471 fn write_records(&mut self, start: FileWriteStart, records: &[Vec<u8>]) -> Result<u64, Error> {
472 self.require_access(FileAccessMethod::RECORD_ACCESS)?;
473 let len = self.records.len() as u64;
474 let start = match start {
475 FileWriteStart::At(index) => index,
476 FileWriteStart::Append => len,
477 };
478 let end = start
479 .checked_add(records.len() as u64)
480 .ok_or_else(file_full)?;
481 if end > self.max_record_count.max(len) {
482 return Err(file_full());
483 }
484 let (start_idx, end_idx) = (
485 usize::try_from(start).map_err(|_| file_full())?,
486 usize::try_from(end).map_err(|_| file_full())?,
487 );
488 let existing = octet_total(&self.records);
491 let replaced = octet_total(
492 &self.records[start_idx.min(self.records.len())..end_idx.min(self.records.len())],
493 );
494 let projected = existing - replaced + octet_total(records);
495 if projected > self.max_file_size.max(existing) {
496 return Err(file_full());
497 }
498 let mut updated = std::mem::take(&mut self.records);
499 if end_idx > updated.len() {
500 updated.resize(end_idx, Vec::new());
501 }
502 updated[start_idx..end_idx].clone_from_slice(records);
503 self.set_records(updated);
504 Ok(start)
505 }
506}
507
508impl FileConfiguration for FileObject {
509 fn set_access_method(&mut self, method: FileAccessMethod) {
510 FileObject::set_file_access_method(self, method.to_raw());
511 }
512
513 fn set_stream_data(&mut self, data: Vec<u8>) -> Result<(), Error> {
514 self.require_access(FileAccessMethod::STREAM_ACCESS)?;
515 FileObject::set_data(self, data);
516 Ok(())
517 }
518
519 fn stream_data(&self) -> Result<&[u8], Error> {
520 self.require_access(FileAccessMethod::STREAM_ACCESS)?;
521 Ok(FileObject::data(self))
522 }
523
524 fn set_record_data(&mut self, records: Vec<Vec<u8>>) -> Result<(), Error> {
525 self.require_access(FileAccessMethod::RECORD_ACCESS)?;
526 FileObject::set_records(self, records);
527 Ok(())
528 }
529
530 fn record_data(&self) -> Result<&[Vec<u8>], Error> {
531 self.require_access(FileAccessMethod::RECORD_ACCESS)?;
532 Ok(FileObject::records(self))
533 }
534
535 fn set_max_file_size(&mut self, max_octets: u64) -> u64 {
536 FileObject::set_max_file_size(self, max_octets);
537 FileObject::max_file_size(self)
538 }
539
540 fn set_max_record_count(&mut self, max_records: u64) -> u64 {
541 FileObject::set_max_record_count(self, max_records);
542 FileObject::max_record_count(self)
543 }
544}
545
546impl BACnetObject for FileObject {
547 fn object_identifier(&self) -> ObjectIdentifier {
548 self.oid
549 }
550
551 fn object_name(&self) -> &str {
552 &self.name
553 }
554
555 fn read_property(
556 &self,
557 property: PropertyIdentifier,
558 array_index: Option<u32>,
559 ) -> Result<PropertyValue, Error> {
560 if let Some(result) = read_identity_properties!(self, property, array_index) {
562 return result;
563 }
564
565 match property {
566 p if p == PropertyIdentifier::OBJECT_TYPE => {
567 Ok(PropertyValue::Enumerated(ObjectType::FILE.to_raw()))
568 }
569 p if p == PropertyIdentifier::FILE_TYPE => {
570 Ok(PropertyValue::CharacterString(self.file_type.clone()))
571 }
572 p if p == PropertyIdentifier::FILE_SIZE => Ok(PropertyValue::Unsigned(self.file_size)),
573 p if p == PropertyIdentifier::MODIFICATION_DATE => Ok(PropertyValue::List(vec![
574 PropertyValue::Date(self.modification_date.0),
575 PropertyValue::Time(self.modification_date.1),
576 ])),
577 p if p == PropertyIdentifier::ARCHIVE => Ok(PropertyValue::Boolean(self.archive)),
578 p if p == PropertyIdentifier::READ_ONLY => Ok(PropertyValue::Boolean(self.read_only)),
579 p if p == PropertyIdentifier::FILE_ACCESS_METHOD => {
580 Ok(PropertyValue::Enumerated(self.file_access_method))
581 }
582 p if p == PropertyIdentifier::RECORD_COUNT => match self.record_count {
583 Some(count) => Ok(PropertyValue::Unsigned(count)),
584 None => Err(common::unknown_property_error()),
585 },
586 _ => Err(common::unknown_property_error()),
587 }
588 }
589
590 fn write_property(
591 &mut self,
592 property: PropertyIdentifier,
593 _array_index: Option<u32>,
594 value: PropertyValue,
595 _priority: Option<u8>,
596 ) -> Result<(), Error> {
597 if let Some(result) = common::write_description(&mut self.description, property, &value) {
598 return result;
599 }
600
601 match property {
602 p if p == PropertyIdentifier::ARCHIVE => {
603 if let PropertyValue::Boolean(v) = value {
604 self.archive = v;
605 Ok(())
606 } else {
607 Err(common::invalid_data_type_error())
608 }
609 }
610 p if p == PropertyIdentifier::FILE_TYPE => {
611 if let PropertyValue::CharacterString(s) = value {
612 self.file_type = s;
613 Ok(())
614 } else {
615 Err(common::invalid_data_type_error())
616 }
617 }
618 p if p == PropertyIdentifier::READ_ONLY => {
619 Err(common::write_access_denied_error())
622 }
623 p if p == PropertyIdentifier::FILE_SIZE => resize::write_stream(self, value),
624 p if p == PropertyIdentifier::FILE_ACCESS_METHOD => {
625 Err(common::write_access_denied_error())
626 }
627 p if p == PropertyIdentifier::MODIFICATION_DATE => {
628 Err(common::write_access_denied_error())
629 }
630 p if p == PropertyIdentifier::RECORD_COUNT => {
631 if _array_index.is_none() && self.record_count.is_none() {
632 Err(common::unknown_property_error())
633 } else {
634 resize::write_records(self, value)
635 }
636 }
637 _ => Err(crate::common::unhandled_write_error(
638 self.property_metadata().as_ref(),
639 property,
640 _array_index,
641 )),
642 }
643 }
644
645 fn property_metadata(&self) -> Cow<'_, [crate::property_metadata::PropertyMetadata]> {
646 metadata::for_object(self)
647 }
648
649 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
650 crate::property_metadata::property_list_from_metadata(self.property_metadata().as_ref())
651 }
652
653 fn bind_clock_internal(&mut self, clock: Option<Arc<dyn ClockReader>>) {
654 self.clock = clock;
655 }
656
657 fn file_configuration_internal(&self) -> Option<&dyn FileConfiguration> {
658 Some(self)
659 }
660
661 fn file_configuration_internal_mut(&mut self) -> Option<&mut dyn FileConfiguration> {
662 Some(self)
663 }
664
665 fn file_storage_internal(&self) -> Option<&dyn FileStorage> {
666 Some(self)
667 }
668
669 fn file_storage_internal_mut(&mut self) -> Option<&mut dyn FileStorage> {
670 Some(self)
671 }
672}
673
674#[cfg(test)]
679mod resize_tests;
680#[cfg(test)]
681mod storage_tests;
682#[cfg(test)]
683mod tests;