Multi-Reader
Examples for managing multiple reader instances simultaneously. Each instance is independent and thread-safe.
Two Readers
- C++
- C#
- Python
#include <iostream>
#include "Reader/Reader.h"
int main() {
using namespace Rik;
using namespace RikCommon;
ReaderHandle handle1 = nullptr;
ReaderHandle handle2 = nullptr;
try {
// Reader 1
ReaderDefinition readerDef1;
readerDef1.DeviceId.VendorId = 0x0C27;
readerDef1.DeviceId.ProductId = 0x3BFA;
readerDef1.ProtocolType = PROTOCOL_TYPE_FEATURE_REPORT;
handle1 = AbstractReader::CreateReaderInstance(readerDef1, 3);
auto* app1 = AbstractReader::GetInstance(handle1);
app1->Init();
// Reader 2
ReaderDefinition readerDef2;
readerDef2.DeviceId.VendorId = 0x0C27;
readerDef2.DeviceId.ProductId = 0x3BFB;
readerDef2.ProtocolType = PROTOCOL_TYPE_FEATURE_REPORT;
handle2 = AbstractReader::CreateReaderInstance(readerDef2, 3);
auto* app2 = AbstractReader::GetInstance(handle2);
app2->Init();
// Use both
auto meta1 = app1->GetMetadataStruct();
auto meta2 = app2->GetMetadataStruct();
std::cout << "Reader 1: " << meta1.PartNumber << std::endl;
std::cout << "Reader 2: " << meta2.PartNumber << std::endl;
// Cleanup
app1->Close();
AbstractReader::DestroyInstance(handle1);
app2->Close();
AbstractReader::DestroyInstance(handle2);
return 0;
} catch (const ReaderException& e) {
std::cerr << "Error: " << e.Message << std::endl;
if (handle1) AbstractReader::DestroyInstance(handle1);
if (handle2) AbstractReader::DestroyInstance(handle2);
return 1;
}
}
using rfIDEAS.ReaderIntegrationKit;
using rfIDEAS.ReaderIntegrationKit.Objects;
using rfIDEAS.ReaderIntegrationKit.Enum;
using rfIDEAS.ReaderIntegrationKit.Exceptions;
try
{
// Reader 1
var readerDef1 = new ReaderDefinition
{
DeviceId = new DeviceId { VendorId = 0x0C27, ProductId = 0x3BFA },
ProtocolType = ProtocolType.FeatureReport
};
using var app1 = new Reader(readerDef1);
app1.Init();
// Reader 2
var readerDef2 = new ReaderDefinition
{
DeviceId = new DeviceId { VendorId = 0x0C27, ProductId = 0x3BFB },
ProtocolType = ProtocolType.FeatureReport
};
using var app2 = new Reader(readerDef2);
app2.Init();
// Use both
var meta1 = app1.GetMetadata();
var meta2 = app2.GetMetadata();
Console.WriteLine($"Reader 1: {meta1.PartNumber}");
Console.WriteLine($"Reader 2: {meta2.PartNumber}");
}
catch (ReaderException ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
import reader_integration_kit.facade as rik
from reader_integration_kit.structures import (
ReaderDefinition, DeviceId, SerialPortSettings
)
from reader_integration_kit.enum import ProtocolType
from reader_integration_kit.errors import ReaderException
try:
# Reader 1
reader_def1 = ReaderDefinition(
DeviceId=DeviceId(VendorId=0x0C27, ProductId=0x3BFA),
ProtocolType=ProtocolType.FEATURE_REPORT,
SerialPortSettings=SerialPortSettings()
)
# Reader 2
reader_def2 = ReaderDefinition(
DeviceId=DeviceId(VendorId=0x0C27, ProductId=0x3BFB),
ProtocolType=ProtocolType.FEATURE_REPORT,
SerialPortSettings=SerialPortSettings()
)
with rik.Reader(reader_def1) as app1:
app1.init()
with rik.Reader(reader_def2) as app2:
app2.init()
meta1 = app1.get_metadata()
meta2 = app2.get_metadata()
print(f"Reader 1: {meta1.get('PartNumber')}")
print(f"Reader 2: {meta2.get('PartNumber')}")
except ReaderException as e:
print(f"Error: {e.message}")
Identical Readers (Same VID/PID)
When multiple readers share the same VID/PID, use serial numbers or USB paths to distinguish them. See Connection Strategies for full details on each approach.
By Serial Number
Add a serial number alongside VID/PID to filter to a specific device. On newer rf IDEAS readers, the USB serial number matches the reader's ESN.
- C++
- C#
- Python
ReaderDefinition readerDef1{};
readerDef1.DeviceId.VendorId = 0x0C27;
readerDef1.DeviceId.ProductId = 0x3BFA;
readerDef1.DeviceId.SerialNumber = L"ABC123";
readerDef1.ProtocolType = PROTOCOL_TYPE_FEATURE_REPORT;
ReaderDefinition readerDef2{};
readerDef2.DeviceId.VendorId = 0x0C27;
readerDef2.DeviceId.ProductId = 0x3BFA;
readerDef2.DeviceId.SerialNumber = L"DEF456";
readerDef2.ProtocolType = PROTOCOL_TYPE_FEATURE_REPORT;
using System.Runtime.InteropServices;
var readerDef1 = new ReaderDefinition
{
DeviceId = new DeviceId
{
VendorId = 0x0C27,
ProductId = 0x3BFA,
SerialNumber = Marshal.StringToHGlobalUni("ABC123")
},
ProtocolType = ProtocolType.FeatureReport
};
var readerDef2 = new ReaderDefinition
{
DeviceId = new DeviceId
{
VendorId = 0x0C27,
ProductId = 0x3BFA,
SerialNumber = Marshal.StringToHGlobalUni("DEF456")
},
ProtocolType = ProtocolType.FeatureReport
};
import ctypes
reader_def1 = ReaderDefinition(
DeviceId=DeviceId(
VendorId=0x0C27,
ProductId=0x3BFA,
SerialNumber=ctypes.cast(ctypes.c_wchar_p("ABC123"), ctypes.c_void_p)
),
ProtocolType=ProtocolType.FEATURE_REPORT,
SerialPortSettings=SerialPortSettings()
)
reader_def2 = ReaderDefinition(
DeviceId=DeviceId(
VendorId=0x0C27,
ProductId=0x3BFA,
SerialNumber=ctypes.cast(ctypes.c_wchar_p("DEF456"), ctypes.c_void_p)
),
ProtocolType=ProtocolType.FEATURE_REPORT,
SerialPortSettings=SerialPortSettings()
)
Connect one reader at a time and read its ESN from the metadata (GetMetadataStruct().ESN in C++, GetMetadata().ESN in C#, get_metadata()["ESN"] in Python). On newer readers, this ESN matches the USB serial number in the device descriptor.
On older rf IDEAS readers, the USB serial number may be empty or non-unique. If serial numbers are not reliable for your readers, use USB paths instead.
By USB Path
If serial numbers are not available, use UsbPath to target specific physical USB ports. When UsbPath is set, VID/PID and serial number are ignored -- the connection is made purely by port location.
- C++
- C#
- Python
ReaderDefinition readerDef1{};
readerDef1.ProtocolType = PROTOCOL_TYPE_FEATURE_REPORT;
std::wcsncpy(readerDef1.DeviceId.UsbPath, L"1-7",
sizeof(readerDef1.DeviceId.UsbPath) / sizeof(wchar_t) - 1);
ReaderDefinition readerDef2{};
readerDef2.ProtocolType = PROTOCOL_TYPE_FEATURE_REPORT;
std::wcsncpy(readerDef2.DeviceId.UsbPath, L"1-8",
sizeof(readerDef2.DeviceId.UsbPath) / sizeof(wchar_t) - 1);
using System.Runtime.InteropServices;
var readerDef1 = new ReaderDefinition
{
DeviceId = new DeviceId
{
UsbPath = Marshal.StringToHGlobalUni("1-7")
},
ProtocolType = ProtocolType.FeatureReport
};
var readerDef2 = new ReaderDefinition
{
DeviceId = new DeviceId
{
UsbPath = Marshal.StringToHGlobalUni("1-8")
},
ProtocolType = ProtocolType.FeatureReport
};
import ctypes
reader_def1 = ReaderDefinition(
DeviceId=DeviceId(
UsbPath=ctypes.cast(ctypes.c_wchar_p("1-7"), ctypes.c_void_p)
),
ProtocolType=ProtocolType.FEATURE_REPORT,
SerialPortSettings=SerialPortSettings()
)
reader_def2 = ReaderDefinition(
DeviceId=DeviceId(
UsbPath=ctypes.cast(ctypes.c_wchar_p("1-8"), ctypes.c_void_p)
),
ProtocolType=ProtocolType.FEATURE_REPORT,
SerialPortSettings=SerialPortSettings()
)
USB paths identify physical ports, not devices. If you move a reader to a different port, its path changes.
See Discovering USB Paths for how to find topological paths on Linux and Windows.
Thread Safety
All RIK instances are internally thread-safe. You can access different reader instances from different threads without external synchronization. Accessing the same instance from multiple threads is also safe -- RIK handles locking internally.
#include <thread>
void readerWorker(ReaderHandle handle, int id) {
auto* app = AbstractReader::GetInstance(handle);
auto metadata = app->GetMetadataStruct();
std::cout << "Reader " << id << ": " << metadata.PartNumber << std::endl;
}
// After creating and initializing handle1 and handle2:
std::thread t1(readerWorker, handle1, 1);
std::thread t2(readerWorker, handle2, 2);
t1.join();
t2.join();