]> rtime.felk.cvut.cz Git - CanFestival-3.git/blob - objdictgen/nodemanager.py
Fixed automatic SDO server creation for slave
[CanFestival-3.git] / objdictgen / nodemanager.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 #This file is part of CanFestival, a library implementing CanOpen Stack. 
5 #
6 #Copyright (C): Edouard TISSERANT, Francis DUPIN and Laurent BESSARD
7 #
8 #See COPYING file for copyrights details.
9 #
10 #This library is free software; you can redistribute it and/or
11 #modify it under the terms of the GNU Lesser General Public
12 #License as published by the Free Software Foundation; either
13 #version 2.1 of the License, or (at your option) any later version.
14 #
15 #This library is distributed in the hope that it will be useful,
16 #but WITHOUT ANY WARRANTY; without even the implied warranty of
17 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 #Lesser General Public License for more details.
19 #
20 #You should have received a copy of the GNU Lesser General Public
21 #License along with this library; if not, write to the Free Software
22 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
24 from gnosis.xml.pickle import *
25 from gnosis.xml.pickle.util import setParanoia
26 setParanoia(0)
27
28 from node import *
29 import eds_utils, gen_cfile
30
31 from types import *
32 import os, re
33
34 UndoBufferLength = 20
35
36 type_model = re.compile('([\_A-Z]*)([0-9]*)')
37 range_model = re.compile('([\_A-Z]*)([0-9]*)\[([\-0-9]*)-([\-0-9]*)\]')
38
39 # ID for the file viewed
40 CurrentID = 0
41
42 # Returns a new id
43 def GetNewId():
44     global CurrentID
45     CurrentID += 1
46     return CurrentID
47
48 """
49 Class implementing a buffer of changes made on the current editing Object Dictionary
50 """
51
52 class UndoBuffer:
53
54     """
55     Constructor initialising buffer
56     """
57     def __init__(self, currentstate, issaved = False):
58         self.Buffer = []
59         self.CurrentIndex = -1
60         self.MinIndex = -1
61         self.MaxIndex = -1
62         # if current state is defined
63         if currentstate:
64             self.CurrentIndex = 0
65             self.MinIndex = 0
66             self.MaxIndex = 0
67         # Initialising buffer with currentstate at the first place
68         for i in xrange(UndoBufferLength):
69             if i == 0:
70                 self.Buffer.append(currentstate)
71             else:
72                 self.Buffer.append(None)
73         # Initialising index of state saved
74         if issaved:
75             self.LastSave = 0
76         else:
77             self.LastSave = -1
78     
79     """
80     Add a new state in buffer
81     """
82     def Buffering(self, currentstate):
83         self.CurrentIndex = (self.CurrentIndex + 1) % UndoBufferLength
84         self.Buffer[self.CurrentIndex] = currentstate
85         # Actualising buffer limits
86         self.MaxIndex = self.CurrentIndex
87         if self.MinIndex == self.CurrentIndex:
88             # If the removed state was the state saved, there is no state saved in the buffer
89             if self.LastSave == self.MinIndex:
90                 self.LastSave = -1
91             self.MinIndex = (self.MinIndex + 1) % UndoBufferLength
92         self.MinIndex = max(self.MinIndex, 0)
93     
94     """
95     Return current state of buffer
96     """
97     def Current(self):
98         return self.Buffer[self.CurrentIndex]
99     
100     """
101     Change current state to previous in buffer and return new current state
102     """
103     def Previous(self):
104         if self.CurrentIndex != self.MinIndex:
105             self.CurrentIndex = (self.CurrentIndex - 1) % UndoBufferLength
106             return self.Buffer[self.CurrentIndex]
107         return None
108     
109     """
110     Change current state to next in buffer and return new current state
111     """
112     def Next(self):
113         if self.CurrentIndex != self.MaxIndex:
114             self.CurrentIndex = (self.CurrentIndex + 1) % UndoBufferLength
115             return self.Buffer[self.CurrentIndex]
116         return None
117     
118     """
119     Return True if current state is the first in buffer
120     """
121     def IsFirst(self):
122         return self.CurrentIndex == self.MinIndex
123     
124     """
125     Return True if current state is the last in buffer
126     """
127     def IsLast(self):
128         return self.CurrentIndex == self.MaxIndex
129
130     """
131     Note that current state is saved
132     """
133     def CurrentSaved(self):
134         self.LastSave = self.CurrentIndex
135         
136     """
137     Return True if current state is saved
138     """
139     def IsCurrentSaved(self):
140         return self.LastSave == self.CurrentIndex
141
142
143
144 """
145 Class which control the operations made on the node and answer to view requests
146 """
147
148 class NodeManager:
149
150     """
151     Constructor
152     """
153     def __init__(self):
154         self.LastNewIndex = 0
155         self.FilePaths = {}
156         self.FileNames = {}
157         self.NodeIndex = None
158         self.CurrentNode = None
159         self.UndoBuffers = {}
160
161 #-------------------------------------------------------------------------------
162 #                         Type and Map Variable Lists
163 #-------------------------------------------------------------------------------
164     
165     """
166     Return the list of types defined for the current node
167     """
168     def GetCurrentTypeList(self):
169         if self.CurrentNode:
170             return self.CurrentNode.GetTypeList()
171         else:
172             return ""
173
174     """
175     Return the list of variables that can be mapped for the current node
176     """
177     def GetCurrentMapList(self):
178         if self.CurrentNode:
179             return self.CurrentNode.GetMapList()
180         else:
181             return ""
182
183 #-------------------------------------------------------------------------------
184 #                        Create Load and Save Functions
185 #-------------------------------------------------------------------------------
186
187     """
188     Create a new node and add a new buffer for storing it
189     """
190     def CreateNewNode(self, name, id, type, description, profile, filepath, NMT, options):
191         # Create a new node
192         node = Node()
193         # Try to load profile given
194         result = self.LoadProfile(profile, filepath, node)
195         if not result:
196             # if success, initialising node
197             self.CurrentNode = node
198             self.CurrentNode.SetNodeName(name)
199             self.CurrentNode.SetNodeID(id)
200             self.CurrentNode.SetNodeType(type)
201             self.CurrentNode.SetNodeDescription(description)
202             AddIndexList = self.GetMandatoryIndexes()
203             AddSubIndexList = []
204             if NMT == "NodeGuarding":
205                 AddIndexList.extend([0x100C, 0x100D])
206             elif NMT == "Heartbeat":
207                 AddIndexList.append(0x1017)
208             for option in options:
209                 if option == "DS302":
210                     DS302Path = os.path.join(os.path.split(__file__)[0], "config/DS-302.prf")
211                     # Charging DS-302 profile if choosen by user
212                     if os.path.isfile(DS302Path):
213                         try:
214                             execfile(DS302Path)
215                             self.CurrentNode.SetDS302Profile(Mapping)
216                             self.CurrentNode.ExtendSpecificMenu(AddMenuEntries)
217                         except:
218                             return "Problem with DS-302! Syntax Error."
219                     else:
220                         return "Couldn't find DS-302 in 'config' folder!"
221                 elif option == "GenSYNC":
222                     AddIndexList.extend([0x1005, 0x1006])
223                 elif option == "Emergency":
224                     AddIndexList.append(0x1014)
225                 elif option == "SaveConfig":
226                     AddIndexList.extend([0x1010, 0x1011, 0x1020])
227                 elif option == "StoreEDS":
228                     AddIndexList.extend([0x1021, 0x1022])
229             if type == "slave":
230                 # add default SDO server
231                 AddIndexList.append(0x1200)
232                 # add default 4 receive and 4 transmit PDO
233                 for comm, mapping in [(0x1400, 0x1600),(0x1800, 0x1A00)]:
234                     firstparamindex = self.GetLineFromIndex(comm)
235                     firstmappingindex = self.GetLineFromIndex(mapping)
236                     AddIndexList.extend(range(firstparamindex, firstparamindex + 4))
237                     for idx in range(firstmappingindex, firstmappingindex + 4):
238                         AddIndexList.append(idx)
239                         AddSubIndexList.append((idx, 8))
240             # Add a new buffer 
241             index = self.AddNodeBuffer(self.CurrentNode.Copy(), False)
242             self.SetCurrentFilePath("")
243             # Add Mandatory indexes
244             self.ManageEntriesOfCurrent(AddIndexList, [])
245             for idx, num in AddSubIndexList:
246                 self.AddSubentriesToCurrent(idx, num)
247             return index
248         else:
249             return result
250     
251     """
252     Load a profile in node
253     """
254     def LoadProfile(self, profile, filepath, node):
255         if profile != "None":
256             # Try to charge the profile given
257             try:
258                 execfile(filepath)
259                 node.SetProfileName(profile)
260                 node.SetProfile(Mapping)
261                 node.SetSpecificMenu(AddMenuEntries)
262                 return None
263             except:
264                 return "Syntax Error\nBad OD Profile file!"
265         else:
266             # Default profile
267             node.SetProfileName("None")
268             node.SetProfile({})
269             node.SetSpecificMenu([])
270             return None
271
272     """
273     Open a file and store it in a new buffer
274     """
275     def OpenFileInCurrent(self, filepath):
276         try:
277             # Open and load file
278             file = open(filepath, "r")
279             node = load(file)
280             file.close()
281             self.CurrentNode = node
282             self.CurrentNode.SetNodeID(0)
283             # Add a new buffer and defining current state
284             index = self.AddNodeBuffer(self.CurrentNode.Copy(), True)
285             self.SetCurrentFilePath(filepath)
286             return index
287         except:
288             return "Unable to load file \"%s\"!"%filepath
289
290     """
291     Save current node in  a file
292     """
293     def SaveCurrentInFile(self, filepath = None):
294         # if no filepath given, verify if current node has a filepath defined
295         if not filepath:
296             filepath = self.GetCurrentFilePath()
297             if filepath == "":
298                 return False
299         # Save node in file
300         file = open(filepath, "w")
301         dump(self.CurrentNode, file)
302         file.close()
303         self.SetCurrentFilePath(filepath)
304         # Update saved state in buffer
305         self.UndoBuffers[self.NodeIndex].CurrentSaved()
306         return True
307
308     """
309     Close current state
310     """
311     def CloseCurrent(self, ignore = False):
312         # Verify if it's not forced that the current node is saved before closing it
313         if self.NodeIndex in self.UndoBuffers and (self.UndoBuffers[self.NodeIndex].IsCurrentSaved() or ignore):
314             self.RemoveNodeBuffer(self.NodeIndex)
315             if len(self.UndoBuffers) > 0:
316                 previousindexes = [idx for idx in self.UndoBuffers.keys() if idx < self.NodeIndex]
317                 nextindexes = [idx for idx in self.UndoBuffers.keys() if idx > self.NodeIndex]
318                 if len(previousindexes) > 0:
319                     previousindexes.sort()
320                     self.NodeIndex = previousindexes[-1]
321                 elif len(nextindexes) > 0:
322                     nextindexes.sort()
323                     self.NodeIndex = nextindexes[0]
324                 else:
325                     self.NodeIndex = None
326             else:
327                 self.NodeIndex = None
328             return True
329         return False
330
331     """
332     Import an eds file and store it in a new buffer if no node edited
333     """
334     def ImportCurrentFromEDSFile(self, filepath):
335         # Generate node from definition in a xml file
336         result = eds_utils.GenerateNode(filepath)
337         if isinstance(result, Node):
338             self.CurrentNode = result
339             index = self.AddNodeBuffer(self.CurrentNode.Copy(), False)
340             self.SetCurrentFilePath("")
341             return index
342         else:
343             return result
344     
345     """
346     Export to an eds file and store it in a new buffer if no node edited
347     """
348     def ExportCurrentToEDSFile(self, filepath):
349         return eds_utils.GenerateEDSFile(filepath, self.CurrentNode)
350     
351     """
352     Build the C definition of Object Dictionary for current node 
353     """
354     def ExportCurrentToCFile(self, filepath):
355         if self.CurrentNode:
356             return gen_cfile.GenerateFile(filepath, self.CurrentNode)
357
358 #-------------------------------------------------------------------------------
359 #                        Add Entries to Current Functions
360 #-------------------------------------------------------------------------------
361
362     """
363     Add the specified number of subentry for the given entry. Verify that total
364     number of subentry (except 0) doesn't exceed nbmax defined
365     """
366     def AddSubentriesToCurrent(self, index, number, node = None):
367         disable_buffer = node != None
368         if node == None:
369             node = self.CurrentNode
370         # Informations about entry
371         length = node.GetEntry(index, 0)
372         infos = node.GetEntryInfos(index)
373         subentry_infos = node.GetSubentryInfos(index, 1)
374         # Get default value for subindex
375         if "default" in subentry_infos:
376             default = subentry_infos["default"]
377         else:
378             default = self.GetTypeDefaultValue(subentry_infos["type"])   
379         # First case entry is record
380         if infos["struct"] & OD_IdenticalSubindexes: 
381             for i in xrange(1, min(number,subentry_infos["nbmax"]-length) + 1):
382                 node.AddEntry(index, length + i, default)
383             if not disable_buffer:
384                 self.BufferCurrentNode()
385             return None
386         # Second case entry is array, only possible for manufacturer specific
387         elif infos["struct"] & OD_MultipleSubindexes and 0x2000 <= index <= 0x5FFF:
388             values = {"name" : "Undefined", "type" : 5, "access" : "rw", "pdo" : True}
389             for i in xrange(1, min(number,0xFE-length) + 1):
390                 node.AddMappingEntry(index, length + i, values = values.copy())
391                 node.AddEntry(index, length + i, 0)
392             if not disable_buffer:
393                 self.BufferCurrentNode()
394             return None
395             
396
397     """
398     Remove the specified number of subentry for the given entry. Verify that total
399     number of subentry (except 0) isn't less than 1
400     """
401     def RemoveSubentriesFromCurrent(self, index, number):
402         # Informations about entry
403         infos = self.GetEntryInfos(index)
404         length = self.CurrentNode.GetEntry(index, 0)
405         if "nbmin" in infos:
406             nbmin = infos["nbmin"]
407         else:
408             nbmin = 1
409         # Entry is a record, or is an array of manufacturer specific
410         if infos["struct"] & OD_IdenticalSubindexes or 0x2000 <= index <= 0x5FFF and infos["struct"] & OD_IdenticalSubindexes:
411             for i in xrange(min(number, length - nbmin)):
412                 self.RemoveCurrentVariable(index, length - i)
413             self.BufferCurrentNode()
414
415     """
416     Add a SDO Server to current node
417     """
418     def AddSDOServerToCurrent(self):
419         # An SDO Server is already defined at index 0x1200
420         if self.CurrentNode.IsEntry(0x1200):
421             indexlist = [self.GetLineFromIndex(0x1201)]
422             if None not in indexlist:
423                 self.ManageEntriesOfCurrent(indexlist, [])
424         # Add an SDO Server at index 0x1200
425         else:
426             self.ManageEntriesOfCurrent([0x1200], [])
427         
428     """
429     Add a SDO Server to current node
430     """
431     def AddSDOClientToCurrent(self):
432         indexlist = [self.GetLineFromIndex(0x1280)]
433         if None not in indexlist:
434             self.ManageEntriesOfCurrent(indexlist, [])
435
436     """
437     Add a Transmit PDO to current node
438     """
439     def AddPDOTransmitToCurrent(self):
440         indexlist = [self.GetLineFromIndex(0x1800),self.GetLineFromIndex(0x1A00)]
441         if None not in indexlist:
442             self.ManageEntriesOfCurrent(indexlist, [])
443         
444     """
445     Add a Receive PDO to current node
446     """
447     def AddPDOReceiveToCurrent(self):
448         indexlist = [self.GetLineFromIndex(0x1400),self.GetLineFromIndex(0x1600)]
449         if None not in indexlist:
450             self.ManageEntriesOfCurrent(indexlist, [])
451
452     """
453     Add a list of entries defined in profile for menu item selected to current node
454     """
455     def AddSpecificEntryToCurrent(self, menuitem):
456         indexlist = []
457         for menu, indexes in self.CurrentNode.GetSpecificMenu():
458             if menuitem == menu:
459                 for index in indexes:
460                     indexlist.append(self.GetLineFromIndex(index))
461         if None not in indexlist:
462             self.ManageEntriesOfCurrent(indexlist, [])
463
464     """
465     Search the first index available for a pluri entry from base_index
466     """
467     def GetLineFromIndex(self, base_index):
468         found = False
469         index = base_index
470         infos = self.GetEntryInfos(base_index)
471         while index < base_index + infos["incr"]*infos["nbmax"] and not found:
472             if not self.CurrentNode.IsEntry(index):
473                 found = True
474             else:
475                 index += infos["incr"]
476         if found:
477             return index
478         return None
479     
480     """
481     Add entries specified in addinglist and remove entries specified in removinglist
482     """
483     def ManageEntriesOfCurrent(self, addinglist, removinglist, node = None):
484         disable_buffer = node != None
485         if node == None:
486             node = self.CurrentNode
487         # Add all the entries in addinglist
488         for index in addinglist:
489             infos = self.GetEntryInfos(index)
490             if infos["struct"] & OD_MultipleSubindexes:
491                 # First case entry is a record
492                 if infos["struct"] & OD_IdenticalSubindexes:
493                     subentry_infos = self.GetSubentryInfos(index, 1)
494                     if "default" in subentry_infos:
495                         default = subentry_infos["default"]
496                     else:
497                         default = self.GetTypeDefaultValue(subentry_infos["type"])
498                     node.AddEntry(index, value = [])
499                     if "nbmin" in subentry_infos:
500                         for i in xrange(subentry_infos["nbmin"]):
501                             node.AddEntry(index, i + 1, default)
502                     else:
503                         node.AddEntry(index, 1, default)
504                 # Second case entry is a record
505                 else:
506                     i = 1
507                     subentry_infos = self.GetSubentryInfos(index, i)
508                     while subentry_infos:
509                         if "default" in subentry_infos:
510                             default = subentry_infos["default"]
511                         else:
512                             default = self.GetTypeDefaultValue(subentry_infos["type"])
513                         node.AddEntry(index, i, default)
514                         i += 1
515                         subentry_infos = self.GetSubentryInfos(index, i)
516             # Third case entry is a record
517             else:
518                 subentry_infos = self.GetSubentryInfos(index, 0)
519                 if "default" in subentry_infos:
520                     default = subentry_infos["default"]
521                 else:
522                     default = self.GetTypeDefaultValue(subentry_infos["type"])
523                 node.AddEntry(index, 0, default)
524         # Remove all the entries in removinglist
525         for index in removinglist:
526             self.RemoveCurrentVariable(index)
527         if not disable_buffer:
528             self.BufferCurrentNode()
529         return None
530
531
532     """
533     Reset an subentry from current node to its default value
534     """
535     def SetCurrentEntryToDefault(self, index, subindex, node = None):
536         disable_buffer = node != None
537         if node == None:
538             node = self.CurrentNode
539         if node.IsEntry(index, subindex):
540             subentry_infos = self.GetSubentryInfos(index, subindex)
541             if "default" in subentry_infos:
542                 default = subentry_infos["default"]
543             else:
544                 default = self.GetTypeDefaultValue(subentry_infos["type"])
545             node.SetEntry(index, subindex, default)
546             if not disable_buffer:
547                 self.BufferCurrentNode()
548
549     """
550     Remove an entry from current node. Analize the index to perform the correct
551     method
552     """
553     def RemoveCurrentVariable(self, index, subIndex = None):
554         Mappings = self.CurrentNode.GetMappings()
555         if index < 0x1000 and subIndex == None:
556             type = self.CurrentNode.GetEntry(index, 1)
557             for i in Mappings[-1]:
558                 for value in Mappings[-1][i]["values"]:
559                     if value["type"] == index:
560                         value["type"] = type
561             self.CurrentNode.RemoveMappingEntry(index)
562             self.CurrentNode.RemoveEntry(index)
563         elif index == 0x1200 and subIndex == None:
564             self.CurrentNode.RemoveEntry(0x1200)
565         elif 0x1201 <= index <= 0x127F and subIndex == None:
566             self.CurrentNode.RemoveLine(index, 0x127F)
567         elif 0x1280 <= index <= 0x12FF and subIndex == None:
568             self.CurrentNode.RemoveLine(index, 0x12FF)
569         elif 0x1400 <= index <= 0x15FF or 0x1600 <= index <= 0x17FF and subIndex == None:
570             if 0x1600 <= index <= 0x17FF and subIndex == None:
571                 index -= 0x200
572             self.CurrentNode.RemoveLine(index, 0x15FF)
573             self.CurrentNode.RemoveLine(index + 0x200, 0x17FF)
574         elif 0x1800 <= index <= 0x19FF or 0x1A00 <= index <= 0x1BFF and subIndex == None:
575             if 0x1A00 <= index <= 0x1BFF:
576                 index -= 0x200
577             self.CurrentNode.RemoveLine(index, 0x19FF)
578             self.CurrentNode.RemoveLine(index + 0x200, 0x1BFF)
579         else:
580             found = False
581             for menu,list in self.CurrentNode.GetSpecificMenu():
582                 for i in list:
583                     iinfos = self.GetEntryInfos(i)
584                     indexes = [i + incr * iinfos["incr"] for incr in xrange(iinfos["nbmax"])] 
585                     if index in indexes:
586                         found = True
587                         diff = index - i
588                         for j in list:
589                             jinfos = self.GetEntryInfos(j)
590                             self.CurrentNode.RemoveLine(j + diff, j + jinfos["incr"]*jinfos["nbmax"], jinfos["incr"])
591             self.CurrentNode.RemoveMapVariable(index, subIndex)
592             if not found:
593                 infos = self.GetEntryInfos(index)
594                 if not infos["need"]:
595                     self.CurrentNode.RemoveEntry(index, subIndex)
596             if index in Mappings[-1]:
597                 self.CurrentNode.RemoveMappingEntry(index, subIndex)
598
599     def AddMapVariableToCurrent(self, index, name, struct, number, node = None):
600         if 0x2000 <= index <= 0x5FFF:
601             disable_buffer = node != None
602             if node == None:
603                 node = self.CurrentNode
604             if not node.IsEntry(index):
605                 node.AddMappingEntry(index, name = name, struct = struct)
606                 if struct == var:
607                     values = {"name" : name, "type" : 0x05, "access" : "rw", "pdo" : True}
608                     node.AddMappingEntry(index, 0, values = values)
609                     node.AddEntry(index, 0, 0)
610                 else:
611                     values = {"name" : "Number of Entries", "type" : 0x05, "access" : "ro", "pdo" : False}
612                     node.AddMappingEntry(index, 0, values = values)
613                     if struct == rec:
614                         values = {"name" : name + " %d[(sub)]", "type" : 0x05, "access" : "rw", "pdo" : True, "nbmax" : 0xFE}
615                         node.AddMappingEntry(index, 1, values = values)
616                         for i in xrange(number):
617                             node.AddEntry(index, i + 1, 0)
618                     else:
619                         for i in xrange(number):
620                             values = {"name" : "Undefined", "type" : 0x05, "access" : "rw", "pdo" : True}
621                             node.AddMappingEntry(index, i + 1, values = values)
622                             node.AddEntry(index, i + 1, 0)
623                 if not disable_buffer:
624                     self.BufferCurrentNode()
625                 return None
626             else:
627                 return "Index 0x%04X already defined!"%index
628         else:
629             return "Index 0x%04X isn't a valid index for Map Variable!"%index
630
631     def AddUserTypeToCurrent(self, type, min, max, length):
632         index = 0xA0
633         while index < 0x100 and self.CurrentNode.IsEntry(index):
634             index += 1
635         if index < 0x100:
636             customisabletypes = self.GetCustomisableTypes()
637             name, valuetype = customisabletypes[type]
638             size = self.GetEntryInfos(type)["size"]
639             default = self.GetTypeDefaultValue(type)
640             if valuetype == 0:
641                 self.CurrentNode.AddMappingEntry(index, name = "%s[%d-%d]"%(name, min, max), struct = 3, size = size, default = default)
642                 self.CurrentNode.AddMappingEntry(index, 0, values = {"name" : "Number of Entries", "type" : 0x05, "access" : "ro", "pdo" : False})
643                 self.CurrentNode.AddMappingEntry(index, 1, values = {"name" : "Type", "type" : 0x05, "access" : "ro", "pdo" : False})
644                 self.CurrentNode.AddMappingEntry(index, 2, values = {"name" : "Minimum Value", "type" : type, "access" : "ro", "pdo" : False})
645                 self.CurrentNode.AddMappingEntry(index, 3, values = {"name" : "Maximum Value", "type" : type, "access" : "ro", "pdo" : False})
646                 self.CurrentNode.AddEntry(index, 1, type)
647                 self.CurrentNode.AddEntry(index, 2, min)
648                 self.CurrentNode.AddEntry(index, 3, max)
649             elif valuetype == 1:
650                 self.CurrentNode.AddMappingEntry(index, name = "%s%d"%(name, length), struct = 3, size = length * size, default = default)
651                 self.CurrentNode.AddMappingEntry(index, 0, values = {"name" : "Number of Entries", "type" : 0x05, "access" : "ro", "pdo" : False})
652                 self.CurrentNode.AddMappingEntry(index, 1, values = {"name" : "Type", "type" : 0x05, "access" : "ro", "pdo" : False})
653                 self.CurrentNode.AddMappingEntry(index, 2, values = {"name" : "Length", "type" : 0x05, "access" : "ro", "pdo" : False})
654                 self.CurrentNode.AddEntry(index, 1, type)
655                 self.CurrentNode.AddEntry(index, 2, length)
656             self.BufferCurrentNode()
657             return None
658         else:
659             return "Too many User Types have already been defined!"
660
661 #-------------------------------------------------------------------------------
662 #                      Modify Entry and Mapping Functions
663 #-------------------------------------------------------------------------------
664
665     def SetCurrentEntryCallbacks(self, index, value):
666         if self.CurrentNode and self.CurrentNode.IsEntry(index):
667             entry_infos = self.GetEntryInfos(index)
668             if "callback" not in entry_infos:
669                 self.CurrentNode.SetParamsEntry(index, None, callback = value)
670                 self.BufferCurrentNode()
671
672     def SetCurrentEntry(self, index, subIndex, value, name, editor, node = None):
673         disable_buffer = node != None
674         if node == None:
675             node = self.CurrentNode
676         if node and node.IsEntry(index):
677             if name == "value":
678                 if editor == "map":
679                     value = node.GetMapValue(value)
680                     if value:
681                         node.SetEntry(index, subIndex, value)
682                 elif editor == "bool":
683                     value = value == "True"
684                     node.SetEntry(index, subIndex, value)
685                 elif editor == "time":
686                     node.SetEntry(index, subIndex, value)
687                 elif editor == "number":
688                     try:
689                         node.SetEntry(index, subIndex, int(value))
690                     except:
691                         pass
692                 elif editor == "float":
693                     try:
694                         node.SetEntry(index, subIndex, float(value))
695                     except:
696                         pass
697                 elif editor == "domain":
698                     try:
699                         if len(value) % 2 != 0:
700                             value = "0" + value
701                         value = value.decode('hex_codec')
702                         node.SetEntry(index, subIndex, value)
703                     except:
704                         pass
705                 elif editor == "dcf":
706                     node.SetEntry(index, subIndex, value)
707                 else:
708                     subentry_infos = self.GetSubentryInfos(index, subIndex)
709                     type = subentry_infos["type"]
710                     dic = {}
711                     for typeindex, typevalue in CustomisableTypes:
712                         dic[typeindex] = typevalue
713                     if type not in dic:
714                         type = node.GetEntry(type)[1]
715                     if dic[type] == 0:
716                         try:
717                             if value.startswith("$NODEID"):
718                                 value = "\"%s\""%value
719                             elif value.startswith("0x"):
720                                 value = int(value, 16)
721                             else:
722                                 value = int(value)
723                             node.SetEntry(index, subIndex, value)
724                         except:
725                             pass
726                     else:
727                         node.SetEntry(index, subIndex, value)
728             elif name in ["comment", "save"]:
729                 if editor == "option":
730                     value = value == "Yes"
731                 if name == "save":
732                     node.SetParamsEntry(index, subIndex, save = value)
733                 elif name == "comment":
734                     node.SetParamsEntry(index, subIndex, comment = value)
735             else:
736                 if editor == "type":
737                     value = self.GetTypeIndex(value)
738                     size = self.GetEntryInfos(value)["size"]
739                     node.UpdateMapVariable(index, subIndex, size)
740                 elif editor in ["access","raccess"]:
741                     dic = {}
742                     for abbrev,access in AccessType.iteritems():
743                         dic[access] = abbrev
744                     value = dic[value]
745                     if editor == "raccess" and not node.IsMappingEntry(index):
746                         entry_infos = self.GetEntryInfos(index)
747                         node.AddMappingEntry(index, name = entry_infos["name"], struct = 7)
748                         node.AddMappingEntry(index, 0, values = self.GetSubentryInfos(index, 0, False).copy())
749                         node.AddMappingEntry(index, 1, values = self.GetSubentryInfos(index, 1, False).copy())
750                 node.SetMappingEntry(index, subIndex, values = {name : value})
751             if not disable_buffer:
752                 self.BufferCurrentNode()
753             return None
754
755     def SetCurrentEntryName(self, index, name):
756         self.CurrentNode.SetMappingEntry(index, name=name)
757         self.BufferCurrentNode()
758
759     def SetCurrentUserType(self, index, type, min, max, length):
760         customisabletypes = self.GetCustomisableTypes()
761         values, valuetype = self.GetCustomisedTypeValues(index)
762         name, new_valuetype = customisabletypes[type]
763         size = self.GetEntryInfos(type)["size"]
764         default = self.GetTypeDefaultValue(type)
765         if new_valuetype == 0:
766             self.CurrentNode.SetMappingEntry(index, name = "%s[%d-%d]"%(name, min, max), struct = 3, size = size, default = default) 
767             if valuetype == 1:
768                 self.CurrentNode.SetMappingEntry(index, 2, values = {"name" : "Minimum Value", "type" : type, "access" : "ro", "pdo" : False})
769                 self.CurrentNode.AddMappingEntry(index, 3, values = {"name" : "Maximum Value", "type" : type, "access" : "ro", "pdo" : False})
770             self.CurrentNode.SetEntry(index, 1, type)
771             self.CurrentNode.SetEntry(index, 2, min)
772             if valuetype == 1:
773                 self.CurrentNode.AddEntry(index, 3, max)
774             else:
775                 self.CurrentNode.SetEntry(index, 3, max)
776         elif new_valuetype == 1:
777             self.CurrentNode.SetMappingEntry(index, name = "%s%d"%(name, length), struct = 3, size = size, default = default)
778             if valuetype == 0:
779                 self.CurrentNode.SetMappingEntry(index, 2, values = {"name" : "Length", "type" : 0x02, "access" : "ro", "pdo" : False})
780                 self.CurrentNode.RemoveMappingEntry(index, 3)
781             self.CurrentNode.SetEntry(index, 1, type)
782             self.CurrentNode.SetEntry(index, 2, length)
783             if valuetype == 0:
784                 self.CurrentNode.RemoveEntry(index, 3)
785         self.BufferCurrentNode()
786
787 #-------------------------------------------------------------------------------
788 #                      Current Buffering Management Functions
789 #-------------------------------------------------------------------------------
790
791     def BufferCurrentNode(self):
792         self.UndoBuffers[self.NodeIndex].Buffering(self.CurrentNode.Copy())
793
794     def CurrentIsSaved(self):
795         return self.UndoBuffers[self.NodeIndex].IsCurrentSaved()
796
797     def OneFileHasChanged(self):
798         result = False
799         for buffer in self.UndoBuffers.values():
800             result |= not buffer.IsCurrentSaved()
801         return result
802
803     def GetBufferNumber(self):
804         return len(self.UndoBuffers)
805
806     def GetBufferIndexes(self):
807         return self.UndoBuffers.keys()
808
809     def LoadCurrentPrevious(self):
810         self.CurrentNode = self.UndoBuffers[self.NodeIndex].Previous().Copy()
811     
812     def LoadCurrentNext(self):
813         self.CurrentNode = self.UndoBuffers[self.NodeIndex].Next().Copy()
814
815     def AddNodeBuffer(self, currentstate = None, issaved = False):
816         self.NodeIndex = GetNewId()
817         self.UndoBuffers[self.NodeIndex] = UndoBuffer(currentstate, issaved)
818         self.FilePaths[self.NodeIndex] = ""
819         self.FileNames[self.NodeIndex] = ""
820         return self.NodeIndex
821
822     def ChangeCurrentNode(self, index):
823         if index in self.UndoBuffers.keys():
824             self.NodeIndex = index
825             self.CurrentNode = self.UndoBuffers[self.NodeIndex].Current().Copy()
826     
827     def RemoveNodeBuffer(self, index):
828         self.UndoBuffers.pop(index)
829         self.FilePaths.pop(index)
830         self.FileNames.pop(index)
831     
832     def GetCurrentNodeIndex(self):
833         return self.NodeIndex
834     
835     def GetCurrentFilename(self):
836         return self.GetFilename(self.NodeIndex)
837     
838     def GetAllFilenames(self):
839         indexes = self.UndoBuffers.keys()
840         indexes.sort()
841         return [self.GetFilename(idx) for idx in indexes]
842     
843     def GetFilename(self, index):
844         if self.UndoBuffers[index].IsCurrentSaved():
845             return self.FileNames[index]
846         else:
847             return "~%s~"%self.FileNames[index]
848     
849     def SetCurrentFilePath(self, filepath):
850         self.FilePaths[self.NodeIndex] = filepath
851         if filepath == "":
852             self.LastNewIndex += 1
853             self.FileNames[self.NodeIndex] = "Unnamed%d"%self.LastNewIndex
854         else:
855             self.FileNames[self.NodeIndex] = os.path.splitext(os.path.basename(filepath))[0]
856                 
857     def GetCurrentFilePath(self):
858         if len(self.FilePaths) > 0:
859             return self.FilePaths[self.NodeIndex]
860         else:
861             return ""
862     
863     def GetCurrentBufferState(self):
864         first = self.UndoBuffers[self.NodeIndex].IsFirst()
865         last = self.UndoBuffers[self.NodeIndex].IsLast()
866         return not first, not last
867
868 #-------------------------------------------------------------------------------
869 #                         Profiles Management Functions
870 #-------------------------------------------------------------------------------
871
872     def GetCurrentCommunicationLists(self):
873         list = []
874         for index in MappingDictionary.iterkeys():
875             if 0x1000 <= index < 0x1200:
876                 list.append(index)
877         return self.GetProfileLists(MappingDictionary, list)
878     
879     def GetCurrentDS302Lists(self):
880         return self.GetSpecificProfileLists(self.CurrentNode.GetDS302Profile())
881     
882     def GetCurrentProfileLists(self):
883         return self.GetSpecificProfileLists(self.CurrentNode.GetProfile())
884     
885     def GetSpecificProfileLists(self, mappingdictionary):
886         validlist = []
887         exclusionlist = []
888         for name, list in self.CurrentNode.GetSpecificMenu():
889             exclusionlist.extend(list)
890         for index in mappingdictionary.iterkeys():
891             if index not in exclusionlist:
892                 validlist.append(index)
893         return self.GetProfileLists(mappingdictionary, validlist)
894     
895     def GetProfileLists(self, mappingdictionary, list):
896         dictionary = {}
897         current = []
898         for index in list:
899             dictionary[index] = (mappingdictionary[index]["name"], mappingdictionary[index]["need"])
900             if self.CurrentNode.IsEntry(index):
901                 current.append(index)
902         return dictionary, current
903
904     def GetCurrentNextMapIndex(self):
905         if self.CurrentNode:
906             index = 0x2000
907             while self.CurrentNode.IsEntry(index) and index < 0x5FFF:
908                 index += 1
909             if index < 0x6000:
910                 return index
911             else:
912                 return None
913
914     def CurrentDS302Defined(self):
915         if self.CurrentNode:
916             return len(self.CurrentNode.GetDS302Profile()) > 0
917         return False
918
919 #-------------------------------------------------------------------------------
920 #                         Node State and Values Functions
921 #-------------------------------------------------------------------------------
922     
923     def GetCurrentNodeName(self):
924         if self.CurrentNode:
925             return self.CurrentNode.GetNodeName()
926         else:
927             return ""
928
929     def GetCurrentNodeCopy(self):
930         if self.CurrentNode:
931             return self.CurrentNode.Copy()
932         else:
933             return None
934     
935     def GetCurrentNodeID(self, node = None):
936         if self.CurrentNode:
937             return self.CurrentNode.GetNodeID()
938         else:
939             return None
940
941     def GetCurrentNodeInfos(self):
942         name = self.CurrentNode.GetNodeName()
943         id = self.CurrentNode.GetNodeID()
944         type = self.CurrentNode.GetNodeType()
945         description = self.CurrentNode.GetNodeDescription()
946         return name, id, type, description
947     
948     def SetCurrentNodeInfos(self, name, id, type, description):
949         self.CurrentNode.SetNodeName(name)
950         self.CurrentNode.SetNodeID(id)
951         self.CurrentNode.SetNodeType(type)
952         self.CurrentNode.SetNodeDescription(description)
953         self.BufferCurrentNode()
954
955     def GetCurrentNodeDefaultStringSize(self):
956         if self.CurrentNode:
957             return self.CurrentNode.GetDefaultStringSize()
958         else:
959             return Node.DefaultStringSize
960     
961     def SetCurrentNodeDefaultStringSize(self, size):
962         if self.CurrentNode:
963             self.CurrentNode.SetDefaultStringSize(size)
964         else:
965             Node.DefaultStringSize = size
966
967     def GetCurrentProfileName(self):
968         if self.CurrentNode:
969             return self.CurrentNode.GetProfileName()
970         return ""
971
972     def IsCurrentEntry(self, index):
973         if self.CurrentNode:
974             return self.CurrentNode.IsEntry(index)
975         return False
976     
977     def GetCurrentEntry(self, index, subIndex = None, compute = True):
978         if self.CurrentNode:
979             return self.CurrentNode.GetEntry(index, subIndex, compute)
980         return None
981     
982     def GetCurrentParamsEntry(self, index, subIndex = None):
983         if self.CurrentNode:
984             return self.CurrentNode.GetParamsEntry(index, subIndex)
985         return None
986     
987     def GetCurrentValidIndexes(self, min, max):
988         validindexes = []
989         for index in self.CurrentNode.GetIndexes():
990             if min <= index <= max:
991                 validindexes.append((self.GetEntryName(index), index))
992         return validindexes
993         
994     def GetCurrentValidChoices(self, min, max):
995         validchoices = []
996         exclusionlist = []
997         for menu, indexes in self.CurrentNode.GetSpecificMenu():
998             exclusionlist.extend(indexes)
999             good = True
1000             for index in indexes:
1001                 good &= min <= index <= max
1002             if good:
1003                 validchoices.append((menu, None))
1004         list = [index for index in MappingDictionary.keys() if index >= 0x1000]
1005         profiles = self.CurrentNode.GetMappings(False)
1006         for profile in profiles:
1007             list.extend(profile.keys())
1008         list.sort()
1009         for index in list:
1010             if min <= index <= max and not self.CurrentNode.IsEntry(index) and index not in exclusionlist:
1011                 validchoices.append((self.GetEntryName(index), index))
1012         return validchoices
1013     
1014     def HasCurrentEntryCallbacks(self, index):
1015         if self.CurrentNode:
1016             return self.CurrentNode.HasEntryCallbacks(index)
1017         return False
1018     
1019     def GetCurrentEntryValues(self, index):
1020         if self.CurrentNode:
1021             return self.GetNodeEntryValues(self.CurrentNode, index)
1022     
1023     def GetNodeEntryValues(self, node, index):
1024         if node and node.IsEntry(index):
1025             entry_infos = node.GetEntryInfos(index)
1026             data = []
1027             editors = []
1028             values = node.GetEntry(index, compute = False)
1029             params = node.GetParamsEntry(index)
1030             if type(values) == ListType:
1031                 for i, value in enumerate(values):
1032                     data.append({"value" : value})
1033                     data[-1].update(params[i])      
1034             else:
1035                 data.append({"value" : values})
1036                 data[-1].update(params)
1037             for i, dic in enumerate(data):
1038                 infos = node.GetSubentryInfos(index, i)
1039                 dic["subindex"] = "0x%02X"%i
1040                 dic["name"] = infos["name"]
1041                 dic["type"] = node.GetTypeName(infos["type"])
1042                 dic["access"] = AccessType[infos["access"]]
1043                 dic["save"] = OptionType[dic["save"]]
1044                 editor = {"subindex" : None, "save" : "option", "callback" : "option", "comment" : "string"}
1045                 if type(values) == ListType and i == 0:
1046                     editor["name"] = None
1047                     editor["type"] = None
1048                     if 0x1600 <= index <= 0x17FF or 0x1A00 <= index <= 0x1C00:
1049                         editor["access"] = "raccess"
1050                     else:
1051                         editor["access"] = None
1052                     editor["value"] = None
1053                 else:
1054                     if infos["user_defined"]:
1055                         if entry_infos["struct"] & OD_IdenticalSubindexes:
1056                             editor["name"] = None
1057                             if i > 1:
1058                                 editor["type"] = None
1059                                 editor["access"] = None
1060                             else:
1061                                 editor["type"] = "type"
1062                                 editor["access"] = "access"
1063                         else:
1064                             if entry_infos["struct"] & OD_MultipleSubindexes:
1065                                 editor["name"] = "string"
1066                             else:
1067                                 editor["name"] = None
1068                             editor["type"] = "type"
1069                             editor["access"] = "access"
1070                     else:
1071                         editor["name"] = None
1072                         editor["type"] = None
1073                         editor["access"] = None
1074                     if index < 0x260:
1075                         editor["value"] = None
1076                         if i == 1:
1077                             dic["value"] = node.GetTypeName(dic["value"])
1078                     elif 0x1600 <= index <= 0x17FF or 0x1A00 <= index <= 0x1C00:
1079                         editor["value"] = "map"
1080                         dic["value"] = node.GetMapName(dic["value"])
1081                     else:
1082                         if dic["type"].startswith("VISIBLE_STRING") or dic["type"].startswith("OCTET_STRING"):
1083                             editor["value"] = "string"
1084                         elif dic["type"] in ["TIME_OF_DAY","TIME_DIFFERENCE"]:
1085                             editor["value"] = "time"
1086                         elif dic["type"] == "DOMAIN":
1087                             if index == 0x1F22:
1088                                 editor["value"] = "dcf"
1089                             else:
1090                                 editor["value"] = "domain"
1091                             dic["value"] = dic["value"].encode('hex_codec')
1092                         elif dic["type"] == "BOOLEAN":
1093                             editor["value"] = "bool"
1094                             dic["value"] = BoolType[dic["value"]]
1095                         result = type_model.match(dic["type"])
1096                         if result:
1097                             values = result.groups()
1098                             if values[0] == "UNSIGNED":
1099                                 try:
1100                                     format = "0x%0" + str(int(values[1])/4) + "X"
1101                                     dic["value"] = format%dic["value"]
1102                                 except:
1103                                     pass
1104                                 editor["value"] = "string"
1105                             if values[0] == "INTEGER":
1106                                 editor["value"] = "number"
1107                             elif values[0] == "REAL":
1108                                 editor["value"] = "float"
1109                             elif values[0] in ["VISIBLE_STRING", "OCTET_STRING"]:
1110                                 editor["length"] = values[0]
1111                         result = range_model.match(dic["type"])
1112                         if result:
1113                             values = result.groups()
1114                             if values[0] in ["UNSIGNED", "INTEGER", "REAL"]:
1115                                 editor["min"] = values[2]
1116                                 editor["max"] = values[3]
1117                 editors.append(editor)
1118             return data, editors
1119         else:
1120             return None
1121
1122     def AddToDCF(self, node_id, index, subindex, size, value):
1123         if self.CurrentNode.IsEntry(0x1F22, node_id):
1124             dcf_value = self.CurrentNode.GetEntry(0x1F22, node_id)
1125             if dcf_value != "":
1126                 nbparams = BE_to_LE(dcf_value[:4])
1127             else:
1128                 nbparams = 0
1129             new_value = LE_to_BE(nbparams + 1, 4) + dcf_value[4:]
1130             new_value += LE_to_BE(index, 2) + LE_to_BE(subindex, 1) + LE_to_BE(size, 4) + LE_to_BE(value, size)
1131             self.CurrentNode.SetEntry(0x1F22, node_id, new_value)
1132
1133 #-------------------------------------------------------------------------------
1134 #                         Node Informations Functions
1135 #-------------------------------------------------------------------------------
1136
1137     def GetCustomisedTypeValues(self, index):
1138         if self.CurrentNode:
1139             values = self.CurrentNode.GetEntry(index)
1140             customisabletypes = self.GetCustomisableTypes()
1141             return values, customisabletypes[values[1]][1]
1142         else:
1143             return None, None
1144
1145     def GetEntryName(self, index):
1146         if self.CurrentNode:
1147             return self.CurrentNode.GetEntryName(index)
1148         else:
1149             return FindEntryName(index, MappingDictionary)
1150     
1151     def GetEntryInfos(self, index):
1152         if self.CurrentNode:
1153             return self.CurrentNode.GetEntryInfos(index)
1154         else:
1155             return FindEntryInfos(index, MappingDictionary)
1156     
1157     def GetSubentryInfos(self, index, subindex):
1158         if self.CurrentNode:
1159             return self.CurrentNode.GetSubentryInfos(index, subindex)
1160         else:
1161             result = FindSubentryInfos(index, subindex, MappingDictionary)
1162             if result:
1163                 result["user_defined"] = False
1164             return result
1165     
1166     def GetTypeIndex(self, typename):
1167         if self.CurrentNode:
1168             return self.CurrentNode.GetTypeIndex(typename)
1169         else:
1170             return FindTypeIndex(typename, MappingDictionary)
1171     
1172     def GetTypeName(self, typeindex):
1173         if self.CurrentNode:
1174             return self.CurrentNode.GetTypeName(typeindex)
1175         else:
1176             return FindTypeName(typeindex, MappingDictionary)
1177     
1178     def GetTypeDefaultValue(self, typeindex):
1179         if self.CurrentNode:
1180             return self.CurrentNode.GetTypeDefaultValue(typeindex)
1181         else:
1182             return FindTypeDefaultValue(typeindex, MappingDictionary)
1183     
1184     def GetMapVariableList(self):
1185         if self.CurrentNode:
1186             return self.CurrentNode.GetMapVariableList()
1187         else:
1188             return []
1189
1190     def GetMandatoryIndexes(self):
1191         if self.CurrentNode:
1192             return self.CurrentNode.GetMandatoryIndexes()
1193         else:
1194             return FindMandatoryIndexes(MappingDictionary)
1195     
1196     def GetCustomisableTypes(self):
1197         dic = {}
1198         for index, valuetype in CustomisableTypes:
1199             name = self.GetTypeName(index)
1200             dic[index] = [name, valuetype]
1201         return dic
1202     
1203     def GetCurrentSpecificMenu(self):
1204         if self.CurrentNode:
1205             return self.CurrentNode.GetSpecificMenu()
1206         return []
1207