commit ab916dc82a58b62044fe5cf6351e64668deb0e06 Author: AJ Isaacs Date: Mon Oct 27 18:48:23 2025 -0400 Added Files diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..6d350ef --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,14 @@ +{ + "permissions": { + "allow": [ + "Skill(roslyn-bridge)", + "Bash(powershell -File:*)", + "Bash(curl:*)", + "Bash(dotnet:*)", + "Bash(rm:*)", + "Bash(powershell:*)" + ], + "deny": [], + "ask": [] + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5a5f80a --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ + +#Ignore thumbnails created by Windows +Thumbs.db +#Ignore files built by Visual Studio +*.obj +*.exe +*.pdb +*.user +*.aps +*.pch +*.vspscc +*_i.c +*_p.c +*.ncb +*.suo +*.tlb +*.tlh +*.bak +*.cache +*.ilk +*.log +[Bb]in +[Dd]ebug*/ +*.lib +*.sbr +obj/ +[Rr]elease*/ +_ReSharper*/ +[Tt]est[Rr]esult* +.vs/ +#Nuget packages folder +packages/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..6c8a5e1 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "PepLib"] + path = PepLib + url = https://git.thecozycat.net/aj/PepLib.git diff --git a/DEPLOY.md b/DEPLOY.md new file mode 100644 index 0000000..c3bb5a3 --- /dev/null +++ b/DEPLOY.md @@ -0,0 +1,280 @@ +# PepApi Deployment Guide + +## Quick Deployment (Windows Service) + +### Prerequisites +- .NET 8 Runtime or SDK installed +- Administrator privileges +- Network access to PepDB SQL Server +- Access to nest files directory + +### Deploy as Windows Service + +```powershell +# Run as Administrator +powershell -ExecutionPolicy Bypass -File Deploy-PepApi.ps1 -OpenFirewall +``` + +This will: +- Build and publish PepApi.Core in Release mode +- Install to `C:\Services\PepApi` +- Create Windows Service named "PepApi" +- Configure service to run on port 8085 +- Open Windows Firewall for port 8085 +- Start the service automatically + +### Custom Deployment + +```powershell +# Custom service name and location +powershell -ExecutionPolicy Bypass -File Deploy-PepApi.ps1 ` + -ServiceName "MyPepApi" ` + -InstallDir "D:\Services\PepApi" ` + -Urls "http://*:9000" ` + -OpenFirewall +``` + +### Configuration + +After deployment, **you must update** `C:\Services\PepApi\appsettings.json`: + +```json +{ + "ConnectionStrings": { + "PepDB": "data source=YOUR_SERVER\\INSTANCE;initial catalog=PEP;integrated security=True;..." + }, + "PepSettings": { + "NestDirectory": "\\\\YOUR_SERVER\\PEP Nest", + "MaterialsFile": "C:\\Pep\\PEP2012\\CONFIG\\material.lfn" + } +} +``` + +Then restart the service: +```powershell +Restart-Service -Name PepApi +``` + +--- + +## Alternative: Self-Hosted (No Service) + +### Build and Run + +```bash +cd PepApi.Core +dotnet publish -c Release -o ./publish +cd publish +.\PepApi.Core.exe +``` + +### Run with Custom Settings + +```bash +.\PepApi.Core.exe --urls "http://*:8085" +``` + +--- + +## Alternative: IIS Deployment + +### 1. Install Prerequisites +- Install ASP.NET Core Hosting Bundle from Microsoft +- Create IIS Application Pool (No Managed Code) + +### 2. Publish Application + +```bash +dotnet publish PepApi.Core/PepApi.Core.csproj -c Release -o C:\inetpub\PepApi +``` + +### 3. Configure IIS +- Create new website in IIS Manager +- Point to `C:\inetpub\PepApi` +- Use Application Pool with "No Managed Code" +- Set binding to port 8085 + +### 4. Update Configuration +- Edit `C:\inetpub\PepApi\appsettings.json` +- Restart IIS site + +--- + +## Verify Deployment + +### Check Service Status +```powershell +Get-Service -Name PepApi +``` + +### Test API +```powershell +# Health check +Invoke-WebRequest http://localhost:8085/swagger + +# Test nests endpoint +Invoke-WebRequest http://localhost:8085/nests/2024 +``` + +### View Logs +```powershell +# Service logs in Event Viewer +Get-EventLog -LogName Application -Source PepApi -Newest 20 + +# Or check console output if running self-hosted +``` + +--- + +## Service Management + +### Start Service +```powershell +Start-Service -Name PepApi +``` + +### Stop Service +```powershell +Stop-Service -Name PepApi +``` + +### Restart Service +```powershell +Restart-Service -Name PepApi +``` + +### Uninstall Service +```powershell +Stop-Service -Name PepApi +sc.exe delete PepApi +``` + +--- + +## Firewall Configuration + +If you didn't use `-OpenFirewall` during deployment: + +```powershell +# Open port 8085 +New-NetFirewallRule -DisplayName "PepApi HTTP 8085" ` + -Direction Inbound ` + -Protocol TCP ` + -LocalPort 8085 ` + -Action Allow +``` + +--- + +## Troubleshooting + +### Service Won't Start + +1. Check Event Viewer for errors: + ```powershell + Get-EventLog -LogName Application -Source PepApi -Newest 10 + ``` + +2. Verify configuration: + - Database connection string is correct + - Network paths are accessible + - Files exist at specified paths + +3. Test manually: + ```bash + cd C:\Services\PepApi + .\PepApi.Core.exe + ``` + +### Database Connection Errors + +- Verify SQL Server is accessible +- Check Windows Authentication permissions +- Test connection string with SSMS +- Ensure `TrustServerCertificate=True` in connection string + +### File Access Errors + +- Verify service account has read access to: + - Nest directory (`\\REMCOSRV0\PEP Nest`) + - Materials file (`C:\Pep\PEP2012\CONFIG\material.lfn`) +- Configure service to run as appropriate account + +### Port Already in Use + +```powershell +# Find what's using port 8085 +netstat -ano | findstr :8085 + +# Kill the process +Stop-Process -Id [PID] -Force + +# Or deploy on different port +Deploy-PepApi.ps1 -Urls "http://*:9000" +``` + +--- + +## Update/Redeploy + +To update the service with new code: + +```powershell +# Simply run the deploy script again +powershell -ExecutionPolicy Bypass -File Deploy-PepApi.ps1 -OpenFirewall +``` + +The script will: +- Stop the existing service +- Deploy the new version +- Restart the service + +**Note:** Your `appsettings.json` changes will be preserved. + +--- + +## API Endpoints + +Once deployed, the API will be available at: + +- **Swagger UI:** `http://localhost:8085/swagger` +- **OpenAPI Spec:** `http://localhost:8085/swagger/v1/swagger.json` + +### Endpoints +- `GET /nests/{year}` - Get nests for year +- `GET /nests/{nestName}?year=YYYY` - Get nest (year optional, finds most recent) +- `GET /nests/{nestName}/download?year=YYYY` - Download nest file +- `GET /nests/{nestName}/plates?year=YYYY` - Get plates +- `GET /materials` - Get all materials +- `GET /materials/{id}` - Get specific material + +--- + +## Security Notes + +⚠️ **Before production deployment:** + +1. Enable HTTPS (not configured by default) +2. Add authentication/authorization +3. Review CORS policy (currently allows all origins) +4. Update DotNetZip package (has security vulnerability) +5. Run service with least-privilege account + +--- + +## Performance Tips + +- Consider adding response caching +- Enable response compression in production +- Monitor database query performance +- Add connection pooling configuration if needed + +--- + +## Support + +For issues: +1. Check Event Viewer logs +2. Review `appsettings.json` configuration +3. Test database and file access manually +4. Review MIGRATION_SUMMARY.md for detailed changes diff --git a/Deploy-PepApi.ps1 b/Deploy-PepApi.ps1 new file mode 100644 index 0000000..1cce02c --- /dev/null +++ b/Deploy-PepApi.ps1 @@ -0,0 +1,188 @@ +<# +Deploy PepApi as a Windows Service + +Examples: + # Run from repository root: + powershell -ExecutionPolicy Bypass -File Deploy-PepApi.ps1 -ServiceName PepApi -InstallDir C:\Services\PepApi -Urls "http://*:8085" -OpenFirewall + + # Custom installation: + powershell -ExecutionPolicy Bypass -File Deploy-PepApi.ps1 -ServiceName PepApiService -InstallDir D:\MyServices\PepApi -Urls "http://*:8085" -OpenFirewall + +Requires: dotnet SDK/runtime installed and administrative privileges. +#> + +Param( + [string]$ServiceName = "PepApi", + [string]$PublishConfiguration = "Release", + [string]$InstallDir = "C:\Services\PepApi", + [string]$Urls = "http://*:8085", + [switch]$OpenFirewall, + [int]$PublishTimeoutSeconds = 180, + [int]$ServiceStopTimeoutSeconds = 30, + [int]$ServiceStartTimeoutSeconds = 30 +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +# Detect repository root +$ScriptDir = Split-Path -Parent $PSCommandPath +$RepoRoot = if ((Split-Path -Leaf $ScriptDir) -eq 'scripts') { + Split-Path -Parent $ScriptDir +} else { + $ScriptDir +} + +Write-Host "Repository root: $RepoRoot" +$ProjectPath = Join-Path $RepoRoot 'PepApi.Core\PepApi.Core.csproj' + +if (-not (Test-Path -LiteralPath $ProjectPath)) { + throw "Project not found at: $ProjectPath" +} + +function Ensure-Dir($path) { + if (-not (Test-Path -LiteralPath $path)) { + New-Item -ItemType Directory -Path $path | Out-Null + } +} + +function Stop-And-DeleteService($name) { + $svc = Get-Service -Name $name -ErrorAction SilentlyContinue + if ($null -ne $svc) { + if ($svc.Status -ne 'Stopped') { + Write-Host "Stopping service '$name'..." + Stop-Service -Name $name -Force -ErrorAction SilentlyContinue + try { $svc.WaitForStatus('Stopped',[TimeSpan]::FromSeconds($ServiceStopTimeoutSeconds)) | Out-Null } catch {} + # If still running, kill by PID + $q = & sc.exe queryex $name 2>$null + $pidLine = $q | Where-Object { $_ -match 'PID' } + if ($pidLine -and ($pidLine -match '(\d+)$')) { + $procId = [int]$Matches[1] + if ($procId -gt 0) { + try { Write-Host "Killing service process PID=$procId ..."; Stop-Process -Id $procId -Force } catch {} + } + } + } + Write-Host "Deleting service '$name'..." + sc.exe delete $name | Out-Null + Start-Sleep -Seconds 1 + } +} + +function Publish-App() { + Write-Host "Publishing PepApi.Core to $InstallDir ..." + Ensure-Dir $InstallDir + + # Run dotnet publish directly - output will be visible + & dotnet publish $ProjectPath -c $PublishConfiguration -o $InstallDir + + if ($LASTEXITCODE -ne 0) { + throw "dotnet publish failed with exit code $LASTEXITCODE" + } + + # Copy appsettings.json if it doesn't exist in install dir (preserve existing config) + $sourceSettings = Join-Path (Split-Path -Parent $ProjectPath) 'appsettings.json' + $targetSettings = Join-Path $InstallDir 'appsettings.json' + + Write-Host "" + Write-Host "============================================" + Write-Host "IMPORTANT: Configuration File" -ForegroundColor Yellow + Write-Host "============================================" + Write-Host "Please review and update the configuration file at:" -ForegroundColor Yellow + Write-Host " $targetSettings" -ForegroundColor Cyan + Write-Host "" + Write-Host "Update the following settings:" -ForegroundColor Yellow + Write-Host " - ConnectionStrings:PepDB (SQL Server connection)" -ForegroundColor White + Write-Host " - PepSettings:NestDirectory (network path to nests)" -ForegroundColor White + Write-Host " - PepSettings:MaterialsFile (path to material.lfn)" -ForegroundColor White + Write-Host "============================================" + Write-Host "" +} + +function Stop-ExeLocks($path) { + $procs = Get-Process -ErrorAction SilentlyContinue | Where-Object { + $_.Path -and ($_.Path -ieq $path) + } + foreach ($p in $procs) { + try { Write-Host "Killing process $($p.Id) $($p.ProcessName) ..."; Stop-Process -Id $p.Id -Force } catch {} + } + # Wait until unlocked + for ($i=0; $i -lt 50; $i++) { + $still = Get-Process -ErrorAction SilentlyContinue | Where-Object { $_.Path -and ($_.Path -ieq $path) } + if (-not $still) { break } + Start-Sleep -Milliseconds 200 + } +} + +function Create-Service($name, $bin, $urls) { + $binPath = '"' + $bin + '" --urls ' + $urls + Write-Host "Creating service '$name' with binPath: $binPath" + # Note: space after '=' is required for sc.exe syntax + sc.exe create $name binPath= "$binPath" start= auto DisplayName= "$name" | Out-Null + # Set recovery to restart on failure + sc.exe failure $name reset= 86400 actions= restart/60000/restart/60000/restart/60000 | Out-Null +} + +function Start-ServiceSafe($name) { + Write-Host "Starting service '$name'..." + Start-Service -Name $name + (Get-Service -Name $name).WaitForStatus('Running',[TimeSpan]::FromSeconds($ServiceStartTimeoutSeconds)) | Out-Null + sc.exe query $name | Write-Host +} + +if (-not (Get-Command dotnet -ErrorAction SilentlyContinue)) { + throw "dotnet SDK/Runtime not found in PATH. Please install .NET 8+ or add it to PATH." +} + +Write-Host "================================================" +Write-Host "PepApi Deployment Script" -ForegroundColor Green +Write-Host "================================================" +Write-Host "Service Name: $ServiceName" +Write-Host "Install Dir: $InstallDir" +Write-Host "URLs: $Urls" +Write-Host "Configuration: $PublishConfiguration" +Write-Host "Open Firewall: $OpenFirewall" +Write-Host "================================================" +Write-Host "" + +Stop-And-DeleteService -name $ServiceName +Stop-ExeLocks -path (Join-Path $InstallDir 'PepApi.Core.exe') +try { Remove-Item -LiteralPath (Join-Path $InstallDir 'PepApi.Core.exe') -Force -ErrorAction SilentlyContinue } catch {} +Publish-App + +$exe = Join-Path $InstallDir 'PepApi.Core.exe' +if (-not (Test-Path -LiteralPath $exe)) { + throw "Expected published executable not found: $exe" +} + +Create-Service -name $ServiceName -bin $exe -urls $Urls + +if ($OpenFirewall) { + $port = ($Urls -split ':')[-1] + if ($port -match '^(\d+)$') { + $ruleName = "$ServiceName HTTP $port" + $existingRule = Get-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue + if ($null -eq $existingRule) { + Write-Host "Creating firewall rule for TCP port $port ..." + New-NetFirewallRule -DisplayName $ruleName -Direction Inbound -Protocol TCP -LocalPort $port -Action Allow | Out-Null + } else { + Write-Host "Firewall rule '$ruleName' already exists, skipping creation." + } + } +} + +Start-ServiceSafe -name $ServiceName + +Write-Host "" +Write-Host "================================================" +Write-Host "Deployment Complete!" -ForegroundColor Green +Write-Host "================================================" +Write-Host "Service '$ServiceName' is running." +Write-Host "API available at: $Urls" +Write-Host "Swagger UI at: $($Urls -replace '\*', 'localhost')/swagger" +Write-Host "" +Write-Host "Next Steps:" -ForegroundColor Yellow +Write-Host " 1. Verify configuration in: $InstallDir\appsettings.json" +Write-Host " 2. Test API endpoint: $($Urls -replace '\*', 'localhost')/swagger" +Write-Host " 3. Check service status: Get-Service -Name $ServiceName" +Write-Host "================================================" diff --git a/PepApi.Core/Configuration/PepSettings.cs b/PepApi.Core/Configuration/PepSettings.cs new file mode 100644 index 0000000..c110328 --- /dev/null +++ b/PepApi.Core/Configuration/PepSettings.cs @@ -0,0 +1,7 @@ +namespace PepApi.Core.Configuration; + +public class PepSettings +{ + public string NestDirectory { get; set; } = string.Empty; + public string MaterialsFile { get; set; } = string.Empty; +} diff --git a/PepApi.Core/Controllers/MaterialsController.cs b/PepApi.Core/Controllers/MaterialsController.cs new file mode 100644 index 0000000..4d3f8ca --- /dev/null +++ b/PepApi.Core/Controllers/MaterialsController.cs @@ -0,0 +1,41 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; +using PepApi.Core.Configuration; +using PepLib.IO; + +namespace PepApi.Core.Controllers; + +[ApiController] +[Route("materials")] +public class MaterialsController : ControllerBase +{ + private readonly string _materialsFile; + + public MaterialsController(IOptions settings) + { + _materialsFile = settings.Value.MaterialsFile; + } + + [HttpGet] + public async Task>> GetMaterials() + { + var reader = new MaterialDataReader(); + await Task.Run(() => reader.Read(_materialsFile)); + + return Ok(reader.Materials); + } + + [HttpGet("{materialNo:int}")] + public async Task>> GetMaterial(int materialNo) + { + var reader = new MaterialDataReader(); + await Task.Run(() => reader.Read(_materialsFile)); + + var materials = reader.Materials.Where(m => m.Number == materialNo).ToList(); + + if (!materials.Any()) + return NotFound(new { message = $"Material {materialNo} not found" }); + + return Ok(materials); + } +} \ No newline at end of file diff --git a/PepApi.Core/Controllers/NestsController.cs b/PepApi.Core/Controllers/NestsController.cs new file mode 100644 index 0000000..0173fef --- /dev/null +++ b/PepApi.Core/Controllers/NestsController.cs @@ -0,0 +1,231 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; +using Microsoft.EntityFrameworkCore; +using PepApi.Core.Configuration; +using PepApi.Core.Models; +using PepLib; +using PepLib.Data; +using Plate = PepApi.Core.Models.Plate; + +namespace PepApi.Core.Controllers; + +[ApiController] +[Route("nests")] +public class NestsController : ControllerBase +{ + private readonly PepDB _db; + private readonly string _nestDirectory; + + public NestsController(PepDB db, IOptions settings) + { + _db = db; + _nestDirectory = settings.Value.NestDirectory; + } + + [HttpGet("{year:int:regex(^\\d{{4}}$)}")] + public async Task>> GetNests(int year, [FromQuery] NestFilterData? filter) + { + if (year == 0) + year = DateTime.Now.Year; + + var dir = Path.Combine(_nestDirectory, year.ToString()); + + if (!Directory.Exists(dir)) + { + return Ok(new List()); + } + + var nestHeaders = await _db.NestHeaders + .Where(n => n.DateProgrammed != null && n.DateProgrammed.Value.Year == year) + .ToListAsync(); + + var nestSummaries = nestHeaders.Select(ConvertFrom).ToList(); + + if (filter != null) + { + return Ok(filter.Apply(nestSummaries.AsQueryable()).ToList()); + } + + return Ok(nestSummaries); + } + + private static NestSummary ConvertFrom(NestHeader nestHeader) + { + return new NestSummary + { + Comments = nestHeader.Comments, + Customer = nestHeader.CustomerName, + DateCreated = nestHeader.DateProgrammed!.Value, + DateLastModified = nestHeader.ModifiedDate!.Value, + HasErrors = !nestHeader.Errors.IsNullOrWhiteSpace(), + MaterialGrade = nestHeader.MatGrade, + MaterialNumber = int.Parse(nestHeader.Material ?? "0"), + Name = nestHeader.NestName, + Notes = nestHeader.Remarks, + ProgrammedBy = nestHeader.Programmer, + Revision = nestHeader.UserDefined1, + Status = PepHelper.GetStatus(nestHeader.Status), + Application = PepHelper.GetApplication(nestHeader.Application) + }; + } + + // Get nest by name - finds most recent if year not specified + [HttpGet("{nestName}")] + public async Task> GetNestInfo(string nestName, [FromQuery] int? year = null) + { + var nestFile = year.HasValue + ? GetNestPath(nestName, year.Value) + : await FindMostRecentNestAsync(nestName); + + if (nestFile == null || !System.IO.File.Exists(nestFile)) + return NotFound(new { message = "Nest not found" }); + + var details = await GetNestDetailsAsync(nestFile); + return Ok(details); + } + + // Get nest by year and name (backward compatibility) + [HttpGet("{year:int:regex(^\\d{{4}}$)}/{nestName}")] + public async Task> GetNestInfoByYear(int year, string nestName) + { + var nestFile = GetNestPath(nestName, year); + + if (!System.IO.File.Exists(nestFile)) + return NotFound(new { message = "Nest not found" }); + + var details = await GetNestDetailsAsync(nestFile); + return Ok(details); + } + + // Download nest file - finds most recent if year not specified + [HttpGet("{nestName}/download")] + public async Task DownloadFile(string nestName, [FromQuery] int? year = null) + { + var filePath = year.HasValue + ? GetNestPath(nestName, year.Value) + : await FindMostRecentNestAsync(nestName); + + if (filePath == null || !System.IO.File.Exists(filePath)) + return NotFound(new { message = "Nest not found" }); + + var bytes = System.IO.File.ReadAllBytes(filePath); + var fileName = Path.GetFileName(filePath); + var mimeType = "application/octet-stream"; + + return File(bytes, mimeType, fileName); + } + + // Download nest file by year (backward compatibility) + [HttpGet("{year:int:regex(^\\d{{4}}$)}/{nestName}/download")] + public IActionResult DownloadFileByYear(int year, string nestName) + { + var filePath = GetNestPath(nestName, year); + + if (!System.IO.File.Exists(filePath)) + return NotFound(new { message = "Nest not found" }); + + var bytes = System.IO.File.ReadAllBytes(filePath); + var fileName = Path.GetFileName(filePath); + var mimeType = "application/octet-stream"; + + return File(bytes, mimeType, fileName); + } + + // Get plates - finds most recent if year not specified + [HttpGet("{nestName}/plates")] + public async Task>> GetPlates(string nestName, [FromQuery] int? year = null) + { + var nestFile = year.HasValue + ? GetNestPath(nestName, year.Value) + : await FindMostRecentNestAsync(nestName); + + if (nestFile == null || !System.IO.File.Exists(nestFile)) + return NotFound(new { message = "Nest not found" }); + + var nest = Nest.Load(nestFile); + var plates = PepHelper.GetPlates(nest); + var combined = PepHelper.CombineLikePlates(plates); + + return Ok(combined); + } + + // Get plates by year (backward compatibility) + [HttpGet("{year:int:regex(^\\d{{4}}$)}/{nestName}/plates")] + public ActionResult> GetPlatesByYear(int year, string nestName) + { + var nestFile = GetNestPath(nestName, year); + + if (!System.IO.File.Exists(nestFile)) + return NotFound(new { message = "Nest not found" }); + + var nest = Nest.Load(nestFile); + var plates = PepHelper.GetPlates(nest); + var combined = PepHelper.CombineLikePlates(plates); + + return Ok(combined); + } + + private async Task FindMostRecentNestAsync(string nestName) + { + // Query database for most recent nest by name + var mostRecent = await _db.NestHeaders + .Where(n => n.NestName.ToUpper() == nestName.ToUpper() && n.DateProgrammed != null) + .OrderByDescending(n => n.DateProgrammed) + .FirstOrDefaultAsync(); + + if (mostRecent == null) + return null; + + var year = mostRecent.DateProgrammed!.Value.Year; + return GetNestPath(nestName, year); + } + + private string GetNestPath(string nestName, int year) + { + var fileName = nestName + ".zip"; + var filePath = Path.Combine(_nestDirectory, year.ToString(), fileName); + + if (System.IO.File.Exists(filePath)) + return filePath; + + fileName = nestName + ".pep"; + filePath = Path.Combine(_nestDirectory, year.ToString(), fileName); + + return filePath; + } + + private async Task GetNestDetailsAsync(string nestFilePath) + { + var nest = Nest.Load(nestFilePath); + var dir = Path.GetDirectoryName(nestFilePath) + "\\"; + var name = Path.GetFileNameWithoutExtension(nestFilePath).ToUpper(); + + var info = await _db.NestHeaders + .FirstOrDefaultAsync(n => n.NestName.ToUpper() == name && dir == n.Path); + + if (info == null) + throw new Exception("Nest header not found in database"); + + var details = new NestDetails + { + Name = info.NestName, + DateCreated = info.DateProgrammed!.Value, + Status = PepHelper.GetStatus(info.Status), + DateLastModified = info.ModifiedDate!.Value, + Material = PepHelper.GetMaterial(nest), + Plates = PepHelper.GetPlates(nest), + Parts = PepHelper.GetParts(nest), + NumberOfTestSquares = PepHelper.GetTestSquareCount(nest), + AreTestSquaresOutOfSequence = !PepHelper.AreTestSquaresInCorrectOrder(nest), + Customer = info.CustomerName, + Comments = info.Comments, + HasErrors = !info.Errors.IsNullOrWhiteSpace(), + Notes = info.Remarks, + ProgrammedBy = info.Programmer, + Revision = info.UserDefined1, + Application = PepHelper.GetApplication(info.Application) + }; + + return details; + } +} diff --git a/PepApi.Core/Extensions.cs b/PepApi.Core/Extensions.cs new file mode 100644 index 0000000..dc33541 --- /dev/null +++ b/PepApi.Core/Extensions.cs @@ -0,0 +1,67 @@ +using PepApi.Core.Models; +using PepLib; + +namespace PepApi.Core +{ + public static class Extensions + { + public static bool IsNullOrWhiteSpace(this string s) + { + return string.IsNullOrWhiteSpace(s); + } + + public static bool IsNullOrEmpty(this string s) + { + return string.IsNullOrEmpty(s); + } + + public static bool IsNaN(this double d) + { + return double.IsNaN(d); + } + + public static bool IsInfinity(this double d) + { + return double.IsInfinity(d); + } + + public static bool IsNegativeInfinity(this double d) + { + return double.IsNegativeInfinity(d); + } + + public static bool IsPositiveInfinity(this double d) + { + return double.IsPositiveInfinity(d); + } + + public static bool IsEven(this int i) + { + return i % 2 == 0; + } + + public static bool IsOdd(this int i) + { + return i % 2 != 0; + } + + public static NestSummary ConvertToNestSummary(this NestInfo nest) + { + return new NestSummary + { + Name = nest.Name, + DateCreated = nest.DateCreated, + DateLastModified = nest.DateLastModified, + Status = nest.Status.ToString(), + Comments = nest.Comments, + Customer = nest.Customer, + ProgrammedBy = nest.ProgrammedBy, + MaterialNumber = nest.MaterialNumber, + MaterialGrade = nest.MaterialGrade, + Notes = nest.Notes, + HasErrors = !nest.Errors.IsNullOrWhiteSpace(), + Revision = nest.UserDefined1 + }; + } + } +} diff --git a/PepApi.Core/Models/Material.cs b/PepApi.Core/Models/Material.cs new file mode 100644 index 0000000..ef573f7 --- /dev/null +++ b/PepApi.Core/Models/Material.cs @@ -0,0 +1,13 @@ +namespace PepApi.Core.Models +{ + public class Material + { + public int Number { get; set; } + + public string Grade { get; set; } + + public double Thickness { get; set; } + + public string Description { get; set; } + } +} diff --git a/PepApi.Core/Models/NestDetails.cs b/PepApi.Core/Models/NestDetails.cs new file mode 100644 index 0000000..8a7a03e --- /dev/null +++ b/PepApi.Core/Models/NestDetails.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; + +namespace PepApi.Core.Models +{ + public class NestDetails + { + public NestDetails() + { + Parts = new List(); + Plates = new List(); + } + + public string Name { get; set; } + + public DateTime DateCreated { get; set; } + + public DateTime DateLastModified { get; set; } + + public string Status { get; set; } + + public string Comments { get; set; } + + public string Customer { get; set; } + + public string ProgrammedBy { get; set; } + + public Material Material { get; set; } + + public string Notes { get; set; } + + public bool HasErrors { get; set; } + + public string Revision { get; set; } + + public string Application { get; set; } + + public int NumberOfTestSquares { get; set; } + + public bool AreTestSquaresOutOfSequence { get; set; } + + public List Parts { get; set; } + + public List Plates { get; set; } + } +} diff --git a/PepApi.Core/Models/NestFilterData.cs b/PepApi.Core/Models/NestFilterData.cs new file mode 100644 index 0000000..f0e1f37 --- /dev/null +++ b/PepApi.Core/Models/NestFilterData.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using PepLib; + +namespace PepApi.Core.Models +{ + public class NestFilterData + { + public string Name { get; set; } + + public string Customer { get; set; } + + public string Comments {get;set;} + + public DateTime? StartDate { get; set; } + + public DateTime? EndDate { get; set; } + + public int? MaterialNumber { get; set; } + + public string MaterialGrade { get; set; } + + public string ProgrammedBy { get; set; } + + public string Notes { get; set; } + + public string Status { get; set; } + + public string Application { get; set; } + + public IQueryable Apply(IQueryable nests) + { + if (this.Name != null) + { + var x = this.Name.ToUpper(); + nests = nests.Where(n => n.Name.ToUpper().Contains(x)); + } + + if (this.Customer != null) + { + var x = this.Customer.ToUpper(); + nests = nests.Where(n => n.Customer.ToUpper().Contains(x)); + } + + if (this.Comments != null) + { + var x = this.Comments.ToUpper(); + nests = nests.Where(n => n.Comments.ToUpper().Contains(x)); + } + + if (this.MaterialNumber != null) + { + nests = nests.Where(n => n.MaterialNumber == this.MaterialNumber); + } + + if (this.MaterialGrade != null) + { + var x = this.MaterialGrade.ToUpper(); + nests = nests.Where(n => n.MaterialGrade.ToUpper().Contains(x)); + } + + if (this.ProgrammedBy != null) + { + var x = this.ProgrammedBy.ToUpper(); + nests = nests.Where(n => n.ProgrammedBy.ToUpper().Contains(x)); + } + + if (this.Notes != null) + { + var x = this.Notes.ToUpper(); + nests = nests.Where(n => n.Notes.ToUpper().Contains(x)); + } + + if (this.StartDate != null) + { + nests = nests.Where(n => n.DateCreated >= this.StartDate); + } + + if (this.EndDate != null) + { + nests = nests.Where(n => n.DateCreated <= this.EndDate); + } + + if (this.Status != null) + { + var x = this.Status.ToUpper(); + nests = nests.Where(n => n.Status.ToUpper() == x); + } + + if (this.Application != null) + { + var x = this.Application.ToUpper(); + nests = nests.Where(n => n.Application.ToUpper() == x); + } + + return nests; + } + } +} diff --git a/PepApi.Core/Models/NestSummary.cs b/PepApi.Core/Models/NestSummary.cs new file mode 100644 index 0000000..9b2b155 --- /dev/null +++ b/PepApi.Core/Models/NestSummary.cs @@ -0,0 +1,35 @@ +using PepLib; +using System; + +namespace PepApi.Core.Models +{ + public class NestSummary + { + public string Name { get; set; } + + public DateTime DateCreated { get; set; } + + public DateTime DateLastModified { get; set; } + + public string Status { get; set; } + + public string Comments { get; set; } + + public string Customer { get; set; } + + public string ProgrammedBy { get; set; } + + public int MaterialNumber { get; set; } + + public string MaterialGrade { get; set; } + + public string Notes { get; set; } + + public bool HasErrors { get; set; } + + public string Revision { get; set; } + + public string Application { get; set; } + + } +} diff --git a/PepApi.Core/Models/Part.cs b/PepApi.Core/Models/Part.cs new file mode 100644 index 0000000..fbfab0a --- /dev/null +++ b/PepApi.Core/Models/Part.cs @@ -0,0 +1,10 @@ +namespace PepApi.Core.Models +{ + public class Part + { + public string Name { get; set; } + public int QtyNested { get; set; } + public int QtyRequired { get; set; } + public int[] NestedOn { get; set; } + } +} diff --git a/PepApi.Core/Models/Plate.cs b/PepApi.Core/Models/Plate.cs new file mode 100644 index 0000000..b201f40 --- /dev/null +++ b/PepApi.Core/Models/Plate.cs @@ -0,0 +1,20 @@ +namespace PepApi.Core.Models +{ + public class Plate + { + public string Name { get; set; } + + public double Thickness { get; set; } + + public Material Material { get; set; } + + public Size Size { get; set; } + + public int Qty { get; set; } + + public override string ToString() + { + return $"{Material.Thickness}, {Material.Grade} {Material.Description}, {Size.ToString()}"; + } + } +} diff --git a/PepApi.Core/Models/Size.cs b/PepApi.Core/Models/Size.cs new file mode 100644 index 0000000..bad2741 --- /dev/null +++ b/PepApi.Core/Models/Size.cs @@ -0,0 +1,14 @@ +namespace PepApi.Core.Models +{ + public class Size + { + public double Width { get; set; } + + public double Length { get; set; } + + public override string ToString() + { + return $"{Width} x {Length}"; + } + } +} diff --git a/PepApi.Core/PepApi.Core.csproj b/PepApi.Core/PepApi.Core.csproj new file mode 100644 index 0000000..7cc1e4a --- /dev/null +++ b/PepApi.Core/PepApi.Core.csproj @@ -0,0 +1,18 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + diff --git a/PepApi.Core/PepApi.Core.http b/PepApi.Core/PepApi.Core.http new file mode 100644 index 0000000..12d8f23 --- /dev/null +++ b/PepApi.Core/PepApi.Core.http @@ -0,0 +1,6 @@ +@PepApi.Core_HostAddress = http://localhost:5151 + +GET {{PepApi.Core_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/PepApi.Core/PepHelper.cs b/PepApi.Core/PepHelper.cs new file mode 100644 index 0000000..ae9a2ad --- /dev/null +++ b/PepApi.Core/PepHelper.cs @@ -0,0 +1,234 @@ +using System.Collections.Generic; +using System.Linq; +using PepLib; +using Material = PepApi.Core.Models.Material; +using Part = PepApi.Core.Models.Part; +using Plate = PepApi.Core.Models.Plate; +using Size = PepApi.Core.Models.Size; + +namespace PepApi.Core +{ + internal static class PepHelper + { + public static bool IsTestSquare(PepLib.Part part) + { + if (part == null || part.DrawingName == null) + return false; + + var drawingName = part.DrawingName.ToUpper(); + + if (drawingName.Contains("TEST SQUARE")) + { + return true; + } + + return false; + } + + public static int GetTestSquareCount(Nest nest) + { + var count = 0; + + foreach (var plate in nest.Plates) + { + foreach (var part in plate.Parts) + { + if (IsTestSquare(part)) + count++; + } + } + + return count; + } + + public static bool AreTestSquaresInCorrectOrder(PepLib.Plate plate) + { + var partsStarted = false; + + foreach (var part in plate.Parts) + { + var isTestSquare = IsTestSquare(part); + + if (isTestSquare) + { + if (partsStarted) + { + // test square found after a part + // test squares must be cut first + return false; + } + } + else + { + if (part.IsDisplayOnly) + continue; + + partsStarted = true; + } + } + + return true; + } + + public static bool AreTestSquaresInCorrectOrder(PepLib.Nest nest) + { + foreach (var plate in nest.Plates) + { + if (!AreTestSquaresInCorrectOrder(plate)) + { + return false; + } + } + + return true; + } + + public static string GetStatus(int statusNum) + { + switch (statusNum) + { + case -1: return "Deleted"; + case 0: return "To be cut"; + case 1: return "Quote"; + case 2: return "Has been cut"; + case 3: return "Hold"; + case 4: return "Quote, accepted, to be cut"; + case 5: return "Quote, accepted, has been cut"; + case 6: return "Quote, accepted, hold"; + case 7: return "Quote, accepted, moved to wip"; + case 8: return "Quote, rejected"; + case 9: return "Quote, timed out"; + default: return "N/A"; + } + } + + public static string GetApplication(int applicationID) + { + switch (applicationID) + { + case 1: return "Laser"; + case 2: return "Plasma"; + default: return "N/A"; + } + } + + public static void UpdateQtyNested(Part part, Nest nest) + { + part.QtyNested = 0; + + var nestedOn = new List(); + var partName = part.Name.ToUpper(); + + for (int i = 0; i < nest.Plates.Count; i++) + { + var plate = nest.Plates[i]; + var qtyNested = 0; + + qtyNested = plate.Parts.Count(p => p.DrawingName?.ToUpper() == partName); + + // plate.Duplicates is actually the quantity, so 1 Duplicate = 1 plate + qtyNested *= plate.Duplicates; + + if (qtyNested > 0) + { + part.QtyNested += qtyNested; + nestedOn.Add(i); + } + } + + part.NestedOn = nestedOn.ToArray(); + } + + public static List GetParts(Nest nest) + { + var parts = new List(); + + for (int i = 0; i < nest.Drawings.Count; i++) + { + var drawing = nest.Drawings[i]; + + if (drawing.Name.IsNullOrWhiteSpace()) + continue; + + var dwg = nest.Drawings.FirstOrDefault(d => d.Name == drawing.Name); + + var part = new Part + { + Name = drawing.Name, + QtyRequired = dwg.QtyRequired + }; + + UpdateQtyNested(part, nest); + + parts.Add(part); + } + + return parts; + } + + public static List GetPlates(Nest nest) + { + var plates = new List(); + + for (int i = 0; i < nest.Plates.Count; i++) + { + var plate = nest.Plates[i]; + + var p = new Plate + { + Name = plate.Name, + Material = GetMaterial(plate), + Qty = plate.Duplicates, + Thickness = plate.Thickness, + Size = new Size + { + Width = plate.Size.Height, + Length = plate.Size.Width + } + }; + + plates.Add(p); + } + + return plates; + } + + public static List CombineLikePlates(IEnumerable plates) + { + var combinedPlatesList = new List(); + var uniquePlates = plates.GroupBy(p => p.ToString()); + + foreach (var item in uniquePlates) + { + var plate = item.First(); + plate.Name = null; + plate.Qty = item.Sum((Plate p) => p.Qty); + + combinedPlatesList.Add(plate); + } + + return combinedPlatesList + .OrderBy(p => p.Material.Number) + .ThenBy(p => p.Material.Grade) + .ThenBy(p => p.Size.Width) + .ThenBy(p => p.Size.Length) + .ToList(); + } + + public static Material GetMaterial(PepLib.Plate plate) + { + return new Material + { + Number = plate.Material.Id, + Grade = plate.Material.Grade, + Thickness = plate.Thickness, + Description = plate.Description + }; + } + + public static Material GetMaterial(PepLib.Nest nest) + { + return GetMaterial(nest.Plates.First()); + } + } +} diff --git a/PepApi.Core/Program.cs b/PepApi.Core/Program.cs new file mode 100644 index 0000000..8768dcf --- /dev/null +++ b/PepApi.Core/Program.cs @@ -0,0 +1,61 @@ +using Microsoft.EntityFrameworkCore; +using PepApi.Core.Configuration; +using PepLib.Data; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container +builder.Services.AddControllers() + .AddJsonOptions(options => + { + options.JsonSerializerOptions.PropertyNamingPolicy = null; // Keep PascalCase for backward compatibility + }); + +// Configure PepSettings from appsettings.json +builder.Services.Configure( + builder.Configuration.GetSection("PepSettings")); + +// Configure Entity Framework Core with SQL Server (read-only with connection pooling) +builder.Services.AddDbContextPool(options => +{ + options.UseSqlServer(builder.Configuration.GetConnectionString("PepDB")); + options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); // Read-only optimization +}); + +// Add Swagger/OpenAPI +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(options => +{ + options.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo + { + Title = "PepApi", + Version = "v1", + Description = "PEP Nesting API - Modern ASP.NET Core version" + }); +}); + +// Add CORS if needed +builder.Services.AddCors(options => +{ + options.AddDefaultPolicy(policy => + { + policy.AllowAnyOrigin() + .AllowAnyMethod() + .AllowAnyHeader(); + }); +}); + +var app = builder.Build(); + +// Configure the HTTP request pipeline +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseCors(); + +app.MapControllers(); + +app.Run(); diff --git a/PepApi.Core/Properties/launchSettings.json b/PepApi.Core/Properties/launchSettings.json new file mode 100644 index 0000000..0b67f30 --- /dev/null +++ b/PepApi.Core/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:53377", + "sslPort": 44393 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5151", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7066;http://localhost:5151", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/PepApi.Core/appsettings.Development.json b/PepApi.Core/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/PepApi.Core/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/PepApi.Core/appsettings.json b/PepApi.Core/appsettings.json new file mode 100644 index 0000000..1361731 --- /dev/null +++ b/PepApi.Core/appsettings.json @@ -0,0 +1,23 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "PepDB": "data source=REMCOSRV0\\RWS64;initial catalog=PEP;integrated security=True;MultipleActiveResultSets=True;TrustServerCertificate=True" + }, + "PepSettings": { + "NestDirectory": "\\\\REMCOSRV0\\PEP Nest", + "MaterialsFile": "C:\\Pep\\PEP2012\\CONFIG\\material.lfn" + }, + "Kestrel": { + "Endpoints": { + "Http": { + "Url": "http://*:8085" + } + } + } +} diff --git a/PepApi.sln b/PepApi.sln new file mode 100644 index 0000000..1beb7d2 --- /dev/null +++ b/PepApi.sln @@ -0,0 +1,51 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36518.9 d17.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepApi.Core", "PepApi.Core\PepApi.Core.csproj", "{6761DEB6-14DD-4930-B1BB-BEC16A278E73}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepLib.Core", "PepLib.Core\PepLib.Core.csproj", "{3E03F837-2CD5-4681-BED9-7179DF766DF6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6761DEB6-14DD-4930-B1BB-BEC16A278E73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6761DEB6-14DD-4930-B1BB-BEC16A278E73}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6761DEB6-14DD-4930-B1BB-BEC16A278E73}.Debug|x64.ActiveCfg = Debug|Any CPU + {6761DEB6-14DD-4930-B1BB-BEC16A278E73}.Debug|x64.Build.0 = Debug|Any CPU + {6761DEB6-14DD-4930-B1BB-BEC16A278E73}.Debug|x86.ActiveCfg = Debug|Any CPU + {6761DEB6-14DD-4930-B1BB-BEC16A278E73}.Debug|x86.Build.0 = Debug|Any CPU + {6761DEB6-14DD-4930-B1BB-BEC16A278E73}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6761DEB6-14DD-4930-B1BB-BEC16A278E73}.Release|Any CPU.Build.0 = Release|Any CPU + {6761DEB6-14DD-4930-B1BB-BEC16A278E73}.Release|x64.ActiveCfg = Release|Any CPU + {6761DEB6-14DD-4930-B1BB-BEC16A278E73}.Release|x64.Build.0 = Release|Any CPU + {6761DEB6-14DD-4930-B1BB-BEC16A278E73}.Release|x86.ActiveCfg = Release|Any CPU + {6761DEB6-14DD-4930-B1BB-BEC16A278E73}.Release|x86.Build.0 = Release|Any CPU + {3E03F837-2CD5-4681-BED9-7179DF766DF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E03F837-2CD5-4681-BED9-7179DF766DF6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E03F837-2CD5-4681-BED9-7179DF766DF6}.Debug|x64.ActiveCfg = Debug|Any CPU + {3E03F837-2CD5-4681-BED9-7179DF766DF6}.Debug|x64.Build.0 = Debug|Any CPU + {3E03F837-2CD5-4681-BED9-7179DF766DF6}.Debug|x86.ActiveCfg = Debug|Any CPU + {3E03F837-2CD5-4681-BED9-7179DF766DF6}.Debug|x86.Build.0 = Debug|Any CPU + {3E03F837-2CD5-4681-BED9-7179DF766DF6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E03F837-2CD5-4681-BED9-7179DF766DF6}.Release|Any CPU.Build.0 = Release|Any CPU + {3E03F837-2CD5-4681-BED9-7179DF766DF6}.Release|x64.ActiveCfg = Release|Any CPU + {3E03F837-2CD5-4681-BED9-7179DF766DF6}.Release|x64.Build.0 = Release|Any CPU + {3E03F837-2CD5-4681-BED9-7179DF766DF6}.Release|x86.ActiveCfg = Release|Any CPU + {3E03F837-2CD5-4681-BED9-7179DF766DF6}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {39F3E901-33B2-4CAB-8BEB-5FDDB48998F9} + EndGlobalSection +EndGlobal diff --git a/PepLib.Core/AngleConverter.cs b/PepLib.Core/AngleConverter.cs new file mode 100644 index 0000000..50c30dd --- /dev/null +++ b/PepLib.Core/AngleConverter.cs @@ -0,0 +1,17 @@ +using System; + +namespace PepLib +{ + public static class AngleConverter + { + public static double ToDegrees(double radians) + { + return 180.0 / Math.PI * radians; + } + + public static double ToRadians(double degrees) + { + return Math.PI / 180.0 * degrees; + } + } +} diff --git a/PepLib.Core/ApplicationType.cs b/PepLib.Core/ApplicationType.cs new file mode 100644 index 0000000..98bb2ac --- /dev/null +++ b/PepLib.Core/ApplicationType.cs @@ -0,0 +1,15 @@ + +namespace PepLib +{ + public enum ApplicationType + { + None = 0x0, + Laser = 0x1, + Flame = 0x2, + Punch = 0x3, + PlasmaPunch = 0x4, + Waterjet = 0x6, + LaserPunch = 0x7, + FlamePlasma = 0x9 + } +} diff --git a/PepLib.Core/Box.cs b/PepLib.Core/Box.cs new file mode 100644 index 0000000..e42fc73 --- /dev/null +++ b/PepLib.Core/Box.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PepLib +{ + public class Box + { + public Box() + : this(0, 0, 0, 0) + { + } + + public Box(double x, double y, double w, double h) + { + Location = new Vector(x, y); + Size = new PepLib.Size(0, 0); + Width = w; + Height = h; + } + + public Vector Location; + + public Vector Center + { + get { return new Vector(X + Width * 0.5, Y + Height * 0.5); } + } + + public Size Size; + + public double X + { + get { return Location.X; } + set { Location.X = value; } + } + + public double Y + { + get { return Location.Y; } + set { Location.Y = value; } + } + + public double Width + { + get { return Size.Width; } + set { Size.Width = value; } + } + + public double Height + { + get { return Size.Height; } + set { Size.Height = value; } + } + + public void MoveTo(double x, double y) + { + X = x; + Y = y; + } + + public void MoveTo(Vector pt) + { + X = pt.X; + Y = pt.Y; + } + + public void Offset(double x, double y) + { + X += x; + Y += y; + } + + public void Offset(Vector voffset) + { + Location += voffset; + } + + public double Left + { + get { return X; } + } + + public double Right + { + get { return X + Width; } + } + + public double Top + { + get { return Y + Height; } + } + + public double Bottom + { + get { return Y; } + } + + public double Area() + { + return Width * Height; + } + + public double Perimeter() + { + return Width * 2 + Height * 2; + } + + public bool IsIntersecting(Box box) + { + if (Left >= box.Right) + return false; + + if (Right >= box.Left) + return false; + + if (Top <= box.Bottom) + return false; + + if (Bottom <= box.Top) + return false; + + return true; + } + + public bool Contains(Box box) + { + if (box.Left < Left) + return false; + + if (box.Right > Right) + return false; + + if (box.Bottom < Bottom) + return false; + + if (box.Top > Top) + return false; + + return true; + } + + public bool Contains(Vector pt) + { + return pt.X >= Left && pt.X <= Right + && pt.Y >= Bottom && pt.Y <= Top; + } + + public override string ToString() + { + return string.Format("[Box: X={0}, Y={1}, Width={2}, Height={3}]", X, Y, Width, Height); + } + } +} diff --git a/PepLib.Core/Codes/CircularMove.cs b/PepLib.Core/Codes/CircularMove.cs new file mode 100644 index 0000000..7032fa5 --- /dev/null +++ b/PepLib.Core/Codes/CircularMove.cs @@ -0,0 +1,74 @@ + +namespace PepLib.Codes +{ + public class CircularMove : Motion + { + public CircularMove() + { + } + + public CircularMove(Vector endPoint, Vector centerPoint, RotationType rotation = RotationType.CCW) + { + EndPoint = endPoint; + CenterPoint = centerPoint; + Rotation = rotation; + } + + public CircularMove(double x, double y, double i, double j, RotationType rotation = RotationType.CCW) + { + EndPoint = new Vector(x, y); + CenterPoint = new Vector(i, j); + Rotation = rotation; + } + + public RotationType Rotation { get; set; } + + public EntityType Type { get; set; } + + public Vector CenterPoint { get; set; } + + public override void Rotate(double angle) + { + base.Rotate(angle); + CenterPoint = CenterPoint.Rotate(angle); + } + + public override void Rotate(double angle, Vector origin) + { + base.Rotate(angle, origin); + CenterPoint = CenterPoint.Rotate(angle, origin); + } + + public override void Offset(double x, double y) + { + base.Offset(x, y); + CenterPoint = new Vector(CenterPoint.X + x, CenterPoint.Y + y); + } + + public override void Offset(Vector voffset) + { + base.Offset(voffset); + CenterPoint += voffset; + } + + public override CodeType CodeType() + { + return Codes.CodeType.CircularMove; + } + + public override ICode Clone() + { + return new CircularMove(EndPoint, CenterPoint, Rotation) + { + Type = Type + }; + } + + public override string ToString() + { + return Rotation == RotationType.CW ? + string.Format("G02 X{0} Y{1} I{2} J{3}", EndPoint.X, EndPoint.Y, CenterPoint.X, CenterPoint.Y) : + string.Format("G03 X{0} Y{1} I{2} J{3}", EndPoint.X, EndPoint.Y, CenterPoint.X, CenterPoint.Y); + } + } +} diff --git a/PepLib.Core/Codes/CodeType.cs b/PepLib.Core/Codes/CodeType.cs new file mode 100644 index 0000000..a1c3136 --- /dev/null +++ b/PepLib.Core/Codes/CodeType.cs @@ -0,0 +1,14 @@ + +namespace PepLib.Codes +{ + public enum CodeType + { + CircularMove, + Comment, + LinearMove, + RapidMove, + SetFeedrate, + SetKerf, + SubProgramCall + } +} diff --git a/PepLib.Core/Codes/Comment.cs b/PepLib.Core/Codes/Comment.cs new file mode 100644 index 0000000..434a23b --- /dev/null +++ b/PepLib.Core/Codes/Comment.cs @@ -0,0 +1,32 @@ + +namespace PepLib.Codes +{ + public class Comment : ICode + { + public Comment() + { + } + + public Comment(string value) + { + Value = value; + } + + public string Value { get; set; } + + public CodeType CodeType() + { + return Codes.CodeType.Comment; + } + + public ICode Clone() + { + return new Comment(Value); + } + + public override string ToString() + { + return ':' + Value; + } + } +} diff --git a/PepLib.Core/Codes/EntityType.cs b/PepLib.Core/Codes/EntityType.cs new file mode 100644 index 0000000..8c93f03 --- /dev/null +++ b/PepLib.Core/Codes/EntityType.cs @@ -0,0 +1,13 @@ +namespace PepLib.Codes +{ + public enum EntityType + { + Display, + Scribe, + Cut, + InternalLeadin, + InternalLeadout, + ExternalLeadin, + ExternalLeadout + } +} diff --git a/PepLib.Core/Codes/ICode.cs b/PepLib.Core/Codes/ICode.cs new file mode 100644 index 0000000..ed78d86 --- /dev/null +++ b/PepLib.Core/Codes/ICode.cs @@ -0,0 +1,8 @@ +namespace PepLib.Codes +{ + public interface ICode + { + CodeType CodeType(); + ICode Clone(); + } +} diff --git a/PepLib.Core/Codes/LinearMove.cs b/PepLib.Core/Codes/LinearMove.cs new file mode 100644 index 0000000..f4c92a8 --- /dev/null +++ b/PepLib.Core/Codes/LinearMove.cs @@ -0,0 +1,42 @@ + +namespace PepLib.Codes +{ + public class LinearMove : Motion + { + public LinearMove() + : this(new Vector()) + { + } + + public LinearMove(double x, double y) + : this(new Vector(x, y)) + { + } + + public LinearMove(Vector endPoint) + { + EndPoint = endPoint; + Type = EntityType.Cut; + } + + public EntityType Type { get; set; } + + public override CodeType CodeType() + { + return Codes.CodeType.LinearMove; + } + + public override ICode Clone() + { + return new LinearMove(EndPoint) + { + Type = Type + }; + } + + public override string ToString() + { + return string.Format("G01 X{0} Y{1}", EndPoint.X, EndPoint.Y); + } + } +} diff --git a/PepLib.Core/Codes/Motion.cs b/PepLib.Core/Codes/Motion.cs new file mode 100644 index 0000000..220418d --- /dev/null +++ b/PepLib.Core/Codes/Motion.cs @@ -0,0 +1,32 @@ + +namespace PepLib.Codes +{ + public abstract class Motion : IMovable, ICode + { + public Vector EndPoint { get; set; } + + public virtual void Rotate(double angle) + { + EndPoint = EndPoint.Rotate(angle); + } + + public virtual void Rotate(double angle, Vector origin) + { + EndPoint = EndPoint.Rotate(angle, origin); + } + + public virtual void Offset(double x, double y) + { + EndPoint = new Vector(EndPoint.X + x, EndPoint.Y + y); + } + + public virtual void Offset(Vector voffset) + { + EndPoint += voffset; + } + + public abstract CodeType CodeType(); + + public abstract ICode Clone(); + } +} diff --git a/PepLib.Core/Codes/RapidMove.cs b/PepLib.Core/Codes/RapidMove.cs new file mode 100644 index 0000000..746e99c --- /dev/null +++ b/PepLib.Core/Codes/RapidMove.cs @@ -0,0 +1,34 @@ +namespace PepLib.Codes +{ + public class RapidMove : Motion + { + public RapidMove() + { + } + + public RapidMove(Vector endPoint) + { + EndPoint = endPoint; + } + + public RapidMove(double x, double y) + { + EndPoint = new Vector(x, y); + } + + public override CodeType CodeType() + { + return Codes.CodeType.RapidMove; + } + + public override ICode Clone() + { + return new RapidMove(EndPoint); + } + + public override string ToString() + { + return string.Format("G00 X{0} Y{1}", EndPoint.X, EndPoint.Y); + } + } +} diff --git a/PepLib.Core/Codes/SetFeedrate.cs b/PepLib.Core/Codes/SetFeedrate.cs new file mode 100644 index 0000000..6095f35 --- /dev/null +++ b/PepLib.Core/Codes/SetFeedrate.cs @@ -0,0 +1,32 @@ + +namespace PepLib.Codes +{ + public class SetFeedrate : ICode + { + public SetFeedrate() + { + } + + public SetFeedrate(double value) + { + Value = value; + } + + public double Value { get; set; } + + public CodeType CodeType() + { + return Codes.CodeType.SetFeedrate; + } + + public ICode Clone() + { + return new SetFeedrate(Value); + } + + public override string ToString() + { + return string.Format("F{0}", Value); + } + } +} diff --git a/PepLib.Core/Codes/SetKerf.cs b/PepLib.Core/Codes/SetKerf.cs new file mode 100644 index 0000000..97d2c99 --- /dev/null +++ b/PepLib.Core/Codes/SetKerf.cs @@ -0,0 +1,34 @@ + +namespace PepLib.Codes +{ + public class SetKerf : ICode + { + public SetKerf(KerfType kerf = KerfType.Left) + { + Kerf = kerf; + } + + public KerfType Kerf { get; set; } + + public CodeType CodeType() + { + return Codes.CodeType.SetKerf; + } + + public ICode Clone() + { + return new SetKerf(Kerf); + } + + public override string ToString() + { + if (Kerf == KerfType.None) + return "G40"; + + if (Kerf == KerfType.Left) + return "G41"; + + return "G42"; + } + } +} diff --git a/PepLib.Core/Codes/SubProgramCall.cs b/PepLib.Core/Codes/SubProgramCall.cs new file mode 100644 index 0000000..47eaebd --- /dev/null +++ b/PepLib.Core/Codes/SubProgramCall.cs @@ -0,0 +1,85 @@ + +namespace PepLib.Codes +{ + public class SubProgramCall : ICode + { + private double rotation; + private Loop loop; + + public SubProgramCall() + { + } + + public SubProgramCall(int loopId, int repeatCount, double rotation) + { + LoopId = loopId; + RepeatCount = repeatCount; + Rotation = rotation; + } + + /// + /// The id associated with the current set loop. + /// + public int LoopId { get; set; } + + /// + /// Number of times the loop is cut. + /// + public int RepeatCount { get; set; } + + /// + /// Gets or sets the loop associated with the loop id. + /// + public Loop Loop + { + get { return loop; } + set + { + loop = (Loop)value.Clone(); + UpdateLoopRotation(); + } + } + + /// + /// Gets or sets the current rotation of the loop in degrees. + /// + public double Rotation + { + get { return rotation; } + set + { + rotation = value; + UpdateLoopRotation(); + } + } + + private void UpdateLoopRotation() + { + if (loop != null) + { + var diffAngle = AngleConverter.ToRadians(rotation) - loop.Rotation; + + if (!diffAngle.IsEqualTo(0.0)) + loop.Rotate(diffAngle); + } + } + + public CodeType CodeType() + { + return Codes.CodeType.SubProgramCall; + } + + public ICode Clone() + { + return new SubProgramCall(LoopId, RepeatCount, Rotation) + { + Loop = Loop + }; + } + + public override string ToString() + { + return string.Format("G92 L{0} R{1} P{2}", LoopId, RepeatCount, Rotation); + } + } +} diff --git a/PepLib.Core/Data/Drawing.cs b/PepLib.Core/Data/Drawing.cs new file mode 100644 index 0000000..419dd79 --- /dev/null +++ b/PepLib.Core/Data/Drawing.cs @@ -0,0 +1,216 @@ +namespace PepLib.Data +{ + using System; + using System.Collections.Generic; + using System.ComponentModel.DataAnnotations; + using System.ComponentModel.DataAnnotations.Schema; + + + [Table("Drawing")] + public partial class Drawing + { + public int ID { get; set; } + + [Required] + [StringLength(254)] + public string Name { get; set; } + + [Required] + [StringLength(20)] + public string CustID { get; set; } + + [Required] + [StringLength(32)] + public string Revision { get; set; } + + [Required] + [StringLength(254)] + public string Path { get; set; } + + [Required] + [StringLength(254)] + public string File { get; set; } + + [Required] + [StringLength(254)] + public string InUseBy { get; set; } + + public DateTime? InUseDate { get; set; } + + [Required] + [StringLength(254)] + public string Status { get; set; } + + [Required] + [StringLength(254)] + public string StatusModifiedBy { get; set; } + + public DateTime? StatusModifiedDate { get; set; } + + public DateTime? CreationDate { get; set; } + + public DateTime? LastEditDate { get; set; } + + public DateTime? LastRefDate { get; set; } + + [Required] + [StringLength(254)] + public string Description { get; set; } + + [Required] + [StringLength(254)] + public string Customer { get; set; } + + [Required] + [StringLength(254)] + public string Comment { get; set; } + + [Required] + [StringLength(254)] + public string Notes { get; set; } + + public byte Grain { get; set; } + + public double GrainAngle { get; set; } + + [Required] + [StringLength(254)] + public string Material { get; set; } + + [Required] + [StringLength(254)] + public string MatGrade { get; set; } + + [Required] + [StringLength(254)] + public string Programmer { get; set; } + + [Required] + [StringLength(254)] + public string CreatedBy { get; set; } + + [Required] + [StringLength(254)] + public string Type { get; set; } + + public byte CommonCut { get; set; } + + public byte CombineCut { get; set; } + + [Required] + [StringLength(254)] + public string Errors { get; set; } + + [Required] + [StringLength(254)] + public string Hardness { get; set; } + + [Required] + [StringLength(254)] + public string Specification { get; set; } + + public byte NestInCutOuts { get; set; } + + [Required] + [StringLength(254)] + public string UserDefined1 { get; set; } + + [Required] + [StringLength(254)] + public string UserDefined2 { get; set; } + + [Required] + [StringLength(254)] + public string UserDefined3 { get; set; } + + [Required] + [StringLength(254)] + public string UserDefined4 { get; set; } + + [Required] + [StringLength(254)] + public string UserDefined5 { get; set; } + + [Required] + [StringLength(254)] + public string UserDefined6 { get; set; } + + public short Machine { get; set; } + + [Required] + [StringLength(254)] + public string Application { get; set; } + + public int PartCount { get; set; } + + public int Color { get; set; } + + public short CombineMethod { get; set; } + + public byte SeqCutouts { get; set; } + + public byte AllowMirror { get; set; } + + [Required] + [StringLength(254)] + public string SourceFile { get; set; } + + public DateTime? SourceDate { get; set; } + + public int SourceSize { get; set; } + + [Required] + [StringLength(254)] + public string CadScaled { get; set; } + + public int CadDimVerified { get; set; } + + public int CadDimCount { get; set; } + + public double Width { get; set; } + + public double Length { get; set; } + + public double RectArea { get; set; } + + public double ExtArea { get; set; } + + public double TrueArea { get; set; } + + public double ExtUtil { get; set; } + + public double TrueUtil { get; set; } + + public double SmallestAreaAng { get; set; } + + public double SmallestAreaLen { get; set; } + + public double SmallestAreaWid { get; set; } + + public double SmallestYAng { get; set; } + + public double SmallestYLen { get; set; } + + public double SmallestYWid { get; set; } + + public double CutLength { get; set; } + + public double ScribeLength { get; set; } + + public int Checked { get; set; } + + public byte PepBendStatus { get; set; } + + public int HasBevel { get; set; } + + public int HasLeadIn { get; set; } + + public int HasTab { get; set; } + + public DateTime? ModifiedDate { get; set; } + + [Required] + [StringLength(254)] + public string ModifiedBy { get; set; } + } +} diff --git a/PepLib.Core/Data/NestDetail.cs b/PepLib.Core/Data/NestDetail.cs new file mode 100644 index 0000000..723e5f5 --- /dev/null +++ b/PepLib.Core/Data/NestDetail.cs @@ -0,0 +1,84 @@ +namespace PepLib.Data +{ + using System; + using System.Collections.Generic; + using System.ComponentModel.DataAnnotations; + using System.ComponentModel.DataAnnotations.Schema; + + + [Table("NestDetail")] + public partial class NestDetail + { + [Key] + public int AutoInc { get; set; } + + [Required] + [StringLength(200)] + public string NestName { get; set; } + + public int CopyID { get; set; } + + [StringLength(200)] + public string Drawing { get; set; } + + [Required] + [StringLength(20)] + public string CustID { get; set; } + + public int? DwgLoopNo { get; set; } + + [StringLength(40)] + public string DwgRevision { get; set; } + + [StringLength(40)] + public string CustomerNo { get; set; } + + [StringLength(64)] + public string CustomerName { get; set; } + + public int? QtyReq { get; set; } + + public int? QtyNstd { get; set; } + + public int? QtyRem { get; set; } + + public double? CutDist { get; set; } + + public double? ScribeDist { get; set; } + + public int? CutTime { get; set; } + + public int? PierceCount { get; set; } + + public int? IntersectionCount { get; set; } + + public double? Area1 { get; set; } + + public double? Area2 { get; set; } + + public double? CostPerPart { get; set; } + + public double? Net1Weight { get; set; } + + public double? Net2Weight { get; set; } + + public double? Net2WithRemWeight { get; set; } + + public double? Net3Weight { get; set; } + + public double? Net4Weight { get; set; } + + public double? GrossWeight { get; set; } + + public double? PercentOfMaterial { get; set; } + + public double? PercentOfTime { get; set; } + + public double? RotationConstraint { get; set; } + + public DateTime? ModifiedDate { get; set; } + + [StringLength(254)] + public string ModifiedBy { get; set; } + } +} diff --git a/PepLib.Core/Data/NestDrawing.cs b/PepLib.Core/Data/NestDrawing.cs new file mode 100644 index 0000000..97837a5 --- /dev/null +++ b/PepLib.Core/Data/NestDrawing.cs @@ -0,0 +1,79 @@ +namespace PepLib.Data +{ + using System; + using System.Collections.Generic; + using System.ComponentModel.DataAnnotations; + using System.ComponentModel.DataAnnotations.Schema; + + + [Table("NestDrawing")] + public partial class NestDrawing + { + [Key] + public int AutoInc { get; set; } + + [Required] + [StringLength(254)] + public string NestName { get; set; } + + public int CopyID { get; set; } + + [Required] + [StringLength(254)] + public string Drawing { get; set; } + + [Required] + [StringLength(20)] + public string CustID { get; set; } + + [Required] + [StringLength(32)] + public string DwgRevision { get; set; } + + [Required] + [StringLength(254)] + public string DwgDesc { get; set; } + + [Required] + [StringLength(254)] + public string ImageFile { get; set; } + + [Required] + [StringLength(254)] + public string UserDefined1 { get; set; } + + [Required] + [StringLength(254)] + public string UserDefined2 { get; set; } + + [Required] + [StringLength(254)] + public string UserDefined3 { get; set; } + + [Required] + [StringLength(254)] + public string UserDefined4 { get; set; } + + [Required] + [StringLength(254)] + public string UserDefined5 { get; set; } + + [Required] + [StringLength(254)] + public string UserDefined6 { get; set; } + + [Required] + [StringLength(254)] + public string Description { get; set; } + + public decimal SizeX { get; set; } + + public decimal SizeY { get; set; } + + public DateTime? ModifiedDate { get; set; } + + [Required] + [StringLength(254)] + public string ModifiedBy { get; set; } + } +} diff --git a/PepLib.Core/Data/NestHeader.cs b/PepLib.Core/Data/NestHeader.cs new file mode 100644 index 0000000..831f13a --- /dev/null +++ b/PepLib.Core/Data/NestHeader.cs @@ -0,0 +1,287 @@ +namespace PepLib.Data +{ + using System; + using System.Collections.Generic; + using System.ComponentModel.DataAnnotations; + using System.ComponentModel.DataAnnotations.Schema; + + + [Table("NestHeader")] + public partial class NestHeader + { + [Key] + public int AutoInc { get; set; } + + [Required] + [StringLength(254)] + public string NestName { get; set; } + + public int CopyID { get; set; } + + [Required] + [StringLength(20)] + public string CustID { get; set; } + + [Required] + [StringLength(254)] + public string CustomerName { get; set; } + + public DateTime? DateProgrammed { get; set; } + + [Required] + [StringLength(254)] + public string Material { get; set; } + + [Required] + [StringLength(128)] + public string MatDescription { get; set; } + + [Required] + [StringLength(254)] + public string MatGrade { get; set; } + + public double MatThick { get; set; } + + public double MatCost { get; set; } + + [Required] + [StringLength(254)] + public string Programmer { get; set; } + + public int Machine { get; set; } + + public int Application { get; set; } + + [Required] + [StringLength(254)] + public string Post { get; set; } + + [Required] + [StringLength(254)] + public string Comments { get; set; } + + [Required] + [StringLength(254)] + public string Remarks { get; set; } + + public int ProgramCount { get; set; } + + public int Duplicates { get; set; } + + public double MachineCostPerHour { get; set; } + + public double MachineHoursNeeded { get; set; } + + public double MatDensity { get; set; } + + public double HandlingCostPerPlate { get; set; } + + public double HandlingCostPerPart { get; set; } + + public int PlateCount { get; set; } + + public int PartCount { get; set; } + + public double CostOfConsumablesPerPierce { get; set; } + + public double ConsumablesPerPierceNeeded { get; set; } + + public bool CPTApplied { get; set; } + + public double CuttingPierceTime { get; set; } + + public double ScribePierceTime { get; set; } + + public double IntersectDelayTime { get; set; } + + public double HeadUpDownTimeBetweenCutouts { get; set; } + + public double HeadUpDownTimeBetweenParts { get; set; } + + public double RapidFeedRate { get; set; } + + public double PercentOfFeedRateForArcs { get; set; } + + public double PercentOfFeedRateForSmallHoles { get; set; } + + public double PercentOfFeedRateForMediumHoles { get; set; } + + public double PercentOfFeedRateForSmallCutouts { get; set; } + + public double AssistGasPressure { get; set; } + + [Required] + [StringLength(3)] + public string TypeOfGas { get; set; } + + public double NozzleSize { get; set; } + + public double CostOfGasPerCF { get; set; } + + public double CFOfGasNeeded { get; set; } + + public double TotalCutDist { get; set; } + + public double TotalRapidDist { get; set; } + + public double EdgePierceCount { get; set; } + + public double BubblePierceCount { get; set; } + + public double RadiusCornerCount { get; set; } + + public bool IncludeRemnantCost { get; set; } + + public double ClampRepositionTime { get; set; } + + public double PlateLoadTime { get; set; } + + public double PlateUnloadTime { get; set; } + + public double TimePerPart { get; set; } + + public int rStatus { get; set; } + + public DateTime? rDateStamp { get; set; } + + [Required] + [StringLength(254)] + public string UserDefined1 { get; set; } + + [Required] + [StringLength(254)] + public string UserDefined2 { get; set; } + + [Required] + [StringLength(254)] + public string UserDefined3 { get; set; } + + [Required] + [StringLength(254)] + public string UserDefined4 { get; set; } + + [Required] + [StringLength(254)] + public string UserDefined5 { get; set; } + + [Required] + [StringLength(254)] + public string UserDefined6 { get; set; } + + public double ActualCutTime { get; set; } + + public double ActualStartTime { get; set; } + + public double ActualEndTime { get; set; } + + public double CuttingFeedRate { get; set; } + + public DateTime? ModifiedDate { get; set; } + + [Required] + [StringLength(254)] + public string ModifiedBy { get; set; } + + public int Status { get; set; } + + [Required] + [StringLength(254)] + public string Path { get; set; } + + public DateTime? DueDate { get; set; } + + public int NestGenTime { get; set; } + + public int NestEditTime { get; set; } + + public int NestEditCount { get; set; } + + [Required] + [StringLength(254)] + public string NestGenMethod { get; set; } + + public double ScribingFeedRate { get; set; } + + [Required] + [StringLength(254)] + public string FeedRateScenarios { get; set; } + + public double PercentOfFeedRateForInsideBevel { get; set; } + + public double PercentOfFeedRateForOutsideBevel { get; set; } + + public double SetupTime { get; set; } + + public double BubbleEdgePierceTime { get; set; } + + public double BevelTorchTiltTime { get; set; } + + [Required] + [StringLength(3)] + public string OutputJobCosting { get; set; } + + public double CadDrawingCharge { get; set; } + + public double MaterialMarkup { get; set; } + + public double OverheadMarkup { get; set; } + + public double CostOfFreightPerPound { get; set; } + + public double DrillCostPerHole { get; set; } + + public double DrillCostPerUniquePart { get; set; } + + public double DrillCostHardConsumable { get; set; } + + public double DrillPiercesPerHardConsumable { get; set; } + + public double DrillTimeChangeHardConsumable { get; set; } + + [Required] + [StringLength(3)] + public string ReportNestedDrawingsOnly { get; set; } + + [Required] + [StringLength(3)] + public string DisplayTimingInfo { get; set; } + + [Required] + [StringLength(3)] + public string OutputPostTechTable { get; set; } + + [Required] + [StringLength(254)] + public string WeightTypeForDisplay { get; set; } + + [Required] + [StringLength(254)] + public string WeightTypeForCosting { get; set; } + + [Required] + [StringLength(254)] + public string Errors { get; set; } + + public int DefToolLib { get; set; } + + [Required] + [StringLength(254)] + public string DefPlateSize { get; set; } + + [Required] + [StringLength(254)] + public string DefKerfDirection { get; set; } + + [Required] + [StringLength(254)] + public string InUse { get; set; } + + [Required] + [StringLength(254)] + public string ApplicationName { get; set; } + + public DateTime? RequestedSchedDate { get; set; } + + public double AutoGrainDim { get; set; } + } +} diff --git a/PepLib.Core/Data/PepDB.cs b/PepLib.Core/Data/PepDB.cs new file mode 100644 index 0000000..7b054ac --- /dev/null +++ b/PepLib.Core/Data/PepDB.cs @@ -0,0 +1,163 @@ +using Microsoft.EntityFrameworkCore; + +namespace PepLib.Data; + +public partial class PepDB : DbContext +{ + public PepDB(DbContextOptions options) + : base(options) + { + // Configure as read-only - no change tracking for better performance + ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; + ChangeTracker.AutoDetectChangesEnabled = false; + } + + public virtual DbSet Drawings { get; set; } + public virtual DbSet NestDetails { get; set; } + public virtual DbSet NestDrawings { get; set; } + public virtual DbSet NestHeaders { get; set; } + public virtual DbSet PlateDetails { get; set; } + public virtual DbSet PlateHeaders { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(entity => + { + entity.Property(e => e.Name).IsUnicode(false); + entity.Property(e => e.CustID).IsUnicode(false); + entity.Property(e => e.Revision).IsUnicode(false); + entity.Property(e => e.Path).IsUnicode(false); + entity.Property(e => e.File).IsUnicode(false); + entity.Property(e => e.InUseBy).IsUnicode(false); + entity.Property(e => e.Status).IsUnicode(false); + entity.Property(e => e.StatusModifiedBy).IsUnicode(false); + entity.Property(e => e.Description).IsUnicode(false); + entity.Property(e => e.Customer).IsUnicode(false); + entity.Property(e => e.Comment).IsUnicode(false); + entity.Property(e => e.Notes).IsUnicode(false); + entity.Property(e => e.Material).IsUnicode(false); + entity.Property(e => e.MatGrade).IsUnicode(false); + entity.Property(e => e.Programmer).IsUnicode(false); + entity.Property(e => e.CreatedBy).IsUnicode(false); + entity.Property(e => e.Type).IsUnicode(false); + entity.Property(e => e.Errors).IsUnicode(false); + entity.Property(e => e.Hardness).IsUnicode(false); + entity.Property(e => e.Specification).IsUnicode(false); + entity.Property(e => e.UserDefined1).IsUnicode(false); + entity.Property(e => e.UserDefined2).IsUnicode(false); + entity.Property(e => e.UserDefined3).IsUnicode(false); + entity.Property(e => e.UserDefined4).IsUnicode(false); + entity.Property(e => e.UserDefined5).IsUnicode(false); + entity.Property(e => e.UserDefined6).IsUnicode(false); + entity.Property(e => e.Application).IsUnicode(false); + entity.Property(e => e.SourceFile).IsUnicode(false); + entity.Property(e => e.CadScaled).IsUnicode(false); + entity.Property(e => e.ModifiedBy).IsUnicode(false); + }); + + modelBuilder.Entity(entity => + { + entity.Property(e => e.NestName).IsUnicode(false); + entity.Property(e => e.Drawing).IsUnicode(false); + entity.Property(e => e.CustID).IsUnicode(false); + entity.Property(e => e.DwgRevision).IsUnicode(false); + entity.Property(e => e.CustomerNo).IsUnicode(false); + entity.Property(e => e.CustomerName).IsUnicode(false); + entity.Property(e => e.ModifiedBy).IsUnicode(false); + }); + + modelBuilder.Entity(entity => + { + entity.Property(e => e.NestName).IsUnicode(false); + entity.Property(e => e.Drawing).IsUnicode(false); + entity.Property(e => e.CustID).IsUnicode(false); + entity.Property(e => e.DwgRevision).IsUnicode(false); + entity.Property(e => e.DwgDesc).IsUnicode(false); + entity.Property(e => e.ImageFile).IsUnicode(false); + entity.Property(e => e.UserDefined1).IsUnicode(false); + entity.Property(e => e.UserDefined2).IsUnicode(false); + entity.Property(e => e.UserDefined3).IsUnicode(false); + entity.Property(e => e.UserDefined4).IsUnicode(false); + entity.Property(e => e.UserDefined5).IsUnicode(false); + entity.Property(e => e.UserDefined6).IsUnicode(false); + entity.Property(e => e.Description).IsUnicode(false); + entity.Property(e => e.SizeX).HasPrecision(15, 4); + entity.Property(e => e.SizeY).HasPrecision(15, 4); + entity.Property(e => e.ModifiedBy).IsUnicode(false); + }); + + modelBuilder.Entity(entity => + { + entity.Property(e => e.NestName).IsUnicode(false); + entity.Property(e => e.CustID).IsUnicode(false); + entity.Property(e => e.CustomerName).IsUnicode(false); + entity.Property(e => e.Material).IsUnicode(false); + entity.Property(e => e.MatDescription).IsUnicode(false); + entity.Property(e => e.MatGrade).IsUnicode(false); + entity.Property(e => e.Programmer).IsUnicode(false); + entity.Property(e => e.Post).IsUnicode(false); + entity.Property(e => e.Comments).IsUnicode(false); + entity.Property(e => e.Remarks).IsUnicode(false); + entity.Property(e => e.TypeOfGas).IsUnicode(false); + entity.Property(e => e.UserDefined1).IsUnicode(false); + entity.Property(e => e.UserDefined2).IsUnicode(false); + entity.Property(e => e.UserDefined3).IsUnicode(false); + entity.Property(e => e.UserDefined4).IsUnicode(false); + entity.Property(e => e.UserDefined5).IsUnicode(false); + entity.Property(e => e.UserDefined6).IsUnicode(false); + entity.Property(e => e.ModifiedBy).IsUnicode(false); + entity.Property(e => e.Path).IsUnicode(false); + entity.Property(e => e.NestGenMethod).IsUnicode(false); + entity.Property(e => e.FeedRateScenarios).IsUnicode(false); + entity.Property(e => e.OutputJobCosting).IsUnicode(false); + entity.Property(e => e.ReportNestedDrawingsOnly).IsUnicode(false); + entity.Property(e => e.DisplayTimingInfo).IsUnicode(false); + entity.Property(e => e.OutputPostTechTable).IsUnicode(false); + entity.Property(e => e.WeightTypeForDisplay).IsUnicode(false); + entity.Property(e => e.WeightTypeForCosting).IsUnicode(false); + entity.Property(e => e.Errors).IsUnicode(false); + entity.Property(e => e.DefPlateSize).IsUnicode(false); + entity.Property(e => e.DefKerfDirection).IsUnicode(false); + entity.Property(e => e.InUse).IsUnicode(false); + entity.Property(e => e.ApplicationName).IsUnicode(false); + entity.Property(e => e.Application).IsUnicode(false); + }); + + modelBuilder.Entity(entity => + { + entity.Property(e => e.NestName).IsUnicode(false); + entity.Property(e => e.Drawing).IsUnicode(false); + entity.Property(e => e.DwgRevision).IsUnicode(false); + entity.Property(e => e.LoopList).IsUnicode(false); + entity.Property(e => e.DwgDesc).IsUnicode(false); + entity.Property(e => e.WorkOrder).IsUnicode(false); + entity.Property(e => e.Note).IsUnicode(false); + entity.Property(e => e.Sales).IsUnicode(false); + entity.Property(e => e.Remarks).IsUnicode(false); + entity.Property(e => e.RequiredGrade).IsUnicode(false); + entity.Property(e => e.JobNo).IsUnicode(false); + entity.Property(e => e.Sequence).IsUnicode(false); + entity.Property(e => e.Marking).IsUnicode(false); + entity.Property(e => e.ModifiedBy).IsUnicode(false); + entity.Property(e => e.LifetimeList).IsUnicode(false); + entity.Property(e => e.CustPO).IsUnicode(false); + entity.Property(e => e.CustID).IsUnicode(false); + }); + + modelBuilder.Entity(entity => + { + entity.Property(e => e.NestName).IsUnicode(false); + entity.Property(e => e.InvPlateName).IsUnicode(false); + entity.Property(e => e.RemnantSize).IsUnicode(false); + entity.Property(e => e.PlateSize).IsUnicode(false); + entity.Property(e => e.HeatLot).IsUnicode(false); + entity.Property(e => e.UpdateStatus).IsUnicode(false); + entity.Property(e => e.ImageFile).IsUnicode(false); + entity.Property(e => e.Note).IsUnicode(false); + entity.Property(e => e.ProgramName).IsUnicode(false); + entity.Property(e => e.ModifiedBy).IsUnicode(false); + entity.Property(e => e.Location).IsUnicode(false); + entity.Property(e => e.NestedSize).IsUnicode(false); + }); + } +} diff --git a/PepLib.Core/Data/PlateDetail.cs b/PepLib.Core/Data/PlateDetail.cs new file mode 100644 index 0000000..378ed7b --- /dev/null +++ b/PepLib.Core/Data/PlateDetail.cs @@ -0,0 +1,99 @@ +namespace PepLib.Data +{ + using System; + using System.Collections.Generic; + using System.ComponentModel.DataAnnotations; + using System.ComponentModel.DataAnnotations.Schema; + + + [Table("PlateDetail")] + public partial class PlateDetail + { + [Key] + public int AutoInc { get; set; } + + [Required] + [StringLength(254)] + public string NestName { get; set; } + + public int CopyID { get; set; } + + public int? PlateNumber { get; set; } + + [StringLength(254)] + public string Drawing { get; set; } + + [StringLength(32)] + public string DwgRevision { get; set; } + + [StringLength(254)] + public string LoopList { get; set; } + + [StringLength(254)] + public string DwgDesc { get; set; } + + public double? DwgMatUtil { get; set; } + + public double? DwgPercentCutTime { get; set; } + + [StringLength(254)] + public string WorkOrder { get; set; } + + public DateTime? OrderRecvDate { get; set; } + + public DateTime? OrderDueDate { get; set; } + + public int? QtyReq { get; set; } + + public int? QtyNstd { get; set; } + + public int? QtyAccepted { get; set; } + + public int? QtyCut { get; set; } + + [StringLength(254)] + public string Note { get; set; } + + [StringLength(254)] + public string Sales { get; set; } + + [StringLength(254)] + public string Remarks { get; set; } + + [StringLength(254)] + public string RequiredGrade { get; set; } + + public double? RequiredThickness { get; set; } + + [Required] + [StringLength(254)] + public string JobNo { get; set; } + + [Column(TypeName = "text")] + [Required] + public string Sequence { get; set; } + + [Required] + [StringLength(254)] + public string Marking { get; set; } + + public DateTime? ModifiedDate { get; set; } + + [StringLength(254)] + public string ModifiedBy { get; set; } + + [Required] + [StringLength(254)] + public string LifetimeList { get; set; } + + public int LifetimeLargest { get; set; } + + [Required] + [StringLength(254)] + public string CustPO { get; set; } + + [Required] + [StringLength(20)] + public string CustID { get; set; } + } +} diff --git a/PepLib.Core/Data/PlateHeader.cs b/PepLib.Core/Data/PlateHeader.cs new file mode 100644 index 0000000..144215c --- /dev/null +++ b/PepLib.Core/Data/PlateHeader.cs @@ -0,0 +1,117 @@ +namespace PepLib.Data +{ + using System; + using System.Collections.Generic; + using System.ComponentModel.DataAnnotations; + using System.ComponentModel.DataAnnotations.Schema; + + + [Table("PlateHeader")] + public partial class PlateHeader + { + [Key] + public int AutoInc { get; set; } + + [Required] + [StringLength(40)] + public string NestName { get; set; } + + public int CopyID { get; set; } + + public int? PlateNumber { get; set; } + + public int? DupNo { get; set; } + + [StringLength(128)] + public string InvPlateName { get; set; } + + public double InvCostPerWeight { get; set; } + + public double? RapidDist { get; set; } + + public double? CutDist { get; set; } + + public int? CutTime { get; set; } + + public double? PlateWeight { get; set; } + + public double? PlateCost { get; set; } + + [StringLength(20)] + public string RemnantSize { get; set; } + + public double? RemnantArea { get; set; } + + public double? RemnantCost { get; set; } + + public double? RemnantWeight { get; set; } + + [StringLength(20)] + public string PlateSize { get; set; } + + public int? PlateDuplicates { get; set; } + + public double? PlateUtilization { get; set; } + + public double? PlateMaterialUtil { get; set; } + + public double? TotalArea1 { get; set; } + + public double? TotalArea2 { get; set; } + + public int? Status { get; set; } + + public DateTime? Statusdate { get; set; } + + public int? rStatus { get; set; } + + public DateTime? rDateStamp { get; set; } + + [StringLength(40)] + public string HeatLot { get; set; } + + [StringLength(20)] + public string UpdateStatus { get; set; } + + public double? ActualCutTime { get; set; } + + public double? ActualStartTime { get; set; } + + public double? ActualEndTime { get; set; } + + [StringLength(254)] + public string ImageFile { get; set; } + + [StringLength(254)] + public string Note { get; set; } + + [StringLength(254)] + public string ProgramName { get; set; } + + public DateTime? ModifiedDate { get; set; } + + [StringLength(254)] + public string ModifiedBy { get; set; } + + [StringLength(254)] + public string Location { get; set; } + + public DateTime? DateCut { get; set; } + + public short InvImpAllocated { get; set; } + + public double CombSavingDist { get; set; } + + public int HeadRaises { get; set; } + + public int RapidCount { get; set; } + + [Required] + [StringLength(25)] + public string NestedSize { get; set; } + + public double NestedLength { get; set; } + + public double NestedWidth { get; set; } + } +} diff --git a/PepLib.Core/Drawing.cs b/PepLib.Core/Drawing.cs new file mode 100644 index 0000000..82a640b --- /dev/null +++ b/PepLib.Core/Drawing.cs @@ -0,0 +1,282 @@ +using System; +using System.Collections.Generic; +using System.IO; +using PepLib.Codes; +using PepLib.IO; + +namespace PepLib +{ + public class Drawing + { + public DrawingInfo Info { get; set; } + + public List Loops { get; set; } + + public Drawing() + { + Loops = new List(); + } + + public static Drawing Load(string nestfile) + { + var reader = new DrawingReader(); + reader.Read(nestfile); + return reader.Drawing; + } + + public static Drawing Load(Stream stream) + { + var reader = new DrawingReader(); + reader.Read(stream); + return reader.Drawing; + } + + public void ResolveLoops() + { + for (int i = 0; i < Loops.Count; ++i) + { + var loop = Loops[i]; + ResolveLoops(loop); + } + } + + private void ResolveLoops(Program pgm) + { + for (int i = 0; i < pgm.Count; ++i) + { + var code = pgm[i]; + + if (code.CodeType() != CodeType.SubProgramCall) + continue; + + var subpgmcall = (SubProgramCall)code; + + var loop = GetLoop(subpgmcall.LoopId); + + if (loop == null) + throw new Exception("Loop not found"); + + subpgmcall.Loop = loop; + } + } + + public Loop GetLoop(int id) + { + string name = GetLoopName(id); + return GetLoop(name); + } + + + private Loop GetLoop(string name) + { + for (int i = 0; i < Loops.Count; ++i) + { + if (Loops[i].Name == name) + return Loops[i]; + } + + return null; + } + + private string GetLoopName(int loopId) + { + return string.Format("{0}.loop-{1}", Info.Name, loopId.ToString().PadLeft(3, '0')); + } + + public static bool TryLoad(string nestfile, out Drawing drawing) + { + try + { + drawing = Load(nestfile); + } + catch (Exception) + { + drawing = null; + return false; + } + + return true; + } + + public static bool TryLoad(Stream stream, out Drawing drawing) + { + try + { + drawing = Load(stream); + } + catch (Exception) + { + drawing = null; + return false; + } + + return true; + } + + #region DrawingInfo wrapper properties + + public string Name + { + get { return Info.Name; } + set { Info.Name = value; } + } + + public string Revision + { + get { return Info.Revision; } + set { Info.Revision = value; } + } + + public string Customer + { + get { return Info.Customer; } + set { Info.Customer = value; } + } + + public string Description + { + get { return Info.Description; } + set { Info.Description = value; } + } + + public string Comment + { + get { return Info.Comment; } + set { Info.Comment = value; } + } + + public string Notes + { + get { return Info.Notes; } + set { Info.Notes = value; } + } + + public string Source + { + get { return Info.Source; } + set { Info.Source = value; } + } + + public DateTime CreationDate + { + get { return Info.CreationDate; } + set { Info.CreationDate = value; } + } + + public DateTime LastModifiedDate + { + get { return Info.LastModifiedDate; } + set { Info.LastModifiedDate = value; } + } + + public DateTime LastReferenceDate + { + get { return Info.LastReferenceDate; } + set { Info.LastReferenceDate = value; } + } + + public int MachineNumber + { + get { return Info.MachineNumber; } + set { Info.MachineNumber = value; } + } + + public ApplicationType Application + { + get { return Info.Application; } + set { Info.Application = value; } + } + + public int MaterialNumber + { + get { return Info.MaterialNumber; } + set { Info.MaterialNumber = value; } + } + + public string MaterialGrade + { + get { return Info.MaterialGrade; } + set { Info.MaterialGrade = value; } + } + + public string Specification + { + get { return Info.Specification; } + set { Info.Specification = value; } + } + + public string Hardness + { + get { return Info.Hardness; } + set { Info.Hardness = value; } + } + + public GrainType Grain + { + get { return Info.Grain; } + set { Info.Grain = value; } + } + + public string ProgrammedBy + { + get { return Info.ProgrammedBy; } + set { Info.ProgrammedBy = value; } + } + + public string CreatedBy + { + get { return Info.CreatedBy; } + set { Info.CreatedBy = value; } + } + + public string Errors + { + get { return Info.Errors; } + set { Info.Errors = value; } + } + + public DrawingType Type + { + get { return Info.Type; } + set { Info.Type = value; } + } + + public string UserDefined1 + { + get { return Info.UserDefined1; } + set { Info.UserDefined1 = value; } + } + + public string UserDefined2 + { + get { return Info.UserDefined2; } + set { Info.UserDefined2 = value; } + } + + public string UserDefined3 + { + get { return Info.UserDefined3; } + set { Info.UserDefined3 = value; } + } + + public string UserDefined4 + { + get { return Info.UserDefined4; } + set { Info.UserDefined4 = value; } + } + + public string UserDefined5 + { + get { return Info.UserDefined5; } + set { Info.UserDefined5 = value; } + } + + public string UserDefined6 + { + get { return Info.UserDefined6; } + set { Info.UserDefined6 = value; } + } + + #endregion + } +} diff --git a/PepLib.Core/DrawingInfo.cs b/PepLib.Core/DrawingInfo.cs new file mode 100644 index 0000000..d5d84be --- /dev/null +++ b/PepLib.Core/DrawingInfo.cs @@ -0,0 +1,109 @@ +using System; +using System.IO; +using System.Text; +using PepLib.IO; + +namespace PepLib +{ + public class DrawingInfo + { + public string Name { get; set; } + + public string Revision { get; set; } + + public string Customer { get; set; } + + public string Description { get; set; } + + public string Comment { get; set; } + + public string Notes { get; set; } + + public string Source { get; set; } + + public DateTime CreationDate { get; set; } + + public DateTime LastModifiedDate { get; set; } + + public DateTime LastReferenceDate { get; set; } + + public int MachineNumber { get; set; } + + public ApplicationType Application { get; set; } + + public int MaterialNumber { get; set; } + + public string MaterialGrade { get; set; } + + public string Specification { get; set; } + + public string Hardness { get; set; } + + public GrainType Grain { get; set; } + + public string ProgrammedBy { get; set; } + + public string CreatedBy { get; set; } + + public string Errors { get; set; } + + public DrawingType Type { get; set; } + + public string UserDefined1 { get; set; } + + public string UserDefined2 { get; set; } + + public string UserDefined3 { get; set; } + + public string UserDefined4 { get; set; } + + public string UserDefined5 { get; set; } + + public string UserDefined6 { get; set; } + + public static DrawingInfo Load(string nestFile) + { + var reader = new DrawingInfoReader(); + reader.Read(nestFile); + return reader.Info; + } + + public static DrawingInfo Load(Stream stream) + { + var reader = new DrawingInfoReader(); + reader.Read(stream); + return reader.Info; + } + + public static bool TryLoad(string nestfile, out DrawingInfo drawingInfo) + { + try + { + drawingInfo = Load(nestfile); + } + catch (Exception) + { + drawingInfo = null; + return false; + } + + return true; + } + + public static bool TryLoad(Stream stream, out DrawingInfo drawingInfo) + { + try + { + drawingInfo = Load(stream); + } + catch (Exception) + { + drawingInfo = null; + return false; + } + + return true; + } + } +} + diff --git a/PepLib.Core/DrawingType.cs b/PepLib.Core/DrawingType.cs new file mode 100644 index 0000000..dd5c0d0 --- /dev/null +++ b/PepLib.Core/DrawingType.cs @@ -0,0 +1,12 @@ + +namespace PepLib +{ + public enum DrawingType + { + None = 0x20, + Drawing = 0x44, + Product = 0x50, + Rotary = 0x52, + Tool = 0x54 + } +} diff --git a/PepLib.Core/Generic.cs b/PepLib.Core/Generic.cs new file mode 100644 index 0000000..aaf4cea --- /dev/null +++ b/PepLib.Core/Generic.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PepLib +{ + public static class Generic + { + public static void Swap(ref T a, ref T b) + { + T c = a; + a = b; + b = c; + } + } +} diff --git a/PepLib.Core/GrainType.cs b/PepLib.Core/GrainType.cs new file mode 100644 index 0000000..6a4345c --- /dev/null +++ b/PepLib.Core/GrainType.cs @@ -0,0 +1,11 @@ + +namespace PepLib +{ + public enum GrainType + { + No = 0x0, + Yes = 0x1, + Soft = 0x2, + Hard = 0x3 + } +} diff --git a/PepLib.Core/IMovable.cs b/PepLib.Core/IMovable.cs new file mode 100644 index 0000000..c76b3ac --- /dev/null +++ b/PepLib.Core/IMovable.cs @@ -0,0 +1,10 @@ +namespace PepLib +{ + public interface IMovable + { + void Rotate(double angle); + void Rotate(double angle, Vector origin); + void Offset(double x, double y); + void Offset(Vector voffset); + } +} diff --git a/PepLib.Core/IO/DrawingInfoReader.cs b/PepLib.Core/IO/DrawingInfoReader.cs new file mode 100644 index 0000000..a141e03 --- /dev/null +++ b/PepLib.Core/IO/DrawingInfoReader.cs @@ -0,0 +1,100 @@ +using System; +using System.IO; +using System.Text; + +namespace PepLib.IO +{ + public sealed class DrawingInfoReader + { + public DrawingInfo Info { get; private set; } + + public DrawingInfoReader() + { + Info = new DrawingInfo(); + } + + public DrawingInfoReader(DrawingInfo info) + { + Info = info; + } + + public void Read(Stream stream) + { + Info.Name = ReadString(0xC8, ref stream); + Info.Revision = ReadString(0x20, ref stream); + Info.CreationDate = DateTime.Parse(ReadString(0xA, ref stream)); + Info.LastModifiedDate = DateTime.Parse(ReadString(0xA, ref stream)); + Info.LastReferenceDate = DateTime.Parse(ReadString(0xA, ref stream)); + Info.Description = ReadString(0xC8, ref stream); + Info.Customer = ReadString(0x40, ref stream); + Info.Comment = ReadString(0x40, ref stream); + Info.Notes = ReadString(0x400, ref stream); + Info.Grain = (GrainType)ReadByte(ref stream); + + stream.Seek(0x9, SeekOrigin.Current); + + Info.MaterialNumber = int.Parse(ReadString(0x40, ref stream)); + Info.MaterialGrade = ReadString(0x10, ref stream); + Info.ProgrammedBy = ReadString(0x40, ref stream); + Info.CreatedBy = ReadString(0x40, ref stream); + Info.Type = (DrawingType)ReadByte(ref stream); + + stream.Seek(0x4, SeekOrigin.Current); + + Info.Errors = ReadString(0x64, ref stream); + Info.Hardness = ReadString(0x20, ref stream); + Info.Specification = ReadString(0x40, ref stream); + + stream.Seek(0x2, SeekOrigin.Current); + + Info.UserDefined1 = ReadString(0x20, ref stream); + Info.UserDefined2 = ReadString(0x20, ref stream); + Info.UserDefined3 = ReadString(0x20, ref stream); + Info.UserDefined4 = ReadString(0x40, ref stream); + Info.UserDefined5 = ReadString(0x40, ref stream); + Info.UserDefined6 = ReadString(0x40, ref stream); + Info.MachineNumber = ReadByte(ref stream); + + stream.Seek(0x1, SeekOrigin.Current); + + Info.Application = (ApplicationType)ReadByte(ref stream); + } + + public void Read(string nestFile) + { + if (!File.Exists(nestFile)) + { + var msg = string.Format("File Not Found: {0}", nestFile); + throw new FileNotFoundException(msg); + } + + Stream stream = null; + string name; + + try + { + ZipHelper.ExtractByExtension(nestFile, ".dir", out name, out stream); + Read(stream); + } + finally + { + if (stream != null) + stream.Close(); + } + } + + private static string ReadString(int length, ref Stream stream) + { + var buffer = new byte[length]; + stream.Read(buffer, 0, length); + return Encoding.Default.GetString(buffer).Trim(); + } + + private static byte ReadByte(ref Stream stream) + { + var buffer = new byte[0x1]; + stream.Read(buffer, 0, 1); + return buffer[0]; + } + } +} diff --git a/PepLib.Core/IO/DrawingReader.cs b/PepLib.Core/IO/DrawingReader.cs new file mode 100644 index 0000000..153eda0 --- /dev/null +++ b/PepLib.Core/IO/DrawingReader.cs @@ -0,0 +1,113 @@ +using Ionic.Zip; +using System; +using System.Diagnostics; +using System.IO; +using System.Text.RegularExpressions; + +namespace PepLib.IO +{ + public sealed class DrawingReader + { + public Drawing Drawing { get; private set; } + + public DrawingReader() + { + Drawing = new Drawing(); + } + + public DrawingReader(Drawing drawing) + { + Drawing = drawing; + } + + public void Read(Stream stream) + { + var zipStream = new ZipInputStream(stream); + + ZipEntry theEntry; + + while ((theEntry = zipStream.GetNextEntry()) != null) + { + var size = 2048; + var data = new byte[size]; + var memstream = new MemoryStream(); + + while (true) + { + size = zipStream.Read(data, 0, data.Length); + + if (size > 0) + { + memstream.Write(data, 0, size); + memstream.Flush(); + } + else break; + } + + memstream.Seek(0, SeekOrigin.Begin); + + var extension = Path.GetExtension(theEntry.FileName); + + switch (extension) + { + case ".dir": + LoadInfo(memstream); + memstream.Close(); + continue; + } + + if (Regex.IsMatch(extension, "loop-\\d\\d\\d")) + Drawing.Loops.Add(ReadLoop(theEntry.FileName, memstream)); + + memstream.Close(); + } + + zipStream.Close(); + + Drawing.ResolveLoops(); + } + + public void Read(string nestFile) + { + if (!File.Exists(nestFile)) + { + var msg = string.Format("File Not Found: {0}", nestFile); + throw new FileNotFoundException(msg); + } + + Stream stream = null; + + try + { + stream = new FileStream(nestFile, FileMode.Open); + Read(stream); + } + finally + { + if (stream != null) + stream.Close(); + } + } + + private void LoadInfo(Stream stream) + { + try + { + Drawing.Info = DrawingInfo.Load(stream); + } + catch (Exception exception) + { + Debug.WriteLine(exception.Message); + Debug.WriteLine(exception.StackTrace); + } + } + + private Loop ReadLoop(string name, Stream stream) + { + var reader = new LoopReader(); + reader.Read(name, stream); + + return reader.Loop; + } + } +} diff --git a/PepLib.Core/IO/LoopReader.cs b/PepLib.Core/IO/LoopReader.cs new file mode 100644 index 0000000..7b98eb3 --- /dev/null +++ b/PepLib.Core/IO/LoopReader.cs @@ -0,0 +1,134 @@ +using System; +using System.IO; +using PepLib.Codes; + +namespace PepLib.IO +{ + internal sealed class LoopReader + { + public Loop Loop { get; private set; } + + public LoopReader() + { + Loop = new Loop(); + } + + public LoopReader(Loop loop) + { + Loop = loop; + } + + public void Read(string name, Stream stream) + { + var pgm = Program.Load(stream); + + Loop.Name = name; + Loop.AddRange(pgm); + LoadInfo(); + } + + private void LoadInfo() + { + for (int i = Loop.Count - 1; i >= 0; --i) + { + var code = Loop[i]; + + if (code.CodeType() != CodeType.Comment) + continue; + + var comment = (Comment)code; + + if (LoadInfo(comment.Value)) + Loop.RemoveAt(i); + } + } + + private bool LoadInfo(string value) + { + if (value.StartsWith("REF")) + { + ParseReferenceData(value); + return true; + } + + if (value.StartsWith("DRAWING")) + { + ParseDrawingData(value); + return true; + } + + if (value.StartsWith("DXF")) + { + ParseDxfData(value); + return true; + } + + return false; + } + + private void ParseReferenceData(string data) + { + var parts = data.Split(','); + + if (parts.Length != 3) + return; + + int xindex = parts[0].IndexOf('X'); + parts[0] = parts[0].Remove(0, xindex); + + double x = 0; + double y = 0; + + var xsplit = parts[0].Split('='); + + if (xsplit.Length == 2) + x = ReadDouble(xsplit[1]); + + var ysplit = parts[1].Split('='); + + if (ysplit.Length == 2) + y = ReadDouble(ysplit[1]); + + var datesplit = parts[2].Split('='); + + if (datesplit.Length == 2) + { + DateTime date; + DateTime.TryParse(datesplit[1], out date); + Loop.LastReferenceDate = date; + } + + Loop.ReferencePoint = new Vector(x, y); + } + + private void ParseDrawingData(string data) + { + var index = data.IndexOf('='); + + if (index == -1) + Loop.DrawingName = string.Empty; + + Loop.DrawingName = data.Remove(0, index + 1).Trim(); + } + + private void ParseDxfData(string data) + { + var index = data.IndexOf('='); + + if (index == -1) + Loop.DxfPath = string.Empty; + + Loop.DxfPath = data.Remove(0, index + 1).Trim(); + } + + private static double ReadDouble(string s, double defaultValue = 0.0) + { + double f; + + if (!double.TryParse(s, out f)) + return defaultValue; + + return f; + } + } +} \ No newline at end of file diff --git a/PepLib.Core/IO/MaterialDataReader.cs b/PepLib.Core/IO/MaterialDataReader.cs new file mode 100644 index 0000000..290f5ba --- /dev/null +++ b/PepLib.Core/IO/MaterialDataReader.cs @@ -0,0 +1,90 @@ +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace PepLib.IO +{ + public class MaterialDataReader + { + public List Materials { get; set; } + + public MaterialDataReader() + { + Materials = new List(); + } + + public void Read(Stream stream) + { + const int dataLength = 2000; + var count = stream.Length / dataLength; + var binreader = new BinaryReader(stream); + + for (int i = 0; i < count; i++) + { + var data = new MaterialData(); + + int id; + int.TryParse(ReadString(64, ref stream), out id); + data.Number = id; + data.Grade = ReadString(16, ref stream); + data.Name = ReadString(200, ref stream); + data.Density = binreader.ReadDouble(); + stream.Seek(8, SeekOrigin.Current); + data.Thickness = binreader.ReadDouble(); + + Materials.Add(data); + + stream.Position = i * dataLength; + } + } + + public void Read(string file) + { + if (!File.Exists(file)) + { + var msg = string.Format("File Not Found: {0}", file); + throw new FileNotFoundException(msg); + } + + Stream stream = null; + + try + { + stream = File.OpenRead(file); + Read(stream); + } + finally + { + if (stream != null) + stream.Close(); + } + } + + private static string ReadString(int length, ref Stream stream) + { + var buffer = new byte[length]; + stream.Read(buffer, 0, length); + return Encoding.Default.GetString(buffer).Trim(); + } + + private static byte ReadByte(ref Stream stream) + { + var buffer = new byte[0x1]; + stream.Read(buffer, 0, 1); + return buffer[0]; + } + } + + public class MaterialData + { + public int Number { get; set; } + + public string Name { get; set; } + + public string Grade { get; set; } + + public double Density { get; set; } + + public double Thickness { get; set; } + } +} diff --git a/PepLib.Core/IO/NestInfoReader.cs b/PepLib.Core/IO/NestInfoReader.cs new file mode 100644 index 0000000..73bec80 --- /dev/null +++ b/PepLib.Core/IO/NestInfoReader.cs @@ -0,0 +1,124 @@ +using System; +using System.IO; +using System.Text; + +namespace PepLib.IO +{ + internal sealed class NestInfoReader + { + public NestInfo Info { get; private set; } + + public NestInfoReader() + { + Info = new NestInfo(); + } + + public NestInfoReader(NestInfo info) + { + Info = info; + } + + public void Read(Stream stream) + { + var binReader = new BinaryReader(stream); + + Info.Name = ReadString(0xC8, ref stream); + Info.DateCreated = DateTime.Parse(ReadString(0xA, ref stream)); + Info.DateLastModified = DateTime.Parse(ReadString(0xA, ref stream)); + Info.LoopCount = binReader.ReadInt16(); + Info.ProgramCount = binReader.ReadInt16(); + Info.Customer = ReadString(0x40, ref stream); + Info.ProgrammedBy = ReadString(0x40, ref stream); + Info.Comments = ReadString(0x40, ref stream); + + // skip 2 bytes + stream.Seek(0x2, SeekOrigin.Current); + + Info.MaterialNumber = int.Parse(ReadString(0x40, ref stream)); + Info.MaterialGrade = ReadString(0x10, ref stream); + + // skip 2 bytes + stream.Seek(0x2, SeekOrigin.Current); + + Info.Notes = ReadString(0x400, ref stream); + Info.PostedAs = ReadString(0x64, ref stream); + Info.Errors = ReadString(0x64, ref stream); + Info.UserDefined1 = ReadString(0x20, ref stream); + Info.UserDefined2 = ReadString(0x20, ref stream); + Info.UserDefined3 = ReadString(0x20, ref stream); + Info.UserDefined4 = ReadString(0x40, ref stream); + Info.UserDefined5 = ReadString(0x40, ref stream); + Info.UserDefined6 = ReadString(0x40, ref stream); + Info.DefaultPlateSize = ReadString(0x1E, ref stream); + Info.Kerf = ReadString(0x3, ref stream); + + // skip 4 bytes + stream.Seek(0x4, SeekOrigin.Current); + + switch (ReadByte(ref stream)) + { + case 0: + Info.Status = StatusType.ToBeCut; + break; + + case 1: + Info.Status = StatusType.Quote; + break; + + case 2: + Info.Status = StatusType.HasBeenCut; + break; + + case 3: + Info.Status = StatusType.Temp; + break; + + default: + Info.Status = StatusType.ToBeCut; + break; + } + + // skip 16 bytes + stream.Seek(16, SeekOrigin.Current); + + Info.PlateCount = binReader.ReadInt16(); + } + + public void Read(string nestFile) + { + if (!File.Exists(nestFile)) + { + var msg = string.Format("File Not Found: {0}", nestFile); + throw new FileNotFoundException(msg); + } + + Stream stream = null; + string name; + + try + { + ZipHelper.ExtractByExtension(nestFile, ".dir", out name, out stream); + Read(stream); + } + finally + { + if (stream != null) + stream.Close(); + } + } + + private static string ReadString(int length, ref Stream stream) + { + var buffer = new byte[length]; + stream.Read(buffer, 0, length); + return Encoding.Default.GetString(buffer).Trim(); + } + + private static byte ReadByte(ref Stream stream) + { + var buffer = new byte[0x1]; + stream.Read(buffer, 0, 1); + return buffer[0]; + } + } +} diff --git a/PepLib.Core/IO/NestReader.cs b/PepLib.Core/IO/NestReader.cs new file mode 100644 index 0000000..eb96b19 --- /dev/null +++ b/PepLib.Core/IO/NestReader.cs @@ -0,0 +1,165 @@ +using Ionic.Zip; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; + +namespace PepLib.IO +{ + public sealed class NestReader + { + public Nest Nest { get; private set; } + + private readonly Dictionary plates; + private readonly Dictionary loops; + + public NestReader() + : this(new Nest()) + { + } + + public NestReader(Nest nest) + { + Nest = nest; + plates = new Dictionary(); + loops = new Dictionary(); + } + + public void Read(Stream stream) + { + const string plateExtensionPattern = "plate-\\d\\d\\d"; + const string loopExtensionPattern = "loop-\\d\\d\\d"; + + var zipStream = new ZipInputStream(stream); + + ZipEntry theEntry; + + while ((theEntry = zipStream.GetNextEntry()) != null) + { + var size = 2048; + var data = new byte[size]; + var memstream = new MemoryStream(); + + while (true) + { + size = zipStream.Read(data, 0, data.Length); + + if (size > 0) + { + memstream.Write(data, 0, size); + memstream.Flush(); + } + else break; + } + + memstream.Seek(0, SeekOrigin.Begin); + + var extension = Path.GetExtension(theEntry.FileName); + + switch (extension) + { + case ".report": + LoadReport(memstream); + memstream.Close(); + continue; + + case ".dwg-info": + LoadDrawingInfo(memstream); + memstream.Close(); + continue; + + default: + Debug.WriteLine("Unknown file: " + theEntry.FileName); + break; + } + + if (Regex.IsMatch(extension, loopExtensionPattern)) + loops.Add(theEntry.FileName, memstream); + else if (Regex.IsMatch(extension, plateExtensionPattern)) + plates.Add(theEntry.FileName, memstream); + } + + zipStream.Close(); + + foreach (var loop in loops) + Nest.Loops.Add(ReadLoop(loop.Key, loop.Value)); + + Nest.ResolveLoops(); + + foreach (var plate in plates) + Nest.Plates.Add(ReadPlate(plate.Key, plate.Value)); + } + + public void Read(string nestFile) + { + if (!File.Exists(nestFile)) + { + var msg = string.Format("File Not Found: {0}", nestFile); + throw new FileNotFoundException(msg); + } + + Stream stream = null; + + try + { + stream = new FileStream(nestFile, FileMode.Open); + Read(stream); + } + finally + { + if (stream != null) + stream.Close(); + } + } + + private void LoadReport(Stream stream) + { + try + { + Nest.Report = Report.Load(stream); + } + catch (Exception exception) + { + Debug.WriteLine(exception.Message); + Debug.WriteLine(exception.StackTrace); + } + } + + private void LoadDrawingInfo(Stream stream) + { + var buffer = new byte[2000]; + + while (stream.Read(buffer, 0, buffer.Length) > 0) + { + var name = Encoding.Default.GetString(buffer, 200, 200).Trim(); + var qty = BitConverter.ToInt32(buffer, 432); + + var drawing = new NestDrawing + { + Name = name, + QtyRequired = qty + }; + + Nest.Drawings.Add(drawing); + } + } + + private Loop ReadLoop(string name, Stream stream) + { + var reader = new LoopReader(); + reader.Read(name, stream); + + return reader.Loop; + } + + private Plate ReadPlate(string name, Stream stream) + { + var reader = new PlateReader(); + reader.Read(name, stream, Nest); + + return reader.Plate; + } + } +} diff --git a/PepLib.Core/IO/PlateReader.cs b/PepLib.Core/IO/PlateReader.cs new file mode 100644 index 0000000..53bae97 --- /dev/null +++ b/PepLib.Core/IO/PlateReader.cs @@ -0,0 +1,338 @@ +using System.IO; +using PepLib.Codes; + +namespace PepLib.IO +{ + internal sealed class PlateReader + { + public Plate Plate { get; private set; } + + public PlateReader() + { + Plate = new Plate(); + Plate.Duplicates = 1; + } + + public PlateReader(Plate plate) + { + Plate = plate; + } + + public void Read(string name, Stream stream, Nest nest) + { + var pos = new Vector(0, 0); + var pgm = Program.Load(stream); + + Plate.Name = name; + + for (int i = 0; i < pgm.Count; i++) + { + var block = pgm[i]; + + switch (block.CodeType()) + { + case CodeType.CircularMove: + { + var arc = (CircularMove)block; + pos = arc.EndPoint; + break; + } + + case CodeType.LinearMove: + { + var line = (LinearMove)block; + pos = line.EndPoint; + break; + } + + case CodeType.RapidMove: + { + var rapid = (RapidMove)block; + pos = rapid.EndPoint; + break; + } + + case CodeType.Comment: + { + var comment = (Comment)block; + LoadInfo(comment.Value); + break; + } + + case CodeType.SubProgramCall: + { + var subpgm = (SubProgramCall)block; + var loop = nest.GetLoop(subpgm.LoopId); + var part = Part.Create(loop, pos, AngleConverter.ToRadians(subpgm.Rotation)); + + var nextBlock = pgm[i + 1]; + + if (nextBlock.CodeType() == CodeType.Comment) + { + var comment = nextBlock as Comment; + + if (comment.Value == "DISPLAY ONLY") + { + part.IsDisplayOnly = true; + i++; + } + } + + Plate.Parts.Add(part); + break; + } + } + } + } + + private void LoadInfo(string value) + { + if (value.StartsWith("POSTED FILES")) + ParsePostedFiles(value); + + else if (value.StartsWith("HEAT LOT")) + ParseHeatLot(value); + + else if (value.StartsWith("SPACING")) + ParseSpacing(value); + + else if (value.StartsWith("CUT A TOTAL OF ")) + ParseNumberOfDuplicates(value); + + else if (value.StartsWith("EDGES,")) + ParseEdgeSpacing(value); + + else if (value.StartsWith("PLATE SCALING")) + ParsePlateSize(value); + + else if (value.StartsWith("MACHINE")) + ParseMachine(value); + + else if (value.StartsWith("MATERIAL")) + ParseMaterial(value); + + else if (value.StartsWith("GRADE")) + ParseGrade(value); + + else if (value.StartsWith("DESCRIPTION")) + ParseDescription(value); + + else if (value.StartsWith("PLATE THICKNESS")) + ParseThickness(value); + + else if (value.StartsWith("DENSITY")) + ParseDensity(value); + + else if (value.StartsWith("TORCHES")) + ParseTorchCount(value); + } + + private void ParseNumberOfDuplicates(string data) + { + var parts = data.Split(' '); + + if (parts.Length != 7) + return; + + int dup; + int.TryParse(parts[4], out dup); + + Plate.Duplicates = dup; + } + + private void ParsePostedFiles(string data) + { + if (data.Length < 14) + return; + + Plate.PostedFiles = data.Remove(0, 14).Trim(); + } + + private void ParseHeatLot(string data) + { + if (data.Length < 9) + return; + + Plate.HeatLot = data.Remove(0, 9).Trim(); + } + + private void ParseSpacing(string data) + { + var parts = data.Split('='); + + if (parts.Length != 2) + return; + + double spacing; + double.TryParse(parts[1], out spacing); + + Plate.PartSpacing = spacing; + } + + private void ParseEdgeSpacing(string data) + { + var parts = data.Split(','); + + if (parts.Length != 5) + return; + + var leftSplit = parts[1].Split('='); + if (leftSplit.Length == 2) + { + double x; + double.TryParse(leftSplit[1], out x); + Plate.EdgeSpacing.Left = x; + } + + var bottomSplit = parts[2].Split('='); + if (bottomSplit.Length == 2) + { + double x; + double.TryParse(bottomSplit[1], out x); + Plate.EdgeSpacing.Bottom = x; + } + + var rightSplit = parts[3].Split('='); + if (rightSplit.Length == 2) + { + double x; + double.TryParse(rightSplit[1], out x); + Plate.EdgeSpacing.Right = x; + } + + var topSplit = parts[4].Split('='); + if (topSplit.Length == 2) + { + double x; + double.TryParse(topSplit[1], out x); + Plate.EdgeSpacing.Top = x; + } + } + + private void ParsePlateSize(string data) + { + var quadrantIndex = data.IndexOf("QUADRANT"); + + if (quadrantIndex != -1) + { + var plateData = data.Remove(quadrantIndex); + var plateDataSplit = plateData.Split('='); + if (plateDataSplit.Length == 2) + { + Size plateSize; + Size.TryParse(plateDataSplit[1], out plateSize); + Plate.Size = plateSize; + } + + var quadrantData = data.Remove(0, quadrantIndex); + var quadrantDataSplit = quadrantData.Split('='); + if (quadrantDataSplit.Length == 2) + { + int quadrant; + int.TryParse(quadrantDataSplit[1], out quadrant); + Plate.Quadrant = quadrant; + } + } + else + { + var plateDataSplit = data.Split('='); + if (plateDataSplit.Length == 2) + { + Size plateSize; + Size.TryParse(plateDataSplit[1], out plateSize); + Plate.Size = plateSize; + } + } + } + + private void ParseMachine(string data) + { + var parts = data.Split(','); + + if (parts.Length != 2) + return; + + var machineSplit = parts[0].Split('='); + if (machineSplit.Length == 2) + { + int num; + int.TryParse(machineSplit[1], out num); + Plate.Machine.Id = num; + } + + Plate.Machine.Name = parts[1].Trim(); + } + + private void ParseMaterial(string data) + { + var parts = data.Split('='); + + if (parts.Length != 2) + return; + + int material; + int.TryParse(parts[1], out material); + + Plate.Material.Id = material; + } + + private void ParseGrade(string data) + { + var parts = data.Split('='); + + if (parts.Length != 2) + return; + + Plate.Material.Grade = parts[1].Trim(); + } + + private void ParseDescription(string data) + { + var parts = data.Split('='); + + if (parts.Length != 2) + return; + + Plate.Description = parts[1].Trim(); + } + + private void ParseThickness(string data) + { + var parts = data.Split('='); + + if (parts.Length != 2) + return; + + double thickness; + double.TryParse(parts[1], out thickness); + + Plate.Thickness = thickness; + } + + private void ParseDensity(string data) + { + var parts = data.Split('='); + + if (parts.Length != 2) + return; + + double density; + double.TryParse(parts[1], out density); + + Plate.Material.Density = density; + } + + private void ParseTorchCount(string data) + { + var parts = data.Split('='); + + if (parts.Length != 2) + return; + + int torchCount; + int.TryParse(parts[1], out torchCount); + + Plate.TorchCount = torchCount; + } + } +} \ No newline at end of file diff --git a/PepLib.Core/IO/ProgramReader.cs b/PepLib.Core/IO/ProgramReader.cs new file mode 100644 index 0000000..f80a0d7 --- /dev/null +++ b/PepLib.Core/IO/ProgramReader.cs @@ -0,0 +1,404 @@ +using System.Collections.Generic; +using System.IO; +using System.Text; +using PepLib.Codes; + +namespace PepLib.IO +{ + internal sealed class ProgramReader + { + private const int BufferSize = 200; + private int codeIndex; + private CodeBlock block; + private CodeSection section; + + public Program Program { get; private set; } + + public ProgramReader() + { + Program = new Program(); + } + + public ProgramReader(Program program) + { + Program = program; + } + + public void Read(Stream stream) + { + foreach (var line in GetLines(stream)) + { + block = ParseBlock(line); + ProcessCurrentBlock(); + } + } + + private IEnumerable GetLines(Stream stream) + { + var buffer = new byte[BufferSize]; + + while (stream.Read(buffer, 0, BufferSize) > 0) + { + yield return Encoding.ASCII.GetString(buffer); + } + } + + private CodeBlock ParseBlock(string line) + { + var block = new CodeBlock(); + Code code = null; + + for (int i = 0; i < line.Length; ++i) + { + var c = line[i]; + + if (char.IsLetter(c)) + block.Add((code = new Code(c))); + else if (c == ':') + { + block.Add((new Code(c, line.Remove(0, i + 1).Trim()))); + break; + } + else if (code != null) + code.Value += c; + } + + return block; + } + + private void ProcessCurrentBlock() + { + var code = GetFirstCode(); + + while (code != null) + { + switch (code.Id) + { + case ':': + Program.Add(new Comment(code.Value)); + code = GetNextCode(); + break; + + case 'G': + int value = int.Parse(code.Value); + + switch (value) + { + case 0: + case 1: + section = CodeSection.Line; + ReadLine(value == 0); + code = GetCurrentCode(); + break; + + case 2: + case 3: + section = CodeSection.Arc; + ReadArc(value == 2 ? RotationType.CW : RotationType.CCW); + code = GetCurrentCode(); + break; + + case 92: + section = CodeSection.SubProgram; + ReadSubProgram(); + code = GetCurrentCode(); + break; + + case 40: + Program.Add(new SetKerf() { Kerf = KerfType.None }); + code = GetNextCode(); + break; + + case 41: + Program.Add(new SetKerf() { Kerf = KerfType.Left }); + code = GetNextCode(); + break; + + case 42: + Program.Add(new SetKerf() { Kerf = KerfType.Right }); + code = GetNextCode(); + break; + + default: + code = GetNextCode(); + break; + } + break; + + case 'F': + Program.Add(new SetFeedrate() { Value = double.Parse(code.Value) }); + code = GetNextCode(); + break; + + default: + code = GetNextCode(); + break; + } + } + } + + private void ReadLine(bool isRapid) + { + double x = 0; + double y = 0; + var type = EntityType.Cut; + + while (section == CodeSection.Line) + { + var code = GetNextCode(); + + if (code == null) + { + section = CodeSection.Unknown; + break; + } + + switch (code.Id) + { + case 'X': + x = double.Parse(code.Value); + break; + + case 'Y': + y = double.Parse(code.Value); + break; + + case ':': + { + var value = code.Value.Trim().ToUpper(); + + switch (value) + { + case "EXTERNAL LEAD-IN": + type = EntityType.ExternalLeadin; + break; + + case "EXTERNAL LEAD-OUT": + type = EntityType.ExternalLeadout; + break; + + case "INTERNAL LEAD-IN": + type = EntityType.InternalLeadin; + break; + + case "INTERNAL LEAD-OUT": + type = EntityType.InternalLeadout; + break; + + case "DISPLAY": + type = EntityType.Display; + break; + } + break; + } + + default: + section = CodeSection.Unknown; + break; + } + } + + if (isRapid) + Program.Add(new RapidMove(x, y)); + else + Program.Add(new LinearMove(x, y) { Type = type }); + } + + private void ReadArc(RotationType rotation) + { + double x = 0; + double y = 0; + double i = 0; + double j = 0; + var type = EntityType.Cut; + + while (section == CodeSection.Arc) + { + var code = GetNextCode(); + + if (code == null) + { + section = CodeSection.Unknown; + break; + } + + switch (code.Id) + { + case 'X': + x = double.Parse(code.Value); + break; + + case 'Y': + y = double.Parse(code.Value); + break; + + case 'I': + i = double.Parse(code.Value); + break; + + case 'J': + j = double.Parse(code.Value); + break; + + case ':': + { + var value = code.Value.Trim().ToUpper(); + + switch (value) + { + case "EXTERNAL LEAD-IN": + type = EntityType.ExternalLeadin; + break; + + case "EXTERNAL LEAD-OUT": + type = EntityType.ExternalLeadout; + break; + + case "INTERNAL LEAD-IN": + type = EntityType.InternalLeadin; + break; + + case "INTERNAL LEAD-OUT": + type = EntityType.InternalLeadout; + break; + + case "DISPLAY": + type = EntityType.Display; + break; + } + break; + } + + default: + section = CodeSection.Unknown; + break; + } + } + + Program.Add(new CircularMove() + { + EndPoint = new Vector(x, y), + CenterPoint = new Vector(i, j), + Rotation = rotation, + Type = type + }); + } + + private void ReadSubProgram() + { + int l = 0; + int r = 0; + double p = 0; + + while (section == CodeSection.SubProgram) + { + var code = GetNextCode(); + + if (code == null) + { + section = CodeSection.Unknown; + break; + } + + switch (code.Id) + { + case 'L': + l = int.Parse(code.Value); + break; + + case 'R': + r = int.Parse(code.Value); + break; + + case 'P': + p = double.Parse(code.Value); + break; + + default: + section = CodeSection.Unknown; + break; + } + } + + Program.Add(new SubProgramCall() { LoopId = l, RepeatCount = r, Rotation = p }); + } + + private Code GetNextCode() + { + codeIndex++; + + if (codeIndex >= block.Count) + return null; + + return block[codeIndex]; + } + + private Code GetCurrentCode() + { + if (codeIndex >= block.Count) + return null; + + return block[codeIndex]; + } + + private Code GetFirstCode() + { + if (block.Count == 0) + return null; + + codeIndex = 0; + + return block[codeIndex]; + } + + private class Code + { + public Code(char id) + { + Id = id; + Value = string.Empty; + } + + public Code(char id, string value) + { + Id = id; + Value = value; + } + + public char Id { get; private set; } + + public string Value { get; set; } + + public override string ToString() + { + return Id + Value; + } + } + + private class CodeBlock : List + { + public void Add(char id, string value) + { + Add(new Code(id, value)); + } + + public override string ToString() + { + var builder = new StringBuilder(); + + foreach (var code in this) + builder.Append(code.ToString() + " "); + + return builder.ToString(); + } + } + + private enum CodeSection + { + Unknown, + Arc, + Line, + SubProgram + } + } +} diff --git a/PepLib.Core/IO/ReportReader.cs b/PepLib.Core/IO/ReportReader.cs new file mode 100644 index 0000000..c2d39c7 --- /dev/null +++ b/PepLib.Core/IO/ReportReader.cs @@ -0,0 +1,378 @@ +using System; +using System.Diagnostics; +using System.IO; + +namespace PepLib.IO +{ + internal sealed class ReportReader + { + public Report Report { get; private set; } + + public ReportReader() + { + Report = new Report(); + } + + public ReportReader(Report report) + { + Report = report; + } + + public void Read(Stream stream) + { + var reader = new StreamReader(stream); + + Report.Drawing dwg = null; + Report.Plate plt = null; + + var section = Section.Unknown; + + string line; + + while ((line = reader.ReadLine()) != null) + { + var equalIndex = line.IndexOf('='); + + if (equalIndex != -1) + { + var valueIndex = equalIndex + 1; + var key = line.Substring(0, equalIndex).Trim(); + var value = line.Substring(valueIndex, line.Length - valueIndex).Trim(); + + switch (section) + { + case Section.NestHeader: + ReadNestHeaderData(key, value); + break; + + case Section.NestedPlates: + ReadNestedPlatesData(key, value, plt); + break; + + case Section.QuantitiesNested: + ReadQuantitiesNestedData(key, value, dwg); + break; + + case Section.Unknown: + break; + } + } + else + { + var category = line.Trim(); + + switch (category) + { + case "Nest header": + section = Section.NestHeader; + continue; + case "Nested plates": + section = Section.NestedPlates; + continue; + case "Quantities nested": + section = Section.QuantitiesNested; + continue; + } + + switch (section) + { + case Section.NestedPlates: + if (category.StartsWith("Plate")) + Report.Plates.Add((plt = new Report.Plate())); + break; + + case Section.QuantitiesNested: + if (category.StartsWith("Drawing")) + Report.Drawings.Add((dwg = new Report.Drawing())); + break; + + default: + Debug.WriteLine("Unknown category: " + category); + break; + } + } + } + } + + public void Read(string nestFile) + { + if (!File.Exists(nestFile)) + { + var msg = string.Format("File Not Found: {0}", nestFile); + throw new FileNotFoundException(msg); + } + + Stream stream = null; + string name; + + try + { + ZipHelper.ExtractByExtension(nestFile, ".report", out name, out stream); + Read(stream); + } + finally + { + if (stream != null) + stream.Close(); + } + } + + private void ReadNestHeaderData(string key, string value) + { + switch (key) + { + case "Program": + Report.Name = value; + break; + + case "Customer name": + Report.Customer = value; + break; + + case "Date programmed": + DateTime date; + DateTime.TryParse(value, out date); + Report.DateProgrammed = date; + break; + + case "Material": + Report.Material = value; + break; + + case "Programmed by": + Report.ProgrammedBy = value; + break; + + case "Machine": + Report.Machine = value; + break; + + case "Comments": + Report.Comments = value; + break; + + case "Remarks": + Report.Remarks = value; + break; + + default: + Debug.WriteLine(string.Format("Report.ReadNestHeaderData: \"{0}\" not implemented", key)); + break; + } + } + + private void ReadNestedPlatesData(string key, string value, Report.Plate plt) + { + switch (key) + { + case "Plate number": + plt.Name = value; + break; + + case "Thickness": + plt.Thickness = ParseDouble(value); + break; + + case "Plate Size": + ReadPlateSize(value, plt); + break; + + case "Material": + plt.MaterialNumber = ParseInt32(value); + break; + + case "Grade": + plt.MaterialGrade = value; + break; + + case "Material Description": + plt.MaterialDescription = value; + break; + + case "Dup plates": + plt.Quantity = int.Parse(value); + break; + + case "Plate Util": + plt.PlateUtilization = ParsePercent(value); + break; + + case "Material Util": + plt.MaterialUtilization = ParsePercent(value); + break; + + case "Total Area1": + plt.Area1 = ParseDouble(value); + break; + + case "Total Area2": + plt.Area2 = ParseDouble(value); + break; + + case "Bubble pierces": + plt.BubblePierceCount = ParseInt32(value); + break; + + case "Total cutting time": + ReadCuttingTime(value, Report); + break; + + case "Cutting feedrate": + Report.CutFeedrate = ParseInt32(value); + break; + + case "Rapid feedrate": + Report.RapidFeedrate = ParseInt32(value); + break; + + default: + Debug.WriteLine(string.Format("Report.ReadNestedPlatesData: \"{0}\" not implemented", key)); + break; + } + } + + private void ReadQuantitiesNestedData(string key, string value, Report.Drawing dwg) + { + switch (key) + { + case "Customer Name": + dwg.Customer = value; + break; + + case "Drawing Name": + dwg.Name = value; + break; + + case "Revision": + dwg.Revision = value; + break; + + case "Qty Req": + dwg.QtyRequired = ParseInt32(value); + break; + + case "Qty Nstd": + dwg.QtyNested = ParseInt32(value); + break; + + case "# of Pierces": + dwg.PierceCount = ParseInt32(value); + break; + + case "Intersections": + dwg.IntersectionCount = ParseInt32(value); + break; + + case "Area1*": + dwg.Area1 = ParseDouble(value); + break; + + case "Area2**": + dwg.Area2 = ParseDouble(value); + break; + + case "% of Material": + dwg.PercentOfMaterial = ParsePercent(value); + break; + + case "% of Time": + dwg.PercentOfCutTime = ParsePercent(value); + dwg.TotalCutTime = + TimeSpan.FromTicks((long)(Report.TotalCutTime.Ticks * dwg.PercentOfCutTime / 100.0)); + break; + + default: + Debug.WriteLine(string.Format("Report.ReadQuantitiesNestedData: \"{0}\" not implemented", key)); + break; + } + } + + private void ReadPlateSize(string value, Report.Plate plt) + { + var a = value.ToUpper().Split('X'); + + var x = float.Parse(a[0]); + var y = float.Parse(a[1]); + + if (x < y) + { + plt.Width = x; + plt.Length = y; + } + else + { + plt.Width = y; + plt.Length = x; + } + } + + private void ReadCuttingTime(string value, Report report) + { + var parts = value.Split(','); + + int hrs = 0, min = 0, sec = 0; + + foreach (var part in parts) + { + if (part.Contains("hr")) + hrs = int.Parse(part.Remove(part.IndexOf("hr"))); + + else if (part.Contains("min")) + min = int.Parse(part.Remove(part.IndexOf("min"))); + + else if (part.Contains("sec")) + sec = int.Parse(part.Remove(part.IndexOf("sec"))); + } + + report.TotalCutTime = new TimeSpan(hrs, min, sec); + } + + private static double ParsePercent(string s, double defaultValue = 0.0) + { + var t = s.TrimEnd('%', ' '); + double f; + + if (!double.TryParse(t, out f)) + { + Debug.WriteLine("Failed to convert \"" + s + "\" from percent string to double"); + return defaultValue; + } + + return f; + } + + private static double ParseDouble(string s, double defaultValue = 0.0) + { + double f; + + if (!double.TryParse(s, out f)) + { + Debug.WriteLine("Failed to convert \"" + s + "\" from string to double"); + return defaultValue; + } + + return f; + } + + private static int ParseInt32(string s, int defaultValue = 0) + { + int i; + + if (!int.TryParse(s, out i)) + { + Debug.WriteLine("Failed to convert \"" + s + "\" from string to int"); + return defaultValue; + } + + return i; + } + + private enum Section + { + Unknown, + NestHeader, + NestedPlates, + QuantitiesNested, + } + } +} diff --git a/PepLib.Core/IniConfig.cs b/PepLib.Core/IniConfig.cs new file mode 100644 index 0000000..76e2e34 --- /dev/null +++ b/PepLib.Core/IniConfig.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace PepLib +{ + public class IniConfig + { + public List Nodes; + + public IniConfig() + { + Nodes = new List(); + } + + private static int LeadingWhitespaceCount(string s) + { + for (int i = 0; i < s.Length; ++i) + if (s[i] != ' ') return i; + + return 0; + } + + public Node FindNode(string path) + { + return FindNode(path, Nodes); + } + + private Node FindNode(string path, List nodes) + { + var a = path.Split('/'); + + var b = nodes.FirstOrDefault(node => + { + if (node is KeyNode) + { + var c = node as KeyNode; + return c.Name.ToUpper() == a[0].ToUpper(); + } + else + { + return node.Value == a[0].Trim(); + } + }); + + string path2 = string.Empty; + + for (int i = 1; i < a.Length; ++i) + path2 += a[i] + '/'; + + if (b == null || a.Length == 1) + return b; + else + return FindNode(path2.TrimEnd('/'), b.Children); + } + + public static IniConfig Load(string file) + { + var doc = new IniConfig(); + var reader = new StreamReader(file); + + Node currentNode = null; + string line; + + while ((line = reader.ReadLine()) != null) + { + int spaces = LeadingWhitespaceCount(line) / 2; + var node = new Node(); + node.Value = line.Trim(); + + var keyNode = KeyNode.Parse(node); + + if (keyNode != null) + node = keyNode; + + int currentdepth = currentNode != null ? currentNode.Level : 0; + + if (spaces == 0) + doc.Nodes.Add(node); + else if (spaces == currentdepth) + currentNode.Parent.AddChild(node); + else if (spaces > currentdepth) + currentNode.AddChild(node); + else if (spaces < currentdepth) + { + var n = currentNode.Parent; + + while (spaces < n.Level) + n = n.Parent; + + n.Parent.AddChild(node); + } + + currentNode = node; + } + + reader.Close(); + + return doc; + } + } +} diff --git a/PepLib.Core/KerfType.cs b/PepLib.Core/KerfType.cs new file mode 100644 index 0000000..90e9b53 --- /dev/null +++ b/PepLib.Core/KerfType.cs @@ -0,0 +1,10 @@ + +namespace PepLib +{ + public enum KerfType + { + None, + Left, + Right + } +} diff --git a/PepLib.Core/Loop.cs b/PepLib.Core/Loop.cs new file mode 100644 index 0000000..01b2975 --- /dev/null +++ b/PepLib.Core/Loop.cs @@ -0,0 +1,57 @@ +using System; +using PepLib.Codes; + +namespace PepLib +{ + public class Loop : Program + { + public Loop() + { + Mode = ProgrammingMode.Incremental; + } + + public string Name { get; set; } + + public Vector ReferencePoint { get; set; } + + public DateTime LastReferenceDate { get; set; } + + public string DrawingName { get; set; } + + public string DxfPath { get; set; } + + public override void Rotate(double angle) + { + base.Rotate(angle); + ReferencePoint = ReferencePoint.Rotate(angle); + } + + public override void Rotate(double angle, Vector origin) + { + base.Rotate(angle, origin); + ReferencePoint = ReferencePoint.Rotate(angle); + } + + public object Clone() + { + var loop = new Loop() + { + Name = this.Name, + ReferencePoint = this.ReferencePoint, + LastReferenceDate = this.LastReferenceDate, + DrawingName = this.DrawingName, + DxfPath = this.DxfPath, + Rotation = this.Rotation + }; + + var codes = new ICode[this.Count]; + + for (int i = 0; i < this.Count; ++i) + codes[i] = this[i].Clone(); + + loop.AddRange(codes); + + return loop; + } + } +} diff --git a/PepLib.Core/Machine.cs b/PepLib.Core/Machine.cs new file mode 100644 index 0000000..8fbb0c7 --- /dev/null +++ b/PepLib.Core/Machine.cs @@ -0,0 +1,9 @@ +namespace PepLib +{ + public class Machine + { + public int Id { get; set; } + + public string Name { get; set; } + } +} diff --git a/PepLib.Core/Material.cs b/PepLib.Core/Material.cs new file mode 100644 index 0000000..e219c2d --- /dev/null +++ b/PepLib.Core/Material.cs @@ -0,0 +1,11 @@ +namespace PepLib +{ + public class Material + { + public int Id { get; set; } + + public string Grade { get; set; } + + public double Density { get; set; } + } +} diff --git a/PepLib.Core/MathHelper.cs b/PepLib.Core/MathHelper.cs new file mode 100644 index 0000000..70071ee --- /dev/null +++ b/PepLib.Core/MathHelper.cs @@ -0,0 +1,55 @@ +using System; + +namespace PepLib +{ + public static class MathHelper + { + public const double HalfPI = Math.PI * 0.5; + public const double TwoPI = Math.PI * 2.0; + + public static double NormalizeAngleRad(double angle) + { + double r = angle % TwoPI; + return r < 0 ? TwoPI + r : r; + } + + public static double NormalizeAngleDeg(double angle) + { + double r = angle % 360.0; + return r < 0 ? 360.0 + r : r; + } + + public static bool IsAngleBetween(double angle, double a1, double a2, bool reversed = false) + { + if (reversed) + Generic.Swap(ref a1, ref a2); + + var diff = NormalizeAngleRad(a2 - a1); + + // full circle + if (a2.IsEqualTo(a1)) + return true; + + a1 = NormalizeAngleRad(angle - a1); + a2 = NormalizeAngleRad(a2 - angle); + + return diff >= a1 - Tolerance.Epsilon || + diff >= a2 - Tolerance.Epsilon; + } + + public static double RoundDownToNearest(double num, double factor) + { + return factor == 0 ? num : Math.Floor(num / factor) * factor; + } + + public static double RoundUpToNearest(double num, double factor) + { + return factor == 0 ? num : Math.Ceiling(num / factor) * factor; + } + + public static double RoundToNearest(double num, double factor) + { + return factor == 0 ? num : Math.Round(num / factor) * factor; + } + } +} diff --git a/PepLib.Core/Nest.cs b/PepLib.Core/Nest.cs new file mode 100644 index 0000000..18be77b --- /dev/null +++ b/PepLib.Core/Nest.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; +using System.IO; +using PepLib.Codes; +using PepLib.IO; + +namespace PepLib +{ + public class Nest + { + public Nest() + { + Report = new Report(); + Loops = new List(); + Plates = new List(); + Drawings = new List(); + } + + public Report Report { get; set; } + + public List Loops { get; set; } + + public List Plates { get; set; } + + public List Drawings { get; set; } + + public void ResolveLoops() + { + for (int i = 0; i < Loops.Count; ++i) + { + var loop = Loops[i]; + ResolveLoops(loop); + } + } + + private void ResolveLoops(Program pgm) + { + for (int i = 0; i < pgm.Count; ++i) + { + var code = pgm[i]; + + if (code.CodeType() != CodeType.SubProgramCall) + continue; + + var subpgmcall = (SubProgramCall)code; + + var loop = GetLoop(subpgmcall.LoopId); + + if (loop == null) + throw new Exception("Loop not found"); + + subpgmcall.Loop = loop; + } + } + + public int GetQtyNested(string drawing) + { + int qty = 0; + + foreach (var plate in Plates) + qty += plate.GetQtyNested(drawing); + + return qty; + } + + private Loop GetLoop(string name) + { + for (int i = 0; i < Loops.Count; ++i) + { + if (Loops[i].Name == name) + return Loops[i]; + } + + return null; + } + + public Loop GetLoop(int id) + { + var ext = $".loop-{id.ToString().PadLeft(3, '0')}"; + + for (int i = 0; i < Loops.Count; ++i) + { + if (Loops[i].Name.EndsWith(ext)) + return Loops[i]; + } + + return null; + } + + public static Nest Load(string nestfile) + { + var reader = new NestReader(); + reader.Read(nestfile); + return reader.Nest; + } + + public static Nest Load(Stream stream) + { + var reader = new NestReader(); + reader.Read(stream); + return reader.Nest; + } + + public static bool TryLoad(string nestfile, out Nest nest) + { + try + { + nest = Load(nestfile); + } + catch (Exception) + { + nest = null; + return false; + } + + return true; + } + + public static bool TryLoad(Stream stream, out Nest nest) + { + try + { + nest = Load(stream); + } + catch (Exception) + { + nest = null; + return false; + } + + return true; + } + } +} diff --git a/PepLib.Core/NestDrawing.cs b/PepLib.Core/NestDrawing.cs new file mode 100644 index 0000000..f87e946 --- /dev/null +++ b/PepLib.Core/NestDrawing.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PepLib +{ + public class NestDrawing + { + public string Name { get; set; } + + public int QtyRequired { get; set; } + } +} diff --git a/PepLib.Core/NestIndex.cs b/PepLib.Core/NestIndex.cs new file mode 100644 index 0000000..8e6780f --- /dev/null +++ b/PepLib.Core/NestIndex.cs @@ -0,0 +1,68 @@ +using System.Collections.Generic; +using System.IO; +using System.Text; +using PepLib.IO; + +namespace PepLib +{ + public class NestIndex + { + public string Directory { get; set; } + + public List Entries; + + public NestIndex() + { + Entries = new List(); + } + + public string GetPath(NestInfo entry) + { + return Path.Combine(Directory, entry.Name + ".zip"); + } + + public static NestIndex LoadFromDir(string directory) + { + var file = Path.Combine(directory, "pepfiles.lfn"); + return Load(file); + } + + public static NestIndex Load(string file) + { + if (!File.Exists(file)) + return null; + + var index = new NestIndex() { Directory = Path.GetDirectoryName(file) }; + var stream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + var reader = new StreamReader(stream); + var buffer = new char[4000]; + + while (reader.Read(buffer, 0, buffer.Length) > 0) + { + var memstream = new MemoryStream(Encoding.ASCII.GetBytes(buffer)); + var inforeader = new NestInfoReader(); + + inforeader.Read(memstream); + index.Entries.Add(inforeader.Info); + } + + reader.Close(); + + return index; + } + + public static NestIndex Build(string directory) + { + var index = new NestIndex() { Directory = directory }; + + foreach (var file in System.IO.Directory.GetFiles(directory, "*.zip")) + { + var reader = new NestInfoReader(); + reader.Read(file); + index.Entries.Add(reader.Info); + } + + return index; + } + } +} diff --git a/PepLib.Core/NestInfo.cs b/PepLib.Core/NestInfo.cs new file mode 100644 index 0000000..231dd6b --- /dev/null +++ b/PepLib.Core/NestInfo.cs @@ -0,0 +1,99 @@ +using System; +using System.IO; +using PepLib.IO; + +namespace PepLib +{ + public class NestInfo + { + public string Name { get; set; } + + public DateTime DateCreated { get; set; } + + public DateTime DateLastModified { get; set; } + + public StatusType Status { get; set; } + + public int LoopCount { get; set; } + + public int ProgramCount { get; set; } + + public int PlateCount { get; set; } + + public string Comments { get; set; } + + public string Customer { get; set; } + + public string ProgrammedBy { get; set; } + + public int MaterialNumber { get; set; } + + public string MaterialGrade { get; set; } + + public string Notes { get; set; } + + public string DefaultPlateSize { get; set; } + + public string Kerf { get; set; } + + public string PostedAs { get; set; } + + public string Errors { get; set; } + + public string UserDefined1 { get; set; } + + public string UserDefined2 { get; set; } + + public string UserDefined3 { get; set; } + + public string UserDefined4 { get; set; } + + public string UserDefined5 { get; set; } + + public string UserDefined6 { get; set; } + + public static NestInfo Load(string nestFile) + { + var reader = new NestInfoReader(); + reader.Read(nestFile); + return reader.Info; + } + + public static NestInfo Load(Stream stream) + { + var reader = new NestInfoReader(); + reader.Read(stream); + return reader.Info; + } + + public static bool TryLoad(string nestFile, out NestInfo nestInfo) + { + try + { + nestInfo = Load(nestFile); + } + catch (Exception) + { + nestInfo = null; + return false; + } + + return true; + } + + public static bool TryLoad(Stream stream, out NestInfo nestInfo) + { + try + { + nestInfo = Load(stream); + } + catch (Exception) + { + nestInfo = null; + return false; + } + + return true; + } + } +} \ No newline at end of file diff --git a/PepLib.Core/Node.cs b/PepLib.Core/Node.cs new file mode 100644 index 0000000..1ee9070 --- /dev/null +++ b/PepLib.Core/Node.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace PepLib +{ + public class Node + { + private Node parent; + + public List Children; + + public Node() + { + Children = new List(); + } + + public Node Parent + { + get { return parent; } + set + { + parent = value; + UpdateDepth(); + } + } + + public string Value { get; set; } + + private void UpdateDepth() + { + if (Parent != null) + Level = Parent.Level + 1; + else + Level = 0; + + foreach (var node in Children) + node.Parent = this; + } + + public int Level { get; protected set; } + + public void AddChild(Node node) + { + node.Parent = this; + Children.Add(node); + } + + public void Write(TextWriter writer) + { + writer.WriteLine("".PadLeft(Level * 2) + this.ToString()); + + foreach (var node in Children) + node.Write(writer); + } + + public override string ToString() + { + return Value; + } + } + + public class KeyNode : Node + { + public string Name; + + public static KeyNode Parse(Node node) + { + var index = node.Value.IndexOf('='); + + if (index == -1) + return null; + + return new KeyNode() + { + Name = node.Value.Remove(index), + Value = node.Value.Remove(0, index + 1).Trim() + }; + } + + public override string ToString() + { + return string.Format("{0}={1}", Name, Value); + } + } +} diff --git a/PepLib.Core/Part.cs b/PepLib.Core/Part.cs new file mode 100644 index 0000000..4dc10ee --- /dev/null +++ b/PepLib.Core/Part.cs @@ -0,0 +1,125 @@ +using System; + +namespace PepLib +{ + public class Part : IMovable + { + private Loop baseLoop; + private Vector location; + + private Part() + { + BoundingBox = new Box(); + } + + public Box BoundingBox { get; protected set; } + + public Vector Location + { + get { return location; } + set + { + BoundingBox.Offset(value - location); + location = value; + } + } + + public string Name + { + get { return baseLoop.Name; } + set { baseLoop.Name = value; } + } + + /// + /// Reference point relative to the part location. + /// + public Vector ReferencePoint + { + get { return baseLoop.ReferencePoint; } + set { baseLoop.ReferencePoint = value; } + } + + /// + /// Reference point relative to the zero point. + /// + public Vector AbsoluteReferencePoint + { + get { return baseLoop.ReferencePoint + location; } + set { baseLoop.ReferencePoint = value - location; } + } + + public DateTime LastReferenceDate + { + get { return baseLoop.LastReferenceDate; } + set { baseLoop.LastReferenceDate = value; } + } + + public string DrawingName + { + get { return baseLoop.DrawingName; } + set { baseLoop.DrawingName = value; } + } + + public string DxfPath + { + get { return baseLoop.DxfPath; } + set { baseLoop.DxfPath = value; } + } + + public double Rotation + { + get { return baseLoop.Rotation; } + } + + public bool IsDisplayOnly { get; set; } = false; + + public void Rotate(double angle) + { + baseLoop.Rotate(angle); + location = Location.Rotate(angle); + UpdateBounds(); + } + + public void Rotate(double angle, Vector origin) + { + baseLoop.Rotate(angle); + location = Location.Rotate(angle, origin); + UpdateBounds(); + } + + public void Offset(double x, double y) + { + location = new Vector(location.X + x, location.Y + y); + BoundingBox.Offset(x, y); + } + + public void Offset(Vector voffset) + { + location += voffset; + BoundingBox.Offset(voffset); + } + + public Program Program + { + get { return baseLoop; } + } + + public void UpdateBounds() + { + BoundingBox = baseLoop.GetBoundingBox(); + BoundingBox.Offset(Location); + } + + public static Part Create(Loop loop, Vector location, double rotation = 0.0) + { + var part = new Part(); + part.baseLoop = (Loop)loop.Clone(); + part.baseLoop.Mode = ProgrammingMode.Incremental; + part.baseLoop.Rotate(rotation); + part.Location = location; + part.UpdateBounds(); + + return part; + } + } +} diff --git a/PepLib.Core/PartListExtensions.cs b/PepLib.Core/PartListExtensions.cs new file mode 100644 index 0000000..b7f0800 --- /dev/null +++ b/PepLib.Core/PartListExtensions.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PepLib +{ + public static class PartListExtensions + { + public static Box GetBoundingBox(this List parts) + { + if (parts.Count == 0) + return new Box(); + + var firstpart = parts[0]; + + double minX = firstpart.BoundingBox.X; + double minY = firstpart.BoundingBox.Y; + double maxX = firstpart.BoundingBox.X + firstpart.BoundingBox.Width; + double maxY = firstpart.BoundingBox.Y + firstpart.BoundingBox.Height; + + foreach (var part in parts) + { + var box = part.BoundingBox; + + if (box.Left < minX) + minX = box.Left; + + if (box.Right > maxX) + maxX = box.Right; + + if (box.Bottom < minY) + minY = box.Bottom; + + if (box.Top > maxY) + maxY = box.Top; + } + + return new Box(minX, minY, maxX - minX, maxY - minY); + } + } +} diff --git a/PepLib.Core/PepLib.Core.csproj b/PepLib.Core/PepLib.Core.csproj new file mode 100644 index 0000000..75c261e --- /dev/null +++ b/PepLib.Core/PepLib.Core.csproj @@ -0,0 +1,18 @@ + + + + net8.0 + enable + enable + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + diff --git a/PepLib.Core/Plate.cs b/PepLib.Core/Plate.cs new file mode 100644 index 0000000..354edf8 --- /dev/null +++ b/PepLib.Core/Plate.cs @@ -0,0 +1,266 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace PepLib +{ + public class Plate : IMovable + { + public Plate() + : this(60, 120) + { + } + + public Plate(double width, double length) + : this(new Size(width, length)) + { + } + + public Plate(Size size) + { + EdgeSpacing = new Spacing(); + Size = size; + Machine = new Machine(); + Material = new Material(); + Parts = new List(); + Quadrant = 1; + } + + public string Name { get; set; } + + public string PostedFiles { get; set; } + + public string HeatLot { get; set; } + + public double Thickness { get; set; } + + public double PartSpacing { get; set; } + + public Spacing EdgeSpacing { get; set; } + + public Size Size { get; set; } + + public Machine Machine { get; set; } + + public Material Material { get; set; } + + public List Parts { get; set; } + + public string Description { get; set; } + + public int Duplicates { get; set; } + + public int Quadrant { get; set; } + + public int TorchCount { get; set; } + + public void Rotate90CCW(bool keepSameQuadrant = true) + { + Size = new Size(Size.Width, Size.Height); + + Rotate(MathHelper.HalfPI); + + if (keepSameQuadrant) + { + switch (Quadrant) + { + case 1: + Offset(Size.Width, 0); + break; + + case 2: + Offset(0, Size.Height); + break; + + case 3: + Offset(-Size.Width, 0); + break; + + case 4: + Offset(0, -Size.Height); + break; + } + } + else + { + Quadrant = Quadrant > 3 ? 1 : Quadrant + 1; + } + } + + public void Rotate90CW(bool keepSameQuadrant = true) + { + const double oneAndHalfPI = Math.PI * 1.5; + + Size = new Size(Size.Width, Size.Height); + + Rotate(oneAndHalfPI); + + if (keepSameQuadrant) + { + switch (Quadrant) + { + case 1: + Offset(0, Size.Height); + break; + + case 2: + Offset(-Size.Width, 0); + break; + + case 3: + Offset(0, -Size.Height); + break; + + case 4: + Offset(Size.Width, 0); + break; + } + } + else + { + Quadrant = Quadrant < 2 ? 4 : Quadrant - 1; + } + } + + public void Rotate180(bool keepSameQuadrant = true) + { + if (keepSameQuadrant) + { + Vector centerpt; + + switch (Quadrant) + { + case 1: + centerpt = new Vector(Size.Width * 0.5, Size.Height * 0.5); + break; + + case 2: + centerpt = new Vector(-Size.Width * 0.5, Size.Height * 0.5); + break; + + case 3: + centerpt = new Vector(-Size.Width * 0.5, -Size.Height * 0.5); + break; + + case 4: + centerpt = new Vector(Size.Width * 0.5, -Size.Height * 0.5); + break; + + default: + return; + } + + Rotate(Math.PI, centerpt); + } + else + { + Rotate(Math.PI); + Quadrant = (Quadrant + 2) % 4; + + if (Quadrant == 0) + Quadrant = 4; + } + } + + public void Rotate(double angle) + { + for (int i = 0; i < Parts.Count; ++i) + { + var part = Parts[i]; + part.Rotate(angle); + } + } + + public void Rotate(double angle, Vector origin) + { + for (int i = 0; i < Parts.Count; ++i) + { + var part = Parts[i]; + part.Rotate(angle, origin); + } + } + + public void Offset(double x, double y) + { + for (int i = 0; i < Parts.Count; ++i) + { + var part = Parts[i]; + part.Offset(x, y); + } + } + + public void Offset(Vector voffset) + { + for (int i = 0; i < Parts.Count; ++i) + { + var part = Parts[i]; + part.Offset(voffset); + } + } + + public int GetQtyNested(string drawing) + { + var name = drawing.ToUpper(); + + return Parts.Count(p => p.DrawingName.ToUpper() == name); + } + + public Box GetBoundingBox(bool includeParts) + { + var plateBox = new Box(); + + switch (Quadrant) + { + case 1: + plateBox.X = 0; + plateBox.Y = 0; + break; + + case 2: + plateBox.X = (float)-Size.Width; + plateBox.Y = 0; + break; + + case 3: + plateBox.X = (float)-Size.Width; + plateBox.Y = (float)-Size.Height; + break; + + case 4: + plateBox.X = 0; + plateBox.Y = (float)-Size.Height; + break; + + default: + return new Box(); + } + + plateBox.Width = Size.Width; + plateBox.Height = Size.Height; + + if (!includeParts) + return plateBox; + + var boundingBox = new Box(); + var partsBox = Parts.GetBoundingBox(); + + boundingBox.X = partsBox.Left < plateBox.Left + ? partsBox.Left + : plateBox.Left; + + boundingBox.Y = partsBox.Bottom < plateBox.Bottom + ? partsBox.Bottom + : plateBox.Bottom; + + boundingBox.Width = partsBox.Right > plateBox.Right + ? partsBox.Right - boundingBox.X + : plateBox.Right - boundingBox.X; + + boundingBox.Height = partsBox.Top > plateBox.Top + ? partsBox.Top - boundingBox.Y + : plateBox.Top - boundingBox.Y; + + return boundingBox; + } + } +} diff --git a/PepLib.Core/PlateListExtensions.cs b/PepLib.Core/PlateListExtensions.cs new file mode 100644 index 0000000..382eb01 --- /dev/null +++ b/PepLib.Core/PlateListExtensions.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; + +namespace PepLib +{ + public static class PlateListExtensions + { + public static void JoinLikePlates(this List plates) + { + START: + + for (int i = 0; i < plates.Count; ++i) + { + var p1 = plates[i]; + + for (int j = 0; j < plates.Count; ++j) + { + var p2 = plates[j]; + + if (i == j) + continue; + + if (p1.Width != p2.Width) + continue; + + if (p1.Length != p2.Length) + continue; + + if (p1.MaterialDescription != p2.MaterialDescription) + continue; + + if (p1.Thickness != p2.Thickness) + continue; + + p1.Quantity += p2.Quantity; + plates.Remove(p2); + goto START; + } + } + } + } +} diff --git a/PepLib.Core/Program.cs b/PepLib.Core/Program.cs new file mode 100644 index 0000000..6eedc5a --- /dev/null +++ b/PepLib.Core/Program.cs @@ -0,0 +1,367 @@ +using System.Collections.Generic; +using System.IO; +using PepLib.Codes; +using PepLib.IO; +using System; + +namespace PepLib +{ + public class Program : List, IMovable + { + private ProgrammingMode mode; + + public Program(ProgrammingMode mode = ProgrammingMode.Absolute) + { + Mode = mode; + } + + public ProgrammingMode Mode + { + get { return mode; } + set + { + if (value == ProgrammingMode.Absolute) + SetProgrammingModeAbs(); + else + SetProgrammingModeInc(); + } + } + + public double Rotation { get; protected set; } + + private void SetProgrammingModeInc() + { + if (mode == ProgrammingMode.Incremental) + return; + + var pos = new Vector(0, 0); + + for (int i = 0; i < Count; ++i) + { + var code = this[i]; + var motion = code as Motion; + + if (motion != null) + { + var pos2 = motion.EndPoint; + motion.Offset(-pos.X, -pos.Y); + pos = pos2; + } + } + + mode = ProgrammingMode.Incremental; + } + + private void SetProgrammingModeAbs() + { + if (mode == ProgrammingMode.Absolute) + return; + + var pos = new Vector(0, 0); + + for (int i = 0; i < Count; ++i) + { + var code = this[i]; + var motion = code as Motion; + + if (motion != null) + { + motion.Offset(pos); + pos = motion.EndPoint; + } + } + + mode = ProgrammingMode.Absolute; + } + + public virtual void Rotate(double angle) + { + var mode = Mode; + + SetProgrammingModeAbs(); + + for (int i = 0; i < Count; ++i) + { + var code = this[i]; + + if (code.CodeType() == CodeType.SubProgramCall) + { + var subpgm = (SubProgramCall)code; + + if (subpgm.Loop != null) + subpgm.Loop.Rotate(angle); + } + + if (code is IMovable == false) + continue; + + var code2 = (IMovable)code; + + code2.Rotate(angle); + } + + if (mode == ProgrammingMode.Incremental) + SetProgrammingModeInc(); + + Rotation = MathHelper.NormalizeAngleRad(Rotation + angle); + } + + public virtual void Rotate(double angle, Vector origin) + { + var mode = Mode; + + SetProgrammingModeAbs(); + + for (int i = 0; i < Count; ++i) + { + var code = this[i]; + + if (code.CodeType() == CodeType.SubProgramCall) + { + var subpgm = (SubProgramCall)code; + + if (subpgm.Loop != null) + subpgm.Loop.Rotate(angle); + } + + if (code is IMovable == false) + continue; + + var code2 = (IMovable)code; + + code2.Rotate(angle, origin); + } + + if (mode == ProgrammingMode.Incremental) + SetProgrammingModeInc(); + + Rotation = MathHelper.NormalizeAngleRad(Rotation + angle); + } + + public void Offset(double x, double y) + { + var mode = Mode; + + SetProgrammingModeAbs(); + + for (int i = 0; i < Count; ++i) + { + var code = this[i]; + + if (code is IMovable == false) + continue; + + var code2 = (IMovable)code; + + code2.Offset(x, y); + } + + if (mode == ProgrammingMode.Incremental) + SetProgrammingModeInc(); + } + + public void Offset(Vector voffset) + { + var mode = Mode; + + SetProgrammingModeAbs(); + + for (int i = 0; i < Count; ++i) + { + var code = this[i]; + + if (code is IMovable == false) + continue; + + var code2 = (IMovable)code; + + code2.Offset(voffset); + } + + if (mode == ProgrammingMode.Incremental) + SetProgrammingModeInc(); + } + + public Box GetBoundingBox() + { + var origin = new Vector(0, 0); + return GetBoundingBox(ref origin); + } + + private Box GetBoundingBox(ref Vector pos) + { + double minX = 0.0; + double minY = 0.0; + double maxX = 0.0; + double maxY = 0.0; + + for (int i = 0; i < Count; ++i) + { + var code = this[i]; + + switch (code.CodeType()) + { + case CodeType.LinearMove: + { + var line = (LinearMove)code; + var pt = Mode == ProgrammingMode.Absolute ? + line.EndPoint : + line.EndPoint + pos; + + if (pt.X > maxX) + maxX = pt.X; + else if (pt.X < minX) + minX = pt.X; + + if (pt.Y > maxY) + maxY = pt.Y; + else if (pt.Y < minY) + minY = pt.Y; + + pos = pt; + + break; + } + + case CodeType.RapidMove: + { + var line = (RapidMove)code; + var pt = Mode == ProgrammingMode.Absolute + ? line.EndPoint + : line.EndPoint + pos; + + if (pt.X > maxX) + maxX = pt.X; + else if (pt.X < minX) + minX = pt.X; + + if (pt.Y > maxY) + maxY = pt.Y; + else if (pt.Y < minY) + minY = pt.Y; + + pos = pt; + + break; + } + + case CodeType.CircularMove: + { + var arc = (CircularMove)code; + var radius = arc.CenterPoint.DistanceTo(arc.EndPoint); + + Vector endpt; + Vector centerpt; + + if (Mode == ProgrammingMode.Incremental) + { + endpt = arc.EndPoint + pos; + centerpt = arc.CenterPoint + pos; + } + else + { + endpt = arc.EndPoint; + centerpt = arc.CenterPoint; + } + + double minX1; + double minY1; + double maxX1; + double maxY1; + + if (pos.X < endpt.X) + { + minX1 = pos.X; + maxX1 = endpt.X; + } + else + { + minX1 = endpt.X; + maxX1 = pos.X; + } + + if (pos.Y < endpt.Y) + { + minY1 = pos.Y; + maxY1 = endpt.Y; + } + else + { + minY1 = endpt.Y; + maxY1 = pos.Y; + } + + var startAngle = pos.AngleFrom(centerpt); + var endAngle = endpt.AngleFrom(centerpt); + + // switch the angle to counter clockwise. + if (arc.Rotation == RotationType.CW) + Generic.Swap(ref startAngle, ref endAngle); + + startAngle = MathHelper.NormalizeAngleRad(startAngle); + endAngle = MathHelper.NormalizeAngleRad(endAngle); + + if (MathHelper.IsAngleBetween(MathHelper.HalfPI, startAngle, endAngle)) + maxY1 = centerpt.Y + radius; + + if (MathHelper.IsAngleBetween(Math.PI, startAngle, endAngle)) + minX1 = centerpt.X - radius; + + const double oneHalfPI = Math.PI * 1.5; + + if (MathHelper.IsAngleBetween(oneHalfPI, startAngle, endAngle)) + minY1 = centerpt.Y - radius; + + if (MathHelper.IsAngleBetween(MathHelper.TwoPI, startAngle, endAngle)) + maxX1 = centerpt.X + radius; + + if (maxX1 > maxX) + maxX = maxX1; + + if (minX1 < minX) + minX = minX1; + + if (maxY1 > maxY) + maxY = maxY1; + + if (minY1 < minY) + minY = minY1; + + pos = endpt; + + break; + } + + case CodeType.SubProgramCall: + { + var subpgm = (SubProgramCall)code; + var box = subpgm.Loop.GetBoundingBox(ref pos); + + if (box.Left < minX) + minX = box.Left; + + if (box.Right > maxX) + maxX = box.Right; + + if (box.Bottom < minY) + minY = box.Bottom; + + if (box.Top > maxY) + maxY = box.Top; + + break; + } + } + } + + return new Box(minX, minY, maxX - minX, maxY - minY); + } + + public static Program Load(Stream stream) + { + var reader = new ProgramReader(); + reader.Read(stream); + return reader.Program; + } + } +} diff --git a/PepLib.Core/ProgrammingMode.cs b/PepLib.Core/ProgrammingMode.cs new file mode 100644 index 0000000..937eb32 --- /dev/null +++ b/PepLib.Core/ProgrammingMode.cs @@ -0,0 +1,8 @@ +namespace PepLib +{ + public enum ProgrammingMode + { + Absolute, + Incremental + } +} diff --git a/PepLib.Core/Report.Drawing.cs b/PepLib.Core/Report.Drawing.cs new file mode 100644 index 0000000..fb357e1 --- /dev/null +++ b/PepLib.Core/Report.Drawing.cs @@ -0,0 +1,53 @@ +using System; + +namespace PepLib +{ + public partial class Report + { + public class Drawing + { + public string Customer { get; set; } + + public string Name { get; set; } + + public string Revision { get; set; } + + public int QtyRequired { get; set; } + + public int QtyNested { get; set; } + + public int QtyRemaining + { + get { return QtyRequired - QtyNested; } + } + + public double CutDistance { get; set; } + + public double ScribeDistance { get; set; } + + public double BevelDistance { get; set; } + + public TimeSpan TotalCutTime { get; set; } + + public int PierceCount { get; set; } + + public int IntersectionCount { get; set; } + + public double Area1 { get; set; } + + public double Area2 { get; set; } + + public bool IncludeRemnantInCost { get; set; } + + public double NetWeight1 { get; set; } + + public double NetWeight2 { get; set; } + + public double GrossWeight { get; set; } + + public double PercentOfMaterial { get; set; } + + public double PercentOfCutTime { get; set; } + } + } +} diff --git a/PepLib.Core/Report.Plate.cs b/PepLib.Core/Report.Plate.cs new file mode 100644 index 0000000..5a8feca --- /dev/null +++ b/PepLib.Core/Report.Plate.cs @@ -0,0 +1,35 @@ + +namespace PepLib +{ + public partial class Report + { + public class Plate + { + public string Name { get; set; } + + public double Thickness { get; set; } + + public double Width { get; set; } + + public double Length { get; set; } + + public int MaterialNumber { get; set; } + + public string MaterialGrade { get; set; } + + public string MaterialDescription { get; set; } + + public int Quantity { get; set; } + + public double PlateUtilization { get; set; } + + public double MaterialUtilization { get; set; } + + public double Area1 { get; set; } + + public double Area2 { get; set; } + + public int BubblePierceCount { get; set; } + } + } +} diff --git a/PepLib.Core/Report.cs b/PepLib.Core/Report.cs new file mode 100644 index 0000000..358b69a --- /dev/null +++ b/PepLib.Core/Report.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using PepLib.IO; + +namespace PepLib +{ + public partial class Report + { + public Report() + { + Drawings = new List(); + Plates = new List(); + } + + public List Drawings { get; set; } + + public List Plates { get; set; } + + public string Name { get; set; } + + public string Customer { get; set; } + + public DateTime DateProgrammed { get; set; } + + public string Material { get; set; } + + public string ProgrammedBy { get; set; } + + public string Machine { get; set; } + + public string Comments { get; set; } + + public string Remarks { get; set; } + + public TimeSpan TotalCutTime { get; set; } + + public double TotalGasUsed { get; set; } + + public double TotalRapidDistance { get; set; } + + public int TotalHeadRaises { get; set; } + + public double CutFeedrate { get; set; } + + public double RapidFeedrate { get; set; } + + public TimeSpan PierceTime { get; set; } + + public int PlateCount() + { + return Plates.Sum(plate => plate.Quantity); + } + + public int ProgramCount() + { + return Plates.Count; + } + + public double CutDistance() + { + return Drawings.Sum(dwg => dwg.CutDistance); + } + + public double ScribeDistance() + { + return Drawings.Sum(dwg => dwg.ScribeDistance); + } + + public double BevelDistance() + { + return Drawings.Sum(dwg => dwg.BevelDistance); + } + + public int TotalPierceCount() + { + return Drawings.Sum(dwg => dwg.PierceCount); + } + + public static Report Load(string nestFile) + { + var reader = new ReportReader(); + reader.Read(nestFile); + return reader.Report; + } + + public static Report Load(Stream stream) + { + var reader = new ReportReader(); + reader.Read(stream); + return reader.Report; + } + + public static bool TryLoad(string nestfile, out Report report) + { + try + { + report = Load(nestfile); + } + catch (Exception) + { + report = null; + return false; + } + + return true; + } + + public static bool TryLoad(Stream stream, out Report report) + { + try + { + report = Load(stream); + } + catch (Exception) + { + report = null; + return false; + } + + return true; + } + } +} \ No newline at end of file diff --git a/PepLib.Core/RotationType.cs b/PepLib.Core/RotationType.cs new file mode 100644 index 0000000..bb33c51 --- /dev/null +++ b/PepLib.Core/RotationType.cs @@ -0,0 +1,16 @@ + +namespace PepLib +{ + public enum RotationType + { + /// + /// Clockwise + /// + CW, + + /// + /// Counter-Clockwise + /// + CCW + } +} diff --git a/PepLib.Core/Size.cs b/PepLib.Core/Size.cs new file mode 100644 index 0000000..fd9f2cb --- /dev/null +++ b/PepLib.Core/Size.cs @@ -0,0 +1,45 @@ +namespace PepLib +{ + public class Size + { + public Size(double height, double width) + { + Height = height; + Width = width; + } + + public double Height { get; set; } + + public double Width { get; set; } + + public static Size Parse(string size) + { + var a = size.ToUpper().Split('X'); + + var height = double.Parse(a[0]); + var width = double.Parse(a[1]); + + return new Size(height, width); + } + + public static bool TryParse(string s, out Size size) + { + try + { + size = Parse(s); + } + catch + { + size = new Size(0, 0); + return false; + } + + return true; + } + + public override string ToString() + { + return string.Format("{0} x {1}", Height, Width); + } + } +} \ No newline at end of file diff --git a/PepLib.Core/Spacing.cs b/PepLib.Core/Spacing.cs new file mode 100644 index 0000000..3fdfc81 --- /dev/null +++ b/PepLib.Core/Spacing.cs @@ -0,0 +1,27 @@ + +namespace PepLib +{ + public class Spacing + { + public Spacing() + : this(0, 0, 0, 0) + { + } + + public Spacing(double l, double b, double r, double t) + { + Left = l; + Bottom = b; + Right = r; + Top = t; + } + + public double Left { get; set; } + + public double Bottom { get; set; } + + public double Right { get; set; } + + public double Top { get; set; } + } +} diff --git a/PepLib.Core/StatusType.cs b/PepLib.Core/StatusType.cs new file mode 100644 index 0000000..d08bdb3 --- /dev/null +++ b/PepLib.Core/StatusType.cs @@ -0,0 +1,11 @@ + +namespace PepLib +{ + public enum StatusType + { + ToBeCut, + Quote, + HasBeenCut, + Temp + } +} diff --git a/PepLib.Core/Tolerance.cs b/PepLib.Core/Tolerance.cs new file mode 100644 index 0000000..74cc01b --- /dev/null +++ b/PepLib.Core/Tolerance.cs @@ -0,0 +1,14 @@ +using System; + +namespace PepLib +{ + public static class Tolerance + { + public const double Epsilon = 0.0001; + + public static bool IsEqualTo(this double a, double b, double tolerance = Epsilon) + { + return Math.Abs(b - a) <= tolerance; + } + } +} diff --git a/PepLib.Core/Util.cs b/PepLib.Core/Util.cs new file mode 100644 index 0000000..49ed57f --- /dev/null +++ b/PepLib.Core/Util.cs @@ -0,0 +1,32 @@ +using System; +using System.Diagnostics; +using System.IO; + +namespace PepLib +{ + public static class Util + { + public static string GetNestFileFormat(string filename) + { + try + { + var name = Path.GetFileName(filename); + var ext = Path.GetExtension(name); + + if (name.LastIndexOf(ext) > 5 && !name.Contains("-")) + name = name.Insert(5, "-"); + + if (name.LastIndexOf(ext) > 8 && char.IsLetter(name[8])) + name = name.Remove(8, 1); + + return Path.Combine(Path.GetDirectoryName(filename), name); + } + catch (SystemException ex) + { + Debug.WriteLine(ex.Message); + } + + return string.Empty; + } + } +} diff --git a/PepLib.Core/Vector.cs b/PepLib.Core/Vector.cs new file mode 100644 index 0000000..dda5cc2 --- /dev/null +++ b/PepLib.Core/Vector.cs @@ -0,0 +1,124 @@ +using System; + +namespace PepLib +{ + public struct Vector + { + public double X; + public double Y; + + public Vector(double x, double y) + { + X = x; + Y = y; + } + + public double DistanceTo(Vector pt) + { + double vx = pt.X - this.X; + double vy = pt.Y - this.Y; + + return Math.Sqrt(vx * vx + vy * vy); + } + + public double DistanceTo(double x, double y) + { + double vx = x - this.X; + double vy = y - this.Y; + + return Math.Sqrt(vx * vx + vy * vy); + } + + public double Angle() + { + return MathHelper.NormalizeAngleRad(Math.Atan2(Y, X)); + } + + public double AngleTo(Vector pt) + { + return (pt - this).Angle(); + } + + public double AngleFrom(Vector pt) + { + return (this - pt).Angle(); + } + + public static Vector operator +(Vector pt1, Vector pt2) + { + return new Vector(pt1.X + pt2.X, pt1.Y + pt2.Y); + } + + public static Vector operator -(Vector pt1, Vector pt2) + { + return new Vector(pt1.X - pt2.X, pt1.Y - pt2.Y); + } + + public static Vector operator -(Vector pt) + { + return new Vector(-pt.X, -pt.Y); + } + + public static bool operator ==(Vector pt1, Vector pt2) + { + return pt1.DistanceTo(pt2) <= Tolerance.Epsilon; + } + + public static bool operator !=(Vector pt1, Vector pt2) + { + return !(pt1 == pt2); + } + + public Vector Rotate(double angle) + { + var v = new Vector(); + + double cos = Math.Cos(angle); + double sin = Math.Sin(angle); + + v.X = X * cos - Y * sin; + v.Y = X * sin + Y * cos; + + return v; + } + + public Vector Rotate(double angle, Vector origin) + { + var v = new Vector(); + var pt = this - origin; + + double cos = Math.Cos(angle); + double sin = Math.Sin(angle); + + v.X = pt.X * cos - pt.Y * sin + origin.X; + v.Y = pt.X * sin + pt.Y * cos + origin.Y; + + return v; + } + + public Vector Clone() + { + return new Vector(X, Y); + } + + public override bool Equals(object obj) + { + if (!(obj is Vector)) + return false; + + var pt = (Vector)obj; + + return (X.IsEqualTo(pt.X)) && (Y.IsEqualTo(pt.Y)); + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + public override string ToString() + { + return string.Format("[Vector: X:{0}, Y:{1}]", X, Y); + } + } +} diff --git a/PepLib.Core/ZipHelper.cs b/PepLib.Core/ZipHelper.cs new file mode 100644 index 0000000..fba1140 --- /dev/null +++ b/PepLib.Core/ZipHelper.cs @@ -0,0 +1,116 @@ +using Ionic.Zip; +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; + +namespace PepLib +{ + public static class ZipHelper + { + /// + /// Returns the files that match the specified pattern. + /// + /// Input zip file. + /// Pattern to match. + /// Names of the files that match the pattern. + /// Data of the files that match the pattern. + /// + public static int ExtractByPattern(string file, string pattern, out string[] names, out Stream[] streams) + { + var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read); + var zipStream = new ZipInputStream(fileStream); + var nameList = new List(); + var streamList = new List(); + + ZipEntry theEntry; + + while ((theEntry = zipStream.GetNextEntry()) != null) + { + if (!Regex.IsMatch(theEntry.FileName, pattern)) + continue; + + nameList.Add(theEntry.FileName); + + var memstream = new MemoryStream(); + var size = 2048; + var data = new byte[size]; + + while (true) + { + size = zipStream.Read(data, 0, data.Length); + + if (size > 0) + { + memstream.Write(data, 0, size); + memstream.Flush(); + } + else break; + } + + memstream.Seek(0, SeekOrigin.Begin); + streamList.Add(memstream); + } + + zipStream.Close(); + + names = nameList.ToArray(); + streams = streamList.ToArray(); + + return streams.Length; + } + + /// + /// Returns the first file found that matches the specified file extension. + /// + /// Input zip file. + /// Extension to match. + /// The name of the file that matches the file extension. + /// The data of the file that matches the file extension. + /// + public static bool ExtractByExtension(string file, string extension, out string name, out Stream stream) + { + var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read); + var zipStream = new ZipInputStream(fileStream); + var memstream = new MemoryStream(); + + ZipEntry theEntry; + + while ((theEntry = zipStream.GetNextEntry()) != null) + { + if (Path.GetExtension(theEntry.FileName) != extension) + continue; + + int size = 2048; + var data = new byte[size]; + + while (true) + { + size = zipStream.Read(data, 0, data.Length); + + if (size > 0) + { + memstream.Write(data, 0, size); + memstream.Flush(); + } + else break; + } + + zipStream.Close(); + memstream.Seek(0, SeekOrigin.Begin); + + stream = memstream; + name = theEntry.FileName; + + return true; + } + + zipStream.Close(); + memstream.Close(); + + stream = null; + name = null; + + return false; + } + } +}