diff --git a/docs/images/CallCenter_de.svg b/docs/images/CallCenter_de.svg new file mode 100644 index 0000000..d633159 --- /dev/null +++ b/docs/images/CallCenter_de.svg @@ -0,0 +1,242 @@ + + + \ No newline at end of file diff --git a/docs/images/CallCenter_de_dark.svg b/docs/images/CallCenter_de_dark.svg new file mode 100644 index 0000000..f5a32dc --- /dev/null +++ b/docs/images/CallCenter_de_dark.svg @@ -0,0 +1,202 @@ + + + \ No newline at end of file diff --git a/docs/images/CallCenter_en.svg b/docs/images/CallCenter_en.svg new file mode 100644 index 0000000..ef591ee --- /dev/null +++ b/docs/images/CallCenter_en.svg @@ -0,0 +1,211 @@ + + + \ No newline at end of file diff --git a/docs/images/CallCenter_en_dark.svg b/docs/images/CallCenter_en_dark.svg new file mode 100644 index 0000000..a4a7f06 --- /dev/null +++ b/docs/images/CallCenter_en_dark.svg @@ -0,0 +1,171 @@ + + + \ No newline at end of file diff --git a/docs/index.js b/docs/index.js index 9f138c3..15df594 100644 --- a/docs/index.js +++ b/docs/index.js @@ -87,6 +87,7 @@ mainNavBar.insertBefore(buildMultiNavDropdown("DesignMenu",language.GUI.tabDesig {id: "Compare", name: language.GUI.tabCompare, modes: {values: true}}, {id: "ShortestQueue", name: language.GUI.tabShortestQueue}, {id: "EconomyOfScale", name: language.GUI.tabEconomyOfScale, modes: {table: true, diagram: true}}, + {id: "CallCenter", name: language.GUI.tabCallCenter, modes: {diagram: true}}, ]),mainNavBar.children[insertCount++]); /* Content */ diff --git a/docs/js/Language.js b/docs/js/Language.js index e1cf5a3..f50a0d6 100644 --- a/docs/js/Language.js +++ b/docs/js/Language.js @@ -87,7 +87,10 @@ lang.GUI.formulaShortestQueueBInfo2=""; lang.GUI.formulaShortestQueueInfoAB="Wahrscheinlichkeit an der kürzeren Warteschlange länger warten zu müssen"; lang.GUI.formulaEconomyOfScale="Economy of Scale"; lang.GUI.formulaEconomyOfScaleLong="Economy of Scale (Positiver Skaleneffekt)"; -lang.GUI.formulaEconomyOfScaleInfo="Die Economy of Scale beschreibt in Bezug auf stochatische Systeme den Effekt, dass die Wartezeiten bei insgesamt identischer Auslastung bei größeren Systemen kürzer ausfallen, als bei kleineren Systemen. Bei Warteschlangenmodellen mit mehr Bedienern können sich Schwankungen im Ankunftsstrom und bei den Bedienzeiten besser gegenseitig ausgleichen als bei kleineren Systemen."; +lang.GUI.formulaEconomyOfScaleInfo="Die Economy of Scale beschreibt in Bezug auf stochastische Systeme den Effekt, dass die Wartezeiten bei insgesamt identischer Auslastung bei größeren Systemen kürzer ausfallen, als bei kleineren Systemen. Bei Warteschlangenmodellen mit mehr Bedienern können sich Schwankungen im Ankunftsstrom und bei den Bedienzeiten besser gegenseitig ausgleichen als bei kleineren Systemen."; +lang.GUI.formulaCallCenter="Callcenter-Planung"; +lang.GUI.formulaCallCenterLong="Callcenter-Planung"; +lang.GUI.formulaCallCenterInfo="In diesem Modell können die Auswirkungen des Hinzufügens oder Entfernens einzelner Bediener von der Bedienstation untersucht werden. Neben einem Diagramm, in dem die Kenngrößen in Abhängigkeit von der Anzahl an eingesetzten Agenten angezeigt werden, wird in Tabellenform ausgegeben, welche konkreten Auswirkungen einzelne Veränderungsschritte haben."; lang.GUI.tabHome="Start"; lang.GUI.tabErlangB="Erlang-B-Formel"; lang.GUI.tabErlangBInfo="M/M/c/C"; @@ -108,6 +111,7 @@ lang.GUI.tabDesign="Systemdesign"; lang.GUI.tabCompare="Vergleich verschiedener Strategien"; lang.GUI.tabShortestQueue="Wahl der kürzesten Schlange"; lang.GUI.tabEconomyOfScale="Economy of Scale"; +lang.GUI.tabCallCenter="Callcenter-Planung"; lang.GUI.tabSimulation='Simulation'; lang.GUI.tabSimulationInfo='Wenn die Möglichkeiten der (erweiterten) Erlang-C-Formel und der (erweiterten) Allen-Cunneen-Näherungsformel zur Modellierung eines Warteschlangensystems nicht mehr ausreichen, kann eines der hier angebotenen Opensource Simulationswerkzeuge eingesetzt werden.'; lang.GUI.tabHelp="Hilfe"; @@ -742,6 +746,17 @@ Im Kontext der Warteschlangentheorie ist jedoch besonders der Effekt, dass sich In dem Rechenmodell kann die mittlere Auslastung (ρ) der Bediener fest vorgegeben werden. Wird nun die Anzahl an Bedienern (c) verändert, so ändert sich automatisch auch die Zwischenankunftszeit (E[I]), so dass insgesamt die Auslastung (ρ=E[S]/E[I]/c) konstant bleibt. Auf diese Weise können über die Variation der Anzahl an Bedienern verschieden große Systeme jeweils bei exakt derselben Auslastung verglichen werden.
`; +lang.text.CallCenterDiagram=` ++Sofern Anrufwiederholungen nicht abgebildet werden müssen, können Callcenter über die erweiterte Erlang-C-Formel abgebildet werden. +Während Anrufsraten und Bediendauern meist nicht beeinflussbar sind, ist die Anzahl an eingesetzten Agenten die Stellgröße, über die Betriebskosten und Leistungskenngrößen des Systems optimiert werden können. +Je mehr Agenten eingesetzt werden, desto besser fallen die Kenngrößen aus, aber auch desto mehr Lohnkosten fallen an. +
++Auf diese Seite kann die Anzahl an Agenten für vorgegebene Rahmenparameter variiert werden. Die Auswirkungen der verschiedenen Anzahlen an Agenten werden nicht nur in grafischer Form dargestellt, sondern auch +als Tabelle inkl. Zusatzzeilen zur Angabe der jeweiligen Veränderungen von Schritt zu Schritt. +
`; + /* English */ const languageEN={}; @@ -809,6 +824,9 @@ lang.GUI.formulaShortestQueueInfoAB="Probability of having to wait longer in the lang.GUI.formulaEconomyOfScale="Economy of Scale"; lang.GUI.formulaEconomyOfScaleLong="Economy of Scale"; lang.GUI.formulaEconomyOfScaleInfo="In terms of stochastic systems, the economy of scale describes the effect that waiting times are shorter in larger systems than in smaller systems, with an overall identical workload. In queueing models with more operators, fluctuations in the arrival stream and in the service times can balance each other out better than in smaller systems."; +lang.GUI.formulaCallCenter="Call center planning"; +lang.GUI.formulaCallCenterLong="Call center planning"; +lang.GUI.formulaCallCenterInfo="This model can be used to examine the effects of adding or removing individual operators from the process station. In addition to a diagram in which the results are displayed as a function of the number of agents used, the specific effects of individual change steps are shown in tabular form."; lang.GUI.tabHome="Start"; lang.GUI.tabErlangB="Erlang-B formula"; lang.GUI.tabErlangBInfo="M/M/c/C"; @@ -829,6 +847,7 @@ lang.GUI.tabDesign="System design"; lang.GUI.tabCompare="Comparison of different strategies"; lang.GUI.tabShortestQueue="Choice of the shortest queue"; lang.GUI.tabEconomyOfScale="Economy of Scale"; +lang.GUI.tabCallCenter="Call center planning"; lang.GUI.tabSimulation='Simulation'; lang.GUI.tabSimulationInfo='If the capabilities of the (extended) Erlang-C formula and the (extended) Allen-Cunneen approximation formula are not sufficient anymore to model a queueing system, one of the open source simulation tools offered here can be used.'; lang.GUI.tabHelp="Help"; @@ -1473,6 +1492,17 @@ In the context of queueing theory, the effect that in larger systems the fluctua In the calculation model, the average utilization (ρ) of the operators can be fixed. If the number of operators (c) is now changed, the inter-arrival time (E[I]) also changes automatically, so that the overall utilization (ρ=E[S]/E[I]/c) remains constant. In this way, by varying the number of operators, systems of different sizes can each be compared at exactly the same workload. `; +lang.text.CallCenterDiagram=` ++If retry is not needed to be mapped, call centers can be mapped using the extended Erlang C formula. +While arrival rates and service times cannot usually be influenced, the number of agents deployed is the control variable that can be used to optimize the operating costs and performance parameters of the system. +The more agents deployed, the better the performance indicators, but also the higher the labor costs. +
++On this page, the number of agents can be varied for specified framework parameters. The effects of the different numbers of agents are not only displayed in graphical form, +but also as a table including additional lines to indicate the respective changes from step to step. +
`; + /* Activate language */ const language=(document.documentElement.lang=='de')?languageDE:languageEN; \ No newline at end of file diff --git a/docs/js/gui_CallCenter.js b/docs/js/gui_CallCenter.js new file mode 100644 index 0000000..e2062de --- /dev/null +++ b/docs/js/gui_CallCenter.js @@ -0,0 +1,224 @@ +/* +Copyright 2024 Alexander Herzog + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +export {tilesCallCenter}; + +import {TilesBuilder, Table} from './tools_gui.js'; +import {calcExtErlangC} from './gui_ExtErlangC.js'; +import {language} from './Language.js'; + + +/** + * Input tiles for the economy of scale + */ +const tilesCallCenter=new TilesBuilder('CallCenter'); + +tilesCallCenter.add( + language.model.inputInterArrivalTimeMean, + "E[I]", + "EI", + 100, + 5, + 150, + language.model.invalidPositiveFloat, + language.model.invalidPositiveFloat, + language.model.inputInterArrivalTimeMeanInfo1, + language.model.inputInterArrivalTimeMeanInfo2, + "PositiveFloat", + false, + false +); + +tilesCallCenter.add( + language.model.inputServiceTimeMean, + "E[S]", + "ES", + 500, + 5, + 1000, + language.model.invalidPositiveFloat, + language.model.invalidPositiveFloat, + language.model.inputServiceTimeMeanInfo1, + language.model.inputServiceTimeMeanInfo2, + "PositiveFloat", + false, + false +); + +tilesCallCenter.add( + language.model.inputWaitingTimeToleranceMean, + "E[WT]", + "EWT", + 900, + 30, + 60, + language.model.invalidPositiveFloat, + language.model.invalidPositiveFloat, + language.model.inputWaitingTimeToleranceMeanInfo1, + language.model.inputWaitingTimeToleranceMeanInfo2, + "PositiveFloat", + false, + false +); + +tilesCallCenter.add( + language.model.inputServiceLevel, + "t", + "t", + 20, + 1, + 0, + language.model.invalidNotNegativeFloat, + language.model.invalidPositiveFloat, + language.model.inputServiceLevelInfo1, + language.model.inputServiceLevelInfo2, + "NotNegativeFloat", + true, + false +); + +tilesCallCenter.add( + language.model.inputNumberOfOperators, + "c", + "c", + 5, + 1, + 10, + language.model.invalidPositiveInt, + language.model.invalidPositiveInt, + language.model.inputNumberOfOperatorsInfo1, + language.model.inputNumberOfOperatorsInfo2, + "PositiveInt", + false, + true +); + +/** + * Calculates the call center results for an individual set of input parameters. + * @param {Object} input Input values + * @returns {Object} Results + */ +function calcCallCenter(input) { + const temp=[...input]; + temp.splice(2,0,1000); + const c=temp[5]; + temp[5]=temp[4]; + temp[4]=c; + return calcExtErlangC(temp); +} + +/** + * Generates a results table based on the input values in table or diagram mode. + * @param {String} mode Which input elements are to be used ("Table" or "Diagram")? + * @returns {Object} Table object with the calculated values. + */ +function calcCallCenterTable(mode) { + const input=tilesCallCenter.rangeValues(mode); + if (input==null) return null; + + let table=new Table(); + + table.addHeading('E[I]',language.model.inputInterArrivalTimeMean); + table.addHeading('E[S]',language.model.inputServiceTimeMean); + table.addHeading('K',language.statistics.SystemSize); + table.addHeading('E[WT]',language.model.inputWaitingTimeToleranceMean); + table.addHeading('c',language.model.inputNumberOfOperators); + table.addHeading('t',language.model.inputServiceLevelSeconds); + table.addHeading('a ('+language.statistics.characteristicsModeInput+')',language.statistics.Workload+' ('+language.statistics.characteristicsModeInput+')'); + table.addHeading('ρ ('+language.statistics.characteristicsModeInput+')',language.statistics.Utilization+' ('+language.statistics.characteristicsModeInput+')'); + table.addHeading('P(A)',language.statistics.waitingCancelationProbability); + table.addHeading('E[I] ('+language.statistics.characteristicsModeNet+')',language.model.inputInterArrivalTimeMean+' ('+language.statistics.characteristicsModeNet+')'); + table.addHeading('ρ ('+language.statistics.characteristicsModeNet+')',language.statistics.Utilization+' ('+language.statistics.characteristicsModeNet+')'); + table.addHeading('E[W]',language.statistics.averageWaitingTime); + table.addHeading('E[V]',language.statistics.averageResidenceTime); + table.addHeading('E[NQ]',language.statistics.averageNQ); + table.addHeading('E[NS]',language.statistics.averageNS); + table.addHeading('E[N]',language.statistics.averageN); + table.addHeading('P(N=0)',language.statistics.emptySystemProbability); + table.addHeading('P(W>0)',language.statistics.waitingProbability); + table.addHeading('P(W≤t)',language.model.inputServiceLevel); + + table.calc(input,function(table,input) { + const data=calcCallCenter(input); + table.addCol(data.EI); + table.addCol(data.ES); + table.addCol(data.K); + table.addCol(data.EWT); + table.addCol(data.c); + table.addCol(data.t); + table.addCol(data.a); + table.addColPercent(data.rho); + table.addColPercent(data.PA); + table.addCol(data.EINet); + table.addColPercent(data.rhoNet); + table.addCol(data.EW); + table.addCol(data.EV); + table.addCol(data.ENQ); + table.addCol(data.ENS); + table.addCol(data.EN); + table.addColPercent(data.PNeq0); + table.addColPercent(data.PWgt0); + table.addColPercent(data.PWlet); + }); + + return table; +} + +/* Diagram */ + +/** + * Callback to notify the tiles system that a fix/range tab has changed (in diagram mode). + * @param {Object} sender Tab which was changed + */ +function changeTabCallCenterDiagram(sender) { + tilesCallCenter.updateTabs(sender,'Diagram'); + updateCallCenterDiagram(); +} + +/** + * Callback for updating the diagram results. + */ +function updateCallCenterDiagram() { + const table=calcCallCenterTable('Diagram'); + if (table==null) return; + + let xAxisTitle=''; + switch (table.xValuesCol) { + case 0: xAxisTitle='E[I] ('+language.statistics.unitTime+')'; break; + case 1: xAxisTitle='E[S] ('+language.statistics.unitTime+')'; break; + case 2: xAxisTitle='E[WT] ('+language.statistics.unitTime+')'; break; + case 3: xAxisTitle='c ('+language.statistics.unitNumber+')'; break; + case 4: xAxisTitle='t ('+language.statistics.unitTime+')'; break; + } + + const ySetup=[ + {columnIndex: 8, color: 'red', mode: 'percent'}, /* P(A) */ + {columnIndex: 11, color: 'yellow', mode: 'time'}, /* E[W] */ + {columnIndex: 12, color: 'green', mode: 'time'}, /* E[V] */ + {columnIndex: 13, color: 'orange', mode: 'number'}, /* E[NQ] */ + {columnIndex: 15, color: 'blue', mode: 'number'}, /* E[N] */ + {columnIndex: 7, color: 'lightgray', mode: 'percent'}, /* rho */ + {columnIndex: 10, color: 'gray', mode: 'percent'}, /* rhoNet */ + {columnIndex: 18, color: 'black', mode: 'percent'} /* P(W<=t) */ + ]; + + table.diagram('CallCenterDiagram_results',table.xValuesCol,xAxisTitle,ySetup,{x: 4, y: Array.from({length: 12},(_,i)=>i+7)}); +} + +/* General setup */ + +window.updateCallCenterDiagram=updateCallCenterDiagram; +window.changeTabCallCenterDiagram=changeTabCallCenterDiagram; \ No newline at end of file diff --git a/docs/js/gui_Start.js b/docs/js/gui_Start.js index 60458a1..332952f 100644 --- a/docs/js/gui_Start.js +++ b/docs/js/gui_Start.js @@ -28,6 +28,7 @@ import {tilesExtAC} from './gui_ExtAC.js'; import {tilesCompare} from './gui_Compare.js'; import {tilesShortestQueue} from './gui_ShortestQueue.js'; import {tilesEconomyOfScale} from './gui_EconomyOfScale.js'; +import {tilesCallCenter} from './gui_CallCenter.js'; import {formulasErlangB, formulasErlangC, formulasExtErlangC, formulasPC, formulasKingman, formulasAC, formulasExtAC, formulasCompare, formulasShortestQueue} from './FormulaBuilder.js'; import {language} from './Language.js'; @@ -101,6 +102,7 @@ function buildStartTiles(isDesktopApp) { block+=buildStartTile(6,language.GUI.formulaCompare,language.GUI.formulaCompareInfo,"Compare","100%",false,"svg","602.67 / 279.23",{values: true}); block+=buildStartTile(6,language.GUI.formulaShortestQueue,language.GUI.formulaShortestQueueInfo,"ShortestQueue","100%",false,"svg","602.67 / 319.07"); block+=buildStartTile(6,language.GUI.formulaEconomyOfScale,language.GUI.formulaEconomyOfScaleInfo,"EconomyOfScale","100%",false,"svg","151.34 / 93.66",{table: true, diagram: true}); + block+=buildStartTile(6,language.GUI.formulaCallCenter,language.GUI.formulaCallCenterInfo,"CallCenter","100%",false,"svg","151.34 / 61.89",{diagram: true}); block+=buildStartTile(6,language.GUI.tabSimulation,language.GUI.tabSimulationInfo,"Simulation","100%",true,'webp','640 / 481',false," "); block+="