Community Tip - You can subscribe to a forum, label or individual post and receive email notifications when someone posts a new topic or reply. Learn more! X
How can I remove Remote Things that are show in Monitoring -> Remote Things?
I've been working on a service to automatically create new things based on Resources["DeviceFunctions"].GetUnboundRemoteThings() and binding them too.
Now I have a huge amount of remote things showing in the Remote Things' Monitoring page's "All" tab (with red mark, so they are all offline). There're no actual Things for them to delete, though. That's why it's odd that they don't appear on the "Unbound" tab.
There're no identifiers specified, so when I connect and bind those names with SDK agent, they seem like online, otherwise offline.
If I try to create a Thing with that name, I get an error saying: save failed, status: "Conflict", message: "Object With This Name Already Exists"
even though I can't find Thing with that name.
It seems to have been my bad for not having the the things unbound after closing the SDK agent. Then I just deleted those things and it got somewhat messed up.
Solved! Go to Solution.
Most likely these have become 'ghost' entities because they were incorrectly created.
Try restarting Tomcat.
Most likely these have become 'ghost' entities because they were incorrectly created.
Try restarting Tomcat.
Thanks! Got finally in touch with the guy on the server side and he did the restart.
Ghosts are gone now
Also note when creating entities via service it is important to include proper error handling in the script. See the following article....
https://support.ptc.com/appserver/cs/view/solution.jsp?n=CS198580&lang=en_US
Thanks but it seems, we don't have PTC Global Support agreement so I have no access to that article. Some error handling's already included to my service script. I'll ask if we can get that agreement.
try {
var params = { tags: undefined /* TAGS */,
thingTemplateName: "GenericThing" /*THINGTEMPLATENAME */,
description: "new thing" /* STRING */,
name: thing1 /* STRING */ };
// no return
Resources["EntityServices"].CreateThing(params);
Things[thing1].EnableThing();
Things[thing1].RestartThing();
}
catch(e) {
logger.error("Unable to create Thing");
var params = { name: thing1 /* THINGNAME */ };
// no return
Resources["EntityServices"].DeleteThing(params);
}
Thanks!
I actually did have the try catch statement there but was missing DeleteThing method from catch side.
There's a problem with DeleteThing in catch block. If the Thing exists really, this catch will delete the real Thing and not the ghost one.
Is is possible to distinguish between a real Thing and a ghost one ?
Hi Quang-Dung,
You can't have Ghost and Real Thing with the same name on the same system, or it's a Ghost or it's a real Thing.
If you are asking how to know if a Thing it's a Ghost or a Real Thing, I think there isn't an answer to this.
If you want to prevent to create an already existing thing, try to check first for it's existence.
Don't try to do Try/Catch/Delete to an unconsistent system, first restart your system, then do it on the correct way.
Carles.
Hi Carles,
thank you for your answer. Can you please tell me a service to find Thing only by name, without Template etc. ? I tried SearchThings with a query parameter without success. I wonder if I have to use searchExpression parameter instead.
You may do something like Things["ThingName"]==null or Things["ThingName"]==undefined.
Hi Quang-Dung,
In regards to ghost entities, I would also like to share a script with you with which you can detect all ghost things in your system. It doesn't help you delete them, but you could run it in a subscription to get notified when you have any ghost entities and then immediately restart the server. The service receives as an input parameter your server name (HostURL) and an AppKey name (not the actual appKey value). It basically compares the Things in the spot light search (database) with the Things in the REST API (in memory). If there is a difference, it will print that out as an infotable (which will be the result of your service).
In my case the DataShape for the resulting Infotable is one specific to my project, but you can change it to include just the thingName.
Let me know if this helps or if you have any questions.
try { var params = {
searchExpression: "*" /* STRING */,
types: {"items":["Thing"]}
/* JSON */,
withPermissions: true /* BOOLEAN */,
aspects: {"isSystemObject":false}
/* JSON */,
searchDescriptions: true /* BOOLEAN */,
sortBy: "lastModifiedDate" /* STRING */,
isAscending: false /* BOOLEAN */,
};
// result: INFOTABLE dataShape: SpotlightSearch
/* controllers in spotlight search may contain entities
that have been created only in memory
*/
var spotlightSearchItems = Resources["SearchFunctions"].SpotlightSearch(params);
/* the infotable below will contain ONLY things
that are also in the ThingWorx database
*/
// result: INFOTABLE dataShape: RootEntityList
// result: STRING
var KeyID = ApplicationKeys[AppKey].GetKeyID();
var params = {
proxyScheme: undefined /* STRING */,
headers: undefined /* JSON */,
ignoreSSLErrors: undefined /* BOOLEAN */,
useNTLM: undefined /* BOOLEAN */,
workstation: undefined /* STRING */,
useProxy: undefined /* BOOLEAN */,
withCookies: undefined /* BOOLEAN */,
proxyHost: undefined /* STRING */,
url: HostURL + "/Thingworx/Things?appKey=" + KeyID/* STRING */,
timeout: undefined /* NUMBER */,
proxyPort: undefined /* INTEGER */,
password: undefined /* STRING */,
domain: undefined /* STRING */,
username: undefined /* STRING */
};
// result: JSON
//var controllers = Resources["ContentLoaderFunctions"].LoadJSON(params);
var entities = Resources["ContentLoaderFunctions"].LoadJSON(params);
var params = {
infoTableName : "InfoTable",
dataShapeName : "ControllersDisplayShape"
};
// CreateInfoTableFromDataShape(infoTableName:STRING("InfoTable"), dataShapeName:STRING):INFOTABLE(ControllersDisplayShape)
var thingsInfotable = Resources["InfoTableFunctions"].CreateInfoTableFromDataShape(params);
for (var i = 0; i < entities.rows.length; i++) {
if (entities.rows.isSystemObject == false)
thingsInfotable.AddRow({
ControllerName:entities.rows.name,
ControllerDisplayName:entities.rows.description
});
}
var ghostsCount = thingsInfotable.getRowCount()- spotlightSearchItems.getRowCount();
logger.info("Number of ghosts is " + ghostsCount);
// If the ghost count is zero, the result will just be an empty infotable
var params = {
infoTableName : "InfoTable",
dataShapeName : "ControllersDisplayShape"
};
// CreateInfoTableFromDataShape(infoTableName:STRING("InfoTable"), dataShapeName:STRING):INFOTABLE(DisplayNameDataShape)
var result = Resources["InfoTableFunctions"].CreateInfoTableFromDataShape(params);
// if the ghostCount is higher than 0, we want to check which is(are) the actual ghost controller(s)
if (ghostsCount > 0) {
for (var i = 0; i < thingsInfotable.getRowCount(); i++) {
for (var j = 0 ; j < spotlightSearchItems.getRowCount(); j++) {
if (thingsInfotable.rows.ControllerName == spotlightSearchItems.rows
//we discard from the spotlight search every controller which we already found in the DB
//to reduce our search time
thingsInfotable.RemoveRow(i);
i--;
break;
}
}
}
// the remaining infotable will contain only the ghosts
var result = thingsInfotable;
}
} catch (err) {
logger.error ("Encountered error " + err + " at line " + err.lineNumber) ;
}
Thank you! The concept works perfectly for me. Also, I learnt some new useful services like RemoveRow which are not listed in the InfoTableFunctions' services.