TLDR: मैं एक mqtt अनुरोध से aws iot से aws लैम्ब्डा को एक छोटा पेलोड कैसे भेजूं, जिसका एपीगेटवे के माध्यम से लिनक्स में स्थानीय रूप से चल रहे एक इलेक्ट्रॉन ऐप के लिए एक खुला कनेक्शन है।

मेरे पास निम्नलिखित कोड के साथ init.js के रूप में एक esp8266 है, यह कोड सफलतापूर्वक aws iot को अपना संदेश भेजता है, एक नियम के साथ एक लैम्ब्डा को ट्रिगर करने के लिए सेट किया जाता है जिसे सेंडमेसेज कहा जाता है। अब यह प्रेषण लैम्ब्डा websockets के माध्यम से मेरी लिनक्स मशीन पर स्थानीय रूप से एक इलेक्ट्रोन ऐप से जुड़ा हुआ है। मैं इलेक्ट्रॉन ऐप से websockets के माध्यम से एपीआई गेटवे wss url पर संदेश भेजने में सक्षम हूं। मैंने इस उदाहरण का अनुसरण किया यहां जो एपीआई गेटवे और एडब्ल्यूएस लैम्ब्डा (एक सेंडमेसेज लैम्ब्डा) के साथ सभी वेबसोकेट सेट करता है।

load("api_config.js");
load("api_gpio.js");
load("api_mqtt.js");
load("api_sys.js");
load("api_timer.js");

let pin = 0;
GPIO.set_button_handler(
  pin,
  GPIO.PULL_UP,
  GPIO.INT_EDGE_NEG,
  50,
  function (x) {
    let res = MQTT.pub(
      "mOS/topic1",
      JSON.stringify({ action: "sendmessage", data: "pushed" }),
      1
    );

    print(res);
    print("Published:", res ? "yes" : "no");
    let connected = MQTT.isConnected();

    print(connected);
  },
  true
);
print("Flash button is configured on GPIO pin", pin);
print("Press the flash button now!");

मुझे पता है कि आईओटी से सेंडमैसेज लैम्ब्डा के संदेश को एक websockets संदेश होना चाहिए, लेकिन इसमें केवल {"action":"sendmessage","data":"hello world"} की न्यूनतम वस्तु है, इसमें जानकारी का एक गुच्छा गुम है जिसे एक websocket की आवश्यकता होगी। लेकिन मुझे एडब्ल्यूएस आईओटी के बीच एक वेबसोकेट कनेक्शन की आवश्यकता नहीं है - और प्रेषण लैम्ब्डा, मुझे आईओटी से जाने के लिए इसकी आवश्यकता है -> न्यूनतम पेलोड के साथ संदेश लैम्ब्डा -> आईओटी से पेलोड के साथ वेबसोकेट के माध्यम से इलेक्ट्रॉन ऐप।

संदेश भेजें लैम्ब्डा

// Copyright 2018-2020Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0

const AWS = require('aws-sdk');

const ddb = new AWS.DynamoDB.DocumentClient({ apiVersion: '2012-08-10', region: process.env.AWS_REGION });

const { TABLE_NAME } = process.env;

exports.handler = async event => {
  let connectionData;
  
  try {
    connectionData = await ddb.scan({ TableName: TABLE_NAME, ProjectionExpression: 'connectionId' }).promise();
  } catch (e) {
    return { statusCode: 500, body: e.stack };
  }
  
  const apigwManagementApi = new AWS.ApiGatewayManagementApi({
    apiVersion: '2018-11-29',
    endpoint: event.requestContext.domainName + '/' + event.requestContext.stage
  });
  
  const postData = JSON.parse(event.body).data;
  
  const postCalls = connectionData.Items.map(async ({ connectionId }) => {
    try {
      await apigwManagementApi.postToConnection({ ConnectionId: connectionId, Data: postData }).promise();
    } catch (e) {
      if (e.statusCode === 410) {
        console.log(`Found stale connection, deleting ${connectionId}`);
        await ddb.delete({ TableName: TABLE_NAME, Key: { connectionId } }).promise();
      } else {
        throw e;
      }
    }
  });
  
  try {
    await Promise.all(postCalls);
  } catch (e) {
    return { statusCode: 500, body: e.stack };
  }

  return { statusCode: 200, body: 'Data sent.' };
};

ऑनकनेक्ट लैम्ब्डा

// SPDX-License-Identifier: MIT-0

const AWS = require('aws-sdk');

const ddb = new AWS.DynamoDB.DocumentClient({ apiVersion: '2012-08-10', region: process.env.AWS_REGION });

exports.handler = async event => {
  const putParams = {
    TableName: process.env.TABLE_NAME,
    Item: {
      connectionId: event.requestContext.connectionId
    }
  };

  try {
    await ddb.put(putParams).promise();
  } catch (err) {
    return { statusCode: 500, body: 'Failed to connect: ' + JSON.stringify(err) };
  }

  return { statusCode: 200, body: 'Connected.' };
};

ऑनडिस्कनेक्ट लैम्ब्डा

// Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0

// https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api-route-keys-connect-disconnect.html
// The $disconnect route is executed after the connection is closed.
// The connection can be closed by the server or by the client. As the connection is already closed when it is executed, 
// $disconnect is a best-effort event. 
// API Gateway will try its best to deliver the $disconnect event to your integration, but it cannot guarantee delivery.

const AWS = require('aws-sdk');

const ddb = new AWS.DynamoDB.DocumentClient({ apiVersion: '2012-08-10', region: process.env.AWS_REGION });

exports.handler = async event => {
  const deleteParams = {
    TableName: process.env.TABLE_NAME,
    Key: {
      connectionId: event.requestContext.connectionId
    }
  };

  try {
    await ddb.delete(deleteParams).promise();
  } catch (err) {
    return { statusCode: 500, body: 'Failed to disconnect: ' + JSON.stringify(err) };
  }

  return { statusCode: 200, body: 'Disconnected.' };
};

मेरे इलेक्ट्रॉन ऐप में मेरे पास वेबसोकेट का परीक्षण करने के लिए निम्न कोड है लेकिन मुझे एक निषिद्ध त्रुटि मिल रही है। Wscat के साथ हाउवर यह काम करता है ...

"use strict";
const { app, BrowserWindow } = require("electron");
const { Notification } = require("electron");
const WebSocket = require("ws");

function createWindow() {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true,
    },
  });

  win.loadFile("index.html");
  win.webContents.openDevTools();
}

app.whenReady().then(createWindow);

app.on("window-all-closed", () => {
  if (process.platform !== "darwin") {
    app.quit();
  }
});

app.on("activate", () => {
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow();
  }
});

// Tell express to use the body-parser middleware and to not parse extended bodies

const url = "wss://random.execute-api.us-east-1.amazonaws.com/Prod";
const connection = new WebSocket(url);

connection.onopen = () => {
  connection.send("hello world");
};

connection.onmessage = (e) => {
  console.log(e.data);
};

connection.onerror = (error) => {
  console.log(`WebSocket error: ${error}`);
};

function showNotification() {
  const notification = {
    title: "Basic Notification",
    body: `notification`,
  };

  new Notification(notification).show();
}

app.whenReady().then(createWindow).then(showNotification);

अब मैं लैम्ब्डा को एक ही डेटा भेजने के लिए अपना एमक्यूटी इवेंट सेट करता हूं लेकिन मुझे लैम्ब्डा में निम्न त्रुटि मिलती है

{
    "errorType": "TypeError",
    "errorMessage": "Cannot read property 'domainName' of undefined",
    "stack": [
        "TypeError: Cannot read property 'domainName' of undefined",
        "    at Runtime.exports.handler (/var/task/app.js:29:28)",
        "    at processTicksAndRejections (internal/process/task_queues.js:97:5)"
    ]
}

अपडेट: यहां मेरा आखिरी लैम्ब्डा है जहां मैं आईओटी से एक घटना प्राप्त करने के बाद डब्ल्यूएसएस पते पर एक संदेश भेजता हूं, लेकिन यह काम नहीं करता है यह कंसोल ईवेंट को लॉग करता है लेकिन किसी भी ws.on फ़ंक्शन को आग नहीं लगाता है

// const axios = require('axios')
// const url = 'http://checkip.amazonaws.com/';
const WebSocket = require("ws");
let response;

/**
 *
 * Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format
 * @param {Object} event - API Gateway Lambda Proxy Input Format
 *
 * Context doc: https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html
 * @param {Object} context
 *
 * Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html
 * @returns {Object} object - API Gateway Lambda Proxy Output Format
 *
 */
exports.lambdaHandler = async (event, context) => {
  try {
    // const ret = await axios(url);

    console.log(event);

    const url = "wss://obsf.execute-api.us-east-1.amazonaws.com/Prod";
    const ws = new WebSocket(url);

    var test = { action: "sendmessage", data: "hello world from button" };

    ws.on("open", function open() {
      ws.send(JSON.stringify(test));
    });

    ws.on("message", function incoming(data) {
      console.log(data);
    });

    response = {
      statusCode: 200,
      body: JSON.stringify({
        message: "hello world",
        // location: ret.data.trim()
      }),
    };
  } catch (err) {
    console.log(err);
    return err;
  }

  return response;
};

अद्यतन: अंत में मैंने यह कोशिश की है और मुझे कोई त्रुटि भी नहीं मिल रही है, मुझे पता है कि ws वहां है क्योंकि अगर मैं इसे सांत्वना देता हूं तो यह कार्यों के समूह के साथ एक बड़ी वस्तु देता है

    console.log(ws); this returns a large object

    ws.on("error", console.error); this does nothing
3
Anders Kitson 10 नवम्बर 2020, 01:05

2 जवाब

सबसे बढ़िया उत्तर

ऐसा लगता है कि आप 2 ट्रिगर स्रोतों को संभालने के लिए 1 लैम्ब्डा सेट कर रहे हैं, एक IoT सेवा है, दूसरा API गेटवे वेबसोकेट है। चूंकि आप 1 लैम्ब्डा का उपयोग करते हैं, इसलिए स्रोतों से अनुरोध आने पर आपको मामलों को संभालना होगा:

  1. जबकि event.requestContext एपीआई गेटवे से अनुरोध ट्रिगर होने पर उपलब्ध होता है, यह तब उपलब्ध नहीं होता है जब IoT सेवा से अनुरोध ट्रिगर होता है (IoT ईवेंट ऑब्जेक्ट यहां देखें https://docs.aws.amazon.com/lambda/latest/dg/services-iotevents.html) . तो आपको जिस त्रुटि का सामना करना पड़ा (जो Cannot read property 'domainName' of undefined" है) उसी के बारे में है। आपको IoT सेवा से लैम्ब्डा ट्रिगर को बंद कर देना चाहिए या IoT सेवा से आने पर अनुरोध को संभालना चाहिए।
  2. मैं निषिद्ध त्रुटि के बारे में निश्चित नहीं हूं लेकिन यह ऐसा है जैसे आपने एपीआई गेटवे डब्ल्यूएस को असंरचित संदेश भेजा है, यह connection.send("hello world"); के बजाय connection.send(JSON.stringify({ action: "sendmessage", data: "hello world" })); होना चाहिए

पोस्ट अपडेट के आधार पर संपादित:

मुझे पता है कि ws वहां है क्योंकि अगर मैं इसे सांत्वना देता हूं तो यह कार्यों के समूह के साथ एक बड़ी वस्तु देता है

लैम्ब्डा फ़ंक्शन वास्तव में एक सर्वर नहीं है, यह एक उदाहरण नोड वातावरण है (इसीलिए इसे फ़ंक्शन कहा जाता है), लैम्ब्डा फ़ंक्शन उस तरह से काम नहीं करता जैसा आप सोचते हैं कि सामान्य Nodejs ऐप करता है, इसका कंटेनर (नोड वातावरण) आमतौर पर रुका हुआ है (या फ्रीज) जब भी इसका काम हो जाता है तो आप इसके कंटेनर को सामान्य सर्वर की तरह जिंदा नहीं रख सकते। यही कारण है कि जब आप वेबसोकेट ऑब्जेक्ट को लॉग कंसोल कर सकते हैं, तो आप इसे जीवित नहीं रख सकते हैं, जब भी आप वापस आते हैं/प्रतिक्रिया करते हैं तो नोडजेएस कंटेनर पहले ही रोक दिया गया था।

चूंकि आप लैम्ब्डा में डब्ल्यूएस कनेक्शन खोलने के लिए वेबसोकेट ऑब्जेक्ट का उपयोग नहीं कर सकते हैं, अमेज़ॅन एपीआई गेटवे के माध्यम से ऐसा करने का एक तरीका प्रदान करता है। जिस तरह से हम एपीआई गेटवे वेबसोकेट के साथ काम करते हैं वह सामान्य सर्वर से भी अलग है, यह कुछ ऐसा होगा:

  • उपयोगकर्ता -> वेब सॉकेट से कनेक्ट करने के लिए एपीआई गेटवे से अनुरोध -> लैम्ब्डा 1 को कॉल करें (ऑनकनेक्ट फ़ंक्शन)
  • उपयोगकर्ता -> वेबसाईट पर संदेश भेजने के लिए एपीआई गेटवे से अनुरोध -> लैम्ब्डा 2 को कॉल करें (भेजें संदेश फ़ंक्शन)
  • उपयोगकर्ता -> कनेक्शन बंद करने के लिए एपीआई गेटवे से अनुरोध -> लैम्ब्डा 3 को कॉल करें (ऑनडिस्कनेक्ट फ़ंक्शन)

ऊपर दी गई 3 सेटिंग API गेटवे में कॉन्फ़िगर की गई हैं (https ://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api-integrations.html), 3 कार्यों का तर्क onconnect, sendmessage, ondisconnect को हम जिस तरह से डिजाइन करते हैं उसके आधार पर 1 लैम्ब्डा या 3 लैम्ब्डा फ़ंक्शंस में संभाला जा सकता है, मैं आपके 3 लैम्ब्डा फ़ंक्शंस की जांच करता हूं और यह ठीक दिखता है।

मैं देख रहा हूँ कि आप IoT का उपयोग करना चाहते हैं लेकिन मुझे यकीन नहीं है कि क्यों। आपको IoT से संबंधित किसी भी चीज़ के बिना पहले अपने Websocket API का परीक्षण करना चाहिए। यह बेहतर होगा यदि आप बता सकते हैं कि आप यहां क्या हासिल करना चाहते हैं क्योंकि IoT एक पब्लिश/सब्सक्राइब/मैसेजिंग चैनल की तरह काम करता है और मुझे नहीं लगता कि इसे यहां इस्तेमाल करना जरूरी है।

1
Hoang Dao 17 नवम्बर 2020, 07:59

मैं इसे सिर्फ यह दिखाने के लिए पोस्ट कर रहा हूं कि मैंने इसे कैसे हल किया।

मैंने इसके बजाय एक नोड एक्सप्रेस ईसी 2 सर्वर बनाया जो आईओटी डिवाइस से अनुरोध को संभाला और फिर इसे मेरे डब्ल्यूएसएस सर्वर पर भेज दिया, यह इसका मुख्य भाग है जो काम कर रहा है

मैंने यहाँ यही किया है एक्सप्रेस नोड js websockets websocket सर्वर से संदेश प्राप्त कर रहा है लेकिन उन्हें भेजने में सक्षम नहीं है

और यह कोड का मुख्य भाग है जो काम कर रहा है

app.post('/doorbell', (req, res) => {
  var hmm1 = { action: 'sendmessage', data: 'hello world from post request' };

  if (ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify(hmm1));
    res.send('heya hey');
  } else {
    res.send('The WebSocket connection is not open');
  }
});
0
Anders Kitson 17 नवम्बर 2020, 17:12