From the documentation: This does not appear to be the proper location for a custom icon library in 8.3. Did a search for material.svg and it is not found. I'm assuming this is still allowed. What folder should a custom library go into now?
Use a Custom Icon Repository
The materials icon repository is a great source for icons, but if you have a custom library of icons, you can set it up to be accessible by Ignition. All icon repositories are stored as SVG files within the Gateway's Installation Directory. A typical path to an icon file will look like:
Navigate to your install directory, and in the /data/config/resources/core/ folder, create a new folder com.inductiveautomation.perspective/ (if it doesn't already exist).
Within that folder create an icons/ folder.
Within that folder create a <repository name>/ folder.
Within that folder, create:
The spritesheet of your SVG, at whatever arbitrary filename you want, e.g. icons.svg.
A config.json file that contains a single entry, svgFileName, that contains the same name (icons.svg or whatever else you chose)
{
"svgFileName": "icons.svg"
}
A resource.json file that references both config.json and your custom icons SVG:
As an aside, I'm not sure if there's a rationale I'm not aware of for the indirection of the config.json resource. I'm going to make a case to drop it for final release, so you would just have a resource.json file pointing to the svg file (by whatever arbitrary name). There may be a complicated upgrade related reason we had to do things this way, though.
I haven’t been able to get the icons to open in designer by adding the icon folder as described above. I have my svg file formatted per this section of the manual: Images and Icons in Perspective | Ignition User Manual. Nothing populates in the migrated-icons folder after a gateway restart either.
E [P.IconManager ] [15:28:30.982] [platform-executor-84]: Unable to parse PlantPAxIcons icon library collection=core
org.xml.sax.SAXParseException: An invalid XML character (Unicode: 0x0) was found in the element content of the document.
at java.xml/com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(DOMParser.java:262)
at java.xml/com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:342)
at com.inductiveautomation.perspective.gateway.assets.icons.IconManagerImpl.computeLibraryAndCache(IconManagerImpl.java:182)
at com.inductiveautomation.perspective.gateway.assets.icons.IconManagerImpl.lambda$onResourceAdded$5(IconManagerImpl.java:150)
at java.base/java.util.concurrent.ConcurrentHashMap.compute(ConcurrentHashMap.java:1916)
at com.inductiveautomation.perspective.gateway.assets.icons.IconManagerImpl.onResourceAdded(IconManagerImpl.java:150)
at com.inductiveautomation.ignition.gateway.config.NamedResourceHandler$Lifecycle.onAfterChanges(NamedResourceHandler.java:705)
at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionLifecycle$LifecycleResourceListener.onAfterChanges(ResourceCollectionLifecycle.java:183)
at com.inductiveautomation.ignition.common.resourcecollection.AbstractResourceCollection.notifyListener(AbstractResourceCollection.java:386)
at com.inductiveautomation.ignition.common.resourcecollection.AbstractResourceCollection.notifyResourceListeners(AbstractResourceCollection.java:315)
at com.inductiveautomation.ignition.common.resourcecollection.AbstractResourceCollection.updateEffectiveState(AbstractResourceCollection.java:156)
at com.inductiveautomation.ignition.common.resourcecollection.RuntimeResourceCollection.applyChange(RuntimeResourceCollection.java:267)
at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionLifecycleFactory$1.lambda$updateOrStartAffected$5(ResourceCollectionLifecycleFactory.java:210)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionLifecycleFactory$1.updateOrStartAffected(ResourceCollectionLifecycleFactory.java:190)
at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionLifecycleFactory$1.collectionUpdated(ResourceCollectionLifecycleFactory.java:167)
at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionManagerImpl.notifyCollectionChanged(ResourceCollectionManagerImpl.java:1610)
at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionManagerImpl.notifyCollectionChanged(ResourceCollectionManagerImpl.java:1594)
at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionManagerImpl.lambda$onFileTreeChange$27(ResourceCollectionManagerImpl.java:1515)
at java.base/java.lang.Iterable.forEach(Iterable.java:75)
at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionManagerImpl.lambda$onFileTreeChange$28(ResourceCollectionManagerImpl.java:1514)
at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionManagerImpl.lambda$wrapNotificationTask$0(ResourceCollectionManagerImpl.java:204)
at com.inductiveautomation.ignition.common.util.ExecutionQueue$PollAndExecute.run(ExecutionQueue.java:238)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:840)
E [P.IconManager ] [15:28:30.985] [platform-executor-84]: Unable to parse PlantPAxIcons icon library collection=core
org.xml.sax.SAXParseException: An invalid XML character (Unicode: 0x0) was found in the element content of the document.
at java.xml/com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(DOMParser.java:262)
at java.xml/com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:342)
at com.inductiveautomation.perspective.gateway.assets.icons.IconManagerImpl.computeLibraryAndCache(IconManagerImpl.java:182)
at com.inductiveautomation.perspective.gateway.assets.icons.IconManagerImpl.lambda$onResourcesUpdated$7(IconManagerImpl.java:167)
at java.base/java.util.concurrent.ConcurrentHashMap.compute(ConcurrentHashMap.java:1916)
at com.inductiveautomation.perspective.gateway.assets.icons.IconManagerImpl.lambda$onResourcesUpdated$8(IconManagerImpl.java:166)
at java.base/java.lang.Iterable.forEach(Iterable.java:75)
at com.inductiveautomation.perspective.gateway.assets.icons.IconManagerImpl.onResourcesUpdated(IconManagerImpl.java:166)
at com.inductiveautomation.ignition.gateway.config.NamedResourceHandler$Lifecycle.onAfterChanges(NamedResourceHandler.java:720)
at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionLifecycle$LifecycleResourceListener.onAfterChanges(ResourceCollectionLifecycle.java:183)
at com.inductiveautomation.ignition.common.resourcecollection.AbstractResourceCollection.notifyListener(AbstractResourceCollection.java:386)
at com.inductiveautomation.ignition.common.resourcecollection.AbstractResourceCollection.notifyResourceListeners(AbstractResourceCollection.java:315)
at com.inductiveautomation.ignition.common.resourcecollection.AbstractResourceCollection.updateEffectiveState(AbstractResourceCollection.java:156)
at com.inductiveautomation.ignition.common.resourcecollection.RuntimeResourceCollection.applyChange(RuntimeResourceCollection.java:267)
at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionLifecycleFactory$1.lambda$updateOrStartAffected$5(ResourceCollectionLifecycleFactory.java:210)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionLifecycleFactory$1.updateOrStartAffected(ResourceCollectionLifecycleFactory.java:190)
at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionLifecycleFactory$1.collectionUpdated(ResourceCollectionLifecycleFactory.java:167)
at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionManagerImpl.notifyCollectionChanged(ResourceCollectionManagerImpl.java:1610)
at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionManagerImpl.notifyCollectionChanged(ResourceCollectionManagerImpl.java:1594)
at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionManagerImpl.lambda$onFileTreeChange$27(ResourceCollectionManagerImpl.java:1515)
at java.base/java.lang.Iterable.forEach(Iterable.java:75)
at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionManagerImpl.lambda$onFileTreeChange$28(ResourceCollectionManagerImpl.java:1514)
at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionManagerImpl.lambda$wrapNotificationTask$0(ResourceCollectionManagerImpl.java:204)
at com.inductiveautomation.ignition.common.util.ExecutionQueue$PollAndExecute.run(ExecutionQueue.java:238)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:840)
Your fix worked perfectly for me in the 8.3.0 release, but on a fresh Gateway (running 8.3.1) using the same setup methodology (and using the same SVG sprite sheet), I am getting the following error.
Error
Failed to load resource: the server responded with a status of 404 (Not Found)
My config.json and resource.json files are identical to yours (I even used the same icons.svg file name). If I follow the link at the end of the Dev Tools Console Error I am greeted with the following page:
Any solutions would be appreciated, I very well may have made a silly mistake somewhere when copying my resource from one Gateway to another but I can’t figure out what’s going on. Any ideas?
I am so sorry! I completely forgot to check the logs (didn’t even realise an error like this would get logged). There was a small error in my SVG. I assume I must have accidentally introduced it when copying the file over somehow.
Yes, I’ve spent hours trying to get the ‘sample’ SVG file to be recognised, and now I’ve got an exception preventing my gateway from starting.
wrapper.log contains: java.nio.file.FileAlreadyExistsException: Resource collection path '/usr/local/bin/ignition/data/config/resources/core' exists but is not empty
Gateway error is ‘unable to create ‘core’ resource collection’. I don’t understand the exception, since the core folder has never been empty.. very confusing.
If you're attempting to preload some portion of the core resource collection, make sure that you've got a data/config/resources/core/config-mode.json file in place:
The exception you're getting is when it tries to load the resource collection, cannot find the config-mode.json and then fails to create the collection fresh since the directory is not empty.
Thanks for the quick response. I’m not aware of preloading and I’ve never heard of the config-mode.json file. All I want is some customised icons instead of the materials set.
It wasn’t me getting the exception, it was the Gateway. Placing creating the custom icons svg and associated config as described in the examples linked to above is what broke it, and removing the folders did not fix anything. I had to completely remove Ignition, re-install and restore a backup.
So is there any way to safely add custom icons in 8.3 or not?
The sequence Paul posted above assumes that the Ignition installation is an existing one and that the core resource collection has already been created.
Based on the error you mentioned:
java.nio.file.FileAlreadyExistsException: Resource collection path '/usr/local/bin/ignition/data/config/resources/core' exists but is not empty
... it indicates that you might have started with a completely clean Ignition installation (i.e. one that hasn't launched for the first time) and created the folders and files per that sequence. If this is the case, you'll have to add the extra step of adding that config-mode.json file to the core resource collection base folder.
If this isn't the case, it might help to have a) more information on your specific environment, and/or b) more details from your wrapper.log.