Node.js TypeError [ERR_INVALID_ARG_TYPE]错误排查求助——IoT设备远程传感器数据采集与控制代码问题
TypeError: "path" argument is undefined when reading Thing Description files in Node.js IoT project
我正在开发一个IoT项目,目标是获取温湿度等传感器数据并远程控制设备,但运行代码时遇到了一个类型错误,反复检查代码都没能定位到问题根源。以下是报错信息:
node:internal/errors:464 ErrorCaptureStackTrace(err); ^ TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string or an instance of Buffer or URL. Received undefined at Object.openSync (node:fs:577:10) at Object.readFileSync (node:fs:453:35) at readTd (C:\Users\Mehdi\Desktop\WS2022\IoT_Mehdi\iotrlws2122cons2-ge37vok\6_averageSensors.js:175:24) at C:\Users\Mehdi\Desktop\WS2022\IoT_Mehdi\iotrlws2122cons2-ge37vok\6_averageSensors.js:201:46 at step (C:\Users\Mehdi\Desktop\WS2022\IoT_Mehdi\iotrlws2122cons2-ge37vok\6_averageSensors.js:33:23) at Object.next (C:\Users\Mehdi\Desktop\WS2022\IoT_Mehdi\iotrlws2122cons2-ge37vok\6_averageSensors.js:14:53) at fulfilled (C:\Users\Mehdi\Desktop\WS2022\IoT_Mehdi\iotrlws2122cons2-ge37vok\6_averageSensors.js:5:58) at processTicksAndRejections (node:internal/process/task_queues:96:5) { code: 'ERR_INVALID_ARG_TYPE' }
我的完整代码如下:
import { Logger, createLogMessage } from './utils/Logger'; import { AffordanceType, RecipientType, Operation } from './utils/LogTemplate'; import { HttpsClientFactory } from "@node-wot/binding-http"; import ConsumedThing from "@node-wot/core/dist/consumed-thing"; import { Servient } from "@node-wot/core"; import fs = require("fs"); import path = require("path"); import { exit } from "process"; import { ThingDescription } from "wot-typescript-definitions"; const servient = new Servient(); let creds = { "urn:dev:ops:32473-sensehat-001": { // Here the username and password I Provided you with "username": "xxxxxx", "password": "xxxxxx" }, "urn:dev:ops:32473-sensehat-002": { // Here the username and password I Provided you with "username": "xxxxxx", "password": "xxxxxx" }, "urn:dev:ops:32473-sensehat-003": { // Here the username and password I Provided you with "username": "xxxxxx", "password": "xxxxxx" }, "urn:dev:ops:32473-sensehat-004": { // Here the username and password I Provided you with "username": "xxxxxx", "password": "xxxxxx" }, "urn:dev:ops:32473-HueIndoorSensor-1": { // Here the username and password I Provided you with "username": "xxxxxx", "password": "xxxxxx" }, "urn:dev:ops:32473-HueIndoorSensor-2": { // Here the username and password I Provided you with "username": "xxxxxx", "password": "xxxxxx" }, "urn:dev:ops:32473-HueOutdoorSensor-1": { // Here the username and password I Provided you with "username": "xxxxxx", "password": "xxxxxx" }, }; servient.addClientFactory(new HttpsClientFactory(null)); servient.addCredentials(creds); const delay = async (ms: number) => { await new Promise<void>(resolve => setTimeout(() => resolve(), ms)); } var logger = new Logger(); async function readLoggedProperty(thing: any, propertyName: string, options?: any): Promise<any> { var logMsg = createLogMessage( { name: propertyName, type: AffordanceType.Property }, 1, Operation.Readproperty, RecipientType.Thing, thing.getThingDescription().title, thing.getThingDescription().id, null ); logger.addMessage(logMsg); // logger.saveMessages("6_averageSensors", "HTTP"); let ret = await thing.readProperty(propertyName, options); var logMsg = createLogMessage( { name: propertyName, type: AffordanceType.Property }, 1, Operation.Invokeaction, RecipientType.Controller, thing.getThingDescription().title, thing.getThingDescription().id, ret ); logger.addMessage(logMsg); // logger.saveMessages("6_averageSensors", "HTTP"); return ret; } async function invokeLoggedAction(thing: any, actionName: string, params?: any, options?: any): Promise<any> { var logMsg = createLogMessage( { name: actionName, type: AffordanceType.Action }, 1, Operation.Invokeaction, RecipientType.Thing, thing.getThingDescription().title, thing.getThingDescription().id, params ); logger.addMessage(logMsg); // logger.saveMessages("6_averageSensors", "HTTP"); await thing.invokeAction(actionName, params, options).then(function (ret) { var logMsg = createLogMessage( { name: actionName, type: AffordanceType.Action }, 1, Operation.Invokeaction, RecipientType.Controller, thing.getThingDescription().title, thing.getThingDescription().id, ret ); logger.addMessage(logMsg); logger.saveMessages("6_averageSensors", "HTTP"); return ret; }) } let td_sensHats1: ThingDescription; function readTd (tdPath: string){ let read_file = fs.readFileSync(tdPath); var td = JSON.parse(read_file.toString()); return td; } var ConsumedthingsHat=[] var ConsumedthingsHue=[] async function main() { var array_SenseHat = ["SenseHat1.json","SenseHat2.json","SenseHat3.json","SenseHat4.json"]; //declaration var array_Hue=["Hue Indoor Sensor 1.json","Hue Indoor Sensor 2.json","Hue Outdoor Sensor.json"]; //declaration await servient.start().then(async (WoT) => { for (let m = 0 ; m < 5; m ++) { var td = readTd(array_SenseHat[m]); const thing = await WoT.consume(td) as ConsumedThing; console.log(thing) ConsumedthingsHat.push(thing); } for (let m = 0 ; m < 4; m ++) { var td = readTd(array_Hue[m]); const thing = await WoT.consume(td) as ConsumedThing; console.log(thing) ConsumedthingsHue.push(thing); } // await servient.start().then(async (WoT) => { const thing1 = await WoT.consume(ConsumedthingsHat[0]); // Average Teamperature let array_tempvalues: number[] = []; for (let j = 0; j < 5; j++) { // var td = await readTd(array_SenseHat[j]); // const temp_Value = await WoT.consume(td) as ConsumedThing; let temp = await readLoggedProperty(ConsumedthingsHat[j], "temperature"); await array_tempvalues.push(temp); } let avgTemp = array_tempvalues.reduce((total, actual_TempValue, index, array) => { return (total + actual_TempValue) / array.length; }) let payload1 = { "avgTmp": avgTemp.toFixed(2) }; await invokeLoggedAction(thing1, "showMessage", payload1); // Avg pressure on hat2 const thing2 = await WoT.consume(ConsumedthingsHat[1]); let arrayPressureValues: number[] = []; for (let j = 0; j < 5; j++) { // const element = await WoT.consume(ConsumedthingsHat[j]); let pressure = await readLoggedProperty(ConsumedthingsHat[j], "pressure"); arrayPressureValues.push(pressure); } let avgPress = arrayPressureValues.reduce((total, actual_Pressure, ind, array) => { return (total + actual_Pressure) / array.length; }) let payload2 = { "avgPressure": avgPress.toFixed(2) }; await invokeLoggedAction(thing2, "showMessage", payload2); // Avg humidity on hat2 let arrayHumidity: number[] = []; const thing3 = await WoT.consume(ConsumedthingsHat[2]); for (let j = 0; j <5; j++) { // const element = await WoT.consume(ConsumedthingsHat[j]); let humid = await readLoggedProperty(ConsumedthingsHat[j], "humidity"); arrayHumidity.push(humid); } let avgHumid = arrayHumidity.reduce((total, actual_Humidity, ind, array) => { return (total + actual_Humidity) / array.length; }) let payload3 = { "textString": avgHumid.toFixed(2) }; await invokeLoggedAction(thing3, "showMessage", payload3); // Avg humidity on hat2 let arrayLight: number[] = []; const thing4 = await WoT.consume(ConsumedthingsHue[0]); for (let j = 0; j < 5; j++) { // const element = await WoT.consume(ConsumedthingsHue[j]); let info = await readLoggedProperty(ConsumedthingsHat[j], "lightInformation"); arrayLight.push(info["state"]["lightlevel"]); } let avgLight = arrayLight.reduce((total, actual_Light, ind, array) => { return (total + actual_Light) / array.length; }) let payload4 = { "textString": avgLight.toFixed(2) }; await invokeLoggedAction(thing4, "showMessage", payload4); await delay(5000).then(async () => { for (let j = 0; j < 5; j++) { const element1= array_SenseHat[j]; await invokeLoggedAction(element1,"clear"); console.log("Nothing Displayed"); } }) }) } main()
请求协助排查该错误的根源并提供解决方法。
问题根源分析
从报错信息可以看到,错误发生在readTd函数调用fs.readFileSync(tdPath)时,传入的tdPath是undefined。追踪代码可以发现两个核心问题:
循环次数与数组长度不匹配:
- 你的
array_SenseHat数组只有4个元素,但第一个for循环写的是m < 5,当m=4时,array_SenseHat[4]就是undefined,传给readTd就会导致路径参数无效。 - 同理,
array_Hue数组只有3个元素,但第二个for循环写的是m < 4,当m=3时,array_Hue[3]也是undefined,同样触发错误。
- 你的
后续循环存在相同的越界问题:
计算平均温度、压力、湿度的循环都写了j <5,但ConsumedthingsHat数组最多只有4个有效元素,后续也会出现访问undefined元素的问题。另外最后一个清除显示的循环,还错误地把文件路径字符串传给了需要ConsumedThing实例的invokeLoggedAction函数。
解决方法
1. 修正循环终止条件,使用数组长度而非硬编码数字
把所有循环的终止条件改成依赖数组的length属性,这样无论数组元素数量变化,循环都不会越界:
// 修正SenseHat循环 for (let m = 0 ; m < array_SenseHat.length; m ++) { var td = readTd(array_SenseHat[m]); const thing = await WoT.consume(td) as ConsumedThing; console.log(thing) ConsumedthingsHat.push(thing); } // 修正Hue循环 for (let m = 0 ; m < array_Hue.length; m ++) { var td = readTd(array_Hue[m]); const thing = await WoT.consume(td) as ConsumedThing; console.log(thing) ConsumedthingsHue.push(thing); } // 修正温度平均值循环 for (let j = 0; j < ConsumedthingsHat.length; j++) { let temp = await readLoggedProperty(ConsumedthingsHat[j], "temperature"); array_tempvalues.push(temp); } // 同理修正压力、湿度、光照的循环,都使用ConsumedthingsHat.length或ConsumedthingsHue.length
2. 给readTd函数添加参数校验(可选但推荐)
在readTd函数开头添加参数检查,提前抛出更明确的错误,方便后续调试:
function readTd (tdPath: string){ if (!tdPath) { throw new Error(`Failed to read Thing Description: path is undefined. Check if your device array has enough elements.`); } let read_file = fs.readFileSync(tdPath); var td = JSON.parse(read_file.toString()); return td; }
3. 修正最后一个清除显示的循环
最后一个循环里,你把array_SenseHat[j](字符串路径)传给了invokeLoggedAction,但这个函数需要接收一个ConsumedThing实例,不是路径。应该改成:
await delay(5000).then(async () => { for (let j = 0; j < ConsumedthingsHat.length; j++) { await invokeLoggedAction(ConsumedthingsHat[j],"clear"); console.log("Nothing Displayed"); } })
额外优化建议
- 避免在
await servient.start().then(...)里面嵌套过多异步逻辑,改用async/await的链式写法,代码会更易读。 - 可以给
ConsumedthingsHat和ConsumedthingsHue添加类型注解,符合TypeScript的规范,比如:const ConsumedthingsHat: ConsumedThing[] = [];
内容的提问来源于stack exchange,提问作者Spearitch502
相关产品推荐
相关产品推荐

