IndustrialKit is a framework that allows you to create applications for design and management means of production. This material provides learning with its main functionality.

Begins

It is enough for you to have any actual iPad. For greater convenience, it is also recommended to get a separate keyboard.
Install the Swift Playground app and open it. This is a development environment in which you can create your applications – write a code, add resources and of course – use Swift Packages.

Now download the starting playground. It already contains the necessary basic set of listings and models, as well as the added IndustrialKit package. You can also download the completed playground.

Make a part

Let's start with something simple – with the part. We initialize it by specifying the name and scene name from which the visual model will be taken – the part node.

Next, using the ObjectSceneView control, we display the part node on application view.

Part View
Preview
import SwiftUI
import SceneKit
import IndustrialKit
struct ContentView: View
{
var body: some View
{
PartView()
}
}
let bushing = Part(name: "Bushing", scene_name: "Bushing.scn")
struct PartView: View
{
var body: some View
{
ObjectSceneView(node: bushing.node!)
.frame(width: 280, height: 280)
.background
{
Rectangle()
.fill(.thinMaterial)
.shadow(radius: 4)
}
}
}
view raw PartView.swift hosted with ❤ by GitHub

Make a tool

Now move on to the tool. We set a scene with the tool model. Next, we initialize the tool, where in addition to the name and scene, the model controller and connector are also specified. The first allows you to control the visual model of the tool. The second provides connection to a real tool in production.

Tools perform technological operations associated with certain values of the operation code supplied to the perform function of the tool. For the gripper in question, values 0 and 1 are used – closing and opening, respectively.

Let's create a field to set a custom opcode value with button to perform it.

Tool View
Preview
import SwiftUI
import SceneKit
import IndustrialKit
//...
let tool_scene = SCNScene(named: "Gripper.scn") ?? SCNScene()
struct ToolView: View
{
@State var gripper = Tool(name: "Gripper", model_controller: Gripper_Controller(), connector: ToolConnector(), scene: tool_scene)
@State var code = 0
var body: some View
{
VStack
{
ObjectSceneView(scene: tool_scene, on_init: { scene_view in prepare_scene() })
.frame(width: 280, height: 280)
.background
{
Rectangle()
.fill(.thinMaterial)
.shadow(radius: 4)
}
HStack
{
TextField("0", value: $code, format: .number)
.textFieldStyle(.roundedBorder)
.frame(width: 64)
Button(action: { perform_tool(code) })
{
Label("Perform", systemImage: "play")
}
.buttonStyle(.borderedProminent)
}
.padding(.top, 4)
}
}
// Prepare scene with tool visual model
func prepare_scene()
{
let viewed_node = tool_scene.rootNode.childNode(withName: "tool", recursively: true)
apply_bit_mask(node: viewed_node ?? SCNNode(), Workspace.tool_bit_mask)
gripper.workcell_connect(scene: tool_scene, name: "tool")
}
// Perform tool by inputed code
func perform_tool(_ code: Int)
{
gripper.perform(code: code)
{
print("Finished")
}
}
}
view raw ToolView2.swift hosted with ❤ by GitHub

We can also replace that with a view for controlling the gripper with two buttons.

Tool View with opcode input
Preview
import SwiftUI
import SceneKit
import IndustrialKit
//...
let tool_scene = SCNScene(named: "Gripper.scn") ?? SCNScene()
struct ToolView: View
{
//...
var body: some View
{
VStack
{
ObjectSceneView(scene: tool_scene, on_init: { scene_view in prepare_scene() })
.frame(width: 280, height: 280)
.background
{
Rectangle()
.fill(.thinMaterial)
.shadow(radius: 4)
}
HStack
{
Button(action: { perform_tool(0) })
{
Label("Close", systemImage: "arrowtriangle.right.and.line.vertical.and.arrowtriangle.left.fill")
}
.buttonStyle(.bordered)
Button(action: { perform_tool(1) })
{
Label("Open", systemImage: "arrowtriangle.left.and.line.vertical.and.arrowtriangle.right.fill")
}
.buttonStyle(.bordered)
}
.padding(.top, 4)
}
}
//...
}
view raw ToolView.swift hosted with ❤ by GitHub

Now let's create a button to perform a sequence of operations.

The sequence can be specified as a nesting of single operations (perform_single function), or as a program (perform_program function).

Tool View with program
Preview
import SwiftUI
import SceneKit
import IndustrialKit
//...
struct ToolView: View
{
//...
var body: some View
{
VStack
{
ObjectSceneView(scene: tool_scene, on_init: { scene_view in prepare_scene() })
.frame(width: 280, height: 280)
.background
{
Rectangle()
.fill(.thinMaterial)
.shadow(radius: 4)
}
Button(action: perform_program)
{
Label("Perform", systemImage: "play")
}
.buttonStyle(.borderedProminent)
.padding(.top, 4)
}
}
// Prepare scene with tool visual model
func prepare_scene()
{
let viewed_node = tool_scene.rootNode.childNode(withName: "tool", recursively: true)
apply_bit_mask(node: viewed_node ?? SCNNode(), Workspace.tool_bit_mask)
gripper.workcell_connect(scene: tool_scene, name: "tool")
}
// Perform tool by inputed code
func perform_tool(_ code: Int)
{
gripper.perform(code: code)
{
print("Finished")
}
}
// Order of single actions
func perform_single()
{
gripper.perform(code: 0)
{
gripper.perform(code: 1)
}
}
// Operation codes program
func perform_program()
{
let open_close_program = OperationsProgram(name: "O/C")
open_close_program.add_code(OperationCode(0))
open_close_program.add_code(OperationCode(1))
gripper.add_program(open_close_program)
gripper.select_program(name: "O/C")
gripper.start_pause_performing()
}
}
view raw ToolView3.swift hosted with ❤ by GitHub

Make a robot

And now, we move on to the robot. It initializes with the name, controller, connector and scene name parameters. However, there are differences in the approach to displaying the robot on the stage.

First, an empty scene robot_scene is created. Next, when initializing the scene, you must first connect to the scene using the workcell_connect function on view appear. The position of the origin of the coordinate system of the manipulator's movement space sets by origin_location and origin_rotation variable.

In order for the robot model to be updated in real time, the robot update function (update_model) is specified in the ObjectSceneView control when initializing the control to be called on the scene rendering.

Robot View
Preview
let robot_scene = SCNScene()
struct RobotView: View
{
@State var arm = Robot(name: "Robot", model_controller: _6DOF_Controller(), connector: RobotConnector(), scene_name: "6DOF.scn")
var body: some View
{
VStack(spacing: 0)
{
ObjectSceneView(scene: robot_scene, on_init: { scene_view in prepare_scene() })
.frame(width: 280, height: 280)
.background
{
Rectangle()
.fill(.thinMaterial)
.shadow(radius: 4)
}
}
}
// Prepare scene with robot visual model
func prepare_scene()
{
arm.perform_update()
arm.workcell_connect(scene: robot_scene, name: "unit", connect_camera: false)
arm.origin_location = [250, 0, 50]
}
}
view raw RobotView.swift hosted with ❤ by GitHub

Next, using the PositionControl control, we ensure that the position parameters of the manipulator (location & rotation) are changed manually.

Robot View with control
Preview
import SwiftUI
import SceneKit
import IndustrialKit
//...
struct RobotView: View
{
//...
var body: some View
{
VStack(spacing: 0)
{
ObjectSceneView(scene: robot_scene, on_init: { scene_view in prepare_scene() })
.frame(width: 280, height: 280)
.background
{
Rectangle()
.fill(.thinMaterial)
.shadow(radius: 4)
}
PositionControl(location: $arm.pointer_location, rotation: $arm.pointer_rotation, scale: $arm.space_scale)
.frame(width: 320)
.disabled(arm.performed)
}
}
//...
}

Similar to a tool for a robot, a sequence of operations – movements to certain positions can be specified. Position parameters are specified in a rectangular coordinate system local to the robot. Additionally, the angles of rotation in the position and the speed of movement into it can be specified.

Performing sequence can be specified either as nested single actions or as a position program.

Robot View with program
Preview
import SwiftUI
import SceneKit
import IndustrialKit
//...
struct RobotView: View
{
//...
var body: some View
{
VStack(spacing: 0)
{
ObjectSceneView(scene: robot_scene, on_init: { scene_view in prepare_scene() })
.frame(width: 280, height: 280)
.background
{
Rectangle()
.fill(.thinMaterial)
.shadow(radius: 4)
}
Button(action: perform_program)
{
Label("Perform", systemImage: "play")
}
.buttonStyle(.borderedProminent)
.padding()
}
}
//...
// Single point actions perform
func perform_single()
{
arm.move_to(point: PositionPoint(x: 50, y: 0, z: 50))
{
arm.move_to(point: PositionPoint(x: 100, y: 0, z: 50, r: 0, p: 90, w: 0))
{
arm.move_to(point: PositionPoint(x: 100, y: 0, z: 50, r: 0, p: 90, w: 0, move_speed: 40))
}
}
}
// Position program perform
func perform_program()
{
arm.points_node?.isHidden = true
let program = PositionsProgram(name: "Square")
program.add_point(PositionPoint(x: 10, y: 10, z: 10))
program.add_point(PositionPoint(x: 100, y: 10, z: 10))
program.add_point(PositionPoint(x: 100, y: 100, z: 10))
program.add_point(PositionPoint(x: 10, y: 100, z: 10))
program.add_point(PositionPoint(x: 10, y: 10, z: 10))
arm.add_program(program)
arm.select_program(name: "Square")
arm.start_pause_moving()
}
}

Conclusion

You learned methods for creating and basic use of individual components of a manufacturing system – robots, tools, and parts.

This material is quite sufficient for creating applications for monitoring and modeling individual industrial devices, as well as combining them. This is the basis for further work with the framework. 

The next stage is complex application within Workspace and Ithi Macro Assembler.