Overview

The program below creates some JSON for describing manufacturing transactions. The program does not use higher level APIs like FinderAPI, AptAPI, or ToleranceAPI, so it is useful to get a sense of the actual data organization.

The JSON output for the simple facing workingstep is shown below. It summarizes the position within the process, tool, operation, feature, and some other basic parameters.

Simple facing workingstep Simple Facing Workingstep

The Output

The output is shown below. The tools are summarized at the top and referenced using JSON schema-style references, while workpieces (which have all of the STEP CAD geometry) are just referenced by a UUID from the ANCHOR section of the STEP file.

{
 name: thready_facing_op_revolved,
 tools: {
 1: {
 id: 1,
 type: ENDMILL,
 overall_assembly_length: {
 value: 1.0,
 unit: MM
 },
 effective_cutting_diameter: {
 value: 50.6,
 unit: MM
 },
 maximum_depth_of_cut: {
 value: 1.5,
 unit: MM
 },
 hand_of_cut: null,
 coolant_through_tool: null,
 number_of_effective_teeth: 1.0,
 edge_radius: {
 value: 0.03,
 unit: MM
 },
 tool_cutting_edge_angle: {
 value: 0.0,
 unit: DEG
 }
 }
 },
 project: {
 id: thready_facing_op_revolved,
 main_workplan: {
 id: Workplan,
 type: WORKPLAN,
 enabled: true,
 as_is_geometry: null,
 to_be_geometry: e2408c22-a893-11ed-a9b5-18dbf245276c,
 removal_geometry: null,
 twin_start: 2023年02月07日T16:32:31-05:00,
 twin_end: 2023年02月07日T16:32:32-05:00,
 elements: [
 {
 id: minimal geometry facing,
 type: WORKPLAN,
 enabled: true,
 as_is_geometry: null,
 to_be_geometry: null,
 removal_geometry: null,
 twin_start: 2023年02月07日T16:32:31-05:00,
 twin_end: 2023年02月07日T16:32:32-05:00,
 elements: [
 {
 id: WS 1 face roughing and finishing,
 type: MACHINING_WORKINGSTEP,
 enabled: true,
 as_is_geometry: null,
 to_be_geometry: null,
 removal_geometry: null,
 twin_start: 2023年02月07日T16:32:31-05:00,
 twin_end: 2023年02月07日T16:32:32-05:00,
 operation: {
 id: ,
 type: PLANE_FINISH_MILLING,
 tool: {
 $ref: #/tools/1
 },
 toolpath: [
 {
 id: clear WS 1 TP 1,
 type: CUTTER_LOCATION_TRAJECTORY,
 basiccurve: {
 name: ,
 type: polyline,
 points: [
 [
 0.0,
 -53.36,
 115.0
 ],
 [
 0.0,
 -53.36,
 110.0
 ]
 ]
 }
 },
 {
 id: entry WS 1 TP 2,
 type: CUTTER_LOCATION_TRAJECTORY,
 basiccurve: {
 name: ,
 type: polyline,
 points: [
 [
 0.0,
 -53.36,
 110.0
 ],
 [
 0.0,
 -53.36,
 104.0
 ]
 ]
 }
 },
 {
 id: WS 1 TP 3,
 type: CUTTER_LOCATION_TRAJECTORY,
 basiccurve: {
 name: ,
 type: polyline,
 points: [
 [
 0.0,
 -53.36,
 104.0
 ],
 [
 0.0,
 53.36,
 104.0
 ]
 ]
 }
 },
 {
 id: WS 1 TP 4,
 type: CUTTER_LOCATION_TRAJECTORY,
 basiccurve: {
 name: ,
 type: polyline,
 points: [
 [
 0.0,
 53.36,
 104.0
 ],
 [
 0.0,
 53.36,
 115.0
 ]
 ]
 }
 }
 ],
 axial_cutting_depth: {
 value: 1.0,
 unit: MM
 },
 overcut_length: null
 },
 feature: {
 id: disk feature,
 type: REVOLVED_FLAT,
 workpiece: 6b35d480-930c-451e-93c6-232565a10e05,
 explicit_representation: [
 69ebf3b7-4982-42f1-80c6-51dde4459c0d
 ],
 placement: [
 1.0,
 0.0,
 0.0,
 0.0,
 0.0,
 1.0,
 0.0,
 0.0,
 0.0,
 0.0,
 1.0,
 0.0,
 0.0,
 0.0,
 104.0,
 1.0
 ],
 profile_length: {
 value: 46.0,
 unit: MM
 }
 }
 }
 ],
 setup: null
 }
 ],
 setup: null
 }
 }
}

The Program

The program to make the JSON is shown below. Just using the built in python json.dumps on a STEP object gives an "Object is not JSON serializable" error. Beating on the jsons package gets a bit further but ultimately runs into a circular reference error.

In any case, just serializing AIM and ARM attributes in a mechanical manner would not be useful to the receiver. We define a JSONEncoder subclass to handle the different ARM constructs and fine tune the JSON into something useful for a particular purpose.

The JSONEncoder class uses the JSONFMT table of functions keyed on the ARM type to find a function that returns a plain dictionary for a given STEP construct, which is used for the json. Customization is done with a special function for each ARM type.

# MAKE JSON FROM STEP OBJECTS
#
import sys
import json
import uuid
from json import JSONEncoder
from pathlib import Path
from steptools import step
from steptools.step import AptAPI as apt
import stepdatetime
# JSON utility functions -----------------------------------------------
def json_boolean(strval, tval, fval, dflt=None) -> bool:
 if not strval: return dflt
 strval = strval.casefold()
 if strval == tval.casefold(): return True
 if strval == fval.casefold(): return False
 return None
def exec_enabled(o) -> bool:
 # convert strings and defaults to simple T/F
 if o is None: return False
 return json_boolean(o['enabled'], 'enabled', 'disabled', True)
def json_uuid(o) -> str:
 if o is None: return None
 # get existing or set to new uuid
 U = apt.set_uuid(o, str(uuid.uuid1()))
 return U
def json_faces(lst) -> list:
 if len(lst) == 0: return None
 VAL = list()
 for o in lst:
 VAL.append(json_uuid(o))
 return VAL
 
def json_measure(o) -> dict:
 if o is None: return None
 if step.isinstance(o, 'measure_with_unit'):
 V = o.value_component[0]
 U = step.Unit.fromstep(o.unit_component).name
 return { 'value': V, 'unit': U }
 return None
def json_tool_ref(o) -> dict:
 if o is None: return None
 return {
 '$ref': '#/tools/' + o['its_id'],
 }
# EXECUTABLES --------------------------------------------------
# ARM atts on executables
# 'as_is_geometry': None,
# 'enabled': None,
# 'fixture_geometry': None,
# 'its_id': 'Workingstep 1',
# 'its_security_classification': <step.ArmCollection size 0>,
# 'machine_used': None,
# 'process_properties': <step.ArmCollection size 0>,
# 'removal_geometry': None,
# 'to_be_geometry': None,
# 'twin_end': None,
# 'twin_exception': None,
# 'twin_plan': None,
# 'twin_source': None,
# 'twin_start': None,
# 'twin_worktime': None}
# AIM atts on executables
# 'name': 'Workingstep 1',
# 'consequence': '',
# 'description': 'machining',
# 'purpose': '',
def json_exec(o) -> dict:
 D = {
 'id': o['its_id'],
 # 'uuid': json_uuid(o),
 'type': step.arm_type(o),
 'enabled': exec_enabled(o),
 'as_is_geometry': json_uuid(o['as_is_geometry']),
 'to_be_geometry': json_uuid(o['to_be_geometry']),
 'removal_geometry': json_uuid(o['removal_geometry']),
 }
 D['twin_start'] = stepdatetime.asisoformat(o.twin_start)
 D['twin_end'] = stepdatetime.asisoformat(o.twin_end)
 
 return D
def json_enabled_elements(lst) -> list:
 VAL = list()
 for o in lst:
 if exec_enabled(o):
 VAL.append(o)
 return VAL
 
# OPERATIONS --------------------------------------------------
def json_operation(o) -> dict:
 # 'approach': None,
 # 'cam_properties': <step.ArmCollection size 0>,
 # 'consequence': '',
 # 'description': '',
 # 'its_id': 'start',
 # 'its_machine_functions': <step.Object ARM MILLING_MACHINE_FUNCTIONS #39952 machining_functions>,
 # 'its_machining_strategy': None,
 # 'its_op_security_classification': <step.ArmCollection size 0>,
 # 'its_technology': <step.Object ARM MILLING_TECHNOLOGY #39931 machining_technology>,
 # 'its_tool': <step.Object ARM TWIST_DRILL #39905 machining_tool>,
 # 'its_tool_direction': None,
 # 'its_toolpath': <step.ArmCollection size 6>,
 # 'name': 'start',
 # 'overcut_length': None,
 # 'process_properties': <step.ArmCollection size 0>,
 # 'purpose': '',
 # 'retract': None,
 # 'retract_plane': None,
 # 'start_point': None 
 return {
 'id': o['its_id'],
 'type': step.arm_type(o),
 # not on probing
 # 'machine_functions': o['its_machine_functions'],
 # 'technology': o['its_technology'],
 'tool': json_tool_ref(o['its_tool']),
 'toolpath': o['its_toolpath'],
 }
def json_drilling(o) -> dict:
 D = json_operation(o)
 D['cutting_depth'] = json_measure(o.cutting_depth)
 #D['previous_diameter'] = json_measure(o.previous_diameter)
 #D['dwell_time_bottom'] = json_measure(o.dwell_time_bottom)
 #D['feed_on_retract'] = json_measure(o.feed_on_retract)
 return D
def json_plane_milling(o) -> dict:
 D = json_operation(o)
 D['axial_cutting_depth'] = json_measure(o.axial_cutting_depth)
 D['overcut_length'] = json_measure(o.overcut_length)
 return D
# CUTTING TOOLS --------------------------------------------------
def json_tool(o) -> dict:
 return {
 'id': o['its_id'],
 'type': step.arm_type(o),
 }
def json_milling_machine_cutting_tool(o) -> dict:
 D = json_tool(o)
 # cutting components
 D['overall_assembly_length'] = json_measure(o.overall_assembly_length)
 D['effective_cutting_diameter'] = json_measure(o.effective_cutting_diameter)
 D['maximum_depth_of_cut'] = json_measure(o.maximum_depth_of_cut)
 D['hand_of_cut'] = o.hand_of_cut
 D['coolant_through_tool'] = json_boolean(
 o.hand_of_cut, 'supported', 'not supported'
 )
 return D
def json_milling_tool(o) -> dict:
 D = json_milling_machine_cutting_tool(o)
 D['number_of_effective_teeth'] = o.number_of_effective_teeth
 D['edge_radius'] = json_measure(o.edge_radius)
 return D
def json_endmill(o) -> dict:
 D = json_milling_tool(o)
 D['tool_cutting_edge_angle'] = json_measure(o.tool_cutting_edge_angle)
 return D
 
def json_drilling_tool(o) -> dict:
 D = json_milling_machine_cutting_tool(o)
 D['point_angle'] = json_measure(o.point_angle)
 return D
def json_countersink(o) -> dict:
 D = json_drilling_tool(o)
 D['minimum_cutting_diameter'] = json_measure(o.minimum_cutting_diameter)
 D['maximum_usable_length'] = json_measure(o.maximum_usable_length)
 return D
def json_user_defined_milling_tool(o) -> dict:
 D = json_milling_machine_cutting_tool(o)
 D['identifier'] = o.identifier
 D['corner_radius'] = json_measure(o.corner_radius)
 D['corner_radius_center_horizontal'] = json_measure(o.corner_radius_center_horizontal)
 D['corner_radius_center_vertical'] = json_measure(o.corner_radius_center_vertical) 
 D['taper_angle'] = json_measure(o.taper_angle) 
 D['tip_outer_angle'] = json_measure(o.tip_outer_angle)
 return D
# FEATURES --------------------------------------------------
def json_feature(o) -> dict:
 D = {
 'id': o['its_id'],
 'type': step.arm_type(o),
 'workpiece': json_uuid(o['its_workpiece']),
 'explicit_representation': json_faces(o['explicit_representation']),
 }
 try:
 D['placement'] = step.Xform.fromstep(o.feature_placement)
 except:
 pass
 return D
def json_revolved_flat(o) -> dict:
 D = json_feature(o)
 # flat edge shape has linear profile length
 PROF = o['flat_edge_shape']
 D['profile_length'] = json_measure(PROF.profile_length)
 return D
 
# TOOLPATHS --------------------------------------------------
def json_toolpath(o) -> dict:
 D = {
 'id': o['its_id'],
 'type': step.arm_type(o),
 'basiccurve': json_curve(o['basiccurve']),
 }
 return D
def json_curve(o) -> dict:
 if step.isinstance(o, 'polyline'):
 D = {
 'name': o['name'],
 'type': step.type(o),
 'points': [],
 }
 pts = D['points']
 for obj in o.points:
 pts.append(step.Vec.fromstep(obj))
 return D
 
 return o
 
# ------------------------------------------------------------
# FORMAT TABLE
#
# This is a simple dictionary that goes from the ARM type of an object
# to a function that returns a dictionary for the json. To deal with
# the inheritance, I have some common functions for execs, operations,
# etc. that can be combined with | with a dictionary for the specifics
# of a particular ARM type.
# 
#
JSONFMT = {
 # 'description': None,
 # 'formation': <step.Object #11 product_definition_formation>,
 # 'frame_of_reference': <step.Object #17 product_definition_context>,
 # 'id': '',
 # 'its_id': 'thready_fixed',
 # 'its_manufacturer': None,
 # 'its_manufacturer_organization': None,
 # 'its_owner': <step.Object ARM PERSON_AND_ADDRESS #1190 person>,
 # 'its_owner_organization': None,
 # 'its_release': <step.Object #443 date_and_time>,
 # 'its_security_classification': <step.ArmCollection size 0>,
 # 'its_status': <step.Object ARM APPROVAL #412 approval>,
 # 'its_workpieces': <step.ArmCollection size 1>,
 # 'main_workplan': <step.Object ARM WORKPLAN #1469 machining_workplan>}
 'PROJECT': (lambda o: {
 'id': o['its_id'],
 'main_workplan': o['main_workplan'],
 }),
 # 'its_channel': None,
 # 'its_elements': <step.ArmCollection size 1>,
 # 'its_minimum_machine_params': None,
 # 'its_setup': <step.Object ARM SETUP #409 product_def inition_formation>,
 # 'planning_operation': None,
 # 'toolpath_orientation': <step.Object #1473 axis2_placement_3d>,
 'WORKPLAN': (lambda o: json_exec(o) | {
 'elements': json_enabled_elements(o['its_elements']),
 #'toolpath_orientation': step.Xform.fromstep(o['toolpath_orientation']),
 'setup': o['its_setup'],
 }),
 'SELECTIVE': (lambda o: json_exec(o) | {
 'elements': json_enabled_elements(o['its_elements']),
 }),
 'PARALLEL': (lambda o: json_exec(o) | {
 'branches': json_enabled_elements(o['branches']),
 }),
 'NON_SEQUENTIAL': (lambda o: json_exec(o) | {
 'elements': json_enabled_elements(o['its_elements']),
 }),
 
 # 'final_features': <step.ArmCollection size 0>,
 # 'its_feature': <step.Object ARM FEATURE_TEMPLATE #1485 datum_feature_and_instanced_feature_and_thread>,
 # 'its_operation': <step.Object ARM THREADING_ROUGH #1381 threading_turning_operation>,
 # 'its_secplane': <step.Object #379 plane>,
 # 'its_secplane_rep': <step.Object #378 representation>,
 # 'toolpath_orientation': None,
 'MACHINING_WORKINGSTEP': (lambda o: json_exec(o) | {
 'operation': o['its_operation'],
 'feature': o['its_feature'],
 #'toolpath_orientation': step.Xform.fromstep(o['toolpath_orientation']),
 }),
 # Operations
 'BORING': json_operation,
 'BOTTOM_AND_SIDE_FINISH_MILLING': json_operation,
 'BOTTOM_AND_SIDE_ROUGH_MILLING': json_operation,
 'CENTER_MILLING': json_operation,
 'CONTOURING_FINISH': json_operation,
 'CONTOURING_ROUGH': json_operation,
 'CUTTING_IN': json_operation,
 'DRILLING': json_drilling,
 'FACING_FINISH': json_operation,
 'FACING_ROUGH': json_operation,
 'FREEFORM_FINISH_MILLING': json_operation,
 'FREEFORM_OPERATION': json_operation,
 'FREEFORM_ROUGH_MILLING': json_operation,
 'GROOVING_FINISH': json_operation,
 'GROOVING_ROUGH': json_operation,
 'KNURLING': json_operation,
 'MULTISTEP_DRILLING': json_drilling,
 'PLANE_FINISH_MILLING': json_plane_milling,
 'PLANE_ROUGH_MILLING': json_plane_milling,
 'REAMING': json_operation,
 'SIDE_FINISH_MILLING': json_operation,
 'SIDE_ROUGH_MILLING': json_operation,
 'TAPPING': json_operation,
 'THREADING_FINISH': json_operation,
 'THREADING_ROUGH': json_operation,
 # Probing Operations
 'WORKPIECE_COMPLETE_PROBING': json_operation,
 'WORKPIECE_PROBING': json_operation,
 # NC Functions
 'DISPLAY_MESSAGE': json_exec,
 'EXTENDED_NC_FUNCTION': json_exec,
 'INDEX_TABLE': json_exec,
 'LOAD_TOOL': json_exec,
 'NON_SEQUENTIAL': json_exec,
 'OPTIONAL_STOP': json_exec,
 'PROGRAM_STOP': json_exec,
 'RETURN_HOME': json_exec,
 'UNLOAD_TOOL': json_exec,
 # Features
 'CATALOGUE_THREAD': json_feature,
 'CHAMFER': json_feature,
 'CIRCULAR_BOSS': json_feature,
 'CIRCULAR_CLOSED_SHAPE_PROFILE': json_feature,
 'CIRCULAR_PATTERN': json_feature,
 'CLOSED_POCKET': json_feature,
 'COMPLEX_BOSS': json_feature,
 'COMPOUND_FEATURE': json_feature,
 'COUNTERBORE_HOLE': json_feature,
 'COUNTERBORE_HOLE_TEMPLATE': json_feature,
 'COUNTERSUNK_HOLE': json_feature,
 'COUNTERSUNK_HOLE_TEMPLATE': json_feature,
 'DEF INED_MARKING': json_feature,
 'DEF INED_THREAD': json_feature,
 'DIAGONAL_KNURL': json_feature,
 'DIAMOND_KNURL': json_feature,
 'EDGE_ROUND': json_feature,
 'FEATURE_TEMPLATE': json_feature,
 'GENERAL_FEATURE': json_feature,
 'GENERAL_OUTSIDE_PROFILE': json_feature,
 'GENERAL_PATTERN': json_feature,
 'GENERAL_REVOLUTION': json_feature,
 'GENERAL_SHAPE_PROFILE': json_feature,
 'GROOVE': json_feature,
 'OPEN_POCKET': json_feature,
 'OUTER_DIAMETER': json_feature,
 'OUTER_DIAMETER_TO_SHOULDER': json_feature,
 'PARTIAL_CIRCULAR_SHAPE_PROFILE': json_feature,
 'PLACED_FEATURE': json_feature,
 'PLANAR_FACE': json_feature,
 'RECTANGULAR_BOSS': json_feature,
 'RECTANGULAR_CLOSED_SHAPE_PROFILE': json_feature,
 'RECTANGULAR_OPEN_SHAPE_PROFILE': json_feature,
 'RECTANGULAR_PATTERN': json_feature,
 'REVOLVED_FLAT': json_revolved_flat,
 'ROUND_HOLE': json_feature,
 'ROUND_HOLE_TEMPLATE': json_feature,
 'ROUNDED_END': json_feature,
 'SLOT': json_feature,
 'SPHERICAL_CAP': json_feature,
 'STEP': json_feature,
 'STRAIGHT_KNURL': json_feature,
 'TOOL_KNURL': json_feature,
 'TOOLPATH_FEATURE': json_feature,
 # Cutting Tools
 'BALLNOSE_ENDMILL': json_endmill,
 'BULLNOSE_ENDMILL': json_endmill,
 'COMBINED_DRILL_AND_REAMER': json_tool,
 'COMBINED_DRILL_AND_TAP': json_tool,
 'COUNTERBORE': json_drilling_tool,
 'COUNTERSINK': json_countersink,
 'DOVETAIL_MILL': json_milling_tool,
 'DRILL': json_drilling_tool,
 'ENDMILL': json_endmill,
 'FACEMILL': json_milling_tool,
 'GENERAL_TURNING_TOOL': json_tool,
 'GROOVING_TOOL': json_tool,
 'KNURLING_TOOL': json_tool,
 'PROFILED_END_MILL': json_endmill,
 'REAMING_CUTTING_TOOL': json_tool,
 'ROTATING_BORING_CUTTING_TOOL': json_tool,
 'SHOULDERMILL': json_milling_tool,
 'SIDE_MILL': json_milling_tool,
 'SPADE_DRILL': json_drilling_tool,
 'SPOTDRILL': json_drilling_tool,
 'STEP_DRILL': json_drilling_tool, # has more
 'T_SLOT_MILL': json_milling_tool,
 'TAPERED_REAMER': json_tool,
 'TAPPING_CUTTING_TOOL': json_tool,
 'THREAD_MILL': json_milling_tool,
 'TOUCH_PROBE': json_tool,
 'TURNING_MACHINE_CUTTING_TOOL': json_tool,
 'TURNING_THREADING_TOOL': json_tool,
 'TWIST_DRILL': json_drilling_tool,
 'TWIST_DRILL_BASE': json_drilling_tool,
 'USER_DEFINED_MILLING_TOOL': json_user_def ined_milling_tool,
 'USER_DEFINED_TURNING_TOOL': json_tool,
 # Toolpaths
 'CUTTER_LOCATION_TRAJECTORY': json_toolpath,
}
# extra keyword args in dumps are passed to the ctor. can use that to
# implement an omit_null keyword.
class STEPEncoder(JSONEncoder):
 def default(self, o):
 if isinstance(o, step.ArmCollection):
 return list(o)
 if not isinstance(o, step.Object):
 return o
 if step.isinstance(o, 'RoseAggregate'):
 return list(o)
 # arm type usually returns a single name but might return a
 # list of types instead
 TLIST = step.arm_type(o)
 if not isinstance(TLIST,list): TLIST = [ TLIST ]
 # return first json cvt function that we find
 for AT in TLIST:
 FN = JSONFMT.get(AT)
 if callable(FN):
 return FN(o)
 
 return None
# TOP LEVEL OBJECT --------------------------------------------------
def make_json_root(d) -> dict:
 D = {
 'name': d.name(),
 'tools': {},
 'project': apt.get_current_project(),
 }
 for obj in step.DesignCursor(d, "machining_tool"):
 if not obj.name: continue
 D['tools'][obj.name] = obj
 return D
 
 
def main() -> int:
 """Export project from a file"""
 step.verbose(False)
 args = iter(sys.argv) # Parse command line arguments
 PROG = next(args)
 SRC = None
 DST = None
 while True:
 arg = next(args, None)
 if arg is None:
 break
 
 if arg == -o:
 DST = next(args, None)
 if DST is None:
 print(option: -o <file>, file = sys.stderr )
 sys.exit(1)
 continue
 
 SRC = Path(arg)
 break
 if SRC is None:
 print(no input file specified, file = sys.stderr )
 sys.exit(1)
 if DST is None:
 DST = SRC.with_suffix('.json')
 # read data, must recognize arm objects before using adaptive
 D = step.open_project(SRC)
 OBJ = make_json_root(D)
 y = json.dumps(OBJ, indent=4, cls=STEPEncoder)
 print (y)
 # may need to save design if it has changed because of new uuids
 return 0
if __name__ == '__main__':
 sys.exit(main())
Copyright © 2026 STEP Tools Inc. All Rights Reserved.

AltStyle によって変換されたページ (->オリジナル) /