sqlite

Programming/안드로이드 2018.09.29 01:03 Posted by 파란크리스마스

출처

DBManager.java

package com.bluexmas.common;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DBManager extends SQLiteOpenHelper {

    private static String TAG = DBManager.class.getName();

    // Database 버전
    public static final int DB_VERSION = 12;
    // Database 이름 (SQLite 파일명)
    public static final String DB_NAME = "bluexmas.db";

    // DBManager 객체 - 한번만 객체가 생성되도록 static 으로 변수 선언 : singleton 패턴
    private static DBManager instance = null;

    private Context context;

    // private 생성자 : singleton 패턴
    private DBManager(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
        this.context = context;
    }

    // DBManager 객체 반환 : singleton 패턴
    public static DBManager getInstance(Context context) {
        if (instance == null)
            instance = new DBManager(context);
        return instance;
    }

    public Context getContext() {
        return this.context;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        // 테이블 생성 관련 SQL 스크립트 실행
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // DB_VERSION 버전이 올라 가면 변경될 SQL 스크립트 실행
    }
}

VideoEntity.java

package com.bluexmas.common;

public class VideoEntity {

    public static final String TABLE_NAME = "video_info";

    public static final String COLUMN_NAME_MEDIA_ID = "v_id";
    public static final String COLUMN_NAME_TYPECODE = "type_code";
    public static final String COLUMN_NAME_FILEPATH = "path";
    public static final String COLUMN_NAME_REGDATE = "regdate";

    public String getTableName() {
        return TABLE_NAME;
    }

    public String genCreateSQL() {
        return "CREATE TABLE "+this.getTableName()+"( "
                + COLUMN_NAME_MEDIA_ID+" INTEGER PRIMARY KEY AUTOINCREMENT, "
                + COLUMN_NAME_TYPECODE+" CHAR(2) NOT NULL, "
                + COLUMN_NAME_FILEPATH+" TEXT NOT NULL, "
                + COLUMN_NAME_REGDATE+" TEXT NOT NULL "
                + ");";
    }
}

DBManager.java

package com.bluexmas.common;

public class DBManager extends SQLiteOpenHelper {

    //
    private static String TAG = DBManager.class.getName();

    // Database 버전
    public static final int DB_VERSION = 12;
    // Database 이름 (SQLite 파일명)
    public static final String DB_NAME = "bluexmas.db";

    // DBManager 객체 - 한번만 객체가 생성되도록 static 으로 변수 선언 : singleton 패턴
    private static DBManager instance = null;

    private Context context;

    private VideoEntity videoEntity = null;

    // private 생성자 : singleton 패턴
    private DBManager(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
        this.context = context;

        // VideoEntity 생체 생성
        this.videoEntity = new VideoEntity();
    }

    // DBManager 객체 반환 : singleton 패턴
    public static DBManager getInstance(Context context) {
        if (instance == null)
            instance = new DBManager(context);
        return instance;
    }

    public Context getContext() {
        return context;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        // 테이블 생성 관련 SQL 스크립트 실행

        // VideoEntity 테이블 생성
        db.execSQL(this.videoEntity.genCreateSQL());
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // DB_VERSION 버전이 올라 가면 변경될 SQL 스크립트 실행
    }

    // 비디오 정보 추가 (레코드 추가)
    public int addVideo(String type_code, String path) {
        if (path == null)
            return;

        // DB 객체 가져오기
        SQLiteDatabase db = this.getWritableDatabase();

        //
        ContentValues values = new ContentValues();
        values.put(this.videoEntity.COLUMN_NAME_TYPECODE, type_code);
        values.put(this.videoEntity.COLUMN_NAME_FILEPATH, path);
        values.put(this.videoEntity.COLUMN_NAME_REGDATE, "-");

        // 데이터 삽입
        long newRowId = db.insert(this.videoEntity.getTableName(), null, values);

        return getLastID(this.videoEntity.getTableName());
    }

    /**
     * 마지막 추가한 ID 구하기
     * @param table_name 테이블 이름
     * @return ID
     */
    public int getLastID(String table_name) {
        // DB 객체 가져오기
        SQLiteDatabase db = this.getWritableDatabase();

        final String query = "SELECT last_insert_rowid() FROM "+ table_name;
        Cursor cur = db.rawQuery(query, null);
        cur.moveToFirst();
        int last_id = cur.getInt(0);
        cur.close();
        return last_id;
    }
}



-

출처

안드로이드 부팅 이벤트 받기

BootReceiver,java - 부팅 이벤트를 받은 BroadcastReceiver를 상속받은 클래스 생성

package com.bluexmas.common;

import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
import android.util.Log;
import android.widget.Toast;

public class BootReceiver extends BroadcastReceiver {

    private final static String TAG = BootReceiver.class.getSimpleName();

    // BroadcastReceiver를 상속하여 처리 해줍니다.
    @Override
    public void onReceive(final Context context, Intent intent) {
        // TODO Auto-generated method stub
        // 전달 받은 Broadcast의 값을 가져오기
        // androidmanifest.xml에 정의한 인텐트 필터를 받아 올 수 있습니다.
        String action = intent.getAction();
        // 전달된 값이 '부팅완료' 인 경우에만 동작 하도록 조건문을 설정 해줍니다.
        if (action.equals("android.intent.action.BOOT_COMPLETED")) {
            // TODO
            // 부팅 이후 처리해야 코드 작성
            // Ex.서비스 호출, 특정 액티비티 호출등등
            Log.d(TAG, "action = " + action);

            //
            new Handler().postDelayed(new Runnable() {
                // 3초 후에 실행
                @Override public void run() {
                    //Toast.makeText(context, "-- BootReceiver.onReceive", Toast.LENGTH_LONG).show();

                    // BackgroundService
                    Intent serviceLauncher = new Intent(context, BackgroundService.class);
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                        context.startForegroundService(serviceLauncher);
                    } else {
                        context.startService(serviceLauncher);
                    }
                }
            }, 3000);
        }
    }

    public static boolean isServiceRunning(Context context, Class serviceClass) {
        ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (serviceClass.getName().equals(service.service.getClassName())) {
                Log.i (TAG,"ServiceRunning? = "+true);
                return true;
            }
        }
        Log.i(TAG,"ServiceRunning? = "+ false);
        return false;
    }
}

AndroidManifest.xml 내용추가

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.bluexmas.main" >

    <!-- 안드로이드 부팅 이벤트 받기 권한 추가 -->
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

    <application
        android:name="com.bluexmas.main.BluexmasApplication"
        android:icon="@drawable/icon"
        android:label="@string/app_name"
        android:largeHeap="true">

        <!-- 안드로이드 부팅 이벤트 받기 : BroadcastReceiver -->
        <receiver
            android:name="com.bluexmas.common.BootReceiver"
            android:enabled="true"
            android:exported="false"
            android:label="BOOTReceiver">

            <intent-filter >
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
            </intent-filter>
        </receiver>

앱이 종료 이후 백그라운드 서비스 실행 유지

서비스 생성

package com.bluexmas.common;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.os.SystemClock;
import android.util.Log;

import java.util.Timer;
import java.util.TimerTask;

public class BackgroundService extends Service {

    private final static String TAG = BackgroundService.class.getSimpleName();

    private Context context = null;
    public int counter=0;

    // 생성자1 : 반듯이 필요
    public BackgroundService() {
    }

    // 생성자2
    public BackgroundService(Context applicationContext) {
        super();
        context = applicationContext;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        // 서비스에서 가장 먼저 호출됨(최초에 한번만)
        Log.d(TAG, "BackgroundService.onCreate");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        // 서비스가 호출될 때마다 실행
        Log.d(TAG, "BackgroundService.onStartCommand");
        //
        startTimer();
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // 서비스가 종료될 때 실행
        Log.d(TAG, "BackgroundService.onDestroy");
        //
        Intent broadcastIntent = new Intent("com.bluexmas.common.RestartService");
        sendBroadcast(broadcastIntent);
        stoptimertask();
    }

    private Timer timer;
    private TimerTask timerTask;
    long oldTime=0;
    public void startTimer() {
        //set a new Timer
        timer = new Timer();

        //initialize the TimerTask's job
        initializeTimerTask();

        //schedule the timer, to wake up every 1 second
        timer.schedule(timerTask, 1000, 1000); //
    }

    /**
     * it sets the timer to print the counter every x seconds
     */
    public void initializeTimerTask() {
        timerTask = new TimerTask() {
            public void run() {
                Log.i(TAG, "in timer ++++  "+ (counter++));
            }
        };
    }

    /**
     * not needed
     */
    public void stoptimertask() {
        //stop the timer, if it's not already null
        if (timer != null) {
            timer.cancel();
            timer = null;
        }
    }
}

AndroidManifest.xml 내용추가 - 서비스와 종료 이벤트를 받은 receiver 등록

        <!-- BackgroundService -->
        <service
            android:name="com.bluexmas.common.BackgroundService"
            android:enabled="true" >
        </service>

        <receiver
            android:name="com.bluexmas.common.RestarterBroadcastReceiver"
            android:enabled="true"
            android:exported="true"
            android:label="RestartServiceWhenStopped">
            <intent-filter>
                <action android:name="com.bluexmas.common.RestartService" />
            </intent-filter>
        </receiver>

    </application>

종료 이벤트를 받은 receiver 클래스 생성

package com.bluexmas.common;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class RestarterBroadcastReceiver extends BroadcastReceiver {

    private final static String TAG = RestarterBroadcastReceiver.class.getSimpleName();

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i(TAG, "RestarterBroadcastReceiver.onReceive");
        context.startService(new Intent(context, BackgroundService.class));
    }
}

MainActivity - 서비스가 실행하고 있지 않는 경우 서비스 실행

    // BackgroundService
    private Intent mBackgroundServiceIntent;
    private BackgroundService mBackgroundService;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // ... 생략 ...

        // BackgroundService
        mBackgroundService = new BackgroundService(getApplicationContext());
        mBackgroundServiceIntent = new Intent(getApplicationContext(), mBackgroundService.getClass());
        // 서비스가 실행 중인지 확인
        if (!BootReceiver.isServiceRunning(this, mBackgroundService.getClass())) {
            // 서비스가 실행하고 있지 않는 경우 서비스 실행
            startService(mBackgroundServiceIntent);
        }
    }

사용자가 강제로 서비스를 종료하더라도 자동으로 서비스를 다시 시작하기

AndroidManifest.xml 내용추가 - android:stopWithTask="false"로 설정해야 onTaskRemoved 메소드가 호출됨

        <!-- BackgroundService -->
        <service
            android:name="com.bluexmas.common.BackgroundService"
            android:enabled="true"
            android:stopWithTask="false">
        </service>

기존 BackgroundService 클래스에 onTaskRemoved 오버라이드 메소드 구현

    // 사용자가 강제로 서비스를 종료하더라도 자동으로 서비스를 다시 시작하는 방법은 무엇입니까?
    @Override
    public void onTaskRemoved(Intent rootIntent) {
        Log.d(TAG, "BackgroundService.onTaskRemoved");
        //create an intent that you want to start again.
        Intent intent = new Intent(getApplicationContext(), BackgroundService.class);
        PendingIntent pendingIntent = PendingIntent.getService(this, 1, intent, PendingIntent.FLAG_ONE_SHOT);
        AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        alarmManager.set(AlarmManager.RTC_WAKEUP, SystemClock.elapsedRealtime() + 5000, pendingIntent);
        super.onTaskRemoved(rootIntent);
    }

Swift4 tip

Programming/Swift 2018.08.27 10:14 Posted by 파란크리스마스

출처

Dictionary to JSON

출처 : Convert Dictionary to JSON in Swift - Stack Overflow

        let jsonData = try! JSONSerialization.data(withJSONObject: parameters, options: JSONSerialization.WritingOptions.prettyPrinted)
        let jsonString = NSString(data: jsonData, encoding: String.Encoding.utf8.rawValue)! as String
        print(jsonString)

화면전환, 파라미터 전달

출처 : Swift 화면 전환 기법: 뷰 컨트롤러, 내비게이션 컨트롤러, 세그웨이 : 네이버 블로그

    func native_video_recording(_ data : NSDictionary) { 
        let vcRecord = self.storyboard!.instantiateViewController(withIdentifier: "vcRecord") as! RecordController // 1
        vcRecord.setParamData(data) // 파라미터 전달
        vcRecord.modalTransitionStyle = UIModalTransitionStyle.crossDissolve // 2
        self.present(vcRecord, animated: true) // 3
    }

카메라 리소스 구하기

출처 : GitHub - parthibanios/custom-Video-Recording-Swift-3.0: Record video using AVCapture AVFoundation framework

    //MARK:- Setup Camera
    func setupSession() -> Bool {
        
        //captureSession.sessionPreset = AVCaptureSessionPresetHigh
        //1080
        captureSession.sessionPreset = AVCaptureSession.Preset.hd1920x1080
        
        // Setup Camera
        let camera = AVCaptureDevice.default(for: .video)
        do {
            let input = try AVCaptureDeviceInput(device: camera!)
            if captureSession.canAddInput(input) {
                captureSession.addInput(input)
                activeInput = input
            }
        } catch {
            print("Error setting device video input: \(error)")
            return false
        }
        
        // Setup Microphone
        let microphone = AVCaptureDevice.default(for: .audio)
        do {
            let micInput = try AVCaptureDeviceInput(device: microphone!)
            if captureSession.canAddInput(micInput) {
                captureSession.addInput(micInput)//addInput(micInput)
            }
        } catch {
            print("Error setting device audio input: \(error)")
            return false
        }
        movieOutput.maxRecordedDuration = CMTimeMake(9, 1)
        
        // Movie output
        if captureSession.canAddOutput(movieOutput) {
            captureSession.addOutput(movieOutput)
        }
        
        return true
    }

도큐먼트 디렉토리 구하기 - UIFileSharingEnabled 설정의 Root 경로

출처 : Swift로 파일 다루기 :

Eth Developer's Lab

    //EDIT 1: I FORGOT THIS AT FIRST
    func getMp4PathURL() -> URL? {
        /*
        let directory = NSTemporaryDirectory() as NSString
        
        if directory != "" {
            let path = directory.appendingPathComponent(NSUUID().uuidString + ".mp4")
            return URL(fileURLWithPath: path)
        }
        return nil
        */
        
        // 도큐먼트 디렉토리 구하기 - UIFileSharingEnabled의 Root 경로
        guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return nil }
        let fileName = NSUUID().uuidString + ".mp4"
        let fileURL = documentsDirectory.appendingPathComponent(fileName)
        
        /*
        print(imageURL)
        do {
            let imageData = try Data(contentsOf: imageURL)
            return UIImage(data: imageData)
        } catch let err as NSError {
            print("이미지 로딩 에러 : \(err)")
        }
        */
        
        return fileURL
    }

Swift4 : Alamofire 프로젝트에 추가하기

출처 : [iOS / Swift4] Alamofire 사용하기 | ry4nkim

Podfile 파일 편집

# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

target 'project' do
  # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
  use_frameworks!

  project 'project.xcodeproj'

  # Pods for project
  pod 'Firebase/Core'
  pod 'Firebase/Messaging'
  pod 'Alamofire', '~> 4.7'

end

pod install

$ pod install
Analyzing dependencies
Downloading dependencies
Installing Alamofire (4.7.3)
Installing Firebase 5.4.0
Installing FirebaseAnalytics (5.0.1)
Using FirebaseCore (5.0.5)
Using FirebaseInstanceID (3.1.1)
Using FirebaseMessaging (3.0.3)
Using GoogleToolboxForMac (2.1.4)
Using Protobuf (3.6.0)
Installing nanopb (0.3.8)
Generating Pods project
Integrating client project
Sending stats
Pod installation complete! There are 3 dependencies from the Podfile and 9 total pods installed.
 
[!] Automatically assigning platform `ios` with version `9.3` on target `project` because no platform was specified. Please specify a platform for this target in your Podfile. See `https://guides.cocoapods.org/syntax/podfile.html#platform`.

Alamofire 파일 업로드, 결과 JSON 파싱

출력 : swift3 - upload image to server using Alamofire - Stack Overflow, ios - How to parse JSON response from Alamofire API in Swift? - Stack Overflow

        Alamofire.upload(multipartFormData: { (multipartFormData) in
            /*
            for (key, value) in parameters {
                multipartFormData.append("\(value)".data(using: String.Encoding.utf8)!, withName: key as String)
            }
            */
            multipartFormData.append("\(jsonString)".data(using: String.Encoding.utf8)!, withName: "json_str")
            
            /*
            if let data = imageData{
                multipartFormData.append(data, withName: "image", fileName: "image.png", mimeType: "image/png")
            }
            */
            multipartFormData.append(mp4FileUrl, withName: "test.mp4")
            
        }, usingThreshold: UInt64.init(), to: self.upload_url, method: .post, headers: headers) { (result) in
            switch result{
            case .success(let upload, _, _):
                upload.responseJSON { response in
                    print("Succesfully uploaded")
                    if let err = response.error {
                        print(err)
                        onCompletion?(nil, err)
                        return
                    }
                    if let result = response.result.value {
                        let JSON = result as! NSDictionary
                        print(JSON)
                        onCompletion?(JSON, nil)
                    }
                }
            case .failure(let error):
                print("Error in upload: \(error.localizedDescription)")
                onCompletion?(nil, error)
            }
        }

Timer, String format

출처 : ios - Swift - Update/Refresh Label that Displays Time - Stack Overflow

    let movieOutput = AVCaptureMovieFileOutput()
    var durationTimer: Timer?

    //MARK:- Setup Camera
    func setupSession() -> Bool {
        
        //captureSession.sessionPreset = AVCaptureSessionPresetHigh
        //1080
        captureSession.sessionPreset = AVCaptureSession.Preset.hd1920x1080
        
        // Setup Camera
        // 생략
        // Setup Microphone
        // 생략
 
        // 녹화 시간 제한
        movieOutput.maxRecordedDuration = CMTimeMake(300, 1)
        
        return true
    }

    @IBAction func startRecording() {

        if movieOutput.isRecording == false {
        
// ... 생략 ...
            
            self.seconds = 0
            self.durationTimer = Timer(timeInterval: 1.0, target: self, selector: #selector(self.refreshDurationLabel), userInfo: nil, repeats: true)
            RunLoop.current.add(self.durationTimer!, forMode: RunLoopMode.commonModes)
            self.durationTimer?.fire()
        } else {
            self.durationTimer?.invalidate()
            self.durationTimer = nil
            self.seconds = 0
            //self.durationTxt.text = secondsToFormatTimeFull(second: 0)
            stopRecording()
        }
    }

    func secondsToFormatTimeFull(second: Int)->String {
        let sec : Int = second % 60
        let min : Int = second / 60
        return String(format : "%02d:%02d", min, sec)
    }

    @objc func refreshDurationLabel() {
        self.durationTxt.text = secondsToFormatTimeFull(second: self.seconds)
        seconds = seconds + 1
    }

연락처 조회, 선택, WebView 자바스크립트 호출(evaluateJavaScript)

출처 : ContactsUISample/ViewController.swift at master · koogawa/ContactsUISample · GitHub
[SWIFT] 주소록에 저장된 데이터 불러오기 - Things take time - 티스토리

import Contacts
import ContactsUI

class WebViewBase : UIViewController, CNContactPickerDelegate {

    func native_show_contacts() {
        let pickerViewController = CNContactPickerViewController()
        pickerViewController.delegate = self
        
        // Display only a person's phone, email, and postal address
        let displayedItems = [CNContactPhoneNumbersKey, CNContactEmailAddressesKey, CNContactPostalAddressesKey]
        pickerViewController.displayedPropertyKeys = displayedItems
        
        // Show the picker
        self.present(pickerViewController, animated: true, completion: nil)
    }

    // MARK: CNContactPickerDelegate methods
    // Called when a property of the contact has been selected by the user.
    func contactPicker(picker: CNContactPickerViewController, didSelectContactProperty contactProperty: CNContactProperty) {
        //
    }
    
    func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
        // You can fetch selected name and number in the following way
        
        // user name
        let userName:String = contact.givenName
        
        let name = contact.familyName + contact.givenName
        //print(name)
        
        var primaryPhoneNumberStr:String = ""
        
        if (!contact.phoneNumbers.isEmpty) {
            // user phone number
            let userPhoneNumbers:[CNLabeledValue<cnphonenumber>] = contact.phoneNumbers
            let firstPhoneNumber:CNPhoneNumber = userPhoneNumbers[0].value
            
            
            // user phone number string
            primaryPhoneNumberStr = firstPhoneNumber.stringValue
        }
        
        //print("phonenum = \(primaryPhoneNumberStr)")
        
        webView.evaluateJavaScript("setContract('\(name)', '\(primaryPhoneNumberStr)')")
    }
    
    // Called when the user taps Cancel.
    func contactPickerDidCancel(picker: CNContactPickerViewController) {
        //
    }
}

jni

Programming/Java 2018.08.25 23:07 Posted by 파란크리스마스

출처

py_pwm.h

typedef struct
{
    //PyObject_HEAD
    unsigned int gpio;
    float freq;
    float dutycycle;
} PWMObject;

int PWM_init(PWMObject *self, int channel, float frequency);
void PWM_start(PWMObject *self, float dutycycle);
int PWM_ChangeDutyCycle(PWMObject *self, float dutycycle);

PWMObject.java

package com.pi4j.wiringpi;

public class PWMObject {

	public int gpio;
	public float freq;
	public float dutycycle;

	public PWMObject() {

	}

	public int getGpio() {
		return gpio;
	}

	public void setGpio(int gpio) {
		this.gpio = gpio;
	}

	public float getFreq() {
		return freq;
	}

	public void setFreq(float freq) {
		this.freq = freq;
	}

	public float getDutycycle() {
		return dutycycle;
	}

	public void setDutycycle(float dutycycle) {
		this.dutycycle = dutycycle;
	}

}

com_pi4j_wiringpi_ASUSGpio.h

#include <jni.h>
/* Header for class com_pi4j_wiringpi_ASUSGpio */

#ifndef _Included_com_pi4j_wiringpi_ASUSGpio
#define _Included_com_pi4j_wiringpi_ASUSGpio
#ifdef __cplusplus
extern "C" {
#endif

#undef com_pi4j_wiringpi_ASUSGpio_ASUS
#define com_pi4j_wiringpi_ASUSGpio_ASUS 13L
#undef com_pi4j_wiringpi_ASUSGpio_OUTPUT
#define com_pi4j_wiringpi_ASUSGpio_OUTPUT 1L

JNIEXPORT jint JNICALL Java_com_pi4j_wiringpi_ASUSGpio_pySetmode
  (JNIEnv *, jclass, jint);

JNIEXPORT jint JNICALL Java_com_pi4j_wiringpi_ASUSGpio_pySetupChannel
  (JNIEnv *, jclass, jint, jint);

JNIEXPORT jobject JNICALL Java_com_pi4j_wiringpi_ASUSGpio_pwmInit
  (JNIEnv *, jclass, jint, jfloat);

JNIEXPORT void JNICALL Java_com_pi4j_wiringpi_ASUSGpio_pwmStart
  (JNIEnv *, jclass, jobject, jfloat);

JNIEXPORT jint JNICALL Java_com_pi4j_wiringpi_ASUSGpio_pwmChangeDutyCycle
  (JNIEnv *, jclass, jobject, jfloat);

#ifdef __cplusplus
}
#endif
#endif

com_pi4j_wiringpi_ASUSGpio.c

#include <stdio.h>
#include <jni.h>
#include <wiringPi.h>
#include <RKIO.h>
#include <wiringTB.h>
#include <constants.h>
#include <common.h>
#include <c_gpio.h>
#include <py_gpio.h>
#include <py_pwm.h>
#include "com_pi4j_wiringpi_ASUSGpio.h"

void setPwmObject(JNIEnv *env, jclass targetClass, jobject newObject, PWMObject pwm) {
	
	jfieldID fid;
	
	// JniObject 객체의 intField 필드값 설정
	fid = (*env)->GetFieldID(env, targetClass, "gpio", "I");
	(*env)->SetIntField(env, newObject, fid, pwm.gpio);
	
	// JniObject 객체의 intField 필드값 설정
	fid = (*env)->GetFieldID(env, targetClass, "freq", "F");
	(*env)->SetFloatField(env, newObject, fid, pwm.freq);
	
	// JniObject 객체의 intField 필드값 설정
	fid = (*env)->GetFieldID(env, targetClass, "dutycycle", "F");
	(*env)->SetFloatField(env, newObject, fid, pwm.dutycycle);
}

void getPwmObject(JNIEnv *env, jclass targetClass, jobject newObject, PWMObject *pwm) {
	
	jfieldID fid;
	
	// JniObject 객체의 intField 필드값 설정
	fid = (*env)->GetFieldID(env, targetClass, "gpio", "I");
	pwm->gpio = (*env)->GetIntField(env, newObject, fid);
	
	// JniObject 객체의 intField 필드값 설정
	fid = (*env)->GetFieldID(env, targetClass, "freq", "F");
	pwm->freq = (*env)->GetFloatField(env, newObject, fid);
	
	// JniObject 객체의 intField 필드값 설정
	fid = (*env)->GetFieldID(env, targetClass, "dutycycle", "F");
	pwm->dutycycle = (*env)->GetFloatField(env, newObject, fid);
}

JNIEXPORT jint JNICALL Java_com_pi4j_wiringpi_ASUSGpio_pySetmode
(JNIEnv *env, jclass obj, jint new_mode)
{
	return py_setmode(new_mode);
}

JNIEXPORT jint JNICALL Java_com_pi4j_wiringpi_ASUSGpio_pySetupChannel
(JNIEnv *env, jclass obj, jint channel, jint direction)
{
	return py_setup_channel(channel, direction);
}

JNIEXPORT jobject JNICALL Java_com_pi4j_wiringpi_ASUSGpio_pwmInit
(JNIEnv *env, jclass obj, jint channel, jfloat frequency)
{
	//pwm = GPIO.PWM(myservo,50) # 50hz yani 20mslik periyod
	PWMObject pwm;
	PWM_init(&pwm, channel, frequency);
	//printf("pwm.gpio = %d\n", pwm.gpio);
	
	//
	jclass targetClass = (*env)->FindClass(env, "com/pi4j/wiringpi/PWMObject");
	
	// 생성자 찾기
	jmethodID mid = (*env)->GetMethodID(env, targetClass, "<init>", "()V");

	// 객체 생성(객체 레퍼런스 반환)
	jobject newObject = (*env)->NewObject(env, targetClass, mid, "()V");
	
	setPwmObject(env, targetClass, newObject, pwm);
	
	return newObject;
}

JNIEXPORT void JNICALL Java_com_pi4j_wiringpi_ASUSGpio_pwmStart
(JNIEnv *env, jclass obj, jobject thiz, jfloat dutycycle)
{
	PWMObject pwm;
	
	//
	jclass targetClass = (*env)->FindClass(env, "com/pi4j/wiringpi/PWMObject");
	getPwmObject(env, targetClass, thiz, &pwm);
	
	PWM_start(&pwm, dutycycle);
	setPwmObject(env, targetClass, thiz, pwm);
}

JNIEXPORT jint JNICALL Java_com_pi4j_wiringpi_ASUSGpio_pwmChangeDutyCycle
(JNIEnv *env, jclass obj, jobject thiz, jfloat dutycycle)
{
	PWMObject pwm;
	
	//
	jclass targetClass = (*env)->FindClass(env, "com/pi4j/wiringpi/PWMObject");
	getPwmObject(env, targetClass, thiz, &pwm);
	
	int result = PWM_ChangeDutyCycle(&pwm, dutycycle);
	setPwmObject(env, targetClass, thiz, pwm);
	
	return result;
}

ASUSGpio.java

package com.pi4j.wiringpi;

import com.pi4j.util.NativeLibraryLoader;

public class ASUSGpio {

  // private constructor
  private ASUSGpio()  {
    // forbid object construction
  }

  public static final int ASUS = 13;

  public static final int OUTPUT = 1;

  static {
    // Load the platform library
    NativeLibraryLoader.load("libpi4j.so");
  }

  public static native int pySetmode(int new_mode);

  public static native int pySetupChannel(int channel, int direction);

  public static native PWMObject pwmInit(int channel, float frequency);

  public static native void pwmStart(PWMObject self, float dutycycle);

  public static native int pwmChangeDutyCycle(PWMObject self, float dutycycle);
}

빌드

$ export SimulatedPlatform="TinkerBoard GPIO Provider"
$ mvn clean install

-----------------

hml-equation-parser 패키지를 설치

$ sudo apt-get install pandoc
$ sudo pip3 install typing
$ sudo pip3 install hml_equation_parser

jpserve 패키지를 설치

$ sudo pip install jpserve

jython 설치

$ java -jar jython-installer-2.7.0.jar

Font awesome 가운데 정렬

Programming/JavaScript, Sencha 2018.08.13 23:46 Posted by 파란크리스마스

출처

css 파일 참조

<link rel="stylesheet" href="<c:url value="/commons/fontawesome/css/all.css"/>">

css

.fa {
	vertical-align: middle;
}

html

<i class="fa fa-plus-circle fa-2x" aria-hidden="true" style="margin-left: 5px;"></i>
<i class="fa fa-minus-circle fa-2x" aria-hidden="true"></i>

Linux에 Maven 설치하기

Programming/Java 2018.07.21 22:06 Posted by 파란크리스마스

출처

Maven 다운로드

$ wget http://mirror.navercorp.com/apache/maven/maven-3/3.5.4/binaries/apache-maven-3.5.4-bin.tar.gz

Maven 압축 해제

$ tar xvf apache-maven-3.5.4-bin.tar.gz

Maven /opt 디렉토리 밑으로 이동

$ sudo mv apache-maven-3.5.4 /opt

Maven /opt/maven 링크 설정

$ sudo ln -s /opt/apache-maven-3.5.4 /opt/maven

Maven 환경설정

$ echo "export M2_HOME=/opt/maven" | sudo tee -a /etc/profile
$ source /etc/profile
$ echo "export PATH=$PATH:$M2_HOME/bin" | sudo tee -a /etc/profile
$ source /etc/profile

Maven 설치확인

$ mvn -version
Apache Maven 3.5.4 (1edded0938998edf8bf061f1ceb3cfdeccf443fe; 2018-06-18T02:33:14+08:00)
Maven home: /opt/maven
Java version: 1.8.0_171, vendor: Oracle Corporation, runtime: /usr/lib/jvm/java-8-openjdk-armhf/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "3.4.39-bpi-m2p-kernel", arch: "arm", family: "unix"

jstree

Programming/JavaScript, Sencha 2018.07.21 18:53 Posted by 파란크리스마스

출처

ajax

	// ajax demo
	$('#math_class_tree')
		.on('changed.jstree', function (e, data) {
			console.log('select node id / ' + data.node.id);
		})
		.jstree({
			'core' : {
				'data' : {
					"url" : function(node) {
						console.log('node.id = ' + node.id);
						return "json.mat";
					},
					"data" : function(node) {
						return {
							'cmd' : 'mat_class_table',
							'subcmd' : 'tree',
							'id' : node.id
						};
					},
					"dataType" : "json",
					"success" : function(data, status, res, parent) {
						console.log("test");
					}
				}
			}
		});


Swift4 - WKWebView 에서 console.log 사용하기

Programming/Swift 2018.07.02 13:13 Posted by 파란크리스마스

출처

Code

    func initWebView(activityIndicator: UIActivityIndicatorView) {
        // 생략...
        contentController.add(self, name: "native_console_log")
        // 생략...
    }

    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if(message.name == "native_console_log") {
            native_console_log(didReceive: message)
        }
    }

    func native_console_log(didReceive message: WKScriptMessage) {
        print(NSString(string: "console.log: \(message.body)"))
    }
 
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        #if CONSOLE_LOG
            let js = "var console = { log: function(message) { webkit.messageHandlers.native_console_log.postMessage(JSON.stringify(message)) } }"
            webView.evaluateJavaScript(js)
        #endif
    }

빌드 옵션 추가

Tomcat SSL 설정 - https 서비스

Programming/Java 2018.06.21 00:42 Posted by 파란크리스마스

출처

키생성 (bluexmas.jks 파일 생성)

C:\Users\bluesanta>"%JAVA_HOME%\bin\keytool" -genkey -keysize 2048 -keyalg RSA -keystore bluexmas.jks -dname "CN=me.co.kr, OU=team, O=company, L=seoul, ST=seoul, C=kr" -validity 3650
키 저장소 비밀번호 입력:
새 비밀번호 다시 입력:
<mykey>에 대한 키 비밀번호를 입력하십시오.
        (키 저장소 비밀번호와 동일한 경우 Enter 키를 누름):
새 비밀번호 다시 입력:

Warning:
JKS 키 저장소는 고유 형식을 사용합니다. "keytool -importkeystore -srckeystore bluexmas.jks -destkeystore bluexmas.jks -deststoretype pkcs12"를 사용하는 산업 표준 형식인 PKCS12로 이전하는 것이 좋습니다.

키파일 적용 (conf/server.xml)

    <Connector 
    	port="8443" 
	protocol="HTTP/1.1" SSLEnabled="true"
	maxThreads="150" scheme="https" secure="true" clientAuth="false"
	keystoreFile="conf/bluexmas.jks" keystorePass="bluexmas"
	sslEnabledProtocols="TLSv1.1,TLSv1.2"
	ciphers="TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
		TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
		TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA256,
		TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA256,
		TLS_RSA_WITH_AES_256_CBC_SHA" />

리다이렉트 시키기 (WEB-INF/web.xml 내용추가)

	<security-constraint>   
		<web-resource-collection>      
			<web-resource-name>Secured</web-resource-name>      
			<url-pattern>/*</url-pattern>   
		</web-resource-collection>   
		<user-data-constraint>      
			<transport-guarantee>CONFIDENTIAL</transport-guarantee>   
		</user-data-constraint>
	</security-constraint>

TOMCAT - jmx 로 monitoring

Programming/Java 2017.12.07 23:46 Posted by 파란크리스마스

출처 : 아파치 톰캣(apache tomcat) 을 jmx 로 monitoring 하기

catalina-jmx-remote.jar 파일 Tomcat이 설치된 디렉토리의 lib디렉토리에 복사

https://archive.apache.org/dist/tomcat/tomcat-8/v8.5.23/bin/extras/

$ cd /usr/local/apache-tomcat-8.5.23/lib
$ wget https://archive.apache.org/dist/tomcat/tomcat-8/v8.5.23/bin/extras/catalina-jmx-remote.jar

IP 확인

$ ifconfig | grep "inet addr"
          inet addr:192.168.0.24  Bcast:192.168.0.255  Mask:255.255.255.0
          inet addr:127.0.0.1  Mask:255.0.0.0

setenv.sh 파일 생성

$ cd /usr/local/apache-tomcat-8.5.23/bin

setenv.sh 파일 내용

$ vi setenv.sh
  • -Dcom.sun.management.jmxremote.authenticate=false / 인증하지 않음
  • -Djava.rmi.server.hostname=192.168.0.24 / 톰캣이 구동되는 서버의 IP
  • -Dcom.sun.management.jmxremote.ssl=false / SSL 을 사용하지 않음
#!/bin/sh

JMX_OPTS=" -Dcom.sun.management.jmxremote \
                 -Dcom.sun.management.jmxremote.authenticate=false \
                 -Djava.rmi.server.hostname=192.168.0.24 \
                 -Dcom.sun.management.jmxremote.ssl=false "
CATALINA_OPTS=" ${JMX_OPTS} ${CATALINA_OPTS}"

conf/server.xml 수정 - Listener 추가

<Server port="8005" shutdown="SHUTDOWN">

<!-- 생략 --->

<Listener className="org.apache.catalina.mbeans.JmxRemoteLifecycleListener"
        rmiRegistryPortPlatform="9840" rmiServerPortPlatform="9841"/>

jdk의 jconsole.exe 실행

권한파일(jmxremote.access)과 로그인 암호파일(jmxremote.password) 생성

$ cd /usr/local/apache-tomcat-8.5.23/
$ echo -e "monitorRole readonly\ncontrolRole readwrite" > conf/jmxremote.access
$ cat conf/jmxremote.access
monitorRole readonly
controlRole readwrite
$ echo -e "monitorRole tomcat\ncontrolRole tomcat" > conf/jmxremote.password
$ cat conf/jmxremote.password
monitorRole tomcat
controlRole tomcat

setenv.sh 파일 수정

$ cd /usr/local/apache-tomcat-8.5.23/bin
$ vi setenv.sh

#!/bin/sh

JMX_OPTS=" -Dcom.sun.management.jmxremote \
           -Dcom.sun.management.jmxremote.authenticate=true \
           -Dcom.sun.management.jmxremote.password.file=$CATALINA_BASE/conf/jmxremote.password  \
           -Dcom.sun.management.jmxremote.access.file=$CATALINA_BASE/conf/jmxremote.access  \
           -Djava.rmi.server.hostname=192.168.0.24 \
           -Dcom.sun.management.jmxremote.ssl=false "
CATALINA_OPTS=" ${JMX_OPTS} ${CATALINA_OPTS}"

setenv.bat 파일 내용 - Windows 용

set JMX_OPTS=-Dcom.sun.management.jmxremote ^
-Dcom.sun.management.jmxremote.authenticate=true ^
-Dcom.sun.management.jmxremote.password.file=%CATALINA_HOME%/conf/jmxremote.password ^
-Dcom.sun.management.jmxremote.access.file=%CATALINA_HOME%/conf/jmxremote.access ^
-Djava.rmi.server.hostname=192.168.0.31 ^
-Dcom.sun.management.jmxremote.ssl=false

set CATALINA_OPTS=%JMX_OPTS% %CATALINA_OPTS%

Nexacro - 공통라이브러리, 공통 팝업 사용하기

Programming/Nexacro 2017.12.03 14:38 Posted by 파란크리스마스

출처 : 넥사크로플랫폼(nexacro platform) HTML5 API 설치

기존 Nexacro 프로젝트 폴더에서 frame, image, lib 폴더를 복사해서 현재 프로젝트에 붙여넣기

복사한 폴더 등록 (서비스 등록)

TypeDefinition 더블 클릭

[Services] 텝 [Add] 버튼 선택

frame 추가

image 추가

lib 추가

추가된 목록

등록 확인

공통 라이브러리 참조

include "lib::comLib.xjs"

Nexacro - Grid에 CheckBox 추가 하기

Programming/Nexacro 2017.12.03 11:59 Posted by 파란크리스마스

출처 : 파트 IV. 컴포넌트 / 16. Grid 16.10그리드 행/열 합계 - 넥사크로플랫폼 ...
농부지기 :: Nexacro.Grid - all Checked 처리
농부지기 :: Nexacro.Grid - grid header

Dataset에 Checkbox의 체크값을 저장하기 위한 컬럼 추가

컬럼 추가 버튼 선택

chk 컬럼 추가

Grid의 컬럼 추가

[Insert] 버튼을 선택하여 Grid에 컬럼을 추가

컬럼헤더의 text 수정

컬럼의 expr, text 수정

  • expr : expr:chk=='Y'?'1':'0'
  • text : bind:chk

컬럼의 displaytype, edittype를 checkbox로 수정

  • displaytype : checkbox
  • edittype : checkbox

Grid의 Checkbox 컬럼의 체크 값 변경시 Dataset에 반영되도록 스크립트 작성

Nexacro 의 체크박스는 체크가 된 경우 1 / 체크가 되지 않은 경우 0의 Dataset의 체크값을 변경하기 위해서 스크립트 작성

this.Dataset00_oncolumnchanged = function(obj:Dataset, e:nexacro.DSColChangeEventInfo)
{
	// chk 컬럼인 경우
	if(e.columnid == "chk") {
		// 다른 이벤트가 동작하지 않도록 설정
		obj.enableevent = false;  //false can stop event temporarily  
		// 변경된 값이 1(체크가 된 경우)인경우
		if(e.newvalue == '1') {
			// 1인 경우 Dataset의 chk 값을 Y로 변경
			obj.setColumn(e.row,"chk",'Y');
		} else if(e.newvalue == '0') {
			// 0인 경우 Dataset의 chk 값을 N로 변경
			obj.setColumn(e.row,"chk",'N');  
		}
		// 다시 이벤트가 정상 동작하도록 설정
		obj.enableevent = true;
	}
}

체크값 전달을 위해서 SampleVO 클래스에 chk 변수 추가

public class SampleVO extends DefaultVO implements Serializable {
	
	private java.lang.String chk;

// ... 생략 ...

	public java.lang.String getChk() {
		return chk;
	}

	public void setChk(java.lang.String chk) {
		this.chk = chk;
	}


... 생략 ...

컬럼 헤더 클릭시 체크박스 전부 선택되도록 스크립트 작성

this.Grid00_onheadclick = function(obj:Grid, e:nexacro.GridClickEventInfo)
{
	// 첫 번째 컬럼인 경우
	if (e.col == 0) { 
		// Dataset의 레코드 개수 만큼 반복
		for(var nRow=0; nRow < this.Dataset00.getRowCount(); nRow++) {
			// chk 컬럼의 값을 Y로 변경
			this.Dataset00.setColumn(nRow, "chk", 'Y');
		}
	}
}

체크값 확인

package bizservice.nexa.sample.service;

public class EgovSampleServiceXpImpl extends AbstractServiceImpl implements EgovSampleServiceXp {

	 public void multiDataModifyService(List<Object> tranInfo,
				Map<String, Object> inVar, Map<String, List<Object>> inDataset,
				Map<String, Object> outVar, Map<String, Object> outDataset)
				throws Exception {
				
			//서비스단 개발
			List<Object> list = inDataset.get("input1");
			
			for (int i = 0; i < list.size(); i++) {
				Object obj = list.get(i);
				if (obj instanceof SampleVO){
					SampleVO row = (SampleVO)obj;
					System.out.println("pk.post_id = " + row.getPost_id() + "/체크여부 = " + row.getChk());
					// 추가 작업 구현
				}
			}

조회 SQL문 수정 (src\main\resources\egovframework\sqlmap\bizservice\sample\Sample.xml) - 불필요한 작업

	<!-- Sample resultObject -->
	<resultMap id="sample" class="bizservice.nexa.sample.vo.SampleVO">
		<result property="chk" column="CHK" />  <!-- chk 컬럼 추가 -->

<!-- 생략 -->
        
	<select id="sampleDAO.selectSampleVOList" parameterClass="searchVO" resultMap="sample">
		SELECT 'N' chk,
		       A.num AS num,
 		       A.TITLE AS TITLE, 
		       A.REG_ID AS REG_ID, 
 		       A.REG_DATE AS REG_DATE, 
		       A.POST_ID AS POST_ID, 
		       A.CONTENTS AS CONTENTS, 
		       A.COMMUNITY_ID AS COMMUNITY_ID,
		       A.COUNT AS COUNT
		  FROM XP_BOARD A
		 WHERE 1=1
	</select>

<!-- 생략 -->