bacnet/BacnetSlaveEditor.py
changeset 2250 86f61c4dfe76
parent 2020 6dddf3070806
child 3303 0ffb41625592
equal deleted inserted replaced
2249:602fdd08dfab 2250:86f61c4dfe76
    21 #
    21 #
    22 # You should have received a copy of the GNU General Public License
    22 # You should have received a copy of the GNU General Public License
    23 # along with this program; if not, write to the Free Software
    23 # along with this program; if not, write to the Free Software
    24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
    24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
    25 
    25 
       
    26 from __future__ import absolute_import
       
    27 from collections import Counter
       
    28 
    26 import wx
    29 import wx
    27 from collections        import Counter
    30 
    28 from pickle             import dump
    31 # Import some libraries on Beremiz code
    29 from util.BitmapLibrary import GetBitmap
    32 from util.BitmapLibrary import GetBitmap
    30 
    33 from controls.CustomGrid import CustomGrid
    31 
    34 from controls.CustomTable import CustomTable
    32 
       
    33 # Import some libraries on Beremiz code...
       
    34 from controls.CustomGrid        import CustomGrid
       
    35 from controls.CustomTable       import CustomTable
       
    36 from editors.ConfTreeNodeEditor import ConfTreeNodeEditor
    35 from editors.ConfTreeNodeEditor import ConfTreeNodeEditor
    37 from graphics.GraphicCommons    import ERROR_HIGHLIGHT
    36 from graphics.GraphicCommons import ERROR_HIGHLIGHT
    38 
       
    39 
    37 
    40 
    38 
    41 # BACnet Engineering units taken from: ASHRAE 135-2016, clause/chapter 21
    39 # BACnet Engineering units taken from: ASHRAE 135-2016, clause/chapter 21
    42 BACnetEngineeringUnits = [ 
    40 BACnetEngineeringUnits = [
    43     ('(Acceleration) meters-per-second-per-second (166)',              166   ),
    41     ('(Acceleration) meters-per-second-per-second (166)',              166),
    44     ('(Area) square-meters (0)',                                       0     ),
    42     ('(Area) square-meters (0)',                                       0),
    45     ('(Area) square-centimeters (116)',                                116   ),
    43     ('(Area) square-centimeters (116)',                                116),
    46     ('(Area) square-feet (1)',                                         1     ),
    44     ('(Area) square-feet (1)',                                         1),
    47     ('(Area) square-inches (115)',                                     115   ),
    45     ('(Area) square-inches (115)',                                     115),
    48     ('(Currency) currency1 (105)',                                     105   ),
    46     ('(Currency) currency1 (105)',                                     105),
    49     ('(Currency) currency2 (106)',                                     106   ),
    47     ('(Currency) currency2 (106)',                                     106),
    50     ('(Currency) currency3 (107)',                                     107   ),
    48     ('(Currency) currency3 (107)',                                     107),
    51     ('(Currency) currency4 (108)',                                     108   ),
    49     ('(Currency) currency4 (108)',                                     108),
    52     ('(Currency) currency5 (109)',                                     109   ),
    50     ('(Currency) currency5 (109)',                                     109),
    53     ('(Currency) currency6 (110)',                                     110   ),
    51     ('(Currency) currency6 (110)',                                     110),
    54     ('(Currency) currency7 (111)',                                     111   ),
    52     ('(Currency) currency7 (111)',                                     111),
    55     ('(Currency) currency8 (112)',                                     112   ),
    53     ('(Currency) currency8 (112)',                                     112),
    56     ('(Currency) currency9 (113)',                                     113   ),
    54     ('(Currency) currency9 (113)',                                     113),
    57     ('(Currency) currency10 (114)',                                    114   ),
    55     ('(Currency) currency10 (114)',                                    114),
    58     ('(Electrical) milliamperes (2)',                                  2     ),
    56     ('(Electrical) milliamperes (2)',                                  2),
    59     ('(Electrical) amperes (3)',                                       3     ),
    57     ('(Electrical) amperes (3)',                                       3),
    60     ('(Electrical) amperes-per-meter (167)',                           167   ),
    58     ('(Electrical) amperes-per-meter (167)',                           167),
    61     ('(Electrical) amperes-per-square-meter (168)',                    168   ),
    59     ('(Electrical) amperes-per-square-meter (168)',                    168),
    62     ('(Electrical) ampere-square-meters (169)',                        169   ),
    60     ('(Electrical) ampere-square-meters (169)',                        169),
    63     ('(Electrical) decibels (199)',                                    199   ),
    61     ('(Electrical) decibels (199)',                                    199),
    64     ('(Electrical) decibels-millivolt (200)',                          200   ),
    62     ('(Electrical) decibels-millivolt (200)',                          200),
    65     ('(Electrical) decibels-volt (201)',                               201   ),
    63     ('(Electrical) decibels-volt (201)',                               201),
    66     ('(Electrical) farads (170)',                                      170   ),
    64     ('(Electrical) farads (170)',                                      170),
    67     ('(Electrical) henrys (171)',                                      171   ),
    65     ('(Electrical) henrys (171)',                                      171),
    68     ('(Electrical) ohms (4)',                                          4     ),
    66     ('(Electrical) ohms (4)',                                          4),
    69     ('(Electrical) ohm-meter-squared-per-meter (237)',                 237   ),
    67     ('(Electrical) ohm-meter-squared-per-meter (237)',                 237),
    70     ('(Electrical) ohm-meters (172)',                                  172   ),
    68     ('(Electrical) ohm-meters (172)',                                  172),
    71     ('(Electrical) milliohms (145)',                                   145   ),
    69     ('(Electrical) milliohms (145)',                                   145),
    72     ('(Electrical) kilohms (122)',                                     122   ),
    70     ('(Electrical) kilohms (122)',                                     122),
    73     ('(Electrical) megohms (123)',                                     123   ),
    71     ('(Electrical) megohms (123)',                                     123),
    74     ('(Electrical) microsiemens (190)',                                190   ),
    72     ('(Electrical) microsiemens (190)',                                190),
    75     ('(Electrical) millisiemens (202)',                                202   ),
    73     ('(Electrical) millisiemens (202)',                                202),
    76     ('(Electrical) siemens (173)',                                     173   ),
    74     ('(Electrical) siemens (173)',                                     173),
    77     ('(Electrical) siemens-per-meter (174)',                           174   ),
    75     ('(Electrical) siemens-per-meter (174)',                           174),
    78     ('(Electrical) teslas (175)',                                      175   ),
    76     ('(Electrical) teslas (175)',                                      175),
    79     ('(Electrical) volts (5)',                                         5     ),
    77     ('(Electrical) volts (5)',                                         5),
    80     ('(Electrical) millivolts (124)',                                  124   ),
    78     ('(Electrical) millivolts (124)',                                  124),
    81     ('(Electrical) kilovolts (6)',                                     6     ),
    79     ('(Electrical) kilovolts (6)',                                     6),
    82     ('(Electrical) megavolts (7)',                                     7     ),
    80     ('(Electrical) megavolts (7)',                                     7),
    83     ('(Electrical) volt-amperes (8)',                                  8     ),
    81     ('(Electrical) volt-amperes (8)',                                  8),
    84     ('(Electrical) kilovolt-amperes (9)',                              9     ),
    82     ('(Electrical) kilovolt-amperes (9)',                              9),
    85     ('(Electrical) megavolt-amperes (10)',                             10    ),
    83     ('(Electrical) megavolt-amperes (10)',                             10),
    86     ('(Electrical) volt-amperes-reactive (11)',                        11    ),
    84     ('(Electrical) volt-amperes-reactive (11)',                        11),
    87     ('(Electrical) kilovolt-amperes-reactive (12)',                    12    ),
    85     ('(Electrical) kilovolt-amperes-reactive (12)',                    12),
    88     ('(Electrical) megavolt-amperes-reactive (13)',                    13    ),
    86     ('(Electrical) megavolt-amperes-reactive (13)',                    13),
    89     ('(Electrical) volts-per-degree-kelvin (176)',                     176   ),
    87     ('(Electrical) volts-per-degree-kelvin (176)',                     176),
    90     ('(Electrical) volts-per-meter (177)',                             177   ),
    88     ('(Electrical) volts-per-meter (177)',                             177),
    91     ('(Electrical) degrees-phase (14)',                                14    ),
    89     ('(Electrical) degrees-phase (14)',                                14),
    92     ('(Electrical) power-factor (15)',                                 15    ),
    90     ('(Electrical) power-factor (15)',                                 15),
    93     ('(Electrical) webers (178)',                                      178   ),
    91     ('(Electrical) webers (178)',                                      178),
    94     ('(Energy) ampere-seconds (238)',                                  238   ),
    92     ('(Energy) ampere-seconds (238)',                                  238),
    95     ('(Energy) volt-ampere-hours (239)',                               239   ),
    93     ('(Energy) volt-ampere-hours (239)',                               239),
    96     ('(Energy) kilovolt-ampere-hours (240)',                           240   ),
    94     ('(Energy) kilovolt-ampere-hours (240)',                           240),
    97     ('(Energy) megavolt-ampere-hours (241)',                           241   ),
    95     ('(Energy) megavolt-ampere-hours (241)',                           241),
    98     ('(Energy) volt-ampere-hours-reactive (242)',                      242   ),
    96     ('(Energy) volt-ampere-hours-reactive (242)',                      242),
    99     ('(Energy) kilovolt-ampere-hours-reactive (243)',                  243   ),
    97     ('(Energy) kilovolt-ampere-hours-reactive (243)',                  243),
   100     ('(Energy) megavolt-ampere-hours-reactive (244)',                  244   ),
    98     ('(Energy) megavolt-ampere-hours-reactive (244)',                  244),
   101     ('(Energy) volt-square-hours (245)',                               245   ),
    99     ('(Energy) volt-square-hours (245)',                               245),
   102     ('(Energy) ampere-square-hours (246)',                             246   ),
   100     ('(Energy) ampere-square-hours (246)',                             246),
   103     ('(Energy) joules (16)',                                           16    ),
   101     ('(Energy) joules (16)',                                           16),
   104     ('(Energy) kilojoules (17)',                                       17    ),
   102     ('(Energy) kilojoules (17)',                                       17),
   105     ('(Energy) kilojoules-per-kilogram (125)',                         125   ),
   103     ('(Energy) kilojoules-per-kilogram (125)',                         125),
   106     ('(Energy) megajoules (126)',                                      126   ),
   104     ('(Energy) megajoules (126)',                                      126),
   107     ('(Energy) watt-hours (18)',                                       18    ),
   105     ('(Energy) watt-hours (18)',                                       18),
   108     ('(Energy) kilowatt-hours (19)',                                   19    ),
   106     ('(Energy) kilowatt-hours (19)',                                   19),
   109     ('(Energy) megawatt-hours (146)',                                  146   ),
   107     ('(Energy) megawatt-hours (146)',                                  146),
   110     ('(Energy) watt-hours-reactive (203)',                             203   ),
   108     ('(Energy) watt-hours-reactive (203)',                             203),
   111     ('(Energy) kilowatt-hours-reactive (204)',                         204   ),
   109     ('(Energy) kilowatt-hours-reactive (204)',                         204),
   112     ('(Energy) megawatt-hours-reactive (205)',                         205   ),
   110     ('(Energy) megawatt-hours-reactive (205)',                         205),
   113     ('(Energy) btus (20)',                                             20    ),
   111     ('(Energy) btus (20)',                                             20),
   114     ('(Energy) kilo-btus (147)',                                       147   ),
   112     ('(Energy) kilo-btus (147)',                                       147),
   115     ('(Energy) mega-btus (148)',                                       148   ),
   113     ('(Energy) mega-btus (148)',                                       148),
   116     ('(Energy) therms (21)',                                           21    ),
   114     ('(Energy) therms (21)',                                           21),
   117     ('(Energy) ton-hours (22)',                                        22    ),
   115     ('(Energy) ton-hours (22)',                                        22),
   118     ('(Enthalpy) joules-per-kilogram-dry-air (23)',                    23    ),
   116     ('(Enthalpy) joules-per-kilogram-dry-air (23)',                    23),
   119     ('(Enthalpy) kilojoules-per-kilogram-dry-air (149)',               149   ),
   117     ('(Enthalpy) kilojoules-per-kilogram-dry-air (149)',               149),
   120     ('(Enthalpy) megajoules-per-kilogram-dry-air (150)',               150   ),
   118     ('(Enthalpy) megajoules-per-kilogram-dry-air (150)',               150),
   121     ('(Enthalpy) btus-per-pound-dry-air (24)',                         24    ),
   119     ('(Enthalpy) btus-per-pound-dry-air (24)',                         24),
   122     ('(Enthalpy) btus-per-pound (117)',                                117   ),
   120     ('(Enthalpy) btus-per-pound (117)',                                117),
   123     ('(Entropy) joules-per-degree-kelvin (127)',                       127   ),
   121     ('(Entropy) joules-per-degree-kelvin (127)',                       127),
   124     ('(Entropy) kilojoules-per-degree-kelvin (151)',                   151   ),
   122     ('(Entropy) kilojoules-per-degree-kelvin (151)',                   151),
   125     ('(Entropy) megajoules-per-degree-kelvin (152)',                   152   ),
   123     ('(Entropy) megajoules-per-degree-kelvin (152)',                   152),
   126     ('(Entropy) joules-per-kilogram-degree-kelvin (128)',              128   ),
   124     ('(Entropy) joules-per-kilogram-degree-kelvin (128)',              128),
   127     ('(Force) newton (153)',                                           153   ),
   125     ('(Force) newton (153)',                                           153),
   128     ('(Frequency) cycles-per-hour (25)',                               25    ),
   126     ('(Frequency) cycles-per-hour (25)',                               25),
   129     ('(Frequency) cycles-per-minute (26)',                             26    ),
   127     ('(Frequency) cycles-per-minute (26)',                             26),
   130     ('(Frequency) hertz (27)',                                         27    ),
   128     ('(Frequency) hertz (27)',                                         27),
   131     ('(Frequency) kilohertz (129)',                                    129   ),
   129     ('(Frequency) kilohertz (129)',                                    129),
   132     ('(Frequency) megahertz (130)',                                    130   ),
   130     ('(Frequency) megahertz (130)',                                    130),
   133     ('(Frequency) per-hour (131)',                                     131   ),
   131     ('(Frequency) per-hour (131)',                                     131),
   134     ('(Humidity) grams-of-water-per-kilogram-dry-air (28)',            28    ),
   132     ('(Humidity) grams-of-water-per-kilogram-dry-air (28)',            28),
   135     ('(Humidity) percent-relative-humidity (29)',                      29    ),
   133     ('(Humidity) percent-relative-humidity (29)',                      29),
   136     ('(Length) micrometers (194)',                                     194   ),
   134     ('(Length) micrometers (194)',                                     194),
   137     ('(Length) millimeters (30)',                                      30    ),
   135     ('(Length) millimeters (30)',                                      30),
   138     ('(Length) centimeters (118)',                                     118   ),
   136     ('(Length) centimeters (118)',                                     118),
   139     ('(Length) kilometers (193)',                                      193   ),
   137     ('(Length) kilometers (193)',                                      193),
   140     ('(Length) meters (31)',                                           31    ),
   138     ('(Length) meters (31)',                                           31),
   141     ('(Length) inches (32)',                                           32    ),
   139     ('(Length) inches (32)',                                           32),
   142     ('(Length) feet (33)',                                             33    ),
   140     ('(Length) feet (33)',                                             33),
   143     ('(Light) candelas (179)',                                         179   ),
   141     ('(Light) candelas (179)',                                         179),
   144     ('(Light) candelas-per-square-meter (180)',                        180   ),
   142     ('(Light) candelas-per-square-meter (180)',                        180),
   145     ('(Light) watts-per-square-foot (34)',                             34    ),
   143     ('(Light) watts-per-square-foot (34)',                             34),
   146     ('(Light) watts-per-square-meter (35)',                            35    ),
   144     ('(Light) watts-per-square-meter (35)',                            35),
   147     ('(Light) lumens (36)',                                            36    ),
   145     ('(Light) lumens (36)',                                            36),
   148     ('(Light) luxes (37)',                                             37    ),
   146     ('(Light) luxes (37)',                                             37),
   149     ('(Light) foot-candles (38)',                                      38    ),
   147     ('(Light) foot-candles (38)',                                      38),
   150     ('(Mass) milligrams (196)',                                        196   ),
   148     ('(Mass) milligrams (196)',                                        196),
   151     ('(Mass) grams (195)',                                             195   ),
   149     ('(Mass) grams (195)',                                             195),
   152     ('(Mass) kilograms (39)',                                          39    ),
   150     ('(Mass) kilograms (39)',                                          39),
   153     ('(Mass) pounds-mass (40)',                                        40    ),
   151     ('(Mass) pounds-mass (40)',                                        40),
   154     ('(Mass) tons (41)',                                               41    ),
   152     ('(Mass) tons (41)',                                               41),
   155     ('(Mass Flow) grams-per-second (154)',                             154   ),
   153     ('(Mass Flow) grams-per-second (154)',                             154),
   156     ('(Mass Flow) grams-per-minute (155)',                             155   ),
   154     ('(Mass Flow) grams-per-minute (155)',                             155),
   157     ('(Mass Flow) kilograms-per-second (42)',                          42    ),
   155     ('(Mass Flow) kilograms-per-second (42)',                          42),
   158     ('(Mass Flow) kilograms-per-minute (43)',                          43    ),
   156     ('(Mass Flow) kilograms-per-minute (43)',                          43),
   159     ('(Mass Flow) kilograms-per-hour (44)',                            44    ),
   157     ('(Mass Flow) kilograms-per-hour (44)',                            44),
   160     ('(Mass Flow) pounds-mass-per-second (119)',                       119   ),
   158     ('(Mass Flow) pounds-mass-per-second (119)',                       119),
   161     ('(Mass Flow) pounds-mass-per-minute (45)',                        45    ),
   159     ('(Mass Flow) pounds-mass-per-minute (45)',                        45),
   162     ('(Mass Flow) pounds-mass-per-hour (46)',                          46    ),
   160     ('(Mass Flow) pounds-mass-per-hour (46)',                          46),
   163     ('(Mass Flow) tons-per-hour (156)',                                156   ),
   161     ('(Mass Flow) tons-per-hour (156)',                                156),
   164     ('(Power) milliwatts (132)',                                       132   ),
   162     ('(Power) milliwatts (132)',                                       132),
   165     ('(Power) watts (47)',                                             47    ),
   163     ('(Power) watts (47)',                                             47),
   166     ('(Power) kilowatts (48)',                                         48    ),
   164     ('(Power) kilowatts (48)',                                         48),
   167     ('(Power) megawatts (49)',                                         49    ),
   165     ('(Power) megawatts (49)',                                         49),
   168     ('(Power) btus-per-hour (50)',                                     50    ),
   166     ('(Power) btus-per-hour (50)',                                     50),
   169     ('(Power) kilo-btus-per-hour (157)',                               157   ),
   167     ('(Power) kilo-btus-per-hour (157)',                               157),
   170     ('(Power) joule-per-hours (247)',                                  247   ),
   168     ('(Power) joule-per-hours (247)',                                  247),
   171     ('(Power) horsepower (51)',                                        51    ),
   169     ('(Power) horsepower (51)',                                        51),
   172     ('(Power) tons-refrigeration (52)',                                52    ),
   170     ('(Power) tons-refrigeration (52)',                                52),
   173     ('(Pressure) pascals (53)',                                        53    ),
   171     ('(Pressure) pascals (53)',                                        53),
   174     ('(Pressure) hectopascals (133)',                                  133   ),
   172     ('(Pressure) hectopascals (133)',                                  133),
   175     ('(Pressure) kilopascals (54)',                                    54    ),
   173     ('(Pressure) kilopascals (54)',                                    54),
   176     ('(Pressure) millibars (134)',                                     134   ),
   174     ('(Pressure) millibars (134)',                                     134),
   177     ('(Pressure) bars (55)',                                           55    ),
   175     ('(Pressure) bars (55)',                                           55),
   178     ('(Pressure) pounds-force-per-square-inch (56)',                   56    ),
   176     ('(Pressure) pounds-force-per-square-inch (56)',                   56),
   179     ('(Pressure) millimeters-of-water (206)',                          206   ),
   177     ('(Pressure) millimeters-of-water (206)',                          206),
   180     ('(Pressure) centimeters-of-water (57)',                           57    ),
   178     ('(Pressure) centimeters-of-water (57)',                           57),
   181     ('(Pressure) inches-of-water (58)',                                58    ),
   179     ('(Pressure) inches-of-water (58)',                                58),
   182     ('(Pressure) millimeters-of-mercury (59)',                         59    ),
   180     ('(Pressure) millimeters-of-mercury (59)',                         59),
   183     ('(Pressure) centimeters-of-mercury (60)',                         60    ),
   181     ('(Pressure) centimeters-of-mercury (60)',                         60),
   184     ('(Pressure) inches-of-mercury (61)',                              61    ),
   182     ('(Pressure) inches-of-mercury (61)',                              61),
   185     ('(Temperature) degrees-celsius (62)',                             62    ),
   183     ('(Temperature) degrees-celsius (62)',                             62),
   186     ('(Temperature) degrees-kelvin (63)',                              63    ),
   184     ('(Temperature) degrees-kelvin (63)',                              63),
   187     ('(Temperature) degrees-kelvin-per-hour (181)',                    181   ),
   185     ('(Temperature) degrees-kelvin-per-hour (181)',                    181),
   188     ('(Temperature) degrees-kelvin-per-minute (182)',                  182   ),
   186     ('(Temperature) degrees-kelvin-per-minute (182)',                  182),
   189     ('(Temperature) degrees-fahrenheit (64)',                          64    ),
   187     ('(Temperature) degrees-fahrenheit (64)',                          64),
   190     ('(Temperature) degree-days-celsius (65)',                         65    ),
   188     ('(Temperature) degree-days-celsius (65)',                         65),
   191     ('(Temperature) degree-days-fahrenheit (66)',                      66    ),
   189     ('(Temperature) degree-days-fahrenheit (66)',                      66),
   192     ('(Temperature) delta-degrees-fahrenheit (120)',                   120   ),
   190     ('(Temperature) delta-degrees-fahrenheit (120)',                   120),
   193     ('(Temperature) delta-degrees-kelvin (121)',                       121   ),
   191     ('(Temperature) delta-degrees-kelvin (121)',                       121),
   194     ('(Time) years (67)',                                              67    ),
   192     ('(Time) years (67)',                                              67),
   195     ('(Time) months (68)',                                             68    ),
   193     ('(Time) months (68)',                                             68),
   196     ('(Time) weeks (69)',                                              69    ),
   194     ('(Time) weeks (69)',                                              69),
   197     ('(Time) days (70)',                                               70    ),
   195     ('(Time) days (70)',                                               70),
   198     ('(Time) hours (71)',                                              71    ),
   196     ('(Time) hours (71)',                                              71),
   199     ('(Time) minutes (72)',                                            72    ),
   197     ('(Time) minutes (72)',                                            72),
   200     ('(Time) seconds (73)',                                            73    ),
   198     ('(Time) seconds (73)',                                            73),
   201     ('(Time) hundredths-seconds (158)',                                158   ),
   199     ('(Time) hundredths-seconds (158)',                                158),
   202     ('(Time) milliseconds (159)',                                      159   ),
   200     ('(Time) milliseconds (159)',                                      159),
   203     ('(Torque) newton-meters (160)',                                   160   ),
   201     ('(Torque) newton-meters (160)',                                   160),
   204     ('(Velocity) millimeters-per-second (161)',                        161   ),
   202     ('(Velocity) millimeters-per-second (161)',                        161),
   205     ('(Velocity) millimeters-per-minute (162)',                        162   ),
   203     ('(Velocity) millimeters-per-minute (162)',                        162),
   206     ('(Velocity) meters-per-second (74)',                              74    ),
   204     ('(Velocity) meters-per-second (74)',                              74),
   207     ('(Velocity) meters-per-minute (163)',                             163   ),
   205     ('(Velocity) meters-per-minute (163)',                             163),
   208     ('(Velocity) meters-per-hour (164)',                               164   ), 
   206     ('(Velocity) meters-per-hour (164)',                               164),
   209     ('(Velocity) kilometers-per-hour (75)',                            75    ), 
   207     ('(Velocity) kilometers-per-hour (75)',                            75),
   210     ('(Velocity) feet-per-second (76)',                                76    ), 
   208     ('(Velocity) feet-per-second (76)',                                76),
   211     ('(Velocity) feet-per-minute (77)',                                77    ), 
   209     ('(Velocity) feet-per-minute (77)',                                77),
   212     ('(Velocity) miles-per-hour (78)',                                 78    ), 
   210     ('(Velocity) miles-per-hour (78)',                                 78),
   213     ('(Volume) cubic-feet (79)',                                       79    ), 
   211     ('(Volume) cubic-feet (79)',                                       79),
   214     ('(Volume) cubic-meters (80)',                                     80    ), 
   212     ('(Volume) cubic-meters (80)',                                     80),
   215     ('(Volume) imperial-gallons (81)',                                 81    ), 
   213     ('(Volume) imperial-gallons (81)',                                 81),
   216     ('(Volume) milliliters (197)',                                     197   ), 
   214     ('(Volume) milliliters (197)',                                     197),
   217     ('(Volume) liters (82)',                                           82    ), 
   215     ('(Volume) liters (82)',                                           82),
   218     ('(Volume) us-gallons (83)',                                       83    ), 
   216     ('(Volume) us-gallons (83)',                                       83),
   219     ('(Volumetric Flow) cubic-feet-per-second (142)',                  142   ), 
   217     ('(Volumetric Flow) cubic-feet-per-second (142)',                  142),
   220     ('(Volumetric Flow) cubic-feet-per-minute (84)',                   84    ), 
   218     ('(Volumetric Flow) cubic-feet-per-minute (84)',                   84),
   221     ('(Volumetric Flow) million-standard-cubic-feet-per-minute (254)', 254   ), 
   219     ('(Volumetric Flow) million-standard-cubic-feet-per-minute (254)', 254),
   222     ('(Volumetric Flow) cubic-feet-per-hour (191)',                    191   ), 
   220     ('(Volumetric Flow) cubic-feet-per-hour (191)',                    191),
   223     ('(Volumetric Flow) cubic-feet-per-day (248)',                     248   ), 
   221     ('(Volumetric Flow) cubic-feet-per-day (248)',                     248),
   224     ('(Volumetric Flow) standard-cubic-feet-per-day (47808)',          47808 ), 
   222     ('(Volumetric Flow) standard-cubic-feet-per-day (47808)',          47808),
   225     ('(Volumetric Flow) million-standard-cubic-feet-per-day (47809)',  47809 ), 
   223     ('(Volumetric Flow) million-standard-cubic-feet-per-day (47809)',  47809),
   226     ('(Volumetric Flow) thousand-cubic-feet-per-day (47810)',          47810 ), 
   224     ('(Volumetric Flow) thousand-cubic-feet-per-day (47810)',          47810),
   227     ('(Volumetric Flow) thousand-standard-cubic-feet-per-day (47811)', 47811 ), 
   225     ('(Volumetric Flow) thousand-standard-cubic-feet-per-day (47811)', 47811),
   228     ('(Volumetric Flow) pounds-mass-per-day (47812)',                  47812 ), 
   226     ('(Volumetric Flow) pounds-mass-per-day (47812)',                  47812),
   229     ('(Volumetric Flow) cubic-meters-per-second (85)',                 85    ), 
   227     ('(Volumetric Flow) cubic-meters-per-second (85)',                 85),
   230     ('(Volumetric Flow) cubic-meters-per-minute (165)',                165   ), 
   228     ('(Volumetric Flow) cubic-meters-per-minute (165)',                165),
   231     ('(Volumetric Flow) cubic-meters-per-hour (135)',                  135   ), 
   229     ('(Volumetric Flow) cubic-meters-per-hour (135)',                  135),
   232     ('(Volumetric Flow) cubic-meters-per-day (249)',                   249   ), 
   230     ('(Volumetric Flow) cubic-meters-per-day (249)',                   249),
   233     ('(Volumetric Flow) imperial-gallons-per-minute (86)',             86    ), 
   231     ('(Volumetric Flow) imperial-gallons-per-minute (86)',             86),
   234     ('(Volumetric Flow) milliliters-per-second (198)',                 198   ), 
   232     ('(Volumetric Flow) milliliters-per-second (198)',                 198),
   235     ('(Volumetric Flow) liters-per-second (87)',                       87    ), 
   233     ('(Volumetric Flow) liters-per-second (87)',                       87),
   236     ('(Volumetric Flow) liters-per-minute (88)',                       88    ), 
   234     ('(Volumetric Flow) liters-per-minute (88)',                       88),
   237     ('(Volumetric Flow) liters-per-hour (136)',                        136   ), 
   235     ('(Volumetric Flow) liters-per-hour (136)',                        136),
   238     ('(Volumetric Flow) us-gallons-per-minute (89)',                   89    ), 
   236     ('(Volumetric Flow) us-gallons-per-minute (89)',                   89),
   239     ('(Volumetric Flow) us-gallons-per-hour (192)',                    192   ), 
   237     ('(Volumetric Flow) us-gallons-per-hour (192)',                    192),
   240     ('(Other) degrees-angular (90)',                                   90    ), 
   238     ('(Other) degrees-angular (90)',                                   90),
   241     ('(Other) degrees-celsius-per-hour (91)',                          91    ), 
   239     ('(Other) degrees-celsius-per-hour (91)',                          91),
   242     ('(Other) degrees-celsius-per-minute (92)',                        92    ), 
   240     ('(Other) degrees-celsius-per-minute (92)',                        92),
   243     ('(Other) degrees-fahrenheit-per-hour (93)',                       93    ), 
   241     ('(Other) degrees-fahrenheit-per-hour (93)',                       93),
   244     ('(Other) degrees-fahrenheit-per-minute (94)',                     94    ), 
   242     ('(Other) degrees-fahrenheit-per-minute (94)',                     94),
   245     ('(Other) joule-seconds (183)',                                    183   ), 
   243     ('(Other) joule-seconds (183)',                                    183),
   246     ('(Other) kilograms-per-cubic-meter (186)',                        186   ), 
   244     ('(Other) kilograms-per-cubic-meter (186)',                        186),
   247     ('(Other) kilowatt-hours-per-square-meter (137)',                  137   ), 
   245     ('(Other) kilowatt-hours-per-square-meter (137)',                  137),
   248     ('(Other) kilowatt-hours-per-square-foot (138)',                   138   ), 
   246     ('(Other) kilowatt-hours-per-square-foot (138)',                   138),
   249     ('(Other) watt-hours-per-cubic-meter (250)',                       250   ), 
   247     ('(Other) watt-hours-per-cubic-meter (250)',                       250),
   250     ('(Other) joules-per-cubic-meter (251)',                           251   ), 
   248     ('(Other) joules-per-cubic-meter (251)',                           251),
   251     ('(Other) megajoules-per-square-meter (139)',                      139   ), 
   249     ('(Other) megajoules-per-square-meter (139)',                      139),
   252     ('(Other) megajoules-per-square-foot (140)',                       140   ), 
   250     ('(Other) megajoules-per-square-foot (140)',                       140),
   253     ('(Other) mole-percent (252)',                                     252   ), 
   251     ('(Other) mole-percent (252)',                                     252),
   254     ('(Other) no-units (95)',                                          95    ), 
   252     ('(Other) no-units (95)',                                          95),
   255     ('(Other) newton-seconds (187)',                                   187   ), 
   253     ('(Other) newton-seconds (187)',                                   187),
   256     ('(Other) newtons-per-meter (188)',                                188   ), 
   254     ('(Other) newtons-per-meter (188)',                                188),
   257     ('(Other) parts-per-million (96)',                                 96    ), 
   255     ('(Other) parts-per-million (96)',                                 96),
   258     ('(Other) parts-per-billion (97)',                                 97    ), 
   256     ('(Other) parts-per-billion (97)',                                 97),
   259     ('(Other) pascal-seconds (253)',                                   253   ), 
   257     ('(Other) pascal-seconds (253)',                                   253),
   260     ('(Other) percent (98)',                                           98    ), 
   258     ('(Other) percent (98)',                                           98),
   261     ('(Other) percent-obscuration-per-foot (143)',                     143   ), 
   259     ('(Other) percent-obscuration-per-foot (143)',                     143),
   262     ('(Other) percent-obscuration-per-meter (144)',                    144   ), 
   260     ('(Other) percent-obscuration-per-meter (144)',                    144),
   263     ('(Other) percent-per-second (99)',                                99    ), 
   261     ('(Other) percent-per-second (99)',                                99),
   264     ('(Other) per-minute (100)',                                       100   ), 
   262     ('(Other) per-minute (100)',                                       100),
   265     ('(Other) per-second (101)',                                       101   ), 
   263     ('(Other) per-second (101)',                                       101),
   266     ('(Other) psi-per-degree-fahrenheit (102)',                        102   ), 
   264     ('(Other) psi-per-degree-fahrenheit (102)',                        102),
   267     ('(Other) radians (103)',                                          103   ), 
   265     ('(Other) radians (103)',                                          103),
   268     ('(Other) radians-per-second (184)',                               184   ), 
   266     ('(Other) radians-per-second (184)',                               184),
   269     ('(Other) revolutions-per-minute (104)',                           104   ), 
   267     ('(Other) revolutions-per-minute (104)',                           104),
   270     ('(Other) square-meters-per-newton (185)',                         185   ), 
   268     ('(Other) square-meters-per-newton (185)',                         185),
   271     ('(Other) watts-per-meter-per-degree-kelvin (189)',                189   ), 
   269     ('(Other) watts-per-meter-per-degree-kelvin (189)',                189),
   272     ('(Other) watts-per-square-meter-degree-kelvin (141)',             141   ), 
   270     ('(Other) watts-per-square-meter-degree-kelvin (141)',             141),
   273     ('(Other) per-mille (207)',                                        207   ), 
   271     ('(Other) per-mille (207)',                                        207),
   274     ('(Other) grams-per-gram (208)',                                   208   ), 
   272     ('(Other) grams-per-gram (208)',                                   208),
   275     ('(Other) kilograms-per-kilogram (209)',                           209   ), 
   273     ('(Other) kilograms-per-kilogram (209)',                           209),
   276     ('(Other) grams-per-kilogram (210)',                               210   ), 
   274     ('(Other) grams-per-kilogram (210)',                               210),
   277     ('(Other) milligrams-per-gram (211)',                              211   ), 
   275     ('(Other) milligrams-per-gram (211)',                              211),
   278     ('(Other) milligrams-per-kilogram (212)',                          212   ), 
   276     ('(Other) milligrams-per-kilogram (212)',                          212),
   279     ('(Other) grams-per-milliliter (213)',                             213   ), 
   277     ('(Other) grams-per-milliliter (213)',                             213),
   280     ('(Other) grams-per-liter (214)',                                  214   ), 
   278     ('(Other) grams-per-liter (214)',                                  214),
   281     ('(Other) milligrams-per-liter (215)',                             215   ), 
   279     ('(Other) milligrams-per-liter (215)',                             215),
   282     ('(Other) micrograms-per-liter (216)',                             216   ), 
   280     ('(Other) micrograms-per-liter (216)',                             216),
   283     ('(Other) grams-per-cubic-meter (217)',                            217   ), 
   281     ('(Other) grams-per-cubic-meter (217)',                            217),
   284     ('(Other) milligrams-per-cubic-meter (218)',                       218   ), 
   282     ('(Other) milligrams-per-cubic-meter (218)',                       218),
   285     ('(Other) micrograms-per-cubic-meter (219)',                       219   ), 
   283     ('(Other) micrograms-per-cubic-meter (219)',                       219),
   286     ('(Other) nanograms-per-cubic-meter (220)',                        220   ), 
   284     ('(Other) nanograms-per-cubic-meter (220)',                        220),
   287     ('(Other) grams-per-cubic-centimeter (221)',                       221   ), 
   285     ('(Other) grams-per-cubic-centimeter (221)',                       221),
   288     ('(Other) becquerels (222)',                                       222   ), 
   286     ('(Other) becquerels (222)',                                       222),
   289     ('(Other) kilobecquerels (223)',                                   223   ), 
   287     ('(Other) kilobecquerels (223)',                                   223),
   290     ('(Other) megabecquerels (224)',                                   224   ), 
   288     ('(Other) megabecquerels (224)',                                   224),
   291     ('(Other) gray (225)',                                             225   ), 
   289     ('(Other) gray (225)',                                             225),
   292     ('(Other) milligray (226)',                                        226   ), 
   290     ('(Other) milligray (226)',                                        226),
   293     ('(Other) microgray (227)',                                        227   ), 
   291     ('(Other) microgray (227)',                                        227),
   294     ('(Other) sieverts (228)',                                         228   ), 
   292     ('(Other) sieverts (228)',                                         228),
   295     ('(Other) millisieverts (229)',                                    229   ), 
   293     ('(Other) millisieverts (229)',                                    229),
   296     ('(Other) microsieverts (230)',                                    230   ), 
   294     ('(Other) microsieverts (230)',                                    230),
   297     ('(Other) microsieverts-per-hour (231)',                           231   ), 
   295     ('(Other) microsieverts-per-hour (231)',                           231),
   298     ('(Other) millirems (47814)',                                      47814 ), 
   296     ('(Other) millirems (47814)',                                      47814),
   299     ('(Other) millirems-per-hour (47815)',                             47815 ), 
   297     ('(Other) millirems-per-hour (47815)',                             47815),
   300     ('(Other) decibels-a (232)',                                       232   ), 
   298     ('(Other) decibels-a (232)',                                       232),
   301     ('(Other) nephelometric-turbidity-unit (233)',                     233   ), 
   299     ('(Other) nephelometric-turbidity-unit (233)',                     233),
   302     ('(Other) pH (234)',                                               234   ), 
   300     ('(Other) pH (234)',                                               234),
   303     ('(Other) grams-per-square-meter (235)',                           235   ), 
   301     ('(Other) grams-per-square-meter (235)',                           235),
   304     ('(Other) minutes-per-degree-kelvin (236)',                        236   )  
   302     ('(Other) minutes-per-degree-kelvin (236)',                        236)
   305     ] # BACnetEngineeringUnits 
   303 ]  # BACnetEngineeringUnits
   306    
   304 
   307    
   305 
   308    
       
   309 # ObjectID (22 bits ID + 10 bits type) => max = 2^22-1 = 4194303
   306 # ObjectID (22 bits ID + 10 bits type) => max = 2^22-1 = 4194303
   310 #  However, ObjectID 4194303 is not allowed!
   307 #  However, ObjectID 4194303 is not allowed!
   311 #  4194303 is used as a special value when object Id reference is referencing an undefined object 
   308 #  4194303 is used as a special value when object Id reference is referencing an undefined object
   312 #  (similar to NULL in C)
   309 #  (similar to NULL in C)
   313 BACnetObjectID_MAX = 4194302
   310 BACnetObjectID_MAX = 4194302
   314 BACnetObjectID_NUL = 4194303
   311 BACnetObjectID_NUL = 4194303
   315 
   312 
   316 
   313 
   317 
       
   318 # A base class
   314 # A base class
   319 # what would be a purely virtual class in C++
   315 # what would be a purely virtual class in C++
   320 class ObjectProperties:
   316 class ObjectProperties(object):
   321     # this __init_() function is currently not beeing used!
   317     # this __init_() function is currently not beeing used!
       
   318 
   322     def __init__(self):
   319     def __init__(self):
   323         #nothing to do 
   320         # nothing to do
   324         return
   321         return
   325 
   322 
   326 
   323 
   327 class BinaryObject(ObjectProperties):
   324 class BinaryObject(ObjectProperties):
   328     # 'PropertyNames' will be used as the header for each column of the Object Properties grid! 
   325     # 'PropertyNames' will be used as the header for each column of the Object Properties grid!
   329     # Warning: The rest of the code depends on the existance of an "Object Identifier" and "Object Name" 
   326     # Warning: The rest of the code depends on the existance of an "Object Identifier" and "Object Name"
   330     #          Be sure to use these exact names for these BACnet object properties!
   327     # Be sure to use these exact names for these BACnet object properties!
   331     PropertyNames    = ["Object Identifier", "Object Name", "Description"]
   328     PropertyNames = ["Object Identifier", "Object Name", "Description"]
   332     ColumnAlignments = [   wx.ALIGN_RIGHT  , wx.ALIGN_LEFT, wx.ALIGN_LEFT]
   329     ColumnAlignments = [wx.ALIGN_RIGHT, wx.ALIGN_LEFT, wx.ALIGN_LEFT]
   333     ColumnSizes      = [        40         ,      80      ,      80      ]
   330     ColumnSizes = [40, 80, 80]
   334     PropertyConfig   = {"Object Identifier": {"GridCellEditor"  : wx.grid.GridCellNumberEditor,
   331     PropertyConfig = {
   335                                               "GridCellRenderer"   : wx.grid.GridCellNumberRenderer,
   332         "Object Identifier": {"GridCellEditor": wx.grid.GridCellNumberEditor,
   336                                               "GridCellEditorParam": "0,4194302" 
   333                               "GridCellRenderer": wx.grid.GridCellNumberRenderer,
   337                                                                      # syntax for GridCellNumberEditor -> "min,max" 
   334                               # syntax for GridCellNumberEditor -> "min,max"
   338                                                                      # ObjectID (22 bits ID + 10 bits type) => max = 2^22-1 
   335                               # ObjectID (22 bits ID + 10 bits type) => max = 2^22-1
   339                                              },
   336                               "GridCellEditorParam": "0,4194302"},
   340                         "Object Name"      : {"GridCellEditor"  : wx.grid.GridCellTextEditor,
   337         "Object Name": {"GridCellEditor": wx.grid.GridCellTextEditor,
   341                                               "GridCellRenderer": wx.grid.GridCellStringRenderer},  
   338                         "GridCellRenderer": wx.grid.GridCellStringRenderer},
   342                         "Description"      : {"GridCellEditor"  : wx.grid.GridCellTextEditor,
   339         "Description": {"GridCellEditor": wx.grid.GridCellTextEditor,
   343                                               "GridCellRenderer": wx.grid.GridCellStringRenderer}
   340                         "GridCellRenderer": wx.grid.GridCellStringRenderer}
   344                        }
   341     }
   345     
   342 
       
   343 
   346 class AnalogObject(ObjectProperties):
   344 class AnalogObject(ObjectProperties):
   347     # 'PropertyNames' will be used as the header for each column of the Object Properties grid! 
   345     # 'PropertyNames' will be used as the header for each column of the Object Properties grid!
   348     # Warning: The rest of the code depends on the existance of an "Object Identifier" and "Object Name" 
   346     # Warning: The rest of the code depends on the existance of an "Object Identifier" and "Object Name"
   349     #          Be sure to use these exact names for these BACnet object properties!
   347     #          Be sure to use these exact names for these BACnet object properties!
   350     #
   348     #
   351     # NOTE: Although it is not listed here (so it does not show up in the GUI, this object will also
   349     # NOTE: Although it is not listed here (so it does not show up in the GUI, this object will also
   352     #       keep another entry for a virtual property named "Unit ID". This virtual property
   350     #       keep another entry for a virtual property named "Unit ID". This virtual property
   353     #       will store the ID corresponding to the "Engineering Units" currently chosen.
   351     #       will store the ID corresponding to the "Engineering Units" currently chosen.
   354     #       This virtual property is kept synchronised to the "Engineering Units" property
   352     #       This virtual property is kept synchronised to the "Engineering Units" property
   355     #       by the function PropertyChanged() which should be called by the OnCellChange event handler.
   353     #       by the function PropertyChanged() which should be called by the OnCellChange event handler.
   356     PropertyNames    = ["Object Identifier", "Object Name", "Description", "Engineering Units"] #'Unit ID'
   354     PropertyNames = ["Object Identifier", "Object Name",
   357     ColumnAlignments = [   wx.ALIGN_RIGHT  , wx.ALIGN_LEFT, wx.ALIGN_LEFT,    wx.ALIGN_LEFT   ]
   355                      "Description", "Engineering Units"]  # 'Unit ID'
   358     ColumnSizes      = [        40         ,      80      ,      80      ,         200        ]
   356     ColumnAlignments = [
   359     PropertyConfig   = {"Object Identifier": {"GridCellEditor"     : wx.grid.GridCellNumberEditor,
   357         wx.ALIGN_RIGHT, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT]
   360                                               "GridCellRenderer"   : wx.grid.GridCellNumberRenderer,
   358     ColumnSizes = [40, 80, 80, 200]
   361                                               "GridCellEditorParam": "0,4194302"
   359     PropertyConfig = {
   362                                                                      # syntax for GridCellNumberEditor -> "min,max" 
   360         "Object Identifier": {"GridCellEditor": wx.grid.GridCellNumberEditor,
   363                                                                      # ObjectID (22 bits ID + 10 bits type) => max = 2^22-1 
   361                               "GridCellRenderer": wx.grid.GridCellNumberRenderer,
   364                                              },
   362                               "GridCellEditorParam": "0,4194302"},
   365                         "Object Name"      : {"GridCellEditor"     : wx.grid.GridCellTextEditor,
   363         "Object Name": {"GridCellEditor": wx.grid.GridCellTextEditor,
   366                                               "GridCellRenderer"   : wx.grid.GridCellStringRenderer},  
   364                         "GridCellRenderer": wx.grid.GridCellStringRenderer},
   367                         "Description"      : {"GridCellEditor"     : wx.grid.GridCellTextEditor,
   365         "Description": {"GridCellEditor": wx.grid.GridCellTextEditor,
   368                                               "GridCellRenderer"   : wx.grid.GridCellStringRenderer},  
   366                         "GridCellRenderer": wx.grid.GridCellStringRenderer},
   369                         "Engineering Units": {"GridCellEditor"     : wx.grid.GridCellChoiceEditor,
   367         "Engineering Units": {"GridCellEditor": wx.grid.GridCellChoiceEditor,
   370                                               "GridCellRenderer"   : wx.grid.GridCellStringRenderer,  
   368                               # use string renderer with choice editor!
   371                                                                      # use string renderer with choice editor!
   369                               "GridCellRenderer": wx.grid.GridCellStringRenderer,
   372                                               "GridCellEditorParam": ','.join([x[0] for x in BACnetEngineeringUnits]) 
   370                               # syntax for GridCellChoiceEditor -> comma separated values
   373                                                                      # syntax for GridCellChoiceEditor -> comma separated values 
   371                               "GridCellEditorParam": ','.join([x[0] for x in BACnetEngineeringUnits])}
   374                                               }  
   372     }
   375                        }
   373 
   376 
   374     # obj_properties should be a dictionary, with keys "Object Identifier",
   377     # obj_properties should be a dictionary, with keys "Object Identifier", "Object Name", "Description", ...
   375     # "Object Name", "Description", ...
   378     def UpdateVirtualProperties(self, obj_properties):
   376     def UpdateVirtualProperties(self, obj_properties):
   379         obj_properties["Unit ID"] = [x[1] for x in BACnetEngineeringUnits if x[0] == obj_properties["Engineering Units"]][0]
   377         obj_properties["Unit ID"] = [x[1]
   380 
   378                                      for x in BACnetEngineeringUnits if x[0] == obj_properties["Engineering Units"]][0]
   381          
   379 
   382 
   380 
   383 class MultiSObject(ObjectProperties):
   381 class MultiSObject(ObjectProperties):
   384     # 'PropertyNames' will be used as the header for each column of the Object Properties grid! 
   382     # 'PropertyNames' will be used as the header for each column of the Object Properties grid!
   385     # Warning: The rest of the code depends on the existance of an "Object Identifier" and "Object Name" 
   383     # Warning: The rest of the code depends on the existance of an "Object Identifier" and "Object Name"
   386     #          Be sure to use these exact names for these BACnet object properties!
   384     # Be sure to use these exact names for these BACnet object properties!
   387     PropertyNames    = ["Object Identifier", "Object Name", "Description", "Number of States"]
   385     PropertyNames = [
   388     ColumnAlignments = [   wx.ALIGN_RIGHT  , wx.ALIGN_LEFT, wx.ALIGN_LEFT,   wx.ALIGN_CENTER ]
   386         "Object Identifier", "Object Name", "Description", "Number of States"]
   389     ColumnSizes      = [        40         ,      80      ,      80      ,         120       ]
   387     ColumnAlignments = [
   390     PropertyConfig   = {"Object Identifier": {"GridCellEditor"     : wx.grid.GridCellNumberEditor,
   388         wx.ALIGN_RIGHT, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_CENTER]
   391                                               "GridCellRenderer"   : wx.grid.GridCellNumberRenderer,
   389     ColumnSizes = [40, 80, 80, 120]
   392                                               "GridCellEditorParam": "0,4194302"
   390     PropertyConfig = {
   393                                                                      # syntax for GridCellNumberEditor -> "min,max" 
   391         "Object Identifier": {"GridCellEditor": wx.grid.GridCellNumberEditor,
   394                                                                      # ObjectID (22 bits ID + 10 bits type) => max = 2^22-1 
   392                               "GridCellRenderer": wx.grid.GridCellNumberRenderer,
   395                                              },
   393                               "GridCellEditorParam": "0,4194302"},
   396                         "Object Name"      : {"GridCellEditor"     : wx.grid.GridCellTextEditor,
   394         "Object Name": {"GridCellEditor": wx.grid.GridCellTextEditor,
   397                                               "GridCellRenderer"   : wx.grid.GridCellStringRenderer},  
   395                         "GridCellRenderer": wx.grid.GridCellStringRenderer},
   398                         "Description"      : {"GridCellEditor"     : wx.grid.GridCellTextEditor,
   396         "Description": {"GridCellEditor": wx.grid.GridCellTextEditor,
   399                                               "GridCellRenderer"   : wx.grid.GridCellStringRenderer},  
   397                         "GridCellRenderer": wx.grid.GridCellStringRenderer},
   400                         "Number of States" : {"GridCellEditor"     : wx.grid.GridCellNumberEditor,
   398         # MultiState Values are encoded in unsigned integer
   401                                               "GridCellRenderer"   : wx.grid.GridCellNumberRenderer,
   399         # (in BACnet => uint8_t), and can not be 0.
   402                                               "GridCellEditorParam": "1,255" # syntax for GridCellNumberEditor -> "min,max" 
   400         # See ASHRAE 135-2016, section 12.20.4
   403                                                                              # MultiState Values are encoded in unsigned integer
   401         "Number of States": {"GridCellEditor": wx.grid.GridCellNumberEditor,
   404                                                                              # (in BACnet => uint8_t), and can not be 0.
   402                              "GridCellRenderer": wx.grid.GridCellNumberRenderer,
   405                                                                              # See ASHRAE 135-2016, section 12.20.4
   403                              # syntax for GridCellNumberEditor -> "min,max"
   406                                              }
   404                              "GridCellEditorParam": "1,255"}
   407                        }
   405     }
   408 
       
   409 
   406 
   410 
   407 
   411 # The default values to use for each BACnet object type
   408 # The default values to use for each BACnet object type
   412 #
   409 #
   413 # Note that the 'internal plugin parameters' get stored in the data table, but
   410 # Note that the 'internal plugin parameters' get stored in the data table, but
   414 # are not visible in the GUI. They are used to generate the 
   411 # are not visible in the GUI. They are used to generate the
   415 # EDE files as well as the C code
   412 # EDE files as well as the C code
   416 class BVObject(BinaryObject):
   413 class BVObject(BinaryObject):
   417     DefaultValues    = {"Object Identifier":"",
   414     DefaultValues = {"Object Identifier": "",
   418                         "Object Name"      :"Binary Value", 
   415                      "Object Name": "Binary Value",
   419                         "Description"      :"",
   416                      "Description": "",
   420                         # internal plugin parameters...
   417                      # internal plugin parameters...
   421                         "BACnetObjTypeID"  :5,
   418                      "BACnetObjTypeID": 5,
   422                         "Ctype"            :"uint8_t",
   419                      "Ctype": "uint8_t",
   423                         "Settable"         :"Y"
   420                      "Settable": "Y"}
   424                         }
   421 
   425 
   422 
   426 class BOObject(BinaryObject):
   423 class BOObject(BinaryObject):
   427     DefaultValues    = {"Object Identifier":"",
   424     DefaultValues = {"Object Identifier": "",
   428                         "Object Name"      :"Binary Output", 
   425                      "Object Name": "Binary Output",
   429                         "Description"      :"",
   426                      "Description": "",
   430                         # internal plugin parameters...
   427                      # internal plugin parameters...
   431                         "BACnetObjTypeID"  :4,
   428                      "BACnetObjTypeID": 4,
   432                         "Ctype"            :"uint8_t",
   429                      "Ctype": "uint8_t",
   433                         "Settable"         :"Y"
   430                      "Settable": "Y"}
   434                         }
   431 
   435 
   432 
   436 class BIObject(BinaryObject):
   433 class BIObject(BinaryObject):
   437     DefaultValues    = {"Object Identifier":"",
   434     DefaultValues = {"Object Identifier": "",
   438                         "Object Name"      :"Binary Input", 
   435                      "Object Name": "Binary Input",
   439                         "Description"      :"",
   436                      "Description": "",
   440                         # internal plugin parameters...
   437                      # internal plugin parameters...
   441                         "BACnetObjTypeID"  :3,
   438                      "BACnetObjTypeID": 3,
   442                         "Ctype"            :"uint8_t",
   439                      "Ctype": "uint8_t",
   443                         "Settable"         :"N"
   440                      "Settable": "N"}
   444                         }
   441 
   445 
   442 
   446 class AVObject(AnalogObject):
   443 class AVObject(AnalogObject):
   447     DefaultValues    = {"Object Identifier":"", 
   444     DefaultValues = {"Object Identifier": "",
   448                         "Object Name"      :"Analog Value",
   445                      "Object Name": "Analog Value",
   449                         "Description"      :"", 
   446                      "Description": "",
   450                         "Engineering Units":'(Other) no-units (95)',
   447                      "Engineering Units": '(Other) no-units (95)',
   451                         # internal plugin parameters...
   448                      # internal plugin parameters...
   452                         "Unit ID"          :95,   # the ID of the engineering unit
   449                      "Unit ID": 95,   # the ID of the engineering unit
   453                                                   # will get updated by UpdateVirtualProperties()
   450                      # will get updated by
   454                         "BACnetObjTypeID"  :2,
   451                      # UpdateVirtualProperties()
   455                         "Ctype"            :"float",
   452                      "BACnetObjTypeID": 2,
   456                         "Settable"         :"Y"
   453                      "Ctype": "float",
   457                         }
   454                      "Settable": "Y"}
       
   455 
   458 
   456 
   459 class AOObject(AnalogObject):
   457 class AOObject(AnalogObject):
   460     DefaultValues    = {"Object Identifier":"", 
   458     DefaultValues = {"Object Identifier": "",
   461                         "Object Name"      :"Analog Output",
   459                      "Object Name": "Analog Output",
   462                         "Description"      :"", 
   460                      "Description": "",
   463                         "Engineering Units":'(Other) no-units (95)',
   461                      "Engineering Units": '(Other) no-units (95)',
   464                         # internal plugin parameters...
   462                      # internal plugin parameters...
   465                         "Unit ID"          :95,   # the ID of the engineering unit
   463                      "Unit ID": 95,   # the ID of the engineering unit
   466                                                   # will get updated by UpdateVirtualProperties()
   464                      # will get updated by
   467                         "BACnetObjTypeID"  :1,
   465                      # UpdateVirtualProperties()
   468                         "Ctype"            :"float",
   466                      "BACnetObjTypeID": 1,
   469                         "Settable"         :"Y"
   467                      "Ctype": "float",
   470                         }
   468                      "Settable": "Y"}
       
   469 
   471 
   470 
   472 class AIObject(AnalogObject):
   471 class AIObject(AnalogObject):
   473     DefaultValues    = {"Object Identifier":"", 
   472     DefaultValues = {"Object Identifier": "",
   474                         "Object Name"      :"Analog Input",
   473                      "Object Name": "Analog Input",
   475                         "Description"      :"", 
   474                      "Description": "",
   476                         "Engineering Units":'(Other) no-units (95)',
   475                      "Engineering Units": '(Other) no-units (95)',
   477                         # internal plugin parameters...
   476                      # internal plugin parameters...
   478                         "Unit ID"          :95,   # the ID of the engineering unit
   477                      "Unit ID": 95,   # the ID of the engineering unit
   479                                                   # will get updated by UpdateVirtualProperties()
   478                      # will get updated by
   480                         "BACnetObjTypeID"  :0,
   479                      # UpdateVirtualProperties()
   481                         "Ctype"            :"float",
   480                      "BACnetObjTypeID": 0,
   482                         "Settable"         :"N"
   481                      "Ctype": "float",
   483                         }
   482                      "Settable": "N"}
       
   483 
   484 
   484 
   485 class MSVObject(MultiSObject):
   485 class MSVObject(MultiSObject):
   486     DefaultValues    = {"Object Identifier":"",
   486     DefaultValues = {"Object Identifier": "",
   487                         "Object Name"      :"Multi-state Value",
   487                      "Object Name": "Multi-state Value",
   488                         "Description"      :"",
   488                      "Description": "",
   489                         "Number of States" :"255",
   489                      "Number of States": "255",
   490                         # internal plugin parameters...
   490                      # internal plugin parameters...
   491                         "BACnetObjTypeID"  :19,
   491                      "BACnetObjTypeID": 19,
   492                         "Ctype"            :"uint8_t",
   492                      "Ctype": "uint8_t",
   493                         "Settable"         :"Y"
   493                      "Settable": "Y"}
   494                         }
   494 
   495 
   495 
   496 class MSOObject(MultiSObject):
   496 class MSOObject(MultiSObject):
   497     DefaultValues    = {"Object Identifier":"",
   497     DefaultValues = {"Object Identifier": "",
   498                         "Object Name"      :"Multi-state Output",
   498                      "Object Name": "Multi-state Output",
   499                         "Description"      :"",
   499                      "Description": "",
   500                         "Number of States" :"255",
   500                      "Number of States": "255",
   501                         # internal plugin parameters...
   501                      # internal plugin parameters...
   502                         "BACnetObjTypeID"  :14,
   502                      "BACnetObjTypeID": 14,
   503                         "Ctype"            :"uint8_t",
   503                      "Ctype": "uint8_t",
   504                         "Settable"         :"Y"
   504                      "Settable": "Y"}
   505                         }
   505 
   506 
   506 
   507 class MSIObject(MultiSObject):
   507 class MSIObject(MultiSObject):
   508     DefaultValues    = {"Object Identifier":"",
   508     DefaultValues = {"Object Identifier": "",
   509                         "Object Name"      :"Multi-state Input",
   509                      "Object Name": "Multi-state Input",
   510                         "Description"      :"",
   510                      "Description": "",
   511                         "Number of States" :"255",
   511                      "Number of States": "255",
   512                         # internal plugin parameters...
   512                      # internal plugin parameters...
   513                         "BACnetObjTypeID"  :13,
   513                      "BACnetObjTypeID": 13,
   514                         "Ctype"            :"uint8_t",
   514                      "Ctype": "uint8_t",
   515                         "Settable"         :"N"
   515                      "Settable": "N"}
   516                         }
       
   517 
       
   518 
       
   519 
       
   520 
       
   521 
       
   522 
       
   523 
       
   524 
       
   525 
   516 
   526 
   517 
   527 class ObjectTable(CustomTable):
   518 class ObjectTable(CustomTable):
   528     #  A custom wx.grid.PyGridTableBase using user supplied data
   519     #  A custom wx.grid.PyGridTableBase using user supplied data
   529     #  
   520     #
   530     #  This will basically store a list of BACnet objects that the slave will support/implement. 
   521     #  This will basically store a list of BACnet objects that the slave will support/implement.
   531     #  There will be one instance of this ObjectTable class for each BACnet object type 
   522     #  There will be one instance of this ObjectTable class for each BACnet object type
   532     #  (e.g. Binary Value, Analog Input, Multi State Output, ...)
   523     #  (e.g. Binary Value, Analog Input, Multi State Output, ...)
   533     #  
   524     #
   534     #  The list of BACnet objects will actually be stored within the self.data variable
   525     #  The list of BACnet objects will actually be stored within the self.data variable
   535     #  (declared in CustomTable). Self.data will be a list of dictionaries (one entry per BACnet
   526     #  (declared in CustomTable). Self.data will be a list of dictionaries (one entry per BACnet
   536     #  object). All of these dictionaries in the self.data list will have entries whose keys actually
   527     #  object). All of these dictionaries in the self.data list will have entries whose keys actually
   537     #  depend on the BACnet type object being handled. The keys in the dictionary will be 
   528     #  depend on the BACnet type object being handled. The keys in the dictionary will be
   538     #  the entries in the PropertyNames list of one of the following classes:
   529     #  the entries in the PropertyNames list of one of the following classes:
   539     #  (BVObject, BOObject, BIObject, AVObject, AOObject, AIObject, MSVObject, MSOObject, MSIObject).
   530     #  (BVObject, BOObject, BIObject, AVObject, AOObject, AIObject, MSVObject, MSOObject, MSIObject).
   540     #
   531     #
   541     #  For example, when handling Binary Value BACnet objects, 
   532     #  For example, when handling Binary Value BACnet objects,
   542     #   self.data will be a list of dictionaries (one entry per row)
   533     #   self.data will be a list of dictionaries (one entry per row)
   543     #     self.data[n] will be a dictionary, with keys "Object Identifier", "Object Name", "Description"
   534     #     self.data[n] will be a dictionary, with keys "Object Identifier", "Object Name", "Description"
   544     #     for example: self.data[n] = {"Object Identifier":33, "Object Name":"room1", "Description":"xx"} 
   535     #     for example: self.data[n] = {"Object Identifier":33, "Object Name":"room1", "Description":"xx"}
   545     #  
   536     #
   546     #  Note that this ObjectTable class merely stores the configuration data.
   537     #  Note that this ObjectTable class merely stores the configuration data.
   547     #  It does not control the display nor the editing of this data. 
   538     #  It does not control the display nor the editing of this data.
   548     #  This data is typically displayed within a grid, that is controlled by the ObjectGrid class.
   539     #  This data is typically displayed within a grid, that is controlled by the ObjectGrid class.
   549     #
   540     #
       
   541 
   550     def __init__(self, parent, data, BACnetObjectType):
   542     def __init__(self, parent, data, BACnetObjectType):
   551         #   parent          : the _BacnetSlavePlug object that is instantiating this ObjectTable
   543         #   parent          : the _BacnetSlavePlug object that is instantiating this ObjectTable
   552         #   data            : a list with the data to be shown on the grid
   544         #   data            : a list with the data to be shown on the grid
   553         #                       (i.e., a list containing the BACnet object properties)
   545         #                       (i.e., a list containing the BACnet object properties)
   554         #                       Instantiated in _BacnetSlavePlug
   546         #                       Instantiated in _BacnetSlavePlug
   558         #                     However, self.BACnetObjectType will be an instance
   550         #                     However, self.BACnetObjectType will be an instance
   559         #                     of said class as we later need to call methods from this class.
   551         #                     of said class as we later need to call methods from this class.
   560         #                     (in particular, the UpdateVirtualProperties() method)
   552         #                     (in particular, the UpdateVirtualProperties() method)
   561         #
   553         #
   562         # The base class must be initialized *first*
   554         # The base class must be initialized *first*
   563         CustomTable.__init__(self, parent, data, BACnetObjectType.PropertyNames)
   555         CustomTable.__init__(
       
   556             self, parent, data, BACnetObjectType.PropertyNames)
   564         self.BACnetObjectType = BACnetObjectType()
   557         self.BACnetObjectType = BACnetObjectType()
   565         self.ChangesToSave    = False
   558         self.ChangesToSave = False
   566 
   559 
   567     #def _GetRowEdit(self, row):
   560     # def _GetRowEdit(self, row):
   568         #row_edit = self.GetValueByName(row, "Edit")
   561         # row_edit = self.GetValueByName(row, "Edit")
   569         #var_type = self.Parent.GetTagName()
   562         # var_type = self.Parent.GetTagName()
   570         #bodytype = self.Parent.Controler.GetEditedElementBodyType(var_type)
   563         # bodytype = self.Parent.Controler.GetEditedElementBodyType(var_type)
   571         #if bodytype in ["ST", "IL"]:
   564         # if bodytype in ["ST", "IL"]:
   572             #row_edit = True;
   565         #     row_edit = True;
   573         #return row_edit
   566         # return row_edit
   574 
   567 
   575     def _updateColAttrs(self, grid):
   568     def _updateColAttrs(self, grid):
   576         #  wx.grid.Grid -> update the column attributes to add the
   569         #  wx.grid.Grid -> update the column attributes to add the
   577         #  appropriate renderer given the column name.
   570         #  appropriate renderer given the column name.
   578         #  
   571         #
   579         #  Otherwise default to the default renderer.
   572         #  Otherwise default to the default renderer.
   580         #print "ObjectTable._updateColAttrs() called!!!"
   573         # print "ObjectTable._updateColAttrs() called!!!"
   581         for row in range(self.GetNumberRows()):
   574         for row in range(self.GetNumberRows()):
   582             for col in range(self.GetNumberCols()):
   575             for col in range(self.GetNumberCols()):
   583                 PropertyName   = self.BACnetObjectType.PropertyNames[col]
   576                 PropertyName = self.BACnetObjectType.PropertyNames[col]
   584                 PropertyConfig = self.BACnetObjectType.PropertyConfig[PropertyName]
   577                 PropertyConfig = self.BACnetObjectType.PropertyConfig[PropertyName]
   585                 grid.SetReadOnly            (row, col, False)
   578                 grid.SetReadOnly(row, col, False)
   586                 grid.SetCellEditor          (row, col, PropertyConfig["GridCellEditor"]  ())
   579                 grid.SetCellEditor(row, col, PropertyConfig["GridCellEditor"]())
   587                 grid.SetCellRenderer        (row, col, PropertyConfig["GridCellRenderer"]())
   580                 grid.SetCellRenderer(row, col, PropertyConfig["GridCellRenderer"]())
   588                 grid.SetCellBackgroundColour(row, col, wx.WHITE)
   581                 grid.SetCellBackgroundColour(row, col, wx.WHITE)
   589                 grid.SetCellTextColour      (row, col, wx.BLACK)
   582                 grid.SetCellTextColour(row, col, wx.BLACK)
   590                 if "GridCellEditorParam" in PropertyConfig:
   583                 if "GridCellEditorParam" in PropertyConfig:
   591                     grid.GetCellEditor(row, col).SetParameters(PropertyConfig["GridCellEditorParam"])
   584                     grid.GetCellEditor(row, col).SetParameters(
       
   585                         PropertyConfig["GridCellEditorParam"])
   592             self.ResizeRow(grid, row)
   586             self.ResizeRow(grid, row)
   593 
   587 
   594     def FindValueByName(self, PropertyName, PropertyValue):
   588     def FindValueByName(self, PropertyName, PropertyValue):
   595         # find the row whose property named PropertyName has the value PropertyValue
   589         # find the row whose property named PropertyName has the value PropertyValue
   596         # Returns: row number
   590         # Returns: row number
   598         #              FindValueByName("Object Identifier", 1002).
   592         #              FindValueByName("Object Identifier", 1002).
   599         for row in range(self.GetNumberRows()):
   593         for row in range(self.GetNumberRows()):
   600             if int(self.GetValueByName(row, PropertyName)) == PropertyValue:
   594             if int(self.GetValueByName(row, PropertyName)) == PropertyValue:
   601                 return row
   595                 return row
   602         return None
   596         return None
   603       
   597 
   604     # Return a list containing all the values under the column named 'colname'  
   598     # Return a list containing all the values under the column named 'colname'
   605     def GetAllValuesByName(self, colname):
   599     def GetAllValuesByName(self, colname):
   606         values = []
   600         values = []
   607         for row in range(self.GetNumberRows()):
   601         for row in range(self.GetNumberRows()):
   608             values.append(self.data[row].get(colname))
   602             values.append(self.data[row].get(colname))
   609         return values
   603         return values
   610 
   604 
   611     # Returns a dictionary with:
   605     # Returns a dictionary with:
   612     #      keys: IDs    of BACnet objects
   606     #      keys: IDs    of BACnet objects
   613     #     value: number of BACnet objects using this same Id 
   607     #     value: number of BACnet objects using this same Id
   614     #            (values larger than 1 indicates an error as BACnet requires unique
   608     #            (values larger than 1 indicates an error as BACnet requires unique
   615     #             object IDs for objects of the same type)
   609     #             object IDs for objects of the same type)
   616     def GetObjectIDCount(self):
   610     def GetObjectIDCount(self):
   617         # The dictionary is built by first creating a list containing the IDs of all BACnet objects
   611         # The dictionary is built by first creating a list containing the IDs
       
   612         # of all BACnet objects
   618         ObjectIDs = self.GetAllValuesByName("Object Identifier")
   613         ObjectIDs = self.GetAllValuesByName("Object Identifier")
   619         ObjectIDs_as_int = [int(x) for x in ObjectIDs] # list of integers instead of strings...
   614         # list of integers instead of strings...
       
   615         ObjectIDs_as_int = [int(x) for x in ObjectIDs]
   620         # This list is then transformed into a collections.Counter class
   616         # This list is then transformed into a collections.Counter class
   621         # Which is then transformed into a dictionary using dict()
   617         # Which is then transformed into a dictionary using dict()
   622         return dict(Counter(ObjectIDs_as_int))
   618         return dict(Counter(ObjectIDs_as_int))
   623 
   619 
   624     # Check whether any object ID is used more than once (not valid in BACnet)
   620     # Check whether any object ID is used more than once (not valid in BACnet)
   637         if hasattr(self.BACnetObjectType, 'UpdateVirtualProperties'):
   633         if hasattr(self.BACnetObjectType, 'UpdateVirtualProperties'):
   638             for ObjProp in self.data:
   634             for ObjProp in self.data:
   639                 self.BACnetObjectType.UpdateVirtualProperties(ObjProp)
   635                 self.BACnetObjectType.UpdateVirtualProperties(ObjProp)
   640 
   636 
   641 
   637 
   642 
       
   643 class ObjectGrid(CustomGrid):
   638 class ObjectGrid(CustomGrid):
   644     # A custom wx.grid.Grid (CustomGrid derives from wx.grid.Grid)
   639     # A custom wx.grid.Grid (CustomGrid derives from wx.grid.Grid)
   645     # 
   640     #
   646     # Needed mostly to customise the initial values of newly added rows, and to 
   641     # Needed mostly to customise the initial values of newly added rows, and to
   647     # validate if the inserted data follows BACnet rules.
   642     # validate if the inserted data follows BACnet rules.
   648     #
   643     #
   649     #
   644     #
   650     # This ObjectGrid class:
   645     # This ObjectGrid class:
   651     #   Creates and controls the GUI __grid__ for configuring all the BACnet objects of one
   646     #   Creates and controls the GUI __grid__ for configuring all the BACnet objects of one
   654     #   object (this organization is not likely to change in the future).
   649     #   object (this organization is not likely to change in the future).
   655     #
   650     #
   656     #   The grid uses one line/row per BACnet object, and one column for each property of the BACnet
   651     #   The grid uses one line/row per BACnet object, and one column for each property of the BACnet
   657     #   object. The column titles change depending on the specific type of BACnet object being edited
   652     #   object. The column titles change depending on the specific type of BACnet object being edited
   658     #   (BVObject, BOObject, BIObject, AVObject, AOObject, AIObject, MSVObject, MSOObject, MSIObject).
   653     #   (BVObject, BOObject, BIObject, AVObject, AOObject, AIObject, MSVObject, MSOObject, MSIObject).
   659     #   The editor to use for each column is also obtained from that class (e.g. TextEditor, 
   654     #   The editor to use for each column is also obtained from that class (e.g. TextEditor,
   660     #   NumberEditor, ...)
   655     #   NumberEditor, ...)
   661     #
   656     #
   662     #   This class does NOT store the data in the grid. It merely controls its display and editing.
   657     #   This class does NOT store the data in the grid. It merely controls its display and editing.
   663     #   The data in the grid is stored within an object of class ObjectTable
   658     #   The data in the grid is stored within an object of class ObjectTable
   664     #
   659     #
       
   660 
   665     def __init__(self, *args, **kwargs):
   661     def __init__(self, *args, **kwargs):
   666         CustomGrid.__init__(self, *args, **kwargs)
   662         CustomGrid.__init__(self, *args, **kwargs)
   667 
   663 
   668     # Called when a new row is added by clicking Add button
   664     # Called when a new row is added by clicking Add button
   669     #    call graph: CustomGrid.OnAddButton() --> CustomGrid.AddRow() --> ObjectGrid._AddRow()
   665     # call graph: CustomGrid.OnAddButton() --> CustomGrid.AddRow() -->
       
   666     # ObjectGrid._AddRow()
   670     def _AddRow(self, new_row):
   667     def _AddRow(self, new_row):
   671         if new_row > 0:
   668         if new_row > 0:
   672             self.Table.InsertRow(new_row, self.Table.GetRow(new_row - 1).copy())
   669             self.Table.InsertRow(new_row, self.Table.GetRow(new_row - 1).copy())
   673         else:
   670         else:
   674             self.Table.InsertRow(new_row, self.DefaultValue.copy())
   671             self.Table.InsertRow(new_row, self.DefaultValue.copy())
   675         self.Table.SetValueByName(new_row, "Object Identifier", BACnetObjectID_NUL) # start off with invalid object ID
   672         # start off with invalid object ID
       
   673         self.Table.SetValueByName(new_row, "Object Identifier", BACnetObjectID_NUL)
   676         # Find an apropriate BACnet object ID for the new object.
   674         # Find an apropriate BACnet object ID for the new object.
   677         # We choose a first attempt (based on object ID of previous line + 1)
   675         # We choose a first attempt (based on object ID of previous line + 1)
   678         new_object_id = 0
   676         new_object_id = 0
   679         if new_row > 0:
   677         if new_row > 0:
   680             new_object_id = int(self.Table.GetValueByName(new_row - 1, "Object Identifier"))
   678             new_object_id = int(
       
   679                 self.Table.GetValueByName(new_row - 1, "Object Identifier"))
   681             new_object_id += 1
   680             new_object_id += 1
   682         # Check whether the chosen object ID is not already in use. 
   681         # Check whether the chosen object ID is not already in use.
   683         # If in use, add 1 to the attempted object ID and recheck...
   682         # If in use, add 1 to the attempted object ID and recheck...
   684         while self.Table.FindValueByName("Object Identifier", new_object_id) is not None:
   683         while self.Table.FindValueByName("Object Identifier", new_object_id) is not None:
   685             new_object_id += 1
   684             new_object_id += 1
   686             # if reached end of object IDs, cycle back to 0 
   685             # if reached end of object IDs, cycle back to 0
   687             # (remember, we may have started at any inital object ID > 0, so it makes sense to cyclce back to 0)
   686             # (remember, we may have started at any inital object ID > 0, so it makes sense to cyclce back to 0)
   688             # warning: We risk entering an inifinite loop if all object IDs are already used. 
   687             # warning: We risk entering an inifinite loop if all object IDs are already used.
   689             #          The likelyhood of this happening is extremely low, (we would need 2^22 elements in the table!)
   688             #          The likelyhood of this happening is extremely low, (we would need 2^22 elements in the table!)
   690             #          so don't worry about it for now.
   689             #          so don't worry about it for now.
   691             if new_object_id > BACnetObjectID_MAX:
   690             if new_object_id > BACnetObjectID_MAX:
   692                 new_object_id = 0
   691                 new_object_id = 0
   693         # Set the object ID of the new object to the apropriate value
   692         # Set the object ID of the new object to the apropriate value
   694         # ... and append the ID to the default object name (so the name becomes unique)
   693         # ... and append the ID to the default object name (so the name becomes unique)
   695         new_object_name = self.DefaultValue.get("Object Name") + " " + str(new_object_id)
   694         new_object_name = self.DefaultValue.get(
   696         self.Table.SetValueByName(new_row, "Object Name"      , new_object_name)        
   695             "Object Name") + " " + str(new_object_id)
       
   696         self.Table.SetValueByName(
       
   697             new_row, "Object Name", new_object_name)
   697         self.Table.SetValueByName(new_row, "Object Identifier", new_object_id)
   698         self.Table.SetValueByName(new_row, "Object Identifier", new_object_id)
   698         self.Table.ResetView(self)
   699         self.Table.ResetView(self)
   699         return new_row
   700         return new_row
   700 
       
   701 
   701 
   702     # Called when a object ID is changed
   702     # Called when a object ID is changed
   703     #    call graph: ObjectEditor.OnVariablesGridCellChange() --> this method
   703     #    call graph: ObjectEditor.OnVariablesGridCellChange() --> this method
   704     # Will check whether there is a duplicate object ID, and highlight it if so.
   704     # Will check whether there is a duplicate object ID, and highlight it if so.
   705     def HighlightDuplicateObjectIDs(self):
   705     def HighlightDuplicateObjectIDs(self):
   706         if self.Table.GetNumberRows() < 2:
   706         if self.Table.GetNumberRows() < 2:
   707             # Less than 2 rows. No duplicates are possible! 
   707             # Less than 2 rows. No duplicates are possible!
   708             return
   708             return
   709         IDsCount = self.Table.GetObjectIDCount()
   709         IDsCount = self.Table.GetObjectIDCount()
   710         # check ALL object IDs for duplicates...
   710         # check ALL object IDs for duplicates...
   711         for row in range(self.Table.GetNumberRows()):
   711         for row in range(self.Table.GetNumberRows()):
   712             obj_id1 = int(self.Table.GetValueByName(row, "Object Identifier"))
   712             obj_id1 = int(self.Table.GetValueByName(row, "Object Identifier"))
   713             if IDsCount[obj_id1] > 1:
   713             if IDsCount[obj_id1] > 1:
   714                 # More than 1 BACnet object using this ID! Let us Highlight this row with errors...            
   714                 # More than 1 BACnet object using this ID! Let us Highlight this row with errors...
   715                 # TODO: change the hardcoded column number '0' to a number obtained at runtime
   715                 # TODO: change the hardcoded column number '0' to a number obtained at runtime
   716                 #       that is guaranteed to match the column titled "Object Identifier"
   716                 #       that is guaranteed to match the column titled "Object Identifier"
   717                 self.SetCellBackgroundColour(row, 0, ERROR_HIGHLIGHT[0])
   717                 self.SetCellBackgroundColour(row, 0, ERROR_HIGHLIGHT[0])
   718                 self.SetCellTextColour      (row, 0, ERROR_HIGHLIGHT[1])
   718                 self.SetCellTextColour(row, 0, ERROR_HIGHLIGHT[1])
   719             else: 
   719             else:
   720                 self.SetCellBackgroundColour(row, 0, wx.WHITE)
   720                 self.SetCellBackgroundColour(row, 0, wx.WHITE)
   721                 self.SetCellTextColour      (row, 0, wx.BLACK)
   721                 self.SetCellTextColour(row, 0, wx.BLACK)
   722         # Refresh the graphical display to take into account any changes we may have made
   722         # Refresh the graphical display to take into account any changes we may
       
   723         # have made
   723         self.ForceRefresh()
   724         self.ForceRefresh()
   724         return None        
   725         return None
   725 
       
   726 
   726 
   727     # Called when the user changes the name of BACnet object (using the GUI grid)
   727     # Called when the user changes the name of BACnet object (using the GUI grid)
   728     #    call graph: ObjectEditor.OnVariablesGridCellChange() --> 
   728     #    call graph: ObjectEditor.OnVariablesGridCellChange() -->
   729     #                --> BacnetSlaveEditorPlug.HighlightAllDuplicateObjectNames() -->
   729     #                --> BacnetSlaveEditorPlug.HighlightAllDuplicateObjectNames() -->
   730     #                --> ObjectEditor.HighlightDuplicateObjectNames() -->
   730     #                --> ObjectEditor.HighlightDuplicateObjectNames() -->
   731     #                -->   (this method)
   731     #                -->   (this method)
   732     # Will check whether there is a duplicate BACnet object name, and highlight it if so.
   732     # Will check whether there is a duplicate BACnet object name, and highlight it if so.
   733     #
   733     #
   734     # Since the names of BACnet objects must be unique within the whole bacnet server (and 
   734     # Since the names of BACnet objects must be unique within the whole bacnet server (and
   735     # not just among the BACnet objects of the same class (e.g. Analog Value, Binary Input, ...)
   735     # not just among the BACnet objects of the same class (e.g. Analog Value, Binary Input, ...)
   736     # to work properly this method must be passed a list of the names of all BACnet objects
   736     # to work properly this method must be passed a list of the names of all BACnet objects
   737     # currently configured.
   737     # currently configured.
   738     #
   738     #
   739     # AllObjectNamesFreq: a dictionary using as key the names of all currently configured BACnet
   739     # AllObjectNamesFreq: a dictionary using as key the names of all currently configured BACnet
   741     def HighlightDuplicateObjectNames(self, AllObjectNamesFreq):
   741     def HighlightDuplicateObjectNames(self, AllObjectNamesFreq):
   742         for row in range(self.Table.GetNumberRows()):
   742         for row in range(self.Table.GetNumberRows()):
   743             # TODO: change the hardcoded column number '1' to a number obtained at runtime
   743             # TODO: change the hardcoded column number '1' to a number obtained at runtime
   744             #       that is guaranteed to match the column titled "Object Name"
   744             #       that is guaranteed to match the column titled "Object Name"
   745             if AllObjectNamesFreq[self.Table.GetValueByName(row, "Object Name")] > 1:
   745             if AllObjectNamesFreq[self.Table.GetValueByName(row, "Object Name")] > 1:
   746                 # This is an error! Highlight it...            
   746                 # This is an error! Highlight it...
   747                 self.SetCellBackgroundColour(row, 1, ERROR_HIGHLIGHT[0])
   747                 self.SetCellBackgroundColour(row, 1, ERROR_HIGHLIGHT[0])
   748                 self.SetCellTextColour      (row, 1, ERROR_HIGHLIGHT[1])
   748                 self.SetCellTextColour(row, 1, ERROR_HIGHLIGHT[1])
   749             else: 
   749             else:
   750                 self.SetCellBackgroundColour(row, 1, wx.WHITE)
   750                 self.SetCellBackgroundColour(row, 1, wx.WHITE)
   751                 self.SetCellTextColour      (row, 1, wx.BLACK)
   751                 self.SetCellTextColour(row, 1, wx.BLACK)
   752         # Refresh the graphical display to take into account any changes we may have made
   752         # Refresh the graphical display to take into account any changes we may
       
   753         # have made
   753         self.ForceRefresh()
   754         self.ForceRefresh()
   754         return None        
   755         return None
   755       
   756 
   756       
       
   757       
       
   758 
   757 
   759 class ObjectEditor(wx.Panel):
   758 class ObjectEditor(wx.Panel):
   760     # This ObjectEditor class:
   759     # This ObjectEditor class:
   761     #   Creates and controls the GUI window for configuring all the BACnet objects of one
   760     #   Creates and controls the GUI window for configuring all the BACnet objects of one
   762     #   (generic) BACnet object type (e.g. Binary Value, Analog Input, Multi State Output, ...)
   761     #   (generic) BACnet object type (e.g. Binary Value, Analog Input, Multi State Output, ...)
   763     #   This 'window' is currenty displayed within one tab of the bacnet plugin, but this
   762     #   This 'window' is currenty displayed within one tab of the bacnet plugin, but this
   764     #   may change in the future!
   763     #   may change in the future!
   765     #
   764     #
   766     #   It includes a grid to display all the BACnet objects of its type , as well as the buttons 
   765     #   It includes a grid to display all the BACnet objects of its type , as well as the buttons
   767     #   to insert, delete and move (up/down) a BACnet object in the grid.
   766     #   to insert, delete and move (up/down) a BACnet object in the grid.
   768     #   It also includes the sizers and spacers required to lay out the grid and buttons
   767     #   It also includes the sizers and spacers required to lay out the grid and buttons
   769     #   in the wndow.
   768     #   in the wndow.
   770     #
   769     #
       
   770 
   771     def __init__(self, parent, window, controller, ObjTable):
   771     def __init__(self, parent, window, controller, ObjTable):
   772         # window:  the window in which the editor will open.
   772         # window:  the window in which the editor will open.
   773         # controller: The ConfigTreeNode object that controlls the data presented by 
   773         # controller: The ConfigTreeNode object that controlls the data presented by
   774         #             this 'config tree node editor'
   774         #             this 'config tree node editor'
   775         #
   775         #
   776         # parent:  wx._controls.Notebook
   776         # parent:  wx._controls.Notebook
   777         # window:  BacnetSlaveEditorPlug (i.e. beremiz.bacnet.BacnetSlaveEditor.BacnetSlaveEditorPlug)
   777         # window:  BacnetSlaveEditorPlug (i.e. beremiz.bacnet.BacnetSlaveEditor.BacnetSlaveEditorPlug)
   778         # controller: controller will be an object of class
   778         # controller: controller will be an object of class
   779         #            FinalCTNClass         (i.e. beremiz.ConfigTreeNode.FinalCTNClass )
   779         #            FinalCTNClass         (i.e. beremiz.ConfigTreeNode.FinalCTNClass )
   780         #            (FinalCTNClass inherits from: ConfigTreeNode and _BacnetSlavePlug)
   780         #            (FinalCTNClass inherits from: ConfigTreeNode and _BacnetSlavePlug)
   781         #            (For the BACnet plugin, it is easier to think of controller as a _BacnetSlavePlug, 
   781         #            (For the BACnet plugin, it is easier to think of controller as a _BacnetSlavePlug,
   782         #             as the other classes are generic to all plugins!!)
   782         #             as the other classes are generic to all plugins!!)
   783         #
   783         #
   784         # ObjTable: The object of class ObjectTable that stores the data displayed in the grid.
   784         # ObjTable: The object of class ObjectTable that stores the data displayed in the grid.
   785         #           This object is instantiated and managed by the _BacnetSlavePlug class.
   785         #           This object is instantiated and managed by the _BacnetSlavePlug class.
   786         #
   786         #
   787         self.window   = window
   787         self.window = window
   788         self.controller  = controller
   788         self.controller = controller
   789         self.ObjTable = ObjTable
   789         self.ObjTable = ObjTable
   790       
   790 
   791         wx.Panel.__init__(self, parent)
   791         wx.Panel.__init__(self, parent)
   792         
   792 
   793         # The main sizer, 2 rows: top row for buttons, bottom row for 2D grid
   793         # The main sizer, 2 rows: top row for buttons, bottom row for 2D grid
   794         self.MainSizer = wx.FlexGridSizer(cols=1, hgap=10, rows=2, vgap=0)
   794         self.MainSizer = wx.FlexGridSizer(cols=1, hgap=10, rows=2, vgap=0)
   795         self.MainSizer.AddGrowableCol(0)
   795         self.MainSizer.AddGrowableCol(0)
   796         self.MainSizer.AddGrowableRow(1)
   796         self.MainSizer.AddGrowableRow(1)
   797 
   797 
   798         # sizer placed on top row of main sizer: 
   798         # sizer placed on top row of main sizer:
   799         #   1 row; 6 columns: 1 static text, one stretchable spacer, 4 buttons
   799         #   1 row; 6 columns: 1 static text, one stretchable spacer, 4 buttons
   800         controls_sizer = wx.FlexGridSizer(cols=6, hgap=4, rows=1, vgap=5)
   800         controls_sizer = wx.FlexGridSizer(cols=6, hgap=4, rows=1, vgap=5)
   801         controls_sizer.AddGrowableCol(0)
   801         controls_sizer.AddGrowableCol(0)
   802         controls_sizer.AddGrowableRow(0)
   802         controls_sizer.AddGrowableRow(0)
   803         self.MainSizer.Add(controls_sizer, border=5, flag=wx.GROW|wx.ALL)
   803         self.MainSizer.Add(controls_sizer, border=5, flag=wx.GROW | wx.ALL)
   804 
   804 
   805         # the buttons that populate the controls sizer (itself in top row of the main sizer)
   805         # the buttons that populate the controls sizer (itself in top row of the main sizer)
   806         # NOTE: the _("string") function will translate the "string" to the local language
   806         # NOTE: the _("string") function will translate the "string" to the local language
   807         controls_sizer.Add(wx.StaticText(self, label=_('Object Properties:')), flag=wx.ALIGN_BOTTOM)         
   807         controls_sizer.Add(
       
   808             wx.StaticText(self, label=_('Object Properties:')), flag=wx.ALIGN_BOTTOM)
   808         controls_sizer.AddStretchSpacer()
   809         controls_sizer.AddStretchSpacer()
   809         for name, bitmap, help in [
   810         for name, bitmap, help in [
   810                 ("AddButton"   , "add_element"   , _("Add variable"      )),
   811                 ("AddButton", "add_element", _("Add variable")),
   811                 ("DeleteButton", "remove_element", _("Remove variable"   )),
   812                 ("DeleteButton", "remove_element", _("Remove variable")),
   812                 ("UpButton"    , "up"            , _("Move variable up"  )),
   813                 ("UpButton", "up", _("Move variable up")),
   813                 ("DownButton"  , "down"          , _("Move variable down"))]:
   814                 ("DownButton", "down", _("Move variable down"))]:
   814             button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap),
   815             button = wx.lib.buttons.GenBitmapButton(
   815                                                     size=wx.Size(28, 28), 
   816                 self, bitmap=GetBitmap(bitmap),
   816                                                     style=wx.NO_BORDER)
   817                 size=wx.Size(28, 28),
       
   818                 style=wx.NO_BORDER)
   817             button.SetToolTipString(help)
   819             button.SetToolTipString(help)
   818             setattr(self, name, button)
   820             setattr(self, name, button)
   819             controls_sizer.Add(button)         
   821             controls_sizer.Add(button)
   820 
   822 
   821         # the variable grid that will populate the bottom row of the main sizer 
   823         # the variable grid that will populate the bottom row of the main sizer
   822         panel = self
   824         panel = self
   823         self.VariablesGrid = ObjectGrid(panel, style=wx.VSCROLL)
   825         self.VariablesGrid = ObjectGrid(panel, style=wx.VSCROLL)
   824         #self.VariablesGrid.SetDropTarget(VariableDropTarget(self)) # use only to enable drag'n'drop
   826         # use only to enable drag'n'drop
   825         self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE,     self.OnVariablesGridCellChange)
   827         # self.VariablesGrid.SetDropTarget(VariableDropTarget(self))
   826         #self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.OnVariablesGridCellLeftClick)
   828         self.VariablesGrid.Bind(
   827         #self.VariablesGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN,    self.OnVariablesGridEditorShown)
   829             wx.grid.EVT_GRID_CELL_CHANGE,     self.OnVariablesGridCellChange)
       
   830         # self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.OnVariablesGridCellLeftClick)
       
   831         # self.VariablesGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN,    self.OnVariablesGridEditorShown)
   828         self.MainSizer.Add(self.VariablesGrid, flag=wx.GROW)
   832         self.MainSizer.Add(self.VariablesGrid, flag=wx.GROW)
   829  
   833 
   830         # Configure the Variables Grid...
   834         # Configure the Variables Grid...
   831         self.VariablesGrid.SetRowLabelSize(0)  # do not include a leftmost column containing the 'row label'
   835         # do not include a leftmost column containing the 'row label'
       
   836         self.VariablesGrid.SetRowLabelSize(0)
   832         self.VariablesGrid.SetButtons({"Add":    self.AddButton,
   837         self.VariablesGrid.SetButtons({"Add":    self.AddButton,
   833                                        "Delete": self.DeleteButton,
   838                                        "Delete": self.DeleteButton,
   834                                        "Up":     self.UpButton,
   839                                        "Up":     self.UpButton,
   835                                        "Down":   self.DownButton})
   840                                        "Down":   self.DownButton})
   836         # The custom grid needs to know the default values to use when 'AddButton' creates a new row
   841         # The custom grid needs to know the default values to use when 'AddButton' creates a new row
   837         # NOTE: ObjTable.BACnetObjectType will contain the class name of one of the following classes
   842         # NOTE: ObjTable.BACnetObjectType will contain the class name of one of the following classes
   838         # (BVObject, BIObject, BOObject, AVObject, AIObject, AOObject, MSVObject, MSIObject, MSOObject)
   843         # (BVObject, BIObject, BOObject, AVObject, AIObject, AOObject, MSVObject, MSIObject, MSOObject)
   839         # which inherit from one of (BinaryObject, AnalogObject, MultiSObject)
   844         # which inherit from one of (BinaryObject, AnalogObject, MultiSObject)
   840         self.VariablesGrid.SetDefaultValue(self.ObjTable.BACnetObjectType.DefaultValues)
   845         self.VariablesGrid.SetDefaultValue(
       
   846             self.ObjTable.BACnetObjectType.DefaultValues)
   841 
   847 
   842         # self.ObjTable: The table that contains the data displayed in the grid
   848         # self.ObjTable: The table that contains the data displayed in the grid
   843         #  This table was instantiated/created in the initializer for class _BacnetSlavePlug
   849         # This table was instantiated/created in the initializer for class _BacnetSlavePlug
   844         self.VariablesGrid.SetTable(self.ObjTable)
   850         self.VariablesGrid.SetTable(self.ObjTable)
   845         self.VariablesGrid.SetEditable(True)
   851         self.VariablesGrid.SetEditable(True)
   846         # set the column attributes (width, alignment)
   852         # set the column attributes (width, alignment)
   847         # NOTE: ObjTable.BACnetObjectType will contain the class name of one of the following classes
   853         # NOTE: ObjTable.BACnetObjectType will contain the class name of one of the following classes
   848         # (BVObject, BIObject, BOObject, AVObject, AIObject, AOObject, MSVObject, MSIObject, MSOObject)
   854         # (BVObject, BIObject, BOObject, AVObject, AIObject, AOObject, MSVObject, MSIObject, MSOObject)
   849         # which inherit from one of (BinaryObject, AnalogObject, MultiSObject)
   855         # which inherit from one of (BinaryObject, AnalogObject, MultiSObject)
   850         ColumnAlignments = self.ObjTable.BACnetObjectType.ColumnAlignments
   856         ColumnAlignments = self.ObjTable.BACnetObjectType.ColumnAlignments
   851         ColumnSizes      = self.ObjTable.BACnetObjectType.ColumnSizes
   857         ColumnSizes = self.ObjTable.BACnetObjectType.ColumnSizes
   852         for col in range(  self.ObjTable.GetNumberCols()):
   858         for col in range(self.ObjTable.GetNumberCols()):
   853             attr = wx.grid.GridCellAttr()
   859             attr = wx.grid.GridCellAttr()
   854             attr.SetAlignment(ColumnAlignments[col], wx.ALIGN_CENTRE)
   860             attr.SetAlignment(ColumnAlignments[col], wx.ALIGN_CENTRE)
   855             self.VariablesGrid.SetColAttr(col, attr)
   861             self.VariablesGrid.SetColAttr(col, attr)
   856             self.VariablesGrid.SetColMinimalWidth(col, ColumnSizes[col])
   862             self.VariablesGrid.SetColMinimalWidth(col, ColumnSizes[col])
   857             self.VariablesGrid.AutoSizeColumn(col, False)
   863             self.VariablesGrid.AutoSizeColumn(col, False)
   858  
   864 
   859         # layout the items in all sizers, and show them too.
   865         # layout the items in all sizers, and show them too.
   860         self.SetSizer(self.MainSizer)    # Have the wondow 'own' the sizer...   
   866         self.SetSizer(self.MainSizer)    # Have the wondow 'own' the sizer...
   861         #self.MainSizer.ShowItems(True)  # not needed once the window 'owns' the sizer (SetSizer())
   867         # self.MainSizer.ShowItems(True)  # not needed once the window 'owns' the sizer (SetSizer())
   862         #self.MainSizer.Layout()         # not needed once the window 'owns' the sizer (SetSizer())
   868         # self.MainSizer.Layout()         # not needed once the window 'owns' the sizer (SetSizer())
   863         
   869 
   864         # Refresh the view of the grid...
   870         # Refresh the view of the grid...
   865         #   We ask the table to do that, who in turn will configure the grid for us!!
   871         #   We ask the table to do that, who in turn will configure the grid for us!!
   866         #   It will configure the CellRenderers and CellEditors taking into account the type of
   872         #   It will configure the CellRenderers and CellEditors taking into account the type of
   867         #   BACnet object being shown in the grid!!
   873         #   BACnet object being shown in the grid!!
   868         #
   874         #
   870         #   It seems to me that the cell renderers etc. should all be configured right here.
   876         #   It seems to me that the cell renderers etc. should all be configured right here.
   871         #   Unfortunately we inherit from the customTable and customGrid classes in Beremiz
   877         #   Unfortunately we inherit from the customTable and customGrid classes in Beremiz
   872         #   (in order to maintain GUI consistency), so we have to adopt their way of doing things.
   878         #   (in order to maintain GUI consistency), so we have to adopt their way of doing things.
   873         #
   879         #
   874         # NOTE: ObjectTable.ResetView() (remember, ObjTable is of class ObjectTable)
   880         # NOTE: ObjectTable.ResetView() (remember, ObjTable is of class ObjectTable)
   875         #         calls ObjectTable._updateColAttrs(), who will do the configuring. 
   881         #         calls ObjectTable._updateColAttrs(), who will do the configuring.
   876         self.ObjTable.ResetView(self.VariablesGrid)
   882         self.ObjTable.ResetView(self.VariablesGrid)
   877 
   883 
   878 
       
   879     def RefreshView(self):
   884     def RefreshView(self):
   880         #print "ObjectEditor.RefreshView() called!!!"
   885         # print "ObjectEditor.RefreshView() called!!!"
   881         # Check for Duplicate Object IDs is only done within same BACnet object type (ID is unique by type).
   886         # Check for Duplicate Object IDs is only done within same BACnet object type (ID is unique by type).
   882         # The VariablesGrid class can handle it by itself.
   887         # The VariablesGrid class can handle it by itself.
   883         self.VariablesGrid.HighlightDuplicateObjectIDs()
   888         self.VariablesGrid.HighlightDuplicateObjectIDs()
   884         # Check for Duplicate Object Names must be done globally (Object Name is unique within bacnet server)
   889         # Check for Duplicate Object Names must be done globally (Object Name is unique within bacnet server)
   885         # Only the BacnetSlaveEditorPlug can and will handle this.
   890         # Only the BacnetSlaveEditorPlug can and will handle this.
   886         #self.window.HighlightAllDuplicateObjectNames()
   891         # self.window.HighlightAllDuplicateObjectNames()
   887         pass      
   892 
   888 
   893     #
   889     #########################################
       
   890     # Event handlers for the Variables Grid #
   894     # Event handlers for the Variables Grid #
   891     #########################################
   895     #
   892     def OnVariablesGridCellChange(self, event):
   896     def OnVariablesGridCellChange(self, event):
   893         row, col = event.GetRow(), event.GetCol()
   897         col = event.GetCol()
   894         #print "ObjectEditor.OnVariablesGridCellChange(row=%s, col=%s) called!!!" % (row, col)
   898         # print "ObjectEditor.OnVariablesGridCellChange(row=%s, col=%s)
       
   899         # called!!!" % (row, col)
   895         self.ObjTable.ChangesToSave = True
   900         self.ObjTable.ChangesToSave = True
   896         if self.ObjTable.GetColLabelValue(col) == "Object Identifier":
   901         if self.ObjTable.GetColLabelValue(col) == "Object Identifier":
   897             # an Object ID was changed => must check duplicate object IDs. 
   902             # an Object ID was changed => must check duplicate object IDs.
   898             self.VariablesGrid.HighlightDuplicateObjectIDs()
   903             self.VariablesGrid.HighlightDuplicateObjectIDs()
   899         if self.ObjTable.GetColLabelValue(col) == "Object Name":
   904         if self.ObjTable.GetColLabelValue(col) == "Object Name":
   900             # an Object Name was changed => must check duplicate object names. 
   905             # an Object Name was changed => must check duplicate object names.
   901             # Note that this must be done to _all_ BACnet objects, and not just the objects
   906             # Note that this must be done to _all_ BACnet objects, and not just the objects
   902             # of the same BACnet class (Binary Value, Analog Input, ...)
   907             # of the same BACnet class (Binary Value, Analog Input, ...)
   903             # So we have the BacnetSlaveEditorPlug class do it...
   908             # So we have the BacnetSlaveEditorPlug class do it...
   904             self.window.HighlightAllDuplicateObjectNames()
   909             self.window.HighlightAllDuplicateObjectNames()
   905         # There are changes to save => 
   910         # There are changes to save =>
   906         #        udate the enabled/disabled state of the 'save' option in the 'file' menu
   911         # udate the enabled/disabled state of the 'save' option in the 'file' menu
   907         self.window.RefreshBeremizWindow()
   912         self.window.RefreshBeremizWindow()
   908         event.Skip()
   913         event.Skip()
   909 
   914 
   910     def OnVariablesGridCellLeftClick(self, event):
   915     def OnVariablesGridCellLeftClick(self, event):
   911         row = event.GetRow()
   916         pass
   912 
   917 
   913     def OnVariablesGridEditorShown(self, event):
   918     def OnVariablesGridEditorShown(self, event):
   914         row, col = event.GetRow(), event.GetCol()
   919         pass
   915 
   920 
   916     def HighlightDuplicateObjectNames(self, AllObjectNamesFreq):
   921     def HighlightDuplicateObjectNames(self, AllObjectNamesFreq):
   917         return self.VariablesGrid.HighlightDuplicateObjectNames(AllObjectNamesFreq)
   922         return self.VariablesGrid.HighlightDuplicateObjectNames(AllObjectNamesFreq)
   918 
       
   919 
       
   920 
       
   921 
       
   922 
   923 
   923 
   924 
   924 class BacnetSlaveEditorPlug(ConfTreeNodeEditor):
   925 class BacnetSlaveEditorPlug(ConfTreeNodeEditor):
   925     # inheritance tree
   926     # inheritance tree
   926     #  wx.SplitterWindow-->EditorPanel-->ConfTreeNodeEditor-->BacnetSlaveEditorPlug
   927     #  wx.SplitterWindow-->EditorPanel-->ConfTreeNodeEditor-->BacnetSlaveEditorPlug
   927     #
   928     #
   928     # self.Controller -> The object that controls the data displayed in this editor
   929     # self.Controller -> The object that controls the data displayed in this editor
   929     #                    In our case, the object of class _BacnetSlavePlug
   930     #                    In our case, the object of class _BacnetSlavePlug
   930     
   931 
   931     CONFNODEEDITOR_TABS = [
   932     CONFNODEEDITOR_TABS = [
   932         (_("Analog Value Objects"),       "_create_AV_ObjectEditor"),
   933         (_("Analog Value Objects"),       "_create_AV_ObjectEditor"),
   933         (_("Analog Output Objects"),      "_create_AO_ObjectEditor"),
   934         (_("Analog Output Objects"),      "_create_AO_ObjectEditor"),
   934         (_("Analog Input Objects"),       "_create_AI_ObjectEditor"),
   935         (_("Analog Input Objects"),       "_create_AI_ObjectEditor"),
   935         (_("Binary Value Objects"),       "_create_BV_ObjectEditor"),
   936         (_("Binary Value Objects"),       "_create_BV_ObjectEditor"),
   936         (_("Binary Output Objects"),      "_create_BO_ObjectEditor"),
   937         (_("Binary Output Objects"),      "_create_BO_ObjectEditor"),
   937         (_("Binary Input Objects"),       "_create_BI_ObjectEditor"),
   938         (_("Binary Input Objects"),       "_create_BI_ObjectEditor"),
   938         (_("Multi-State Value Objects"),  "_create_MSV_ObjectEditor"),
   939         (_("Multi-State Value Objects"),  "_create_MSV_ObjectEditor"),
   939         (_("Multi-State Output Objects"), "_create_MSO_ObjectEditor"),
   940         (_("Multi-State Output Objects"), "_create_MSO_ObjectEditor"),
   940         (_("Multi-State Input Objects"), "_create_MSI_ObjectEditor")]
   941         (_("Multi-State Input Objects"), "_create_MSI_ObjectEditor")]
   941     
   942 
   942     def _create_AV_ObjectEditor(self, parent):
   943     def _create_AV_ObjectEditor(self, parent):
   943         self.AV_ObjectEditor = ObjectEditor(parent, self, self.Controler, self.Controler.ObjTables["AV_Obj"])
   944         self.AV_ObjectEditor = ObjectEditor(
       
   945             parent, self, self.Controler, self.Controler.ObjTables["AV_Obj"])
   944         return self.AV_ObjectEditor
   946         return self.AV_ObjectEditor
   945         
   947 
   946     def _create_AO_ObjectEditor(self, parent):
   948     def _create_AO_ObjectEditor(self, parent):
   947         self.AO_ObjectEditor = ObjectEditor(parent, self, self.Controler, self.Controler.ObjTables["AO_Obj"])
   949         self.AO_ObjectEditor = ObjectEditor(
       
   950             parent, self, self.Controler, self.Controler.ObjTables["AO_Obj"])
   948         return self.AO_ObjectEditor
   951         return self.AO_ObjectEditor
   949         
   952 
   950     def _create_AI_ObjectEditor(self, parent):
   953     def _create_AI_ObjectEditor(self, parent):
   951         self.AI_ObjectEditor = ObjectEditor(parent, self, self.Controler, self.Controler.ObjTables["AI_Obj"])
   954         self.AI_ObjectEditor = ObjectEditor(
       
   955             parent, self, self.Controler, self.Controler.ObjTables["AI_Obj"])
   952         return self.AI_ObjectEditor
   956         return self.AI_ObjectEditor
   953         
   957 
   954     def _create_BV_ObjectEditor(self, parent):
   958     def _create_BV_ObjectEditor(self, parent):
   955         self.BV_ObjectEditor = ObjectEditor(parent, self, self.Controler, self.Controler.ObjTables["BV_Obj"])
   959         self.BV_ObjectEditor = ObjectEditor(
       
   960             parent, self, self.Controler, self.Controler.ObjTables["BV_Obj"])
   956         return self.BV_ObjectEditor
   961         return self.BV_ObjectEditor
   957         
   962 
   958     def _create_BO_ObjectEditor(self, parent):
   963     def _create_BO_ObjectEditor(self, parent):
   959         self.BO_ObjectEditor = ObjectEditor(parent, self, self.Controler, self.Controler.ObjTables["BO_Obj"])
   964         self.BO_ObjectEditor = ObjectEditor(
       
   965             parent, self, self.Controler, self.Controler.ObjTables["BO_Obj"])
   960         return self.BO_ObjectEditor
   966         return self.BO_ObjectEditor
   961         
   967 
   962     def _create_BI_ObjectEditor(self, parent):
   968     def _create_BI_ObjectEditor(self, parent):
   963         self.BI_ObjectEditor = ObjectEditor(parent, self, self.Controler, self.Controler.ObjTables["BI_Obj"])
   969         self.BI_ObjectEditor = ObjectEditor(
       
   970             parent, self, self.Controler, self.Controler.ObjTables["BI_Obj"])
   964         return self.BI_ObjectEditor
   971         return self.BI_ObjectEditor
   965         
   972 
   966     def _create_MSV_ObjectEditor(self, parent):
   973     def _create_MSV_ObjectEditor(self, parent):
   967         self.MSV_ObjectEditor = ObjectEditor(parent, self, self.Controler, self.Controler.ObjTables["MSV_Obj"])
   974         self.MSV_ObjectEditor = ObjectEditor(
       
   975             parent, self, self.Controler, self.Controler.ObjTables["MSV_Obj"])
   968         return self.MSV_ObjectEditor
   976         return self.MSV_ObjectEditor
   969         
   977 
   970     def _create_MSO_ObjectEditor(self, parent):
   978     def _create_MSO_ObjectEditor(self, parent):
   971         self.MSO_ObjectEditor = ObjectEditor(parent, self, self.Controler, self.Controler.ObjTables["MSO_Obj"])
   979         self.MSO_ObjectEditor = ObjectEditor(
       
   980             parent, self, self.Controler, self.Controler.ObjTables["MSO_Obj"])
   972         return self.MSO_ObjectEditor
   981         return self.MSO_ObjectEditor
   973         
   982 
   974     def _create_MSI_ObjectEditor(self, parent):
   983     def _create_MSI_ObjectEditor(self, parent):
   975         self.MSI_ObjectEditor = ObjectEditor(parent, self, self.Controler, self.Controler.ObjTables["MSI_Obj"])
   984         self.MSI_ObjectEditor = ObjectEditor(
       
   985             parent, self, self.Controler, self.Controler.ObjTables["MSI_Obj"])
   976         return self.MSI_ObjectEditor
   986         return self.MSI_ObjectEditor
   977         
   987 
   978     def __init__(self, parent, controler, window, editable=True):
   988     def __init__(self, parent, controler, window, editable=True):
   979         self.Editable = editable
   989         self.Editable = editable
   980         ConfTreeNodeEditor.__init__(self, parent, controler, window)
   990         ConfTreeNodeEditor.__init__(self, parent, controler, window)
   981     
   991 
   982     def __del__(self):
   992     def __del__(self):
   983         self.Controler.OnCloseEditor(self)
   993         self.Controler.OnCloseEditor(self)
   984     
   994 
   985     def GetConfNodeMenuItems(self):
   995     def GetConfNodeMenuItems(self):
   986         return []
   996         return []
   987     
   997 
   988     def RefreshConfNodeMenu(self, confnode_menu):
   998     def RefreshConfNodeMenu(self, confnode_menu):
   989         return
   999         return
   990 
  1000 
   991     def RefreshView(self):
  1001     def RefreshView(self):
   992         self.HighlightAllDuplicateObjectNames()
  1002         self.HighlightAllDuplicateObjectNames()
  1011         self. BI_ObjectEditor.HighlightDuplicateObjectNames(ObjectNamesCount)
  1021         self. BI_ObjectEditor.HighlightDuplicateObjectNames(ObjectNamesCount)
  1012         self.MSV_ObjectEditor.HighlightDuplicateObjectNames(ObjectNamesCount)
  1022         self.MSV_ObjectEditor.HighlightDuplicateObjectNames(ObjectNamesCount)
  1013         self.MSO_ObjectEditor.HighlightDuplicateObjectNames(ObjectNamesCount)
  1023         self.MSO_ObjectEditor.HighlightDuplicateObjectNames(ObjectNamesCount)
  1014         self.MSI_ObjectEditor.HighlightDuplicateObjectNames(ObjectNamesCount)
  1024         self.MSI_ObjectEditor.HighlightDuplicateObjectNames(ObjectNamesCount)
  1015         return None
  1025         return None
  1016         
  1026 
  1017         
       
  1018     def RefreshBeremizWindow(self):
  1027     def RefreshBeremizWindow(self):
  1019         # self.ParentWindow is the top level Beremiz class (object) that 
  1028         # self.ParentWindow is the top level Beremiz class (object) that
  1020         #                   handles the beremiz window and layout
  1029         #                   handles the beremiz window and layout
  1021         self.ParentWindow.RefreshTitle()        # Refresh the title of the Beremiz window
  1030 
  1022                                                 #  (it changes depending on whether there are
  1031         # Refresh the title of the Beremiz window
  1023                                                 #   changes to save!! )
  1032         #  (it changes depending on whether there are
  1024         self.ParentWindow.RefreshFileMenu()     # Refresh the enabled/disabled state of the
  1033         #   changes to save!! )
  1025                                                 #  entries in the main 'file' menu. 
  1034         self.ParentWindow.RefreshTitle()
  1026                                                 #  ('Save' sub-menu should become enabled
  1035 
  1027                                                 #   if there are changes to save! )
  1036         # Refresh the enabled/disabled state of the
  1028         ###self.ParentWindow.RefreshEditMenu()
  1037         #  entries in the main 'file' menu.
  1029         ###self.ParentWindow.RefreshPageTitles()
  1038         #  ('Save' sub-menu should become enabled
       
  1039         #   if there are changes to save! )
       
  1040         self.ParentWindow.RefreshFileMenu()
       
  1041 
       
  1042         # self.ParentWindow.RefreshEditMenu()
       
  1043         # self.ParentWindow.RefreshPageTitles()