edouard@3979: from __future__ import print_function edouard@3979: from __future__ import absolute_import edouard@3979: edouard@3979: import csv edouard@3979: import functools edouard@3979: from threading import Thread edouard@4012: from collections import OrderedDict as OD edouard@3979: edouard@3979: import wx edouard@3979: import wx.dataview as dv edouard@3979: edouard@3995: import util.paths as paths edouard@3995: edouard@4022: MQTT_UNSUPPORTED_types = set([ edouard@4022: "TIME", edouard@4022: "DATE", edouard@4022: "TOD", edouard@4022: "DT", edouard@4022: "STEP", edouard@4022: "TRANSITION", edouard@4022: "ACTION", edouard@4022: "STRING" edouard@4022: ]) edouard@4022: edouard@4022: MQTT_IEC_types_list =[ edouard@3979: # IEC61131| C type | sz edouard@4022: ("BOOL" , ("uint8_t" , "X")), edouard@4022: ("SINT" , ("int8_t" , "B")), edouard@4022: ("USINT", ("uint8_t" , "B")), edouard@4022: ("BYTE" , ("uint8_t" , "X")), edouard@4022: ("INT" , ("int16_t" , "W")), edouard@4022: ("UINT" , ("uint16_t", "W")), edouard@4022: ("WORD" , ("uint16_t", "W")), edouard@4022: ("DINT" , ("int32_t" , "D")), edouard@4022: ("UDINT", ("uint32_t", "D")), edouard@4022: ("DWORD", ("uint32_t", "D")), edouard@4022: ("LINT" , ("int64_t" , "L")), edouard@4022: ("ULINT", ("uint64_t", "L")), edouard@4022: ("LWORD", ("uint64_t", "L")), edouard@4022: ("REAL" , ("float" , "D")), edouard@4022: ("LREAL", ("double" , "L")) edouard@4022: ] edouard@4022: MQTT_IEC_SUPPORTED_types = list(zip(*MQTT_IEC_types_list)[0]) edouard@4022: MQTT_IEC_types = dict(MQTT_IEC_types_list) edouard@4022: edouard@4022: MQTT_JSON_SUPPORTED_types = set(MQTT_IEC_types.keys()+["STRING"]) edouard@3979: edouard@3979: """ edouard@3979: QoS - Quality of Service edouard@3979: 0 : "At most one delivery" edouard@3979: 1 : "At least one delivery" edouard@3979: 2 : "Exactly one delivery" edouard@3979: """ edouard@3979: QoS_values = [0, 1, 2] edouard@3979: edouard@3980: def boolean(v): edouard@3980: if v in ["False","0"]: edouard@3980: return False edouard@3980: else: edouard@3980: return bool(v) edouard@3980: edouard@3988: _lstcolnames = [ "Topic", "QoS", "Retained", "Type", "Location"] edouard@3988: _lstcolwidths = [ 100, 50, 100, 100, 50] edouard@3988: _lstcoltypess = [ str, int, boolean, str, int] edouard@3988: _lstcoldeflts = [ "a/b/c", "1", False, "DINT", "0"] edouard@3988: edouard@3988: subsublist = lambda l : l[0:2] + l[3:5] edouard@3988: edouard@3988: lstcoldsc = { edouard@3988: "input" : type("",(),dict( edouard@3988: lstcolnames = subsublist(_lstcolnames), edouard@3988: lstcolwidths = subsublist(_lstcolwidths), edouard@3988: lstcoltypess = subsublist(_lstcoltypess), edouard@3988: lstcoldeflts = subsublist(_lstcoldeflts), edouard@3988: Location_column = 3)), edouard@3988: "output" : type("",(),dict( edouard@3988: lstcolnames = _lstcolnames, edouard@3988: lstcolwidths = _lstcolwidths, edouard@3988: lstcoltypess = _lstcoltypess, edouard@3988: lstcoldeflts = _lstcoldeflts, edouard@3988: Location_column = 4)), edouard@3988: } edouard@3979: edouard@3979: directions = ["input", "output"] edouard@3979: edouard@4005: # expected configuration entries with internal default value edouard@3979: authParams = { edouard@3979: "x509":[ edouard@4005: ("Verify", True), edouard@4005: ("KeyStore", None), edouard@4005: ("TrustStore", None)], edouard@3979: "UserPassword":[ edouard@3979: ("User", None), edouard@3979: ("Password", None)]} edouard@3979: edouard@3980: class MQTTTopicListModel(dv.PyDataViewIndexListModel): edouard@3988: def __init__(self, data, log, direction): edouard@3979: dv.PyDataViewIndexListModel.__init__(self, len(data)) edouard@3979: self.data = data edouard@3979: self.log = log edouard@3988: self.dsc = lstcoldsc[direction] edouard@3979: edouard@3979: def GetColumnType(self, col): edouard@3979: return "string" edouard@3979: edouard@3979: def GetValueByRow(self, row, col): edouard@3979: return str(self.data[row][col]) edouard@3979: edouard@3979: # This method is called when the user edits a data item in the view. edouard@3979: def SetValueByRow(self, value, row, col): edouard@3988: expectedtype = self.dsc.lstcoltypess[col] edouard@3979: edouard@3979: try: edouard@3979: v = expectedtype(value) edouard@3979: except ValueError: edouard@3979: self.log("String {} is invalid for type {}\n".format(value,expectedtype.__name__)) edouard@3979: return False edouard@3979: edouard@3988: if col == self.dsc.lstcolnames.index("QoS") and v not in QoS_values: edouard@3979: self.log("{} is invalid for IdType\n".format(value)) edouard@3979: return False edouard@3979: edouard@3999: line = self.data[row] edouard@3999: line[col] = v edouard@3999: self.data[row] = line edouard@3979: return True edouard@3979: edouard@3979: # Report how many columns this model provides data for. edouard@3979: def GetColumnCount(self): edouard@3988: return len(self.dsc.lstcolnames) edouard@3979: edouard@3979: # Report the number of rows in the model edouard@3979: def GetCount(self): edouard@3979: #self.log.write('GetCount') edouard@3979: return len(self.data) edouard@3979: edouard@3979: # Called to check if non-standard attributes should be used in the edouard@3979: # cell at (row, col) edouard@3979: def GetAttrByRow(self, row, col, attr): edouard@3988: if col == self.dsc.Location_column: edouard@3979: attr.SetColour('blue') edouard@3979: attr.SetBold(True) edouard@3979: return True edouard@3979: return False edouard@3979: edouard@3979: edouard@3979: def DeleteRows(self, rows): edouard@3979: # make a copy since we'll be sorting(mutating) the list edouard@3979: # use reverse order so the indexes don't change as we remove items edouard@3979: rows = sorted(rows, reverse=True) edouard@3979: edouard@3979: for row in rows: edouard@3979: # remove it from our data structure edouard@3979: del self.data[row] edouard@3979: # notify the view(s) using this model that it has been removed edouard@3979: self.RowDeleted(row) edouard@3979: edouard@3979: edouard@3979: def AddRow(self, value): edouard@3979: if self.data.append(value): edouard@3979: # notify views edouard@3979: self.RowAppended() edouard@3979: edouard@3979: def InsertDefaultRow(self, row): edouard@3988: self.data.insert(row, self.dsc.lstcoldeflts[:]) edouard@3979: # notify views edouard@3979: self.RowInserted(row) edouard@3979: edouard@3979: def ResetData(self): edouard@3979: self.Reset(len(self.data)) edouard@3979: edouard@3980: class MQTTTopicListPanel(wx.Panel): edouard@4010: def __init__(self, parent, log, model, direction, types_getter): edouard@3979: self.log = log edouard@3979: wx.Panel.__init__(self, parent, -1) edouard@3979: edouard@3979: self.dvc = dv.DataViewCtrl(self, edouard@3979: style=wx.BORDER_THEME edouard@3979: | dv.DV_ROW_LINES edouard@3979: | dv.DV_HORIZ_RULES edouard@3979: | dv.DV_VERT_RULES edouard@3979: | dv.DV_MULTIPLE edouard@3979: ) edouard@3979: edouard@3979: self.model = model edouard@3979: edouard@3979: self.dvc.AssociateModel(self.model) edouard@3979: edouard@4014: self.types_getter = types_getter edouard@4014: self.direction = direction edouard@4014: self.CreateDVCColumns() edouard@3979: edouard@3979: self.Sizer = wx.BoxSizer(wx.VERTICAL) edouard@3979: edouard@3979: titlestr = direction + " variables" edouard@3979: edouard@3979: title = wx.StaticText(self, label = titlestr) edouard@3979: edouard@3979: addbt = wx.Button(self, label="Add") edouard@3979: self.Bind(wx.EVT_BUTTON, self.OnAddRow, addbt) edouard@3979: delbt = wx.Button(self, label="Delete") edouard@3979: self.Bind(wx.EVT_BUTTON, self.OnDeleteRows, delbt) edouard@3979: edouard@3979: topsizer = wx.BoxSizer(wx.HORIZONTAL) edouard@3979: topsizer.Add(title, 1, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5) edouard@3979: topsizer.Add(addbt, 0, wx.LEFT|wx.RIGHT, 5) edouard@3979: topsizer.Add(delbt, 0, wx.LEFT|wx.RIGHT, 5) edouard@3979: self.Sizer.Add(topsizer, 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5) edouard@3979: self.Sizer.Add(self.dvc, 1, wx.EXPAND) edouard@3979: edouard@3979: edouard@4014: def CreateDVCColumns(self): edouard@4014: dsc = lstcoldsc[self.direction] edouard@4014: for idx,(colname,width) in enumerate(zip(dsc.lstcolnames,dsc.lstcolwidths)): edouard@4014: if colname == "Type": edouard@4022: choice_DV_render = dv.DataViewChoiceRenderer(MQTT_IEC_SUPPORTED_types + self.types_getter()) edouard@4014: choice_DV_col = dv.DataViewColumn(colname, choice_DV_render, idx, width=width) edouard@4014: self.dvc.AppendColumn(choice_DV_col) edouard@4014: else: edouard@4014: self.dvc.AppendTextColumn(colname, idx, width=width, mode=dv.DATAVIEW_CELL_EDITABLE) edouard@4014: edouard@4014: def ResetDVCColumns(self): edouard@4014: self.dvc.ClearColumns() edouard@4014: self.CreateDVCColumns() edouard@4014: edouard@3979: def OnAddRow(self, evt): edouard@3979: items = self.dvc.GetSelections() edouard@3979: row = self.model.GetRow(items[0]) if items else 0 edouard@3979: self.model.InsertDefaultRow(row) edouard@3979: edouard@3979: def OnDeleteRows(self, evt): edouard@3979: items = self.dvc.GetSelections() edouard@3979: rows = [self.model.GetRow(item) for item in items] edouard@3979: self.model.DeleteRows(rows) edouard@3979: edouard@3979: edouard@3992: class MQTTClientPanel(wx.SplitterWindow): edouard@4010: def __init__(self, parent, modeldata, log, types_getter): edouard@3979: self.log = log edouard@3992: wx.SplitterWindow.__init__(self, parent, style=wx.SUNKEN_BORDER | wx.SP_3D) edouard@3979: edouard@3979: self.selected_datas = modeldata edouard@3988: self.selected_models = { direction:MQTTTopicListModel( edouard@3988: self.selected_datas[direction], log, direction) for direction in directions } edouard@3980: self.selected_lists = { direction:MQTTTopicListPanel( edouard@3992: self, log, edouard@4010: self.selected_models[direction], direction, types_getter) edouard@3979: for direction in directions } edouard@3979: edouard@3992: self.SplitHorizontally(*[self.selected_lists[direction] for direction in directions]+[300]) edouard@3992: edouard@3979: self.SetAutoLayout(True) edouard@3979: edouard@4014: def RefreshView(self): edouard@4014: for direction in directions: edouard@4014: self.selected_lists[direction].ResetDVCColumns() edouard@4014: edouard@3979: def OnClose(self): edouard@3979: pass edouard@3979: edouard@3979: def __del__(self): edouard@3979: self.OnClose() edouard@3979: edouard@3979: def Reset(self): edouard@3979: for direction in directions: edouard@3979: self.selected_models[direction].ResetData() edouard@3979: edouard@3979: edouard@3979: class MQTTClientList(list): edouard@3988: def __init__(self, log, change_callback, direction): edouard@3979: super(MQTTClientList, self).__init__(self) edouard@3979: self.log = log edouard@3979: self.change_callback = change_callback edouard@3988: self.dsc = lstcoldsc[direction] edouard@3979: edouard@3999: def _filter_line(self, value): edouard@3988: v = dict(list(zip(self.dsc.lstcolnames, value))) edouard@3979: edouard@3979: if type(v["Location"]) != int: edouard@3979: if len(self) == 0: edouard@3979: v["Location"] = 0 edouard@3979: else: edouard@3988: iecnums = set(zip(*self)[self.dsc.Location_column]) edouard@3979: greatest = max(iecnums) edouard@3979: holes = set(range(greatest)) - iecnums edouard@3979: v["Location"] = min(holes) if holes else greatest+1 edouard@3979: edouard@3979: try: edouard@3988: for t,n in zip(self.dsc.lstcoltypess, self.dsc.lstcolnames): edouard@3979: v[n] = t(v[n]) edouard@3979: except ValueError: edouard@3979: self.log("MQTT topic {} (Location={}) has invalid type\n".format(v["Topic"],v["Location"])) edouard@3999: return None edouard@3999: edouard@3999: if v["QoS"] not in QoS_values: edouard@3999: self.log("Unknown QoS\n".format(value)) edouard@3999: return None edouard@3979: edouard@3988: if len(self)>0 and v["Topic"] in list(zip(*self))[self.dsc.lstcolnames.index("Topic")]: edouard@3979: self.log("MQTT topic {} (Location={}) already in the list\n".format(v["Topic"],v["Location"])) edouard@3999: return None edouard@3999: edouard@3999: return [v[n] for n in self.dsc.lstcolnames] edouard@3999: edouard@3999: def insert(self, row, value): edouard@3999: v = self._filter_line(value) edouard@3999: if v is not None: edouard@3999: list.insert(self, row, v) edouard@3999: self.change_callback() edouard@3999: return True edouard@3999: return False edouard@3999: edouard@3999: def append(self, value): edouard@3999: v = self._filter_line(value) edouard@3999: if v is not None: edouard@3999: list.append(self, v) edouard@3999: self.change_callback() edouard@3999: return True edouard@3999: return False edouard@3999: edouard@3999: def __setitem__(self, index, value): edouard@3999: list.__setitem__(self, index, value) edouard@3979: self.change_callback() edouard@3979: edouard@3979: def __delitem__(self, index): edouard@3979: list.__delitem__(self, index) edouard@3979: self.change_callback() edouard@3979: edouard@3999: edouard@3979: class MQTTClientModel(dict): edouard@3979: def __init__(self, log, change_callback = lambda : None): edouard@3979: super(MQTTClientModel, self).__init__() edouard@3979: for direction in directions: edouard@3988: self[direction] = MQTTClientList(log, change_callback, direction) edouard@3979: edouard@3979: def LoadCSV(self,path): edouard@3979: with open(path, 'r') as csvfile: edouard@3979: reader = csv.reader(csvfile, delimiter=',', quotechar='"') edouard@3979: buf = {direction:[] for direction, _model in self.iteritems()} edouard@3979: for direction, model in self.iteritems(): edouard@3979: self[direction][:] = [] edouard@3979: for row in reader: edouard@3979: direction = row[0] edouard@3979: # avoids calling change callback when loading CSV edouard@3999: l = self[direction] edouard@3999: v = l._filter_line(row[1:]) edouard@3999: if v is not None: edouard@3999: list.append(l,v) edouard@3999: # TODO be verbose in case of malformed CSV edouard@3979: edouard@3979: def SaveCSV(self,path): edouard@3979: with open(path, 'w') as csvfile: edouard@3979: for direction, data in self.items(): edouard@3979: writer = csv.writer(csvfile, delimiter=',', edouard@3979: quotechar='"', quoting=csv.QUOTE_MINIMAL) edouard@3979: for row in data: edouard@3979: writer.writerow([direction] + row) edouard@3979: edouard@4012: def GenerateC(self, path, locstr, config, datatype_info_getter): edouard@3995: c_template_filepath = paths.AbsNeighbourFile(__file__, "mqtt_template.c") edouard@3995: c_template_file = open(c_template_filepath , 'rb') edouard@3995: c_template = c_template_file.read() edouard@3995: c_template_file.close() edouard@3986: edouard@4012: json_types = OD() edouard@4012: edouard@3979: formatdict = dict( edouard@3989: locstr = locstr, edouard@3989: uri = config["URI"], edouard@3989: clientID = config["clientID"], edouard@3989: decl = "", edouard@3989: topics = "", edouard@3989: cleanup = "", edouard@3989: init = "", edouard@3998: init_pubsub = "", edouard@3989: retrieve = "", edouard@3989: publish = "", edouard@4012: publish_changes = "", edouard@4012: json_decl = "" edouard@3979: ) edouard@3979: edouard@3986: edouard@3986: # Use Config's "MQTTVersion" to switch between protocol version at build time edouard@3986: if config["UseMQTT5"]: edouard@3986: formatdict["decl"] += """ edouard@3986: #define USE_MQTT_5""".format(**config) edouard@3986: edouard@3979: AuthType = config["AuthType"] edouard@3979: if AuthType == "x509": edouard@4005: for k in ["KeyStore","TrustStore"]: edouard@4005: config[k] = '"'+config[k]+'"' if config[k] else "NULL" edouard@3979: formatdict["init"] += """ edouard@4005: INIT_x509({Verify:d}, {KeyStore}, {TrustStore})""".format(**config) edouard@4005: if AuthType == "PSK": edouard@4005: formatdict["init"] += """ edouard@4005: INIT_PSK("{Secret}", "{ID}")""".format(**config) edouard@3979: elif AuthType == "UserPassword": edouard@3979: formatdict["init"] += """ edouard@3979: INIT_UserPassword("{User}", "{Password}")""".format(**config) edouard@3979: else: edouard@3979: formatdict["init"] += """ edouard@3979: INIT_NoAuth()""" edouard@3979: edouard@3990: for row in self["output"]: edouard@3990: Topic, QoS, _Retained, iec_type, iec_number = row edouard@3990: Retained = 1 if _Retained=="True" else 0 edouard@4011: if iec_type in MQTT_IEC_types: edouard@4011: C_type, iec_size_prefix = MQTT_IEC_types[iec_type] edouard@4011: c_loc_name = "__Q" + iec_size_prefix + locstr + "_" + str(iec_number) edouard@4012: encoding = "SIMPLE" edouard@4022: elif iec_type in MQTT_UNSUPPORTED_types: edouard@4022: raise Exception("Type "+iec_type+" is not supported in MQTT") edouard@4011: else: edouard@4011: C_type = iec_type.upper(); edouard@4011: c_loc_name = "__Q" + locstr + "_" + str(iec_number) edouard@4012: json_types.setdefault(iec_type,OD()).setdefault("OUTPUT",[]).append(c_loc_name) edouard@4012: encoding = "JSON" edouard@4011: edouard@3990: edouard@4022: edouard@3990: formatdict["decl"] += """ edouard@3981: DECL_VAR({iec_type}, {C_type}, {c_loc_name})""".format(**locals()) edouard@3998: formatdict["init_pubsub"] += """ edouard@4012: INIT_PUBLICATION({encoding}, {Topic}, {QoS}, {C_type}, {c_loc_name}, {Retained})""".format(**locals()) edouard@3990: formatdict["publish"] += """ edouard@3990: WRITE_VALUE({c_loc_name}, {C_type})""".format(**locals()) edouard@3990: formatdict["publish_changes"] += """ edouard@4012: PUBLISH_CHANGE({encoding}, {Topic}, {QoS}, {C_type}, {c_loc_name}, {Retained})""".format(**locals()) edouard@3990: edouard@4017: # inputs need to be sorted for bisection search edouard@3990: for row in sorted(self["input"]): edouard@3990: Topic, QoS, iec_type, iec_number = row edouard@4011: if iec_type in MQTT_IEC_types: edouard@4011: C_type, iec_size_prefix = MQTT_IEC_types[iec_type] edouard@4011: c_loc_name = "__I" + iec_size_prefix + locstr + "_" + str(iec_number) edouard@4012: init_topic_call = "INIT_TOPIC" edouard@4022: elif iec_type in MQTT_UNSUPPORTED_types: edouard@4022: raise Exception("Type "+iec_type+" is not supported in MQTT") edouard@4011: else: edouard@4011: C_type = iec_type.upper(); edouard@4011: c_loc_name = "__I" + locstr + "_" + str(iec_number) edouard@4012: init_topic_call = "INIT_JSON_TOPIC" edouard@4012: json_types.setdefault(iec_type,OD()).setdefault("INPUT",[]).append(c_loc_name) edouard@4011: edouard@3990: formatdict["decl"] += """ edouard@3990: DECL_VAR({iec_type}, {C_type}, {c_loc_name})""".format(**locals()) edouard@3990: formatdict["topics"] += """ edouard@4012: {init_topic_call}({Topic}, {iec_type}, {c_loc_name})""".format(**locals()) edouard@3998: formatdict["init_pubsub"] += """ edouard@3984: INIT_SUBSCRIPTION({Topic}, {QoS})""".format(**locals()) edouard@3990: formatdict["retrieve"] += """ edouard@3990: READ_VALUE({c_loc_name}, {C_type})""".format(**locals()) edouard@3979: edouard@4020: # collect all used type with their dependencies edouard@4020: basetypes=[] edouard@4020: arrays=set() edouard@4020: structures=set() edouard@4020: already_generated_types = set() edouard@4020: edouard@4020: def recurseJsonTypes(datatype): edouard@4020: # append derivated type first so we can expect the list edouard@4020: # to be sorted with base types in last position edouard@4012: basetypes.append(datatype) edouard@4012: infos = datatype_info_getter(datatype) edouard@4020: element_type = infos["type"] edouard@4020: if element_type == "Structure": edouard@4020: structures.add(datatype) edouard@4020: for element in infos["elements"]: edouard@4020: field_datatype = element["Type"] edouard@4022: if field_datatype not in MQTT_JSON_SUPPORTED_types and\ edouard@4022: field_datatype not in MQTT_UNSUPPORTED_types: edouard@4020: recurseJsonTypes(field_datatype) edouard@4020: elif element_type == "Array": edouard@4020: arrays.add(datatype) edouard@4020: item_datatype = infos["base_type"] edouard@4022: if item_datatype not in MQTT_JSON_SUPPORTED_types and\ edouard@4022: item_datatype not in MQTT_UNSUPPORTED_types: edouard@4020: recurseJsonTypes(item_datatype) edouard@4020: def typeCategory(iec_type): edouard@4020: if field_iec_type in arrays: edouard@4020: return "ARRAY" edouard@4020: elif field_iec_type in structures: edouard@4020: return "OBJECT" edouard@4020: return "SIMPLE" edouard@4020: edouard@4012: for iec_type,_instances in json_types.items(): edouard@4020: recurseJsonTypes(iec_type) edouard@4020: edouard@4012: # go backard to get most derivated type definition last edouard@4012: # so that CPP can always find base type deinition before edouard@4012: for iec_type in reversed(basetypes): edouard@4012: # avoid repeating type definition edouard@4020: if iec_type in already_generated_types: edouard@4012: continue edouard@4020: already_generated_types.add(iec_type) edouard@4012: edouard@4012: C_type = iec_type.upper() edouard@4012: json_decl = "#define TYPE_" + C_type + "(_P, _A) \\\n" edouard@4012: edouard@4012: infos = datatype_info_getter(iec_type) edouard@4012: edouard@4020: element_type = infos["type"] edouard@4020: if element_type == "Structure": edouard@4020: elements = infos["elements"] edouard@4020: last = len(elements) - 1 edouard@4020: for idx, element in enumerate(elements): edouard@4020: field_iec_type = element["Type"] edouard@4020: if type(field_iec_type) == tuple and field_iec_type[0] == "array": edouard@4020: raise Exception("Inline arrays in structure are not supported. Please use a separate data type for array.") edouard@4020: edouard@4020: field_C_type = field_iec_type.upper() edouard@4020: field_name = element["Name"] edouard@4020: field_C_name = field_name.upper() edouard@4020: decl_type = typeCategory(field_iec_type) edouard@4020: edouard@4020: json_decl += (" _P##_" + decl_type + "(" + edouard@4020: field_C_type + ", " + field_C_name + ", " + field_name + ", _A)" + edouard@4020: ("\n\n" if idx == last else " _P##_separator \\\n")) edouard@4020: edouard@4020: elif element_type == "Array": edouard@4020: dimensions = infos["dimensions"] edouard@4020: if len(dimensions) > 1: edouard@4020: raise Exception("Only 1 dimension arrays are supported") edouard@4020: count = int(dimensions[0][1]) - int(dimensions[0][0]) + 1 edouard@4020: field_iec_type = infos["base_type"] edouard@4020: decl_type = typeCategory(field_iec_type) edouard@4012: field_C_type = field_iec_type.upper() edouard@4020: last = count - 1 edouard@4020: for idx in range(count): edouard@4020: json_decl += (" _P##_ARRAY_" + decl_type + "(" + edouard@4020: field_C_type + ", " + repr(idx) + " , _A)" + edouard@4020: ("\n\n" if idx == last else " _P##_separator \\\n")) edouard@4012: edouard@4012: formatdict["json_decl"] += json_decl edouard@4012: edouard@4012: for iec_type, instances in json_types.items(): edouard@4012: C_type = iec_type.upper() edouard@4012: for direction, instance_list in instances.items(): edouard@4012: for c_loc_name in instance_list: edouard@4012: formatdict["json_decl"] += "DECL_JSON_"+direction+"("+C_type+", "+c_loc_name+")\n" edouard@4012: edouard@3995: Ccode = c_template.format(**formatdict) edouard@3986: edouard@3979: return Ccode edouard@3979: