Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Events
Videos
Audiobooks
Packt 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-lighting-basics
Packt
19 Feb 2016
5 min read
Save for later

Lighting basics

Packt
19 Feb 2016
5 min read
In this article by Satheesh PV, author of the book Unreal Engine 4 Game Development Essentials, we will learn that lighting is an important factor in your game, which can be easily overlooked, and wrong usage can severely impact on performance. But with proper settings, combined with post process, you can create very beautiful and realistic scenes. We will see how to place lights and how to adjust some important values. (For more resources related to this topic, see here.) Placing lights In Unreal Engine 4, lights can be placed in two different ways. Through the modes tab or by right-clicking in the level: Modes tab: In the Modes tab, go to place tab (Shift + 1) and go to the Lights section. From there you can drag and drop various lights. Right-clicking: Right-click in viewport and in Place Actor you can select your light. Once a light is added to the level, you can use transform tool (W to move, E to rotate) to change the position and rotation of your selected light. Since Directional Light casts light from an infinite source, updating their location has no effect. Various lights Unreal Engine 4 features four different types of light Actors. They are: Directional Light: Simulates light from a source that is infinitely far away. Since all shadows casted by this light will be parallel, this is the ideal choice for simulating sunlight. Spot Light: Emits light from a single point in a cone shape. There are two cones (inner cone and outer cone). Within inner cone, light achieves full brightness and between inner and outer cone a falloff takes place, which softens the illumination. That means after inner cone, light slowly loses its illumination as it goes to outer cone. Point Light: Emits light from a single point to all directions, much like a real-world light bulb. Sky Light: Does not really emit light, but instead captures the distant parts of your scene (for example, Actors that are placed beyond Sky Distance Threshold) and applies them as light. That means you can have lights coming from your atmosphere, distant mountains, and so on. Note that Sky Light will only update when you rebuild your lighting or by pressing Recapture Scene (in the Details panel with Sky Light selected). Common light settings Now that we know how to place lights into a scene, let's take a look at some of the common settings of a light. Select your light in a scene and in the Details panel you will see these settings: Intensity: Determines the intensity (energy) of the light. This is in lumens units so, for example, 1700 lm (lumen units) corresponds to a 100 W bulb. Light Color: Determines the color of light. Attenuation Radius: Sets the limit of light. It also calculates the falloff of light. This setting is only available in Point Lights and Spot Lights. Attenuation Radius from left to right: 100, 200, 500. Source Radius: Defines the size of specular highlights on surfaces. This effect can be subdued by adjusting the Min Roughness setting. This also affects building light using Lightmass. Larger Source Radius will cast softer shadows. Since this is processed by Lightmass, it will only work on Lights with mobility set to Static Source Radius 0. Notice the sharp edges of the shadow. Source Radius 0. Notice the sharp edges of the shadow. Source Length: Same as Source Radius. Light mobility Light mobility is an important setting to keep in mind when placing lights in your level because this changes the way light works and impacts on performance. There are three settings that you can choose. They are as follows: Static: A completely static light that has no impact on performance. This type of light will not cast shadows or specular on dynamic objects (for example, characters, movable objects, and so on). Example usage: Use this light where the player will never reach, such as distant cityscapes, ceilings, and so on. You can literally have millions of lights with static mobility. Stationary: This is a mix of static and dynamic light and can change its color and brightness while running the game, but cannot move or rotate. Stationary lights can interact with dynamic objects and is used where the player can go. Movable: This is a completely dynamic light and all properties can be changed at runtime. Movable lights are heavier on performance so use them sparingly. Only four or fewer stationary lights are allowed to overlap each other. If you have more than four stationary lights overlapping each other the light icon will change to red X, which indicates that the light is using dynamic shadows at a severe performance cost! In the following screenshot, you can easily see the overlapping light. Under View Mode, you can change to Stationary Light Overlap to see which light is causing an issue. Summary We will look into different light mobilities and learn more about Lightmass Global Illumination, which is the static Global Illumination solver created by Epic games. We will also learn how to prepare assets to be used with it. Resources for Article:   Further resources on this subject: Understanding Material Design [article] Build a First Person Shooter [article] Machine Learning With R [article]
Read more
  • 0
  • 0
  • 20218

article-image-ces-2019-is-bullshit-we-dont-need-after-2018s-techlash
Richard Gall
08 Jan 2019
6 min read
Save for later

CES 2019 is bullshit we don't need after 2018's techlash

Richard Gall
08 Jan 2019
6 min read
The asinine charade that is CES is running in Las Vegas this week. Describing itself as 'the global stage of innovation', CES attempts to set the agenda for a new year in tech. While ostensibly it's an opportunity to see how technology might impact the lives of all of us over the next decade (or more), it is, in truth, a vapid carnival that does nothing but make the technology industry look stupid. Okay, perhaps I'm being a fun sponge: what's wrong with smart doorbells, internet connected planks of wood and other madcap ideas? Well, nothing really - but those inventions are only the tip of the iceberg. Disagree? Don't worry: you can find the biggest announcements from day one of CES 2019 here. What CES gets wrong Where CES really gets it wrong - and where it drives down a dead end of vacuity - is how it showcases the mind numbing rush to productize and then commercialize some of the really serious developments that could transform the world in a way that is ultimately far less trivial than the glitz and glamor of the way it is presented in the media would suggest. This isn't to say that there there won't be important news and interesting discussions to come out of CES. But even the more interesting topics can be diluted, becoming buzzwords for marketers to latch onto. As Wired remarks on Twitter, "the term AI-powered is used loosely and is almost always a marketing ploy, whether or not a product is impacted by AI." In the same thread, the publication's account also notes that 5G, another big theme for the event, won't be widely available for at least another 12 months. https://twitter.com/WIRED/status/1082294957979910144 Ultimately, what this tells us is that the focus of CES isn't really technology - not in the sense of how we build it and how we should use it. Instead, it is an event dedicated to the ways we can sell it. Perhaps in previous years, the gleeful excitement of CES was nothing but a bit of light as we recover from the holiday period. But this year it's different. 2018 was a year of reckoning in tech, as a range of scandals emerged that underlined the ways in which exciting technological innovation can be misused and deployed against the very people we assume it should be helping. From the Cambridge Analytica scandal to the controversy surrounding Amazon's Rekognition, Google's Project Dragonfly, and Microsoft's relationship with ICE, 2018 was a year that made it clearer than ever that buried somewhere beneath novel and amusing inventions, and better quality television screens are a set of interests that have little interest in making life better for people. The corporate glamor of CES 2019 is just kitsch It's not news that there are certain organisations and institutions that don't have the interests of the majority at heart. But CES 2019 does take on a new complexion in the shadow of all that has happened in 2019. The question 'what's the point of all this' takes on a more serious edge. When you add in the dissent that has come from a growing part of the Silicon Valley workforce, CES 2019 starts to look like an event that, much like many industry leaders, wants to bury the messy and complex reality of building software in favor of marketing buzz. In The Unbearable Lightness of Being, the author Milan Kundera describes kitsch as "the absolute denial of shit." It's following this definition that you can see CES as a kitsch event. This is because the it pushes the decisions and inevitable trade offs that go into developing new technologies and products into the shadows. It doesn't take negative consequences seriously. It's all just 'shit' that should be ignored. This all adds up to a message that seems to be: better doesn't even need to be built. It's here already, no risks, no challenges. Developers don't really feature at CES. That's not necessarily a problem - after all, it's not an event for them, and what developer wants to spend time hearing marketers talk about AI? But if 2018 has taught us anything, it's that a culture of commercialization that refuses to consider consequences other than what can be done in the service of business growth can be immensely damaging. It hurts people, and it might even be hurting democracy. Okay, the way to correct things probably isn't to simply invite more engineers to CES. But by the same token, CES is hardly helping things either. Everything important is happening outside the event Everything important seems to be happening at the periphery of this year's CES, in some instances quite literally outside the building. Apple's ad, for example, might have been a clever piece of branding, but it has captured the attention of the world. Arguably, it's more memorable than much of what's happening inside the event. And although it's possible to be cynical, it does nevertheless raise important questions about a number of companies attitudes to user data. https://twitter.com/NateIngraham/status/1081612316532064257 Another big talking point as this year's event began is who isn't present. Due to the government shutdown a number of officials that were due to attend and speak have had to cancel. This acts as a reminder of the wider context in which CES 2019 is taking place, in which a nativist government looks set on controlling controlling who and how people move across borders. It also highlights how euphemistic the phrase 'consumer technology' really is. TVs and cloud connected toilets might take the headlines, but its government surveillance that will likely have the biggest impact on our lives in the future. Not that any of this seemed to matter to Gary Shapiro, the Chief Executive of the Consumer Technology Association (the organization that puts on CES). Speaking to the BBC, Shapiro said: “It’s embarrassing to be on the world stage with a dominant event in the world of technology, and our federal government... can't be there to host their colleague government executives from around the world.” Shapiro's frustration is understandable from an organizer's perspective. But it also betrays the apparent ethos of CES: what's happening outside doesn't matter. We all deserve better than CES 2019 The new products on show at CES 2019 won't make everything better. There's a chance they will make everything worse. Arguably, the more blindly optimistic we are that they'll make things better, the more likely they are to make things worse. It's only by thinking through complex questions, and taking time to consider the possible consequences of our decision making as developers, product managers, or business people that we can actually be sure that things will get better. This doesn't mean we need to stop getting excited about new inventions and innovations. But things like smart cities and driverless cars pose a whole range of issues that shouldn't be buried in the optimistic schmaltz of events like CES. They need care and attention from policy makers, designers, software engineers, and many others to ensure they are actually going to help to build a better world for people.
Read more
  • 0
  • 0
  • 20214

article-image-working-powershell
Packt
07 Sep 2015
17 min read
Save for later

Working with PowerShell

Packt
07 Sep 2015
17 min read
In this article, you will cover: Retrieving system information – Configuration Service cmdlets Administering hosts and machines – Host and MachineCreation cmdlets Managing additional components – StoreFront Admin and Logging cmdlets (For more resources related to this topic, see here.) Introduction With hundreds or thousands of hosts to configure and machines to deploy, configuring all the components manually could be difficult. As for the previous XenDesktop releases, and also with the XenDesktop 7.6 version, you can find an integrated set of PowerShell modules. With its use, IT technicians are able to reduce the time required to perform management tasks by the creation of PowerShell scripts, which will be used to deploy, manage, and troubleshoot at scale the greatest part of the XenDesktop components. Working with PowerShell instead of the XenDesktop GUI will give you more flexibility in terms of what kind of operations to execute, having a set of additional features to use during the infrastructure creation and configuration phases. Retrieving system information – Configuration Service cmdlets In this recipe, we will use and explain a general-purpose PowerShell cmdlet: the Configuration Service category. This is used to retrieve general configuration parameters, and to obtain information about the implementation of the XenDesktop Configuration Service. Getting ready No preliminary tasks are required. You have already installed the Citrix XenDesktop PowerShell SDK during the installation of the Desktop Controller role machine(s). To be able to run a PowerShell script (.ps1 format), you have to enable the script execution from the PowerShell prompt in the following way, using its application: Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force How to do it… In this section, we will explain and execute the commands associated with the XenDesktop System and Services configuration area: Connect to one of the Desktop Broker servers, by using a remote Desktop connection, for instance. Right-click on the PowerShell icon installed on the Windows taskbar and select the Run as Administrator option. Load the Citrix PowerShell modules by typing the following command and then press the Enter key: Asnp Citrix* As an alternative to the Asnp command, you can use the Add-PSSnapin command. Retrieve the active and configured Desktop Controller features by running the following command: Get-ConfigEnabledFeature To retrieve the current status of the Config Service, run the following command. The output result will be OK in the absence of configuration issues: Get-ConfigServiceStatus To get the connection string used by the Configuration Service and to connect to the XenDesktop database, run the following command: Get-ConfigDBConnection Starting from the previously received output, it's possible to configure the connection string to let the Configuration Service use the system DB. For this command, you have to specify the Server, Initial Catalog, and Integrated Security parameters: Set-ConfigDBConnection –DBConnection"Server=<ServernameInstanceName>; Initial Catalog=<DatabaseName>; Integrated Security=<True | False>" Starting from an existing Citrix database, you can generate a SQL procedure file to use as a backup to recreate the database. Run the following command to complete this task, specifying the DatabaseName and ServiceGroupName parameters: Get-ConfigDBSchema -DatabaseName<DatabaseName> -ServiceGroupName<ServiceGroupName>> Path:FileName.sql You need to configure a destination database with the same name as that of the source DB, otherwise the script will fail! To retrieve information about the active Configuration Service objects (Instance, Service, and Service Group), run the following three commands respectively: Get-ConfigRegisteredServiceInstance Get-ConfigService Get-ConfigServiceGroup To test a set of operations to check the status of the Configuration Service, run the following script: #------------ Script - Configuration Service #------------ Define Variables $Server_Conn="SqlDatabaseServer.xdseven.localCITRIX,1434" $Catalog_Conn="CitrixXD7-Site-First" #------------ write-Host"XenDesktop - Configuration Service CmdLets" #---------- Clear the existing Configuration Service DB connection $Clear = Set-ConfigDBConnection -DBConnection $null Write-Host "Clearing any previous DB connection - Status: " $Clear #---------- Set the Configuration Service DB connection string $New_Conn = Set-ConfigDBConnection -DBConnection"Server=$Server_Conn; Initial Catalog=$Catalog_Conn; Integrated Security=$true" Write-Host "Configuring the DB string connection - Status: " $New_Conn $Configured_String = Get-configDBConnection Write-Host "The new configured DB connection string is: " $Configured_String You have to save this script with the .ps1 extension, in order to invoke it with PowerShell. Be sure to change the specific parameters related to your infrastructure, in order to be able to run the script. This is shown in the following screenshot: How it works... The Configuration Service cmdlets of XenDesktop PowerShell permit the managing of the Configuration Service and its related information: the Metadata for the entire XenDesktop infrastructure, the Service instances registered within the VDI architecture, and the collections of these services, called Service Groups. This set of commands offers the ability to retrieve and check the DB connection string to contact the configured XenDesktop SQL Server database. These operations are permitted by the Get-ConfigDBConnection command (to retrieve the current configuration) and the Set-ConfigDBConnection command (to configure the DB connection string); both the commands use the DB Server Name with the Instance name, DB name, and Integrated Security as information fields. In the attached script, we have regenerated a database connection string. To be sure to be able to recreate it, first of all we have cleared any existing connection, setting it to null (verify the command associated with the $Clear variable), then we have defined the $New_Conn variable, using the Set-ConfigDBConnection command; all the parameters are defined at the top of the script, in the form of variables. Use the Write-Host command to echo results on the standard output. There's more... In some cases, you may need to retrieve the state of the registered services, in order to verify their availability. You can use the Test-ConfigServiceInstanceAvailability cmdlet, retrieving whether the service is responding or not and its response time. Run the following example to test the use of this command: Get-ConfigRegisteredServiceInstance | Test-ConfigServiceInstanceAvailability | more Use the –ForceWaitForOneOfEachType parameter to stop the check for a service category, when one of its services responds. Administering hosts and machines – Host and MachineCreation cmdlets In this recipe, we will describe how to create the connection between the Hypervisor and the XenDesktop servers, and the way to generate machines to assign to the end users, all by using Citrix PowerShell. Getting ready No preliminary tasks are required. You have already installed the Citrix XenDesktop PowerShell SDK during the installation of the Desktop Controller role machine(s). To be sure to be able to run a PowerShell script (the.ps1 format), you have to enable the script execution from the PowerShell prompt in this way: Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force How to do it… In this section, we will discuss the PowerShell commands used to connect XenDesktop with the supported hypervisors plus the creation of the machines from the command line: Connect to one of the Desktop Broker servers. Click on the PowerShell icon installed on the Windows taskbar. Load the Citrix PowerShell modules by typing the following command, and then press the Enter key: Asnp Citrix* To list the available Hypervisor types, execute this task: Get-HypHypervisorPlugin –AdminAddress<BrokerAddress> To list the configured properties for the XenDesktop root-level location (XDHyp:), execute the following command: Get-ChildItemXDHyp:HostingUnits Please refer to the PSPath, Storage, and PersonalvDiskStorage output fields to retrieve information on the storage configuration. Execute the following cmdlet to add a storage resource to the XenDesktop Controller host: Add-HypHostingUnitStorage –LiteralPath<HostPathLocation> -StoragePath<StoragePath> -StorageType<OSStorage|PersonalvDiskStorage> - AdminAddress<BrokerAddress> To generate a snapshot for an existing VM, perform the following task: New-HypVMSnapshot –LiteralPath<HostPathLocation> -SnapshotDescription<Description> Use the Get-HypVMMacAddress -LiteralPath<HostPathLocation> command to list the MAC address of specified desktop VMs. To provision machine instances starting from the Desktop base image template, run the following command: New-ProvScheme –ProvisioningSchemeName<SchemeName> -HostingUnitName<HypervisorServer> -IdentityPoolName<PoolName> -MasterImageVM<BaseImageTemplatePath> -VMMemoryMB<MemoryAssigned> -VMCpuCount<NumberofCPU> To specify the creation of instances with the Personal vDisk technology, use the following option: -UsePersonalVDiskStorage. After the creation process, retrieve the provisioning scheme information by running the following command: Get-ProvScheme –ProvisioningSchemeName<SchemeName> To modify the resources assigned to desktop instances in a provisioning scheme, use the Set-ProvScheme cmdlet. The permitted parameters are –ProvisioningSchemeName, -VMCpuCount, and –VMMemoryMB. To update the desktop instances to the latest version of the Desktop base image template, run the following cmdlet: Publish-ProvMasterVmImage –ProvisioningSchemeName<SchemeName> -MasterImageVM<BaseImageTemplatePath> If you do not want to maintain the pre-update instance version to use as a restore checkpoint, use the –DoNotStoreOldImage option. To create machine instances, based on the previously configured provisioning scheme for an MCS architecture, run this command: New-ProvVM –ProvisioningSchemeName<SchemeName> -ADAccountName"DomainMachineAccount" Use the -FastBuild option to make the machine creation process faster. On the other hand, you cannot start up the machines until the process has been completed. Retrieve the configured desktop instances by using the next cmdlet: Get-ProvVM –ProvisioningSchemeName<SchemeName> -VMName<MachineName> To remove an existing virtual desktop, use the following command: Remove-ProvVM –ProvisioningSchemeName<SchemeName> -VMName<MachineName> -AdminAddress<BrokerAddress> The next script will combine the use of part of the commands listed in this recipe: #------------ Script - Hosting + MCS #----------------------------------- #------------ Define Variables $LitPath = "XDHyp:HostingUnitsVMware01" $StorPath = "XDHyp:HostingUnitsVMware01datastore1.storage" $Controller_Address="192.168.110.30" $HostUnitName = "Vmware01" $IDPool = $(Get-AcctIdentityPool -IdentityPoolName VDI-DESKTOP) $BaseVMPath = "XDHyp:HostingUnitsVMware01VMXD7-W8MCS-01.vm" #------------ Creating a storage location Add-HypHostingUnitStorage –LiteralPath $LitPath -StoragePath $StorPath -StorageTypeOSStorage -AdminAddress $Controller_Address #---------- Creating a Provisioning Scheme New-ProvScheme –ProvisioningSchemeName Deploy_01 -HostingUnitName $HostUnitName -IdentityPoolName $IDPool.IdentityPoolName -MasterImageVM $BaseVMPathT0-Post.snapshot -VMMemoryMB 4096 -VMCpuCount 2 -CleanOnBoot #---------- List the VM configured on the Hypervisor Host dir $LitPath*.vm exit How it works... The Host and MachineCreation cmdlet groups manage the interfacing with the Hypervisor hosts, in terms of machines and storage resources. This allows you to create the desktop instances to assign to the end user, starting from an existing and mapped Desktop virtual machine. The Get-HypHypervisorPlugin command retrieves and lists the available hypervisors to use to deploy virtual desktops and to configure the storage types. You need to configure an operating system storage area or a Personal vDisk storage zone. The way to map an existing storage location from the Hypervisor to the XenDesktop controller is by running the Add-HypHostingUnitStorage cmdlet. In this case you have to specify the destination path on which the storage object will be created (LiteralPath), the source storage path on the Hypervisor machine(s) (StoragePath), and the StorageType previously discussed. The storage types are in the form of XDHyp:HostingUnits<UnitName>. To list all the configured storage objects, execute the following command: dirXDHyp:HostingUnits<UnitName> *.storage After configuring the storage area, we have discussed the Machine Creation Service (MCS) architecture. In this cmdlets collection, we have the availability of commands to generate VM snapshots from which we can deploy desktop instances (New-HypVMSnapshot), and specify a name and a description for the generated disk snapshot. Starting from the available disk image, the New-ProvScheme command permits you to create a resource provisioning scheme, on which to specify the desktop base image, and the resources to assign to the desktop instances (in terms of CPU and RAM -VMCpuCount and –VMMemoryMB), and if generating these virtual desktops in a non-persistent mode (-CleanOnBoot option), with or without the use of the Personal vDisk technology (-UsePersonalVDiskStorage). It's possible to update the deployed instances to the latest base image update through the use of the Publish-ProvMasterVmImage command. In the generated script, we have located all the main storage locations (the LitPath and StorPath variables) useful to realize a provisioning scheme, then we have implemented a provisioning procedure for a desktop based on an existing base image snapshot, with two vCPUs and 4GB of RAM for the delivered instances, which will be cleaned every time they stop and start (by using the -CleanOnBoot option). You can navigate the local and remote storage paths configured with the XenDesktop Broker machine; to list an object category (such as VM or Snapshot) you can execute this command: dirXDHyp:HostingUnits<UnitName>*.<category> There's more... The discussed cmdlets also offer you the technique to preserve a virtual desktop from an accidental deletion or unauthorized use. With the Machine Creation cmdlets group, you have the ability to use a particular command, which allows you to lock critical desktops: Lock-ProvVM. This cmdlet requires as parameters the name of the scheme to which they refer (-ProvisioningSchemeName) and the ID of the virtual desktop to lock (-VMID). You can retrieve the Virtual Machine ID by running the Get-ProvVM command discussed previously. To revert the machine lock, and free the desktop instance from accidental deletion or improper use, you have to execute the Unlock-ProvVM cmdlet, using the same parameter showed for the lock procedure. Managing additional components – StoreFrontÔ admin and logging cmdlets In this recipe, we will use and explain how to manage and configure the StoreFront component, by using the available Citrix PowerShell cmdlets. Moreover, we will explain how to manage and check the configurations for the system logging activities. Getting ready No preliminary tasks are required. You have already installed the Citrix XenDesktop PowerShell SDK during the installation of the Desktop Controller role machine(s). To be able to run a PowerShell script (in the.ps1 format), you have to enable the script execution from the PowerShell prompt in this way: Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force How to do it… In this section, we will explain and execute the commands associated with the Citrix Storefront system: Connect to one of the Desktop Broker servers. Click on the PowerShell icon installed on the Windows taskbar. Load the Citrix PowerShell modules by typing the following command, and then press the Enter key: Asnp Citrix* To execute a command, you have to press the Enter button after completing the right command syntax. Retrieve the currently existing StoreFront service instances, by running the following command: Get-SfService To limit the number of rows as output result, you can add the –MaxRecordCount<value> parameter. To list the detailed information about the StoreFront service(s) currently configured, execute the following command: Get-SfServiceInstance –AdminAddress<ControllerAddress> The status of the currently active StoreFront instances can be retrieved by using the Get-SfServiceStatus command. The OK output will confirm the correct service execution. To list the task history associated with the configured StoreFront instances, you have to run the following command: Get-SfTask You can filter the desired information for the ID of the researched task (-taskid) and sort the results by the use of the –sortby parameter. To retrieve the installed database schema versions, you can execute the following command: Get-SfInstalledDBVersion By applying the –Upgrade and –Downgrade filters, you will receive respectively the schemas for which the database version can be updated or reverted to a previous compatible one. To modify the StoreFront configurations to register its state on a different database, you can use the following command: Set-SfDBConnection-DBConnection<DBConnectionString> -AdminAddress<ControllerAddress> Be careful when you specify the database connection string; if not specified, the existing database connections and configurations will be cleared! To check that the database connection has been correctly configured, the following command is available: Test-SfDBConnection-DBConnection<DBConnectionString>-AdminAddress<ControllerAddress> The second discussed cmdlets allows the logging group to retrieve information about the current status of the logging service and run the following command: Get-LogServiceStatus To verify the language used and whether the logging service has been enabled, run the following command: Get-LogSite The available configurable locales are en, ja, zh-CN, de, es, and fr. The available states are Enabled, Disabled, NotSupported, and Mandatory. The NotSupported state will show you an incorrect configuration for the listed parameters. To retrieve detailed information about the running logging service, you have to use the following command: Get-LogService As discussed earlier for the StoreFront commands, you can filter the output by applying the –MaxRecordCount<value> parameter. In order to get all the operations logged within a specified time range, run the following command; this will return the global operations count: Get-LogSummary –StartDateRange<StartDate>-EndDateRange<EndDate> The date format must be the following: AAAA-MM-GGHH:MM:SS. To list the collected operations per day in the specified time period, run the previous command in the following way: Get-LogSummary –StartDateRange<StartDate> -EndDateRange<EndDate>-intervalSeconds 86400 The value 86400 is the number of seconds that are present in a day. To retrieve the connection string information about the database on which logging data is stored, execute the following command: Get-LogDataStore To retrieve detailed information about the high level operations performed on the XenDesktop infrastructure, you have to run the following command: Get-LogHighLevelOperation –Text <TextincludedintheOperation> -StartTime<FormattedDateandTime> -EndTime<FormattedDateandTime>-IsSuccessful<true | false>-User <DomainUserName>-OperationType<AdminActivity | ConfigurationChange> The indicated filters are not mandatory. If you do not apply any filters, all the logged operations will be returned. This could be a very long output. The same information can be retrieved for the low level system operations in the following way: Get-LogLowLevelOperation-StartTime<FormattedDateandTime> -EndTime<FormattedDateandTime> -IsSuccessful<true | false>-User <DomainUserName> -OperationType<AdminActivity | ConfigurationChange> In the How it works section we will explain the difference between the high and low level operations. To log when a high level operation starts and stops respectively, use the following two commands: Start-LogHighLevelOperation –Text <OperationDescriptionText>- Source <OperationSource> -StartTime<FormattedDateandTime> -OperationType<AdminActivity | ConfigurationChange> Stop-LogHighLevelOperation –HighLevelOperationId<OperationID> -IsSuccessful<true | false> The Stop-LogHighLevelOperation must be related to an existing start high level operation, because they are related tasks. How it works... Here, we have discussed two new PowerShell command collections for the XenDesktop 7 versions: the cmdlet related to the StoreFront platform; and the activities Logging set of commands. The first collection is quite limited in terms of operations, despite the other discussed cmdlets. In fact, the only actions permitted with the StoreFront PowerShell set of commands are retrieving configurations and settings about the configured stores and the linked database. More activities can be performed regarding the modification of existing StoreFront clusters, by using the Get-SfCluster, Add-SfServerToCluster, New-SfCluster, and Set-SfCluster set of operations. More interesting is the PowerShell Logging collection. In this case, you can retrieve all the system-logged data, putting it into two principal categories: High-level operations: These tasks group all the system configuration changes that are executed by using the Desktop Studio, the Desktop Director, or Citrix PowerShell. Low-level operations: This category is related to all the system configuration changes that are executed by a service and not by using the system software's consoles. With the low level operations command, you can filter for a specific high level operation to which the low level refers, by specifying the -HighLevelOperationId parameter. This cmdlet category also gives you the ability to track the start and stop of a high level operation, by the use of Start-LogHighLevelOperation and Stop-LogHighLevelOperation. In this second case, you have to specify the previously started high level operation. There's more... In case of too much information in the log store, you have the ability to clear all of it. To refresh all the log entries, we use the following command: Remove-LogOperation -UserName<DBAdministrativeCredentials> -Password <DBUserPassword>-StartDateRange <StartDate> -EndDateRange <EndDate> The not encrypted –Password parameter can be substituted by –SecurePassword, the password indicated in secure string form. The credentials must be database administrative credentials, with deleting permissions on the destination database. This is a not reversible operation, so ensure that you want to delete the logs in the specified time range, or verify that you have some form of data backup. Resources for Article: Further resources on this subject: Working with Virtual Machines [article] Virtualization [article] Upgrading VMware Virtual Infrastructure Setups [article]
Read more
  • 0
  • 0
  • 20202

article-image-decoding-complex-code-with-chatgpt
Dan MacLean
14 Nov 2023
7 min read
Save for later

Decoding Complex Code with ChatGPT

Dan MacLean
14 Nov 2023
7 min read
Dive deeper into the world of AI innovation and stay ahead of the AI curve! Subscribe to our AI_Distilled newsletter for the latest insights. Don't miss out – sign up today!This article is an excerpt from the book, R Bioinformatics Cookbook - Second Edition, by Dan MacLean. Discover over 80 recipes for modeling and handling real-life biological data using modern libraries from the R ecosystem.IntroductionHey there, fellow code explorers! Ever found yourself staring at a chunk of complex R code, feeling lost in its jumble of symbols and functions? Well, fear not! This article dives into the exciting realm of decoding such cryptic code with the help of an unexpected ally: ChatGPT. Join me on this journey as we uncover the secrets behind intricate code snippets, aiming to demystify and explain them in plain, everyday language.Interpreting complicated code with ChatGPT assistanceChatGPT can be used to interpret R code by leveraging its natural language processing capabilities and its understanding of the R programming language. It’s important to note that while ChatGPT can assist in interpreting and explaining R code, it’s still just an AI language model and may not have knowledge of the most recent updates or specific domain-specific intricacies. Double-checking information with official documentation and consulting experienced developers or experts is always necessary. It is massively useful for simplifying and getting a good first-level understanding in most cases.In this recipe, we’ll look at how to take an arbitrary bit of complex code and explain it in plain English.Getting readyWe need some code to interpret – we’ll use a function from the besthr Github-hosted package at https://github.com/TeamMacLean/besthr/blob/master/R/functions.R and an account with ChatGPT from https://chat.openai.com/.How to do it…Here’s how to work in a ChatGPT session and get it to explain some code to you:1. Paste the following code into the ChatGPT prompt box:explain this R code to me: #' Convert named columns to factors #' #' \code{factorise_cols} returns a copy of the passed data frame in which all #' explicitly named columns are converted to factors with \ code{as.factor} #' All columns with only NA values are ommitted. #' #' @param df data frame #' @param col_list vector of quoted column names #' @keywords internal #' factorise_cols <- function(df, col_list ){ not_all_na <- function(x) any(!is.na(x)) for ( i in col_list){ df <- df %>% dplyr::mutate(!!(i) := as.factor(!!(i)) ) } df %>% dplyr::select_if(not_all_na) }2. Read the response:This R code defines a function called `factorise_cols` that converts explicitly named columns of a data frame into factors using the `as.factor` function. The function takes two parameters: 1. `df`: This is the data frame that contains the columns to be converted. 2. `col_list`: This is a vector of quoted column names. It specifies which columns in the data frame should be converted to factors. The function uses a helper function called `not_all_na` to filter out columns that have only `NA` values. This helper function checks if there are any non-NA values in a column using the `any` and `is.na` functions. Inside the main function, a loop iterates over each column name in the `col_list`. For each column, the function uses the `mutate` function from the `dplyr` package to create a new column in the data frame (`df`) with the same name, but with the values converted to factors using the `as.factor` function. The `!!(i)` syntax is used to unquote the column name. After the loop, the function uses the `select_if` function from `dplyr` to select only the columns that pass the `not_all_na` condition, effectively removing columns that contain only `NA` values. The resulting data frame is then returned as the output of the function. Overall, this function allows you to convert specific columns of a data frame into factors while excluding columns that contain only `NA` values. 3. Ask for a clarification on a certain point:You said 'The !!(i) syntax is used to unquote the column name.' Can you go into more depth?4. Read the response:Certainly! In the given code, the `!!` (bang-bang) syntax is used for unquoting the column name within the context of the `mutate` function. Let's break it down step by step: 1. In the loop, the variable `i` represents each element (column name) of the `col_list` vector. 2. Within the `mutate` function, the column name `i` needs to be unquoted and evaluated as an expression. This is necessary because `mutate` expects column names to be provided as expressions rather than strings. 3. The `!!` (bang-bang) operator is used to perform this unquoting. It is part of the tidy evaluation framework provided by the `dplyr` package. 4. By wrapping `i` with `!!`, it instructs `mutate` to evaluate `i` as an expression, resulting in an unquoted column name. Interpreting complicated code with ChatGPT assistance To illustrate with an example, suppose `col_list` contains the column names `col1` and `col2`. During the loop iteration, when `i` is `"col1"`, the line `!!(i)` would be evaluated as `!!("col1")`, which unquotes the string to yield `col1`. Similarly, when `i` is `"col2"`, `!!(i)` would be evaluated as `!!("col2")`, resulting in `col2`. This unquoting operation is necessary in this context because the `mutate` function expects column names to be provided as unevaluated expressions so that it can perform the necessary data manipulation operations. The `!!` operator allows us to achieve this unquoting within the `mutate` function call.Note that the response from ChatGPT will be somewhat different depending on context and you may not get exactly what was generated here. That is the nature of the thing, but you should get something with the same meaning.How it works…The code in this example comes from a package called besthr that creates estimation statistic plots for visual scoring data. The tool must take in arbitrary column names and work within the tidyverse, so it uses some advanced coding techniques to allow that to happen. We first go and get the codefrom the repository on GitHub (https://github.com/TeamMacLean/besthr/blob/ master/R/functions.R) and paste that into ChatGPT’s prompt box asking it for an explanation.In step 2, we can see the explanation provided (note that the one you get if you try may be different as the model is not guaranteed to reproduce its predictions). The detail is largely correct; certainly, it is sufficient to give us a clear idea of what the code attempts to do and how it does it.Some parts of the explanation aren’t clear, so in step 3, we ask for clarification of a tricky bit, again by typing into the prompt box. And in step 4, we see a more in-depth description of that part.In this way, we can get a clear and readable, plain English description of the job done by a particular piece of code very quickly.There’s more…Other sites can do this, such as Google’s Bard. ChatGPT Plus – a subscription service –also has special plug-ins that help make working with code much easier..ConclusionWho knew cracking code could be this fun and straightforward? With ChatGPT as our trusty sidekick, we've peeked behind the curtains of intricate R code, unraveling its mysteries piece by piece. Remember, while this AI wizardry is fantastic, a mix of human expertise and official documentation remains your ultimate guide through the coding labyrinth. So, armed with newfound knowledge and a reliable AI companion, let's keep exploring, learning, and demystifying the captivating world of programming together!Author BioProfessor Dan MacLean has a Ph.D. in molecular biology from the University of Cambridge and gained postdoctoral experience in genomics and bioinformatics at Stanford University in California. Dan is now Head of Bioinformatics at the world leading Sainsbury Laboratory in Norwich, UK where he works on bioinformatics, genomics, and machine learning. He teaches undergraduates, post-graduates, and post-doctoral students in data science and computational biology. His research group has developed numerous new methods and software in R, Python, and other languages with over 100,000 downloads combined.
Read more
  • 0
  • 0
  • 20185

article-image-generating-synthetic-data-with-llms
Mostafa Ibrahim
09 Nov 2023
8 min read
Save for later

Generating Synthetic Data with LLMs

Mostafa Ibrahim
09 Nov 2023
8 min read
Dive deeper into the world of AI innovation and stay ahead of the AI curve! Subscribe to our AI_Distilled newsletter for the latest insights. Don't miss out – sign up today!IntroductionIn this article, we will delve into the intricate process of synthetic data generation using LLMs. We will shed light on the concept behind the increasing importance of synthetic data, the prowess of LLMs in generating such data, and practical steps to harness the power of advanced models like OpenAI’s GPT-3.5. Whether you’re a seasoned AI enthusiast or a curious newcomer, embark with us on this enlightening journey into the heart of modern machine learning.What are LLMs?Large Language Models (LLMs) are state-of-the-art machine learning architectures primarily designed for understanding and generating human-like text. These models are trained on vast amounts of data, enabling them to perform a wide range of language tasks, from simple text completion to answering complex questions or even crafting coherent articles. Some examples of LLMs include:1. GPT-3 by OpenAI, with 175 billion parameters and up to 2048 tokens per unit.2. BERT by Google, with 340 million parameters and up to 512 tokens per unit.3. T5 (Text-to-Text Transfer Transformer by Google) with parameters ranging from 60 million to 11 billion depending on the model size. The number of tokens it can process is also influenced by its size and setup.That being said, LLMs, with their cutting-edge capabilities in NLP tasks like question answering and text summarization, are also highly regarded for their efficiency in generating synthetic data.Why Is There A Need for Synthetic Data1) Data ScarcityDo you ever grapple with the challenge of insufficient data to train your model? This dilemma is a daily reality for machine learning experts globally. Given that data gathering and processing are among the most daunting aspects of the entire machine-learning journey, the significance of synthetic data cannot be overstated.2) Data Privacy & SecurityReal-world data often contains sensitive information. For industries like healthcare and finance, there are stringent regulations around data usage. Such data may include customer’s credit cards, buying patterns, and diseases. Synthetic data can be used without compromising privacy since it doesn't contain real individual information.The Process of Generating Data with LLMsThe journey of producing synthetic data using Large Language Models begins with the preparation of seed data or guiding queries. This foundational step is paramount as it sets the trajectory for the type of synthetic data one wishes to produce. Whether it's simulating chatbot conversations or creating fictional product reviews, these initial prompts provide LLMs with the necessary context.Once the stage is set, we delve into the actual data generation phase. LLMs, with their advanced architectures, begin crafting text based on patterns they've learned from vast datasets. This capability enables them to produce information that aligns with the characteristics of real-world data, albeit synthesized.Generating Synthetic Data Using OpenAI’s GPT 3.5Step 1: Importing Neseccasry Librariesimport openaiStep 2: Set up the OpenAI API keyopenai.api_key = "Insert Your OpenAI key here"Step 3: Define our synthetic data generation functiondef generate_reviews(prompt, count=1):    reviews = []    for i in range(count):        review_generated = False        while not review_generated:            try:                # Generate a response using the ChatCompletion method                response = openai.ChatCompletion.create(                    model="gpt-3.5-turbo",                    messages=[                        {"role": "system", "content": "You are a helpful assistant."},                        {"role": "user", "content": prompt}                    ]                )                              review = response.choices[0].message['content'].strip()                word_count = len(review.split())                print("word count:", word_count)                # Check if the word count is within the desired range                if 15 <= word_count <= 70:                    print("counted")                    reviews.append(review)                    review_generated = True            except openai.error.OpenAIError as err:                print(f"Encountered an error: {err}")        # Optional: Add a slight variation to the prompt for next iteration        prompt += " Provide another perspective."    return reviewsStep 4: Testing our functionprompt_text = "Write a 25 word positive review for a wireless earbud highlighting its battery life." num_datapoints = 5 generated_reviews = generate_reviews(prompt_text, num_datapoints)Step 5: Printing generated synthetic datafor idx, review in enumerate(generated_reviews):    print(f"Review {idx + 1}: {review}")Output:Review 1: The battery life on these wireless earbuds is absolutely incredible! I can enjoy hours of uninterrupted music without worrying about recharging. Truly impressive!Review 2: "The battery life of these wireless earbuds is phenomenal! I can enjoy my favorite music for hours without worrying about recharging. Truly impressive!"Review 3: This wireless earbud is a game-changer! With an exceptional battery life that lasts all day, I can enjoy uninterrupted music and calls without any worries. It's a must-have for people on the go. Another perspective: As a fitness enthusiast, the long battery life of this wireless earbud is a true blessing. It allows me to power through my workouts without constantly needing to recharge, keeping me focused and motivated.Review 4: This wireless earbud's exceptional battery life is worth praising! It lasts all day long, keeping you immersed in your favorite tunes. A real game-changer for music enthusiasts.Review 5: The battery life of these wireless earbuds is exceptional, lasting for hours on end, allowing you to enjoy uninterrupted music or calls. They truly exceed expectations!Considerations and PitfallsHowever, the process doesn't conclude here. Generated data may sometimes have inconsistencies or lack the desired quality. Hence, post-processing, which involves refining and filtering the output, becomes essential. Furthermore, ensuring the variability and richness of the synthetic data is paramount, as too much uniformity can lead to overfitting when the data is employed for machine learning purposes. This refinement process should aim to eliminate any redundant or unrepresentative samples that could skew the model's learning process.Moreover, validating the synthetic data ensures that it meets the standards and purposes for which it was intended, ensuring both authenticity and reliability.ConclusionThroughout this article, we've navigated the process of synthetic data generation powered by LLMs. We've explained the underlying reasons for the escalating prominence of synthetic data, showcased the unparalleled proficiency of LLMs in creating such data, and provided actionable guidance to leverage the capabilities of pre-trained LLM models like OpenAI’s GPT-3.5.For all AI enthusiasts, we hope this exploration has deepened your appreciation and understanding of the evolving tapestry of machine learning,  LLMs, and synthetic data. As we stand now, it is clear that both synthetic data and LLMs will be central to many breakthroughs to come.Author BioMostafa Ibrahim is a dedicated software engineer based in London, where he works in the dynamic field of Fintech. His professional journey is driven by a passion for cutting-edge technologies, particularly in the realms of machine learning and bioinformatics. When he's not immersed in coding or data analysis, Mostafa loves to travel.Medium
Read more
  • 0
  • 0
  • 20185

article-image-awk-programming-langauge
Pavan Ramchandani
17 May 2018
9 min read
Save for later

That '70s language: AWK programming

Pavan Ramchandani
17 May 2018
9 min read
AWK is an interpreted programming language designed for text processing and report generation. It is typically used for data manipulation, such as searching for items within data, performing arithmetic operations, and restructuring raw data for generating reports in most Unix-like operating systems. Today, we will explore the AWK philosophy and different types of AWK that exist, starting from its original implementation in 1977 at AT&T's Laboratories, Inc. We will also look at the various implementation areas of AWK in data science today. Using AWK programs, one can handle repetitive text-editing problems with very simple and short programs. It is a pattern-action language; it searches for patterns in a given input and, when a match is found, it performs the corresponding action. The pattern can be made of strings, regular expressions, comparison operations on numbers, fields, variables, and so on. It reads the input files and splits each input line of the file into fields automatically. AWK has most of the well-designed features that every programming language should contain. Its syntax particularly resembles that of the C programming language. It is named after its original three authors: Alfred V. Aho Peter J. Weinberger Brian W. Kernighan AWK is a very powerful, elegant, and simple that every person dealing with text processing should be familiar with. This article is an excerpt from a book written by Shiwang Kalkhanda, titled Learning AWK Programming. This book will introduce you to AWK programming language and get you hands-on working with practical implementation of AWK. Types of AWK The AWK language was originally implemented as an AWK utility on Unix. Today, most Linux distributions provide GNU implementation of AWK (GAWK), and a symlink for AWK is created from the original GAWK binary. The AWK utility can be categorized into the following three types, depending upon the type of interpreter it uses for executing AWK programs: AWK: This is the original AWK interpreter available from AT&T Laboratories. However, it is not used much nowadays and hence it might not be well-maintained. Its limitation is that it splits a line into a maximum 99 fields. It was updated and replaced in the mid-1980s with an enhanced version called New AWK (NAWK). NAWK: This is AT&T's latest development on the AWK interpreter. It is well-maintained by one of the original authors of AWK - Dr. Brian W. Kernighan. GAWK: This is the GNU project's implementation of the AWK programming language. All GNU/Linux distributions are shipped with GAWK by default and hence it is the most popular version of AWK. GAWK interpreter is fully compatible with AWK and NAWK. Beyond these, we also have other, less popular, AWK interpreters and translators, mentioned as follows. These variants are useful in operations when you want to translate your AWK program to C, C++, or Perl: MAWK: Michael Brennan interpreter for AWK. TAWK: Thompson Automation interpreter/compiler/Microsoft Windows DLL for AWK. MKSAWK: Mortice Kern Systems interpreter/compiler/for AWK. AWKCC: An AWK translator to C (might not be well-maintained). AWKC++: Brian Kernighan's AWK translator to C++ (experimental). It can be downloaded from: https://9p.io/cm/cs/who/bwk/awkc++.ps. AWK2C: An AWK translator to C. It uses GNU AWK libraries extensively. A2P: An AWK translator to Perl. It comes with Perl. AWKA: Yet another AWK translator to C (comes with the library), based on MAWK. It can be downloaded from: http://awka.sourceforge.net/download.html. When and where to use AWK AWK is simpler than any other utility for text processing and is available as the default on Unix-like operating systems. However, some people might say Perl is a superior choice for text processing, as AWK is functionally a subset of Perl, but the learning curve for Perl is steeper than that of AWK; AWK is simpler than Perl. AWK programs are smaller and hence quicker to execute. Anybody who knows the Linux command line can start writing AWK programs in no time. Here are a few use cases of AWK: Text processing Producing formatted text reports/labels Performing arithmetic operations on fields of a file Performing string operations on different fields of a file Programs written in AWK are smaller than they would be in other higher-level languages for similar text processing operations. AWK programs are interpreted on a GNU/Linux Terminal and thus avoid the compiling, debugging phase of software development in other languages. Getting started with installation This section describes how to set up the AWK environment on your GNU/Linux system, and we'll also discuss the workflow of AWK. Then, we'll look at different methods for executing AWK programs. Installation on Linux Generally, AWK is installed by default on most GNU/Linux distributions. Using the which command, you can check whether it is installed on your system or not. In case AWK is not installed on your system, you can do so in one of two ways: Using the package manager of the corresponding GNU/Linux system Compiling from the source code Let's take a look at each method in detail in the following sections. Using the package manager Different flavors of GNU/Linux distribution have different package-management utilities. If you are using a Debian-based GNU/Linux distribution, such as Ubuntu, Mint, or Debian, then you can install it using the Advance Package Tool (APT) package manager, as follows: [ shiwang@linux ~ ] $ sudo apt-get update -y [ shiwang@linux ~ ] $ sudo apt-get install gawk -y Similarly, to install AWK on an RPM-based GNU/Linux distribution, such as Fedora, CentOS, or RHEL, you can use the Yellowdog Updator Modified (YUM) package manager, as follows: [ root@linux ~ ] # yum update -y [ root@linux ~ ] # yum install gawk -y For installation of AWK on openSUSE, you can use the zypper (zypper command line) package-management utility, as follows: [ root@linux ~ ] # zypper update -y [ root@linux ~ ] # zypper install gawk -y Once the installation is finished, make sure AWK is accessible through the command line. We can check that using the which command, which will return the absolute path of AWK on our system: [ root@linux ~ ] # which awk /usr/bin/awk You can also use awk --version to find the AWK version on our system: [ root@linux ~ ] # awk --version Compiling from the source code Like every other open source utility, the GNU AWK source code is freely available for download as part of the GNU project. Previously, you saw how to install AWK using the package manager; now, you will see how to install AWK by compiling from its source code on the GNU/Linux distribution. The following steps are applicable to most of the GNU/Linux software for installation: Download the source code from a GNU project ftp site. Here, we will use the wget command line utility to download it, however you are free to choose any other program, such as curl, you feel comfortable with: [ shiwang@linux ~ ] $ wget http://ftp.gnu.org/gnu/gawk/gawk-4.1.3.tar.xz Extract the downloaded source code: [ shiwang@linux ~ ] $ tar xvf gawk-4.1.3.tar.xz Change your working directory and execute the configure file to configure the GAWK as per the working environment of your system: [ shiwang@linux ~ ] $ cd gawk-4.1.3 && ./configure Once the configure command completes its execution successfully, it will generate the make file. Now, compile the source code by executing the make command: [ shiwang@linux ~ ] $ make Type make install to install the programs and any data files and documentation. When installing into a prefix owned by root, it is recommended that the package be configured and built as a regular user, and only the make install phase is executed with root privileges: [ shiwang@linux ~ ] $ sudo make install Upon successful execution of these five steps, you have compiled and installed AWK on your GNU/Linux distribution. You can verify this by executing the which awk command in the Terminal or awk --version: [ root@linux ~ ] # which awk /usr/bin/awk Now you have a working AWK/GAWK installation and we are ready to begin AWK programming, but before that, our next section describes the workflow of the AWK interpreter. If you are running on macOS X, AWK, and not GAWK, would be installed as a default on it. For GAWK installation on macOS X, please refer to MacPorts for GAWK. Workflow of AWK Having a basic knowledge of the AWK interpreter workflow will help you to better understand AWK and will result in more efficient AWK program development. Hence, before getting your hands dirty with AWK programming, you need to understand its internals. The AWK workflow can be summarized as shown in the following figure: Let's take a look at each operation: READ OPERATION: AWK reads a line from the input stream (file, pipe, or stdin) and stores it in memory. It works on text input, which can be a file, the standard input stream, or from a pipe, which it further splits into records and fields: Records: An AWK record is a single, continuous data input that AWK works on. Records are bounded by a record separator, whose value is stored in the RS variable. The default value of RS is set to a newline character. So, the lines of input are considered records for the AWK interpreter. Records are read continuously until the end of the input is reached. Figure 1.2  shows how input data is broken into records and then goes further into how it is split into fields: Fields: Each record can further be broken down into individual chunks called fields. Like records, fields are bounded. The default field separator is any amount of whitespace, including tab and space characters. So by default, lines of input are further broken down into individual words separated by whitespace. You can refer to the fields of a record by a field number, beginning with 1. The last field in each record can be accessed by its number or with the NF special variable, which contains the number of fields in the current record, as shown in Figure 1.3: EXECUTE OPERATION: All AWK commands are applied sequentially on the input (records and fields). By default, AWK executes commands on each record/line. This behavior of AWK can be restricted by the use of patterns. REPEAT OPERATION: The process of read and execute is repeated until the end of the file is reached. The following flowchart depicts the workflow:   We introduced you to the AWK programming language and got ourselves a quick primer to get started with application development. If you found this post is useful, do check out the book Learning AWK Programming to learn more about the intricacies of AWK programming language for text processing. The oldest programming languages in use today What is the difference between functional and object oriented programming? Systems programming with Go in UNIX and Linux  
Read more
  • 0
  • 0
  • 20184
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 ₹800/month. Cancel anytime
article-image-migrating-wordpress-blog-middleman-and-deploying-amazon-s3
Mike Ball
07 Nov 2014
11 min read
Save for later

Migrating a WordPress Blog to Middleman and Deploying to Amazon S3

Mike Ball
07 Nov 2014
11 min read
Part 1: Getting up and running with Middleman Many of today’s most prominent web frameworks, such as Ruby on Rails, Django, Wordpress, Drupal, Express, and Spring MVC, rely on a server-side language to process HTTP requests, query data at runtime, and serve back dynamically constructed HTML. These platforms are great, yet developers of dynamic web applications often face complex performance challenges under heavy user traffic, independent of the underlying technology. High traffic, and frequent requests, may exploit processing-intensive code or network latency, in effect yielding a poor user experience or production outage. Static site generators such as Middleman, Jeckyll, and Wintersmith offer developers an elegant, highly scalable alternative to complex, dynamic web applications. Such tools perform dynamic processing and HTML construction during build time rather than runtime. These tools produce a directory of static HTML, CSS, and JavaScript files that can be deployed directly to a web server such as Nginx or Apache. This architecture reduces complexity and encourages a sensible separation of concerns; if necessary, user-specific customization can be handled via client-side interaction with third-party satellite services. In this three part series, we'll walk-through how to get started in developing a Middleman site, some basics of Middleman blogging, how to migrate content from an existing WordPress blog, and how to deploy a Middleman blog to production. We will also learn how to create automated tests, continuous integration, and automated deployments. In this part, we’ll cover the following: Creating a basic Middleman project Middleman configuration basics A quick overview of the Middleman template system Creating a basic Middleman blog Why should you use middleman? Middleman is a mature, full-featured static site generator. It supports a strong templating system, numerous Ruby-based HTML templating tools such as ERb and HAML, as well as a Sprockets-based asset pipeline used to manage CSS, JavaScript, and third-party client-side code. Middleman also integrates well with CoffeeScript, SASS, and Compass. Environment For this tutorial, I’m using an RVM-installed Ruby 2.1.2. I’m on Mac OSX 10.9.4. Installing middleman Install middleman via bundler: $ gem install middleman Create a basic middleman project called middleman-demo: $ middleman init middleman-demo This results in a middleman-demo directory with the following layout: ├── Gemfile ├── Gemfile.lock ├── config.rb └── source    ├── images    │   ├── background.png    │   └── middleman.png    ├── index.html.erb    ├── javascripts    │   └── all.js    ├── layouts    │   └── layout.erb    └── stylesheets        ├── all.css        └── normalize.css[SB4]  There are 5 directories and 10 files. A quick tour Here are a few notes on the middleman-demo layout: The Ruby Gemfile  cites Ruby gem dependencies; Gemfile.lock cites the full dependency chain, including  middleman-demo’s dependencies’ dependencies The config.rb  houses middleman-demo’s configuration The source directory houses middleman-demo ’s source code–the templates, style sheets, images, JavaScript, and other source files required by the  middleman-demo [SB7] site While a Middleman production build is simply a directory of static HTML, CSS, JavaScript, and image files, Middleman sites can be run via a simple web server in development. Run the middleman-demo development server: $ middleman Now, the middleman-demo site can be viewed in your web browser at  http://localhost:4567. Set up live-reloading Middleman comes with the middleman-livereload gem. The gem detects source code changes and automatically reloads the Middleman app. Activate middleman-livereload  by uncommenting the following code in config.rb: # Reload the browser automatically whenever files change configure :development do activate :livereload end Restart the middleman server to allow the configuration change to take effect. Now, middleman-demo should automatically reload on change to config.rb and your web browser should automatically refresh when you edit the source/* code. Customize the site’s appearance Middleman offers a mature HTML templating system. The source/layouts directory contains layouts, the common HTML surrounding individual pages and shared across your site. middleman-demo uses ERb as its template language, though Middleman supports other options such as HAML and Slim. Also note that Middleman supports the ability embed metadata within templates via frontmatter. Frontmatter allows page-specific variables to be embedded via YAML or JSON. These variables are available in a current_page.data namespace. For example, source/index.html.erb contains the following frontmatter specifying a title; it’s available to ERb templates as current_page.data.title: --- title: Welcome to Middleman --- Currently, middleman-demo is a default Middleman installation. Let’s customize things a bit. First, remove all the contents of source/stylesheets/all.css  to remove the default Middleman styles. Next, edit source/index.html.erb to be the following: --- title: Welcome to Middleman Demo --- <h1>Middleman Demo</h1> When viewing middleman-demo at http://localhost:4567, you’ll now see a largely unstyled HTML document with a single Middleman Demo heading. Install the middleman-blog plugin The middleman-blog plugin offers blog functionality to middleman applications. We’ll use middleman-blog in middleman-demo. Add the middleman-blog version 3.5.3 gem dependency to middleman-demo by adding the following to the Gemfile: gem "middleman-blog", "3.5.3 Re-install the middleman-demo gem dependencies, which now include middleman-blog: $ bundle install Activate middleman-blog and specify a URL pattern at which to serve blog posts by adding the following to config.rb: activate :blog do |blog| blog.prefix = "blog" blog.permalink = "{year}/{month}/{day}/{title}.html" end Write a quick blog post Now that all has been configured, let’s write a quick blog post to confirm that middleman-blog works. First, create a directory to house the blog posts: $ mkdir source/blog The source/blog directory will house markdown files containing blog post content and any necessary metadata. These markdown files highlight a key feature of middleman: rather than query a relational database within which content is stored, a middleman application typically reads data from flat files, simple text files–usually markdown–stored within the site’s source code repository. Create a markdown file for middleman-demo ’s first post: $ touch source/blog/2014-08-20-new-blog.markdown Next, add the required frontmatter and content to source/blog/2014-08-20-new-blog.markdown: --- title: New Blog date: 2014/08/20 tags: middleman, blog --- Hello world from Middleman! Features Rich templating system Built-in helpers Easy configuration Asset pipeline Lots more  Note that the content is authored in markdown, a plain text syntax, which is evaluated by Middleman as HTML. You can also embed HTML directly in the markdown post files. GitHub’s documentation provides a good overview of markdown. Next, add the following ERb template code to source/index.html.erb [SB37] to display a list of blog posts on middleman-demo ’s home page: <ul> <% blog.articles.each do |article| %> <li> <%= link_to article.title, article.path %> </li> <% end %> </ul> Now, when running middleman-demo and visiting http://localhost:4567, a link to the new blog post is listed on middleman-demo ’s home page. Clicking the link renders the permalink for the New Blog blog post at blog/2014-08-20/new-blog.html, as is specified in the blog configuration in config.rb. A few notes on the template code Note the use of a link_to method. This is a built-in middleman template helper. Middleman provides template helpers to simplify many common template tasks, such as rendering an anchor tag. In this case, we pass the link_to method two arguments, the intended anchor tag text and the intended href value. In turn, link_to generates the necessary HTML. Also note the use of a blog variable, within which an article’s method houses an array of all blog posts. Where did this come from?  middleman-demo is an instance of  Middleman::Application;  a blog  method on this instance. To explore other Middleman::Application methods, open middleman-demo via the built-in Middleman console by entering the following in your terminal: $ middleman console To view all the methods on the blog, including the aforementioned articles method, enter the following within the console: 2.1.2 :001 > blog.methods To view all the additional methods, beyond the blog, available to the Middleman::Application instance, enter the following within the console: 2.1.2 :001 > self.methods More can be read about all these methods on Middleman::Application’s rdoc.info class documentation. Cleaner URLs Note that the current new blog URL ends in .html. Let’s customize middleman-demo to omit .html from URLs. Add the following config.rb: activate :directory_indexes Now, rather than generating files such as /blog/2014-08-20/new-blog.html,  middleman-demo generates files such as /blog/2014-08-20/new-blog/index.html, thus enabling the page to be served by most web servers at a /blog/2014-08-20/new-blog/ path. Adjusting the templates Let’s adjust our the middleman-demo ERb templates a bit. First, note that <h1>Middleman Demo</h1> only displays on the home page; let’s make it render on all of the site’s pages. Move <h1>Middleman Demo</h1> from  source/index.html.erb  to source/layouts/layout.erb. Put it just inside the <body> tag: <body class="<%= page_classes %>"> <h1>Middleman Demo</h1> <%= yield %> </body> Next, let’s create a custom blog post template. Create the template file: $ touch source/layout/post.erb Add the following to extend the site-wide functionality of source/layouts/layout.erb to  source/layouts/post.erb: <% wrap_layout :layout do %> <h2><%= current_article.title %></h2> <p>Posted <%= current_article.date.strftime('%B %e, %Y') %></p> <%= yield %> <ul> <% current_article.tags.each do |tag| %> <li><a href="/blog/tags/<%= tag %>/"><%= tag %></a></li> <% end %> </ul> <% end %> Note the use of the wrap_layout  ERb helper.  The wrap_layout ERb helper takes two arguments. The first is the name of the layout to wrap, in this case :layout. The second argument is a Ruby block; the contents of the block are evaluated within the <%= yield %> call of source/layouts/layout.erb. Next, instruct  middleman-demo  to use  source/layouts/post.erb  in serving blog posts by adding the necessary configuration to  config.rb : page "blog/*", :layout => :post Now, when restarting the Middleman server and visiting  http://localhost:4567/blog/2014/08/20/new-blog/,  middleman-demo renders a more comprehensive blog template that includes the post’s title, date published, and tags. Let’s add a simple template to render a tags page that lists relevant tagged content. First, create the template: $ touch source/tag.html.erb And add the necessary ERb to list the relevant posts assigned a given tag: <h2>Posts tagged <%= tagname %></h2> <ul> <% page_articles.each do |post| %> <li> <a href="<%= post.url %>"><%= post.title %></a> </li> <% end %> </ul> Specify the blog’s tag template by editing the blog configuration in config.rb: activate :blog do |blog| blog.prefix = 'blog' blog.permalink = "{year}/{month}/{day}/{title}.html" # tag template: blog.tag_template = "tag.html" end Edit config.rb to configure middleman-demo’s tag template to use source/layout.erb rather than source/post.erb: page "blog/tags/*", :layout => :layout Now, when visiting http://localhost:4567/2014/08/20/new-blog/, you should see a linked list of New Blog’s tags. Clicking a tag should correctly render the tags page. Part 1 recap Thus far, middleman-demo serves as a basic Middleman-based blog example. It demonstrates Middleman templating, how to set up the middleman-blog  plugin, and how to make author markdown-based blog posts in Middleman. In part 2, we’ll cover migrating content from an existing Wordpress blog. We’ll also step through establishing an Amazon S3 bucket, building middleman-demo, and deploying to production. In part 3, we’ll cover how to create automated tests, continuous integration, and automated deployments. About this author Mike Ball is a Philadelphia-based software developer specializing in Ruby on Rails and JavaScript. He works for Comcast Interactive Media where he helps build web-based TV and video consumption applications.
Read more
  • 0
  • 0
  • 20173

article-image-concurrency-and-parallelism-swift-2
Packt
22 Feb 2016
35 min read
Save for later

Concurrency and Parallelism with Swift 2

Packt
22 Feb 2016
35 min read
When I first started learning Objective-C, I already had a good understanding of concurrency and multitasking with my background in other languages such as C and Java. This background made it very easy for me to create multithreaded applications using threads in Objective-C. Then, Apple changed everything for me when they released Grand Central Dispatch (GCD) with OS X 10.6 and iOS 4. At first, I went into denial; there was no way GCD could manage my application's threads better than I could. Then I entered the anger phase, GCD was hard to use and understand. Next was the bargaining phase, maybe I can use GCD with my threading code, so I could still control how the threading worked. Then there was the depression phase, maybe GCD does handle the threading better than I can. Finally, I entered the wow phase; this GCD thing is really easy to use and works amazingly well. After using Grand Central Dispatch and Operation Queues with Objective-C, I do not see a reason for using manual threads with Swift. In this artcle, we will learn the following topics: Basics of concurrency and parallelism How to use GCD to create and manage concurrent dispatch queues How to use GCD to create and manage serial dispatch queues How to use various GCD functions to add tasks to the dispatch queues How to use NSOperation and NSOperationQueues to add concurrency to our applications (For more resources related to this topic, see here.) Concurrency and parallelism Concurrency is the concept of multiple tasks starting, running, and completing within the same time period. This does not necessarily mean that the tasks are executing simultaneously. In order for tasks to be run simultaneously, our application needs to be running on a multicore or multiprocessor system. Concurrency allows us to share the processor or cores with multiple tasks; however, a single core can only execute one task at a given time. Parallelism is the concept of two or more tasks running simultaneously. Since each core of our processor can only execute one task at a time, the number of tasks executing simultaneously is limited to the number of cores within our processors. Therefore, if we have, for example, a four-core processor, then we are limited to only four tasks running simultaneously. Today's processors can execute tasks so quickly that it may appear that larger tasks are executing simultaneously. However, within the system, the larger tasks are actually taking turns executing subtasks on the cores. In order to understand the difference between concurrency and parallelism, let's look at how a juggler juggles balls. If you watch a juggler, it seems they are catching and throwing multiple balls at any given time; however, a closer look reveals that they are, in fact, only catching and throwing one ball at a time. The other balls are in the air waiting to be caught and thrown. If we want to be able to catch and throw multiple balls simultaneously, we need to add multiple jugglers. This example is really good because we can think of jugglers as the cores of a processer. A system with a single core processor (one juggler), regardless of how it seems, can only execute one task (catch and throw one ball) at a time. If we want to execute more than one task at a time, we need to use a multicore processor (more than one juggler). Back in the old days when all the processors were single core, the only way to have a system that executed tasks simultaneously was to have multiple processors in the system. This also required specialized software to take advantage of the multiple processors. In today's world, just about every device has a processor that has multiple cores, and both the iOS and OS X operating systems are designed to take advantage of the multiple cores to run tasks simultaneously. Traditionally, the way applications added concurrency was to create multiple threads; however, this model does not scale well to an arbitrary number of cores. The biggest problem with using threads was that our applications ran on a variety of systems (and processors), and in order to optimize our code, we needed to know how many cores/processors could be efficiently used at a given time, which is sometimes not known at the time of development. In order to solve this problem, many operating systems, including iOS and OS X, started relying on asynchronous functions. These functions are often used to initiate tasks that could possibly take a long time to complete, such as making an HTTP request or writing data to disk. An asynchronous function typically starts the long running task and then returns prior to the task completion. Usually, this task runs in the background and uses a callback function (such as closure in Swift) when the task completes. These asynchronous functions work great for the tasks that the OS provides them for, but what if we needed to create our own asynchronous functions and do not want to manage the threads ourselves? For this, Apple provides a couple of technologies. In this artcle, we will be covering two of these technologies. These are GCD and operation queues. GCD is a low-level C-based API that allows specific tasks to be queued up for execution and schedules the execution on any of the available processor cores. Operation queues are similar to GCD; however, they are Cocoa objects and are internally implemented using GCD. Let's begin by looking at GCD. Grand Central Dispatch Grand Central Dispatch provides what is known as dispatch queues to manage submitted tasks. The queues manage these submitted tasks and execute them in a first-in, first- out (FIFO) order. This ensures that the tasks are started in the order they were submitted. A task is simply some work that our application needs to perform. As examples, we can create tasks that perform simple calculations, read/write data to disk, make an HTTP request, or anything else that our application needs to do. We define these tasks by placing the code inside either a function or a closure and adding it to a dispatch queue. GCD provides three types of queues: Serial queues: Tasks in a serial queue (also known as a private queue) are executed one at a time in the order they were submitted. Each task is started only after the preceding task is completed. Serial queues are often used to synchronize access to specific resources because we are guaranteed that no two tasks in a serial queue will ever run simultaneously. Therefore, if the only way to access the specific resource is through the tasks in the serial queue, then no two tasks will attempt to access the resource at the same time or be out of order. Concurrent queues: Tasks in a concurrent queue (also known as a global dispatch queue) execute concurrently; however, the tasks are still started in the order that they were added to the queue. The exact number of tasks that can be executing at any given instance is variable and is dependent on the system's current conditions and resources. The decision on when to start a task is up to GCD and is not something that we can control within our application. Main dispatch queue: The main dispatch queue is a globally available serial queue that executes tasks on the application's main thread. Since tasks put into the main dispatch queue run on the main thread, it is usually called from a background queue when some background processing has finished and the user interface needs to be updated. Dispatch queues offer a number of advantages over traditional threads. The first and foremost advantage is, with dispatch queues, the system handles the creation and management of threads rather than the application itself. The system can scale the number of threads, dynamically based on the overall available resources of the system and the current system conditions. This means that dispatch queues can manage the threads with greater efficiency than we could. Another advantage of dispatch queues is we are able to control the order that our tasks are started. With serial queues, not only do we control the order in which tasks are started, but also ensure that one task does not start before the preceding one is complete. With traditional threads, this can be very cumbersome and brittle to implement, but with dispatch queues, as we will see later in this artcle, it is quite easy. Creating and managing dispatch queues Let's look at how to create and use a dispatch queue. The following three functions are used to create or retrieve queues. These functions are as follows: dispatch_queue_create: This creates a dispatch queue of either the concurrent or serial type dispatch_get_global_queue: This returns a system-defined global concurrent queue with a specified quality of service dispatch_get_main_queue: This returns the serial dispatch queue associated with the application's main thread We will also be looking at several functions that submit tasks to a queue for execution. These functions are as follows: dispatch_async: This submits a task for asynchronous execution and returns immediately. dispatch_sync: This submits a task for synchronous execution and waits until it is complete before it returns. dispatch_after: This submits a task for execution at a specified time. dispatch_once: This submits a task to be executed once and only once while this application is running. It will execute the task again if the application restarts. Before we look at how to use the dispatch queues, we need to create a class that will help us demonstrate how the various types of queues work. This class will contain two basic functions. The first function will simply perform some basic calculations and then return a value. Here is the code for this function, which is named doCalc(): func doCalc() { var x=100 var y = x*x _ = y/x } The other function, which is named performCalculation(), accepts two parameters. One is an integer named iterations, and the other is a string named tag. The performCalculation () function calls the doCalc() function repeatedly until it calls the function the same number of times as defined by the iterations parameter. We also use the CFAbsoluteTimeGetCurrent() function to calculate the elapsed time it took to perform all of the iterations and then print the elapse time with the tag string to the console. This will let us know when the function completes and how long it took to complete it. The code for this function looks similar to this: func performCalculation(iterations: Int, tag: String) { let start = CFAbsoluteTimeGetCurrent() for var i=0; i<iterations; i++ { self.doCalc() } let end = CFAbsoluteTimeGetCurrent() print("time for (tag): (end-start)") } These functions will be used together to keep our queues busy, so we can see how they work. Let's begin by looking at the GCD functions by using the dispatch_queue_create() function to create both concurrent and serial queues. Creating queues with the dispatch_queue_create() function The dispatch_queue_create() function is used to create both concurrent and serial queues. The syntax of the dispatch_queue_create() function looks similar to this: func dispatch_queue_t dispatch_queue_create (label:   UnsafePointer<Int8>, attr: dispatch_queue_attr_t!) - >     dispatch_queue_t! It takes the following parameters: label: This is a string label that is attached to the queue to uniquely identify it in debugging tools, such as Instruments and crash reports. It is recommended that we use a reverse DNS naming convention. This parameter is optional and can be nil. attr: This specifies the type of queue to make. This can be DISPATCH_QUEUE_SERIAL, DISPATCH_QUEUE_CONCURRENT or nil. If this parameter is nil, a serial queue is created. The return value for this function is the newly created dispatch queue. Let's see how to use the dispatch_queue_create() function by creating a concurrent queue and seeing how it works. Some programming languages use the reverse DNS naming convention to name certain components. This convention is based on a registered domain name that is reversed. As an example, if we worked for company that had a domain name mycompany.com with a product called widget, the reverse DNS name will be com.mycompany.widget. Creating concurrent dispatch queues with the dispatch_queue_create() function The following line creates a concurrent dispatch queue with the label of cqueue.hoffman.jon: let queue = dispatch_queue_create("cqueue.hoffman.jon", DIS-PATCH_QUEUE_CONCURRENT) As we saw in the beginning of this section, there are several functions that we can use to submit tasks to a dispatch queue. When we work with queues, we generally want to use the dispatch_async() function to submit tasks because when we submit a task to a queue, we usually do not want to wait for a response. The dispatch_async() function has the following signature: func dispatch_async (queue: dispatch_queue_t!, block: dis- patch_queue_block!) The following example shows how to use the dispatch_async() function with the concurrent queue we just created: let c = { performCalculation(1000, tag: "async0") } dispatch_async(queue, c) In the preceding code, we created a closure, which represents our task, that simply calls the performCalculation() function of the DoCalculation instance requesting that it runs through 1000 iterations of the doCalc() function. Finally, we use the dispatch_async() function to submit the task to the concurrent dispatch queue. This code will execute the task in a concurrent dispatch queue, which is separate from the main thread. While the preceding example works perfectly, we can actually shorten the code a little bit. The next example shows that we do not need to create a separate closure as we did in the preceding example; we can also submit the task to execute like this: dispatch_async (queue) { calculation.performCalculation(10000000, tag: "async1") } This shorthand version is how we usually submit small code blocks to our queues. If we have larger tasks, or tasks that we need to submit multiple times, we will generally want to create a closure and submit the closure to the queue as we showed originally. Let's see how the concurrent queue actually works by adding several items to the queue and looking at the order and time that they return. The following code will add three tasks to the queue. Each task will call the performCalculation() function with various iteration counts. Remember that the performCalculation() function will execute the calculation routine continuously until it is executed the number of times as defined by the iteration count passed in. Therefore, the larger the iteration count we pass into the performCalculation() function, the longer it should take to execute. Let's take a look at the following code: dispatch_async (queue) { calculation.performCalculation(10000000, tag: "async1") } dispatch_async(queue) { calculation.performCalculation(1000, tag: "async2") } dispatch_async(queue) { calculation.performCalculation(100000, tag: "async3") } Notice that each of the functions is called with a different value in the tag parameter. Since the performCalculation() function prints out the tag variable with the elapsed time, we can see the order in which the tasks complete and the time it took to execute. If we execute the preceding code, we should see the following results: time for async2: 0.000200986862182617 time for async3: 0.00800204277038574 time for async1: 0.461670994758606 The elapse time will vary from one run to the next and from system to system. Since the queues function in a FIFO order, the task that had the tag of async1 was executed first. However, as we can see from the results, it was the last task to finish. Since this is a concurrent queue, if it is possible (if the system has available resources), the blocks of code will execute concurrently. This is why the tasks with the tags of async2 and async3 completed prior to the task that had the async1 tag, even though the execution of the async1 task began before the other two. Now, let's see how a serial queue executes tasks. Creating a serial dispatch queue with the dispatch_queue_create() function A serial queue functions is a little different than a concurrent queue. A serial queue will only execute one task at a time and will wait for one task to complete before starting the next task. This queue, like the concurrent dispatch queue, follows a first-in first-out order. The following line of code will create a serial queue with the label of squeue.hoffman.jon: let queue2 = dispatch_queue_create("squeue.hoffman.jon", DIS-PATCH_QUEUE_SERIAL) Notice that we create the serial queue with the DISPATCH_QUEUE_SERIAL attribute. If you recall, when we created the concurrent queue, we created it with the DISPATCH_QUEUE_CONCURRENT attribute. We can also set this attribute to nil, which will create a serial queue by default. However, it is recommended to always set the attribute to either DISPATCH_QUEUE_SERIAL or DISPATCH_QUEUE_CONCURRENT to make it easier to identify which type of queue we are creating. As we saw with the concurrent dispatch queues, we generally want to use the dispatch_async() function to submit tasks because when we submit a task to a queue, we usually do not want to wait for a response. If, however, we did want to wait for a response, we would use the dispatch_synch() function. var calculation = DoCalculations() let c = { calculation.performCalculation(1000, tag: "sync0") } dispatch_async(queue2, c) Just like with the concurrent queues, we do not need to create a closure to submit a task to the queue. We can also submit the task like this: dispatch_async (queue2) { calculation.performCalculation(100000, tag: "sync1") } Let's see how the serial queues works by adding several items to the queue and looking at the order and time that they complete. The following code will add three tasks, which will call the performCalculation() function with various iteration counts, to the queue: dispatch_async (queue2) { calculation.performCalculation(100000, tag: "sync1") } dispatch_async(queue2) { calculation.performCalculation(1000, tag: "sync2") } dispatch_async(queue2) { calculation.performCalculation(100000, tag: "sync3") } Just like with the concurrent queue example, we call the performCalculation() function with various iteration counts and different values in the tag parameter. Since the performCalculation() function prints out the tag string with the elapsed time, we can see the order that the tasks complete in and the time it takes to execute. If we execute this code, we should see the following results: time for sync1: 0.00648999214172363 time for sync2: 0.00009602308273315 time for sync3: 0.00515800714492798 The elapse time will vary from one run to the next and from system to system.  Unlike the concurrent queues, we can see that the tasks completed in the same order that they were submitted, even though the sync2 and sync3 tasks took considerably less time to complete. This demonstrates that a serial queue only executes one task at a time and that the queue waits for each task to complete before starting the next one. Now that we have seen how to use the dispatch_queue_create() function to create both concurrent and serial queues, let's look at how we can get one of the four system- defined, global concurrent queues using the dispatch_get_global_queue() function. Requesting concurrent queues with the dispatch_get_global_queue() function The system provides each application with four concurrent global dispatch queues of different priority levels. The different priority levels are what distinguish these queues. The four priorities are: DISPATCH_QUEUE_PRIORITY_HIGH: The items in this queue run with the highest priority and are scheduled before items in the default and low priority queues DISPATCH_QUEUE_PRIORITY_DEFAULT: The items in this queue run at the default priority and are scheduled before items in the low priority queue but after items in the high priority queue DISPATCH_QUEUE_PRIORITY_LOW: The items in this queue run with a low priority and are schedule only after items in the high and default queues DISPATCH_QUEUE_PRIORITY_BACKGROUND: The items in this queue run with a background priority, which has the lowest priority Since these are global queues, we do not need to actually create them; instead, we ask for a reference to the queue with the priority level needed. To request a global queue, we use the dispatch_get_global_queue() function. This function has the following syntax: func dispatch_get_global_queue(identifier: Int, flags: UInt) -> ? dispatch_queue_t! Here, the following parameters are defined: identifier: This is the priority of the queue we are requesting flags: This is reserved for future expansion and should be set to zero at this time We request a queue using the dispatch_get_global_queue() function, as shown in the following example: let queue = dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) In this example, we are requesting the global queue with the default priority. We can then use this queue exactly as we used the concurrent queues that we created with the dispatch_queue_create() function. The difference between the queues returned with the dispatch_get_global_queue() function and the ones created with the dispatch_create_queue() function is that with the dispatch_create_queue() function, we are actually creating a new queue. The queues that are returned with the dispatch_get_global_queue() function are global queues that are created when our application first starts; therefore, we are requesting a queue rather than creating a new one. When we use the dispatch_get_global_queue() function, we avoid the overhead of creating the queue; therefore, I recommend using the dispatch_get_global_queue() function unless you have a specific reason to create a queue. Requesting the main queue with the dispatch_get_main_queue() function The dispatch_get_main_queue() function returns the main queue for our application. The main queue is automatically created for the main thread when the application starts. This main queue is a serial queue; therefore, items in this queue are executed one at a time, in the order that they were submitted. We will generally want to avoid using this queue unless we have a need to update the user interface from a background thread. The dispatch_get_main_queue() function has the following syntax: func dispatch_get_main_queue() -> dispatch_queue_t! The following code example shows how to request the main queue: let mainQueue = dispatch_get_main_queue(); We will then submit tasks to the main queue exactly as we would any other serial queue. Just remember that anything submitted to this queue will run on the main thread, which is the thread that all the user interface updates run on; therefore, if we submitted a long running task, the user interface will freeze until that task is completed. In the previous sections, we saw how the dispatch_async() functions submit tasks to concurrent and serial queues. Now, let's look at two additional functions that we can use to submit tasks to our queues. The first function we will look at is the dispatch_after() function. Using the dispatch_after() function There will be times that we need to execute tasks after a delay. If we were using a threading model, we would need to create a new thread, perform some sort of delay or sleep function, and execute our task. With GCD, we can use the dispatch_after() function. The dispatch_after() function takes the following syntax: func dispatch_after(when: dispatch_time_t, queue:   dispatch_queue_t, block: dispatch_block_t) Here, the dispatch_after() function takes the following parameters: when: This is the time that we wish the queue to execute our task in queue: This is the queue that we want to execute our task in block: This is the task to execute As with the dispatch_async() and dispatch_synch() functions, we do not need to include our task as a parameter. We can include our task to execute between two curly brackets exactly as we did previously with the dispatch_async() and dispatch_synch() functions. As we can see from the dispatch_after() function, we use the dispatch_time_t type to define the time to execute the task. We use the dispatch_time() function to create the dispatch_time_t type. The dispatch_time() function has the following syntax: func dispatch_time(when: dispatch_time_t, delta:Int64) ->   dispatch_time_t Here, the dispatch_time() function takes the following parameter: when: This value is used as the basis for the time to execute the task. We generally pass the DISPATCH_TIME_NOW value to create the time, based on the current time. delta: This is the number of nanoseconds to add to the when parameter to get our time. We will use the dispatch_time() and dispatch_after() functions like this: var delayInSeconds = 2.0 let eTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delayInSeconds * Double(NSEC_PER_SEC))) dispatch_after(eTime, queue2) { print("Times Up") } The preceding code will execute the task after a two-second delay. In the dispatch_ time() function, we create a dispatch_time_t type that is two seconds in the future. The NSEC_PER_SEC constant is use to calculate the nanoseconds from seconds. After the two-second delay, we print the message, Times Up, to the console. There is one thing to watch out for with the dispatch_after() function. Let's take a look at the following code: let queue2 = dispatch_queue_create("squeue.hoffman.jon", DIS-PATCH_QUEUE_SERIAL) var delayInSeconds = 2.0 let pTime = dispatch_time (DISPATCH_TIME_NOW,Int64(delayInSeconds * Double(NSEC_PER_SEC))) dispatch_after(pTime, queue2) { print("Times Up") } dispatch_sync(queue2) { calculation.performCalculation(100000, tag: "sync1") } In this code, we begin by creating a serial queue and then adding two tasks to the queue. The first task uses the dispatch_after() function, and the second task uses the dispatch_sync() function. Our initial thought would be that when we executed this code within the serial queue, the first task would execute after a two-second delay and then the second task would execute; however, this would not be correct. The first task is submitted to the queue and executed immediately. It also returns immediately, which lets the queue execute the next task while it waits for the correct time to execute the first task. Therefore, even though we are running the tasks in a serial queue, the second task completes before the first task. The following is an example of the output if we run the preceding code: time for sync1: 0.00407701730728149 Times Up The final GCD function that we are going to look at is dispatch_once(). Using the dispatch_once() function The dispatch_once() function will execute a task once, and only once, for the lifetime of the application. What this means is that the task will be executed and marked as executed, then that task will not be executed again unless the application restarts. While the dispatch_once() function can be and has been used to implement the singleton pattern, there are other easier ways to do this. The dispatch_once() function is great for executing initialization tasks that need to run when our application initially starts. These initialization tasks can consist of initializing our data store or variables and objects. The following code shows the syntax for the dispatch_once() function: func dispatch_once (predicate: UnsafeMutablePoin- ter<dispatch_once_t>,block: dispatch_block_t!) Let's look at how to use the dispatch_once() function: var token: dispatch_once_t = 0 func example() { dispatch_once(&token) { print("Printed only on the first call") } print("Printed for each call") } In this example, the line that prints the message, Printed only on the first call, will be executed only once, no matter how many times the function is called. However, the line that prints the Printed for each call message will be executed each time the function is called. Let's see this in action by calling this function four times, like this: for i in 0..<4 { example() } If we execute this example, we should see the following output: Printed only on the first call Printed for each call Printed for each call Printed for each call Printed for each call Notice, in this example, that we only see the Printed only on the first call message once whereas we see the Printed for each call message all the four times that we call the function. Now that we have looked at GCD, let's take a look at operation queues. Using NSOperation and NSOperationQueue types The NSOperation and NSOperationQueues types, working together, provide us with an alternative to GCD for adding concurrency to our applications. Operation queues are Cocoa objects that function like dispatch queues and internally, operation queues are implemented using GCD. We define the tasks (NSOperations) that we wish to execute and then add the task to the operation queue (NSOperationQueue). The operation queue will then handle the scheduling and execution of tasks. Operation queues are instances of the NSOperationQueue class and operations are instances of the NSOperation class. The operation represents a single unit of work or task. The NSOperation type is an abstract class that provides a thread-safe structure for modeling the state, priority, and dependencies. This class must be subclassed in order to perform any useful work. Apple does provide two concrete implementations of the NSOperation type that we can use as-is for situations where it does not make sense to build a custom subclass. These subclasses are NSBlockOperation and NSInvocationOperation. More than one operation queue can exist at the same time, and actually, there is always at least one operation queue running. This operation queue is known as the main queue. The main queue is automatically created for the main thread when the application starts and is where all the UI operations are performed. There are several ways that we can use the NSOperation and NSOperationQueues classes to add concurrency to our application. In this artcle, we will look at three different ways. The first one we will look at is using the NSBlockOperation implementation of the NSOperation abstract class. Using the NSBlockOperation implementation of NSOperation In this section, we will be using the same DoCalculation class that we used in the Grand Central Dispatch section to keep our queues busy with work so that we can see how the NSOpererationQueues class work. The NSBlockOperation class is a concrete implementation of the NSOperation type that can manage the execution of one or more blocks. This class can be used to execute several tasks at once without the need to create separate operations for each task. Let's see how to use the NSBlockOperation class to add concurrency to our application. The following code shows how to add three tasks to an operation queue using a single NSBlockOperation instance: let calculation = DoCalculations() let operationQueue = NSOperationQueue() let blockOperation1: NSBlockOperation = NSBlockOpera-tion.init(block: { calculation.performCalculation(10000000, tag: "Operation 1") }) blockOperation1.addExecutionBlock( { calculation.performCalculation(10000, tag: "Operation 2") } ) blockOperation1.addExecutionBlock( { calculation.performCalculation(1000000, tag: "Operation 3") } ) operationQueue.addOperation(blockOperation1) In this code, we begin by creating an instance of the DoCalculation class and an instance of the NSOperationQueue class. Next, we created an instance of the NSBlockOperation class using the init constructor. This constructor takes a single parameter, which is a block of code that represents one of the tasks we want to execute in the queue. Next, we add two additional tasks to the NSBlockOperation instance using the addExecutionBlock() method. This is one of the differences between dispatch queues and operations. With dispatch queues, if resources are available, the tasks are executed as they are added to the queue. With operations, the individual tasks are not executed until the operation itself is submitted to an operation queue. Once we add all of the tasks to the NSBlockOperation instance, we then add the operation to the NSOperationQueue instance that we created at the beginning of the code. At this point, the individual tasks within the operation start to execute. This example shows how to use NSBlockOperation to queue up multiple tasks and then pass the tasks to the operation queue. The tasks are executed in a FIFO order; therefore, the first task that is added to the NSBlockOperation instance will be the first task executed. However, since the tasks can be executed concurrently if we have the available resources, the output from this code should look similar to this: time for Operation 2: 0.00546294450759888 time for Operation 3: 0.0800899863243103 time for Operation 1: 0.484337985515594 What if we do not want our tasks to run concurrently? What if we wanted them to run serially like the serial dispatch queue? We can set a property in our operation queue that defines the number of tasks that can be run concurrently in the queue. The property is called maxConcurrentOperationCount and is used like this: operationQueue.maxConcurrentOperatio nCount = 1 However, if we added this line to our previous example, it will not work as expected. To see why this is, we need to understand what the property actually defines. If we look at Apple's NSOperationQueue class reference, the definition of the property says, "The maximum number of queued operations that can execute at the same time." What this tells us is that the maxConcurrentOperationCount property defines the number of operations (this is the key word) that can be executed at the same time. The NSBlockOperation instance, which we added all of our tasks to, represents a single operation; therefore, no other NSBlockOperation added to the queue will execute until the first one is complete, but the individual tasks within the operation will execute concurrently. To run the tasks serially, we would need to create a separate instance of the NSBlockOperations for each task. Using an instance of the NSBlockOperation class good if we have a number of tasks that we want to execute concurrently, but they will not start executing until we add the operation to an operation queue. Let's look at a simpler way of adding tasks to an operation queue using the queues addOperationWithBlock() methods. Using the addOperationWithBlock() method of the operation queue The NSOperationQueue class has a method named addOperationWithBlock() that makes it easy to add a block of code to the queue. This method automatically wraps the block of code in an operation object and then passes that operation to the queue itself. Let's see how to use this method to add tasks to a queue: let operationQueue = NSOperationQueue() let calculation = DoCalculations() operationQueue.addOperationWithBlock() { calculation.performCalculation(10000000, tag: "Operation1") } operationQueue.addOperationWithBlock() { calculation.performCalculation(10000, tag: "Operation2") } operationQueue.addOperationWithBlock() { calculation.performCalculation(1000000, tag: "Operation3") } In the NSBlockOperation example, earlier in this artcle, we added the tasks that we wished to execute into an NSBlockOperation instance. In this example, we are adding the tasks directly to the operation queue, and each task represents one complete operation. Once we create the instance of the operation queue, we then use the addOperationWithBlock() method to add the tasks to the queue. Also, in the NSBlockOperation example, the individual tasks did not execute until all of the tasks were added to the NSBlockOperation object and then that operation was added to the queue. This addOperationWithBlock() example is similar to the GCD example where the tasks begin executing as soon as they are added to the operation queue. If we run the preceding code, the output should be similar to this: time for Operation2: 0.0115870237350464 time for Operation3: 0.0790849924087524 time for Operation1: 0.520610988140106 You will notice that the operations are executed concurrently. With this example, we can execute the tasks serially by using the maxConcurrentOperationCount property that we mentioned earlier. Let's try this by initializing the NSOperationQueue instance like this: var operationQueue = NSOperationQueue() operationQueue.maxConcurrentOperationCount = 1 Now, if we run the example, the output should be similar to this: time for Operation1: 0.418763995170593 time for Operation2: 0.000427007675170898 time for Operation3: 0.0441589951515198 In this example, we can see that each task waited for the previous task to complete prior to starting. Using the addOperationWithBlock() method to add tasks, the operation queue is generally easier than using the NSBlockOperation method; however, the tasks will begin as soon as they are added to the queue, which is usually the desired behavior. Now, let's look at how we can subclass the NSOperation class to create an operation that we can add directly to an operation queue. Subclassing the NSOperation class The previous two examples showed how to add small blocks of code to our operation queues. In these examples, we called the performCalculations method in the DoCalculation class to perform our tasks. These examples illustrate two really good ways to add concurrency for functionally that is already written, but what if, at design time, we want to design our DoCalculation class for concurrency? For this, we can subclass the NSOperation class. The NSOperation abstract class provides a significant amount of infrastructure. This allows us to very easily create a subclass without a lot of work. We should at least provide an initialization method and a main method. The main method will be called when the queue begins executing the operation: Let's see how to implement the DoCalculation class as a subclass of the NSOperation class; we will call this new class MyOperation: class MyOperation: NSOperation { let iterations: Int let tag: String init(iterations: Int, tag: String) { self.iterations = iterations self.tag = tag } override func main() { performCalculation() } func performCalculation() { let start = CFAbsoluteTimeGetCurrent() for var i=0; i<iterations; i++ { self.doCalc() } let end = CFAbsoluteTimeGetCurrent() print("time for (tag): (end-start)") } func doCalc() { let x=100 let y = x*x _ = y/x } } We begin by defining that the MyOperation class is a subclass of the NSOperation class. Within the implementation of the class, we define two class constants, which represent the iteration count and the tag that the performCalculations() method uses. Keep in mind that when the operation queue begins executing the operation, it will call the main() method with no parameters; therefore, any parameters that we need to pass in must be passed in through the initializer. In this example, our initializer takes two parameters that are used to set the iterations and tag classes constants. Then the main() method, that the operation queue is going to call to begin execution of the operation, simply calls the performCalculation() method. We can now very easily add instances of our MyOperation class to an operation queue, like this: var operationQueue = NSOperationQueue() operationQueue.addOperation(MyOperation (iterations: 10000000, tag: "Operation 1")) operationQueue.addOperation(MyOperation (iterations: 10000, tag: "Operation 2")) operationQueue.addOperation(MyOperation (iterations: 1000000, tag: "Operation 3")) If we run this code, we will see the following results: time for Operation 2: 0.00187397003173828 time for Operation 3: 0.104826986789703 time for Operation 1: 0.866684019565582 As we saw earlier, we can also execute the tasks serially by adding the following line, which sets the maxConcurrentOperationCount property of the operation queue: operationQueue.maxConcurrentOperationCount = 1 If we know that we need to execute some functionality concurrently prior to writing the code, I will recommend subclassing the NSOperation class, as shown in this example, rather than using the previous examples. This gives us the cleanest implementation; however, there is nothing wrong with using the NSBlockOperation class or the addOperationWithBlock() methods described earlier in this section. Summary Before we consider adding concurrency to our application, we should make sure that we understand why we are adding it and ask ourselves whether it is necessary. While concurrency can make our application more responsive by offloading work from our main application thread to a background thread, it also adds extra complexity to our code and overhead to our application. I have even seen numerous applications, in various languages, which actually run better after we pulled out some of the concurrency code. This is because the concurrency was not well thought out or planned. With this in mind, it is always a good idea to think and talk about concurrency while we are discussing the application's expected behavior. At the start of this artcle, we had a discussion about running tasks concurrently compared to running tasks in parallel. We also discussed the hardware limitation that limits how many tasks can run in parallel on a given device. Having a good understanding of those concepts is very important to understanding how and when to add concurrency to our projects. While GCD is not limited to system-level applications, before we use it in our application, we should consider whether operation queues would be easier and more appropriate for our needs. In general, we should use the highest level of abstraction that meets our needs. This will usually point us to using operation queues; however, there really is nothing preventing us from using GCD, and it may be more appropriate for our needs. One thing to keep in mind with operation queues is that they do add additional overhead because they are Cocoa objects. For the large majority of applications, this little extra overhead should not be an issue or even noticed; however, for some projects, such as games that need every last resource that they can get, this extra overhead might very well be an issue. Resources for Article: Further resources on this subject: Swift for Open Source Developers [article] Your First Swift 2 Project [article] Exploring Swift [article]
Read more
  • 0
  • 0
  • 20135

article-image-create-an-rnn-based-python-machine-translation-system-tutorial
Sunith Shetty
20 Jul 2018
22 min read
Save for later

Create an RNN based Python machine translation system [Tutorial]

Sunith Shetty
20 Jul 2018
22 min read
Machine translation is a process which uses neural network techniques to automatically translate text from one language to the another, with no human intervention required. In today’s machine learning tutorial, we will understand the architecture and learn how to train and build your own machine translation system. This project will help us automatically translate German to produce English sentences. This article is an excerpt from a book written by Luca Massaron, Alberto Boschetti,  Alexey Grigorev, Abhishek Thakur, and Rajalingappaa Shanmugamani titled TensorFlow Deep Learning Projects. Walkthrough of the architecture A machine translation system receives as input an arbitrary string in one language and produces, as output, a string with the same meaning but in another language. Google Translate is one example (but also many other main IT companies have their own). There, users are able to translate to and from more than 100 languages. Using the webpage is easy: on the left just put the sentence you want to translate (for example, Hello World), select its language (in the example, it's English), and select the language you want it to be translated to. Here's an example where we translate the sentence Hello World to French: Is it easy? At a glance, we may think it's a simple dictionary substitution. Words are chunked, the translation is looked up on the specific English-to-French dictionary, and each word is substituted with its translation. Unfortunately, that's not the case. In the example, the English sentence has two words, while the French one has three. More generically, think about phrasal verbs (turn up, turn off, turn on, turn down), Saxon genitive, grammatical gender, tenses, conditional sentences... they don't always have a direct translation, and the correct one should follow the context of the sentence. That's why, for doing machine translation, we need some artificial intelligence tools. Specifically, as for many other natural language processing (NLP) tasks, we'll be using recurrent neural networks (RNNs).  The main feature they have is that they work on sequences: given an input sequence, they produce an output sequence. The objective of this article is to create the correct training pipeline for having a sentence as the input sequence, and its translation as the output one. Remember also the no free lunch theorem: this process isn't easy, and more solutions can be created with the same result. Here, in this article, we will propose a simple but powerful one. First of all, we start with the corpora: it's maybe the hardest thing to find since it should contain a high fidelity translation of many sentences from a language to another one. Fortunately, NLTK, a well-known package of Python for NLP, contains the corpora Comtrans. Comtrans is the acronym of combination approach to machine translation and contains an aligned corpus for three languages: German, French, and English. In this project, we will use these corpora for a few reasons, as follows: It's easy to download and import in Python. No preprocessing is needed to read it from disk / from the internet. NLTK already handles that part. It's small enough to be used on many laptops (a few dozen thousands sentences). It's freely available on the internet. For more information about the Comtrans project, go to http://www.fask.uni-mainz.de/user/rapp/comtrans/. More specifically, we will try to create a machine translation system to translate German to English. We picked these two languages at random among the ones available in the Comtrans corpora: feel free to flip them, or use the French corpora instead. The pipeline of our project is generic enough to handle any combination. Let's now investigate how the corpora is organized by typing some commands: from nltk.corpus import comtrans print(comtrans.aligned_sents('alignment-de-en.txt')[0]) The output is as follows: <AlignedSent: 'Wiederaufnahme der S...' -> 'Resumption of the se...'> The pairs of sentences are available using the function aligned_sents. The filename contains the from and to language. In this case, as for the following part of the project, we will translate German (de) to English (en). The returned object is an instance of the class nltk.translate.api.AlignedSent. By looking at the documentation, the first language is accessible with the attribute words, while the second language is accessible with the attribute mots. So, to extract the German sentence and its English translation separately, we should run: print(comtrans.aligned_sents()[0].words) print(comtrans.aligned_sents()[0].mots) The preceding code outputs: ['Wiederaufnahme', 'der', 'Sitzungsperiode'] ['Resumption', 'of', 'the', 'session'] How nice! The sentences are already tokenized, and they look as sequences. In fact, they will be the input and (hopefully) the output of the RNN which will provide the service of machine translation from German to English for our project. Furthermore, if you want to understand the dynamics of the language, Comtrans makes available the alignment of the words in the translation: print(comtrans.aligned_sents()[0].alignment) The preceding code outputs: 0-0 1-1 1-2 2-3 The first word in German is translated to the first word in English (Wiederaufnahme to Resumption), the second to the second (der to both of and the), and the third (at index 1) is translated with the fourth (Sitzungsperiode to session). Pre-processing of the corpora The first step is to retrieve the corpora. We've already seen how to do this, but let's now formalize it in a function. To make it generic enough, let's enclose these functions in a file named corpora_tools.py. Let's do some imports that we will use later on: import pickle import re from collections import Counter from nltk.corpus import comtrans Now, let's create the function to retrieve the corpora: def retrieve_corpora(translated_sentences_l1_l2='alignment-de-en.txt'): print("Retrieving corpora: {}".format(translated_sentences_l1_l2)) als = comtrans.aligned_sents(translated_sentences_l1_l2) sentences_l1 = [sent.words for sent in als] sentences_l2 = [sent.mots for sent in als] return sentences_l1, sentences_l2 This function has one argument; the file containing the aligned sentences from the NLTK Comtrans corpora. It returns two lists of sentences (actually, they're a list of tokens), one for the source language (in our case, German), the other in the destination language (in our case, English). On a separate Python REPL, we can test this function: sen_l1, sen_l2 = retrieve_corpora() print("# A sentence in the two languages DE & EN") print("DE:", sen_l1[0]) print("EN:", sen_l2[0]) print("# Corpora length (i.e. number of sentences)") print(len(sen_l1)) assert len(sen_l1) == len(sen_l2) The preceding code creates the following output: Retrieving corpora: alignment-de-en.txt # A sentence in the two languages DE & EN DE: ['Wiederaufnahme', 'der', 'Sitzungsperiode'] EN: ['Resumption', 'of', 'the', 'session'] # Corpora length (i.e. number of sentences) 33334 We also printed the number of sentences in each corpora (33,000) and asserted that the number of sentences in the source and the destination languages is the same. In the following step, we want to clean up the tokens. Specifically, we want to tokenize punctuation and lowercase the tokens. To do so, we can create a new function in corpora_tools.py. We will use the regex module to perform the further splitting tokenization: def clean_sentence(sentence): regex_splitter = re.compile("([!?.,:;$"')( ])") clean_words = [re.split(regex_splitter, word.lower()) for word in sentence] return [w for words in clean_words for w in words if words if w] Again, in the REPL, let's test the function: clean_sen_l1 = [clean_sentence(s) for s in sen_l1] clean_sen_l2 = [clean_sentence(s) for s in sen_l2] print("# Same sentence as before, but chunked and cleaned") print("DE:", clean_sen_l1[0]) print("EN:", clean_sen_l2[0]) The preceding code outputs the same sentence as before, but chunked and cleaned: DE: ['wiederaufnahme', 'der', 'sitzungsperiode'] EN: ['resumption', 'of', 'the', 'session'] Nice! The next step for this project is filtering the sentences that are too long to be processed. Since our goal is to perform the processing on a local machine, we should limit ourselves to sentences up to N tokens. In this case, we set N=20, in order to be able to train the learner within 24 hours. If you have a powerful machine, feel free to increase that limit. To make the function generic enough, there's also a lower bound with a default value set to 0, such as an empty token set. The logic of the function is very easy: if the number of tokens for a sentence or its translation is greater than N, then the sentence (in both languages) is removed: def filter_sentence_length(sentences_l1, sentences_l2, min_len=0, max_len=20): filtered_sentences_l1 = [] filtered_sentences_l2 = [] for i in range(len(sentences_l1)): if min_len <= len(sentences_l1[i]) <= max_len and min_len <= len(sentences_l2[i]) <= max_len: filtered_sentences_l1.append(sentences_l1[i]) filtered_sentences_l2.append(sentences_l2[i]) return filtered_sentences_l1, filtered_sentences_l2 Again, let's see in the REPL how many sentences survived this filter. Remember, we started with more than 33,000: filt_clean_sen_l1, filt_clean_sen_l2 = filter_sentence_length(clean_sen_l1, clean_sen_l2) print("# Filtered Corpora length (i.e. number of sentences)") print(len(filt_clean_sen_l1)) assert len(filt_clean_sen_l1) == len(filt_clean_sen_l2) The preceding code prints the following output: # Filtered Corpora length (i.e. number of sentences) 14788 Almost 15,000 sentences survived, that is, half of the corpora. Now, we finally move from text to numbers (which AI mainly uses). To do so, we shall create a dictionary of the words for each language. The dictionary should be big enough to contain most of the words, though we can discard some if the language has words with low occourrence. This is a common practice even in the tf-idf (term frequency within a document, multiplied by the inverse of the document frequency, i.e. in how many documents that token appears), where very rare words are discarded to speed up the computation, and make the solution more scalable and generic. We need here four special symbols in both dictionaries: One symbol for padding (we'll see later why we need it) One symbol for dividing the two sentences One symbol to indicate where the sentence stops One symbol to indicate unknown words (like the very rare ones) For doing so, let's create a new file named data_utils.py containing the following lines of code: _PAD = "_PAD" _GO = "_GO" _EOS = "_EOS" _UNK = "_UNK" _START_VOCAB = [_PAD, _GO, _EOS, _UNK] PAD_ID = 0 GO_ID = 1 EOS_ID = 2 UNK_ID = 3 OP_DICT_IDS = [PAD_ID, GO_ID, EOS_ID, UNK_ID] Then, back to the corpora_tools.py file, let's add the following function: import data_utils def create_indexed_dictionary(sentences, dict_size=10000, storage_path=None): count_words = Counter() dict_words = {} opt_dict_size = len(data_utils.OP_DICT_IDS) for sen in sentences: for word in sen: count_words[word] += 1 dict_words[data_utils._PAD] = data_utils.PAD_ID dict_words[data_utils._GO] = data_utils.GO_ID dict_words[data_utils._EOS] = data_utils.EOS_ID dict_words[data_utils._UNK] = data_utils.UNK_ID for idx, item in enumerate(count_words.most_common(dict_size)): dict_words[item[0]] = idx + opt_dict_size if storage_path: pickle.dump(dict_words, open(storage_path, "wb")) return dict_words This function takes as arguments the number of entries in the dictionary and the path of where to store the dictionary. Remember, the dictionary is created while training the algorithms: during the testing phase it's loaded, and the association token/symbol should be the same one as used in the training. If the number of unique tokens is greater than the value set, only the most popular ones are selected. At the end, the dictionary contains the association between a token and its ID for each language. After building the dictionary, we should look up the tokens and substitute them with their token ID. For that, we need another function: def sentences_to_indexes(sentences, indexed_dictionary): indexed_sentences = [] not_found_counter = 0 for sent in sentences: idx_sent = [] for word in sent: try: idx_sent.append(indexed_dictionary[word]) except KeyError: idx_sent.append(data_utils.UNK_ID) not_found_counter += 1 indexed_sentences.append(idx_sent) print('[sentences_to_indexes] Did not find {} words'.format(not_found_counter)) return indexed_sentences This step is very simple; the token is substituted with its ID. If the token is not in the dictionary, the ID of the unknown token is used. Let's see in the REPL how our sentences look after these steps: dict_l1 = create_indexed_dictionary(filt_clean_sen_l1, dict_size=15000, storage_path="/tmp/l1_dict.p") dict_l2 = create_indexed_dictionary(filt_clean_sen_l2, dict_size=10000, storage_path="/tmp/l2_dict.p") idx_sentences_l1 = sentences_to_indexes(filt_clean_sen_l1, dict_l1) idx_sentences_l2 = sentences_to_indexes(filt_clean_sen_l2, dict_l2) print("# Same sentences as before, with their dictionary ID") print("DE:", list(zip(filt_clean_sen_l1[0], idx_sentences_l1[0]))) This code prints the token and its ID for both the sentences. What's used in the RNN will be just the second element of each tuple, that is, the integer ID: # Same sentences as before, with their dictionary ID DE: [('wiederaufnahme', 1616), ('der', 7), ('sitzungsperiode', 618)] EN: [('resumption', 1779), ('of', 8), ('the', 5), ('session', 549)] Please also note how frequent tokens, such as the and of in English, and der in German, have a low ID. That's because the IDs are sorted by popularity (see the body of the function create_indexed_dictionary). Even though we did the filtering to limit the maximum size of the sentences, we should create a function to extract the maximum size. For the lucky owners of very powerful machines, which didn't do any filtering, that's the moment to see how long the longest sentence in the RNN will be. That's simply the function: def extract_max_length(corpora): return max([len(sentence) for sentence in corpora]) Let's apply the following to our sentences: max_length_l1 = extract_max_length(idx_sentences_l1) max_length_l2 = extract_max_length(idx_sentences_l2) print("# Max sentence sizes:") print("DE:", max_length_l1) print("EN:", max_length_l2) As expected, the output is: # Max sentence sizes: DE: 20 EN: 20 The final preprocessing step is padding. We need all the sequences to be the same length, therefore we should pad the shorter ones. Also, we need to insert the correct tokens to instruct the RNN where the string begins and ends. Basically, this step should: Pad the input sequences, for all being 20 symbols long Pad the output sequence, to be 20 symbols long Insert an _GO at the beginning of the output sequence and an _EOS at the end to position the start and the end of the translation This is done by this function (insert it in the corpora_tools.py): def prepare_sentences(sentences_l1, sentences_l2, len_l1, len_l2): assert len(sentences_l1) == len(sentences_l2) data_set = [] for i in range(len(sentences_l1)): padding_l1 = len_l1 - len(sentences_l1[i]) pad_sentence_l1 = ([data_utils.PAD_ID]*padding_l1) + sentences_l1[i] padding_l2 = len_l2 - len(sentences_l2[i]) pad_sentence_l2 = [data_utils.GO_ID] + sentences_l2[i] + [data_utils.EOS_ID] + ([data_utils.PAD_ID] * padding_l2) data_set.append([pad_sentence_l1, pad_sentence_l2]) return data_set To test it, let's prepare the dataset and print the first sentence: data_set = prepare_sentences(idx_sentences_l1, idx_sentences_l2, max_length_l1, max_length_l2) print("# Prepared minibatch with paddings and extra stuff") print("DE:", data_set[0][0]) print("EN:", data_set[0][1]) print("# The sentence pass from X to Y tokens") print("DE:", len(idx_sentences_l1[0]), "->", len(data_set[0][0])) print("EN:", len(idx_sentences_l2[0]), "->", len(data_set[0][1])) The preceding code outputs the following: # Prepared minibatch with paddings and extra stuff DE: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1616, 7, 618] EN: [1, 1779, 8, 5, 549, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] # The sentence pass from X to Y tokens DE: 3 -> 20 EN: 4 -> 22 As you can see, the input and the output are padded with zeros to have a constant length (in the dictionary, they correspond to _PAD, see data_utils.py), and the output contains the markers 1 and 2 just before the start and the end of the sentence. As proven effective in the literature, we're going to pad the input sentences at the start and the output sentences at the end. After this operation, all the input sentences are 20 items long, and the output sentences 22. Training the machine translator So far, we've seen the steps to preprocess the corpora, but not the model used. The model is actually already available on the TensorFlow Models repository, freely downloadable from https://github.com/tensorflow/models/blob/master/tutorials/rnn/translate/seq2seq_model.py. The piece of code is licensed with Apache 2.0. We really thank the authors for having open sourced such a great model. Copyright 2015 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the License); You may not use this file except in compliance with the License. You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software. Distributed under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. We will see the usage of the model throughout this section. First, let's create a new file named train_translator.py and put in some imports and some constants. We will save the dictionary in the /tmp/ directory, as well as the model and its checkpoints: import time import math import sys import pickle import glob import os import tensorflow as tf from seq2seq_model import Seq2SeqModel from corpora_tools import * path_l1_dict = "/tmp/l1_dict.p" path_l2_dict = "/tmp/l2_dict.p" model_dir = "/tmp/translate " model_checkpoints = model_dir + "/translate.ckpt" Now, let's use all the tools created in the previous section within a function that, given a Boolean flag, returns the corpora. More specifically, if the argument is False, it builds the dictionary from scratch (and saves it); otherwise, it uses the dictionary available in the path: def build_dataset(use_stored_dictionary=False): sen_l1, sen_l2 = retrieve_corpora() clean_sen_l1 = [clean_sentence(s) for s in sen_l1] clean_sen_l2 = [clean_sentence(s) for s in sen_l2] filt_clean_sen_l1, filt_clean_sen_l2 = filter_sentence_length(clean_sen_l1, clean_sen_l2) if not use_stored_dictionary: dict_l1 = create_indexed_dictionary(filt_clean_sen_l1, dict_size=15000, storage_path=path_l1_dict) dict_l2 = create_indexed_dictionary(filt_clean_sen_l2, dict_size=10000, storage_path=path_l2_dict) else: dict_l1 = pickle.load(open(path_l1_dict, "rb")) dict_l2 = pickle.load(open(path_l2_dict, "rb")) dict_l1_length = len(dict_l1) dict_l2_length = len(dict_l2) idx_sentences_l1 = sentences_to_indexes(filt_clean_sen_l1, dict_l1) idx_sentences_l2 = sentences_to_indexes(filt_clean_sen_l2, dict_l2) max_length_l1 = extract_max_length(idx_sentences_l1) max_length_l2 = extract_max_length(idx_sentences_l2) data_set = prepare_sentences(idx_sentences_l1, idx_sentences_l2, max_length_l1, max_length_l2) return (filt_clean_sen_l1, filt_clean_sen_l2), data_set, (max_length_l1, max_length_l2), (dict_l1_length, dict_l2_length) This function returns the cleaned sentences, the dataset, the maximum length of the sentences, and the lengths of the dictionaries. Also, we need to have a function to clean up the model. Every time we run the training routine we need to clean up the model directory, as we haven't provided any garbage information. We can do this with a very simple function: def cleanup_checkpoints(model_dir, model_checkpoints): for f in glob.glob(model_checkpoints + "*"): os.remove(f) try: os.mkdir(model_dir) except FileExistsError: pass Finally, let's create the model in a reusable fashion: def get_seq2seq_model(session, forward_only, dict_lengths, max_sentence_lengths, model_dir): model = Seq2SeqModel( source_vocab_size=dict_lengths[0], target_vocab_size=dict_lengths[1], buckets=[max_sentence_lengths], size=256, num_layers=2, max_gradient_norm=5.0, batch_size=64, learning_rate=0.5, learning_rate_decay_factor=0.99, forward_only=forward_only, dtype=tf.float16) ckpt = tf.train.get_checkpoint_state(model_dir) if ckpt and tf.train.checkpoint_exists(ckpt.model_checkpoint_path): print("Reading model parameters from {}".format(ckpt.model_checkpoint_path)) model.saver.restore(session, ckpt.model_checkpoint_path) else: print("Created model with fresh parameters.") session.run(tf.global_variables_initializer()) return model This function calls the constructor of the model, passing the following parameters: The source vocabulary size (German, in our example) The target vocabulary size (English, in our example) The buckets (in our example is just one, since we padded all the sequences to a single size) The long short-term memory (LSTM) internal units size The number of stacked LSTM layers The maximum norm of the gradient (for gradient clipping) The mini-batch size (that is, how many observations for each training step) The learning rate The learning rate decay factor The direction of the model The type of data (in our example, we will use flat16, that is, float using 2 bytes) To make the training faster and obtain a model with good performance, we have already set the values in the code; feel free to change them and see how it performs. The final if/else in the function retrieves the model, from its checkpoint, if the model already exists. In fact, this function will be used in the decoder too to retrieve and model on the test set. Finally, we have reached the function to train the machine translator. Here it is: def train(): with tf.Session() as sess: model = get_seq2seq_model(sess, False, dict_lengths, max_sentence_lengths, model_dir) # This is the training loop. step_time, loss = 0.0, 0.0 current_step = 0 bucket = 0 steps_per_checkpoint = 100 max_steps = 20000 while current_step < max_steps: start_time = time.time() encoder_inputs, decoder_inputs, target_weights = model.get_batch([data_set], bucket) _, step_loss, _ = model.step(sess, encoder_inputs, decoder_inputs, target_weights, bucket, False) step_time += (time.time() - start_time) / steps_per_checkpoint loss += step_loss / steps_per_checkpoint current_step += 1 if current_step % steps_per_checkpoint == 0: perplexity = math.exp(float(loss)) if loss < 300 else float("inf") print ("global step {} learning rate {} step-time {} perplexity {}".format( model.global_step.eval(), model.learning_rate.eval(), step_time, perplexity)) sess.run(model.learning_rate_decay_op) model.saver.save(sess, model_checkpoints, global_step=model.global_step) step_time, loss = 0.0, 0.0 encoder_inputs, decoder_inputs, target_weights = model.get_batch([data_set], bucket) _, eval_loss, _ = model.step(sess, encoder_inputs, decoder_inputs, target_weights, bucket, True) eval_ppx = math.exp(float(eval_loss)) if eval_loss < 300 else float("inf") print(" eval: perplexity {}".format(eval_ppx)) sys.stdout.flush() The function starts by creating the model. Also, it sets some constants on the steps per checkpoints and the maximum number of steps. Specifically, in the code, we will save a model every 100 steps and we will perform no more than 20,000 steps. If it still takes too long, feel free to kill the program: every checkpoint contains a trained model, and the decoder will use the most updated one. At this point, we enter the while loop. For each step, we ask the model to get a minibatch of data (of size 64, as set previously). The method get_batch returns the inputs (that is, the source sequence), the outputs (that is, the destination sequence), and the weights of the model. With the method step, we run one step of the training. One piece of information returned is the loss for the current minibatch of data. That's all the training! To report the performance and store the model every 100 steps, we print the average perplexity of the model (the lower, the better) on the 100 previous steps, and we save the checkpoint. The perplexity is a metric connected to the uncertainty of the predictions: the more confident we're about the tokens, the lower will be the perplexity of the output sentence. Also, we reset the counters and we extract the same metric from a single minibatch of the test set (in this case, it's a random minibatch of the dataset), and performances of it are printed too. Then, the training process restarts again. As an improvement, every 100 steps we also reduce the learning rate by a factor. In this case, we multiply it by 0.99. This helps the convergence and the stability of the training. We now have to connect all the functions together. In order to create a script that can be called by the command line but is also used by other scripts to import functions, we can create a main, as follows: if __name__ == "__main__": _, data_set, max_sentence_lengths, dict_lengths = build_dataset(False) cleanup_checkpoints(model_dir, model_checkpoints) train() In the console, you can now train your machine translator system with a very simple command: $> python train_translator.py On an average laptop, without an NVIDIA GPU, it takes more than a day to reach a perplexity below 10 (12+ hours). This is the output: Retrieving corpora: alignment-de-en.txt [sentences_to_indexes] Did not find 1097 words [sentences_to_indexes] Did not find 0 words Created model with fresh parameters. global step 100 learning rate 0.5 step-time 4.3573073434829713 perplexity 526.6638556683066 eval: perplexity 159.2240770935855 [...] global step 10500 learning rate 0.180419921875 step-time 4.35106209993362414 perplexity 2.0458043055629487 eval: perplexity 1.8646006006241982 [...] In this article, we've seen how to create a machine translation system based on an RNN. We've seen how to organize the corpus, and how to train it. To know more about how to test and translate the model, do checkout this book TensorFlow Deep Learning Projects. Google’s translation tool is now offline – and more powerful than ever thanks to AI Anatomy of an automated machine learning algorithm (AutoML) FAE (Fast Adaptation Engine): iOlite’s tool to write Smart Contracts using machine translation
Read more
  • 0
  • 1
  • 20135

article-image-upgrading-packaging-publishing-react-vr-app
Sunith Shetty
08 Jun 2018
19 min read
Save for later

Upgrading, packaging, and publishing your React VR app

Sunith Shetty
08 Jun 2018
19 min read
It is fun to develop and experience virtual worlds at home. Eventually, though, you want the world to see your creation. To do that, we need to package and publish our app. In the course of development, upgrades to React may come along; before publishing, you will need to decide whether you need to "code freeze" and ship with a stable version, or upgrade to a new version. This is a design decision. In today’s tutorial, we will learn to upgrade React VR and bundle the code in order to publish on the web. This article is an excerpt from a book written by John Gwinner titled Getting Started with React VR. This book will get you well-versed with Virtual Reality (VR) and React VR components to create your own VR apps. One of the neat things, although it can be frustrating, is that web projects are frequently updated.  There are a couple of different ways to do an upgrade: You can install/create a new app with the same name You will then go to your old app and copy everything over This is a facelift upgrade or Rip and Replace Do an update. Mostly, this is an update to package.json, and then delete node_modules and rebuild it. This is an upgrade in place. It is up to you which method you use, but the major difference is that an upgrade in place is somewhat easier—no source code to modify and copy—but it may or may not work. A Facelift upgrade also relies on you using the correct react-vr-cli. There is a notice that runs whenever you run React VR from the Command Prompt that will tell you whether it's old: The error or warning that comes up about an upgrade when you run React VR from a Command Prompt may fly by quickly. It takes a while to run, so you may go away for a cup of coffee. Pay attention to red lines, seriously. To do an upgrade in place, you will typically get an update notification from Git if you have subscribed to the project. If you haven't, you should go to: http://bit.ly/ReactVR, create an account (if you don't have one already), and click on the eyeball icon to join the watch list. Then, you will get an email every time there is an upgrade. We will cover the most straightforward way to do an upgrade—upgrade in place, first. Upgrading in place How do you know what version of React you have installed? From a Node.js prompt, type this: npm list react-vr Also, check the version of react-vr-web: npm list react-vr-web Check the version of react-vr-cli (the command-line interface, really only for creating the hello world app). npm list react-vr-cli Check the version of ovrui (open VR's user interface): npm list ovrui You can check these against the versions on the documentation. If you've subscribed to React VR on GitHub (and you should!), then you will get an email telling you that there is an upgrade. Note that the CLI will also tell you if it is out of date, although this only applies when you are creating a new application (folder/website). The release notes are at: http://bit.ly/VRReleases . There, you will find instructions to upgrade. The upgrade instructions usually have you do the following: Delete your node_modules directory. Open your package.json file. Update react-vr, react-vr-web, and ovrui to "New version number" for example, 2.0.0. Update react to "a.b.c". Update react-native to "~d.e.f". Update three to "^g.h.k". Run npm install or yarn. Note the ~ and ^ symbols; ~version means approximately equivalent to version and ^version means compatible with version. This is a help, as you may have other packages that may want other versions of react-native and three, specifically. To get the values of {a...k}, refer to the release notes. I have also found that you may need to include these modules in the devDependencies section of package.json: "react-devtools": "^2.5.2", "react-test-renderer": "16.0.0", You may see this error: module.js:529 throw err; ^ Error: Cannot find module './node_modules/react-native/packager/blacklist' If you do, make the following changes in your projects root folder in the rncli.config.js file. Replace the var blacklist = require('./node_modules/react-native/packager/blacklist'); line with var blacklist = require('./node_modules/metro-bundler/src/blacklist');. Third-party dependencies If you have been experimenting and adding modules with npm install <something>, you may find, after an upgrade, that things do not work. The package.json file also needs to know about all the additional packages you installed during experimentation. This is the project way (npm way) to ensure that Node.js knows we need a particular piece of software. If you have this issue, you'll need to either repeat the install with the—save parameter, or edit the dependencies section in your package.json file. { "name": "WalkInAMaze", "version": "0.0.1", "private": true, "scripts": { "start": "node -e "console.log('open browser at http://localhost:8081/vr/\n\n');" && node node_modules/react-native/local-cli/cli.js start", "bundle": "node node_modules/react-vr/scripts/bundle.js", "open": "node -e "require('xopen')('http://localhost:8081/vr/')"", "devtools": "react-devtools", "test": "jest" }, "dependencies": { "ovrui": "~2.0.0", "react": "16.0.0", "react-native": "~0.48.0", "three": "^0.87.0", "react-vr": "~2.0.0", "react-vr-web": "~2.0.0", "mersenne-twister": "^1.1.0" }, "devDependencies": { "babel-jest": "^19.0.0", "babel-preset-react-native": "^1.9.1", "jest": "^19.0.2", "react-devtools": "^2.5.2", "react-test-renderer": "16.0.0", "xopen": "1.0.0" }, "jest": { "preset": "react-vr" } } Again, this is the manual way; a better way is to use npm install <package> -save. The -s qualifier saves the new package you've installed in package.json. The manual edits can be handy to ensure that you've got the right versions if you get a version mismatch. If you mess around with installing and removing enough packages, you will eventually mess up your modules. If you get errors even after removing node_modules, issue these commands: npm cache clean --force npm start -- --reset-cache The cache clean won't do it by itself; you need the reset-cache, otherwise, the problem packages will still be saved, even if they don't physically exist! Really broken upgrades – rip and replace If, however, after all that work, your upgrade still does not work, all is not lost. We can do a rip and replace upgrade. Note that this is sort of a "last resort", but it does work fairly well. Follow these steps: Ensure that your react-vr-cli package is up to date, globally: [F:ReactVR]npm install react-vr-cli -g C:UsersJohnAppDataRoamingnpmreact-vr -> C:UsersJohnAppDataRoamingnpmnode_modulesreact-vr-cliindex.js + react-vr-cli@0.3.6 updated 8 packages in 2.83s This is important, as when there is a new version of React, you may not have the most up-to-date react-vr-cli. It will tell you when you use it that there is a newer version out, but that line frequently scrolls by; if you get bored and don't note, you can spend a lot of time trying to install an updated version, to no avail. An npm generates a lot of verbiage, but it is important to read what it says, especially red formatted lines. Ensure that all CLI (DOS) windows, editing sessions, Node.js running CLIs, and so on, are closed. (You shouldn't need to reboot, however; just close everything using the old directory). Rename the old code to MyAppName140 (add a version number to the end of the old react-vr directory). Create the application, using react-vr init MyAppName, in other words, the original app name. The next step is easiest using a diff program (refer to http://bit.ly/WinDiff). I use Beyond Compare, but there are other ones too. Choose one and install it, if needed. Compare the two directories, .MyAppName (new) and .MyAppName140, and see what files have changed. Move over any new files from your old app, including assets (you can probably copy over the entire static_assets folder). Merge any files that have changed, except package.json. Generally, you will need to merge these files: index.vr.js client.js (if you changed it) For package.json, see what lines have been added, and install those packages in the new app via npm install <missed package> --save, or start the app and see what is missing. Remove any files seeded by the hello world app, such as chess-world.jpg (unless you are using that background, of course). Usually, you don't change the rn-cli.config.js file (unless you modified the seeded version). Most code will move directly over. Ensure that you change the application name if you changed the directory name, but with the preceding directions, you won't have to. The preceding list of upgrade steps may be slightly easier if there are massive changes to React VR; it will require some picking through source files. The source is pretty straightforward, so this should be easy in practice. I found that these techniques will work best if the automatic upgrade did not work. As mentioned earlier, the time to do a major upgrade probably is not right before publishing the app, unless there is some new feature you need. You want to adequately test your app to ensure that there aren't any bugs. I'm including the upgrade steps here, though, but not because you should do it right before publishing. Getting your code ready to publish Honestly, you should never put off organizing your clothes until, oh, wait, we're talking about code. You should never put off organizing your code until the night you want to ship it. Even the code you think is throw away may end up in production. Learn good coding habits and style from the beginning. Good code organization Good code, from the very start, is very important for many reasons: If your code uses sloppy indentation, it's more difficult to read. Many code editors, such as Visual Studio Code, Atom, and Webstorm, will format code for you, but don't rely on these tools. Poor naming conventions can hide problems. An improper case on variables can hide problems, such as using this.State instead of this.state. Most of the time spent coding, as much as 80%, is in maintenance. If you can't read the code, you can't maintain it. When you're a starting out programmer, you frequently think you'll always be able to read your own code, but when you pick up a piece years later and say "Who wrote this junk?" and then realize it was you, you will quit doing things like a, b, c, d variable names and the like. Most software at some point is maintained, read, copied, or used by someone other than the author. Most programmers think code standards are for "the other guy," yet complain when they have to code well. Who then does? Most programmers will immediately ask for the code documentation and roll their eyes when they don't find it. I usually ask to see the documentation they wrote for their last project. Every programmer I've hired usually gives me a deer in the headlights look. This is why I usually require good comments in the code. A good comment is not something like this: //count from 99 to 1 for (i=99; i>0; i--) ... A good comment is this: //we are counting bottles of beer for (i=99; i>0; i--) ... Cleaning the lint trap (checking code standards) When you wash clothes, the lint builds up and will eventually clog your washing machine or dryer, or cause a fire. In the PC world, old code, poorly typed names, and all can also build up. Refactoring is one way to clean up the code. I highly recommend that you use some form of version control, such as Git or bitbucket to check your code; while refactoring, it's quite possible to totally mess up your code and if you don't use version control, you may lose a lot of work. A great way to do a code review of your work, before you publish, is to use a linter. Linters go through your code and point out problems (crud), improper syntax, things that may work differently than you intend, and generally try to pick up your room after you, like your mom does. While you might not like it if your mom does that, these tools are invaluable. Computers are, after all, very picky and why not use the machines against each other? One of the most common ways to let software check your software for JavaScript is a program called ESLint. You can read about it at: http://bit.ly/JSLinter. To install ESLint, you can do it via npm like most packages—npm install eslint --save-dev. The --save-dev option puts a requirement in your project while you are developing. Once you've published your app, you won't need to pack the ESLint information with your project! There are a number of other things you need to get ESLint to work properly; read the configuration pages and go through the tutorials. A lot depends on what IDE you use. You can use ESLint with Visual Studio, for example. Once you've installed ESLint, you need to configure a local configuration file. Do this with eslint --init. The --init command will display a prompt that will ask you how to configure the rules it will follow. It will ask a series of questions, and ask what style to use. AirBNB is fairly common, although you can use others; there's no wrong choice. If you are working for a company, they may already have standards, so check with management. One of the prompts will ask if you need React. React VR coding style Coding style can be nearly religious, but in the JavaScript and React world, some standards are very common. AirBNB has one good, fairly well–regarded style guide at: http://bit.ly/JStyle. For React VR, some style options to consider are as follows: Use lowercase for the first letter of a variable name. In other words, this.props.currentX, not this.props.CurrentX, and don't use underscores (this is called camelCase). Use PascalCase only when naming constructors or classes. As you're using PascalCase for files, make the filename match the class, so   import MyClass from './MyClass'. Be careful about 0 vs {0}. In general, learn JavaScript and React. Always use const or let to declare variables to avoid polluting the global namespace. Avoid using ++ and --. This one was hard for me, being a C++ programmer. Hopefully, by the time you've read this, I've fixed it in the source examples. If not, do as I say, not as I do! Learn the difference between == and ===, and use them properly, another thing that is new for C++ and C# programmers. In general, I highly recommend that you pour over these coding styles and use a linter when you write your code: Third-party dependencies For your published website/application to really work reliably, we also need to update package.json; this is sort of the "project" way to ensure that Node.js knows we need a particular piece of software. We will edit the "dependencies" section to add the last line,(bold emphasis mine, bold won't show up in a text editor, obviously!): { "name": "WalkInAMaze", "version": "0.0.1", "private": true, "scripts": { "start": "node -e "console.log('open browser at http://localhost:8081/vr/\n\n');" && node node_modules/react-native/local-cli/cli.js start", "bundle": "node node_modules/react-vr/scripts/bundle.js", "open": "node -e "require('xopen')('http://localhost:8081/vr/')"", "devtools": "react-devtools", "test": "jest" }, "dependencies": { "ovrui": "~2.0.0", "react": "16.0.0", "react-native": "~0.48.0", "three": "^0.87.0", "react-vr": "~2.0.0", "react-vr-web": "~2.0.0", "mersenne-twister": "^1.1.0" }, "devDependencies": { "babel-jest": "^19.0.0", "babel-preset-react-native": "^1.9.1", "jest": "^19.0.2", "react-devtools": "^2.5.2", "react-test-renderer": "16.0.0", "xopen": "1.0.0" }, "jest": { "preset": "react-vr" } } This is the manual way; a better way is to use npm install <package> -s. The -s qualifier saves the new package you've installed in package.json. The manual edits can be handy to ensure that you've got the right versions, if you get a version mismatch. If you mess around with installing and removing enough packages, you will eventually mess up your modules. If you get errors, even after removing node_modules, issue these commands: npm start -- --reset-cache npm cache clean --force The cache clean won't do it by itself; you need the reset–cache, otherwise the problem packages will still be saved, even if they don't physically exist! Bundling for publishing on the web Assuming that you have your project dependencies set up correctly to get your project to run from a web server, typically through an ISP or service provider, you need to "bundle" it. React VR has a script that will package up everything into just a few files. Note, of course, that your desktop machine counts as a "web server", although I wouldn't recommend that you expose your development machine to the web. The better way to have other people experience your new Virtual Reality is to bundle it and put it on a commercial web service. Packaging React VR for release on a website The basic process is easy with the React VR provided script: Go to the VR directory where you normally run npm start, and run the npm run bundle command: You will then go to your website the same way you normally upload files, and create a directory called vr. In your project directory, in our case f:ReactVRWalkInAMaze, find the following files in .VRBuild: client.bundle.js index.bundle.js Copy those to your website. Make a directory called static_assets. Copy all of your files (that your app uses) from AppNamestatic_assets to the new static_assets folder. Ensure that you have MIME mapping set up for all of your content; in particular, .obj, .mtl, and .gltf files may need new mappings. Check with your web server documentation: For gltf files, use model/gltf-binary Any .bin files used by gltf should be application/octet-stream For .obj files, I've used application/octet-stream The official list is at http://bit.ly/MimeTypes Very generally, application/octet-stream will send the files "exactly" as they are on the server, so this is sort of a general purpose "catch all" Copy the index.html from the root of your application to the directory on your website where you are publishing the app; in our case, it'll be the vr directory, so the file is alongside the two .js files. Modify index.html for the following lines (note the change to ./index.vr): <html> <head> <title>WalkInAMaze</title> <style>body { margin: 0; }</style> <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"> </head> <body> <!-- When you're ready to deploy your app, update this line to point to your compiled client.bundle.js --> <script src="./client.bundle?platform=vr"></script> <script> // Initialize the React VR application ReactVR.init( // When you're ready to deploy your app, update this line to point to // your compiled index.bundle.js './index.vr.bundle?platform=vr&dev=false', // Attach it to the body tag document.body ); </script> </body> </html> Note for a production release, which means if you're pointing to a prebuilt bundle on a static web server and not the React Native bundler, the dev and platform flags actually won't do anything, so there's no difference between dev=true, dev=false, or even dev=foobar. Obtaining releases and attribution If you used any assets from anywhere on the web, ensure that you have the proper release. For example, many Daz3D or Poser models do not include the rights to publish the geometry information; including these on your website as an OBJ or glTF file may be a violation of that agreement. Someone could easily download the model, or nearly all the geometry fairly easily, and then use it for something else. I am not a lawyer; you should check with wherever you get your models to ensure that you have permission, and if necessary, attribute properly. Attribution licenses are a little difficult with a VR world, unless you embed the attribution into a graphic somewhere; as we've seen, adding text can sometimes be distracting, and you will always have scale issues. If you embed a VR world in a page with <iframe>, you can always give proper attribution on the HTML side. However, this isn't really VR. Checking image sizes and using content delivery sites Some of the images you use, especially the ones in a <pano> statement, can be quite large. You may need to optimize these for proper web speed and responsiveness. This is a fairly general topic, but one thing that can help is a content delivery network (CDN), especially if your world will be a high-volume one. Adding a CDN to your web server is easy. You host your asset files from a separate location, and you pass the root directory as the assetRoot at the ReactVR.init() call. For example, if your files were hosted at https://cdn.example.com/vr_assets/, you would change the method call in index.html to include the following third argument: ReactVR.init( './index.bundle.js?platform=vr&dev=false', document.body, { assetRoot: 'https://cdn.example.com/vr_assets/' } ); Optimizing your models If you were watching the web console, you may have noted this model being loaded over and over. It is not necessarily the most efficient way. Consider other techniques such as passing a model for the various child components as a prop. Polygon decimation is another technique that is very valuable in optimizing models for the web and VR. With the glTF file format, you can use "normal maps" and still make a low polygon model look like a high-resolution one. Techniques to do this are well documented in the game development field. These techniques really do work well. You should also optimize models to not display unseen geometry. If you are showing a car model with blacked out windows, for example, there is no need to have engine detail and interior details loaded (unless the windows are transparent). This sounds obvious, although I found the lamp that I used to illustrate the lighting examples had almost tripled the number of polygons than was needed; the glass lamp shade had inner and outer polygons that were inside the model. We learned to do version upgrades, and if need be, how to do rip and replace upgrades. We further discussed when to do an upgrade and how to publish it on the web. If you are interested to know about how to include existing high-performance web code into a VR app, you may refer to the book Getting Started with React VR.   Build a Virtual Reality Solar System in Unity for Google Cardboard Understanding the hype behind Magic Leap’s New Augmented Reality Headsets Leap Motion open sources its $100 augmented reality headset, North Star
Read more
  • 0
  • 0
  • 20134
article-image-what-to-expect-at-cloud-data-summit-2019-a-summit-hosted-in-the-cloud
Sugandha Lahoti
09 Oct 2019
2 min read
Save for later

What to expect at Cloud Data Summit 2019 - a summit hosted in the cloud

Sugandha Lahoti
09 Oct 2019
2 min read
2019’s Cloud Data Summit is quickly approaching (scheduled to take place on October 16th-17th). This year it will be different; it will be hosted online as a 100% virtual summit. This event will feature industry-leading speakers and thought leaders talking about the hype of AI, big data, machine learning, PaaS and IaaS technologies.  Although it will be a 100% virtual summit, this conference will have all the features of a standard conference - main stage discussions, speaker panels, peer networking sessions, roundtables, breakout sessions, and group lunches. All topics will be presented in a way that is comfortable for both technical and non-technically inclined attendees and will cover real-world implementations, how-to’s, best practices, potential pitfalls, and how to leverage the full potential of the cloud’s data and processing power. Attendees of the Cloud Data Summit include Google, Spotify, IBM, SAP, Microsoft, Apple and more.   Here’s a list of featured speakers: Jay Natarajan, US AI Lead, Microsoft, Lead Architect, Microsoft Dan Linstedt, Inventor of Data Vault Methodology CEO of LearnDataVault.com CTO and Co-Founder of Scalefree. T. Scott Clendaniel, Co-founder & Consultant, Cottrell Consulting Barr Moses, Co-founder & CEO of Monte Carlo Data Dr. Joe Perez, Sr. Systems Analyst, Team Lead NC Department of Health and Human Services Kurt Cagle, CEO of Semantical LLC, Contributor to Forbes and Managing Editor of Cognitive World Joshua Cottrell, Co-founder & Consultant Cottrell Consulting Jawad Sartaj, Chief Analytics Officer Somos Community Care Daniel O’Connor, Head of Product Data Practice Aware Web Solutions Inc. Eric Axelrod, Founder of Cloud Data Summit, President & Chief Architect, DIGR, and Executive Advisor Those individuals or organizations interested in learning more about the Cloud Data Summit or to register for attendance can visit their official website. If you fall in any of these categories - Business Executives, Data and IT Executives, Data Managers, Data Scientists, Data Engineers, Data Warehouse Architects  (so, anyone who is interested in learning about cloud migration and its consequences) -, Cloud Data Summit is not to be missed. For current students and new graduates, tickets are up to 80% off via the special student registration form.
Read more
  • 0
  • 0
  • 20109

article-image-unity-game-engine-assets-2d-game-development
Amarabha Banerjee
08 Aug 2018
9 min read
Save for later

Implementing Unity game engine and assets for 2D game development [Tutorial]

Amarabha Banerjee
08 Aug 2018
9 min read
The rise of mobile platforms has been in part thanks to its popularity with indie developers, who prefer the short development cycles. The most prevalent medium on mobile is 2D and Unity has a host of features that support 2D game development, including Sprite Editing and Packing, as well as physics specifically designed for 2D games. In this tutorial, we will look at creating Unity game engine and assets for 2D games. This article is an excerpt from the book Unity 2017 Game Development Essentials written by Tommaso Lintrami.  Setting up the scene and preparing game assets Create a new scene from the main menu by navigating to Assets | Create | Scene, and name it ParallaxGame. In this new scene, we will set up, step by step, all the elements for our 2D game prototype. First of all, we will switch the camera setting in the Scene view to 2D by clicking on the button as shown by the red arrow in the following screenshot: As you can see, now the Scene view camera is orthographic. You can't rotate it as you wish, as you can do with the 3D camera. Of course, we will want to change this setting on our Main Camera as well. Also, we want to change the Orthographic size to 4.5 to have the correct view of the scene. Instead, for the Skybox, we will choose a very dark or black color as clear color in the depth setting. This is how the Inspector should look when these settings are done: While the Clipping Planes distances are important for setting the size of the frustum cone of a 3D, for the Perspective camera (inside which everything will be rendered by the engine), we should only set the Orthographic Size to 4.5, to have the correct distance of the 2D camera from the scene. When these settings are done, proceed by importing Chapter2-3-4.unitypackage into the project. You can either double-click on the package file with Unity open, or use the top menu: Assets | Import | Custom Package. If you haven't imported all the materials from the book's code already, be sure to include the Sprites subfolder. After the import, look in the Sprites/Parallax/DarkCave folder in the Project view and you will find some images imported as textures (as per default). The first thing we want to do now is to change the import settings of these images, in the Inspector, from Texture to Sprite (2D and UI). To do so, select all the images in the Project view in the Sprites/Parallax/DarkCave folder, all except the _reference_main_post file. Which is just a picture used as a reference of what the game level should look like: The Import Settings shown in the Inspector after selecting the seven images in the Project view The Max Size setting is hidden (-) because we have a multi-selection of image files. After having made the multiple selections, again, in the Inspector, we will do the following: Set the Texture Type option to Sprites (2D and UI). By default, images are imported as textures; to import them as Sprites, this type must be set. Uncheck the Generate Mip Maps option as we don't need MIP maps for this project as we are not going to look at the Sprites from a distant point of view, for example, games with the zoom-in/zoom-out feature (like the original Grand Theft Auto 2D game) would need this setting checked. Set Max Size to the maximum allowed. To ensure that you import all the images at their maximum resolution, set this to 8192. This is the maximum resolution size for an image on a modern PC, imported as a Sprite or texture. We set it so high because most of the background images we have in the collection are around 6,000 pixels wide. Click on the Apply button to apply these changes to all the images that were selected: The Project view showing the content of the folder after the images have been set to Sprite in the Import Settings Placing the prefabs in the game Unity can place the prefabs in the game in many ways, the usual, visual method is to drag a stored prefab or another kind of file/object directly into the scene. Before dragging in the Sprites we imported, we will create an empty GameObject and rename it ParallaxCave. We will drag the layer images we just imported as Sprites, one by one, from the Project view (pointing at the Assets/Chapters2-3-4/Sprites/Background/DarkCave folder) into the Scene view, or more simply, directly in the Hierarchy view as the children of our ParallaxCave GameObject, resulting in a scene Hierarchy like the one illustrated here: You can't drag all of them instantly because Unity will prompt you to save an animation filename for the selected collection of Sprites; we will see this later for our character and for the collectable graphics. The ParallaxCave GameObject and its children are in blue because this GameObject is stored as a prefab. When the link with the prefab is broken for a modification, the GameObject in the Hierarchy will become black again. When you see a red GameObject in the scene, it means that the prefab file that was linked to that GameObject was deleted. Importing and placing background layers In any game engine, 2D elements, such as Sprites, are rendered following a sort order; this order is also called the z-order because it is a way to express the depth or to cope with the missing z axis in a two-dimensional context. The sort order is assigned an integer number which can be positive or negative; 0 is the middle point of this draw order. Ideally, a sort order of zero expresses the middle ground, where the player will act, or near its layer. Look at this image: All positive numbers will render the Sprite element in front of the other elements with a lower number. The graphic set we are going to use was taken from the Open Game Art website at http://opengameart.org. For simplicity, the provided background image files are named with a number within parentheses, for example, middleground(z1), which means that this image should be rendered with a z sort order of 1. Change the sort order property of the Sprite component on each child object under ParallaxCave according to the value in the parentheses at the end of their filenames. This will rearrange the graphics into the appropriately sorted order. After we place and set the correct layer order for all the images, we should arrange and scale the layers in a proper manner to end as something like the reference image furnished in the Assets/Chapters2-3-4/Sprites/Background/DarkCave/ folder. You can take a look at the final result for this part anytime, by saving the current scene and loading the Chapter3_start.unity scene. On the optimization side, Sprites can be packed together in a single atlas texture with the Sprite Packer into a single image atlas (a single image containing a whole group of Sprites). Implementing parallax scrolling Parallax scrolling is a graphic technique where the background content (that is, an image) is moved at a different speed than the foreground content while scrolling. The technique was derived from the multiplane camera technique used in traditional animation since the 1930s. Parallax scrolling was popular in the 1980s and early 1990s and started to see light with video games such as Moon Patrol and Jungle Hunt, both released in 1982. On such a display system, a game can produce parallax by simply changing each layer's position by a different amount in the same direction. Layers that move more quickly are perceived to be closer to the virtual camera. Layers can be placed in front of the playfield, the layer containing the objects with which the player interacts, for various reasons, such as to provide increased dimension, obscure some of the action of the game, or distract the player. Here follows a short list of the first parallax scrolling games which made the history of video games: Moon Patrol (Atari, 1982) https://youtu.be/HBOKWCpwGfM https://en.wikipedia.org/wiki/Moon_Patrol Shadow of the Beast (Psygnosis, 1989) https://youtu.be/w6Osnolfxqw https://en.wikipedia.org/wiki/Shadow_of_the_Beast Super Mario World (Nintendo, 1990) https://www.youtube.com/watch?v=htFJTiVH5Ao https://en.wikipedia.org/wiki/Super_Mario_World Sonic The Hedgehog (Sega, 1991) https://youtu.be/dws4ij2IFH4 https://en.wikipedia.org/wiki/Sonic_the_Hedgehog_(1991_video_game) Making it last forever There are many roads we could take to make the hero run last forever and to achieve parallax scrolling. You can find a lot of different ready-made solutions in the Asset Store and there are also many General Public License (GPL) open source pieces of code written in C that we could take inspiration from. Using the Asset Store I chose FreeParallax from the Asset Store because it is powerful, free, and a well-written piece of code. Also, the modifications needed to achieve our game prototype on this class are very few. Let's download and import the system from the Asset Store. First, navigate to http://u3d.as/bvv: Click on the Open in Unity button to allow Unity to open this entry in the Asset Store window. You can, alternatively, search for the package directly in Unity  by opening the store from the top menu: Windows | Asset Store (recommended). In the search box type: parallax; also choose FREE ONLY like in this screenshot: You should now find the correct entry, the Free Parallax for Unity(2D) package. You can now download the package and import it into your project straight away. We saw how to create Unity game engine and assets for 2D games. Check out the book Unity 2017 Game Development Essentials to know more ways of creating interactive 2D games. Unite Berlin 2018 Keynote: Unity partners with Google, launches Ml-Agents ToolKit 0.4, Project MARS AI for Unity game developers: How to emulate real-world senses in your NPC agent Working with Unity Variables to script powerful Unity 2017 games
Read more
  • 0
  • 0
  • 20093

article-image-using-classes
Packt
28 Nov 2014
26 min read
Save for later

Using Classes

Packt
28 Nov 2014
26 min read
In this article by Meir Bar-Tal and Jonathon Lee Wright, authors of Advanced UFT 12 for Test Engineers Cookbook, we will cover the following recipes: Implementing a class Implementing a simple search class Implementing a generic Login class Implementing function pointers (For more resources related to this topic, see here.) Introduction This article describes how to use classes in VBScript, along with some very useful and illustrative implementation examples. Classes are a fundamental feature of object-oriented programming languages such as C++, C#, and Java. Classes enable us to encapsulate data fields with the methods and properties that process them, in contrast to global variables and functions scattered in function libraries. UFT already uses classes, such as with reserved objects, and Test Objects are also instances of classes. Although elementary object-oriented features such as inheritance and polymorphism are not supported by VBScript, using classes can be an excellent choice to make your code more structured, better organized, and more efficient and reusable. Implementing a class In this recipe, you will learn the following: The basic concepts and the syntax required by VBScript to implement a class The different components of a class and interoperation How to implement a type of generic constructor function for VBScript classes How to use a class during runtime Getting ready From the File menu, navigate to New | Function Library…, or use the Alt + Shift + N shortcut. Name the new function library cls.MyFirstClass.vbs and associate it with your test. How to do it... We will build our MyFirstClass class from the ground up. There are several steps one must follow to implement a class; they are follows: Define the class as follows: Class MyFirstClass Next, we define the class fields. Fields are like regular variables, but encapsulated within the namespace defined by the class. The fields can be private or public. A private field can be accessed only by class members. A public field can be accessed from any block of code. The code is as follows: Class MyFirstClass Private m_sMyPrivateString Private m_oMyPrivateObject Public m_iMyPublicInteger End Class It is a matter of convention to use the prefix m_ for class member fields; and str for string, int for integer, obj for Object, flt for Float, bln for Boolean, chr for Character, lng for Long, and dbl for Double, to distinguish between fields of different data types. For examples of other prefixes to represent additional data types, please refer to sites such as https://en.wikipedia.org/wiki/Hungarian_notation. Hence, the private fields' m_sMyPrivateString and m_oMyPrivateObject will be accessible only from within the class methods, properties, and subroutines. The public field m_iMyPublicInteger will be accessible from any part of the code that will have a reference to an instance of the MyFirstClass class; and it can also allow partial or full access to private fields, by implementing public properties. By default, within a script file, VBScript treats as public identifiers such as function and subroutines and any constant or variable defined with Const and Dim respectively,, even if not explicitly defined. When associating function libraries to UFT, one can limit access to specific globally defined identifiers, by preceding them with the keyword Private. The same applies to members of a class, function, sub, and property. Class fields must be preceded either by Public or Private; the public scope is not assumed by VBScript, and failing to precede a field identifier with its access scope will result in a syntax error. Remember that, by default, VBScript creates a new variable if the explicit option is used at the script level to force explicit declaration of all variables in that script level. Next, we define the class properties. A property is a code structure used to selectively provide access to a class' private member fields. Hence, a property is often referred to as a getter (to allow for data retrieval) or setter (to allow for data change). A property is a special case in VBScript; it is the only code structure that allows for a duplicate identifier. That is, one can have a Property Get and a Property Let procedure (or Property Set, to be used when the member field actually is meant to store a reference to an instance of another class) with the same identifier. Note that Property Let and Property Set accept a mandatory argument. For example: Class MyFirstClass Private m_sMyPrivateString Private m_oMyPrivateObject    Public m_iMyPublicInteger  Property Get MyPrivateString()    MyPrivateString = m_sMyPrivateString End Property Property Let MyPrivateString(ByVal str)    m_sMyPrivateString = str End Property Property Get MyPrivateObject()    Set MyPrivateObject = m_oMyPrivateObject End Property Private Property Set MyPrivateObject(ByRef obj)    Set m_oMyPrivateObject = obj End Property End Class The public field m_iMyPublicInteger can be accessed from any code block, so defining a getter and setter (as properties are often referred to) for such a field is optional. However, it is a good practice to define fields as private and explicitly provide access through public properties. For fields that are for exclusive use of the class members, one can define the properties as private. In such a case, usually, the setter (Property Let or Property Set) would be defined as private, while the getter (Property Get) would be defined as public. This way, one can prevent other code components from making changes to the internal fields of the class to ensure data integrity and validity. Define the class methods and subroutines. A method is a function, which is a member of a class. Like fields and properties, methods (as well as subroutines) can be Private or Public. For example: Class MyFirstClass '… Continued Private Function MyPrivateFunction(ByVal str)    MsgBox TypeName(me) & " – Private Func: " & str    MyPrivateFunction = 0 End Function Function MyPublicFunction(ByVal str)    MsgBox TypeName(me) & " – Public Func: " & str    MyPublicFunction = 0 End Function Sub MyPublicSub(ByVal str)    MsgBox TypeName(me) & " – Public Sub: " & str End Sub End Class Keep in mind that subroutines do not return a value. Functions by design should not return a value, but they can be implemented as a subroutine. A better way is to, in any case, have a function return a value that tells the caller if it executed properly or not (usually zero (0) for no errors and one (1) for any fault). Recall that a function that is not explicitly assigned a value function and is not explicitly assigned a value, will return empty, which may cause problems if the caller attempts to evaluate the returned value. Now, we define how to initialize the class when a VBScript object is instantiated: Set obj = New MyFirstClass The Initialize event takes place at the time the object is created. It is possible to add code that we wish to execute every time an object is created. So, now define the standard private subroutine Class_Initialize, sometimes referred to (albeit only by analogy) as the constructor of the class. If implemented, the code will automatically be executed during the Initialize event. For example, if we add the following code to our class: Private Sub Class_Initialize MsgBox TypeName(me) & " started" End Sub Now, every time the Set obj = New MyFirstClass statement is executed, the following message will be displayed: Define how to finalize the class. We finalize a class when a VBScript object is disposed of (as follows), or when the script exits the current scope (such as when a local object is disposed when a function returns control to the caller), or a global object is disposed (when UFT ends its run session): Set obj = Nothing The Finalize event takes place at the time when the object is removed from memory. It is possible to add code that we wish to execute, every time an object is disposed of. If so, then define the standard private subroutine Class_Terminate, sometimes referred to (albeit only by analogy) as the destructor of the class. If implemented, the code will automatically be executed during the Finalize event. For example, if we add the following code to our class: Private Sub Class_Terminate MsgBox TypeName(me) & " ended" End Sub Now, every time the Set obj = Nothing statement is executed, the following message will be displayed: Invoking (calling) a class method or property is done as follows: 'Declare variables Dim obj, var 'Calling MyPublicFunction obj.MyPublicFunction("Hello") 'Retrieving the value of m_sMyPrivateString var = obj.MyPrivateString 'Setting the value of m_sMyPrivateString obj.MyPrivateString = "My String" Note that the usage of the public members is done by using the syntax obj.<method or property name>, where obj is the variable holding the reference to the object of class. The dot operator (.) after the variable identifier provides access to the public members of the class. Private members can be called only by other members of the class, and this is done like any other regular function call. VBScript supports classes with a default behavior. To utilize this feature, we need to define a single default method or property that will be invoked every time an object of the class is referred to, without specifying which method or property to call. For example, if we define the public method MyPublicFunction as default: Public Default Function MyPublicFunction(ByVal str) MsgBox TypeName(me) & " – Public Func: " & str MyPublicFunction = 0 End Function Now, the following statements would invoke the MyPublicFunction method implicitly: Set obj = New MyFirstClass obj("Hello") This is exactly the same as if we called the MyPublicFunction method explicitly: Set obj = New MyFirstClass obj.MyPublicFunction("Hello") Contrary to the usual standard for such functions, a default method or property must be explicitly defined as public. Now, we will see how to add a constructor-like function. When using classes stored in function libraries, UFT (know as QTP in previous versions), cannot create an object using the New operator inside a test Action. In general, the reason is linked to the fact that UFT uses a wrapper on top of WSH, which actually executes the VBScript (VBS 5.6) code. Therefore, in order to create instances of such a custom class, we need to use a kind of constructor function that will perform the New operation from the proper memory namespace. Add the following generic constructor to your function library: Function Constructor(ByVal sClass) Dim obj On Error Resume Next 'Get instance of sClass Execute "Set obj = New [" & sClass & "]" If Err.Number <> 0 Then    Set obj = Nothing      Reporter.ReportEvent micFail, "Constructor", "Failed      to create an instance of class '" & sClass & "'." End If Set Constructor = obj End Function We will then instantiate the object from the UFT Action, as follows: Set obj = Constructor("MyFirstClass") Consequently, use the object reference in the same fashion as seen in the previous line of code: obj.MyPublicFunction("Hello") How it works... As mentioned earlier, using the internal public fields, methods, subroutines, and properties is done using a variable followed by the dot operator and the relevant identifier (for example, the function name). As to the constructor, it accepts a string with the name of a class as an argument, and attempts to create an instance of the given class. By using the Execute command (which performs any string containing valid VBScript syntax), it tries to set the variable obj with a new reference to an instance of sClass. Hence, we can handle any custom class with this function. If the class cannot be instantiated (for instance, because the string passed to the function is faulty, the function library is not associated to the test, or there is a syntax error in the function library), then an error would arise, which is gracefully handled by using the error-handling mechanism, leading to the function returning nothing. Otherwise, the function will return a valid reference to the newly created object. See also The following articles at www.advancedqtp.com are part of a wider collection, which also discuss classes and code design in depth: An article by Yaron Assa at http://www.advancedqtp.com/introduction-to-classes An article by Yaron Assa at http://www.advancedqtp.com/introduction-to-code-design An article by Yaron Assa at http://www.advancedqtp.com/introduction-to-design-patterns Implementing a simple search class In this recipe, we will see how to create a class that can be used to execute a search on Google. Getting ready From the File menu, navigate to New | Test, and name the new test SimpleSearch. Then create a new function library by navigating to New | Function Library, or use the Alt + Shift + N shortcut. Name the new function library cls.Google.vbs and associate it with your test. How to do it... Proceed with the following steps: Define an environment variable as OPEN_URL. Insert the following code in the new library: Class GoogleSearch Public Function DoSearch(ByVal sQuery)    With me.Page_      .WebEdit("name:=q").Set sQuery      .WebButton("html id:=gbqfba").Click    End With    me.Browser_.Sync     If me.Results.WaitProperty("visible", 1, 10000) Then      DoSearch = GetNumResults()    Else      DoSearch = 0      Reporter.ReportEvent micFail, TypeName(Me),        "Search did not retrieve results until timeout"    End If End Function Public Function GetNumResults()    Dim tmpStr    tmpStr = me.Results.GetROProperty("innertext")    tmpStr = Split(tmpStr, " ")    GetNumResults = CLng(tmpStr(1)) 'Assumes the number      is always in the second entry End Function Public Property Get Browser_()    Set Browser_ = Browser(me.Title) End Property Public Property Get Page_()    Set Page_ = me.Browser_.Page(me.Title) End Property Public Property Get Results()    Set Results = me.Page_.WebElement(me.ResultsId) End Property Public Property Get ResultsId()    ResultsId = "html id:=resultStats" End Property Public Property Get Title()    Title = "title:=.*Google.*" End Property Private Sub Class_Initialize    If Not me.Browser_.Exist(0) Then      SystemUtil.Run "iexplore.exe",        Environment("OPEN_URL")      Reporter.Filter = rfEnableErrorsOnly      While Not Browser_.Exist(0)        Wait 0, 50      Wend      Reporter.Filter = rfEnableAll      Reporter.ReportEvent micDone, TypeName(Me),        "Opened browser"    Else      Reporter.ReportEvent micDone, TypeName(Me),        "Browser was already open"    End If End Sub Private Sub Class_Terminate    If me.Browser_.Exist(0) Then      me.Browser_.Close      Reporter.Filter = rfEnableErrorsOnly      While me.Browser_.Exist(0)        wait 0, 50      Wend      Reporter.Filter = rfEnableAll      Reporter.ReportEvent micDone, TypeName(Me),        "Closed browser"    End If End Sub End Class In Action, write the following code: Dim oGoogleSearch Dim oListResults Dim oDicSearches Dim iNumResults Dim sMaxResults Dim iMaxResults '--- Create these objects only in the first iteration If Not LCase(TypeName(oListResults)) = "arraylist" Then Set oListResults =    CreateObject("System.Collections.ArrayList") End If If Not LCase(TypeName(oDicSearches)) = "Dictionary" Then Set oDicSearches = CreateObject("Scripting.Dictionary") End If '--- Get a fresh instance of GoogleSearch Set oGoogleSearch = GetGoogleSearch() '--- Get search term from the DataTable for each action iteration sToSearch = DataTable("Query", dtLocalSheet) iNumResults = oGoogleSearch.DoSearch(sToSearch) '--- Store the results of the current iteration '--- Store the number of results oListResults.Add iNumResults '--- Store the search term attached to the number of results as key (if not exists) If Not oDicSearches.Exists(iNumResults) Then oDicSearches.Add iNumResults, sToSearch End If 'Last iteration (assuming we always run on all rows), so perform the comparison between the different searches If CInt(Environment("ActionIteration")) = DataTable.LocalSheet.GetRowCount Then 'Sort the results ascending oListResults.Sort 'Get the last item which is the largest iMaxResults = oListResults.item(oListResults.Count-1) 'Print to the Output pane for debugging Print iMaxResults 'Get the search text which got the most results sMaxResults = oDicSearches(iMaxResults) 'Report result Reporter.ReportEvent micDone, "Max search", sMaxResults    & " got " & iMaxResults 'Dispose of the objects used Set oListResults = Nothing Set oDicSearches = Nothing Set oGoogleSearch = Nothing End If In the local datasheet, create a parameter named Query and enter several values to be used in the test as search terms. Next, from the UFT home page navigate to View | Test Flow, and then right-click with the mouse on the Action component in the graphic display, then select Action Call Properties and set the Action to run on all rows. How it works... The Action takes care to preserve the data collected through the iterations in the array list oListResults and the dictionary oDicSearches. It checks if it reaches the last iteration after each search is done. Upon reaching the last iteration, it analyses the data to decide which term yielded the most results. A more detailed description of the workings of the code can be seen as follows. First, we create an instance of the GoogleSearch class, and the Class_Initialize subroutine automatically checks if the browser is not already open. If not open, Class_Initialize opens it with the SystemUtil.Run command and waits until it is open at the web address defined in Environment("OPEN_URL"). The Title property always returns the value of the Descriptive Programming (DP) value required to identify the Google browser and page. The Browser_, Page_, and Results properties always return a reference to the Google browser, page, and WebElement respectively, which hold the text with the search results. After the browser is open, we retrieve the search term from the local DataTable parameter Query and call the GoogleSearch DoSearch method with the search term string as parameter. The DoSearch method returns a value with the number of results, which are given by the internal method GetNumResults. In the Action, we store the number itself and add to the dictionary, an entry with this number as the key and the search term as the value. When the last iteration is reached, an analysis of the results is automatically done by invoking the Sort method of oListResults ArrayList, getting the last item (the greatest), and then retrieving the search term associated with this number from the dictionary; it reports the result. At last, we dispose off all the objects used, and then the Class_Terminate subroutine automatically checks if the browser is open. If open, then the Class_Terminate subroutine closes the browser. Implementing a generic Login class In this recipe, we will see how to implement a generic Login class. The class captures both, the GUI structure and the processes that are common to all applications with regard to their user access module. It is agnostic to the particular object classes, their technologies, and other identification properties. The class shown here implements the command wrapper design pattern, as it encapsulates a process (Login) with the main default method (Run). Getting ready You can use the same function library cls.Google.vbs as in the previous recipe Implementing a simple search class, or create a new one (for instance, cls.Login.vbs) and associate it with your test. How to do it... In the function library, we will write the following code to define the class Login: Class Login Private m_wndContainer 'Such as a Browser, Window,    SwfWindow Private m_wndLoginForm 'Such as a Page, Dialog,    SwfWindow Private m_txtUsername 'Such as a WebEdit, WinEdit,    SwfEdit Private m_txtIdField 'Such as a WebEdit, WinEdit,    SwfEdit Private m_txtPassword 'Such as a WebEdit, WinEdit,    SwfEdit Private m_chkRemember 'Such as a WebCheckbox,    WinCheckbox, SwfCheckbox Private m_btnLogin   'Such as a WebEdit, WinEdit,    SwfEdit End Class These fields define the test objects, which are required for any Login class, and the following fields are used to keep runtime data for the report: Public Status 'As Integer Public Info 'As String The Run function is defined as a Default method that accepts a Dictionary as argument. This way, we can pass a set of named arguments, some of which are optional, such as timeout. Public Default Function Run(ByVal ArgsDic)    'Check if the timeout parameter was passed, if not      assign it 10 seconds    If Not ArgsDic.Exists("timeout") Then ArgsDic.Add      "timeout", 10    'Check if the client window exists    If Not me.Container.Exist(ArgsDic("timeout")) Then      me.Status = micFail      me.Info   = "Failed to detect login        browser/dialog/window."      Exit Function    End If    'Set the Username    me.Username.Set ArgsDic("Username")    'If the login form has an additional mandatory field    If me.IdField.Exist(ArgsDic("timeout")) And      ArgsDic.Exists("IdField") Then      me.IdField.Set ArgsDic("IdField")    End If    'Set the password    me.Password.SetSecure ArgsDic("Password")    'It is a common practice that Login forms have a      checkbox to keep the user logged-in if set ON    If me.Remember.Exist(ArgsDic("timeout")) And      ArgsDic.Exists("Remember") Then      me.Remember.Set ArgsDic("Remember")    End If    me.LoginButton.Click End Function The Run method actually performs the login procedure, setting the username and password, as well as checking or unchecking the Remember Me or Keep me Logged In checkbox according to the argument passed with the ArgsDic dictionary. The Initialize method accepts Dictionary just like the Run method. However, in this case, we pass the actual TOs with which we wish to perform the login procedure. This way, we can actually utilize the class for any Login form, whatever the technology used to develop it. We can say that the class is technology agnostic. The parent client dialog/browser/window of the objects is retrieved using the GetTOProperty("parent") statement: Function Initialize(ByVal ArgsDic)    Set m_txtUsername = ArgsDic("Username")    Set m_txtIdField = ArgsDic("IdField")    Set m_txtPassword = ArgsDic("Password")    Set m_btnLogin   = ArgsDic("LoginButton")    Set m_chkRemember = ArgsDic("Remember")    'Get Parents    Set m_wndLoginForm =      me.Username.GetTOProperty("parent")    Set m_wndContainer =      me.LoginForm.GetTOProperty("parent") End Function In addition, here you can see the following properties used in the class for better readability: Property Get Container()    Set Container = m_wndContainer End Property Property Get LoginForm()    Set LoginForm = m_wndLoginForm End Property Property Get Username()    Set Username = m_txtUsername End Property Property Get IdField()    Set IdField = m_txtIdField End Property Property Get Password()    Set Password = m_txtPassword End Property Property Get Remember()    Set Remember = m_chkRemember End Property Property Get LoginButton()  Set LoginButton = m_btnLogin End Property Private Sub Class_Initialize()    'TODO: Additional initialization code here End Sub Private Sub Class_Terminate()    'TODO: Additional finalization code here End Sub We will also add a custom function to override the WinEdit and WinEditor Type methods: Function WinEditSet(ByRef obj, ByVal str) obj.Type str End Function This way, no matter which technology the textbox belongs to, the Set method will work seamlessly. To actually test the Login class, write the following code in the Test Action (this time we assume that the Login form was already opened by another procedure): Dim ArgsDic, oLogin 'Register the set method for the WinEdit and WinEditor RegisterUserFunc "WinEdit", "WinEditSet", "Set" RegisterUserFunc "WinEditor", "WinEditSet", "Set" 'Create a Dictionary object Set ArgsDic = CreateObject("Scripting.Dictionary") 'Create a Login object Set oLogin = New Login 'Add the test objects to the Dictionary With ArgsDic .Add "Username",    Browser("Gmail").Page("Gmail").WebEdit("txtUsername") .Add "Password",    Browser("Gmail").Page("Gmail").WebEdit("txtPassword") .Add "Remember",    Browser("Gmail").Page("Gmail")    .WebCheckbox("chkRemember") .Add "LoginButton",    Browser("Gmail").Page("Gmail").WebButton("btnLogin") End With 'Initialize the Login class oLogin.Initialize(ArgsDic) 'Initialize the dictionary to pass the arguments to the login ArgsDic.RemoveAll With ArgsDic .Add "Username", "myuser" .Add "Password", "myencriptedpassword" .Add "Remember", "OFF" End With 'Login oLogin.Run(ArgsDic) 'or: oLogin(ArgsDic) 'Report result Reporter.ReportEvent oLogin.Status, "Login", "Ended with " & GetStatusText(oLogin.Status) & "." & vbNewLine & oStatus.Info 'Dispose of the objects Set oLogin = Nothing Set ArgsDic = Nothing How it works... Here we will not delve into the parts of the code already explained in the Implementing a simple search class recipe. Let's see what we did in this recipe: We registered the custom function WinEditSet to the WinEdit and WinEditor TO classes using RegisterUserFunc. As discussed previously, this will make every call to the method set to be rerouted to our custom function, resulting in applying the correct method to the Standard Windows text fields. Next, we created the objects we need, a Dictionary object and a Login object. Then, we added the required test objects to Dictionary, and then invoked its Initialize method, passing the Dictionary as the argument. We cleared Dictionary and then added to it the values needed for actually executing the login (Username, Password, and the whether to remember the user or keep logged in checkboxes usually used in Login forms). We called the Run method for the Login class with the newly populated Dictionary. Later, we reported the result by taking the Status and Info public fields from the oLogin object. At the end of the script, we unregistered the custom function from all classes in the environment (StdWin in this case). Implementing function pointers What is a function pointer? A function pointer is a variable that stores the memory address of a block of code that is programmed to fulfill a specific function. Function pointers are useful to avoid complex switch case structures. Instead, they support direct access in runtime to previously loaded functions or class methods. This enables the construction of callback functions. A callback is, in essence, an executable code that is passed as an argument to a function. This enables more generic coding, by having lower-level modules calling higher-level functions or subroutines. This recipe will describe how to implement function pointers in VBScript, a scripting language that does not natively support the usage of pointers. Getting ready Create a new function library (for instance, cls.FunctionPointers.vbs) and associate it with your test. How to do it... Write the following code in the function library: Class WebEditSet    Public Default Function Run(ByRef obj, ByVal sText)        On Error Resume Next         Run = 1 'micFail (pessimistic initialization)        Select Case True            Case   obj.Exist(0) And _                    obj.GetROProperty("visible") And _                    obj.GetROProperty("enabled")                              'Perform the set operation                    obj.Set(sText)                              Case Else                Reporter.ReportEvent micWarning,                  TypeName(me), "Object not available."              Exit Function        End Select        If Err.Number = 0 Then            Run = 0 'micPass        End If    End Function End Class Write the following code in Action: Dim pFunctiontion Set pFunctiontion = New WebEditSet Reporter.ReportEvent pFunctiontion(Browser("Google").Page("Google") .WebEdit("q"), "UFT"), "Set the Google Search WebEdit", "Done" How it works... The WebEditSet class actually implements the command wrapper design pattern (refer to also the Implementing a generic Login class recipe). This recipe also demonstrates an alternative way of overriding any native UFT TO method without recurring to the RegisterUserFunc method. First, we create an instance of the WebEditSet class and set the reference to our pFunctiontion variable. Note that the Run method of WebEditSet is declared as a default function, so we can invoke its execution by merely referring to the object reference, as is done with the statement pFunctiontion in the last line of code in the How to do it… section. This way, pFunctiontion actually functions as if it were a function pointer. Let us take a close look at the following line of code, beginning with Reporter.ReportEvent: Reporter.ReportEvent pFunc(Browser("Google").Page("Google").WebEdit("q"), "UFT"), "Set the Google Search WebEdit", "Done" We call the ReportEvent method of Reporter, and as its first parameter, instead of a status constant such as micPass or micFail, we pass pFunctiontion and the arguments accepted by the Run method (the target TO and its parameter, a string). This way of using the function pointer actually implements a kind of callback. The value returned by the Run method of WebEditSet will determine whether UFT will report a success or failure in regard to the Set operation. It will return through the call invoked by accessing the function pointer. See also The following articles are part of a wider collection at www.advancedqtp.com, which also discusses function pointers in depth: An article by Meir Bar-Tal at http://www.advancedqtp.com/ function-pointers-in-vb-script-revised An article by Meir Bar-Tal at http://www.advancedqtp.com/using-to-custom-property-as-function-pointer Summary In this article, we learned how to implement a general class; basic concepts and the syntax required by VBScript to implement a class. Then we saw how to implement a simple class that can be used to execute a search on Google and a generic Login class. We also saw how to implement function pointers in VBScript along with various links to the articles that discusses function pointers. Resources for Article: Further resources on this subject: DOM and QTP [Article] Getting Started with Selenium Grid [Article] Quick Start into Selenium Tests [Article]
Read more
  • 0
  • 0
  • 20092
article-image-mips-open-sourced-under-mips-open-program-makes-the-semiconductor-space-and-soc-ones-to-watch-for-in-2019
Melisha Dsouza
18 Dec 2018
4 min read
Save for later

MIPS open sourced under ‘MIPS Open Program’, makes the semiconductor space and SoC, ones to watch for in 2019

Melisha Dsouza
18 Dec 2018
4 min read
On 17th December, Wave Computing announced that it will put MIPS on open source, with MIPS Instruction Set Architecture (ISA) and MIPS’ latest core R6 to be made available in the first quarter of 2019. With a vision to “accelerate the ability for semiconductor companies, developers and universities to adopt and innovate using MIPS for next-generation system-on-chip (SoC) designs”, Wave computings’ MIPS Open program will give participants full access to the most recent versions of the 32-bit and 64-bit MIPS ISA free of charge, without any licensing or royalty fees. Under this program, participants will have full access to the most recent versions of the 32-bit and 64-bit MIPS ISA free of charge – with no licensing or royalty fees. Additionally, participants in the MIPS Open program will be licensed under MIPS’ existing worldwide patents. Addressing the “lack of open source access to true industry-standard, patent-protected and silicon-proven RISC architectures”, Art Swift, president of Wave Computing’s MIPS IP Business claims that MIPS will bring to the open-source community “commercial-ready” instruction sets with “industrial-strength” architecture, where “Chip designers will have opportunities to design their own cores based on proven and well-tested instruction sets for any purposes.” Lee Flanagin, Wave’s senior vice president and chief business officer further added in the post that the MIPS Open initiative is a key part of Wave’s ‘AI for All’ vision. He says that “The MIPS-based solutions developed under MIPS Open will complement our existing and future MIPS IP cores that Wave will continue to create and license globally as part of our overall portfolio of systems, solutions and IP. This will ensure current and new MIPS customers will have a broad array of solutions from which to choose for their SoC designs, and will also have access to a vibrant MIPS development community and ecosystem.” The MIPS Open initiative further will encourage the adoption of MIPS while helping customers develop new, MIPS-compatible solutions for a variety of emerging market applications from third-party tool vendors, software developers and universities. RISC-V versus MIPS? Considering that the RISC-V instruction set architecture is also free and open for anyone to use,  the internet went abuzz with speculations about competition between RISC-V and MIPS and the potential future of both. Hacker news also saw comments like: “Had this happened two or three years ago, RISC-V would have never been born.” In an interview to EE Times, Rupert Baines, CEO of UltraSoC, said that “Given RISC-V’s momentum, MIPS going open source is an interesting, shrewd move.”  He observed, “MIPS already has a host of quality tools and software environment. This is a smart way to amplify MIPS’ own advantage, without losing much.” Linley Gwennap, principal analyst at the Linley Group compared the two chips and stated that, “The MIPS ISA is more complete than RISC-V. For example, it includes DSP and SIMD extensions, which are still in committee for RISC-V.”. Calling the MIPS software development tools more mature than RISC-V, he went on to list down the benefits of MIPS over RISC: “MIPS also provides patent protection and a central authority to avoid ISA fragmentation, both of which RISC-V lacks. These factors give MIPS an advantage for commercial implementations, particularly for customer-facing cores.” Hacker News and Twitter are bustling with comments on this move by Wave computing. Opinions are split over which architecture is more preferable to use. For the most part, customers appear excited about this news. https://twitter.com/corkmork/status/1074857920293027840 https://twitter.com/plessl/status/1074778310025076736 You can head over to Wave Computing’s official blog to know more about this announcement. The Linux and RISC-V foundations team up to drive open source development and adoption of RISC-V instruction set architecture (ISA) Arm releases free Cortex-M processor cores for FPGAs, includes measures to combat FOSSi threat SpectreRSB targets CPU return stack buffer, found on Intel, AMD, and ARM chipsets  
Read more
  • 0
  • 0
  • 20089

Packt
11 Sep 2013
15 min read
Save for later

Get Connected – Bluetooth Basics

Packt
11 Sep 2013
15 min read
(For more resources related to this topic, see here.) Why Bluetooth? There are other forms of wireless communication that we could use, like infrared and Wi-Fi, but Bluetooth is perfect for many household projects. It is cheap, very easy to set up, will typically use less power than Wi-Fi because of the shorter range, and it's very responsive. It's important to keep in mind that there isn't a single "best" form of communication. Each type will suit each project (or perhaps budget) in different ways. In terms of performance, I have found that a short message will be transmitted in under 20 milliseconds from one device to another, and the signal will work for just less than 10 meters (30 feet). These numbers, however, will vary based on your environment. Things you need The things required for this project are as follows: Netduino Breadboard Bluetooth module Windows Phone 8 Lots of different Bluetooth modules exist, but I have found that the JY-MCU is very cheap (around $10) and reliable. Any Windows Phone 8 device can be used, as they all have Bluetooth. The project setup The setup for this project is extremely basic because we are just connecting the Bluetooth module and nothing else. Once our phone is connected we will use it to control the onboard LED, however, you can expand this to control anything else too. The Bluetooth module you buy may look slightly different to the diagram, but not to worry, just make sure you match up the labels on the Bluetooth module (GND, 3-3V or VCC, TX, and RX) to the diagram. If you encounter a situation where everything is hooked up right but no data is flowing, examine the minimum baud rate in your Bluetooth module's manual or specifications sheet. It has been reported that some Bluetooth modules do not work well communicating at 9600 baud. This can be easily remedied by setting the baud rate in your SerialPort's constructor to 115200. For example, SerialPort(new SerialPort(SerialPorts.COM1, 115200, Parity.None, 8, StopBits.One). Once it is wired up, we can get onto the coding. First we will do the Netduino part. The Netduino will listen for messages over Bluetooth, and will set the brightness of the onboard LED based on the percentage it receives. The Netduino will also listen for "ping", and if it receives this then it will send the same text back to the other device. We do this as an initial message to make sure that it gets from the phone to the Netduino, and then back to the phone successfully. After that we will code the phone application. The phone will connect, send a "ping", and then wait until it receives the "ping" back. When the phone receives the "ping" back then it can start sending messages. In this article only Windows Phone 8 will be covered, however, the same concepts apply, and it won't be too hard to code the equivalent app for another platform. The Netduino code will remain the same no matter what device you connect to. Coding Because we will be using a phone to connect to the Netduino, there are two distinct parts which need to be coded. The Netduino code Open up Visual Studio and create a new Netduino Plus 2 Application. Add a reference to SecretLabs.NETMF.Hardware.PWM. Open Program.cs from the Solution Explorer. You need to add the following using statements at the top: using System.IO.Ports;using System.Text;using NPWM = SecretLabs.NETMF.Hardware.PWM; You need to get the phone paired with the Bluetooth module on the Netduino. So in Program.cs, replace the Main method with this: private static SerialPort _bt;public static void Main(){_bt = new SerialPort(SerialPorts.COM1, 9600,Parity.None, 8, StopBits.One);_bt.Open();while (true){Thread.Sleep(Timeout.Infinite);}} This code creates a new instance of a SerialPort (the Bluetooth module), then opens it, and finally has a loop (which will just pause forever). Plug in your Netduino and run the code. Give it a few seconds until the blue light goes off—at this point the Bluetooth module should have a flashing red LED. On your Windows Phone, go to Settings | Bluetooth and make sure that it is turned on. In the list of devices there should be one which is the Bluetooth module (mine is called "linvor") so tap it to connect. If it asks for a pin try the default of "1234", or check the data sheet. As it connects, the red LED on the Bluetooth module will go solid, meaning that it is connected. It will automatically disconnect in 10 seconds; that's fine. Now that you've checked that it connects correctly, start adding in the real code: private static SerialPort _bt;private static NPWM _led;private static string _buffer;public static void Main(){_led = new NPWM(Pins.ONBOARD_LED);_bt = new SerialPort(SerialPorts.COM1, 9600,Parity.None, 8, StopBits.One);_bt.DataReceived += new SerialDataReceivedEventHandler(rec_DataReceived);_bt.Open();while (true){Thread.Sleep(Timeout.Infinite);}} This is close to the code you replaced but also creates an instance of the onboard LED, and declares a string to use as a buffer for the received data. Next you need to create the event handler that will be fired when data is received. Something that can easily trip you up is thinking that each message will come through as a whole. That's incorrect. So if you send a "ping" from your phone, it will usually come through in two separate messages of "p" and "ing". The simplest way to work around that is to just have a delimiter that marks the end of a message (in the same way as military personnel end radio communications by saying "10-4"). So send the message as "ping|" with a pipe at the end. This code for the DataReceived event handler builds up a buffer until it finds a pipe (|), then processes the message, then resets the buffer (or sets it to whatever is after the pipe, which will be the first part of the next message): private static void rec_DataReceived(object sender,SerialDataReceivedEventArgs e){byte[] bytes = new byte[_bt.BytesToRead];_bt.Read(bytes, 0, bytes.Length);char[] converted = new char[bytes.Length];for (int b = 0; b < bytes.Length; b++){converted[b] = (char)bytes[b];}string str = new String(converted);if (str != null && str.Length > 0){if (str.IndexOf("|") > -1){_buffer += str.Substring(0, str.IndexOf("|"));ProcessReceivedString(_buffer);_buffer = str.Substring(str.LastIndexOf("|") +1);}else{_buffer += str;}}} At the start of the event handler, you create a byte array to hold the received data, then loop through that array and convert each byte to a char and put those chars into a char array. Once you have a char array, create a new string using the char array as a parameter, which will give the string representation of the array. After checking that it is not null or empty you check whether it has a pipe (meaning it contains the end of a message). If so, add all the characters up to the pipe onto the buffer and then process the buffer. If there is no pipe then just add to the buffer. The only thing that remains is the method to process the received string (the buffer) and a method to send messages back to the phone. So put these methods below the event handler that you just added: private static void ProcessReceivedString(string _buffer){if (_buffer == "ping"){Write(_buffer);}else{uint val = UInt32.Parse(_buffer);_led.SetDutyCycle(val);}}private static void Write(string message){byte[] bytes = Encoding.UTF8.GetBytes(message + "|");_bt.Write(bytes, 0, bytes.Length);} As mentioned before, if you receive a "ping" then just send it back, or alternatively convert the string into an unsigned integer and set the brightness of the onboard LED. The last method simply adds a pipe to the end of the string, converts it to a byte array, then writes it to the Bluetooth SerialPort to send to the phone. At this point, you should run the code on the Netduino, but keep in mind that the same thing as before will happen because we are not sending it any data yet. So next up, we need to make the phone application that helps us send messages to the Netduino. The phone code As mentioned, we will be using a Windows Phone 8 device to connect to the Netduino. The same principles demonstrated in this section will apply to any platform, and it all revolves around just knowing how to read and write the Bluetooth data. You may notice that much of the phone code resembles the Netduino code—this is because both are merely sending and receiving messages. Before moving on, you will need the Windows Phone 8 SDK installed. Download and install it from here: http://developer.windowsphone.com You may need to close any copies of Visual Studio that are open. Once it is installed you can go ahead and open the Netduino project (from the previous section) again, then follow these steps: We could create the phone project in the same solution as the Netduino project, but in terms of debugging, it's easier to have them in separate instances of Visual Studio. So open up another copy of Visual Studio and click on File | New | Project. Find the Windows Phone App template by navigating to Installed | Templates | Visual C# | Windows Phone. Name the project and then click on OK to create it. A dialog may appear asking you to choose which version of the OS you would like to target. Make sure that Windows Phone OS 8.0 is selected (Windows Phone 7.1 does not have the required APIs for third party developers). When creating a new Windows Phone application, MainPage.xaml will automatically be displayed. This is the first page of the app that the user will see when they run your app. XAML is the layout language used on Windows Phone, and if you've ever used HTML then you will be quite at home. In the XAML window, scroll down until you find the grid named ContentPanel. Replace it with: <Grid x_Name="ContentPanel" Grid.Row="1"Margin="12,0,12,0"><Slider IsEnabled="False" Minimum="0" Maximum="100"x:Name="slider" ValueChanged="slider_ValueChanged"/></Grid> This will add a Slider control to the page with the value at the far left being 0 and the far right being 100—essentially a percent. Whenever the user drags the slider, it will fire the ValueChanged event handler, which you will add soon. That is the only UI change you need to make. So in the Solution Explorer, right-click on MainPage.xaml | View Code. Add these Using statements to the top: using Windows.Storage.Streams;using System.Text;using Windows.Networking.Sockets;using Windows.Networking.Proximity;using System.Runtime.InteropServices.WindowsRuntime; We need to declare some variables, so replace the MainPage constructor with this: StreamSocket _socket;string _receivedBuffer = "";bool _isConnected = false;public MainPage(){InitializeComponent();TryConnect();}private void slider_ValueChanged(object sender,RoutedPropertyChangedEventArgs<double> e){if (_isConnected){Write(((int)slider.Value).ToString());}}async private void Write(string str){var dataBuffer = GetBufferFromByteArray(Encoding.UTF8.GetBytes(str + "|"));await _socket.OutputStream.WriteAsync(dataBuffer);}private IBuffer GetBufferFromByteArray(byte[] package){using (DataWriter dw = new DataWriter()){dw.WriteBytes(package);return dw.DetachBuffer();}} The StreamSocket is essentially a way to interact with the phone's Bluetooth chip, which will be used in multiple methods in the app. When the slider's value changes, we check that the phone is connected to the Netduino, and then use the Write method to send the value. The Write method is similar to the one we made on the Netduino, except it requires a few lines extra to convert the byte array into an IBuffer. In the previous step, you might have noticed that we ran a method called TryConnect in the MainPage constructor. As you may have guessed, in this method we will try to connect to the Netduino. Add this method below the ones you added previously: private async void TryConnect(){PeerFinder.AlternateIdentities["Bluetooth:Paired"] ="";var pairedDevices = await PeerFinder.FindAllPeersAsync();if (pairedDevices.Count == 0){MessageBox.Show("Make sure you pair the devicefirst.");}else{SystemTray.SetProgressIndicator(this,new ProgressIndicator { IsIndeterminate = true,Text = "Connecting", IsVisible = true });PeerInformation selectedDevice = pairedDevices[0];_socket = new StreamSocket();await _socket.ConnectAsync(selectedDevice.HostName, "1");WaitForData(_socket);Write("ping");}} We first get a list of all devices that have been paired with the phone (even if they are not currently connected), and display an error message if there are no devices. If it does find one or more devices, then we display a progress bar at the top of the screen (in the SystemTray) and proceed to connect to the first Bluetooth device in the list. It is important to note that in the example code we are connecting to the first device in the list—in a real-world app, you would display the list to the user and let them decide which is the right device. After connecting, we call a method to wait for data to be received (this will happen in the background and will not block the rest of the code), and then write the initial ping message. Don't worry, we are almost there! The second last method you need to add is the one that will wait for the data to be received. It is an asynchronous method, which means that it can have a line within it that blocks execution (for instance, in the following code the line that waits for data will block the thread), but the rest of the app will carry on fine. Add in this method: async private void WaitForData(StreamSocket socket){try{byte[] bytes = new byte[5];await socket.InputStream.ReadAsync(bytes.AsBuffer(), 5, InputStreamOptions.Partial);bytes = bytes.TakeWhile((v, index) =>bytes.Skip(index).Any(w => w != 0x00)).ToArray();string str = Encoding.UTF8.GetString(bytes, 0,bytes.Length);if (str.Contains("|")){_receivedBuffer += str.Substring(0,str.IndexOf("|"));DoSomethingWithReceivedString(_receivedBuffer);_receivedBuffer = str.Substring(str.LastIndexOf("|") + 1);}else{_receivedBuffer += str;}}catch{MessageBox.Show("There was a problem");}finally{WaitForData(socket);}} Yes, this code looks complicated, but it is simple enough to understand. First we create a new byte array (the size of the array isn't too important, and you can change it to suit your application), then wait for data to come from the Netduino. Once it does, we copy all non-null bytes to our array, then convert the array to a string. From here, it is exactly like the Netduino code. The final code left to write is the part that handles the received messages. In this simple app, we don't need to check for anything except the return of the "ping". Once we receive that ping, we know it has connected successfully and we enable the slider control to let the user start using it: private void DoSomethingWithReceivedString(string buffer){if (buffer == "ping"){_isConnected = true;slider.IsEnabled = true;SystemTray.SetProgressIndicator(this, null);}} We also set the progress bar to null to hide it. Windows Phone (and other platforms) needs to explicitly define what capabilities they require for security reasons. Using Bluetooth is one such capability, so to define that we are using it, in the Solution Explorer find the Properties item below the project name. Left-click on the little arrow on the left of it to expand its children. Now double-click on WMAppManifest.xml to open it up then click the Capabilities tab near the top. The list on the left defines each specific capability. Ensure that both ID_CAP_PROXIMITY and ID_CAP_NETWORKING are checked. And that's it! Make sure your Netduino is plugged in (and running the program you made in this article), then plug your Windows Phone 8 in, and run the code. The run button may say Emulator X, you will need to change it to Device by clicking on the little down arrow on the right of the button. Once the two devices are connected, slide the slider on the phone forwards and backwards to see the onboard LED on the Netduino go brighter and dimmer. Not working? If the phone does not connect after a few seconds then something has probably gone wrong. After double-checking your wiring, the best thing to try is to unplug both the Netduino and phone, then plug them back in. If you are using a different Bluetooth board, you may have to pair it again to the phone. Repeat step 5 of the The Netduino Code section of this article. With both plugged back in, run the Netduino code (and give it a few seconds to boot up), then run the phone code. If that still doesn't work, unplug both again, and only plug back in the Netduino. When it is powered up, it will run the last application deployed to it. Then with your phone unplugged, go to the app list and find the phone app you made, and tap on it to run it. Summary You've managed to control your Netduino from afar! This article had a lot more code than most of the rest will because of needing to code both the Netduino and phone. However, the knowledge you've gained here will help you in many other projects, and we will be using this article as a base for some of the others. Resources for Article: Further resources on this subject: Automating the Audio Parameters – How it Works [Article] Ease the Chaos with Automated Patching [Article] Skype automation [Article]
Read more
  • 0
  • 0
  • 20079
Modal Close icon
Modal Close icon