- buildTerminalPhaseMap: Schritt 1b - Leitungen mit expliziter Farbe als Startpunkte (nur Gerät→Gerät, keine Abgänge) - buildTerminalPhaseMap: Block-Durchreichung (Top↔Bottom) entfernt - buildTerminalPhaseMap: Junction-Verbindungen (Terminal→Leitung) bidirektional verarbeitet via _connectionById Index - PWA: Abgangs-Rendering mit Index-Fallback wenn source_terminal_id fehlt - PWA: Abgangs-Labels max-height 130px, min-height 30px - Auto-Naming: EquipmentCarrier create/update → 'R' + count - Auto-Naming: EquipmentPanel update → 'Feld ' + count - pwa_api.php: Hardcoded Fallbacks 'Feld'/'Hutschiene' entfernt - pwa.js: Hutschiene Auto-Naming dynamisch aus Panel-Carrier-Anzahl - kundenkarte.js: Carrier-Dialog Placeholder 'z.B. R1 (automatisch)' - SW Cache auf v12.5 hochgezählt Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
448 lines
16 KiB
PHP
Executable file
448 lines
16 KiB
PHP
Executable file
<?php
|
|
/* Copyright (C) 2026 Alles Watt lauft
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*/
|
|
|
|
/**
|
|
* Class EquipmentConnection
|
|
* Manages connections between equipment (generic for all system types)
|
|
*/
|
|
class EquipmentConnection extends CommonObject
|
|
{
|
|
public $element = 'equipmentconnection';
|
|
public $table_element = 'kundenkarte_equipment_connection';
|
|
|
|
public $fk_source;
|
|
public $source_terminal = 'output';
|
|
public $source_terminal_id;
|
|
public $bundled_terminals; // 'all' = alle Terminals belegt, '0,1,2' = spezifische Indizes, NULL = einzeln
|
|
public $fk_target;
|
|
public $target_terminal = 'input';
|
|
public $target_terminal_id;
|
|
|
|
// Connection properties
|
|
public $connection_type;
|
|
public $color;
|
|
|
|
// Output/endpoint info
|
|
public $output_label;
|
|
public $output_location; // Räumlichkeit/Örtlichkeit des Verbrauchers
|
|
|
|
// Medium info (cable, wire, etc.)
|
|
public $medium_type;
|
|
public $medium_spec;
|
|
public $medium_length;
|
|
|
|
// Rail info
|
|
public $is_rail = 0;
|
|
public $rail_start_te;
|
|
public $rail_end_te;
|
|
public $rail_phases; // '3P', '3P+N', 'L1', 'L1N', etc.
|
|
public $excluded_te; // Comma-separated TE positions to exclude (gaps for FI)
|
|
public $num_lines = 1; // Number of lines for busbar (1-5)
|
|
public $fk_busbar_type; // Reference to busbar type template
|
|
public $phases_config; // JSON array of phase labels from busbar type
|
|
|
|
public $fk_carrier;
|
|
public $position_y = 0;
|
|
public $path_data; // SVG path for manually drawn connections
|
|
public $note_private;
|
|
public $status = 1;
|
|
|
|
public $date_creation;
|
|
public $fk_user_creat;
|
|
public $fk_user_modif;
|
|
|
|
// Loaded objects
|
|
public $source_label;
|
|
public $target_label;
|
|
public $carrier_label;
|
|
public $source_pos;
|
|
public $source_width;
|
|
public $target_pos;
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param DoliDB $db Database handler
|
|
*/
|
|
public function __construct($db)
|
|
{
|
|
$this->db = $db;
|
|
}
|
|
|
|
/**
|
|
* Create object in database
|
|
*
|
|
* @param User $user User that creates
|
|
* @return int Return integer <0 if KO, Id of created object if OK
|
|
*/
|
|
public function create($user)
|
|
{
|
|
global $conf;
|
|
|
|
$error = 0;
|
|
$now = dol_now();
|
|
|
|
$this->db->begin();
|
|
|
|
$sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
|
|
$sql .= "entity, fk_source, source_terminal, source_terminal_id, bundled_terminals, fk_target, target_terminal, target_terminal_id,";
|
|
$sql .= " connection_type, color, output_label, output_location,";
|
|
$sql .= " medium_type, medium_spec, medium_length,";
|
|
$sql .= " is_rail, rail_start_te, rail_end_te, rail_phases, excluded_te, num_lines, fk_busbar_type, fk_carrier, position_y, path_data,";
|
|
$sql .= " note_private, status, date_creation, fk_user_creat";
|
|
$sql .= ") VALUES (";
|
|
$sql .= ((int) $conf->entity);
|
|
$sql .= ", ".($this->fk_source > 0 ? ((int) $this->fk_source) : "NULL");
|
|
$sql .= ", '".$this->db->escape($this->source_terminal ?: 'output')."'";
|
|
$sql .= ", ".($this->source_terminal_id ? "'".$this->db->escape($this->source_terminal_id)."'" : "NULL");
|
|
$sql .= ", ".($this->bundled_terminals ? "'".$this->db->escape($this->bundled_terminals)."'" : "NULL");
|
|
$sql .= ", ".($this->fk_target > 0 ? ((int) $this->fk_target) : "NULL");
|
|
$sql .= ", '".$this->db->escape($this->target_terminal ?: 'input')."'";
|
|
$sql .= ", ".($this->target_terminal_id ? "'".$this->db->escape($this->target_terminal_id)."'" : "NULL");
|
|
$sql .= ", ".($this->connection_type ? "'".$this->db->escape($this->connection_type)."'" : "NULL");
|
|
$sql .= ", ".($this->color ? "'".$this->db->escape($this->color)."'" : "NULL");
|
|
$sql .= ", ".($this->output_label ? "'".$this->db->escape($this->output_label)."'" : "NULL");
|
|
$sql .= ", ".($this->output_location ? "'".$this->db->escape($this->output_location)."'" : "NULL");
|
|
$sql .= ", ".($this->medium_type ? "'".$this->db->escape($this->medium_type)."'" : "NULL");
|
|
$sql .= ", ".($this->medium_spec ? "'".$this->db->escape($this->medium_spec)."'" : "NULL");
|
|
$sql .= ", ".($this->medium_length ? "'".$this->db->escape($this->medium_length)."'" : "NULL");
|
|
$sql .= ", ".((int) $this->is_rail);
|
|
$sql .= ", ".($this->rail_start_te > 0 ? ((int) $this->rail_start_te) : "NULL");
|
|
$sql .= ", ".($this->rail_end_te > 0 ? ((int) $this->rail_end_te) : "NULL");
|
|
$sql .= ", ".($this->rail_phases ? "'".$this->db->escape($this->rail_phases)."'" : "NULL");
|
|
$sql .= ", ".($this->excluded_te ? "'".$this->db->escape($this->excluded_te)."'" : "NULL");
|
|
$sql .= ", ".((int) ($this->num_lines > 0 ? $this->num_lines : 1));
|
|
$sql .= ", ".($this->fk_busbar_type > 0 ? ((int) $this->fk_busbar_type) : "NULL");
|
|
$sql .= ", ".($this->fk_carrier > 0 ? ((int) $this->fk_carrier) : "NULL");
|
|
$sql .= ", ".((int) $this->position_y);
|
|
$sql .= ", ".($this->path_data ? "'".$this->db->escape($this->path_data)."'" : "NULL");
|
|
$sql .= ", ".($this->note_private ? "'".$this->db->escape($this->note_private)."'" : "NULL");
|
|
$sql .= ", ".((int) $this->status);
|
|
$sql .= ", '".$this->db->idate($now)."'";
|
|
$sql .= ", ".((int) $user->id);
|
|
$sql .= ")";
|
|
|
|
$resql = $this->db->query($sql);
|
|
if (!$resql) {
|
|
$error++;
|
|
$this->errors[] = "Error ".$this->db->lasterror();
|
|
}
|
|
|
|
if (!$error) {
|
|
$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
|
|
}
|
|
|
|
if ($error) {
|
|
$this->db->rollback();
|
|
return -1 * $error;
|
|
} else {
|
|
$this->db->commit();
|
|
return $this->id;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load object from database
|
|
*
|
|
* @param int $id ID of record
|
|
* @return int Return integer <0 if KO, 0 if not found, >0 if OK
|
|
*/
|
|
public function fetch($id)
|
|
{
|
|
$sql = "SELECT c.*, ";
|
|
$sql .= " src.label as source_label, tgt.label as target_label, car.label as carrier_label";
|
|
$sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as c";
|
|
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_equipment as src ON c.fk_source = src.rowid";
|
|
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_equipment as tgt ON c.fk_target = tgt.rowid";
|
|
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_equipment_carrier as car ON c.fk_carrier = car.rowid";
|
|
$sql .= " WHERE c.rowid = ".((int) $id);
|
|
|
|
$resql = $this->db->query($sql);
|
|
if ($resql) {
|
|
if ($this->db->num_rows($resql)) {
|
|
$obj = $this->db->fetch_object($resql);
|
|
|
|
$this->id = $obj->rowid;
|
|
$this->entity = $obj->entity;
|
|
$this->fk_source = $obj->fk_source;
|
|
$this->source_terminal = $obj->source_terminal;
|
|
$this->source_terminal_id = $obj->source_terminal_id;
|
|
$this->bundled_terminals = isset($obj->bundled_terminals) ? $obj->bundled_terminals : null;
|
|
$this->fk_target = $obj->fk_target;
|
|
$this->target_terminal = $obj->target_terminal;
|
|
$this->target_terminal_id = $obj->target_terminal_id;
|
|
$this->connection_type = $obj->connection_type;
|
|
$this->color = $obj->color;
|
|
$this->output_label = $obj->output_label;
|
|
$this->output_location = isset($obj->output_location) ? $obj->output_location : null;
|
|
$this->medium_type = $obj->medium_type;
|
|
$this->medium_spec = $obj->medium_spec;
|
|
$this->medium_length = $obj->medium_length;
|
|
$this->is_rail = $obj->is_rail;
|
|
$this->rail_start_te = $obj->rail_start_te;
|
|
$this->rail_end_te = $obj->rail_end_te;
|
|
$this->rail_phases = $obj->rail_phases;
|
|
$this->excluded_te = $obj->excluded_te;
|
|
$this->fk_carrier = $obj->fk_carrier;
|
|
$this->position_y = $obj->position_y;
|
|
$this->path_data = isset($obj->path_data) ? $obj->path_data : null;
|
|
$this->note_private = $obj->note_private;
|
|
$this->status = $obj->status;
|
|
$this->date_creation = $this->db->jdate($obj->date_creation);
|
|
$this->fk_user_creat = $obj->fk_user_creat;
|
|
$this->fk_user_modif = $obj->fk_user_modif;
|
|
|
|
$this->source_label = $obj->source_label;
|
|
$this->target_label = $obj->target_label;
|
|
$this->carrier_label = $obj->carrier_label;
|
|
|
|
$this->db->free($resql);
|
|
return 1;
|
|
} else {
|
|
$this->db->free($resql);
|
|
return 0;
|
|
}
|
|
} else {
|
|
$this->error = $this->db->lasterror();
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update object in database
|
|
*
|
|
* @param User $user User that modifies
|
|
* @return int Return integer <0 if KO, >0 if OK
|
|
*/
|
|
public function update($user)
|
|
{
|
|
$error = 0;
|
|
|
|
$this->db->begin();
|
|
|
|
$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
|
|
$sql .= " fk_source = ".($this->fk_source > 0 ? ((int) $this->fk_source) : "NULL");
|
|
$sql .= ", source_terminal = '".$this->db->escape($this->source_terminal ?: 'output')."'";
|
|
$sql .= ", source_terminal_id = ".($this->source_terminal_id ? "'".$this->db->escape($this->source_terminal_id)."'" : "NULL");
|
|
$sql .= ", bundled_terminals = ".($this->bundled_terminals ? "'".$this->db->escape($this->bundled_terminals)."'" : "NULL");
|
|
$sql .= ", fk_target = ".($this->fk_target > 0 ? ((int) $this->fk_target) : "NULL");
|
|
$sql .= ", target_terminal = '".$this->db->escape($this->target_terminal ?: 'input')."'";
|
|
$sql .= ", target_terminal_id = ".($this->target_terminal_id ? "'".$this->db->escape($this->target_terminal_id)."'" : "NULL");
|
|
$sql .= ", connection_type = ".($this->connection_type ? "'".$this->db->escape($this->connection_type)."'" : "NULL");
|
|
$sql .= ", color = ".($this->color ? "'".$this->db->escape($this->color)."'" : "NULL");
|
|
$sql .= ", output_label = ".($this->output_label ? "'".$this->db->escape($this->output_label)."'" : "NULL");
|
|
$sql .= ", output_location = ".($this->output_location ? "'".$this->db->escape($this->output_location)."'" : "NULL");
|
|
$sql .= ", medium_type = ".($this->medium_type ? "'".$this->db->escape($this->medium_type)."'" : "NULL");
|
|
$sql .= ", medium_spec = ".($this->medium_spec ? "'".$this->db->escape($this->medium_spec)."'" : "NULL");
|
|
$sql .= ", medium_length = ".($this->medium_length ? "'".$this->db->escape($this->medium_length)."'" : "NULL");
|
|
$sql .= ", is_rail = ".((int) $this->is_rail);
|
|
$sql .= ", rail_start_te = ".($this->rail_start_te > 0 ? ((int) $this->rail_start_te) : "NULL");
|
|
$sql .= ", rail_end_te = ".($this->rail_end_te > 0 ? ((int) $this->rail_end_te) : "NULL");
|
|
$sql .= ", rail_phases = ".($this->rail_phases ? "'".$this->db->escape($this->rail_phases)."'" : "NULL");
|
|
$sql .= ", excluded_te = ".($this->excluded_te ? "'".$this->db->escape($this->excluded_te)."'" : "NULL");
|
|
$sql .= ", fk_busbar_type = ".($this->fk_busbar_type > 0 ? ((int) $this->fk_busbar_type) : "NULL");
|
|
$sql .= ", fk_carrier = ".($this->fk_carrier > 0 ? ((int) $this->fk_carrier) : "NULL");
|
|
$sql .= ", position_y = ".((int) $this->position_y);
|
|
$sql .= ", path_data = ".($this->path_data ? "'".$this->db->escape($this->path_data)."'" : "NULL");
|
|
$sql .= ", note_private = ".($this->note_private ? "'".$this->db->escape($this->note_private)."'" : "NULL");
|
|
$sql .= ", status = ".((int) $this->status);
|
|
$sql .= ", fk_user_modif = ".((int) $user->id);
|
|
$sql .= " WHERE rowid = ".((int) $this->id);
|
|
|
|
$resql = $this->db->query($sql);
|
|
if (!$resql) {
|
|
$error++;
|
|
$this->errors[] = "Error ".$this->db->lasterror();
|
|
}
|
|
|
|
if ($error) {
|
|
$this->db->rollback();
|
|
return -1 * $error;
|
|
} else {
|
|
$this->db->commit();
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete object in database
|
|
*
|
|
* @param User $user User that deletes
|
|
* @return int Return integer <0 if KO, >0 if OK
|
|
*/
|
|
public function delete($user)
|
|
{
|
|
$error = 0;
|
|
$this->db->begin();
|
|
|
|
$sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." WHERE rowid = ".((int) $this->id);
|
|
$resql = $this->db->query($sql);
|
|
if (!$resql) {
|
|
$error++;
|
|
$this->errors[] = "Error ".$this->db->lasterror();
|
|
}
|
|
|
|
if ($error) {
|
|
$this->db->rollback();
|
|
return -1 * $error;
|
|
} else {
|
|
$this->db->commit();
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetch all connections for a carrier
|
|
*
|
|
* @param int $carrierId Carrier ID
|
|
* @param int $activeOnly Only active connections
|
|
* @return array Array of EquipmentConnection objects
|
|
*/
|
|
public function fetchByCarrier($carrierId, $activeOnly = 1)
|
|
{
|
|
$results = array();
|
|
|
|
$sql = "SELECT c.*, ";
|
|
$sql .= " src.label as source_label, src.position_te as source_pos, src.width_te as source_width,";
|
|
$sql .= " tgt.label as target_label, tgt.position_te as target_pos,";
|
|
$sql .= " bt.phases_config as busbar_phases_config";
|
|
$sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as c";
|
|
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_equipment as src ON c.fk_source = src.rowid";
|
|
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_equipment as tgt ON c.fk_target = tgt.rowid";
|
|
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_busbar_type as bt ON c.fk_busbar_type = bt.rowid";
|
|
$sql .= " WHERE c.fk_carrier = ".((int) $carrierId);
|
|
if ($activeOnly) {
|
|
$sql .= " AND c.status = 1";
|
|
}
|
|
$sql .= " ORDER BY c.position_y ASC, c.rowid ASC";
|
|
|
|
$resql = $this->db->query($sql);
|
|
if ($resql) {
|
|
while ($obj = $this->db->fetch_object($resql)) {
|
|
$conn = new EquipmentConnection($this->db);
|
|
$conn->id = $obj->rowid;
|
|
$conn->entity = $obj->entity;
|
|
$conn->fk_source = $obj->fk_source;
|
|
$conn->source_terminal = $obj->source_terminal;
|
|
$conn->source_terminal_id = $obj->source_terminal_id;
|
|
$conn->bundled_terminals = isset($obj->bundled_terminals) ? $obj->bundled_terminals : null;
|
|
$conn->fk_target = $obj->fk_target;
|
|
$conn->target_terminal = $obj->target_terminal;
|
|
$conn->target_terminal_id = $obj->target_terminal_id;
|
|
$conn->connection_type = $obj->connection_type;
|
|
$conn->color = $obj->color;
|
|
$conn->output_label = $obj->output_label;
|
|
$conn->medium_type = $obj->medium_type;
|
|
$conn->medium_spec = $obj->medium_spec;
|
|
$conn->medium_length = $obj->medium_length;
|
|
$conn->is_rail = $obj->is_rail;
|
|
$conn->rail_start_te = $obj->rail_start_te;
|
|
$conn->rail_end_te = $obj->rail_end_te;
|
|
$conn->rail_phases = $obj->rail_phases;
|
|
$conn->excluded_te = $obj->excluded_te;
|
|
$conn->fk_carrier = $obj->fk_carrier;
|
|
$conn->position_y = $obj->position_y;
|
|
$conn->path_data = isset($obj->path_data) ? $obj->path_data : null;
|
|
$conn->status = $obj->status;
|
|
|
|
$conn->source_label = $obj->source_label;
|
|
$conn->source_pos = $obj->source_pos;
|
|
$conn->source_width = $obj->source_width;
|
|
$conn->target_label = $obj->target_label;
|
|
$conn->target_pos = $obj->target_pos;
|
|
$conn->fk_busbar_type = isset($obj->fk_busbar_type) ? $obj->fk_busbar_type : null;
|
|
$conn->phases_config = isset($obj->busbar_phases_config) ? $obj->busbar_phases_config : null;
|
|
|
|
$results[] = $conn;
|
|
}
|
|
$this->db->free($resql);
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Fetch all outputs for an equipment
|
|
*
|
|
* @param int $equipmentId Equipment ID
|
|
* @return array Array of EquipmentConnection objects
|
|
*/
|
|
public function fetchOutputs($equipmentId)
|
|
{
|
|
$results = array();
|
|
|
|
$sql = "SELECT * FROM ".MAIN_DB_PREFIX.$this->table_element;
|
|
$sql .= " WHERE fk_source = ".((int) $equipmentId);
|
|
$sql .= " AND fk_target IS NULL";
|
|
$sql .= " AND status = 1";
|
|
$sql .= " ORDER BY position_y ASC";
|
|
|
|
$resql = $this->db->query($sql);
|
|
if ($resql) {
|
|
while ($obj = $this->db->fetch_object($resql)) {
|
|
$conn = new EquipmentConnection($this->db);
|
|
$conn->id = $obj->rowid;
|
|
$conn->fk_source = $obj->fk_source;
|
|
$conn->connection_type = $obj->connection_type;
|
|
$conn->color = $obj->color;
|
|
$conn->output_label = $obj->output_label;
|
|
$conn->medium_type = $obj->medium_type;
|
|
$conn->medium_spec = $obj->medium_spec;
|
|
$conn->medium_length = $obj->medium_length;
|
|
$conn->fk_carrier = $obj->fk_carrier;
|
|
$conn->position_y = $obj->position_y;
|
|
$conn->status = $obj->status;
|
|
|
|
$results[] = $conn;
|
|
}
|
|
$this->db->free($resql);
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Get display color
|
|
*
|
|
* @return string Color hex code
|
|
*/
|
|
public function getColor()
|
|
{
|
|
if (!empty($this->color)) {
|
|
return $this->color;
|
|
}
|
|
return '#888888'; // Default grey
|
|
}
|
|
|
|
/**
|
|
* Get display label for output
|
|
*
|
|
* @return string Display label
|
|
*/
|
|
public function getDisplayLabel()
|
|
{
|
|
$parts = array();
|
|
|
|
if ($this->output_label) {
|
|
$parts[] = $this->output_label;
|
|
}
|
|
if ($this->medium_type) {
|
|
$mediumInfo = $this->medium_type;
|
|
if ($this->medium_spec) {
|
|
$mediumInfo .= ' '.$this->medium_spec;
|
|
}
|
|
if ($this->medium_length) {
|
|
$mediumInfo .= ' ('.$this->medium_length.')';
|
|
}
|
|
$parts[] = $mediumInfo;
|
|
}
|
|
|
|
return implode(' - ', $parts);
|
|
}
|
|
}
|