Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update web socket examples #111

Merged
merged 9 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/C/src/browser-ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ The UI runs in the browser and connects to the program via either HTTP or via a
</tr>
<tr>
<td> <img src="img/Uptime.png" alt="Uptime" width="400">
<td> <a href="Uptime.lf"> Uptime.lf</a>: This version combines ServerUI with WebSocketServer to serve a web page and then feed it data continuously through a web socket. The application displays the total time that application has been running and updates this time once per second.</td>
<td> <a href="Uptime.lf"> Uptime.lf</a>: This version uses WebSocketServer to serve a web page and then feed it data continuously through a web socket. The application displays the total time that application has been running and updates this time once per second.</td>
</tr>
</table>
24 changes: 11 additions & 13 deletions examples/C/src/browser-ui/Uptime.lf
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
/**
* This example combines `ServerUI` with `WebSocketServer`. The former starts a web server that
* listens for HTTP requests on port 8080 and serves the web page defined in `uptime.html`. That web
* page includes JavaScript that connects to a web socket on port 8080 that is provided by the
* `WebSocketServer` reactor. The resulting web page simply reports the total time that this program
* has been running. That time is updated on the web page once per second.
* This example uses `WebSocketServer` to start a web server that listens for HTTP requests on port
* 8080 and serves the web page defined in `uptime.html`. That web page includes JavaScript that
* connects to a web socket on port 8080 that is provided by the `WebSocketServer` reactor. The
* resulting web page simply reports the total time that this program has been running. That time is
* updated on the web page once per second.
*
* This uses the <a href="https://libwebsockets.org">libwebsockets</a> (see <a
* href="https://libwebsockets.org/lws-api-doc-main/html/index.html">API documentation</a> and <a
* href="https://libwebsockets.org/lws-api-doc-main/html/md_READMEs_README_build.html">installation
* instructions</a>). To install on MacOS, we recommending using brew:
* <pre> brew install libwebsockets
* </pre> This puts the compiled libraries in {@code /usr/local/lib}, and these libraries can be
* linked to using the {@code -lwebsockets} compile option or the {@code WebSocketCmake.txt} Cmake
* include file.
* ```
* brew install libwebsockets
* ```
* This puts the compiled libraries in {@code /usr/local/lib}, and these libraries can be linked to
* using the {@code -lwebsockets} compile option or the {@code WebSocketCmake.txt} Cmake include
* file.
*
* @author Edward A. Lee
*/
Expand All @@ -30,10 +32,6 @@ main reactor {
hostport=8080,
initial_file = {= LF_SOURCE_DIRECTORY LF_FILE_SEPARATOR "Uptime.html" =})

reaction(startup) {=
lf_print("Point your browser to http://localhost:8080");
=}

reaction(seconds) -> w.send {=
instant_t uptime = lf_time_logical_elapsed();
// Truncate to the nearest second.
Expand Down
10 changes: 4 additions & 6 deletions examples/C/src/lib/WebSocketServer.lf
Original file line number Diff line number Diff line change
Expand Up @@ -315,12 +315,6 @@ reactor WebSocketServer(
if (to_send->binary) {
result = lws_write(to_send->wsi, &buffer[LWS_PRE], length, LWS_WRITE_BINARY);
} else {
// Check for null terminator.
if (buffer[LWS_PRE + length - 1] != '\0') {
// Null terminator is missing.
buffer[LWS_PRE + length] = '\0';
length += 1;
}
result = lws_write(to_send->wsi, &buffer[LWS_PRE], length, LWS_WRITE_TEXT);
}
if (result < length) {
Expand Down Expand Up @@ -495,6 +489,10 @@ reactor WebSocketServer(

lf_thread_t listener;
lf_thread_create(&listener, &websocket_thread, &self->status);

if (self->initial_file != NULL) {
lf_print("**** Point your browser to http://localhost:%d", self->hostport);
}
=}

reaction(received_action) -> received {=
Expand Down
11 changes: 7 additions & 4 deletions examples/C/src/lib/WebSocketServerString.lf
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* the running LF program. To specify a file located in the same directory as your source LF file,
* you can use a syntax like this:
* ```
* s = new WebSocketServer(
* s = new WebSocketServerString(
* initial_file = {= LF_SOURCE_DIRECTORY LF_FILE_SEPARATOR "filename.html" =}
* )
* ```
Expand All @@ -40,15 +40,15 @@
*
* A key limitation is that this should use the secure sockets API in libwebsockets to get SSL.
*
* @param hostport The host port number, which defaults to 8000.
* @param hostport The host port number, which defaults to 8080.
*
* @author Edward A. Lee
*/
target C

import WebSocketServer from "WebSocketServer.lf"

reactor WebSocketServerString(hostport: int = 8000, initial_file: string = {= NULL =}) {
reactor WebSocketServerString(hostport: int = 8080, initial_file: string = {= NULL =}) {
input in_dynamic: char*
input in_static: string

Expand All @@ -62,6 +62,9 @@ reactor WebSocketServerString(hostport: int = 8000, initial_file: string = {= NU

reaction(startup) {=
self->ws.connected = false;
if (self->initial_file != NULL) {
lf_print("**** Point your browser to http://localhost:%d", self->hostport);
}
=}

reaction(in_dynamic, in_static) -> server.send {=
Expand All @@ -76,7 +79,7 @@ reactor WebSocketServerString(hostport: int = 8000, initial_file: string = {= NU
strcpy(message_copy, in_static->value);
}
to_send->message = message_copy;
to_send->length = strlen(in_dynamic->value) + 1;
to_send->length = strlen(in_dynamic->value);
to_send->wsi = self->ws.wsi;

lf_set(server.send, to_send);
Expand Down
4 changes: 2 additions & 2 deletions examples/C/src/sdv/ParkingAssist.html
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ <h1>Controls</h1>
window.onload = function() {

// Create the web socket connection for the dashboard display.
const socket = new WebSocket('ws://localhost:241', 'ws');
const socket = new WebSocket('ws://localhost:8080', 'ws');

socket.addEventListener('open', (event) => {
console.log('WebSocket connection established for dashboard display');
Expand Down Expand Up @@ -97,7 +97,7 @@ <h1>Controls</h1>
});

// Create the web socket connection for the controls.
const socket2 = new WebSocket('ws://localhost:240', 'ws');
const socket2 = new WebSocket('ws://localhost:8081', 'ws');

socket2.addEventListener('open', (event) => {
console.log('WebSocket connection established for the controls.');
Expand Down
29 changes: 10 additions & 19 deletions examples/C/src/sdv/ParkingAssist.lf
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@
* should be displayed.
* <li> There is only one distance sensor and no mechanism for setting up a test environment where
* obstacles are placed.
* <li> The web server works by being polled. It should instead accept a web socket connection so
* that the program can push data to the display.
* <li> The web server has no security.
* </ul>
*
Expand All @@ -36,9 +34,8 @@ target C {
keepalive: true
}

import WebSocketServer from "../lib/WebSocketServer.lf"
import WebSocketServerString from "../lib/WebSocketServerString.lf"
import PlayWaveform from "../rhythm/PlayWaveform.lf"
import ServerUI from "../lib/ServerUI.lf"

/**
* Emulator for automotive sensors. It outputs sensor data periodically with the specified sample
Expand All @@ -64,7 +61,7 @@ reactor Sensors(

timer t(0, sample_interval)

s = new WebSocketServer(hostport=240, max_clients=1) // Allow only one client
s = new WebSocketServerString(hostport=8081)

/**
* Given JSON of the form {..., "key":"value", ... }, if the value is an int, put that int value
Expand All @@ -88,7 +85,7 @@ reactor Sensors(
=}

reaction(s.received) {=
char* json = s.received->value->message;
char* json = s.received->value;
lf_print("Received control: %s", json);
int accelerator;
if (json_to_int(json, "accelerator", &accelerator) == 0) {
Expand Down Expand Up @@ -126,24 +123,19 @@ reactor Dashboard {
input speed: int
input front_distance: int

state connection: web_socket_instance_t = {= {0} =}
state connection: bool = false

s = new WebSocketServer(hostport=241, max_clients=1) // Allow only one client
s = new WebSocketServerString(
hostport=8080,
initial_file = {= LF_SOURCE_DIRECTORY LF_FILE_SEPARATOR "ParkingAssist.html" =})

reaction(speed, front_distance) -> s.send {=
reaction(speed, front_distance) -> s.in_dynamic {=
// Ignore the inputs if we are not connected.
if (self->connection.connected) {
if (self->connection) {
// Construct payload.
char* message;
asprintf(&message, "{\"front_distance\": %d, \"speed\": %d}", front_distance->value, speed->value);

// Construct struct to send.
web_socket_message_t* response = (web_socket_message_t*)malloc(sizeof(web_socket_message_t));
response->length = strlen(message); // Do not include the null terminator.
response->wsi = self->connection.wsi;
response->message = message;

lf_set(s.send, response);
lf_set(s.in_dynamic, message);
}
=}

Expand Down Expand Up @@ -200,7 +192,6 @@ main reactor {
s = new Sensors()
d = new Dashboard()
a = new SoundAlert()
p = new ServerUI(initial_file="ParkingAssist.html")
s.speed -> d.speed
s.front_distance -> d.front_distance
s.front_distance -> a.front_distance
Expand Down
Loading