I like the approach suggested by @Arash, but this creates 3 additional files, which really adds up if you have a large network and need to check this regularly. Here's my solution using python.
The geometric network build forces the X,Y of the nodes and the pipe ends to be coincident. You can access these locations with the shape tokens in arcpy cursors and match them. The shape tokens for lines return an array of the vertices in the order that they were drawn. In my network, the draw order of the pipes is heavily QA'd because we use this to set the flow directions. So, the first vertex is the start of the pipe, and the last vertex is the end of the pipe.
Reference: ASSETID = id of pipe, UNITID = node id at start of pipe, UNITID2 = node id at end of pipe.
nodes = "mergeNodes"
pipes = "SEWER_1"
nodeDict = {}
pipeDict = {}
#populate node dictionary with X,Y as the key and node ID as the value
for node in arcpy.da.SearchCursor(nodes, ["UNITID", "SHAPE@XY"]):
nodeDict[(node[1][0], node[1][1])] = node[0]
#populate pipe dictionary with pipe ID as the key and list of X,Y as values
#vertices populated in the order that the line was draw
#so that [0] is the first vertex and [-1] is the final vertex
for pipe in arcpy.da.SearchCursor(pipes, ["ASSETID", "SHAPE@"]):
for arrayOb in pipe[1]:
for point in arrayOb:
if pipe[0] in pipeDict:
pipeDict[pipe[0]].append((point.X, point.Y))
else:
pipeDict[pipe[0]] = [(point.X, point.Y)]
#populate UNITID with the first vertex of the line
#populate UNITID2 with the final vertex of the line
with arcpy.da.UpdateCursor(pipes, ["ASSETID", "UNITID", "UNITID2"]) as cur:
for pipe in cur:
if pipeDict[pipe[0]][0] in nodeDict:
pipe[1] = nodeDict[pipeDict[pipe[0]][0]]
if pipeDict[pipe[0]][-1] in nodeDict:
pipe[2] = nodeDict[pipeDict[pipe[0]][-1]]
cur.updateRow(pipe)
1234
; You can't say if the two codes are 1 & 234 or 12 & 34 or 123 & 4. It might be better if you keep a symbol for delimiting the two codes, say12-34