@things-factory/spc 8.0.0-beta.1 → 8.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/spc",
3
- "version": "8.0.0-beta.1",
3
+ "version": "8.0.0-beta.2",
4
4
  "main": "dist-server/index.js",
5
5
  "browser": "dist-client/index.js",
6
6
  "things-factory": true,
@@ -30,8 +30,8 @@
30
30
  "@operato/dataset": "^8.0.0-beta",
31
31
  "@operato/form": "^8.0.0-beta",
32
32
  "@operato/spc": "^8.0.0-beta",
33
- "@things-factory/dataset": "^8.0.0-beta.1",
34
- "@things-factory/shell": "^8.0.0-beta.1"
33
+ "@things-factory/dataset": "^8.0.0-beta.2",
34
+ "@things-factory/shell": "^8.0.0-beta.2"
35
35
  },
36
- "gitHead": "36c494e587640c1490318ef7b95adab02606e0c2"
36
+ "gitHead": "f03431a09435511b2595515658f9cb8f78ba4ebb"
37
37
  }
@@ -1 +0,0 @@
1
- export default function bootstrap() {}
package/client/index.ts DELETED
File without changes
@@ -1,336 +0,0 @@
1
- import '@material/web/icon/icon.js'
2
-
3
- import '@operato/app/filters-form.js'
4
- import '@operato/spc/ox-chart-xbar-r.js'
5
- import '@operato/spc/ox-chart-i-mr.js'
6
- import '@operato/spc/ox-chart-u.js'
7
- import '@operato/spc/ox-chart-c.js'
8
- import '@operato/spc/ox-chart-p.js'
9
- import '@operato/spc/ox-chart-np.js'
10
-
11
- import { ScrollbarStyles, CommonHeaderStyles } from '@operato/styles'
12
- import { PageView, store } from '@operato/shell'
13
- import { css, html, PropertyValues, nothing } from 'lit'
14
- import { customElement, property, query, state } from 'lit/decorators.js'
15
- import { client } from '@operato/graphql'
16
- import { i18next, localize } from '@operato/i18n'
17
- import { isMobileDevice } from '@operato/utils'
18
- import { FilterConfig, FilterValue, OxFiltersFormBase } from '@operato/form'
19
-
20
- import { connect } from 'pwa-helpers/connect-mixin'
21
- import gql from 'graphql-tag'
22
-
23
- function formatDate(timestamp: number) {
24
- const date = new Date(timestamp)
25
- const year = date.getFullYear()
26
- const month = String(date.getMonth() + 1).padStart(2, '0') // 월은 0부터 시작하므로 1을 더함
27
- const day = String(date.getDate()).padStart(2, '0')
28
- const hours = String(date.getHours()).padStart(2, '0')
29
- const minutes = String(date.getMinutes()).padStart(2, '0')
30
- const seconds = String(date.getSeconds()).padStart(2, '0')
31
-
32
- return `${month}-${day} ${hours}:${minutes}`
33
- }
34
-
35
- @customElement('spc-chart-page')
36
- export class SpcChartPage extends connect(store)(localize(i18next)(PageView)) {
37
- static styles = [
38
- ScrollbarStyles,
39
- CommonHeaderStyles,
40
- css`
41
- :host {
42
- display: flex;
43
- flex-direction: column;
44
-
45
- width: 100%;
46
- }
47
-
48
- .chart {
49
- flex: 1;
50
- padding: var(--spacing-medium);
51
- }
52
- `
53
- ]
54
-
55
- @state() dataSetId?: string = ''
56
- @state() variable?: string = ''
57
- @state() chartType?: string = ''
58
- @state() spcChart: any
59
- @state() filtersValue?: FilterValue[]
60
- @state() variables: { display: string; value: string }[] = []
61
-
62
- @query('ox-filters-form-base') formBase!: OxFiltersFormBase
63
-
64
- private get filtersConfig(): FilterConfig[] {
65
- return [
66
- {
67
- name: 'dataSetId',
68
- type: 'resource-id',
69
- label: 'dataset',
70
- operator: 'eq',
71
- options: {
72
- queryName: 'dataSets'
73
- },
74
- onchange: (value, formBase) => {
75
- requestAnimationFrame(async () => {
76
- const variableFilter = formBase.getFieldByName('variable') as HTMLInputElement
77
- variableFilter.value = ''
78
-
79
- this.variables = [
80
- {
81
- display: '',
82
- value: ''
83
- },
84
- ...(await this.fetchVariables(value as string))
85
- ]
86
- })
87
-
88
- this.dataSetId = value
89
-
90
- return false /* whether filters-change event triggered or not */
91
- }
92
- },
93
- {
94
- name: 'variable',
95
- type: 'select',
96
- label: 'variable',
97
- operator: 'eq',
98
- options: () => this.variables,
99
- onchange: value => (this.variable = value)
100
- },
101
- {
102
- name: 'chartType',
103
- type: 'select',
104
- label: 'chart',
105
- operator: 'eq',
106
- options: ['Xbar-R', 'I-MR', 'C', 'U', 'P', 'NP'],
107
- onchange: value => (this.chartType = value)
108
- },
109
- {
110
- name: 'dateRange',
111
- type: 'date',
112
- label: 'date',
113
- operator: 'between',
114
- value: [
115
- {
116
- name: 'today',
117
- params: {
118
- relativeDays: -30
119
- }
120
- },
121
- {
122
- name: 'today',
123
- params: {
124
- relativeDays: -1
125
- }
126
- }
127
- ]
128
- }
129
- ]
130
- }
131
-
132
- get context() {
133
- return {
134
- title: i18next.t('title.spc-chart'),
135
- help: 'spc/spc-chart'
136
- }
137
- }
138
-
139
- render() {
140
- const { dataSet = {}, variable, charts = [] } = this.spcChart || {}
141
-
142
- var plotters: { chart: string; data: any[] }[] = []
143
-
144
- switch (this.chartType) {
145
- case 'Xbar-R':
146
- const xbar = charts.find(r => r.chartType == 'Xbar')
147
- const r = charts.find(r => r.chartType == 'R')
148
-
149
- plotters.push({ chart: 'xbar-r', data: xbar?.plots ?? [] })
150
- break
151
-
152
- case 'I-MR':
153
- const i = charts.find(r => r.chartType == 'I')
154
- const mr = charts.find(r => r.chartType == 'MR')
155
-
156
- plotters.push({ chart: 'i-mr', data: i?.plots ?? [] })
157
- break
158
-
159
- case 'P':
160
- const p = charts.find(r => r.chartType == 'P')
161
- plotters.push({ chart: 'p', data: p?.plots ?? [] })
162
- break
163
-
164
- case 'NP':
165
- const np = charts.find(r => r.chartType == 'NP')
166
- plotters.push({ chart: 'np', data: np?.plots ?? [] })
167
- break
168
-
169
- case 'C':
170
- const c = charts.find(r => r.chartType == 'C')
171
- plotters.push({ chart: 'c', data: c?.plots ?? [] })
172
- break
173
-
174
- case 'U':
175
- const u = charts.find(r => r.chartType == 'U')
176
- plotters.push({ chart: 'u', data: u?.plots ?? [] })
177
-
178
- default:
179
- }
180
-
181
- plotters = plotters.map(plotter => {
182
- return {
183
- ...plotter,
184
- data: plotter.data.map(plot => {
185
- return {
186
- ...plot,
187
- x: formatDate(Number(plot.x))
188
- }
189
- })
190
- }
191
- })
192
-
193
- return html`
194
- <div class="header">
195
- <div class="title"><md-icon>apps</md-icon>${i18next.t('title.spc-chart')}</div>
196
- <ox-filters-form-base
197
- class="filters"
198
- .value=${this.filtersValue}
199
- .filters=${this.filtersConfig}
200
- ?url-params-sensitive=${false}
201
- @filters-change=${(e: CustomEvent) => {
202
- this.fetchSpcChart()
203
- }}
204
- >
205
- </ox-filters-form-base>
206
- </div>
207
-
208
- ${plotters.map(({ chart, data }) =>
209
- chart == 'xbar-r'
210
- ? html` <ox-chart-xbar-r class="chart" .plots=${data}></ox-chart-xbar-r> `
211
- : chart == 'i-mr'
212
- ? html` <ox-chart-i-mr class="chart" .plots=${data}></ox-chart-i-mr> `
213
- : chart == 'u'
214
- ? html` <ox-chart-u class="chart" .plots=${data}></ox-chart-u> `
215
- : chart == 'c'
216
- ? html` <ox-chart-c class="chart" .plots=${data}></ox-chart-c> `
217
- : chart == 'p'
218
- ? html` <ox-chart-p class="chart" .plots=${data}></ox-chart-p> `
219
- : chart == 'np'
220
- ? html` <ox-chart-np class="chart" .plots=${data}></ox-chart-np> `
221
- : nothing
222
- )}
223
- `
224
- }
225
-
226
- updated(changes: PropertyValues<this>) {
227
- if (!changes.has('spcChart')) {
228
- this.fetchSpcChart()
229
- }
230
- }
231
-
232
- async pageUpdated(changes: any, lifecycle: any) {
233
- if (this.active) {
234
- this.dataSetId = lifecycle.resourceId
235
- }
236
- }
237
-
238
- async fetchSpcChart() {
239
- const {
240
- dataSetId,
241
- dateRange: [fromDate, toDate],
242
- variable,
243
- chartType
244
- } = (await this.formBase.getQueryFilters()).reduce((sum, field) => {
245
- sum[field.name] = field.value
246
- return sum
247
- }, {} as any)
248
-
249
- if (!(dataSetId && fromDate && toDate && variable && chartType)) {
250
- return
251
- }
252
-
253
- const response = await client.query({
254
- query: gql`
255
- query spcChart(
256
- $dataSetId: String!
257
- $variable: String!
258
- $chartType: String!
259
- $fromDate: String!
260
- $toDate: String!
261
- ) {
262
- spcChart(
263
- dataSetId: $dataSetId
264
- variable: $variable
265
- chartType: $chartType
266
- fromDate: $fromDate
267
- toDate: $toDate
268
- ) {
269
- dataSet {
270
- id
271
- name
272
- }
273
- variable
274
- charts {
275
- chartType
276
- controlLimits {
277
- ucl
278
- lcl
279
- cl
280
- }
281
- specLimits {
282
- usl
283
- lsl
284
- target
285
- }
286
- plots {
287
- x
288
- values
289
- xbar
290
- r
291
- i
292
- mr
293
- n
294
- defects
295
- }
296
- }
297
- }
298
- }
299
- `,
300
- variables: {
301
- dataSetId,
302
- variable,
303
- chartType,
304
- fromDate,
305
- toDate
306
- }
307
- })
308
-
309
- this.spcChart = response.data.spcChart
310
- }
311
-
312
- async fetchVariables(id: string): Promise<{ display: string; value: string }[]> {
313
- const response = await client.query({
314
- query: gql`
315
- query dataSet($id: String!) {
316
- dataSet(id: $id) {
317
- dataItems {
318
- name
319
- tag
320
- }
321
- }
322
- }
323
- `,
324
- variables: {
325
- id
326
- }
327
- })
328
-
329
- return response.data.dataSet.dataItems.map(dataItem => {
330
- return {
331
- display: dataItem.name,
332
- value: dataItem.name
333
- }
334
- })
335
- }
336
- }
package/client/route.ts DELETED
@@ -1,7 +0,0 @@
1
- export default function route(page) {
2
- switch (page) {
3
- case 'spc-chart':
4
- import('./pages/spc-chart-page')
5
- return page
6
- }
7
- }
@@ -1,13 +0,0 @@
1
- {
2
- "extends": "../../tsconfig-base.json",
3
- "compilerOptions": {
4
- "experimentalDecorators": true,
5
- "skipLibCheck": true,
6
- "strict": true,
7
- "declaration": true,
8
- "module": "esnext",
9
- "outDir": "../dist-client",
10
- "baseUrl": "./"
11
- },
12
- "include": ["./**/*"]
13
- }
@@ -1,44 +0,0 @@
1
- import { DataItemSpecSet, DataUseCase, EvaluationResult } from '@things-factory/dataset'
2
-
3
- export class DataUseCaseSPC implements DataUseCase {
4
- getSpecification(): DataItemSpecSet {
5
- return {
6
- name: 'SPC',
7
- description: 'Statistical Process Control Data Spec',
8
- help: '',
9
- specs: [
10
- {
11
- type: 'spc-limits' /* 'A value which seperates acceptability from unacceptability' */,
12
- label: 'control limits',
13
- name: 'controlLimits'
14
- }
15
- ]
16
- }
17
- }
18
-
19
- evaluate(spec: any, values: any | any[]): EvaluationResult {
20
- const { minimum, maximum, acceptables } = spec['controlLimits']
21
-
22
- if (!(values instanceof Array)) {
23
- values = [values]
24
- }
25
-
26
- for (let i = 0; i < values.length; i++) {
27
- const value = values[i]
28
-
29
- if (minimum != null && value < minimum) {
30
- return { oos: true, ooc: true }
31
- }
32
-
33
- if (maximum != null && value > maximum) {
34
- return { oos: true, ooc: true }
35
- }
36
-
37
- if (acceptables != null && !acceptables.includes(value)) {
38
- return { oos: true, ooc: true }
39
- }
40
- }
41
-
42
- return { oos: false, ooc: false }
43
- }
44
- }
@@ -1 +0,0 @@
1
- export * from './data-use-case-spc'
@@ -1,29 +0,0 @@
1
- import { SPCChartPlot } from '../../service/spc-chart/spc-chart-type'
2
-
3
- interface ProcessCapabilityAnalysis {
4
- cp: number
5
- cpk: number
6
- }
7
-
8
- function calculateProcessCapability(plots: SPCChartPlot[], USL: number, LSL: number): ProcessCapabilityAnalysis {
9
- // 전체 평균 계산
10
- const overallMean = plots.reduce((sum, plot) => sum + plot.xbar, 0) / plots.length
11
-
12
- // 전체 표준 편차 계산
13
- let allValues = plots.flatMap(plot => plot.values)
14
- const meanOfAllValues = allValues.reduce((sum, value) => sum + value, 0) / allValues.length
15
- const squaredDiffs = allValues.map(value => Math.pow(value - meanOfAllValues, 2))
16
- const variance = squaredDiffs.reduce((sum, diff) => sum + diff, 0) / (allValues.length - 1)
17
- const stdDeviation = Math.sqrt(variance)
18
-
19
- // CP 계산
20
- const cp = (USL - LSL) / (6 * stdDeviation)
21
-
22
- // CPK 계산
23
- const cpk = Math.min((USL - overallMean) / (3 * stdDeviation), (overallMean - LSL) / (3 * stdDeviation))
24
-
25
- return {
26
- cp,
27
- cpk
28
- }
29
- }
@@ -1,31 +0,0 @@
1
- import { SPCChartPlot, SPCChartAnalysis } from '../../service/spc-chart/spc-chart-type'
2
-
3
- export function calculateCChartAnalysisResult(plots: SPCChartPlot[]): SPCChartAnalysis {
4
- // 각 샘플의 평균을 계산
5
- plots = plots.map(plot => ({
6
- ...plot,
7
- defects: plot.values.map(Boolean).length,
8
- n: plot.values.length
9
- }))
10
-
11
- // 총 결함 수를 계산합니다.
12
- const totalDefects = plots.reduce((sum, { defects }) => sum + defects, 0)
13
-
14
- // 평균 결함 수(C-bar)를 계산합니다.
15
- const cl = totalDefects / plots.length
16
-
17
- // 상한 제어선(UCL)과 하한 제어선(LCL)을 계산합니다.
18
- // 포아송 분포를 가정할 때, UCL = C-bar + 3*sqrt(C-bar), LCL = C-bar - 3*sqrt(C-bar) (단, LCL이 음수인 경우 0으로 설정)
19
- const ucl = cl + 3 * Math.sqrt(cl)
20
- const lcl = Math.max(cl - 3 * Math.sqrt(cl), 0)
21
-
22
- return {
23
- chartType: 'C',
24
- controlLimits: {
25
- ucl,
26
- lcl,
27
- cl
28
- },
29
- plots
30
- }
31
- }
@@ -1,38 +0,0 @@
1
- interface HistogramSample {
2
- value: number
3
- }
4
-
5
- interface HistogramBin {
6
- binRange: string // 구간을 나타내는 문자열, 예: "0-1"
7
- count: number // 해당 구간에 속하는 데이터의 개수
8
- }
9
-
10
- interface HistogramResult {
11
- chartType: 'Histogram'
12
- bins: HistogramBin[] // 히스토그램의 각 구간과 해당 구간의 데이터 개수
13
- }
14
-
15
- export function calculateHistogram(samples: HistogramSample[], numBins: number): HistogramResult {
16
- const values = samples.map(sample => sample.value)
17
- const minValue = Math.min(...values)
18
- const maxValue = Math.max(...values)
19
- const range = maxValue - minValue
20
- const binWidth = range / numBins
21
-
22
- // 초기화된 구간 배열 생성
23
- const bins: HistogramBin[] = Array.from({ length: numBins }, (_, i) => ({
24
- binRange: `${(minValue + i * binWidth).toFixed(2)}-${(minValue + (i + 1) * binWidth).toFixed(2)}`,
25
- count: 0
26
- }))
27
-
28
- // 각 샘플을 적절한 구간에 할당하고 개수를 세어 구간의 개수를 업데이트합니다.
29
- samples.forEach(sample => {
30
- const binIndex = Math.min(numBins - 1, Math.floor((sample.value - minValue) / binWidth))
31
- bins[binIndex].count++
32
- })
33
-
34
- return {
35
- chartType: 'Histogram',
36
- bins
37
- }
38
- }
@@ -1,31 +0,0 @@
1
- import { SPCChartPlot, SPCChartAnalysis } from '../../service/spc-chart/spc-chart-type'
2
-
3
- export function calculateIChartAnalysisResult(plots: SPCChartPlot[]): SPCChartAnalysis {
4
- // 각 샘플의 평균을 계산
5
- plots = plots.map((plot, index) => ({
6
- ...plot,
7
- i: plot.values[0],
8
- mr: index == 0 ? 0 : Math.abs(plot.values[0] - plots[index - 1].values[0])
9
- }))
10
-
11
- // 개별 측정값의 평균을 계산하여 중심선(cl)을 구합니다.
12
- const cl = plots.reduce((acc, plot) => acc + plot.values[0], 0) / plots.length
13
-
14
- // 개별 측정값의 표준편차를 계산합니다.
15
- const standardDeviation = Math.sqrt(plots.reduce((acc, plot) => acc + Math.pow(plot.values[0] - cl, 2), 0) / plots.length)
16
-
17
- // 공정 변동을 고려하여 상한 제어선(ucl)과 하한 제어선(lcl)을 계산합니다.
18
- // 여기서는 3시그마(3 * 표준편차)를 사용합니다. 상황에 따라 적절한 시그마 수준을 조정할 수 있습니다.
19
- const ucl = cl + 3 * standardDeviation
20
- const lcl = cl - 3 * standardDeviation
21
-
22
- return {
23
- chartType: 'I',
24
- controlLimits: {
25
- cl,
26
- ucl,
27
- lcl
28
- },
29
- plots
30
- }
31
- }
@@ -1,171 +0,0 @@
1
- const result = {
2
- dataset: {
3
- id: 'aaa',
4
- name: 'bbb'
5
- },
6
- query: {},
7
- analysisResults: [
8
- {
9
- chartType: 'Xbar',
10
- variables: [
11
- {
12
- name: 'variable1',
13
- stats: {
14
- mean: 22,
15
- UCL: 25,
16
- LCL: 19,
17
- CL: 22
18
- },
19
- samples: [
20
- { x: '1', values: 22 },
21
- { x: '2', values: 23 }
22
- ]
23
- }
24
- ]
25
- },
26
- {
27
- chartType: 'R',
28
- variables: [
29
- {
30
- name: 'variable2',
31
- stats: {
32
- range: 1.3,
33
- UCL: 2.1,
34
- LCL: 0.5,
35
- CL: 1.3
36
- },
37
- samples: [
38
- { x: '1', value: 1.2 },
39
- { x: '2', value: 1.3 }
40
- ]
41
- }
42
- ]
43
- },
44
- {
45
- chartType: 'p',
46
- variables: [
47
- {
48
- name: 'variable3',
49
- stats: {
50
- proportion: 0.05,
51
- UCL: 0.1,
52
- LCL: 0.01
53
- },
54
- samples: [
55
- { x: '1', value: 0.05 },
56
- { x: '2', value: 0.06 }
57
- ]
58
- }
59
- ]
60
- },
61
- {
62
- chartType: 'np',
63
- variables: [
64
- {
65
- name: 'variable4',
66
- stats: {
67
- nonconforming: 5,
68
- UCL: 10,
69
- LCL: 1
70
- },
71
- samples: [
72
- { x: '1', value: 5 },
73
- { x: '2', value: 4 }
74
- ]
75
- }
76
- ]
77
- },
78
- {
79
- chartType: 'c',
80
- variables: [
81
- {
82
- name: 'variable5',
83
- stats: {
84
- count: 20,
85
- UCL: 25,
86
- LCL: 15
87
- },
88
- samples: [
89
- { x: '1', value: 20 },
90
- { x: '2', value: 21 }
91
- ]
92
- }
93
- ]
94
- },
95
- {
96
- chartType: 'u',
97
- variables: [
98
- {
99
- name: 'variable6',
100
- stats: {
101
- defectsPerUnit: 1.2,
102
- UCL: 1.5,
103
- LCL: 0.9
104
- },
105
- samples: [
106
- { x: '1', value: 1.2 },
107
- { x: '2', value: 1.1 }
108
- ]
109
- }
110
- ]
111
- },
112
- {
113
- chartType: 'I',
114
- variables: [
115
- {
116
- name: 'variable7',
117
- stats: {
118
- individualValue: 10,
119
- UCL: 12,
120
- LCL: 8
121
- },
122
- samples: [
123
- { x: '1', value: 10 },
124
- { x: '2', value: 11 }
125
- ]
126
- }
127
- ]
128
- },
129
- {
130
- chartType: 'MR',
131
- variables: [
132
- {
133
- name: 'variable8',
134
- stats: {
135
- movingRange: 2,
136
- UCL: 3,
137
- LCL: 1
138
- },
139
- samples: [
140
- { x: '1', value: 2 },
141
- { x: '2', value: 2.5 }
142
- ]
143
- }
144
- ]
145
- },
146
- {
147
- chartType: 'Histogram',
148
- variables: [
149
- {
150
- name: 'variable9',
151
- bins: [
152
- { binRange: '0-1', count: 5 },
153
- { binRange: '1-2', count: 10 }
154
- ]
155
- }
156
- ]
157
- },
158
- {
159
- chartType: 'Pareto',
160
- variables: [
161
- {
162
- name: 'defectType',
163
- categories: [
164
- { category: 'Scratch', count: 20 },
165
- { category: 'Dent', count: 15 }
166
- ]
167
- }
168
- ]
169
- }
170
- ]
171
- }
@@ -1,30 +0,0 @@
1
- import { SPCChartPlot, SPCChartAnalysis } from '../../service/spc-chart/spc-chart-type'
2
-
3
- export function calculateMRChartAnalysisResult(plots: SPCChartPlot[]): SPCChartAnalysis {
4
- // 각 샘플의 평균을 계산
5
- plots = plots.map((plot, index) => ({
6
- ...plot,
7
- i: plot.values[0],
8
- mr: index == 0 ? 0 : Math.abs(plot.values[0] - plots[index - 1].values[0])
9
- }))
10
-
11
- // 변동 범위의 평균을 계산하여 중심선(CL)을 구합니다.
12
- const cl = plots.slice(1).reduce((acc, plot) => acc + plot.mr, 0) / (plots.length - 1)
13
-
14
- // 공정 변동을 고려하여 상한 제어선(UCL)을 계산합니다.
15
- // MR 차트에서는 3.267 * MR-Bar을 사용합니다 (2개의 측정값을 사용하는 경우).
16
- const ucl = 3.267 * cl
17
-
18
- // MR 차트에서 하한 제어선(LCL)은 일반적으로 사용되지 않거나 0으로 설정됩니다.
19
- const lcl = 0 // 하한 제어선
20
-
21
- return {
22
- chartType: 'MR',
23
- controlLimits: {
24
- cl,
25
- ucl,
26
- lcl
27
- },
28
- plots
29
- }
30
- }
@@ -1,30 +0,0 @@
1
- import { SPCChartPlot, SPCChartAnalysis } from '../../service/spc-chart/spc-chart-type'
2
-
3
- export function calculateNPChartAnalysisResult(plots: SPCChartPlot[]): SPCChartAnalysis {
4
- // 각 샘플의 평균을 계산
5
- plots = plots.map(plot => ({
6
- ...plot,
7
- defects: plot.values.reduce((sum, v) => sum + v, 0),
8
- n: plot.values.length
9
- }))
10
-
11
- const totalNonConforming = plots.reduce((acc, plot) => acc + plot.defects, 0)
12
- const totalUnits = plots.reduce((acc, plot) => acc + plot.n, 0)
13
- const NPBar = totalNonConforming / plots.length // 평균 불량품 수
14
-
15
- // p 차트 공식을 사용하여 UCL과 LCL을 계산합니다.
16
- const pBar = totalNonConforming / totalUnits // 전체 불량품 비율
17
- const ucl = NPBar + 3 * Math.sqrt(NPBar * (1 - pBar))
18
- let lcl = NPBar - 3 * Math.sqrt(NPBar * (1 - pBar))
19
- lcl = lcl < 0 ? 0 : lcl // LCL이 음수인 경우 0으로 설정
20
-
21
- return {
22
- chartType: 'NP',
23
- controlLimits: {
24
- ucl,
25
- lcl,
26
- cl: NPBar
27
- },
28
- plots
29
- }
30
- }
@@ -1,29 +0,0 @@
1
- import { SPCChartPlot, SPCChartAnalysis } from '../../service/spc-chart/spc-chart-type'
2
-
3
- export function calculatePChartAnalysisResult(plots: SPCChartPlot[]): SPCChartAnalysis {
4
- // 각 샘플의 평균을 계산
5
- plots = plots.map(plot => ({
6
- ...plot,
7
- defects: plot.values.reduce((sum, v) => sum + v, 0),
8
- n: plot.values.length
9
- }))
10
-
11
- // 결함 있는 단위의 평균 비율(PBar)을 계산합니다.
12
- const totalDefectiveUnits = plots.reduce((acc, plot) => acc + plot.defects, 0)
13
- const totalUnits = plots.reduce((acc, plot) => acc + plot.n, 0)
14
- const PBar = totalDefectiveUnits / totalUnits
15
-
16
- // 샘플 그룹의 크기(n)에 따라 UCL과 LCL을 계산합니다.
17
- const ucl = PBar + 3 * Math.sqrt((PBar * (1 - PBar)) / totalUnits)
18
- const lcl = PBar - 3 * Math.sqrt((PBar * (1 - PBar)) / totalUnits) < 0 ? 0 : PBar - 3 * Math.sqrt((PBar * (1 - PBar)) / totalUnits) // LCL이 음수가 되지 않도록 처리
19
-
20
- return {
21
- chartType: 'P',
22
- controlLimits: {
23
- ucl,
24
- lcl,
25
- cl: PBar
26
- },
27
- plots
28
- }
29
- }
@@ -1,32 +0,0 @@
1
- interface ParetoCategory {
2
- category: string // 카테고리 이름
3
- count: number // 해당 카테고리의 데이터 개수 또는 중요도
4
- }
5
-
6
- interface ParetoResult {
7
- chartType: 'Pareto'
8
- categories: ParetoCategory[] // Pareto 차트에 표시될 카테고리 및 중요도 정보
9
- }
10
-
11
- export function calculatePareto(data: { category: string }[]): ParetoResult {
12
- // 각 카테고리별 데이터 개수 계산
13
- const categoryCounts: { [category: string]: number } = {}
14
- data.forEach(item => {
15
- const category = item.category
16
- categoryCounts[category] = (categoryCounts[category] || 0) + 1
17
- })
18
-
19
- // 카테고리와 데이터 개수를 배열로 변환
20
- const categories: ParetoCategory[] = Object.keys(categoryCounts).map(category => ({
21
- category,
22
- count: categoryCounts[category]
23
- }))
24
-
25
- // 데이터 개수에 따라 내림차순으로 정렬
26
- categories.sort((a, b) => b.count - a.count)
27
-
28
- return {
29
- chartType: 'Pareto',
30
- categories
31
- }
32
- }
@@ -1,34 +0,0 @@
1
- import { SPCChartPlot, SPCChartAnalysis } from '../../service/spc-chart/spc-chart-type'
2
-
3
- export function calculateRChartAnalysisResult(plots: SPCChartPlot[]): SPCChartAnalysis {
4
- // 각 샘플 그룹의 범위를 계산합니다.
5
- plots = plots.map(plot => {
6
- return {
7
- ...plot,
8
- r: Math.max(...plot.values) - Math.min(...plot.values)
9
- }
10
- })
11
-
12
- // 모든 샘플 그룹의 범위의 평균값(R-bar)을 계산합니다.
13
- const RBar = plots.reduce((acc, plot) => acc + plot.r, 0) / plots.length
14
-
15
- // 샘플 그룹의 크기(n)에 따라 D3와 D4 값을 설정합니다.
16
- // 예시에서는 n=5인 경우의 D3와 D4 값을 사용합니다. 실제 구현에서는 샘플 크기에 맞는 값을 사용해야 합니다.
17
- const n = plots[0]?.values.length // 샘플 크기 추정(모든 그룹이 같은 크기를 가정)
18
- const D3 = 0 // n=5에 대한 D3 값
19
- const D4 = 2.114 // n=5에 대한 D4 값
20
-
21
- // UCL과 LCL을 계산합니다.
22
- const ucl = D4 * RBar
23
- const lcl = D3 * RBar // D3가 0인 경우, LCL은 0이 됩니다.
24
-
25
- return {
26
- chartType: 'R',
27
- controlLimits: {
28
- ucl,
29
- lcl,
30
- cl: RBar
31
- },
32
- plots
33
- }
34
- }
@@ -1,31 +0,0 @@
1
- import { SPCChartPlot, SPCChartAnalysis } from '../../service/spc-chart/spc-chart-type'
2
-
3
- export function calculateUChartAnalysisResult(plots: SPCChartPlot[]): SPCChartAnalysis {
4
- // 각 샘플의 평균을 계산
5
- plots = plots.map(plot => ({
6
- ...plot,
7
- defects: plot.values.map(Boolean).length,
8
- n: plot.values.length
9
- }))
10
-
11
- // 단위당 결함 수의 평균(UBar)을 계산합니다.
12
- const totalDefects = plots.reduce((acc, plot) => acc + plot.defects, 0)
13
- const totalUnits = plots.reduce((acc, plot) => acc + plot.n, 0)
14
- const UBar = totalDefects / totalUnits
15
-
16
- // 샘플 그룹의 크기(n)에 따라 상수를 결정합니다. 여기서는 일반적인 상수 값을 사용합니다.
17
- const n = plots.length // 샘플 그룹의 수
18
- // UCL과 LCL 계산을 위한 상수. 실제 값은 샘플 크기와 분포에 따라 달라질 수 있습니다.
19
- const ucl = UBar + 3 * Math.sqrt(UBar / totalUnits)
20
- const lcl = UBar - 3 * Math.sqrt(UBar / totalUnits) < 0 ? 0 : UBar - 3 * Math.sqrt(UBar / totalUnits) // LCL이 음수가 되지 않도록 처리
21
-
22
- return {
23
- chartType: 'U',
24
- controlLimits: {
25
- ucl,
26
- lcl,
27
- cl: UBar
28
- },
29
- plots
30
- }
31
- }
@@ -1,53 +0,0 @@
1
- import { SPCChartPlot, SPCChartAnalysis } from '../../service/spc-chart/spc-chart-type'
2
-
3
- function getA2Value(sampleSize: number): number {
4
- const a2Values: { [key: number]: number } = {
5
- 2: 1.88,
6
- 3: 1.023,
7
- 4: 0.729,
8
- 5: 0.577,
9
- 6: 0.483
10
- // 다른 샘플 크기에 대한 A2 값 추가...
11
- }
12
-
13
- return a2Values[sampleSize] || 0 // 샘플 크기에 대한 A2 값이 정의되지 않은 경우 0을 반환
14
- }
15
-
16
- export function calculateXBarAnalysisResult(plots: SPCChartPlot[]): SPCChartAnalysis {
17
- // 각 샘플의 평균을 계산
18
- plots = plots.map(plot => ({
19
- ...plot,
20
- xbar: plot.values ? plot.values.reduce((sum, val) => sum + val, 0) / plot.values.length : 0,
21
- r: Math.max(...plot.values) - Math.min(...plot.values)
22
- }))
23
-
24
- const plotSize = plots[0]?.values?.length // 샘플 크기 추정(모든 그룹이 같은 크기를 가정)
25
-
26
- // 각 샘플의 범위(R) 계산
27
- const ranges = plots.map(plot => {
28
- if (plot.values) {
29
- return Math.max(...plot.values) - Math.min(...plot.values)
30
- }
31
- return 0
32
- })
33
-
34
- // 범위(R)의 평균 계산
35
- const averageRange = ranges.reduce((sum, r) => sum + r, 0) / ranges.length
36
-
37
- // 전체 평균(중심선, CL) 계산
38
- const overallMean = plots.reduce((sum, plot) => sum + plot.xbar, 0) / plots.length
39
-
40
- const A2 = getA2Value(plotSize)
41
- const ucl = overallMean + A2 * averageRange
42
- const lcl = overallMean - A2 * averageRange
43
-
44
- return {
45
- chartType: 'Xbar',
46
- controlLimits: {
47
- ucl,
48
- lcl,
49
- cl: overallMean
50
- },
51
- plots
52
- }
53
- }
@@ -1,53 +0,0 @@
1
- import { SPCChartPlot, SPCChartAnalysis } from '../../service/spc-chart/spc-chart-type'
2
-
3
- function getA2Value(sampleSize: number): number {
4
- const a2Values: { [key: number]: number } = {
5
- 2: 1.88,
6
- 3: 1.023,
7
- 4: 0.729,
8
- 5: 0.577,
9
- 6: 0.483
10
- // 다른 샘플 크기에 대한 A2 값 추가...
11
- }
12
-
13
- return a2Values[sampleSize] || 0 // 샘플 크기에 대한 A2 값이 정의되지 않은 경우 0을 반환
14
- }
15
-
16
- export function calculateXBarAnalysisResult(plots: SPCChartPlot[]): SPCChartAnalysis {
17
- // 각 샘플의 평균을 계산
18
- plots = plots.map(plot => ({
19
- ...plot,
20
- xbar: plot.values ? plot.values.reduce((sum, val) => sum + val, 0) / plot.values.length : 0,
21
- r: Math.max(...plot.values) - Math.min(...plot.values)
22
- }))
23
-
24
- const plotSize = plots[0]?.values?.length // 샘플 크기 추정(모든 그룹이 같은 크기를 가정)
25
-
26
- // 각 샘플의 범위(R) 계산
27
- const ranges = plots.map(plot => {
28
- if (plot.values) {
29
- return Math.max(...plot.values) - Math.min(...plot.values)
30
- }
31
- return 0
32
- })
33
-
34
- // 범위(R)의 평균 계산
35
- const averageRange = ranges.reduce((sum, r) => sum + r, 0) / ranges.length
36
-
37
- // 전체 평균(중심선, CL) 계산
38
- const overallMean = plots.reduce((sum, plot) => sum + plot.xbar, 0) / plots.length
39
-
40
- const A2 = getA2Value(plotSize)
41
- const ucl = overallMean + A2 * averageRange
42
- const lcl = overallMean - A2 * averageRange
43
-
44
- return {
45
- chartType: 'Xbar',
46
- controlLimits: {
47
- ucl,
48
- lcl,
49
- cl: overallMean
50
- },
51
- plots
52
- }
53
- }
package/server/index.ts DELETED
@@ -1,10 +0,0 @@
1
- export * from './service'
2
-
3
- import { DataUseCase } from '@things-factory/dataset'
4
- import { DataUseCaseSPC } from './controllers'
5
-
6
- process.on('bootstrap-module-start' as any, async ({ app, config, schema }: any) => {
7
- DataUseCase.registerUseCase('SPC', new DataUseCaseSPC())
8
-
9
- console.log('[spc:bootstrap] SPC has just registered as a DataUseCase.')
10
- })
@@ -1,21 +0,0 @@
1
- /* EXPORT ENTITY TYPES */
2
-
3
- /* IMPORT ENTITIES AND RESOLVERS */
4
- import { entities as SpcChartEntities, resolvers as SpcChartResolvers, subscribers as SpcChartSubscribers } from './spc-chart'
5
-
6
- export const entities = [
7
- /* ENTITIES */
8
- ...SpcChartEntities
9
- ]
10
-
11
- export const subscribers = [
12
- /* SUBSCRIBERS */
13
- ...SpcChartSubscribers
14
- ]
15
-
16
- export const schema = {
17
- resolverClasses: [
18
- /* RESOLVER CLASSES */
19
- ...SpcChartResolvers
20
- ]
21
- }
@@ -1,5 +0,0 @@
1
- import { SpcChartQuery } from './spc-chart-query'
2
-
3
- export const entities = []
4
- export const resolvers = [SpcChartQuery]
5
- export const subscribers = []
@@ -1,102 +0,0 @@
1
- import { Resolver, Query, Arg, Ctx } from 'type-graphql'
2
- import { Between, In } from 'typeorm'
3
-
4
- import { getRepository } from '@things-factory/shell'
5
- import { User } from '@things-factory/auth-base'
6
- import { DataSet, DataSample } from '@things-factory/dataset'
7
-
8
- import { SPCChartAnalysis, SPCChartAnalysisResult } from './spc-chart-type'
9
- import { calculateXBarAnalysisResult } from '../../controllers/spc-chart/x-bar'
10
- import { calculateRChartAnalysisResult } from '../../controllers/spc-chart/r'
11
- import { calculateIChartAnalysisResult } from '../../controllers/spc-chart/i'
12
- import { calculateMRChartAnalysisResult } from '../../controllers/spc-chart/mr'
13
- import { calculateUChartAnalysisResult } from '../../controllers/spc-chart/u'
14
- import { calculateCChartAnalysisResult } from '../../controllers/spc-chart/c'
15
- import { calculatePChartAnalysisResult } from '../../controllers/spc-chart/p'
16
- import { calculateNPChartAnalysisResult } from '../../controllers/spc-chart/np'
17
-
18
- @Resolver(DataSet)
19
- export class SpcChartQuery {
20
- @Query(returns => SPCChartAnalysisResult!, { nullable: true, description: 'To fetch a SpcChart' })
21
- async spcChart(
22
- @Arg('dataSetId') dataSetId: string,
23
- @Arg('variable') variable: string,
24
- @Arg('chartType') chartType: string,
25
- @Arg('fromDate') fromDate: string,
26
- @Arg('toDate') toDate: string,
27
- @Ctx() context: ResolverContext
28
- ): Promise<SPCChartAnalysisResult> {
29
- const { domain } = context.state
30
-
31
- const dataSet = (await getRepository(DataSet).findOne({
32
- where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, id: dataSetId }
33
- })) as DataSet
34
-
35
- if (!dataSet) {
36
- throw 'no given dataset'
37
- }
38
-
39
- const dataItem = dataSet.dataItems.find(dataItem => dataItem.name == variable)
40
-
41
- if (!dataItem) {
42
- throw 'no given variables in the dataset'
43
- }
44
-
45
- // TODO timezone
46
- const fromTime = new Date(fromDate) /* default: 30days before */
47
- const toTime = new Date(toDate) /* default: today */
48
-
49
- const dataSamples = (await getRepository(DataSample).find({
50
- where: {
51
- dataSet: { id: dataSet.id },
52
- createdAt: Between(fromTime, toTime)
53
- }
54
- })) as DataSample[]
55
-
56
- const tag = dataItem.tag
57
- const samples = dataSamples
58
- .map(dataSample => {
59
- const data = dataSample.data[tag]
60
-
61
- return {
62
- x: dataSample.createdAt,
63
- values: Array.isArray(data) ? data : [data]
64
- }
65
- })
66
- .filter(sample => {
67
- const { x, values } = sample
68
- return x && values && values.length > 0 && values.every(v => v ?? false)
69
- })
70
-
71
- const charts = [] as SPCChartAnalysis[]
72
-
73
- switch (chartType) {
74
- case 'Xbar-R':
75
- charts.push(calculateXBarAnalysisResult(samples as any))
76
- charts.push(calculateRChartAnalysisResult(samples as any))
77
- break
78
- case 'I-MR':
79
- charts.push(calculateIChartAnalysisResult(samples as any))
80
- charts.push(calculateMRChartAnalysisResult(samples as any))
81
- break
82
- case 'C':
83
- charts.push(calculateUChartAnalysisResult(samples as any))
84
- break
85
- case 'U':
86
- charts.push(calculateCChartAnalysisResult(samples as any))
87
- break
88
- case 'P':
89
- charts.push(calculatePChartAnalysisResult(samples as any))
90
- break
91
- case 'NP':
92
- charts.push(calculateNPChartAnalysisResult(samples as any))
93
- break
94
- }
95
-
96
- return {
97
- dataSet,
98
- variable,
99
- charts
100
- }
101
- }
102
- }
@@ -1,105 +0,0 @@
1
- import { ObjectType, Field, ID, Int, Float } from 'type-graphql'
2
-
3
- import { DataSet } from '@things-factory/dataset'
4
-
5
- @ObjectType()
6
- export class SPCChartPlot {
7
- @Field(type => ID)
8
- x: string
9
-
10
- @Field(type => [Float], { nullable: true })
11
- values?: number[]
12
-
13
- @Field(type => Float, { nullable: true })
14
- xbar?: number
15
-
16
- @Field(type => Float, { nullable: true })
17
- r?: number
18
-
19
- @Field(type => Float, { nullable: true })
20
- i?: number
21
-
22
- @Field(type => Float, { nullable: true })
23
- mr?: number
24
-
25
- @Field(type => Float, { nullable: true })
26
- n?: number
27
-
28
- @Field(type => Float, { nullable: true })
29
- defects?: number
30
- }
31
-
32
- @ObjectType()
33
- class SPCControlLimits {
34
- @Field(type => Float, { nullable: true })
35
- ucl?: number
36
-
37
- @Field(type => Float, { nullable: true })
38
- lcl?: number
39
-
40
- @Field(type => Float, { nullable: true })
41
- cl?: number
42
- }
43
-
44
- @ObjectType()
45
- class SPCSpecLimits {
46
- @Field(type => Float, { nullable: true })
47
- target?: number
48
-
49
- @Field(type => Float, { nullable: true })
50
- lsl?: number
51
-
52
- @Field(type => Float, { nullable: true })
53
- usl?: number
54
- }
55
-
56
- @ObjectType()
57
- export class SPCChartAnalysis {
58
- @Field()
59
- chartType: string
60
-
61
- @Field(type => SPCControlLimits, { nullable: true })
62
- controlLimits?: SPCControlLimits
63
-
64
- @Field(type => SPCSpecLimits, { nullable: true })
65
- specLimits?: SPCSpecLimits
66
-
67
- @Field(type => [SPCChartPlot], { nullable: true })
68
- plots?: SPCChartPlot[]
69
-
70
- // @Field(type => [Bin], { nullable: true })
71
- // bins?: Bin[]
72
-
73
- // @Field(type => [Category], { nullable: true })
74
- // categories?: Category[]
75
- }
76
-
77
- // @ObjectType()
78
- // class Bin {
79
- // @Field()
80
- // binRange: string
81
-
82
- // @Field()
83
- // count: number
84
- // }
85
-
86
- // @ObjectType()
87
- // class Category {
88
- // @Field()
89
- // category: string
90
-
91
- // @Field()
92
- // count: number
93
- // }
94
-
95
- @ObjectType()
96
- export class SPCChartAnalysisResult {
97
- @Field(type => DataSet)
98
- dataSet: DataSet
99
-
100
- @Field()
101
- variable: string
102
-
103
- @Field(type => [SPCChartAnalysis])
104
- charts: SPCChartAnalysis[]
105
- }
@@ -1,10 +0,0 @@
1
- {
2
- "extends": "../../tsconfig-base.json",
3
- "compilerOptions": {
4
- "strict": false,
5
- "module": "commonjs",
6
- "outDir": "../dist-server",
7
- "baseUrl": "./"
8
- },
9
- "include": ["./**/*"]
10
- }