add all files

This commit is contained in:
Rucus
2026-02-17 09:29:34 -06:00
parent b8c8d67c67
commit 782d203799
21925 changed files with 2433086 additions and 0 deletions

View File

@@ -0,0 +1,29 @@
# LASUCA Shared Endpoint AI Guide
## Architecture
- Single PHP micro-endpoint; every request enters `public/index.php`, loads config, calls `Lasuca\SharedEndpoint\fetchLatestItems()` from `src/Database.php`, then JSON-encodes the payload.
- No frameworks or Composer setup; stick to core PHP 8+ features already in use (`strict_types`, namespaced functions, `mysqli`).
- Output contract is the canonical data feed for multiple dashboards; preserve field names (`status`, `metadata`, `items`) and the nested keys consumed by clients.
## Data access
- `fetchLatestItems()` opens a short-lived `mysqli` connection using the array returned by `config.php`; closes connections explicitly after the query.
- Query targets the `items` table ordered by `ID`; schema is assumed to expose `ID`, `Name`, `Value`, and `Timestamp`. Add columns by extending both the `SELECT` statement and the array mapping.
- Values are normalised before returning: `tagId` → int, `value`/`rounded1`/`rounded2` → float, `timestamp` stays as the DB-provided string. Keep these conversions when expanding the payload.
## Configuration & caching
- `config.php` reads environment variables (`LASUCA_DB_*`, `LASUCA_CACHE_TTL`) with sensible on-prem defaults; prefer overriding via env vars rather than editing the file.
- `LASUCA_CACHE_TTL` controls both the config cache and the HTTP `Cache-Control` header (currently hard-coded to `1` second in `public/index.php`). Align these when making caching changes.
## Response handling
- Success responses include `metadata.generatedAt` as an ISO 8601 (UTC) timestamp via `gmdate(DATE_ATOM)` and `metadata.count` from `count($items)`.
- Errors are caught in `public/index.php`: respond with HTTP 500 and a JSON body `{ "status": "error", "message": ... }`. Preserve this shape so clients can detect failure without parsing HTML.
- Use `JSON_PRETTY_PRINT` when changing serialization to keep human-readable outputs during troubleshooting.
## Local workflows
- Serve locally from the parent directory with `php -S localhost:8081 shared-endpoint/public/index.php`; this mimics production routing without extra tooling.
- Database connectivity is the only external dependency; mock or stub calls by swapping `fetchLatestItems()` for a fixture in tests or by wrapping the function in a seam.
## Extension tips
- When adding new endpoints, follow the same pattern: thin public script, shared logic in `src/` under the `Lasuca\SharedEndpoint` namespace, and configuration driven by env-aware arrays.
- Keep new helpers free of side effects beyond DB reads so downstream caching remains predictable.
- Document any new environment variables in both `config.php` comments and the top-level `README.md` so operators stay in sync.

60
shared-endpoint/README.md Normal file
View File

@@ -0,0 +1,60 @@
# LASUCA Shared Endpoint
This micro-endpoint exposes the historian snapshot consumed by both the desktop dashboard and the factory display clients. It returns a normalised JSON payload so updates happen in one place.
## Project layout
```
shared-endpoint/
├── config.php # Connection + cache settings (override with env vars in production)
├── public/
│ └── index.php # JSON endpoint (`/shared-endpoint/public/index.php`)
└── src/
└── Database.php # `fetchLatestItems()` helper wrapping the `items` table
```
## Running locally
1. Set the required environment variables (optional if you reuse the on-prem credentials):
- `LASUCA_DB_HOST`
- `LASUCA_DB_USER`
- `LASUCA_DB_PASSWORD`
- `LASUCA_DB_NAME`
- `LASUCA_CACHE_TTL` (seconds, defaults to 1)
2. Serve the project (from one level above `shared-endpoint`):
```powershell
php -S localhost:8081 shared-endpoint/public/index.php
```
The endpoint will respond with:
```json
{
"status": "ok",
"metadata": {
"generatedAt": "2025-10-16T14:22:00+00:00",
"count": 321
},
"items": [
{
"tagId": 1,
"name": "RUNHRSTODAY",
"value": 12,
"rounded1": 12.0,
"rounded2": 12.00,
"timestamp": "2025-10-16 09:20:31"
}
]
}
```
## Integrating with the dashboards
1. Move this folder so it sits alongside `overviews/` (both projects can then include `../shared-endpoint/public/index.php`).
2. Update the existing UI code to call `fetchLatestItems()` through the endpoint instead of including `includes/items.php` directly:
- Server-side PHP can `file_get_contents('http://localhost:8081')` and `json_decode` the result.
- Front-end polling can swap `data/main.php` for the JSON feed if you add a thin adapter.
3. When you edit tag metadata, change it once in the database—every consumer will see it on the next poll.
> **Tip:** keep the endpoint repo in its own workspace or git submodule so both the control room and display teams can update it without stepping on each other.

View File

@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
/*
* Shared endpoint configuration.
*/
return [
'mysql' => [
'host' => getenv('LASUCA_DB_HOST') ?: '192.168.0.10',
'username' => getenv('LASUCA_DB_USER') ?: 'corey',
'password' => getenv('LASUCA_DB_PASSWORD') ?: '41945549',
'database' => getenv('LASUCA_DB_NAME') ?: 'controls',
],
'cache' => [
// Seconds to cache responses in shared storage (set to 0 to disable).
'ttl' => (int) (getenv('LASUCA_CACHE_TTL') ?: 1),
],
];

View File

@@ -0,0 +1,289 @@
<?php
declare(strict_types=1);
?><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Shared Endpoint JSON Viewer</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
:root {
color-scheme: light dark;
--bg: #f7f7f7;
--fg: #222;
--accent: #0b5ed7;
--accent-contrast: #fff;
--table-bg: #fff;
--table-border: #ccc;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #121212;
--fg: #efefef;
--table-bg: #1e1e1e;
--table-border: #333;
}
}
body {
margin: 0;
font-family: system-ui, sans-serif;
background: var(--bg);
color: var(--fg);
}
header {
padding: 1.5rem 2rem 1rem;
background: var(--table-bg);
border-bottom: 1px solid var(--table-border);
}
header h1 {
margin: 0 0 0.5rem;
font-size: 1.6rem;
}
.controls {
display: flex;
flex-wrap: wrap;
gap: 1rem;
align-items: center;
}
.controls label {
font-weight: 600;
}
.controls input[type="text"],
.controls select {
padding: 0.5rem;
border-radius: 0.4rem;
border: 1px solid var(--table-border);
background: var(--bg);
color: var(--fg);
}
.controls button {
padding: 0.6rem 1.2rem;
border-radius: 999px;
border: none;
background: var(--accent);
color: var(--accent-contrast);
font-weight: 600;
cursor: pointer;
}
main {
padding: 1.5rem;
max-width: 1200px;
margin: 0 auto;
}
table {
width: 100%;
border-collapse: collapse;
background: var(--table-bg);
border: 1px solid var(--table-border);
border-radius: 0.75rem;
overflow: hidden;
}
th, td {
padding: 0.75rem 0.9rem;
border-bottom: 1px solid var(--table-border);
text-align: left;
font-variant-numeric: tabular-nums;
}
th {
font-size: 0.85rem;
text-transform: uppercase;
letter-spacing: 0.05em;
background: rgba(0, 0, 0, 0.03);
}
tr:last-child td {
border-bottom: none;
}
.badge {
display: inline-block;
padding: 0.25rem 0.5rem;
border-radius: 0.5rem;
background: rgba(11, 94, 215, 0.12);
color: var(--accent);
font-size: 0.75rem;
font-weight: 600;
}
.meta {
margin: 1rem 0;
display: flex;
flex-wrap: wrap;
gap: 1rem;
font-size: 0.95rem;
}
.meta span {
font-weight: 600;
}
.error {
margin: 1.5rem auto;
max-width: 600px;
padding: 1rem 1.25rem;
border-radius: 0.75rem;
background: rgba(220, 53, 69, 0.1);
color: #842029;
border: 1px solid rgba(220, 53, 69, 0.25);
}
.hidden {
display: none;
}
</style>
</head>
<body>
<header>
<h1>Shared Endpoint JSON Viewer</h1>
<div class="controls">
<label for="filter">Filter:</label>
<input type="text" id="filter" placeholder="Search tagId, tagKey, name…">
<label for="sort">Sort by:</label>
<select id="sort">
<option value="tagId">tagId</option>
<option value="tagKey">tagKey</option>
<option value="name">name</option>
<option value="value">value</option>
<option value="rounded1">rounded1</option>
<option value="rounded2">rounded2</option>
<option value="timestamp">timestamp</option>
</select>
<select id="order">
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
</select>
<button type="button" id="refresh">Refresh</button>
</div>
</header>
<main>
<div class="meta hidden" id="metadata"></div>
<div class="error hidden" id="error"></div>
<table id="itemsTable" class="hidden">
<thead>
<tr>
<th>tagId</th>
<th>tagKey</th>
<th>name</th>
<th>value</th>
<th>rounded1</th>
<th>rounded2</th>
<th>timestamp</th>
</tr>
</thead>
<tbody></tbody>
</table>
</main>
<script>
const filterInput = document.getElementById('filter');
const sortSelect = document.getElementById('sort');
const orderSelect = document.getElementById('order');
const refreshButton = document.getElementById('refresh');
const errorBox = document.getElementById('error');
const metadataBox = document.getElementById('metadata');
const table = document.getElementById('itemsTable');
const tbody = table.querySelector('tbody');
const ENDPOINT = '../index.php';
let items = [];
function showError(message) {
errorBox.textContent = message;
errorBox.classList.remove('hidden');
table.classList.add('hidden');
metadataBox.classList.add('hidden');
}
function hideError() {
errorBox.classList.add('hidden');
}
function updateMetadata({ generatedAt, count }) {
metadataBox.innerHTML = `
<span>Generated at: ${generatedAt}</span>
<span class="badge">${count} items</span>
`;
metadataBox.classList.remove('hidden');
}
function render(list) {
tbody.innerHTML = '';
for (const item of list) {
const row = document.createElement('tr');
row.innerHTML = `
<td>${item.tagId}</td>
<td>${item.tagKey ?? '—'}</td>
<td>${item.name}</td>
<td>${item.value}</td>
<td>${item.rounded1}</td>
<td>${item.rounded2}</td>
<td>${item.timestamp}</td>
`;
tbody.appendChild(row);
}
table.classList.toggle('hidden', list.length === 0);
}
function applyFilterSort() {
const term = filterInput.value.trim().toLowerCase();
let filtered = items;
if (term) {
filtered = items.filter((item) =>
String(item.tagId).includes(term) ||
(item.tagKey ?? '').toLowerCase().includes(term) ||
item.name.toLowerCase().includes(term) ||
String(item.value).includes(term) ||
String(item.rounded1).includes(term) ||
String(item.rounded2).includes(term) ||
item.timestamp.toLowerCase().includes(term)
);
}
const sortKey = sortSelect.value;
const factor = orderSelect.value === 'asc' ? 1 : -1;
filtered = filtered.slice().sort((a, b) => {
const av = a[sortKey] ?? '';
const bv = b[sortKey] ?? '';
if (typeof av === 'number' && typeof bv === 'number') {
return (av - bv) * factor;
}
return String(av).localeCompare(String(bv)) * factor;
});
render(filtered);
}
async function fetchItems() {
hideError();
try {
const response = await fetch(ENDPOINT, {
headers: {
'Accept': 'application/json',
},
});
if (!response.ok) {
throw new Error(`Endpoint responded with ${response.status}`);
}
const payload = await response.json();
if (payload.status !== 'ok') {
const fallbackMsg = 'Shared endpoint returned error status';
const message = payload.message || fallbackMsg;
throw new Error(message);
}
items = payload.items || [];
const metadata = payload.metadata ?? {
generatedAt: 'unknown',
count: items.length,
};
updateMetadata(metadata);
applyFilterSort();
} catch (error) {
showError(error instanceof Error ? error.message : 'Unknown error');
}
}
filterInput.addEventListener('input', applyFilterSort);
sortSelect.addEventListener('change', applyFilterSort);
orderSelect.addEventListener('change', applyFilterSort);
refreshButton.addEventListener('click', fetchItems);
fetchItems();
</script>
</body>
</html>

View File

@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
use function Lasuca\SharedEndpoint\fetchLatestItems;
require_once __DIR__ . '/../src/Database.php';
$config = include __DIR__ . '/../config.php';
try {
$items = fetchLatestItems($config['mysql']);
} catch (Throwable $throwable) {
http_response_code(500);
header('Content-Type: application/json');
echo json_encode(
[
'status' => 'error',
'message' => $throwable->getMessage(),
]
);
exit;
}
$metadata = [
'generatedAt' => gmdate(DATE_ATOM),
'count' => count($items),
];
$payload = [
'status' => 'ok',
'metadata' => $metadata,
'items' => $items,
];
header('Content-Type: application/json');
header('Cache-Control: max-age=1');
echo json_encode($payload, JSON_PRETTY_PRINT);

View File

@@ -0,0 +1,112 @@
"""Tiny Python proxy for the LASUCA shared endpoint."""
from __future__ import annotations
import os
import time
from typing import Any, Dict, Optional
import requests
from flask import Flask, Response, abort, jsonify
DEFAULT_ENDPOINT = "http://localhost/shared-endpoint/public/index.php"
DEFAULT_CACHE_SECONDS = 1
DEFAULT_TIMEOUT_SECONDS = 3
app = Flask(__name__)
def _bool_env(var_name: str, default: bool = False) -> bool:
value = os.getenv(var_name)
if value is None:
return default
return value.strip().lower() in {"1", "true", "yes", "on"}
def get_settings() -> Dict[str, Any]:
return {
"endpoint": os.getenv("LASUCA_PHP_FEED", DEFAULT_ENDPOINT),
"cache_seconds": int(os.getenv("LASUCA_PROXY_CACHE_SECONDS", DEFAULT_CACHE_SECONDS)),
"timeout": float(os.getenv("LASUCA_PROXY_TIMEOUT", DEFAULT_TIMEOUT_SECONDS)),
"forward_headers": _bool_env("LASUCA_PROXY_FORWARD_HEADERS", False),
}
class CacheEntry:
__slots__ = ("payload", "timestamp", "etag")
def __init__(self, payload: Dict[str, Any], etag: Optional[str]) -> None:
self.payload = payload
self.timestamp = time.time()
self.etag = etag
_cache: Dict[str, CacheEntry] = {}
def _build_cache_key(settings: Dict[str, Any]) -> str:
endpoint = settings["endpoint"]
forward_headers = settings["forward_headers"]
return f"{endpoint}|fh={int(forward_headers)}"
def _http_get(settings: Dict[str, Any]) -> requests.Response:
headers = {}
if settings["forward_headers"]:
for header in ("Authorization", "X-Forwarded-For", "Cookie"):
value = os.getenv(f"LASUCA_PROXY_HEADER_{header.replace('-', '_').upper()}")
if value:
headers[header] = value
response = requests.get(settings["endpoint"], timeout=settings["timeout"], headers=headers)
response.raise_for_status()
return response
@app.route("/healthz", methods=["GET"])
def health_check() -> Response:
return jsonify({"status": "ok"})
@app.route("/items", methods=["GET"])
def items() -> Response:
settings = get_settings()
cache_key = _build_cache_key(settings)
now = time.time()
cache_entry = _cache.get(cache_key)
if cache_entry:
age = now - cache_entry.timestamp
if age <= settings["cache_seconds"]:
response = jsonify(cache_entry.payload)
if cache_entry.etag is not None:
response.headers["ETag"] = cache_entry.etag
response.headers["X-Cache-Hit"] = "1"
response.headers["Cache-Control"] = f"max-age={settings['cache_seconds']}"
return response
try:
upstream_response = _http_get(settings)
payload = upstream_response.json()
except requests.RequestException as exc:
abort(502, f"Shared endpoint failed: {exc}")
except ValueError as exc:
abort(502, f"Invalid JSON from shared endpoint: {exc}")
if payload.get("status") != "ok":
abort(502, payload.get("message", "Shared endpoint error"))
etag = upstream_response.headers.get("ETag")
_cache[cache_key] = CacheEntry(payload, etag)
response = jsonify(payload)
if etag is not None:
response.headers["ETag"] = etag
response.headers["X-Cache-Hit"] = "0"
response.headers["Cache-Control"] = f"max-age={settings['cache_seconds']}"
return response
if __name__ == "__main__":
host = os.getenv("LASUCA_PROXY_HOST", "0.0.0.0")
port = int(os.getenv("LASUCA_PROXY_PORT", "5050"))
debug = _bool_env("LASUCA_PROXY_DEBUG", False)
app.run(host=host, port=port, debug=debug)

View File

@@ -0,0 +1,2 @@
flask>=3.0
requests>=2.32

View File

@@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
namespace Lasuca\SharedEndpoint;
use mysqli;
use RuntimeException;
/**
* Fetch the latest snapshot of all SCADA tags.
*
* @param array<string,string> $config Connection credentials map.
*
* @return array<int,array<string,mixed>> Normalised tag rows.
*/
function fetchLatestItems(array $config): array
{
$connection = new mysqli(
$config['host'],
$config['username'],
$config['password'],
$config['database']
);
if ($connection->connect_error) {
$message = 'MySQL connection failed: ' . $connection->connect_error;
throw new RuntimeException($message);
}
$query = <<<SQL
SELECT
ID,
Name,
ROUND(Value, 0) AS Value,
ROUND(Value, 1) AS RoundedValue1,
ROUND(Value, 2) AS RoundedValue2,
Timestamp
FROM items
ORDER BY ID ASC
SQL;
$result = $connection->query($query);
if ($result === false) {
$message = 'Query failed: ' . $connection->error;
$connection->close();
throw new RuntimeException($message);
}
$rows = [];
while ($row = $result->fetch_assoc()) {
$rows[] = [
'tagId' => (int) $row['ID'],
'name' => $row['Name'],
'value' => (float) $row['Value'],
'rounded1' => (float) $row['RoundedValue1'],
'rounded2' => (float) $row['RoundedValue2'],
'timestamp' => $row['Timestamp'],
];
}
$result->free();
$connection->close();
return $rows;
}

View File

@@ -0,0 +1,628 @@
<?php
// phpcs:ignoreFile
use JsonException;
use RuntimeException;
$configuredBaseUrl = getenv('192.168.0.10');
if ($configuredBaseUrl !== false && $configuredBaseUrl !== '') {
$baseUrl = rtrim($configuredBaseUrl, '/');
$endpointUrl = $baseUrl . '192.168.0.10/shared-endpoint/public/index.php';
$scheme = parse_url($endpointUrl, PHP_URL_SCHEME) ?: 'http';
} else {
$host = $_SERVER['HTTP_HOST'] ?? 'localhost';
$isHttps = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || ($_SERVER['SERVER_PORT'] ?? null) === '443';
$scheme = $isHttps ? 'https' : 'http';
$endpointUrl = sprintf('%s://192.168.0.10/shared-endpoint/public/index.php', $scheme, $host);
}
$context = null;
if (strcasecmp($scheme, 'https') === 0) {
$host = parse_url($endpointUrl, PHP_URL_HOST) ?: 'localhost';
if (in_array($host, ['localhost', '127.0.0.1'], true)) {
$context = stream_context_create([
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false,
],
]);
}
}
$response = @file_get_contents($endpointUrl, false, $context ?: null);
if ($response === false && strcasecmp($scheme, 'https') === 0) {
$fallbackUrl = preg_replace('#^https#i', 'http', $endpointUrl);
$response = @file_get_contents($fallbackUrl);
if ($response !== false) {
$endpointUrl = $fallbackUrl;
}
}
if ($response === false) {
throw new RuntimeException('Unable to reach shared endpoint: ' . $endpointUrl);
}
try {
$payload = json_decode($response, true, flags: JSON_THROW_ON_ERROR);
} catch (JsonException $exception) {
throw new RuntimeException('Malformed JSON from shared endpoint', 0, $exception);
}
if (($payload['status'] ?? null) !== 'ok') {
$message = $payload['message'] ?? 'unknown error';
throw new RuntimeException('Shared endpoint returned an error: ' . $message);
}
if (!isset($payload['items']) || !is_array($payload['items'])) {
$snippet = substr(strip_tags($response), 0, 200);
throw new RuntimeException('Shared endpoint response missing items. Snippet: ' . $snippet);
}
$roundedid = [];
$rounded = [];
$rounded1 = [];
$ID = [];
$value = [];
foreach ($payload['items'] as $item) {
$tagKey = str_pad((string) $item['tagId'], 5, '0', STR_PAD_LEFT);
$value[$item['name']] = $item['value'];
$rounded[$item['name']] = $item['rounded1'];
$rounded1[$item['name']] = $item['rounded2'];
$roundedid[$tagKey] = $item['rounded2'];
$ID[$tagKey] = $item['value'];
}
$unixtime=time();
$unix5am=mktime(10, 00, 00);
if ($unixtime<=$unix5am) {
$unixtime += 86400;
}
$since5am=($unixtime-$unix5am)/3600;
$avgtonstoday=round($value['W TONS GROUND']/$since5am*24, 0);
ob_start();
?>
<table width="100%" border="1" cellspacing="0" cellpadding="4">
<tr>
<td colspan="4" id="title">Steam</td>
</tr>
<tr>
<td id="vtitle" >Exhaust Pressure:</a></td>
<td colspan="2" id="sum-count"><?php echo $roundedid['00302']; ?></td>
<td id="vtitle">PSI</a></td>
</tr>
<tr class="milling-alt-row">
<td id="vtitle" >Live Steam Pressure:</a></td>
<td colspan="2" id="sum-count"><?php echo $value['PT_001']; ?></td>
<td colspan="2" id="vtitle">PSI</a></td>
</tr>
<tr>
<td colspan="4" id="title">Tank Levels</td>
</tr>
<tr>
<td id="vtitle" >Juice Tank 1 Level:</td>
<td colspan="3" ><progress id="progresstanks" data-label="<?php echo $value['Juice Tank1']; ?>%" max="100" value="<?php echo $value['Juice Tank1']; ?>"></progress></td>
</tr>
<tr class="milling-alt-row">
<td id="vtitle" >Juice Tank 2 Level:</td>
<td colspan="3" align="left"><progress id="progresstanks" data-label="<?php echo $value['Juice Tank2']; ?>%" max="100" value="<?php echo $value['Juice Tank2']; ?>"></progress></td
</tr>
<tr>
<td id="vtitle" id="padded">Total Syrup:</a></td>
<td colspan="3"><progress id="progresstanks" data-label="<?php echo round(($value['Syrup Overflow Lvl']*.82569)+($value['Syrup RCVR']*.17431), 0); ?>%" max="100" value="<?php echo round(($value['Syrup Overflow Lvl']*.82569)+($value['Syrup RCVR']*.17431), 0); ?>"></meter></td>
</tr>
<td colspan="4" id="title">YTD Grinding Info</td>
</tr>
<tr>
<td colspan="4" id="total-tons" align="center">Total <font size="" color="0eb31d"><b><?php require "../includes/milltotal.php";?></b></font> Tons Ground</td>
</tr>
<tr>
<td colspan="4" id="total-tons" align="center">East Mills <font size="" color="0eb31d"><b><?php require "../includes/easttotal.php";?></b></font> Tons Ground</td>
</tr>
<tr>
<td colspan="4" id="total-tons" align="center">West Mills <font size="" color="0eb31d"><b><?php require "../includes/westtotal.php";?></b></font> Tons Ground</td>
</tr>
<tr>
<td colspan="4" id="title">Daily Grinding Totals</td>
</tr>
<tr>
<td colspan="4" id="total-tons" align="center">Record Of <font size="" color="0eb31d"><b><?php require "../includes/record.php";?></b></font> Tons On <font size="" color="0eb31d"><b><?php require "../includes/recorddate.php";?></b></font></td>
</tr>
<tr>
<td colspan="4" id="total-tons" align="center">Total Of <font size="" color="0eb31d"><b><?php echo ($value['PREVTONS'] + $value['WPREVGROUND']); ?></b></font> Tons Ground Yesterday</td>
</tr>
<tr>
<td colspan="4" id="total-tons" align="center">Total Of <font size="" color="0eb31d"><b><?php echo ($value['CANETOT'] + $value['W TONS GROUND']); ?></b></font> Tons Ground</td>
</tr>
<tr>
<td colspan="4" id="total-tons" align="center">Total Of <font size="" color="0eb31d"><b><?php require "../includes/millprojected60min.php";?></b></font> Tons Projected</td>
</tr>
<tr>
<td colspan="4" id="total-tons" align="center">Total Rate Of <font size="" color="0eb31d"><b><?php require "../includes/stablerate30.php";?></b></font> Tons/HR</td>
</tr>
<tr>
<tr class="milling-alt-row">
<td id="vtitle" ></td>
<td id="sum-count">East</td>
<td id="sum-count">West</td>
<td id="vtitle"></td>
</tr>
<tr>
<td id="vtitle" >Tons Per Hour:</a></td>
<td id="sum-count"><?php echo $value['TONS_PER_HOUR']; ?></td>
<td id="sum-count"> <?php echo $value['RATE']; ?></td>
<td id="vtitle">Tons/Hr</a></td>
</tr>
<tr class="milling-alt-row">
<td id="vtitle" >Avg Tons Per Hour:</a></td>
<td id="sum-count"><?php require "../includes/east15minavg.php";?></td>
<td id="sum-count"><?php require "../includes/west15minavg.php";?></td>
<td id="vtitle">Tons/15</a></td>
</tr>
<tr>
<td id="vtitle" >Tons Ground:</a></td>
<td id="sum-count"><?php echo $value['CANETOT']; ?></td>
<td id="sum-count"><?php echo $value['W TONS GROUND']; ?> </td>
<td id="vtitle">Tons</a></td>
</tr>
<tr class="milling-alt-row">
<td id="vtitle" >AVG TCD:</a></td>
<td id="sum-count"><?php echo $value['AVGTCD']; ?></td>
<td id="sum-count"> <?php echo $avgtonstoday; ?></td>
<td id="vtitle">Tons</a></td>
</tr>
<tr>
<td id="vtitle" >Run Hours Today:</a></td>
<td id="sum-count"><?php echo $rounded['RUNHRSTODAY']; ?></td>
<td id="sum-count"><?php echo $value['todayrun'] / (10); ?></td>
<td id="vtitle">Hours</a></td>
</tr>
<tr class="milling-alt-row">
<td id="vtitle" >Loss Time Today:</a></td>
<td id="sum-count"><?php echo $rounded['LOSSTIME']; ?></td>
<td id="sum-count"><?php echo $value['todayloss'] / (10); ?></td>
<td id="vtitle">Hours</a></td>
</tr>
<tr>
<td id="vtitle" >Previous Day TCD:</a></td>
<td id="sum-count"><?php echo $value['PREVTONS']; ?></td>
<td id="sum-count"><?php echo $value['WPREVGROUND']; ?></td>
<td id="vtitle">Tons</a></td>
</tr>
<tr class="milling-alt-row">
<td id="vtitle" >Previous Day Run Hours:</a></td>
<td id="sum-count"><?php echo $value['PREVTIME']; ?></td>
<td id="sum-count"><?php // echo $value['WPREVTIME'] / (10 ** $d); ?></td>
<td id="vtitle">Hours</a></td>
</tr>
<tr>
<td id="vtitle" >Previous Day PPH/Ton:</a></td>
<td id="sum-count"><?php echo $value['LBSPERHR']; ?></td>
<td id="sum-count"> </td>
<td id="vtitle">PPH/Ton</a></td>
</tr>
<tr class="milling-alt-row">
<td id="vtitle" >Previous Day IMB 5:</a></td>
<td id="sum-count"><?php echo round($value['EIMB5PrevDay'] * 8.34 / 2000); ?></td>
<td id="sum-count"> </td>
<td id="vtitle">Tons</a></td>
</tr>
<tr>
<td id="vtitle" >Previous Day IMB 6:</a></td>
<td id="sum-count"><?php echo round($value['EIMB6PrevDay'] * 8.34 / 2000); ?></td>
<td id="sum-count"> </td>
<td id="vtitle">Tons</a></td>
</tr>
<tr class="milling-alt-row">
<td id="vtitle" >Imibition 5 Flow:</a></td>
<td id="sum-count"><?php echo $value['IMB5Flow']; ?></td>
<td id="sum-count"><?php echo $value['WMillIMBFlow']; ?> </td>
<td id="vtitle">GPM</a></td>
</tr>
<tr>
<td id="vtitle" >Imibition 5 Total:</a></td>
<td id="sum-count"><?php echo $value['IMB5TOT']; ?></td>
<td id="sum-count"></td>
<td id="vtitle">GPM</a></td>
</tr>
<tr class="milling-alt-row">
<td id="vtitle" >Imibition 6 Flow:</a></td>
<td id="sum-count"><?php echo $value['IMB6Flow']; ?></td>
<td id="sum-count"> </td>
<td id="vtitle">GPM</a></td>
</tr>
<tr>
<td id="vtitle" >Imibition 6 Total:</a></td>
<td id="sum-count"><?php echo $value['IMB6TOT']; ?></td>
<td id="sum-count"></td>
<td id="vtitle">GPM</a></td>
</tr>
<tr class="milling-alt-row">
<td id="vtitle" >Imibition 5 SP:</a></td>
<td id="sum-count"><?php echo $ID['00164']; ?></td>
<td id="sum-count"><?php echo $value['WMillIMBSP']; ?></td>
<td id="vtitle">GPM</a></td>
</tr>
<tr>
<td id="vtitle" >Imibition 6 SP:</a></td>
<td id="sum-count"><?php echo $value['IMB6SP']; ?></td>
<td id="sum-count"> </td>
<td id="vtitle">GPM</a></td>
</tr>
<tr>
<td colspan="4" id="title">Main Carrier</td>
</tr>
<tr>
<td id="vtitle" ></td>
<td id="sum-count">East</td>
<td id="sum-count">West</td>
<td id="vtitle"></td>
</tr>
<tr class="milling-alt-row">
<td id="vtitle" >MCC FPM:</a></td>
<td id="sum-count"><?php echo $value['MAINSPD']; ?></td>
<td id="sum-count"><?php echo $value['Feet/Minute']; ?></td>
<td id="vtitle">FPM</a></td>
</tr>
<tr>
<td id="vtitle" >MCC Output:</a></td>
<td id="sum-count"><?php echo $value['MCCOUTPUT']; ?></td>
<td id="sum-count"><?php echo $value['Output']; ?></td>
<td id="vtitle">%</a></td>
</tr>
<tr class="milling-alt-row">
<td id="vtitle" >MCC Tons:</a></td>
<td id="sum-count"><?php echo $rounded1['MCCTONS']; ?></td>
<td id="sum-count"> </td>
<td id="vtitle">Tons</a></td>
</tr>
<tr>
<td colspan="4" id="title">Mill 1</td>
</tr>
<tr>
<td id="vtitle" >Chute Level:</a></td>
<td id="sum-count"><?php echo $ID['00350']; ?></td>
<td id="sum-count"><?php echo $ID['00914']; ?></td>
<td id="vtitle">%</a></td>
</tr>
<!--
<tr>
<td id="vtitle" >Ring Pressure:</a></td>
<td id="sum-count"><?php echo $value['MILL1PRES']; ?></td>
<td id="vtitle">PSI</a></td>
</tr>
-->
<tr class="milling-alt-row">
<td id="vtitle" >Turbin Speed:</a></td>
<td id="sum-count"><?php echo $value['EMILL1RPM']; ?></td>
<td id="sum-count"><?php echo $ID['00926']; ?></td>
<td id="vtitle">RPM</a></td>
</tr>
<tr>
<td id="vtitle" >Turbin Output:</a></td>
<td id="sum-count"><?php echo $value['MILL1OUTPUT']; ?></td>
<td id="sum-count"> </td>
<td id="vtitle">%</a></td>
</tr>
<tr class="milling-alt-row">
<td id="vtitle" >Set Point East:</td>
<?php
if ($value['LEVEL_THROUGHPUT_SWITCH'] == 1) {
echo "<td colspan=\"2\" id=\"sum-count\" >".$value['WEIGHT_WSP']."</font></td>";
}
if ($value['LEVEL_THROUGHPUT_SWITCH'] == 0) {
echo "<td colspan=\"2\" id=\"sum-count\">".$value['MILL1WSP']."</font></td>";
}
?>
<?php
if ($value['LEVEL_THROUGHPUT_SWITCH'] == 1) {
echo "<td id=\"vtitle\">Tons/Hr</font></td>";
}
if ($value['LEVEL_THROUGHPUT_SWITCH'] == 0) {
echo "<td id=\"vtitle\">%</font></td>";
}
?>
</tr><tr>
<td id="vtitle" >Set Point West:</td>
<?php
if ($value['LEVEL_THROUGHPUT_SWITCH'] == 1) {
echo "<td colspan=\"2\" id=\"sum-count\" > </font></td>";
}
if ($value['LEVEL_THROUGHPUT_SWITCH'] == 0) {
echo "<td colspan=\"2\" id=\"sum-count\"> </font></td>";
}
?>
<?php
if ($value['LEVEL_THROUGHPUT_SWITCH'] == 1) {
echo "<td id=\"vtitle\">Tons/Hr</font></td>";
}
if ($value['LEVEL_THROUGHPUT_SWITCH'] == 0) {
echo "<td id=\"vtitle\">%</font></td>";
}
?>
</tr>
<tr>
<?php
if ($value['MILL1AUTOMAN1'] == 1) {
echo "<td colspan=\"4\" id=\"gopen\">Auto</td>";
}
if ($value['MILL1AUTOMAN1'] == 0) {
echo "<td colspan=\"4\" id=\"gclose\">Manual</td>";
}
?>
</tr>
<tr>
<td colspan="4" id="title">Mill 2</td>
</tr>
<tr>
<td id="vtitle" ></td>
<td id="sum-count">East</td>
<td id="sum-count">West</td>
<td id="vtitle"></td>
</tr>
<tr class="milling-alt-row">
<td id="vtitle" >Chute Level:</td>
<td id="sum-count"><?php echo $value['M2LVL']; ?></td>
<td id="sum-count"><?php echo $ID['00916']; ?></td>
<td id="vtitle">%</td>
</tr>
<!--
<tr>
<td id="vtitle" >Ring Pressure:</td>
<td id="sum-count"><?php echo $value['MIL2PRES']; ?></td>
<td id="vtitle">PSI</td>
</tr>
-->
<tr>
<td id="vtitle" >Turbin Speed:</td>
<td id="sum-count"><?php echo $value['EMILL2RPM']; ?></td>
<td id="sum-count"><?php echo $ID['00930']; ?></td>
<td id="vtitle">RPM</td>
</tr>
<tr>
<td colspan="4" id="title">Mill 3</td>
</tr>
<tr class="milling-alt-row">
<td id="vtitle" >Chute Level:</td>
<td id="sum-count"><?php echo $value['MIL3LVL']; ?></td>
<td id="sum-count"><?php echo $ID['01020']; ?></td>
<td id="vtitle">%</td>
</tr>
<!--
<tr>
<td id="vtitle" >Ring Pressure:</td>
<td id="sum-count"><?php echo $value['MIL3PRES']; ?></td>
<td id="vtitle">PSI</td>
</tr>
-->
<tr>
<td id="vtitle" >Turbin Speed:</td>
<td id="sum-count"><?php echo $value['EMILL3RPM']; ?></td>
<td id="sum-count"><?php echo $value['W3RPM']; ?></td>
<td id="vtitle">RPM</td>
</tr>
<tr>
<td colspan="4" id="title">Mill 4</td>
</tr>
<tr class="milling-alt-row">
<td id="vtitle" >Chute Level:</td>
<td id="sum-count"><?php echo $value['MIL4LVL']; ?></td>
<td id="sum-count"><?php echo $ID['00918']; ?></td>
<td id="vtitle">%</td>
</tr>
<!--
<tr>
<td id="vtitle" >Ring Pressure:</td>
<td id="sum-count"><?php echo $value['MIL4PRES']; ?></td>
<td id="vtitle">PSI</td>
</tr>
-->
<tr>
<td id="vtitle" >Turbin Speed:</td>
<td id="sum-count"><?php echo $value['EMILL4RPM']; ?></td>
<td id="sum-count"><?php echo $ID['00934']; ?></td>
<td id="vtitle">RPM</td>
</tr>
<tr>
<td colspan="4" id="title">Mill 5</td>
</tr>
<tr class="milling-alt-row">
<td id="vtitle" >Chute Level:</td>
<td id="sum-count"><?php echo $value['MIL5LVL']; ?></td>
<td id="sum-count"><?php echo $ID['00920']; ?></td>
<td id="vtitle">%</td>
</tr>
<!--
<tr>
<td id="vtitle" >Ring Pressure:</td>
<td id="sum-count"><?php echo $value['MIL5PRES']; ?></td>
<td id="vtitle">PSI</td>
</tr>
-->
<tr>
<td id="vtitle" >Turbin Speed:</td>
<td id="sum-count"><?php echo $value['EMILL5RPM']; ?></td>
<td id="sum-count"><?php echo $ID['00940']; ?></td>
<td id="vtitle">RPM</td>
</tr>
<tr>
<td colspan="4" id="title">Mill 6</td>
</tr>
<tr class="milling-alt-row">
<td id="vtitle" >Chute Level:</td>
<td colspan="2" id="sum-count"><?php echo $value['MIL6LVL']; ?></td>
<td id="vtitle">%</td>
</tr>
<!--
<tr>
<td id="vtitle" >Ring Pressure:</td>
<td id="sum-count"><?php echo $value['MIL6PRES']; ?></td>
<td id="vtitle">PSI</td>
</tr>
-->
<tr>
<td id="vtitle" >Turbin Speed:</td>
<td colspan="2" id="sum-count"><?php echo $value['EMILL6RPM']; ?></td>
<td id="vtitle">RPM</td>
</tr>
</table>
<br>
<table class="col-4" border="1" cellspacing="0" cellpadding="4">
<tr>
<td colspan="3" id="title">Shredder</td>
</tr>
<!--
<tr>
<td id="vtitle" >Ring Pressure:</td>
<td id="sum-count"><?php echo $value['KNIFE3PRES']; ?></td>
<td id="vtitle">PSI</td>
</tr>
-->
<tr>
<td id="vtitle" >Turbin Speed:</td>
<td id="sum-count"><?php echo $value['shredderrpm']; ?></td>
<td id="vtitle">RPM</td>
</tr>
<!--
<tr>
<td id="vtitle" >Output:</td>
<td id="sum-count"><?php // echo 0; ?></td>
<td id="vtitle">%</td>
</tr>
-->
<tr>
<?php
if ($value['KC3'] == 1) {
echo "<td colspan=\"3\" id=\"gopen\">Control</td>";
}
if ($value['KC3'] == 0) {
echo "<td colspan=\"3\" id=\"gclose\">Knife</td>";
}
?>
</tr>
</table>
<table class="col-4" border="1" cellspacing="0" cellpadding="4">
<tr>
<td colspan="3" id="title">Knife 1</td>
</tr>
<!--
<tr>
<td id="vtitle">Ring Pressure:</a></td>
<td id="sum-count"><?php echo $value['KNIFE1PRES']; ?></td>
<td id="vtitle">PSI</a></td>
</tr>
-->
<tr class="milling-alt-row">
<td id="vtitle" >Turbin Speed:</td>
<td id="sum-count"><?php echo $value['K1SPD']; ?></td>
<td id="vtitle">RPM</td>
</tr>
<tr>
<td id="vtitle" >Output:</td>
<td id="sum-count"><?php echo $value['KNIFE1OP']; ?></td>
<td id="vtitle">%</td>
</tr>
<tr>
<?php
if ($value['KC1'] == 1) {
echo "<td colspan=\"3\" id=\"gopen\">Control</td>";
}
if ($value['KC1'] == 0) {
echo "<td colspan=\"3\" id=\"gclose\">Knife</td>";
}
?>
</tr>
</table>
<table class="col-4" border="1" cellspacing="0" cellpadding="4">
<tr>
<td colspan="4" id="title">Knife 2</td>
</tr>
<!--
<tr>
<td id="vtitle" >Ring Pressure:</td>
<td id="sum-count"><?php echo $value['KNIFE2PRES']; ?></td>
<td id="vtitle">PSI</td>
</tr>
-->
<tr class="milling-alt-row">
<td id="vtitle" >Turbin Speed:</td>
<td id="sum-count"><?php echo $value['KNF2SPD']; ?></td>
<td id="vtitle">RPM</td>
</tr>
<tr>
<td id="vtitle" >Output:</td>
<td id="sum-count"><?php echo $value['KNIFE2OP']; ?></td>
<td id="vtitle">%</td>
</tr>
<tr>
<?php
if ($value['KC2'] == 1) {
echo "<td colspan=\"3\" id=\"gopen\">Control</td>";
}
if ($value['KC2'] == 0) {
echo "<td colspan=\"3\" id=\"gclose\">Knife</td>";
}
?>
</tr>
</table>
<table class="col-4" border="1" cellspacing="0" cellpadding="4">
<tr>
<td colspan="3" id="title">Knife 3</td>
</tr>
<!--
<tr>
<td id="vtitle" >Ring Pressure:</td>
<td id="sum-count"><?php echo $value['KNIFE3PRES']; ?></td>
<td id="vtitle">PSI</td>
</tr>
-->
<tr class="milling-alt-row">
<td id="vtitle" >Turbin Speed:</td>
<td id="sum-count"><?php echo $value['KNF3SPD']; ?></td>
<td id="vtitle">RPM</td>
</tr>
<tr>
<td id="vtitle" >Output:</td>
<td id="sum-count"><?php echo $value['KNIFE3OP']; ?></td>
<td id="vtitle">%</td>
</tr>
<tr>
<?php
if ($value['KC3'] == 1) {
echo "<td colspan=\"3\" id=\"gopen\">Control</td>";
}
if ($value['KC3'] == 0) {
echo "<td colspan=\"3\" id=\"gclose\">Knife</td>";
}
?>
</tr>
</table>
<?php
$payload = ob_get_clean();
$etag = '"' . md5($payload) . '"';
$clientEtag = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? trim($_SERVER['HTTP_IF_NONE_MATCH']) : null;
if ($clientEtag && $clientEtag === $etag) {
header('HTTP/1.1 304 Not Modified');
header('Cache-Control: no-cache, must-revalidate');
header('ETag: ' . $etag);
exit;
}
header('Cache-Control: no-cache, must-revalidate');
header('Content-Type: text/html; charset=utf-8');
header('ETag: ' . $etag);
echo $payload;