using System.Runtime.InteropServices; namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS; /// /// P/Invoke surface for Fanuc FWLIB (Fwlib32.dll). Declarations extracted from /// fwlib32.h in the strangesast/fwlib repo; the licensed DLL itself is NOT shipped /// with OtOpcUa — the deployment places Fwlib32.dll next to the server executable /// or on PATH. /// /// /// Deliberately narrow — only the calls actually makes. /// FOCAS has 800+ functions in fwlib32.h; pulling in every one would bloat the /// P/Invoke surface + signal more coverage than this driver provides. Expand as capabilities /// are added. /// internal static class FwlibNative { private const string Library = "Fwlib32.dll"; // ---- Handle lifetime ---- /// Open an Ethernet FWLIB handle. Returns EW_OK (0) on success; handle written out. [DllImport(Library, EntryPoint = "cnc_allclibhndl3", CharSet = CharSet.Ansi, ExactSpelling = true)] public static extern short AllcLibHndl3( [MarshalAs(UnmanagedType.LPStr)] string ipaddr, ushort port, int timeout, out ushort handle); [DllImport(Library, EntryPoint = "cnc_freelibhndl", ExactSpelling = true)] public static extern short FreeLibHndl(ushort handle); // ---- PMC ---- /// PMC range read. is the ADR_* enum; is 0 byte / 1 word / 2 long. [DllImport(Library, EntryPoint = "pmc_rdpmcrng", ExactSpelling = true)] public static extern short PmcRdPmcRng( ushort handle, short addrType, short dataType, ushort startNumber, ushort endNumber, ushort length, ref IODBPMC buffer); [DllImport(Library, EntryPoint = "pmc_wrpmcrng", ExactSpelling = true)] public static extern short PmcWrPmcRng( ushort handle, ushort length, ref IODBPMC buffer); // ---- Parameters ---- [DllImport(Library, EntryPoint = "cnc_rdparam", ExactSpelling = true)] public static extern short RdParam( ushort handle, ushort number, short axis, short length, ref IODBPSD buffer); [DllImport(Library, EntryPoint = "cnc_wrparam", ExactSpelling = true)] public static extern short WrParam( ushort handle, short length, ref IODBPSD buffer); // ---- Macro variables ---- [DllImport(Library, EntryPoint = "cnc_rdmacro", ExactSpelling = true)] public static extern short RdMacro( ushort handle, short number, short length, ref ODBM buffer); [DllImport(Library, EntryPoint = "cnc_wrmacro", ExactSpelling = true)] public static extern short WrMacro( ushort handle, short number, short length, int macroValue, short decimalPointCount); // ---- Status ---- [DllImport(Library, EntryPoint = "cnc_statinfo", ExactSpelling = true)] public static extern short StatInfo(ushort handle, ref ODBST buffer); // ---- Timers ---- /// /// cnc_rdtimer — read CNC running timers. : 0 = power-on /// time (ms), 1 = operating time (ms), 2 = cycle time (ms), 3 = cutting time (ms). /// Only the cycle-time variant is consumed today (issue #258); the call is generic /// so the surface can grow without another P/Invoke. /// [DllImport(Library, EntryPoint = "cnc_rdtimer", ExactSpelling = true)] public static extern short RdTimer(ushort handle, short type, ref IODBTMR buffer); // ---- Modal codes ---- /// /// cnc_modal — read modal information for one G-group or auxiliary code. /// : 1..21 = G-group N (single group), 100 = M, 101 = S, /// 102 = T, 103 = B (per Fanuc FOCAS reference). : 0 = /// active modal commands. We only consume types 100..103 today (M/S/T/B); the /// G-group decode is deferred to a follow-up because the ODBMDL union /// varies by group + series (issue #259). /// [DllImport(Library, EntryPoint = "cnc_modal", ExactSpelling = true)] public static extern short Modal(ushort handle, short type, short block, ref ODBMDL buffer); // ---- Tooling ---- /// /// cnc_rdtnum — read the currently selected tool number. Returns /// EW_OK + populates with the active T-code. /// Tool life + current offset index reads (cnc_rdtlinfo/cnc_rdtlsts/ /// cnc_rdtofs) are deferred per the F1-d plan — those calls use ODBTLIFE* /// unions whose shape varies per series. /// [DllImport(Library, EntryPoint = "cnc_rdtnum", ExactSpelling = true)] public static extern short RdToolNumber(ushort handle, ref IODBTNUM buffer); // ---- Work coordinate offsets ---- /// /// cnc_rdzofs — read one work-coordinate offset slot. : /// 1..6 = G54..G59 (standard). Extended G54.1 P1..P48 use cnc_rdzofsr /// and are deferred. : -1 = all axes returned, 1..N = single /// axis. : 12 + (N axes * 8) — we request -1 and let FWLIB /// fill up to 's 8-axis ceiling. /// [DllImport(Library, EntryPoint = "cnc_rdzofs", ExactSpelling = true)] public static extern short RdWorkOffset( ushort handle, short number, short axis, short length, ref IODBZOFS buffer); // ---- Structs ---- /// /// IODBPMC — PMC range I/O buffer. 8-byte header + 40-byte union. We marshal the union /// as a fixed byte buffer + interpret per on the managed side. /// [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct IODBPMC { public short TypeA; public short TypeD; public ushort DatanoS; public ushort DatanoE; // 40-byte union: cdata[5] / idata[5] / ldata[5] / fdata[5] / dbdata[5] — dbdata is the widest. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)] public byte[] Data; } /// /// IODBPSD — CNC parameter I/O buffer. Axis-aware; for non-axis parameters pass axis=0. /// Union payload is bytes / shorts / longs — we marshal 32 bytes as the widest slot. /// [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct IODBPSD { public short Datano; public short Type; // axis index (0 for non-axis) [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] public byte[] Data; } /// ODBM — macro variable read buffer. Value = McrVal / 10^DecVal. [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct ODBM { public short Datano; public short Dummy; public int McrVal; // long in C; 32-bit signed public short DecVal; // decimal-point count } /// /// IODBTMR — running-timer read buffer per fwlib32.h. Minute portion in /// ; sub-minute remainder in milliseconds in . /// [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct IODBTMR { public int Minute; public int Msec; } /// /// ODBMDL — single-group modal read buffer. 4-byte header + a 4-byte union which we /// marshal as a fixed byte array. For type=100..103 (M/S/T/B) the union holds an /// int aux_data at offset 0; we read the first short for symmetry with /// the FWLIB g_modal.aux_data width on G-group reads. The G-group decode /// (type=1..21) is deferred — see for context (issue #259). /// [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct ODBMDL { public short Datano; public short Type; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] Data; } /// /// IODBTNUM — current tool number read buffer. holds the active /// T-code (Fanuc reference uses long; we narrow to short on the /// managed side because surfaces as /// Int16). Issue #260, F1-d. /// [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct IODBTNUM { public short Datano; public short Type; public int Data; } /// /// IODBZOFS — work-coordinate offset read buffer. 4-byte header + per-axis /// OFSB blocks (8 bytes each: 4-byte signed integer data + 2-byte /// dec decimal-point count + 2-byte unit + 2-byte disp). /// We marshal a fixed ceiling of 8 axes (= 64 bytes); the managed side reads /// only the first 3 (X / Y / Z) per the F1-d effort sizing. /// [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct IODBZOFS { public short Datano; public short Type; // Up to 8 axes * 8 bytes per OFSB = 64 bytes. Each block: int data, short dec, // short unit, short disp (10 bytes per fwlib32.h). We size for the worst case. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 80)] public byte[] Data; } /// ODBST — CNC status info. Machine state, alarm flags, automatic / edit mode. [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct ODBST { public short Dummy; public short TmMode; public short Aut; public short Run; public short Motion; public short Mstb; public short Emergency; public short Alarm; public short Edit; } } /// /// PMC address-letter → FOCAS ADR_* numeric code. Per Fanuc FOCAS/2 spec the codes /// are: G=0, F=1, Y=2, X=3, A=4, R=5, T=6, K=7, C=8, D=9, E=10. Exposed internally + /// tested so the FwlibFocasClient translation is verifiable without the DLL loaded. /// internal static class FocasPmcAddrType { public static short? FromLetter(string letter) => letter.ToUpperInvariant() switch { "G" => 0, "F" => 1, "Y" => 2, "X" => 3, "A" => 4, "R" => 5, "T" => 6, "K" => 7, "C" => 8, "D" => 9, "E" => 10, _ => null, }; } /// PMC data-type numeric codes per FOCAS/2: 0 = byte, 1 = word, 2 = long, 4 = float, 5 = double. internal static class FocasPmcDataType { public const short Byte = 0; public const short Word = 1; public const short Long = 2; public const short Float = 4; public const short Double = 5; public static short FromFocasDataType(FocasDataType t) => t switch { FocasDataType.Bit or FocasDataType.Byte => Byte, FocasDataType.Int16 => Word, FocasDataType.Int32 => Long, FocasDataType.Float32 => Float, FocasDataType.Float64 => Double, _ => Byte, }; }