Skip to content

Commit 7ad209c

Browse files
committed
1 parent 2433df8 commit 7ad209c

10 files changed

Lines changed: 494 additions & 5 deletions

composer.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,15 @@
1313
}
1414
],
1515
"require": {
16-
"react/socket": "^1.16",
1716
"nette/php-generator": "^4.1",
1817
"ramsey/uuid": "^4.7",
19-
"justinrainbow/json-schema": "^6.0",
20-
"phrity/websocket": "^3.2"
18+
"justinrainbow/json-schema": "^6.0"
19+
},
20+
"require-dev": {
21+
},
22+
"suggest": {
23+
"react/socket": "Required for running WebSocket examples",
24+
"phrity/websocket": "Required for running WebSocket examples"
2125
},
2226
"bin": [
2327
"bin/ocpp-php"

src/Examples/v16/CentralSystem.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99

1010
require __DIR__ . '/../../../vendor/autoload.php';
1111

12+
if (!class_exists('WebSocket\Server')) {
13+
throw new Exception("phrity/websocket is required for examples. Run: composer require phrity/websocket");
14+
}
15+
1216

1317
// Helper function to parse JSON messages (very basic)
1418
function parseJsonMessage($data)

src/Examples/v16/ChargePoint.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
require __DIR__ . '/../../../vendor/autoload.php';
44

5+
if (!class_exists('WebSocket\Client')) {
6+
throw new Exception("phrity/websocket is required for examples. Run: composer require phrity/websocket");
7+
}
8+
59
use SolutionForest\OcppPhp\Ocpp\JsonSchemaValidator;
610
use SolutionForest\OcppPhp\Ocpp\v16\Calls;
711

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
<?php
2+
3+
/**
4+
* Example demonstrating the factory methods for creating Call and CallResult objects
5+
* from raw OCPP message arrays.
6+
*
7+
* This eliminates boilerplate code when parsing incoming OCPP messages.
8+
*/
9+
10+
require __DIR__ . '/../../../vendor/autoload.php';
11+
12+
use SolutionForest\OcppPhp\Ocpp\JsonSchemaRegistry;
13+
use SolutionForest\OcppPhp\Ocpp\JsonSchemaValidator;
14+
use SolutionForest\OcppPhp\Ocpp\Messages\Call;
15+
use SolutionForest\OcppPhp\Ocpp\Messages\CallResult;
16+
17+
echo "=== OCPP v1.6 Factory Methods Example ===\n\n";
18+
19+
$registry = new JsonSchemaRegistry();
20+
21+
// ============================================
22+
// Example 1: Create a Call from raw message array
23+
// ============================================
24+
echo "--- Example 1: Creating Call from raw array ---\n";
25+
26+
// This is how a raw OCPP Call message looks when received via WebSocket
27+
$rawBootNotification = [
28+
2, // messageTypeID (2 = Call)
29+
"19223201", // messageId
30+
"BootNotification", // action
31+
[ // payload
32+
"chargePointVendor" => "VendorX",
33+
"chargePointModel" => "ModelY",
34+
"chargePointSerialNumber" => "SN123456",
35+
"firmwareVersion" => "1.0.0"
36+
]
37+
];
38+
39+
// Using Call::fromArray() static method
40+
$bootCall = Call::fromArray($rawBootNotification, 'v1.6');
41+
42+
echo "Created BootNotification Call:\n";
43+
echo " - Vendor: {$bootCall->chargePointVendor}\n";
44+
echo " - Model: {$bootCall->chargePointModel}\n";
45+
echo " - Serial: {$bootCall->chargePointSerialNumber}\n";
46+
echo " - Message ID: {$bootCall->messageId}\n";
47+
48+
// Validate the object
49+
JsonSchemaValidator::validate($bootCall, 'v1.6');
50+
echo " - Validation: PASSED\n\n";
51+
52+
// ============================================
53+
// Example 2: Create a Call using the registry
54+
// ============================================
55+
echo "--- Example 2: Creating Call via JsonSchemaRegistry ---\n";
56+
57+
$rawHeartbeat = [
58+
2,
59+
"19223202",
60+
"Heartbeat",
61+
[]
62+
];
63+
64+
$heartbeatCall = $registry->createFromArray($rawHeartbeat, 'v1.6');
65+
echo "Created Heartbeat Call via registry\n";
66+
echo " - Message ID: {$heartbeatCall->messageId}\n";
67+
echo " - Type: " . get_class($heartbeatCall) . "\n\n";
68+
69+
// ============================================
70+
// Example 3: Create a Call with constructor payload
71+
// ============================================
72+
echo "--- Example 3: Creating Call with constructor payload ---\n";
73+
74+
use SolutionForest\OcppPhp\Ocpp\v16\Calls\StatusNotification;
75+
76+
$statusCall = new StatusNotification([
77+
'connectorId' => 1,
78+
'status' => 'Available',
79+
'errorCode' => 'NoError',
80+
'timestamp' => date('c')
81+
]);
82+
83+
echo "Created StatusNotification with constructor payload:\n";
84+
echo " - Connector ID: {$statusCall->connectorId}\n";
85+
echo " - Status: {$statusCall->status}\n";
86+
echo " - Error Code: {$statusCall->errorCode}\n\n";
87+
88+
// ============================================
89+
// Example 4: Create a CallResult from raw message array
90+
// ============================================
91+
echo "--- Example 4: Creating CallResult from raw array ---\n";
92+
93+
// This is how a raw OCPP CallResult message looks when received
94+
$rawBootResponse = [
95+
3, // messageTypeID (3 = CallResult)
96+
"19223201", // messageId (matches the original Call)
97+
[ // payload
98+
"status" => "Accepted",
99+
"currentTime" => date('c'),
100+
"interval" => 300
101+
]
102+
];
103+
104+
// For CallResult, we need to specify which action this is a response to
105+
$bootResult = CallResult::fromArray($rawBootResponse, 'BootNotification', 'v1.6');
106+
107+
echo "Created BootNotification CallResult:\n";
108+
echo " - Status: {$bootResult->status}\n";
109+
echo " - Interval: {$bootResult->interval}\n";
110+
echo " - Message ID: {$bootResult->messageId}\n";
111+
112+
// Validate the object
113+
JsonSchemaValidator::validate($bootResult, 'v1.6');
114+
echo " - Validation: PASSED\n\n";
115+
116+
// ============================================
117+
// Example 5: Using registry for CallResult
118+
// ============================================
119+
echo "--- Example 5: Creating CallResult via JsonSchemaRegistry ---\n";
120+
121+
$rawHeartbeatResponse = [
122+
3,
123+
"19223202",
124+
[
125+
"currentTime" => date('c')
126+
]
127+
];
128+
129+
// When using the registry, pass the action as third parameter
130+
$heartbeatResult = $registry->createFromArray($rawHeartbeatResponse, 'v1.6', 'Heartbeat');
131+
echo "Created Heartbeat CallResult via registry\n";
132+
echo " - Current Time: {$heartbeatResult->currentTime}\n";
133+
echo " - Type: " . get_class($heartbeatResult) . "\n\n";
134+
135+
// ============================================
136+
// Example 6: Full round-trip simulation
137+
// ============================================
138+
echo "--- Example 6: Full round-trip simulation ---\n";
139+
140+
// Simulate receiving a raw message (as if from WebSocket)
141+
$incomingJson = '[2, "msg-001", "MeterValues", {"connectorId": 1, "meterValue": []}]';
142+
$incomingMessage = json_decode($incomingJson, true);
143+
144+
echo "Received raw JSON: {$incomingJson}\n";
145+
146+
// Parse into typed object
147+
$meterValuesCall = Call::fromArray($incomingMessage, 'v1.6');
148+
echo "Parsed into: " . get_class($meterValuesCall) . "\n";
149+
150+
// Convert back to array for sending
151+
$outgoingArray = $meterValuesCall->toArray();
152+
$outgoingJson = json_encode($outgoingArray);
153+
echo "Converted back to JSON: {$outgoingJson}\n\n";
154+
155+
echo "=== Example Complete ===\n";

src/Examples/v201/CentralSystem.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88

99
require __DIR__ . '/../../../vendor/autoload.php';
1010

11+
if (!class_exists('WebSocket\Server')) {
12+
throw new Exception("phrity/websocket is required for examples. Run: composer require phrity/websocket");
13+
}
14+
1115

1216
// Helper function to parse JSON messages (very basic)
1317
function parseJsonMessage($data)

src/Examples/v201/ChargePoint.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
require __DIR__ . '/../../../vendor/autoload.php';
44

5+
if (!class_exists('WebSocket\Client')) {
6+
throw new Exception("phrity/websocket is required for examples. Run: composer require phrity/websocket");
7+
}
8+
59
use SolutionForest\OcppPhp\Ocpp\JsonSchemaValidator;
610
use SolutionForest\OcppPhp\Ocpp\v16\Calls;
711

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
<?php
2+
3+
/**
4+
* Example demonstrating the factory methods for creating Call and CallResult objects
5+
* from raw OCPP 2.0.1 message arrays.
6+
*
7+
* This eliminates boilerplate code when parsing incoming OCPP messages.
8+
*/
9+
10+
require __DIR__ . '/../../../vendor/autoload.php';
11+
12+
use SolutionForest\OcppPhp\Ocpp\JsonSchemaRegistry;
13+
use SolutionForest\OcppPhp\Ocpp\JsonSchemaValidator;
14+
use SolutionForest\OcppPhp\Ocpp\Messages\Call;
15+
use SolutionForest\OcppPhp\Ocpp\Messages\CallResult;
16+
17+
echo "=== OCPP v2.0.1 Factory Methods Example ===\n\n";
18+
19+
$registry = new JsonSchemaRegistry();
20+
21+
// ============================================
22+
// Example 1: Create a Call from raw message array
23+
// ============================================
24+
echo "--- Example 1: Creating BootNotification Call from raw array ---\n";
25+
26+
// This is how a raw OCPP 2.0.1 BootNotification looks
27+
$rawBootNotification = [
28+
2, // messageTypeID (2 = Call)
29+
"boot-001", // messageId
30+
"BootNotification", // action
31+
[ // payload (OCPP 2.0.1 format)
32+
"chargingStation" => [
33+
"model" => "ModelX",
34+
"vendorName" => "VendorY"
35+
],
36+
"reason" => "PowerUp"
37+
]
38+
];
39+
40+
// Using Call::fromArray() static method
41+
$bootCall = Call::fromArray($rawBootNotification, 'v2.0.1');
42+
43+
echo "Created BootNotification Call:\n";
44+
echo " - Message ID: {$bootCall->messageId}\n";
45+
echo " - Type: " . get_class($bootCall) . "\n";
46+
47+
// Validate the object
48+
try {
49+
JsonSchemaValidator::validate($bootCall, 'v2.0.1');
50+
echo " - Validation: PASSED\n\n";
51+
} catch (Exception $e) {
52+
echo " - Validation: {$e->getMessage()}\n\n";
53+
}
54+
55+
// ============================================
56+
// Example 2: Create a Call using the registry
57+
// ============================================
58+
echo "--- Example 2: Creating Heartbeat via JsonSchemaRegistry ---\n";
59+
60+
$rawHeartbeat = [
61+
2,
62+
"hb-001",
63+
"Heartbeat",
64+
[]
65+
];
66+
67+
$heartbeatCall = $registry->createFromArray($rawHeartbeat, 'v2.0.1');
68+
echo "Created Heartbeat Call via registry\n";
69+
echo " - Message ID: {$heartbeatCall->messageId}\n";
70+
echo " - Type: " . get_class($heartbeatCall) . "\n\n";
71+
72+
// ============================================
73+
// Example 3: Create a CallResult from raw message array
74+
// ============================================
75+
echo "--- Example 3: Creating BootNotification CallResult from raw array ---\n";
76+
77+
$rawBootResponse = [
78+
3, // messageTypeID (3 = CallResult)
79+
"boot-001", // messageId
80+
[ // payload (OCPP 2.0.1 format)
81+
"status" => "Accepted",
82+
"currentTime" => date('c'),
83+
"interval" => 300
84+
]
85+
];
86+
87+
// For CallResult, we need to specify which action this is a response to
88+
$bootResult = CallResult::fromArray($rawBootResponse, 'BootNotification', 'v2.0.1');
89+
90+
echo "Created BootNotification CallResult:\n";
91+
echo " - Status: {$bootResult->status}\n";
92+
echo " - Interval: {$bootResult->interval}\n";
93+
echo " - Message ID: {$bootResult->messageId}\n\n";
94+
95+
// ============================================
96+
// Example 4: StatusNotification (v2.0.1 format)
97+
// ============================================
98+
echo "--- Example 4: StatusNotification in v2.0.1 format ---\n";
99+
100+
$rawStatusNotification = [
101+
2,
102+
"status-001",
103+
"StatusNotification",
104+
[
105+
"timestamp" => date('c'),
106+
"connectorStatus" => "Available",
107+
"evseId" => 1,
108+
"connectorId" => 1
109+
]
110+
];
111+
112+
$statusCall = Call::fromArray($rawStatusNotification, 'v2.0.1');
113+
echo "Created StatusNotification Call:\n";
114+
echo " - Type: " . get_class($statusCall) . "\n";
115+
echo " - Message ID: {$statusCall->messageId}\n\n";
116+
117+
// ============================================
118+
// Example 5: Handling unknown message types
119+
// ============================================
120+
echo "--- Example 5: Error handling for unknown actions ---\n";
121+
122+
try {
123+
$unknownMessage = [2, "msg-999", "UnknownAction", []];
124+
Call::fromArray($unknownMessage, 'v2.0.1');
125+
} catch (Exception $e) {
126+
echo "Caught expected error: {$e->getMessage()}\n\n";
127+
}
128+
129+
// ============================================
130+
// Example 6: Full workflow simulation
131+
// ============================================
132+
echo "--- Example 6: Full workflow simulation ---\n";
133+
134+
// Step 1: Receive raw JSON from WebSocket
135+
$incomingJson = '[2, "trans-001", "TransactionEvent", {"eventType": "Started", "timestamp": "2026-01-30T12:00:00Z", "triggerReason": "Authorized", "seqNo": 0, "transactionInfo": {"transactionId": "tx-123"}}]';
136+
echo "1. Received: {$incomingJson}\n";
137+
138+
// Step 2: Parse JSON
139+
$rawMessage = json_decode($incomingJson, true);
140+
141+
// Step 3: Create typed object
142+
$transactionCall = Call::fromArray($rawMessage, 'v2.0.1');
143+
echo "2. Parsed into: " . get_class($transactionCall) . "\n";
144+
145+
// Step 4: Access typed properties (if they exist on the class)
146+
echo "3. Message ID: {$transactionCall->messageId}\n";
147+
148+
// Step 5: Convert back for response
149+
$responseArray = $transactionCall->toArray();
150+
echo "4. Serialized: " . json_encode($responseArray) . "\n\n";
151+
152+
echo "=== Example Complete ===\n";

0 commit comments

Comments
 (0)