KAL - kalignite Programing XFS Intro series II

Continuando con las series de programación, Kal es un software multivendor para kioskos y de ATMs mediante este software permite la centralización de la gestión desde un mismo punto y de forma multivendor de varias marcas de hardware de ATMs, mediante su plataforma kalignite.

Esta plataforma  permite mediante múltiples interfaces el acceso a los perimetrales mediante la implementación de los Kxcontrolers también llamadas DLLs de funciones y esto permite darle funcionalidades lógicas a los perimetrales para operar en entornos financieros.

Explicación basica de Kalignite Platform 

En este caso queremos mostrar como hacer una interacción con una aplicación hecha a mano , aunque existe el KXdesingner o Active X Control, dentro de la suite que facilita extremadamente este tipo de desarrollos la idea es explicar a mano como interacción desde HTML hacia un perimetral.

Para empezar pueden usar cualquier IDE, o notepad que permita escribir y guardar en formato HTML vamos a hacer una pequeña aplicación de dispensador.

Para el caso de KAL tiene una interfaz que se puede programar con Vbscripts y HTML la cual pues usaremos para tal demostración., aunque para los bancos con el KXFormsDesigner ayuda y ya resuelve mucho trabajo de programación como si fuera un dreamweaver no la usaremos para este ejemplo

vamos a reproducir un accionador de capacidades y estatus de dispensador como esto:

estas funciones dependen de KXCashDispenser, Claro que en el siguiente código para evitar incidentes evitamos ciertas cositas de los formatos html,  pero para el programador entrenado se pueden ver a simple vista si intentamos montar una aplicación HTML, que está faltando agregar a mano  ;)

<SCRIPT language=vbscript src=Common.vbs></SCRIPT>
<script LANGUAGE="VBScript">
<!--

sub buttonEnableCashUnitEvents_OnClick()
	call KXCashDispenser1.EnableCashUnitEvents()
end sub

sub buttonDisableCashUnitEvents_OnClick()
	DisplayValue LabelCashUnitInfoChangedEvent, ""
	call KXCashDispenser1.DisableCashUnitEvents()
end sub

-->
</script>

<script LANGUAGE="VBScript">
<!--

' Global Declarations

Dim gNotesInStacker
Dim gNotesAtOutput
Dim CUNum
Dim initialisation
Dim ErrorTargetLabel
Dim CashPresentTargetLabel

Dim Control

dim CashUnitConfigurationInitiated
dim LabelNotSupported


' Initialisation

Sub window_onload()
    Set Control = KXCashDispenser1
    Set ErrorTargetLabel = lblErrorStatus
	gNotesInStacker = False
	gNotesAtOutput	= False
	txtMixDenomination.innerText = "0, 1"
	initialisation = False
	CashUnitConfigurationInitiated = False
	Set LabelNotSupported = LabelCalibrateComplete

    ' Warning: Don't call any methods or properties on the control on startup
    ' otherwise the service name and instance name cannot be set.
end sub


Sub KXCashDispenser1_DeviceError()
    frameLastEvent.UpdateLastEvent "DeviceError"
	lblErrorStatus.innerText = "DeviceError"
	ErrorTargetLabel.innerText = "DeviceError"
	UpdateAllStatusDisplays
end sub

Sub KXCashDispenser1_FatalError(hResult)
    frameLastEvent.UpdateLastEvent "FatalError"
	lblErrorStatus.innerText = "FatalError " & hResult
end sub

Sub KXCashDispenser1_Timeout()
    frameLastEvent.UpdateLastEvent "Timeout"
	lblPresentReturn.innerText = "Timeout"
	call UpdateAllStatusDisplays
end sub

Sub KXCashDispenser1_StatusChanged(PropertyName,OldValue,NewValue)
    frameLastEvent.UpdateLastEvent "StatusChanged"
    frameStatusChanged.StatusChanged PropertyName, OldValue, NewValue
end sub

Sub KXCashDispenser1_CashUnitInfoChanged(cu)
    frameLastEvent.UpdateLastEvent "CashUnitInfoChanged"
	LabelCashUnitInfoChangedEvent.innerText = "Cash Unit Info Changed (" & cu & ")"
end sub

Sub KXCashDispenser1_CashUnitThresholdCrossed(cu)
    frameLastEvent.UpdateLastEvent "CashUnitThresholdCrossed"
	LabelCashUnitInfoChangedEvent.innerText = "Cash Unit Threshold Crossed (" & cu & ")"
End Sub

Sub DisplayLastDispense()
    lblLastDispenseCounts.innerText = ToString(KXCashDispenser1.LastDispenseCounts)
    lblLastDispensePresented.innerText = KXCashDispenser1.LastDispenseWasPresented
End Sub

Sub UpdateAllStatusDisplays()
    If KXCashDispenser1.StDeviceStatus <> "NODEVICE" then
	DisplayStatus
	DisplayLastDispense
	call btnGetMixInfo_OnClick()
	call btnGetCUStatus_OnClick()
    else
	lblDeviceStatus.innerText = KXCashDispenser1.StDeviceStatus
	lblDetailedDeviceStatus.innerText = KXCashDispenser1.StDetailedDeviceStatus
    end if
end sub


Sub ClearAllEventDisplays()
	DisplayValue lblMixReturn, ""
	DisplayValue lblDispenseReturn, ""
	DisplayValue lblRejectReturn, ""
	DisplayValue lblPresentReturn, ""
	DisplayValue lblRetractReturn, ""
	DisplayValue lblShutterStateChange, ""
	DisplayValue lblExchangeStateChange, ""
	DisplayValue lblErrorStatus, ""
	DisplayValue lblCountResult, ""
	DisplayValue lblCountedItems, ""
	DisplayValue lblDispensedItems, ""
    frameStatusChanged.ClearEventLabels
    frameEventManagement.ClearEventLabels
    frameLastEvent.ClearEventLabels
End Sub


Sub KXCashDispenser1_CashUnitError(cu)
    frameLastEvent.UpdateLastEvent "CashUnitError"
	alert "CashUnitError(" & cu & ")"
End Sub


-->
</script>
<h2 align="center"><a name="Mixing and Dispensing Cash">Mix y dispensador de  Cash</a></h2>

<SCRIPT id="ClientEventHandlersVBS" language=vbscript>
<!--
OPTION EXPLICIT

Sub btnMix_OnClick()
	call ClearAllEventDisplays()
	call KXCashDispenser1.Mix(txtMixAmount.Value, txtMixCurrency.Value, txtMixAlgorithm.Value)
end sub

Sub KXCashDispenser1_MixComplete(NoteCounts)
    frameLastEvent.UpdateLastEvent "MixComplete"
	call UpdateAllStatusDisplays()
	lblMixReturn.innerText = "MixComplete"
	txtMixDenomination.innerText = ToString(NoteCounts)
end sub

Sub btnDispense_OnClick()
	set ErrorTargetLabel = lblDispenseReturn
	set CashPresentTargetLabel = lblCountResult
	call ClearAllEventDisplays()
	if txtMixDenomination.Value <> "" then
		dim Denom
		Denom = ToArray(txtMixDenomination.Value)
		call KXCashDispenser1.Dispense(txtMixAmount.Value,Denom, txtMixCurrency.Value, txtMixAlgorithm.Value)
	else
		MsgBox("pon la demonicacion primero!")
	end if
end sub

Sub KXCashDispenser1_CashDispensed()
    frameLastEvent.UpdateLastEvent "CashDispensed"
	lblDispenseReturn.innerText = "CashDispensed"
	call UpdateAllStatusDisplays()
	gNotesInStacker = True
end sub

Sub btnReject_OnClick()
	set ErrorTargetLabel = lblRejectReturn
	call ClearAllEventDisplays()
'	if gNotesInStacker 	then
		call KXCashDispenser1.Reject()
		gNotesInStacker = False
'	else
'		MsgBox(" primero de dispensa antes de hacer reject de las notas!")
'	end if
end sub

Sub KXCashDispenser1_CashRejected()
    frameLastEvent.UpdateLastEvent "CashRejected"
	call UpdateAllStatusDisplays()
	lblRejectReturn.innerText = "CashRejected"
	gNotesInStacker = False
end sub

Sub btnPresent_OnClick()
	set ErrorTargetLabel = lblPresentReturn
	set CashPresentTargetLabel = lblPresentReturn
	call ClearAllEventDisplays()
	call KXCashDispenser1.Present(txtTimeout.Value)
end sub

' Present event handlers

Sub KXCashDispenser1_CashPresented()
    frameLastEvent.UpdateLastEvent "CashPresented"
	CashPresentTargetLabel.innerText = "CashPresented"
	call UpdateAllStatusDisplays()
	gNotesInStacker = False
	gNotesAtOutput = True
end sub

Sub KXCashDispenser1_WaitCancelled()
    	frameLastEvent.UpdateLastEvent "WaitCancelled"
	lblPresentReturn.innerText = "WaitCancelled"
	call UpdateAllStatusDisplays()
end sub

Sub KXCashDispenser1_CashTaken()
    frameLastEvent.UpdateLastEvent "CashTaken"
	CashPresentTargetLabel.innerText = "CashTaken"
	call UpdateAllStatusDisplays()
	gNotesAtOutput = False
end sub

Sub KXCashDispenser1_NotDispensable()
    frameLastEvent.UpdateLastEvent "NotDispensable"
	lblDispenseReturn.innerText = "NotDispensable"
	call UpdateAllStatusDisplays()
end sub

Sub btnCancelWait_OnClick()
	set ErrorTargetLabel = lblPresentReturn
	call ClearAllEventDisplays()
	call KXCashDispenser1.CancelWaitForCashTaken()
end sub

Sub btnRetract_OnClick()
    set CashPresentTargetLabel = lblRetractReturn
	set ErrorTargetLabel = lblRetractReturn
	call ClearAllEventDisplays()
	call KXCashDispenser1.Retract()
end sub

Sub btnRetractToPosition_OnClick()
    set CashPresentTargetLabel = lblRetractReturn
	set ErrorTargetLabel = lblRetractReturn
	call ClearAllEventDisplays()
	call KXCashDispenser1.RetractToPosition(txtRetractLocation.Value)
end sub

Sub KXCashDispenser1_CashRetracted()
    frameLastEvent.UpdateLastEvent "CashRetracted"
	lblRetractReturn.innerText = "CashRetracted"
	call UpdateAllStatusDisplays()
	gNotesAtOutput = False
end sub

Sub btnOpenShutter_OnClick()
	set ErrorTargetLabel = lblShutterStateChange
	call ClearAllEventDisplays()
	if (KXCashDispenser1.CpHasShutter) then
		call KXCashDispenser1.OpenShutter()
	else
		call MsgBox("Este SPI no tiene capacidad par abrir el shutter mira el error xfs!")
	end if
end sub

Sub KXCashDispenser1_ShutterOpen()
    frameLastEvent.UpdateLastEvent "ShutterOpen"
	lblShutterStateChange.innerText = "ShutterOpen"
	call UpdateAllStatusDisplays()
end sub

Sub btnCloseShutter_OnClick()
	set ErrorTargetLabel = lblShutterStateChange
	call ClearAllEventDisplays()
	if (KXCashDispenser1.CpHasShutter) then
		call KXCashDispenser1.CloseShutter()
	else
		call MsgBox("This Service Provider hasn't got the capabilities to close the shutter!")
	end if
end sub

Sub KXCashDispenser1_ShutterClosed()
    frameLastEvent.UpdateLastEvent "ShutterClosed"
	lblShutterStateChange.innerText = "ShutterClosed"
	call UpdateAllStatusDisplays()
end sub

Sub btnMixAndDispense_OnClick()
	set ErrorTargetLabel = lblDispenseReturn
	call ClearAllEventDisplays()

	call KXCashDispenser1.MixAndDispense(txtMixAmount.Value, txtMixCurrency.Value, txtMixAlgorithm.Value)
end sub

Sub btnDispenseAndPresent_OnClick()
	set ErrorTargetLabel = lblPresentReturn
	set CashPresentTargetLabel = lblPresentReturn
	call ClearAllEventDisplays()
	if txtMixDenomination.Value <> "" and txtTimeout.Value <> ""then
		dim Denom
		Denom = ToArray(txtMixDenomination.Value)
		call KXCashDispenser1.DispenseAndPresent(txtMixAmount.Value,Denom, txtMixCurrency.Value, txtMixAlgorithm.Value, txtTimeout.Value)
	else
		MsgBox(" insrta la demonimación y el tiempo primero!")
	end if
end sub

Sub ButtonLastDispenseExtendedStatus_OnClick()
    Dim Devstatus
	Devstatus = KXCashDispenser1.StDeviceStatus

	if Devstatus <> "NODEVICE" then
		LabelLastDispenseExtendedStatus.innerText = KXCashDispenser1.LastDispenseExtendedStatus(TextLastDispenseExtendedStatus.value)
	end if
end sub

-->
</SCRIPT>

<p><small>inserta el monto a dispensar y presiona
            "Calculate Mix" to calculate the note mix (if the Service
            Provider supports this), or enter the mix as a comma-separated array
            of note counts from each cash unit (denomination). Press
            "Dispense" then "Present" to finish the
            transaction. </small>
            </p>
<table align=center width="90%" height="396">
  <tr>
    <td width="45%" colspan="3">Mix y Dispensar</td>
    <td width="30%" colspan="2">Peesentar &amp; Retract</td>
    <td width="25%" colspan="2">Control de Shutter </td>
  </tr>
  <tr>
    <td width="15%"><small>Mix Algorithm:</small> <TEXTAREA class=Name id=txtMixAlgorithm>1</TEXTAREA></td>
    <td width="15%"><small>Cantidad a dispensar </small><TEXTAREA class=Name id=txtMixAmount>100</TEXTAREA></td>
    <td width="15%"><small>Divisa a dispensar</small> <TEXTAREA class=Name id=txtMixCurrency>GBP</TEXTAREA></td>
    <td width="30%" colspan=2><small> Timeout (ms)</small> <TEXTAREA class=Name id=txtTimeout>30000</TEXTAREA></td>
    <td width="25%" colspan=2 rowspan=2>&nbsp;&nbsp; </td>
  </tr>
  <tr>
    <td width="45%" colspan="3"><small>Las Notes de cada denominación a dispensar</small> <TEXTAREA class=Name id=txtMixDenomination></TEXTAREA></td>
    <td width="30%" colspan="2"><small>Retract location</small> <TEXTAREA class=Name id=txtRetractLocation>RETRACT</TEXTAREA></td>
  </tr>
  <tr>
    <td width="15%"><button id=btnMix style="MARGIN-TOP: 25px; HEIGHT: 50px">Calculate <br>MIX</button></td>
    <td width="15%"><button id=btnDispense style="MARGIN-TOP: 25px; HEIGHT: 50px">Dispensar!!</button></td>
    <td width="15%"><button id=btnReject style="MARGIN-TOP: 25px; HEIGHT: 50px">Reject</button></td>
    <td width="15%"><button id=btnPresent style="MARGIN-TOP: 25px; HEIGHT: 50px">PRESENTAR</button></td>
    <td width="15%">
      <button id=btnCancelWait style="HEIGHT: 33px">Cancel Wait</button><br>
      <button id=btnRetract style="HEIGHT: 33px">Retract</button><br>
      <button id=btnRetractToPosition style="HEIGHT: 33px">Retract a<br>Posicion</button>
    </td>
    <td width="12%"><button id=btnOpenShutter style="MARGIN-TOP: 25px; HEIGHT: 50px">Abrir</button></td>
    <td width="12%"><button id=btnCloseShutter style="MARGIN-TOP: 25px; HEIGHT: 50px">Cerrar</button></td>
  </tr>
  <tr>
    <td width="45%" colspan="3"><button id=btnMixAndDispense>Mix y Dispensar</button></td>
    <td width="30%" colspan="2"><button id=btnDispenseAndPresent>Dispensar y presentar </button></td>
    <td width="25%" colspan="2">&nbsp; </td>
  </tr>
  <tr>
    <td width="15%" class=label id=lblMixReturn>(unset)</td>
    <td width="15%" class=label id=lblDispenseReturn>(unset)</td>
    <td width="15%" class=label id=lblRejectReturn>(unset)</td>
    <td width="15%" class=label id=lblPresentReturn>(unset)</td>
    <td width="15%" class=label id=lblRetractReturn>(unset)</td>
    <td width="25%" colspan=2 class=label id=lblShutterStateChange>(unset)</td>
  </tr>
</table>
<P>&nbsp;</P>

Este código básicamente si el SPI no está ocupado que para ejecutarse no deben permitir de la suite kalignite se ejecute , esto se puede hacer booteando directo al S.O., entonces permite tomar control del SPI para interacciones desde una interfaz HTML a las funciones de KXCashDispenser, y concretamente permitir dispensar si las configuraciones concuerdan con el ATM.

No solamente un ATM son importantes las funciones de dispensado existen muchisimas mas como ( y no limitantes a ):

  • Funciones de Audio
  • Card reader
  • Biométrico
  • Lector de códigos de barras
  • aceptadores de dinero
  • Journal
  • PinPad
  • Impresora
  • sensores ..etc..

Por nombrar algunas funciones entonces en general esto es solo una pequeña demostración de todas las funciones que podemos controlar local o con algún REST API de forma remota del ATM, nos podemos encontrar con más de 3000 funciones , sensores y estados a programar para una operación lógica normal en entornos financieros cabe destacar que en estas series de programación de ATMS, para este caso solo sirven para la interfaz HTML y VBS del software KAL.

Saludos

Rafael Revert

Rafael Revert

CTO and Co-founder of Cyttek Group and international consulting company specialized in providing Cyber Security , ATM, IA, Big Data and custom products for different sectors
Panama