Files
weinviews-unitouch/tools/scan-plc.ps1
T
admin 7bfe8945ef 文档:设备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 握手
2026-10-27 11:56:23 +08:00

128 lines
5.3 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 扫描 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