从 SDK 继续
上一步已经在配置工具里测过,并把代码导出。SDK 在安装目录 C:\DARRA\Profinet_Slave\SDK\,也可从公共仓库安装(C# 用 NuGet、Python 用 PyPI、Java 用 Maven Central、Rust 用 crates.io,见下载页)。C# 写 using Darra.PnS; 即可,其他语言见各 SDK 页。
最小循环
站名和网卡在配置工具里设。程序侧:new → Start → 映射一次过程数据(0 拷贝:结构体映射在过程映像上,源头变字段自己变)→ Stop。
总线周期由内核完成,不必自写周期循环,任意线程可同时用。Read / Write 这类是快照(拷出来的实例不会自己变);0 拷贝入口是各语言的 InputsMapping / OutputsMapping 同名入口(I 侧只读、Q 侧可写)。
using System.Runtime.InteropServices;
using Darra.PnS;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct FromPlc // I:控制器 → 设备
{
public byte Present;
public short Recipe; // 线上大端:布局对上即可,取值走 Read(ref rx)
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ToPlc // Q:设备 → 控制器
{
public byte Ready;
public byte Busy;
}
using (var device = new DarraPnS())
{
device.Start();
// 映射一次
ref readonly var inp = ref device.ProcessData.InputsMapping<FromPlc>();
ref ToPlc outp = ref device.ProcessData.OutputsMapping<ToPlc>();
while (running)
{
if (!device.Connected)
continue;
byte present = inp.Present; // 读 I:总线刷新后自己变
outp.Ready = 1; // 写 Q:直接落过程映像
}
device.Stop();
}
多字节字段是 PROFINET 网络大端(线上字节序):直接当宿主 short / int 读会字节颠倒,要宿主序数值走 Read / Write(大端编解码)。
- C++
- C
- Python
- Java
- Rust
#include "pns.hpp"
using namespace darra::pns;
#pragma pack(push, 1)
struct FromPlc // I:控制器 → 设备
{
uint8_t Present;
uint8_t Trigger;
int16_t Recipe;
};
struct ToPlc // Q:设备 → 控制器
{
uint8_t Ready;
uint8_t Busy;
uint8_t Ok;
uint8_t Ng;
};
#pragma pack(pop)
PnSService device;
device.Start();
// 映射一次
const FromPlc& inp = device.ProcessData().InputsMapping<FromPlc>();
ToPlc& outp = device.ProcessData().OutputsMapping<ToPlc>();
while (running)
{
if (!device.Connected())
continue;
uint8_t present = inp.Present; // 读 I:总线刷新后自己变
outp.Ready = 1; // 写 Q:直接落过程映像
}
device.Stop();
#include "pns.h"
#pragma pack(push, 1)
typedef struct { /* I:控制器 → 设备 */
uint8_t Present;
uint8_t Trigger;
} from_plc_t;
typedef struct { /* Q:设备 → 控制器 */
uint8_t Ready;
uint8_t Busy;
uint8_t Ok;
uint8_t Ng;
} to_plc_t;
#pragma pack(pop)
pns_service_handle_t session = NULL;
PnSServiceConnectRealtime(&session);
PnSServiceStart(session);
/* 映射一次 */
const uint8_t *i = NULL; /* I 只读 */
volatile uint8_t *q = NULL; /* Q 可写 */
size_t ni = 0, nq = 0;
if (PnSProcessDataInputsMappingRO(session, &i, &ni) != PNS_OK ||
PnSProcessDataOutputsMapping(session, &q, &nq) != PNS_OK ||
i == NULL || q == NULL || /* 失败时视图为空 */
ni < sizeof(from_plc_t) || nq < sizeof(to_plc_t))
{
/* 过程映像不可用 */
}
const from_plc_t *inp = (const from_plc_t *)i;
to_plc_t *outp = (to_plc_t *)q;
while (running)
{
if (!PnSServiceIsConnected(session))
continue;
uint8_t present = inp->Present; /* 读 I:总线刷新后自己变 */
outp->Ready = 1; /* 写 Q:直接落过程映像 */
}
PnSServiceStop(session);
PnSServiceClose(session);
import ctypes
from darra_pns import PnSService
class FromPlc(ctypes.BigEndianStructure): # I:控制器 → 设备
_pack_ = 1
_fields_ = [("present", ctypes.c_uint8),
("recipe", ctypes.c_int16)]
class ToPlc(ctypes.BigEndianStructure): # Q:设备 → 控制器
_pack_ = 1
_fields_ = [("ready", ctypes.c_uint8),
("busy", ctypes.c_uint8)]
with PnSService() as device:
device.start()
# 映射一次
inp = device.process_data.inputs_mapping(FromPlc)
outp = device.process_data.outputs_mapping(ToPlc)
while running:
if not device.connected:
continue
present = inp.present # 读 I:总线刷新后自己变
outp.ready = 1 # 写 Q:直落共享区
device.stop()
快照是 device.read_struct("Hi") / device.write_struct("Bf", 1, 1.5) 与 device.process_data.read_struct / write_struct —— 拷出来的实例不会自己变。地址化读写是兼容路径:
device.write_bool("Q0.0", True)
from_plc = device.read_bool("I0.0")
import com.darra.pns.service.PnSService;
import com.sun.jna.Structure;
import com.sun.jna.Structure.FieldOrder;
@FieldOrder({"present", "recipe"})
class FromPlc extends Structure { // I:控制器 → 设备
public byte present;
public short recipe;
public FromPlc() { super(ALIGN_NONE); }
}
@FieldOrder({"ready", "busy"})
class ToPlc extends Structure { // Q:设备 → 控制器
public byte ready;
public byte busy;
public ToPlc() { super(ALIGN_NONE); }
}
try (PnSService device = new PnSService()) {
device.start();
// 映射一次
FromPlc inp = device.processData().inputsMapping(FromPlc.class);
ToPlc outp = device.processData().outputsMapping(ToPlc.class);
while (running) {
if (!device.connected())
continue;
inp.read(); // 过程映像 → 托管缓存
byte present = inp.present; // 读 I:总线刷新后自己变
outp.ready = 1;
outp.write(); // 写 Q:托管缓存 → 过程映像
}
device.stop();
}
use darra_pns::{PnSService, ServiceResult};
#[repr(C, packed)]
struct FromPlc { present: u8, recipe: i16 } // I:控制器 → 设备
#[repr(C, packed)]
struct ToPlc { ready: u8, busy: u8 } // Q:设备 → 控制器
fn main() -> ServiceResult<()> {
let device = PnSService::new();
device.start()?;
// 映射一次
let inp = unsafe { device.process_data().inputs_mapping::<FromPlc>()? };
let outp = unsafe { device.process_data().outputs_mapping::<ToPlc>()? };
while running {
if !device.connected() {
continue;
}
let present = inp.present; // 读 I:总线刷新后自己变
outp.ready = 1; // 写 Q:直接落过程映像
let _ = present;
}
device.stop()?;
Ok(())
}
地址化读写(Q0.0 / I0.0)是兼容路径。
配置工具顶栏「导出」可写出各语言示例。
