This commit is contained in:
Mikhail Chatillon 2023-03-27 12:31:32 +02:00 коммит произвёл GitHub
Родитель 1d0df74355
Коммит da7d8a0d1d
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
16 изменённых файлов: 0 добавлений и 8639 удалений

103
.github/workflows/universal_decoder_ci.yaml поставляемый
Просмотреть файл

@ -1,103 +0,0 @@
name: Universal Decoder CI
on:
pull_request:
branches:
- master
- dev
paths:
- 'Samples/UniversalDecoder/**'
push:
branches:
- master
- dev
paths:
- 'Samples/UniversalDecoder/**'
workflow_dispatch:
inputs:
pushDockerImage:
description: 'Push docker image to the repository'
type: boolean
required: false
default: 'false'
jobs:
Build_And_Test:
name: Build and Test Solution
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./Samples/UniversalDecoder
env:
DOCKER_REPOSITORY: ${{ secrets.DOCKER_REPOSITORY }}
DOCKER_LOGIN: ${{ secrets.DOCKER_LOGIN }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
steps:
- name: Check out repository code
uses: actions/checkout@v3
- name: Use node JS
uses: actions/setup-node@v3
with:
node-version: '14'
- run: npm install
- name: Get vendor file
run: npm run codecs
- name: Run Tests
run: npm test
Build_And_Push:
name: Build and Push Solution
needs: Build_And_Test
runs-on: ubuntu-latest
strategy:
matrix:
image:
- arm32v7
- arm64v8
- amd64
defaults:
run:
working-directory: ./Samples/UniversalDecoder
outputs:
tag: ${{ steps.vars.outputs.tag }}
steps:
- name: Check out repository code
uses: actions/checkout@v3
- uses: docker/setup-buildx-action@v2
id: buildx
with:
install: true
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Use node JS
uses: actions/setup-node@v3
with:
node-version: '14'
- run: npm install
- name: Get vendor file
run: npm run codecs
- name: Get the image version
id: vars
run: echo ::set-output name=tag::0.0.$(echo ${GITHUB_SHA})
- name: Login to Docker, and build and push the tagged Docker image
if: ${{ github.event.inputs.pushDockerImage == 'true' }}
run: |
docker login -u ${{ secrets.DOCKER_LOGIN }} -p ${{ secrets.DOCKER_PASSWORD }}
docker buildx build -f "Dockerfile.${{ matrix.image }}" -t ${{ secrets.DOCKER_REPOSITORY }}/universaldecoder:${{steps.vars.outputs.tag}}-${{ matrix.image }} --output type=image,push=true "."
- name: Build the tagged Docker image without push
if: ${{ github.event.inputs.pushDockerImage == 'false' }}
run: docker buildx build -f "Dockerfile.${{ matrix.image }}" -t ${{ secrets.DOCKER_REPOSITORY }}/universaldecoder:${{steps.vars.outputs.tag}}-${{ matrix.image }} "."
Docker_Manifests:
if: ${{ github.event.inputs.pushDockerImage == 'true' }}
name: Create manifests
runs-on: ubuntu-latest
needs: Build_And_Push
steps:
- run: echo "output ${{needs.Test_And_Push.outputs.tag}}"
- name: Login to docker registry
run: docker login -u ${{ secrets.DOCKER_LOGIN }} -p ${{ secrets.DOCKER_PASSWORD }}
- name: Create manifest list
run: docker manifest create ${{ secrets.DOCKER_REPOSITORY }}/universaldecoder:${{needs.Build_And_Push.outputs.tag}} ${{ secrets.DOCKER_REPOSITORY }}/universaldecoder:${{needs.Build_And_Push.outputs.tag}}-arm32v7 ${{ secrets.DOCKER_REPOSITORY }}/universaldecoder:${{needs.Build_And_Push.outputs.tag}}-amd64 ${{ secrets.DOCKER_REPOSITORY }}/universaldecoder:${{needs.Build_And_Push.outputs.tag}}-arm64v8 --amend
- name: Push manifest
run: docker manifest push ${{ secrets.DOCKER_REPOSITORY }}/universaldecoder:${{needs.Build_And_Push.outputs.tag}}

Просмотреть файл

@ -3,5 +3,3 @@
The following samples are available:
- [Decoder](https://azure.github.io/iotedge-lorawan-starterkit/samples/decoders/decoder/)
- [UniversalDecoder](https://azure.github.io/iotedge-lorawan-starterkit/samples/decoders/universal/)
- [CayenneDecoder](https://azure.github.io/iotedge-lorawan-starterkit/samples/decoders/cayenne/)

Просмотреть файл

@ -1,19 +0,0 @@
# NOTE: Use either docker.io or your own registry address to build the image
ARG SOURCE_CONTAINER_REGISTRY_ADDRESS=your-registry-address.azurecr.io
FROM $SOURCE_CONTAINER_REGISTRY_ADDRESS/amd64/node:14-alpine
WORKDIR /app/
COPY package*.json ./
COPY NOTICE.txt ./
RUN npm install --production
COPY *.js ./
COPY codecs ./codecs
USER node
EXPOSE 8080
CMD ["node", "app.js"]

Просмотреть файл

@ -1,19 +0,0 @@
# NOTE: Use either docker.io or your own registry address to build the image
ARG SOURCE_CONTAINER_REGISTRY_ADDRESS=your-registry-address.azurecr.io
FROM $SOURCE_CONTAINER_REGISTRY_ADDRESS/arm32v7/node:14-slim
WORKDIR /app/
COPY package*.json ./
COPY NOTICE.txt ./
RUN npm install --production
COPY *.js ./
COPY codecs ./codecs
USER node
EXPOSE 8080
CMD ["node", "app.js"]

Просмотреть файл

@ -1,19 +0,0 @@
# NOTE: Use either docker.io or your own registry address to build the image
ARG SOURCE_CONTAINER_REGISTRY_ADDRESS=your-registry-address.azurecr.io
FROM $SOURCE_CONTAINER_REGISTRY_ADDRESS/arm64v8/node:14-slim
WORKDIR /app/
COPY package*.json ./
COPY NOTICE.txt ./
RUN npm install --production
COPY *.js ./
COPY codecs ./codecs
USER node
EXPOSE 8080
CMD ["node", "app.js"]

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,51 +0,0 @@
'use strict';
const {logger} = require('./app.logging');
const decoders = (() => {
try {
return require('./codecs');
} catch (e) {
if (e instanceof Error && e.code === 'MODULE_NOT_FOUND') {
return {};
} else {
throw e;
}
}
})();
function getAllDecoders() {
return Object.keys(decoders);
}
// gets decoder by name
function getDecoder(decoderName) {
if (decoderName === 'DecoderValueSensor') {
// return inline decoder implementation
return {
decodeUplink: (input) => { return { data: input.bytes.join('') } }
}
}
const decoder = decoders[decoderName];
if (!decoder) {
throw new Error(`No codec found: ${decoderName}`);
}
return decoder;
}
function decode(decoderName, payload, fPort) {
const decoder = getDecoder(decoderName);
const input = {
bytes: Buffer.from(payload, 'base64').toString('utf8').split(''),
fPort: parseInt(fPort)
};
logger.debug(`Decoder ${decoderName} input: ${JSON.stringify(input)}`);
const output = decoder.decodeUplink(input);
logger.debug(`Decoder ${decoderName} output: ${JSON.stringify(output)}`);
return output;
}
module.exports = { getAllDecoders, decode };

Просмотреть файл

@ -1,8 +0,0 @@
'use strict';
const port = 8080;
const app = require('./app.routes');
app.listen(port, () => {
console.log(`Server started at http://localhost:${port}`)
})

Просмотреть файл

@ -1,24 +0,0 @@
'use strict'
const pino = require('pino');
const expressPino = require('express-pino-logger');
const logger = pino({ level: process.env.LOG_LEVEL || 'info' });
const expressLogger = expressPino({
logger,
serializers: {
req: (req) => ({
method: req.method,
url: req.url,
}),
res: (res) => ({
statusCode: res.statusCode,
}),
err: (err) => ({
type: err.type,
message: err.message
}),
},
});
module.exports = {logger, expressLogger};

Просмотреть файл

@ -1,44 +0,0 @@
'use strict';
const express = require('express');
const {param, query, validationResult} = require('express-validator');
const {expressLogger, logger} = require('./app.logging');
const decoder = require('./app.decoder');
const app = express();
app.use(expressLogger);
app.get('/decoders', (req, res, next) => {
res.send(decoder.getAllDecoders());
});
app.get('/api/:decodername',
param('decodername').notEmpty().withMessage("is missing"),
query('payload').notEmpty().withMessage("is missing").isBase64().withMessage("must be a base64 encoded string"),
query('fport').isInt(),
(req, res, next) => {
// Input validation
if (!validationResult(req).isEmpty()) {
const error = `Invalid inputs: ${(validationResult(req).formatWith(e => `'${e.param}' ${e.msg}`).array().join(", "))}`;
logger.warn(error);
return res.status(400).send({error});
}
const output = decoder.decode(req.params.decodername, req.query.payload, req.query.fport);
res.send({
value: output.data,
});
});
// Error handling
app.use(function (err, req, res, next) {
logger.error(err);
res.status(500).send({
error: err.message,
rawPayload: req.query.payload
});
})
module.exports = app;

Просмотреть файл

@ -1,13 +0,0 @@
{
"Registrations": [
{
"Component": {
"Type": "git",
"git": {
"RepositoryUrl": "https://github.com/TheThingsNetwork/lorawan-devices",
"CommitHash": "de9ebd35677bdd36887019e6c3d319bc4b35615e"
}
}
}
]
}

Просмотреть файл

@ -1,17 +0,0 @@
{
"$schema-version": "0.0.1",
"description": "Universal decoder",
"image": {
"repository": "oneweek202105.azurecr.io/universaldecoder",
"tag": {
"version": "0.0.7",
"platforms": {
"amd64": "./Dockerfile.amd64",
"arm32v7": "./Dockerfile.arm32v7",
"arm64v8": "./Dockerfile.arm64v8"
}
},
"buildOptions": []
},
"language": "javascript"
}

3718
Samples/UniversalDecoder/package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,38 +0,0 @@
{
"name": "universaldecoder",
"version": "0.0.1",
"description": "Universal decoder module for LoraWan",
"repository": {
"type": "git",
"url": "git+https://github.com/Azure/generator-azure-iot-edge-module"
},
"license": "MIT",
"scripts": {
"start": "LOG_LEVEL=debug node app.js | ./node_modules/.bin/pino-pretty",
"test": "jest",
"codecs": "rm -fR node_modules/lorawan-devices codecs/* && git clone --depth 1 https://github.com/TheThingsNetwork/lorawan-devices.git node_modules/lorawan-devices && node tools/copy-codecs.js"
},
"dependencies": {
"esprima-next": "^5.8.4",
"express": "^4.18.2",
"express-pino-logger": "^7.0.0",
"express-validator": "^6.14.1",
"fs-extra": "^11.1.0",
"glob": "^8.0.3",
"pino": "^8.8.0",
"pino-pretty": "^9.1.1"
},
"devDependencies": {
"jest": "^29.3.1",
"supertest": "^6.3.3"
},
"jest": {
"testEnvironment": "node",
"transformIgnorePatterns": [
"/codecs/"
],
"coveragePathIgnorePatterns": [
"/node_modules/"
]
}
}

Просмотреть файл

@ -1,145 +0,0 @@
const request = require('supertest')
const app = require('../app.routes')
describe('DecoderValueSensor', () => {
it('should decode basic data', async () => {
const res = await sendRequest('DecoderValueSensor', 'ABCDE12345', 1);
expect(res.statusCode).toEqual(200);
expect(res.body).toEqual({
value: 'ABCDE12345',
});
});
it('should handle missing parameters', async () => {
const res = await request(app)
.get(`/api/DecoderValueSensor`)
.send();
expect(res.statusCode).toEqual(400);
expect(res.body).toEqual({
error: 'Invalid inputs: \'payload\' is missing, \'fport\' Invalid value',
});
});
it('should handle wrong payload', async () => {
const res = await request(app)
.get(`/api/DecoderValueSensor`)
.query({
payload: "not-base64",
fport: 1,
})
.send();
expect(res.statusCode).toEqual(400);
expect(res.body).toEqual({
error: 'Invalid inputs: \'payload\' must be a base64 encoded string',
});
});
it('should handle wrong fPort', async () => {
const res = await sendRequest('DecoderValueSensor', 'ABCDE12345', "not-int");
expect(res.statusCode).toEqual(400);
expect(res.body).toEqual({
error: 'Invalid inputs: \'fport\' Invalid value',
});
});
});
describe('invalid-decoder', () => {
it('should return error', async () => {
const res = await sendRequest('invalid-decoder', 'ABCDE12345', 1);
expect(res.statusCode).toEqual(500);
expect(res.body).toEqual({
error: "No codec found: invalid-decoder",
rawPayload: "QUJDREUxMjM0NQ=="
});
});
});
describe('loravisionshield', () => {
it('should decode led state on', async () => {
const res = await sendRequest('loravisionshield', '1', 1);
expect(res.statusCode).toEqual(200);
expect(res.body).toEqual({
value: {
ledState: "on"
},
});
});
it('should decode led state off', async () => {
const res = await sendRequest('loravisionshield', '0', 1);
expect(res.statusCode).toEqual(200);
expect(res.body).toEqual({
value: {
ledState: "off"
},
});
});
});
describe('tpl110-0292', () => {
it('should decode parking status occupied', async () => {
const res = await sendRequest('tpl110-0292', '1', 1);
expect(res.statusCode).toEqual(200);
expect(res.body).toEqual({
value: {
type: "parking status",
occupied: true
},
});
});
it('should decode parking status not occupied', async () => {
const res = await sendRequest('tpl110-0292', '0', 1);
expect(res.statusCode).toEqual(200);
expect(res.body).toEqual({
value: {
type: "parking status",
occupied: false
},
});
});
it('should decode device heartbeat', async () => {
const res = await sendRequest('tpl110-0292', '1111', 2);
expect(res.statusCode).toEqual(200);
expect(res.body).toEqual({
value: {
occupied: true,
temperature: "1",
type: "heartbeat"
},
});
});
});
// The following test has been disabled because decoder script was changed
// A fix in copy-codecs.js is required to re-enable the import of this decoder #1833
// describe('lw001-bg', () => {
// it('should decode all 1s', async () => {
// const res = await sendRequest('lw001-bg', '1111111111111111', 1);
// expect(res.statusCode).toEqual(200);
// expect(res.body).toEqual({
// value: {
// barometer: 65792.1,
// batterylevel: 0.01,
// devicestatus: "1",
// firmwareversion: 101,
// humidity: 25.61,
// macversion: 0,
// temperature: -19.39,
// type: "Device Information Packet"
// },
// });
// });
// });
function sendRequest(decoderName, payload, fPort) {
return request(app)
.get(`/api/${decoderName}`)
.query({
payload: Buffer.from(payload).toString('base64'),
fport: fPort,
devEui: "0000000000000000",
})
.send();
}

Просмотреть файл

@ -1,46 +0,0 @@
'use strict';
const glob = require('glob');
const fs = require('fs');
const fse = require('fs-extra');
const path = require('path');
const esprima = require('esprima-next');
var args = process.argv.slice(2);
const srcDir = args[0] || './node_modules/lorawan-devices/vendor';
const dstDir = args[1] || './codecs';
const indexFilePath = path.join(dstDir, 'index.js');
const index = glob.sync(`**/*`,
{
cwd: srcDir,
nodir: true,
ignore: [
'**/*.jpg',
'**/*.png',
'**/*.svg',
]
}).map(f => {
const srcPath = `${srcDir}/${f}`;
const dstPath = `${dstDir}/${f}`;
console.log(`Copying ${srcPath} to ${dstPath}`);
fse.copySync(srcPath, dstPath);
if (f.endsWith(".js")) {
// Sniff a top-level declaration for a function named "decodeUplink"
// and include the decoder if only one is found.
const tree = esprima.parseScript(fs.readFileSync(srcPath).toString());
if (tree.body.find(n => n.type === 'FunctionDeclaration' && n.id.name === 'decodeUplink')) {
fs.appendFile(dstPath, '\nmodule.exports={decodeUplink};', function (err) {
if (err) throw err;
console.log(`Patching ${dstPath}`);
});
return dstPath;
}
}
})
.filter(f => f)
.map(f => [path.basename(f).split('.')[0], path.relative(dstDir, f).replace(/\\/g, '/')]);
fs.writeFileSync(indexFilePath,
`module.exports = {\n${index.map(([k, v]) => `${JSON.stringify(k)}: require(${JSON.stringify('./' + v)})`).join(',\n ')}\n};\n`);