951 lines
28 KiB
PHP
951 lines
28 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
// phpcs:disable
|
|
|
|
header('Cache-Control: no-store, no-cache, must-revalidate');
|
|
header('Pragma: no-cache');
|
|
header('Content-Type: text/html; charset=UTF-8');
|
|
|
|
date_default_timezone_set('America/Chicago');
|
|
|
|
/**
|
|
* Capture the rendered output of a partial include and return it as a string.
|
|
*/
|
|
function capture_include(string $relativePath): string
|
|
{
|
|
ob_start();
|
|
include __DIR__ . '/' . $relativePath;
|
|
|
|
return trim(ob_get_clean());
|
|
}
|
|
|
|
/**
|
|
* Safely fetch a value from an array.
|
|
*
|
|
* @param array<mixed> $source
|
|
*/
|
|
function array_value(array $source, string $key)
|
|
{
|
|
return array_key_exists($key, $source) ? $source[$key] : null;
|
|
}
|
|
|
|
function numeric_or_null($value): ?float
|
|
{
|
|
if ($value === null) {
|
|
return null;
|
|
}
|
|
|
|
if (is_numeric($value)) {
|
|
return (float) $value;
|
|
}
|
|
|
|
$normalised = str_replace([',', ' '], '', (string) $value);
|
|
$normalised = preg_replace('/[^0-9.\-]/', '', $normalised ?? '');
|
|
|
|
if ($normalised === '' || !is_numeric($normalised)) {
|
|
return null;
|
|
}
|
|
|
|
return (float) $normalised;
|
|
}
|
|
|
|
function format_number($value, int $decimals = 0): string
|
|
{
|
|
$numeric = numeric_or_null($value);
|
|
|
|
return $numeric === null ? '—' : number_format($numeric, $decimals);
|
|
}
|
|
|
|
function format_with_unit($value, string $unit, int $decimals = 0): string
|
|
{
|
|
$formatted = format_number($value, $decimals);
|
|
|
|
return $formatted === '—' ? $formatted : $formatted . ' ' . $unit;
|
|
}
|
|
|
|
/**
|
|
* @param array<float|null> $values
|
|
*/
|
|
function sum_numeric(array $values): ?float
|
|
{
|
|
$total = 0.0;
|
|
$found = false;
|
|
|
|
foreach ($values as $value) {
|
|
$numeric = numeric_or_null($value);
|
|
if ($numeric === null) {
|
|
continue;
|
|
}
|
|
|
|
$total += $numeric;
|
|
$found = true;
|
|
}
|
|
|
|
return $found ? $total : null;
|
|
}
|
|
|
|
function clamp_percent(?float $value): float
|
|
{
|
|
if ($value === null) {
|
|
return 0.0;
|
|
}
|
|
|
|
return max(0.0, min(100.0, $value));
|
|
}
|
|
|
|
function percent_label($value, int $decimals = 0): string
|
|
{
|
|
$numeric = numeric_or_null($value);
|
|
|
|
return $numeric === null ? '—' : format_number($numeric, $decimals) . '%';
|
|
}
|
|
|
|
function html(string $value): string
|
|
{
|
|
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
|
|
}
|
|
|
|
/**
|
|
* @param array<mixed> $source
|
|
*/
|
|
function value_num(array $source, string $key): ?float
|
|
{
|
|
return numeric_or_null(array_value($source, $key));
|
|
}
|
|
|
|
/**
|
|
* @param array<mixed> $source
|
|
*/
|
|
function value_as_int(array $source, string $key): ?int
|
|
{
|
|
$numeric = value_num($source, $key);
|
|
|
|
return $numeric === null ? null : (int) round($numeric);
|
|
}
|
|
|
|
function build_status(?int $rawValue, bool $autoIsZero = true): ?array
|
|
{
|
|
if ($rawValue === null) {
|
|
return null;
|
|
}
|
|
|
|
$isAuto = $autoIsZero ? $rawValue === 0 : $rawValue === 1;
|
|
|
|
return [
|
|
'label' => $isAuto ? 'Auto' : 'Manual',
|
|
'class' => $isAuto ? 'status-pill--ok' : 'status-pill--alert',
|
|
];
|
|
}
|
|
|
|
function build_switch(?int $rawValue, string $labelOn = 'On', string $labelOff = 'Off'): ?array
|
|
{
|
|
if ($rawValue === null) {
|
|
return null;
|
|
}
|
|
|
|
$isOn = $rawValue === 0;
|
|
|
|
return [
|
|
'label' => $isOn ? $labelOn : $labelOff,
|
|
'class' => $isOn ? 'status-pill--ok' : 'status-pill--warn',
|
|
];
|
|
}
|
|
|
|
function classify_live_steam(?float $value): ?array
|
|
{
|
|
if ($value === null) {
|
|
return null;
|
|
}
|
|
|
|
if ($value <= 155) {
|
|
return ['label' => 'Low', 'class' => 'status-pill--alert'];
|
|
}
|
|
|
|
if ($value <= 165) {
|
|
return ['label' => 'Watch', 'class' => 'status-pill--warn'];
|
|
}
|
|
|
|
return ['label' => 'Stable', 'class' => 'status-pill--ok'];
|
|
}
|
|
|
|
function classify_exhaust(?float $value): ?array
|
|
{
|
|
if ($value === null) {
|
|
return null;
|
|
}
|
|
|
|
if ($value <= 9) {
|
|
return ['label' => 'Low', 'class' => 'status-pill--alert'];
|
|
}
|
|
|
|
if ($value <= 13) {
|
|
return ['label' => 'Watch', 'class' => 'status-pill--warn'];
|
|
}
|
|
|
|
return ['label' => 'Stable', 'class' => 'status-pill--ok'];
|
|
}
|
|
|
|
function tank_level_state(?float $value): ?string
|
|
{
|
|
if ($value === null) {
|
|
return null;
|
|
}
|
|
|
|
if ($value <= 20) {
|
|
return 'low';
|
|
}
|
|
|
|
if ($value >= 90) {
|
|
return 'caution';
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
include '../../items.php';
|
|
|
|
$valuesZero = $value ?? [];
|
|
$roundedZero = $rounded ?? [];
|
|
$idsZero = $ID ?? [];
|
|
$roundedOneZero = $rounded1 ?? [];
|
|
|
|
include '../../items2dec.php';
|
|
|
|
$valuesTwo = $value ?? [];
|
|
$idsTwo = $ID ?? [];
|
|
|
|
$value = $valuesZero;
|
|
$rounded = $roundedZero;
|
|
$ID = $idsZero;
|
|
$rounded1 = $roundedOneZero;
|
|
|
|
$now = new DateTimeImmutable('now', new DateTimeZone('America/Chicago'));
|
|
$generatedAt = $now->format(DateTimeInterface::RFC3339);
|
|
|
|
$cropDayRaw = capture_include('cropday.php');
|
|
$cropDay = $cropDayRaw !== '' ? $cropDayRaw : null;
|
|
$tonsIn = numeric_or_null(capture_include('tonsin.php'));
|
|
$recordTons = numeric_or_null(capture_include('record.php'));
|
|
$recordDateRaw = capture_include('recorddate.php');
|
|
$recordDate = $recordDateRaw !== '' ? $recordDateRaw : null;
|
|
$westAvg = numeric_or_null(capture_include('west15minavg.php'));
|
|
|
|
$groundToday = sum_numeric([
|
|
value_num($value, 'CANETOT'),
|
|
value_num($value, 'W TONS GROUND'),
|
|
]);
|
|
|
|
$groundYesterday = sum_numeric([
|
|
value_num($value, 'PREVTONS'),
|
|
value_num($value, 'WPREVGROUND'),
|
|
]);
|
|
|
|
$runHours = value_num($value, 'RUNHRSTODAY');
|
|
$prevRunHours = value_num($value, 'PREVTIME');
|
|
$totalTonsPerHour = sum_numeric([
|
|
value_num($value, 'TONS_PER_HOUR'),
|
|
value_num($value, 'RATE'),
|
|
]);
|
|
|
|
$lossTime = value_num($value, 'LOSSTIME');
|
|
$eastAverage = value_num($value, 'AVG_TONS_PER_HOUR');
|
|
|
|
$summaryCards = [
|
|
[
|
|
'label' => 'Cane In Today',
|
|
'value' => format_number($tonsIn),
|
|
'unit' => 'tons',
|
|
],
|
|
[
|
|
'label' => 'Ground Today',
|
|
'value' => format_number($groundToday),
|
|
'unit' => 'tons',
|
|
],
|
|
[
|
|
'label' => 'Total Ground Yesterday',
|
|
'value' => format_number($groundYesterday),
|
|
'unit' => 'tons',
|
|
],
|
|
[
|
|
'label' => 'Run Hours',
|
|
'value' => format_number($runHours, 1),
|
|
'unit' => 'hours',
|
|
],
|
|
[
|
|
'label' => 'Prev Run Hours',
|
|
'value' => format_number($prevRunHours, 1),
|
|
'unit' => 'hours',
|
|
],
|
|
[
|
|
'label' => 'Total Tons per Hour',
|
|
'value' => format_number($totalTonsPerHour),
|
|
'unit' => 'tons/hr',
|
|
],
|
|
[
|
|
'label' => 'East 15 Minute Avg',
|
|
'value' => format_number($eastAverage),
|
|
'unit' => 'tons/hr',
|
|
],
|
|
[
|
|
'label' => 'West 15 Minute Avg',
|
|
'value' => format_number($westAvg),
|
|
'unit' => 'tons/hr',
|
|
],
|
|
[
|
|
'label' => 'Loss Time Today',
|
|
'value' => format_number($lossTime, 2),
|
|
'unit' => 'hours',
|
|
],
|
|
[
|
|
'label' => 'Record Day',
|
|
'value' => format_number($recordTons),
|
|
'unit' => 'tons',
|
|
'note' => $recordDate ? 'on ' . $recordDate : null,
|
|
],
|
|
];
|
|
|
|
$levelThroughputSwitch = value_as_int($value, 'LEVEL_THROUGHPUT_SWITCH');
|
|
$millFourUnit = $levelThroughputSwitch === 1 ? 'tons/hr' : '%';
|
|
|
|
$eastStages = [
|
|
[
|
|
'label' => 'Mill 1',
|
|
'stage' => '1',
|
|
'level' => value_num($value, 'MILL1LVLPV'),
|
|
'setpoint' => value_num($value, 'MILL1SP'),
|
|
'setpointUnit' => '%',
|
|
'rpm' => value_num($value, 'EMILL1RPM'),
|
|
'outputLabel' => 'Mill 1 OP',
|
|
'outputValue' => value_num($value, 'MILL1OUTPUT'),
|
|
'outputUnit' => '%',
|
|
'mode' => build_status(value_as_int($value, 'MILL1AUTOMAN'), true),
|
|
],
|
|
[
|
|
'label' => 'Mill 2',
|
|
'stage' => '2',
|
|
'level' => value_num($value, 'M2LVL'),
|
|
'setpoint' => value_num($value, 'MILL2SP'),
|
|
'setpointUnit' => '%',
|
|
'rpm' => value_num($value, 'EMILL2RPM'),
|
|
'outputLabel' => 'MCC Output',
|
|
'outputValue' => value_num($value, 'MCCOUTPUT'),
|
|
'outputUnit' => '%',
|
|
'mode' => null,
|
|
],
|
|
[
|
|
'label' => 'Mill 3',
|
|
'stage' => '3',
|
|
'level' => value_num($value, 'MIL3LVL'),
|
|
'setpoint' => value_num($value, 'MILL3SP'),
|
|
'setpointUnit' => '%',
|
|
'rpm' => value_num($value, 'EMILL3RPM'),
|
|
'outputLabel' => 'MCC Tons',
|
|
'outputValue' => value_num($rounded1, 'MCCTONS'),
|
|
'outputUnit' => 'tons',
|
|
'mode' => null,
|
|
],
|
|
[
|
|
'label' => 'Mill 4',
|
|
'stage' => '4',
|
|
'level' => value_num($value, 'MIL4LVL'),
|
|
'setpoint' => value_num($value, 'MILL4SP'),
|
|
'setpointUnit' => '%',
|
|
'rpm' => value_num($value, 'EMILL4RPM'),
|
|
'outputLabel' => 'Mill 1 SP',
|
|
'outputValue' => value_num($value, 'WEIGHT_WSP'),
|
|
'outputUnit' => $millFourUnit,
|
|
'mode' => build_status(value_as_int($value, 'MILL1AUTOMAN1'), false),
|
|
],
|
|
[
|
|
'label' => 'Mill 5',
|
|
'stage' => '5',
|
|
'level' => value_num($value, 'MIL5LVL'),
|
|
'setpoint' => value_num($value, 'MILL5SP'),
|
|
'setpointUnit' => '%',
|
|
'rpm' => value_num($value, 'EMILL5RPM'),
|
|
'outputLabel' => 'Grinding Rate',
|
|
'outputValue' => value_num($value, 'TONS_PER_HOUR'),
|
|
'outputUnit' => 'tons/hr',
|
|
'mode' => null,
|
|
],
|
|
[
|
|
'label' => 'Mill 6',
|
|
'stage' => '6',
|
|
'level' => value_num($value, 'MIL6LVL'),
|
|
'setpoint' => value_num($value, 'MILL6SP'),
|
|
'setpointUnit' => '%',
|
|
'rpm' => value_num($value, 'EMILL6RPM'),
|
|
'outputLabel' => 'Average Rate',
|
|
'outputValue' => value_num($value, 'AVG_TONS_PER_HOUR'),
|
|
'outputUnit' => 'tons/15 min',
|
|
'mode' => null,
|
|
],
|
|
];
|
|
|
|
$eastFooterMetrics = [
|
|
['label' => 'Imbibition 6 Flow', 'value' => format_with_unit(value_num($value, 'IMBFLOW'), 'gpm')],
|
|
['label' => 'Imbibition 6 SP', 'value' => format_with_unit(value_num($value, 'IMBSP'), 'gpm')],
|
|
['label' => 'Imbibition 5 Flow', 'value' => format_with_unit(value_num($value, 'IMBFLOW'), 'gpm')],
|
|
['label' => 'Imbibition 5 SP', 'value' => format_with_unit(value_num($value, 'IMB5SP'), 'gpm')],
|
|
];
|
|
|
|
$westStages = [
|
|
[
|
|
'label' => 'Mill 1',
|
|
'stage' => '1',
|
|
'level' => value_num($ID, '00654'),
|
|
'setpoint' => null,
|
|
'setpointUnit' => '%',
|
|
'rpm' => value_num($ID, '00666'),
|
|
'outputLabel' => 'MCC FPM',
|
|
'outputValue' => value_num($value, 'Feet/Minute'),
|
|
'outputUnit' => 'fpm',
|
|
'mode' => null,
|
|
],
|
|
[
|
|
'label' => 'Mill 2',
|
|
'stage' => '2',
|
|
'level' => value_num($ID, '00656'),
|
|
'setpoint' => null,
|
|
'setpointUnit' => '%',
|
|
'rpm' => value_num($ID, '00670'),
|
|
'outputLabel' => 'MCC Output',
|
|
'outputValue' => value_num($ID, '00638'),
|
|
'outputUnit' => '%',
|
|
'mode' => null,
|
|
],
|
|
[
|
|
'label' => 'Mill 4',
|
|
'stage' => '4',
|
|
'level' => value_num($ID, '00658'),
|
|
'setpoint' => null,
|
|
'setpointUnit' => '%',
|
|
'rpm' => value_num($ID, '00674'),
|
|
'outputLabel' => 'Mill 1 SP',
|
|
'outputValue' => null,
|
|
'outputUnit' => '%',
|
|
'mode' => null,
|
|
],
|
|
[
|
|
'label' => 'Mill 5',
|
|
'stage' => '5',
|
|
'level' => value_num($ID, '00660'),
|
|
'setpoint' => null,
|
|
'setpointUnit' => '%',
|
|
'rpm' => value_num($ID, '00680'),
|
|
'outputLabel' => 'Grinding Rate',
|
|
'outputValue' => value_num($value, 'RATE'),
|
|
'outputUnit' => 'tons/hr',
|
|
'mode' => null,
|
|
],
|
|
];
|
|
|
|
$westFooterMetrics = [
|
|
['label' => 'Imbibition Flow', 'value' => format_with_unit(value_num($ID, '00624'), 'gpm')],
|
|
['label' => 'Average Rate', 'value' => format_with_unit($westAvg, 'tons/15 min')],
|
|
];
|
|
|
|
$steamFlow = value_num($value, 'TTL_STEAMFLOW2');
|
|
$liveSteam = value_num($value, 'PT_001');
|
|
$exhaustPressure = value_num($ID, '00244');
|
|
$exhaustMode = build_status(value_as_int($value, 'ExhaustAM'), true);
|
|
$vaporHeader = value_num($value, 'VAPOR HDR PRES');
|
|
|
|
$boilerOverview = [
|
|
[
|
|
'label' => 'Total Steam Flow',
|
|
'value' => format_number($steamFlow),
|
|
'unit' => 'kpph',
|
|
],
|
|
[
|
|
'label' => 'Live Steam Pressure',
|
|
'value' => format_number($liveSteam),
|
|
'unit' => 'psi',
|
|
'status' => classify_live_steam($liveSteam),
|
|
],
|
|
[
|
|
'label' => 'Exhaust Pressure',
|
|
'value' => format_number($exhaustPressure),
|
|
'unit' => 'psi',
|
|
'status' => classify_exhaust($exhaustPressure),
|
|
'mode' => $exhaustMode,
|
|
],
|
|
[
|
|
'label' => 'Vapor Header Pressure',
|
|
'value' => format_number($vaporHeader),
|
|
'unit' => 'psi',
|
|
],
|
|
];
|
|
|
|
$boilerFlows = [
|
|
['label' => 'Boiler 1', 'value' => format_number(value_num($ID, '00252'))],
|
|
['label' => 'Boiler 2', 'value' => format_number(value_num($ID, '00256'))],
|
|
['label' => 'Boiler 3', 'value' => format_number(value_num($value, 'FT_301'))],
|
|
['label' => 'Boiler 4', 'value' => format_number(value_num($value, 'FT_401'))],
|
|
['label' => 'Boiler 5', 'value' => format_number(value_num($value, 'FT_501'))],
|
|
['label' => 'Boiler 6', 'value' => format_number(value_num($value, 'FT_601'))],
|
|
['label' => 'Boiler 7', 'value' => format_number(value_num($ID, '00046'))],
|
|
['label' => 'Boiler 8', 'value' => format_number(value_num($ID, '00074'))],
|
|
];
|
|
|
|
$syrupOverflow = value_num($value, 'Syrup Overflow Lvl');
|
|
$syrupReceiver = value_num($value, 'Syrup RCVR');
|
|
$totalSyrup = null;
|
|
|
|
if ($syrupOverflow !== null || $syrupReceiver !== null) {
|
|
$totalSyrup = 0.0;
|
|
if ($syrupOverflow !== null) {
|
|
$totalSyrup += $syrupOverflow * 0.82569;
|
|
}
|
|
if ($syrupReceiver !== null) {
|
|
$totalSyrup += $syrupReceiver * 0.17431;
|
|
}
|
|
}
|
|
|
|
$tankLevels = [
|
|
['label' => 'Juice Tank 1', 'value' => value_num($value, 'Juice Tank1')],
|
|
['label' => 'Juice Tank 2', 'value' => value_num($value, 'Juice Tank2')],
|
|
['label' => 'Syrup Receiver', 'value' => $syrupReceiver],
|
|
['label' => 'Syrup Overflow', 'value' => $syrupOverflow],
|
|
['label' => 'Total Syrup', 'value' => $totalSyrup],
|
|
['label' => 'Condensate Water', 'value' => value_num($value, 'CondensateWater')],
|
|
['label' => 'Sweet Water', 'value' => value_num($value, 'SweetwaterTank')],
|
|
['label' => 'Cold Water INJ', 'value' => value_num($value, 'Cold Water Injection LVL')],
|
|
['label' => 'A1 Receiver', 'value' => value_num($value, 'A1 RCVR')],
|
|
['label' => 'A1 Overflow', 'value' => value_num($value, 'A1 Molasses Overflow Lvl')],
|
|
['label' => 'A2 Receiver', 'value' => value_num($value, 'A2 RCVR')],
|
|
['label' => 'A2 Overflow', 'value' => value_num($value, 'A2 Molasses Overflow Lvl')],
|
|
['label' => 'B Receiver', 'value' => value_num($value, 'B RCVR')],
|
|
['label' => 'B Overflow', 'value' => value_num($value, 'B Molasses Overflow Lvl')],
|
|
['label' => 'Flash Tank', 'value' => value_num($value, 'FlashTankLVL')],
|
|
];
|
|
|
|
$truckDumpState = build_status(value_as_int($value, 'LATCHON'), true);
|
|
$truckDumpMetrics = [
|
|
['label' => 'PV', 'value' => format_number(value_num($valuesTwo, 'TD INPUT'), 1)],
|
|
[
|
|
'label' => 'Setpoint',
|
|
'value' => format_number(
|
|
value_as_int($value, 'TD SP3 ON/OFF') === 1
|
|
? value_num($valuesTwo, 'TD SP3')
|
|
: value_num($valuesTwo, 'TD SP1'),
|
|
1
|
|
),
|
|
'suffix' => value_as_int($value, 'TD SP3 ON/OFF') === 1 ? 'SP3' : 'SP',
|
|
],
|
|
['label' => 'Output', 'value' => format_with_unit(value_num($valuesTwo, 'TD OUTPUT'), '%', 1)],
|
|
['label' => 'Ratio', 'value' => format_number(value_num($valuesTwo, 'TD RATIO'), 2)],
|
|
['label' => 'Belt Scale', 'value' => format_with_unit(value_num($valuesTwo, 'TDBELTSCALE'), 'lbs', 0)],
|
|
[
|
|
'label' => 'Belt State',
|
|
'status' => build_switch(value_as_int($value, 'TDBELTSCALEONOFF'), 'Scale On', 'Scale Off'),
|
|
],
|
|
[
|
|
'label' => 'Mode',
|
|
'value' => value_as_int($value, 'TD MILLGROUND') === null
|
|
? '—'
|
|
: (
|
|
value_as_int($value, 'TD MILLGROUND') === 0
|
|
? 'Dump Mill'
|
|
: 'Dump Ground'
|
|
),
|
|
],
|
|
];
|
|
|
|
$tables = [
|
|
[
|
|
'label' => 'S Table',
|
|
'pv' => format_number(value_num($valuesTwo, 'SE SCALE'), 1),
|
|
'sp' => format_number(
|
|
value_as_int($value, 'STABLE SP3 ON/OFF') === 1
|
|
? value_num($valuesTwo, 'STABLE SP1')
|
|
: value_num($valuesTwo, 'SE SETPOINT'),
|
|
1
|
|
),
|
|
'spLabel' => value_as_int($value, 'STABLE SP3 ON/OFF') === 1 ? 'SP3' : 'SP',
|
|
'output' => format_with_unit(value_num($valuesTwo, 'SE OUTPUT'), '%', 1),
|
|
'mode' => build_status(value_as_int($value, 'SAM'), true),
|
|
],
|
|
[
|
|
'label' => 'NE Table',
|
|
'pv' => format_number(value_num($valuesTwo, 'NE SCALE'), 1),
|
|
'sp' => format_number(
|
|
value_as_int($value, 'NE SP SWITCH') === 1
|
|
? value_num($valuesTwo, 'NE SETPOINT2')
|
|
: value_num($valuesTwo, 'NE SETPOINT'),
|
|
1
|
|
),
|
|
'spLabel' => value_as_int($value, 'NE SP SWITCH') === 1 ? 'SP3' : 'SP',
|
|
'output' => format_with_unit(value_num($valuesTwo, 'NE OUTPUT'), '%', 1),
|
|
'mode' => build_status(value_as_int($value, 'NAM'), true),
|
|
],
|
|
[
|
|
'label' => 'W Table',
|
|
'pv' => format_number(value_num($valuesTwo, 'W SCALE'), 1),
|
|
'sp' => format_number(
|
|
value_as_int($value, 'W SP SWITCH') === 1
|
|
? value_num($valuesTwo, 'W SETPOINT2')
|
|
: value_num($valuesTwo, 'W SETPOINT'),
|
|
1
|
|
),
|
|
'spLabel' => value_as_int($value, 'W SP SWITCH') === 1 ? 'SP3' : 'SP',
|
|
'output' => format_with_unit(value_num($valuesTwo, 'W OUTPUT'), '%', 1),
|
|
'mode' => build_status(value_as_int($value, 'WAM'), true),
|
|
],
|
|
];
|
|
|
|
$knives = [
|
|
[
|
|
'label' => 'Knife 1',
|
|
'pressure' => format_with_unit(value_num($valuesTwo, 'KNF1PRES'), 'psi', 1),
|
|
'speed' => format_with_unit(value_num($valuesTwo, 'K1SPD'), 'rpm', 0),
|
|
'output' => format_with_unit(value_num($valuesTwo, 'KNIFE1OP'), '%', 1),
|
|
'mode' => value_as_int($value, 'KC1') === null
|
|
? null
|
|
: (
|
|
value_as_int($value, 'KC1') === 1
|
|
? ['label' => 'Control', 'class' => 'status-pill--ok']
|
|
: ['label' => 'Knife', 'class' => 'status-pill--warn']
|
|
),
|
|
],
|
|
[
|
|
'label' => 'Knife 2',
|
|
'pressure' => format_with_unit(value_num($valuesTwo, 'KNF2PRES'), 'psi', 1),
|
|
'speed' => format_with_unit(value_num($valuesTwo, 'KNF2SPD'), 'rpm', 0),
|
|
'output' => format_with_unit(value_num($valuesTwo, 'KNIFE2OP'), '%', 1),
|
|
'mode' => value_as_int($value, 'KC2') === null
|
|
? null
|
|
: (
|
|
value_as_int($value, 'KC2') === 1
|
|
? ['label' => 'Control', 'class' => 'status-pill--ok']
|
|
: ['label' => 'Knife', 'class' => 'status-pill--warn']
|
|
),
|
|
],
|
|
[
|
|
'label' => 'Knife 3',
|
|
'pressure' => format_with_unit(value_num($valuesTwo, 'KNF3PRES'), 'psi', 1),
|
|
'speed' => format_with_unit(value_num($valuesTwo, 'KNF3SPD'), 'rpm', 0),
|
|
'output' => format_with_unit(value_num($valuesTwo, 'KNIFE3OP'), '%', 1),
|
|
'mode' => value_as_int($value, 'KC3') === null
|
|
? null
|
|
: (
|
|
value_as_int($value, 'KC3') === 1
|
|
? ['label' => 'Control', 'class' => 'status-pill--ok']
|
|
: ['label' => 'Knife', 'class' => 'status-pill--warn']
|
|
),
|
|
],
|
|
];
|
|
?>
|
|
<div class="board-stack" data-generated-at="<?php echo html($generatedAt); ?>">
|
|
<div class="meta-banner">
|
|
<?php if ($cropDay !== null) { ?>
|
|
<span>Crop Day <strong><?php echo html($cropDay); ?></strong></span>
|
|
<?php } ?>
|
|
<span>Server time <strong><?php echo html($now->format('M j, Y g:i:s A')); ?></strong></span>
|
|
</div>
|
|
|
|
<section class="section">
|
|
<header class="section__header">
|
|
<h2 class="section__title">Key Production Metrics</h2>
|
|
<p class="section__meta">Plant-wide summary</p>
|
|
</header>
|
|
<div class="stat-grid">
|
|
<?php foreach ($summaryCards as $card) { ?>
|
|
<article class="stat-card">
|
|
<p class="stat-card__label"><?php echo html($card['label']); ?></p>
|
|
<p class="stat-card__value"><?php echo html($card['value']); ?></p>
|
|
<?php if (!empty($card['unit'])) { ?>
|
|
<p class="stat-card__unit"><?php echo html($card['unit']); ?></p>
|
|
<?php } ?>
|
|
<?php if (!empty($card['note'])) { ?>
|
|
<p class="stat-card__note"><?php echo html($card['note']); ?></p>
|
|
<?php } ?>
|
|
</article>
|
|
<?php } ?>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="section">
|
|
<header class="section__header">
|
|
<h2 class="section__title">East Tandem</h2>
|
|
<p class="section__meta">Six-mill train status</p>
|
|
</header>
|
|
<div class="tandem-grid">
|
|
<?php foreach ($eastStages as $stage) { ?>
|
|
<article class="stage-card">
|
|
<div class="stage-card__header">
|
|
<div class="stage-card__title">
|
|
<span class="stage-card__badge"><?php echo html($stage['stage']); ?></span>
|
|
<span><?php echo html($stage['label']); ?></span>
|
|
</div>
|
|
<?php if (!empty($stage['mode'])) { ?>
|
|
<span class="status-pill <?php echo html($stage['mode']['class']); ?>">
|
|
<?php echo html($stage['mode']['label']); ?>
|
|
</span>
|
|
<?php } ?>
|
|
</div>
|
|
|
|
<div class="progress">
|
|
<div class="progress__track">
|
|
<div
|
|
class="progress__bar"
|
|
style="width: <?php echo clamp_percent(numeric_or_null($stage['level'])); ?>%;"
|
|
></div>
|
|
</div>
|
|
<div class="progress__label">
|
|
<span>Level</span>
|
|
<span><?php echo html(percent_label($stage['level'])); ?></span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="metric-list">
|
|
<div class="metric">
|
|
<span class="metric__label">Setpoint</span>
|
|
<span class="metric__value">
|
|
<?php echo html(format_number($stage['setpoint'])); ?>
|
|
<em><?php echo html($stage['setpointUnit']); ?></em>
|
|
</span>
|
|
</div>
|
|
<div class="metric">
|
|
<span class="metric__label">RPM</span>
|
|
<span class="metric__value">
|
|
<?php echo html(format_with_unit($stage['rpm'], 'rpm')); ?>
|
|
</span>
|
|
</div>
|
|
<div class="metric">
|
|
<span class="metric__label"><?php echo html($stage['outputLabel']); ?></span>
|
|
<span class="metric__value">
|
|
<?php echo html(format_with_unit($stage['outputValue'], $stage['outputUnit'])); ?>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</article>
|
|
<?php } ?>
|
|
</div>
|
|
<div class="mini-grid">
|
|
<?php foreach ($eastFooterMetrics as $metric) { ?>
|
|
<div class="mini-card">
|
|
<span class="mini-card__label"><?php echo html($metric['label']); ?></span>
|
|
<span class="mini-card__value"><?php echo html($metric['value']); ?></span>
|
|
</div>
|
|
<?php } ?>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="section">
|
|
<header class="section__header">
|
|
<h2 class="section__title">West Tandem</h2>
|
|
<p class="section__meta">Grinding performance</p>
|
|
</header>
|
|
<div class="tandem-grid">
|
|
<?php foreach ($westStages as $stage) { ?>
|
|
<article class="stage-card">
|
|
<div class="stage-card__header">
|
|
<div class="stage-card__title">
|
|
<span class="stage-card__badge"><?php echo html($stage['stage']); ?></span>
|
|
<span><?php echo html($stage['label']); ?></span>
|
|
</div>
|
|
</div>
|
|
<div class="progress">
|
|
<div class="progress__track">
|
|
<div
|
|
class="progress__bar"
|
|
style="width: <?php echo clamp_percent(numeric_or_null($stage['level'])); ?>%;"
|
|
></div>
|
|
</div>
|
|
<div class="progress__label">
|
|
<span>Level</span>
|
|
<span><?php echo html(percent_label($stage['level'])); ?></span>
|
|
</div>
|
|
</div>
|
|
<div class="metric-list">
|
|
<div class="metric">
|
|
<span class="metric__label">RPM</span>
|
|
<span class="metric__value"><?php echo html(format_with_unit($stage['rpm'], 'rpm')); ?></span>
|
|
</div>
|
|
<div class="metric">
|
|
<span class="metric__label"><?php echo html($stage['outputLabel']); ?></span>
|
|
<span class="metric__value"><?php echo html(format_with_unit($stage['outputValue'], $stage['outputUnit'])); ?></span>
|
|
</div>
|
|
</div>
|
|
</article>
|
|
<?php } ?>
|
|
</div>
|
|
<div class="mini-grid">
|
|
<?php foreach ($westFooterMetrics as $metric) { ?>
|
|
<div class="mini-card">
|
|
<span class="mini-card__label"><?php echo html($metric['label']); ?></span>
|
|
<span class="mini-card__value"><?php echo html($metric['value']); ?></span>
|
|
</div>
|
|
<?php } ?>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="section">
|
|
<header class="section__header">
|
|
<h2 class="section__title">Boilers</h2>
|
|
<p class="section__meta">Steam generation health</p>
|
|
</header>
|
|
<div class="stat-grid">
|
|
<?php foreach ($boilerOverview as $card) { ?>
|
|
<article class="stat-card">
|
|
<p class="stat-card__label"><?php echo html($card['label']); ?></p>
|
|
<p class="stat-card__value"><?php echo html($card['value']); ?></p>
|
|
<p class="stat-card__unit"><?php echo html($card['unit']); ?></p>
|
|
<?php if (!empty($card['status'])) { ?>
|
|
<p class="stat-card__note">
|
|
<span class="status-pill <?php echo html($card['status']['class']); ?>">
|
|
<?php echo html($card['status']['label']); ?>
|
|
</span>
|
|
</p>
|
|
<?php } ?>
|
|
<?php if (!empty($card['mode'])) { ?>
|
|
<p class="stat-card__note">
|
|
<span class="status-pill <?php echo html($card['mode']['class']); ?>">
|
|
<?php echo html($card['mode']['label']); ?>
|
|
</span>
|
|
</p>
|
|
<?php } ?>
|
|
</article>
|
|
<?php } ?>
|
|
</div>
|
|
<div class="mini-grid">
|
|
<?php foreach ($boilerFlows as $boiler) { ?>
|
|
<div class="mini-card">
|
|
<span class="mini-card__label"><?php echo html($boiler['label']); ?></span>
|
|
<span class="mini-card__value"><?php echo html($boiler['value']); ?> <small>kpph</small></span>
|
|
</div>
|
|
<?php } ?>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="section">
|
|
<header class="section__header">
|
|
<h2 class="section__title">Tank Levels</h2>
|
|
<p class="section__meta">Percent of capacity</p>
|
|
</header>
|
|
<div class="tank-grid">
|
|
<?php foreach ($tankLevels as $tank) { ?>
|
|
<?php $state = tank_level_state($tank['value']); ?>
|
|
<article
|
|
class="tank-card"
|
|
<?php if ($state !== null) { ?>data-level="<?php echo html($state); ?>"<?php } ?>
|
|
>
|
|
<div class="tank-card__title">
|
|
<span><?php echo html($tank['label']); ?></span>
|
|
<span class="tank-card__meta"><?php echo html(percent_label($tank['value'])); ?></span>
|
|
</div>
|
|
<div class="progress">
|
|
<div class="progress__track">
|
|
<div
|
|
class="progress__bar"
|
|
style="width: <?php echo clamp_percent(numeric_or_null($tank['value'])); ?>%;"
|
|
></div>
|
|
</div>
|
|
</div>
|
|
</article>
|
|
<?php } ?>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="section">
|
|
<header class="section__header">
|
|
<h2 class="section__title">Truck Dump & Tables</h2>
|
|
<p class="section__meta">Front-end preparation</p>
|
|
</header>
|
|
<div class="equipment-grid">
|
|
<article class="equipment-card">
|
|
<div class="equipment-card__title">
|
|
<span>Truck Dump</span>
|
|
<?php if ($truckDumpState !== null) { ?>
|
|
<span class="status-pill <?php echo html($truckDumpState['class']); ?>">
|
|
<?php echo html($truckDumpState['label']); ?>
|
|
</span>
|
|
<?php } ?>
|
|
</div>
|
|
<div class="equipment-card__rows">
|
|
<?php foreach ($truckDumpMetrics as $metric) { ?>
|
|
<div class="equipment-row">
|
|
<span class="equipment-row__label"><?php echo html($metric['label']); ?></span>
|
|
<span class="equipment-row__value">
|
|
<?php if (!empty($metric['status'])) { ?>
|
|
<span class="status-pill <?php echo html($metric['status']['class']); ?>">
|
|
<?php echo html($metric['status']['label']); ?>
|
|
</span>
|
|
<?php } else { ?>
|
|
<?php echo html($metric['value'] ?? '—'); ?>
|
|
<?php if (!empty($metric['suffix'])) { ?>
|
|
<small><?php echo html($metric['suffix']); ?></small>
|
|
<?php } ?>
|
|
<?php } ?>
|
|
</span>
|
|
</div>
|
|
<?php } ?>
|
|
</div>
|
|
</article>
|
|
|
|
<?php foreach ($tables as $table) { ?>
|
|
<article class="equipment-card">
|
|
<div class="equipment-card__title">
|
|
<span><?php echo html($table['label']); ?></span>
|
|
<?php if ($table['mode'] !== null) { ?>
|
|
<span class="status-pill <?php echo html($table['mode']['class']); ?>">
|
|
<?php echo html($table['mode']['label']); ?>
|
|
</span>
|
|
<?php } ?>
|
|
</div>
|
|
<div class="equipment-card__rows">
|
|
<div class="equipment-row">
|
|
<span class="equipment-row__label">PV</span>
|
|
<span class="equipment-row__value"><?php echo html($table['pv']); ?></span>
|
|
</div>
|
|
<div class="equipment-row">
|
|
<span class="equipment-row__label">Setpoint</span>
|
|
<span class="equipment-row__value">
|
|
<?php echo html($table['sp']); ?>
|
|
<small><?php echo html($table['spLabel']); ?></small>
|
|
</span>
|
|
</div>
|
|
<div class="equipment-row">
|
|
<span class="equipment-row__label">Output</span>
|
|
<span class="equipment-row__value"><?php echo html($table['output']); ?></span>
|
|
</div>
|
|
</div>
|
|
</article>
|
|
<?php } ?>
|
|
</div>
|
|
|
|
<div class="equipment-card">
|
|
<div class="equipment-card__title">
|
|
<span>Knife Train</span>
|
|
</div>
|
|
<div class="equipment-subgrid">
|
|
<?php foreach ($knives as $knife) { ?>
|
|
<div class="equipment-mini">
|
|
<div class="equipment-mini__title">
|
|
<span><?php echo html($knife['label']); ?></span>
|
|
<?php if ($knife['mode'] !== null) { ?>
|
|
<span class="status-pill <?php echo html($knife['mode']['class']); ?>">
|
|
<?php echo html($knife['mode']['label']); ?>
|
|
</span>
|
|
<?php } ?>
|
|
</div>
|
|
<div class="equipment-row">
|
|
<span class="equipment-row__label">Pressure</span>
|
|
<span class="equipment-row__value"><?php echo html($knife['pressure']); ?></span>
|
|
</div>
|
|
<div class="equipment-row">
|
|
<span class="equipment-row__label">Speed</span>
|
|
<span class="equipment-row__value"><?php echo html($knife['speed']); ?></span>
|
|
</div>
|
|
<div class="equipment-row">
|
|
<span class="equipment-row__label">Output</span>
|
|
<span class="equipment-row__value"><?php echo html($knife['output']); ?></span>
|
|
</div>
|
|
</div>
|
|
<?php } ?>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
<?php // phpcs:enable ?>
|