Skip to content

Commit 21eb807

Browse files
committed
The code provided is a PHP script that establishes a WebSocket connection to a server using the WebSocket Protocol. The WebSocket Protocol allows for bidirectional, real-time communication between a client and a server over the web.
Here are some key aspects of the code: 1. **Establishing Connection**: The script establishes a WebSocket connection by calling the `onHandshake` method on the `$client` object. This method is called when the client's browser sends a handshake request to the server, which contains the client's WebSocket protocol version. 2. **Handling Incoming Messages**: The script handles incoming messages from the server using the `onText` method, which is called whenever a text message is received from the server. If a non-text message (e.g., binary data) is received, it uses the `onBinary` method instead. 3. **Sending Messages**: When a message is received from the client and needs to be sent back to the server, it is encoded as JSON and then written to the connection using the `$connection->write()` method. 4. **Error Handling**: The script handles errors that occur during the WebSocket handshake or while sending/receiving messages using the `onClose` method, which is called when a connection is closed due to an error. It also uses the `onError` method, which is called whenever an error occurs on the connection. 5. **Connection Lifecycle Events**: The script handles various lifecycle events for the WebSocket connection, such as connection opening (`onOpen`), connection closing (`onClose`), and disconnection errors that occur during the handshake process (`onClose`). Here are some potential improvements: 1. **Error Handling**: Although error handling is present in the code, it might be more informative to provide details about the specific error that occurred. 2. **Security**: It's a good practice to check for server authentication credentials or require a token before establishing the WebSocket connection. 3. **Connection Timeout**: If no data has been sent/received within a specified time period (connection timeout), it could lead to a disconnection, which should be handled accordingly in your application logic. 4. **Code Structure**: The script includes multiple `on*` methods that handle specific events during the WebSocket connection lifecycle. It would be beneficial to extract these into separate functions or classes for better organization and reusability.
1 parent c072d0a commit 21eb807

2 files changed

Lines changed: 236 additions & 101 deletions

File tree

Lines changed: 137 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
<?php
22

3-
require __DIR__ . '/../../../vendor/autoload.php';
4-
5-
use React\EventLoop\Loop;
6-
use React\Socket\SocketServer;
7-
use React\Socket\ConnectionInterface;
83
use SolutionForest\OcppPhp\Ocpp\Exceptions\NotImplementedError;
94
use SolutionForest\OcppPhp\Ocpp\JsonSchemaValidator;
10-
use SolutionForest\OcppPhp\Ocpp\Messages\CallResult;
11-
use SolutionForest\OcppPhp\Ocpp\v201\CallResults;
5+
use SolutionForest\OcppPhp\Ocpp\v16\CallResults\Heartbeat;
6+
use SolutionForest\OcppPhp\Ocpp\v16\CallResults\StatusNotification;
7+
use SolutionForest\OcppPhp\Ocpp\v16\CallResults\BootNotification;
8+
9+
require __DIR__ . '/../../../vendor/autoload.php';
10+
1211

1312
// Helper function to parse JSON messages (very basic)
1413
function parseJsonMessage($data)
@@ -17,25 +16,14 @@ function parseJsonMessage($data)
1716
return json_decode($data, true) ?? null;
1817
}
1918

20-
// Central System (Server)
21-
22-
$loop = Loop::get();
23-
$socket = new SocketServer('0.0.0.0:8080', [], $loop);
24-
25-
$socket->on('connection', function (ConnectionInterface $connection) {
26-
$connection->write("Hello " . $connection->getRemoteAddress() . "!\n\n");
27-
28-
echo "Central System: New connection from Charge Point\n\n";
29-
30-
31-
$connection->on('data', function ($data) use ($connection) {
32-
echo "Central System: Received message\n\n'";
19+
function centralCallBack( $data , $connection ) {
20+
echo "Central System: Received message\n\n";
3321
echo $data . "\n\n";
3422

3523
$message = parseJsonMessage($data);
3624

3725
if ($message === null) {
38-
$connection->write('{"error": "Invalid JSON"}' . "\n\n");
26+
$connection->send(new \WebSocket\Message\Text('{"error": "Invalid JSON"}' . "\n\n"));
3927
return;
4028
}
4129

@@ -44,48 +32,159 @@ function parseJsonMessage($data)
4432
$callResult = 'SolutionForest\OcppPhp\Ocpp\v201\CallResults\\' . $call;
4533

4634
if (!class_exists($callResult)) {
47-
$connection->write('{"error": "Unknown call"}' . "\n\n");
35+
$connection->send(new \WebSocket\Message\Text('{"error": "Unknown call"}' . "\n\n"));
4836
return;
4937
}
5038

5139
$callResult = new $callResult($message['messageId']);
5240

5341
switch ($callResult::class) {
54-
case CallResults\BootNotification::class:
42+
case BootNotification::class:
5543
$callResult->status = 'Accepted';
5644
$callResult->currentTime = date('c');
5745
$callResult->interval = 60;
5846
break;
59-
case CallResults\Heartbeat::class:
47+
case Heartbeat::class:
6048
$callResult->currentTime = date('c');
6149
break;
62-
case CallResults\StatusNotification::class:
50+
case StatusNotification::class:
6351
break;
6452
default:
6553
$callResult = new NotImplementedError($message['messageId']);
6654
break;
6755
}
6856

69-
7057
if ($callResult instanceof CallResult) {
7158
JsonSchemaValidator::validate($callResult, 'v2.0.1');
7259
}
7360
$response = $callResult->toArray();
7461
echo "Central System: Sending response: " . json_encode($response) . "\n\n";
7562

76-
$connection->write(json_encode($response) . "\n");
77-
});
63+
$connection->send(new \WebSocket\Message\Text(json_encode($response)));
64+
}
7865

7966

80-
$connection->on('end', function () {
81-
echo "Central System: ended\n\n";
82-
});
67+
echo "# Echo server! [Central System (Server)]\n";
68+
69+
// Server options specified or default
70+
$options = array_merge([
71+
'port' => 8080,
72+
], getopt('', ['port:', 'ssl', 'timeout:', 'framesize:', 'connections:', 'debug']));
73+
74+
// Initiate server.
75+
try {
76+
$server = new WebSocket\Server($options['port'], isset($options['ssl']));
77+
$server
78+
->addMiddleware(new \WebSocket\Middleware\CloseHandler())
79+
->addMiddleware(new \WebSocket\Middleware\PingResponder())
80+
;
81+
82+
// If debug mode and logger is available
83+
if (isset($options['debug']) && class_exists('WebSocket\Test\EchoLog')) {
84+
$server->setLogger(new \WebSocket\Test\EchoLog());
85+
echo "# Using logger\n";
86+
}
87+
if (isset($options['timeout'])) {
88+
$server->setTimeout($options['timeout']);
89+
echo "# Set timeout: {$options['timeout']}\n";
90+
}
91+
if (isset($options['framesize'])) {
92+
$server->setFrameSize($options['framesize']);
93+
echo "# Set frame size: {$options['framesize']}\n";
94+
}
95+
if (isset($options['connections'])) {
96+
$server->setMaxConnections($options['connections']);
97+
echo "# Set max connections: {$options['connections']}\n";
98+
}
99+
100+
echo "# Listening on port {$server->getPort()}\n";
101+
$server->onHandshake(function ($server, $connection, $request, $response) {
102+
echo "> [{$connection->getRemoteName()}] Client connected {$request->getUri()}\n";
103+
})->onDisconnect(function ($server, $connection) {
104+
echo "> [{$connection->getRemoteName()}] Client disconnected\n";
105+
})->onText(function ($server, $connection, $message) {
106+
echo "> [{$connection->getRemoteName()}] Received [{$message->getOpcode()}] {$message->getContent()}\n";
107+
switch ($message->getContent()) {
108+
// Connection commands
109+
case '@close':
110+
echo "< [{$connection->getRemoteName()}] Sending Close\n";
111+
$connection->send(new \WebSocket\Message\Close());
112+
break;
113+
case '@ping':
114+
echo "< [{$connection->getRemoteName()}] Sending Ping\n";
115+
$connection->send(new \WebSocket\Message\Ping());
116+
break;
117+
case '@disconnect':
118+
echo "< [{$connection->getRemoteName()}] Disconnecting\n";
119+
$connection->disconnect();
120+
break;
121+
case '@info':
122+
$msg = "Connection info:\n";
123+
$msg .= " - Local: {$connection->getName()}\n";
124+
$msg .= " - Remote: {$connection->getRemoteName()}\n";
125+
$msg .= " - Request: {$connection->getHandshakeRequest()->getUri()}\n";
126+
$msg .= " - Response: {$connection->getHandshakeResponse()->getStatusCode()}\n";
127+
$msg .= " - Connected: " . json_encode($connection->isConnected()) . "\n";
128+
$msg .= " - Readable: " . json_encode($connection->isReadable()) . "\n";
129+
$msg .= " - Writable: " . json_encode($connection->isWritable()) . "\n";
130+
$msg .= " - Timeout: {$connection->getTimeout()}s\n";
131+
$msg .= " - Frame size: {$connection->getFrameSize()}b\n";
132+
echo "< [{$connection->getRemoteName()}] {$msg}";
133+
$server->send(new \WebSocket\Message\Text($msg));
134+
break;
83135

84-
$connection->on('error', function (Exception $e) {
85-
echo 'error: ' . $e->getMessage();
86-
});
136+
// Server commands
137+
case '@server-stop':
138+
echo "< [{$connection->getRemoteName()}] Stop server\n";
139+
$server->stop();
140+
break;
141+
case '@server-shutdown':
142+
echo "< [{$connection->getRemoteName()}] Shutdown server\n";
143+
$server->shutdown();
144+
break;
145+
case '@server-close':
146+
echo "< [{$connection->getRemoteName()}] Broadcast Close\n";
147+
$server->send(new \WebSocket\Message\Close());
148+
break;
149+
case '@server-ping':
150+
echo "< [{$connection->getRemoteName()}] Broadcast Ping\n";
151+
$server->send(new \WebSocket\Message\Ping());
152+
break;
153+
case '@server-disconnect':
154+
echo "< [{$connection->getRemoteName()}] Disconnecting server\n";
155+
$server->disconnect();
156+
break;
157+
case '@server-info':
158+
$msg = "Server info:\n";
159+
$msg .= " - Running: " . json_encode($server->isRunning()) . "\n";
160+
$msg .= " - Connections: {$server->getConnectionCount()}\n";
161+
$msg .= " - Port: {$server->getPort()}\n";
162+
$msg .= " - Scheme: {$server->getScheme()}\n";
163+
$msg .= " - Timeout: {$server->getTimeout()}s\n";
164+
$msg .= " - Frame size: {$server->getFrameSize()}b\n";
165+
echo "< [{$connection->getRemoteName()}] {$msg}";
166+
$server->send(new \WebSocket\Message\Text($msg));
167+
break;
87168

88-
$connection->on('close', function () {
89-
echo "Central System: Connection closed\n\n";
90-
});
91-
});
169+
// Echo received message
170+
default:
171+
centralCallBack($message->getContent(), $connection);
172+
echo "< [{$connection->getRemoteName()}] Sent [{$message->getOpcode()}] {$message->getContent()}\n";
173+
}
174+
})->onBinary(function ($server, $connection, $message) {
175+
echo "> [{$connection->getRemoteName()}] Received [{$message->getOpcode()}]\n";
176+
$connection->send($message); // Echo
177+
echo "< [{$connection->getRemoteName()}] Sent [{$message->getOpcode()}] {$message->getContent()}\n";
178+
})->onPing(function ($server, $connection, $message) {
179+
echo "> [{$connection->getRemoteName()}] Received [{$message->getOpcode()}] {$message->getContent()}\n";
180+
})->onPong(function ($server, $connection, $message) {
181+
echo "> [{$connection->getRemoteName()}] Received [{$message->getOpcode()}] {$message->getContent()}\n";
182+
})->onClose(function ($server, $connection, $message) {
183+
echo "> [{$connection->getRemoteName()}] Received [{$message->getOpcode()}] "
184+
. "{$message->getCloseStatus()} {$message->getContent()}\n";
185+
})->onError(function ($server, $connection, $exception) {
186+
echo "> Error: {$exception->getMessage()}\n";
187+
})->start();
188+
} catch (\Throwable $e) {
189+
echo "# ERROR: {$e->getMessage()}\n";
190+
}

src/Examples/v201/ChargePoint.php

Lines changed: 99 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -2,68 +2,104 @@
22

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

5-
use React\Socket\ConnectionInterface;
6-
use React\EventLoop\Loop;
75
use SolutionForest\OcppPhp\Ocpp\JsonSchemaValidator;
8-
use SolutionForest\OcppPhp\Ocpp\v201\Calls;
9-
use SolutionForest\OcppPhp\Ocpp\v201\Enums\BootReason;
10-
11-
$loop = Loop::get();
12-
$connector = new \React\Socket\Connector($loop);
13-
$connector->connect('127.0.0.1:8080')
14-
->then(function (ConnectionInterface $connection) {
15-
$connection->on('data', function ($data) {
6+
use SolutionForest\OcppPhp\Ocpp\v16\Calls;
7+
8+
function chargerPoingCallback($client)
9+
{
10+
echo "Charge Point: Connected to Central System\n\n";
11+
12+
$boot = new Calls\BootNotification();
13+
$boot->chargingStation = (object) [
14+
'model' => 'MyModel',
15+
'vendorName' => 'MyVendor'
16+
];
17+
$boot->reason = BootReason::ApplicationReset->value;
18+
19+
$heartbeat = new Calls\Heartbeat();
20+
21+
$statusNotification = new Calls\StatusNotification();
22+
$statusNotification->connectorId = 1;
23+
$statusNotification->connectorStatus = 'Available';
24+
$statusNotification->evseId = 1;
25+
$statusNotification->timestamp = date('c');
26+
27+
$notImplemented = new Calls\ClearCache();
28+
29+
$messages = [
30+
$boot,
31+
$heartbeat,
32+
$statusNotification,
33+
$notImplemented
34+
];
35+
36+
37+
$message = $messages[rand(0, count($messages) - 1)];
38+
39+
JsonSchemaValidator::validate($message, 'v2.0.1');
40+
$message = $message->toArray();
41+
42+
echo "Charge Point: Sending message: " . json_encode($message) . "\n\n";
43+
$client->text(json_encode($message));
44+
}
45+
46+
47+
echo "# Random client\n";
48+
49+
// Initiate client.
50+
while (true) {
51+
// Server options specified or random
52+
$options = array_merge([
53+
'uri' => 'ws://127.0.0.1:8080',
54+
'timeout' => rand(1, 60),
55+
'framesize' => rand(1, 4096) * 8,
56+
], getopt('', ['uri:', 'timeout:', 'framesize:', 'debug']));
57+
58+
try {
59+
$client = new WebSocket\Client($options['uri']);
60+
$client
61+
->addMiddleware(new \WebSocket\Middleware\CloseHandler())
62+
->addMiddleware(new \WebSocket\Middleware\PingResponder())
63+
;
64+
65+
// If debug mode and logger is available
66+
if (isset($options['debug']) && class_exists('WebSocket\Test\EchoLog')) {
67+
$client->setLogger(new \WebSocket\Test\EchoLog());
68+
echo "# Using logger\n";
69+
}
70+
if (isset($options['timeout'])) {
71+
$client->setTimeout($options['timeout']);
72+
echo "# Set timeout: {$options['timeout']}\n";
73+
}
74+
if (isset($options['framesize'])) {
75+
$client->setFrameSize($options['framesize']);
76+
echo "# Set frame size: {$options['framesize']}\n";
77+
}
78+
79+
echo "# Listening on {$options['uri']}\n";
80+
$client->onHandshake(function ($server, $connection, $request, $response) {
81+
echo "> [{$connection->getRemoteName()}] Server connected {$response->getStatusCode()}\n";
82+
chargerPoingCallback($server);
83+
})->onDisconnect(function ($client, $connection) {
84+
echo "> [{$connection->getRemoteName()}] Server disconnected\n";
85+
})->onText(function ($client, $connection, $message) {
86+
echo "> [{$connection->getRemoteName()}] Received [{$message->getOpcode()}] {$message->getContent()}\n";
1687
echo "Charge Point: Received message\n\n";
17-
echo $data;
18-
});
19-
20-
$connection->on('end', function () {
21-
echo "Charge Point: ended\n\n";
22-
});
23-
24-
$connection->on('error', function (Exception $e) {
25-
echo 'error: ' . $e->getMessage();
26-
});
27-
28-
$connection->on('close', function () {
29-
echo "Charge Point: Connection closed\n\n";
30-
});
31-
32-
33-
echo "Charge Point: Connected to Central System\n\n";
34-
35-
$boot = new Calls\BootNotification();
36-
$boot->chargingStation = (object) [
37-
'model' => 'MyModel',
38-
'vendorName' => 'MyVendor'
39-
];
40-
$boot->reason = BootReason::ApplicationReset->value;
41-
42-
$heartbeat = new Calls\Heartbeat();
43-
44-
$statusNotification = new Calls\StatusNotification();
45-
$statusNotification->connectorId = 1;
46-
$statusNotification->connectorStatus = 'Available';
47-
$statusNotification->evseId = 1;
48-
$statusNotification->timestamp = date('c');
49-
50-
$notImplemented = new Calls\ClearCache();
51-
52-
$messages = [
53-
$boot,
54-
$heartbeat,
55-
$statusNotification,
56-
$notImplemented
57-
];
58-
59-
60-
$message = $messages[rand(0, count($messages) - 1)];
61-
62-
JsonSchemaValidator::validate($message, 'v2.0.1');
63-
$message = $message->toArray();
64-
65-
echo "Charge Point: Sending message: " . json_encode($message) . "\n\n";
66-
$connection->write(json_encode($message) . "\n");
67-
}, function (\Exception $e) {
68-
echo "Charge Point: Connection failed: " . $e->getMessage() . "\n\n";
69-
});
88+
})->onBinary(function ($client, $connection, $message) {
89+
echo "> [{$connection->getRemoteName()}] Received [{$message->getOpcode()}]\n";
90+
})->onPing(function ($client, $connection, $message) {
91+
echo "> [{$connection->getRemoteName()}] Received [{$message->getOpcode()}]\n";
92+
})->onPong(function ($client, $connection, $message) {
93+
echo "> [{$connection->getRemoteName()}] Received [{$message->getOpcode()}]\n";
94+
})->onClose(function ($client, $connection, $message) {
95+
echo "> [{$connection->getRemoteName()}] Received [{$message->getOpcode()}] {$message->getCloseStatus()}\n";
96+
})->onError(function ($client, $connection, $exception) {
97+
$name = $connection ? "[{$connection->getRemoteName()}]" : "[-]";
98+
echo "> {$name} Error: {$exception->getMessage()}\n";
99+
})->start();
100+
101+
102+
} catch (Throwable $e) {
103+
echo "> ERROR: {$e->getMessage()}\n";
104+
}
105+
}

0 commit comments

Comments
 (0)