netlinx-erb 1.1.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d7d128ee559e5039b040d656ada09d817f56be6c
4
- data.tar.gz: 9d255c77d16daf54de3eda423593d9a5f347fea5
3
+ metadata.gz: 058274100b19e63f0c50839b65a4e514a93dbdb4
4
+ data.tar.gz: 04b579e0639b2d4dd95a630fd1fb8e514f373131
5
5
  SHA512:
6
- metadata.gz: b71728fcd3e0895efb397cbcfda9738c837250254eb939249106f67d1c38e4f48190bbabf400638fa59c0808a80e3ab37ca8426f05e045ac4aec3e81bd8a927e
7
- data.tar.gz: e9ee04ac514436a62c73520f4f3fa4945b65beb50ec718298a3934f86c341dae276f02af40a205bb81bfc80499373b8c1326639a71df2c1abee07e3006c2c8fa
6
+ metadata.gz: 9faa8838f45ded9b837c28e1fefa81394d8305d7433ce503bff322ba8b14f9a6ab137b5e1d4ed23cb48f04823f2705951d54322747e6cd82a5d6e4a5827c331d
7
+ data.tar.gz: e8ecc9bfe0a0dca6ce4fa9f309d4564efe67a17c805806766be362e5f23b59b0604b48dbcfba77833b52354727c9002e23f9f2823a03172c87261c6236b7ad63
data/README.md CHANGED
@@ -2,12 +2,13 @@
2
2
 
3
3
  netlinx-erb
4
4
 
5
- A code generation utility for AMX NetLinx control systems.
5
+ A code generation framework for AMX NetLinx control systems.
6
6
 
7
7
  [![Gem Version](https://badge.fury.io/rb/netlinx-erb.svg)](http://badge.fury.io/rb/netlinx-erb)
8
8
  [![API Documentation](http://img.shields.io/badge/docs-api-blue.svg)](http://www.rubydoc.info/gems/netlinx-erb)
9
+ [![MIT License](https://img.shields.io/badge/license-MIT-yellowgreen.svg)](https://github.com/amclain/netlinx-erb/blob/master/license.txt)
9
10
 
10
- Syntax highlighting is included in [sublime-netlinx](https://github.com/amclain/sublime-netlinx).
11
+ Syntax highlighting is included in [sublime-netlinx](https://github.com/amclain/sublime-netlinx#sublime-text-amx-netlinx-plugin).
11
12
 
12
13
 
13
14
  ## Overview
@@ -54,7 +55,7 @@ arguments.
54
55
 
55
56
  For the following function:
56
57
 
57
- ```c
58
+ ```netlinx
58
59
  define_function patch_video(integer input, integer output)
59
60
  {
60
61
  // Patch video matrix.
@@ -112,23 +113,456 @@ installed on your computer for this utility to work. It is included in the
112
113
  NetLinx Studio installation by default.*
113
114
 
114
115
  **If you receive the following error when running gem install:**
115
- `Unable to download data from https://rubygems.org/ - SSL_connect returned=1`
116
+ ```text
117
+ Unable to download data from https://rubygems.org/ - SSL_connect returned=1
118
+ ```
116
119
 
117
120
  Follow this guide:
118
121
  [Workaround RubyGems' SSL errors on Ruby for Windows (RubyInstaller)](https://gist.github.com/luislavena/f064211759ee0f806c88)
119
122
 
120
123
 
121
- ## Prerequisite Knowledge
124
+ ## Prerequisites
122
125
 
123
126
  netlinx-erb is a complex utility and does have a learning curve. However, the
124
127
  time invested in learning this utility pays off in time saved from generating
125
128
  code that would otherwise be handwritten, and troubleshooting fewer bugs. Due
126
129
  to this, project maintenance also becomes easier.
127
130
 
131
+ ### Programming Languages
132
+
128
133
  Basic experience with the [Ruby programming language](https://www.ruby-lang.org)
129
134
  is required, as well as [ERB templating](http://www.stuartellis.eu/articles/erb/).
135
+ The concept of [Model Oriented Programming (MOP)](http://download.imatix.com/mop/introduction.html)
136
+ is also used by this framework.
137
+
138
+ **Resources:**
139
+
140
+ * [Head First Ruby](http://shop.oreilly.com/product/9780596803995.do)
141
+ * [Design Patterns in Ruby](http://www.amazon.com/Design-Patterns-Ruby-Russ-Olsen/dp/0321490452/ref=sr_1_1?ie=UTF8&qid=1424904889&sr=8-1&keywords=ruby+design+patterns)
142
+ * [Practical Object-Oriented Design in Ruby](http://www.amazon.com/Practical-Object-Oriented-Design-Ruby-Addison-Wesley/dp/0321721330/ref=sr_1_2?ie=UTF8&qid=1424904889&sr=8-2&keywords=ruby+design+patterns)
143
+
144
+ ### Development Tools
145
+
146
+ #### Text Editor
147
+
148
+ A good text editor is crucial for working with netlinx-erb. [Sublime Text 3](http://www.sublimetext.com/3)
149
+ with the [sublime-netlinx](https://github.com/amclain/sublime-netlinx#sublime-text-amx-netlinx-plugin)
150
+ plugin is recommended, as it provides syntax highlighting and code completion
151
+ for netlinx-erb.
152
+
153
+ >***Use a Single Editor Well***
154
+
155
+ >*The editor should be an extension of your hand; make sure your editor is
156
+ configurable, extensible, and programmable.*
157
+ -- [The Pragmatic Programmer](http://www.informit.com/store/pragmatic-programmer-from-journeyman-to-master-9780201616224)
158
+
159
+ #### Command Prompt
160
+
161
+ The command prompt is a powerful, flexible way to issue commands. Due to this,
162
+ many of the tools that netlinx-erb is built on use command line interfaces.
163
+
164
+ This guide will assume the reader is proficient with the command prompt.
165
+ SS64 is a great [command line reference](http://ss64.com/) if you need to look
166
+ up a command.
167
+
168
+
169
+ ## Workflow
170
+
171
+ Developing a NetLinx project with netlinx-erb is significantly different than
172
+ with NetLinx Studio. Although netlinx-erb and NetLinx Studio are not strictly
173
+ mutually exclusive, trying to use NetLinx Studio to develop a netlinx-erb
174
+ project will create unnecessary friction.
175
+
176
+ There are three applications you will bounce between when developing a
177
+ netlinx-erb project:
178
+
179
+ * Text Editor
180
+ * Command Prompt
181
+ * Source Control Management System
182
+
183
+ At times you may need to open some of the standalone NetLinx tools like
184
+ NetLinx Diagnostics.
185
+
186
+ ### Transitioning From NetLinx Studio
187
+
188
+ The big difference to understand coming from NetLinx Studio is that NetLinx
189
+ Studio is designed to be a monolithic, all-in-one application that contains
190
+ all of the features that you need. Or at least that's the theory. The problem
191
+ is that in reality NetLinx Studio only contains the features that AMX thinks
192
+ you need, and can't support features you want to add yourself.
193
+
194
+ What happens when you want to add code generation and automation to NetLinx
195
+ Studio to save time on repetitive tasks? Well, you can't.
196
+
197
+ netlinx-erb takes the opposite approach, building on many different components
198
+ that are smaller in scope. To the greatest extent possible, these components
199
+ are extendable, customizable, and cross-platform. This means you're able to
200
+ modify a netlinx-erb development environment to suit a particular project, or
201
+ your workflow in general.
202
+
203
+ Integrating with source control management (SCM) systems like [Mercurial](http://tortoisehg.bitbucket.org/)
204
+ and [Git](http://git-scm.com/) was also an important goal of netlinx-erb. Due
205
+ to this, most files are plain text and typically easy to read by a human. The
206
+ philosophy is that configuration should happen in your text editor, not a
207
+ proprietary GUI.
130
208
 
131
209
 
132
210
  ## Getting Started
133
211
 
134
- ## Code Examples
212
+ ### Creating A New Project
213
+
214
+ Open the command prompt in the directory used for your NetLinx projects and type:
215
+
216
+ ```text
217
+ netlinx-erb -n my_project
218
+ ```
219
+
220
+ Enter the `my_project` directory and take a minute to skim through the files
221
+ that have been generated.
222
+
223
+ ### Configuring The Workspace
224
+
225
+ `workspace.config.yaml`, referred to as the workspace configuration, is a text
226
+ file that replaces the functionality of a NetLinx Studio `.apw` workspace file.
227
+ Change this file to the following:
228
+
229
+ ```yaml
230
+ systems:
231
+ -
232
+ name: My Project
233
+ connection: 192.168.1.2 # (or your master)
234
+ touch_panels:
235
+ -
236
+ path: touch_panel.TP4
237
+ dps:
238
+ - 10001:1:0
239
+ - 10002:1:0
240
+ ir:
241
+ -
242
+ path: cable_box.irl
243
+ dps: 5001:1:0
244
+ ```
245
+
246
+ * [YAML Workspace Configuration Reference](https://github.com/amclain/netlinx-workspace#yaml-workspace-configuration)
247
+
248
+ Now create `My Project.axs` and `include/cable-box.axi`. Using Sublime Text,
249
+ these files can be populated using the `NetLinx: New From Template: Overview`
250
+ and `NetLinx: New From Template: Include` commands, respectively. If you used
251
+ the templates, comment out the code for the [logger](https://github.com/amclain/amx-lib-log#amx-log-library)
252
+ for this example.
253
+
254
+ ```netlinx
255
+ (***********************************************************)
256
+ (* INCLUDES GO BELOW *)
257
+ (***********************************************************)
258
+
259
+ // Comment this out for the example.
260
+ // #include 'amx-lib-log'
261
+
262
+ (***********************************************************)
263
+ (* STARTUP CODE GOES BELOW *)
264
+ (***********************************************************)
265
+ DEFINE_START
266
+
267
+ // Comment this out for the example.
268
+ // logSetLevel(LOG_LEVEL_DETAIL);
269
+ ```
270
+
271
+ Also create `ir/cable_box.irl` and `touch_panel/touch_panel.TP4`. These files can
272
+ be empty, or the real thing. It doesn't matter for the example.
273
+
274
+ To get an idea of how the workspace config file relates to a traditional NetLinx
275
+ Studio workspace, run:
276
+
277
+ ```text
278
+ rake generate_apw
279
+ ```
280
+
281
+ Open `My Project.apw` in NetLinx Studio and take a look at the workspace tree.
282
+
283
+ ![NetLinx Studio Workspace Screenshot](guides/getting_started/my_project_apw_01.png)
284
+
285
+ The master source code, touch panel, and IR files show up in the tree, just like
286
+ we would expect. What you might not expect is that `cable-box` has shown up
287
+ under the `Include` folder even though it wasn't specified in the config.
288
+ This is a feature of [netlinx-workspace](https://github.com/amclain/netlinx-workspace#netlinx-workspace),
289
+ which automatically consumes include files since there will probably be a lot of
290
+ them. Don't worry though, unwanted [files can be explicity excluded](https://github.com/amclain/netlinx-workspace/blob/6e99397b4fcfa6bd1cd6766008fd75e8dd5092c0/spec/workspace/yaml/single_system/workspace.config.yaml#L11-L13).
291
+
292
+ ### Code Generation
293
+
294
+ *At this point it is important to have a working knowledge of Ruby and ERB. (See
295
+ [prerequisites](https://github.com/amclain/netlinx-erb#prerequisites).)*
296
+
297
+ In this example we'll connect touch panel buttons to the corresponding buttons
298
+ on the cable box remote control. To keep the code encapulated, we'll have
299
+ `include/cable-box.axi` model the cable box's remote control, and
300
+ `include/ui/template/panel.axi.erb.tmpl` will model the functions of the
301
+ identical touch panels.
302
+
303
+ * ["My Project" Reference Files](guides/getting_started/my_project)
304
+
305
+ First, create [include/cable-box.axi](guides/getting_started/my_project/include/cable-box.axi).
306
+ This file uses the traditional `.axi` extension because no code generation is
307
+ necessary. For a file this simple, code generation may actually create more work
308
+ and make the code harder to understand.
309
+
310
+ Next we'll configure the touch panels. Open `include/ui/_config.axi.erb`. This
311
+ is where we'll instruct the system to generate `.axi` files for each of the
312
+ touch panels:
313
+
314
+ ```ruby
315
+ # Params - Converted into @tmpl_[key]
316
+ # First key (panel name) is available as @tmpl_suffix
317
+ touch_panels = {
318
+ CONFERENCE_TABLE: { dps: 10001 },
319
+ WALL: { dps: 10002 },
320
+ }
321
+ ```
322
+
323
+ The important thing to notice about this file is that values can be passed into
324
+ each touch panel's hash, which then become available in the template as instance
325
+ variables. By using the instance variable `@tmpl_dps` in the template, the value
326
+ `10001` will be written to `panel-conference-table.axi`, and `10002` will be
327
+ written to `panel-wall.axi`. We'll go over this more when creating the template
328
+ file.
329
+
330
+ * Note: [_config.axi.erb will be deprecated](https://github.com/amclain/netlinx-erb/issues/1)
331
+
332
+ >**Why not use `DEFINE_COMBINE`?**
333
+
334
+ >Device combining concatenates all of the events into a single DPS, hiding which
335
+ touch panel actually sent the event. Conceptually, all of the physical touch
336
+ panels have to be thought of as one virtual touch panel -- they all mirror each
337
+ other. This means that touch panels that want to share the same code are forced
338
+ to share the same state as well.
339
+
340
+ >The answer to this problem is an advanced topic that will be covered in
341
+ another section. It is practical in situations like room combining where touch
342
+ panel B needs to operate autonomously when the rooms are separated, but needs to
343
+ mirror touch panel A when the rooms are combined (a state machine).
344
+
345
+ Since the touch panels share the same design file, `touch_panel.TP4`,
346
+ we'll use code generation to create the source code for each panel based on a
347
+ single template.
348
+
349
+ Create [include/ui/template/panel.axi.erb.tmpl](guides/getting_started/my_project/include/ui/template/panel.axi.erb.tmpl).
350
+ The first thing to notice is that unique names for the include guards can be
351
+ code generated:
352
+
353
+ ```netlinx+erb
354
+ (***********************************************************
355
+ Example Touch Panel
356
+
357
+ For the netlinx-erb getting started project.
358
+ ************************************************************)
359
+
360
+ #if_not_defined <%= "MY_PROJECT_TP_#{@tmpl_suffix}" %>
361
+ #define <%= "MY_PROJECT_TP_#{@tmpl_suffix}" %> 1
362
+ ```
363
+
364
+ Let's apply this to assigning the DPS to each touch panel. Since a device
365
+ definition takes the form of `CONSTANT_NAME = DPS`, we can use code generation
366
+ to populate the constant name and device number for each file:
367
+
368
+ ```netlinx+erb
369
+ (***********************************************************)
370
+ (* DEVICE NUMBER DEFINITIONS GO BELOW *)
371
+ (***********************************************************)
372
+ DEFINE_DEVICE
373
+
374
+ <%= "#{@dvTP} = #{@tmpl_dps}:1:0;" %>
375
+ ```
376
+
377
+ When the `.axi` files are generated, `panel-conference-table.axi` will contain
378
+ `dvTP_CONFERENCE_TABLE = 10001:1:0;`, and `panel-wall.axi` will contain
379
+ `dvTP_WALL = 10002:1:0;`.
380
+
381
+ >When authoring an `erb` template it is important to think on a higher level of
382
+ abstration than you would with an `axi` file, keeping in mind that you're
383
+ writing code that writes code. Creating variations of a similar piece of code is
384
+ a perfect job for the code generator.
385
+
386
+ At this point we have a few different sets of data that need to be connected
387
+ together:
388
+
389
+ * Touch panel button numbers
390
+ * Named constants for those buttons
391
+ * The key on the cable box remote control that needs to be triggered when its
392
+ corresponding touch panel button is pressed
393
+
394
+ These connections can be described in one place, making future changes simple:
395
+
396
+ ```netlinx+erb
397
+ (***********************************************************)
398
+ (* CONSTANT DEFINITIONS GO BELOW *)
399
+ (***********************************************************)
400
+ DEFINE_CONSTANT
401
+
402
+ <%
403
+ # Remember, this template generates multiple files.
404
+ # Guard your global code to prevent include conflicts!
405
+ -%>
406
+ #if_not_defined MY_PROJECT_TP_CONSTANTS
407
+ #define MY_PROJECT_TP_CONSTANTS 1
408
+
409
+ <% global_constant_justify = 26 -%>
410
+ // Cable Box Buttons
411
+ <%=
412
+ generate_constant_ivars cable_box_buttons = {
413
+ # :btn - Touch panel button number.
414
+ # :key - Cable box remote control key from `cable-box.axi`.
415
+ BTN_CABLE_BOX_1: { btn: 101, key: :CABLE_BOX_KEY_1 },
416
+ BTN_CABLE_BOX_2: { btn: 102, key: :CABLE_BOX_KEY_2 },
417
+ BTN_CABLE_BOX_3: { btn: 103, key: :CABLE_BOX_KEY_3 },
418
+ BTN_CABLE_BOX_4: { btn: 104, key: :CABLE_BOX_KEY_4 },
419
+ BTN_CABLE_BOX_5: { btn: 105, key: :CABLE_BOX_KEY_5 },
420
+ BTN_CABLE_BOX_6: { btn: 106, key: :CABLE_BOX_KEY_6 },
421
+ BTN_CABLE_BOX_7: { btn: 107, key: :CABLE_BOX_KEY_7 },
422
+ BTN_CABLE_BOX_8: { btn: 108, key: :CABLE_BOX_KEY_8 },
423
+ BTN_CABLE_BOX_9: { btn: 109, key: :CABLE_BOX_KEY_9 },
424
+ BTN_CABLE_BOX_0: { btn: 110, key: :CABLE_BOX_KEY_0 },
425
+ }
426
+
427
+ print_constant_hash cable_box_buttons.remap(:btn), justify: global_constant_justify
428
+ %>
429
+
430
+ #end_if
431
+ ```
432
+
433
+ * [Helper Method API Reference](http://www.rubydoc.info/gems/netlinx-erb/NetLinx/ERB/Helpers)
434
+
435
+ Now it's time to add a button event handler to connect the touch panel button
436
+ to the cable box IR code:
437
+
438
+ ```netlinx+erb
439
+ (***********************************************************)
440
+ (* THE EVENTS GO BELOW *)
441
+ (***********************************************************)
442
+ DEFINE_EVENT
443
+
444
+ // Cable Box Controls
445
+ <%=
446
+ button_event_block(cable_box_buttons.remap(:key), momentary: true) { |key|
447
+ "cable_box_key(#{key})"
448
+ }
449
+ %>
450
+ ```
451
+
452
+ Does this section of code look unusually short compared to its NetLinx
453
+ counterpart? Well there's a good reason for that: The code it writes is
454
+ incredibly repetitive and therefore a lot of work can be handed off to the
455
+ code generator. Even better, since this code references the `cable_box_buttons`
456
+ hash, every time a button is added or modified this section of generated code is
457
+ updated automatically.
458
+
459
+ ```netlinx
460
+ // GENERATED FILE `panel-conference-table.axi`
461
+
462
+ (***********************************************************)
463
+ (* THE EVENTS GO BELOW *)
464
+ (***********************************************************)
465
+ DEFINE_EVENT
466
+
467
+ // Cable Box Controls
468
+ button_event[dvTP_CONFERENCE_TABLE, BTN_CABLE_BOX_1]
469
+ button_event[dvTP_CONFERENCE_TABLE, BTN_CABLE_BOX_2]
470
+ button_event[dvTP_CONFERENCE_TABLE, BTN_CABLE_BOX_3]
471
+ button_event[dvTP_CONFERENCE_TABLE, BTN_CABLE_BOX_4]
472
+ button_event[dvTP_CONFERENCE_TABLE, BTN_CABLE_BOX_5]
473
+ button_event[dvTP_CONFERENCE_TABLE, BTN_CABLE_BOX_6]
474
+ button_event[dvTP_CONFERENCE_TABLE, BTN_CABLE_BOX_7]
475
+ button_event[dvTP_CONFERENCE_TABLE, BTN_CABLE_BOX_8]
476
+ button_event[dvTP_CONFERENCE_TABLE, BTN_CABLE_BOX_9]
477
+ button_event[dvTP_CONFERENCE_TABLE, BTN_CABLE_BOX_0]
478
+ {
479
+ push:
480
+ {
481
+ to[button.input];
482
+
483
+ switch (button.input.channel)
484
+ {
485
+ case BTN_CABLE_BOX_1: cable_box_key(CABLE_BOX_KEY_1);
486
+ case BTN_CABLE_BOX_2: cable_box_key(CABLE_BOX_KEY_2);
487
+ case BTN_CABLE_BOX_3: cable_box_key(CABLE_BOX_KEY_3);
488
+ case BTN_CABLE_BOX_4: cable_box_key(CABLE_BOX_KEY_4);
489
+ case BTN_CABLE_BOX_5: cable_box_key(CABLE_BOX_KEY_5);
490
+ case BTN_CABLE_BOX_6: cable_box_key(CABLE_BOX_KEY_6);
491
+ case BTN_CABLE_BOX_7: cable_box_key(CABLE_BOX_KEY_7);
492
+ case BTN_CABLE_BOX_8: cable_box_key(CABLE_BOX_KEY_8);
493
+ case BTN_CABLE_BOX_9: cable_box_key(CABLE_BOX_KEY_9);
494
+ case BTN_CABLE_BOX_0: cable_box_key(CABLE_BOX_KEY_0);
495
+ }
496
+ }
497
+
498
+ release: {}
499
+ }
500
+ ```
501
+
502
+ A remote control is a simple example of code generation in action. For a device
503
+ like a video matrix, imagine what happens when one of the inputs or outputs
504
+ needs to be repatched or renamed. All of the configuration information is in one
505
+ place; no need to find-and-replace throughout a file. This also helps to make
506
+ the code self-documenting, as all of the system configuration information is
507
+ grouped together.
508
+
509
+ ```ruby
510
+ matrix_inputs = {
511
+ VID_SRC_BLANK: { input: 0, name: "Blank" },
512
+ VID_SRC_ROOM_1_PODIUM: { input: 1, name: "Podium 1" },
513
+ VID_SRC_ROOM_1_WP: { input: 2, name: "Wall Panel 1" },
514
+ VID_SRC_ROOM_2_PODIUM: { input: 7, name: "Podium 2" },
515
+ VID_SRC_ROOM_2_WP: { input: 4, name: "Wall Panel 2" },
516
+ VID_SRC_ROOM_3_PODIUM: { input: 5, name: "Podium 3" },
517
+ VID_SRC_ROOM_3_WP: { input: 6, name: "Wall Panel 3" },
518
+ VID_SRC_BLURAY: { input: 9, name: "Blu-Ray" },
519
+ VID_SRC_CABLE: { input: 10, name: "Cable TV" },
520
+ }
521
+ ```
522
+
523
+ >**Separating Configuration From Implementation**
524
+
525
+ >netlinx-erb is designed to keep configuration and implementation code separated
526
+ as much as reasonably possible. This makes configuration changes fast and easy,
527
+ with significantly less risk that those changes will introduce bugs or break
528
+ the system.
529
+
530
+ Now that we have a touch panel template, open `My Project.axs` and add the
531
+ includes for `panel-conference-table` and `panel-wall`:
532
+
533
+ ```netlinx
534
+ (***********************************************************)
535
+ (* INCLUDES GO BELOW *)
536
+ (***********************************************************)
537
+
538
+ // Comment this out for the example.
539
+ // #include 'amx-lib-log'
540
+
541
+ #include 'panel-conference-table'
542
+ #include 'panel-wall'
543
+ ```
544
+
545
+ Also remember the include for `cable-box` in `panel.axi.erb.tmpl`:
546
+
547
+ ```netlinx
548
+ (***********************************************************)
549
+ (* INCLUDES GO BELOW *)
550
+ (***********************************************************)
551
+
552
+ #include 'cable-box'
553
+ ```
554
+
555
+ ### Compiling
556
+
557
+ From the command line:
558
+
559
+ ```text
560
+ rake
561
+ ```
562
+
563
+ Yes, it's really that simple. This command runs the code generator, generates
564
+ the RPC file, compiles the project, and creates the source code bundle. You can
565
+ also add your own [rake tasks](https://github.com/ruby/rake#description) if you
566
+ need to customize this process.
567
+
568
+ See all of the built-in rake tasks with `rake -T`.