--- a/editors/ConfTreeNodeEditor.py Tue Aug 23 08:39:08 2022 +0200
+++ b/editors/ConfTreeNodeEditor.py Tue Sep 06 21:06:36 2022 +0200
@@ -435,13 +435,16 @@
name = element_infos["name"]
value = element_infos["value"]
- staticbox = wx.StaticBox(self.ParamsEditor,
- label="%s - %s" % (_(name), _(value)),
- staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL)
- sizer.Add(staticboxsizer, border=5, flag=wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT)
- self.GenerateSizerElements(staticboxsizer, element_infos["children"], element_path)
- callback = self.GetChoiceContentCallBackFunction(combobox, staticboxsizer, element_path)
+ if element_infos["children"]: + staticbox = wx.StaticBox(self.ParamsEditor, + label="%s - %s" % (_(name), _(value)), + staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL) + sizer.Add(staticboxsizer, border=5, flag=wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT) + self.GenerateSizerElements(staticboxsizer, element_infos["children"], element_path) + callback = self.GetChoiceContentCallBackFunction(combobox, element_path) for choice in element_infos["type"]:
@@ -527,7 +530,7 @@
textctrl.Bind(wx.EVT_TEXT, callback)
textctrl.Bind(wx.EVT_KILL_FOCUS, callback)
- if not isinstance(element_infos["type"], list) and element_infos["use"] == "optional":
+ if not isinstance(element_infos["type"], list) and element_infos.get("use", None) == "optional": bt = wx.BitmapButton(self.ParamsEditor,
bitmap=wx.ArtProvider.GetBitmap(wx.ART_UNDO, wx.ART_TOOLBAR, (16,16)),
@@ -579,7 +582,7 @@
- def GetChoiceContentCallBackFunction(self, choicectrl, staticboxsizer, path):
+ def GetChoiceContentCallBackFunction(self, choicectrl, path): def OnChoiceContentChanged(event):
self.SetConfNodeParamsAttribute(path, choicectrl.GetStringSelection())
wx.CallAfter(self.RefreshConfNodeParamsSizer)
--- a/opc_ua/client.py Tue Aug 23 08:39:08 2022 +0200
+++ b/opc_ua/client.py Tue Sep 06 21:06:36 2022 +0200
@@ -6,7 +6,7 @@
from editors.ConfTreeNodeEditor import ConfTreeNodeEditor
from PLCControler import LOCATION_CONFNODE, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT
-from .opcua_client_maker import OPCUAClientPanel, OPCUAClientModel, UA_IEC_types
+from .opcua_client_maker import OPCUAClientPanel, OPCUAClientModel, UA_IEC_types, authParams import util.paths as paths
@@ -29,17 +29,56 @@
self.Controler.GetCTRoot().logger.write(msg)
- return self.Controler.GetServerURI()
def CreateOPCUAClient_UI(self, parent):
- return OPCUAClientPanel(parent, self.Controler.GetModelData(), self.Log, self.UriGetter)
+ return OPCUAClientPanel(parent, self.Controler.GetModelData(), self.Log, self.Controler.GetConfig) class OPCUAClient(object):
XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="OPCUAClient">
+ <xsd:element name="AuthType" minOccurs="0"> + <xsd:choice minOccurs="0"> + <xsd:element name="x509"> + <xsd:element name="Policy"> + <xsd:documentation>Default to Basic256Sha256 if not specified</xsd:documentation> + <xsd:choice minOccurs="0"> + <xsd:element name="Basic256Sha256"/> + <xsd:element name="Basic128Rsa15"/> + <xsd:element name="Basic256"/> + <xsd:element name="Mode"> + <xsd:choice minOccurs="0"> + <xsd:element name="SignAndEncrypt"/> + <xsd:element name="Sign"/> + <xsd:attribute name="Certificate" type="xsd:string" use="optional" default="certificate.pem"/> + <xsd:attribute name="PrivateKey" type="xsd:string" use="optional" default="private_key.pem"/> + <xsd:element name="UserPassword"> + <xsd:attribute name="User" type="xsd:string" use="optional"/> + <xsd:attribute name="Password" type="xsd:string" use="optional"/> <xsd:attribute name="Server_URI" type="xsd:string" use="optional" default="opc.tcp://localhost:4840"/>
@@ -61,8 +100,18 @@
- def GetServerURI(self):
- return self.GetParamsAttributes("OPCUAClient.Server_URI")["value"]
+ cfg = lambda path: self.GetParamsAttributes("OPCUAClient."+path)["value"] + AuthType = cfg("AuthType") + res = dict(URI=cfg("Server_URI"), AuthType=AuthType) + paramList = authParams.get(AuthType, None) + for name,default in paramList: + res[name] = cfg("AuthType."+name) return os.path.join(self.CTNPath(), 'selected.csv')
@@ -76,8 +125,7 @@
locstr = "_".join(map(str, current_location))
c_path = os.path.join(buildpath, "opcua_client__%s.c" % locstr)
- c_code = self.modeldata.GenerateC(c_path, locstr,
- self.GetParamsAttributes("OPCUAClient.Server_URI")["value"])
+ c_code = self.modeldata.GenerateC(c_path, locstr, self.GetConfig()) with open(c_path, 'wb') as c_file:
--- a/opc_ua/opcua_client_maker.py Tue Aug 23 08:39:08 2022 +0200
+++ b/opc_ua/opcua_client_maker.py Tue Sep 06 21:06:36 2022 +0200
@@ -38,6 +38,16 @@
directions = ["input", "output"]
+ ("Certificate", "certificate.der"), + ("PrivateKey", "private_key.pem"), + ("Policy", "Basic256Sha256"), + ("Mode", "SignAndEncrypt")], class OPCUASubListModel(dv.DataViewIndexListModel):
def __init__(self, data, log):
dv.DataViewIndexListModel.__init__(self, len(data))
@@ -230,7 +240,7 @@
class OPCUAClientPanel(wx.SplitterWindow):
- def __init__(self, parent, modeldata, log, uri_getter):
+ def __init__(self, parent, modeldata, log, config_getter): wx.SplitterWindow.__init__(self, parent, -1)
@@ -242,7 +252,7 @@
self.inout_sizer.AddGrowableRow(1)
- self.uri_getter = uri_getter
+ self.config_getter = config_getter self.connect_button = wx.ToggleButton(self.inout_panel, -1, "Browse Server")
@@ -298,7 +308,12 @@
self.tree.SetMainColumn(0)
- self.client = Client(self.uri_getter())
+ config = self.config_getter() + self.client = Client(config["URI"]) + # client.set_security_string("Basic256Sha256,SignAndEncrypt,certificate-example.der,private-key-example.pem") self.client.load_type_definitions() # load definition of server specific structures/extension objects
rootnode = self.client.get_root_node()
@@ -478,7 +493,7 @@
writer.writerow([direction] + row)
- def GenerateC(self, path, locstr, server_uri):
+ def GenerateC(self, path, locstr, config): template = """/* code generated by beremiz OPC-UA extension */
#include <open62541/client_config_default.h>
@@ -492,65 +507,66 @@
static C_type c_loc_name##_buf = 0; \\
C_type *c_loc_name = &c_loc_name##_buf;
-void __cleanup_%(locstr)s(void)
+void __cleanup_{locstr}(void) UA_Client_disconnect(client);
UA_Client_delete(client);
#define INIT_READ_VARIANT(ua_type, c_loc_name) \\
UA_Variant_init(&c_loc_name##_variant);
-#define INIT_WRITE_VARIANT(ua_type, ua_type_enum, c_loc_name) \\
+#define INIT_WRITE_VARIANT(ua_type, ua_type_enum, c_loc_name) \\ UA_Variant_setScalar(&c_loc_name##_variant, (ua_type*)c_loc_name, &UA_TYPES[ua_type_enum]);
-int __init_%(locstr)s(int argc,char **argv)
+int __init_{locstr}(int argc,char **argv) client = UA_Client_new();
UA_ClientConfig_setDefault(UA_Client_getConfig(client));
- retval = UA_Client_connect(client, "%(uri)s");
- if(retval != UA_STATUSCODE_GOOD) {
+ retval = UA_Client_connect(client, "{uri}"); + if(retval != UA_STATUSCODE_GOOD) {{ UA_Client_delete(client);
#define READ_VALUE(ua_type, ua_type_enum, c_loc_name, ua_nodeid_type, ua_nsidx, ua_node_id) \\
retval = UA_Client_readValueAttribute( \\
client, ua_nodeid_type(ua_nsidx, ua_node_id), &c_loc_name##_variant); \\
if(retval == UA_STATUSCODE_GOOD && UA_Variant_isScalar(&c_loc_name##_variant) && \\
- c_loc_name##_variant.type == &UA_TYPES[ua_type_enum]) { \\
+ c_loc_name##_variant.type == &UA_TYPES[ua_type_enum]) {{ \\ c_loc_name##_buf = *(ua_type*)c_loc_name##_variant.data; \\
UA_Variant_clear(&c_loc_name##_variant); /* Unalloc requiered on each read ! */ \\
-void __retrieve_%(locstr)s(void)
+void __retrieve_{locstr}(void)
-#define WRITE_VALUE(ua_type, c_loc_name, ua_nodeid_type, ua_nsidx, ua_node_id) \\
+#define WRITE_VALUE(ua_type, c_loc_name, ua_nodeid_type, ua_nsidx, ua_node_id) \\ UA_Client_writeValueAttribute( \\
client, ua_nodeid_type(ua_nsidx, ua_node_id), &c_loc_name##_variant);
-void __publish_%(locstr)s(void)
+void __publish_{locstr}(void)
+ # TODO: pass authentication code. @@ -581,7 +597,7 @@
formatdict["publish"] += """
WRITE_VALUE({ua_type}, {c_loc_name}, {ua_nodeid_type}, {ua_nsidx}, {ua_node_id})""".format(**locals())
- Ccode = template%formatdict
+ Ccode = template.format(**formatdict) @@ -594,7 +610,20 @@
frame = wx.Frame(None, -1, "OPCUA Client Test App", size=(800,600))
- uri = sys.argv[1] if len(sys.argv)>1 else "opc.tcp://localhost:4840"
+ config["URI"] = sys.argv[1] if argc>1 else "opc.tcp://localhost:4840" + config["AuthType"] = AuthType + for (name, default), value in izip_longest(authParams[AuthType], sys.argv[3:]): + raise Exception(name+" param expected") test_panel = wx.Panel(frame)
test_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
@@ -603,7 +632,7 @@
modeldata = OPCUAClientModel(print)
- opcuatestpanel = OPCUAClientPanel(test_panel, modeldata, print, lambda:uri)
+ opcuatestpanel = OPCUAClientPanel(test_panel, modeldata, print, lambda:config) @@ -624,7 +653,7 @@
-I ../../open62541/arch/ ../../open62541/build/bin/libopen62541.a
-"""%(path, path[:-2]) + modeldata.GenerateC(path, "test", uri) + """
+"""%(path, path[:-2]) + modeldata.GenerateC(path, "test", config) + """ int main(int argc, char *argv[]) {
--- a/xmlclass/xmlclass.py Tue Aug 23 08:39:08 2022 +0200
+++ b/xmlclass/xmlclass.py Tue Sep 06 21:06:36 2022 +0200
@@ -598,11 +598,15 @@
+ p = etree.Element(infos["name"]) - "initial": lambda: None,
"check": lambda x: x is None or infos["minOccurs"] == 0 and x
@@ -1450,14 +1454,14 @@
parts = path.split(".", 1)
if parts[0] in attributes:
- raise ValueError("Wrong path!")
+ raise ValueError("Wrong path: "+path) attr_type = gettypeinfos(attributes[parts[0]]["attr_type"]["basename"],
attributes[parts[0]]["attr_type"]["facets"])
value = getattr(self, parts[0], "")
elif parts[0] in elements:
if elements[parts[0]]["elmt_type"]["type"] == SIMPLETYPE:
- raise ValueError("Wrong path!")
+ raise ValueError("Wrong path: "+path) attr_type = gettypeinfos(elements[parts[0]]["elmt_type"]["basename"],
elements[parts[0]]["elmt_type"]["facets"])
value = getattr(self, parts[0], "")
@@ -1466,7 +1470,7 @@
attr = getattr(self, parts[0], None)
- raise ValueError("Wrong path!")
+ raise ValueError("Wrong path: "+path) return attr.getElementInfos(parts[0])
@@ -1477,7 +1481,7 @@
elif "base" in classinfos:
classinfos["base"].getElementInfos(name, path)
- raise ValueError("Wrong path!")
+ raise ValueError("Wrong path: "+path) children.extend(self.getElementAttributes())
@@ -1519,7 +1523,7 @@
parts = path.split(".", 1)
if parts[0] in attributes:
- raise ValueError("Wrong path!")
+ raise ValueError("Wrong path: "+path) if attributes[parts[0]]["attr_type"]["basename"] == "boolean":
setattr(self, parts[0], value)
elif attributes[parts[0]]["use"] == "optional" and value == None:
@@ -1534,7 +1538,7 @@
elif parts[0] in elements:
if elements[parts[0]]["elmt_type"]["type"] == SIMPLETYPE:
- raise ValueError("Wrong path!")
+ raise ValueError("Wrong path: "+path) if elements[parts[0]]["elmt_type"]["basename"] == "boolean":
setattr(self, parts[0], value)
elif attributes[parts[0]]["minOccurs"] == 0 and value == "":
@@ -1738,6 +1742,8 @@
return NAMESPACE_PATTERN.sub("", etree.tostring(self, pretty_print=True, encoding='utf-8')).decode('utf-8')
+ def getElementInfos(self, name, path=None, derived=False): + return {"name": name, "type": TAG, "value": None, "use": None, "children": []} class XMLElementClassLookUp(etree.PythonElementClassLookup):