Narrowcasting
Dit document beschrijft de integratie voor derde partijen specifiek voor narrowcasting.
Het bestaat uit de volgende onderdelen:
Oproepen
Het kunnen ontvangen van een oproep vanuit Universe.Spreekuurverloop
Het kunnen ontvangen van spreekuurverloop van agenda’s.Wachttijden
Het kunnen ontvangen van wachttijden op basis van inloop.
Deze integratie is beschikbaar vanaf 2023.* of hoger
Dit document beschrijft de koppeling tussen Logis.P en systemen van derden (hierna genoemd ‘leveranciers’) zoals leveranciers van Narrowcasting of Digital Signage systemen. Met behulp van deze koppeling kan de ontvangende partij de oproepopdrachten op het scherm tonen.
Oproepen
Koppeling
Alle oproepen gegenereerd in het Logis.P systeem worden op de interne messagebus RabbitMQ gezet als een zogeheten CFMCall JSON bericht.
Dit CFMCall bericht ziet er als volgt uit:
{
"QueueID": 1,
"TicketID": "23",
"BalieDisplayText": "Ga naar de balie",
"DisplayPrint": "A015",
"TerminalID": "TEST"
}
Toegang tot de RabbitMQ messagebus verloopt via onze LogisP.Endpoints.Integration.Hub, waarmee u verbinding kunt maken met behulp van @microsoft/signalr NPM package.
Ter authenticatie worden door Logis.P de volgende gegevens verstrekt:
Apikey = unieke sleutel voor de leverancier
TerminalID = unieke sleutel per narrowcasting device van de leverancier
Velden
Veld | Omschrijving |
---|---|
QueueID | ID van de wachtrij |
TicketID | Technische sleutel (uniek) van de uitgegeven ticket. Indien er aanvullend wordt gecommuniceerd dient dit nummer meegezonden te worden bij het opvragen van gegevens. Normaliter zal dit veld niet worden gebruikt.
|
BalieDisplayText | Textuele beschrijving (wordt normaliter getoond) waar de patient zich moet melden. Bijvoorbeeld de onderzoekskamer of de balie. |
DisplayPrint | Het nummer dat op het ticket is afgedrukt. Dit zal normaliter getoond worden. |
TerminalID | De ID van het device waarop de oproep getoond moet worden. |
Voorbeeld Javascript SignalR connectie
Hieronder treft u een HTML pagina met javascript aan, welke het leggen van de verbinding met onze Logisp.Endpoints.Integration.Hub illustreert.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>DEMO SIGNALR Logis.P</title>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.1/signalr.js"></script>
<script>
// Setup the connection.
const connection = new signalR.HubConnectionBuilder() .withUrl("http://localhost:5000/integrationHub?terminalID=TEST&apikey=0a2c1fd4-98a1-4bb4-91a2-4bb54eba063d")
.configureLogging(signalR.LogLevel.Information)
.build();
async function start() {
try {
await connection.start();
console.log("SignalR Connected.");
} catch (err) {
console.log(err);
setTimeout(start, 5000);
}
};
connection.onclose(async () => {
await start();
});
// Start the connection.
start();
// Receive the message.
connection.on('CFMCall', (messageText) => {
//{"QueueID":1,"TicketID":"23","BalieDisplayText":"Ga naar de balie","DisplayPrint":"A015","TerminalID":"TEST"}
console.log(`${JSON.stringify(messageText)}`);
const newMessage = document.createElement('li');
newMessage.appendChild(document.createTextNode(`${JSON.stringify(messageText)}`));
document.getElementById('messages').appendChild(newMessage);
});
</script>
<div class="signalr-demo">
<ul id="messages"></ul>
</div>
</body>
</html>
De terminalID en de apikey worden in dit voorbeeld doorgegeven in de geel gearceerde querystring.
Voorbeeld C# SignalR connectie
using System;
using System.Threading.Tasks;
using System.Windows;
using LogisP.Contracts.Bus.Kiosk;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.AspNetCore.WebUtilities;
namespace LogisP.Endpoints.Integration.ConnectionExample
{
internal class Program
{
static void Main(string[] args) {
var uri = "http://localhost:5000/integrationHub";
var queryString = new Dictionary<string, string>
{
{"terminalID", "TEST"},
};
var uriWithQueryString = QueryHelpers.AddQueryString(uri, queryString);
//setup connection
HubConnection connection = new HubConnectionBuilder()
.WithUrl(uriWithQueryString, options =>
{
options.Headers.Add("apikey", "0a2c1fd4-98a1-4bb4-91a2- 4bb54eba063d");
})
.WithAutomaticReconnect()
.Build();
connection.Closed += async (error) =>
{
Console.WriteLine($"Connection closed by remote client with error {error?.Message}");
};
connection.On<CfmCall>("CFMCall", (message) =>
{
//Receive message
//{"QueueID":1,"TicketID":"23","BalieDisplayText":"Ga naar:","DisplayPrint":"A015","TerminalID":"TEST"}
Console.WriteLine($"Received cfmcall with data {message.BalieDisplayText} for terminalID {message.TerminalID}");
});
try
{
connection.StartAsync();
Console.WriteLine($"Connecting to {uri} with connection state {connection.State.ToString()}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex}");
}
Console.ReadKey(); // wait for userinput
}}}
De terminalID en de apikey worden in dit voorbeeld doorgegeven in de geel gearceerde codeblokken.
Een volledig C# project met bovenstaande voorbeeldcode kan op verzoek verstrekt worden door Logis.P
Voorbeeld Angular SignalR connectie
import { BehaviorSubject, Observable } from 'rxjs';
import { Injectable } from '@angular/core';
import * as signalR from "@microsoft/signalr"
import { cfmcall } from '../models/cfmcall.model';
@Injectable({
providedIn: 'root'
})
export class SignalrhubService {
private hubConnection: signalR.HubConnection | undefined
public cfmCall$:BehaviorSubject<cfmcall|undefined> = new BehaviorSubject<cfmcall|undefined>(undefined);
public connectionStatus$:BehaviorSubject<number> = new BehaviorSubject<number>(0);
public connectionState: number = 0;
constructor() {
}
public startConnection = (terminalID: string, apikey: string) => {
const self = this;
// connecting
this.connectionState = -1;
this.connectionStatus$.next(-1)
this.hubConnection = new signalR.HubConnectionBuilder()
.withUrl(`http://localhost:5000/integrationHub?terminalID=${terminalID}&apikey=${apikey}`
,
{
withCredentials: false,
skipNegotiation: true,
transport: signalR.HttpTransportType.WebSockets
})
.withAutomaticReconnect()
.build();
this.hubConnection
.start( )
.then(() => {
console.log('Connection started');
this.connectionState = 1;
this.connectionStatus$.next(1);
this.addListeners();
})
.catch(err => {
console.log('Error while starting connection: ' + err);
self.connectionStatus$.next(0);
console.log('Connection stopped');
self.connectionState = 0;
});
this.hubConnection.onclose(function (e) {
self.connectionStatus$.next(0);
console.log('Connection stopped');
self.connectionState = 0;
});
this.hubConnection.onreconnecting(function(e) {
self.connectionStatus$.next(2);
console.log('Connection reconnecting');
self.connectionState = 2;
});
this.hubConnection.onreconnected(function(e) {
self.connectionStatus$.next(1);
console.log('Connection onreconnected');
self.connectionState = 3;
});
}
private addListeners = () => {
this.hubConnection?.on('CFMCall', (data) => {
this.cfmCall$.next(data);
console.log(data);
});
}
public StopConnection = () => {
if (this.hubConnection) {
this.hubConnection.stop().then();
}
}
}
De terminalID en de apikey worden in dit voorbeeld doorgegeven in de grijs gearceerde querystring.
Een volledig Angular project met bovenstaande voorbeeldcode kan op verzoek verstrekt worden door Logis.P.
Testomgeving
Logis.P kan op verzoek een testomgeving in de cloud beschikbaar stellen, waarbij om de x minuten een CFMCall bericht op de RabbitMQ messagebus gezet wordt om de SignalR connectie vanuit het client narrowcasting programma te kunnen testen.
Hiertoe worden van tevoren uitgewisseld:
Apikey = unieke sleutel voor de leverancier
TerminalID = unieke sleutel per narrowcasting device van de leverancier
URL = http(s) verbinding met Logisp.Endpoints.Integration.Hub
Spreekuurverloop – uitlooptijden
Logis.P houdt per agenda de uitloop bij indien ingeschakeld. De berekening wordt elke 60 seconden bijgewerkt.
Belangrijk bij narrowcasting is de juiste agenda’s te tonen bij het juiste scherm. Hiervoor bestaan een aantal methodes:
Op basis van afdelingscode in de afdeling
Op basis van LocationID in CDR_Schedule object (de afspraak). Dit wordt gevuld vanuit de HL7 koppeling.
Op basis van de ExternalID van de wachtrijgroep waarbinnen de wachtrijen vallen die op basis van wachtrij ID in CDR_CustomerflowQueue gekoppeld zijn (Voorwaarde dat de afspraak gekoppeld is aan een ticket)
Alle uitloop
Welke methode gebruikt zal worden moet afgestemd worden binnen het project met de betrokken implementatie consultant.
Koppeling
De API is een standaard REST API met berichtgeving in JSON formaat. NC leverancier krijgt toestemming om verbinding te maken op basis van een uitgegeven apikey door Logis.P. De apikey moet meegegeven worden in elk bericht in de header "X-ApiKey".
Alle naamgeving van de API is in het Engels.
Wij gebruiken de volgende http status codes consistent:
"200" - alles ok
"400" - bad request (indien documenten verlopen zijn, of sessie niet geldig)
"401" - geen toegang
"500" - systeem fout
Een systeem fout kan een body bevatten met extra informatie.
De url om verbinding mee te maken ziet er zo uit:
https://[klantomgeving.klant.nl]/integration/api/narrowcasting/[aanroep]
Uitloop
Hier staan de aanroepen hoogover beschreven. Er is een openapi JSON (Swagger) beschikbaar voor gedetailleerde technische specificatie van de API, welke op verzoek door Logis.P verstrekt kan worden.
Alle aanroepen hieronder zijn GET requests.
Op basis van afdelingscode
Url voor aanroep:
https://[klantomgeving.klant.nl]/integration/api/narrowcasting/departments
Parameter | Waarde | Omschrijving | Optioneel |
---|---|---|---|
[QUERY]departments | ExternalID van afdeling. U kunt meerdere ExternalID’s meegeven. | Haalt de agenda’s op gekoppeld aan deze afdeling | Nee |
[QUERY]showzerodelay | True of false | Indien true dan worden ook agenda’s getoond die geen uitloop hebben | Ja, standaard true |
[QUERY]onlyshowactiveresources | True of false | Indien true dan worden enkel agenda’s getoond die in de laatste 30 minuten een afspraak hadden en geen afspraken meer hebben op die dag | Ja, standaard true |
Voorbeeld aanroep:
https://[klantomgeving.klant.nl]/integration/api/narrowcasting/departments?departments=Bloedafname&departments=Cardiologie&showzerodelay=true&onlyshowactiveresources=false
Op basis van LocationID
Url voor aanroep:
https://[klantomgeving.klant.nl]/integration/api/narrowcasting/location
Parameter | Waarde | Omschrijving | Optioneel |
---|---|---|---|
[QUERY]locationid | LocationID van afspraak. Dit kunnen er meerdere zijn. | Haalt de agenda’s op gekoppeld aan deze afspraak met betreffende LocationID. De LocationID matched met alles dat daarmee begint. Stel bij een afspraak staat LocationID “route6” dan matched het ook met “route606” | Nee |
[QUERY]filter | “default” of “complete” | Indien men enkel wil matchen met specifieke LocationID dan kan men de filter “complete”. Dan moet het een exacte match zijn | Ja, standaard “default” |
[QUERY]showzerodelay | True of false | Indien true dan worden ook agenda’s getoon die geen uitloop hebben | Ja, standaard true |
[QUERY]onlyshowactiveresources | True of false | Indien true dan worden enkel agenda’s getoond die in de laatste 30 minuten een afspraak hadden en geen afspraken meer hebben op die dag | Ja, standaard true |
Voorbeeld aanroep:
https://[klantomgeving.klant.nl]/api/narrowcasting/location?locationids=route1&locationids=route2&filter=default&showzerodelay=true&onlyshowactiveresources=false
Op basis van wachtrij
Url voor aanroep:
https://[klantomgeving.klant.nl]/integration/api/narrowcasting/queue
Parameter | Waarde | Omschrijving | Optioneel |
[QUERY] groupExternalIDs | ExternalID van de groep waarin de wachtrij vermeld in de customerflowqueuezit zit. U kunt meerdere ExternalIDs meegeven. | Haalt de agenda’s op gekoppeld aan de afspraak in de wachtrijen behorende bij de betreffende wachtrijgroepen | Nee |
[QUERY]showzerodelay | True of false | Indien true dan worden ook agenda’s getoond die geen uitloop hebben | Ja, standaard true |
[QUERY]onlyshowactiveresources | True of false | Indien true dan worden enkel agenda’s getoond die in de laatste 30 minuten een afspraak hadden en geen afspraken meer hebben op die dag | Ja, standaard true |
Voorbeeld aanroep:
https://[klantomgeving.klant.nl]/ integration/api/narrowcasting/queue?groupExternalIDs=a&groupExternalIDs=b&groupExternalIDs=c&showzerodelay=true&onlyshowactiveresources=true
Alles
Url voor aanroep:
https://[klantomgeving.klant.nl]/integration/api/narrowcasting/all
Parameter | Waarde | Omschrijving | Optioneel |
---|---|---|---|
[QUERY]showzerodelay | True of false | Indien true dan worden ook agenda’s getoond die geen uitloop hebben | Ja, standaard true |
[QUERY]onlyshowactiveresources | True of false | Indien true dan worden enkel agenda’s getoond die in de laatste 30 minuten een afspraak hadden en geen afspraken meer hebben op die dag | Ja, standaard true |
Voorbeeld aanroep:
https://[klantomgeving.klant.nl]/integration/api/narrowcasting/all?showzerodelay=true&onlyshowactiveresources=false
Response
De response is een JSON en ziet er als volgt uit:
[
{
"Department": "Bloedafname",
"Resource": "001",
"ResourceDescription": "Ronald heel lange naam hier",
"Display": "10",
"Delay": "10"
},
{
"Department": "Bloedafname",
"Resource": "004",
"ResourceDescription": "Piet",
"Display": "10", //de regels van afronden, tonen vanaf en correctie zijn hier toegepast
"Delay": "10" //de daadwerkelijke waarde
},
{
"Department": "Cardiologie",
"Resource": "002",
"ResourceDescription": "Janssen",
"Display": "0",
"Delay": "0"
}
]
Inloop
Op basis van wachtrijgroep id {groupid}
Url voor aanroep:
https://[klantomgeving.klant.nl]/integration/api/narrowcasting/waitingtimes
Parameter | Waarde | Omschrijving | Optioneel |
---|---|---|---|
[QUERY]groupid | ID van wachtrijgroep | Haalt wachttijden op van de groep, en individueel van de wachtrijen | Nee |
Voorbeeld aanroep:
https://[klantomgeving.klant.nl]/integration/api/narrowcasting/waitingtimes?groupid=1
Response
{
“Name”: ”Group”,
"ExpectedInMinutes": 5,
"ActualInMinutes": 1,
"WaitinginQueue": 5,
"WaitingTimesPerCounter": [
{
“Name”: ”Bloedafname”,
“ExpectedInMinutes": 5,
"ActualInMinutes": 1,
"WaitinginQueue": 5
},
{
…
}
]
}