239 lines
7.2 KiB
PowerShell
239 lines
7.2 KiB
PowerShell
|
|
#Requires -Version 5.1
|
||
|
|
<#
|
||
|
|
.SYNOPSIS
|
||
|
|
Complete TwinCAT EL6695 Primary deployment from scratch.
|
||
|
|
|
||
|
|
.DESCRIPTION
|
||
|
|
Creates a fresh TwinCAT project with:
|
||
|
|
1. Hardware scan (EtherCAT devices)
|
||
|
|
2. PDO configuration (1024-byte IO)
|
||
|
|
3. PLC program (GVL + MAIN)
|
||
|
|
4. Activation + restart
|
||
|
|
|
||
|
|
Based on reference scripts in ethercat-linux/scripts/
|
||
|
|
#>
|
||
|
|
param(
|
||
|
|
[string]$ProjectDir = 'C:\Users\tonycao\work\twincat3-auto-cli\TwinCAT_EL6695_Primary',
|
||
|
|
[string]$Solution = 'C:\Users\tonycao\work\twincat3-auto-cli\TwinCAT_EL6695_Primary\TwinCATProject1.sln',
|
||
|
|
[string]$Tsproj = 'C:\Users\tonycao\work\twincat3-auto-cli\TwinCAT_EL6695_Primary\TwinCATProject1.tsproj',
|
||
|
|
[string]$TargetNetId = '169.254.176.217.1.1',
|
||
|
|
[int]$PayloadBytes = 1024,
|
||
|
|
[switch]$SkipScan,
|
||
|
|
[switch]$Visible
|
||
|
|
)
|
||
|
|
|
||
|
|
$ErrorActionPreference = 'Continue'
|
||
|
|
$LogDir = Join-Path $ProjectDir 'Logs'
|
||
|
|
New-Item -ItemType Directory -Force -Path $LogDir | Out-Null
|
||
|
|
$LogFile = Join-Path $LogDir "full-deploy_$(Get-Date -Format 'yyyyMMdd_HHmmss').log"
|
||
|
|
|
||
|
|
function Log($m) {
|
||
|
|
$line = (Get-Date -Format 'HH:mm:ss') + " $m"
|
||
|
|
Add-Content -Path $LogFile -Value $line -Encoding utf8 -ErrorAction SilentlyContinue
|
||
|
|
Write-Host $line
|
||
|
|
}
|
||
|
|
function LogSection($m) { Log ''; Log ('='*60); Log " $m"; Log ('='*60) }
|
||
|
|
|
||
|
|
Log '=== TwinCAT EL6695 Full Deploy ==='
|
||
|
|
Log "ProjectDir: $ProjectDir"
|
||
|
|
Log "TargetNetId: $TargetNetId"
|
||
|
|
Log "PayloadBytes: $PayloadBytes"
|
||
|
|
|
||
|
|
# ---- Step 1: Check/Fix PDO in tsproj ----
|
||
|
|
LogSection 'Step 1/6: Verify PDO configuration'
|
||
|
|
|
||
|
|
if (-not (Test-Path $Tsproj)) {
|
||
|
|
Log "ERROR: tsproj not found: $Tsproj"
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
|
||
|
|
$content = [System.IO.File]::ReadAllText($Tsproj, [System.Text.Encoding]::UTF8)
|
||
|
|
|
||
|
|
# Check if PDO already has 1024-byte entries
|
||
|
|
$has1024 = $content -match 'ARRAY \[0\.\.1023\] OF BYTE'
|
||
|
|
if ($has1024) {
|
||
|
|
Log " PDO already has 1024-byte entries"
|
||
|
|
} else {
|
||
|
|
Log " Adding 1024-byte PDO entries..."
|
||
|
|
$lastSub = "{0:X4}" -f ($PayloadBytes - 1)
|
||
|
|
|
||
|
|
$oldPdoIn = '<Pdo Name="IO Inputs" Index="#x1a08" Flags="#x0020" SyncMan="3"/>'
|
||
|
|
$newPdoIn = @"
|
||
|
|
<Pdo Name="IO Inputs" Index="#x1a08" Flags="#x0020" SyncMan="3">
|
||
|
|
<Entry Name="IO Inputs" Index="#x6000" Sub="#x01">
|
||
|
|
<Type>ARRAY [0..$lastSub] OF BYTE</Type>
|
||
|
|
</Entry>
|
||
|
|
</Pdo>
|
||
|
|
"@
|
||
|
|
if ($content -match [regex]::Escape($oldPdoIn)) {
|
||
|
|
$content = $content -replace [regex]::Escape($oldPdoIn), $newPdoIn
|
||
|
|
Log " IO Inputs PDO fixed"
|
||
|
|
} else { Log " WARN: IO Inputs PDO pattern not found" }
|
||
|
|
|
||
|
|
$oldPdoOut = '<Pdo Name="IO Outputs" Index="#x1608" InOut="1" Flags="#x0020" SyncMan="2"/>'
|
||
|
|
$newPdoOut = @"
|
||
|
|
<Pdo Name="IO Outputs" Index="#x1608" InOut="1" Flags="#x0020" SyncMan="2">
|
||
|
|
<Entry Name="IO Outputs" Index="#x7000" Sub="#x01">
|
||
|
|
<Type>ARRAY [0..$lastSub] OF BYTE</Type>
|
||
|
|
</Entry>
|
||
|
|
</Pdo>
|
||
|
|
"@
|
||
|
|
if ($content -match [regex]::Escape($oldPdoOut)) {
|
||
|
|
$content = $content -replace [regex]::Escape($oldPdoOut), $newPdoOut
|
||
|
|
Log " IO Outputs PDO fixed"
|
||
|
|
} else { Log " WARN: IO Outputs PDO pattern not found" }
|
||
|
|
|
||
|
|
[System.IO.File]::WriteAllText($Tsproj, $content, [System.Text.Encoding]::UTF8)
|
||
|
|
}
|
||
|
|
|
||
|
|
# ---- Step 2: Verify PLC code exists ----
|
||
|
|
LogSection 'Step 2/6: Verify PLC code'
|
||
|
|
|
||
|
|
$plcFiles = @(
|
||
|
|
"$ProjectDir\PlcSync\GVLs\GVL_Sync.TcGVL",
|
||
|
|
"$ProjectDir\PlcSync\POUs\MAIN.TcPOU",
|
||
|
|
"$ProjectDir\PlcSync\DUTs\DUT_EL6695_IO.TcDUT",
|
||
|
|
"$ProjectDir\PlcSync\DUTs\U_EL6695_IO.TcDUT"
|
||
|
|
)
|
||
|
|
|
||
|
|
$allOk = $true
|
||
|
|
foreach ($f in $plcFiles) {
|
||
|
|
if (Test-Path $f) { Log " OK: $(Split-Path $f -Leaf)" }
|
||
|
|
else { Log " MISSING: $(Split-Path $f -Leaf)"; $allOk = $false }
|
||
|
|
}
|
||
|
|
|
||
|
|
if (-not $allOk) {
|
||
|
|
Log "ERROR: PLC files missing. Run project setup first."
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
|
||
|
|
# ---- Step 3: Build project ----
|
||
|
|
LogSection 'Step 3/6: Build project'
|
||
|
|
|
||
|
|
$dte = $null
|
||
|
|
try {
|
||
|
|
$dte = New-Object -ComObject TcXaeShell.DTE.17.0
|
||
|
|
} catch {
|
||
|
|
try { $dte = New-Object -ComObject TcXaeShell.DTE.15.0 } catch { Log "FATAL: No TcXaeShell"; exit 1 }
|
||
|
|
}
|
||
|
|
|
||
|
|
$dte.SuppressUI = -not $Visible
|
||
|
|
$dte.MainWindow.Visible = $Visible
|
||
|
|
|
||
|
|
Log " Opening solution..."
|
||
|
|
$dte.Solution.Open($Solution)
|
||
|
|
|
||
|
|
$timeout = 120
|
||
|
|
for ($i = 1; $i -le $timeout; $i++) {
|
||
|
|
Start-Sleep -Seconds 1
|
||
|
|
if ($dte.Solution.IsOpen) { Log " Solution open after ${i}s"; break }
|
||
|
|
}
|
||
|
|
if (-not $dte.Solution.IsOpen) { Log "ERROR: Solution did not open"; $dte.Quit(); exit 1 }
|
||
|
|
|
||
|
|
# Wait for System Manager
|
||
|
|
Log " Waiting for System Manager..."
|
||
|
|
Start-Sleep -Seconds 15
|
||
|
|
$sm = $null
|
||
|
|
for ($i = 1; $i -le 30; $i++) {
|
||
|
|
try {
|
||
|
|
$sm = $dte.Solution.Projects.Item(1).Object
|
||
|
|
if ($sm) { Log " SysManager ready after ${i}s"; break }
|
||
|
|
} catch { }
|
||
|
|
Start-Sleep -Seconds 2
|
||
|
|
}
|
||
|
|
if (-not $sm) { Log "ERROR: SysManager not ready"; $dte.Quit(); exit 1 }
|
||
|
|
|
||
|
|
# Set target
|
||
|
|
try { $sm.SetTargetNetId($TargetNetId); Log " SetTargetNetId OK" } catch { Log " SetTargetNetId err: $_" }
|
||
|
|
|
||
|
|
# Build
|
||
|
|
Log " Building..."
|
||
|
|
try { $dte.ExecuteCommand('Build.BuildSolution') } catch { Log " Build command err: $_" }
|
||
|
|
Start-Sleep -Seconds 30
|
||
|
|
|
||
|
|
# Check build state
|
||
|
|
$sb = $dte.Solution.SolutionBuild
|
||
|
|
$buildState = $sb.BuildState
|
||
|
|
$buildInfo = $sb.LastBuildInfo
|
||
|
|
Log " BuildState=$buildState, LastBuildInfo=$buildInfo"
|
||
|
|
|
||
|
|
if ($buildState -ne 1) {
|
||
|
|
Log "WARN: Build not complete, waiting more..."
|
||
|
|
Start-Sleep -Seconds 30
|
||
|
|
}
|
||
|
|
|
||
|
|
# ---- Step 4: Hardware scan (optional) ----
|
||
|
|
if (-not $SkipScan) {
|
||
|
|
LogSection 'Step 4/6: Hardware scan'
|
||
|
|
Log " Scanning EtherCAT devices..."
|
||
|
|
try {
|
||
|
|
$sm.ActivateConfiguration()
|
||
|
|
Log " Pre-activate OK"
|
||
|
|
Start-Sleep -Seconds 10
|
||
|
|
} catch { Log " Pre-activate err: $_" }
|
||
|
|
|
||
|
|
# Try to reload devices
|
||
|
|
$bytes = @(0xE9,0x87,0x8D,0xE6,0x96,0xB0,0xE5,0x8A,0xA0,0xE8,0xBD,0xBD,0xE8,0xAE,0xBE,0xE5,0xA4,0x87)
|
||
|
|
$reloadCmd = 'TwinCAT.' + [System.Text.Encoding]::UTF8.GetString($bytes)
|
||
|
|
try { $dte.ExecuteCommand($reloadCmd); Log " Reload devices OK" } catch { Log " Reload err: $_" }
|
||
|
|
Start-Sleep -Seconds 15
|
||
|
|
}
|
||
|
|
|
||
|
|
# ---- Step 5: Activate configuration ----
|
||
|
|
LogSection 'Step 5/6: Activate configuration'
|
||
|
|
|
||
|
|
Log " Saving configuration..."
|
||
|
|
try { $sm.SaveConfiguration() } catch { Log " Save err: $_" }
|
||
|
|
|
||
|
|
Log " Activating..."
|
||
|
|
try {
|
||
|
|
$sm.ActivateConfiguration()
|
||
|
|
Log " ActivateConfiguration OK"
|
||
|
|
} catch {
|
||
|
|
Log " ActivateConfiguration err: $_"
|
||
|
|
}
|
||
|
|
|
||
|
|
Start-Sleep -Seconds 15
|
||
|
|
|
||
|
|
# ---- Step 6: Start TwinCAT runtime ----
|
||
|
|
LogSection 'Step 6/6: Start TwinCAT runtime'
|
||
|
|
|
||
|
|
Log " Starting TwinCAT..."
|
||
|
|
try {
|
||
|
|
$sm.StartRestartTwinCAT()
|
||
|
|
Log " StartRestartTwinCAT executed"
|
||
|
|
} catch {
|
||
|
|
Log " StartRestartTwinCAT err: $_"
|
||
|
|
}
|
||
|
|
|
||
|
|
# Wait for RUN state
|
||
|
|
$adsDll = 'C:\Program Files (x86)\Beckhoff\TwinCAT\3.1\Components\Base\v170\TwinCAT.Ads.dll'
|
||
|
|
Add-Type -Path $adsDll -ErrorAction SilentlyContinue
|
||
|
|
|
||
|
|
function Get-TcState {
|
||
|
|
try {
|
||
|
|
$c = New-Object TwinCAT.Ads.TcAdsClient
|
||
|
|
$c.Connect($TargetNetId, 10000)
|
||
|
|
$s = $c.ReadState()
|
||
|
|
$c.Dispose()
|
||
|
|
return "$($s.AdsState)"
|
||
|
|
} catch { return "ERR" }
|
||
|
|
}
|
||
|
|
|
||
|
|
for ($i = 1; $i -le 20; $i++) {
|
||
|
|
Start-Sleep -Seconds 5
|
||
|
|
$st = Get-TcState
|
||
|
|
Log " state +$($i*5)s: $st"
|
||
|
|
if ($st -eq 'Run') { break }
|
||
|
|
if ($st -eq 'Config') { Log " Still in Config mode..." }
|
||
|
|
}
|
||
|
|
|
||
|
|
Log "Final state: $(Get-TcState)"
|
||
|
|
|
||
|
|
# Save and cleanup
|
||
|
|
try { $dte.Solution.SaveAs($Solution) } catch { }
|
||
|
|
try { $dte.Quit() } catch { }
|
||
|
|
|
||
|
|
LogSection 'Deploy complete'
|
||
|
|
Log "Log file: $LogFile"
|