Leyton Bostre

Communication in Loud Industrial Manufacturing Environments

Clime landing page
Chat page

Overview

Problem

Workers in noisy factory environments suffer from poor situational awareness and communication, leading to increased risks and inefficiencies.

Solution

An app designed to detect alarms, accurately transmit spoken words, send mass or selective messages, and display messages across different factory sections for improved communication and safety.

My Role

Front End Team Lead

Sc

pe

January 2024 - Present

Team: 6 members

Experience as Frontend Team Lead

As the frontend team lead, I was responsible for assigning tasks and keeping my team on track. The frontend team consisted of two other members, both of whom were new to React Native. Much of my time during lab was spent helping them fix various bugs, teaching them best practices, and making design decisions. Overall, I enjoyed being the frontend team lead because I remember being in their position, and it was gratifying to be the person who could offer support and guidance.

Alarms page
Alarms page with swipe to delete

Sample alarms page. This page displays a list of alerts for the user, including machine alarms and important announcements. I implemented a swipe-to-delete functionality on this page to enhance ease of use.

Clime landing page
Chat page

On the left is the landing page, the page that the user first sees when logging onto the app. They have the ability to either create a group or join an already existing group.

On the right is the chat page, the page that the user can use to communicate with their group. The user can tap on the red circular button to start voice recording, record a message, then tap on the red circular button again to stop recording. One neat part about the app is that the voice recording is transmitted to other users in real time, meaning that as they talk the words pop up on the users phones. No need waiting for the whole message to be recorded!

Connecting Other Pages to WebSocket Using Global State

const GlobalStateContext = createContext(undefined);
let globalStateCreated = false;

export const GlobalStateProvider = ({children}) => {
    // replace ip with your ip
    if(globalStateCreated){
        return
    }
    let ws = new WebSocket('ws://' + '{ip}' + '/ws/test/');
    alert("Created websocket");
    globalStateCreated = true;

    const [globalState, setGlobalState] = useState({ws: ws, pin: 0, name: ""});
    return (
        <GlobalStateContext.Provider value={{globalState, setGlobalState}}>
            {children}
        </GlobalStateContext.Provider>
    )
}

export const useGlobalState = () => useContext(GlobalStateContext);

export default function Layout() {
    return <GlobalStateProvider>
            <Stack screenOptions={{
            headerStyle:{backgroundColor:"#0a1b2d"},
            headerTintColor:"#fff",
            headerTitleStyle: {fontWeight:"bold", fontFamily:"Cabin_400Regular", fontSize:24},
        }}>
            <Stack.Screen
                name="alerts"
                options={{
                    title: "Alerts",
                    presentation: 'modal',
                }}
            />
        </Stack>
    </GlobalStateProvider>
}

Using WebSocket as our primary communication method between the server and the client, we want to ensure there is only one instance running per device. We achieve this using global context. In the code above, we first create a provider that initializes the WebSocket instance. This provider is then wrapped around the entire app, ensuring that every page has access to the same WebSocket instance.

Updating the Chat Messages State

const updateMessages = (m, username, final) => {
    if(!final) {
        const newArr = [...temp];
        newArr[newArr.length - 1] = m;
        newArr.push("")
        setTemp(newArr);
    } else {
        const newArr = [...temp];
        newArr[newArr.length - 1] = m;
        setTemp(newArr);
    }
    const reversedMessages = [...messages].reverse();
    let updateApplied = false;

    for (let i = 0; i < reversedMessages.length; i++) {
        if (reversedMessages[i].user.name === username) {
            if (!updateApplied) {
                if (getDateDifferenceInSeconds(reversedMessages[i].createdAt, new Date()) > 10) {
                    const newMessage = createMessage(m, username == localParams["name"] ? 1 : undefined, username)
                    reversedMessages.unshift(newMessage);
                    break;
                }
                reversedMessages[i] = {
                    ...reversedMessages[i],
                    text: m,
                    createdAt: new Date(),
                };
                updateApplied = true;
                break
            }
        }
    }
    if(!updateApplied) {
        const newMessage = createMessage(m, username == localParams["name"] ? 1 : undefined, username)
        reversedMessages.unshift(newMessage);
    }
    const finalMessages = reversedMessages.reverse();
    setMessages(finalMessages);
}

Since chat messages are stored as objects in JavaScript, we need to store them in a state to keep the object continuously updated. The parameters for this function are the message itself (m), the username of the sender (username), and a flag indicating whether the call is the end of the message (final).

To ensure we start from the most recent messages, we reverse the previous messages list. The logic for updating the state is as follows: if the user is the same as the one who sent the last message and less than 10 seconds have passed since that message started, we simply update their message with the new text. If more than 10 seconds have passed, we create a new message. If the user sending the message is different from the user who sent the previous message, a new message is created. Finally, we reorder the list correctly and update the messages state.