722 lines
27 KiB
C#
722 lines
27 KiB
C#
using RobotNet.VDA5050;
|
|
using RobotNet10.MapEditor.Shared.DTOs.Edge;
|
|
using RobotNet10.MapEditor.Shared.DTOs.Layout;
|
|
using RobotNet10.MapEditor.Shared.DTOs.LayoutData;
|
|
using RobotNet10.MapEditor.Shared.DTOs.Node;
|
|
using RobotNet10.MapEditor.Shared.DTOs.Requests;
|
|
using RobotNet10.MapEditor.Shared.DTOs.Responses;
|
|
using RobotNet10.MapEditor.Shared.DTOs.Station;
|
|
using RobotNet10.MapEditor.Shared.DTOs.VehicleType;
|
|
using System.Globalization;
|
|
using System.Net.Http.Headers;
|
|
using System.Net.Http.Json;
|
|
|
|
namespace RobotNet10.MapEditor.Services.API;
|
|
|
|
/// <summary>
|
|
/// Service for communicating with MapManager REST API
|
|
/// </summary>
|
|
public class MapManagerApiService(HttpClient httpClient)
|
|
{
|
|
private readonly HttpClient _httpClient = httpClient;
|
|
private readonly string? _baseUrl = httpClient.BaseAddress?.AbsoluteUri;
|
|
|
|
// ==========================================
|
|
// LAYOUTS
|
|
// ==========================================
|
|
|
|
/// <summary>
|
|
/// Search layouts (returns nested Versions and Levels)
|
|
/// </summary>
|
|
public async Task<List<LayoutDto>> SearchLayoutsAsync(string? search = null)
|
|
{
|
|
var url = $"{_baseUrl}api/layouts";
|
|
if (!string.IsNullOrEmpty(search))
|
|
url += $"?search={Uri.EscapeDataString(search)}";
|
|
|
|
try
|
|
{
|
|
var response = await _httpClient.GetAsync(url);
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
{
|
|
var errorMessage = await GetDetailedErrorMessageAsync(response, url);
|
|
throw new HttpRequestException(errorMessage);
|
|
}
|
|
|
|
return await response.Content.ReadFromJsonAsync<List<LayoutDto>>() ?? new();
|
|
}
|
|
catch (HttpRequestException)
|
|
{
|
|
throw; // Re-throw with detailed message
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new HttpRequestException($"Failed to load layouts from {url}: {ex.Message}", ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get detailed error message from HTTP response
|
|
/// </summary>
|
|
private async Task<string> GetDetailedErrorMessageAsync(HttpResponseMessage response, string url)
|
|
{
|
|
var statusCode = response.StatusCode;
|
|
var statusText = response.ReasonPhrase ?? statusCode.ToString();
|
|
|
|
// Try to read error message from response body
|
|
string? errorDetail = null;
|
|
try
|
|
{
|
|
var content = await response.Content.ReadAsStringAsync();
|
|
if (!string.IsNullOrWhiteSpace(content) && content.Length < 500)
|
|
{
|
|
// Try to parse as JSON
|
|
try
|
|
{
|
|
using var doc = System.Text.Json.JsonDocument.Parse(content);
|
|
var root = doc.RootElement;
|
|
|
|
if (root.TryGetProperty("error", out var errorProp))
|
|
errorDetail = errorProp.GetString();
|
|
else if (root.TryGetProperty("message", out var messageProp))
|
|
errorDetail = messageProp.GetString();
|
|
else if (root.ValueKind == System.Text.Json.JsonValueKind.String)
|
|
errorDetail = root.GetString();
|
|
}
|
|
catch
|
|
{
|
|
// If not JSON, use content as-is if reasonable length
|
|
errorDetail = content;
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// Ignore errors reading response body
|
|
}
|
|
|
|
// Build detailed error message
|
|
var message = $"HTTP {(int)statusCode} {statusText}";
|
|
if (!string.IsNullOrWhiteSpace(errorDetail))
|
|
{
|
|
message += $": {errorDetail}";
|
|
}
|
|
message += $" (URL: {url})";
|
|
|
|
return message;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get layout by ID
|
|
/// </summary>
|
|
public async Task<LayoutDto?> GetLayoutAsync(Guid layoutId)
|
|
{
|
|
return await _httpClient.GetFromJsonAsync<LayoutDto>(
|
|
$"{_baseUrl}api/layouts/{layoutId}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create new layout
|
|
/// </summary>
|
|
public async Task<LayoutDto> CreateLayoutAsync(CreateLayoutRequest request)
|
|
{
|
|
var response = await _httpClient.PostAsJsonAsync($"{_baseUrl}api/layouts", request);
|
|
response.EnsureSuccessStatusCode();
|
|
return await response.Content.ReadFromJsonAsync<LayoutDto>()
|
|
?? throw new Exception("Failed to create layout");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update layout
|
|
/// </summary>
|
|
public async Task<LayoutDto> UpdateLayoutAsync(Guid layoutId, UpdateLayoutRequest request)
|
|
{
|
|
var response = await _httpClient.PutAsJsonAsync($"{_baseUrl}api/layouts/{layoutId}", request);
|
|
response.EnsureSuccessStatusCode();
|
|
return await response.Content.ReadFromJsonAsync<LayoutDto>()
|
|
?? throw new Exception("Failed to update layout");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Delete layout
|
|
/// </summary>
|
|
public async Task DeleteLayoutAsync(Guid layoutId)
|
|
{
|
|
var response = await _httpClient.DeleteAsync($"{_baseUrl}api/layouts/{layoutId}");
|
|
response.EnsureSuccessStatusCode();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Activate layout
|
|
/// </summary>
|
|
public async Task<LayoutDto> ActivateLayoutAsync(Guid layoutId)
|
|
{
|
|
var response = await _httpClient.PostAsync($"{_baseUrl}api/layouts/{layoutId}/activate", null);
|
|
response.EnsureSuccessStatusCode();
|
|
return await response.Content.ReadFromJsonAsync<LayoutDto>()
|
|
?? throw new Exception("Failed to activate layout");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deactivate layout
|
|
/// </summary>
|
|
public async Task<LayoutDto> DeactivateLayoutAsync(Guid layoutId)
|
|
{
|
|
var response = await _httpClient.PostAsync($"{_baseUrl}api/layouts/{layoutId}/deactivate", null);
|
|
response.EnsureSuccessStatusCode();
|
|
return await response.Content.ReadFromJsonAsync<LayoutDto>()
|
|
?? throw new Exception("Failed to deactivate layout");
|
|
}
|
|
|
|
// ==========================================
|
|
// VERSIONS
|
|
// ==========================================
|
|
|
|
/// <summary>
|
|
/// Create new version for a layout
|
|
/// </summary>
|
|
public async Task<LayoutVersionDto> CreateVersionAsync(Guid layoutId, CreateLayoutVersionRequest request)
|
|
{
|
|
var response = await _httpClient.PostAsJsonAsync(
|
|
$"{_baseUrl}api/layouts/{layoutId}/versions", request);
|
|
response.EnsureSuccessStatusCode();
|
|
return await response.Content.ReadFromJsonAsync<LayoutVersionDto>()
|
|
?? throw new Exception("Failed to create version");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get all versions for a layout
|
|
/// </summary>
|
|
public async Task<List<LayoutVersionDto>> GetVersionsAsync(Guid layoutId)
|
|
{
|
|
return await _httpClient.GetFromJsonAsync<List<LayoutVersionDto>>(
|
|
$"{_baseUrl}api/layouts/{layoutId}/versions") ?? new();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get version by ID
|
|
/// </summary>
|
|
public async Task<LayoutVersionDto?> GetVersionAsync(Guid versionId)
|
|
{
|
|
return await _httpClient.GetFromJsonAsync<LayoutVersionDto>(
|
|
$"{_baseUrl}api/layouts/versions/{versionId}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Delete version
|
|
/// </summary>
|
|
public async Task DeleteVersionAsync(Guid versionId)
|
|
{
|
|
var response = await _httpClient.DeleteAsync($"{_baseUrl}api/layouts/versions/{versionId}");
|
|
response.EnsureSuccessStatusCode();
|
|
}
|
|
|
|
// ==========================================
|
|
// LEVELS
|
|
// ==========================================
|
|
|
|
/// <summary>
|
|
/// Create new level for a version
|
|
/// </summary>
|
|
public async Task<LayoutLevelDto> CreateLevelAsync(Guid versionId, CreateLayoutLevelRequest request)
|
|
{
|
|
var response = await _httpClient.PostAsJsonAsync(
|
|
$"{_baseUrl}api/layouts/versions/{versionId}/levels", request);
|
|
response.EnsureSuccessStatusCode();
|
|
return await response.Content.ReadFromJsonAsync<LayoutLevelDto>()
|
|
?? throw new Exception("Failed to create level");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a new level with background image in a single request
|
|
/// </summary>
|
|
/// <param name="versionId">Version ID</param>
|
|
/// <param name="layoutLevelId">Layout level identifier string</param>
|
|
/// <param name="levelOrder">Level order</param>
|
|
/// <param name="resolution">Resolution in meters per pixel</param>
|
|
/// <param name="originX">Origin X coordinate in meters</param>
|
|
/// <param name="originY">Origin Y coordinate in meters</param>
|
|
/// <param name="imageStream">Image stream (PNG format)</param>
|
|
/// <param name="fileName">Image file name</param>
|
|
/// <returns>Created layout level DTO</returns>
|
|
public async Task<LayoutLevelDto> CreateLevelWithImageAsync(
|
|
Guid versionId,
|
|
string layoutLevelId,
|
|
int levelOrder,
|
|
double resolution,
|
|
double originX,
|
|
double originY,
|
|
Stream imageStream,
|
|
string fileName)
|
|
{
|
|
using var content = new MultipartFormDataContent();
|
|
|
|
// Add form fields
|
|
content.Add(new StringContent(layoutLevelId), "layoutLevelId");
|
|
content.Add(new StringContent(levelOrder.ToString()), "levelOrder");
|
|
content.Add(new StringContent(resolution.ToString(CultureInfo.InvariantCulture)), "resolution");
|
|
content.Add(new StringContent(originX.ToString(CultureInfo.InvariantCulture)), "originX");
|
|
content.Add(new StringContent(originY.ToString(CultureInfo.InvariantCulture)), "originY");
|
|
|
|
// Add image file
|
|
var streamContent = new StreamContent(imageStream);
|
|
streamContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");
|
|
content.Add(streamContent, "file", fileName);
|
|
|
|
var response = await _httpClient.PostAsync(
|
|
$"{_baseUrl}api/layouts/versions/{versionId}/levels/with-image",
|
|
content);
|
|
|
|
response.EnsureSuccessStatusCode();
|
|
return await response.Content.ReadFromJsonAsync<LayoutLevelDto>()
|
|
?? throw new Exception("Failed to create level with image");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get all levels for a version
|
|
/// </summary>
|
|
public async Task<List<LayoutLevelDto>> GetLevelsAsync(Guid versionId)
|
|
{
|
|
return await _httpClient.GetFromJsonAsync<List<LayoutLevelDto>>(
|
|
$"{_baseUrl}api/layouts/versions/{versionId}/levels") ?? new();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get level by ID
|
|
/// </summary>
|
|
public async Task<LayoutLevelDto?> GetLevelAsync(Guid levelId)
|
|
{
|
|
return await _httpClient.GetFromJsonAsync<LayoutLevelDto>(
|
|
$"{_baseUrl}api/layouts/levels/{levelId}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update level
|
|
/// </summary>
|
|
public async Task<LayoutLevelDto> UpdateLevelAsync(Guid levelId, UpdateLayoutLevelRequest request)
|
|
{
|
|
var response = await _httpClient.PutAsJsonAsync($"{_baseUrl}api/layouts/levels/{levelId}", request);
|
|
response.EnsureSuccessStatusCode();
|
|
return await response.Content.ReadFromJsonAsync<LayoutLevelDto>()
|
|
?? throw new Exception("Failed to update level");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Delete level
|
|
/// </summary>
|
|
public async Task DeleteLevelAsync(Guid levelId)
|
|
{
|
|
var response = await _httpClient.DeleteAsync($"{_baseUrl}api/layouts/levels/{levelId}");
|
|
response.EnsureSuccessStatusCode();
|
|
}
|
|
|
|
// ==========================================
|
|
// LAYOUT DATA (For Preview)
|
|
// ==========================================
|
|
|
|
/// <summary>
|
|
/// Get comprehensive layout data (nodes, edges, stations)
|
|
/// </summary>
|
|
public async Task<LayoutDataDto> GetLayoutDataAsync(Guid layoutLevelId)
|
|
{
|
|
return await _httpClient.GetFromJsonAsync<LayoutDataDto>(
|
|
$"{_baseUrl}api/layout-data/{layoutLevelId}") ?? new();
|
|
}
|
|
|
|
// ==========================================
|
|
// IMAGES
|
|
// ==========================================
|
|
|
|
/// <summary>
|
|
/// Get background image for a layout level
|
|
/// </summary>
|
|
public async Task<byte[]?> GetLayoutImageAsync(Guid layoutLevelId)
|
|
{
|
|
try
|
|
{
|
|
return await _httpClient.GetByteArrayAsync($"{_baseUrl}api/images/layout/{layoutLevelId}");
|
|
}
|
|
catch (HttpRequestException)
|
|
{
|
|
return null; // Image doesn't exist
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Upload background image for a layout level
|
|
/// </summary>
|
|
public async Task UploadLayoutImageAsync(Guid layoutLevelId, Stream imageStream, string fileName)
|
|
{
|
|
using var content = new MultipartFormDataContent();
|
|
var streamContent = new StreamContent(imageStream);
|
|
streamContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");
|
|
content.Add(streamContent, "file", fileName);
|
|
|
|
var response = await _httpClient.PostAsync(
|
|
$"{_baseUrl}api/images/layout/{layoutLevelId}", content);
|
|
response.EnsureSuccessStatusCode();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Delete background image for a layout level
|
|
/// </summary>
|
|
public async Task DeleteLayoutImageAsync(Guid layoutLevelId)
|
|
{
|
|
var response = await _httpClient.DeleteAsync($"{_baseUrl}api/images/layout/{layoutLevelId}");
|
|
response.EnsureSuccessStatusCode();
|
|
}
|
|
|
|
// ==========================================
|
|
// NODES
|
|
// ==========================================
|
|
|
|
/// <summary>
|
|
/// Get node by ID
|
|
/// </summary>
|
|
public async Task<NodeDto?> GetNodeAsync(Guid nodeId)
|
|
{
|
|
return await _httpClient.GetFromJsonAsync<NodeDto>($"{_baseUrl}api/nodes/{nodeId}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update node properties
|
|
/// </summary>
|
|
public async Task<NodeDto> UpdateNodeAsync(Guid nodeId, UpdateNodeRequest request)
|
|
{
|
|
var response = await _httpClient.PutAsJsonAsync($"{_baseUrl}api/nodes/{nodeId}", request);
|
|
response.EnsureSuccessStatusCode();
|
|
return await response.Content.ReadFromJsonAsync<NodeDto>()
|
|
?? throw new Exception("Failed to update node");
|
|
}
|
|
|
|
// ==========================================
|
|
// EDGES
|
|
// ==========================================
|
|
|
|
/// <summary>
|
|
/// Create a new edge with automatic node detection/creation
|
|
/// </summary>
|
|
public async Task<EdgeDto> CreateEdgeAsync(CreateEdgeRequest request)
|
|
{
|
|
var response = await _httpClient.PostAsJsonAsync($"{_baseUrl}api/edges", request);
|
|
response.EnsureSuccessStatusCode();
|
|
return await response.Content.ReadFromJsonAsync<EdgeDto>()
|
|
?? throw new Exception("Failed to create edge");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get edge by ID
|
|
/// </summary>
|
|
public async Task<EdgeDto?> GetEdgeAsync(Guid edgeId)
|
|
{
|
|
return await _httpClient.GetFromJsonAsync<EdgeDto>($"{_baseUrl}api/edges/{edgeId}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update edge properties
|
|
/// </summary>
|
|
public async Task<EdgeDto> UpdateEdgeAsync(Guid edgeId, UpdateEdgeRequest request)
|
|
{
|
|
var response = await _httpClient.PutAsJsonAsync($"{_baseUrl}api/edges/{edgeId}", request);
|
|
response.EnsureSuccessStatusCode();
|
|
return await response.Content.ReadFromJsonAsync<EdgeDto>()
|
|
?? throw new Exception("Failed to update edge");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Delete edge
|
|
/// </summary>
|
|
public async Task DeleteEdgeAsync(Guid edgeId)
|
|
{
|
|
var response = await _httpClient.DeleteAsync($"{_baseUrl}api/edges/{edgeId}");
|
|
response.EnsureSuccessStatusCode();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Delete multiple edges in batch
|
|
/// </summary>
|
|
public async Task DeleteEdgesBatchAsync(List<Guid> edgeIds)
|
|
{
|
|
var request = new DeleteEdgesRequest { EdgeIds = edgeIds };
|
|
|
|
// Send DELETE request with body (non-standard but required by API)
|
|
var content = new StringContent(
|
|
System.Text.Json.JsonSerializer.Serialize(request, JsonOptionExtends.Write),
|
|
System.Text.Encoding.UTF8,
|
|
"application/json");
|
|
|
|
var deleteRequest = new HttpRequestMessage(HttpMethod.Delete, $"{_baseUrl}api/edges/batch")
|
|
{
|
|
Content = content
|
|
};
|
|
|
|
var response = await _httpClient.SendAsync(deleteRequest);
|
|
response.EnsureSuccessStatusCode();
|
|
}
|
|
|
|
// ==========================================
|
|
// MERGE/SPLIT OPERATIONS
|
|
// ==========================================
|
|
|
|
/// <summary>
|
|
/// Merge multiple nodes into one node
|
|
/// </summary>
|
|
public async Task<MergeNodesResponse> MergeNodesAsync(MergeNodesRequest request)
|
|
{
|
|
var response = await _httpClient.PostAsJsonAsync($"{_baseUrl}api/layout-data/merge-nodes", request);
|
|
|
|
if (response.StatusCode == System.Net.HttpStatusCode.BadRequest)
|
|
{
|
|
var errorContent = await response.Content.ReadAsStringAsync();
|
|
throw new InvalidOperationException($"Failed to merge nodes: {errorContent}");
|
|
}
|
|
|
|
response.EnsureSuccessStatusCode();
|
|
return await response.Content.ReadFromJsonAsync<MergeNodesResponse>()
|
|
?? throw new Exception("Failed to merge nodes");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Split a node into multiple nodes
|
|
/// </summary>
|
|
public async Task<SplitNodeResponse> SplitNodeAsync(SplitNodeRequest request)
|
|
{
|
|
var response = await _httpClient.PostAsJsonAsync($"{_baseUrl}api/layout-data/split-node", request);
|
|
|
|
if (response.StatusCode == System.Net.HttpStatusCode.BadRequest)
|
|
{
|
|
var errorContent = await response.Content.ReadAsStringAsync();
|
|
throw new InvalidOperationException($"Failed to split node: {errorContent}");
|
|
}
|
|
|
|
response.EnsureSuccessStatusCode();
|
|
return await response.Content.ReadFromJsonAsync<SplitNodeResponse>()
|
|
?? throw new Exception("Failed to split node");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Save all layout changes (nodes and edges) in a batch operation
|
|
/// </summary>
|
|
public async Task<SaveLayoutDataResponse> SaveLayoutDataAsync(SaveLayoutDataRequest request)
|
|
{
|
|
var response = await _httpClient.PostAsJsonAsync($"{_baseUrl}api/layout-data/save", request);
|
|
|
|
if (response.StatusCode == System.Net.HttpStatusCode.BadRequest)
|
|
{
|
|
var errorContent = await response.Content.ReadAsStringAsync();
|
|
throw new InvalidOperationException($"Failed to save layout data: {errorContent}");
|
|
}
|
|
|
|
response.EnsureSuccessStatusCode();
|
|
return await response.Content.ReadFromJsonAsync<SaveLayoutDataResponse>()
|
|
?? throw new Exception("Failed to save layout data");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copy selected nodes and edges with an offset
|
|
/// </summary>
|
|
public async Task<CopyNodesResponse> CopyNodesAsync(CopyNodesRequest request)
|
|
{
|
|
var response = await _httpClient.PostAsJsonAsync($"{_baseUrl}api/layout-data/copy-nodes", request);
|
|
|
|
if (response.StatusCode == System.Net.HttpStatusCode.BadRequest)
|
|
{
|
|
var errorContent = await response.Content.ReadAsStringAsync();
|
|
throw new InvalidOperationException($"Failed to copy nodes: {errorContent}");
|
|
}
|
|
|
|
response.EnsureSuccessStatusCode();
|
|
return await response.Content.ReadFromJsonAsync<CopyNodesResponse>()
|
|
?? throw new Exception("Failed to copy nodes");
|
|
}
|
|
|
|
// ==========================================
|
|
// VEHICLE TYPES
|
|
// ==========================================
|
|
|
|
/// <summary>
|
|
/// Get all vehicle types, optionally filtered by active status
|
|
/// </summary>
|
|
public async Task<List<VehicleTypeDto>> GetVehicleTypesAsync(bool? isActive = null)
|
|
{
|
|
var url = $"{_baseUrl}api/vehicles";
|
|
if (isActive.HasValue)
|
|
url += $"?isActive={isActive.Value}";
|
|
|
|
return await _httpClient.GetFromJsonAsync<List<VehicleTypeDto>>(url) ?? new();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get vehicle type by database ID
|
|
/// </summary>
|
|
public async Task<VehicleTypeDto?> GetVehicleTypeAsync(Guid id)
|
|
{
|
|
return await _httpClient.GetFromJsonAsync<VehicleTypeDto>($"{_baseUrl}api/vehicles/{id}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get vehicle type by VehicleTypeId string
|
|
/// </summary>
|
|
public async Task<VehicleTypeDto?> GetVehicleTypeByStringIdAsync(string vehicleTypeId)
|
|
{
|
|
return await _httpClient.GetFromJsonAsync<VehicleTypeDto>(
|
|
$"{_baseUrl}api/vehicles/vehicleTypeId/{Uri.EscapeDataString(vehicleTypeId)}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Search vehicle types by query string
|
|
/// </summary>
|
|
public async Task<List<VehicleTypeDto>> SearchVehicleTypesAsync(string query)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(query))
|
|
return new List<VehicleTypeDto>();
|
|
|
|
var url = $"{_baseUrl}api/vehicles/search?query={Uri.EscapeDataString(query)}";
|
|
return await _httpClient.GetFromJsonAsync<List<VehicleTypeDto>>(url) ?? new();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a new vehicle type
|
|
/// </summary>
|
|
public async Task<VehicleTypeDto> CreateVehicleTypeAsync(CreateVehicleTypeRequest request)
|
|
{
|
|
var response = await _httpClient.PostAsJsonAsync($"{_baseUrl}api/vehicles", request);
|
|
|
|
if (response.StatusCode == System.Net.HttpStatusCode.BadRequest)
|
|
{
|
|
var errorContent = await response.Content.ReadAsStringAsync();
|
|
throw new InvalidOperationException($"Failed to create vehicle type: {errorContent}");
|
|
}
|
|
|
|
response.EnsureSuccessStatusCode();
|
|
return await response.Content.ReadFromJsonAsync<VehicleTypeDto>()
|
|
?? throw new Exception("Failed to create vehicle type");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update an existing vehicle type
|
|
/// </summary>
|
|
public async Task<VehicleTypeDto> UpdateVehicleTypeAsync(Guid id, UpdateVehicleTypeRequest request)
|
|
{
|
|
var response = await _httpClient.PutAsJsonAsync($"{_baseUrl}api/vehicles/{id}", request);
|
|
|
|
if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
|
|
{
|
|
var errorContent = await response.Content.ReadAsStringAsync();
|
|
throw new InvalidOperationException($"Vehicle type not found: {errorContent}");
|
|
}
|
|
|
|
response.EnsureSuccessStatusCode();
|
|
return await response.Content.ReadFromJsonAsync<VehicleTypeDto>()
|
|
?? throw new Exception("Failed to update vehicle type");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Delete a vehicle type
|
|
/// </summary>
|
|
public async Task DeleteVehicleTypeAsync(Guid id)
|
|
{
|
|
var response = await _httpClient.DeleteAsync($"{_baseUrl}api/vehicles/{id}");
|
|
|
|
if (response.StatusCode == System.Net.HttpStatusCode.BadRequest)
|
|
{
|
|
var errorContent = await response.Content.ReadAsStringAsync();
|
|
throw new InvalidOperationException($"Cannot delete vehicle type: {errorContent}");
|
|
}
|
|
|
|
response.EnsureSuccessStatusCode();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get usage information for a vehicle type
|
|
/// </summary>
|
|
public async Task<VehicleTypeUsageInfoDto> GetVehicleTypeUsageAsync(Guid id)
|
|
{
|
|
return await _httpClient.GetFromJsonAsync<VehicleTypeUsageInfoDto>(
|
|
$"{_baseUrl}api/vehicles/{id}/usage")
|
|
?? throw new Exception("Failed to get vehicle type usage info");
|
|
}
|
|
|
|
// ==========================================
|
|
// STATIONS
|
|
// ==========================================
|
|
|
|
/// <summary>
|
|
/// Get all stations for a layout level
|
|
/// </summary>
|
|
public async Task<List<StationDto>> GetStationsByLevelAsync(Guid layoutLevelId)
|
|
{
|
|
return await _httpClient.GetFromJsonAsync<List<StationDto>>(
|
|
$"{_baseUrl}api/stations/level/{layoutLevelId}") ?? new();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get station by database ID
|
|
/// </summary>
|
|
public async Task<StationDto?> GetStationAsync(Guid stationId)
|
|
{
|
|
return await _httpClient.GetFromJsonAsync<StationDto>(
|
|
$"{_baseUrl}api/stations/{stationId}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a new station
|
|
/// </summary>
|
|
public async Task<StationDto> CreateStationAsync(CreateStationRequest request)
|
|
{
|
|
var response = await _httpClient.PostAsJsonAsync($"{_baseUrl}api/stations", request);
|
|
|
|
if (response.StatusCode == System.Net.HttpStatusCode.BadRequest)
|
|
{
|
|
var errorContent = await response.Content.ReadAsStringAsync();
|
|
throw new InvalidOperationException($"Failed to create station: {errorContent}");
|
|
}
|
|
|
|
response.EnsureSuccessStatusCode();
|
|
return await response.Content.ReadFromJsonAsync<StationDto>()
|
|
?? throw new Exception("Failed to create station");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update an existing station
|
|
/// </summary>
|
|
public async Task<StationDto> UpdateStationAsync(Guid stationId, UpdateStationRequest request)
|
|
{
|
|
var response = await _httpClient.PutAsJsonAsync($"{_baseUrl}api/stations/{stationId}", request);
|
|
|
|
if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
|
|
{
|
|
var errorContent = await response.Content.ReadAsStringAsync();
|
|
throw new InvalidOperationException($"Station not found: {errorContent}");
|
|
}
|
|
|
|
if (response.StatusCode == System.Net.HttpStatusCode.BadRequest)
|
|
{
|
|
var errorContent = await response.Content.ReadAsStringAsync();
|
|
throw new InvalidOperationException($"Failed to update station: {errorContent}");
|
|
}
|
|
|
|
response.EnsureSuccessStatusCode();
|
|
return await response.Content.ReadFromJsonAsync<StationDto>()
|
|
?? throw new Exception("Failed to update station");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Delete a station
|
|
/// </summary>
|
|
public async Task DeleteStationAsync(Guid stationId)
|
|
{
|
|
var response = await _httpClient.DeleteAsync($"{_baseUrl}api/stations/{stationId}");
|
|
|
|
if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
|
|
{
|
|
var errorContent = await response.Content.ReadAsStringAsync();
|
|
throw new InvalidOperationException($"Station not found: {errorContent}");
|
|
}
|
|
|
|
response.EnsureSuccessStatusCode();
|
|
}
|
|
}
|
|
|