This document is to demonstrate how to develop hardware station customizatin and debug that for trouble shooting
Through this demon, you will learn how to develop these below things: There are 3 sample provide:
-
In Sample#1, you will learn:
- How to develop a hardware station solution.
- How to make a new POS page including html page, view file in Typescript, View Mode code file in Typescript.
- How to open the page you just created through adding a command bar button in the existing page.
- How to call hardware station API in POS TS code.
-
In Sample #2, you will learn:
- How to overide the existing hardware logic.
-
In Sample #3, you will learn:
- How to delevelop a payment device.
Sample #1 - Coin Dispenser
The final effect of this sample is like this:
- Go to show journal, click the new command button we added:
- The new page will open:
- Click each button to call hardware station to do health check, or dispense 10 coin, or 1000 coin, if there is no enough coin to dispense, the hardare station will return error:
The steps of making this solution is as below:
- Hardware station project structure are like below:
It is .Net Standard 2.0 Class Library project:
- The POS project structure as below:
You need make sure the manifest file is correct. - Some key code to implement this solution:
How to call hardware station in POS Typescript code:
import { IExtensionViewControllerContext } from "PosApi/Create/Views"; import { HardwareStationDeviceActionRequest, HardwareStationDeviceActionResponse } from "PosApi/Consume/Peripherals"; export default class ExampleViewModel { public title: string; private _context: IExtensionViewControllerContext; constructor(context: IExtensionViewControllerContext) { this._context = context; this.title = this._context.resources.getString("string_0001"); } public pingCoinDispenser(): void { let hardwareStationDeviceActionRequest: HardwareStationDeviceActionRequest<HardwareStationDeviceActionResponse> = new HardwareStationDeviceActionRequest("CUSTOMPING", "CustomPing", { Message: "Knock, knock!" } ); this._context.runtime.executeAsync(hardwareStationDeviceActionRequest).then((result) => { this._context.logger.logInformational("Message from HWS: " + result.data.response); }).catch((err) => { this._context.logger.logInformational("Failure in executing Hardware Station request"); }); } public dispenseThousandCoins(): void { this._dispenseCoins(1000); } public dispenseTenCoins(): void { this._dispenseCoins(10); } private _dispenseCoins(amount: number): void { let hardwareStatationDeviceActionRequest: HardwareStationDeviceActionRequest<HardwareStationDeviceActionResponse> = new HardwareStationDeviceActionRequest("COINDISPENSER", "DispenseChange", { Amount: amount, DeviceName: "MyCoinDispenser" }); this._context.runtime.executeAsync(hardwareStatationDeviceActionRequest).then(() => { this._context.logger.logInformational("Hardware Station request executed successfully"); }).catch((err) => { this._context.logger.logInformational("Failure in executing Hardware Station request"); throw err; }); } }
Add a command button to show journal view to open this new page:
import { IExtensionCommandContext } from "PosApi/Extend/Views/AppBarCommands";
import * as ShowJournalView from "PosApi/Extend/Views/ShowJournalView";
export default class CoinDispenserCommand extends ShowJournalView.ShowJournalExtensionCommandBase {
constructor(context: IExtensionCommandContext<ShowJournalView.IShowJournalToExtensionCommandMessageTypeMap>) {
super(context);
this.id = "coinDispenserCommand";
this.label = "Coin Dispenser";
this.extraClass = "iconInvoice";
this.isVisible = true;
this.canExecute = true;
}
protected init(state: ShowJournalView.IShowJournalExtensionCommandState): void {
}
protected execute(): void {
this.isProcessing = true;
this.context.navigator.navigate("ExampleView");
this.isProcessing = false;
}
}
Hardware station controller and service code like below:
[RoutePrefix("COINDISPENSER")]
public class CoinDispenserController : IController
{
private const string CoinDispenserTestName = "MockOPOSCoinDispenser";
[HttpPost]
public async Task<bool> DispenseChange(CoinDispenseRequest request, IEndpointContext context)
{
ThrowIf.Null(request, "request");
string deviceName = request.DeviceName;
if (string.IsNullOrWhiteSpace(deviceName))
{
deviceName = CoinDispenserController.CoinDispenserTestName;
}
try
{
var openCoinDispenserDeviceRequest = new OpenCoinDispenserDeviceRequest(deviceName, null);
await context.ExecuteAsync<NullResponse>(openCoinDispenserDeviceRequest);
var dispenseChangeCoinDispenserDeviceRequest = new DispenseChangeCoinDispenserDeviceRequest(request.Amount);
await context.ExecuteAsync<NullResponse>(dispenseChangeCoinDispenserDeviceRequest);
return true;
}
catch (Exception ex)
{
throw new PeripheralException("Microsoft_Dynamics_Commerce_HardwareStation_CoinDispenser_Error", ex.Message, ex);
}
finally
{
var closeCoinDispenserDeviceRequest = new CloseCoinDispenserDeviceRequest();
await context.ExecuteAsync<NullResponse>(closeCoinDispenserDeviceRequest);
}
}
public class OposCoinDispenser : INamedRequestHandler, IDisposable
{
private int coinAmount = 1000;
private bool isOpen = false;
public string HandlerName
{
get { return PeripheralType.Opos; }
}
public IEnumerable<Type> SupportedRequestTypes
{
get
{
return new[]
{
typeof(OpenCoinDispenserDeviceRequest),
typeof(DispenseChangeCoinDispenserDeviceRequest),
typeof(CloseCoinDispenserDeviceRequest)
};
}
}
public Response Execute(Request request)
{
ThrowIf.Null(request, "request");
Type requestType = request.GetType();
if(requestType == typeof(OpenCoinDispenserDeviceRequest))
{
var openRequest = (OpenCoinDispenserDeviceRequest)request;
this.Open(openRequest.DeviceName);
}
else if (requestType == typeof(DispenseChangeCoinDispenserDeviceRequest))
{
var dispenseChangeRequest = (DispenseChangeCoinDispenserDeviceRequest)request;
this.DispenseChange(dispenseChangeRequest.Amount);
}
else if(requestType == typeof(CloseCoinDispenserDeviceRequest))
{
this.Close();
}
else
{
throw new NotSupportedException(string.Format("Request '{0}' is not supported", requestType));
}
return new NullResponse();
}
...
-
Add POS project and Hardware station Project to Store Commerce installer and build it:
and then install the POS installer
-
Debug it and you can the hardware station code is hit successfully:
The steps to implement a payment device is like below:
1. Go through this document: https://docs.microsoft.com/en-us/dynamics365/commerce/dev-itpro/end-to-end-payment-extension
2. Go to hardware profile, change it as this:
-
Clone the code from https://github.com/microsoft/Dynamics365Commerce.InStore/tree/release/9.39/src/HardwareStationSample/PaymentDevice
-
Make a cash and carry transaction, you can see the break point got hit: