我正在尝试通过在覆盆子pi上运行nodejs应用程序来创建度量仪表板,该应用程序将附加到大型监视器上。
应用程序每小时每小时查询一次SQL Server。当我在我的Mac上运行它时效果很好,但是当我在树莓上运行它时,它似乎运行一次或两次,然后停止。我已经在树莓上禁用了屏保,但我不知道如何调试它。我将包含完整的代码,因为我不确定它是tick()函数本身还是别的......
"use strict";
var fs = require('fs');
var readline = require('readline');
var google = require('googleapis');
var googleAuth = require('google-auth-library');
var pg = require('pg');
const config = {
user: '<redacted>',
database: '<redacted>',
password: '<redacted>',
host: '<redacted>',
port: 5432,
max: 10,
idleTimeoutMillis: 5000,
};
const pool = new pg.Pool(config);
const spreadsheetId = '<this my internal sheet>'; // fulfillment kpis
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/sheets.googleapis.com-nodejs-quickstart.json
const SCOPES = ['https://www.googleapis.com/auth/spreadsheets'];
const TOKEN_DIR = (process.env.HOME || process.env.HOMEPATH ||
process.env.USERPROFILE) + '/.credentials/';
const TOKEN_PATH = TOKEN_DIR + 'olr-test.json';
// Load client secrets from a local file.
fs.readFile('secret.json', function processClientSecrets(err, content) {
if (err) {
console.log('Error loading client secret file: ' + err);
return;
}
// Authorize a client with the loaded credentials, then call the
// Google Sheets API.
authorize(JSON.parse(content), begin); // <---- LOOK HERE...WHERE IT REALLY BEGINS...I THINK
});
/**
* Create an OAuth2 client with the given credentials, and then execute the
* given callback function.
*
* @param {Object} credentials The authorization client credentials.
* @param {function} callback The callback to call with the authorized client.
*/
function authorize(credentials, callback) {
var clientSecret = credentials.installed.client_secret;
var clientId = credentials.installed.client_id;
var redirectUrl = credentials.installed.redirect_uris[0];
var auth = new googleAuth();
var oauth2Client = new auth.OAuth2(clientId, clientSecret, redirectUrl);
// Check if we have previously stored a token.
fs.readFile(TOKEN_PATH, function (err, token) {
if (err) {
getNewToken(oauth2Client, callback);
} else {
oauth2Client.credentials = JSON.parse(token);
callback(oauth2Client);
}
});
}
/**
* Get and store new token after prompting for user authorization, and then
* execute the given callback with the authorized OAuth2 client.
*
* @param {google.auth.OAuth2} oauth2Client The OAuth2 client to get token for.
* @param {getEventsCallback} callback The callback to call with the authorized
* client.
*/
function getNewToken(oauth2Client, callback) {
var authUrl = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES
});
console.log('Authorize this app by visiting this url: ', authUrl);
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question('Enter the code from that page here: ', function (code) {
rl.close();
oauth2Client.getToken(code, function (err, token) {
if (err) {
console.log('Error while trying to retrieve access token', err);
return;
}
oauth2Client.credentials = token;
storeToken(token);
callback(oauth2Client);
});
});
}
/**
* Store token to disk be used in later program executions.
*
* @param {Object} token The token to store to disk.
*/
function storeToken(token) {
try {
fs.mkdirSync(TOKEN_DIR);
} catch (err) {
if (err.code != 'EEXIST') {
throw err;
}
}
fs.writeFile(TOKEN_PATH, JSON.stringify(token));
console.log('Token stored to ' + TOKEN_PATH);
}
// Let's get the information out of xTuple now...
function begin(auth) {
// Promise function to return results from SQL query passed as an argument
function getResults(passedQuery) {
return new Promise((resolve, reject) => {
pool.connect((err, client, done) => {
var sql = client.query(passedQuery);
sql.on('row', (row, result) => {
result.addRow(row);
});
sql.on('end', (result) => {
var rightNow = timeStamp();
// console.log('Query finished at %s', rightNow);
resolve(result.rows);
});
done(err);
if (err) {
return console.error('error running query', err);
}
});
});
pool.on('error', (err, client) => {
console.error('idle client error', err.message, err.stack);
});
}
function convertArray(sqlObj) {
return new Promise((resolve, reject) => {
var result = [];
if (sqlObj.length > 0) {
var order = Object.keys(sqlObj[0]);
var result = sqlObj.map((row) => {
return order.map((col) => {
return row[col];
});
});
}
result[0].push(timeStamp());
resolve(result);
});
}
function appendSheet (value) {
return new Promise((resolve, reject) => {
var sheets = google.sheets('v4');
var options = {
auth: auth,
spreadsheetId: spreadsheetId,
range: "Sales ABPH!A1:B",
valueInputOption: "USER_ENTERED",
resource: {
range: "Sales ABPH!A1:B",
majorDimension: "ROWS",
values: value,
}
}
// console.log(options);
sheets.spreadsheets.values.append(options, (err, res) => {
if (err) {
reject(err);
} else {
resolve(res);
}
});
});
}
function fixFormats(id) {
return new Promise((resolve, reject) => {
var sheets = google.sheets('v4');
var options = {
auth: auth,
spreadsheetId: spreadsheetId,
resource: {
requests: [{
repeatCell: {
fields: "userEnteredFormat.numberFormat",
range: {
sheetId: id,
startColumnIndex: 0, // inclusive
endColumnIndex: 1, // exclusive...meaning this counts UP TO column 4 but NOT 5
},
cell: {
userEnteredFormat: {
numberFormat: {
type: "CURRENCY",
pattern: "\"$\"#,##0.00",
}
}
}
}
}],
}
}
sheets.spreadsheets.batchUpdate(options, (err, res) => {
if (err) {
reject(err);
} else {
resolve(res);
}
});
});
}
function timeStamp() {
var now = new Date();
var date = [now.getMonth() + 1, now.getDate()];
var time = [now.getHours(),now.getMinutes(),now.getSeconds()];
var suffix = (time[0] < 12) ? "AM" : "PM";
time[0] = (time[0] < 12) ? time[0] : time[0] - 12;
time[0] = time[0] || 12;
for (var i = 1; i < 3; i++) {
if (time[i] < 10) {
time[i] = "0" + time[i];
}
}
return date.join("/") + " " + time.join(":") + " " + suffix;
}
function insertRow() {
return new Promise((resolve, reject) => {
var sheets = google.sheets('v4');
var options = {
auth: auth,
spreadsheetId: spreadsheetId,
resource: {
requests: [{
insertRange: {
range: {
sheetId: 1769404692,
startRowIndex: 1,
endRowIndex: 2,
startColumnIndex: 1,
endColumnIndex: 4
},
shiftDimension: "ROWS",
}
}],
}
}
sheets.spreadsheets.batchUpdate(options, (err, res) => {
if (err) {
reject(err);
} else {
resolve(res);
}
});
});
}
function update() {
getResults(`
select formatMoney(sum(qry.total)) as \"Booked\"
FROM cohead c
LEFT OUTER JOIN
(SELECT coitem_cohead_id, SUM(coitem_qtyord*coitem_price) as total
FROM coitem
WHERE coitem_status NOT IN (\'X\')
GROUP BY coitem_cohead_id) as qry
ON (c.cohead_id=qry.coitem_cohead_id)
WHERE (extract(DAY FROM c.cohead_orderdate) = extract(DAY from current_date) AND extract(MONTH from c.cohead_orderdate) = extract(MONTH from current_date) AND extract(YEAR from c.cohead_orderdate) = extract(YEAR from current_date))
`)
.then((sqlObj) => {
insertRow();
return convertArray(sqlObj);
})
.then((results) => {
console.log(results);
return appendSheet(results);
})
.then((fixMe) => {
fixFormats('1769404692');
})
.catch((error) => {
console.log('Something went wrong...\n%s', error);
});
tick();
}
function tick(force) {
var d = new Date();
var min = d.getMinutes();
var sec = d.getSeconds();
var hr = d.getHours();
if (force || (min == '0' && sec == '0') && (hr >= 7 || hr <= 19))
update();
else
setTimeout(update, (60 * (60 - min) + (60 - sec)) * 1000);
}
tick(true);
}
我正在使用nodemon运行它来监视脚本文件中的更改,如果找到则重新启动。
我的预感是,覆盆子会以某种方式进入某种“睡眠”模式,但仍会输出视频。
编辑:我以为我在这里找到了一个解决方案:https://raspberrypi.stackexchange.com/questions/4773/raspberry-pi-sleep-mode-how-to-avoid/56046#56046
我已经为wlan0关闭了power_save(这是我用于LAN连接的)然后重新启动。同样的问题,但再次检查power_save选项看起来像是重新打开了。
我刚测试关闭它,重新启动,并立即再次检查它是否已重新启用。
编辑#2:确认如果我在wlan0上设置power_save并且不重启,它仍然不会在适当的超时运行循环。我等了30分钟才重新启动它并且运行正常,但这是因为tick()在第一次运行时被强制执行。