Skip to content

Commit

Permalink
Merge pull request #2 from twihno/main
Browse files Browse the repository at this point in the history
feat: added support for grai 96
  • Loading branch information
russss authored Nov 5, 2024
2 parents e34810a + e4158df commit 226d541
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 0 deletions.
162 changes: 162 additions & 0 deletions src/epc/grai.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
//! Global Returnable Asset Identifier
//!
//! This is a combination of a company prefix assigned by GS1, an asset type
//! assigned by that company, and a serial number which allows an item to
//! be uniquely identified.
use crate::epc::{EPCValue, EPC};
use crate::error::Result;
use bitreader::BitReader;

/// Metadata for a partition
#[derive(Debug, PartialEq)]
#[allow(dead_code)]
struct Partition {
bits: u8,
digits: u8,
}

/// Decoded partition metadata
#[derive(Debug, PartialEq)]
struct GraiPartition {
company_prefix: Partition,
asset_type: Partition,
}

/// Get the number of bits and digits for the company prefix and the asset type based on the partition value.
///
/// Decoded according to Table 14-14 "GRAI Partition Table" in the GS1 EPC Tag Data Standard v2.1
fn decode_partition_value(partition_value: u8) -> Result<GraiPartition> {
match partition_value {
0 => Ok(GraiPartition {
company_prefix: Partition {
bits: 40,
digits: 12,
},
asset_type: Partition { bits: 4, digits: 0 },
}),
1 => Ok(GraiPartition {
company_prefix: Partition {
bits: 37,
digits: 11,
},
asset_type: Partition { bits: 7, digits: 1 },
}),
2 => Ok(GraiPartition {
company_prefix: Partition {
bits: 34,
digits: 10,
},
asset_type: Partition {
bits: 10,
digits: 2,
},
}),
3 => Ok(GraiPartition {
company_prefix: Partition {
bits: 30,
digits: 9,
},
asset_type: Partition {
bits: 14,
digits: 3,
},
}),
4 => Ok(GraiPartition {
company_prefix: Partition {
bits: 27,
digits: 8,
},
asset_type: Partition {
bits: 17,
digits: 4,
},
}),
5 => Ok(GraiPartition {
company_prefix: Partition {
bits: 24,
digits: 7,
},
asset_type: Partition {
bits: 20,
digits: 5,
},
}),
6 => Ok(GraiPartition {
company_prefix: Partition {
bits: 20,
digits: 6,
},
asset_type: Partition {
bits: 24,
digits: 6,
},
}),
_ => Err("Invalid partition value".into()),
}
}

// EPC Header Filter Partition GS1
// Company
// Prefix
// Asset Type Serial

/// 96-bit Global Returnable Asset Identifier
///
/// This comprises a manager number, an object class, and a numeric serial
/// number.
#[derive(PartialEq, Debug)]
pub struct GRAI96 {
/// Filter
pub filter: u8,
/// Partition
pub partition: u8,
/// GS1 Company Prefix
pub company_prefix: u64,
/// Asset type
pub asset_type: u32,
/// Serial number
pub serial: u64,
}

impl EPC for GRAI96 {
// GS1 EPC TDS section 14.6.4
fn to_uri(&self) -> String {
format!(
"urn:epc:id:grai:{}.{}.{}",
self.company_prefix, self.asset_type, self.serial
)
}

fn to_tag_uri(&self) -> String {
format!(
"urn:epc:tag:grai-96:{}.{}.{}.{}",
self.filter, self.company_prefix, self.asset_type, self.serial
)
}

fn get_value(&self) -> EPCValue {
EPCValue::GRAI96(self)
}
}

// GS1 EPC TDS Section 14.6.4
pub fn decode_grai96(data: &[u8]) -> Result<Box<dyn EPC>> {
let mut reader = BitReader::new(data);

let filter = reader.read_u8(3)?;
let partition = reader.read_u8(3)?;

let grai_partition = decode_partition_value(partition)?;

let company_prefix = reader.read_u64(grai_partition.company_prefix.bits)?;
let asset_type = reader.read_u32(grai_partition.asset_type.bits)?;
let serial = reader.read_u64(38)?;

Ok(Box::new(GRAI96 {
filter,
partition,
company_prefix,
asset_type,
serial,
}))
}
3 changes: 3 additions & 0 deletions src/epc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use num_enum::TryFromPrimitive;
use std::convert::TryFrom;

pub mod gid;
pub mod grai;
pub mod sgtin;
pub mod sscc;
pub mod tid;
Expand Down Expand Up @@ -87,6 +88,7 @@ pub enum EPCValue<'a> {
SGTIN198(&'a sgtin::SGTIN198),
SSCC96(&'a sscc::SSCC96),
GID96(&'a gid::GID96),
GRAI96(&'a grai::GRAI96),
}

fn take_header(data: &[u8]) -> Result<(&[u8], EPCBinaryHeader)> {
Expand All @@ -100,6 +102,7 @@ pub fn decode_binary(data: &[u8]) -> Result<Box<dyn EPC>> {

Ok(match header {
EPCBinaryHeader::GID96 => gid::decode_gid96(data)?,
EPCBinaryHeader::GRAI96 => grai::decode_grai96(data)?,
EPCBinaryHeader::SGITN96 => sgtin::decode_sgtin96(data)?,
EPCBinaryHeader::SGITN198 => sgtin::decode_sgtin198(data)?,
EPCBinaryHeader::SSCC96 => sscc::decode_sscc96(data)?,
Expand Down
8 changes: 8 additions & 0 deletions tests/test_epc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,12 @@ fn test_examples() {
let data = decode_binary(&hex::decode("3500E86F8000A9E000000586").unwrap()).unwrap();
assert_eq!(data.to_uri(), "urn:epc:id:gid:952056.2718.1414");
assert_eq!(data.to_tag_uri(), "urn:epc:tag:gid-96:952056.2718.1414");

// GRAI-96
let data = decode_binary(&hex::decode("3376451FD40C0E400000162E").unwrap()).unwrap();
assert_eq!(data.to_uri(), "urn:epc:id:grai:9521141.12345.5678");
assert_eq!(
data.to_tag_uri(),
"urn:epc:tag:grai-96:3.9521141.12345.5678"
);
}

0 comments on commit 226d541

Please sign in to comment.