文档:设备4=制丸1、设备5=上料清洗(EB 设备列表实测),修正 net-apply 结论与 201 判据
- 地址表 5.8/5.9:设备 4/5 归属互换,补实测 IP 192.168.189.22 / 192.168.189.2,协议 S7CommPlus 符号寻址 - 宏指令 net-apply:确认代码正确、仅注释写反(上料 IP→设备5、制丸 IP→设备4) - 开发主文件 9.x/10.1:设备引用同步互换;新增「符号名 + 设备绑定」判据;删除不适用的「取消优化块访问」 - 新增 tools/scan-plc.ps1:在不受沙箱限制的 PowerShell 里扫 102 端口并做 COTP 握手
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
# 扫描 PLC 网络:找 S7 设备(端口 102),并做 COTP 握手确认是不是西门子 PLC
|
||||
#
|
||||
# 用法(在【不受 DSH 沙箱限制】的普通 PowerShell 里运行):
|
||||
# powershell -ExecutionPolicy Bypass -File tools\scan-plc.ps1
|
||||
# powershell -ExecutionPolicy Bypass -File tools\scan-plc.ps1 -Subnet 192.168.0.0/16
|
||||
# powershell -ExecutionPolicy Bypass -File tools\scan-plc.ps1 -Subnet 192.168.189.0/24 -TimeoutMs 400
|
||||
#
|
||||
# 输出:IP / MAC / 是否 S7(COTP 握手 D0)/ 响应耗时
|
||||
|
||||
param(
|
||||
[string]$Subnet, # 形如 192.168.189.0/24;省略则自动取有线网卡所在网段
|
||||
[int]$TimeoutMs = 600,
|
||||
[int]$Threads = 128
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
# ---------- 1. 自动取网段 ----------
|
||||
if (-not $Subnet) {
|
||||
$cand = Get-NetIPAddress -AddressFamily IPv4 -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.IPAddress -notlike '127.*' -and $_.IPAddress -notlike '169.254.*' -and $_.InterfaceAlias -notlike '*VMware*' }
|
||||
if (-not $cand) {
|
||||
Write-Host '找不到可用网卡,请手动指定 -Subnet,例如 -Subnet 192.168.0.0/24' -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
$pick = $cand | Select-Object -First 1
|
||||
$ip = $pick.IPAddress; $plen = $pick.PrefixLength
|
||||
$Subnet = "$ip/$plen"
|
||||
Write-Host ("使用网卡 {0} 的地址 {1}" -f $pick.InterfaceAlias, $Subnet) -ForegroundColor Cyan
|
||||
}
|
||||
|
||||
$parts = $Subnet.Split('/')
|
||||
$baseIp = $parts[0]
|
||||
$prefix = [int]$parts[1]
|
||||
|
||||
Write-Host ("扫描 {0} 的 TCP 102(S7),超时 {1}ms,并发 {2} ..." -f $Subnet, $TimeoutMs, $Threads) -ForegroundColor Cyan
|
||||
|
||||
# ---------- 2. 并发扫描 + COTP 握手(C# 实现,PS 5.1 也能跑) ----------
|
||||
$src = @'
|
||||
using System;
|
||||
using System.Net.Sockets;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class PlcScan
|
||||
{
|
||||
static readonly byte[] COTP_CR = new byte[] {
|
||||
0x03,0x00,0x00,0x16, 0x11,0xE0,0x00,0x00, 0x00,0x01,0x00, 0xC1,0x02,0x01,0x00,
|
||||
0xC2,0x02,0x01,0x02, 0xC0,0x01,0x0A
|
||||
};
|
||||
|
||||
public static string[] Scan(string cidrIp, int prefix, int port, int timeoutMs, int threads)
|
||||
{
|
||||
var p = cidrIp.Split('.');
|
||||
uint ip = ((uint)int.Parse(p[0]) << 24) | ((uint)int.Parse(p[1]) << 16) | ((uint)int.Parse(p[2]) << 8) | (uint)int.Parse(p[3]);
|
||||
uint mask = prefix == 0 ? 0u : (0xFFFFFFFFu << (32 - prefix));
|
||||
uint net = ip & mask, bcast = net | ~mask;
|
||||
var bag = new ConcurrentBag<string>();
|
||||
Parallel.For((long)net + 1, (long)bcast, new ParallelOptions { MaxDegreeOfParallelism = threads }, i =>
|
||||
{
|
||||
string s = string.Format("{0}.{1}.{2}.{3}", (i >> 24) & 255, (i >> 16) & 255, (i >> 8) & 255, i & 255);
|
||||
try { using (var c = new TcpClient()) { var ar = c.BeginConnect(s, port, null, null); if (ar.AsyncWaitHandle.WaitOne(timeoutMs) && c.Connected) bag.Add(s); } } catch { }
|
||||
});
|
||||
var arr = bag.ToArray(); Array.Sort(arr); return arr;
|
||||
}
|
||||
|
||||
public static bool IsS7(string ip, int timeoutMs)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var c = new TcpClient())
|
||||
{
|
||||
var ar = c.BeginConnect(ip, 102, null, null);
|
||||
if (!ar.AsyncWaitHandle.WaitOne(timeoutMs) || !c.Connected) return false;
|
||||
c.EndConnect(ar);
|
||||
c.ReceiveTimeout = timeoutMs;
|
||||
var st = c.GetStream();
|
||||
st.Write(COTP_CR, 0, COTP_CR.Length);
|
||||
st.Flush();
|
||||
var buf = new byte[64];
|
||||
var r = st.BeginRead(buf, 0, buf.Length, null, null);
|
||||
if (!r.AsyncWaitHandle.WaitOne(timeoutMs)) return false;
|
||||
int n = st.EndRead(r);
|
||||
// COTP Connection Confirm: 0x03 0x00 ... 0x11 0xD0
|
||||
return n >= 6 && buf[0] == 0x03 && buf[5] == 0xD0;
|
||||
}
|
||||
}
|
||||
catch { return false; }
|
||||
}
|
||||
}
|
||||
'@
|
||||
Add-Type -TypeDefinition $src -Language CSharp
|
||||
|
||||
$sw = [Diagnostics.Stopwatch]::StartNew()
|
||||
$hosts = [PlcScan]::Scan($baseIp, $prefix, 102, $TimeoutMs, $Threads)
|
||||
$sw.Stop()
|
||||
Write-Host ("端口 102 开放的主机:{0} 台(扫描耗时 {1:N1}s)" -f $hosts.Count, $sw.Elapsed.TotalSeconds) -ForegroundColor Green
|
||||
|
||||
if ($hosts.Count -eq 0) {
|
||||
Write-Host '没发现开着 102 的主机。检查:① 网线是否插在同一交换机;② 本机 IP 是否与 PLC 同网段;③ PLC 是否启用;④ 防火墙。' -ForegroundColor Yellow
|
||||
exit 0
|
||||
}
|
||||
|
||||
# ---------- 3. 取 MAC 并确认 S7 ----------
|
||||
$arp = @{}
|
||||
(arp -a) | ForEach-Object {
|
||||
if ($_ -match '^\s*(\d+\.\d+\.\d+\.\d+)\s+([0-9a-fA-F-]{17})\s+') { $arp[$matches[1]] = $matches[2].ToUpper() }
|
||||
}
|
||||
|
||||
$rows = foreach ($h in $hosts) {
|
||||
$t0 = [Diagnostics.Stopwatch]::StartNew()
|
||||
$isS7 = [PlcScan]::IsS7($h, [Math]::Max($TimeoutMs, 1000))
|
||||
$t0.Stop()
|
||||
[pscustomobject]@{
|
||||
IP = $h
|
||||
MAC = if ($arp.ContainsKey($h)) { $arp[$h] } else { '(未知)' }
|
||||
S7握手 = if ($isS7) { '是' } else { '否' }
|
||||
耗时ms = [int]$t0.Elapsed.TotalMilliseconds
|
||||
}
|
||||
}
|
||||
|
||||
$rows | Sort-Object IP | Format-Table -AutoSize
|
||||
|
||||
$out = Join-Path $PSScriptRoot 'scan-plc-result.csv'
|
||||
$rows | Export-Csv -Path $out -NoTypeInformation -Encoding UTF8
|
||||
Write-Host ("结果已保存:{0}" -f $out) -ForegroundColor Cyan
|
||||
Write-Host '把这张表贴给开发,即可继续判断设备 4 / 设备 5 是哪个 IP。' -ForegroundColor Cyan
|
||||
Reference in New Issue
Block a user