Esistono standard per le strutture di dati del diagramma di flusso?

-1

Spesso sono incaricato di flussi relativamente complessi che i miei datori di lavoro vorranno sperimentare riguardo alla quantità di passaggi e al loro ordine. Sono un appaltatore e quando il mio lavoro viene consegnato a un altro sviluppatore, mi piacerebbe spedire qualcosa che sia facile da capire rapidamente - e un flusso con più di 10 passaggi è un bel boccone. Quindi ho scritto un prototipo che prende una struttura dati JSON fatta in casa, che consente

  1. Esecuzione di quella struttura dati con un oggetto stato passato a tutte le condizioni e proseguendo attraverso il vero percorso del diagramma di flusso.
  2. Trasformare la struttura dei dati in un grafico che può essere visualizzato, come nell'esempio seguente.

Itipisupportatisono:Condizione,Passo/StatoeTermina.

Hovistodiversiprogettistididiagrammidiflussolàfuoriemichiedevoseavrebbeavutosensoimplementareinunsottoinsiemedellalorostrutturadati,quindiidiagrammidiflussopossonoesseremodificatisiaincodicecheinunvisualdesigner.Forseconsentirebbel'esecuzionedeidiagrammidiflussoinaltrilinguaggidiprogrammazione,poichélamiaimplementazioneèsoloinTypeScript.

Esistonostandardperlestrutturedidatideldiagrammadiflussoe/oesisteunostrumentostandarddefactopermodificareidiagrammidiflussochedevonoessereancheeseguiti?

Questoèilprototipo.Sipregadinotarecheèmolto,moltosolounprototipo!

import{inspect}from"util"
import { Graph, json, alg } from "graphlib"
import * as fs from "fs"

let predicates = {
    isSomeCondition2: () => false,
    IsCvrActive: () => false,
    shouldContactCustomerService: () => false,
    canDeliverProduct: () => true,
    isChangingSupplier: () => true,
    isSomeCondition: () => true,
    isReceipt: () => false
}

let flow =
{
    type: "Flow", name: "Primary flow", nodes: [
        { type: "Step", name: "Enter CVR" },
        {
            type: "Condition", name: "Is CVR Active?", predicateKey: "IsCvrActive",
            right: {
                type: "Step", name: "CVR is inactive",
                left: { type: "Terminate", name: "Terminate (inactive cvr)" }
            }
        },
        {
            type: "Condition", name: "Should contact customer service?", predicateKey: "shouldContactCustomerService",
            left: {
                type: "Step", name: "Contact customer service",
                left: { type: "Terminate", name: "Terminate (contact customer service)" }
            }
        },
        {
            type: "Condition", name: "Can deliver product?", predicateKey: "canDeliverProduct",
            right: {
                type: "Step", name: "Cannot deliver product",
                left: { type: "Terminate", name: "Terminate (cannot deliver product)" }
            }
        },
        {
            type: "Condition", name: "DataHub has PODs for CVR?", predicateKey: "isChangingSupplier",
            left: { type: "Flow", name: "Switch provider", nodes: [
                { type: "Step", name: "Power destination addresses" }
            ]},
            right: { type: "Flow", name: "Relocation", nodes: [
                { type: "Step", name: "Enter address" },
                { type: "Step", name: "Enter POD" },
                { type: "Step", name: "Enter estimated annual volume" },
                { type: "Step", name: "Enter latest meter reading" }
            ]}
        },
        { type: "Step", name: "First payment" },
        {
            type: "Condition", name: "What receipt?", predicateKey: "isReceipt",
            left: { type: "Step", name: "Receipt1" },
            right: { type: "Step", name: "Receipt2" }
        }]
}

let start = new Date()

//console.log("== EVALUATED")
//let evaluatedFlow = evaluateFlow(flow)
//console.log(evaluatedFlow)

var i = 0;

let graph = traverseFlow(flow)

console.log("PARENT Is this a condition?")
console.log(graph.parent('Is this a condition?'));

console.log("PARENT Enter CVR")
console.log(graph.parent('Enter CVR'));

console.log("== GRAPH")
var graphJson = json.write(graph)
fs.writeFileSync("./graph.html", writeHtml(graphJson));
//console.log(inspect(graphJson, { showHidden: false, depth: null }))

console.log("== MINIMUM STEPS")
//let minimumSteps = findMinimumSteps(graph)
//console.log(minimumSteps)

let end = <any>new Date() - <any>start
console.info("\n\nExecution time: %dms", end)

function findMinimumSteps(graph: Graph) {
    let root = graph.sources()[0]
    let sinks = graph.sinks().filter(node => graph.node(node).type !== "Terminate")
    let dijkstraResult = alg.dijkstra(graph, root, e => graph.node(e.w).type === "Condition" ? 0 : 1)
    console.log("=== DIJSKTRA")
    console.log(dijkstraResult)
    let shortestPath = sinks
        .map(node => dijkstraResult[node])
        .reduce((a, b) => a.distance < b.distance ? a : b)
    return shortestPath.distance + 1
}

function traverseFlow(flow: any, graph?: Graph, parent?: any): Graph {
    graph = graph || new Graph({ compound: true })
    let sinks = parent ? [parent.name] : graph.sinks()

    flow.nodes
        .map(createFlowGraph)
        .forEach((subGraph: Graph) => {

            subGraph.nodes().forEach(node => {
                graph.setNode(node, subGraph.node(node))
                let parentFlow = subGraph.parent(node) || flow.name
                if(graph.node(parentFlow)) {
                    graph.setParent(node, parentFlow)
                }
            })
            subGraph.edges().forEach(edge => graph.setEdge(edge, subGraph.edge(edge)))

            sinks.forEach(outNode => {
                let label = graph.node(outNode)
                if (label.type !== "Terminate" && label.type !== "Flow")
                    subGraph.sources().forEach(inNode => {
                        if(subGraph.node(inNode).type !== "Flow")
                            graph.setEdge(outNode, inNode, { label: label.type === "Condition" ? conditionLabel(label, inNode) : undefined })
                    })
            })

            let unconnectedConditions = subGraph.filterNodes(n =>
                subGraph.node(n).type === "Condition" && (subGraph.outEdges(n) || []).length === 1).nodes()

            sinks = subGraph.sinks().filter(node => subGraph.node(node).type !== "Flow").concat(unconnectedConditions)
        })

    return graph
}

function createFlowGraph(root: any): Graph {
    let graph = new Graph({ compound: true })
    traverseBinaryNode(graph, root)
    return graph
}

function conditionLabel(condition: any, node: any) {
    let { left, right } = condition

    if(left && left.type === "Flow")
        left = left.nodes[0]

    if(right && right.type === "Flow")
        right = right.nodes[0]

    if(left && left.name === node)
        return true

    if(right && right.name === node)
        return false

    return !!condition.right
}

function traverseBinaryNode(g: Graph, node: any, parent?: any) {

    if(node.type === "Flow")
        g.setNode(node.name, (<any>Object).assign({}, node, {  label: node.name, clusterLabelPos: 'top', style: 'fill: #fff' }))
    else
        g.setNode(node.name, (<any>Object).assign({}, node))

    if (node.type === "Flow")
        traverseFlow(node, g, parent)

    if (node.left)
        traverseBinaryNode(g, node.left, node)

    if (node.right)
        traverseBinaryNode(g, node.right, node)

    if (parent && node.type !== "Flow")
        g.setEdge(parent.name, node.name, { label: parent.type === "Condition" ? conditionLabel(parent, node.name) : undefined })
}

function evaluateFlow(flow: any) {
    let steps = []
    for (let node of flow.nodes) {
        if (!evaluateNode(node, steps))
            break
    }
    return steps
}

function evaluateNode(node: any, steps: any[]) {
    steps.push(node)

    switch (node.type) {
        case "Condition": {
            if (predicates[node.predicateKey]())
                return evaluateNode(node.left, steps)
            else if (node.right)
                return evaluateNode(node.right, steps)

            // Continue to the next binary tree in the flow
            return true
        }
        case "Step": {
            if (node.left)
                return evaluateNode(node.left, steps)

            // Continue to the next binary tree in the flow
            return true
        }
        case "Terminate": {
            // Stop evaluating the flow
            return false
        }
    }

    throw Error("Unknown node type")
}

function writeHtml(graphJson) {
    return '
    <!doctype html>
    <html>
        <head></head>
        <body>
        <script src="https://d3js.org/d3.v4.min.js"></script><scriptsrc="https://dagrejs.github.io/project/dagre-d3/latest/dagre-d3.js"></script>


        <style id="css">
        /* This sets the color for "TK" nodes to a light blue green. */
        g.type-TK > rect {
          fill: #00ffd0;
        }

        text {
          font-weight: 300;
          font-family: "Helvetica Neue", Helvetica, Arial, sans-serf;
          font-size: 14px;
        }

        .node rect {
          stroke: #999;
          fill: #fff;
          stroke-width: 1.5px;
        }

        .edgePath path {
          stroke: #333;
          stroke-width: 1.5px;
        }

        .clusters rect {
            fill: #00ffd0;
            stroke: #999;
            stroke-width: 1.5px;
          }

        </style>

        <svg id="svg-canvas" width=3000 height=3000></svg>


        <script>
// Here we"re setting nodeclass, which is used by our custom drawNodes function
// below.
var g = dagreD3.graphlib.json.read(JSON.parse(\'${JSON.stringify(graphJson, null, 2)}\')).setGraph({})

// Create the renderer
var render = new dagreD3.render();

// Set up an SVG group so that we can translate the final graph.
var svg = d3.select("svg"),
    svgGroup = svg.append("g");

// Run the renderer. This is what draws the final graph.
render(d3.select("svg g"), g);

// Center the graph
var xCenterOffset = (svg.attr("width") - g.graph().width) / 2;
svgGroup.attr("transform", "translate(" + xCenterOffset + ", 20)");
svg.attr("height", g.graph().height + 40);

        </script>

        </body>
    </html>
    '
}

Prima fai npm install graphlib quindi esegui tsc file.ts && node file.js , quindi apri graph.html

    
posta Jan Sommer 19.10.2018 - 15:57
fonte

1 risposta

1

Modello e notazione dei processi di business (BPMN) è una rappresentazione grafica per specificare i processi di business in un modello di processo di business.

Esempiodiunmodellodiprocessoaziendaleediunanotazioneperunprocessoconunflussonormale. DaWikipedia.

IlXMLProcessDefinitionLanguage(XPDL)èunformatostandardizzatodallaWorkflowManagementCoalition(WfMC)perscambiareledefinizionideiprocessidibusinesstradiversiprodottidelflussodilavoro.

XPDLèattualmenteilmigliorformatodifileperloscambiodidiagrammiBPMN;èstatoprogettatoappositamentepermemorizzaretuttigliaspettidiundiagrammaBPMN.XPDLcontieneelementipercontenereinformazionigrafiche,comelaposizioneXeYdeinodi,nonchéaspettieseguibilichepotrebberoessereutilizzatipereseguireunprocesso.

Ulterioriletture
XML Process Definition Language (XPDL) su Wikipedia
XPDL 2.2 Sito web dedicato alle specifiche XPDL del WfMC
Modello di processo di business e notazione (BPMN) su Wikipedia

    
risposta data 19.10.2018 - 17:11
fonte

Leggi altre domande sui tag