155 lines
4.7 KiB
PHP
155 lines
4.7 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/db/config.php';
|
|
|
|
const RECLINER_PATTERNS = ['continuous', 'pulse'];
|
|
|
|
function project_meta(): array
|
|
{
|
|
$projectName = $_SERVER['PROJECT_NAME'] ?? getenv('PROJECT_NAME') ?: 'Recliner Haptics Studio';
|
|
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? getenv('PROJECT_DESCRIPTION') ?: 'Browser-based recliner simulator with gamepad vibration controls, saved presets, and a live recline angle visualizer.';
|
|
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? getenv('PROJECT_IMAGE_URL') ?: '';
|
|
|
|
return [
|
|
'name' => (string) $projectName,
|
|
'description' => (string) $projectDescription,
|
|
'image' => (string) $projectImageUrl,
|
|
];
|
|
}
|
|
|
|
function ensure_recliner_schema(): void
|
|
{
|
|
db()->exec(
|
|
"CREATE TABLE IF NOT EXISTS recliner_presets (
|
|
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
name VARCHAR(120) NOT NULL,
|
|
angle_deg TINYINT UNSIGNED NOT NULL,
|
|
intensity_pct TINYINT UNSIGNED NOT NULL,
|
|
pattern_mode VARCHAR(20) NOT NULL,
|
|
notes VARCHAR(255) DEFAULT NULL,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
|
|
);
|
|
}
|
|
|
|
function default_preset(): array
|
|
{
|
|
return [
|
|
'name' => 'Demo Lounge',
|
|
'angle_deg' => 112,
|
|
'intensity_pct' => 44,
|
|
'pattern_mode' => 'continuous',
|
|
'notes' => 'Balanced demo profile for a calm showroom preview.',
|
|
];
|
|
}
|
|
|
|
function validate_preset_input(array $input): array
|
|
{
|
|
$errors = [];
|
|
|
|
$name = trim((string) ($input['name'] ?? ''));
|
|
if ($name === '' || strlen($name) < 2 || strlen($name) > 120) {
|
|
$errors['name'] = 'Preset name must be between 2 and 120 characters.';
|
|
}
|
|
|
|
$angle = filter_var($input['angle_deg'] ?? null, FILTER_VALIDATE_INT, [
|
|
'options' => ['min_range' => 0, 'max_range' => 160],
|
|
]);
|
|
if ($angle === false) {
|
|
$errors['angle_deg'] = 'Angle must be between 0° and 160°.';
|
|
}
|
|
|
|
$intensity = filter_var($input['intensity_pct'] ?? null, FILTER_VALIDATE_INT, [
|
|
'options' => ['min_range' => 0, 'max_range' => 100],
|
|
]);
|
|
if ($intensity === false) {
|
|
$errors['intensity_pct'] = 'Intensity must be between 0% and 100%.';
|
|
}
|
|
|
|
$pattern = (string) ($input['pattern_mode'] ?? 'continuous');
|
|
if (!in_array($pattern, RECLINER_PATTERNS, true)) {
|
|
$errors['pattern_mode'] = 'Choose a supported vibration pattern.';
|
|
}
|
|
|
|
$notes = trim((string) ($input['notes'] ?? ''));
|
|
if (strlen($notes) > 255) {
|
|
$errors['notes'] = 'Notes must stay under 255 characters.';
|
|
}
|
|
|
|
return [
|
|
'errors' => $errors,
|
|
'data' => [
|
|
'name' => $name,
|
|
'angle_deg' => $angle === false ? 0 : (int) $angle,
|
|
'intensity_pct' => $intensity === false ? 0 : (int) $intensity,
|
|
'pattern_mode' => $pattern,
|
|
'notes' => $notes,
|
|
],
|
|
];
|
|
}
|
|
|
|
function save_preset(array $data): int
|
|
{
|
|
$stmt = db()->prepare(
|
|
'INSERT INTO recliner_presets (name, angle_deg, intensity_pct, pattern_mode, notes)
|
|
VALUES (:name, :angle_deg, :intensity_pct, :pattern_mode, :notes)'
|
|
);
|
|
|
|
$stmt->bindValue(':name', $data['name'], PDO::PARAM_STR);
|
|
$stmt->bindValue(':angle_deg', $data['angle_deg'], PDO::PARAM_INT);
|
|
$stmt->bindValue(':intensity_pct', $data['intensity_pct'], PDO::PARAM_INT);
|
|
$stmt->bindValue(':pattern_mode', $data['pattern_mode'], PDO::PARAM_STR);
|
|
$stmt->bindValue(':notes', $data['notes'] !== '' ? $data['notes'] : null, $data['notes'] !== '' ? PDO::PARAM_STR : PDO::PARAM_NULL);
|
|
$stmt->execute();
|
|
|
|
return (int) db()->lastInsertId();
|
|
}
|
|
|
|
function get_recent_presets(int $limit = 8): array
|
|
{
|
|
$limit = max(1, min(24, $limit));
|
|
$stmt = db()->query(
|
|
'SELECT id, name, angle_deg, intensity_pct, pattern_mode, notes, created_at
|
|
FROM recliner_presets
|
|
ORDER BY created_at DESC, id DESC
|
|
LIMIT ' . $limit
|
|
);
|
|
|
|
return $stmt->fetchAll() ?: [];
|
|
}
|
|
|
|
function get_preset(int $id): ?array
|
|
{
|
|
$stmt = db()->prepare(
|
|
'SELECT id, name, angle_deg, intensity_pct, pattern_mode, notes, created_at
|
|
FROM recliner_presets
|
|
WHERE id = :id
|
|
LIMIT 1'
|
|
);
|
|
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
|
|
$stmt->execute();
|
|
$preset = $stmt->fetch();
|
|
|
|
return $preset ?: null;
|
|
}
|
|
|
|
function preset_tone(int $angle): string
|
|
{
|
|
if ($angle >= 135) {
|
|
return 'Deep recline';
|
|
}
|
|
if ($angle >= 90) {
|
|
return 'Lounge';
|
|
}
|
|
if ($angle >= 45) {
|
|
return 'Reading';
|
|
}
|
|
|
|
return 'Upright';
|
|
}
|
|
|
|
function e(?string $value): string
|
|
{
|
|
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
|
|
} |