Folder reorganize 1
This commit is contained in:
238
includes/kepware-rest.php
Normal file
238
includes/kepware-rest.php
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user