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