@model Applications.UniversalApi_WebConsole_App.Models.VeederRoot_ATG_Console_Handler_Models.AtgController @{ ViewData["Title"] = "Auto Tank Gauge System"; Layout = null; } <script> function clearCanvas(canvas) { var cxt = canvas.getContext("2d"); cxt.clearRect(0, 0, canvas.width, canvas.height); } /* * draw a circle in canvas by specify its parameters. * @@constructor * @@param {string} canvas - all things will draw on. * @@param {string} x - center x coordinate value of the circle. * @@param {string} y - center y coordinate value of the circle. * @@param {string} r - radius of the circle. * @@param {string} productDatas - [{height: int-productHeight, color: string-colorValue, text: string-displayText}]. * @@param {string} horiLineDatas - [{height: int-lineHeightValue, strokeColor: string-colorValue, text: string-displayText}]. */ function drawTank(canvas, x, y, r, productDatas, horiLineDatas) { var context = canvas.getContext('2d'); /*draw a circle for a tank.*/ context.beginPath(); context.setLineDash([]); context.arc(x, y, r, 0, 2 * Math.PI, false); context.closePath(); context.lineWidth = 1; context.fillStyle = 'white'; context.fill(); context.strokeStyle = '#550000'; context.stroke(); productDatas.forEach(function (data) { //console.log('height: ' + data.height + ' with color: ' + data.color); var temp_tri_w = Math.sqrt(Math.pow(r, 2) - Math.pow(r - data.height, 2)); var temp_tri_h = r - data.height; //console.log('temp_tri_h: '+temp_tri_h+', cos: '+temp_tri_h/r+', acos: '+Math.acos(temp_tri_h/r)); var sectorArc = Math.acos(temp_tri_h / r) * 2;///360 * 2* Math.PI; //console.log('height: ' + data.height + ', sectorArc: ' + sectorArc); /*draw the sector area*/ context.beginPath(); context.arc(x, y, r, (Math.PI - sectorArc) / 2, (Math.PI - sectorArc) / 2 + sectorArc, false); //var compensate_pixel_count = 10; line_start_x = x - temp_tri_w;//+compensate_pixel_count; line_end_x = x - temp_tri_w + temp_tri_w * 2;//-compensate_pixel_count; line_start_y = y + r - data.height; line_end_y = y + r - data.height; context.moveTo(line_start_x, line_start_y); context.lineTo(line_end_x, line_end_y); //context.strokeStyle = '#a4d3ff'; //context.stroke(); context.closePath(); context.lineWidth = 1; context.fillStyle = data.color; context.fill(); context.font = 'Bold 14px Arial'; context.textAlign = 'center'; context.textBaseline = 'middle'; context.fillStyle = 'black'; // a color name or by using rgb/rgba/hex values context.fillText(data.text, line_start_x - 10, line_start_y); // text and position // context.strokeStyle = '#550000'; // context.stroke(); }); horiLineDatas.forEach(function (data) { if (data.height > r * 2) { console.error('horiLine length should not exceed r*2, but now is ' + data.height); return; } var coordinate_height_y = r * 2 - data.height + y - r; //圆的标准方程(x-a)²+(y-b)²=r² var comp = Math.sqrt(Math.pow(r, 2) - Math.pow(coordinate_height_y - y, 2)); line_start_x = -comp + x; line_end_x = comp + x; //console.log('horiLineDatas, comp is ' + comp + ', line_start_x is' + line_start_x // + ', line_end_x is ' + line_end_x); context.beginPath(); context.lineWidth = 1; context.setLineDash([3, 2]);/*dashes are 3px and spaces are 2px*/ context.moveTo(line_start_x, coordinate_height_y); context.lineTo(line_end_x, coordinate_height_y); context.strokeStyle = data.strokeColor;//'#a4d3ff'; context.stroke(); context.font = 'italic 10px Arial'; context.textAlign = 'center'; context.textBaseline = 'middle'; context.fillStyle = 'gray'; // a color name or by using rgb/rgba/hex values context.fillText(data.text, line_start_x + (line_end_x - line_start_x) / 2, coordinate_height_y - 4); // text and position //context.closePath(); }); } </script> <script src="~/js/Chart.bundle.js"></script> <script src="~/js/jquery-3.4.1.min.js"></script> @*<div class="text-center"> <h3 class="display-4">This is the ATG Console Web App</h3> </div>*@ @if (Model == null || Model.Tanks == null) { <h1>The console is <b>NOT ready</b> for connection, will auto reload once it's done...</h1> <script> setInterval(function () { location.reload() }, 3000); </script> } @if (Model.Tanks != null && Model.Tanks.Any()) @foreach (var tank in Model.Tanks) { var atgConsoleLengthUnitDisplayStr = "mm"; var atgConsoleVolumeUnitDisplayStr = "L"; if (Model.SystemUnit == Edge.Core.IndustryStandardInterface.ATG.SystemUnit.US) atgConsoleLengthUnitDisplayStr = "inches"; else if (Model.SystemUnit == Edge.Core.IndustryStandardInterface.ATG.SystemUnit.ImperialGallons) atgConsoleLengthUnitDisplayStr = "yard"; <div style="border-color:gray;border-style:solid;border-width:thin;padding:4px"> <p style="margin-top:@(10*(tank.TankNumber-1))px"> Tank No.: <b>@tank.TankNumber</b>, has product: <b>@(tank.Product?.ProductLabel ?? "")(code: @(tank.Product?.ProductCode ?? ""))</b> is in tank state: <b>@(tank.State)</b>, Diameter: @(tank.Diameter ?? -1)@atgConsoleLengthUnitDisplayStr </p> <div style="display:flex;"> <!-- Chart.js uses its parent container to update the canvas render and display sizes. However, this method requires the container to be relatively positioned and dedicated to the chart canvas only. --> <div class="chart-container" style="position: relative;height:@(100/Model.Tanks.Count())vh; width:100vh;"> <canvas id="tank_@(tank.TankNumber)_Height_Water_Chart" aria-label="ThisIsTheCanvas" style="">Your browser does not support the canvas element.</canvas> </div> <canvas id="tank_@(tank.TankNumber)_Height_Water_SectionalView" aria-label="ThisIsTheCanvas" style="height:@(100/Model.Tanks.Count())vh; width:50vh;"> Your browser does not support the canvas element. </canvas> </div> </div> <script> var ctx_tank_@(tank.TankNumber)_Height_Water_Chart = document.getElementById('tank_@(tank.TankNumber)_Height_Water_Chart'); var tank_@(tank.TankNumber)_Height_Water_Chart = new Chart(ctx_tank_@(tank.TankNumber)_Height_Water_Chart, { type: 'line', data: { //labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'], datasets: [{ label: '油高', //data: [1761.84, 1661.84, 1561.84, 1461.84, 1361.84, 761.84, 50.84], backgroundColor: 'rgba(204, 204, 0, 0.2)', borderColor: [ 'rgba(255, 99, 132, 1)', 'rgba(54, 162, 235, 1)', 'rgba(255, 206, 86, 1)', 'rgba(75, 192, 192, 1)', 'rgba(153, 102, 255, 1)', 'rgba(255, 159, 64, 1)' ], borderWidth: 1 }, { label: '水高', //data: [61.84, 90.15, 61.84, 120.84, 150.84, 200.84, 350.84], backgroundColor: 'rgba(0, 255, 255, 0.2)', borderColor: [ 'rgba(255, 99, 132, 1)', 'rgba(54, 162, 235, 1)', 'rgba(255, 206, 86, 1)', 'rgba(75, 192, 192, 1)', 'rgba(153, 102, 255, 1)', 'rgba(255, 159, 64, 1)' ], borderWidth: 1 }] }, options: { maintainAspectRatio: false, scales: { yAxes: [{ ticks: { beginAtZero: true, // Include a 'mm' sign in the values callback: function (value, index, values) { return value + '@atgConsoleLengthUnitDisplayStr'; } } }] } } }); //var canvas = document.getElementById('tank_@(tank.TankNumber)_Height_Water_SectionalView'); </script> } <script> var productHeight = 0; var waterHeight = 0; var maxChartStackDataCount = 30; function add_Height_Water_Data(__chart, tankNumber) { $.getJSON('Details/?tankNumber=' + tankNumber, function (datas) { if (datas == null) { console.log('response datas is null'); return; } //console.log(datas); datas.forEach(function (d) { var chartName = 'tank_' + d.tankNumber + '_Height_Water_Chart'; var chart = window[chartName]; if (chart == null) return; //console.log(chart); /* * purge line chart data */ chart.data.datasets.forEach((purgeDataset) => { if (purgeDataset.data.length > maxChartStackDataCount) { //console.log(chartName + '.data in datasets[' + purgeDataset.label + '] has its length of ' + purgeDataset.data.length + ', will truncate'); purgeDataset.data = purgeDataset.data.slice(purgeDataset.data.length - maxChartStackDataCount, purgeDataset.data.length); //chart.data.labels = chart.data.labels.slice(chart.data.labels.length - maxChartStackDataCount, chart.data.labels.length); //console.log(' ' + chartName + ' has truncated and the dataset.data.length now is ' + purgeDataset.data.length); //for (var i = 0; i < dataset.data.length - maxChartStackDataCount; i++) //dataset.data.pop(); } }); if (chart.data.labels.length > maxChartStackDataCount) { chart.data.labels = chart.data.labels.slice(chart.data.labels.length - maxChartStackDataCount, chart.data.labels.length); } //each batch will adding a new x axis value, most likely a time str. chart.data.labels.push(d.timeStamp); chart.data.datasets.forEach((dataset) => { if (dataset.label === "油高") { dataset.data.push(d.height); productHeight = d.height; } else if (dataset.label === "水高") { dataset.data.push(d.water); waterHeight = d.water; } else return; chart.update({ duration: 600, easing: 'easeOutBounce' }); var sectionalViewCanvas = document.getElementById('tank_' + d.tankNumber + '_Height_Water_SectionalView'); this.clearCanvas(sectionalViewCanvas); this.drawTank(sectionalViewCanvas, 150, 80, 60, [ { height: productHeight / 1800 * 60 * 2, color: 'orange', text: '92#' }, { height: waterHeight / 1800 * 60 * 2, color: '#00ced1', text: 'water' }], [{ height: 90, strokeColor: 'red', text: 'fuel high' }, { height: 30, strokeColor: 'blue', text: 'water high' }]); }); }); }) } function removeData(chart) { chart.data.labels.pop(); chart.data.datasets.forEach((dataset) => { dataset.data.pop(); }); chart.update(); } setInterval(function () { add_Height_Water_Data(null, null) }, 1500); </script>