이전 섹션에서는 다음을 포함하여 프로젝트를 시작하기 위한 샘플 및 가이드를 제공했습니다.
- 간단한 템플릿 만들기
- 서비스 객체 클래스를 구현하여 PosExplorer를 통해 포인트 오브 서비스 샘플 애플리케이션에서 컴파일하고 볼 수 있도록 합니다.
- 스레드 도우미 클래스 구현
이 샘플에서는 이러한 모든 단계를 결합하여 다중 스레드 MSR 서비스 개체 클래스를 만듭니다. 이 샘플은 실제로 하드웨어에서 읽지 않습니다. 단순히 시스템을 통해 테스트 데이터를 푸시합니다. 그러나 서비스 개체와 관련된 코드를 추가하는 방법을 보여 줍니다.
서비스 개체에 대해 이 코드를 사용자 지정하려면
PosAssembly조직의 이름을 포함할 수 있도록 AssemblyInfo.cs 특성을 수정합니다.네임스페이스의 이름이 조직 및 서비스 개체에 적합한지 확인합니다.
ServiceObject만들고 있는 서비스 개체의 형식, 이름, 설명 및 버전 번호를 포함하도록 특성을 수정합니다.이 서비스 개체를
HardwareId플러그 앤 플레이 디바이스 또는 다양한 디바이스와 연결하는 특성을 추가합니다.서비스 개체 판독기 스레드 소개에 제공된 ThreadHelper 클래스를 포함합니다. 코드를 소스 파일에 붙여넣거나 프로젝트에서 별도의 소스 파일로 컴파일하여 이 작업을 수행할 수 있습니다. ThreadHelper 클래스가 액세스 가능한 네임스페이스에 있는지 확인합니다.
서비스 개체에서 사용하는 기본 클래스 및 지원하려는 기능에 따라 필요한 멤버를 구현합니다. 이 샘플은 기능이 거의 없는 작동하는 MSR 서비스 개체입니다.
요구 사항
이 샘플을 컴파일하려면 프로젝트에 올바른 참조 및 전역 특성이 있어야 합니다. .NET 서비스 개체에 대한 작업 POS를 아직 만들지 않은 경우 서비스 개체 샘플: 시작 섹션을 검토합니다.
또한 이전 섹션인 서비스 개체 판독기 스레드 소개의 코드를 프로젝트에 포함해야 합니다.
입증합니다
대부분의 서비스 개체는 하드웨어를 모니터링하고 다양한 들어오는 데이터 이벤트를 애플리케이션에 알리기 위해 두 번째 스레드를 사용해야 합니다. 이 샘플에서는 다중 스레드 서비스 개체를 만드는 한 가지 방법을 보여줍니다. 서비스 개체 판독기 스레드 소개에서 설명한 ServiceObjectThreadHelper 클래스를 사용하여 이 작업을 수행합니다.
도우미 클래스를 사용하려면 애플리케이션이 ServiceObjectThreadHelper 인터페이스를 구현하는 새 클래스를 정의해야 합니다. 이 인터페이스에는 다음 세 가지 메서드가 포함됩니다.
- ServiceObjectThreadOpen 이 메서드는 스레드 초기화 중에 호출되며 하드웨어별 리소스를 초기화하는 데 사용해야 합니다.
- ServiceObjectThreadClose 이 메서드는 스레드가 종료될 때 호출되며 하드웨어별 리소스를 해제하는 데 사용해야 합니다.
- ServiceObjectThreadProcedure 이 메서드는 스레드가 성공적으로 시작되고 하드웨어 이벤트에서 대기하는 루프를 반복해야 하며 적절한 ManualEvent 가 트리거될 때까지 종료되지 않아야 합니다.
이 코드는 서비스 개체 샘플 만들기 항목에 제시된 샘플을 기반으로 하며 다음 기능을 추가합니다.
- ServiceObjectThreadHelper에서 파생된 클래스를 만듭니다.
- MsrThreadingObject 클래스의 인스턴스를 만듭니다. 이 클래스의 생성자는 서비스 개체에 대한 참조인 단일 인수를 사용합니다.
- 서비스 개체에서 MsrThreadingObject 개체의 메서드를 호출하여 스레드 도우미를 적절하게 시작하고 중지합니다.
예시
using System;
using System.Threading;
using Microsoft.PointOfService;
using Microsoft.PointOfService.BaseServiceObjects;
using System.Text;
namespace Samples.ServiceObjects.Advanced.MSR
{
public class MsrThreadingObject :
ServiceObjectThreadHelper, IDisposable
{
// This is a helper class which will depend on
// being able to call back into the actual Service
// Object to pass along data. However, you cannot
// keep a strong reference to the Service Object,
// since that will prevent proper disposal, which
// may create a state in which all hardware resources
// are not properly released by the SO. Therefore,
// create a weak reference. From this reference,
// you can get a temporary strong reference, which
// you act on and then release.
WeakReference ServiceObjectReference;
// The name of the Service Object.
string ObjectName;
public MsrThreadingObject(AdvancedSampleMsr so)
{
ObjectName = GetType().Name;
ServiceObjectReference = new WeakReference(so);
}
~MsrThreadingObject()
{
Dispose(true);
}
private bool IsDisposed = false;
protected virtual void Dispose(bool disposing)
{
if (!IsDisposed)
{
IsDisposed = true;
base.Dispose(disposing);
}
}
public void Dispose()
{
Dispose(false);
}
#region Methods of ServiceObjectThreadHelper
// This will be called during initialization.
public override void ServiceObjectThreadOpen()
{
Logger.Info(ObjectName, "Msr Thread Open");
}
// This method will be called during shutdown.
public override void ServiceObjectThreadClose()
{
Logger.Info(ObjectName, "Msr Thread Open");
}
public override void ServiceObjectThreadProcedure(
AutoResetEvent ThreadStopEvent)
{
// Convert a C# string into a sample byte array.
UTF8Encoding encoder = new UTF8Encoding();
// Convert sample data to a byte array.
byte[] MsrData = encoder.GetBytes(
"This is MSR test data");
Logger.Info(ObjectName, "Msr Thread Procedure Entered");
while (true)
{
// When this method is called by the
// ServiceObjectThreadHelper, it is obligated to
// exit when the event ThreadStopEvent has been
// set.
// Additionally, this method will also wait for
// hardware events or for a time-out. That should
// be done here.
// This example waits for the event to be set
// or times out after several seconds.
if (ThreadStopEvent.WaitOne(2000, false))
{
break;
}
Logger.Info(ObjectName, "Reader Thread cycling");
// Try to get a strong reference to the Service
// Object using the weak reference saved when
// this helper object was created.
AdvancedSampleMsr msr =
ServiceObjectReference.Target
as AdvancedSampleMsr;
// If this fails, that means the Service
// Object has already been disposed of. Exit the
// thread.
if (msr == null)
{
break;
}
// Using the strong reference, you can now make
// calls back into the Service Object.
msr.OnCardSwipe(MsrData);
msr = null;
}
#endregion Methods of ServiceObjectThreadHelper
}
// Implementation of the Service Object class. This class
// implements all the methods needed for an MSR Service
// Object.
//
// A Service Object which supports a Plug and Play device
// should also have a HardwareId attribute here.
[HardwareId(
@"HID\Vid_05e0&Pid_038a",
@"HID\Vid_05e0&Pid_038a")]
[ServiceObject(
DeviceType.Msr,
"AdvancedSampleMsr",
"Advanced Sample Msr Service Object",
1,
9)]
public class AdvancedSampleMsr : MsrBase
{
// String returned for various health checks.
private string MyHealthText;
private const string PollingStatistic =
"Polling Interval";
// Create a class with interface methods called from the
// threading object.
MsrThreadingObject ReadThread;
public AdvancedSampleMsr()
{
// DevicePath must be set before Open() is called.
// In the case of Plug and Play hardware, the POS
// for .NET Base class will set this value.
DevicePath = "Sample Msr";
Properties.CapIso = true;
Properties.CapTransmitSentinels = true;
Properties.DeviceDescription =
"Advanced Sample Msr";
// Initialize the string to be returned from
// CheckHealthText().
MyHealthText = "";
}
~AdvancedSampleMsr()
{
// Code added from previous sections to terminate
// the read thread started by the thread-helper
// object.
ReadThread.CloseThread();
Dispose(false);
}
protected override void Dispose(bool disposing)
{
try
{
if (disposing)
{
if (ReadThread != null)
{
ReadThread.Dispose();
ReadThread = null;
}
}
}
finally
{
// Must call base class Dispose.
base.Dispose(disposing);
}
}
#region Internal Members
// This is a private method called from the task
// interface when a data event occurs in the reader
// thread.
internal void OnCardSwipe(byte[] CardData)
{
// Simple sample data.
UTF8Encoding utf8 = new UTF8Encoding();
byte[] track1Data = utf8.GetBytes(
"this is test track 1");
byte[] track2Data = utf8.GetBytes(
"this is test track 2");
// Call GoodRead(), UnreadableCard, or FailedRead
// from here.
GoodRead(
track1Data,
track2Data,
null,
null,
Microsoft.PointOfService.BaseServiceObjects.CardType.Iso);
}
#endregion Internal Members
#region PosCommon overrides
// PosCommon.Open.
public override void Open()
{
// Call base class Open.
base.Open();
// Initialize statistic values.
// Set values for common statistics.
SetStatisticValue(StatisticManufacturerName,
"Microsoft Corporation");
SetStatisticValue(
StatisticManufactureDate, "2004-10-23");
SetStatisticValue(
StatisticModelName, "Msr Simulator");
SetStatisticValue(
StatisticMechanicalRevision, "1.0");
SetStatisticValue(
StatisticInterface, "USB");
// Create a new manufacturer statistic.
CreateStatistic(
PollingStatistic,
false,
"milliseconds");
// Set handlers for statistics stored in hardware.
// Create a class with interface methods called
// from the threading object.
ReadThread = new MsrThreadingObject(this);
}
// PosCommon.CheckHealthText.
public override string CheckHealthText
{
get
{
// MsrBasic.VerifyState(mustBeClaimed,
// mustBeEnabled).
VerifyState(false, false);
return MyHealthText;
}
}
// PosCommon.CheckHealth.
public override string CheckHealth(
HealthCheckLevel
level)
{
// Verify that device is open, claimed, and enabled.
VerifyState(true, true);
// Your code here checks the health of the device and
// returns a descriptive string.
// Cache result in the CheckHealthText property.
MyHealthText = "Ok";
return MyHealthText;
}
// PosCommon.DirectIO.
public override DirectIOData DirectIO(
int command,
int data,
object obj)
{
return new DirectIOData(data, obj);
}
public override bool DeviceEnabled
{
get
{
return base.DeviceEnabled;
}
set
{
if (value != base.DeviceEnabled)
{
base.DeviceEnabled = value;
if (value == false)
{
// Stop the reader thread when the
// device is disabled.
ReadThread.CloseThread();
}
else
{
try
{
// Enabling the device, start the
// reader thread.
ReadThread.OpenThread();
}
catch (Exception e)
{
base.DeviceEnabled = false;
if (e is PosControlException)
throw;
throw new PosControlException(
"Unable to Enable Device",
ErrorCode.Failure, e);
}
}
}
}
}
#endregion PosCommon overrides.
#region MsrBasic Overrides
// MsrBasic.MsrFieldData
// Once the track data is retrieved, this method is
// called when the application accesses various data
// properties in the MsrBasic class. For example,
// FirstName and AccountNumber.
protected override MsrFieldData ParseMsrFieldData(
byte[] track1Data,
byte[] track2Data,
byte[] track3Data,
byte[] track4Data,
CardType cardType)
{
// MsrFieldData contains the data elements that
// MsrBasic will return as properties to the
// application, as they are requested.
MsrFieldData data = new MsrFieldData();
// Parse the raw track data and store in fields to
// be used by the app.
data.FirstName = "FirstName";
data.Surname = "LastName";
data.Title = "Mr.";
data.AccountNumber = "123412341234";
return data;
}
// MsrBasic.MsrTrackData.
protected override MsrTrackData ParseMsrTrackData(
byte[] track1Data,
byte[] track2Data,
byte[] track3Data,
byte[] track4Data,
CardType cardType)
{
MsrTrackData data = new MsrTrackData();
// Modify the track data as appropriate for your SO.
// Remove the sentinel characters from the track data,
// for example.
data.Track1Data = (byte[])track1Data.Clone();
data.Track2Data = (byte[])track2Data.Clone();
return data;
}
#endregion MsrBasic overrides
}
}
}
또한 참조하십시오
업무
기타 리소스
.NET