develop-db_models: DB 설계 및 models 파일 개발/수정
develop-routes: routes, dao, service, lib 폴더 개발/수정
안전한 작업을 위해 develop branch에서 main을 pull해오고 push한다음, main에서 develop branch를 pull해온다.
위와 같은 방법이 꼭 좋은 방법은 아니다. 가능하면 main에서 바로 develop branch를 pull해오자.
git merge <branch 명>
이렇게 머지하면 로컬에서 머지가 되는데 commit이 동시에 된다.
확인은 git log로 한다.
로컬에서 main으로 develop branch를 merge하고!! -> git push origin main
을 하면!!! develop branch tag는 그자리에 그대로 있겠으나, branch 화살표가 main으로 향해 있을 것이다. 그다음에 아래에 있는 git 착각하기 쉬운 점
에서 tag를 옮기는 방법을 참고하면 된다.
branch와 tag는 다른것이다.
develop branch가 main에 pull당해도
내가 tag라고 표현하는 회색 branch명
이 main과 같은 위치에 있지 아니하면, 그 해당하는 branch명
의 현 위치는 main 과 같은것이 아니다. 이것을 착각하면 기껏 개발한 코드를 날릴 수가 있다.
아래 캡처처럼 주황색 원있는곳에 각각 태그가 있었으나, develop branch 상태에서git checout develop-db_models
-> git pull origin main
을 하였고, -> 그다음에 git push origin develop-db_models
이순서로 진행했다.
중간중간 git log
로 현상황을 확인해야 한다.
아래 사진에 표시한 것처럼 develop-routes branch는 main과 다른 위치에 있었다.
내가 원하는것은 이미 branch는 병합이 되었으니 develop-routes
라 하는 tag를 main과 같은 위치로 옮기고 싶은 것이다.
아래 사진은 먼저 테스트하고 나서의 결과이다.
한번더 해보자 이번에는 develop-db_models
branch를 옮길 것이다.
routes/auth.js - 로그인 라우터
lib/tokenUtil.js - 로그인 완료 후, client에게 jwt 형식으로 token 발행
acess token
refresh token
cookie
session
XSS 공격
CSRF 공격
작업지시서(작업전, 작업중 list) - 완료이력 - 중단이력
위의 4가지 data를 select 하여 list화 하여야 한다.
작업 상태 분류를 위한, workStatuses 테이블 추가
컬럼 명: id(PK), workStatus(Unique)
'0' : beforeWorking - 작업 전,
'1' : working - 작업 중,
'2' : finishingWork - 작업 완료,
'3' : emoWork - 작업 중단
orders 테이블에 workStatus 컬럼 추가 (FK)
작업현황 page에서 '작업지시서 등록'하면 workStatus = 0 (default)
'작업시작' 버튼 누르면 workStatus = 1
'작업완료' 버튼 누르면 workStatus = 2
'작업중단' 버튼 누르면 workStatus = 3
완료이력 page list 출력 시, workStatus(작업 상태) 컬럼 추가
설비를 모니터링하고 제어하기 위해 설비 데이터를 수집해야한다.
우리가 사용하는 edukit은 시계열 데이터베이스를 이용하는 것이 합리적으로 판단하여(팀원과 의논한 결과) influxDB를 사용하기로 하였다.
docker-compose up -d
복사까지 완료하면 docker-compose up -d
를 했을 때, 아래처럼 설정대로 data, meta, wal폴더가 생성된다.
이상태가 되면 이제 influxdb와 연동이 된 것이다.
docker-compose.yaml
version: '3.9' services: influxdb: image: influxdb:1.8.10 ports: - 8087:8086 # default 설정 8086이나 테스트위해 8086을 로컬 influxdb로 띄웠기 때문에 8087을 사용한다. volumes: # - ./influx/config:/etc/influxdb # influxdb.conf 파일은 수작업으로 가져와야한다. - ./influx/storage:/var/lib/influxdb tty: true # bash 사용 stdin_open: true networks: webnet: # docker-compose up -d // 서비스 실행 # docker-compose ps // 서비스 실행 상태 확인 # docker-compose exec simulator-server npm i // 시뮬레이터 서버 npm 모듈 설치 # docker-compose exec simulator-server npm run docker // 시뮬레이터 서버 실행 # mqttbox를 이용하여 테스트
참조:influx tutorials
참조:node-influx(new InfluxDB)에 관하여
mqtt 연동하여 데이터를 받아온다. on
이벤트로 데이터를 수집한다.
new InfluxDB()로 연결 옵션 집합을 지정하여 단일 InfluxDB 인스턴스에 연결한다.
.getDatabaseNames()로 data 존재를 확인한뒤(query: show databases)
없으면 createDatabase()로 data를 생성한다.
writePoints()로 가공완료한 데이터를 입력한다.(query: insert <measurement명>,<tag키>=<tag값>,<tag키1>=<tag값1> <fields키>=<fields값>,<fields키1>=<fields값1> <옵션>
node.js에서 data를 입력할때, 객체형으로 가공하애야 한다.
const <tag용 객체 변수> = {
<tag키>: <tag값>
...
}
const <fields용 객체 변수> = {
<fields키>: <fields값>
...
}
위의 상태에서 node.js와 연동이 가능하다.
subscrib.js 파일에 테스트용으로 설정하였다.
const express = require('express'); const http = require('http'); const mqtt = require('mqtt'); const Influx = require('influx'); const app = express(); // const client = mqtt.connect('mqtt://220.90.129.47:1883'); const client = mqtt.connect('mqtt://220.90.129.60:1883'); // const client = mqtt.connect('mqtt://localhost'); // 나홀로 test용 client.subscribe('#'); // Connect to a single InfluxDB instance by specifying a set of connection options. const influx = new Influx.InfluxDB({ // host: 'hehe-back_influxdb_1', host: 'localhost', port: '8087', database: 'backend', schema: [ { measurement: 'plcdata', fields: { // DataTime: Influx.FieldType.DataTime, Start: Influx.FieldType.BOOLEAN, No1PartsError: Influx.FieldType.BOOLEAN, No1_Action: Influx.FieldType.BOOLEAN, No2_Action: Influx.FieldType.BOOLEAN, No3Ready: Influx.FieldType.BOOLEAN, ColorSensor: Influx.FieldType.BOOLEAN, VisionSensor: Influx.FieldType.INTEGER, Reset: Influx.FieldType.BOOLEAN, no1_on_off: Influx.FieldType.BOOLEAN, no2_on_off: Influx.FieldType.BOOLEAN, no3_on_off: Influx.FieldType.BOOLEAN, sensor1_on_off: Influx.FieldType.BOOLEAN, sensor2_on_off: Influx.FieldType.BOOLEAN, No1Delay: Influx.FieldType.INTEGER, No1Count: Influx.FieldType.INTEGER, No2Count: Influx.FieldType.INTEGER, No3Count: Influx.FieldType.INTEGER, lamp_green: Influx.FieldType.BOOLEAN, lamp_yellow: Influx.FieldType.BOOLEAN, lamp_red: Influx.FieldType.BOOLEAN, No3Motor1: Influx.FieldType.INTEGER, No3Motor2: Influx.FieldType.INTEGER, No1ChipFull: Influx.FieldType.BOOLEAN, No2Chip: Influx.FieldType.BOOLEAN, No2CubeFull: Influx.FieldType.BOOLEAN, No2InPoint: Influx.FieldType.BOOLEAN, No2OutPoint: Influx.FieldType.BOOLEAN, No2Sol: Influx.FieldType.BOOLEAN, No2SolAction: Influx.FieldType.BOOLEAN, No2BackToSquare: Influx.FieldType.BOOLEAN, No2Mode: Influx.FieldType.BOOLEAN, No3Chip: Influx.FieldType.BOOLEAN, VisionCmdMemory: Influx.FieldType.BOOLEAN, No3DiceReading: Influx.FieldType.INTEGER, Emergency: Influx.FieldType.BOOLEAN, OutputLimit: Influx.FieldType.INTEGER, DiceValue: Influx.FieldType.INTEGER, DiceComparisonValue: Influx.FieldType.INTEGER, ColorSensorSensing: Influx.FieldType.BOOLEAN, No3Gripper: Influx.FieldType.BOOLEAN, }, tags: ['DataTime'], }, ], }); influx .getDatabaseNames() // eslint-disable-next-line consistent-return .then((names) => { if (!names.includes('backend')) { console.log(`My database names are: ${names.join(', ')}`); console.log('there is no database'); return influx.createDatabase('backend'); } }) /* .then(() => { http.createServer(app).listen(3000, () => { // console.log(`My database names are2: ${names.join(', ')}`); console.log('Listening on port 3000'); }); }) */ .catch((err) => { console.error('Error creating Influx database!'); console.log(({ err })); }); client.on('message', (topic, payload) => { console.log(`0: ${topic}: ${payload}`); // console.log(`1: ${topic}: ${JSON.stringify(payload)}`); // console.log(`2: ${topic}: ${JSON.parse(payload).tags}`); // console.log(`3: ${topic}: ${JSON.parse(payload).Wrapper}`); // console.log(`4: ${topic}: ${JSON.parse(payload).Wrapper[1].tagId}`); // console.log(`5: ${topic}: ${JSON.parse(payload).Wrapper[1].name}`); const keys = []; const values = []; let tags = {}; let fields = {}; for (let i = 0; i < JSON.parse(payload).Wrapper.length; i += 1) { keys[i] = JSON.parse(payload).Wrapper[i].name; values[i] = JSON.parse(payload).Wrapper[i].value; console.log(i, JSON.parse(payload).Wrapper.length, keys[i], values[i]); } console.log('keys: ', keys); console.log('values: ', values); for (let i = 0; i < keys.length; i += 1) { if (keys[i] === 'DataTime') { tags = { [keys[i]]: values[i], }; } else { fields = { ...fields, [keys[i]]: values[i], }; } console.log(i, keys.length); console.log('tags: ', tags); console.log('fields: ', fields); } influx.writePoints([ { measurement: 'plcdata', tags, fields, }, ], { // database: 'backend', // retentionPolicy: '1d', precision: 's', // 정밀도 (s)초 }).catch((error) => { console.error(`Error saving data to InfluxDB! ${error.stack}`); }); });
query: select*from <measuerment명>
UI사용 방법
influxDB studio 프로그램을 사용하여 확인가능
2조 멘토링 관련 - Sub 을 통한 PLC Write
//네임스페이스추가(설치해야함)
using uPLibrary.Networking.M2Mqtt;
using uPLibrary.Networking.M2Mqtt.Messages;
//전역으로 클래스선언
MqttClient client;
string clientId;
//MQTT 브로커와 연결하는 부분
string BrokerAddress = "broker.mqtt-dashboard.com";
client = new MqttClient(BrokerAddress);
// register a callback-function (we have to implement, see below) which is called by the library when a message was received
client.MqttMsgPublishReceived += client_MqttMsgPublishReceived;
// use a unique id as client id, each time we start the application
clientId = Guid.NewGuid().ToString();
client.Connect(clientId);
//MQTT이벤트 핸들러
void client_MqttMsgPublishReceived(object sender, MqttMsgPublishEventArgs e)
{
string ReceivedMessage = Encoding.UTF8.GetString(e.Message);
Console.WriteLine(ReceivedMessage);
//DO SOMETHING..!
}
//Subscribe Topic 추가
client.Subscribe(new string[] { Topic }, new byte[] { 0 }); // we need arrays as parameters because we can subscribe to different topics with one call
97 라인은 소켓 통신이 끊겼을때 다시 통신 하는 코드
106, 108 라인은 서버에서 샌드 데이터를 받아들이는 펑션이 구성되고 있다
그리고 데이터를 받아들였을때 113 번의 dynamic test 데이터를 통해
115 번 라인으로 돌아가도록 되어 있음
[](https://velog.velcdn.com/images/ansunny1170/post/0bddfdbe-a22a-49b3-b737-425b56d2d0fb/image.png)
MQTT 를 통한 해결책
1번에서의 Do someting 영역에 111번 라인부터 넣으면 PLC 어드레스에서 기반한 커넥션이 이루어 진후 test 대신 dynamic ReceivedMessage 를 통해 MQTT 리시브 데이터를 전달하고 이를 tagid 와 Value 항목을 통해 전달하여 라이팅 되도록 구성
즉 111 ~ 226번 라인을 //DO SOMETHING..! 에 넣을 것!!
안하면 아래와 같은 에러가 발생함
Sequelize sync error 참조되는 <table>
테이블을 위한 주어진 키와 일치하는 고유, 제약 조건이 없습니다.
내가 원하는 기능은, 관리자가 작업자를 최초 등록할 때, 이름 = 아이디로 사용할 것인데, 동명이인에게는 순서대로 이름 + 숫자
의 규칙으로 아이디를 부여할 것이다.
홍길동 -> 홍길동1 -> 홍길동2 -> ...
이렇게 하기위해서는 db상에서 중복된 아이디 체크를 하면 되겠지만, 더좋은 방법은 해당이름으로 등록된 아이디 중에 가장 큰 숫자로(가장 최근에) 등록된 아이디를 찾아내면 되는 것이다.
내가 생각한 방법은 from에 subquery를 사용하여 name중에 원하는 이름%
에 해당하는 table로 추려 거기서 가장 큰값의 id에 해당하는 row 하나를 읽어오는 것이다.
여기서 주의할 점은 postrgresql에서 subquery를 사용할 때 alias
는 필수라고 한다.
가장 큰 문제는 subquery를 sequelize에서 어떻게 표현하는가 이다. 찬찬히 읽어봐야 겠으나, 공식사이트에도 관련애용이 풍부하지는 않다.
findOne()
함수를 사용하여 LIMIT 1
의 효과를 주었고,name col
에서 like
로 원하는 이름을 간추렸다. 여기서 User.like는 안되고 [Op.like]만 되는 문제는 확인해야 한다.order
문으로 id를 기준으로 DESC
조건을 주었다. idOverlabCheck(params) {
return new Promise((resolve, reject) => {
User.findOne({
where: { name: { [Op.like]: `${params.name}%` } },
order: [['id', 'DESC']],
})
.then((idCehckResult) => {
resolve(idCehckResult);
}).catch((err) => {
reject(err);
});
});
},
};
※가장 좋은것은 sequelize에서 데이터를 받을 때, row가 아닌 id max값에 해당하는 name col값만 받아오면 되는 것이다...
아래 사진처럼 하면 되는데 이건 또 sequelize에서 어떻게 표현 할 수 있을까?
idOverlabCheck(params) {
return new Promise((resolve, reject) => {
User.findOne({
where: { name: { [Op.like]: `${params.name}%` } },
order: [['id', 'DESC']],
attributes: ['name'] // 이렇게 하면 내가 원했던 col만 출력할 수 있다.
// attributes: ['name', 'alias_name'], // alias를 넣고 싶다면!
})
.then((idCehckResult) => {
resolve(idCehckResult);
}).catch((err) => {
reject(err);
});
});
},
node.js사용하여 influx연동하는 기술 blog정보가 매우 적다(한국에선)
대부분 mqtt-telegraf-influxdb 순서로 연결을 하였으며 그 것도 node.js를 사용하지 않은 상태였다.
influxdb2.0부터는 달라진점도 더러있어 적용이 더욱 힘들었다.
node.js를 사용하여 influxdb에 연동한 사례는 해외사이트에 자료가 있었는데 대략 5개정도 참고할 수 있는 것을 찾았다.
해당 소스를 통해 influxdb node.js 연동 코드를 연구하였고, 준비해 놓을 수 있었다.
가장 큰 문제는 influxdb의 실행이었다. 방법은 일반 실행파일을 이용하여 실행하는 것과 docker 시스템을 이용하여 porting하는 것이다. 내가 찾은 방법들은 모두 docker파일과 ui를 이용하는 것들 뿐이었고, docker-compose.yaml파일을 사용하는 것은 없었다.
또한 influx1.x와 influx2.x는 차이가 커서 이것도 문제가 많았다.
나는 influx1.8.1을 선택하였다.
작성한 연동 코드와 docker파일 중 어느것도 문제가 없는지 확신할 수 없었기에, 방향을 돌려 실행파일을 이용해보았다.
자세한 설명은 본 게시글 위에 있다.
참조에 도움이 되었던 사이트를 기재하겠다.
참조:
node-influx
node-influx-tutorial
node 명령문, keyword 참조
node-quant - github
influxdb-nodejs - github
mqtt-influxdb-bridge - github
node-infux - github
influxdb-image - docker
MQTT to InfluxDB
influxdb studio, influxdb ui - github
docker-compose.yaml 파일 경로에서
docker-compose 명령어를 사용할 수 있음.
query문 예제
update items
set quantity = (select quantity from items where id =1) - 5
where id = 1;
sequelize문 예제
Item.update(
{
quantity: Item.findOne({
where: { name: params.name },
attributes: ['quantity'],
}) - 5,
},
{
where: { name: params.name },
},
)
이렇게 할경우 문제가 있는데
Item.findOne... 으로 하면 최종 return되는 값은
Promise { undefined } 이거나
Promise { Item { dataValues { quantity {.... 이러한 값이거나
Promise { } 으로 받아진다.
.then을 붙여서 resolve해서 받아도 똑같다.