Technical Description:\
The module (also referred to as a 'bundle') is a zip file, e.g. '.war' or '.jar' or '.zip', that can be uploaded to a PNPSCADA server, to add more functionality.\
\
In the root of the zip file must be a property file called BUNDLE.PRP, which has keyname-keyvalue pairs describing the bundle, and various other properties of the bundle.\
\
The module does not necessarily include executable code.\
\
If the module includes executable code, the project of the customer should be linking to the pnpscada_api.jar file - for which we will publish a javadoc - to get methods it can call in pnpscada, and classes and interfaces it can use and extend. The classes are in the package 'org.pnpscada.api.*', but if there are dependencies between modules, then a module would typically also be built around the jars of the other module, although it should not include it, since the other module already includes it - that would ensure it picks up the latest version of the other module. Modules should ALWAYS be backwards compatible. If not, please create a new module. These interfaces between modules would typically be defined through an Interface, for which a developer would allocate a unique id, in a similar way as he does for a bundle id below. \
\
The fundamental modularity of PNPSCADA revolves around Interfaces, the meaning of which the developers can define themselves. Much of the Module SDK is actually just the definition of the meaning of various Interfaces. If you adhere strictly to the definition of an Interface, when exporting it to others or importing if from others, it means you can import or export to other modules that you didn't know about beforehand, making PNPSCADA fundamentally extendable. For instance, if you can talk to a device over an InputStream/OutputStream pair, you can import that Interface, enabling PNPSCADA to link you to the device transparently through various different means, e.g. APNs, active GPRS modems, etherpads, IP-Telemetry, dial up modems, and more, without you as the protocol developer needing to implement interfaces to them all. Likewise, you can export the meter interface, and implement its requirements, which would enable various reports that works on meters to 'just work' on your devices, without you having to write all the reports. Or you can write reports on 'meters', generically, not necessarily caring exactly what kinds of meters they are.\
\
Some important properties of the BUNDLE.PRP file are:\
bundle.id - the bundle id. This is an 8 byte long. This must be unique across all PNPSCADA servers, so it is usually the server id multiplied by a big number (1000000), plus the instance of the bundle. There is a special servlet where a developer can allocate a unique bundle id for his bundle.\
bundle.permission - An integer. if this is 0, it can be installed in organizations that does not own a box, so it is typically non-executive or 'safe' content. A '1' would be 'restricted', so only a server admin can install it.\
bundle.version - this is the version number of the bundle (int), later versions being later releases. This is useful when debugging and upgrading modules\
bundle.author - A string (128) with the name of the author\
bundle.descr - A string (128) with the human readable short description of the bundle\
bundle.url - A string (128) with the url of the web page where you can read more about it. Optional.\
bundle.deploy - optional, for executable modules only, this is the class path of a class implementing the org.pnpscada.api.BundleDeploy interface, and we will call the deploy method when deploying the module, and it takes 2 parameters, namely (long id, long org), where org is a unique id of the organization over all PNPSCADA servers, and id is the bundle.id (see above). This gets called the first time a specific bundle is installed on a server.\
bundle.redeploy - optional, for executable modules only, this is the class path of a class implementing the org.pnpscada.api.BundleReDeploy interface, and we will call the redeploy method when deploying the module, and it takes 2 parameters, namely (long id, long org), where org is a unique id of the organization over all PNPSCADA servers, and id is the bundle.id (see above). This gets called when the bundle is upgraded from a previous version on this server.\
bundle.undeploy - optional, for executable modules only, this is the class path of a class implementing the org.pnpscada.adi.BundleUnDeploy interface, and we will call the undeploy method when undeploying the module, and it takes 2 parameters, namely (long id, long org), where org is a unique id of the organization over all PNPSCADA servers, and id is the bundle.id (see above). This gets called when the bundle is uninstalled from this server.\
bundle.startup - optional, for executable modules only, this is the class path of a class implementing the org.pnpscada.adi.BundleStartup interface, and we will call the startup method after deploying the module, and also at startup of the PNPSCADA server process. It takes 2 parameters, namely (long id, long org), where org is a unique id of the organization over all PNPSCADA servers, and id is the bundle.id (see above).\
bundle.childdeploy\
bundle.childundeploy\
bundle.childredeploy\
bundle.childstartup
- these 4 specify the classes implementing the org.pnpscada.api.BundleChildDeploy, org.pnpscada.api.BundleChildUnDeploy, org.pnpscada.api.BundleChildReDeploy and org.pnpscada.api.BundleChildStartup interface, calling the methods childdeploy, childundeploy, childredeploy and childstartup, with the same 2 parameters as above, of the classes on which this module is dependent. This is for instance very handy for a level 0 module that gets 'interpreted' by a level 1 module.\
bundle.date - UCT time of creation date in format YYYY-MM-DD HH:mm:SS\
dependency.0.id - the first bundle id on which this bundle is dependant. For further dependencies, specify dependency.1.id, dependency.2.id etc.\
dependency.0.version - the earliest version that we are compatible with. We need AT LEAST that version of the dependency module to work. Remember that it is expected that modules would be backwards compatible.\
\
\
Any properties starting with 'db.' are to insert rows into the database, and the format is in the form of:\
'db.[table name]<.row index for additional sorting>.[column name]=value'.\
Essentially, you need to create new classes, interfaces, pages, servlets, notifications, and new meter events using this method.\
Where possible, in most all cases, the bundle id will be inserted with the row, so that cleanup can happen easily. The bundle id would typically be a column called 'bundleid'.\
\
Information about bundles is saved in table 'bundlepnp'. Information about bundle dependencies are stored in table 'bundledep'.\
\
All files of the bundle is extracted in directory: bundle/[bundle-id]/[version]/ or in\
bundle/[bundle-id]/[version]/content if it is not a prp file or a jsp file.\
Other Executable files are not extracted here (.class; .java; .jar).\
content files are also extracted to docroot/[bundle-id]/ , which can be browsed with a web browser on that server under /[bundle-id]/*