#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 = ''
$newPdoIn = @"
ARRAY [0..$lastSub] OF BYTE
"@
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 = ''
$newPdoOut = @"
ARRAY [0..$lastSub] OF BYTE
"@
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"