# Default Script

The following code block contains the default script that your vision system uses for MQTT Message Queue Telemetry Transport (MQTT) is a lightweight messaging protocol used to send and receive data between devices. formatting:

Note: When creating your own custom script for MQTT formatting, make sure that you include the default export class for the script engine to load. You can define any additional classes, functions, or variables.
Copy

    import { device, connection } from "script-sdk";

    // JsonFormatter provides a basic JSON protocol for
    // communication between the device and the MQTT broker.
    //
    // It supports invoking commands on the device
    // and publishing results from the device to the connection.
    export default class JsonFormatter {
        constructor() {
            // Setup topics for notifications and responses
            this.resultsTopic = `cognex/${connection.clientId}/results`;
            this.heartbeatTopic = `cognex/${connection.clientId}/heartbeat`;
            this.responseTopic = `cognex/${connection.clientId}/responses`;

            // Set up a Handler for the results from the device
            device.streams.results.setHandler(async (result) => {
                await this.onResult(result);
            });

            // Set up a Handler for incoming messages from the connection
            connection.subscriptions.setHandler(async (payload, topic, props) => {
                await this.onIncomingMessage(payload, topic, props);
            }, "json");

            // Heartbeat every 60 seconds
            setInterval(async () => {
                await this.onHeartbeat();
            }, 60000);
        }

        async onResult(result) {
            // Publish the result to our results topic
            connection.publish(result, this.resultsTopic, getDefaultPublishOptions());
        }

        async onIncomingMessage(payload, topic, props) {
            // When we receive a message, we expect it to follow
            // our Command request format.
            // If it does not, we will ignore it.
            if (!isValidRequest(payload)) {
                console.warn("Received invalid request:", payload);
                return;
            }

            let response;
            try {
                // If the payload is an array, we will only process the first item
                let commandArgs = payload.params;
                if (Array.isArray(commandArgs)) {
                    commandArgs = commandArgs[0];
                }

                const result = await device.commands[payload.method].execute(commandArgs);
                response = {
                    id: payload.id,
                    result: result,
                };
            } catch (err) {
                response = {
                    id: payload.id,
                    error: {
                        code: -32603, // Internal error
                        message: err.message || "Internal error",
                    },
                };
            }

            // MQTTv5 allows response topics to be specified in the properties
            // Check if one was provided, otherwise use the default response topic
            const responseTopic = props?.responseTopic || this.responseTopic;

            // If the incoming request has an ID, we need to send a response with the matching ID
            if (response.id !== undefined) {
                connection.publish(response, responseTopic, getDefaultPublishOptions());
            }
        }

        async onHeartbeat() {
            connection.publish({ status: "Healthy" }, this.heartbeatTopic, getDefaultPublishOptions());
        }
    }

    function getDefaultPublishOptions() {
        // Options can be set here as needed.
        // If not set, defaults will be used.
        const options = {
            // Mqtt V3 and V5
            //
            // qos: 1,
            //
            // retain: false,
            // Mqtt V5 only
            // contentType: "application/json",
            //
            // payloadFormatIndicator: 1,
            //
            // messageExpiryIntervalSecs: 60,
            //
            // topicAlias: 23,
            //
            // correlationData: "correlation-data",
            //
            // userProperties: { "prop1": "value1" },
        };
        return options;
    }

    function isValidRequest(payload) {
        // Must be an object
        // Must have a "method" property of type string
        // "id" is optional, if present must be a string, number, or null
        // "params" is optional, if present must be an object or array
        if (typeof payload !== "object" || payload === null) {
            return false;
        }

        if (!("method" in payload) || typeof payload.method !== "string") {
            return false;
        }

        return true;
    }