roccomuso / node-ads

NodeJS Twincat ADS protocol implementation
58 stars 21 forks source link

Occasional "Symbol not found" #19

Closed acidos closed 6 years ago

acidos commented 6 years ago

I have an app which is periodically writing few variables in PLC. The period is every 5 minute.

var options = {
    host: "127.0.0.1",
    amsNetIdTarget: "10.10.2.100.1.1",
    amsNetIdSource: "10.10.2.100.1.15",
    amsPortTarget: 801
}

var client = ads.connect(options, function() {
                    WriteAds(client, values[0]).
                    then(() => WriteAds(client, values[1])).
                    then(() => WriteAds(client, values[2])).
                    then(() => WriteAds(client, values[3])).
                    then(() => WriteAds(client, values[4])).
                    then(() => WriteAds(client, values[5])).
                    then(() => WriteAds(client, values[6])).
                    then(() => WriteAds(client, validityHandle)).
                    catch((err) => { 
                        log.error("Error writing to ADS: ", err);
                    }).
                    finally(() => ReleaseAndDisconnect(client))
                })

function ReleaseAndDisconnect(client)
{
    return new Promise(function(resolve, reject){
        client.end(function (){ resolve() })
        resolve()
    })
}

function WriteAds(client, h)
{
    return new Promise(function(resolve, reject){
        client.write(h, function(err) {
            if (err) 
            {
                reject(err)
            }
            else
            {
                resolve()
            }
        })
    })
}

As you can see, there are 6 handles in array and 1 standalone handle. This is the exception

Error writing to ADS: symbol not found
Error: symbol not found
    at getError (C:\pkg\node_modules\node-ads\lib\ads.js:1290:13)
    at Object.getWriteResult (C:\pkg\node_modules\node-ads\lib\ads.js:792:13)
    at Object.analyseResponse (C:\pkg\node_modules\node-ads\lib\ads.js:224:24)
    at Object.checkResponseStream (C:\pkg\node_modules\node-ads\lib\ads.js:177:25)
    at Socket.<anonymous> (C:\pkg\node_modules\node-ads\lib\ads.js:116:25)
    at emitOne (events.js:116:13)
    at Socket.emit (events.js:211:7)
    at addChunk (_stream_readable.js:263:12)
    at readableAddChunk (_stream_readable.js:250:11)
    at Socket.Readable.push (_stream_readable.js:208:10)

It happens on standalone variable. Variables before are written just fine.

I captured ADS traffic and can see that ADSIGRP_SYM_HNDBYNAME is called for all variables following ADSIGRP_SYM_VALBYHND, except last one. The last one only received ADSIGRP_SYM_VALBYHND without prior ADSIGRP_SYM_HNDBYNAME. Not sure where index/offset was discovered, but there is some value. Is it cached somewhere?

PLCHome commented 6 years ago

Global symname must start with a dot: .engine, program symname must start with the programname: MAIN.UpTyp.timerUp.PT.

There is also a problem with special characters, because node-ads pass the texts as UTF8 to the PLC and not as ASCII. Do you want to tell us the symname name?

You can use getSymbols to trace the known names.

client = ads.connect(options, function() {
    this.getSymbols(function(err, symbols) {
        if (err) console.log(err)
        console.log(symbols)
        this.end()
    })
})

        client.end(function (){ resolve() })
        resolve()

better you remove the second resolve() ;-)

acidos commented 6 years ago

This is the failing handle

var validityHandle = {
    symname: 'MAIN.PKG.bEnable',
    bytelength: ads.BOOL,  
    propname: 'value',
    value: true
}

The successful ones are MAIN.PKG.BLA_BLA matching exactly to the ones declared in PLC. The type of others are mixed, including float, bool, though I don't think it's related. As I stated, the error "symbol not found" happens occasionally, but with some pattern. If I just start my node app, first(or first few) times everything is successful. The program is running on setInterval of 5 minutes. On consequent timer events, the exception occurs.

acidos commented 6 years ago

better you remove the second resolve() ;-)

ah, dankeschön :)

acidos commented 6 years ago

So what I tried is moved then(() => WriteAds(client, validityHandle)). to the top, to see if the failure is related to the last Write, but it failed again on exactly this handle. If something wrong with the name of the handle, why it's writing successfully always very first time?

PLCHome commented 6 years ago

I think the second resolve() is not the problem here.

I am puzzled about:

_The last one only received ADSIGRP_SYM_VALBYHND without prior ADSIGRP_SYMHNDBYNAME.

node-ads has a peculiarity, it writes all information into the transferred object validityHandle. ADSIGRP_SYM_HNDBYNAME is not called when a handle is handed over. But then the symname is forbidden. I am confused

acidos commented 6 years ago

So I have bunch of ADSIGRP_SYM_HNDBYNAME calls like below for all handles in values array:

image

Then all of the sudden I see ADSIGRP_SYM_VALBYHND to read the value for bEnable I guess, not sure where it got index/offset: image

Then an error: image

Tomorrow I'm going to do getSymbols, to see if the index/offset is matching. Second, I'm going to capture the first successful operation and compare.

acidos commented 6 years ago

I did a quick capture of successful call and I see ADSIGRP_SYM_HNDBYNAME

image

PLCHome commented 6 years ago

this is a part of var getHandle = function

    if (typeof handle.symhandle === 'undefined') {
      var commandOptions = {
        indexGroup: ADSIGRP.GET_SYMHANDLE_BYNAME,
        indexOffset: 0x00000000,
        writeBuffer: buf,
        readLength: 4,
        symname: handle.symname
      }

      writeReadCommand.call(ads, commandOptions, function (err, result) {
      .....
}

plase try only for a test:

function WriteAds(client, h)
{
    return new Promise(function(resolve, reject){
        client.write(h, function(err) {
           delete(h.symhandle)
           if (err) 
            {
                reject(err)
            }
            else
            {
                resolve()
            }
        })
    })
}

or use a clean object:

function WriteAds(client, h)
{
    return new Promise(function(resolve, reject){
        var newH = Object.assign({},h)
        client.write(newH, function(err) {
           if (err) 
            {
                reject(err)
            }
            else
            {
                resolve()
            }
        })
    })
}

because client.end releases all symhandle you got in the session

var end = function (cb) {
 ...
  releaseSymHandles.call(ads, function () {
   ...

Did you slove the connection problem on the localhost?

acidos commented 6 years ago

So you mean the old value of symhandle is persisting through the multiple calls? Can be true, because only that object is static and I never reset it. I'll try it tomorrow morning, off to bed now. Thanks!

PLCHome commented 6 years ago

So you mean the old value of synhandle is persisting through the multiple calls?

yes that's javascript

PLCHome commented 6 years ago

If I look at the code, it looks like you would pick up a value via http-get and send it to the PLC. Ever thought about NODE-RED?

Timer Node -> HTTP Request Node -> ADS Write Node

acidos commented 6 years ago

Did you slove the connection problem on the localhost?

I'll update previous topic in a moment

acidos commented 6 years ago

If I look at the code, it looks like you would pick up a value via http-get and send it to the PLC. Ever thought about NODE-RED?

Timer Node -> HTTP Request Node -> ADS Write Node

My web request is pretty complex and requires login, cookies, cheerio, header case normalization to extract data. The function is already there(around 200 lines of code) and I'm not sure if I can easily do it with HTTP Request Node.

Also for ADS I need to write float values, extracted from the web, then at the end put validity flag, before invalidation timer expires in PLC. Not sure if this can be achieved in NR as flexible as I do it in single js file.

Currently I installed it as windows service, using node-windows and it's running smoothly. Thanks to you, the problem with symbol not found solved (though I think lib should not keep the state in user object).

With that said, I love NR and always looking for opportunities to use it. But for this task it feels like shooting bird with canon.

PLCHome commented 6 years ago

Thanks to you, the problem with symbol not found solved

Added note in the readme.md @roccomuso please close

roccomuso commented 6 years ago

ok thanks!