Folder reorganize 1

This commit is contained in:
Rucus
2026-02-17 12:44:37 -06:00
parent ec99d85bc2
commit f0ae0ab905
17427 changed files with 2071 additions and 1059030 deletions

238
includes/kepware-rest.php Normal file
View File

@@ -0,0 +1,238 @@
<?php // phpcs:ignoreFile
declare(strict_types=1);
require_once __DIR__ . '/../config/control-settings.php';
/**
* Procedural helper functions for talking to the Kepware IoT Gateway REST API.
*/
if (!function_exists('kepware_config')) {
/**
* Retrieve Kepware REST configuration values.
*
* @return array<string, mixed>
*/
function kepware_config(): array
{
static $config;
if ($config === null) {
$settings = include __DIR__ . '/../config/control-settings.php';
$config = $settings['kepware'] ?? [];
}
return $config;
}
}
if (!function_exists('kepware_rest_request')) {
/**
* Issue an HTTP request to the Kepware REST server.
*
* @param string $method HTTP verb (GET, POST, ...).
* @param string $path Relative REST path.
* @param array<string, mixed>|null $body Optional JSON payload.
*
* @return array<string, mixed>
*/
function kepware_rest_request(string $method, string $path, ?array $body = null): array
{
$config = kepware_config();
$baseUrl = rtrim((string) ($config['base_url'] ?? ''), '/');
if ($baseUrl === '') {
throw new RuntimeException('Kepware base URL is not configured.');
}
$url = $baseUrl . '/' . ltrim($path, '/');
$username = (string) ($config['username'] ?? '');
$password = (string) ($config['password'] ?? '');
$timeout = (float) ($config['timeout'] ?? 5.0);
$verify = (bool) ($config['verify_tls'] ?? false);
$attempts = (int) ($config['retry_attempts'] ?? 1);
if ($attempts < 1) {
$attempts = 1;
}
$retryDelay = (float) ($config['retry_delay'] ?? 0.0);
if ($retryDelay < 0) {
$retryDelay = 0.0;
}
$postFields = null;
if ($body !== null) {
$postFields = json_encode($body, JSON_THROW_ON_ERROR);
}
$options = [
CURLOPT_URL => $url,
CURLOPT_CUSTOMREQUEST => strtoupper($method),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_USERPWD => $username . ':' . $password,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Accept: application/json',
],
CURLOPT_TIMEOUT => $timeout,
CURLOPT_CONNECTTIMEOUT => $timeout,
CURLOPT_SSL_VERIFYPEER => $verify,
CURLOPT_SSL_VERIFYHOST => $verify ? 2 : 0,
];
if ($postFields !== null) {
$options[CURLOPT_POSTFIELDS] = $postFields;
}
$retryableCurlErrors = [
CURLE_COULDNT_CONNECT,
CURLE_COULDNT_RESOLVE_HOST,
CURLE_COULDNT_RESOLVE_PROXY,
CURLE_GOT_NOTHING,
CURLE_OPERATION_TIMEDOUT,
CURLE_PARTIAL_FILE,
CURLE_RECV_ERROR,
CURLE_SEND_ERROR,
CURLE_SSL_CONNECT_ERROR,
];
for ($attempt = 1; $attempt <= $attempts; $attempt++) {
$handle = curl_init();
if ($handle === false) {
throw new RuntimeException('Unable to initialise cURL for Kepware request.');
}
curl_setopt_array($handle, $options);
$response = curl_exec($handle);
$error = curl_error($handle);
$errno = curl_errno($handle);
$status = (int) curl_getinfo($handle, CURLINFO_HTTP_CODE);
curl_close($handle);
if ($response === false) {
$shouldRetry = in_array($errno, $retryableCurlErrors, true) && $attempt < $attempts;
if ($shouldRetry) {
error_log(sprintf(
'Kepware request to %s attempt %d failed with cURL error %d: %s. Retrying in %.2f seconds.',
$path,
$attempt,
$errno,
$error,
$retryDelay
));
if ($retryDelay > 0.0) {
usleep((int) round($retryDelay * 1_000_000));
}
continue;
}
throw new RuntimeException('Kepware request failed: ' . $error, $errno);
}
$decoded = json_decode($response, true);
if (!is_array($decoded)) {
$snippet = substr($response, 0, 200);
error_log('Kepware returned a non-JSON payload: ' . $snippet);
if ($attempt < $attempts) {
if ($retryDelay > 0.0) {
usleep((int) round($retryDelay * 1_000_000));
}
continue;
}
throw new RuntimeException('Invalid JSON response from Kepware: ' . $snippet);
}
if ($status >= 400) {
if ($status >= 500 && $status < 600 && $attempt < $attempts) {
error_log(sprintf(
'Kepware request to %s attempt %d returned HTTP %d. Retrying in %.2f seconds.',
$path,
$attempt,
$status,
$retryDelay
));
if ($retryDelay > 0.0) {
usleep((int) round($retryDelay * 1_000_000));
}
continue;
}
throw new RuntimeException('Kepware request error (HTTP ' . $status . ').');
}
if ($attempt > 1) {
error_log(sprintf('Kepware request to %s succeeded on attempt %d.', $path, $attempt));
}
return $decoded;
}
throw new RuntimeException('Kepware request failed after ' . $attempts . ' attempts.');
}
}
if (!function_exists('kepware_read')) {
/**
* Read tag values from Kepware.
*
* @param string[] $tagIds List of tag identifiers.
*
* @return array<string, array<string, mixed>>
*/
function kepware_read(array $tagIds): array
{
if ($tagIds === []) {
return [];
}
$query = http_build_query(['ids' => implode(',', $tagIds)]);
$payload = kepware_rest_request('GET', 'read?' . $query);
$results = [];
foreach ($payload['readResults'] ?? [] as $result) {
if (!isset($result['id'])) {
continue;
}
$results[$result['id']] = [
'value' => $result['v'] ?? null,
'timestamp' => $result['t'] ?? null,
'status' => $result['s'] ?? null,
];
}
return $results;
}
}
if (!function_exists('kepware_write')) {
/**
* Write tag values via Kepware.
*
* @param array<int, array<string, mixed>> $writeRequest
*
* @return array<int, array<string, mixed>>
*/
function kepware_write(array $writeRequest): array
{
if ($writeRequest === []) {
return [];
}
$payload = kepware_rest_request('POST', 'write', $writeRequest);
if (isset($payload['writeResults']) && is_array($payload['writeResults'])) {
return $payload['writeResults'];
}
return $payload;
}
}