Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

How-To Tutorials

7019 Articles
article-image-introduction-openvpn
Packt
20 Aug 2015
13 min read
Save for later

Introduction to OpenVPN

Packt
20 Aug 2015
13 min read
In this article by Eric Crist and Jan Just Keijser, the authors of the book Mastering OpenVPN, you will cover: Types of VPNs Comparison of VPNs (For more resources related to this topic, see here.) What is a VPN? Put simply, a VPN allows an administrator to create a local network between multiple computers on varying network segments. In some instances, those machines can be on the same LAN, they can be distant from each other across the vast Internet, or they can even be connected across a multitude of connection media such as wireless uplinks, satellite, dial-up-networking, and so on. The P in VPN comes from the added protection to make that virtual network private. Network traffic that is flowing over a VPN is often referred to as inside the (VPN) tunnel, compared to all other traffic that is outside the tunnel. In the following figure, network traffic is shown as it traditionally traverses across multiple network segments and the general Internet. Here, this traffic is relatively open to inspection and analysis. Though protected protocols such as HTTPS and SSH are less vulnerable, they are still identifiable: if an attacker is snooping network traffic, he can still see what type of connection is made from which computer to which server. When a VPN is used, the traffic inside the tunnel is no longer identifiable. Traffic within a VPN can be anything you would send over a local or wide-area network: web traffic, e-mail, text, graphics, and so on. Examples of some applications include the following: Automated Teller Machines: ATMs may use a VPN to connect more securely to banking systems. Open / Free Wi-Fi: With the proliferation of free or open wireless networks, everyday users can utilize a VPN to protect the entirety of their Internet browsing. Corporate networks: Corporations and other organizations may use a VPN to connect multiple office locations or even entire data centers. GeoIP / Location-based services: Some websites serve data based on geographic location using GeoIP databases and other records. A VPN can allow you to "bounce" through another machine in a location closer to the content you really want. Internet video services such as Hulu, YouTube, and Netflix are common examples. Bypassing censorship / Political freedom: Some regimes, such as North Korea or China have extraordinarily restrictive censorship rules. The "Great Firewall of China" is one extreme example. Lock-downs of Internet access during political uprisings such as "Arab Spring" attempt to contain and control reports outside the conflict. VPNs can aid in getting outside those restrictive rules to the greater Internet. Here is an example of traffic within a VPN. While the VPN itself is routed across the Internet like in the preceding figure, devices along the network path only see VPN traffic; those devices are completely unaware of what in being transmitted inside the private tunnel. Protected protocols, such as HTTPS and SSH, will still be protected inside the tunnel from other VPN users, but will additionally unidentifiable from outside the tunnel. A VPN not only encrypts the traffic within, it hides and protects individual data streams from those outside the tunnel. It should be noted that the preceding figure shows both the strength and one of the greatest threats of VPN technologies. The VPN tunnel is dug through routers and firewalls on both sides. Thus, all network traffic that is flowing via the VPN tunnel is bypassing the regular network defenses, unless special measures are taken to police the VPN traffic. Most VPN implementations utilize some form of encryption and, additionally, authentication. Encryption of the VPN ensures that other parties that may be monitoring traffic between systems cannot decode and further analyze otherwise sensitive data. Authentication has two components, each in a different context. First, there is user or system authentication that ensures those connecting to the service are authorized. This type of authentication may be in the form of per-user certificates, or a username/password combination. Further, rules specific to a given user can be negotiated such as specific routes, firewall rules, or other scripts and utilities. Typically, these are unique to a single instance, though even that can be configurable (when OpenVPN is used, see --duplicate-cn). The second component of authentication is added protection to the communication stream. In this case, a method of signing each packet sent is established. Each system verifies the VPN packets it receives are properly signed before decrypting the payload. By authenticating packets that are already encrypted, a system can save processing time by not even decrypting packets that do not meet the authentication rules. In the end, this prevents a very real potential Denial of Service (DoS) attack as well as thwarts Man in the Middle (MITM) attacks, assuming the signing keys are kept secure! Types of VPNs There are many VPN products available on the market, both commercial and open source. Almost all of these VPN products can be separated into the four categories: PPTP-protocol based VPNs IPSec-protocol based VPNs SSL-based VPNs OpenVPN Some people argue that OpenVPN is also an SSL-based VPN, as it uses an SSL or TLS-like protocol to establish a secure connection. However, we have created a separate category for OpenVPN, as it is different from almost every other SSL-based VPN solution. We will now go into more detail about each of the four types of VPNs: PPTP One of the oldest VPN protocols is the Point-to-Point Tunneling Protocol (PPTP) developed by Microsoft and Ascend in 1999. It is officially registered as RFC2637 (see https://www.ietf.org/rfc/rfc2637.txt for the full standard). The PPTP client has been included in Windows ever since 1995 and is still included in most operating systems. Nowadays, the PPTP protocol is considered fundamentally insecure, as the strength of the security of the connection is directly related to the strength of the authentication mechanism chosen (example, the password). Thus, an insecure password leads to an insecure VPN connection. Most PPTP setups use the MS-CHAPv2 protocol for encrypting passwords, and it is this protocol which is fundamentally broken. The security of the PPTP protocol, including the Microsoft MS-CHAPv2 extensions, has been discussed in the article available at https://www.schneier.com/paper-pptpv2.html. It is also possible to use X.509 certificates for securing a PPTP connection, which does lead to a fairly secure connection. However, not all PPTP clients support EAP-TLS, which is needed to allow the use of X.509 certificates. PPTP uses two channels, a control channel for setting up the connection and another channel for data transport. The control channel is initiated over TCP port 1723. The data channel uses the General Routing Encapsulation (GRE) protocol, which is IP protocol 47. For comparison, "regular" TCP/IP traffic is done using IP protocol 6 (TCP) and 17 (UDP). PPTP clients are available on almost all operating systems, ranging from Windows to Linux and UNIX derivatives to iOS and Android devices. IPSec The IPSec standard is the official IEEE/IETF standard for IP security. It is officially registered as RFC2411 (see https://www.ietf.org/rfc/rfc2411.txt for the full standard). IPSec is also built into the IPv6 standard. IPSec operates at level 2 and 3 of the OSI model of the network stack. It introduces the concept of security policies, which makes it extremely flexible and powerful, but also notoriously hard to configure and troubleshoot. Security policies allow an administrator to encrypt traffic between two endpoints based on many parameters, such as the source and destination IP address as well as the source and destination TCP or UDP ports. IPSec can be configured to use pre-shared keys or X.509 certificates to secure the VPN connection. Additionally, it uses either X.509 certificates, one-time passwords, or username/password protocols to authenticate the VPN connection. There are two modes of operation in IPSec: tunneling mode and transport mode. Transport mode is used most often in combination with the Level 2 Tunneling Protocol (L2TP). This L2TP protocol performs the user authentication as described in the preceding section. The IPSec clients built into most operating systems usually perform IPSec+L2TP, although it is also possible to set up an IPSec-only connection. The IPSec VPN client built into Microsoft Windows uses IPSec+L2TP by default, but it is possible to disable or bypass it. However, this involves cryptic commands and security policy changes. Like PPTP, IPSec also uses two channels: a control channel for setting up the connection and one for data transport. The control channel is initiated over UDP port 500 or 4500. The data channel uses the Encapsulated Security Payload (ESP) protocol, which is IP protocol 50. For comparison, "regular" TCP/IP traffic is done using IP protocol 6 (TCP) and 17 (UDP). The integrity of IPSec packets is ensured using Hash-based Message Authentication Code (HMAC), which is the same method that OpenVPN uses. One of the main disadvantages of IPSec is that many vendors have implemented extensions to the standard, which makes it hard (if not impossible) to connect two IPSec endpoints from different vendors. IPSec software is included in almost all operating systems, as well as firewall, router, and switch firmware. SSL-based VPNs The most commonly used VPNs nowadays are SSL-based VPNs, which are based on the SSL/TLS protocol. SSL-based VPNs are often called client-less VPNs or web-based VPNs, although there are some vendors that provide separate client software, such as Cisco AnyConnect and Microsoft SSTP. Most SSL-based VPNs use the same network protocol as is used for secure website (HTTPS), while OpenVPN uses a custom format for encrypting and signing data traffic. This is the main reason why OpenVPN is listed as a separate VPN category. There is no well-defined standard for SSL-based VPNs, but most use the SSL/TLS protocol to set up and secure the connection. The connection is secured using X.509 certificates in most cases, with one-time password or username/password protocols for authenticating the connection. SSL-based VPNs are very similar to connections used to secure websites (HTTPS) and the same protocol and channel (TCP and port 443) is often used. Even though SSL-based VPNs are often called web-based or client-less, there are quite a few vendors that use a browser plugin or ActiveX control to "enhance" the VPN connection. This makes the VPN noninteroperable with unsupported browsers or operating systems. OpenVPN OpenVPN is often called an SSL-based VPN, as it uses the SSL/TLS protocol to secure the connection. However, OpenVPN also uses HMAC in combination with a digest (or hashing) algorithm for ensuring the integrity of the packets delivered. It can be configured to use pre-shared keys as well as X.509 certificates. These features are not typically offered by other SSL-based VPNs. Furthermore, OpenVPN uses a virtual network adapter (a tun or tap device) as an interface between the user-level OpenVPN software and the operating system. In general, any operating system that has support for a tun/tap device can run OpenVPN. This currently includes Linux, Free/Open/NetBSD, Solaris, AIX, Windows, Mac OS as well as iOS/Android devices. For all these platforms, client software needs to be installed, which sets OpenVPN apart from client-less or web-based VPNs. The OpenVPN protocol is not defined in an RFC standard, but the protocol is publicly available as OpenVPN is open source software. The fact that it is open source actually makes OpenVPN more secure than closed-source VPNs, as the code is continually inspected by different people. Also, there is very little chance of secret backdoors being built into OpenVPN. OpenVPN has the notion of a control channel and a data channel, both of which are encrypted and secured differently. However, all traffic passes over a single UDP or TCP connection. The control channel is encrypted and secured using SSL/TLS, the data channel is encrypted using a custom encryption protocol. The default protocol and port for OpenVPN is UDP and port 1194. Before IANA granted OpenVPN an official port assignment, older clients (2.0-beta16 and older) defaulted to port 5000. Comparison of VPNs Each of the different VPN technologies has its own characteristics, advantages, and disadvantages. Even though this article is about OpenVPN, there are use-cases where, for example, an IPSec-based VPN is more suitable, depending on the requirement of the users. Advantages and disadvantages of PPTP The main advantage of PPTP-based VPNs is that the VPN client software is built into most operating systems. Also, the startup time for configuring and initializing a PPTP VPN connection is quite short. Disadvantages of PPTP-based VPNs are the lack of security and the lack of configuration options on both the client and server side. Furthermore, the EAP-TLS extension that enables the use of X.509 certificates is fully supported only on Microsoft Windows, although a patch exists for the open source pppd package to enable EAP-TLS support. The pppd package is included in almost every Linux distribution. Also, if one must resort to using EAP-TLS, then the ease of setting up a PPTP VPN is greatly diminished. This is because EAP-TLS requires setting up a public key infrastructure, just like IPSec and OpenVPN. Another major disadvantage of PPTP is the use of the GRE protocol, which does not integrate well with NAT'ing devices. Advantages and disadvantages of IPSec Advantages of the IPSec protocol are its strong security, good support by different vendors and platforms, including xDSL and Wi-Fi routers, as well as the ability to use fine-grained security policies to control the flow of traffic. The downsides of IPSec are that it is notoriously difficult to configure and troubleshoot, different IPSec implementations from different vendors do not play nicely together, and IPSec does not integrate well with NAT'ted networks. Most notably, it is not recommended and sometimes not even possible to run an IPSec server that is on a NAT'ted network. Advantages and disadvantages of SSL-based VPNs SSL-based VPNs or web-based VPNs have the advantage that there is no or very little client software involved. This makes installation and initialization on the client side very easy. The disadvantage of a web-based VPN is that it is often not a full-blown VPN and allows access to a single server or set of servers. Also, it is harder to share local data with the remote site or server. Advantages and disadvantages of OpenVPN Advantages of OpenVPN are its ease of deployment, its configurability, and the ability to deploy OpenVPN in restricted networks, including NAT'ted networks. Also, OpenVPN includes security features that are as strong as IPSec-based solutions, including hardware token security and support for different user authentication mechanisms. Disadvantages of OpenVPN are its current lack of scalability and its dependence on the installation of client-side software. Another disadvantage is the lack of a GUI for configuration and management. Especially, the tap interface driver for Microsoft Windows has often caused deployment issues when a new version of Windows is released. Summary In this article, we started out by explaining what a VPN is. We then discussed some examples of different types of VPN protocols, including PPTP, IPSec, and OpenVPN. At the end, we compared the various VPNs. Resources for Article: Further resources on this subject: Untangle VPN Services [article] Setting up VPNaaS in OpenStack [article] Securing OpenStack Networking [article]
Read more
  • 0
  • 0
  • 14545

article-image-installing-software-and-updates
Packt
20 Aug 2015
16 min read
Save for later

Installing Software and Updates

Packt
20 Aug 2015
16 min read
In this article by Fuat Ulugay, author of the book Learning Puppet for Windows Server, we will learn how to install, update, and uninstall a software using Chocolatey. We will also automate these processes as much as possible. You will learn the following topics: What is Chocolatey How to install and update a software with Chocolatey How to use Chocolatey and Puppet together to install/update the mostly used softwares How to update Puppet agents How to uninstall a software using Chocolatey (For more resources related to this topic, see here.) What is Chocolatey? Chocolatey is a package manager for Windows. There are commands for Linux such as apt-get and yum for package management. They are very easy to use. Whenever you need to install something, you just write apt-get install packagename or yum install packagename. Here, the idea is to have a similar functionality in Windows. You can see more details about Chocolatey at https://chocolatey.org. After learning what Chocolatey is, we will install it manually and install some software using this. Later, we will see how to use Chocolatey with Puppet. Installing Chocolatey The installation of Chocolatey is very simple. Open Command Prompt with administrator rights and copy and paste the following command: C:> @powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" && SET PATH=%PATH%;%ALLUSERSPROFILE%chocolateybin The output of this installation command is shown in the following screenshot. As you can see, the installation is very easy. After installing Chocolatey, just close Command Prompt and open a new one. Now, write the following command to see if Chocolatey is installed and it is giving the output:3 C:> choco --version Installing software with Chocolatey Now, let's try to install a software with Chocolatey. To do this, we need to know the package name. For example, assume that we want to install Notepad++. Let's check this out at https://chocolatey.org/. Searching for notepad brings the details as shown in the following screenshot: As you can see, there are two results for the same version. The package ending with .install is for portable installations. We will use the regular one. We can see how to install Notepad++ in the following screenshot. The command is as follows: C:> choco install notepadplusplus Uninstalling a software with Chocolatey Chocolatey, also, has the option to uninstall the installed packages. However, there are some exceptions such as: The software to be uninstalled must be installed using Chocolatey The software choco package must have its uninstall script As you can see, the uninstall part may not work properly. Let's check the details for Notepad++. On the Web, check the details for the package. In the Files section, there must be an uninstall script, otherwise, the uninstall will not work correctly. It means that when you want to uninstall it, you need to do it manually: Now, let's check out another software that we can install and uninstall. This time we will check out 7-Zip. Here are the details of 7-Zip: As you can see in the previous screenshot, this has the uninstall file. Now, let's try to install and uninstall. Normally, all the packages should also have an uninstall package. However, never assume this and check whether it can be uninstalled correctly. When checked, you will see that most of the packages do not have an uninstall option. So the uninstall functionality of Chocolatey is not dependable at the moment. Another important point is that choco uninstall will not give errors, even if it does not uninstall the package. Using Chocolatey to install a software After learning Chocolatey and its limits, we will continue with using Chocolatey with Puppet. Using both of them together will be a great plus for us and all the installation process will be much easier. When we manage installations with Chocolatey in Puppet, we will not need to find the installation package, its version, and how to run it silently. The installation will be completed with a very little effort. To use Chocolatey with Puppet, there is a module from Puppet Forge that we need to install. Go to Puppet Forge website and search for Chocolatey. You can see the module in the following screenshot. We will install the chocolatey/chocolatey module: To install the module, go to Puppet Master, open a terminal window, and run the following command: # sudo puppet module install chocolatey-chocolatey Now, it is time to install a software using Chocolatey. For this purpose, we need to write a module. A sample manifest is as follows: package { 'notepadplusplus': ensure => installed|latest|'1.0.0'|absent, provider => 'chocolatey', install_options => ['-pre','-params','"','param1','param2','"'], uninstall_options => ['-r'], source => 'https://myfeed.example.com/api/v2', } Now, let's check the details step by step: package { 'notepadplusplus': Here, we define the package name that is listed in https://chocolatey.org/. ensure: Here, you have different options. installed makes sure that it is installed. latest updates the software whenever there is a new version at https://chocolatey.org/. If you give the version number, such as '1.0.0', it will install this version. absent uninstalls the package. Do not trust the uninstall functionality as we have mentioned previously! provider => 'chocolatey': Here, we change the package provider so that the installation is handled by Chocolatey. install_options: Normally, the Chocolatey packages are installed silently. However, you also have the option to use different installation options. uninstall_options: Here you can put different options, for the uninstallation. source: We will not use this one. However, if you have different sources or your own Chocolatey server, you can reference it here. The following is a more simple form that we will normally use. As you can see, it is very simple to install software using Chocolatey: package { 'notepadplusplus': ensure => installed, provider => 'chocolatey', } As you will remember, we used Workrave as an example before. Now, let's write a new module that uses Chocolatey, and then compare it with our default first module to see the differences. We will create a new module named chocoworkrave.  Our old module structure, installworkrave, is shown in the following screenshot: Here, we can already see that there is less effort required. We do not need to find and upload the installation file. As you can see, more effort is required with the default provider. You need to find the exact name of the installation. You need to download the installation and upload it to server. You need to send the installation to the host. You need to give the installation options. When you have Chocolatey as provider, you just need to know the package name. The workrave package for Chocolatey has problems, and is not working properly. So, if you see that your installation is not working properly, do not spend time with it. Installing Firefox as an example First, search Chocolatey for Firefox and find the relevant package. In our example, as you can see in the previous screenshot, the name of the package is firefox. Now, let's write our module. We can use chocofirefox as a name for our module. Installing Chocolatey using Puppet It may occur to you that installation of Chocolatey to each host may be a burden. To install Chocolatey automatically on each host, you can use a module named ceritsc/chocolatey_sw in Puppet Forge. After the installation of this module, if you assign this module to any of your hosts or host groups, Chocolatey will be installed on them. Using Chocolatey to update a software One of the challenges for IT is to keep the client software up to date. It is easy for Windows updates that can be handled automatically. However, when it comes to third-party softwares, the updates may become a burden. Next, we will see how Puppet and Chocolatey deal with updates. As an example, we will use the Java Runtime installation. We will first install an older version and see whether it is updated correctly. Here is the package that we will use: When we scroll down, we will also see some older versions. We will first install the older version, 7.0.75. We will do it manually from Command Prompt: Clicking on the older version will give us the details about installing it via Chocolatey. To update Java, we will create a new module named chocojre. Now, let's test this and see whether the update works as expected. First, let's be sure that the correct version was installed from Control PanelProgramsPrograms and Features in our Windows host. Re-checking the programs in Windows, we see that the new Java version is installed. However, there is a little problem; the old version is also there. For most software, the old version will be no more; however, this is not the case for Java. Using Puppet and Chocolatey to update mostly used software We learned how to use Chocolatey to install and update a software. Now, the next step is to remove some of the burden from our shoulders. I know that there are always problems with some of the updates for certain softwares. There are always new versions and new workload to fulfill. Here is a list of softwares that we mostly need and are frequently updated. You can, of course, create your own list: The Java Runtime environment Adobe Reader The Flash Player plugin The Flash Player plugin activex Firefox Chrome iTunes 7-Zip After creating the list of softwares, our next step is to check https://chocolatey.org/ and find their package names: The Java Runtime environment: javaruntime Adobe Reader: adobereader The Flash Player plugin: flashplayerplugin The Flash Player plugin activex: flashplayeractivex Firefox: firefox Chrome: google-chrome-x64 iTunes: itunes 7-Zip: 7zip After learning each of the package's name, you can use one module and put all of them in it, or you can create one module for each of them. It will be better if we stick to the second option. Sometimes, there are cases where you should not update a software. For example, your document management software may be using an older version of Java. Upgrading it to the newer version may just cause problems for the users. In this case, you may have to use different update policies for different softwares. Keeping the modules separate will help you to easily differentiate. The example manifest for 7-Zip is as follows: # install 7zip using chocolatey class choco7zip { package { '7zip': ensure => latest, provider => 'chocolatey', } } Here, by just changing the class name and package name, you can create many different modules to update different kinds of software. When you are done with these modules, you will never have to deal with the Java, Adobe, or Flash updates. This will increase your end user satisfaction, as they will not see the popups of the software updates, which they cannot complete because of missing admin rights. Also, it will help your security and you will have your updates implemented sooner. The on-time updates will patch the security problems and vulnerabilities. If you want to keep the updates in one class, you can use the following sample class. In this class, you only need to add additional package names: # update software using chocolatey class choco7zip { $packages = [ "javaruntime", "adobereader", " flashplayerplugin" ] package { $packages: ensure => latest, provider => 'chocolatey', } } One more detail you need to know is that you can also use the latest option for installation. So instead of writing ensure => installed, ensure => latest will help you to install the latest version and keep it updated. Updating the Puppet agents One of the challenging tasks we may have is to update of the Puppet agents. Before updating the agents, ensure that the agent version is never higher than the server version. Thus, we should first start updating our server. Updating the server Before updating your server, ensure that you have a backup. The easiest method to update is to write the following commands. These two commands will update your Linux server and if there are any updates related to Foreman and Puppet, they will also be implemented: $ sudo apt-get update $ sudo apt-get upgrade -y The following screenshot shows that there are many updates for the server: After some major updates, the server may require a restart. In this case, write the following command to restart your server: $ sudo reboot To check the Puppet Server version, write the following command: $ puppet --version Updating the agents with Chocolatey Trying to find online computers and sending the updates again and again may become a burden for you. It will be much easier if Puppet also handles its agent updates. Let's check the version in one of our hosts. Now, we will upgrade it to version 3.8.1. To do this, search for puppet at https://chocolatey.org/. This is already the version we require. While testing this package, the default installation causes a restart. We do not want to disturb the users with a restart. So, we will overwrite its parameters. To do this, we need to first check the installation parameters of the puppet installation file. You can download the installation from https://downloads.puppetlabs.com/windows/. After the download, write the package name followed by /? in Command Prompt. This will show you the installation option similar to the one shown in the following screenshot: In the previous screenshot, you can see that we need two options, /norestart and /quiet, to install the agent silently and prevent a reboot. After learning these details, we are ready to continue with the manifest details. We will create a module named puppetagent. In these details, we can see two more installation options: -override: This option is used to override any options that were defined -installArgs: This is used to indicate that there are new installation arguments Always use the version number to prevent problems. This will ensure that you are not having a version newer than your server and that you have full control over the Puppet agent versions. After completing all the details, it is test time again. Let's see what happens when we do a test run: The previous screenshot shows that it first gives an error. Although, we have put the NORESTART option, it tries to execute the shutdown /a command, which causes an error code with 1116. However, when we check the version, we can see that the update is successful. Finally, the next run gives no error as the installation is successful. Installing Puppet 3.7.5 gives no error, However, the 3.8.1 version gives an error, which is not important. This is again the case when we may see in new open-source technologies. Putting everything together, it will be best to test any module with Chocolatey before going live. Another problem is that sometimes, the update may not correctly run and you may need to correct it manually on the host. If your Puppet agent does not run correctly anymore, use the following command in Command Prompt to fix the agent: choco install puppet -version 3.8.1 –force. Uninstalling a software After learning the different ways of installing a software, now we will learn how to uninstall a software. At times, you may need to remove some softwares from each client. Instead of dealing with them one by one, you can use Puppet and automate the removal process. For this purpose, it is fine to use the package resource of Puppet. As an example, we will uninstall the older versions of Java. Here, we will uninstall two packages: Java 7 Update 75 and Java 7 Update 75 (64-bit). We have already created a module for Java Update: chocojre. Now, let's modify it so that it does not only install the latest version, but also uninstalls the older one. We will, also, require the latest version before uninstalling the previous version. There is no easy way to remove all older versions. So, we need to specify each of them manually. For uninstalling, the only change we need to add is ensure => absent. At times, there are leftovers from the upgraded software; it is a good idea to remove the older versions. It will be much easier to use one module to update the same software and uninstall its older versions. Uninstalling an older version of a software that cannot be differentiated by its name Assume that we have a case where there are two versions of a software installed. We want to install the older version. However, we cannot differentiate them by their names because they are identical. We had this situation after upgrading Puppet. As you can see in the following screenshot, there are two different Puppet agent versions installed with the same name: Here, if we use ensure => absent, both the packages will be removed. In this case, the Puppet connection will be lost. Here, we will need a slightly advanced approach. Now, we know that the Puppet agent installation is an MSI package. Checking the registry details, we can find its uninstall string. We will run regedit.exe and go to the HKLMSoftwareMicrosoftWindowsCurrentVersionUninstall folder. Here, we need to find Puppet version 3.7.4. Here is the full uninstall string: MsiExec.exe /X{9241D505-58E0-47CF-97A1-5E195F02FA94} We will also add /Q so it uninstalls quietly. The new command becomes: MsiExec.exe /Q /X{9241D505-58E0-47CF-97A1-5E195F02FA94} When you run the uninstallation of older Puppet agents, it also breaks the newer ones. So, this is just an example to show you how to uninstall older packages. Normally, installation packages automatically remove the older version. However, it is not always the case as we see in Puppet agent and Java. As Puppet older uninstallation file breaks the newer one, never use this code in production. We will add our uninstallation code to our puppetagent module. Also, we are inserting a condition that the uninstallation will only work when the Puppet agent version is 3.8.1. This will prevent the uninstallation from working, when the running version is an older one. Summary In this article, we learned how to install a software using Chocolatey. Later, we used both Puppet and Chocolatey in tandem to make our installations and updates much easier. We also checked out some of the softwares that are most used and how to always keep them updated. Finally, we learned how to update Puppet agents and uninstall a software. If you want to learn more about Puppet, there are many more books about it. The Puppet documentation is, also, one of the places you may check out from time to time. If you have problems and need to ask questions, there are different options available such as: For Enterprise users, Puppet has commercial support at https://tickets.puppetlabs.com/secure/Dashboard.jspa Google groups such as puppet-users and puppet-bugs The #puppet IRC channel on freenode Resources for Article: Further resources on this subject: Puppet and OS Security Tools [article] Puppet Language and Style [article] A Peek Under the Hood – Facts, Types, and Providers [article]
Read more
  • 0
  • 0
  • 4452

article-image-getting-started-arduino
Packt
20 Aug 2015
12 min read
Save for later

Getting Started with Arduino

Packt
20 Aug 2015
12 min read
Hello there! If you are reading this article by Adith Jagadish Boloor, the author of the book Arduino By Example, it means that you've taken your first step to make fascinating projects using Arduinos. This article will teach you how to set up an Arduino and write your first Arduino code. You'll be in good hands whilst you learn some of the basics aspects of coding using the Arduino platform, which will allow you to build almost anything from robots, home automation systems, touch interfaces, sensory systems, and so on. Firstly, you will learn how to install the powerful Arduino software, then set that up, followed by hooking up your Arduino board and, after making sure that everything is fine and well, you will write your first code! Once you are comfortable with that, we will modify the code to make it do something more, which is often what Arduino coders do. We do not just create completely new programs but often we build on what has been done before, to make it better and more suited to our objectives. The contents of this article are divided into the following topics: Prerequisites Setting up Hello World Summary (For more resources related to this topic, see here.) Prerequisites Well, you can't jump onto a horse without putting on a saddle first, can you? This section will cover what components you need to start coding on an Arduino. These can be purchased from your favorite electrical hobby store or simply ordered online. Materials needed 1x Arduino compatible board such as an Arduino UNO 1x USB cable A to B 2x LEDs 2x 330Ω resistors A mini breadboard 5x male-to-male jumper wires Note The UNO can be substituted for any other Arduino board (Mega, Leonardo and so on) for most of the projects. These boards have their own extra features. For example, the Mega has almost double the number of I/O (input/output) pins for added functionality. The Leonardo has a feature which enables it to control the keyboard and mouse of your computer. Setting up This topic involves downloading the Arduino software, installing the drivers, hooking up the Arduino, and understanding the IDE menus. Downloading and installing the software Arduino is open source-oriented. This means all the software is free to use non-commercially. Go to http://arduino.cc/en/Main/Software and download the latest version for your specific operating system. If you are using a Mac, make sure you choose the right Java version, and similarly on Linux, download the 32 or 64 bit version according to your computer. Arduino download page Windows Once you have downloaded the setup file, run it. If it asks for administrator privileges, allow it. Install it in its default location (C:Program FilesArduino or C:Program Files (x86)Arduino). Create a new folder in this location and rename it My Codes or something where you can conveniently store all your programs. Mac OS X Once the ZIP file has finished downloading, double-click to expand it. Copy the Arduino application to the Applications folder. You won't have to install additional drivers to make the Arduino work since we will be using only the Arduino UNO and MEGA. You're all set. If you didn't get anything to work, go to https://www.arduino.cc/en/guide/macOSX. Linux (Ubuntu 12.04 and above) Once you have downloaded the latest version of Arduino from the above link, install the compiler and the library packages using the following command: sudo apt-get update && sudo apt-get install arduino arduino-core If you are using a different version of Linux, this official Arduino walkthrough at http://playground.arduino.cc/Learning/Linux will help you out. Connecting the Arduino It is time to hook up the Arduino board. Plug in the respective USB terminals to the USB cable and the tiny LEDs on the Arduino should begin to flash. Arduino UNO plugged in If the LEDs didn't turn on, ensure that the USB port on your computer is functioning and make sure the cable isn't faulty. If it still does not light up, there is something wrong with your board and you should get it checked. Windows The computer will begin to install the drivers for the Arduino by itself. If it does not succeed, do the following: Open Device Manager Click on Ports (COM & LPT) Right-click on Unknown Device and select Properties Click install driver and choose browse files on the computer Choose the drivers folder in the previously installed Arduino folder The computer should say that your Arduino UNO (USB) has been successfully installed on COM port (xx). Here xx refers to a single or double digit number. If this message didn't pop up, go back to the Device Manager and check if it has been installed under COM ports. Arduino UNO COM port Remember the (COMxx) port that the Arduino UNO was installed on. Mac OS X If you are using Mac OS, a dialog box will tell you that a new network interface has been detected. Click Network Preferences and select Apply. Even though the Arduino board may show up as Not Configured, it should be working perfectly. Linux You are ready to go. The serial ports for Mac OS and Linux will be obtained once the Arduino software has been launched. The Arduino IDE The Arduino software, commonly referred to as the Arduino IDE (integrated development environment). The IDE for Windows, Mac OS and Linux is almost identical. Now let's look at some of the highlights of this software. Arduino IDE This is the window that you will see when you first start up the IDE. The tick/check mark verifies that your code's syntax is correct. The arrow pointing right is the button that uploads the code to the board and checks if the code has been changed since the last upload or verification. The magnifying glass is the Serial Monitor. This is used to input text or output debugging statements or sensor values. Examples of Arduino Every Arduino programmer starts by using one of these examples. Even after mastering Arduino, one would still return here to find examples to use. Arduino tools The screenshot shows the tools that are available in the Arduino IDE. The Board option opens up all the different boards that the software supports. Hello World The easiest way to start working with Arduinos begins here. You'll learn how to output print statements. The Arduino uses a Serial Monitor for displaying information such as print statements, sensor data and the like. This is a very powerful tool for debugging long codes. Now for your first code! Writing a simple print statement Open up the Arduino IDE and copy the following code into a new sketch. void setup() { Serial.begin(9600); Serial.println("Hello World!"); } void loop() { } Open Tools | Board and choose Arduino UNO, as shown in the following screenshot: Open Tools | Port and choose the appropriate port (remember the previous COM xx number? select that), as shown in the following screenshot. For Mac and Linux users, once you have connected the Arduino board, going to Tools | Serial Port will give you a list of ports. The Arduino is typically something like /dev/tty.usbmodem12345 where 12345 will be different. Selecting port Finally, hit the upload button. If everything is fine, the LEDs on the Arduino should start flickering as the code is uploaded to the Arduino. The code will then have uploaded to the Arduino. To see what you have accomplished, click on the Serial Monitor button on the right side and switch the baud rate on the Serial Monitor window to 9600. You should see your message Hello World! waiting for you there. LED blink That wasn't too bad but it isn't cool enough. This article will enlighten you, literally. Open up a new sketch. Go to File | Examples | Basics | Blink. Blink example Before we upload the code, we need to make sure of one more thing. Remember the LED that we spoke about in the prerequisites? Let’s learn a bit about it before plugging it in. LED basics We will make use of it now. Plug in the LED such that the longer leg goes into pin 13 and the shorter leg goes into the GND pin as in the following images: LED blink setup (Fritzing) This diagram is made using software called Fritzing. This software will be used in future projects to make it cleaner to see and easier to understand as compared to a photograph with all the wires running around. Fritzing is opensource software which you can learn more about at www.fritzing.org. Arduino LED setup Upload the code. Your LED will start blinking, as shown in the following image. Lit up LED Isn't it just fascinating? You just programmed your first hardware. There's no stopping you now. Let's see what the code does and what happens when you change it. This is the blink example code that you just used. /* Blink Turns on an LED on for one second, then off for one second, repeatedly. This example code is in the public domain. */ //Pin 13 has an LED connected on most Arduino boards. //give it a name: int led = 13; //the setup routine runs once when you press reset: void setup() { // initialize the digital pin as an output. pinMode(led, OUTPUT); } //the loop routine runs over and over again forever: void loop() { digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level) delay(1000); // wait for a second digitalWrite(led, LOW); // turn the LED off by making the voltage LOW delay(1000); // wait for a second } We have three major sections in this code. int led = 13; This line simply stores the numerical PIN value onto a variable called led. void setup() { // initialize the digital pin as an output. pinMode(led, OUTPUT); } This is the setup function. Here is where you tell the Arduino what is connected on each used pin. In this case, we tell the Arduino that there is an output device (LED) on pin 13. void loop() { digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level) delay(1000); // wait for a second digitalWrite(led, LOW); // turn the LED off by making the voltage LOW delay(1000); // wait for a second } This is the loop function. It tells the Arduino to keep repeating whatever is inside it in a sequence. The digitalWrite command is like a switch that can be turned ON (HIGH) or OFF (LOW). The delay(1000) function simply makes the Arduino wait for a second before heading to the next line. If you wanted to add another LED, you'd need some additional tools and some changes to the code. This is the setup that you want to create. Connecting two LEDs to an Arduino If this is your first time using a breadboard, take some time to make sure all the connections are in the right place. The colors of the wires don't matter. However, GND is denoted using a black wire and VCC/5V/PWR is denoted with a red wire. The two resistors, each connected in series (acting like a connecting wire itself) with the LEDs limit the current flowing to the LEDs, making sure they don't blow up. As before, create a new sketch and paste in the following code: /* Double Blink Turns on and off two LEDs alternatively for one second each repeatedly. This example code is in the public domain. */ int led1 = 12; int led2 = 13; void setup() { // initialize the digital pins as an output. pinMode(led1, OUTPUT); pinMode(led2, OUTPUT); // turn off LEDs before loop begins digitalWrite(led1, LOW); // turn the LED off (LOW is the voltage level) digitalWrite(led2, LOW); // turn the LED off (LOW is the voltage level) } //the loop routine runs over and over again forever: void loop() { digitalWrite(led1, HIGH); // turn the LED on (HIGH is the voltage level) digitalWrite(led2, LOW); // turn the LED off (LOW is the voltage level) delay(1000); // wait for a second digitalWrite(led1, LOW); // turn the LED off (LOW is the voltage level) digitalWrite(led2, HIGH); // turn the LED on (HIGH is the voltage level) delay(1000); // wait for a second } Once again, make sure the connections are made properly, especially the positive LEDs (longer to OUTPUT PIN) and the negative (shorter to GND) terminals. Save the code into DoubleBlink.ino. Now, if you make any changes to it, you can always retrieve it. Upload the code. 3… 2… 1… And there you have it, an alternating LED blink cycle created purely with the Arduino. You can try changing the delay to see its effects. For the sake of completeness, I would like to mention that you could take this mini-project further by using a battery to power the system and decorate your desk/room/house. Summary You have now completed the basic introduction to the world of Arduino. In short, you have successfully set up your Arduino and have written your first code. You also learned how to modify the existing code to create something new, making it more suitable for your specific needs. This methodology will be applied repeatedly while programming, because almost all the code available is open source and it saves time and energy. Resources for Article: Further resources on this subject: Central Air and Heating Thermostat [article] Christmas Light Sequencer [article] Webcam and Video Wizardry [article]
Read more
  • 0
  • 0
  • 14356

article-image-scientific-computing-apis-python
Packt
20 Aug 2015
23 min read
Save for later

Scientific Computing APIs for Python

Packt
20 Aug 2015
23 min read
In this article, by Hemant Kumar Mehta author of the book Mastering Python Scientific Computing we will have comprehensive discussion of features and capabilities of various scientific computing APIs and toolkits in Python. Besides the basics, we will also discuss some example programs for each of the APIs. As symbolic computing is relatively different area of computerized mathematics, we have kept a special sub section within the SymPy section to discuss basics of computerized algebra system. In this article, we will cover following topics: Scientific numerical computing using NumPy and SciPy Symbolic Computing using SymPy (For more resources related to this topic, see here.) Numerical Scientific Computing in Python The scientific computing mainly demands for facility of performing calculations on algebraic equations, matrices, differentiations, integrations, differential equations, statistics, equation solvers and much more. By default Python doesn't come with these functionalities. However, development of NumPy and SciPy has enabled us to perform these operations and much more advanced functionalities beyond these operations. NumPy and SciPy are very powerful Python packages that enable the users to efficiently perform the desired operations for all types of scientific applications. NumPy package NumPy is the basic Python package for the scientific computing. It provides facility of multi-dimensional arrays and basic mathematical operations such as linear algebra. Python provides several data structure to store the user data, while the most popular data structures are lists and dictionaries. The list objects may store any type of Python object as an element. These elements can be processed using loops or iterators. The dictionary objects store the data in key, value format. The ndarrays data structure The ndaarays are also similar to the list but highly flexible and efficient. The ndarrays is an array object to represent multidimensional array of fixed-size items. This array should be homogeneous. It has an associated object of dtype to define the data type of elements in the array. This object defines type of the data (integer, float, or Python object), size of data in bytes, byte ordering (big-endian or little-endian). Moreover, if the type of data is record or sub-array then it also contains details about them. The actual array can be constructed using any one of the array, zeros or empty methods. Another important aspect of ndarrays is that the size of arrays can be dynamically modified. Moreover, if the user needs to remove some elements from the arrays then it can be done using the module for masked arrays. In a number of situations, scientific computing demands deletion/removal of some incorrect or erroneous data. The numpy.ma module provides the facility of masked array to easily remove selected elements from arrays. A masked array is nothing but the normal ndarrays with a mask. Mask is another associated array with true or false values. If for a particular position mask has true value then the corresponding element in the main array is valid and if the mask is false then the corresponding element in the main array is invalid or masked. In such case while performing any computation on such ndarrays the masked elements will not be considered. File handling Another important aspect of scientific computing is storing the data into files and NumPy supports reading and writing on both text as well as binary files. Mostly, text files are good way for reading, writing and data exchange as they are inherently portable and most of the platforms by default have capabilities to manipulate them. However, for some of the applications sometimes it is better to use binary files or the desired data for such application can only be stored in binary files. Sometimes the size of data and nature of data like image, sound etc. requires them to store in binary files. In comparison to text files binary files are harder to manage as they have specific formats. Moreover, the size of binary files are comparatively very small and the read/ write operations are very fast then the read/ write text files. This fast read/ write is most suitable for the application working on large datasets. The only drawback of binary files manipulated with NumPy is that they are accessible only through NumPy. Python has text file manipulation functions such as open, readlines and writelines functions. However, it is not performance efficient to use these functions for scientific data manipulation. These default Python functions are very slow in reading and writing the data in file. NumPy has high performance alternative that load the data into ndarrays before actual computation.  In NumPy, text files can be accessed using numpy.loadtxt and numpy.savetxt functions.  The loadtxt function can be used to load the data from text files to the ndarrays. NumPy also has a separate functions to manipulate the data in binary files. The function for reading and writing are numpy.load and numpy.save respectively. Sample NumPy programs The NumPy array can be created from a list or tuple using the array, this method can transform sequences of sequences into two dimensional array. import numpy as np x = np.array([4,432,21], int) print x                            #Output [  4 432  21] x2d = np.array( ((100,200,300), (111,222,333), (123,456,789)) ) print x2d Output: [  4 432  21] [[100 200 300] [111 222 333] [123 456 789]] Basic matrix arithmetic operation can be easily performed on two dimensional arrays as used in the following program.  Basically these operations are actually applied on elements hence the operand arrays must be of equal size, if the size is not matching then performing these operations will cause a runtime error. Consider the following example for arithmetic operations on one dimensional array. import numpy as np x = np.array([4,5,6]) y = np.array([1,2,3]) print x + y                      # output [5 7 9] print x * y                      # output [ 4 10 18] print x - y                       # output [3 3 3]    print x / y                       # output [4 2 2] print x % y                    # output [0 1 0] There is a separate subclass named as matrix to perform matrix operations. Let us understand matrix operation by following example which demonstrates the difference between array based multiplication and matrix multiplication. The NumPy matrices are 2-dimensional and arrays can be of any dimension. import numpy as np x1 = np.array( ((1,2,3), (1,2,3), (1,2,3)) ) x2 = np.array( ((1,2,3), (1,2,3), (1,2,3)) ) print "First 2-D Array: x1" print x1 print "Second 2-D Array: x2" print x2 print "Array Multiplication" print x1*x2   mx1 = np.matrix( ((1,2,3), (1,2,3), (1,2,3)) ) mx2 = np.matrix( ((1,2,3), (1,2,3), (1,2,3)) ) print "Matrix Multiplication" print mx1*mx2   Output: First 2-D Array: x1 [[1 2 3]  [1 2 3]  [1 2 3]] Second 2-D Array: x2 [[1 2 3]  [1 2 3]  [1 2 3]] Array Multiplication [[1 4 9]  [1 4 9]  [1 4 9]] Matrix Multiplication [[ 6 12 18]  [ 6 12 18]  [ 6 12 18]] Following is a simple program to demonstrate simple statistical functions given in NumPy: import numpy as np x = np.random.randn(10)   # Creates an array of 10 random elements print x mean = x.mean() print mean std = x.std() print std var = x.var() print var First Sample Output: [ 0.08291261  0.89369115  0.641396   -0.97868652  0.46692439 -0.13954144  -0.29892453  0.96177167  0.09975071  0.35832954] 0.208762357623 0.559388806817 0.312915837192 Second Sample Output: [ 1.28239629  0.07953693 -0.88112438 -2.37757502  1.31752476  1.50047537   0.19905071 -0.48867481  0.26767073  2.660184  ] 0.355946458357 1.35007701045 1.82270793415 The above programs are some simple examples of NumPy. SciPy package SciPy extends Python and NumPy support by providing advanced mathematical functions such as differentiation, integration, differential equations, optimization, interpolation, advanced statistical functions, equation solvers etc. SciPy is written on top of the NumPy array framework. SciPy has utilized the arrays and the basic operations on the arrays provided in NumPy and extended it to cover most of the mathematical aspects regularly required by scientists and engineers for their applications. In this article we will cover examples of some basic functionality. Optimization package The optimization package in SciPy provides facility to solve univariate and multivariate minimization problems. It provides solutions to minimization problems using a number of algorithms and methods. The minimization problem has wide range of application in science and commercial domains. Generally, we perform linear regression, search for function's minimum and maximum values, finding the root of a function, and linear programming for such cases. All these functionalities are supported by the optimization package.  Interpolation package A number of interpolation methods and algorithms are provided in this package as built-in functions. It provides facility to perform univariate and multivariate interpolation, one dimensional and two dimensional Splines etc. We use univariate interpolation when data is dependent of one variable and if data is around more than one variable then we use multivariate interpolation. Besides these functionalities it also provides additional functionality for Lagrange and Taylor polynomial interpolators. Integration and differential equations in SciPy Integration is an important mathematical tool for scientific computations. The SciPy integrations sub-package provides functionalities to perform numerical integration. SciPy provides a range of functions to perform integration on equations and data. It also has ordinary differential equation integrator. It provides various functions to perform numerical integrations using a number of methods from mathematics using numerical analysis. Stats module SciPy Stats module contains a functions for most of the probability distributions and wide range or statistical functions. Supported probability distributions include various continuous distribution, multivariate distributions and discrete distributions. The statistical functions range from simple means to the most of the complex statistical concepts, including skewness, kurtosis chi-square test to name a few. Clustering package and Spatial Algorithms in SciPy Clustering analysis is a popular data mining technique having wide range of application in scientific and commercial applications. In Science domain biology, particle physics, astronomy, life science, bioinformatics are few subjects widely using clustering analysis for problem solution. Clustering analysis is being used extensively in computer science for computerized fraud detection, security analysis, image processing etc. The clustering package provides functionality for K-mean clustering, vector quantization, hierarchical and agglomerative clustering functions. The spatial class has functions to analyze distance between data points using triangulations, Voronoi diagrams, and convex hulls of a set of points. It also has KDTree implementations for performing nearest-neighbor lookup functionality. Image processing in SciPy           SciPy provides support for performing various image processing operations including basic reading and writing of image files, displaying images, simple image manipulations operations such as cropping, flipping, rotating etc. It has also support for image filtering functions such as mathematical morphing, smoothing, denoising and sharpening of images. It also supports various other operations such as image segmentation by labeling pixels corresponding to different objects, Classification, Feature extraction for example edge detection etc. Sample SciPy programs In the subsequent subsections we will discuss some example programs using SciPy modules and packages. We start with a simple program performing standard statistical computations. After this, we will discuss a program performing finding a minimal solution using optimizations. At last we will discuss image processing programs. Statistics using SciPy The stats module of SciPy has functions to perform simple statistical operations and various probability distributions. The following program demonstrates simple statistical calculations using SciPy stats.describe function. This single function operates on an array and returns number of elements, minimum value, maximum value, mean, variance, skewness and kurtosis. import scipy as sp import scipy.stats as st s = sp.randn(10) n, min_max, mean, var, skew, kurt = st.describe(s) print("Number of elements: {0:d}".format(n)) print("Minimum: {0:3.5f} Maximum: {1:2.5f}".format(min_max[0], min_max[1])) print("Mean: {0:3.5f}".format(mean)) print("Variance: {0:3.5f}".format(var)) print("Skewness : {0:3.5f}".format(skew)) print("Kurtosis: {0:3.5f}".format(kurt)) Output: Number of elements: 10 Minimum: -2.00080 Maximum: 0.91390 Mean: -0.55638 Variance: 0.93120 Skewness : 0.16958 Kurtosis: -1.15542 Optimization in SciPY Generally, in mathematical optimization a non convex function called Rosenbrock function is used to test the performance of the optimization algorithm. The following program is demonstrating the minimization problem on this function. The Rosenbrock function of N variable is given by following equation and it has minimum value 0 at xi =1. The program for the above function is: import numpy as np from scipy.optimize import minimize   # Definition of Rosenbrock function def rosenbrock(x):      return sum(100.0*(x[1:]-x[:-1]**2.0)**2.0 + (1-x[:-1])**2.0)   x0 = np.array([1, 0.7, 0.8, 2.9, 1.1]) res = minimize(rosenbrock, x0, method = 'nelder-mead', options = {'xtol': 1e-8, 'disp': True})   print(res.x) Output is: Optimization terminated successfully.          Current function value: 0.000000          Iterations: 516          Function evaluations: 827 [ 1.  1.  1.  1.  1.] The last line is the output of print(res.x) where all the elements of array are 1. Image processing using SciPy Following two programs are developed to demonstrate the image processing functionality of SciPy. First of these program is simply displaying the standard test image widely used in the field of image processing called Lena. The second program is applying geometric transformation on this image. It performs image cropping and rotation by 45 %. The following program is displaying Lena image using matplotlib API. The imshow method renders the ndarrays into an image and the show method displays the image. from scipy import misc l = misc.lena() misc.imsave('lena.png', l) import matplotlib.pyplot as plt plt.gray() plt.imshow(l) plt.show() Output: The output of the above program is the following screen shot: The following program is performing geometric transformation. This program is displaying transformed images and along with the original image as a four axis array. import scipy from scipy import ndimage import matplotlib.pyplot as plt import numpy as np   lena = scipy.misc.lena() lx, ly = lena.shape crop_lena = lena[lx/4:-lx/4, ly/4:-ly/4] crop_eyes_lena = lena[lx/2:-lx/2.2, ly/2.1:-ly/3.2] rotate_lena = ndimage.rotate(lena, 45)   # Four axes, returned as a 2-d array f, axarr = plt.subplots(2, 2) axarr[0, 0].imshow(lena, cmap=plt.cm.gray) axarr[0, 0].axis('off') axarr[0, 0].set_title('Original Lena Image') axarr[0, 1].imshow(crop_lena, cmap=plt.cm.gray) axarr[0, 1].axis('off') axarr[0, 1].set_title('Cropped Lena') axarr[1, 0].imshow(crop_eyes_lena, cmap=plt.cm.gray) axarr[1, 0].axis('off') axarr[1, 0].set_title('Lena Cropped Eyes') axarr[1, 1].imshow(rotate_lena, cmap=plt.cm.gray) axarr[1, 1].axis('off') axarr[1, 1].set_title('45 Degree Rotated Lena')   plt.show() Output: The SciPy and NumPy are core of Python's support for scientific computing as they provide solid functionality of numerical computing. Symbolic computations using SymPy Computerized computations performed over the mathematical symbols without evaluating or changing their meaning is called as symbolic computations. Generally the symbolic computing is also called as computerized algebra and such computerized system are called computer algebra system. The following subsection has a brief and good introduction to SymPy. Computer Algebra System (CAS) Let us discuss the concept of CAS. CAS is a software or toolkit to perform computations on mathematical expressions using computers instead of doing it manually. In the beginning, using computers for these applications was named as computer algebra and now this concept is called as symbolic computing. CAS systems may be grouped into two types. First is the general purpose CAS and the second type is the CAS specific to particular problem. The general purpose systems are applicable to most of the area of algebraic mathematics while the specialized CAS are the systems designed for the specific area such as group theory or number theory. Most of the time, we prefer the general purpose CAS to manipulate the mathematical expressions for scientific applications. Features of a general purpose CAS Various desired features of a general purpose computer algebra system for scientific applications are as: A user interface to manipulate mathematical expressions. An interface for programming  and debugging Such systems requires simplification of various mathematical expressions hence, a simplifier is a most essential component of such computerized algebra system. The general purpose CAS system must support exhaustive set of functions to perform various mathematical operations required by any algebraic computations Most of the applications perform extensive computations an efficient memory management is highly essential. The system must provide support to perform mathematical computations on high precision numbers and large quantities. A brief idea of SymPy SymPy is an open source and Python based implementation of computerized algebra system (CAS). The philosophy behind the SymPy development is to design and develop a CAS having all the desired features yet its code as simple as possible so that it will be highly and easily extensible. It is written completely in Python and do not requires any external library. The basic idea about using SymPy is the creation and manipulation of expressions. Using SymPy, the user represents mathematical expressions in Python language using SymPy classes and objects. These expressions are composed of numbers, symbols, operators, functions etc. The functions are the modules to perform a mathematical functionality such as logarithms, trigonometry etc. The development of SymPy was started by Ondřej Čertíkin August 2006. Since then, it has been grown considerably with the contributions more than hundreds of the contributors. This library now consists of 26 different integrated modules. These modules have capability to perform computations required for basic symbolic arithmetic, calculus, algebra, discrete mathematics, quantum physics, plotting and printing with the option to export the output of the computations to LaTeX and other formats. The capabilities of SymPy can be divided into two categories as core capability and advanced capabilities as SymPy library is divided into core module with several advanced optional modules. The various supported functionality by various modules are as follows: Core capabilities The core capability module supports basic functionalities required by any mathematical algebra operations to be performed. These operations include basic arithmetic like multiplications, addition, subtraction and division, exponential etc. It also supports simplification of expressions to simplify complex expressions. It provides the functionality of expansion of series and symbols. Core module also supports functions to perform operations related to trigonometry, hyperbola, exponential, roots of equations, polynomials, factorials and gamma functions, logarithms etc. and a number of special functions for B-Splines, spherical harmonics, tensor functions, orthogonal polynomials etc. There is strong support also given for pattern matching operations in the core module. Core capabilities of the SymPy also include the functionalities to support substitutions required by algebraic operations. It not only supports the high precision arithmetic operations over integers, rational and gloating point numbers but also non-commutative variables and symbols required in polynomial operations. Polynomials Various functions to perform polynomial operations belong to the polynomial module. These functions includes basic polynomial operations such as division, greatest common divisor (GCD) least common multiplier (LCM), square-free factorization, representation of  polynomials with symbolic coefficients, some special operations like computation of resultant, deriving trigonometric identities, Partial fraction decomposition, facilities for Gröbner basis over polynomial rings and fields. Calculus Various functionalities supporting different operations required by basic and advanced calculus are provided in this module. It supports functionalities required by limits, there is a limit function for this. It also supports differentiation and integrations and series expansion, differential equations and calculus of finite differences. SymPy is also having special support for definite integrals and integral transforms. In differential it supports numerical differential, composition of derivatives and fractional derivatives.  Solving equations Solver is the name of the SymPy module providing equations solving functionality. This module supports solving capabilities for complex polynomials, roots of polynomials and solving system of polynomial equations. There is a function to solve the algebraic equations. It not only provides support for solutions for differential equations including ordinary differential equations, some forms of partial differential equations, initial and boundary values problems etc. but also supports solution of difference equations. In mathematics, difference equation is also called recurrence relations, that is an equation that recursively defines a sequence or multidimensional array of values. Discrete math Discrete mathematics includes those mathematical structures which are discrete in nature rather than the continuous mathematics like calculus. It deals with the integers, graphs, statements from logic theory etc. This module has full support for binomial coefficient, products, summations etc. This module also supports various functions from number theory including residual theory, Euler's Totient, partition and a number of functions dealing with prime numbers and their factorizations. SymPy also supports creation and manipulations of logic expressions using symbolic and Boolean values. Matrices SymPy has a strong support for various operations related to the matrices and determinants. Matrix belongs to linear algebra category of mathematics. It supports creation of matrix, basic matrix operations like multiplication, addition, matrix of zeros and ones, creation of random matrix and performing operations on matrix elements. It also supports special functions line computation of Hessian matrix for a function, Gram-Schmidt process on set of vectors, Computation of Wronskian for matrix of functions etc. It has also full support for Eigenvalues/eigenvectors, matrix inversion, solution of matrix and determinants.  For computing determinants of the matrix, it also supports Bareis' fraction-free algorithm and berkowitz algorithms besides the other methods. For matrices it also supports nullspace calculation, cofactor expansion tools, derivative calculation for matrix elements, calculation of dual of matrix etc. Geometry SymPy is also having module that supports various operations associated with the two-dimensional (2-D) geometry. It supports creation of various 2-D entity or objects such as point, line, circle, ellipse, polygon, triangle, ray, segment etc. It also allows us to perform query on these entities such as area of some of the suitable objects line ellipse/ circle or triangle, intersection points of lines etc. It also supports other queries line tangency determination, finding similarity and intersection of entities. Plotting There is a very good module that allows us to draw two-dimensional and three-dimensional plots. At present, the plots are rendered using the matplotlib package. It also supports other packages such as TextBackend, Pyglet, textplot etc.  It has a very good interactive interface facility of customizations and plotting of various geometric entities. The plotting module has the following functions: Plotting 2-D line plots Plotting of 2-D parametric plots. Plotting of 2-D implicit and region plots. Plotting of 3-D plots of functions involving two variables. Plotting of 3-D line and surface plots etc. Physics There is a module to solve the problem from Physics domain. It supports functionality for mechanics including classical and quantum mechanics, high energy physics. It has functions to support Pauli Algebra, quantum harmonic oscillators in 1-D and 3-D. It is also having functionality for optics. There is a separate module that integrates unit systems into SymPy. This will allow users to select the specific unit system for performing his/ her computations and conversion between the units. The unit systems are composed of units and constant for computations. Statistics The statistics module introduced in SymPy to support the various concepts of statistics required in mathematical computations. Apart from supporting various continuous and discrete statistical distributions, it also supports functionality related to the symbolic probability. Generally, these distributions support functions for random number generations in SymPy. Printing SymPy is having a module for provide full support for Pretty-Printing. Pretty-print is the idea of conversions of various stylistic formatting into the text files such as source code, text files and markup files or similar content. This module produces the desired output by printing using ASCII and or Unicode characters. It supports various printers such as LATEX and MathML printer. It is also capable of producing source code in various programming languages such as c, Python or FORTRAN. It is also capable of producing contents using markup languages like HTML/ XML. SymPy modules The following list has formal names of the modules discussed in above paragraphs: Assumptions: assumption engine Concrete: symbolic products and summations Core: basic class structure: Basic, Add, Mul, Pow etc. functions: elementary and special functions galgebra: geometric algebra geometry: geometric entities integrals: symbolic integrator interactive: interactive sessions (e.g. IPython) logic: boolean algebra, theorem proving matrices: linear algebra, matrices mpmath: fast arbitrary precision numerical math ntheory: number theoretical functions parsing: Mathematica and Maxima parsers physics: physical units, quantum stuff plotting: 2D and 3D plots using Pyglet polys: polynomial algebra, factorization printing: pretty-printing, code generation series: symbolic limits and truncated series simplify: rewrite expressions in other forms solvers: algebraic, recurrence, differential statistics: standard probability distributions utilities: test framework, compatibility stuf There are numerous symbolic computing systems available in various mathematical toolkits. There are some proprietary software such as Maple/ Mathematica and there are some open source alternatives also such as Singular/ AXIOM. However, these products have their own scripting language, difficult to extend their functionality and having slow development cycle. Whereas SymPy is highly extensible, designed and developed in Python language and open source API that supports speedy development life cycle. Simple exemplary programs These are some very simple examples to get idea about the capacity of SymPy. These are less than ten lines of SymPy source codes which covers topics ranging from basis symbol manipulations to limits, differentiations and integrations. We can test the execution of these programs on SymPy live running SymPy online on Google App Engine available on http://live.sympy.org/. Basic symbol manipulation The following code is defines three symbols, an expression on these symbols and finally prints the expression. import sympy a = sympy.Symbol('a') b = sympy.Symbol('b') c = sympy.Symbol('c') e = ( a * b * b + 2 * b * a * b) + (a * a + c * c) print e Output: a**2 + 3*a*b**2 + c**2     (here ** represents power operation). Expression expansion in SymPy The following program demonstrates the concept of expression expansion. It defines two symbols and a simple expression on these symbols and finally prints the expression and its expanded form. import sympy a = sympy.Symbol('a') b = sympy.Symbol('b') e = (a + b) ** 4 print e print e.expand() Output: (a + b)**4 a**4 + 4*a**3*b + 6*a**2*b**2 + 4*a*b**3 + b**4 Simplification of expression or formula The SymPy has facility to simplify the mathematical expressions. The following program is having two expressions to simplify and displays the output after simplifications of the expressions. import sympy x = sympy.Symbol('x') a = 1/x + (x*exp(x) - 1)/x simplify(a) simplify((x ** 3 +  x ** 2 - x - 1)/(x ** 2 + 2 * x + 1)) Output: ex x – 1 Simple integrations The following program is calculates the integration of two simple functions. import sympy from sympy import integrate x = sympy.Symbol('x') integrate(x ** 3 + 2 * x ** 2 + x, x) integrate(x / (x ** 2 + 2 * x), x) Output: x**4/4+2*x**3/3+x**2/2 log(x + 2) Summary In this article, we have discussed the concepts, features and selective sample programs of various scientific computing APIs and toolkits. The article started with a discussion of NumPy and SciPy. After covering NymPy, we have discussed concepts associated with symbolic computing and SymPy. In the remaining article we have discussed the Interactive computing and data analysis & visualization alog with their APIs or toolkits. IPython is the python toolkit for interactive computing. We have also discussed the data analysis package Pandas and the data visualization API names Matplotlib. Resources for Article: Further resources on this subject: Optimization in Python [article] How to do Machine Learning with Python [article] Bayesian Network Fundamentals [article]
Read more
  • 0
  • 0
  • 9389

article-image-api-detail
Packt
20 Aug 2015
25 min read
Save for later

The API in Detail

Packt
20 Aug 2015
25 min read
In this article by Hugo Solis, author of book, Kivy Cookbook, we will learn to create simple API with the help of App class. We will also learn how to load images of asynchronous data, parsing of data, exception handling, utils applications and use of factory objects. Working of audio, video and camera in Kivy will be explained in this article. Also we will learn text manipulation and and usage of spellcheck option. We will see how to add different effects to the courser in this article. (For more resources related to this topic, see here.) Kivy is actually an API for Python, which lets us create cross-platform apps. An application programming interface (API) is a set of routines, protocols, and tools to build software applications. Generally, we call Kivy as a framework because it also has procedures and instructions, such as the Kv language, which are not present in Python. Frameworks are environments that come with support programs, compilers, code libraries, tool sets, and APIs. In this article, we want to review the Kivy API reference. We will go through some useful classes of the API. Every time we import a Kivy package, we will be dealing with an API. Even though the usual imports are from kivy.uix, there are more options and classes in the Kivy API. The Kivy developers have created the API reference, which you can refer to online at http://kivy.org/docs/api-kivy.html for an exhaustive information. Getting to know the API Our starting point is going to be the App class, which is the base to create Kivy applications. In this recipe, we are going to create a simple app that uses some resources from this class. Getting ready It is important to see the role of the App class in the code. How to do it… To complete this recipe, we will create a Python file to make the resources present in the App class. Let's follow these steps: Import the kivy package. Import the App package. Import the Widget package. Define the MyW() class. Define the e1App() class instanced as App. Define the build() method and give an icon and a title to the app. Define the on_start() method. Define the on_pause() method. Define the on_resume() method. Define the on_stop() method. End the app with the usual lines. import kivy from kivy.app import App from kivy.uix.widget import Widget class MyW(Widget): pass class e1App(App): def build(self): self.title = 'My Title' self.icon = 'f0.png' returnMyW() def on_start(self): print("Hi") return True def on_pause(self): print("paused") return True def on_resume(self): print("active") pass def on_stop(self): print("Bye!") pass if __name__ == '__main__': e1App().run() How it works… In the second line, we import the most common kivy package. This is the most used element of the API because it permits to create applications. The third line is an importation from kivy.uix that could be the second most used element, because the majority of the widgets are in there. In the e1app class, we have the usual build() method where we have the line: self.title = 'My Title' We are providing a title to the app. As you can remember, the default title should be e1 because of the class's name, but now we are using the title that we want. We have the next line: self.icon = 'f0.png' We are giving the app an icon. The default is the Kivy logo, but with this instruction, we are using the image in the file f0.png. In addition, we have the following method: def on_start(self): print("Hi") return True It is in charge of all actions performed when the app starts. In this case, it will print the word Hi in the console. The method is as follows: def on_pause(self): print("paused") return True This is the method that is performed when the app is paused when it is taken off from RAM. This event is very common when the app is running in a mobile device. You should return True if your app can go into pause mode, otherwise return False and your application will be stopped. In this case, we will print the word paused in the console, but it is very important that you save important information in the long-term memory, because there can be errors in the resume of the app and most mobiles don't allow real multitasking and pause apps when switching between them. This method is used with: def on_resume(self): print("active") pass The on_resume method is where we verify and correct any error in the sensible data of the app. In this case, we are only printing the word active in the console. The last method is: def on_stop(self): print("Bye!") pass It is where all the actions are performed before the app closes. Normally, we save data and take statistics in this method, but in this recipe, we just say Bye! in the console. There's more… There is another method, the load_kv method, that you can invoke in the build method, which permits to make our own selection of the KV file to use and not the default one. You only have to add follow line in the build() method: self.load_kv(filename='e2.kv') See also The natural way to go deeper in this recipe is to take a look at the special characteristics that the App has for the multiplatform support that Kivy provides. Using the asynchronous data loader An asynchronous data loader permits to load images even if its data is not available. It has diverse applications, but the most common is to load images from the Internet, because this makes our app always useful even in the absence of Web connectivity. In this recipe, we will generate an app that loads an image from the Internet. Getting ready We did image loading from the Internet. We need an image from the Web, so find it and grab its URL. How to do it… We need only a Python file and the URL in this recipe. To complete the recipe: Import the usual kivy package. Import the Image and Loader packages. Import the Widget package. Define the e2App class. Define the _image_Loaded() method, which loads the image in the app. Define the build() method. In this method, load the image in a proxy image. Define the image variable instanced as Image(). Return the image variable to display the load image: import kivy kivy.require('1.9.0') from kivy.app import App from kivy.uix.image import Image from kivy.loader import Loader class e2App(App): def _image_loaded(self, proxyImage): if proxyImage.image.texture: self.image.texture = proxyImage.image.texture def build(self): proxyImage = Loader.image( 'http://iftucr.org/IFT/ANL_files/artistica.jpg') proxyImage.bind(on_load=self._image_loaded) self.image = Image() return self.image if __name__ == '__main__': e2App().run() How it works… The line that loads the image is: proxyImage = Loader.image( 'http://iftucr.org/IFT/ANL_files/artistica.jpg') We assign the image to the proxyImage variable because we are not sure if the image exists or could be retrieved from the Web. We have the following line: proxyImage.bind(on_load=self._image_loaded) We bind the event on_load to the variable proxyImage. The used method is: def _image_loaded(self, proxyImage): if proxyImageifproxyImage.image.texture: self.image.texture = proxyImage.image.texture It verifies whether the image is loaded or not; if not, then it does not change the image. This is why, we said that this will load in an asynchronous way. There's more… You can also load an image from a file in the traditional way. We have the following line: proxyImage = Loader.image( 'http://iftucr.org/IFT/ANL_files/artistica.jpg') Replace the preceding line with: proxyImage = Loader.image('f0.png') Here, f0.png is the name of the file to load. Logging objects The log in any software is useful for many aspects, one of them being exception handling. Kivy is always logging information about its performance. It creates a log file of every running of our app. Every programmer knows how helpful logging is for software engineering. In this recipe, we want to show information of our app in that log. How to do it… We will use a Python file with the MyW() usual class where we will raise an error and display it in the Kivy log. To complete the recipe, follow these steps: Import the usual kivy package. Import the Logger packages. Define the MyW() class. Trigger an info log. Trigger a debug log. Perform an exception. Trigger an exception log: import kivy kivy.require('1.9.0') from kivy.app import App from kivy.uix.widget import Widget from kivy.logger import Logger class MyW(Widget): Logger.info('MyW: This is an info message.') Logger.debug('MyW: This is a debug message.') try: raise Exception('exception') except Exception: Logger.exception('Something happened!') class e3App(App): def build(self): return MyW() if __name__ == '__main__': e3App().run() How it works… In this recipe, we are creating three logs. The first in the line is: Logger.info('MyW: This is an info message.') This is an info log, which is associated just with the supplementary information. The label MyW is just a convention, but you could use it in whatever way you like. Using the convention, we track where the log was performed in the code. We will see a log made by that line as: [INFO ] [MyW ] This is an info message The next line also performs a log notation: Logger.debug('MyW: This is a debug message.') This line will produce a debug log commonly used to debug the code. Consider the following line: Logger.exception('Something happened!') This will perform an error log, which would look like: [ERROR ] Something happened! In addition to the three present in this recipe, you can use trace, warning, and critical logging objects. There's more… We also have the trace, warning, error, and critical methods in the Logger class that work similarly to the methods described in this recipe. The log file by default is located in the .kivy/logs/ folder of the user running the app, but you can always change it in the Kivy configuration file. Additionally, you can access the last 100 messages for debugging purposes even if the logger is not enabled. This is made with the help of LoggerHistory as follows: from kivy.logger import LoggerHistory print(LoggerHistory.history) So, the console will display the last 100 logs. See also More information about logging can be found at http://kivy.org/docs/api-kivy.logger.html. Parsing Actually, Kivy has the parser package that helps in the CSS parsing. Even though it is not a complete parsing, it helps to parse instructions related to a framework. The recipe will show some that you could find useful in your context. How to do it… The parser package has eight classes, so we will work in Python to review all of them. Let's follow the next steps: Import the parser package. from kivy.parser import * Parse a color from a string. parse_color('#090909') Parse a string to a string. parse_string("(a,1,2)")a Parse a string to a boolean value. parse_bool("0") Parse a string to a list of two integers. parse_int2("12 54") Parse a string to a list of four floats. parse_float4('54 87.13 35 0.9') Parse a file name. parse_filename('e7.py') Finally, we have parse_int and parse_float, which are aliases of int and float, respectively. How it works… In the second step, we parse any of the common ways to define a color (that is, RGB(r, g, b), RGBA(r, g, b, a), aaa, rrggbb, #aaa or #rrggbb) to a Kivy color definition. The third step takes off the single or double quotes of the string. The fourth step takes a string True for 1 and False for 0 and parses it to its respective boolean value. The last step is probably very useful because it permits verification if that file name is a file available to be used. If the file is found, the resource path is returned. See also To use a more general parser, you can use ply package for Python. Visit https://pypi.python.org/pypi/ply for further information. Applying utils There are some methods in Kivy that cannot be arranged in any other class. They are miscellaneous and could be helpful in some contexts. In this recipe, we will see how to use them. How to do it… In the spirit to show all the methods available, let's work directly in Python. To do the package tour, follow these steps: Import the kivy package. from kivy.utils import * Find the intersection between two lists. intersection(('a',1,2), (1,2)) Find the difference between two lists. difference(('a',1,2), (1,2)) Convert a tuple in a string. strtotuple("1,2") Transform a hex string color to a Kivy color. get_color_from_hex('#000000') Transform a Kivy color to a hex value. get_hex_from_color((0, 1, 0)) Get a random color. get_random_color(alpha='random') Evaluate if a color is transparent. is_color_transparent((0,0,0,0)) Limit the value between a minimum value and maximum value. boundary(a,1,2) Interpolate between two values. interpolate(10, 50, step=10) Mark a function as deprecated. deprecated(MyW) Get the platform where the app is running. <p>platform()</p> How it works… Almost every method presented in this recipe has a transparent syntax. Let's get some detail on two of the steps. The ninth step is the boundary method. It evaluates the value of a, and if this is between 1 and 2, it conserves its value; if it is lower than 1, the method returns 1; if it is greater than 2, the method returns 2. The eleventh step is associated with the warning by using the function MyW; when this function is called the first time, the warning will be triggered. See also If you want to explore this package in detail, you can visit http://kivy.org/docs/api-kivy.utils.html. Leveraging the factory object The factory object represents the last step to create our own widgets because the factory can be used to automatically register any class or module and instantiate classes from any place in the app. This is a Kivy implementation of the factory pattern where a factory is an object to create other objects. This also opens a lot of possibilities to create dynamic codes in Kivy. In this recipe, we will register one of our widgets. Getting ready We will use an adaptation of the code to register the widget as a factory object. Copy the file in the same location of this recipe with the name e7.py. How to do it… In this recipe, we will use one of our simple Python files where we will register our widget using the factory package. Follow these steps: Import the usual kivy packages. In addition, import the Factory package. Register the MyWidget In the build() method of the usual e8App, return Factory.MyWidget. import kivy kivy.require('1.9.0') from kivy.app import App from kivy.uix.widget import Widget from kivy.factory import Factory Factory.register('MyWidget', module='e7') classMyW(Widget): pass class e8App(App): def build(self): returnFactory.MyWidget() if __name__ == '__main__': e8App().run() How it works… Let us note how the magic is done in the following line: Factory.register('MyWidget', module='e7') This line creates the factory object named MyWidget, and let's use it as we want. See how e7 is the name of the file. Actually, this sentence also will create a file named e7.pyc, which we can use as a replacement of the file e7.py if want to distribute our code; now it is not necessary to give the e7.py, since just the e7.pyc file is enough. There's more… This registration is actually permanent, so if you wish to change the registration in the same code, you need to unregister the object. For example, see the following: Factory.unregister('MyWidget') Factory.register('MyWidget', cls=CustomWidget) New_widget = Factory.MyWidget()   See also If you want to know more about this amazing package, you can visit http://kivy.org/docs/api-kivy.factory.html. Working with audio Nowadays, the audio integration in our app is vital. You could not realize a video game without audio or an app that does not use multimedia. We will create a sample with just one button which when pressed plays an audio. Getting ready We need an audio file in this recipe in the traditional audio formats (mp3, mp4, wav, wma, b-mtp, ogg, spx, midi). If you do not have any, you always can get one from sites such as https://www.freesound.org. How to do it… We will use a simple Python file with just one widget to play the audio file. To complete the recipe, let's follow these steps: Import the usual kivy package. Import the SoundLoader package. Define the MyW() class. Define the __init__() method. Create a button with the label Play. Bind the press action with the press() method. Add the widget to the app. Define the press() method. Call the SoundLoader.sound() method for your audio file. Play it with the play()method: import kivy kivy.require('1.9.0') from kivy.app import App from kivy.uix.widget import Widget from kivy.uix.button import Button from kivy.core.audio import SoundLoader class MyW(Widget): def __init__(self, **kwargs): super(MyW, self).__init__(**kwargs) b1=Button(text='Play') b1.bind(on_press=self.press) self.add_widget(b1) def press(self, instance): sound = SoundLoader.load('owl.wav') if sound: print("Sound found at %s" % sound.source) print("Sound is %.3f seconds" % sound.length) sound.play() print('playing') class e4App(App): def build(self): returnMyW() if __name__ == '__main__': e4App().run() How it works… In this recipe, the audio file is a load in the line: sound = SoundLoader.load('owl.wav') Here, we use a .wav format. Look at the following line: if sound: We handle that the file has been correctly loaded. We have the next line: print("Sound found at %s" % sound.source) This prints the name of the file that has been loaded in the console. The next line prints the duration of the file in seconds. See the other line: sound.play() It is where the file is played in the app. There's more… Also, you can use the seek() and stop() methods to navigate to the audio file. Let's say that you want to play the audio after the first minute, you will use: Sound.seek(60) The parameter received by the seek() method must be in seconds. See also If you need more control of the audio, you should visit http://kivy.org/docs/api-kivy.core.audio.html. Working with video The video reproduction is a useful tool for any app. In this app, we will load a widget to reproduce a video file in our app. Getting ready It is necessary to have a video file in the usual format to be reproduced in our app (.avi, .mov, .mpg, .mp4, .flv, .wmv, .ogg). If you do not have one, you can visit https://commons.wikimedia.org/wiki/Main_Page to get free media. How to do it… In this recipe, we are going to use a simple Python file to create our app within a player widget. To complete the task, follow these: Import the usual kivy packages. Import the VideoPlayer package. Define the MyW() class. Define the __init__() method. Define videoplayer with your video. Add the video player to the app. import kivy kivy.require('1.9.0') from kivy.app import App from kivy.uix.widget import Widget from kivy.uix.videoplayer import VideoPlayer class MyW(Widget): def __init__(self, **kwargs): super(MyW, self).__init__(**kwargs) player= VideoPlayer( source='GOR.MOV',state='play', options={'allow_stretch': True}, size=(600,600)) self.add_widget(player) class e5App(App): def build(self): returnMyW() if __name__ == '__main__': e5App().run() How it works… In this recipe, the most important line is: player= VideoPlayer( source='GOR.MOV',state='play', options={'allow_stretch': True}, size=(600,600)) This line loads the file, sets some options, and gives the size to the widget. The option 'allow stretch' let's you modify the image of the video or not. In our recipe, 'allow stretch' is permitted, so the images will be maximized to fit in the widget. There's more… You can also integrate subtitles or annotations to the video in an easy way. You only need a JSON-based file with the same name as the video, in the same location with .jsa extension. For example, let's use this content in the .jsa file: [ {"start": 0, "duration": 2,
 "text": "Here your text"}, {"start": 2, "duration": 2,
"bgcolor": [0.5, 0.2, 0.4, 0.5],
 "text": "You can change the background color"} ] The "start" sentence locates in which second the annotation will show up in the video and the "duration" sentence gives the time in seconds that the annotation will be in the video. See also There are some apps that need more control of the video, so you can visit http://kivy.org/docs/api-kivy.core.video.html for better understanding. Working with a camera It is very common that almost all our personal devices have a camera. So you could find thousands of ways to use a camera signal in your app. In this recipe, we want to create an app that takes control of the camera present in a device. Getting ready Actually, you need to have the correct installation of the packages that permits you to interact with a camera. You can review http://kivy.org/docs/faq.html#gstreamer-compatibility to check if your installation is suitable. How to do it… We are going to use the Python and KV files in this recipe. The KV file will deal with the camera and button to interact with it. The Python code is one of our usual Python files with the definition of the root widget. Let's follow these steps: In the KV file, define the <MyW> rule. In the rule, define BoxLayout with a vertical orientation. Inside the Layout, define the camera widget with play property as false. Also, define the ToggleButton with the press property swifts between play and not play: <MyW>: BoxLayout: orientation: 'vertical' Camera: id: camera play: False ToggleButton: text: 'Play' on_press: camera.play = not camera.play size_hint_y: None height: '48dp' In the Python file, import the usual packages. Define the MyW() class instanced as BoxLayout: import kivy kivy.require('1.9.0') from kivy.app import App from kivy.uix.widget import Widget from kivy.uix.boxlayout import BoxLayout class MyW(BoxLayout): pass class e6App(App): def build(self): return MyW() if __name__ == '__main__': e6App().run() There's more… If we have a device with more than one camera, for example, the handheld device front and rear camera, you can use the index property to switch between them. We have the following line: id: camera Add this line in the KV file: index: 0 The preceding line is to select the first camera, index:1 for the second, and so on. Using spelling Depending on the kind of app that we will develop, we will need to spellcheck text provided by the user. In the Kivy API, there is a package to deal with it. In this recipe, we will give an example of how to do it. Getting ready If you are not using Mac OS X (or OS X as Apple called now), we will need to install the Python package: PyEnchant. For the installation, let's use the pip tool as follows: pip install PyEnchant How to do it… Because this recipe could use it in different contexts, let's work directly in Python. We want to make some suggestions to the word misspelled. To complete the task, follow these steps: Import the Spelling package. from kivy.core.spelling import Spelling Instance the object s as Spelling(). s = Spelling() List the available language. s.list_languages() In this case, select U.S. English. s.select_language('en_US') Ask for a suggestion to the object s. s.suggest('mispell') How it works… The first four steps actually set the kind of suggestion that we want. The fifth step makes the suggestion in line: s.suggest('mispell') The output of the expression is: [u'misspell', u'ispell'] The output is in the order of the used frequency, so misspell is the most probable word that the user wanted to use. Adding effects Effects are one of the most important advances in the computer graphics field. The physics engines help create better effects, and they are under continuous improvement. Effects are pleasing to the end user. They change the whole experience. The kinetic effect is the mechanism that Kivy uses to approach this technology. This effect can be used in diverse applications from the movement of a button to the simulation of real graphical environments. In this recipe, we will review how to set the effect to use it in our apps. Getting ready We are going to use some concepts from physics in this recipe, so it's necessary to have the clear basics. You should start reading about this on Wikipedia at http://en.wikipedia.org/wiki/Acceleration. How to do it… As the applications of this effect are as creative as you want, we are going to work directly in Python to set up the effect. Let's follow these steps: Import the KineticEffect package. from kivy.effects.kinetic import KineticEffect Instance the object effect as KineticEffect(). effect = KineticEffect() Start the effect at second 10. effect.start(10) Update the effect at second 15. effect.update(15) Update the effect again at second 30. effect.update(30) You can always add friction to the movement. effect.friction You can also update the velocity. effect.update_velocity(30) Stop the effect at second 48. effect.stop(48) Get the final velocity. effect.velocity() Get the value in seconds. effect.value() How it works… What we are looking for in this recipe is step 9: effect.velocity() The final velocity is how we can use to describe the movement of any object in a realistic way. As the distances are relatively fixed in the app, you need the velocity to describe any motion. We could incrementally repeat the steps to vary the velocity. There's more… There are other three effects based on the Kinetic effect, which are: ScrollEffect: This is the base class used to implement an effect. It only calculates scrolling and overscroll. DampedScrollEffect: This uses the overscroll information to allow the user to drag more than is expected. Once the user stops the drag, the position is returned to one of the bounds. OpacityScrollEffect: This uses the overscroll information to reduce the opacity of the ScrollView widget. When the user stops the drag, the opacity is set back to 1. See also If you want to go deeper in this topic, you should visit: http://kivy.org/docs/api-kivy.effects.html. Advanced text manipulation Text is one of the most commonly used contents used in the apps. The recipe will create an app with a label widget where we will use text rendering to make our Hello World. How to do it… We are going to use one simple Python files that will just show our Hello World text. To complete the recipe: Import the usual kivy packages. Also, import the label package. Define the e9app class instanced as app. Define the method build() to the class. Return the label widget with our Hello World text. import kivy kivy.require('1.9.0') # Code tested in this version! from kivy.app import App from kivy.uix.label import Label class e9App(App): def build(self): return Label(text='Hello [ref=world][color=0000ff]World[/color][/ref]', markup=True, font_size=80, font_name='DroidSans') if __name__ == '__main__': e9App().run() How it works… Here is the line: return Label(text='Hello [ref=world][color=0000ff]World[/color][/ref]', markup=True, font_size=80, font_name='DroidSans') This is the place where the rendering is done. Look at the text parameter where the token [ref] permits us to reference that specific part of the text (for example, to detect a click in the word World) the token [color] gives a particular color to that part of the text. The parameter markup=True allows the use of tokens. The parameters font_size and font_name will let you select the size and font to use for the text. There's more… There are others parameter with evident functions that the label widget can receive like: bold=False italic=False halign=left valign=bottom shorten=False text_size=None color=None line_height=1.0 Here, they have been evaluated with their default values. See also If you are interested in creating even more varieties of texts, you can visit http://kivy.org/docs/api-kivy.uix.label.html#kivy.uix.label.Labelor http://kivy.org/docs/api-kivy.core.text.html. Summary In this article we learned many things to change the API of our app. We learned to manage images of asynchronous data, to add different effects and to deal with the text visible on the screen. We used audio, video data and camera to create our app. We understood some concept such as exception handling, use of factory objects and parsing of data. Resources for Article: Further resources on this subject: Subtitles – tracking the video progression[article] Images, colors, and backgrounds[article] Sprites, Camera, Actions![article]
Read more
  • 0
  • 0
  • 7170

article-image-improving-inspector-property-and-decorator-drawers
Packt
19 Aug 2015
10 min read
Save for later

Improving the Inspector with Property and Decorator Drawers

Packt
19 Aug 2015
10 min read
In this article by Angelo Tadres, author of the book Extending Unity with Editor Scripting, we will explore a way to create a custom GUI for our properties using Property Drawers. If you've worked on a Unity project for a long time, you know that the bigger your scripts get, the more unwieldy they become. All your public variables take up space in the Inspector Window, and as they accumulate, they begin to convert into one giant and scary monster. Sometimes, organization is the clue. So, in this article, you will learn how to improve your inspectors using Property and Decorator Drawers. (For more resources related to this topic, see here.) A Property Drawer is an attribute that allows you to control how the GUI of a Serializable class or property is displayed in the inspector window. An Attribute is a C# way of defining declarative tags, which you can place on certain entities in your source code to specify additional information. The information that attributes contain is retrieved at runtime through reflection. This approach significantly reduces the amount of work you have to do for the GUI customization because you don't need to write an entire Custom Inspector; instead, you can just apply appropriate attributes to variables in your scripts to tell the editor how you want those properties to be drawn. Unity has several Property Drawers implemented by default. Let's take a look at one of them called Range: using UnityEngine; public class RangeDrawerDemo : MonoBehaviour { [Range (0, 100)] public int IntValue = 50; } If you attach this script to a game object and then select it, you will see the following: Using the Range attribute, we rendered a slider that moves between 0 and 100 instead of the common int field. This is valuable in terms of validating the input for this field. Avoid possible mistakes such as using a negative value to define the radius of a sphere collider, and so on. Let's take a look to the rest of the built-in Property Drawers. Built-in Property Drawers The Unity documentation has information about the built-in property drawers, but there is no such place where you can check all the available ones listed. In this section, we will resolve this. Range The Range attribute restricts a float or int variable in a script to a specific range. When this attribute is used, the float or int will be shown as a slider in the inspector instead of the default number field: public RangeAttribute(float min, float max); public RangeAttribute(float min, float max); [Range (0, 1)] public float FloatRange = 0.5f; [Range (0, 100)] public int IntRange = 50; You will get the following output: Multiline The Multiline attribute is used to show a string value in a multiline text area. You can decide the number of lines of text to make room for. The default is 3 and the text doesn't wrap. The following is an example of the Multiline attribute: public MultilineAttribute(); public MultilineAttribute(int lines); [Multiline (2)] public string StringMultiline = "This text is using a multiline property drawer"; You will get the following output: TextArea The TextArea attribute allows a string to be edited with a height-flexible and scrollable text area. You can specify the minimum and maximum values; a scrollbar will appear if the text is bigger than the area available. Its behavior is better compared to Multiline. The following is an example of the TextArea attribute: public TextAreaAttribute(); public TextAreaAttribute(int minLines, int maxLines); [TextArea (2, 4)] public string StringTextArea = "This text is using a textarea property drawer"; You will get the following output: ContextMenu The ContextMenu attribute adds the method to the context menu of the component. When the user selects the context menu, the method will be executed. The method has to be nonstatic. In the following example, we call the method DoSomething, printing a log in the console: public ContextMenu(string name); [ContextMenu ("Do Something")] public void DoSomething() { Debug.Log ("DoSomething called..."); } You will get the following output: ContextMenuItem The ContextMenuItem attribute is used to add a context menu to a field that calls a named method. In the following example, we call a method to reset the value of the IntReset variable to 0: public ContextMenuItemAttribute(string name, string function); [ContextMenuItem("Reset this value", "Reset")] public int IntReset = 100; public void Reset() { IntReset = 0; } You will get the following output: Built-in Decorator Drawers There is another kind of drawer called Decorator Drawer. These drawers are similar in composition to the Property Drawers, but the main difference is that Decorator Drawers are designed to draw decoration in the inspector and are unassociated with a specific field. This means, while you can only declare one Property Drawer per variable, you can stack multiple decorator drawers in the same field. Let's take a look in the following built-in Decorator Drawers. Header This is the attribute that adds a header to some fields in the inspector: public HeaderAttribute(string header); [Header("This is a group of variables")] public int VarA = 10; public int VarB = 20; You will get the following output: Space The space attribute adds some spacing in the inspector: public SpaceAttribute(float height); public int VarC = 10; [Space(40)] public int VarD = 20; You will get the following output: Tooltip This Tooltip attribute specifies a tooltip for a field: public TooltipAttribute(string tooltip); [Tooltip("This is a tooltip")] public int VarE = 30; You will get the following output: Creating you own Property Drawers If you have a serializable parameter or structure that repeats constantly in your video game and you would like to improve how this renders in the Inspector, you can try to write your own Property Drawer. We will create a property drawer for an integer meant to be a variable to save time in seconds. This Property Drawer will draw a normal int field but also a label with the number of seconds converted to the m:s or h:m:s time format. To implement a Property Drawer, you must create two scripts: The attribute: This declares the attribute and makes it usable in your MonoBehaviour scripts. This will be part of your video game scripts. The drawer: This is responsible for rendering the custom GUI and handling the input of the user. This is placed inside a folder called Editor. The Editor folder is one of the several special folders Unity has. All scripts inside this folder will be treated as editor scripts rather than runtime scripts. For the first script, create one file inside your Unity project called TimeAttribute.cs and then add the following code: using UnityEngine; public class TimeAttribute : PropertyAttribute { public readonly bool DisplayHours; public TimeAttribute (bool displayHours = false) { DisplayHours = displayHours; } } Here we defined the name of the attribute and its parameters. You must create your attribute class extending from the PropertyAttribute class. The name of the class contains the suffix "attribute"; however, when you want to use the attribute over a certain property, the suffix is not needed. In this case, we will use Time and not TimeAttribute to use the property drawer. The TimeAttribute has an optional parameter called DisplayHours. The idea is to display a label under the int field with the time in m:s format by default; if the DisplayHours parameter is true, this will be displayed in h:m:s format. Now is the moment to implement the drawer. To do this, let's create a new script called TimeDrawer.cs inside an Editor folder: using UnityEngine; using UnityEditor; [CustomPropertyDrawer (typeof(TimeAttribute))] public class TimeDrawer : PropertyDrawer { public override float GetPropertyHeight (SerializedProperty property, GUIContent label) { return EditorGUI.GetPropertyHeight (property) * 2; } public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) { if (property.propertyType == SerializedPropertyType.Integer) { property.intValue = EditorGUI.IntField (new Rect (position.x, position.y, position.width, position.height / 2), label, Mathf.Abs(property.intValue)); EditorGUI.LabelField (new Rect (position.x, position.y + position.height / 2, position.width, position.height / 2), " ", TimeFormat (property.intValue)); } else { EditorGUI.LabelField (position, label.text, "Use Time with an int."); } } private string TimeFormat (int seconds) { TimeAttribute time = attribute as TimeAttribute; if (time.DisplayHours) { return string.Format ("{0}:{1}:{2} (h:m:s)", seconds / (60 * 60), ((seconds % (60 * 60)) / 60).ToString ().PadLeft(2,'0'), (seconds % 60).ToString ().PadLeft(2,'0')); } else { return string.Format ("{0}:{1} (m:s)", (seconds / 60).ToString (), (seconds % 60).ToString ().PadLeft(2,'0')); } } } Property Drawers don't support layouts to create a GUI; for this reason, the class you must use here is EditorGUI instead of EditorGUILayout. Using this class requires a little extra effort; you need to define the Rect function that will contain the GUI element each time you want to use it. The CustomPropertyDrawer attribute is part of the UnityEditor namespace, and this is what Unity uses to bind a drawer with a Property attribute. In this case, we passed the TimeAttribute. Your must extend the TimeAttribute from the PropertyDrawer class, and in this way, you will have access to the core methods to create Property Drawers: GetPropertyHeight: This method is responsible for handling the height of the drawer. You need to overwrite this method in order to use it. In our case, we force the size of the drawer to be double. OnGUI: This is where you place all the code related to rendering the GUI. You can create Decorator Drawers too. You just need to follow the same steps we performed to create a Property Drawer, but instead of extending your Drawer from PropertyDrawer, you need to extend from DecoratorDrawer. Also, you will have access to the variable attribute. This has a reference to the attribute class we created, and with this we can access to their variables. To test our code, create a new script called TimeDrawerDemo.cs and add the following code: using UnityEngine; using UnityEditor; [CustomPropertyDrawer (typeof(TimeAttribute))] public class TimeDrawer : PropertyDrawer { public override float GetPropertyHeight (SerializedProperty property, GUIContent label) { return EditorGUI.GetPropertyHeight (property) * 2; } public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) { if (property.propertyType == SerializedPropertyType.Integer) { property.intValue = EditorGUI.IntField (new Rect (position.x, position.y, position.width, position.height / 2), label, Mathf.Abs(property.intValue)); EditorGUI.LabelField (new Rect (position.x, position.y + position.height / 2, position.width, position.height / 2), " ", TimeFormat (property.intValue)); } else { EditorGUI.LabelField (position, label.text, "Use Time with an int."); } } private string TimeFormat (int seconds) { TimeAttribute time = attribute as TimeAttribute; if (time.DisplayHours) { return string.Format ("{0}:{1}:{2} (h:m:s)", seconds / (60 * 60), ((seconds % (60 * 60)) / 60).ToString ().PadLeft(2,'0'), (seconds % 60).ToString ().PadLeft(2,'0')); } else { return string.Format ("{0}:{1} (m:s)", (seconds / 60).ToString (), (seconds % 60).ToString ().PadLeft(2,'0')); } } } After compiling, attach this script to a game object. You will see this on the inspector: The time property uses the time attribute. We can check the three possible scenarios here: The attribute uses a property that is not an int The attribute has the variable DisplayHours = false The attribute has the variable DisplayHours = true A little change makes it easier to set up this data in our game objects. Summary In this article, we created a custom Property Drawer to be used in properties mean to store time in seconds, and in the process, you learned how these are implemented. We also explored the available built-in Property Drawers and Decorator Drawers in Unity. Applying this knowledge to your projects will enable you to add validation to sensible data in your properties and make your scripts more developer friendly. This will also allow you to have a professional look. Resources for Article: Further resources on this subject: Adding a Graphical User Interface [article] Animation features in Unity 5 [article] Event-driven Programming [article]
Read more
  • 0
  • 0
  • 9681
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at $19.99/month. Cancel anytime
article-image-finding-peace-rest
Packt
19 Aug 2015
22 min read
Save for later

Finding Peace in REST

Packt
19 Aug 2015
22 min read
In this article by Ken Doman, author of the book Mastering ArcGIS Server Development with JavaScript, we will discuss and build applications using the ArcGIS API for JavaScript. We've used different API tools to communicate with ArcGIS Server about its map services. But how does the API communicate with ArcGIS Server? (For more resources related to this topic, see here.) In this article, we'll focus on ArcGIS Server. We'll look at how it implements a REST interface. We'll review the ArcGIS REST API, outlined at http://resources.arcgis.com/en/help/arcgis-rest-api/#/The_ArcGIS_REST_API/02r300000054000000/. This describes the file structure and the format of the data that passes between the server and the browser. Finally, we'll extend the application by changing the popup highlighted symbols. What is REST? REST stands for Representational State Transfer. It's a software architecture that focuses on the interface between server and client using a hypermedia environment. It limits the actions that can be performed between the client and the server, but provides enough user information and documentation for the user to navigate amongst data and states. In our discussion of ArcGIS Server and REST in this article, we'll cover the following topics: Handling REST endpoints and data formats The hierarchy of ESRI REST Services, as seen through a browser REST is a methodology based around web pages. A website presents a state (the URL), data transfer (a HTML page, CSS, and JavaScript), and a well-documented way to navigate between the different states (links). While understanding a website to be RESTful is well and good, what makes ArcGIS Server so RESTful? In order for a web service to be considered RESTful, it must meet the following requirements: Client-Server: The roles of the client and server are clearly defined. The client doesn't care if the server contains one or one million records, and the server does not depend on a particular UI for the client. As long as the interface between the client and server remains the same, the client and server code can be changed independently. Stateless: The client handles the state of the application, whereas the server does not have to keep up with it. The client has all it needs to make a request, including the necessary parameters and security tokens. Cacheable: Sometimes client applications cache data for faster performance because the World Wide Web delivers data asynchronously. The server needs to tell the client which requests can be cached and for how long. Layered system: The server-side application can be placed behind a load balancer, a secure system, or a proxy, with no noticeable effect on the client-side application. Code on demand (optional): The server can provide code for the client to run. Examples include Java applets or JavaScript scripts. Not all REST services do this. Uniform interface: With a REST service, the server provides a uniform interface through which the client can interact with the data. The Uniform Interface can be broken down further into four principals. Information requests that include the identification of the resource. This includes everything from the data source to the output type. The client has enough information from a request to manipulate or delete data. Messages from the server contain instructions about how to use them. A state is handled by the client using hypermedia (web pages, query parameters, and session states). If you look into the ArcGIS Server implementation, you'll see that it meets these criteria. Therefore, it's considered RESTful. Looking at a map server ArcGIS Server provides web access to its map service contents. To access the content, you need to know the ArcGIS Server name and the site name. By default, ArcGIS Server is reached through port 6080. It can also be reached through port 80 if it has been configured and licensed to deliver web content. REST Service endpoints can be reached through your browser with this address: http://<GIS Server Name>:6080/<site name>/rest/services Where GIS Server Name refers to the ArcGIS Server machine, and site name refers to the ArcGIS Server instance which, by default, is arcgis. The port number is optional if ArcGIS Server has been set up to deliver traffic on port 80, which is the default port for Internet traffic. When there are multiple GIS Servers, often handling a large load of traffic or complicated services, a web adaptor may be installed. The web adaptor routes traffic to multiple ArcGIS Servers, based on service requests, load balancing, and other related issues. The web adaptor also provides a layer of security, whereby ArcGIS Server machine names are not directly exposed to the outside world. To access the REST service through the web adaptor, use the following URL. http://<web server name>/<web adaptor name>/rest/services As long as ArcGIS Server is accessible from our computer, we can access information in the web browser. By default, the service data is presented as HTML. From there we can see the properties of the REST service, and follow links to other services and actions on the server. This lets developers review and test map services, independent of any application they create. ArcGIS REST services provide a great way to view and interact with service data using HTML, which is good for presentation. Almost all of our applications will interact with the REST service through server requests. ArcGIS Server can therefore communicate through REST using another data format called JSON. Working with JSON JavaScript Object Notation (JSON) provides a structured data format for loosely defined data structures. A JSON object is built with other JSON objects, including strings, numbers, other objects, and arrays. Any data is allowed, as long as everything is self-contained and there is no sloppy formatting with missing brackets and braces. There are a number of ways to test for valid JSON. Visit http://jsonlint.com, where you can copy and paste your JSON and submit it for validation. It will point out missing or broken formatting issues, as well as how to resolve them. JSON validators require that all string items are enclosed in quotes. Single or double quotes will work, as long as you put the same marks at the end of a string as those at the beginning. This includes both JSON object key fields. JavaScript interpreters in a browser are more flexible, key fields do not have to be enclosed by quotes. It all depends on how you're testing the JSON. Before JSON was developed, data was passed from server to client in a format called Extensible Markup Language (XML). XML is a document markup language that shows data in a format both humans and machines can read. The XML format can be read and parsed by a number of programming languages. There are two main reasons why JSON is the preferred data format for web applications when compared to XML. First, JSON data can be consumed immediately by JavaScript applications. XML requires extra steps to parse the data into a usable object. Secondly, JSON data takes up less space. Let's explore that by comparing the following snippets of data. The following snippet is written in XML: <mountain> <name>Mount Everest</name> <elevation>29029</elevation> <elevationUnit>ft</elevationUnit> <mountainRange>Himalaya</mountainRange> <dateOfFirstAscent>May 29, 1953</dateOfFirstAscent> <ascendedBy> <person> <firstName>Tenzing</firstName> <lastName>Norgay</lastName> </person> <person> <firstName>Edmund</firstName> <lastName>Hillary</lastName> </person> </ascendedBy> </mountain> Now, here's the same data written in JSON: { "type": "mountain", "name": "Mount Everest", "elevation": 29029, "elevationUnit": "ft", "mountainRange": "Himilaya", "dateOfFirstAscent": "May 29, 1953", "ascendedBy": [ { "type": "person", "firstName": "Tenzing", "lastName": "Norgay" }, { "type": "person", "firstName": "Edmund", "lastName": "Hillary" } ] } The same data in JSON counts 62 characters less than that in XML. If we take out the line breaks and extra spaces, or minimize the data, the JSON data is 93 characters shorter than the minimized XML. With bandwidth at a premium, especially for mobile browsers, you can see why JSON is the preferred format for data transmission. JSON and PJSON formatting JSON comes in two flavors. The default JSON is minimized, with all the extra spaces and line returns removed. Pretty JSON, or PJSON for short, contains line breaks and spacing to show the structure and hierarchy of the data. The previous Mount Everest example shows what PJSON looks like. While PJSON is easier to read, and therefore easier to troubleshoot for errors, the minimized JSON is much smaller. In the example, the PJSON has 397 characters, while the minimized version has only 277 characters, a 30 percent decrease in size. When viewing ArcGIS REST service data, you can change the format of the data by adding an f query parameter to the REST Service URL. It should look like the following URL: http://<GIS web service>/arcgis/rest/services/?f=<format> Here, you can set f=JSON to receive the raw JSON data, or f=PJSON to receive the human-readable pretty JSON (or padded JSON, if you prefer). Some browsers, such as Google Chrome and Mozilla Firefox, offer third party extensions that reformat raw JSON data into PJSON without making the request. Service Level Let's start by viewing the sample ArcGIS Server services at http://sampleserver6.arcgisonline.com/arcgis/rest/services. When we request the page as HTML, we notice a few things. First, the version of ArcGIS Server is shown (version 10.21 at the time of writing). The version number is important because many features and bits of information may not be present in older versions. Secondly, we see a list of links pointing to folders. These are map services grouped in any way the publisher chooses. We also see a list of map service links below the folder lists. Finally, at the bottom of the page, we see supported interfaces. In this site, we can see the REST interface that we're familiar with. Here's a picture of the service: If we change the format of the REST Service request in our browser to Pretty JSON, by adding ?f=pjson to the end of the URL, we can see roughly how the ArcGIS JavaScript API would see this location: Here, the JSON object returned includes the numeric currentVersion, an array of folder names, and an array of services objects. The service JSON objects contain a name and a type attribute, which tells you what kind of service you're dealing with, and gives you the components you need to construct the URL link to those services. This format is as follows: http://<server>/arcgis/rest/services/<service.name>/<service.type> If we follow the link to our census map service, we can see more details. Map services A map service gives applications access to map data published with ArcGIS Server. It contains information about the map layout, format, contents, and other items necessary to properly render the map with the various ArcGIS API's. The map service URL is formatted as follows: http://<ArcGIS Server REST Services>/<mapName>/MapServer or http://<ArcGIS Server REST Services>/<folder>/<mapName>/MapServer When you navigate to a map service using your browser, you're presented with a lot of information about the map service. The HTML provides links to view the data in different applications, including the ArcGIS JavaScript API and ArcMap. Google Earth is also available if the map service is published to serve data in that format. The HTML for the map service also provides a lot of metadata to help you understand what it's offering. These properties include the Description, Service Description, Copyright Text, and the Document Info. Some of the map service properties can be difficult to understand without some context. We'll review some of the important ones. Remember that properties in this list show how they are listed in the HTML. When shown in JSON, these items are camel-cased (first letter lowercase, no spaces, and capital letters to start each new word after the first). Spatial reference: How the layout of the map compares with the real world, which we'll discuss a little later. Single fused map cache: Lets you know whether the map data has been cached, or if it is dynamic. You can load the layer by using either ArcGISTiledMapServiceLayer or ArcGISDynamicMapServiceLayer, respectively. Initial extent/Full extent: When you first load the map with the ArcGIS JavaScript API, the Initial Extent describes the bounding box of the area you see the first time. The Full Extent is the expected full area of the map service, which may be much wider than all the data. Supported image format types: When ArcGIS Server draws the map layers as tiles, these are the image formats that can be returned. PNG32 is recommended if your data has a lot of semi-transparencies and colors, while PNG8 works well with very simple symbols. Supports dynamic layers: If true, the developer can change the symbology and layer definitions when displaying the map service. Max record count: When submitting a query, identify or some other search, this is the maximum number of results that can be returned by the map service. This information can only be changed by server-side changes to the map service. Finally, the Map Service HTML provides links to a number of related REST Service endpoints. Most of these links extend the existing URL and provide more information about the map service. As a bare minimum, the following should be present: Legend: Displays the symbology of the layers in the map service. Export map: This feature lets you download an image showing an area of the map that fits within a specific bounding box. You can specify parameters. Identify: This lets you identify features within all layers of a map service, based on the geometry passed in. This functionality is used by IdentifyTask. Find: This lets the user search for features based on the presence of a line of text passed to it. This functionality is implemented by FindTask. Map service layers When exploring the layers of a map service, it helps to know what to look for. Map services list the basic contents of their layers within an array of objects in their layers property. All layer objects have the same format, with the same properties. Each layer object has a numeric id property that refers to the layer's zero-based position in the list. Layer objects also have a name property that comes from how the layer was named in the map service. These layers also have a minScale and maxScale property, showing the range within which the layer is visible (with a 0 value meaning there is no minScale or maxScale limitation). When determining visibility, the layer object also contains a Boolean defaultVisibility property that describes whether the layer is initially visible when the map service loads. Map service layer objects also contain information about their layer hierarchy. Each map layer object contains a parentLayerId and a subLayerIds property. The parentLayerId is a number that refers to the index of the parent group layer for the specific layer. A parent layer id of -1 means the layer in question has no parent layer. The subLayerIds are an integer array of the indexes where you can find the sublayers for the particular parent layer. If a layer has no sub layers, the subLayerIds will be a null value, instead of an empty list. You can see an example of map service layers in the following code: layers: [ { "id" : 0, "name" : "Pet Lovers", "parentLayerId" : -1, "defaultVisibility" : true, "subLayerIds" : [1, 2], "minScale" : 16000 "maxScale" : 0 }, { "id" : 1, "name" : "Dog Lovers", "parentLayerId" : 0, "defaultVisibility" : true, "subLayerIds" : null, "minScale" : 16000 "maxScale" : 0 }, { "id" : 2, "name" : "Cat Lovers", "parentLayerId" : 0, "defaultVisibility" : true, "subLayerIds" : null, "minScale" : 16000 "maxScale" : 0 } ], In the preceding snippet, the map service has three layers. The Pet Lovers layer is actually a parentLayer, and corresponds to a group layer assigned in an ArcMap .mxd file. There are two layers in parentLayer, Dog Lovers and Cat Lovers. All layers are visible by default, and the layers do not appear until the map is at a scale lower than 1:16,000, according to minScale. The maxScale property is set to zero, meaning there is no maximum scale where the layer turns off again. Feature services Feature services are similar to map services, but provide more functionality. Their content can be edited, if the database and map settings support those operations. They display their feature symbology without the need of a legend service. Their symbology can also be modified client-side, by changing their renderer. The URL of a feature service is similar to a map service, except that it ends with FeatureServer, as shown in the following: http://<GIS-web-server>/arcgis/rest/services/<folder>/<mapname>/FeatureServer The feature service differs first and foremost in its capabilities. Apart from allowing you to query data, feature service capabilities allow the user to create, update, and/or delete records. Those familiar with CRUD operations will recognize those words as the C, U, and D in CRUD (the R stands for read, which is what happens when you query for results). The capabilities include editing if create, update, or delete are allowed. Also, if the feature service supports file attachments to data, such as photos, the capabilities will include the word "upload". There are other feature service properties that may help you learn more about the service. They include the following: Has Versioned Data: Lets you know that the geodatabase has versioning enabled, which allows edits to be undone/redone. Supports Disconnected Editing: Data can be checked out and edited in an environment without an Internet connection. When the application connects to the Internet again, the data can be checked back in. Sync Enabled: If this is true, feature data can be synced between the geodatabase the data comes from, and another geodatabase. Allow Geometry Updates: If editing is allowed, this lets the API know if the feature geometries can be edited or not. Due to certain permissions, the application might only allow for updates to the feature attributes, while the geometries remain unchanged. Enable Z Defaults: If the data contains height data (z), default values are assigned in the map service. Layer level Map services and feature services are made up of layers. These layers group together geographic features with the same geometry type and the same sets of properties. Layers are referred to by their numerical index in the list. The layer index starts at 0 for the bottom layer, and goes up one for each additional layer. The URL might look something like this for the first layer in a map service: http://<GIS-web-server>/arcgis/rest/services/<folder>/<mapname>/MapServer/0 Map layers offer a whole host of data to help you understand what you're viewing. The layer's name property comes either from its name in the .mxd file, or from the layer in the Table of Contents, if the file is unsaved. The map layer also provides a description, and copyright data. The Display Field property tells the map service what to use when labeling features, if labeling is turned on. Map layers also provide important data that you can use in your application. The type parameter tells you the geometry of the layer, whether it's a point, line, or polygon. Default Visibility lets you know if the layer was originally visible or not when the map service began. Min Scale and Max Scale affect visibility, depending on your zoom level. The map service also lets you know if the layers have attachments, can be modified with different renderers, and how many results can be returned from a query. Fields A map service layer provides information about its attribute by means of the field property. The field property is an array of field objects with similar formats. All fields have a type, a name, and an alias attribute. The type refers to the data type of the field, whether it's a string or an integer, or something else. A current list of supported types can be found at http://resources.arcgis.com/en/help/arcgis-rest-api/#/field/02r300000051000000/. The name attribute is the field name for the property, as found in the geodatabase. Field names don't contain spaces or special characters. The alias field is a string that shows the field name for presentation purposes. Unlike the field name, the alias can have spaces or other special characters. If no alias is assigned in the geodatabase or the map service, the alias field is the same as the field name. For instance, when creating the map service with ArcMap, you might have some data for a block with a field name NUMB_HSES. If you want to show the values for this property in a chart, the field name may look rough and a little confusing. You can then add an alias to the NUMB_HSES field by calling it Number of Houses. That alias provides a much better description for the field: { "type": "esriFieldTypeInteger", "name" "NUMB_HSES", "alias": "Number of Houses" } Domains Field objects may also have domain attributes assigned to them. Domains are limitations on field values imposed at the geodatabase level. Domains are uniquely created in the geodatabase, and can be assigned to feature classes and table fields. Domains make it easier to input the correct values by restricting what can be entered. Instead of allowing users to mistype street names in a report service, for instance, you might provide a field with a domain containing all the correctly typed street names. The user can then select from the list, rather than have to guess how to spell the street name. The ArcGIS REST API supports two varieties of domains: ranges and coded values. Ranges, as the name implies, set a minimum and maximum numeric value for a feature attribute. One example of a range might be an average user rating for restaurants. The restaurant might get somewhere between one and five stars, so you wouldn't want a restaurant to accidently get a value of 6 or a value of less than 1. You can see an example of a rating field with that range domain in this snippet: { "type": "esriFieldTypeInteger", "name": "RATING", "alias": "Rating", "domain": { "type": "range", "name": "Star Rating", "range": [1, 5] } } A coded value domain provides a list of code and value pairs to use as legitimate property values. The coded value list contains items with a name and a code. The code is the value stored in the geodatabase. The name is the text representation of the coded value. They're useful in that users are forced to select a valid value, instead of mistyping a correct value. In the following example, we can see a field with a coded value domain. The field contains state abbreviations, but the domain allows the user to see entire state names: { "type": "esriFieldTypeString", "name": "STATE", "alias": "State", "length": 2, "domain": { "type": "codedValue", "name": "State Abbreviation Codes", "codedValues": [ {"name": "Alabama", "code": "AL"}, {"name": "Alaska", "code": "AK"}, {"name": "Wisconsin", "code": "WI"}, {"name": "Wyoming", "code": "WY"} ] } } In the preceding example, state names are stored in two letter code form. The domain provides a full name reference table with the full names of the states. If you were to send queries for features using this field, you would use the code values. Querying for all features where STATE = 'Alaska' would yield no results, while a query where STATE = 'AK' may give you results. Note that the code and the value don't have to be of the same type. You can have numeric codes for, say, water line part numbers, and coded values to show their descriptive names. Related tables Tables with non-geographic data can be published in a map service. These tables may provide data related to map features, such as comments on campground locations or the sales history of a property. These tables can be searched and queried like features. Relationships between map layers and tables can also be published and searched. Layers and tables can be joined, either by using geodatabase relationship classes, or ad-hoc relationship assignments in ArcMap. When published with ArcMap, those relationships are preserved in ArcGIS Server. The connection between related features and tables is stored within the relationships property of the layer and table. A developer can query related data, based on a selection in the parent feature class. Relationship objects have the same general format. Each relationship object contains a numerical id and relatedTableId. The relatedTableId is linked to the RelationshipQuery object to query for related results. The role describes whether the current layer or table is the origin or the destination of the relationship. Cardinality describes whether a single origin object has one or several destinations related to it. When querying for results, results return much faster if you start with the origin and use RelationshipQuery on the destination tables. Starting with the destination tables may take significantly longer. Summary We have explored the different parts of the REST Service endpoints for ArcGIS Server, the primary data source for our ArcGIS JavaScript API-based applications. We've learned what it means for a service to be RESTful, and how that applies to ArcGIS Server. We've explored the organization of ArcGIS Server map services. With your understanding of how data is handled client-side and server-side, you will be able to implement many of the powerful features that ArcGIS Server offers for web-based applications. Resources for Article: Further resources on this subject: Introduction to Mobile Web ArcGIS Development [article] Server Logs [article] Editing attributes [article]
Read more
  • 0
  • 0
  • 1484

article-image-bizarre-python
Packt
19 Aug 2015
20 min read
Save for later

The strange relationship between objects, functions, generators and coroutines

Packt
19 Aug 2015
20 min read
The strange relationship between objects, functions, generators and coroutines In this article, I’d like to investigate some relationships between functions, objects, generators and coroutines in Python. At a theoretical level, these are very different concepts, but because of Python’s dynamic nature, many of them can appear to be used interchangeably. I discuss useful applications of all of these in my book, Python 3 Object-oriented Programming - Second Edition. In this essay, we’ll examine their relationship in a more whimsical light; most of the code examples below are ridiculous and should not be attempted in a production setting! Let’s start with functions, which are simplest. A function is an object that can be executed. When executed, the function is entered at one place accepting a group of (possibly zero) objects as parameters. The function exits at exactly one place and always returns a single object. Already we see some complications; that last sentence is true, but you might have several counter-questions: What if a function has multiple return statements? Only one of them will be executed in any one call to the function. What if the function doesn’t return anything? Then it it will implicitly return the None object. Can’t you return multiple objects separated by a comma? Yes, but the returned object is actually a single tuple Here’s a look at a function: def average(sequence): avg = sum(sequence) / len(sequence) return avg print(average([3, 5, 8, 7])) which outputs: That’s probably nothing you haven’t seen before. Similarly, you probably know what an object and a class are in Python. I define an object as a collection of data and associated behaviors. A class represents the “template” for an object. Usually the data is represented as a set of attributes and the behavior is represented as a collection of method functions, but this doesn’t have to be true. Here’s a basic object: class Statistics: def __init__(self, sequence): self.sequence = sequence def calculate_average(self): return sum(self.sequence) / len(self.sequence) def calculate_median(self): length = len(self.sequence) is_middle = int(not length % 2) return ( self.sequence[length // 2 - is_middle] + self.sequence[-length // 2]) / 2 statistics = Statistics([5, 2, 3]) print(statistics.calculate_average()) which outputs: This object has one piece of data attached to it: the sequence. It also has two methods besides the initializer. Only one of these methods is used in this particular example, but as Jack Diederich said in his famous Stop Writing Classes talk, a class with only one function besides the initializer should just be a function. So I included a second one to make it look like a useful class (It’s not. The new statistics module in Python 3.4 should be used instead. Never define for yourself that which has been defined, debugged, and tested by someone else). Classes like this are also things you’ve seen before, but with this background in place, we can now look at some bizarre things you might not expect (or indeed, want) to be able to do with a function. For example, did you know that functions are objects? In fact, anything that you can interact with in Python is defined in the source code for the CPython interpreter as a PyObject structure. This includes functions, objects, basic primitives, containers, classes, modules, you name it. This means we can attach attributes to a function just as with any standard object. Ah, but if functions are objects, can you attach functions to functions? Don’t try this at home (and especially don’t do it at work): def statistics(sequence): statistics.sequence = sequence return statistics def calculate_average(): return sum(statistics.sequence) / len(statistics.sequence) statistics.calculate_average = calculate_average print(statistics([1, 5, 8, 4]).calculate_average()) which outputs: This is a pretty crazy example (but we’re just getting started). The statistics function is being set up as an object that has two attributes: sequence is a list and calculate_average is another function object. For fun, the function returns itself so that the print function can call the calculate_average function all in one line. Note that the statistics function here is an object, not a class. Rather than emulating the Statistics class in the previous example, it is more similar to the statistics instance of that class. It is hard to imagine any reason that you would want to write code like this in real life. Perhaps it could be used to implement the Singleton (anti-)pattern popular with some other languages. Since there can only ever be one statistics function, it is not possible to create two distinct instances with two distinct sequence attributes the way we can with the Statistics class. There is generally little need for such code in Python, though, because of its ‘consenting adults’ nature. We can more closely simulate a class by using a function like a constructor: def Statistics(sequence): def self(): return self.average() self.sequence = sequence def average(): return sum(self.sequence) / len(self.sequence) self.average = average return self statistics = Statistics([2, 1, 1]) print(Statistics([1, 4, 6, 2]).average()) print(statistics()) which outputs: That looks an awful lot like Javascript, doesn’t it? The Statistics function acts like a constructor that returns an object (that happens to be a function, named self). That function object has had a couple attributes attached to it, so our function is now an object with both data and behavior. The last three lines show that we can instantiate two separate Statistics “objects” just as if it were a class. Finally, since the statistics object in the last line really is a function, we can even call it directly. It proxies the call through to the average function (or is it a method at this point? I can’t tell anymore) defined on itself. Before we go on, note that this simulated overlapping of functionality does not mean that we are getting exactly the same behavior out of the Python interpreter. While functions are objects, not all objects are functions. The underlying implementation is different, and if you try to do this in production code, you’ll quickly find confusing anomalies. In normal code, the fact that functions can have attributes attached to them is rarely useful. I’ve seen it used for interesting diagnostics or testing, but it’s generally just a hack. However, knowing that functions are objects allows us to pass them around to be called at a later time. Consider this basic partial implementation of an observer pattern: class Observers(list): register_observer = list.append def notify(self): for observer in self: observer() observers = Observers() def observer_one(): print('one was called') def observer_two(): print('two was called') observers.register_observer(observer_one) observers.register_observer(observer_two) observers.notify() which outputs: At line 2, I’ve intentionally reduced the comprehensibility of this code to conform to my initial ‘most of the examples in this article are ridiculous’ thesis. This line creates a new class attribute named register_observer which points to the list.append function. Since the Observers class inherits from the list class, this line essentially creates a shortcut to a method that would look like this: def register_observer(self, item): self.append(item) And this is how you should do it in your code. Nobody’s going to understand what’s going on if you follow my version. The part of this code that you might want to use in real life is the way the callback functions are passed into the registration function at lines 16 and 17. Passing functions around like this is quite common in Python. The alternative, if functions were not objects, would be to create a bunch of classes that have a single method with an uninformative name like execute and pass those around instead. Observers are a bit too useful, though, so in the spirit of keeping things ridiculous, let’s make a silly function that returns a function object: def silly(): print("silly") return silly silly()()()()()() which outputs: Since we’ve seen some ways that functions can (sort of) imitate objects, let’s now make an object that can behave like a function: class Function: def __init__(self, message): self.message = message def __call__(self, name): return "{} says '{}'".format(name, self.message) function = Function("I am a function") print(function('Cheap imitation function')) which outputs: I don’t use this feature often, but it can be useful in a few situations. For example, if you write a function and call it from many different places, but later discover that it needs to maintain some state, you can change the function to an object and implement the __call__ method without changing all the call sites. Or if you have a callback implementation that normally passes functions around, you can use a callable object when you need to store more complicated state. I’ve also seen Python decorators made out of objects when additional state or behavior is required. Now, let’s talk about generators. As you might expect by now, we’ll start with the silliest way to implement generation code. We can use the idea of a function that returns an object to create a rudimentary generatorish object that calculates the Fibonacci sequence: def FibFunction(): a = b = 1 def next(): nonlocal a, b a, b = b, a + b return b return next fib = FibFunction() for i in range(8): print(fib(), end=' ') which outputs: This is a pretty wacky thing to do, but the point is that it is possible to build functions that are able to maintain state between calls. The state is stored in the surrounding closure; we can access that state by referencing them with Python 3’s nonlocal keyword. It is kind of like global except it accesses the state from the surrounding function, not the global namespace. We can, of course, build a similar construct using classic (or classy) object notation: class FibClass(): def __init__(self): self.a = self.b = 1 def __call__(self): self.a, self.b = self.b, self.a + self.b return self.b fib = FibClass() for i in range(8): print(fib(), end=' ') which outputs: Of course, neither of these obeys the iterator protocol. No matter how I wrangle it, I was not able to get FibFunction to work with Python’s builtin next() function, even after looking through the CPython source code for a couple hours. As I mentioned earlier, using the function syntax to build pseudo-objects quickly leads to frustration. However, it’s easy to tweak the object based FibClass to fulfill the iterator protocol: class FibIterator(): def __init__(self): self.a = self.b = 1 def __next__(self): self.a, self.b = self.b, self.a + self.b return self.b def __iter__(self): return self fib = FibIterator() for i in range(8): print(next(fib), end=' ') which outputs: This class is a standard implementation of the iterator pattern. But it’s kind of ugly and verbose. Luckily, we can get the same effect in Python with a function that includes a yield statement to construct a generator. Here’s the Fibonacci sequence as a generator: ef FibGenerator(): a = b = 1 while True: a, b = b, a + b yield b fib = FibGenerator() for i in range(8): print(next(fib), end=' ') print('n', fib) which outputs: The generator version is a bit more readable than the other two implementations. The thing to pay attention to here is that a generator is not a function. The FibGenerator function returns an object as illustrated by the words “generator object” in the last line of output above. Unlike a normal function, a generator function does not execute any code inside it when we call the function. Instead it constructs a generator object and returns that. You could think of this as like an implicit Python decorator; the Python interpreter sees the yield keyword and wraps it in a decorator that returns an object instead. To start the function code executing, we have to use the next function (either explicitly as in the examples, or implicitly by using a for loop or yield from). While a generator is technically an object, it is often convenient to think of the function that creates it as a function that can have data passed in at one place and can return values multiple times. It’s sort of like a generic version of a function (which can have data passed in at one place and return a value at only one place). It is easy to make a generator that behaves not completely unlike a function, by yielding only one value: def average(sequence): yield sum(sequence) / len(sequence) print(next(average([1, 2, 3]))) which outputs: Unfortunately, the call site at line 4 is less readable than a normal function call, since we have to throw that pesky next() in there. The obvious way around this would be to add a __call__ method to the generator but this fails if we try to use attribute assignment or inheritance. There are optimizations that make generators run quickly in C code and also don’t let us assign attributes to them. We can, however, wrap the generator in a function-like object using a ludicrous decorator: def gen_func(func): def wrapper(*args, **kwargs): gen = func(*args, **kwargs) return next(gen) return wrapper @gen_func def average(sequence): yield sum(sequence) / len(sequence) print(average([1, 6, 3, 4])) which outputs: Of course this is an absurd thing to do. I mean, just write a normal function for pity’s sake! But taking this idea a little further, it could be tempting to create a slightly different wrapper: def callable_gen(func): class CallableGen: def __init__(self, *args, **kwargs): self.gen = func(*args, **kwargs) def __next__(self): return self.gen.__next__() def __iter__(self): return self def __call__(self): return next(self) return CallableGen @callable_gen def FibGenerator(): a = b = 1 while True: a, b = b, a + b yield b fib = FibGenerator() for i in range(8): print(fib(), end=' ') which outputs: To completely wrap the generator, we’d need to proxy a few other methods through to the underlying generator including send, close, and throw. This generator wrapper can be used to call a generator any number of times without calling the next function. I’ve been tempted to do this to make my code look cleaner if there are a lot of next calls in it, but I recommend not yielding into this temptation. Coders reading your code, including yourself, will go berserk trying to figure out what that “function call” is doing. Just get used to the next function and ignore this decorator business. So we’ve drawn some parallels between generators, objects and functions. Let’s talk now about one of the more confusing concepts in Python: coroutines. In Python, coroutines are usually defined as “generators that you can send values into”. At an implementation level, this is probably the most sensible definition. In the theoretical sense, however, it is more accurate to define coroutines as constructs that can accept values at one or more locations and return values at one or more locations. Therefore, while in Python it is easy to think of a generator as a special type of function that has yield statements and a coroutine as a special type of generator that we can send data into a different points, a better taxonomy is to think of a coroutine that can accept and return values at multiple locations as a general case, and generators and functions as special types of coroutines that are restricted in where they can accept or return values. So let’s see a coroutine: def LineInserter(lines): out = [] for line in lines: to_append = yield line out.append(line) if to_append is not None: out.append(to_append) return out emily = """I died for beauty, but was scarce Adjusted in the tomb, When one who died for truth was lain In an adjoining room. He questioned softly why I failed? “For beauty,” I replied. “And I for truth,—the two are one; We brethren are,” he said. And so, as kinsmen met a night, We talked between the rooms, Until the moss had reached our lips, And covered up our names. """ inserter = LineInserter(iter(emily.splitlines())) count = 1 try: line = next(inserter) while True: line = next(inserter) if count % 4 else inserter.send('-------') count += 1 except StopIteration as ex: print('n' + 'n'.join(ex.value)) which outputs: The LineInserter object is called a coroutine rather than a generator only because the yield statement is placed on the right side of an assignment operator. Now whenever we yield a line, it stores any value that might have been sent back into the coroutine in the to_append variable. As you can see in the driver code, we can send a value back in using inserter.send. if you instead just use next, the to_append variable gets a value of None. Don’t ask me why next is a function and send is a method when they both do nearly the same thing! In this example, we use the send call to insert a ruler every four lines to separate stanzas in Emily Dickinson’s famous poem. But I used the exact same coroutine in a program that parses the source file for this article. It checks if any line contains the string !#python, and if so, it executes the subsequent code block and inserts the output (see the ‘which outputs’ lines throughout this article) into the article. Coroutines can provide that little extra something when normal ‘one way’ iteration doesn’t quite cut it. The coroutine in the last example is really nice and elegant, but I find the driver code a bit annoying. I think it’s just me, but something about the indentation of a try…catch statement always frustrates me. Recently, I’ve been emulating Python 3.4’s contextlib.suppress context manager to replace except clauses with a callback. For example: def LineInserter(lines): out = [] for line in lines: to_append = yield line out.append(line) if to_append is not None: out.append(to_append) return out from contextlib import contextmanager @contextmanager def generator_stop(callback): try: yield except StopIteration as ex: callback(ex.value) def lines_complete(all_lines): print('n' + 'n'.join(all_lines)) emily = """I died for beauty, but was scarce Adjusted in the tomb, When one who died for truth was lain In an adjoining room. He questioned softly why I failed? “For beauty,” I replied. “And I for truth,—the two are one; We brethren are,” he said. And so, as kinsmen met a night, We talked between the rooms, Until the moss had reached our lips, And covered up our names. """ inserter = LineInserter(iter(emily.splitlines())) count = 1 with generator_stop(lines_complete): line = next(inserter) while True: line = next(inserter) if count % 4 else inserter.send('-------') count += 1 which outputs: The generator_stop now encapsulates all the ugliness, and the context manager can be used in a variety of situations where StopIteration needs to be handled. Since coroutines are undifferentiated from generators, they can emulate functions just as we saw with generators. We can even call into the same coroutine multiple times as if it were a function: def IncrementBy(increment): sequence = yield while True: sequence = yield [i + increment for i in sequence] sequence = [10, 20, 30] increment_by_5 = IncrementBy(5) increment_by_8 = IncrementBy(8) next(increment_by_5) next(increment_by_8) print(increment_by_5.send(sequence)) print(increment_by_8.send(sequence)) print(increment_by_5.send(sequence)) which outputs: Note the two calls to next at lines 9 and 10. These effectively “prime” the generator by advancing it to the first yield statement. Then each call to send essentially looks like a single call to a function. The driver code for this coroutine doesn’t look anything like calling a function, but with some evil decorator magic, we can make it look less disturbing: def evil_coroutine(func): def wrapper(*args, **kwargs): gen = func(*args, **kwargs) next(gen) def gen_caller(arg=None): return gen.send(arg) return gen_caller return wrapper @evil_coroutine def IncrementBy(increment): sequence = yield while True: sequence = yield [i + increment for i in sequence] sequence = [10, 20, 30] increment_by_5 = IncrementBy(5) increment_by_8 = IncrementBy(8) print(increment_by_5(sequence)) print(increment_by_8(sequence)) print(increment_by_5(sequence)) which outputs: The decorator accepts a function and returns a new wrapper function that gets assigned to the IncrementBy variable. Whenever this new IncrementBy is called, it constructs a generator using the original function, and advances it to the first yield statement using next (the priming action from before). It returns a new function that calls send on the generator each time it is called. This function makes the argument default to None so that it can also work if we call next instead of send. The new driver code is definitely more readable, but once again, I would not recommend using this coding style to make coroutines behave like hybrid object/functions. The argument that other coders aren’t going to understand what is going through your head still stands. Plus, since send can only accept one argument, the callable is quite restricted. Before we leave our discussion of the bizarre relationships between these concepts, let’s look at how the stanza processing code could look without an explicit coroutine, generator, function, or object: emily = """I died for beauty, but was scarce Adjusted in the tomb, When one who died for truth was lain In an adjoining room. He questioned softly why I failed? “For beauty,” I replied. “And I for truth,—the two are one; We brethren are,” he said. And so, as kinsmen met a night, We talked between the rooms, Until the moss had reached our lips, And covered up our names. """ for index, line in enumerate(emily.splitlines(), start=1): print(line) if not index % 4: print('------') which outputs: This code is so simple and elegant! This happens to me nearly every time I try to use coroutines. I keep refactoring and simplifying it until I discover that coroutines are making my code less, not more, readable. Unless I am explicitly modeling a state-transition system or trying to do asynchronous work using the terrific asyncio library (which wraps all the possible craziness with StopIteration, cascading exceptions, etc), I rarely find that coroutines are the right tool for the job. That doesn’t stop me from attempting them though, because they are fun. For the record, the LineInserter coroutine actually is useful in the markdown code executor I use to parse this source file. I need to keep track of more transitions between states (am I currently looking for a code block? am I in a code block? Do I need to execute the code block and record the output?) than in the stanza marking example used here. So, it has become clear that in Python, there is more than one way to do a lot of things. Luckily, most of these ways are not very obvious, and there is usually “one, and preferably only one, obvious way to do things”, to quote The Zen Of Python. I hope by this point that you are more confused about the relationship between functions, objects, generators and coroutines than ever before. I hope you now know how to write much code that should never be written. But mostly I hope you’ve enjoyed your exploration of these topics. If you’d like to see more useful applications of these and other Python concepts, grab a copy of Python 3 Object-oriented Programming, Second Edition.
Read more
  • 0
  • 0
  • 33283

article-image-nodes
Packt
19 Aug 2015
18 min read
Save for later

Nodes

Packt
19 Aug 2015
18 min read
In this article by Samanyu Chopra, author of the book iOS Game Development By Example, we will study about nodes, which play an important role in understanding the tree structure of a game. Further, we will discuss about types of nodes in the Sprite Kit and their uses in detail. (For more resources related to this topic, see here.) All you need to know about nodes We have discussed many things about nodes so far. Almost everything you are making in a game with Sprite Kit is a node. Scenes that we are presenting to view are instances of the SKScene class, which is a subclass of the SKEffectNode class, which is itself a subclass of the SKNode class. Indirectly, SKScene is a subclass of the SKNode class. As a game follows the node tree formation, a scene acts like a root node and the other nodes are used as its children. It should be remembered that although SKNode is a base class for the node you see in a scene, it itself does not draw anything. It only provides some basic features to its subclass nodes. All the visual content we see in a Sprite Kit made game, is drawn by using the appropriate SKNode subclasses. Following are some subclasses of SKNode classes, which are used for different behaviors in a Sprite Kit-based game: SKSpriteNode: This class is used to instantiate a texture sprite in the game;SKVideoNode, this class is used to play video content in a scene. SKLabelNode: This class is used to draw labels in a game, with many customizing options, such as font type, font size, font color, and so on. SKShapeNode: This class is used to make a shape based on a path, at run time. For example, drawing a line or making a drawing game. SKEmitterNode: This class is used for emitting particle effects in scene, with many options, such as position, number of particles, color, and so on. SKCropNode: This class is basically used for cropping its child nodes, using a mask. Using this, you can selectively block areas of a layer. SKEffectNode: SKEffectNode is the parent of the SKScene class and the subclass of the SKNode class. It is used for applying image filter to its children. SKLightNode: SKLightNode class is used to make light and shadow effects in scene. SKFieldNode: This is a useful feature of Sprite Kit. You can define a portion of scene with some physical properties, for example, in space game, having a gravity effect on a blackhole, which attracts the things which are nearby. So, these are the basic subclasses of SKNode which are used frequently in Sprite Kit. SKNode provides some basic properties to its subclasses, which are used to view a node inside a scene, such as: position: This sets up the position of a node in a scene xScale: This scales in the width of a node yScale: This scales in the height of a node zRotation: This facilitates the rotation of a node in a clockwise or anti-clockwise direction frame: Node content bounding rectangle without accounting its children We know that the SKNode class does not draw anything by itself. So, what is the use of if? Well, we can use SKNode instances to manage our other nodes in different layers separately, or we can use them to manage different nodes in the same layer. Let's take a look at how we can do this. Using the SKNode object in the game Now, we will discover what the various aspects of SKNode are used for. Say you have to make a body from different parts of sprites, like a car. You can make it from sprites of wheels and body. The wheels and body of a car run in synchronization with each other, so that one control their action together, rather than manage each part separately. This can be done by adding them as a child of the SKNode class object and updating this node to control the activity of the car. The SKNode class object can be used for layering purposes in a game. Suppose we have three layers in our game: the foreground layer, which represents foreground sprites, the middle layer, which represents middle sprites, and the background layer which represents background sprites. If we want a parallax effect in our game, we will have to update each sprite's position separately or we can make three SKNode objects, referring to each layer, and add the sprites to their respective nodes. Now we have to update only these three nodes' position and the sprites will update their position automatically. The SKNode class can be used to make some kind of check point in a game, which is hidden but performs or triggers some event when a player crosses them, such as level end, bonus, or death trap. We can remove or add the whole sub tree inside a node and perform the necessary functions, such as rotating, scaling, positioning, and so on. Well, as we described that we can use the SKNode object as checkpoints in the game, it is important to recognize them in your scene. So, how we do that? Well the SKNode class provides a property for this. Let's find out more about it. Recognition of a node The SKNode class provides a property with a name, to recognize the correct node. It takes string as a parameter. Either you can search a node by its name or can use one of the two methods provided by SKNode, which are as follows: func childNodeWithName(name:String) -> SKNode: This function takes the name string as a parameter, and if it finds a node with a specific name, it returns that node or else it returns nil. If there is more than one node sharing the same name, it will return the first node in the search. func enumerateChildNodesWithName(name:String, usingBlock:((SKNode!,UnsafeMutablePointer<ObjCBool>)->Void)!): When you need all the nodes sharing the same name, use this function. This function takes the name and block as a parameter. In usingBlock, you need to provide two parameters. One matching node, and the other a pointer of type Boolean. In our game, if you remember, we used the name property inside PlayButton to recognize the node when a user taps on it. It's a very useful property to search for the desired node. So, let's have a quick look at other properties or methods of the SKNode class. Initializing a node There are two initializers to make an instance of SKNode. Both are available in iOS 8.0 or later. convenience init (fileNamed filename: String): This initializer is used for making a node by loading an archive file from main bundle. For this, you have to pass a file name with an sks extension in the main bundle. init(): It is used to make a simple node without any parameter. It is useful for layering purposes in a game. As we already discussed the positioning of a node, let's discuss some functions and properties that are used to build a node tree. Building a node tree SKNode provides some functions and properties to work with a node tree. Following are some of the functions: addChild(node:SKNode): This is a very common function and is used mostly to make a node tree structure. We already used it to add nodes to scenes. insertChild(node:SKNode,atIndex index: Int): This is used when you have to insert a child in a specific position in the array. removeFromParent(): This simply removes a node from its parent. removeAllChildren(): It is used when you have to clear all the children in a node. removeChildrenInArray(nodes:[AnyObject]!): It take an array of SKNode objects and removes it from the receiving node. inParentHierarchy(parent:SKNode) -> Bool: It takes an SKNode object to check as a parent of the receiving node, and returns a Boolean value according to that condition. There are some useful properties used in a node tree, as follows: children: This is a read only property. It contains the receiving node's children in the array. parent: This is also a read only property. It contain the reference of the parent of the receiving node, and if there is none, then it returns nil. scene: This too is a read only property. If the node is embedded in the scene, it will contain the reference of the scene, otherwise nil. In a game, we need some specific task on a node, such as changing its position from one point to another, changing sprites in a sequence, and so on. These tasks are done using actions on node. Let's talk about them now. Actions on a node tree Actions are required for some specific tasks in a game. For this, the SKNode class provides some basic functions, which are as follows. runAction(action:SKAction!): This function takes an SKAction class object as a parameter and performs the action on the receiving node. runAction(action:SKAction!,completion block: (() -> Void)!): This function takes an SKAction class object and a compilation block as object. When the action completes, it calls the block.  runAction(action:SKAction,withKey key:String!): This function takes an SKAction class object and a unique key, to identify this action and perform it on the receiving node. actionForKey(key:String) -> SKAction?: This takes a String key as a parameter and returns an associative SKAction object for that key identifier. This happens if it exists, otherwise it returns nil. hasActions() -> Bool: Through this action, if the node has any executing action, it returns true, or else false. removeAllActions(): This function removes all actions from the receiving node. removeActionForKey(key:String): This takes String name as key and removes an action associated with that key, if it exists. Some useful properties to control these actions are as follows: speed: This is used to speed up or speed down the action motion. The default value is 1.0 to run at normal speed; with increasing value, speed increases. paused: This Boolean value determines whether an action on the node should be paused or resumed. Sometimes, we require changing a point coordinate system according to a node inside a scene. The SKNode class provides two functions to interchange a point's coordinate system with respect to a node in a scene. Let's talk about them. Coordinate system of a node We can convert a point with respect to the coordinate system of any node tree. The functions to do that, are as follows: convertPoint(point:CGPoint, fromNode node : SKNode) -> CGPoint: This takes a point in another node's coordinate system and the other node as its parameter, and returns a converted point according to the receiving node's coordinate system. convertPoint(point:CGPoint, toNode node:SKNode) ->CGPoint: It takes a point in the receiving node's coordinate system and the other nodes in the node tree as its parameters, and returns the same point converted according to the other node's coordinate system. We can also determine if a point is inside a node's area or not. containsPoint(p:CGPoint) -> Bool: This returns the Boolean value according to the position of a point inside or outside of a receiving node's bounding box. nodeAtPoint(p:CGPoint) -> SKNode: This returns the deepest descendant node that intersects the point. If that is not there, then it returns the receiver node. nodesAtPoint(p:CGPoint) -> [AnyObject]: This returns an array of all the SKNode objects in the subtree that intersect the point. If no nodes intersect the point, an empty array is returned. Apart from these, the SKNode class provides some other functions and properties too. Let's talk about them. Other functions and properties Some other functions and properties of the SKNode class are as follows: intersectsNode(node:SKNode) -> Bool: As the name suggest, it returns a Boolean value according to the intersection of the receiving node and another node from the function parameter. physicsBody: It is a property of the SKNode class. The default value is nil, which means that this node will not take part in any physical simulation in the scene. If it contains any physical body, then it will change its position and rotation in accordance with the physical simulation in the scene. userData : NSMutableDictionary?: The userData property is used to store data for a node in a dictionary form. We can store position, rotation, and many custom data sets about the node inside it. constraints: [AnyObject]?: It contains an array of constraints SKConstraint objects to the receiving node. Constraints are used to limit the position or rotation of a node inside a scene. reachConstraints: SKReachConstraints?: This is basically used to make restricted values for the receiving node by making an SKReachConstraints object. For example, to make joints move in a human body. Node blending modes: The SKNode class declares an enum SKBlendMode of the int type to blend the receiving node's color by using source and destination pixel colors. The constant’s used for this are as follows: Alpha: It is used to blend source and destination colors by multiplying the source alpha value Add: It is used to add the source and destination colors Subtract: It is used to subtract the source color from the destination color Multiply: It is used to multiply the source color by the destination color MultiplyX2: It is used to multiply the source color by the destination color, and after that, the resulting color is doubled Screen: It is used to multiply the inverted source and the destination color respectively and it then inverts the final result color Replace: It is used to replace the destination color by source color calculateAccumulatedFrame()->CGRect: We know that a node does not draw anything by itself, but if a node has descendants that draw content, then we may be required to know the overall frame size of that node. This function calculates the frame that contains the content of the receiver node and all of its descendants. Now, we are ready to see some basic SKNode subclasses in action. The classes we are going to discuss are as follows: SKLabelNode SKCropNode SKShapeNode SKEmitterNode SKLightNode SKVideoNode To study these classes, we are going to create six different SKScene subclasses in our project, so that we can learn them separately. Now, having learned in detail about nodes, we can proceed further to utilize the concept of nodes in a game. Creating subclasses for our Platformer game With the theoretical understanding of nodes, one wonders how this concept is helpful in developing a game. To understand the development of a game using the concept of Nodes, we now go ahead with writing and executing code for our Platformer game. Create the subclasses of different nodes in Xcode, following the given steps: From the main menu, select New File | Swift | Save As | NodeMenuScene.swift: Make sure Platformer is ticked as the target. Now Create and Open and make the NodeMenuScene class by subclassing SKScene. Following the previous same steps as, make CropScene, ShapeScene, ParticleScene, LightScene, and VideoNodeScene files, respectively. Open the GameViewController.swift file and replace the viewDidLoad function by typing out the following code: override func viewDidLoad() { super.viewDidLoad() let menuscene = NodeMenuScene() let skview = view as SKView skview.showsFPS = true skview.showsNodeCount = true skview.ignoresSiblingOrder = true menuscene.scaleMode = .ResizeFill menuscene.anchorPoint = CGPoint(x: 0.5, y: 0.5) menuscene.size = view.bounds.size skview.presentScene(menuscene) } In this code, we just called our NodeMenuScene class from the GameViewController class. Now, it's time to add some code to the NodeMenuScene class. NodeMenuScene Open the NodeMenuScene.swift file and type in the code as shown next. Do not worry about the length of the code; as this code is for creating the node menu screen, most of the functions are similar to creating buttons: import Foundation import SpriteKit let BackgroundImage = "BG" let FontFile = "Mackinaw1" let sKCropNode = "SKCropNode" let sKEmitterNode = "SKEmitterNode" let sKLightNode = "SKLightNode" let sKShapeNode = "SKShapeNode" let sKVideoNode = "SKVideoNode" class NodeMenuScene: SKScene { let transitionEffect = SKTransition.flipHorizontalWithDuration(1.0) var labelNode : SKNode? var backgroundNode : SKNode? override func didMoveToView(view: SKView) { backgroundNode = getBackgroundNode() backgroundNode!.zPosition = 0 self.addChild(backgroundNode!) labelNode = getLabelNode() labelNode?.zPosition = 1 self.addChild(labelNode!) } func getBackgroundNode() -> SKNode { var bgnode = SKNode() var bgSprite = SKSpriteNode(imageNamed: "BG") bgSprite.xScale = self.size.width/bgSprite.size.width bgSprite.yScale = self.size.height/bgSprite.size.height bgnode.addChild(bgSprite) return bgnode } func getLabelNode() -> SKNode { var labelNode = SKNode() var cropnode = SKLabelNode(fontNamed: FontFile) cropnode.fontColor = UIColor.whiteColor() cropnode.name = sKCropNode cropnode.text = sKCropNode cropnode.position = CGPointMake(CGRectGetMinX(self.frame)+cropnode.frame.width/2, CGRectGetMaxY(self.frame)-cropnode.frame.height) labelNode.addChild(cropnode) var emitternode = SKLabelNode(fontNamed: FontFile) emitternode.fontColor = UIColor.blueColor() emitternode.name = sKEmitterNode emitternode.text = sKEmitterNode emitternode.position = CGPointMake(CGRectGetMinX(self.frame) + emitternode.frame.width/2 , CGRectGetMidY(self.frame) - emitternode.frame.height/2) labelNode.addChild(emitternode) var lightnode = SKLabelNode(fontNamed: FontFile) lightnode.fontColor = UIColor.whiteColor() lightnode.name = sKLightNode lightnode.text = sKLightNode lightnode.position = CGPointMake(CGRectGetMaxX(self.frame) - lightnode.frame.width/2 , CGRectGetMaxY(self.frame) - lightnode.frame.height) labelNode.addChild(lightnode) var shapetnode = SKLabelNode(fontNamed: FontFile) shapetnode.fontColor = UIColor.greenColor() shapetnode.name = sKShapeNode shapetnode.text = sKShapeNode shapetnode.position = CGPointMake(CGRectGetMaxX(self.frame) - shapetnode.frame.width/2 , CGRectGetMidY(self.frame) - shapetnode.frame.height/2) labelNode.addChild(shapetnode) var videonode = SKLabelNode(fontNamed: FontFile) videonode.fontColor = UIColor.blueColor() videonode.name = sKVideoNode videonode.text = sKVideoNode videonode.position = CGPointMake(CGRectGetMaxX(self.frame) - videonode.frame.width/2 , CGRectGetMinY(self.frame) ) labelNode.addChild(videonode) return labelNode } var once:Bool = true override func touchesBegan(touches: NSSet, withEvent event: UIEvent) { if !once { return } for touch: AnyObject in touches { let location = touch.locationInNode(self) let node = self.nodeAtPoint(location) if node.name == sKCropNode { once = false var scene = CropScene() scene.anchorPoint = CGPointMake(0.5, 0.5) scene.scaleMode = .ResizeFill scene.size = self.size self.view?.presentScene(scene, transition:transitionEffect) } else if node.name == sKEmitterNode { once = false var scene = ParticleScene() scene.anchorPoint = CGPointMake(0.5, 0.5) scene.scaleMode = .ResizeFill scene.size = self.size self.view?.presentScene(scene, transition:transitionEffect) } else if node.name == sKLightNode { once = false var scene = LightScene() scene.scaleMode = .ResizeFill scene.size = self.size scene.anchorPoint = CGPointMake(0.5, 0.5) self.view?.presentScene(scene , transition:transitionEffect) } else if node.name == sKShapeNode { once = false var scene = ShapeScene() scene.scaleMode = .ResizeFill scene.size = self.size scene.anchorPoint = CGPointMake(0.5, 0.5) self.view?.presentScene(scene, transition:transitionEffect) } else if node.name == sKVideoNode { once = false var scene = VideoNodeScene() scene.scaleMode = .ResizeFill scene.size = self.size scene.anchorPoint = CGPointMake(0.5, 0.5) self.view?.presentScene(scene , transition:transitionEffect) } } } } We will get the following screen from the previous code: The screen is obtained when we execute the NodeMenuScene,swift file In the preceding code, after import statements, we defined some String variables. We are going to use these variables as Label names in scene .We also added our font name as a string variable. Inside this class, we made two node references: one for background and the other for those labels which we are going to use in this scene. We are using these two nodes to make layers in our game. It is best to categorize the nodes in a scene, so that we can optimize the code. We make an SKTransition object reference of the flip horizontal effect. You can use other transition effects too. Inside the didMoveToView() function, we just get the node and add it to our scene and set their z position. Now, if we look at the getBackgroundNode() function, we can see that we made a node by the SKNode class instance, a background by the SKSpriteNode class instance, and then added it to node and returned it. If you see the syntax of this function, you will see -> SKNode. It means that this function returns an SKNode object. The same goes in the function, getLabelNode(). It also returns a node containing all the SKLabelNode class objects. We have given a font and a name to these labels and set the position of them in the screen. The SKLabelNode class is used to make labels in Sprite Kit with many customizable options. In the touchBegan() function, we get the information on which Label is touched, and we then call the appropriate scene with transitions. With this, we have created a scene with the transition effect. By tapping on each button, you can see the transition effect. d shadows will also change themselves according to the source. Summary In this article, we learned about nodes in detail. We discussed many properties and functions of the SKNode class of Sprite Kit, along with its usage. Also, we discussed about the building of a node tree, and actions on a node tree. Now we are familiar with the major subclasses of SKNode, namely SKLabelNode, SKCropNode, SKShapeNode, SKEmitterNode, SKLightNode, and SKVideoNode, along with their implementation in our game. Resources for Article: Further resources on this subject: Sprites, Camera, Actions! [article] Cross-platform Building [article] Creating Games with Cocos2d-x is Easy and 100 percent Free [article]
Read more
  • 0
  • 0
  • 6193

article-image-optimization-python
Packt
19 Aug 2015
14 min read
Save for later

Optimization in Python

Packt
19 Aug 2015
14 min read
The path to mastering performance in Python has just started. Profiling only takes us half way there. Measuring how our program is using the resources at its disposal only tells us where the problem is, not how to fix it. In this article by Fernando Doglio, author of the book Mastering Python High Performance, we will cover the process of optimization, and to do that, we need to start with the basics. We'll keep it inside the language for now: no external tools, just Python and the right way to use it. We will cover the following topics in this article: Memoization / lookup tables Usage of default arguments (For more resources related to this topic, see here.) Memoization / lookup tables This is one of the most common techniques used to improve the performance of a piece of code (namely a function). We can save the results of expensive function calls associated to a specific set of input values and return the saved result (instead of redoing the whole computation) when the function is called with the remembered input. It might be confused with caching, since it is one case of it, although this term refers also, to other types of optimization (such as HTTP caching, buffering, and so on) This methodology is very powerful, because in practice, it'll turn what should have been a potentially very expensive call into a O(1) function call if the implementation is right. Normally, the parameters are used to create a unique key, which is then used on a dictionary to either save the result or obtain it if it's been already saved. There is, of course, a trade-off to this technique. If we're going to be remembering the returned values of a memoized function, then we'll be exchanging memory space for speed. This is a very acceptable trade-off, unless the saved data becomes more than what the system can handle. Classic use cases for this optimization are function calls that repeat the input parameters often. This will assure that most of the times, the memoized results are returned. If there are many function calls but with different parameters, we'll only store results and spend our memory without any real benefit, as seen in the following diagram: You can clearly see how the blue bar (Fixed params, memoized) is clearly the fastest use case, while the others are all similar due to their nature. Here is the code that generates values for the preceding chart. To generate some sort of time-consuming function, the code will call either the twoParams function or the twoParamsMemoized function several hundred times under different conditions, and it will log the execution time: import math import time import random class Memoized: def __init__(self, fn): self.fn = fn self.results = {} def __call__(self, *args): key = ''.join(map(str, args[0])) try: return self.results[key] except KeyError: self.results[key] = self.fn(*args) return self.results[key] @Memoized def twoParamsMemoized(values, period): totalSum = 0 for x in range(0, 100): for v in values: totalSum = math.pow((math.sqrt(v) * period), 4) + totalSum return totalSum def twoParams(values, period): totalSum = 0 for x in range(0, 100): for v in values: totalSum = math.pow((math.sqrt(v) * period), 4) + totalSum return totalSum def performTest(): valuesList = [] for i in range(0, 10): valuesList.append(random.sample(xrange(1, 101), 10)) start_time = time.clock() for x in range(0, 10): for values in valuesList: twoParamsMemoized(values, random.random()) end_time = time.clock() - start_time print "Fixed params, memoized: %s" % (end_time) start_time = time.clock() for x in range(0, 10): for values in valuesList: twoParams(values, random.random()) end_time = time.clock() - start_time print "Fixed params, without memoizing: %s" % (end_time) start_time = time.clock() for x in range(0, 10): for values in valuesList: twoParamsMemoized(random.sample(xrange(1,2000), 10), random.random()) end_time = time.clock() - start_time print "Random params, memoized: %s" % (end_time) start_time = time.clock() for x in range(0, 10): for values in valuesList: twoParams(random.sample(xrange(1,2000), 10), random.random()) end_time = time.clock() - start_time print "Random params, without memoizing: %s" % (end_time) performTest() The main insight to take from the preceding chart is that just like with every aspect of programming, there is no silver bullet algorithm that will work for all cases. Memoization is clearly a very basic way of optimizing code, but clearly, it won't optimize anything given the right circumstances. As for the code, there is not much to it. It is a very simple, non real-world example of the point I was trying to send across. The performTest function will take care of running a series of 10 tests for every use case and measure the total time each use case takes. Notice that we're not really using profilers at this point. We're just measuring time in a very basic and ad-hoc way, which works for us. The input for both functions is simply a set of numbers on which they will run some math functions, just for the sake of doing something. The other interesting bit about the arguments is that, since the first argument is a list of numbers, we can't just use the args parameter as a key inside the Memoized class' methods. This is why we have the following line: key = ''.join(map(str, args[0])) This line will concatenate all the numbers from the first parameter into a single value, which will act as the key. The second parameter is not used here, because it's always random, which would imply that the key would never be the same. Another variation of the preceding method is to pre-calculate all values from the function (assuming we have a limited number of inputs of course) during initialization and then refer to the lookup table during execution. This approach has several preconditions: The number of input values must be finite; otherwise it's impossible to precalculate everything The lookup table with all of its values must fit into memory Just like before, the input must be repeated, at least once, so the optimization both makes sense and is worth the extra effort There are different approaches when it comes to architecting the lookup table, all offering different types of optimizations. It all depends on the type of application and solution that you're trying to optimize. Here is a set of examples. Lookup on a list or linked list This solution works by iterating over an unsorted list and checking the key against each element, with the associated value as the result we're looking for. This is obviously a very slow method of implementation, with a big O notation of O(n) for both the average and worst case scenarios. Still, given the right circumstances, it could prove to be faster than calling the actual function every time. In this case, using a linked list would improve the performance of the algorithm over using a simple list. However, it would still depend heavily on the type of linked list it is (doubly linked list, simple linked list with direct access to the first and last elements, and so on). Simple lookup on a dictionary This method works using a one-dimensional dictionary lookup, indexed by a key consisting of the input parameters (enough of them create a unique key). In particular cases (like we covered earlier), this is probably one of the fastest lookups, even faster than binary search in some cases with a constant execution time (big O notation of O(1)). Note that this approach is efficient as long as the key-generation algorithm is capable of generating unique keys every time. Otherwise, the performance could degrade over time due to the many collisions on the dictionaries. Binary search This particular method is only possible if the list is sorted. This could potentially be an option depending on the values to sort. Yet, sorting them would require an extra effort that would hurt the performance of the entire effort. However, it presents very good results even in long lists (average big O notation of O(log n)). It works by determining in which half of the list the value is and repeating until either the value is found or the algorithm is able to determine that the value is not in the list. To put all of this into perspective, looking at the Memoized class mentioned earlier, it implements a simple lookup on a dictionary. However, this would be the place to implement either of the other algorithms. Use cases for lookup tables There are some classic example use cases for this type of optimization, but the most common one is probably the optimization of trigonometric functions. Based on the computing time, these functions are really slow. When used repeatedly, they can cause some serious damage to your program's performance. This is why it is normally recommended to precalculate the values of these functions. For functions that deal with an infinite domain universe of possible input values, this task becomes impossible. So, the developer is forced to sacrifice accuracy for performance by precalculating a discrete subdomain of the possible input values (that is, going from floating points down to integer numbers). This approach might not be ideal in some cases, since some systems require both performance and accuracy. So, the solution is to meet in the middle and use some form of interpolation to calculate the wanted value, based on the ones that have been precalculated. It will provide better accuracy. Even though it won't be as performant as using the lookup table directly, it should prove to be faster than doing the trigonometric calculation every time. Let's look at some examples of this, for instance, for the following trigonometric function: def complexTrigFunction(x): return math.sin(x) * math.cos(x)**2 We'll take a look at how simple precalculation won't be accurate enough and how some form of interpolation will result in a better level of accuracy. The following code will precalculate the values for the function on a range from -1000 to 1000 (only integer values). Then, it'll try to do the same calculation (only for a smaller range) for floating point numbers: import math import time from collections import defaultdict import itertools trig_lookup_table = defaultdict(lambda: 0) def drange(start, stop, step): assert(step != 0) sample_count = math.fabs((stop - start) / step) return itertools.islice(itertools.count(start, step), sample_count) def complexTrigFunction(x): return math.sin(x) * math.cos(x)**2 def lookUpTrig(x): return trig_lookup_table[int(x)] for x in range(-1000, 1000): trig_lookup_table[x] = complexTrigFunction(x) trig_results = [] lookup_results = [] init_time = time.clock() for x in drange(-100, 100, 0.1): trig_results.append(complexTrigFunction(x)) print "Trig results: %s" % (time.clock() - init_time) init_time = time.clock() for x in drange(-100, 100, 0.1): lookup_results.append(lookUpTrig(x)) print "Lookup results: %s" % (time.clock() - init_time) for idx in range(0, 200): print "%st%s" % (trig_results [idx], lookup_results[idx]) The results from the preceding code will help demonstrate how the simple lookup table approach is not accurate enough (see the following chart). However, it compensates for it on speed since the original function takes 0.001526 seconds to run while the lookup table only takes 0.000717 seconds. The preceding chart shows how the lack of interpolation hurts the accuracy. You can see how even though both plots are quite similar, the results from the lookup table execution aren't as accurate as the trig function used directly. So, now, let's take another look at the same problem. However, this time, we'll add some basic interpolation (we'll limit the rage of values from -PI to PI): import math import time from collections import defaultdict import itertools trig_lookup_table = defaultdict(lambda: 0) def drange(start, stop, step): assert(step != 0) sample_count = math.fabs((stop - start) / step) return itertools.islice(itertools.count(start, step), sample_count) def complexTrigFunction(x): return math.sin(x) * math.cos(x)**2 reverse_indexes = {} for x in range(-1000, 1000): trig_lookup_table[x] = complexTrigFunction(math.pi * x / 1000) complex_results = [] lookup_results = [] init_time = time.clock() for x in drange(-10, 10, 0.1): complex_results .append(complexTrigFunction(x)) print "Complex trig function: %s" % (time.clock() - init_time) init_time = time.clock() factor = 1000 / math.pi for x in drange(-10 * factor, 10 * factor, 0.1 * factor): lookup_results.append(trig_lookup_table[int(x)]) print "Lookup results: %s" % (time.clock() - init_time) for idx in range(0, len(lookup_results )): print "%st%s" % (complex_results [idx], lookup_results [idx]) As you might've noticed in the previous chart, the resulting plot is periodic (specially because we've limited the range from -PI to PI). So, we'll focus on a particular range of values that will generate one single segment of the plot. The output of the preceding script also shows that the interpolation solution is still faster than the original trigonometric function, although not as fast as it was earlier: Interpolation Solution Original function 0.000118 seconds 0.000343 seconds The following chart is a bit different from the previous one, especially because it shows (in green) the error percentage between the interpolated value and the original one: The biggest error we have is around 12 percent (which represents the peaks we see on the chart). However, it's for the smallest values, such as -0.000852248551417 versus -0.000798905501416. This is a case where the error percentage needs to be contextualized to see if it really matters. In our case, since the values related to that error are so small, we can ignore that error in practice. There are other use cases for lookup tables, such as in image processing. However, for the sake of this article, the preceding example should be enough to demonstrate their benefits and trade-off implied in their usage. Usage of default arguments Another optimization technique, one that is contrary to memoization, is not particularly generic. Instead, it is directly tied to how the Python interpreter works. Default arguments can be used to determine values once at function creation time instead of at run time. This can only be done for functions or objects that will not be changed during program execution. Let's look at an example of how this optimization can be applied. The following code shows two versions of the same function, which does some random trigonometric calculation: import math #original function def degree_sin(deg): return math.sin(deg * math.pi / 180.0) #optimized function, the factor variable is calculated during function creation time, #and so is the lookup of the math.sin method. def degree_sin(deg, factor=math.pi/180.0, sin=math.sin): return sin(deg * factor) This optimization can be problematic if not correctly documented. Since it uses attributes to precompute terms that should not change during the program's execution, it could lead to the creation of a confusing API. With a quick and simple test, we can double-check the performance gain from this optimization: import time import math def degree_sin(deg): return math.sin(deg * math.pi / 180.0) * math.cos(deg * math.pi / 180.0) def degree_sin_opt(deg, factor=math.pi/180.0, sin=math.sin, cos = math.cos): return sin(deg * factor) * cos(deg * factor) normal_times = [] optimized_times = [] for y in range(100): init = time.clock() for x in range(1000): degree_sin(x) normal_times.append(time.clock() - init) init = time.clock() for x in range(1000): degree_sin_opt(x) optimized_times.append(time.clock() - init) print "Normal function: %s" % (reduce(lambda x, y: x + y, normal_times, 0) / 100) print "Optimized function: %s" % (reduce(lambda x, y: x + y, optimized_times, 0 ) / 100) The preceding code measures the time it takes for the script to finish each of the versions of the function to run its code 1000 times. It saves those measurements, and finally, it does an average for each case. The result is displayed in the following chart: It clearly isn't an amazing optimization. However, it does shave off some microseconds from our execution time, so we'll keep it in mind. Just remember that this optimization could cause problems if you're working as part of an OS developer team. Summary In this article, we covered several optimization techniques. Some of them are meant to provide big boosts on speed, save memory. Some of them are just meant to provide minor speed improvements. Most of this article covered Python-specific techniques, but some of them can be translated into other languages as well. Resources for Article: Further resources on this subject: How to do Machine Learning with Python [article] The Essentials of Working with Python Collections [article] Symbolizers [article]
Read more
  • 0
  • 0
  • 24763
article-image-virtually-everything-everyone
Packt
19 Aug 2015
21 min read
Save for later

Virtually Everything for Everyone

Packt
19 Aug 2015
21 min read
This virtual reality thing calls into question, what does it mean to "be somewhere"? Before cell phones, you would call someone and it would make no sense to say, "Hey, where are you?" You know where they are, you called their house, that's where they are. So then cell phones come around and you start to hear people say, "Hello. Oh, I'm at Starbucks," because the person on the other end wouldn't necessarily know where you are, because you became un-tethered from your house for voice communications. So when I saw a VR demo, I had this vision of coming home and my wife has got the kids settled down, she has a couple minutes to herself, and she's on the couch wearing goggles on her face. I come over and tap her on the shoulder, and I'm like, "Hey, where are you?" It's super weird. The person's sitting right in front of you, but you don't know where they are.                                                      -Jonathan Stark, mobile expert and podcaster In this article, by Jonathan Linowes, author of the book Unity Virtual Reality Projects, we will define virtual reality and illustrate how it can be applied not only to games but also many other areas of interest and productivity. Welcome to virtual reality! In this book, we will explore what it takes to create virtual reality experiences on our own. We will take a walk through a series of hands-on projects, step-by-step tutorials, and in-depth discussions using the Unity 5 3D game engine and other free or open source software. Though the virtual reality technology is rapidly advancing, we'll try to capture the basic principles and techniques that you can use to make your VR games and applications feel immersive and comfortable. This article discusses the following topics: What is virtual reality? Differences between virtual reality (VR) and augmented reality (AR) How VR applications may differ from VR games Types of VR experiences Technical skills that are necessary for the development of VR What is virtual reality to you? Today, we are witnesses to the burgeoning consumer virtual reality, an exciting technology that promises to transform in a fundamental way how we interact with information, our friends, and the world at large. What is virtual reality? In general, VR is the computer-generated simulation of a 3D environment, which seems very real to the person experiencing it, using special electronic equipment. The objective is to achieve a strong sense of being present in the virtual environment. Today's consumer tech VR involves wearing a head-mounted display (such as goggles) to view stereoscopic 3D scenes. You can look around by moving your head, and walk around by using hand controls or motion sensors. You are engaged in a fully immersive experience. It's as if you're really there in some other virtual world. The following image shows a guy experiencing an Oculus Rift Development Kit 2 (DK2): Virtual reality is not new. It's been here for decades, albeit hidden away in academic research labs and high-end industrial and military facilities. It was big, clunky, and expensive. Ivan Sutherland invented the first head-mounted display in 1966, which is shown in the following image. It was tethered to the ceiling! In the past, several failed attempts have been made to bring consumer-level virtual reality products to the market. In 2012, Palmer Luckey, the founder of Oculus VR LLC, gave a demonstration of a makeshift head-mounted VR display to John Carmack, the famed developer of Doom, Wolfenstein 3D, and Quake classic video games. Together, they ran a successful Kickstarter campaign and released a developer kit called Oculus Rift Development Kit 1 (DK1) to an enthusiastic community. This caught the attention of investors as well as Mark Zuckerberg, and in March 2014, Facebook bought the company for $2 billion. With no product, no customers, and an infinite promise, the money and attention that it attracted has helped fuel a new category of consumer products. Others have followed suit, including Google, Sony, Samsung, and Steam. New innovations and devices that enhance the VR experience continue to be introduced. Most of the basic research has already been done and the technology is now affordable thanks in large part to the mass adoption of devices that work on mobile technology. There is a huge community of developers with an experience in building 3D games and mobile apps. Creative content producers are joining in and the media is talking it up. At last, virtual reality is real! Say what? Virtual reality is real? Ha! If it's virtual, how can it be... Oh, never mind. Eventually, we will get past the focus on the emerging hardware devices and recognize that content is king. The current generation of 3D development software (commercial, free, and open source) that has spawned a plethora of indie, or independent, game developers can also be used to build non-game VR applications. Though VR finds most of its enthusiasts in the gaming community, the potential applications reach well beyond that. Any business that presently uses 3D modeling and computer graphics will be more effective if it uses the VR technology. The sense of immersive presence that is afforded by VR can enhance all common online experiences today, which includes engineering, social networking, shopping, marketing, entertainment, and business development. In the near future, viewing 3D websites with a VR headset may be as common as visiting ordinary flat websites today. Types of head-mounted displays Presently, there are two basic categories of head-mounted displays for virtual reality—desktop VR and mobile VR. Desktop VR With desktop VR (and console VR), your headset is a peripheral to a more powerful computer that processes the heavy graphics. The computer may be a Windows PC, Mac, Linux, or a game console. Most likely, the headset is connected to the computer with wires. The game runs on the remote machine and the head-mounted display (HMD) is a peripheral display device with a motion sensing input. The term desktop is an unfortunate misnomer since it's just as likely to be stationed in either a living room or a den. The Oculus Rift (https://www.oculus.com/) is an example of a device where the goggles have an integrated display and sensors. The games run on a separate PC. Other desktop headsets include HTC/Valve Vive and Sony's project Morpheus for PlayStation. The Oculus Rift is tethered to a desktop computer via video and USB cables, and generally, the more graphics processing unit (GPU) power, the better. However, for the purpose of this book, we won't have any heavy rendering in our projects, and you can get by even with a laptop (provided it has two USB ports and one HDMI port available). Mobile VR Mobile VR, exemplified by Google Cardboard (http://www.google.com/get/cardboard/), is a simple housing (device) for two lenses and a slot for your mobile phone. The phone's display is used to show the twin stereographic views. It has rotational head tracking, but it has no positional tracking. Cardboard also provides the user with the ability to click or tap its side to make selections in a game. The complexity of the imagery is limited because it uses your phone's processor for rendering the views on the phone display screen. Other mobile VR headsets include Samsung Gear VR and Zeiss VR One, among others. Google provides the open source specifications, and other manufacturers have developed ready-made models for purchase, with prices for the same as low as $15. If you want to find one, just Google it! There are versions of Cardboard-compatible headsets that are available for all sizes of phones—both Android and iOS. Although the quality of the VR experience with a Cardboard device is limited (some even say that it is inadequate) and it's probably a "starter" device that will just be quaint in a couple of years, Cardboard is fine for the small projects in this book, and we'll revisit its limitations from time to time. The difference between virtual reality and augmented reality It's probably worthwhile clarifying what virtual reality is not. A sister technology to VR is augmented reality (AR), which superimposes computer generated imagery (CGI) over views of the real world. Limited uses of AR can be found on smart phones, tablets, handheld gaming systems such as the Nintendo 3DS, and even in some science museum exhibits, which overlay the CGI on top of live video from a camera. The latest innovations in AR are the AR headsets, such as Microsoft HoloLens and Magic Leap, which show the computer graphics directly in your field of view; the graphics are not mixed into a video image. If the VR headsets are like closed goggles, the AR headsets are like translucent sunglasses that employ a technology called light fields to combine the real-world light rays with CGI. A challenge for AR is ensuring that the CGI is consistently aligned with and mapped onto the objects in the real-world space and eliminate latency while moving about so that they (the CGI and objects in real-world space) stay aligned. AR holds as much promise as VR for future applications, but it's different. Though AR intends to engage the user within their current surroundings, virtual reality is fully immersive. In AR, you may open your hand and see a log cabin resting in your palm, but in VR, you're transported directly inside the log cabin and you can walk around inside it. We can also expect to see hybrid devices that somehow either combine VR and AR, or let you switch between modes. Applications versus games The consumer-level virtual reality starts with gaming. Video gamers are already accustomed to being engaged in highly interactive hyper-realistic 3D environments. VR just ups the ante. Gamers are early adopters of high-end graphics technology. Mass production of gaming consoles and PC-based components in the tens of millions and competition between vendors leads to lower prices and higher performance. Game developers follow suit, often pushing the state-of-the-art, squeezing every ounce of performance out of hardware and software. Gamers are a very demanding bunch, and the market has consistently stepped up to keep them satisfied. It's no surprise that many, if not most, of the current wave of the VR hardware and software companies are first targeting the video gaming industry. A majority of the demos and downloads that are available on Oculus Share (https://share.oculus.com/) and Google Play for the Cardboard app (https://play.google.com/store/search?q=cardboard&c=apps) are games. Gamers are the most enthusiastic VR advocates and seriously appreciate its potential. Game developers know that the core of a game is the game mechanics, or the rules, which are largely independent of the skin, or the thematic topic of the game. Gameplay mechanics can include puzzles, chance, strategy, timing, or muscle memory (twitch). VR games can have the same mechanic elements but might need to be adjusted for the virtual environment. For example, a first-person character walking in a console video game is probably going about 1.5 times faster than their actual pace in real life. If this wasn't the case, the player would feel that the game is too slow and boring. Put the same character in a VR scene and they will feel that it is too fast; it could likely make the player feel nauseous. In VR, you will want your characters to walk a normal, earthly pace. Not all video games will map well to VR; it may not be fun to be in the middle of a war zone when you're actually there. That said, virtual reality is also being applied in areas other than gaming. Though games will remain important, non-gaming apps will eventually overshadow them. These applications may differ from games in a number of ways, with the most significant having much less emphasis on game mechanics and more emphasis on either the experience itself or application-specific goals. Of course, this doesn't preclude some game mechanics. For example, the application may be specifically designed to train the user at a specific skill. Sometimes, the gamification of a business or personal application makes it more fun and effective in driving the desired behavior through competition. In general, non-gaming VR applications are less about winning and more about the experience itself. Here are a few examples of the kinds of non-gaming applications that people are working on: Travel and tourism: Visit faraway places without leaving your home. Visit art museums in Paris, New York, and Tokyo in one afternoon. Take a walk on Mars. You can even enjoy Holi, the spring festival of colors, in India while sitting in your wintery cabin in Vermont. Mechanical engineering and industrial design: Computer-aided design software such as AutoCAD and SOLIDWORKS pioneered three-dimensional modeling, simulation, and visualization. With VR, engineers and designers can directly experience the hands-on end product before it's actually built and play with what-if scenarios at a very low cost. Consider iterating a new automobile design. How does it look? How does it perform? How does it appear sitting in the driver's seat? Architecture and civil engineering: Architects and engineers have always constructed scale models of their designs, if only to pitch the ideas to clients and investors, or more importantly, to validate the many assumptions about the design. Presently, modeling and rendering software is commonly used to build virtual models from architectural plans. With VR, the conversation with stakeholders can be so much more confident. Other personnel, such as the interior designers, HVAC, and electrical engineers, can be brought into the process sooner. Real estate: Real estate agents have been quick adopters of the Internet and visualization technology to attract buyers and close sales. Real estate search websites were some of the first successful uses of the Web. Online panoramic video walk-throughs of for-sale properties are commonplace today. With VR, I can be in New York and find a place to live in Los Angeles. This will become even easier with mobile 3D-sensing technologies such as Google Project Tango (https://www.google.com/atap/projecttango), which performs a 3D scan of a room using a smartphone and automatically builds a model of the space. Medicine: The potential of VR for health and medicine may literally be a matter of life and death. Every day, hospitals use MRI and other scanning devices to produce models of our bones and organs that are used for medical diagnosis and possibly pre-operative planning. Using VR to enhance visualization and measurement will provide a more intuitive analysis. Virtual reality is also being used for the simulation of surgery to train medical students. Mental health: Virtual reality experiences have been shown to be effective in a therapeutic context for the treatment of post traumatic stress disorder (PTSD) in what's called exposure therapy, where the patient, guided by a trained therapist, confronts their traumatic memories through the retelling of the experience. Similarly, VR is being used to treat arachnophobia (spiders) and the fear of flying. Education: The educational opportunities for VR are almost too obvious to mention. One of the first successful VR experiences is Titans of Space, which lets you explore the solar system first hand. Science, history, arts, and mathematics—VR will help students of all ages because, as they say, field trips are much more effective than textbooks. Training: Toyota has demonstrated a VR simulation of drivers' education to teach teenagers about the risks of distracted driving. In another project, vocational students got to experience the operating of cranes and other heavy construction equipment. Training for first responders, police, and the fire and rescue workers can be enhanced with VR by presenting highly risky situations and alternative virtual scenarios. The NFL is looking to VR for athletic training. Entertainment and journalism: Virtually attend rock concerts and sporting events. Watch music videos Erotica. Re-experience news events as if you were personally present. Enjoy 360-degree cinematic experiences. The art of storytelling will be transformed by virtual reality. Wow, that's quite a list! This is just the low-hanging fruit. The purpose of this book is not to dive too deeply into any of these applications. Rather, I hope that this survey helps stimulate your thinking and provides a perspective towards how virtual reality has the potential to be virtually anything for everyone. What this book covers This book takes a practical, project-based approach to teach the specifics of virtual reality development using the Unity 3D game development engine. You'll learn how to use Unity 5 to develop VR applications, which can be experienced with devices such as the Oculus Rift or Google Cardboard. However, we have a slight problem here—the technology is advancing very rapidly. Of course, this is a good problem to have. Actually, it's an awesome problem to have, unless you're a developer in the middle of a project or an author of a book on this technology! How does one write a book that does not have obsolete content the day it's published? Throughout the book, I have tried to distill some universal principles that should outlive any near-term advances in virtual reality technology, that includes the following: Categorization of different types of VR experiences with example projects Important technical ideas and skills, especially the ones relevant to the building of VR applications General explanations on how VR devices and software works Strategies to ensure user comfort and avoid VR motion sickness Instructions on using the Unity game engine to build VR experiences Once VR becomes mainstream, many of these lessons will perhaps be obvious rather than obsolete, just like the explanations from the 1980's on how to use a mouse would just be silly today. Who are you? If you are interested in virtual reality, want to learn how it works, or want to create VR experiences yourself, this book is for you. We will walk you through a series of hands-on projects, step-by-step tutorials, and in-depth discussions using the Unity 3D game engine. Whether you're a non-programmer who is unfamiliar with 3D computer graphics, or a person with experience in both but new to virtual reality, you will benefit from this book. It is not a cold start with Unity, but you do not need to be an expert either. Still, if you're new to Unity, you can pick up this book as long as you realize that you'll need to adapt to the pace of the book. Game developers may already be familiar with the concepts in the book, which are reapplied to the VR projects while learning many other ideas that are specific to VR. Engineers and 3D designers may understand many of the 3D concepts, but they may wish to learn to use the game engine for VR. Application developers may appreciate the potential non-gaming uses of VR and want to learn the tools that can make this happen. Whoever you are, we're going to turn you into a 3D Software VR Ninja. Well, OK, this may be a stretch goal for this little book, but we'll try to set you on the way. Types of VR experiences There is not just one kind of virtual reality experience. In fact, there are many. Consider the following types of virtual reality experiences: Diorama: In the simplest case, we build a 3D scene. You're observing from a third-person perspective. Your eye is the camera. Actually, each eye is a separate camera that gives you a stereographic view. You can look around. First-person experience: This time, you're immersed in the scene as a freely moving avatar. Using an input controller (keyboard, game controller, or some other technique), you can walk around and explore the virtual scene. Interactive virtual environment: This is like the first-person experience, but it has an additional feature—while you are in the scene, you can interact with the objects in it. Physics is at play. Objects may respond to you. You may be given specific goals to achieve and challenges with the game mechanics. You might even earn points and keep score. Riding on rails: In this kind of experience, you're seated and being transported through the environment (or, the environment changes around you). For example, you can ride a roller coaster via this virtual reality experience. However, it may not necessarily be an extreme thrill ride. It can be a simple real estate walk-through or even a slow, easy, and meditative experience. 360-degree media: Think panoramic images taken with GoPro® on steroids that are projected on the inside of a sphere. You're positioned at the center of the sphere and can look all around. Some purists don't consider this "real" virtual reality, because you're seeing a projection and not a model rendering. However, it can provide an effective sense of presence. Social VR: When multiple players enter the same VR space and can see and speak with each other's avatars, it becomes a remarkable social experience. In this book, we will implement a number of projects that demonstrate how to build each of these types of VR experience. For brevity, we'll need to keep it pure and simple, with suggestions for areas for further investigation. Technical skills that are important to VR You will learn about the following in this book: World scale: When building for a VR experience, attention to the 3D space and scale is important. One unit in Unity is usually equal to one meter in the virtual world. First-person controls: There are various techniques that can be used to control the movement of your avatar (first-person camera), such as the keyboard keys, game controllers, and head movements. User interfacecontrols: Unlike conventional video (and mobile) games, all user interface components are in world coordinates in VR, not screen coordinates. We'll explore ways to present notices, buttons, selectors, and other User interface (UI) controls to the users so that they can interact and make selections. Physics and gravity: Critical to the sense of presence and immersion in VR is the physics and gravity of the world. We'll use the Unity physics engine to our advantage. Animations: Moving objects within the scene is called "animation" (duh!) It can either be along predefined paths, or it may use AI (artificial intelligence) scripting that follows a logical algorithm in response to events in the environment. Multiuser services: Real-time networking and multiuser games are not easy to implement, but online services make it easy without you having to be a computer engineer. Build and run: Different HMDs use different developer kits (SDK) and assets to build applications that target a specific devise. We'll consider techniques that let you use a single interface for multiple devices. We will write scripts in the C# language and use features of Unity as and when they are needed to get things done. However, there are technical areas that we will not cover, such as realistic rendering, shaders, materials, and lighting. We will not go into modeling techniques, terrains, or humanoid animations. Effective use of advanced input devices and hand and body tracking is proving to be critical to VR, but we won't have a chance to get into it here either. We also won't discuss game mechanics, dynamics, and strategies. We will talk about rendering performance optimization, but not in depth. All of these are very important topics that may be necessary for you to learn (or for someone in your team), in addition to this book, to build complete, successful, and immersive VR applications. Summary In this article, we looked at virtual reality and realized that it can mean a lot of things to different people and can have different applications. There's no single definition, and it's a moving target. We are not alone, as everyone's still trying to figure it out. The fact is that virtual reality is a new medium that will take years, if not decades, to reach its potential. VR is not just for games; it can be a game changer for many different applications. We identified over a dozen. There are different kinds of VR experiences, which we'll explore in the projects in this book. VR headsets can be divided into those that require a separate processing unit (such as a desktop PC or a console) that runs with a powerful GPU and the ones that use your mobile phone for processing. In this book, we will use an Oculus Rift DK2 as an example of desktop VR and Google Cardboard as the example of mobile VR, although there are many alternative and new devices available. We're all pioneers living at an exciting time. Because you're reading this book, you're one, too. Whatever happens next is literally up to you. As the personal computing pioneer Alan Kay said, "The best way to predict the future is to invent it." So, let's get to it! Resources for Article: Further resources on this subject: Looking Back, Looking Forward [article] Unity Networking – The Pong Game [article] Getting Started with Mudbox 2013 [article]
Read more
  • 0
  • 0
  • 3912

article-image-introduction-and-composition
Packt
19 Aug 2015
17 min read
Save for later

Introduction and Composition

Packt
19 Aug 2015
17 min read
In this article written by Diogo Resende, author of the book Node.js High Performance, we will discuss how high performance is hard, and how it depends on many factors. Best performance should be a constant goal for developers. To achieve it, a developer must know the programming language they use and, more importantly, how the language performs under heavy loads, these being disk, memory, network, and processor usage. (For more resources related to this topic, see here.) Developers will make the most out of a language if they know its weaknesses. In a perfect world, since every job is different, a developer should look for the best tool for the job. But this is not feasible and a developer wouldn't be able to know every best tool, so they have to look for the second best tool for every job. A developer will excel if they know few tools but master them. As a metaphor, a hammer is used to drive nails, and you can also use it to break objects apart or forge metals, but you shouldn't use it to drive screws. The same applies to languages and platforms. Some platforms are very good for a lot of jobs but perform really badly at other jobs. This performance can sometimes be mitigated, but at other times, can't be avoided and you should look for better tools. Node.js is not a language; it's actually a platform built on top of V8, Google's open source JavaScript engine. This engine implements ECMAScript, which itself is a simple and very flexible language. I say "simple" because it has no way of accessing the network, accessing the disk, or talking to other processes. It can't even stop execution since it has no kind of exit instruction. This language needs some kind of interface model on top of it to be useful. Node.js does this by exposing a (preferably) nonblocking I/O model using libuv. This nonblocking API allows you to access the filesystem, connect to network services and execute child processes. The API also has two other important elements: buffers and streams. Since JavaScript strings are Unicode friendly, buffers were introduced to help deal with binary data. Streams are used as simple event interfaces to pass data around. Buffers and streams are used all over the API when reading file contents or receiving network packets. A stream is a module, similar to the network module. When loaded, it provides access to some base classes that help create readable, writable, duplex, and transform streams. These can be used to perform all sorts of data manipulation in a simplified and unified format. The buffers module easily becomes your best friend when converting binary data formats to some other format, for example, JSON. Multiple read and write methods help you convert integers and floats, signed or not, big endian or little endian, from 8 bits to 8 bytes long. Most of the platform is designed to be simple, small, and stable. It's designed and ready to create some high-performance applications. Performance analysis Performance is the amount of work completed in a defined period of time and with a set of defined resources. It can be analyzed using one or more metrics that depend on the performance goal. The goal can be low latency, low memory footprint, reduced processor usage, or even reduced power consumption. The act of performance analysis is also called profiling. Profiling is very important for making optimized applications and is achieved by instrumenting either the source or the instance of the application. By instrumenting the source, developers can spot common performance weak spots. By instrumenting an application instance, they can test the application on different environments. This type of instrumentation can also be known by the name benchmarking. Node.js is known for being fast. Actually, it's not that fast; it's just as fast as your resources allow it. What Node.js is best at is not blocking your application because of an I/O task. The perception of performance can be misleading in Node.js applications. In some other languages, when an application task gets blocked—for example, by a disk operation—all other tasks can be affected. In the case of Node.js, this doesn't happen—usually. Some people look at the platform as being single threaded, which isn't true. Your code runs on a thread, but there are a few more threads responsible for I/O operations. Since these operations are extremely slow compared to the processor's performance, they run on a separate thread and signal the platform when they have information for your application. Applications blocking I/O operations perform poorly. Since Node.js doesn't block I/O unless you want it to, other operations can be performed while waiting for I/O. This greatly improves performance. V8 is an open source Google project and is the JavaScript engine behind Node.js. It's responsible for compiling and executing JavaScript, as well as managing your application's memory needs. It is designed with performance in mind. V8 follows several design principles to improve language performance. The engine has a profiler and one of the best and fast garbage collectors that exist, which is one of the keys to its performance. It also does not compile the language into byte code; it compiles it directly into machine code on the first execution. A good background in the development environment will greatly increase the chances of success in developing high-performance applications. It's very important to know how dereferencing works, or why your variables should avoid switching types. Here are other useful tips you would want to follow. You can use a style guide like JSCS and a linter like JSHint to enforce them to for yourself and your team. Here are some of them: Write small functions, as they're more easily optimized Use monomorphic parameters and variables Prefer arrays to manipulate data, as integer-indexed elements are faster Try to have small objects and avoid long prototype chains Avoid cloning objects because big objects will slow the operations Monitoring After an application is put into production mode, performance analysis becomes even more important, as users will be more demanding than you were. Users don't accept anything that takes more than a second, and monitoring the application's behavior over time and over some specific loads will be extremely important, as it will point to you where your platform is failing or will fail next. Yes, your application may fail, and the best you can do is be prepared. Create a backup plan, have fallback hardware, and create service probes. Essentially, anticipate all the scenarios you can think of, and remember that your application will still fail. Here are some of those scenarios and aspects that you should monitor: When in production, application usage is of extreme importance to understand where your application is heading in terms of data size or memory usage. It's important that you carefully define source code probes to monitor metrics—not only performance metrics, such as requests per second or concurrent requests, but also error rate and exception percentage per request served. Your application emits errors and sometimes throws exceptions; it's normal and you shouldn't ignore them. Don't forget the rest of the infrastructure. If your application must perform at high standards, your infrastructure should too. Your server power supply should be uninterruptible and stable, as instability will degrade your hardware faster than it should. Choose your disks wisely, as faster disks are more expensive and usually come in smaller storage sizes. Sometimes, however, this is actually not a bad decision when your application doesn't need that much storage and speed is considered more important. But don't just look at the gigabytes per dollar. Sometimes, it's more important to look at the gigabits per second per dollar. Also, your server temperature and server room should be monitored. High temperatures degrades performance and your hardware has an operation temperature limit. Security, both physical and virtual, is also very important. Everything counts for the standards of high performance, as an application that stops serving its users is not performing at all. Getting high performance Planning is essential in order to achieve the best results possible. High performance is built from the ground up and starts with how you plan and develop. It obviously depends on physical resources, as you can't perform well when you don't have sufficient memory to accomplish your task, but it also depends greatly on how you plan and develop an application. Mastering tools will give much better performance chances than just using them. Setting the bar high from the beginning of development will force the planning to be more prudent. Some bad planning of the database layer can really downgrade performance. Also, cautious planning will cause developers to think more about “use cases and program more consciously. High performance is when you have to think about a new set of resources (processor, memory, storage) because all that you have is exhausted, not just because one resource is. A high-performance application shouldn't need a second server when a little processor is used and the disk is full. In such a case, you just need bigger disks. Applications can't be designed as monolithic these days. An increasing user base enforces a distributed architecture, or at least one that can distribute load by having multiple instances. This is very important to accommodate in the beginning of the planning, as it will be harder to change an application that is already in production. Most common applications will start performing worse over time, not because of deficit of processing power but because of increasing data size on databases and disks. You'll notice that the importance of memory increases and fallback disks become critical to avoiding downtime. It's very important that an application be able to scale horizontally, whether to shard data across servers or across regions. A distributed architecture also increases performance. Geographically distributed servers can be more closed to clients and give a perception of performance. Also, databases distributed by more servers will handle more traffic as a whole and allow DevOps to accomplish zero downtime goals. This is also very useful for maintenance, as nodes can be brought down for support without affecting the application. Testing and benchmarking To know whether an application performs well or not under specific environments, we have to test it. This kind of test is called a benchmark. Benchmarking is important to do and it's specific to every application. Even for the same language and platform, different applications might perform differently, either because of the way in which some parts of an application were structured or the way in which a database was designed. Analyzing the performance will indicate bottleneck of your application, or if you may, the parts of the application that perform not good as others. These are the parts that need to be improved. Constantly trying to improve the worst performing parts will elevate the application's overall performance. There are plenty of tools out there, some more specific or focused on JavaScript applications, such as benchmarkjs (http://benchmarkjs.com/) and ben (https://github.com/substack/node-ben), and others more generic, such as ab (http://httpd.apache.org/docs/2.2/programs/ab.html) and httpload (https://github.com/perusio/httpload). There are several types of benchmark tests depending on the goal, they are as follows: Load testing is the simplest form of benchmarking. It is done to find out how the application performs under a specific load. You can test and find out how many connections an application accepts per second, or how many traffic bytes an application can handle. An application load can be checked by looking at the external performance, such as traffic, and also internal performance, such as the processor used or the memory consumed. Soak testing is used to see how an application performs during a more extended period of time. It is done when an application tends to degrade over time and analysis is needed to see how it reacts. This type of test is important in order to detect memory leaks, as some applications can perform well in some basic tests, but over time, the memory leaks and their performance can degrade. Spike testing is used when a load is increased very fast to see how the application reacts and performs. This test is very useful and important in applications that can have spike usages, and operators need to know how the application will react. Twitter is a good example of an application environment that can be affected by usage spikes (in world events such as sports or religious dates), and need to know how the infrastructure will handle them. All of these tests can become harder as your application grows. Since your user base gets bigger, your application scales and you lose the ability to be able to load test with the resources you have. It's good to be prepared for this moment, especially to be prepared to monitor performance and keep track of soaks and spikes as your application users start to be the ones responsible for continuously test load. Composition in applications Because of this continuous demand of performant applications, composition becomes very important. Composition is a practice where you split the application into several smaller and simpler parts, making them easier to understand, develop, and maintain. It also makes them easier to test and improve. Avoid creating big, monolithic code bases. They don't work well when you need to make a change, and they also don't work well if you need to test and analyze any part of the code to improve it and make it perform better. The Node.js platform helps you—and in some ways, forces you to—compose your code. Node.js Package Manager (NPM) is a great module publishing service. You can download other people's modules and publish your own as well. There are tens of thousands of modules published, which means that you don't have to reinvent the wheel in most cases. This is good since you can avoid wasting time on creating a module and use a module that is already in production and used by many people, which normally means that bugs will be tracked faster and improvements will be delivered even faster. The Node.js platform allows developers to easily separate code. You don't have to do this, as the platform doesn't force you to, but you should try and follow some good practices, such as the ones described in the following sections. Using NPM Don't rewrite code unless you need to. Take your time to try some available modules, and choose the one that is right for you. This reduces the probability of writing faulty code and helps published modules that have a bigger user base. Bugs will be spotted earlier, and more people in different environments will test fixes. Moreover, you will be using a more resilient module. One important and neglected task after starting to use some modules is to track changes and, whenever possible, keep using recent stable versions. If a dependency module has not been updated for a year, you can spot a problem later, but you will have a hard time figuring out what changed between two versions that are a year apart. Node.js modules tend to be improved over time and API changes are not rare. Always upgrade with caution and don't forget to test. Separating your code Again, you should always split your code into smaller parts. Node.js helps you do this in a very easy way. You should not have files bigger than 5 kB. If you have, you better think about splitting it. Also, as a good rule, each user-defined object should have its own separate file. Name your files accordingly: // MyObject.js module.exports = MyObject; function MyObject() { // … } MyObject.prototype.myMethod = function () { … }; Another good rule to check whether you have a file bigger than it should be; that is, it should be easy to read and understand in less than 5 minutes by someone new to the application. If not, it means that it's too complex and it will be harder to track and fix bugs later on. Remember that later on, when your application becomes huge, you will be like a new developer when opening a file to fix something. You can't remember all of the code of the application, and you need to absorb a file behavior fast. Garbage collection When writing applications, managing available memory is boring and difficult. When the application gets complex it's easy to start leaking memory. Many programming languages have automatic memory management, removing this management away from the developer by means of a Garbage Collector (GC). The GC is only a part of this memory management, but it's the most important one and is responsible for reclaiming memory that is no longer in use (garbage), by periodically looking at disposed referenced objects and freeing the memory associated with them. The most common technique used by GC is to monitor reference counting. This means that GC, for each object, holds the number (count) of other objects that reference it. When an object has no references to it, it can be collected, meaning, it can be disposed and its memory freed. CPU Profiling Profiling is boring but it's a good form of software analysis where you measure resource usage. This usage is measured over time and sometimes under specific workloads. Resources can mean anything the application is using, being it memory, disk, network or processor. More specifically, CPU profiling allows one to analyze how and how much your functions use the processor. You can also analyze the opposite, the non-usage of the processor, the idle time. When profiling the processor, we usually take samples of the call stack at a certain frequency and analyze how the stack changes (increases or decreases) over the sampling period. If we use profilers from the operating system we'll have more items in stack than you probably expect, as you'll get internal calls of node.js and V8. Summary Together, Node.js and NPM make a very good platform for developing high-performance applications. Since the language behind them is JavaScript and most applications these days are web applications, these combinations make it an even more appealing choice, as it's one less server-side language to learn (such as PHP or Ruby) and can ultimately allow a developer to share code on the client and server sides. Also, frontend and backend developers can share, read, and improve each other's code. Many developers pick this formula and bring with them many of their habits from the client side. Some of these habits are not applicable because on the server-side, asynchronous tasks must rule as there are many clients connected (as opposed to one) and performance becomes crucial. You now see how the garbage collector task is not that easy. It surely does a very good job managing memory automatically. You can help it a lot, especially if writing applications with performance in mind. Avoiding the GC old space growing is necessary to avoid long GC cycles, pausing your application and sometimes forcing your services to restart. Every time you create a new variable you allocate memory and you get closer to a new GC cycle. Even understanding how memory is managed, sometimes you need to inspect your memory usage behavior In environments seen nowadays, it's very important to be able to profile an application to identify bottlenecks, especially at the processor and memory level. Overall you should focus on your code quality before going into profiling. Resources for Article: Further resources on this subject: Node.js Fundamentals [Article] So, what is Node.js? [Article] Setting up Node [Article]
Read more
  • 0
  • 0
  • 13389

article-image-implementing-reusable-mini-firewall-using-transducers
Packt
19 Aug 2015
7 min read
Save for later

Implementing a Reusable Mini-firewall Using Transducers

Packt
19 Aug 2015
7 min read
In this article by Rafik Naccache, author of the book Clojure Data Structures and Algorithms Cookbook, we will be implementing a reusable mini-firewall using transducers. (For more resources related to this topic, see here.) Clojure's unique way of describing data transformations, reminiscent of its lisp and functional heritage, has set a new standard in the art of designing highly expressive algorithms. Clojure makes you address your problems in terms of highly declarative multi-stage transformations, and more often than not, you'll find your self alternating map, reduce, filter, and likely operations on the powerful seq abstraction to express the solution you came with as if you were explaining it in plain English to some non IT-savvy person. This declarative way of thinking yields much expressive power, and just looking at how SQL is ruling the database industry nowadays confirms that. But there was room for improvement in what Clojure provided for defining compoundable data transformations. First of all, the transformations you were writing were not reusable. Indeed, you'd catch yourself writing the same map operations over and over again, for instance, had you to do that same operation on different types of collections. Of course, you could encapsulate these in functions, but that will not avoid the second problem we wanted to highlight: the intermediate seq structure generated between the transformation stages. As a matter of fact, each and every step your threading macro passes through yields a new seq function, which is not the most efficient possible processing sequence. Lastly, the transformations you specified were closely tied to the type of input or output they operated on, and that gave the Clojure programming language designers the hassle of redefining all of these operations for any new data abstraction they'd come with while evolving the language. For all of these reasons, transducers were introduced starting from Clojure 1.7. As the official documentation put it, they are composable algorithmic transformations. They are independent from the context of their input and output sources and specify only the essence of the transformation in terms of an individual element. Transducers compose directly, without awareness of input or creation of intermediate aggregates. In a nutshell, a transducer is a "transformation from one reducing function to another", that is, a means to modify how to handle the way receiving the elements of some previous step is carried out. For instance, say we define the following transducer (note that transducers are, for the most part, the usual seq manipulation functions, but with an arity decremented by one): (map inc) We created a transformation that increments all the elements that some previous stage handed over to this transformation. Let's see an example of that (note the use of transduce function to apply a transducer on a seq function): (transduce (map inc) conj (range 0 3)) (range 0 3) hands a seq function of integers over to the (map inc) transducer, which on receiving them, gets them incremented and then, as per the transduce function specification, reduces them using the conj function. Transducers are also compoundable. You can achieve this using the traditional comp operator, used for traditional function composition, but there's one subtlety you must be aware of. Generally, comp is applied left to right, like so: (comp f g ) => f(g(x)) In the case of transducers, however, comp yields a transformation that looks as if composition was applied from right to left. This is not a change in the way comp works, but just how transducers are built. Composing transducers yields a function that changes the inner reducing function, as in the following example: (comp tr1 tr2)=> (fn[] ….. tr2(tr1)) So tr1 is applied on the input before tr2, although comp is still applied from left to right! To get a deeper understanding of transducer internals and how exactly they happen to be composed from right to left, watch Rich Hickey's talk about the subject at https://www.youtube.com/watch?v=6mTbuzafcII. We are going to use transducers to build a mini-firewall, implementing a two-stage data-transformation, one map and one filter stages, and are going to see how this mini-firewall can be interchangeably used on a vector or on a core.async channel. How to do it… First of all, be sure to use Clojure 1.7. Here is the Leiningen dependency to include: [org.clojure/clojure "1.7.0-RC1"] Besides, we are going to use some core.async channels in this recipe. Here is the namespace declaration: (ns recipe24.core (:require [clojure.java.io :refer :all] [clojure.core.async :refer [chan go >! <! >!! <!!]])) Now, let's define our first transducer, applying a source NAT on the TCP frames that pass through our firewall: (defn source-nat [pub-ip tcp-frame] ;; Change source ip into the public IP Interface (assoc tcp-frame :source-ip pub-ip)) (def public-interface "1.2.3.4") ;; This is our public interface IP (def tr-source-nat (map (partial source-nat public-interface))) ;; A transducer transforming tcp frames in such a way ;; That Source IP contains the public interface's. After that, let's concern ourselves with discarding or accepting TCP frames whose source, destination IPs, and ports are invalidated according to some connection whitelist: (defn accepted? [accept-rules tcp-frame] (not (nil? (some #{tcp-frame} accept-rules)))) ;; Verify if this TCP frame exists within ;; the accept-rules ACL (def sample-accept-rules [{:source-ip "4.5.3.8" :dest-ip "4.4.3.5" :dest-port 80} {:source-ip "4.5.3.9" :dest-ip "4.4.3.4" :dest-port 80}]) (def tr-filter-rules (filter (partial accepted? sample-accept-rules))) ;; A transducer dropping tcp frames not present ;; in our sample ACL Now we build our mini firewall, a transducer resulting from the composition of the previous two transducers. As we'll verify conformity of the connections before proceeding to the source IP NAT on them, we make sure that tr-filter-rules comes first in our composition: (def firewall(comp tr-filter-rules tr-source-nat)) Let's try our mini-firewall on a vector of TCP frames: (def sample-frames [{:source-ip "1.1.1.1" :dest-ip "2.3.2.2" :dest-port 10} {:source-ip "2.2.2.2" :dest-ip "4.3.4.1" :dest-port 20} {:source-ip "4.5.3.8" :dest-ip "4.4.3.5" :dest-port 30} {:source-ip "4.5.3.9" :dest-ip "4.4.3.4" :dest-port 80}]) (transduce firewall conj sample-frames) ;; => [{:source-ip "1.2.3.4", :dest-ip "4.4.3.4", :dest-port 80}] And now we'll show how our mini-firewall is reusable. For this, we are going to create a random-TCP frame generator that'll throw some traffic into a core.async channel. But this would not be just any channel; it would be one with a transducer, our firewall, attached to it. We'll see what happens when we try to read from it. First of all, let's write the random TCP frames generator: (def source-hosts [ "4.5.3.8" "4.5.3.9" "1.1.1.1" "2.2.2.2" ]) (def dest-hosts [ "4.4.3.5" "4.4.3.4" "2.3.2.2" "4.3.4.1" ]) (def ports [ 80]) (defn get-random-elt [v] (get v (rand-int (dec (count v))))) (defn random-frame [] {:source-ip (get-random-elt source-hosts) :dest-ip (get-random-elt dest-hosts) :dest-port (get-random-elt ports)}) Now it's time to create a core.async channel, to which we'll attach the firewall transducer. Note that when we attach a transducer to a core.async channel, the former must be buffered: (def from-net (chan 10 firewall)) We now need to throw, from time to time, a random TCP frame inside that channel: (defn gen-frames! [] (go (while true (let [fr (random-frame)] (>! from-net fr) (Thread/sleep 1000))))) We also have to build some function to print the TCP frames that were allowed to pass by the firewall: (defn show-fw-output! [] (while true (println "accepted & NAT'ted : " (<!! from-net)))) Let's see what happens when we launch the last two functions: (gen-frames!) (show-fw-output!) accepted & NATted : {:source-ip 1.2.3.4, :dest-ip 4.4.3.4, :dest-port 80} accepted & NATted : {:source-ip 1.2.3.4, :dest-ip 4.4.3.5, :dest-port 80} accepted & NATted : {:source-ip 1.2.3.4, :dest-ip 4.4.3.5, :dest-port 80} accepted & NATted : {:source-ip 1.2.3.4, :dest-ip 4.4.3.5, :dest-port 80} ... As you can see, we could only read through the channel "NAT'ted" TCP frames that have been granted access according to sample-access-rules. Actually, the transducer attached to the core.async channel transforms the data as they are flowing in, and we were able to do that by reusing our transducer specification without having to re-implement it specifically for that particular input type, that is, the channel. Summary In this article, we used transducers to build a mini-firewall, by implementing a two-stage data-transformation; one map and one filter stages, and saw how this mini-firewall can be interchangeably used on a vector or on a core.async channel. Resources for Article: Further resources on this subject: The Observer Pattern [article] Working with Incanter Datasets [article] Clojure for Domain-specific Languages - Design Concepts with Clojure [article]
Read more
  • 0
  • 0
  • 2165
article-image-diving-salt-internals
Packt
19 Aug 2015
32 min read
Save for later

Diving into Salt Internals

Packt
19 Aug 2015
32 min read
In this article by Joseph Hall, author of the book Mastering SaltStack, we will be looking at how Salt works under the hood. In this article, we will: Discover how Salt manages configuration files Look at how the Renderer system works Discuss how the Loader system handles modules Explore the State compiler, which drives so much of Salt With a more comprehensive understanding of the internals of Salt, you will be able to craft configurations and States that take advantage of the architectural decisions that inspired the design of Salt. (For more resources related to this topic, see here.) Understanding the Salt configuration One of the basic ideas around the Salt configuration is that a configuration management system should require as little configuration as possible. A concerted effort has been made by the developers to assign defaults that will apply to as many deployments as possible, while still allowing users to fine-tune the settings to their own needs. If you are just starting with Salt, you may not need to change anything. In fact, most of the time the Master configuration will be exactly what is needed for a small installation, while Minions will require almost no changes, if any. Following the configuration tree By default, most operating systems (primarily Linux-based) will store the Salt configuration in the /etc/salt/ directory. Unix distributions will often use the /usr/local/etc/salt/ directory instead, while Windows uses the C:salt directory. These locations were chosen in order to follow the design most commonly used by the operating system in question, while still using a location that was easy to make use of. We will refer to the /etc/salt/directory, but you can go ahead and replace it with the correct directory for your operating system. There are other paths that Salt makes use of as well. Various caches are typically stored in /var/cache/salt/, sockets are stored in /var/run/salt/, and State trees, Pillar trees, and Reactor files are stored in /srv/salt/, /srv/pillar/, and /srv/reactor/, respectively. Looking inside /etc/salt/ Inside the /etc/salt/ directory, there will generally be one of two files: Master and Minion (both will appear if you treat your Master as a Minion). When the documentation refers to Master configuration, it generally means the /etc/salt/master file, and of course Minion configuration refers to the /etc/salt/minion file. All configuration for these two daemons can technically go into their respective file. However, many users find reasons to break out their configuration into smaller files. This is often for organizational reasons, but there is a practical reason too: because Salt can manage itself, it is often easier to have it manage smaller, templated files, rather than one large, monolithic file. Because of this, the Master can also include any file with a .conf extension, found in the /etc/salt/master.d/ directory (and the Minion likewise in the minion.d/ directory). This is in keeping with the numerous other services that also maintain similar directory structures. Other subsystems inside Salt also make use of the .d/ directory structure. Notably, Salt Cloud makes use of a number of these directories. The /etc/salt/cloud, /etc/salt/cloud.providers, and /etc/salt/cloud.profiles files can also be broken out into the /etc/salt/cloud.d/, /etc/salt/cloud.providers.d/, and /etc/salt/cloud.profiles.d/ directories, respectively. Additionally, it is recommended to store cloud maps in the /etc/salt/cloud.maps.d/ directory. While other configuration formats are available elsewhere in Salt, the format of all of these core configuration files is YAML. This is by necessity; Salt needs a stable starting point from which to configure everything else. Likewise, the /etc/salt/ directory is hard-coded as the default starting point to find these files, though that may be overridden using the --config-dir (or -C) option: # salt-master --config-dir=/other/path/to/salt/ Managing Salt keys Inside the /etc/salt/ directory, there is also a pki/ directory, inside which is a master/ or minion/ directory (or both). This is where the public and private keys are stored. The Minion will only have three files inside the /etc/salt/pki/minion directory: minion.pem (the Minion's private RSA key), minion.pub (the Minion's public RSA key), and minion_master.pub (the Master's public RSA key). The Master will also keep its RSA keys in the /etc/salt/pki/master/ directory: master.pem and master.pub. However, at least three more directories will also appear in here. The minions.pre/ directory contains the public RSA keys for Minions that have contacted the Master but have not yet been accepted. The minions/ directory contains the public RSA keys for Minions that have been accepted on the Master. And the minions_rejected/ directory will contain keys for any Minion that has contacted the Master, but been explicitly rejected. There is nothing particularly special about these directories. The salt-key command on the Master is essentially a convenience tool for the user that moves public key files between directories, as requested. If needed, users can set up their own tools to manage the keys on their own, just by moving files around. Exploring the SLS directories As mentioned, Salt also makes use of other directory trees on the system. The most important of these are the directories that store SLS files, which are, by default, located in /srv/. Of the SLS directories, /srv/salt/ is probably the most important. This directory stores the State SLS files, and their corresponding top files. It also serves as the default root directory for Salt's built-in file server. There will typically be a top.sls file, and several accompanying .sls files and/or directories. A close second is the /srv/pillar/ directory. This directory maintains a copy of the static pillar definitions, if used. Like the /srv/salt/ directory, there will typically be a top.sls file and several accompanying .sls files and directories. But while the top.sls file matches the format used in /srv/salt/, the accompanying .sls files are merely collections of key/value pairs. While they can use Salt's Renderer, the resulting data does not need to conform to Salt's State compiler. Another directory which will hopefully find its way into your arsenal is the /srv/reactor/ directory. Unlike the others, there is no top.sls file in here. That is because the mapping is performed inside the Master configuration instead of the top system. However, the files in this directory do have a specific format. Examining the Salt cache Salt also maintains a cache directory, usually at /var/cache/salt/ (again, this may differ on your operating system). As before, both the Master and the Minion have their own directory for cache data. The Master cache directory contains more entries than the Minion cache, so we'll jump into that first. The Master job cache Probably the first cache directory that you'll run across in every day use is the jobs/ directory. In a default configuration, this contains all the data that the Master stores about the jobs that it executes. This directory uses hashmap-style storage. That means that a piece of identifying information (in this case, a job ID, or JID), has been processed with a hash algorithm, and a directory or directory structure has been created using a part or all of the hash. In this case, a split hash model has been used, where a directory has been created using the first two characters of the hash, and another directory under it has been created with the rest of the hash. The default hash type for Salt is MD5. This can be modified by changing the hash_type value in the Master configuration: hash_type: md5 Keep in mind that the hash_type is an important value that should be decided upon when first setting up a new Salt infrastructure, if MD5 is not the desired value. If it is changed (say, to SHA1) after an infrastructure has been using another value for a while, then any part of Salt that has been making use of it must be cleaned up manually. The JID is easy to interpret: it is a date and time stamp. For instance, a job ID of 20141203081456191706 refers to a job that was started on December 3, 2014, at 56 seconds and 191706 milliseconds past 8:14 AM. The MD5 of that JID would be f716a0e8131ddd6df3ba583fed2c88b7. Therefore, the data that describes that job would be located at the following path: /var/cache/salt/master/jobs/f7/16a0e8131ddd6df3ba583fed2c88b7 In that directory, there will be a file called jid. This will of course contain the job ID. There will also be a series of files with a .p extension. These files are all serialized by msgpack. Looking inside msgpack files If you have checked out a copy of Salt from Git, this data is easy to view. Inside the test/ directory in Salt's Git tree, there is a file called packdump.py. This can be used to dump the contents of the msgpack files to the console. First, there is a a file called .minions.p (notice the leading dot), which contains a list of Minions that were targeted by this job. This will look something like so: [ "minion1", "minion2", "minion3" ] The job itself will be described by a file called .load.p: { "arg": [ "" ], "fun": "test.ping", "jid": "20141203081456191706", "tgt": "*", "tgt_type": "glob", "user": "root" } There will also be one directory for each Minion that was targeted by that job and that contains the return information for that job, for that Minion. Inside that directory will be a file called return.p that contains the return data, serialized by msgpack. Assuming that the job in question did a simple test.ping, the return will look like the following: { "fun": "test.ping", "fun_args": [], "id": "minion1", "jid": "20141203081456191706", "retcode": 0, "return": true, "success": true } The Master-side Minion cache Once Salt has started issuing jobs, another cache directory will show up, called minions/. This directory will contain one entry per Minion, with cached data about that Minion. Inside this directory are two files: data.p and mine.p. The data.p file contains a copy of the Grains and Pillar data for that Minion. A (shortened) data.p file may look like the following: { "grains": { "biosreleasedate": "01/09/2013", "biosversion": "G1ET91WW (2.51 )", "cpu_model": "Intel(R) Core(TM) i5-3210M CPU @ 2.50GHz", "cpuarch": "x86_64", "os": "Ubuntu", "os_family": "Debian", }, "pillar": { "role": "web" } } The mine.p file contains mine data. A Minion can be configured to cache the return data from specific commands, in the cache directory on the Master, so that other Minions can look it up. For instance, if the output from test.ping and network.ip_addrs has been configured, the contents of the mine.p file will look as follows: { "network.ip_addrs": [ "192.168.2.101" ], "test.ping": true } The external file server cache In a default installation, Salt will keep its files in the /srv/salt/ directory. However, an external file server, by definition, maintains an external file store. For instance, the gitfs external file server keeps its files on a Git server, such as GitHub. However, it is incredibly inefficient to ask the Salt Master to always serve files directly from the Git. So, in order to improve efficiency, a copy of the Git tree is stored on the Master. The contents and layout of this tree will vary among the external file server modules. For instance, the gitfs module doesn't store a full directory tree as one might see in a normal Git checkout; it only maintains the information used to create that tree, using whatever branches are available. Other external file servers, however, may contain a full copy of the external source, which is updated periodically. The full path to this cache may look like this: /var/cache/salt/master/gitfs/ where gitfs is the name of the file server module. In order to keep track of the file changes, a directory called hash/ will also exist inside the external file server's cache. Inside hash/, there will be one directory per environment (that is, base, dev, prod, and so on). Each of those will contain what looks like a mirror image of the file tree. However, each actual file name will be appended with .hash.md5 (or the appropriate hash name, if different), and the contents will be the value of the checksum for that file. In addition to the file server cache, there will be another directory called file_lists/ that contains one directory per enabled file server. Inside that directory will be one file per environment, with a .p extension (such as base.p for the base environment). This file will contain a list of files and directories belonging to that environment's directory tree. A shortened version might look like this: { "dirs": [ ".", "vim", "httpd", ], "empty_dirs": [ ], "files": [ "top.sls", "vim/init.sls", "httpd/httpd.conf", "httpd/init.sls", ], "links": [] } This file helps Salt with a quick lookup of the directory structure, without having to constantly descend into a directory tree. The Minion-side proc/ directory The Minion doesn't maintain nearly as many cache directories as the Master, but it does have a couple. The first of these is the proc/ directory, which maintains the data for active jobs on the Minion. It is easy to see this in action. From the Master, issue a sleep command to a Minion: salt myminion test.sleep 300 --async This will kick off a process on the Minion which will wait for 300 seconds (5 minutes) before returning True to the Master. Because the command includes the --async flag, Salt will immediately return a JID to the user. While this process is running, log into the Minion and take a look at the /var/cache/salt/minion/proc/ directory. There should be a file bearing the name of the JID. The unpacked contents of this file will look like the following: {'arg': [300], 'fun': 'test.sleep', 'jid': '20150323233901672076', 'pid': 4741, 'ret': '', 'tgt': 'myminion', 'tgt_type': 'glob', 'user': 'root'} This file will exist until the job is completed on the Minion. If you'd like, you can see the corresponding file on the Master. Use the hashutil.md5_digest function to find the MD5 value of the JID: # salt myminion hashutil.md5_digest 20150323233901672076 External modules The other directory that you are likely to see on the Minion is the extmods/ directory. If custom modules have been synced to the Minion from the Master (using the _modules, _states, etc. directories on the Master), they will appear here. This is also easy to see in action. On the Master, create a _modules/ directory inside /srv/salt/. Inside this directory, create a file called mytest.py, with the following contents: def ping(): return True Then, from the Master, use the saltutil module to sync your new module to a Minion: salt myminion saltutil.sync_modules After a moment, Salt will report that it has finished: myminion: - modules.mytest Log into the Minion and look inside /var/cache/salt/minion/extmods/modules/. There will be two files: mytest.py and mytest.pyc. If you look at the contents of mytest.py, you will see the custom module that you created on the Master. You will also be able to execute the mytest.ping function from the Master: # salt myminion mytest.ping myminion: True The Renderer While the main Master and Minion configuration files must necessarily be stored in YAML, other files in Salt can take advantage of the wealth of file formats that the modern world of technology has to offer. This is because of the rendering system built into Salt, which can take files of arbitrary formats and render them into a structure that is usable by Salt. Rendering SLS files By default, all SLS files in Salt are rendered twice: first through the Jinja templating engine, and then through the PyYAML library. This provides some significant advantages: Jinja provides a fast, powerful, and easy to understand and use templating system that follows a Pythonic mindset, comfortable to many administrators. It is particularly well-suited for managing YAML files. YAML has a very shallow learning curve, making it easy to learn and understand. While it does support more complex syntax, such as parenthesis, brackets, and braces (JSON is technically syntactically-correct YAML), it is not required. However, it was immediately apparent, even before any Renderers were written, that there would be some dissent among users as to which formats were best suited to their own environments. A popular alternative to YAML, which was already in common usage in other software, is JSON. This format is more strict, making it somewhat harder to read, and even more difficult to write correctly. However, because JSON is more strict concerning how data is declared, a properly-formatted JSON file is more accurate than YAML, and easier to parse safely. Mako was also an early addition to the Salt toolkit. While Jinja adds just enough functionality to create a dynamic toolkit, Mako is designed to bring the full power of Python to templates. This is especially popular with a number of users in the DevOps community, who are known to mix code with content in a number of innovative ways. A primary design goal of Salt has always been to provide flexibility, and so the Renderer system was designed to be pluggable in the same way as the other components of Salt. While Jinja and YAML have been made the default, either or both can be replaced and, if necessary, even more Renderers can be brought into the mix. If your needs include changing the global Renderer from yaml_jinja, you can do so in the Master configuration file: renderer: json_mako However, you should consider very carefully whether this is best. Keep in mind that community examples, repositories, and formulae are generally kept in YAML, and if any templating needs to be done, Jinja is usually used. This will affect how you deal with the community or act as an enterprise customer on any support issues, and may confuse any experienced Salt users that your company hires. That said, even with a standard base of Jinja + YAML, there are times when using a different set of Renderers for a small subset of your SLS files is appropriate. Render pipes As previously mentioned, SLS files will be rendered using the configured default. However, it is possible to change how a file is rendered by adding a shebang (also known as, shabang) line to the top of the file. A file that is to be rendered only as YAML will begin with the following line: #!yaml However, in the Salt world, this is generally impractical. Adding a templating engine increases the power of an SLS file significantly. In order to use multiple Renderers in a specific order, add them to the shabang line in the desired order, separated by pipes: #!jinja|yaml This resembles the Unix method of piping smaller programs together, to create larger, more functional programs. There is no imposed limit on how many Renderers are piped together: #!mako|pyobjects|jinja|yaml|json However, this is pretty unrealistic. You will find that, in general, no more than two Renderers need to be used. Indeed, too many Renderers will create a complexity that is unreadable and unmaintainable. Use as many as are needed, and no more. It is important to note that SLS files will ultimately result in a specific data structure. The most accurate way to say this in simple terms is that the data generated by SLS files must be usable by the msgpack serialization package. This is the format used extensively throughout the various subsystems inside Salt (notably, the cache system). Serving templated files SLS files are not the only files that can take advantage of the Renderer. Any file that is served from an SLS file may also be rendered through a templating engine. These files aren't as specific as SLS files, because they do not need to return a specific data format; they only need to result in the arbitrary file contents that will be served by Salt. The most common usage of this is with the file.managed State. Adding a template argument to this State will cause the file to be rendered accordingly: /etc/httpd/conf/httpd.conf: file.managed: - source: salt://httpd/httpd.conf - template: jinja Because the templated file will not return data, Renderers that deal exclusively with data are not available here. But while YAML, JSON, msgpack, and the various Python-based Renderers are not available, Jinja, Mako, Cheetah, and the like can be used. Understanding the Loader The Loader system is at the heart of how Salt works. In a nutshell, Salt is a collection of modules, tied together with the Loader. Even the transport mechanisms, which enable communication between and define the Master, Minion, and Syndic hierarchies make use of modules that are managed by the Loader. Dynamic modules Salt's Loader system is a bit unconventional. Traditionally, most software has been designed to require all components that are supported to be installed. This is not the case with every package, of course. The Apache Web Server is an example of one project that supports a number of components that need not all be installed. Debian-based operating systems manage Apache modules by providing their modules-available/ and modules-enabled/ directories. RedHat-based systems take a different approach: all components that are supported by Apache's httpd package are required to be installed with it. Making such a demand with Salt is beyond unrealistic. So many packages are supported with the default installation of Salt, many of which compete with each other (and some of which compete, in some ways, with Salt itself) that it could be said that to build such a dependency tree into Salt would effectively turn Salt into its own operating system. However, even this is not entirely accurate. Because Salt supports a number of different Linux distributions, in addition to several Unix flavors and even Windows, it would be more accurate to say that installing every package that is supported by Salt would effectively turn Salt into several mutually-exclusive operating systems. Obviously, this is just not possible. Salt is able to handle this using multiple approaches. First, Grains provide critical information to Salt to help identify the platform on which it is running. Grains such as os and os_flavor are used often enough to help Salt know whether to use yum or apt to manage packages, or systemd or upstart to manage services. Each module is also able to check other dependencies on the system. The bulk of Salt's apache module makes use of the apachectl command (or apache2ctl as appropriate), so its availability is dependent upon whether or not that command exists on the system. This set of techniques enables Salt to appropriately detect, as the Minion process starts, which modules to make available to the user. A relatively new feature of Salt's Loader system is the ability to load modules on demand. Modules that support the Lazy Loader functionality will not actually load until requested by the user. This streamlines the start process for the Minion, and makes more effective use of the available resources. Execution modules It has often been said that most of the heavy lifting in Salt is performed by the execution modules. This is because Salt was designed originally as a remote execution system, and most module types that have been added to the loader have been designed to extend the functionality of remote execution. For instance, State modules are designed with one purpose in mind: to enforce the State of a certain aspect of a system. This could be to ensure that a package is installed, or that a service is running. The State module itself doesn't install the package or start the service; it calls out to the execution module to do so. A State module's only job is to add idempotency to an execution module. One could say that an important differentiator between runner modules and execution modules is that runners are designed to be used from the Master, while execution modules are designed to execute remotely on the Minion. However, runners were actually designed with something more specific in mind. System administrators have been using shell scripts for decades. From csh in Unix to bash in Linux, and even batch files in DOS and Windows, this has been the long-running standard. Runner modules were designed to allow Salt users to apply a scripting language to remote executions. Because so many early Salt users were also Python users, it was not generally difficult for them to use Python as their scripting language. As the Salt user base grew, so too did the number of users who were not fluent in Python, but the number of other options available for them also grew. Reactor modules are a type of module that can pull together execution modules and runner modules, and make them available to users with no programming experience. And because Salt States are actually applied using the State execution module, even States are available through Reactors. Cloud modules Cloud modules are not typically thought of by many people as Salt modules, perhaps because Salt Cloud started as a project separate from Salt, but in fact they have always used the Loader system. However, they do work a little differently. Unlike many other modules in Salt, Cloud modules do not make use of execution modules (although there is an execution module that makes use of the Salt Cloud). This is in part because Salt Cloud was designed to run on the Salt Master. However, it does not make use of runner modules either (though again, there is a runner module that can make use of the Salt Cloud). Salt Cloud's initial purpose was to create new VMs on various public cloud providers, and automatically accept their keys on the Salt Master. However, it quickly grew apparent that users wanted to control as many aspects of their cloud providers as possible; not just VM creation. Now Salt Cloud is able to perform any action that is available against a cloud provider. Some providers support more functionality than others. In some cases, this is because demand has not been presented, and in other cases because the appropriate developer has not yet had the resources to make the addition. But often it is because the features available on the provider itself may be limited. Whatever the situation, if a feature is available, then it can be added and made available via the Loader system. Plunging into the State compiler Salt was initially designed as a remote execution system that was to be used for gathering data normally collected by monitoring systems, and storing it for later analysis. However, as functionality grew, so too did a need to manage the execution modules that were doing the heavy lifting. Salt States were born from this need and, before long, the engine that managed them had expanded into other areas of Salt. Imperative versus declarative A point of contention between various configuration management systems is the concept of declarative versus imperative configurations. Before we discuss Salt's take on the matter, let's take a moment to examine the two. It may be easiest to think of imperative programming like a script: perform Task A and, when it is finished, perform Task B; once that has finished, perform Task C. This is what many administrators are used to, especially as it more closely resembles the shell scripts that have been their lifelines for so many decades. Chef is an example of a configuration management suite that is imperative in nature. Declarative definition is a newer concept, and more representative of object oriented programming. The basic idea is, the user declares which tasks need to be performed, and the software performs them in whichever order it sees fit. Generally, dependencies can also be declared that dictate that some tasks are not to be completed until others are. Puppet is a well-known example of a configuration management platform that is declarative in nature. Salt is unique in that it supports both imperative ordering and declarative execution. If no dependencies are defined then, by default, Salt will attempt to execute States in the order in which they appear in the SLS files. If a State fails because it requires a task that appears later, then multiple Salt runs will be required to complete all tasks. However, if dependencies are defined, States will be handled differently. They will still be evaluated in the order in which they appear, but dependencies can cause them to be executed in a different order. Consider the following Salt State: mysql: service: - running pkg: - installed file: - managed - source: salt://mysql/my.cnf - name: /etc/mysql/my.cnf In the first several versions of Salt that supported States, this would have been evaluated lexicographically: the file would have been copied into place first, then the package installed, then the service started, because in the English alphabet, F comes before P, and P comes before S. Happily, this is also the order that is probably desired. However, the default ordering system now in Salt is imperative, meaning States will be evaluated in the order in which they appear. Salt will attempt to start the mysql service, which will fail because the package is not installed. It will then attempt to install the mysql package, which will succeed. If this is a Debian-based system, installation of the package will also cause the service to start, in this case without the correct configuration file. Lastly, Salt will copy the my.cnf file into place, but will make no attempt to restart the service to apply the correct changes. A second State run will report success for all three States (the service is running, the package is installed, and the file is managed as requested), but a manual restart of the mysql service will still be required. Requisites To accommodate ordering issues caused by such issues, Salt uses requisites. These will affect the order in which States are evaluated and executed. Consider the following changes to the above salt State: mysql: service: - running - require: - package: mysql - watch: - file: mysql pkg: - installed - require: - file: mysql file: - managed - source: salt://mysql/my.cnf - name: /etc/mysql/my.cnf Even though the States have been defined in an order that is not appropriate, they will still be evaluated and executed correctly. The following will be the order that will be defined: service: mysql. pkg: mysql. file: mysql However, the mysql service requires that the mysql package is executed first. So, before executing the mysql service, it will look ahead and evaluate the mysql package. But, since the mysql package requires the mysql file to be executed first, it will jump ahead and evaluate the mysql file. Because the file State does not require anything else, Salt will execute it. Having completed the list of requirements for the pkg State, Salt will go back and execute it. And finally, having completed all service requirements, Salt will go back and execute the service. Following successful completion of the service State, it will move onto the next State and see if it has already been executed. It will continue in this fashion until all States have been evaluated and executed. It is in this manner that Salt is able to be both imperative (by allowing statements to be evaluated in the order in which they appear) and declarative (by allowing statements to be executed based on requisites). High and Low States The concept of High State has proven to be one of the most confusing things about Salt. Users understand that the state.highstate command performs a State run, but what exactly is a "High State"? And does the presence of a High State mean that there is a "Low State" as well? There are two parts of the State system that are in effect. "High" data refers generally to data as it is seen by the user. "Low" data refers generally to data as it is ingested and used by Salt. High States If you have worked with State files, you have already seen every aspect of this part of the State system. There are three specific components, each of which builds upon the one before it: High data SLS file High State Each individual State represents a piece of high data. If the previous SLS were broken into individual States they would look like this, respectively (ignoring the fact that duplicate top-level keys would comprise an invalid YAML file): mysql: service: - running - require: - pkg: mysql - watch: - file: mysql mysql: pkg: - installed - require: - file: mysql mysql: file: - managed - source: salt://mysql/my.cnf - name: /etc/mysql/my.cnf When combined together, along with other States they form an SLS file: iptables: service: - running mysql: service: - running - require: - package: mysql - watch: - file: mysql package: - installed - require: - file: mysql file: - managed - source: salt://mysql/my.cnf - name: /etc/mysql/my.cnf When these files are tied together using includes, and further glued together for use inside an environment using a top.sls file, they form a High State. top.sls base: '*': - mysql mysql.sls include: - iptables mysql: service: - running - require: - package: mysql - watch: - file: mysql package: - installed - require: - file: mysql file: - managed - source: salt://mysql/my.cnf - name: /etc/mysql/my.cnf iptables.sls iptables: service: - running When the state.highstate function is executed, Salt will compile all relevant SLS inside the top.sls, and any includes, into a single definition, called a High State. This can be viewed by using the state.show_highstate function: # salt myminion state.show_highstate --out yaml myminion: iptables: service: - running - order: 10000 __sls__: iptables __env__: base mysql: service: - running - require: - pkg: mysql - watch: - file: mysql - order: 10001 pkg: - installed - require: - file: mysql - order: 10002 file: - managed - source: salt://mysql/my.cnf - name: /etc/mysql/my.cnf - order: 10003 __sls__: mysql __env__: base Take note of the extra fields that are included in this output. First, an order is declared. This is something that can be explicitly declared by the user in an SLS file using either real numbers, or the first or last keywords. All States that are set to be first will have their order adjusted accordingly. Numerically ordered States will appear next. Salt will then add 10000 to the last defined number (which is 0 by default), and add any States that are not explicitly ordered. Finally, any States set to last will be added. Salt will also add some variables that it uses internally, to know which environment (__env__) to execute the State in, and which SLS file (__sls__) the State declaration came from. Remember that the order is still no more than a starting point; the actual High State will be executed based first on requisites, and then on order. Low States Once the final High State has been generated, it will be sent to the State compiler. This will reformat the State data into a format that Salt uses internally to evaluate each declaration, and feed data into each State module (which will in turn call the execution modules, as necessary). As with high data, low data can be broken into individual components: Low State Low chunks State module Execution module(s) The low data can be viewed using the state.show_lowstate function: # salt myminion state.show_lowstate --out yaml myminion: - __env__: base __id__: iptables __sls__: iptables fun: running name: iptables order: 10000 state: service - __env__: base __id__: mysql __sls__: mysql fun: running name: mysql order: 10001 require: - package: mysql state: service watch: - file: mysql - __env__: base __id__: mysql __sls__: mysql fun: installed name: mysql order: 10002 require: - file: mysql state: package - __env__: base __id__: mysql __sls__: mysql fun: managed name: /etc/mysql/my.cnf order: 10003 source: salt://mysql/my.cnf state: file Together, all this comprises a Low State. Each individual item is a Low Chunk. The first Low Chunk on this list looks like this: - __env__: base __id__: iptables __sls__: iptables fun: running name: iptables order: 10000 state: service Each low chunk maps to a State module (in this case, service) and a function inside that State module (in this case, running). An ID is also provided at this level (__id__). Salt will map relationships (that is, requisites) between States using a combination of State and __id__. If a name has not been declared by the user, then Salt will automatically use the __id__ as the name. Once a function inside a State module has been called, it will usually map to one or more execution modules which actually do the work. Let's take a moment to examine what goes down when Salt gets to that point. Summary We have discussed how Salt manages its own configuration, as well as the Loader and Renderer systems. We have also gone into significant details about how the State system works. Resources for Article:   Further resources on this subject: Introducing Salt [article] Importing Dynamic Data [article] Veil-Evasion [article]
Read more
  • 0
  • 0
  • 8011

article-image-how-to-build-a-remote-control-car-with-zigbee
Bill Pretty
18 Aug 2015
7 min read
Save for later

How to Build a Remote Control Car with Zigbee

Bill Pretty
18 Aug 2015
7 min read
In this first of several articles Bill Pretty, author of Getting Started with Electronic Projects and Building a Home Security System with BeagleBone, shows us how to use a Zigbee point-to-point network to create our very own remote controlled RC car! What Network are we using? In the final chapter of Getting Started with Electronics Projects, I introduced you to the concept of ZigBee RF transmitters and receivers. We used the Type 1 version of XBee devices, because they were capable of “pin mirroring”. In simple terms, this means that whatever change occurs on the I/O pin of the transmitting device is reflected on the corresponding output pin of the receiving XBee device. This feature allows us to use the receiver XBees in a stand-alone configuration, like we did with the alarm system. The type 1 XBee network is configured as a “Star” network. That means that there is only one ‘master’ and in our case, only one end device. Figure 1 Star Network In the figure above, the controlling XBee is shown in red, while the end device(s) are shown in green. The RC cars that we are going to be using in this article have three ‘basic’ types of steering control. I will refer to them as ‘toy’ cars to differentiate them from higher end ‘model’ cars which tend to come out of the box with servo control and microprocessor control. We will be using toy cars that I got from the local thrift store for less than ten dollars, because the remote control was missing! While the remote would come in handy, it isn’t really necessary, because we will be replacing both ends of the system. The first kind of steering system is the simplest and the cheapest to manufacture. A command from the controller tells the front wheels to turn left or right and the wheels will continue to try and turn in the proper direction as long as they are receiving the command, even though the steering motor has hit the limit of the travel. This type of steering may have a series resistor to prevent the motor from burning out, or it may just rely on the limited current capacity of the battery. When the user centres the control, the motor is turned off and an extension spring returns the wheels to the centre position. This makes our control job really easy. We need four signals/commands; forward, reverse, left and right. The spring does the rest. The second type of steering circuit is a simple single pole, double throw switch; which is connected to the motor. The switch changes position when the motor reaches either the full left or full right position. There is no indication of ‘centre’, so this type of steering still relies on a spring for auto-centring. Figure 2 Switch Feedback The third and most complicated type of steering control tends to be seen in toys with rechargeable batteries, like NiCad or NiMH as well. These types of batteries provide more power than alkaline ‘flash light’ batteries. For that reason a slightly more complex steering system is required. This type of system uses an “analog feedback” servo system. What it is is a standard DC motor connected to a potentiometer; so that when the motor turns so does the pot. The wiper of the pot provides a DC voltage that is proportional to the position of the motor. Figure 3 Analog Feedback Servo The steering control circuit will receive three voltages that we are concerned about; full left, full right and centre. This type of servo requires a comparator circuit or a small microprocessor to detect the three positions. More on comparator circuits in future articles. The following circuit will work with the first two types of steering circuits. The circuit diagrams are shown below. Figure 4 In the circuit above, U2 is half of an SN754410 H-Bridge controller integrated circuit. In order for the motor to move the ‘M1’ and ‘M1/’ signals must be at a different level. A logic 0 on M1/ and a logic 1 on M1 will make the motor turn in one direction and a logic 1 on M1/ and a logic 0 on M1 will make it turn in the opposite direction. While either two logic 1’s or 0’s will cause the motor to stop moving. In this way we have either left/right/centre or forward/reverse/stop control. Figure 5 Figure 5 above, shows the connections to the XBee receiver module. As I said earlier, when an I/O pin on the controller goes low, the corresponding pin on the receiver “mirrors” that input. The outputs M1, M1/, M2 and M2/ are connected to the drive and steering motors. The SN754410 is capable of sourcing or sinking about 1 amp, so AUX-1 and AUX-2 can be used to drive LED’s or other devices. The controller circuit is shown below. The circuit is actually taken from a sample circuit I found on the XBee vendor site: http://examples.digi.com/sensors/joystick-with-xbee/ Figure 6 XBee Transmitter The colours represent the wires which come from the joystick. In this case it is a simple arcade style joystick, which uses four micro switches rather than potentiometers. So when the joystick is moved, one or more inputs to the XBee transmitter are grounded (Logic 0) and this information is sent to the receiver in the car. The joystick, available from Adafruit http://examples.digi.com/sensors/joystick-with-xbee/ is shown below. Figure 7 Adafruit Joystick The AUX-1 input is connected to a simple toggle switch, which in this case is used to control the vehicle’s headlights. This is a very simple controller which is designed to control the first two types of vehicles. A more complex design is required to control more complex vehicles. A single board computer such as an Arduino, RaspberryPi or Beaglebone would be required, so that we can read inputs from the joystick as well as digital and analog inputs sent to it from the vehicle. Summary So that’s it for this blog, I hope you enjoyed reading it. In future editions of this topic, I’ll show you how to configure the XBee modules using the Windows application. This method is a lot easier that the one presented in the example on the Digi site. About the Author Bill began his career in electronics in the early 80’s with a small telecom startup company that would eventually become a large multinational. He left there to pursue a career in commercial aviation in Canada’s north. From there he joined the Ontario Center for Microelectronics, a provincially funded research and development center. Bill left there for a career in the military as a civilian contractor at what was then called Defense Research Establishment Ottawa. That began a career which was to span the next 25 years, and continues today. Over the years Bill has acquired extensive knowledge in the field of technical security and started his own company in 2010. That company is called William Pretty Security Inc. and provides support in the form of research and development, to various law enforcement and private security agencies. Bill has published and presented a number of white papers on the subject of technical security. Bill was also a guest presenter for a number of years at the Western Canada Technical Conference, a law enforcement only conference held every year in western Canada. A selection of these papers is available for download from his web site. www.williamprettysecurity.com If you’re interested in building more of your own projects, then be sure to check out Bill’s titles available now in both print and eBook format! If you’re new to working with microcontrollers be sure to pick up Getting Started with Electronic Projects to start creating a whole host of great projects you can do in a single weekend with LM555, ZigBee, and BeagleBone components! If you’re looking for something more advanced to tinker with, then Bill’s other title - Building a Home Security System with BeagleBone – is perfect for hobbyists looking to make a bigger project!
Read more
  • 0
  • 0
  • 9435
Modal Close icon
Modal Close icon