Skip to content

Commit a1c28f6

Browse files
committed
[UPD] Rel v4.4.0
1 parent e4f3477 commit a1c28f6

19 files changed

Lines changed: 2739 additions & 29 deletions

File tree

.ai/skills/laravel-opcua/SKILL.md

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
---
2+
name: laravel-opcua
3+
description: Laravel 11/12/13 integration for OPC UA. Provides a Facade (Opcua::*), service provider, .env-based named connections, an Artisan daemon command (opcua:session), and transparent session persistence via the opcua-session-manager daemon. Use this skill whenever the user is working with OPC UA from a Laravel application — controllers, jobs, Livewire components, Filament panels, broadcasting, Horizon queues, Octane workers, scheduled tasks, or Pest tests.
4+
license: MIT
5+
version: v4.4.0
6+
compatibility:
7+
php: ">= 8.2"
8+
laravel: "11.x | 12.x | 13.x"
9+
depends_on:
10+
- php-opcua/opcua-client@^4.4.0
11+
- php-opcua/opcua-session-manager@^4.4.0
12+
metadata:
13+
package: php-opcua/laravel-opcua
14+
packagist: https://packagist.org/packages/php-opcua/laravel-opcua
15+
repository: https://github.com/php-opcua/laravel-opcua
16+
documentation: https://php-opcua.com
17+
related:
18+
- php-opcua/opcua-client
19+
- php-opcua/opcua-session-manager
20+
- php-opcua/opcua-cli
21+
- php-opcua/opcua-client-nodeset
22+
---
23+
24+
# laravel-opcua
25+
26+
A thin, idiomatic Laravel layer over `php-opcua/opcua-client`. Three things to remember:
27+
28+
1. The Facade `PhpOpcua\LaravelOpcua\Facades\Opcua` proxies the full `OpcUaClientInterface`. Anything `opcua-client` can do, the Facade can do.
29+
2. `OpcuaManager::shouldUseSessionManager()` decides per-call whether to instantiate a direct `Client` (TCP straight to the server) or a `ManagedClient` (IPC to the long-lived daemon). The decision is transparent to application code.
30+
3. v4.4.0 picked up 21 new client methods (HistoryUpdate, File transfer, Aggregates). They are reachable through `Opcua::*` and `Opcua::connection('plc-1')->*` without any config or service-provider change.
31+
32+
## What this package is for
33+
34+
| You want to | Use |
35+
|---|---|
36+
| Read / write OPC UA nodes from a controller, job, command | `Opcua::read()`, `Opcua::write()` (Facade) |
37+
| Talk to multiple OPC UA servers | Named connections in `config/opcua.php`, `Opcua::connection('plc-1')` |
38+
| Connect to a runtime-discovered endpoint | `Opcua::connectTo($url, $configOverrides, as: 'cache-key')` |
39+
| Avoid one new TCP connection per HTTP request | Run `php artisan opcua:session` as a supervised daemon |
40+
| React to data changes, alarms, etc. via PSR-14 → Laravel Event system | Configure `auto_publish: true` + `auto_connect: true` + `subscriptions: [...]` |
41+
| Test code that touches OPC UA without a server | `PhpOpcua\Client\MockClient` + Facade swap, see `references/TESTING.md` |
42+
| Stream notifications to Livewire / Broadcasting / Notifications / Filament | Register listeners on `DataChangeReceived`, `AlarmActivated`, etc. (see `references/INTEGRATIONS.md`) |
43+
44+
## Mental model
45+
46+
```
47+
Application code
48+
└── Opcua::* (Facade)
49+
└── OpcuaManager::connection($name)
50+
├── shouldUseSessionManager() == true?
51+
│ └── ManagedClient (IPC → daemon → TCP → server)
52+
│ └── TransportFactory picks UnixSocketTransport (Linux/macOS) or TcpLoopbackTransport (Windows)
53+
└── shouldUseSessionManager() == false?
54+
└── ClientBuilder::create()->...->connect() (direct TCP, new connection per call)
55+
```
56+
57+
The two branches expose the same `OpcUaClientInterface`. Your code does not know which it has.
58+
59+
## Quick start
60+
61+
```bash
62+
composer require php-opcua/laravel-opcua
63+
php artisan vendor:publish --tag=opcua-config
64+
```
65+
66+
```env
67+
# .env
68+
OPCUA_ENDPOINT=opc.tcp://plc.example:4840
69+
OPCUA_USERNAME=operator
70+
OPCUA_PASSWORD=changeme
71+
OPCUA_SECURITY_POLICY=Basic256Sha256
72+
OPCUA_SECURITY_MODE=SignAndEncrypt
73+
```
74+
75+
```php
76+
use PhpOpcua\LaravelOpcua\Facades\Opcua;
77+
78+
Opcua::read('i=2259')->getValue(); // 0 = Running
79+
Opcua::write('ns=2;s=Setpoint', 42.5); // auto-detects Double
80+
Opcua::browseRecursive('i=85', maxDepth: 3);
81+
```
82+
83+
## The 3 patterns you will use 90% of the time
84+
85+
### Pattern A — one-shot read/write (no daemon)
86+
87+
Best for HTTP requests, scheduled jobs, Artisan commands. The Facade opens a TCP connection per call, reads/writes, then closes.
88+
89+
```php
90+
public function showServerState(): array
91+
{
92+
$state = Opcua::read('i=2259')->getValue();
93+
return ['state' => $state, 'running' => $state === 0];
94+
}
95+
```
96+
97+
### Pattern B — daemon-backed, transparent session reuse
98+
99+
When you run `php artisan opcua:session` under Supervisor/systemd, every Facade call goes through the daemon. Sessions are reused; you no longer pay the connect + create-session + activate-session round-trip per request.
100+
101+
Run the daemon:
102+
```bash
103+
php artisan opcua:session --log-channel=stack --cache-store=redis
104+
```
105+
106+
Application code does not change. Same `Opcua::read(...)`, but now backed by `ManagedClient` automatically.
107+
108+
### Pattern C — `auto_publish` + Laravel events
109+
110+
Subscribe declaratively in config; receive notifications as Laravel events.
111+
112+
```php
113+
// config/opcua.php
114+
'session_manager' => ['auto_publish' => true],
115+
'connections' => [
116+
'plc-1' => [
117+
'endpoint' => 'opc.tcp://plc.example:4840',
118+
'auto_connect' => true,
119+
'subscriptions' => [[
120+
'publishing_interval' => 500.0,
121+
'monitored_items' => [
122+
['node_id' => 'ns=2;s=Temperature', 'client_handle' => 1],
123+
],
124+
]],
125+
],
126+
],
127+
```
128+
129+
```php
130+
// app/Providers/EventServiceProvider.php
131+
use PhpOpcua\Client\Event\DataChangeReceived;
132+
133+
Event::listen(DataChangeReceived::class, function (DataChangeReceived $e) {
134+
SensorReading::create([
135+
'client_handle' => $e->clientHandle,
136+
'value' => $e->dataValue->getValue(),
137+
'sampled_at' => $e->dataValue->sourceTimestamp,
138+
]);
139+
});
140+
```
141+
142+
The daemon's auto-publish loop dispatches PSR-14 events through Laravel's event dispatcher. Listeners can be queued, broadcast, etc. — see `references/INTEGRATIONS.md`.
143+
144+
## Facade method surface (one-line summary)
145+
146+
Connection management: `connection()`, `connect()`, `connectTo()`, `disconnect()`, `disconnectAll()`, `isSessionManagerRunning()`, `getDefaultConnection()`.
147+
148+
Proxied to the active connection (auto-routed via `__call`):
149+
- Reading: `read`, `readMulti`
150+
- Writing: `write`, `writeMulti`
151+
- Browsing: `browse`, `browseAll`, `browseRecursive`, `browseWithContinuation`, `browseNext`, `resolveNodeId`, `translateBrowsePaths`
152+
- Method calls: `call`
153+
- Subscriptions: `createSubscription`, `createMonitoredItems`, `createEventMonitoredItem`, `modifyMonitoredItems`, `setTriggering`, `deleteMonitoredItems`, `deleteSubscription`, `publish`, `transferSubscriptions`, `republish`
154+
- History read: `historyReadRaw`, `historyReadProcessed`, `historyReadAtTime`
155+
- **History update (v4.4)**: `historyInsertData`, `historyReplaceData`, `historyUpdateData`, `historyDeleteRawModified`, `historyDeleteAtTime`, `historyInsertEvent`, `historyReplaceEvent`, `historyUpdateEvent`, `historyDeleteEvent`
156+
- **File transfer (v4.4)**: `openFile`, `closeFile`, `readFile`, `writeFile`, `getFilePosition`, `setFilePosition`, `createDirectory`, `createFileInDirectory`, `deleteFileSystemObject`, `moveOrCopyFileSystemObject`
157+
- **Aggregates (v4.4)**: `aggregate`, `historyAggregate`
158+
- Trust store: `trustCertificate`, `untrustCertificate`, `getTrustStore`, `getTrustPolicy`
159+
- Discovery: `getEndpoints`, `discoverDataTypes`, `getExtensionObjectRepository`
160+
- Cache / logging: `getLogger`, `getCache`, `invalidateCache`, `flushCache`
161+
- Connection state: `connect`, `disconnect`, `reconnect`, `isConnected`, `getConnectionState`, `getTimeout`, `getAutoRetry`, `getBatchSize`, `getDefaultBrowseMaxDepth`, `getServerMaxNodesPerRead`, `getServerMaxNodesPerWrite`
162+
163+
Full PHPDoc with all signatures: `src/Facades/Opcua.php`.
164+
165+
## When to follow the references
166+
167+
Progressive disclosure — only load what the task needs:
168+
169+
- `references/CONFIG.md` — every `config/opcua.php` key, env vars, named connections, defaults, version-specific keys
170+
- `references/SESSION_MANAGER.md` — daemon command, Supervisor/systemd setup, IPC endpoints, auto-publish vs manual publish, monitoring
171+
- `references/EVENTS.md` — full list of 56 PSR-14 events, payload shapes, queued listener pattern, common listener recipes
172+
- `references/INTEGRATIONS.md` — Octane/FrankenPHP, Horizon/queues, Livewire, Filament, Broadcasting, Notifications, Telescope/Pulse
173+
- `references/SECURITY.md` — policies, modes, trust store, certificate auto-generation, X.509 user auth, env-driven config
174+
- `references/TESTING.md` — Pest setup, MockClient + Facade swap, integration tests with Docker test-suite
175+
- `references/PITFALLS.md` — common gotchas: facade in config files, Octane state, mixed daemon versions, etc.
176+
- `assets/recipes.md` — copy-pasteable code snippets for the 15 most common end-to-end tasks
177+
178+
## Idiomatic patterns
179+
180+
1. **Inject `OpcuaManager`, not the Facade, in long-lived classes.** The Facade resolves the manager every call; injection caches it.
181+
```php
182+
public function __construct(private OpcuaManager $opcua) {}
183+
public function handle(): void { $this->opcua->read(...); }
184+
```
185+
186+
2. **Use named connections per server.** Don't string-build endpoints in code. Define `plc-1`, `plc-2`, `historian` in config, then `Opcua::connection('historian')->historyReadRaw(...)`.
187+
188+
3. **Don't disconnect in HTTP requests when the daemon is enabled.** `ManagedClient::disconnect()` closes the daemon-side session, undoing the connection pooling. Let the session manager handle lifecycle.
189+
190+
4. **For auto-published subscriptions, never call `publish()` yourself.** It returns `auto_publish_active` error. Subscribe to events instead.
191+
192+
5. **Use `useCache: false` for fresh reads of high-churn nodes.** The read metadata cache is the default — pass `refresh: true` to bypass.
193+
194+
6. **Queue listeners for heavy event handling.** A `DataChangeReceived` listener that hits a database should be `ShouldQueue`. Otherwise the daemon publish loop blocks on it.
195+
196+
7. **`Opcua::connectTo()` is for ad-hoc; cache by name** with the `as:` parameter when reused across the request.
197+
198+
8. **Trust store goes on disk, not in DB.** `storage/app/opcua-trust-store/` by default; check it into a deploy volume, not git.
199+
200+
9. **Run the daemon under a dedicated UID with `socket_mode: 0600`.** The Facade-side process must be in the same group/UID.
201+
202+
10. **In Octane, configure `OpcuaManager` as request-scoped via flushed singletons** — see `references/INTEGRATIONS.md` for the `OctaneServiceProvider::tick` hook.
203+
204+
## Exit codes (Artisan `opcua:session`)
205+
206+
| Code | Meaning |
207+
|---|---|
208+
| 0 | Daemon exited cleanly (SIGTERM/SIGINT) |
209+
| 1 | Configuration error (invalid socket_path, missing required key) |
210+
| 2 | Bind failure (port in use, socket-path EACCES, parent dir missing) |
211+
| 3 | Runtime error inside daemon loop (logged via PSR-3 channel) |
212+
213+
Non-zero exits should be caught by Supervisor `autorestart=true` or systemd `Restart=on-failure`.
214+
215+
## Versioning
216+
217+
The Laravel package versions lock-step with `php-opcua/opcua-client` and `php-opcua/opcua-session-manager`. Always upgrade in this order:
218+
219+
1. **Daemon first.** Stop `opcua:session`, `composer update`, restart.
220+
2. **Application second.** `composer update php-opcua/laravel-opcua`.
221+
222+
If you upgrade application before daemon and call a v4.4 method (e.g. `historyInsertData`), `ManagedClient::__call()` will fail with `BadMethodCallException` because the daemon has no handler for it.
223+
224+
## What this skill does NOT cover
225+
226+
- The raw OPC UA protocol — see the `opcua-client` skill.
227+
- The session-manager daemon's IPC protocol — see the `opcua-session-manager` skill.
228+
- CLI usage — see the `opcua-cli` skill.
229+
- Companion-spec types (DI, IA, AutoID, etc.) — see the `opcua-client-nodeset` skill.
230+
231+
Cross-skill workflow example (`docs/recipes/persistent-tag-history.md`):
232+
- `opcua-cli generate:nodeset Vendor.NodeSet2.xml ...` (nodeset skill)
233+
- App reads typed nodes via `Opcua::read()` (this skill)
234+
- Persists into a historian via `Opcua::historyInsertData()` (this skill + opcua-client)
235+
- A Filament panel browses results (this skill + Filament integration)

0 commit comments

Comments
 (0)